UNPKG

firebase-tools

Version:
246 lines (245 loc) 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.KIT_PREFIX = exports.DEFAULT_CODEBASE = void 0; exports.normalize = normalize; exports.validateCodebase = validateCodebase; exports.validateKit = validateKit; exports.validatePrefix = validatePrefix; exports.assertUnique = assertUnique; exports.validateKitInstanceId = validateKitInstanceId; exports.validateAndAddKitInstances = validateAndAddKitInstances; exports.validate = validate; exports.normalizeAndValidate = normalizeAndValidate; exports.configForCodebase = configForCodebase; exports.isLocalConfig = isLocalConfig; exports.isRemoteConfig = isRemoteConfig; exports.isKitConfig = isKitConfig; exports.addKitPrefix = addKitPrefix; exports.requireLocal = requireLocal; exports.resolveConfigDir = resolveConfigDir; exports.shouldUseRuntimeConfig = shouldUseRuntimeConfig; const error_1 = require("../error"); const experiments = require("../experiments"); exports.DEFAULT_CODEBASE = "default"; function normalize(config) { if (!config) { throw new error_1.FirebaseError("No valid functions configuration detected in firebase.json"); } if (Array.isArray(config)) { if (config.length < 1) { throw new error_1.FirebaseError("Requires at least one functions.source in firebase.json."); } return config; } return [config]; } function validateCodebase(codebase) { if (codebase.length === 0 || codebase.length > 63 || !/^[a-z0-9_-]+$/.test(codebase)) { throw new error_1.FirebaseError("Invalid codebase name. Codebase must be less than 64 characters and " + "can contain only lowercase letters, numeric characters, underscores, and dashes."); } } function validateKit(kit) { if (kit.length === 0 || kit.length > 40 || !/^[a-z0-9_-]+$/.test(kit)) { throw new error_1.FirebaseError("Invalid kit name. Kit name must be 40 characters or less and " + "can contain only lowercase letters, numeric characters, underscores, and dashes."); } } function validatePrefix(prefix) { if (prefix.length > 30) { throw new error_1.FirebaseError("Invalid prefix. Prefix must be 30 characters or less."); } if (!/^[a-z](?:[a-z0-9-]*[a-z0-9])?$/.test(prefix)) { throw new error_1.FirebaseError("Invalid prefix. Prefix must start with a lowercase letter, can contain only lowercase letters, numeric characters, and dashes, and cannot start or end with a dash."); } } function validateSingle(config) { if ("kit" in config) { experiments.assertEnabled("kits", "use functions kits"); if (!config.kit) { throw new error_1.FirebaseError("Must specify 'kit' name in a functions kit config."); } validateKit(config.kit); if (config.codebase) { throw new error_1.FirebaseError("Cannot specify both 'kit' and 'codebase' in a single functions config."); } if (config.remoteSource) { throw new error_1.FirebaseError("Cannot specify both 'kit' and 'remoteSource' in a single functions config."); } if (config.prefix) { throw new error_1.FirebaseError("Cannot specify 'prefix' in a functions kit config."); } if (!config.source) { throw new error_1.FirebaseError("Must specify 'source' in a functions kit config."); } if (!config.instances || typeof config.instances !== "object" || Array.isArray(config.instances)) { throw new error_1.FirebaseError("Must specify 'instances' as an object mapping instance IDs to configuration paths in a functions kit config."); } return config; } const { source, remoteSource, runtime, codebase: providedCodebase, ...rest } = config; if (source && remoteSource) { throw new error_1.FirebaseError("Cannot specify both 'source' and 'remoteSource' in a single functions config. Please choose one."); } if (!source && !remoteSource) { throw new error_1.FirebaseError("codebase source must be specified. Must specify either 'source' or 'remoteSource' in a functions config."); } const codebase = providedCodebase ?? exports.DEFAULT_CODEBASE; validateCodebase(codebase); if (config.prefix) { validatePrefix(config.prefix); } const commonConfig = { codebase, ...rest }; if (source) { return { ...commonConfig, source, ...(runtime ? { runtime } : {}), }; } else if (remoteSource) { if (!remoteSource.repository || !remoteSource.ref) { throw new error_1.FirebaseError("remoteSource requires 'repository' and 'ref' to be specified."); } if (!runtime) { throw new error_1.FirebaseError("functions.runtime is required when using remoteSource in firebase.json."); } return { ...commonConfig, remoteSource, runtime, }; } throw new error_1.FirebaseError("Invalid functions config."); } function assertUnique(config, property, propval) { const values = new Set(); if (propval) { values.add(propval); } for (const single of config) { const value = single[property]; if (value !== undefined) { if (values.has(value)) { throw new error_1.FirebaseError(`functions.${String(property)} must be unique but '${String(value)}' was used more than once.`); } values.add(value); } } } function assertUniqueSourcePrefixPair(config) { const sourcePrefixPairs = new Set(); for (const c of config) { let sourceIdentifier; let sourceDescription; if (c.source) { sourceIdentifier = c.source; sourceDescription = `source directory ('${c.source}')`; } else if (c.remoteSource) { sourceIdentifier = `remote:${c.remoteSource.repository}#${c.remoteSource.ref}@dir:${c.remoteSource.dir || "."}`; sourceDescription = `remote source ('${c.remoteSource.repository}')`; } else { continue; } const key = JSON.stringify({ source: sourceIdentifier, prefix: c.prefix || "" }); if (sourcePrefixPairs.has(key)) { throw new error_1.FirebaseError(`More than one functions config specifies the same ${sourceDescription} and prefix ('${c.prefix ?? ""}'). Please add a unique 'prefix' to each function configuration that shares this source to resolve the conflict.`); } sourcePrefixPairs.add(key); } } function validateKitInstanceId(instanceId) { if (instanceId.length === 0 || instanceId.length > 40 || !/^[a-z0-9_](?:[a-z0-9_-]*[a-z0-9_])?$/.test(instanceId)) { throw new error_1.FirebaseError(`Invalid kit instance ID '${instanceId}'. Instance ID must be 40 characters or less, ` + "can contain only lowercase letters, numeric characters, underscores, and dashes, and cannot start or end with a dash."); } } function validateAndAddKitInstances(instanceIds, allProjectInstanceIds) { const seenInBatch = new Set(); for (const instanceId of instanceIds) { validateKitInstanceId(instanceId); if (seenInBatch.has(instanceId) || allProjectInstanceIds.has(instanceId)) { throw new error_1.FirebaseError(`functions kit instance ID must be unique across all kits, but '${instanceId}' was used more than once.`); } seenInBatch.add(instanceId); } for (const instanceId of seenInBatch) { allProjectInstanceIds.add(instanceId); } } function assertUniqueKitInstancesAndCodebases(config) { const codebases = new Set(); const instanceIds = new Set(); for (const c of config) { if ("codebase" in c && c.codebase) { codebases.add(c.codebase); } if ("instances" in c && c.instances) { validateAndAddKitInstances(Object.keys(c.instances), instanceIds); } } for (const instanceId of instanceIds) { if (codebases.has(instanceId)) { throw new error_1.FirebaseError(`functions codebase name and kit instance ID must be mutually exclusive, but '${instanceId}' was used as both a codebase name and a kit instance ID.`); } } } function validate(config) { const validated = config.map((cfg) => validateSingle(cfg)); assertUnique(validated, "codebase"); assertUnique(validated, "kit"); assertUniqueSourcePrefixPair(validated); assertUniqueKitInstancesAndCodebases(validated); return validated; } function normalizeAndValidate(config) { return validate(normalize(config)); } function configForCodebase(config, codebaseOrInstance) { const codebaseCfg = config.find((c) => { if (isKitConfig(c)) { return c.instances && codebaseOrInstance in c.instances; } return c.codebase === codebaseOrInstance; }); if (!codebaseCfg) { throw new error_1.FirebaseError(`No functions config found for codebase or kit instance ${codebaseOrInstance}`); } return codebaseCfg; } function isLocalConfig(c) { return "source" in c && !("kit" in c); } function isRemoteConfig(c) { return "remoteSource" in c; } function isKitConfig(c) { return "kit" in c; } exports.KIT_PREFIX = "kit-"; function addKitPrefix(instanceId) { return `${exports.KIT_PREFIX}${instanceId}`; } function requireLocal(c, purpose) { if (isRemoteConfig(c)) { const msg = purpose ?? "This operation requires a local functions source directory, but the codebase is configured with a remote source."; throw new error_1.FirebaseError(msg); } return c; } function resolveConfigDir(c, instanceId) { if (isKitConfig(c)) { return instanceId ? c.instances[instanceId] : undefined; } return c.configDir || (isLocalConfig(c) ? c.source : undefined); } function shouldUseRuntimeConfig(cfg) { return isLocalConfig(cfg) && cfg.disallowLegacyRuntimeConfig !== true; }