@netlify/edge-bundler 14.0.7 → 14.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node/config.d.ts +2 -0
- package/dist/node/config.test.js +12 -0
- package/dist/node/declaration.d.ts +10 -1
- package/dist/node/declaration.js +31 -2
- package/dist/node/manifest.d.ts +2 -1
- package/dist/node/manifest.js +4 -1
- package/dist/node/manifest.test.js +83 -1
- package/package.json +2 -2
package/dist/node/config.d.ts
CHANGED
|
@@ -11,8 +11,10 @@ export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'
|
|
|
11
11
|
export type Path = `/${string}`;
|
|
12
12
|
export type OnError = 'fail' | 'bypass' | Path;
|
|
13
13
|
export declare const isValidOnError: (value: unknown) => value is OnError;
|
|
14
|
+
export type HeadersConfig = Record<string, boolean | string>;
|
|
14
15
|
interface BaseFunctionConfig {
|
|
15
16
|
cache?: Cache;
|
|
17
|
+
header?: HeadersConfig;
|
|
16
18
|
onError?: OnError;
|
|
17
19
|
name?: string;
|
|
18
20
|
generator?: string;
|
package/dist/node/config.test.js
CHANGED
|
@@ -263,6 +263,18 @@ test('Loads function paths from the in-source `config` function', async () => {
|
|
|
263
263
|
pattern: '^/user-func1/?$',
|
|
264
264
|
excluded_patterns: [],
|
|
265
265
|
path: '/user-func1',
|
|
266
|
+
headers: {
|
|
267
|
+
'x-must-be-there': {
|
|
268
|
+
style: 'exists',
|
|
269
|
+
},
|
|
270
|
+
'x-must-match': {
|
|
271
|
+
pattern: '^(foo|bar)$',
|
|
272
|
+
style: 'regex',
|
|
273
|
+
},
|
|
274
|
+
'x-must-not-be-there': {
|
|
275
|
+
style: 'missing',
|
|
276
|
+
},
|
|
277
|
+
},
|
|
266
278
|
});
|
|
267
279
|
expect(routes[7]).toEqual({
|
|
268
280
|
function: 'user-func3',
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import { FunctionConfig, HTTPMethod, Path } from './config.js';
|
|
1
|
+
import { FunctionConfig, HeadersConfig, HTTPMethod, Path } from './config.js';
|
|
2
2
|
import { FeatureFlags } from './feature_flags.js';
|
|
3
|
+
export type HeaderMatch = {
|
|
4
|
+
pattern: string;
|
|
5
|
+
style: 'regex';
|
|
6
|
+
} | {
|
|
7
|
+
style: 'exists' | 'missing';
|
|
8
|
+
};
|
|
9
|
+
type HeaderMatchers = Record<string, HeaderMatch>;
|
|
3
10
|
interface BaseDeclaration {
|
|
4
11
|
cache?: string;
|
|
5
12
|
function: string;
|
|
13
|
+
header?: HeadersConfig;
|
|
6
14
|
method?: HTTPMethod | HTTPMethod[];
|
|
7
15
|
name?: string;
|
|
8
16
|
generator?: string;
|
|
@@ -22,4 +30,5 @@ export declare const mergeDeclarations: (tomlDeclarations: Declaration[], userFu
|
|
|
22
30
|
* `$` characters.
|
|
23
31
|
*/
|
|
24
32
|
export declare const normalizePattern: (pattern: string) => string;
|
|
33
|
+
export declare const getHeaderMatchers: (headers?: HeadersConfig) => HeaderMatchers;
|
|
25
34
|
export {};
|
package/dist/node/declaration.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BundleError } from './bundle_error.js';
|
|
1
2
|
export const mergeDeclarations = (tomlDeclarations, userFunctionsConfig, internalFunctionsConfig, deployConfigDeclarations, _featureFlags = {}) => {
|
|
2
3
|
const functionsVisited = new Set();
|
|
3
4
|
const declarations = [
|
|
@@ -58,7 +59,7 @@ const createDeclarationsFromFunctionConfigs = (functionConfigs, functionsVisited
|
|
|
58
59
|
if (!functionsVisited.has(name)) {
|
|
59
60
|
// If we have a pattern specified, create a declaration for each pattern.
|
|
60
61
|
if ('pattern' in functionConfig && functionConfig.pattern) {
|
|
61
|
-
const { pattern, excludedPattern } = functionConfig;
|
|
62
|
+
const { header, pattern, excludedPattern } = functionConfig;
|
|
62
63
|
const patterns = Array.isArray(pattern) ? pattern : [pattern];
|
|
63
64
|
patterns.forEach((singlePattern) => {
|
|
64
65
|
const declaration = { function: name, pattern: singlePattern };
|
|
@@ -71,12 +72,15 @@ const createDeclarationsFromFunctionConfigs = (functionConfigs, functionsVisited
|
|
|
71
72
|
if (excludedPattern) {
|
|
72
73
|
declaration.excludedPattern = excludedPattern;
|
|
73
74
|
}
|
|
75
|
+
if (header) {
|
|
76
|
+
declaration.header = header;
|
|
77
|
+
}
|
|
74
78
|
declarations.push(declaration);
|
|
75
79
|
});
|
|
76
80
|
}
|
|
77
81
|
// If we don't have a pattern but we have a path specified, create a declaration for each path.
|
|
78
82
|
else if ('path' in functionConfig && functionConfig.path) {
|
|
79
|
-
const { path, excludedPath } = functionConfig;
|
|
83
|
+
const { header, path, excludedPath } = functionConfig;
|
|
80
84
|
const paths = Array.isArray(path) ? path : [path];
|
|
81
85
|
paths.forEach((singlePath) => {
|
|
82
86
|
const declaration = { function: name, path: singlePath };
|
|
@@ -89,6 +93,9 @@ const createDeclarationsFromFunctionConfigs = (functionConfigs, functionsVisited
|
|
|
89
93
|
if (excludedPath) {
|
|
90
94
|
declaration.excludedPath = excludedPath;
|
|
91
95
|
}
|
|
96
|
+
if (header) {
|
|
97
|
+
declaration.header = header;
|
|
98
|
+
}
|
|
92
99
|
declarations.push(declaration);
|
|
93
100
|
});
|
|
94
101
|
}
|
|
@@ -112,3 +119,25 @@ export const normalizePattern = (pattern) => {
|
|
|
112
119
|
// Strip leading and forward slashes.
|
|
113
120
|
return regexp.toString().slice(1, -1);
|
|
114
121
|
};
|
|
122
|
+
const headerConfigError = `The 'header' configuration property must be an object where keys are strings and values are either booleans or strings (e.g. { "x-header-present": true, "x-header-absent": false, "x-header-matching": "^prefix" }).`;
|
|
123
|
+
export const getHeaderMatchers = (headers) => {
|
|
124
|
+
const matchers = {};
|
|
125
|
+
if (!headers) {
|
|
126
|
+
return matchers;
|
|
127
|
+
}
|
|
128
|
+
if (Object.getPrototypeOf(headers) !== Object.prototype) {
|
|
129
|
+
throw new BundleError(new Error(headerConfigError));
|
|
130
|
+
}
|
|
131
|
+
for (const header in headers) {
|
|
132
|
+
if (typeof headers[header] === 'boolean') {
|
|
133
|
+
matchers[header] = { style: headers[header] ? 'exists' : 'missing' };
|
|
134
|
+
}
|
|
135
|
+
else if (typeof headers[header] === 'string') {
|
|
136
|
+
matchers[header] = { style: 'regex', pattern: normalizePattern(headers[header]) };
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
throw new BundleError(new Error(headerConfigError));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return matchers;
|
|
143
|
+
};
|
package/dist/node/manifest.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { Bundle } from './bundle.js';
|
|
2
2
|
import { FunctionConfig } from './config.js';
|
|
3
|
-
import { Declaration } from './declaration.js';
|
|
3
|
+
import { Declaration, type HeaderMatch } from './declaration.js';
|
|
4
4
|
import { EdgeFunction } from './edge_function.js';
|
|
5
5
|
import { FeatureFlags } from './feature_flags.js';
|
|
6
6
|
import { Layer } from './layer.js';
|
|
7
7
|
interface Route {
|
|
8
8
|
function: string;
|
|
9
|
+
headers?: Record<string, HeaderMatch>;
|
|
9
10
|
pattern: string;
|
|
10
11
|
excluded_patterns: string[];
|
|
11
12
|
path?: string;
|
package/dist/node/manifest.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { wrapBundleError } from './bundle_error.js';
|
|
4
|
-
import { normalizePattern } from './declaration.js';
|
|
4
|
+
import { getHeaderMatchers, normalizePattern } from './declaration.js';
|
|
5
5
|
import { getPackageVersion } from './package_json.js';
|
|
6
6
|
import { RateLimitAction, RateLimitAlgorithm, RateLimitAggregator } from './rate_limit.js';
|
|
7
7
|
import { nonNullable } from './utils/non_nullable.js';
|
|
@@ -119,6 +119,9 @@ const generateManifest = ({ bundles = [], declarations = [], functions, userFunc
|
|
|
119
119
|
if ('method' in declaration) {
|
|
120
120
|
route.methods = normalizeMethods(declaration.method, func.name);
|
|
121
121
|
}
|
|
122
|
+
if ('header' in declaration) {
|
|
123
|
+
route.headers = getHeaderMatchers(declaration.header);
|
|
124
|
+
}
|
|
122
125
|
if ('path' in declaration) {
|
|
123
126
|
route.path = declaration.path;
|
|
124
127
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { test, expect } from 'vitest';
|
|
1
|
+
import { describe, test, expect } from 'vitest';
|
|
2
2
|
// @ts-expect-error current tsconfig.json doesn't allow this, but I don't want to change it
|
|
3
3
|
import { version } from '../package.json' assert { type: 'json' };
|
|
4
4
|
import { getRouteMatcher } from '../test/util.js';
|
|
@@ -514,3 +514,85 @@ test('Generates a manifest with rewrite config', () => {
|
|
|
514
514
|
expect(manifest.routes).toEqual(expectedRoutes);
|
|
515
515
|
expect(manifest.function_config).toEqual(expectedFunctionConfig);
|
|
516
516
|
});
|
|
517
|
+
describe('Header matching', () => {
|
|
518
|
+
test('Throws a bundling error if the type is incorrect', () => {
|
|
519
|
+
const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }];
|
|
520
|
+
expect(() => generateManifest({
|
|
521
|
+
bundles: [],
|
|
522
|
+
// @ts-expect-error Incorrect type
|
|
523
|
+
declarations: [{ function: 'func-1', path: '/f1/*', header: 'foo' }],
|
|
524
|
+
functions,
|
|
525
|
+
})).toThrowError(BundleError);
|
|
526
|
+
expect(() => generateManifest({
|
|
527
|
+
bundles: [],
|
|
528
|
+
declarations: [
|
|
529
|
+
{
|
|
530
|
+
function: 'func-1',
|
|
531
|
+
path: '/f1/*',
|
|
532
|
+
header: {
|
|
533
|
+
'x-correct': true,
|
|
534
|
+
// @ts-expect-error Incorrect type
|
|
535
|
+
'x-not-correct': {
|
|
536
|
+
problem: true,
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
functions,
|
|
542
|
+
})).toThrowError(BundleError);
|
|
543
|
+
});
|
|
544
|
+
test('Writes header matching rules to the manifest', () => {
|
|
545
|
+
const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }];
|
|
546
|
+
const declarations = [
|
|
547
|
+
{
|
|
548
|
+
function: 'func-1',
|
|
549
|
+
path: '/f1/*',
|
|
550
|
+
header: {
|
|
551
|
+
'x-present': true,
|
|
552
|
+
'x-also-present': true,
|
|
553
|
+
'x-absent': false,
|
|
554
|
+
'x-match-prefix': '^prefix(.*)',
|
|
555
|
+
'x-match-exact': 'exact',
|
|
556
|
+
'x-match-suffix': '(.*)suffix$',
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
];
|
|
560
|
+
const { manifest } = generateManifest({
|
|
561
|
+
bundles: [],
|
|
562
|
+
declarations,
|
|
563
|
+
functions,
|
|
564
|
+
});
|
|
565
|
+
const expectedRoutes = [
|
|
566
|
+
{
|
|
567
|
+
function: 'func-1',
|
|
568
|
+
pattern: '^/f1(?:/(.*))/?$',
|
|
569
|
+
excluded_patterns: [],
|
|
570
|
+
path: '/f1/*',
|
|
571
|
+
headers: {
|
|
572
|
+
'x-absent': {
|
|
573
|
+
style: 'missing',
|
|
574
|
+
},
|
|
575
|
+
'x-also-present': {
|
|
576
|
+
style: 'exists',
|
|
577
|
+
},
|
|
578
|
+
'x-match-exact': {
|
|
579
|
+
pattern: '^exact$',
|
|
580
|
+
style: 'regex',
|
|
581
|
+
},
|
|
582
|
+
'x-match-prefix': {
|
|
583
|
+
pattern: '^prefix(.*)$',
|
|
584
|
+
style: 'regex',
|
|
585
|
+
},
|
|
586
|
+
'x-match-suffix': {
|
|
587
|
+
pattern: '^(.*)suffix$',
|
|
588
|
+
style: 'regex',
|
|
589
|
+
},
|
|
590
|
+
'x-present': {
|
|
591
|
+
style: 'exists',
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
];
|
|
596
|
+
expect(manifest.routes).toEqual(expectedRoutes);
|
|
597
|
+
});
|
|
598
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/edge-bundler",
|
|
3
|
-
"version": "14.0
|
|
3
|
+
"version": "14.1.0",
|
|
4
4
|
"description": "Intelligently prepare Netlify Edge Functions for deployment",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/node/index.js",
|
|
@@ -81,5 +81,5 @@
|
|
|
81
81
|
"urlpattern-polyfill": "8.0.2",
|
|
82
82
|
"uuid": "^11.0.0"
|
|
83
83
|
},
|
|
84
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "ea236c6df25a511cc0f0412759610da917c10b20"
|
|
85
85
|
}
|