@netlify/edge-bundler 8.12.0 → 8.12.2

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.
@@ -25,8 +25,8 @@ export declare const addGeneratorFieldIfMissing: (declaration: Declaration, func
25
25
  cache?: string | undefined;
26
26
  function: string;
27
27
  name?: string | undefined;
28
- path: string;
29
- excludedPath?: string | undefined;
28
+ path: `/${string}`;
29
+ excludedPath?: `/${string}` | undefined;
30
30
  } | {
31
31
  generator: string | undefined;
32
32
  cache?: string | undefined;
@@ -7,12 +7,13 @@ export declare const enum Cache {
7
7
  Off = "off",
8
8
  Manual = "manual"
9
9
  }
10
+ export type Path = `/${string}`;
10
11
  export type OnError = 'fail' | 'bypass' | `/${string}`;
11
12
  export declare const isValidOnError: (value: unknown) => value is OnError;
12
13
  export interface FunctionConfig {
13
14
  cache?: Cache;
14
- path?: string | string[];
15
- excludedPath?: string | string[];
15
+ path?: Path | Path[];
16
+ excludedPath?: Path | Path[];
16
17
  onError?: OnError;
17
18
  }
18
19
  export declare const getFunctionConfig: (func: EdgeFunction, importMap: ImportMap, deno: DenoBridge, log: Logger, featureFlags: FeatureFlags) => Promise<FunctionConfig>;
@@ -1,4 +1,4 @@
1
- import { FunctionConfig } from './config.js';
1
+ import { FunctionConfig, Path } from './config.js';
2
2
  interface BaseDeclaration {
3
3
  cache?: string;
4
4
  function: string;
@@ -6,8 +6,8 @@ interface BaseDeclaration {
6
6
  generator?: string;
7
7
  }
8
8
  type DeclarationWithPath = BaseDeclaration & {
9
- path: string;
10
- excludedPath?: string;
9
+ path: Path;
10
+ excludedPath?: Path;
11
11
  };
12
12
  type DeclarationWithPattern = BaseDeclaration & {
13
13
  pattern: string;
@@ -1,12 +1,14 @@
1
1
  declare const defaultFlags: {
2
2
  edge_functions_fail_unsupported_regex: boolean;
3
3
  edge_functions_invalid_config_throw: boolean;
4
+ edge_functions_manifest_validate_slash: boolean;
4
5
  };
5
6
  type FeatureFlag = keyof typeof defaultFlags;
6
7
  type FeatureFlags = Partial<Record<FeatureFlag, boolean>>;
7
8
  declare const getFlags: (input?: Record<string, boolean>, flags?: {
8
9
  edge_functions_fail_unsupported_regex: boolean;
9
10
  edge_functions_invalid_config_throw: boolean;
11
+ edge_functions_manifest_validate_slash: boolean;
10
12
  }) => FeatureFlags;
11
13
  export { defaultFlags, getFlags };
12
14
  export type { FeatureFlag, FeatureFlags };
@@ -1,6 +1,7 @@
1
1
  const defaultFlags = {
2
2
  edge_functions_fail_unsupported_regex: false,
3
3
  edge_functions_invalid_config_throw: false,
4
+ edge_functions_manifest_validate_slash: false,
4
5
  };
5
6
  const getFlags = (input = {}, flags = defaultFlags) => Object.entries(flags).reduce((result, [key, defaultValue]) => ({
6
7
  ...result,
@@ -3,7 +3,7 @@ import { join } from 'path';
3
3
  import { pathToFileURL } from 'url';
4
4
  import { deleteAsync } from 'del';
5
5
  const defaultFormatExportTypeError = (name) => `The Edge Function "${name}" has failed to load. Does it have a function as the default export?`;
6
- const defaultFormatImpoortError = (name) => `There was an error with Edge Function "${name}".`;
6
+ const defaultFormatImportError = (name) => `There was an error with Edge Function "${name}".`;
7
7
  const generateStage2 = async ({ bootstrapURL, distDirectory, fileName, formatExportTypeError, formatImportError, functions, }) => {
8
8
  await deleteAsync(distDirectory, { force: true });
9
9
  await fs.mkdir(distDirectory, { recursive: true });
@@ -15,7 +15,7 @@ const generateStage2 = async ({ bootstrapURL, distDirectory, fileName, formatExp
15
15
  // For the local development environment, we import the user functions with
16
16
  // dynamic imports to gracefully handle the case where the file doesn't have
17
17
  // a valid default export.
18
- const getLocalEntryPoint = (functions, { bootstrapURL, formatExportTypeError = defaultFormatExportTypeError, formatImportError = defaultFormatImpoortError, }) => {
18
+ const getLocalEntryPoint = (functions, { bootstrapURL, formatExportTypeError = defaultFormatExportTypeError, formatImportError = defaultFormatImportError, }) => {
19
19
  const bootImport = `import { boot } from "${bootstrapURL}";`;
20
20
  const declaration = `const functions = {}; const metadata = { functions: {} };`;
21
21
  const imports = functions.map((func) => {
@@ -23,6 +23,10 @@ const generateManifest = ({ bundles = [], declarations = [], featureFlags, funct
23
23
  const postCacheRoutes = [];
24
24
  const manifestFunctionConfig = Object.fromEntries(functions.map(({ name }) => [name, { excluded_patterns: [] }]));
25
25
  for (const [name, { excludedPath, onError }] of Object.entries(functionConfig)) {
26
+ // If the config block is for a function that is not defined, discard it.
27
+ if (manifestFunctionConfig[name] === undefined) {
28
+ continue;
29
+ }
26
30
  if (excludedPath) {
27
31
  const paths = Array.isArray(excludedPath) ? excludedPath : [excludedPath];
28
32
  const excludedPatterns = paths.map(pathToRegularExpression).map(serializePattern);
@@ -1,3 +1,4 @@
1
+ import { FeatureFlags } from '../../feature_flags.js';
1
2
  import ManifestValidationError from './error.js';
2
- export declare const validateManifest: (manifestData: unknown) => void;
3
+ export declare const validateManifest: (manifestData: unknown, featureFlags?: FeatureFlags) => void;
3
4
  export { ManifestValidationError };
@@ -4,13 +4,13 @@ import betterAjvErrors from 'better-ajv-errors';
4
4
  import ManifestValidationError from './error.js';
5
5
  import edgeManifestSchema from './schema.js';
6
6
  let manifestValidator;
7
- const initializeValidator = () => {
7
+ const initializeValidator = (featureFlags) => {
8
8
  if (manifestValidator === undefined) {
9
9
  const ajv = new Ajv({ allErrors: true });
10
10
  ajvErrors(ajv);
11
11
  // regex pattern for manifest route pattern
12
12
  // checks if the pattern string starts with ^ and ends with $
13
- const normalizedPatternRegex = /^\^.*\$$/;
13
+ const normalizedPatternRegex = featureFlags.edge_functions_manifest_validate_slash ? /^\^\/.*\$$/ : /^\^.*\$$/;
14
14
  ajv.addFormat('regexPattern', {
15
15
  validate: (data) => normalizedPatternRegex.test(data),
16
16
  });
@@ -19,8 +19,8 @@ const initializeValidator = () => {
19
19
  return manifestValidator;
20
20
  };
21
21
  // throws on validation error
22
- export const validateManifest = (manifestData) => {
23
- const validate = initializeValidator();
22
+ export const validateManifest = (manifestData, featureFlags = {}) => {
23
+ const validate = initializeValidator(featureFlags);
24
24
  const valid = validate(manifestData);
25
25
  if (!valid) {
26
26
  let errorOutput;
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { test, expect, describe } from 'vitest';
2
+ import { test, expect, describe, beforeEach, vi } from 'vitest';
3
3
  import { validateManifest, ManifestValidationError } from './index.js';
4
4
  // We need to disable all color outputs for the tests as they are different on different platforms, CI, etc.
5
5
  // This only works if this is the same instance of chalk that better-ajv-errors uses
@@ -79,25 +79,42 @@ describe('bundle', () => {
79
79
  });
80
80
  });
81
81
  describe('route', () => {
82
+ let freshValidateManifest;
83
+ beforeEach(async () => {
84
+ // reset all modules, to get a fresh AJV validator for FF changes
85
+ vi.resetModules();
86
+ const indexImport = await import('./index.js');
87
+ freshValidateManifest = indexImport.validateManifest;
88
+ });
82
89
  test('should throw on additional property', () => {
83
90
  const manifest = getBaseManifest();
84
91
  manifest.routes[0].foo = 'bar';
85
- expect(() => validateManifest(manifest)).toThrowErrorMatchingSnapshot();
92
+ expect(() => freshValidateManifest(manifest)).toThrowErrorMatchingSnapshot();
86
93
  });
87
94
  test('should throw on invalid pattern', () => {
88
95
  const manifest = getBaseManifest();
89
96
  manifest.routes[0].pattern = '/^/hello/?$/';
90
- expect(() => validateManifest(manifest)).toThrowErrorMatchingSnapshot();
97
+ expect(() => freshValidateManifest(manifest)).toThrowErrorMatchingSnapshot();
98
+ });
99
+ test('should not throw on missing beginning slash without FF', () => {
100
+ const manifest = getBaseManifest();
101
+ manifest.routes[0].pattern = '^hello/?$';
102
+ expect(() => freshValidateManifest(manifest, { edge_functions_manifest_validate_slash: false })).not.toThrowError();
103
+ });
104
+ test('should throw on missing beginning slash with FF', () => {
105
+ const manifest = getBaseManifest();
106
+ manifest.routes[0].pattern = '^hello/?$';
107
+ expect(() => freshValidateManifest(manifest, { edge_functions_manifest_validate_slash: true })).toThrowErrorMatchingSnapshot();
91
108
  });
92
109
  test('should throw on missing function', () => {
93
110
  const manifest = getBaseManifest();
94
111
  delete manifest.routes[0].function;
95
- expect(() => validateManifest(manifest)).toThrowErrorMatchingSnapshot();
112
+ expect(() => freshValidateManifest(manifest)).toThrowErrorMatchingSnapshot();
96
113
  });
97
114
  test('should throw on missing pattern', () => {
98
115
  const manifest = getBaseManifest();
99
116
  delete manifest.routes[0].pattern;
100
- expect(() => validateManifest(manifest)).toThrowErrorMatchingSnapshot();
117
+ expect(() => freshValidateManifest(manifest)).toThrowErrorMatchingSnapshot();
101
118
  });
102
119
  });
103
120
  // No tests for post_cache_routes as schema shared with routes
@@ -16,7 +16,7 @@ const routesSchema = {
16
16
  pattern: {
17
17
  type: 'string',
18
18
  format: 'regexPattern',
19
- errorMessage: 'pattern needs to be a regex that starts with ^ and ends with $ without any additional slashes before and afterwards',
19
+ errorMessage: 'pattern needs to be a regex that starts with ^ followed by / and ends with $ without any additional slashes before and afterwards',
20
20
  },
21
21
  generator: { type: 'string' },
22
22
  },
@@ -31,7 +31,7 @@ const functionConfigSchema = {
31
31
  items: {
32
32
  type: 'string',
33
33
  format: 'regexPattern',
34
- errorMessage: 'excluded_patterns needs to be an array of regex that starts with ^ and ends with $ without any additional slashes before and afterwards',
34
+ errorMessage: 'excluded_patterns needs to be an array of regex that starts with ^ followed by / and ends with $ without any additional slashes before and afterwards',
35
35
  },
36
36
  },
37
37
  on_error: { type: 'string' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/edge-bundler",
3
- "version": "8.12.0",
3
+ "version": "8.12.2",
4
4
  "description": "Intelligently prepare Netlify Edge Functions for deployment",
5
5
  "type": "module",
6
6
  "main": "./dist/node/index.js",
@@ -57,9 +57,8 @@
57
57
  "@types/glob-to-regexp": "^0.4.1",
58
58
  "@types/node": "^14.18.32",
59
59
  "@types/semver": "^7.3.9",
60
- "@types/sinon": "^10.0.8",
61
60
  "@types/uuid": "^9.0.0",
62
- "@vitest/coverage-c8": "^0.25.0",
61
+ "@vitest/coverage-c8": "^0.29.2",
63
62
  "archiver": "^5.3.1",
64
63
  "chalk": "^4.1.2",
65
64
  "cpy": "^9.0.1",
@@ -68,8 +67,7 @@
68
67
  "nock": "^13.2.4",
69
68
  "tar": "^6.1.11",
70
69
  "typescript": "^4.5.4",
71
- "vite": "^4.0.0",
72
- "vitest": "^0.25.0"
70
+ "vitest": "^0.29.2"
73
71
  },
74
72
  "engines": {
75
73
  "node": "^14.16.0 || >=16.0.0"