diff --git a/packages/edge-bundler/node/config.ts b/packages/edge-bundler/node/config.ts index 2e0b605d0e..2cf9cebf2d 100644 --- a/packages/edge-bundler/node/config.ts +++ b/packages/edge-bundler/node/config.ts @@ -39,10 +39,8 @@ export const isValidOnError = (value: unknown): value is OnError => { return value === 'fail' || value === 'bypass' || value.startsWith('/') } -export interface FunctionConfig { +interface BaseFunctionConfig { cache?: Cache - path?: Path | Path[] - excludedPath?: Path | Path[] onError?: OnError name?: string generator?: string @@ -50,6 +48,18 @@ export interface FunctionConfig { rateLimit?: RateLimit } +export interface FunctionConfigWithPath extends BaseFunctionConfig { + path?: Path | Path[] + excludedPath?: Path | Path[] +} + +export interface FunctionConfigWithPattern extends BaseFunctionConfig { + pattern: string | string[] + excludedPattern?: string | string[] +} + +export type FunctionConfig = FunctionConfigWithPath | FunctionConfigWithPattern + const getConfigExtractor = () => { const packagePath = getPackagePath() const configExtractorPath = join(packagePath, 'deno', 'config.ts') diff --git a/packages/edge-bundler/node/declaration.ts b/packages/edge-bundler/node/declaration.ts index 622c0d06ae..9dd9fc56a6 100644 --- a/packages/edge-bundler/node/declaration.ts +++ b/packages/edge-bundler/node/declaration.ts @@ -63,7 +63,15 @@ const getDeclarationsFromInput = ( if (!config) { // If no config is found, add the declaration as is. declarations.push(declaration) - } else if (config.path?.length) { + } else if ('pattern' in config && config.pattern?.length) { + // If we have a pattern specified as either a string or non-empty array, + // create a declaration for each pattern. + const patterns = Array.isArray(config.pattern) ? config.pattern : [config.pattern] + + patterns.forEach((pattern) => { + declarations.push({ ...declaration, cache: config.cache, pattern }) + }) + } else if ('path' in config && config.path?.length) { // If we have a path specified as either a string or non-empty array, // create a declaration for each path. const paths = Array.isArray(config.path) ? config.path : [config.path] @@ -74,7 +82,7 @@ const getDeclarationsFromInput = ( } else { // With an in-source config without a path, add the config to the declaration. // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { path, excludedPath, ...rest } = config + const { path, excludedPath, pattern, excludedPattern, ...rest } = config as Record declarations.push({ ...declaration, ...rest }) } @@ -92,25 +100,45 @@ const createDeclarationsFromFunctionConfigs = ( const declarations: Declaration[] = [] for (const name in functionConfigs) { - const { cache, excludedPath, path, method } = functionConfigs[name] + const functionConfig = functionConfigs[name] + const { cache, method } = functionConfigs[name] // If we have a path specified, create a declaration for each path. - if (!functionsVisited.has(name) && path) { - const paths = Array.isArray(path) ? path : [path] - - paths.forEach((singlePath) => { - const declaration: Declaration = { function: name, path: singlePath } - if (cache) { - declaration.cache = cache - } - if (method) { - declaration.method = method - } - if (excludedPath) { - declaration.excludedPath = excludedPath - } - declarations.push(declaration) - }) + if (!functionsVisited.has(name)) { + if ('pattern' in functionConfig && functionConfig.pattern) { + const { pattern, excludedPattern } = functionConfig + const patterns = Array.isArray(pattern) ? pattern : [pattern] + patterns.forEach((singlePattern) => { + const declaration: Declaration = { function: name, pattern: singlePattern } + if (cache) { + declaration.cache = cache + } + if (method) { + declaration.method = method + } + if (excludedPattern) { + declaration.excludedPattern = excludedPattern + } + declarations.push(declaration) + }) + } else if ('path' in functionConfig && functionConfig.path) { + const { path, excludedPath } = functionConfig + const paths = Array.isArray(path) ? path : [path] + + paths.forEach((singlePath) => { + const declaration: Declaration = { function: name, path: singlePath } + if (cache) { + declaration.cache = cache + } + if (method) { + declaration.method = method + } + if (excludedPath) { + declaration.excludedPath = excludedPath + } + declarations.push(declaration) + }) + } } } diff --git a/packages/edge-bundler/node/manifest.ts b/packages/edge-bundler/node/manifest.ts index 023a8af2c2..bb6e0c2c13 100644 --- a/packages/edge-bundler/node/manifest.ts +++ b/packages/edge-bundler/node/manifest.ts @@ -3,7 +3,7 @@ import { join } from 'path' import type { Bundle } from './bundle.js' import { wrapBundleError } from './bundle_error.js' -import { Cache, FunctionConfig, Path } from './config.js' +import { Cache, FunctionConfig, FunctionConfigWithPath, FunctionConfigWithPattern, Path } from './config.js' import { Declaration, normalizePattern } from './declaration.js' import { EdgeFunction } from './edge_function.js' import { FeatureFlags } from './feature_flags.js' @@ -97,7 +97,7 @@ const sanitizeEdgeFunctionConfig = (config: Record): return newConfig } -const addExcludedPatterns = ( +const addManifestExcludedPatternsFromConfigExcludedPath = ( name: string, manifestFunctionConfig: Record, excludedPath?: Path | Path[], @@ -110,6 +110,19 @@ const addExcludedPatterns = ( } } +const addManifestExcludedPatternsFromConfigExcludedPattern = ( + name: string, + manifestFunctionConfig: Record, + excludedPattern?: string | string[], +) => { + if (excludedPattern) { + const excludedPatterns = Array.isArray(excludedPattern) ? excludedPattern : [excludedPattern] + const normalizedExcludedPatterns = excludedPatterns.filter(nonNullable).map(normalizePattern).map(serializePattern) + + manifestFunctionConfig[name].excluded_patterns.push(...normalizedExcludedPatterns) + } +} + /** * Normalizes method names into arrays of uppercase strings. * (e.g. "get" becomes ["GET"]) @@ -144,13 +157,20 @@ const generateManifest = ({ const routedFunctions = new Set() const declarationsWithoutFunction = new Set() - for (const [name, { excludedPath, onError, rateLimit }] of Object.entries(userFunctionConfig)) { + for (const [name, singleUserFunctionConfig] of Object.entries(userFunctionConfig)) { // If the config block is for a function that is not defined, discard it. if (manifestFunctionConfig[name] === undefined) { continue } - addExcludedPatterns(name, manifestFunctionConfig, excludedPath) + const { excludedPath, pattern, excludedPattern, onError, rateLimit } = + singleUserFunctionConfig as FunctionConfigWithPath & FunctionConfigWithPattern + + if (pattern && excludedPattern) { + addManifestExcludedPatternsFromConfigExcludedPattern(name, manifestFunctionConfig, excludedPattern) + } else { + addManifestExcludedPatternsFromConfigExcludedPath(name, manifestFunctionConfig, excludedPath) + } manifestFunctionConfig[name] = { ...manifestFunctionConfig[name], @@ -159,14 +179,21 @@ const generateManifest = ({ } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - for (const [name, { excludedPath, path, onError, rateLimit, ...rest }] of Object.entries(internalFunctionConfig)) { + for (const [name, singleInternalFunctionConfig] of Object.entries(internalFunctionConfig)) { // If the config block is for a function that is not defined, discard it. if (manifestFunctionConfig[name] === undefined) { continue } - addExcludedPatterns(name, manifestFunctionConfig, excludedPath) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { onError, rateLimit, path, excludedPath, pattern, excludedPattern, ...rest } = + singleInternalFunctionConfig as FunctionConfigWithPath & FunctionConfigWithPattern + + if (pattern && excludedPattern) { + addManifestExcludedPatternsFromConfigExcludedPattern(name, manifestFunctionConfig, excludedPattern) + } else { + addManifestExcludedPatternsFromConfigExcludedPath(name, manifestFunctionConfig, excludedPath) + } manifestFunctionConfig[name] = { ...manifestFunctionConfig[name],