@netlify/edge-bundler 11.3.0 → 12.0.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.
@@ -0,0 +1,58 @@
1
+ import { assertEquals, assertStringIncludes } from 'https://deno.land/std@0.177.0/testing/asserts.ts'
2
+
3
+ import { join } from 'https://deno.land/std@0.177.0/path/mod.ts'
4
+ import { pathToFileURL } from 'https://deno.land/std@0.177.0/node/url.ts'
5
+
6
+ import { getStage2Entry } from './stage2.ts'
7
+ import { virtualRoot } from './consts.ts'
8
+
9
+ Deno.test('`getStage2Entry` returns a valid stage 2 file', async () => {
10
+ const directory = await Deno.makeTempDir()
11
+ const functions = [
12
+ {
13
+ name: 'func1',
14
+ path: join(directory, 'func1.ts'),
15
+ response: 'Hello from function 1',
16
+ },
17
+ {
18
+ name: 'func2',
19
+ path: join(directory, 'func2.ts'),
20
+ response: 'Hello from function 2',
21
+ },
22
+ ]
23
+
24
+ for (const func of functions) {
25
+ const contents = `export default async () => new Response(${JSON.stringify(func.response)})`
26
+
27
+ await Deno.writeTextFile(func.path, contents)
28
+ }
29
+
30
+ const baseURL = pathToFileURL(directory)
31
+ const stage2 = getStage2Entry(
32
+ directory,
33
+ functions.map(({ name, path }) => ({ name, path })),
34
+ )
35
+
36
+ // Ensuring that the stage 2 paths have the virtual root before we strip it.
37
+ assertStringIncludes(stage2, virtualRoot)
38
+
39
+ // Replacing the virtual root with the URL of the temporary directory so that
40
+ // we can actually import the module.
41
+ const normalizedStage2 = stage2.replaceAll(virtualRoot, `${baseURL.href}/`)
42
+
43
+ const stage2Path = join(directory, 'stage2.ts')
44
+ const stage2URL = pathToFileURL(stage2Path)
45
+
46
+ await Deno.writeTextFile(stage2Path, normalizedStage2)
47
+
48
+ const mod = await import(stage2URL.href)
49
+
50
+ await Deno.remove(directory, { recursive: true })
51
+
52
+ for (const func of functions) {
53
+ const result = await mod.functions[func.name]()
54
+
55
+ assertEquals(await result.text(), func.response)
56
+ assertEquals(mod.metadata.functions[func.name].url, pathToFileURL(func.path).toString())
57
+ }
58
+ })
@@ -157,7 +157,6 @@ class DenoBridge {
157
157
  const options = { env, extendEnv };
158
158
  const ps = DenoBridge.runWithBinary(binaryPath, args, { options, pipeOutput, stderr, stdout });
159
159
  if (ref !== undefined) {
160
- // eslint-disable-next-line no-param-reassign
161
160
  ref.ps = ps;
162
161
  }
163
162
  }
@@ -21,7 +21,6 @@ class BundleError extends Error {
21
21
  const wrapBundleError = (input, options) => {
22
22
  if (input instanceof Error) {
23
23
  if (input.message.includes("The module's source code could not be parsed")) {
24
- // eslint-disable-next-line no-param-reassign
25
24
  input.message = input.stderr;
26
25
  }
27
26
  return new BundleError(input, options);
@@ -2,6 +2,7 @@ import { DenoBridge } from './bridge.js';
2
2
  import { EdgeFunction } from './edge_function.js';
3
3
  import { ImportMap } from './import_map.js';
4
4
  import { Logger } from './logger.js';
5
+ import { RateLimit } from './rate_limit.js';
5
6
  export declare const enum Cache {
6
7
  Off = "off",
7
8
  Manual = "manual"
@@ -18,6 +19,7 @@ export interface FunctionConfig {
18
19
  name?: string;
19
20
  generator?: string;
20
21
  method?: HTTPMethod | HTTPMethod[];
22
+ rateLimit?: RateLimit;
21
23
  }
22
24
  export declare const getFunctionConfig: ({ func, importMap, deno, log, }: {
23
25
  func: EdgeFunction;
@@ -9,6 +9,7 @@ import { DenoBridge } from './bridge.js';
9
9
  import { bundle } from './bundler.js';
10
10
  import { getFunctionConfig } from './config.js';
11
11
  import { ImportMap } from './import_map.js';
12
+ import { RateLimitAction, RateLimitAggregator } from './rate_limit.js';
12
13
  const importMapFile = {
13
14
  baseURL: new URL('file:///some/path/import-map.json'),
14
15
  imports: {
@@ -64,7 +65,7 @@ const functions = [
64
65
  },
65
66
  {
66
67
  testName: 'config with wrong onError',
67
- name: 'func7',
68
+ name: 'func6',
68
69
  source: `
69
70
  export default async () => new Response("Hello from function two")
70
71
  export const config = { onError: "foo" }
@@ -74,7 +75,7 @@ const functions = [
74
75
  {
75
76
  testName: 'config with `path`',
76
77
  expectedConfig: { path: '/home' },
77
- name: 'func6',
78
+ name: 'func7',
78
79
  source: `
79
80
  export default async () => new Response("Hello from function three")
80
81
 
@@ -89,17 +90,74 @@ const functions = [
89
90
  name: 'a displayName',
90
91
  onError: 'bypass',
91
92
  },
92
- name: 'func6',
93
+ name: 'func8',
93
94
  source: `
94
95
  export default async () => new Response("Hello from function three")
95
96
 
96
- export const config = { path: "/home",
97
+ export const config = {
98
+ path: "/home",
97
99
  generator: '@netlify/fake-plugin@1.0.0',
98
100
  name: 'a displayName',
99
101
  onError: 'bypass',
100
102
  }
101
103
  `,
102
104
  },
105
+ {
106
+ testName: 'config with ratelimit',
107
+ expectedConfig: {
108
+ path: '/ratelimit',
109
+ name: 'a limit rate',
110
+ rateLimit: {
111
+ windowSize: 10,
112
+ windowLimit: 100,
113
+ aggregateBy: [RateLimitAggregator.IP, RateLimitAggregator.Domain],
114
+ },
115
+ },
116
+ name: 'func9',
117
+ source: `
118
+ export default async () => new Response("Rate my limits")
119
+
120
+ export const config = {
121
+ path: "/ratelimit",
122
+ rateLimit: {
123
+ windowSize: 10,
124
+ windowLimit: 100,
125
+ aggregateBy: ["ip", "domain"],
126
+ },
127
+ name: 'a limit rate',
128
+ }
129
+ `,
130
+ },
131
+ {
132
+ testName: 'config with rewrite',
133
+ expectedConfig: {
134
+ path: '/rewrite',
135
+ name: 'a limit rewrite',
136
+ rateLimit: {
137
+ action: RateLimitAction.Rewrite,
138
+ to: '/rewritten',
139
+ windowSize: 20,
140
+ windowLimit: 200,
141
+ aggregateBy: [RateLimitAggregator.IP, RateLimitAggregator.Domain],
142
+ },
143
+ },
144
+ name: 'func9',
145
+ source: `
146
+ export default async () => new Response("Rate my limits")
147
+
148
+ export const config = {
149
+ path: "/rewrite",
150
+ rateLimit: {
151
+ action: "rewrite",
152
+ to: "/rewritten",
153
+ windowSize: 20,
154
+ windowLimit: 200,
155
+ aggregateBy: ["ip", "domain"],
156
+ },
157
+ name: 'a limit rewrite',
158
+ }
159
+ `,
160
+ },
103
161
  ];
104
162
  describe('`getFunctionConfig` extracts configuration properties from function file', () => {
105
163
  test.each(functions)('$testName', async (func) => {
@@ -37,6 +37,7 @@ const getDeclarationsFromInput = (inputDeclarations, functionConfigs, functionsV
37
37
  }
38
38
  else {
39
39
  // With an in-source config without a path, add the config to the declaration.
40
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
40
41
  const { path, excludedPath, ...rest } = config;
41
42
  declarations.push({ ...declaration, ...rest });
42
43
  }
@@ -26,7 +26,6 @@ const streamError = () => {
26
26
  };
27
27
  beforeEach(async (ctx) => {
28
28
  const tmpDir = await tmp.dir();
29
- // eslint-disable-next-line no-param-reassign
30
29
  ctx.tmpDir = tmpDir.path;
31
30
  });
32
31
  afterEach(async (ctx) => {
@@ -23,7 +23,6 @@ const findFunctionInDirectory = async (directory) => {
23
23
  for (const candidatePath of candidatePaths) {
24
24
  try {
25
25
  const stats = await fs.stat(candidatePath);
26
- // eslint-disable-next-line max-depth
27
26
  if (stats.isFile()) {
28
27
  functionPath = candidatePath;
29
28
  break;
@@ -11,11 +11,30 @@ interface Route {
11
11
  path?: string;
12
12
  methods?: string[];
13
13
  }
14
+ interface TrafficRules {
15
+ action: {
16
+ type: string;
17
+ config: {
18
+ rate_limit_config: {
19
+ algorithm: string;
20
+ window_size: number;
21
+ window_limit: number;
22
+ };
23
+ aggregate: {
24
+ keys: {
25
+ type: string;
26
+ }[];
27
+ };
28
+ to?: string;
29
+ };
30
+ };
31
+ }
14
32
  export interface EdgeFunctionConfig {
15
33
  excluded_patterns: string[];
16
34
  on_error?: string;
17
35
  generator?: string;
18
36
  name?: string;
37
+ traffic_rules?: TrafficRules;
19
38
  }
20
39
  interface Manifest {
21
40
  bundler_version: string;
@@ -3,6 +3,7 @@ import { join } from 'path';
3
3
  import { wrapBundleError } from './bundle_error.js';
4
4
  import { normalizePattern } from './declaration.js';
5
5
  import { getPackageVersion } from './package_json.js';
6
+ import { RateLimitAction, RateLimitAlgorithm, RateLimitAggregator } from './rate_limit.js';
6
7
  import { nonNullable } from './utils/non_nullable.js';
7
8
  import { ExtendedURLPattern } from './utils/urlpattern.js';
8
9
  const removeEmptyConfigValues = (functionConfig) => Object.entries(functionConfig).reduce((acc, [key, value]) => {
@@ -52,21 +53,31 @@ const generateManifest = ({ bundles = [], declarations = [], functions, userFunc
52
53
  const manifestFunctionConfig = Object.fromEntries(functions.map(({ name }) => [name, { excluded_patterns: [] }]));
53
54
  const routedFunctions = new Set();
54
55
  const declarationsWithoutFunction = new Set();
55
- for (const [name, { excludedPath, onError }] of Object.entries(userFunctionConfig)) {
56
+ for (const [name, { excludedPath, onError, rateLimit }] of Object.entries(userFunctionConfig)) {
56
57
  // If the config block is for a function that is not defined, discard it.
57
58
  if (manifestFunctionConfig[name] === undefined) {
58
59
  continue;
59
60
  }
60
61
  addExcludedPatterns(name, manifestFunctionConfig, excludedPath);
61
- manifestFunctionConfig[name] = { ...manifestFunctionConfig[name], on_error: onError };
62
+ manifestFunctionConfig[name] = {
63
+ ...manifestFunctionConfig[name],
64
+ on_error: onError,
65
+ traffic_rules: getTrafficRulesConfig(rateLimit),
66
+ };
62
67
  }
63
- for (const [name, { excludedPath, path, onError, ...rest }] of Object.entries(internalFunctionConfig)) {
68
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
69
+ for (const [name, { excludedPath, path, onError, rateLimit, ...rest }] of Object.entries(internalFunctionConfig)) {
64
70
  // If the config block is for a function that is not defined, discard it.
65
71
  if (manifestFunctionConfig[name] === undefined) {
66
72
  continue;
67
73
  }
68
74
  addExcludedPatterns(name, manifestFunctionConfig, excludedPath);
69
- manifestFunctionConfig[name] = { ...manifestFunctionConfig[name], on_error: onError, ...rest };
75
+ manifestFunctionConfig[name] = {
76
+ ...manifestFunctionConfig[name],
77
+ on_error: onError,
78
+ traffic_rules: getTrafficRulesConfig(rateLimit),
79
+ ...rest,
80
+ };
70
81
  }
71
82
  declarations.forEach((declaration) => {
72
83
  const func = functions.find(({ name }) => declaration.function === name);
@@ -116,6 +127,29 @@ const generateManifest = ({ bundles = [], declarations = [], functions, userFunc
116
127
  const unroutedFunctions = functions.filter(({ name }) => !routedFunctions.has(name)).map(({ name }) => name);
117
128
  return { declarationsWithoutFunction: [...declarationsWithoutFunction], manifest, unroutedFunctions };
118
129
  };
130
+ const getTrafficRulesConfig = (rl) => {
131
+ if (rl === undefined) {
132
+ return;
133
+ }
134
+ const rateLimitAgg = Array.isArray(rl.aggregateBy) ? rl.aggregateBy : [RateLimitAggregator.Domain];
135
+ const rewriteConfig = 'to' in rl && typeof rl.to === 'string' ? { to: rl.to } : undefined;
136
+ return {
137
+ action: {
138
+ type: rl.action || RateLimitAction.Limit,
139
+ config: {
140
+ ...rewriteConfig,
141
+ rate_limit_config: {
142
+ window_limit: rl.windowLimit,
143
+ window_size: rl.windowSize,
144
+ algorithm: RateLimitAlgorithm.SlidingWindow,
145
+ },
146
+ aggregate: {
147
+ keys: rateLimitAgg.map((agg) => ({ type: agg })),
148
+ },
149
+ },
150
+ },
151
+ };
152
+ };
119
153
  const pathToRegularExpression = (path) => {
120
154
  if (!path) {
121
155
  return null;
@@ -1,9 +1,11 @@
1
- import { env } from 'process';
2
1
  import { test, expect } from 'vitest';
2
+ // @ts-expect-error current tsconfig.json doesn't allow this, but I don't want to change it
3
+ import { version } from '../package.json' assert { type: 'json' };
3
4
  import { getRouteMatcher } from '../test/util.js';
4
5
  import { BundleFormat } from './bundle.js';
5
6
  import { BundleError } from './bundle_error.js';
6
7
  import { generateManifest } from './manifest.js';
8
+ import { RateLimitAction, RateLimitAggregator } from './rate_limit.js';
7
9
  test('Generates a manifest with different bundles', () => {
8
10
  const bundle1 = {
9
11
  extension: '.ext1',
@@ -25,7 +27,7 @@ test('Generates a manifest with different bundles', () => {
25
27
  const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/?$', excluded_patterns: [], path: '/f1' }];
26
28
  expect(manifest.bundles).toEqual(expectedBundles);
27
29
  expect(manifest.routes).toEqual(expectedRoutes);
28
- expect(manifest.bundler_version).toBe(env.npm_package_version);
30
+ expect(manifest.bundler_version).toBe(version);
29
31
  });
30
32
  test('Generates a manifest with display names', () => {
31
33
  const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }];
@@ -46,7 +48,7 @@ test('Generates a manifest with display names', () => {
46
48
  'func-1': { name: 'Display Name' },
47
49
  });
48
50
  expect(manifest.routes).toEqual(expectedRoutes);
49
- expect(manifest.bundler_version).toBe(env.npm_package_version);
51
+ expect(manifest.bundler_version).toBe(version);
50
52
  });
51
53
  test('Generates a manifest with a generator field', () => {
52
54
  const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }];
@@ -100,7 +102,7 @@ test('Generates a manifest with excluded paths and patterns', () => {
100
102
  ];
101
103
  expect(manifest.routes).toEqual(expectedRoutes);
102
104
  expect(manifest.function_config).toEqual({});
103
- expect(manifest.bundler_version).toBe(env.npm_package_version);
105
+ expect(manifest.bundler_version).toBe(version);
104
106
  const matcher = getRouteMatcher(manifest);
105
107
  expect((_a = matcher('/f1/hello')) === null || _a === void 0 ? void 0 : _a.function).toBe('func-1');
106
108
  expect((_b = matcher('/grandparent/parent/child/grandchild.html')) === null || _b === void 0 ? void 0 : _b.function).toBeUndefined();
@@ -122,7 +124,7 @@ test('TOML-defined paths can be combined with ISC-defined excluded paths', () =>
122
124
  expect(manifest.function_config).toEqual({
123
125
  'func-1': { excluded_patterns: ['^/f1/exclude/?$'] },
124
126
  });
125
- expect(manifest.bundler_version).toBe(env.npm_package_version);
127
+ expect(manifest.bundler_version).toBe(version);
126
128
  });
127
129
  test('Filters out internal in-source configurations in user created functions', () => {
128
130
  const functions = [
@@ -311,7 +313,7 @@ test('Generates a manifest without bundles', () => {
311
313
  const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/?$', excluded_patterns: [], path: '/f1' }];
312
314
  expect(manifest.bundles).toEqual([]);
313
315
  expect(manifest.routes).toEqual(expectedRoutes);
314
- expect(manifest.bundler_version).toBe(env.npm_package_version);
316
+ expect(manifest.bundler_version).toBe(version);
315
317
  });
316
318
  test('Generates a manifest with pre and post-cache routes', () => {
317
319
  const bundle1 = {
@@ -349,7 +351,7 @@ test('Generates a manifest with pre and post-cache routes', () => {
349
351
  expect(manifest.bundles).toEqual(expectedBundles);
350
352
  expect(manifest.routes).toEqual(expectedPreCacheRoutes);
351
353
  expect(manifest.post_cache_routes).toEqual(expectedPostCacheRoutes);
352
- expect(manifest.bundler_version).toBe(env.npm_package_version);
354
+ expect(manifest.bundler_version).toBe(version);
353
355
  });
354
356
  test('Generates a manifest with layers', () => {
355
357
  const functions = [
@@ -433,3 +435,82 @@ test('Returns functions without a declaration and unrouted functions', () => {
433
435
  expect(declarationsWithoutFunction).toEqual(['func-3']);
434
436
  expect(unroutedFunctions).toEqual(['func-2', 'func-4']);
435
437
  });
438
+ test('Generates a manifest with rate limit config', () => {
439
+ const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }];
440
+ const declarations = [{ function: 'func-1', path: '/f1/*' }];
441
+ const userFunctionConfig = {
442
+ 'func-1': { rateLimit: { windowLimit: 100, windowSize: 60 } },
443
+ };
444
+ const { manifest } = generateManifest({
445
+ bundles: [],
446
+ declarations,
447
+ functions,
448
+ userFunctionConfig,
449
+ });
450
+ const expectedRoutes = [{ function: 'func-1', pattern: '^/f1(?:/(.*))/?$', excluded_patterns: [], path: '/f1/*' }];
451
+ const expectedFunctionConfig = {
452
+ 'func-1': {
453
+ traffic_rules: {
454
+ action: {
455
+ type: 'rate_limit',
456
+ config: {
457
+ rate_limit_config: {
458
+ window_limit: 100,
459
+ window_size: 60,
460
+ algorithm: 'sliding_window',
461
+ },
462
+ aggregate: {
463
+ keys: [{ type: 'domain' }],
464
+ },
465
+ },
466
+ },
467
+ },
468
+ },
469
+ };
470
+ expect(manifest.routes).toEqual(expectedRoutes);
471
+ expect(manifest.function_config).toEqual(expectedFunctionConfig);
472
+ });
473
+ test('Generates a manifest with rewrite config', () => {
474
+ const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }];
475
+ const declarations = [{ function: 'func-1', path: '/f1/*' }];
476
+ const userFunctionConfig = {
477
+ 'func-1': {
478
+ rateLimit: {
479
+ action: RateLimitAction.Rewrite,
480
+ to: '/new_path',
481
+ windowLimit: 100,
482
+ windowSize: 60,
483
+ aggregateBy: [RateLimitAggregator.Domain, RateLimitAggregator.IP],
484
+ },
485
+ },
486
+ };
487
+ const { manifest } = generateManifest({
488
+ bundles: [],
489
+ declarations,
490
+ functions,
491
+ userFunctionConfig,
492
+ });
493
+ const expectedRoutes = [{ function: 'func-1', pattern: '^/f1(?:/(.*))/?$', excluded_patterns: [], path: '/f1/*' }];
494
+ const expectedFunctionConfig = {
495
+ 'func-1': {
496
+ traffic_rules: {
497
+ action: {
498
+ type: 'rewrite',
499
+ config: {
500
+ to: '/new_path',
501
+ rate_limit_config: {
502
+ window_limit: 100,
503
+ window_size: 60,
504
+ algorithm: 'sliding_window',
505
+ },
506
+ aggregate: {
507
+ keys: [{ type: 'domain' }, { type: 'ip' }],
508
+ },
509
+ },
510
+ },
511
+ },
512
+ },
513
+ };
514
+ expect(manifest.routes).toEqual(expectedRoutes);
515
+ expect(manifest.function_config).toEqual(expectedFunctionConfig);
516
+ });
@@ -105,7 +105,6 @@ const getNPMSpecifiers = async ({ basePath, functions, importMap, environment, r
105
105
  }
106
106
  return fs.readFile(filePath, 'utf8');
107
107
  },
108
- // eslint-disable-next-line require-await
109
108
  resolve: async (specifier, ...args) => {
110
109
  // Start by checking whether the specifier matches any import map defined
111
110
  // by the user.
@@ -0,0 +1,25 @@
1
+ export declare enum RateLimitAlgorithm {
2
+ SlidingWindow = "sliding_window"
3
+ }
4
+ export declare enum RateLimitAggregator {
5
+ Domain = "domain",
6
+ IP = "ip"
7
+ }
8
+ export declare enum RateLimitAction {
9
+ Limit = "rate_limit",
10
+ Rewrite = "rewrite"
11
+ }
12
+ interface SlidingWindow {
13
+ windowLimit: number;
14
+ windowSize: number;
15
+ }
16
+ export type RewriteActionConfig = SlidingWindow & {
17
+ to: string;
18
+ };
19
+ interface RateLimitConfig {
20
+ action?: RateLimitAction;
21
+ aggregateBy?: RateLimitAggregator | RateLimitAggregator[];
22
+ algorithm?: RateLimitAlgorithm;
23
+ }
24
+ export type RateLimit = RateLimitConfig & (SlidingWindow | RewriteActionConfig);
25
+ export {};
@@ -0,0 +1,14 @@
1
+ export var RateLimitAlgorithm;
2
+ (function (RateLimitAlgorithm) {
3
+ RateLimitAlgorithm["SlidingWindow"] = "sliding_window";
4
+ })(RateLimitAlgorithm || (RateLimitAlgorithm = {}));
5
+ export var RateLimitAggregator;
6
+ (function (RateLimitAggregator) {
7
+ RateLimitAggregator["Domain"] = "domain";
8
+ RateLimitAggregator["IP"] = "ip";
9
+ })(RateLimitAggregator || (RateLimitAggregator = {}));
10
+ export var RateLimitAction;
11
+ (function (RateLimitAction) {
12
+ RateLimitAction["Limit"] = "rate_limit";
13
+ RateLimitAction["Rewrite"] = "rewrite";
14
+ })(RateLimitAction || (RateLimitAction = {}));
@@ -14,7 +14,6 @@ const isServerReady = async (port, successRef, ps) => {
14
14
  }
15
15
  try {
16
16
  await fetch(`http://127.0.0.1:${port}`);
17
- // eslint-disable-next-line no-param-reassign
18
17
  successRef.success = true;
19
18
  }
20
19
  catch {
@@ -4,6 +4,7 @@ import fetch from 'node-fetch';
4
4
  const TYPES_URL = 'https://edge.netlify.com';
5
5
  const ensureLatestTypes = async (deno, logger, customTypesURL) => {
6
6
  const typesURL = customTypesURL !== null && customTypesURL !== void 0 ? customTypesURL : TYPES_URL;
7
+ // eslint-disable-next-line prefer-const
7
8
  let [localVersion, remoteVersion] = [await getLocalVersion(deno), ''];
8
9
  try {
9
10
  remoteVersion = await getRemoteVersion(typesURL);
@@ -1,4 +1,2 @@
1
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
1
  export const isNodeError = (error) => error instanceof Error;
3
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
2
  export const isFileNotFoundError = (error) => isNodeError(error) && error.code === 'ENOENT';
@@ -31,5 +31,4 @@ export const validateManifest = (manifestData, _featureFlags = {}) => {
31
31
  throw new ManifestValidationError(errorOutput);
32
32
  }
33
33
  };
34
- // eslint-disable-next-line unicorn/prefer-export-from
35
34
  export { ManifestValidationError };
@@ -5,7 +5,6 @@ import { validateManifest, ManifestValidationError } from './index.js';
5
5
  // This only works if this is the same instance of chalk that better-ajv-errors uses
6
6
  chalk.level = 0;
7
7
  // Factory so we have a new object per test
8
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
8
  const getBaseManifest = () => ({
10
9
  bundles: [
11
10
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/edge-bundler",
3
- "version": "11.3.0",
3
+ "version": "12.0.0",
4
4
  "description": "Intelligently prepare Netlify Edge Functions for deployment",
5
5
  "type": "module",
6
6
  "main": "./dist/node/index.js",
@@ -15,20 +15,10 @@
15
15
  "scripts": {
16
16
  "build": "tsc",
17
17
  "build:dev": "tsc -w",
18
- "prepare": "husky install node_modules/@netlify/eslint-config-node/.husky/",
19
- "prepublishOnly": "npm ci && npm test",
20
18
  "prepack": "npm run build",
21
- "test": "run-s build format test:dev",
22
- "format": "run-s format:check-fix:*",
23
- "format:ci": "run-s format:check:*",
24
- "format:check-fix:lint": "run-e format:check:lint format:fix:lint",
25
- "format:check:lint": "cross-env-shell eslint $npm_package_config_eslint",
26
- "format:fix:lint": "cross-env-shell eslint --fix $npm_package_config_eslint",
27
- "format:check-fix:prettier": "run-e format:check:prettier format:fix:prettier",
28
- "format:check:prettier": "cross-env-shell prettier --check $npm_package_config_prettier",
29
- "format:fix:prettier": "cross-env-shell prettier --write $npm_package_config_prettier",
19
+ "test": "run-s build test:dev",
30
20
  "test:dev": "run-s test:dev:*",
31
- "test:ci": "run-s test:ci:*",
21
+ "test:ci": "run-s test:integration test:ci:*",
32
22
  "test:dev:vitest": "vitest run",
33
23
  "test:dev:vitest:watch": "vitest watch",
34
24
  "test:dev:deno": "deno test --allow-all deno",
@@ -37,24 +27,21 @@
37
27
  "test:integration": "node --experimental-modules test/integration/test.js",
38
28
  "vendor": "deno vendor --force --output deno/vendor https://deno.land/x/eszip@v0.55.2/mod.ts https://deno.land/x/retry@v2.0.0/mod.ts https://deno.land/x/std@0.177.0/path/mod.ts"
39
29
  },
40
- "config": {
41
- "eslint": "--ignore-path .gitignore --cache --format=codeframe --max-warnings=0 \"{node,scripts,.github}/**/*.{js,ts,md,html}\" \"*.{js,ts,md,html}\"",
42
- "prettier": "--ignore-path .gitignore --loglevel=warn \"{node,scripts,.github}/**/*.{js,ts,md,yml,json,html}\" \"*.{js,ts,yml,json,html}\" \".*.{js,ts,yml,json,html}\" \"!**/package-lock.json\" \"!package-lock.json\" \"!node/vendor/**\""
43
- },
44
30
  "keywords": [],
45
31
  "license": "MIT",
46
- "repository": "https://github.com/netlify/edge-bundler",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/netlify/build.git",
35
+ "directory": "packages/edge-bundler"
36
+ },
47
37
  "bugs": {
48
- "url": "https://github.com/netlify/edge-bundler/issues"
38
+ "url": "https://github.com/netlify/build/issues"
49
39
  },
50
40
  "author": "Netlify Inc.",
51
41
  "directories": {
52
42
  "test": "test/node"
53
43
  },
54
44
  "devDependencies": {
55
- "@commitlint/cli": "^17.0.0",
56
- "@commitlint/config-conventional": "^17.0.0",
57
- "@netlify/eslint-config-node": "^7.0.1",
58
45
  "@types/glob-to-regexp": "^0.4.1",
59
46
  "@types/node": "^14.18.32",
60
47
  "@types/semver": "^7.3.9",
@@ -64,7 +51,6 @@
64
51
  "chalk": "^4.1.2",
65
52
  "cpy": "^9.0.1",
66
53
  "cross-env": "^7.0.3",
67
- "husky": "^8.0.0",
68
54
  "nock": "^13.2.4",
69
55
  "tar": "^6.1.11",
70
56
  "typescript": "^5.0.0",
@@ -81,7 +67,7 @@
81
67
  "better-ajv-errors": "^1.2.0",
82
68
  "common-path-prefix": "^3.0.0",
83
69
  "env-paths": "^3.0.0",
84
- "esbuild": "0.20.0",
70
+ "esbuild": "0.20.2",
85
71
  "execa": "^6.0.0",
86
72
  "find-up": "^6.3.0",
87
73
  "get-package-name": "^2.2.0",
@@ -97,5 +83,6 @@
97
83
  "tmp-promise": "^3.0.3",
98
84
  "urlpattern-polyfill": "8.0.2",
99
85
  "uuid": "^9.0.0"
100
- }
86
+ },
87
+ "gitHead": "cf765526c7970810cc1812ac0b2b3d9bb11ea926"
101
88
  }