firebase-tools 11.18.0 → 11.20.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 (35) hide show
  1. package/lib/api.js +3 -2
  2. package/lib/bin/firebase.js +0 -0
  3. package/lib/commands/functions-delete.js +1 -1
  4. package/lib/commands/index.js +5 -0
  5. package/lib/commands/internaltesting-functions-discover.js +25 -0
  6. package/lib/deploy/extensions/prepare.js +6 -1
  7. package/lib/deploy/extensions/v2FunctionHelper.js +53 -0
  8. package/lib/deploy/functions/build.js +17 -2
  9. package/lib/deploy/functions/cel.js +90 -13
  10. package/lib/deploy/functions/params.js +141 -6
  11. package/lib/deploy/functions/prepare.js +40 -25
  12. package/lib/deploy/functions/release/fabricator.js +27 -1
  13. package/lib/deploy/functions/runtimes/discovery/parsing.js +7 -1
  14. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +1 -1
  15. package/lib/deploy/functions/runtimes/node/index.js +2 -2
  16. package/lib/deploy/functions/runtimes/node/versioning.js +30 -14
  17. package/lib/emulator/auth/operations.js +1 -1
  18. package/lib/emulator/commandUtils.js +2 -1
  19. package/lib/emulator/downloadableEmulators.js +6 -6
  20. package/lib/emulator/env.js +29 -27
  21. package/lib/emulator/extensionsEmulator.js +14 -9
  22. package/lib/emulator/functionsEmulator.js +16 -8
  23. package/lib/emulator/pubsubEmulator.js +13 -1
  24. package/lib/emulator/storage/rules/runtime.js +2 -2
  25. package/lib/emulator/workQueue.js +11 -6
  26. package/lib/experiments.js +6 -0
  27. package/lib/extensions/emulator/triggerHelper.js +12 -2
  28. package/lib/frameworks/index.js +7 -1
  29. package/lib/frameworks/next/constants.js +10 -0
  30. package/lib/frameworks/next/index.js +146 -146
  31. package/lib/frameworks/next/utils.js +65 -7
  32. package/lib/gcp/eventarc.js +42 -0
  33. package/lib/serve/hosting.js +5 -5
  34. package/npm-shrinkwrap.json +463 -737
  35. package/package.json +2 -5
@@ -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>?",
@@ -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;
@@ -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) {
@@ -2066,7 +2066,7 @@ function generateBlockingFunctionJwt(state, event, url, timeoutMs, user, options
2066
2066
  photo_url: user.photoUrl,
2067
2067
  disabled: user.disabled,
2068
2068
  phone_number: user.phoneNumber,
2069
- custom_claims: user.customAttributes,
2069
+ custom_claims: JSON.parse(user.customAttributes || "{}"),
2070
2070
  },
2071
2071
  sub: user.localId,
2072
2072
  sign_in_method: options.signInMethod,
@@ -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,16 +28,16 @@ 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: "" }
37
37
  : {
38
- version: "1.11.1",
39
- expectedSize: 3061713,
40
- expectedChecksum: "a4944414518be206280b495f526f18bf",
38
+ version: "1.11.2",
39
+ expectedSize: 3062873,
40
+ expectedChecksum: "fe7f668437d0e3c3b92677aaaade78bf",
41
41
  },
42
42
  pubsub: {
43
43
  version: "0.7.1",
@@ -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;
@@ -1,24 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ExtensionsEmulator = void 0;
4
+ const clc = require("colorette");
5
+ const spawn = require("cross-spawn");
4
6
  const fs = require("fs-extra");
5
7
  const os = require("os");
6
8
  const path = require("path");
7
- const clc = require("colorette");
8
9
  const Table = require("cli-table");
9
- const spawn = require("cross-spawn");
10
10
  const planner = require("../deploy/extensions/planner");
11
+ const ensureApiEnabled_1 = require("../ensureApiEnabled");
11
12
  const error_1 = require("../error");
13
+ const optionsHelper_1 = require("../extensions/emulator/optionsHelper");
12
14
  const refs_1 = require("../extensions/refs");
15
+ const shortenUrl_1 = require("../shortenUrl");
16
+ const constants_1 = require("./constants");
13
17
  const download_1 = require("./download");
14
- const optionsHelper_1 = require("../extensions/emulator/optionsHelper");
15
18
  const emulatorLogger_1 = require("./emulatorLogger");
16
- const types_1 = require("./types");
17
19
  const validation_1 = require("./extensions/validation");
18
- const ensureApiEnabled_1 = require("../ensureApiEnabled");
19
- const shortenUrl_1 = require("../shortenUrl");
20
- const constants_1 = require("./constants");
21
20
  const registry_1 = require("./registry");
21
+ const types_1 = require("./types");
22
22
  class ExtensionsEmulator {
23
23
  constructor(args) {
24
24
  this.want = [];
@@ -112,15 +112,20 @@ class ExtensionsEmulator {
112
112
  }
113
113
  installAndBuildSourceCode(sourceCodePath) {
114
114
  this.logger.logLabeled("DEBUG", "Extensions", `Running "npm install" for ${sourceCodePath}`);
115
- const npmInstall = spawn.sync("npm", ["--prefix", `/${sourceCodePath}/functions/`, "install"], {
115
+ const functionsDirectory = path.resolve(sourceCodePath, "functions");
116
+ const npmInstall = spawn.sync("npm", ["install"], {
116
117
  encoding: "utf8",
118
+ cwd: functionsDirectory,
117
119
  });
118
120
  if (npmInstall.error) {
119
121
  throw npmInstall.error;
120
122
  }
121
123
  this.logger.logLabeled("DEBUG", "Extensions", `Finished "npm install" for ${sourceCodePath}`);
122
124
  this.logger.logLabeled("DEBUG", "Extensions", `Running "npm run gcp-build" for ${sourceCodePath}`);
123
- const npmRunGCPBuild = spawn.sync("npm", ["--prefix", `/${sourceCodePath}/functions/`, "run", "gcp-build"], { encoding: "utf8" });
125
+ const npmRunGCPBuild = spawn.sync("npm", ["run", "gcp-build"], {
126
+ encoding: "utf8",
127
+ cwd: functionsDirectory,
128
+ });
124
129
  if (npmRunGCPBuild.error) {
125
130
  throw npmRunGCPBuild.error;
126
131
  }
@@ -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,7 +381,7 @@ class FunctionsEmulator {
377
381
  }
378
382
  }
379
383
  if (this.args.debugPort) {
380
- emulatableBackend.secretEnv = Object.values(toSetup.reduce((acc, curr) => {
384
+ emulatableBackend.secretEnv = Object.values(triggerDefinitions.reduce((acc, curr) => {
381
385
  for (const secret of curr.secretEnvironmentVariables || []) {
382
386
  acc[secret.key] = secret;
383
387
  }
@@ -717,7 +721,11 @@ class FunctionsEmulator {
717
721
  skipTokenVerification: true,
718
722
  enableCors: true,
719
723
  });
720
- (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);
721
729
  if (this.args.debugPort) {
722
730
  envs["FUNCTION_DEBUG_MODE"] = "true";
723
731
  }
@@ -9,6 +9,9 @@ const types_1 = require("../emulator/types");
9
9
  const constants_1 = require("./constants");
10
10
  const error_1 = require("../error");
11
11
  const registry_1 = require("./registry");
12
+ const child_process_1 = require("child_process");
13
+ const PUBSUB_KILL_COMMAND = "pubsub_pids=$(ps aux | grep '[p]ubsub-emulator' | awk '{print $2}');" +
14
+ " if [ ! -z '$pubsub_pids' ]; then kill -9 $pubsub_pids; fi;";
12
15
  class PubsubEmulator {
13
16
  constructor(args) {
14
17
  this.args = args;
@@ -32,7 +35,16 @@ class PubsubEmulator {
32
35
  return Promise.resolve();
33
36
  }
34
37
  async stop() {
35
- await downloadableEmulators.stop(types_1.Emulators.PUBSUB);
38
+ try {
39
+ await downloadableEmulators.stop(types_1.Emulators.PUBSUB);
40
+ }
41
+ catch (e) {
42
+ this.logger.logLabeled("DEBUG", "pubsub", JSON.stringify(e));
43
+ if (process.platform !== "win32") {
44
+ const buffer = (0, child_process_1.execSync)(PUBSUB_KILL_COMMAND);
45
+ this.logger.logLabeled("DEBUG", "pubsub", "Pubsub kill output: " + JSON.stringify(buffer));
46
+ }
47
+ }
36
48
  }
37
49
  getInfo() {
38
50
  const host = this.args.host || constants_1.Constants.getDefaultHost();
@@ -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
  }
@@ -74,6 +74,12 @@ exports.ALL_EXPERIMENTS = experiments({
74
74
  crossservicerules: {
75
75
  shortDescription: "Allow Firebase Rules to reference resources in other services",
76
76
  },
77
+ internaltesting: {
78
+ shortDescription: "Exposes Firebase CLI commands intended for internal testing purposes.",
79
+ fullDescription: "Exposes Firebase CLI commands intended for internal testing purposes. " +
80
+ "These commands are not meant for public consumption and may break or disappear " +
81
+ "without a notice.",
82
+ },
77
83
  });
78
84
  function isValidExperiment(name) {
79
85
  return Object.keys(exports.ALL_EXPERIMENTS).includes(name);