firebase-tools 11.17.0 → 11.19.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.
Files changed (51) hide show
  1. package/lib/api.js +3 -2
  2. package/lib/commands/index.js +5 -0
  3. package/lib/commands/internaltesting-functions-discover.js +25 -0
  4. package/lib/deploy/extensions/prepare.js +6 -1
  5. package/lib/deploy/extensions/v2FunctionHelper.js +53 -0
  6. package/lib/deploy/functions/build.js +17 -2
  7. package/lib/deploy/functions/cel.js +90 -13
  8. package/lib/deploy/functions/params.js +141 -6
  9. package/lib/deploy/functions/prepare.js +40 -25
  10. package/lib/deploy/functions/release/fabricator.js +27 -1
  11. package/lib/deploy/functions/runtimes/discovery/parsing.js +7 -1
  12. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +1 -1
  13. package/lib/deploy/functions/runtimes/index.js +2 -1
  14. package/lib/deploy/functions/runtimes/node/index.js +2 -2
  15. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +1 -0
  16. package/lib/deploy/functions/runtimes/node/versioning.js +30 -14
  17. package/lib/emulator/commandUtils.js +2 -1
  18. package/lib/emulator/downloadableEmulators.js +3 -3
  19. package/lib/emulator/env.js +29 -27
  20. package/lib/emulator/extensions/validation.js +2 -0
  21. package/lib/emulator/extensionsEmulator.js +1 -1
  22. package/lib/emulator/functionsEmulator.js +44 -32
  23. package/lib/emulator/storage/rules/runtime.js +2 -2
  24. package/lib/emulator/workQueue.js +11 -6
  25. package/lib/experiments.js +6 -0
  26. package/lib/extensions/billingMigrationHelper.js +2 -1
  27. package/lib/extensions/displayExtensionInfo.js +2 -1
  28. package/lib/extensions/emulator/specHelper.js +4 -3
  29. package/lib/extensions/emulator/triggerHelper.js +64 -20
  30. package/lib/extensions/extensionsHelper.js +1 -4
  31. package/lib/extensions/types.js +2 -1
  32. package/lib/extensions/utils.js +14 -1
  33. package/lib/firestore/indexes-api.js +7 -1
  34. package/lib/firestore/indexes-sort.js +4 -0
  35. package/lib/firestore/indexes.js +31 -5
  36. package/lib/firestore/util.js +5 -1
  37. package/lib/firestore/validator.js +7 -1
  38. package/lib/frameworks/angular/index.js +3 -0
  39. package/lib/frameworks/index.js +7 -1
  40. package/lib/frameworks/next/constants.js +10 -0
  41. package/lib/frameworks/next/index.js +170 -127
  42. package/lib/frameworks/next/interfaces.js +2 -0
  43. package/lib/frameworks/next/utils.js +92 -0
  44. package/lib/frameworks/nuxt/index.js +3 -0
  45. package/lib/frameworks/utils.js +24 -0
  46. package/lib/frameworks/vite/index.js +4 -1
  47. package/lib/gcp/eventarc.js +42 -0
  48. package/lib/init/features/emulators.js +1 -1
  49. package/npm-shrinkwrap.json +2 -93
  50. package/package.json +1 -4
  51. package/schema/firebase-config.json +4 -2
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveCpuAndConcurrency = exports.inferBlockingDetails = exports.updateEndpointTargetedStatus = exports.inferDetailsFromExisting = exports.prepare = exports.EVENTARC_SOURCE_ENV = void 0;
3
+ exports.loadCodebases = exports.resolveCpuAndConcurrency = exports.inferBlockingDetails = exports.updateEndpointTargetedStatus = exports.inferDetailsFromExisting = exports.prepare = exports.EVENTARC_SOURCE_ENV = void 0;
4
4
  const clc = require("colorette");
5
5
  const backend = require("./backend");
6
6
  const build = require("./build");
@@ -31,7 +31,7 @@ function hasUserConfig(config) {
31
31
  return Object.keys(config).length > 1;
32
32
  }
33
33
  async function prepare(context, options, payload) {
34
- var _a;
34
+ var _a, _b;
35
35
  const projectId = (0, projectUtils_1.needProjectId)(options);
36
36
  const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
37
37
  context.config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
@@ -40,6 +40,9 @@ async function prepare(context, options, payload) {
40
40
  if (codebases.length === 0) {
41
41
  throw new error_1.FirebaseError("No function matches given --only filters. Aborting deployment.");
42
42
  }
43
+ for (const codebase of codebases) {
44
+ (0, utils_1.logLabeledBullet)("functions", `preparing codebase ${clc.bold(codebase)} for deployment`);
45
+ }
43
46
  const checkAPIsEnabled = await Promise.all([
44
47
  ensureApiEnabled.ensure(projectId, "cloudfunctions.googleapis.com", "functions"),
45
48
  ensureApiEnabled.check(projectId, "runtimeconfig.googleapis.com", "runtimeconfig", true),
@@ -52,45 +55,28 @@ async function prepare(context, options, payload) {
52
55
  if (checkAPIsEnabled[1]) {
53
56
  runtimeConfig = Object.assign(Object.assign({}, runtimeConfig), (await (0, prepareFunctionsUpload_1.getFunctionsConfig)(projectId)));
54
57
  }
55
- context.sources = {};
58
+ const wantBuilds = await loadCodebases(context.config, options, firebaseConfig, runtimeConfig, context.filters);
56
59
  const codebaseUsesEnvs = [];
57
60
  const wantBackends = {};
58
- for (const codebase of codebases) {
59
- (0, utils_1.logLabeledBullet)("functions", `preparing codebase ${clc.bold(codebase)} for deployment`);
61
+ for (const [codebase, wantBuild] of Object.entries(wantBuilds)) {
60
62
  const config = (0, projectConfig_1.configForCodebase)(context.config, codebase);
61
- const sourceDirName = config.source;
62
- if (!sourceDirName) {
63
- throw new error_1.FirebaseError(`No functions code detected at default location (./functions), and no functions source defined in firebase.json`);
64
- }
65
- const sourceDir = options.config.path(sourceDirName);
66
- const delegateContext = {
67
- projectId,
68
- sourceDir,
69
- projectDir: options.config.projectDir,
70
- runtime: config.runtime || "",
71
- };
72
- const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
73
- logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
74
- await runtimeDelegate.validate();
75
- logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
76
- await runtimeDelegate.build();
77
63
  const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
78
64
  const userEnvOpt = {
79
- functionsSource: sourceDir,
65
+ functionsSource: options.config.path(config.source),
80
66
  projectId: projectId,
81
67
  projectAlias: options.projectAlias,
82
68
  };
83
69
  const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
84
70
  const envs = Object.assign(Object.assign({}, userEnvs), firebaseEnvs);
85
- const wantBuild = await runtimeDelegate.discoverBuild(runtimeConfig, firebaseEnvs);
86
71
  const { backend: wantBackend, envs: resolvedEnvs } = await build.resolveBackend(wantBuild, firebaseConfig, userEnvOpt, userEnvs, options.nonInteractive);
87
72
  let hasEnvsFromParams = false;
88
73
  wantBackend.environmentVariables = envs;
89
74
  for (const envName of Object.keys(resolvedEnvs)) {
90
- const envValue = (_a = resolvedEnvs[envName]) === null || _a === void 0 ? void 0 : _a.toString();
75
+ const isList = (_a = resolvedEnvs[envName]) === null || _a === void 0 ? void 0 : _a.legalList;
76
+ const envValue = (_b = resolvedEnvs[envName]) === null || _b === void 0 ? void 0 : _b.toSDK();
91
77
  if (envValue &&
92
78
  !resolvedEnvs[envName].internal &&
93
- !Object.prototype.hasOwnProperty.call(wantBackend.environmentVariables, envName)) {
79
+ (!Object.prototype.hasOwnProperty.call(wantBackend.environmentVariables, envName) || isList)) {
94
80
  wantBackend.environmentVariables[envName] = envValue;
95
81
  hasEnvsFromParams = true;
96
82
  }
@@ -127,6 +113,7 @@ async function prepare(context, options, payload) {
127
113
  }
128
114
  }
129
115
  validate.endpointsAreUnique(wantBackends);
116
+ context.sources = {};
130
117
  for (const [codebase, wantBackend] of Object.entries(wantBackends)) {
131
118
  const config = (0, projectConfig_1.configForCodebase)(context.config, codebase);
132
119
  const sourceDirName = config.source;
@@ -291,3 +278,31 @@ function resolveCpuAndConcurrency(want) {
291
278
  }
292
279
  }
293
280
  exports.resolveCpuAndConcurrency = resolveCpuAndConcurrency;
281
+ async function loadCodebases(config, options, firebaseConfig, runtimeConfig, filters) {
282
+ const codebases = (0, functionsDeployHelper_1.targetCodebases)(config, filters);
283
+ const projectId = (0, projectUtils_1.needProjectId)(options);
284
+ const wantBuilds = {};
285
+ for (const codebase of codebases) {
286
+ const codebaseConfig = (0, projectConfig_1.configForCodebase)(config, codebase);
287
+ const sourceDirName = codebaseConfig.source;
288
+ if (!sourceDirName) {
289
+ throw new error_1.FirebaseError(`No functions code detected at default location (./functions), and no functions source defined in firebase.json`);
290
+ }
291
+ const sourceDir = options.config.path(sourceDirName);
292
+ const delegateContext = {
293
+ projectId,
294
+ sourceDir,
295
+ projectDir: options.config.projectDir,
296
+ runtime: codebaseConfig.runtime || "",
297
+ };
298
+ const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
299
+ logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
300
+ await runtimeDelegate.validate();
301
+ logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
302
+ await runtimeDelegate.build();
303
+ const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
304
+ wantBuilds[codebase] = await runtimeDelegate.discoverBuild(runtimeConfig, firebaseEnvs);
305
+ }
306
+ return wantBuilds;
307
+ }
308
+ exports.loadCodebases = loadCodebases;
@@ -14,6 +14,7 @@ const cloudtasks = require("../../../gcp/cloudtasks");
14
14
  const deploymentTool = require("../../../deploymentTool");
15
15
  const gcf = require("../../../gcp/cloudfunctions");
16
16
  const gcfV2 = require("../../../gcp/cloudfunctionsv2");
17
+ const eventarc = require("../../../gcp/eventarc");
17
18
  const helper = require("../functionsDeployHelper");
18
19
  const poller = require("../../../operation-poller");
19
20
  const pubsub = require("../../../gcp/pubsub");
@@ -36,6 +37,12 @@ const gcfV2PollerOptions = {
36
37
  masterTimeout: 25 * 60 * 1000,
37
38
  maxBackoff: 10000,
38
39
  };
40
+ const eventarcPollerOptions = {
41
+ apiOrigin: api_1.eventarcOrigin,
42
+ apiVersion: "v1",
43
+ masterTimeout: 25 * 60 * 1000,
44
+ maxBackoff: 10000,
45
+ };
39
46
  const rethrowAs = (endpoint, op) => (err) => {
40
47
  logger_1.logger.error(err.message);
41
48
  throw new reporter.DeploymentError(endpoint, op, err);
@@ -212,7 +219,7 @@ class Fabricator {
212
219
  }
213
220
  }
214
221
  async createV2Function(endpoint) {
215
- var _a, _b;
222
+ var _a, _b, _c;
216
223
  const storage = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
217
224
  if (!storage) {
218
225
  logger_1.logger.debug("Precondition failed. Cannot create a GCFv2 function without storage");
@@ -237,6 +244,25 @@ class Fabricator {
237
244
  })
238
245
  .catch(rethrowAs(endpoint, "create topic"));
239
246
  }
247
+ const channel = (_c = apiFunction.eventTrigger) === null || _c === void 0 ? void 0 : _c.channel;
248
+ if (channel) {
249
+ await this.executor
250
+ .run(async () => {
251
+ try {
252
+ const op = await eventarc.createChannel({ name: channel });
253
+ return await poller.pollOperation(Object.assign(Object.assign({}, eventarcPollerOptions), { pollerName: `create-${channel}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
254
+ }
255
+ catch (err) {
256
+ if (err.status === 409) {
257
+ return;
258
+ }
259
+ throw new error_1.FirebaseError("Unexpected error creating Eventarc channel", {
260
+ original: err,
261
+ });
262
+ }
263
+ })
264
+ .catch(rethrowAs(endpoint, "upsert eventarc channel"));
265
+ }
240
266
  const resultFunction = await this.functionExecutor
241
267
  .run(async () => {
242
268
  const op = await gcfV2.createFunction(apiFunction);
@@ -8,7 +8,7 @@ function requireKeys(prefix, yaml, ...keys) {
8
8
  }
9
9
  for (const key of keys) {
10
10
  if (!yaml[key]) {
11
- throw new error_1.FirebaseError(`Expected key ${prefix + key}`);
11
+ throw new error_1.FirebaseError(`Expected key ${prefix + key.toString()}`);
12
12
  }
13
13
  }
14
14
  }
@@ -47,6 +47,12 @@ function assertKeyTypes(prefix, yaml, schema) {
47
47
  }
48
48
  continue;
49
49
  }
50
+ if (schemaType === "List") {
51
+ if (typeof value !== "string" && !Array.isArray(value)) {
52
+ throw new error_1.FirebaseError(`Expected ${fullKey} to be a field list (array or list expression); was ${typeof value}`);
53
+ }
54
+ continue;
55
+ }
50
56
  if (value === null) {
51
57
  if (schemaType.endsWith("?")) {
52
58
  continue;
@@ -48,7 +48,7 @@ function parseRequiredAPIs(manifest) {
48
48
  function assertBuildEndpoint(ep, id) {
49
49
  const prefix = `endpoints[${id}]`;
50
50
  (0, parsing_1.assertKeyTypes)(prefix, ep, {
51
- region: "array",
51
+ region: "List",
52
52
  platform: (platform) => build.AllFunctionsPlatforms.includes(platform),
53
53
  entryPoint: "string",
54
54
  omit: "Field<boolean>?",
@@ -4,7 +4,7 @@ exports.getRuntimeDelegate = exports.getHumanFriendlyRuntimeName = exports.isVal
4
4
  const node = require("./node");
5
5
  const validate = require("../validate");
6
6
  const error_1 = require("../../../error");
7
- const RUNTIMES = ["nodejs10", "nodejs12", "nodejs14", "nodejs16"];
7
+ const RUNTIMES = ["nodejs10", "nodejs12", "nodejs14", "nodejs16", "nodejs18"];
8
8
  const EXPERIMENTAL_RUNTIMES = ["go113"];
9
9
  const DEPRECATED_RUNTIMES = ["nodejs6", "nodejs8"];
10
10
  function isDeprecatedRuntime(runtime) {
@@ -22,6 +22,7 @@ const MESSAGE_FRIENDLY_RUNTIMES = {
22
22
  nodejs12: "Node.js 12",
23
23
  nodejs14: "Node.js 14",
24
24
  nodejs16: "Node.js 16",
25
+ nodejs18: "Node.js 18",
25
26
  go113: "Go 1.13",
26
27
  };
27
28
  function getHumanFriendlyRuntimeName(runtime) {
@@ -38,10 +38,10 @@ class Delegate {
38
38
  this.sourceDir = sourceDir;
39
39
  this.runtime = runtime;
40
40
  this.name = "nodejs";
41
- this._sdkVersion = "";
41
+ this._sdkVersion = undefined;
42
42
  }
43
43
  get sdkVersion() {
44
- if (!this._sdkVersion) {
44
+ if (this._sdkVersion === undefined) {
45
45
  this._sdkVersion = versioning.getFunctionsSDKVersion(this.sourceDir) || "";
46
46
  }
47
47
  return this._sdkVersion;
@@ -14,6 +14,7 @@ const ENGINE_RUNTIMES = {
14
14
  12: "nodejs12",
15
15
  14: "nodejs14",
16
16
  16: "nodejs16",
17
+ 18: "nodejs18",
17
18
  };
18
19
  const ENGINE_RUNTIMES_NAMES = Object.values(ENGINE_RUNTIMES);
19
20
  exports.RUNTIME_NOT_SET = "`runtime` field is required but was not found in firebase.json.\n" +
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkFunctionsSDKVersion = exports.getLatestSDKVersion = exports.getFunctionsSDKVersion = exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING = void 0;
4
- const _ = require("lodash");
3
+ exports.checkFunctionsSDKVersion = exports.getLatestSDKVersion = exports.getFunctionsSDKVersion = exports.findModuleVersion = exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING = void 0;
4
+ const fs = require("fs");
5
+ const path = require("path");
5
6
  const clc = require("colorette");
6
7
  const semver = require("semver");
7
8
  const spawn = require("cross-spawn");
@@ -15,26 +16,41 @@ exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING = clc.bold(clc.yellow("functions:
15
16
  " version that is at least 2.0.0. Please run " +
16
17
  clc.bold("npm i --save firebase-functions@latest") +
17
18
  " in the functions folder.";
18
- function getFunctionsSDKVersion(sourceDir) {
19
- try {
20
- const child = spawn.sync("npm", ["list", "firebase-functions", "--json=true"], {
21
- cwd: sourceDir,
22
- encoding: "utf8",
23
- });
24
- if (child.error) {
25
- logger_1.logger.debug("getFunctionsSDKVersion encountered error:", child.error.stack);
19
+ function findModuleVersion(name, resolvedPath) {
20
+ let searchPath = path.dirname(resolvedPath);
21
+ while (true) {
22
+ if (searchPath === "/" || path.basename(searchPath) === "node_modules") {
23
+ logger_1.logger.debug(`Failed to find version of module ${name}: reached end of search path ${searchPath}`);
26
24
  return;
27
25
  }
28
- const output = JSON.parse(child.stdout);
29
- return _.get(output, ["dependencies", "firebase-functions", "version"]);
26
+ const maybePackageJson = path.join(searchPath, "package.json");
27
+ if (fs.existsSync(maybePackageJson)) {
28
+ const pkg = require(maybePackageJson);
29
+ if (pkg.name === name) {
30
+ return pkg.version;
31
+ }
32
+ logger_1.logger.debug(`Failed to find version of module ${name}: instead found ${pkg.name} at ${searchPath}`);
33
+ return;
34
+ }
35
+ searchPath = path.dirname(searchPath);
36
+ }
37
+ }
38
+ exports.findModuleVersion = findModuleVersion;
39
+ function getFunctionsSDKVersion(sourceDir) {
40
+ try {
41
+ return findModuleVersion("firebase-functions", require.resolve("firebase-functions", { paths: [sourceDir] }));
30
42
  }
31
43
  catch (e) {
44
+ if (e.code === "MODULE_NOT_FOUND") {
45
+ utils.logLabeledWarning("functions", "Couldn't find firebase-functions package in your source code. Have you run 'npm install'?");
46
+ }
32
47
  logger_1.logger.debug("getFunctionsSDKVersion encountered error:", e);
33
48
  return;
34
49
  }
35
50
  }
36
51
  exports.getFunctionsSDKVersion = getFunctionsSDKVersion;
37
52
  function getLatestSDKVersion() {
53
+ var _a;
38
54
  const child = spawn.sync("npm", ["show", "firebase-functions", "--json=true"], {
39
55
  encoding: "utf8",
40
56
  });
@@ -43,10 +59,10 @@ function getLatestSDKVersion() {
43
59
  return;
44
60
  }
45
61
  const output = JSON.parse(child.stdout);
46
- if (_.isEmpty(output)) {
62
+ if (Object.keys(output).length === 0) {
47
63
  return;
48
64
  }
49
- return _.get(output, ["dist-tags", "latest"]);
65
+ return (_a = output["dist-tags"]) === null || _a === void 0 ? void 0 : _a["latest"];
50
66
  }
51
67
  exports.getLatestSDKVersion = getLatestSDKVersion;
52
68
  function checkFunctionsSDKVersion(currentVersion) {
@@ -219,7 +219,8 @@ exports.shutdownWhenKilled = shutdownWhenKilled;
219
219
  async function runScript(script, extraEnv) {
220
220
  utils.logBullet(`Running script: ${clc.bold(script)}`);
221
221
  const env = Object.assign(Object.assign({}, process.env), extraEnv);
222
- (0, env_1.setEnvVarsForEmulators)(env);
222
+ const emulatorInfos = registry_1.EmulatorRegistry.listRunningWithInfo();
223
+ (0, env_1.setEnvVarsForEmulators)(env, emulatorInfos);
223
224
  const proc = childProcess.spawn(script, {
224
225
  stdio: ["inherit", "inherit", "inherit"],
225
226
  shell: true,
@@ -28,9 +28,9 @@ const EMULATOR_UPDATE_DETAILS = {
28
28
  expectedChecksum: "4f41d24a3c0f3b55ea22804a424cc0ee",
29
29
  },
30
30
  storage: {
31
- version: "1.1.1",
32
- expectedSize: 46448285,
33
- expectedChecksum: "691982db4019d49d345a97151bdea7e2",
31
+ version: "1.1.2",
32
+ expectedSize: 47028740,
33
+ expectedChecksum: "983b4415b1e72b109864f1b8e7ea7546",
34
34
  },
35
35
  ui: experiments.isEnabled("emulatoruisnapshot")
36
36
  ? { version: "SNAPSHOT", expectedSize: -1, expectedChecksum: "" }
@@ -3,33 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setEnvVarsForEmulators = void 0;
4
4
  const constants_1 = require("./constants");
5
5
  const types_1 = require("./types");
6
- const registry_1 = require("./registry");
7
- function setEnvVarsForEmulators(env) {
8
- if (registry_1.EmulatorRegistry.isRunning(types_1.Emulators.DATABASE)) {
9
- env[constants_1.Constants.FIREBASE_DATABASE_EMULATOR_HOST] = registry_1.EmulatorRegistry.url(types_1.Emulators.DATABASE).host;
10
- }
11
- if (registry_1.EmulatorRegistry.isRunning(types_1.Emulators.FIRESTORE)) {
12
- const { host } = registry_1.EmulatorRegistry.url(types_1.Emulators.FIRESTORE);
13
- env[constants_1.Constants.FIRESTORE_EMULATOR_HOST] = host;
14
- env[constants_1.Constants.FIRESTORE_EMULATOR_ENV_ALT] = host;
15
- }
16
- if (registry_1.EmulatorRegistry.isRunning(types_1.Emulators.STORAGE)) {
17
- const { host } = registry_1.EmulatorRegistry.url(types_1.Emulators.STORAGE);
18
- env[constants_1.Constants.FIREBASE_STORAGE_EMULATOR_HOST] = host;
19
- env[constants_1.Constants.CLOUD_STORAGE_EMULATOR_HOST] = `http://${host}`;
20
- }
21
- if (registry_1.EmulatorRegistry.isRunning(types_1.Emulators.AUTH)) {
22
- env[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST] = registry_1.EmulatorRegistry.url(types_1.Emulators.AUTH).host;
23
- }
24
- if (registry_1.EmulatorRegistry.isRunning(types_1.Emulators.HUB)) {
25
- env[constants_1.Constants.FIREBASE_EMULATOR_HUB] = registry_1.EmulatorRegistry.url(types_1.Emulators.HUB).host;
26
- }
27
- const pubsubEmulator = registry_1.EmulatorRegistry.isRunning(types_1.Emulators.PUBSUB);
28
- if (pubsubEmulator) {
29
- env[constants_1.Constants.PUBSUB_EMULATOR_HOST] = registry_1.EmulatorRegistry.url(types_1.Emulators.PUBSUB).host;
30
- }
31
- if (registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EVENTARC)) {
32
- env[constants_1.Constants.CLOUD_EVENTARC_EMULATOR_HOST] = `http://${registry_1.EmulatorRegistry.url(types_1.Emulators.EVENTARC).host}`;
6
+ const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
7
+ function setEnvVarsForEmulators(env, emulators) {
8
+ for (const emu of emulators) {
9
+ const host = (0, functionsEmulatorShared_1.formatHost)(emu);
10
+ switch (emu.name) {
11
+ case types_1.Emulators.FIRESTORE:
12
+ env[constants_1.Constants.FIRESTORE_EMULATOR_HOST] = host;
13
+ env[constants_1.Constants.FIRESTORE_EMULATOR_ENV_ALT] = host;
14
+ break;
15
+ case types_1.Emulators.DATABASE:
16
+ env[constants_1.Constants.FIREBASE_DATABASE_EMULATOR_HOST] = host;
17
+ break;
18
+ case types_1.Emulators.STORAGE:
19
+ env[constants_1.Constants.FIREBASE_STORAGE_EMULATOR_HOST] = host;
20
+ env[constants_1.Constants.CLOUD_STORAGE_EMULATOR_HOST] = `http://${host}`;
21
+ break;
22
+ case types_1.Emulators.AUTH:
23
+ env[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST] = host;
24
+ break;
25
+ case types_1.Emulators.HUB:
26
+ env[constants_1.Constants.FIREBASE_EMULATOR_HUB] = host;
27
+ break;
28
+ case types_1.Emulators.PUBSUB:
29
+ env[constants_1.Constants.PUBSUB_EMULATOR_HOST] = host;
30
+ break;
31
+ case types_1.Emulators.EVENTARC:
32
+ env[constants_1.Constants.CLOUD_EVENTARC_EMULATOR_HOST] = `http://${host}`;
33
+ break;
34
+ }
33
35
  }
34
36
  }
35
37
  exports.setEnvVarsForEmulators = setEnvVarsForEmulators;
@@ -59,6 +59,8 @@ function checkForUnemulatedTriggerTypes(backend, options) {
59
59
  return !(0, controller_1.shouldStart)(options, types_1.Emulators.AUTH);
60
60
  case constants_1.Constants.SERVICE_STORAGE:
61
61
  return !(0, controller_1.shouldStart)(options, types_1.Emulators.STORAGE);
62
+ case constants_1.Constants.SERVICE_EVENTARC:
63
+ return !(0, controller_1.shouldStart)(options, types_1.Emulators.EVENTARC);
62
64
  default:
63
65
  return true;
64
66
  }
@@ -142,7 +142,7 @@ class ExtensionsEmulator {
142
142
  const emulatableBackend = {
143
143
  functionsDir,
144
144
  env: nonSecretEnv,
145
- codebase: "",
145
+ codebase: instance.instanceId,
146
146
  secretEnv: secretEnvVariables,
147
147
  predefinedTriggers: extensionTriggers,
148
148
  nodeMajorVersion: nodeMajorVersion,
@@ -107,9 +107,11 @@ class FunctionsEmulator {
107
107
  const httpsFunctionRoutes = [httpsFunctionRoute, `${httpsFunctionRoute}/*`];
108
108
  const listBackendsRoute = `/backends`;
109
109
  const httpsHandler = (req, res) => {
110
- this.workQueue.submit(() => {
110
+ const work = () => {
111
111
  return this.handleHttpsTrigger(req, res);
112
- });
112
+ };
113
+ work.type = `${req.path}-${new Date().toISOString()}`;
114
+ this.workQueue.submit(work);
113
115
  };
114
116
  const multicastHandler = (req, res) => {
115
117
  var _a;
@@ -129,7 +131,7 @@ class FunctionsEmulator {
129
131
  const triggers = this.multicastTriggers[triggerKey] || [];
130
132
  const { host, port } = this.getInfo();
131
133
  triggers.forEach((triggerId) => {
132
- this.workQueue.submit(() => {
134
+ const work = () => {
133
135
  return new Promise((resolve, reject) => {
134
136
  const trigReq = http.request({
135
137
  host: (0, utils_1.connectableHostname)(host),
@@ -143,7 +145,9 @@ class FunctionsEmulator {
143
145
  trigReq.end();
144
146
  resolve();
145
147
  });
146
- });
148
+ };
149
+ work.type = `${triggerId}-${new Date().toISOString()}`;
150
+ this.workQueue.submit(work);
147
151
  });
148
152
  res.json({ status: "multicast_acknowledged" });
149
153
  };
@@ -262,7 +266,6 @@ class FunctionsEmulator {
262
266
  await runtimeDelegate.validate();
263
267
  logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
264
268
  await runtimeDelegate.build();
265
- logger_1.logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
266
269
  const firebaseConfig = this.getFirebaseConfig();
267
270
  const environment = Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: firebaseConfig }), emulatableBackend.env);
268
271
  const userEnvOpt = {
@@ -270,8 +273,9 @@ class FunctionsEmulator {
270
273
  projectId: this.args.projectId,
271
274
  projectAlias: this.args.projectAlias,
272
275
  };
276
+ const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
273
277
  const discoveredBuild = await runtimeDelegate.discoverBuild(runtimeConfig, environment);
274
- const resolution = await (0, build_1.resolveBackend)(discoveredBuild, JSON.parse(firebaseConfig), userEnvOpt, environment);
278
+ const resolution = await (0, build_1.resolveBackend)(discoveredBuild, JSON.parse(firebaseConfig), userEnvOpt, userEnvs);
275
279
  const discoveredBackend = resolution.backend;
276
280
  const endpoints = backend.allEndpoints(discoveredBackend);
277
281
  (0, functionsEmulatorShared_1.prepareEndpoints)(endpoints);
@@ -377,6 +381,12 @@ class FunctionsEmulator {
377
381
  }
378
382
  }
379
383
  if (this.args.debugPort) {
384
+ emulatableBackend.secretEnv = Object.values(toSetup.reduce((acc, curr) => {
385
+ for (const secret of curr.secretEnvironmentVariables || []) {
386
+ acc[secret.key] = secret;
387
+ }
388
+ return acc;
389
+ }, {}));
380
390
  await this.startRuntime(emulatableBackend);
381
391
  }
382
392
  }
@@ -711,7 +721,11 @@ class FunctionsEmulator {
711
721
  skipTokenVerification: true,
712
722
  enableCors: true,
713
723
  });
714
- (0, env_1.setEnvVarsForEmulators)(envs);
724
+ let emulatorInfos = registry_1.EmulatorRegistry.listRunningWithInfo();
725
+ if (this.args.remoteEmulators) {
726
+ emulatorInfos = emulatorInfos.concat(Object.values(this.args.remoteEmulators));
727
+ }
728
+ (0, env_1.setEnvVarsForEmulators)(envs, emulatorInfos);
715
729
  if (this.args.debugPort) {
716
730
  envs["FUNCTION_DEBUG_MODE"] = "true";
717
731
  }
@@ -749,34 +763,32 @@ class FunctionsEmulator {
749
763
  this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${secretPath}: ${e.message}`);
750
764
  }
751
765
  }
752
- if (trigger) {
753
- const secrets = trigger.secretEnvironmentVariables || [];
754
- const accesses = secrets
755
- .filter((s) => !secretEnvs[s.key])
756
- .map(async (s) => {
757
- var _a;
758
- this.logger.logLabeled("INFO", "functions", `Trying to access secret ${s.secret}@latest`);
759
- const value = await (0, secretManager_1.accessSecretVersion)(this.getProjectId(), s.secret, (_a = s.version) !== null && _a !== void 0 ? _a : "latest");
760
- return [s.key, value];
761
- });
762
- const accessResults = await (0, utils_1.allSettled)(accesses);
763
- const errs = [];
764
- for (const result of accessResults) {
765
- if (result.status === "rejected") {
766
- errs.push(result.reason);
767
- }
768
- else {
769
- const [k, v] = result.value;
770
- secretEnvs[k] = v;
771
- }
766
+ const secrets = (trigger === null || trigger === void 0 ? void 0 : trigger.secretEnvironmentVariables) || backend.secretEnv;
767
+ const accesses = secrets
768
+ .filter((s) => !secretEnvs[s.key])
769
+ .map(async (s) => {
770
+ var _a;
771
+ this.logger.logLabeled("INFO", "functions", `Trying to access secret ${s.secret}@latest`);
772
+ const value = await (0, secretManager_1.accessSecretVersion)(this.getProjectId(), s.secret, (_a = s.version) !== null && _a !== void 0 ? _a : "latest");
773
+ return [s.key, value];
774
+ });
775
+ const accessResults = await (0, utils_1.allSettled)(accesses);
776
+ const errs = [];
777
+ for (const result of accessResults) {
778
+ if (result.status === "rejected") {
779
+ errs.push(result.reason);
772
780
  }
773
- if (errs.length > 0) {
774
- this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
775
- "Make sure the credential used for the Functions Emulator have access " +
776
- `or provide override values in ${secretPath}:\n\t` +
777
- errs.join("\n\t"));
781
+ else {
782
+ const [k, v] = result.value;
783
+ secretEnvs[k] = v;
778
784
  }
779
785
  }
786
+ if (errs.length > 0) {
787
+ this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
788
+ "Make sure the credential used for the Functions Emulator have access " +
789
+ `or provide override values in ${secretPath}:\n\t` +
790
+ errs.join("\n\t"));
791
+ }
780
792
  return secretEnvs;
781
793
  }
782
794
  async startRuntime(backend, trigger) {
@@ -283,7 +283,7 @@ function toExpressionValue(obj) {
283
283
  }
284
284
  if (obj == null) {
285
285
  return {
286
- null_value: 0,
286
+ null_value: null,
287
287
  };
288
288
  }
289
289
  if (typeof obj === "object") {
@@ -339,7 +339,7 @@ function createRequestExpressionValue(opts) {
339
339
  },
340
340
  time: toExpressionValue(new Date()),
341
341
  resource: toExpressionValue(opts.file.after ? opts.file.after : null),
342
- auth: opts.token ? createAuthExpressionValue(opts) : { null_value: 0 },
342
+ auth: opts.token ? createAuthExpressionValue(opts) : { null_value: null },
343
343
  };
344
344
  return {
345
345
  map_value: {
@@ -11,7 +11,7 @@ class WorkQueue {
11
11
  this.maxParallelWork = maxParallelWork;
12
12
  this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
13
13
  this.queue = [];
14
- this.workRunningCount = 0;
14
+ this.running = [];
15
15
  this.notifyQueue = () => {
16
16
  };
17
17
  this.notifyWorkFinish = () => {
@@ -37,8 +37,8 @@ class WorkQueue {
37
37
  this.notifyQueue = res;
38
38
  });
39
39
  }
40
- if (this.workRunningCount >= this.maxParallelWork) {
41
- this.logger.logLabeled("DEBUG", "work-queue", `waiting for work to finish (running=${this.workRunningCount})`);
40
+ if (this.running.length >= this.maxParallelWork) {
41
+ this.logger.logLabeled("DEBUG", "work-queue", `waiting for work to finish (running=${this.running})`);
42
42
  await new Promise((res) => {
43
43
  this.notifyWorkFinish = res;
44
44
  });
@@ -74,8 +74,10 @@ class WorkQueue {
74
74
  }
75
75
  getState() {
76
76
  return {
77
+ queuedWork: this.queue.map((work) => work.type),
77
78
  queueLength: this.queue.length,
78
- workRunningCount: this.workRunningCount,
79
+ runningWork: this.running,
80
+ workRunningCount: this.running.length,
79
81
  };
80
82
  }
81
83
  isWorking() {
@@ -85,7 +87,7 @@ class WorkQueue {
85
87
  async runNext() {
86
88
  const next = this.queue.shift();
87
89
  if (next) {
88
- this.workRunningCount++;
90
+ this.running.push(next.type || "anonymous");
89
91
  this.logState();
90
92
  try {
91
93
  await next();
@@ -94,7 +96,10 @@ class WorkQueue {
94
96
  this.logger.log("DEBUG", e);
95
97
  }
96
98
  finally {
97
- this.workRunningCount--;
99
+ const index = this.running.indexOf(next.type || "anonymous");
100
+ if (index !== -1) {
101
+ this.running.splice(index, 1);
102
+ }
98
103
  this.notifyWorkFinish();
99
104
  this.logState();
100
105
  }