Skip to content

Commit 8c72d75

Browse files
committed
feat: add headers to edge functions manifest validation
1 parent ea236c6 commit 8c72d75

File tree

3 files changed

+208
-0
lines changed

3 files changed

+208
-0
lines changed

packages/edge-bundler/node/validation/manifest/__snapshots__/index.test.ts.snap

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,72 @@ REQUIRED must have required property 'format'
5353
6 | ],]
5454
`;
5555
56+
exports[`headers > should throw on additional property in headers 1`] = `
57+
[ManifestValidationError: Validation of Edge Functions manifest failed
58+
ADDTIONAL PROPERTY must NOT have additional properties
59+
60+
33 | "x-custom-header": {
61+
34 | "style": "exists",
62+
> 35 | "foo": "bar"
63+
| ^^^^^ 😲 foo is not expected to be here!
64+
36 | }
65+
37 | }
66+
38 | }]
67+
`;
68+
69+
exports[`headers > should throw on invalid pattern format 1`] = `
70+
[ManifestValidationError: Validation of Edge Functions manifest failed
71+
FORMAT must match format "regexPattern"
72+
73+
33 | "x-custom-header": {
74+
34 | "style": "regex",
75+
> 35 | "pattern": "/^Bearer .+/"
76+
| ^^^^^^^^^^^^^^ 👈🏽 format must match format "regexPattern"
77+
36 | }
78+
37 | }
79+
38 | }]
80+
`;
81+
82+
exports[`headers > should throw on invalid style value 1`] = `
83+
[ManifestValidationError: Validation of Edge Functions manifest failed
84+
ENUM must be equal to one of the allowed values
85+
(exists, missing, regex)
86+
87+
32 | "headers": {
88+
33 | "x-custom-header": {
89+
> 34 | "style": "invalid"
90+
| ^^^^^^^^^ 👈🏽 Unexpected value, should be equal to one of the allowed values
91+
35 | }
92+
36 | }
93+
37 | }]
94+
`;
95+
96+
exports[`headers > should throw on missing style property 1`] = `
97+
[ManifestValidationError: Validation of Edge Functions manifest failed
98+
REQUIRED must have required property 'style'
99+
100+
31 | "bundler_version": "1.6.0",
101+
32 | "headers": {
102+
> 33 | "x-custom-header": {
103+
| ^ ☹️ style is missing here!
104+
34 | "pattern": "^Bearer .+"
105+
35 | }
106+
36 | }]
107+
`;
108+
109+
exports[`headers > should throw when style is regex but pattern is missing 1`] = `
110+
[ManifestValidationError: Validation of Edge Functions manifest failed
111+
REQUIRED must have required property 'pattern'
112+
113+
31 | "bundler_version": "1.6.0",
114+
32 | "headers": {
115+
> 33 | "x-custom-header": {
116+
| ^ ☹️ pattern is missing here!
117+
34 | "style": "regex"
118+
35 | }
119+
36 | }]
120+
`;
121+
56122
exports[`import map URL > should throw on wrong type 1`] = `
57123
[ManifestValidationError: Validation of Edge Functions manifest failed
58124
TYPE must be string

packages/edge-bundler/node/validation/manifest/index.test.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,114 @@ describe('import map URL', () => {
180180
expect(() => validateManifest(manifest)).toThrowErrorMatchingSnapshot()
181181
})
182182
})
183+
184+
describe('headers', () => {
185+
test('should accept valid headers with exists style', () => {
186+
const manifest = getBaseManifest()
187+
manifest.headers = {
188+
'x-custom-header': {
189+
style: 'exists'
190+
}
191+
}
192+
193+
expect(() => validateManifest(manifest)).not.toThrowError()
194+
})
195+
196+
test('should accept valid headers with missing style', () => {
197+
const manifest = getBaseManifest()
198+
manifest.headers = {
199+
'x-custom-header': {
200+
style: 'missing'
201+
}
202+
}
203+
204+
expect(() => validateManifest(manifest)).not.toThrowError()
205+
})
206+
207+
test('should accept valid headers with regex style and pattern', () => {
208+
const manifest = getBaseManifest()
209+
manifest.headers = {
210+
'x-custom-header': {
211+
style: 'regex',
212+
pattern: '^Bearer .+$'
213+
}
214+
}
215+
216+
expect(() => validateManifest(manifest)).not.toThrowError()
217+
})
218+
219+
test('should throw on missing style property', () => {
220+
const manifest = getBaseManifest()
221+
manifest.headers = {
222+
'x-custom-header': {
223+
pattern: '^Bearer .+'
224+
}
225+
}
226+
227+
expect(() => validateManifest(manifest)).toThrowErrorMatchingSnapshot()
228+
})
229+
230+
test('should throw on invalid style value', () => {
231+
const manifest = getBaseManifest()
232+
manifest.headers = {
233+
'x-custom-header': {
234+
style: 'invalid'
235+
}
236+
}
237+
238+
expect(() => validateManifest(manifest)).toThrowErrorMatchingSnapshot()
239+
})
240+
241+
test('should throw when style is regex but pattern is missing', () => {
242+
const manifest = getBaseManifest()
243+
manifest.headers = {
244+
'x-custom-header': {
245+
style: 'regex'
246+
}
247+
}
248+
249+
expect(() => validateManifest(manifest)).toThrowErrorMatchingSnapshot()
250+
})
251+
252+
test('should throw on invalid pattern format', () => {
253+
const manifest = getBaseManifest()
254+
manifest.headers = {
255+
'x-custom-header': {
256+
style: 'regex',
257+
pattern: '/^Bearer .+/'
258+
}
259+
}
260+
261+
expect(() => validateManifest(manifest)).toThrowErrorMatchingSnapshot()
262+
})
263+
264+
test('should throw on additional property in headers', () => {
265+
const manifest = getBaseManifest()
266+
manifest.headers = {
267+
'x-custom-header': {
268+
style: 'exists',
269+
foo: 'bar'
270+
}
271+
}
272+
273+
expect(() => validateManifest(manifest)).toThrowErrorMatchingSnapshot()
274+
})
275+
276+
test('should accept multiple headers with different styles', () => {
277+
const manifest = getBaseManifest()
278+
manifest.headers = {
279+
'x-exists-header': {
280+
style: 'exists'
281+
},
282+
'x-missing-header': {
283+
style: 'missing'
284+
},
285+
'authorization': {
286+
style: 'regex',
287+
pattern: '^Bearer [a-zA-Z0-9]+$'
288+
}
289+
}
290+
291+
expect(() => validateManifest(manifest)).not.toThrowError()
292+
})
293+
})

packages/edge-bundler/node/validation/manifest/schema.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,36 @@ const layersSchema = {
6060
additionalProperties: false,
6161
}
6262

63+
const headersSchema = {
64+
type: 'object',
65+
patternProperties: {
66+
'.*': {
67+
type: 'object',
68+
required: ['style'],
69+
properties: {
70+
pattern: {
71+
type: 'string',
72+
format: 'regexPattern',
73+
},
74+
style: {
75+
type: 'string',
76+
enum: ['exists', 'missing', 'regex'],
77+
},
78+
},
79+
additionalProperties: false,
80+
if: {
81+
properties: {
82+
style: { const: 'regex' },
83+
},
84+
},
85+
then: {
86+
required: ['pattern'],
87+
},
88+
},
89+
},
90+
additionalProperties: false,
91+
}
92+
6393
const edgeManifestSchema = {
6494
type: 'object',
6595
required: ['bundles', 'routes', 'bundler_version'],
@@ -83,6 +113,7 @@ const edgeManifestSchema = {
83113
import_map: { type: 'string' },
84114
bundler_version: { type: 'string' },
85115
function_config: { type: 'object', additionalProperties: functionConfigSchema },
116+
headers: headersSchema,
86117
},
87118
additionalProperties: false,
88119
}

0 commit comments

Comments
 (0)