@uncefact/untp-utils 0.0.1 → 0.2.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.
Files changed (221) hide show
  1. package/LICENSE +202 -674
  2. package/README.md +23 -1
  3. package/build/artefacts/detect-version-from-context.d.ts +46 -0
  4. package/build/artefacts/detect-version-from-context.js +77 -0
  5. package/build/artefacts/detect-version-from-context.js.map +1 -0
  6. package/build/artefacts/detect-version-from-context.test.d.ts +1 -0
  7. package/build/artefacts/detect-version-from-context.test.js +69 -0
  8. package/build/artefacts/detect-version-from-context.test.js.map +1 -0
  9. package/build/artefacts/index.d.ts +3 -0
  10. package/build/artefacts/index.js +4 -0
  11. package/build/artefacts/index.js.map +1 -0
  12. package/build/artefacts/urls.d.ts +79 -0
  13. package/build/artefacts/urls.js +133 -0
  14. package/build/artefacts/urls.js.map +1 -0
  15. package/build/artefacts/urls.test.d.ts +1 -0
  16. package/build/artefacts/urls.test.js +91 -0
  17. package/build/artefacts/urls.test.js.map +1 -0
  18. package/build/artefacts/version.d.ts +9 -0
  19. package/build/artefacts/version.js +17 -0
  20. package/build/artefacts/version.js.map +1 -0
  21. package/build/artefacts/version.test.d.ts +1 -0
  22. package/build/artefacts/version.test.js +16 -0
  23. package/build/artefacts/version.test.js.map +1 -0
  24. package/build/cache/in-memory-ttl-cache.d.ts +2 -0
  25. package/build/cache/in-memory-ttl-cache.js +76 -0
  26. package/build/cache/in-memory-ttl-cache.js.map +1 -0
  27. package/build/cache/in-memory-ttl-cache.test.d.ts +1 -0
  28. package/build/cache/in-memory-ttl-cache.test.js +236 -0
  29. package/build/cache/in-memory-ttl-cache.test.js.map +1 -0
  30. package/build/cache/index.d.ts +2 -0
  31. package/build/cache/index.js +2 -0
  32. package/build/cache/index.js.map +1 -0
  33. package/build/cache/ttl-cache.d.ts +22 -0
  34. package/build/cache/ttl-cache.js +2 -0
  35. package/build/cache/ttl-cache.js.map +1 -0
  36. package/build/common/as-non-empty-string.d.ts +7 -0
  37. package/build/common/as-non-empty-string.js +10 -0
  38. package/build/common/as-non-empty-string.js.map +1 -0
  39. package/build/common/as-non-empty-string.test.d.ts +1 -0
  40. package/build/common/as-non-empty-string.test.js +25 -0
  41. package/build/common/as-non-empty-string.test.js.map +1 -0
  42. package/build/common/require-string.d.ts +12 -0
  43. package/build/common/require-string.js +27 -0
  44. package/build/common/require-string.js.map +1 -0
  45. package/build/common/require-string.test.d.ts +1 -0
  46. package/build/common/require-string.test.js +53 -0
  47. package/build/common/require-string.test.js.map +1 -0
  48. package/build/conformity-vocabulary/codes.d.ts +10 -0
  49. package/build/conformity-vocabulary/codes.js +10 -0
  50. package/build/conformity-vocabulary/codes.js.map +1 -0
  51. package/build/conformity-vocabulary/errors.d.ts +44 -0
  52. package/build/conformity-vocabulary/errors.js +68 -0
  53. package/build/conformity-vocabulary/errors.js.map +1 -0
  54. package/build/conformity-vocabulary/index.d.ts +6 -0
  55. package/build/conformity-vocabulary/index.js +7 -0
  56. package/build/conformity-vocabulary/index.js.map +1 -0
  57. package/build/conformity-vocabulary/parse-conformity-catalogue.d.ts +26 -0
  58. package/build/conformity-vocabulary/parse-conformity-catalogue.js +106 -0
  59. package/build/conformity-vocabulary/parse-conformity-catalogue.js.map +1 -0
  60. package/build/conformity-vocabulary/parse-conformity-catalogue.test.d.ts +1 -0
  61. package/build/conformity-vocabulary/parse-conformity-catalogue.test.js +327 -0
  62. package/build/conformity-vocabulary/parse-conformity-catalogue.test.js.map +1 -0
  63. package/build/conformity-vocabulary/parse-conformity-scheme.d.ts +28 -0
  64. package/build/conformity-vocabulary/parse-conformity-scheme.js +47 -0
  65. package/build/conformity-vocabulary/parse-conformity-scheme.js.map +1 -0
  66. package/build/conformity-vocabulary/parse-conformity-scheme.test.d.ts +1 -0
  67. package/build/conformity-vocabulary/parse-conformity-scheme.test.js +284 -0
  68. package/build/conformity-vocabulary/parse-conformity-scheme.test.js.map +1 -0
  69. package/build/conformity-vocabulary/parsers/v0-7-0.parser.d.ts +19 -0
  70. package/build/conformity-vocabulary/parsers/v0-7-0.parser.js +219 -0
  71. package/build/conformity-vocabulary/parsers/v0-7-0.parser.js.map +1 -0
  72. package/build/conformity-vocabulary/types.d.ts +180 -0
  73. package/build/conformity-vocabulary/types.js +2 -0
  74. package/build/conformity-vocabulary/types.js.map +1 -0
  75. package/build/conformity-vocabulary/validate-conformity-claim.d.ts +26 -0
  76. package/build/conformity-vocabulary/validate-conformity-claim.js +165 -0
  77. package/build/conformity-vocabulary/validate-conformity-claim.js.map +1 -0
  78. package/build/conformity-vocabulary/validate-conformity-claim.test.d.ts +1 -0
  79. package/build/conformity-vocabulary/validate-conformity-claim.test.js +478 -0
  80. package/build/conformity-vocabulary/validate-conformity-claim.test.js.map +1 -0
  81. package/build/http-headers/index.d.ts +5 -0
  82. package/build/http-headers/index.js +6 -0
  83. package/build/http-headers/index.js.map +1 -0
  84. package/build/http-headers/is-safe-header-value.d.ts +10 -0
  85. package/build/http-headers/is-safe-header-value.js +15 -0
  86. package/build/http-headers/is-safe-header-value.js.map +1 -0
  87. package/build/http-headers/is-safe-header-value.test.d.ts +1 -0
  88. package/build/http-headers/is-safe-header-value.test.js +37 -0
  89. package/build/http-headers/is-safe-header-value.test.js.map +1 -0
  90. package/build/http-headers/parse-entity-tag.d.ts +9 -0
  91. package/build/http-headers/parse-entity-tag.js +18 -0
  92. package/build/http-headers/parse-entity-tag.js.map +1 -0
  93. package/build/http-headers/parse-entity-tag.test.d.ts +1 -0
  94. package/build/http-headers/parse-entity-tag.test.js +41 -0
  95. package/build/http-headers/parse-entity-tag.test.js.map +1 -0
  96. package/build/http-headers/parse-imf-date.d.ts +13 -0
  97. package/build/http-headers/parse-imf-date.js +23 -0
  98. package/build/http-headers/parse-imf-date.js.map +1 -0
  99. package/build/http-headers/parse-imf-date.test.d.ts +1 -0
  100. package/build/http-headers/parse-imf-date.test.js +31 -0
  101. package/build/http-headers/parse-imf-date.test.js.map +1 -0
  102. package/build/http-headers/parse-media-type.d.ts +8 -0
  103. package/build/http-headers/parse-media-type.js +21 -0
  104. package/build/http-headers/parse-media-type.js.map +1 -0
  105. package/build/http-headers/parse-media-type.test.d.ts +1 -0
  106. package/build/http-headers/parse-media-type.test.js +39 -0
  107. package/build/http-headers/parse-media-type.test.js.map +1 -0
  108. package/build/http-headers/user-agent.d.ts +35 -0
  109. package/build/http-headers/user-agent.js +45 -0
  110. package/build/http-headers/user-agent.js.map +1 -0
  111. package/build/http-headers/user-agent.test.d.ts +1 -0
  112. package/build/http-headers/user-agent.test.js +27 -0
  113. package/build/http-headers/user-agent.test.js.map +1 -0
  114. package/build/index.d.ts +1 -1
  115. package/build/index.js +1 -1
  116. package/build/index.js.map +1 -1
  117. package/build/loaders/errors.d.ts +21 -0
  118. package/build/loaders/errors.js +45 -0
  119. package/build/loaders/errors.js.map +1 -0
  120. package/build/loaders/index.d.ts +3 -0
  121. package/build/loaders/index.js +4 -0
  122. package/build/loaders/index.js.map +1 -0
  123. package/build/loaders/jsonld-document-loader.d.ts +46 -0
  124. package/build/loaders/jsonld-document-loader.js +34 -0
  125. package/build/loaders/jsonld-document-loader.js.map +1 -0
  126. package/build/loaders/jsonld-document-loader.test.d.ts +1 -0
  127. package/build/loaders/jsonld-document-loader.test.js +71 -0
  128. package/build/loaders/jsonld-document-loader.test.js.map +1 -0
  129. package/build/loaders/schema-loader.d.ts +21 -0
  130. package/build/loaders/schema-loader.js +67 -0
  131. package/build/loaders/schema-loader.js.map +1 -0
  132. package/build/loaders/schema-loader.ssrf.test.d.ts +1 -0
  133. package/build/loaders/schema-loader.ssrf.test.js +55 -0
  134. package/build/loaders/schema-loader.ssrf.test.js.map +1 -0
  135. package/build/loaders/schema-loader.test.d.ts +1 -0
  136. package/build/loaders/schema-loader.test.js +162 -0
  137. package/build/loaders/schema-loader.test.js.map +1 -0
  138. package/build/multibase-digest/multibase-digest.d.ts +23 -0
  139. package/build/multibase-digest/multibase-digest.js +40 -0
  140. package/build/multibase-digest/multibase-digest.js.map +1 -1
  141. package/build/multibase-digest/multibase-digest.test.js +46 -0
  142. package/build/multibase-digest/multibase-digest.test.js.map +1 -1
  143. package/build/node/errors.d.ts +41 -0
  144. package/build/node/errors.js +80 -0
  145. package/build/node/errors.js.map +1 -0
  146. package/build/node/index.d.ts +3 -0
  147. package/build/node/index.js +4 -0
  148. package/build/node/index.js.map +1 -0
  149. package/build/node/is-private-ip.d.ts +41 -0
  150. package/build/node/is-private-ip.js +138 -0
  151. package/build/node/is-private-ip.js.map +1 -0
  152. package/build/node/is-private-ip.test.d.ts +1 -0
  153. package/build/node/is-private-ip.test.js +96 -0
  154. package/build/node/is-private-ip.test.js.map +1 -0
  155. package/build/node/validate-public-url.d.ts +49 -0
  156. package/build/node/validate-public-url.js +94 -0
  157. package/build/node/validate-public-url.js.map +1 -0
  158. package/build/node/validate-public-url.test.d.ts +1 -0
  159. package/build/node/validate-public-url.test.js +177 -0
  160. package/build/node/validate-public-url.test.js.map +1 -0
  161. package/build/resolvers/errors.d.ts +43 -0
  162. package/build/resolvers/errors.js +99 -0
  163. package/build/resolvers/errors.js.map +1 -0
  164. package/build/resolvers/index.d.ts +5 -0
  165. package/build/resolvers/index.js +6 -0
  166. package/build/resolvers/index.js.map +1 -0
  167. package/build/resolvers/resolve-document-if-changed.d.ts +48 -0
  168. package/build/resolvers/resolve-document-if-changed.js +42 -0
  169. package/build/resolvers/resolve-document-if-changed.js.map +1 -0
  170. package/build/resolvers/resolve-document-if-changed.test.d.ts +1 -0
  171. package/build/resolvers/resolve-document-if-changed.test.js +109 -0
  172. package/build/resolvers/resolve-document-if-changed.test.js.map +1 -0
  173. package/build/resolvers/resolve-document.d.ts +86 -0
  174. package/build/resolvers/resolve-document.js +239 -0
  175. package/build/resolvers/resolve-document.js.map +1 -0
  176. package/build/resolvers/resolve-document.test.d.ts +1 -0
  177. package/build/resolvers/resolve-document.test.js +299 -0
  178. package/build/resolvers/resolve-document.test.js.map +1 -0
  179. package/build/resolvers/resolve-json-document.d.ts +40 -0
  180. package/build/resolvers/resolve-json-document.js +42 -0
  181. package/build/resolvers/resolve-json-document.js.map +1 -0
  182. package/build/resolvers/resolve-json-document.test.d.ts +1 -0
  183. package/build/resolvers/resolve-json-document.test.js +61 -0
  184. package/build/resolvers/resolve-json-document.test.js.map +1 -0
  185. package/build/structured-error.d.ts +49 -0
  186. package/build/structured-error.js +17 -0
  187. package/build/structured-error.js.map +1 -0
  188. package/build/structured-error.test.d.ts +1 -0
  189. package/build/structured-error.test.js +112 -0
  190. package/build/structured-error.test.js.map +1 -0
  191. package/build/validation/describe-jsonld-failure.d.ts +50 -0
  192. package/build/validation/describe-jsonld-failure.js +104 -0
  193. package/build/validation/describe-jsonld-failure.js.map +1 -0
  194. package/build/validation/describe-jsonld-failure.test.d.ts +1 -0
  195. package/build/validation/describe-jsonld-failure.test.js +141 -0
  196. package/build/validation/describe-jsonld-failure.test.js.map +1 -0
  197. package/build/validation/errors.d.ts +40 -0
  198. package/build/validation/errors.js +75 -0
  199. package/build/validation/errors.js.map +1 -0
  200. package/build/validation/index.d.ts +4 -0
  201. package/build/validation/index.js +5 -0
  202. package/build/validation/index.js.map +1 -0
  203. package/build/validation/validate-against-schemas.d.ts +22 -0
  204. package/build/validation/validate-against-schemas.js +65 -0
  205. package/build/validation/validate-against-schemas.js.map +1 -0
  206. package/build/validation/validate-against-schemas.test.d.ts +1 -0
  207. package/build/validation/validate-against-schemas.test.js +169 -0
  208. package/build/validation/validate-against-schemas.test.js.map +1 -0
  209. package/build/validation/validate-jsonld.context-cache.test.d.ts +1 -0
  210. package/build/validation/validate-jsonld.context-cache.test.js +32 -0
  211. package/build/validation/validate-jsonld.context-cache.test.js.map +1 -0
  212. package/build/validation/validate-jsonld.d.ts +33 -0
  213. package/build/validation/validate-jsonld.js +99 -0
  214. package/build/validation/validate-jsonld.js.map +1 -0
  215. package/build/validation/validate-jsonld.ssrf.test.d.ts +1 -0
  216. package/build/validation/validate-jsonld.ssrf.test.js +108 -0
  217. package/build/validation/validate-jsonld.ssrf.test.js.map +1 -0
  218. package/build/validation/validate-jsonld.test.d.ts +1 -0
  219. package/build/validation/validate-jsonld.test.js +200 -0
  220. package/build/validation/validate-jsonld.test.js.map +1 -0
  221. package/package.json +77 -11
@@ -0,0 +1,478 @@
1
+ import { ConformityWarningCode } from './codes.js';
2
+ import { validateConformityClaim } from './validate-conformity-claim.js';
3
+ const SCHEME_URI = 'https://example.com/scheme';
4
+ const PROFILE_URI = 'https://example.com/scheme/full/1.0.0';
5
+ const CRITERION_A = 'https://example.com/criterion/a/1.0.0';
6
+ const CRITERION_B = 'https://example.com/criterion/b/1.0.0';
7
+ const TOPIC_A1 = 'https://vocabulary.example.com/conformity-topics/a-1';
8
+ const TOPIC_A2 = 'https://vocabulary.example.com/conformity-topics/a-2';
9
+ function scheme() {
10
+ return {
11
+ canonicalId: SCHEME_URI,
12
+ sourceUrl: SCHEME_URI,
13
+ specVersion: '0.7.0',
14
+ name: 'Test Scheme',
15
+ profiles: [
16
+ {
17
+ canonicalId: PROFILE_URI,
18
+ name: 'Full',
19
+ version: '1.0.0',
20
+ status: 'active',
21
+ criteria: [
22
+ {
23
+ canonicalId: CRITERION_A,
24
+ name: 'A',
25
+ version: '1.0.0',
26
+ status: 'active',
27
+ topics: [{ canonicalId: TOPIC_A1 }, { canonicalId: TOPIC_A2 }],
28
+ tags: [],
29
+ },
30
+ {
31
+ canonicalId: CRITERION_B,
32
+ name: 'B',
33
+ version: '1.0.0',
34
+ status: 'active',
35
+ topics: [],
36
+ tags: [],
37
+ },
38
+ ],
39
+ },
40
+ ],
41
+ };
42
+ }
43
+ describe('validateConformityClaim', () => {
44
+ it('returns an empty array when the claim matches the scheme exactly', () => {
45
+ const claim = {
46
+ scheme: SCHEME_URI,
47
+ profile: PROFILE_URI,
48
+ criteria: [
49
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] },
50
+ { criterion: CRITERION_B, conformityTopics: [] },
51
+ ],
52
+ };
53
+ expect(validateConformityClaim(claim, scheme())).toEqual([]);
54
+ });
55
+ describe('assessment-topic-mismatch', () => {
56
+ it("accepts an assessment declaring a subset of its criteria's published topics", () => {
57
+ const claim = {
58
+ scheme: SCHEME_URI,
59
+ profile: PROFILE_URI,
60
+ criteria: [
61
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] },
62
+ { criterion: CRITERION_B, conformityTopics: [] },
63
+ ],
64
+ assessments: [{ criteria: [CRITERION_A, CRITERION_B], conformityTopics: [TOPIC_A1] }],
65
+ };
66
+ expect(validateConformityClaim(claim, scheme())).toEqual([]);
67
+ });
68
+ it('warns when an assessment declares a topic none of its criteria define', () => {
69
+ const claim = {
70
+ scheme: SCHEME_URI,
71
+ profile: PROFILE_URI,
72
+ criteria: [
73
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] },
74
+ { criterion: CRITERION_B, conformityTopics: [] },
75
+ ],
76
+ assessments: [
77
+ { criteria: [CRITERION_A], conformityTopics: ['https://vocabulary.example.com/conformity-topics/other'] },
78
+ ],
79
+ };
80
+ const warnings = validateConformityClaim(claim, scheme());
81
+ expect(warnings).toHaveLength(1);
82
+ expect(warnings[0].code).toBe(ConformityWarningCode.AssessmentTopicMismatch);
83
+ expect(warnings[0].message).toContain(PROFILE_URI);
84
+ expect(warnings[0].received).toBe('https://vocabulary.example.com/conformity-topics/other');
85
+ expect(warnings[0].expected).toEqual([TOPIC_A1, TOPIC_A2]);
86
+ expect(warnings[0].pointer).toBe('/assessments/0/conformityTopics/0');
87
+ });
88
+ it("builds the union from the profile's published topics, not the claim's declared ones", () => {
89
+ // Criterion A's claim entry declares a topic the profile does not
90
+ // publish for it; the assessment declares that same unpublished topic.
91
+ // The assessment check must warn (the union comes from the profile),
92
+ // even though the claim's own criterion declarations contain the topic.
93
+ const unpublished = 'https://vocabulary.example.com/conformity-topics/unpublished';
94
+ const claim = {
95
+ scheme: SCHEME_URI,
96
+ profile: PROFILE_URI,
97
+ criteria: [
98
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2, unpublished] },
99
+ { criterion: CRITERION_B, conformityTopics: [] },
100
+ ],
101
+ assessments: [{ criteria: [CRITERION_A], conformityTopics: [unpublished] }],
102
+ };
103
+ const assessmentWarnings = validateConformityClaim(claim, scheme()).filter((w) => w.code === ConformityWarningCode.AssessmentTopicMismatch);
104
+ expect(assessmentWarnings).toHaveLength(1);
105
+ expect(assessmentWarnings[0].received).toBe(unpublished);
106
+ expect(assessmentWarnings[0].expected).toEqual([TOPIC_A1, TOPIC_A2]);
107
+ });
108
+ it('fires for every mismatching assessment with per-assessment pointers (no short-circuit)', () => {
109
+ const claim = {
110
+ scheme: SCHEME_URI,
111
+ profile: PROFILE_URI,
112
+ criteria: [
113
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] },
114
+ { criterion: CRITERION_B, conformityTopics: [] },
115
+ ],
116
+ assessments: [
117
+ { criteria: [CRITERION_A], conformityTopics: ['https://vocabulary.example.com/conformity-topics/x'] },
118
+ { criteria: [CRITERION_B], conformityTopics: ['https://vocabulary.example.com/conformity-topics/y'] },
119
+ ],
120
+ };
121
+ const warnings = validateConformityClaim(claim, scheme()).filter((w) => w.code === ConformityWarningCode.AssessmentTopicMismatch);
122
+ expect(warnings).toHaveLength(2);
123
+ expect(warnings[0].pointer).toBe('/assessments/0/conformityTopics/0');
124
+ expect(warnings[1].pointer).toBe('/assessments/1/conformityTopics/0');
125
+ });
126
+ it('passes over an assessment that references no criteria (parent-level topic is its only classification)', () => {
127
+ const claim = {
128
+ scheme: SCHEME_URI,
129
+ profile: PROFILE_URI,
130
+ criteria: [
131
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] },
132
+ { criterion: CRITERION_B, conformityTopics: [] },
133
+ ],
134
+ assessments: [{ criteria: [], conformityTopics: ['https://vocabulary.example.com/conformity-topics/other'] }],
135
+ };
136
+ expect(validateConformityClaim(claim, scheme())).toEqual([]);
137
+ });
138
+ it('passes over an assessment when any referenced criterion does not resolve in the profile', () => {
139
+ const claim = {
140
+ scheme: SCHEME_URI,
141
+ profile: PROFILE_URI,
142
+ criteria: [
143
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] },
144
+ { criterion: CRITERION_B, conformityTopics: [] },
145
+ { criterion: 'https://example.com/criterion/unknown/1.0.0' },
146
+ ],
147
+ assessments: [
148
+ {
149
+ criteria: [CRITERION_A, 'https://example.com/criterion/unknown/1.0.0'],
150
+ conformityTopics: ['https://vocabulary.example.com/conformity-topics/other'],
151
+ },
152
+ ],
153
+ };
154
+ const warnings = validateConformityClaim(claim, scheme());
155
+ const codes = warnings.map((w) => w.code);
156
+ expect(codes).toContain(ConformityWarningCode.CriterionNotInProfile);
157
+ expect(codes).not.toContain(ConformityWarningCode.AssessmentTopicMismatch);
158
+ });
159
+ it('deduplicates a topic two referenced criteria both publish in the reported union', () => {
160
+ // Two criteria publishing an overlapping topic must contribute it once to
161
+ // the `expected` union, so the diagnostic is not polluted with duplicates.
162
+ const SHARED = 'https://vocabulary.example.com/conformity-topics/shared';
163
+ const ONLY_A = 'https://vocabulary.example.com/conformity-topics/only-a';
164
+ const ONLY_C = 'https://vocabulary.example.com/conformity-topics/only-c';
165
+ const CRITERION_C = 'https://example.com/criterion/c/1.0.0';
166
+ const dedupScheme = {
167
+ canonicalId: SCHEME_URI,
168
+ sourceUrl: SCHEME_URI,
169
+ specVersion: '0.7.0',
170
+ name: 'Test Scheme',
171
+ profiles: [
172
+ {
173
+ canonicalId: PROFILE_URI,
174
+ name: 'Full',
175
+ version: '1.0.0',
176
+ status: 'active',
177
+ criteria: [
178
+ {
179
+ canonicalId: CRITERION_A,
180
+ name: 'A',
181
+ version: '1.0.0',
182
+ status: 'active',
183
+ topics: [{ canonicalId: SHARED }, { canonicalId: ONLY_A }],
184
+ tags: [],
185
+ },
186
+ {
187
+ canonicalId: CRITERION_C,
188
+ name: 'C',
189
+ version: '1.0.0',
190
+ status: 'active',
191
+ topics: [{ canonicalId: SHARED }, { canonicalId: ONLY_C }],
192
+ tags: [],
193
+ },
194
+ ],
195
+ },
196
+ ],
197
+ };
198
+ const claim = {
199
+ scheme: SCHEME_URI,
200
+ profile: PROFILE_URI,
201
+ criteria: [
202
+ { criterion: CRITERION_A, conformityTopics: [SHARED, ONLY_A] },
203
+ { criterion: CRITERION_C, conformityTopics: [SHARED, ONLY_C] },
204
+ ],
205
+ assessments: [
206
+ {
207
+ criteria: [CRITERION_A, CRITERION_C],
208
+ conformityTopics: ['https://vocabulary.example.com/conformity-topics/outside'],
209
+ },
210
+ ],
211
+ };
212
+ const warnings = validateConformityClaim(claim, dedupScheme).filter((w) => w.code === ConformityWarningCode.AssessmentTopicMismatch);
213
+ expect(warnings).toHaveLength(1);
214
+ expect(warnings[0].expected).toEqual([SHARED, ONLY_A, ONLY_C]);
215
+ });
216
+ });
217
+ describe('profile absent on the claim', () => {
218
+ it('reports profile-not-specified and skips criterion checks when the scheme is known', () => {
219
+ const claim = {
220
+ scheme: SCHEME_URI,
221
+ criteria: [{ criterion: 'https://example.com/criterion/unknown/1.0.0', conformityTopics: [] }],
222
+ };
223
+ const warnings = validateConformityClaim(claim, scheme());
224
+ expect(warnings).toHaveLength(1);
225
+ expect(warnings[0].code).toBe(ConformityWarningCode.ProfileNotSpecified);
226
+ expect(warnings[0].pointer).toBe('/profile');
227
+ });
228
+ it('still reports scheme-not-found when the scheme is unknown', () => {
229
+ const claim = { scheme: 'https://example.com/other-scheme', criteria: [] };
230
+ const warnings = validateConformityClaim(claim, scheme());
231
+ expect(warnings).toHaveLength(1);
232
+ expect(warnings[0].code).toBe(ConformityWarningCode.SchemeNotFound);
233
+ });
234
+ });
235
+ describe('scheme-not-found', () => {
236
+ it('fires when the scheme is null', () => {
237
+ const claim = { scheme: SCHEME_URI, profile: PROFILE_URI, criteria: [] };
238
+ expect(validateConformityClaim(claim, null)).toEqual([
239
+ expect.objectContaining({
240
+ code: ConformityWarningCode.SchemeNotFound,
241
+ received: SCHEME_URI,
242
+ pointer: '/scheme',
243
+ }),
244
+ ]);
245
+ });
246
+ it("fires when the scheme's canonicalId doesn't match the claim", () => {
247
+ const wrongScheme = { ...scheme(), canonicalId: 'https://other.example/scheme' };
248
+ const claim = {
249
+ scheme: SCHEME_URI,
250
+ profile: PROFILE_URI,
251
+ criteria: [{ criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] }],
252
+ };
253
+ const warnings = validateConformityClaim(claim, wrongScheme);
254
+ expect(warnings.map((w) => w.code)).toEqual([ConformityWarningCode.SchemeNotFound]);
255
+ });
256
+ it('short-circuits: no profile or criterion checks run', () => {
257
+ const claim = {
258
+ scheme: SCHEME_URI,
259
+ profile: 'https://example.com/scheme/does-not-exist/9.9.9',
260
+ criteria: [{ criterion: 'https://example.com/criterion/does-not-exist/9.9.9', conformityTopics: [] }],
261
+ };
262
+ const warnings = validateConformityClaim(claim, null);
263
+ expect(warnings).toHaveLength(1);
264
+ expect(warnings[0].code).toBe(ConformityWarningCode.SchemeNotFound);
265
+ });
266
+ });
267
+ describe('profile-not-found', () => {
268
+ it("fires when the claim's profile URI is not among the scheme's profiles", () => {
269
+ const claim = {
270
+ scheme: SCHEME_URI,
271
+ profile: 'https://example.com/scheme/other/2.0.0',
272
+ criteria: [{ criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] }],
273
+ };
274
+ expect(validateConformityClaim(claim, scheme())).toEqual([
275
+ expect.objectContaining({
276
+ code: ConformityWarningCode.ProfileNotFound,
277
+ received: 'https://example.com/scheme/other/2.0.0',
278
+ expected: [PROFILE_URI],
279
+ pointer: '/profile',
280
+ }),
281
+ ]);
282
+ });
283
+ it('short-circuits: criterion checks do not run', () => {
284
+ const claim = {
285
+ scheme: SCHEME_URI,
286
+ profile: 'https://example.com/scheme/other/2.0.0',
287
+ criteria: [
288
+ { criterion: 'https://example.com/criterion/unknown/1.0.0', conformityTopics: [] },
289
+ { criterion: CRITERION_A, conformityTopics: ['https://vocabulary.example.com/topics/unknown'] },
290
+ ],
291
+ };
292
+ expect(validateConformityClaim(claim, scheme())).toHaveLength(1);
293
+ });
294
+ });
295
+ describe('criterion-not-in-profile', () => {
296
+ it("fires when a claim's criterion URI is not in the profile's published criteria", () => {
297
+ const claim = {
298
+ scheme: SCHEME_URI,
299
+ profile: PROFILE_URI,
300
+ criteria: [
301
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] },
302
+ { criterion: CRITERION_B, conformityTopics: [] },
303
+ { criterion: 'https://example.com/criterion/unknown/1.0.0', conformityTopics: [] },
304
+ ],
305
+ };
306
+ expect(validateConformityClaim(claim, scheme())).toEqual([
307
+ expect.objectContaining({
308
+ code: ConformityWarningCode.CriterionNotInProfile,
309
+ message: expect.stringContaining(PROFILE_URI),
310
+ received: 'https://example.com/criterion/unknown/1.0.0',
311
+ expected: [CRITERION_A, CRITERION_B],
312
+ pointer: '/criteria/2/criterion',
313
+ }),
314
+ ]);
315
+ });
316
+ it('does not run topic checks for a criterion that is not in the profile', () => {
317
+ const claim = {
318
+ scheme: SCHEME_URI,
319
+ profile: PROFILE_URI,
320
+ criteria: [
321
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] },
322
+ { criterion: CRITERION_B, conformityTopics: [] },
323
+ {
324
+ criterion: 'https://example.com/criterion/unknown/1.0.0',
325
+ conformityTopics: ['https://vocabulary.example.com/topics/unknown'],
326
+ },
327
+ ],
328
+ };
329
+ const codes = validateConformityClaim(claim, scheme()).map((w) => w.code);
330
+ expect(codes).toContain(ConformityWarningCode.CriterionNotInProfile);
331
+ expect(codes).not.toContain(ConformityWarningCode.CriterionTopicMismatch);
332
+ });
333
+ });
334
+ describe('criterion-missing', () => {
335
+ it('fires when the profile publishes a criterion the claim does not address', () => {
336
+ const claim = {
337
+ scheme: SCHEME_URI,
338
+ profile: PROFILE_URI,
339
+ criteria: [{ criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] }],
340
+ };
341
+ expect(validateConformityClaim(claim, scheme())).toEqual([
342
+ expect.objectContaining({
343
+ code: ConformityWarningCode.CriterionMissing,
344
+ message: expect.stringContaining(PROFILE_URI),
345
+ expected: CRITERION_B,
346
+ pointer: '/criteria',
347
+ }),
348
+ ]);
349
+ });
350
+ it('fires for every missing criterion (no short-circuit)', () => {
351
+ const claim = { scheme: SCHEME_URI, profile: PROFILE_URI, criteria: [] };
352
+ const missingExpected = validateConformityClaim(claim, scheme())
353
+ .filter((w) => w.code === ConformityWarningCode.CriterionMissing)
354
+ .map((w) => w.expected);
355
+ expect(missingExpected).toEqual([CRITERION_A, CRITERION_B]);
356
+ });
357
+ });
358
+ describe('criterion-topic-mismatch', () => {
359
+ it('fires when the claim omits a topic the profile publishes for the criterion', () => {
360
+ const claim = {
361
+ scheme: SCHEME_URI,
362
+ profile: PROFILE_URI,
363
+ criteria: [
364
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1] },
365
+ { criterion: CRITERION_B, conformityTopics: [] },
366
+ ],
367
+ };
368
+ expect(validateConformityClaim(claim, scheme())).toEqual([
369
+ expect.objectContaining({
370
+ code: ConformityWarningCode.CriterionTopicMismatch,
371
+ message: expect.stringContaining(PROFILE_URI),
372
+ expected: TOPIC_A2,
373
+ pointer: '/criteria/0/conformityTopics',
374
+ }),
375
+ ]);
376
+ });
377
+ it('fires for every published topic when the claim declares none', () => {
378
+ const claim = {
379
+ scheme: SCHEME_URI,
380
+ profile: PROFILE_URI,
381
+ criteria: [
382
+ { criterion: CRITERION_A, conformityTopics: [] },
383
+ { criterion: CRITERION_B, conformityTopics: [] },
384
+ ],
385
+ };
386
+ const mismatchExpected = validateConformityClaim(claim, scheme())
387
+ .filter((w) => w.code === ConformityWarningCode.CriterionTopicMismatch)
388
+ .map((w) => w.expected);
389
+ expect(mismatchExpected).toEqual([TOPIC_A1, TOPIC_A2]);
390
+ });
391
+ it('fires when the claim declares a topic the profile does not publish', () => {
392
+ const claim = {
393
+ scheme: SCHEME_URI,
394
+ profile: PROFILE_URI,
395
+ criteria: [
396
+ {
397
+ criterion: CRITERION_A,
398
+ conformityTopics: [TOPIC_A1, TOPIC_A2, 'https://vocabulary.example.com/topics/wrong'],
399
+ },
400
+ { criterion: CRITERION_B, conformityTopics: [] },
401
+ ],
402
+ };
403
+ expect(validateConformityClaim(claim, scheme())).toEqual([
404
+ expect.objectContaining({
405
+ code: ConformityWarningCode.CriterionTopicMismatch,
406
+ message: expect.stringContaining(PROFILE_URI),
407
+ received: 'https://vocabulary.example.com/topics/wrong',
408
+ expected: [TOPIC_A1, TOPIC_A2],
409
+ pointer: '/criteria/0/conformityTopics/2',
410
+ }),
411
+ ]);
412
+ });
413
+ it('does not fire when the declared topics exactly match the published topics', () => {
414
+ const claim = {
415
+ scheme: SCHEME_URI,
416
+ profile: PROFILE_URI,
417
+ criteria: [
418
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1, TOPIC_A2] },
419
+ { criterion: CRITERION_B, conformityTopics: [] },
420
+ ],
421
+ };
422
+ expect(validateConformityClaim(claim, scheme())).toEqual([]);
423
+ });
424
+ it('matches the topic set regardless of order', () => {
425
+ const claim = {
426
+ scheme: SCHEME_URI,
427
+ profile: PROFILE_URI,
428
+ criteria: [
429
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A2, TOPIC_A1] },
430
+ { criterion: CRITERION_B, conformityTopics: [] },
431
+ ],
432
+ };
433
+ expect(validateConformityClaim(claim, scheme())).toEqual([]);
434
+ });
435
+ it('flags both an omitted and an unexpected topic on the same criterion', () => {
436
+ const claim = {
437
+ scheme: SCHEME_URI,
438
+ profile: PROFILE_URI,
439
+ criteria: [
440
+ { criterion: CRITERION_A, conformityTopics: [TOPIC_A1, 'https://vocabulary.example.com/topics/wrong'] },
441
+ { criterion: CRITERION_B, conformityTopics: [] },
442
+ ],
443
+ };
444
+ expect(validateConformityClaim(claim, scheme())).toEqual([
445
+ expect.objectContaining({
446
+ code: ConformityWarningCode.CriterionTopicMismatch,
447
+ expected: TOPIC_A2,
448
+ pointer: '/criteria/0/conformityTopics',
449
+ }),
450
+ expect.objectContaining({
451
+ code: ConformityWarningCode.CriterionTopicMismatch,
452
+ received: 'https://vocabulary.example.com/topics/wrong',
453
+ pointer: '/criteria/0/conformityTopics/1',
454
+ }),
455
+ ]);
456
+ });
457
+ it('does not run when the claim carries no topic list (version that does not model topics)', () => {
458
+ const claim = {
459
+ scheme: SCHEME_URI,
460
+ profile: PROFILE_URI,
461
+ criteria: [{ criterion: CRITERION_A }, { criterion: CRITERION_B }],
462
+ };
463
+ expect(validateConformityClaim(claim, scheme())).toEqual([]);
464
+ });
465
+ it('treats a runtime null topic list like an absent one and runs no topic check', () => {
466
+ const claim = {
467
+ scheme: SCHEME_URI,
468
+ profile: PROFILE_URI,
469
+ criteria: [
470
+ { criterion: CRITERION_A, conformityTopics: null },
471
+ { criterion: CRITERION_B },
472
+ ],
473
+ };
474
+ expect(validateConformityClaim(claim, scheme())).toEqual([]);
475
+ });
476
+ });
477
+ });
478
+ //# sourceMappingURL=validate-conformity-claim.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-conformity-claim.test.js","sourceRoot":"","sources":["../../src/conformity-vocabulary/validate-conformity-claim.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { isSafeHeaderValue } from './is-safe-header-value.js';
2
+ export { parseEntityTag } from './parse-entity-tag.js';
3
+ export { parseImfDate } from './parse-imf-date.js';
4
+ export { parseMediaType } from './parse-media-type.js';
5
+ export { DEFAULT_USER_AGENT, USER_AGENT_ENV_VAR, isValidHttpUserAgent } from './user-agent.js';
@@ -0,0 +1,6 @@
1
+ export { isSafeHeaderValue } from './is-safe-header-value.js';
2
+ export { parseEntityTag } from './parse-entity-tag.js';
3
+ export { parseImfDate } from './parse-imf-date.js';
4
+ export { parseMediaType } from './parse-media-type.js';
5
+ export { DEFAULT_USER_AGENT, USER_AGENT_ENV_VAR, isValidHttpUserAgent } from './user-agent.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/http-headers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Returns `true` if `value` is free of control characters (CR, LF, NUL,
3
+ * etc.). Use this as the load-bearing security check when echoing any
4
+ * upstream-supplied header value back onto another request: a CR/LF in
5
+ * an attacker-controlled header value enables header-injection attacks.
6
+ *
7
+ * Per-header RFC validators (e.g. {@link parseEntityTag}) call this
8
+ * internally as a fast-fail before their format-specific regex.
9
+ */
10
+ export declare function isSafeHeaderValue(value: string): boolean;
@@ -0,0 +1,15 @@
1
+ // eslint-disable-next-line no-control-regex -- intentional: rejecting control chars is the whole point
2
+ const CONTROL_CHARS_RE = /[\x00-\x1f\x7f]/;
3
+ /**
4
+ * Returns `true` if `value` is free of control characters (CR, LF, NUL,
5
+ * etc.). Use this as the load-bearing security check when echoing any
6
+ * upstream-supplied header value back onto another request: a CR/LF in
7
+ * an attacker-controlled header value enables header-injection attacks.
8
+ *
9
+ * Per-header RFC validators (e.g. {@link parseEntityTag}) call this
10
+ * internally as a fast-fail before their format-specific regex.
11
+ */
12
+ export function isSafeHeaderValue(value) {
13
+ return typeof value === 'string' && !CONTROL_CHARS_RE.test(value);
14
+ }
15
+ //# sourceMappingURL=is-safe-header-value.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-safe-header-value.js","sourceRoot":"","sources":["../../src/http-headers/is-safe-header-value.ts"],"names":[],"mappings":"AAAA,uGAAuG;AACvG,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAE3C;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACpE,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ import { isSafeHeaderValue } from './is-safe-header-value.js';
2
+ describe('isSafeHeaderValue', () => {
3
+ it('accepts printable ASCII', () => {
4
+ expect(isSafeHeaderValue('foo')).toBe(true);
5
+ expect(isSafeHeaderValue('"abc"')).toBe(true);
6
+ expect(isSafeHeaderValue('application/json; charset=utf-8')).toBe(true);
7
+ });
8
+ it('accepts the empty string', () => {
9
+ expect(isSafeHeaderValue('')).toBe(true);
10
+ });
11
+ it('rejects carriage return', () => {
12
+ expect(isSafeHeaderValue('foo\rbar')).toBe(false);
13
+ });
14
+ it('rejects line feed', () => {
15
+ expect(isSafeHeaderValue('foo\nbar')).toBe(false);
16
+ });
17
+ it('rejects CRLF (the canonical header-injection vector)', () => {
18
+ expect(isSafeHeaderValue('"abc"\r\nX-Evil: pwn')).toBe(false);
19
+ });
20
+ it('rejects NUL bytes', () => {
21
+ expect(isSafeHeaderValue('foo\x00bar')).toBe(false);
22
+ });
23
+ it('rejects every control character (0x00 to 0x1f)', () => {
24
+ for (let i = 0; i < 0x20; i += 1) {
25
+ expect(isSafeHeaderValue(`a${String.fromCharCode(i)}b`)).toBe(false);
26
+ }
27
+ });
28
+ it('rejects DEL (0x7f)', () => {
29
+ expect(isSafeHeaderValue('a\x7fb')).toBe(false);
30
+ });
31
+ it('rejects non-string input', () => {
32
+ expect(isSafeHeaderValue(null)).toBe(false);
33
+ expect(isSafeHeaderValue(undefined)).toBe(false);
34
+ expect(isSafeHeaderValue(123)).toBe(false);
35
+ });
36
+ });
37
+ //# sourceMappingURL=is-safe-header-value.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-safe-header-value.test.js","sourceRoot":"","sources":["../../src/http-headers/is-safe-header-value.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,CAAC,iBAAiB,CAAC,iCAAiC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,iBAAiB,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,iBAAiB,CAAC,IAAyB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjE,MAAM,CAAC,iBAAiB,CAAC,SAA8B,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtE,MAAM,CAAC,iBAAiB,CAAC,GAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Validates an HTTP `ETag` header value per RFC 7232 §2.3. Returns the
3
+ * value verbatim if it matches the entity-tag grammar (optional `W/`
4
+ * weak prefix, then a double-quoted opaque-tag of printable ASCII);
5
+ * otherwise returns `undefined`.
6
+ *
7
+ * @see https://www.rfc-editor.org/rfc/rfc7232#section-2.3
8
+ */
9
+ export declare function parseEntityTag(value: string): string | undefined;
@@ -0,0 +1,18 @@
1
+ import { isSafeHeaderValue } from './is-safe-header-value.js';
2
+ // RFC 7232 §2.3: entity-tag = [ weak ] DQUOTE *etagc DQUOTE
3
+ // weak = "W/"; etagc = %x21 / %x23-7E (printable ASCII except DQUOTE).
4
+ const ETAG_RE = /^(?:W\/)?"[!#-~]*"$/;
5
+ /**
6
+ * Validates an HTTP `ETag` header value per RFC 7232 §2.3. Returns the
7
+ * value verbatim if it matches the entity-tag grammar (optional `W/`
8
+ * weak prefix, then a double-quoted opaque-tag of printable ASCII);
9
+ * otherwise returns `undefined`.
10
+ *
11
+ * @see https://www.rfc-editor.org/rfc/rfc7232#section-2.3
12
+ */
13
+ export function parseEntityTag(value) {
14
+ if (!isSafeHeaderValue(value))
15
+ return undefined;
16
+ return ETAG_RE.test(value) ? value : undefined;
17
+ }
18
+ //# sourceMappingURL=parse-entity-tag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-entity-tag.js","sourceRoot":"","sources":["../../src/http-headers/parse-entity-tag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,4DAA4D;AAC5D,uEAAuE;AACvE,MAAM,OAAO,GAAG,qBAAqB,CAAC;AAEtC;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAChD,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACjD,CAAC"}
@@ -0,0 +1 @@
1
+ export {};