firebase-tools 10.2.0 → 10.3.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 (139) hide show
  1. package/lib/apiv2.js +3 -0
  2. package/lib/appdistribution/options-parser-util.js +1 -1
  3. package/lib/auth.js +3 -3
  4. package/lib/command.js +1 -1
  5. package/lib/commands/apps-android-sha-create.js +2 -2
  6. package/lib/commands/apps-sdkconfig.js +1 -1
  7. package/lib/commands/auth-import.js +1 -1
  8. package/lib/commands/database-rules-list.js +2 -2
  9. package/lib/commands/emulators-start.js +1 -1
  10. package/lib/commands/ext-configure.js +58 -4
  11. package/lib/commands/ext-dev-init.js +49 -49
  12. package/lib/commands/ext-export.js +7 -2
  13. package/lib/commands/ext-install.js +163 -104
  14. package/lib/commands/ext-uninstall.js +17 -8
  15. package/lib/commands/ext-update.js +64 -11
  16. package/lib/commands/functions-config-clone.js +1 -1
  17. package/lib/commands/functions-config-export.js +1 -1
  18. package/lib/commands/hosting-clone.js +3 -3
  19. package/lib/commands/remoteconfig-get.js +1 -1
  20. package/lib/config.js +6 -3
  21. package/lib/deploy/extensions/deploymentSummary.js +3 -3
  22. package/lib/deploy/extensions/planner.js +7 -6
  23. package/lib/deploy/extensions/tasks.js +1 -1
  24. package/lib/deploy/functions/backend.js +21 -5
  25. package/lib/deploy/functions/checkIam.js +5 -5
  26. package/lib/deploy/functions/containerCleaner.js +3 -3
  27. package/lib/deploy/functions/ensure.js +3 -3
  28. package/lib/deploy/functions/functionsDeployHelper.js +2 -2
  29. package/lib/deploy/functions/prepare.js +5 -3
  30. package/lib/deploy/functions/pricing.js +1 -1
  31. package/lib/deploy/functions/prompts.js +2 -2
  32. package/lib/deploy/functions/release/fabricator.js +7 -7
  33. package/lib/deploy/functions/release/index.js +1 -1
  34. package/lib/deploy/functions/release/planner.js +43 -26
  35. package/lib/deploy/functions/release/reporter.js +3 -0
  36. package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
  37. package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
  38. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +22 -12
  39. package/lib/deploy/functions/runtimes/golang/index.js +2 -2
  40. package/lib/deploy/functions/runtimes/node/index.js +53 -0
  41. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
  42. package/lib/deploy/functions/runtimes/node/parseTriggers.js +52 -15
  43. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  44. package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
  45. package/lib/deploy/functions/services/index.js +9 -1
  46. package/lib/deploy/functions/services/storage.js +10 -4
  47. package/lib/deploy/functions/triggerRegionHelper.js +1 -1
  48. package/lib/deploy/functions/validate.js +3 -3
  49. package/lib/deploy/hosting/client.js +9 -0
  50. package/lib/deploy/hosting/convertConfig.js +6 -0
  51. package/lib/deploy/hosting/deploy.js +2 -2
  52. package/lib/deploy/hosting/hashcache.js +21 -19
  53. package/lib/deploy/hosting/index.js +5 -5
  54. package/lib/deploy/hosting/prepare.js +25 -25
  55. package/lib/deploy/hosting/release.js +21 -24
  56. package/lib/deploy/hosting/uploader.js +5 -5
  57. package/lib/deploy/remoteconfig/functions.js +2 -2
  58. package/lib/emulator/auth/cloudFunctions.js +1 -1
  59. package/lib/emulator/auth/operations.js +1 -1
  60. package/lib/emulator/commandUtils.js +5 -1
  61. package/lib/emulator/constants.js +4 -0
  62. package/lib/emulator/controller.js +54 -24
  63. package/lib/emulator/download.js +18 -1
  64. package/lib/emulator/downloadableEmulators.js +30 -13
  65. package/lib/emulator/emulatorLogger.js +12 -1
  66. package/lib/emulator/extensions/validation.js +70 -0
  67. package/lib/emulator/extensionsEmulator.js +175 -0
  68. package/lib/emulator/functionsEmulator.js +106 -44
  69. package/lib/emulator/functionsEmulatorRuntime.js +44 -36
  70. package/lib/emulator/functionsEmulatorShared.js +17 -10
  71. package/lib/emulator/functionsEmulatorShell.js +1 -1
  72. package/lib/emulator/functionsEmulatorUtils.js +4 -4
  73. package/lib/emulator/functionsRuntimeWorker.js +2 -2
  74. package/lib/emulator/hub.js +4 -3
  75. package/lib/emulator/loggingEmulator.js +1 -1
  76. package/lib/emulator/pubsubEmulator.js +1 -1
  77. package/lib/emulator/registry.js +10 -2
  78. package/lib/emulator/storage/apis/firebase.js +314 -332
  79. package/lib/emulator/storage/apis/gcloud.js +241 -121
  80. package/lib/emulator/storage/crc.js +5 -1
  81. package/lib/emulator/storage/errors.js +9 -0
  82. package/lib/emulator/storage/files.js +159 -300
  83. package/lib/emulator/storage/index.js +27 -73
  84. package/lib/emulator/storage/metadata.js +65 -51
  85. package/lib/emulator/storage/multipart.js +62 -0
  86. package/lib/emulator/storage/persistence.js +78 -0
  87. package/lib/emulator/storage/rules/config.js +33 -0
  88. package/lib/emulator/storage/rules/manager.js +81 -0
  89. package/lib/emulator/storage/rules/runtime.js +8 -7
  90. package/lib/emulator/storage/rules/utils.js +48 -0
  91. package/lib/emulator/storage/server.js +2 -2
  92. package/lib/emulator/storage/upload.js +106 -0
  93. package/lib/emulator/types.js +3 -0
  94. package/lib/ensureApiEnabled.js +5 -1
  95. package/lib/error.js +1 -1
  96. package/lib/extensions/askUserForParam.js +1 -1
  97. package/lib/extensions/changelog.js +3 -1
  98. package/lib/extensions/checkProjectBilling.js +1 -1
  99. package/lib/extensions/displayExtensionInfo.js +1 -1
  100. package/lib/extensions/emulator/optionsHelper.js +56 -8
  101. package/lib/extensions/emulator/specHelper.js +10 -23
  102. package/lib/extensions/export.js +1 -51
  103. package/lib/extensions/extensionsApi.js +1 -1
  104. package/lib/extensions/extensionsHelper.js +32 -19
  105. package/lib/extensions/listExtensions.js +2 -0
  106. package/lib/extensions/manifest.js +144 -0
  107. package/lib/extensions/metricsUtils.js +4 -4
  108. package/lib/extensions/paramHelper.js +9 -8
  109. package/lib/extensions/refs.js +1 -1
  110. package/lib/extensions/secretsUtils.js +3 -3
  111. package/lib/functional.js +1 -1
  112. package/lib/functions/env.js +6 -7
  113. package/lib/functions/events/v2.js +11 -0
  114. package/lib/gcp/cloudfunctions.js +42 -11
  115. package/lib/gcp/cloudfunctionsv2.js +48 -17
  116. package/lib/gcp/cloudtasks.js +1 -1
  117. package/lib/gcp/docker.js +2 -2
  118. package/lib/gcp/resourceManager.js +4 -4
  119. package/lib/gcp/run.js +2 -2
  120. package/lib/gcp/storage.js +1 -0
  121. package/lib/hosting/api.js +1 -1
  122. package/lib/hosting/functionsProxy.js +15 -5
  123. package/lib/hosting/proxy.js +2 -2
  124. package/lib/init/features/account.js +1 -1
  125. package/lib/management/database.js +1 -1
  126. package/lib/previews.js +1 -1
  127. package/lib/responseToError.js +16 -7
  128. package/lib/serve/functions.js +2 -1
  129. package/lib/serve/hosting.js +1 -1
  130. package/lib/utils.js +15 -2
  131. package/npm-shrinkwrap.json +904 -412
  132. package/package.json +3 -3
  133. package/schema/firebase-config.json +32 -0
  134. package/templates/init/functions/javascript/package.lint.json +3 -3
  135. package/templates/init/functions/javascript/package.nolint.json +2 -2
  136. package/templates/init/functions/typescript/package.lint.json +7 -7
  137. package/templates/init/functions/typescript/package.nolint.json +3 -3
  138. package/lib/deploy/extensions/params.js +0 -39
  139. package/lib/deploy/functions/eventTypes.js +0 -10
@@ -8,7 +8,9 @@ const express = require("express");
8
8
  const clc = require("cli-color");
9
9
  const http = require("http");
10
10
  const jwt = require("jsonwebtoken");
11
+ const cors = require("cors");
11
12
  const url_1 = require("url");
13
+ const events_1 = require("events");
12
14
  const api = require("../api");
13
15
  const logger_1 = require("../logger");
14
16
  const track = require("../track");
@@ -19,7 +21,6 @@ const spawn = require("cross-spawn");
19
21
  const child_process_1 = require("child_process");
20
22
  const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
21
23
  const registry_1 = require("./registry");
22
- const events_1 = require("events");
23
24
  const emulatorLogger_1 = require("./emulatorLogger");
24
25
  const functionsRuntimeWorker_1 = require("./functionsRuntimeWorker");
25
26
  const error_1 = require("../error");
@@ -29,10 +30,10 @@ const defaultCredentials_1 = require("../defaultCredentials");
29
30
  const adminSdkConfig_1 = require("./adminSdkConfig");
30
31
  const types_2 = require("./events/types");
31
32
  const validate_1 = require("../deploy/functions/validate");
32
- const runtimes_1 = require("../deploy/functions/runtimes");
33
+ const secretManager_1 = require("../gcp/secretManager");
34
+ const runtimes = require("../deploy/functions/runtimes");
33
35
  const backend = require("../deploy/functions/backend");
34
36
  const functionsEnv = require("../functions/env");
35
- const secretManager_1 = require("../gcp/secretManager");
36
37
  const EVENT_INVOKE = "functions:invoke";
37
38
  const LOCAL_SECRETS_FILE = ".secret.local";
38
39
  const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
@@ -92,6 +93,7 @@ class FunctionsEmulator {
92
93
  const httpsFunctionRoute = `/${this.args.projectId}/:region/:trigger_name`;
93
94
  const multicastFunctionRoute = `/functions/projects/:project_id/trigger_multicast`;
94
95
  const httpsFunctionRoutes = [httpsFunctionRoute, `${httpsFunctionRoute}/*`];
96
+ const listBackendsRoute = `/backends`;
95
97
  const backgroundHandler = (req, res) => {
96
98
  var _a;
97
99
  const region = req.params.region;
@@ -152,6 +154,10 @@ class FunctionsEmulator {
152
154
  });
153
155
  res.json({ status: "multicast_acknowledged" });
154
156
  };
157
+ const listBackendsHandler = (req, res) => {
158
+ res.json({ backends: this.getBackendInfo() });
159
+ };
160
+ hub.get(listBackendsRoute, cors({ origin: true }), listBackendsHandler);
155
161
  hub.post(backgroundFunctionRoute, dataMiddleware, backgroundHandler);
156
162
  hub.post(multicastFunctionRoute, dataMiddleware, multicastHandler);
157
163
  hub.all(httpsFunctionRoutes, dataMiddleware, httpsHandler);
@@ -161,9 +167,15 @@ class FunctionsEmulator {
161
167
  });
162
168
  return hub;
163
169
  }
164
- async startFunctionRuntime(backend, trigger, proto, runtimeOpts) {
170
+ async invokeTrigger(backend, trigger, proto, runtimeOpts) {
165
171
  const bundleTemplate = this.getBaseBundle();
166
172
  const runtimeBundle = Object.assign(Object.assign({}, bundleTemplate), { proto });
173
+ if (this.args.debugPort) {
174
+ runtimeBundle.debug = {
175
+ functionTarget: trigger.entryPoint,
176
+ functionSignature: (0, functionsEmulatorShared_1.getSignatureType)(trigger),
177
+ };
178
+ }
167
179
  if (!backend.nodeBinary) {
168
180
  throw new error_1.FirebaseError(`No node binary for ${trigger.id}. This should never happen.`);
169
181
  }
@@ -240,20 +252,25 @@ class FunctionsEmulator {
240
252
  }
241
253
  let triggerDefinitions;
242
254
  if (emulatableBackend.predefinedTriggers) {
243
- triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers);
255
+ triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers, emulatableBackend.secretEnv);
244
256
  }
245
257
  else {
246
- const runtimeDelegate = await (0, runtimes_1.getRuntimeDelegate)({
258
+ const runtimeConfig = this.getRuntimeConfig(emulatableBackend);
259
+ const runtimeDelegateContext = {
247
260
  projectId: this.args.projectId,
248
261
  projectDir: this.args.projectDir,
249
262
  sourceDir: emulatableBackend.functionsDir,
250
- });
263
+ };
264
+ if (emulatableBackend.nodeMajorVersion) {
265
+ runtimeDelegateContext.runtime = `nodejs${emulatableBackend.nodeMajorVersion}`;
266
+ }
267
+ const runtimeDelegate = await runtimes.getRuntimeDelegate(runtimeDelegateContext);
251
268
  logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
252
269
  await runtimeDelegate.validate();
253
270
  logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
254
271
  await runtimeDelegate.build();
255
272
  logger_1.logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
256
- const discoveredBackend = await runtimeDelegate.discoverSpec({}, Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), emulatableBackend.env));
273
+ const discoveredBackend = await runtimeDelegate.discoverSpec(runtimeConfig, Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), emulatableBackend.env));
257
274
  const endpoints = backend.allEndpoints(discoveredBackend);
258
275
  triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsFromEndpoints)(endpoints);
259
276
  }
@@ -330,6 +347,9 @@ class FunctionsEmulator {
330
347
  this.logger.logLabeled("SUCCESS", `functions[${definition.id}]`, msg);
331
348
  }
332
349
  }
350
+ if (this.args.debugPort) {
351
+ this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
352
+ }
333
353
  }
334
354
  addRealtimeDatabaseTrigger(projectId, key, eventTrigger) {
335
355
  const databaseEmu = registry_1.EmulatorRegistry.get(types_1.Emulators.DATABASE);
@@ -468,8 +488,26 @@ class FunctionsEmulator {
468
488
  getTriggerKey(def) {
469
489
  return def.eventTrigger ? `${def.id}-${this.triggerGeneration}` : def.id;
470
490
  }
471
- getBackends() {
472
- return this.args.emulatableBackends;
491
+ getBackendInfo() {
492
+ const cf3Triggers = Object.values(this.triggers)
493
+ .filter((t) => !t.backend.extensionInstanceId)
494
+ .map((t) => t.def);
495
+ return this.args.emulatableBackends.map((e) => {
496
+ var _a;
497
+ const envWithSecrets = Object.assign({}, e.env);
498
+ for (const s of e.secretEnv) {
499
+ envWithSecrets[s.key] = backend.secretVersionName(s);
500
+ }
501
+ return {
502
+ directory: e.functionsDir,
503
+ env: envWithSecrets,
504
+ extensionInstanceId: e.extensionInstanceId,
505
+ extension: e.extension,
506
+ extensionVersion: e.extensionVersion,
507
+ extensionSpec: e.extensionSpec,
508
+ functionTriggers: (_a = e.predefinedTriggers) !== null && _a !== void 0 ? _a : cf3Triggers,
509
+ };
510
+ });
473
511
  }
474
512
  addTriggerRecord(def, opts) {
475
513
  const key = this.getTriggerKey(def);
@@ -521,10 +559,21 @@ class FunctionsEmulator {
521
559
  }
522
560
  return process.execPath;
523
561
  }
562
+ getRuntimeConfig(backend) {
563
+ const configPath = `${backend.functionsDir}/.runtimeconfig.json`;
564
+ try {
565
+ const configContent = fs.readFileSync(configPath, "utf8");
566
+ return JSON.parse(configContent.toString());
567
+ }
568
+ catch (e) {
569
+ }
570
+ return {};
571
+ }
524
572
  getUserEnvs(backend) {
525
573
  const projectInfo = {
526
574
  functionsSource: backend.functionsDir,
527
575
  projectId: this.args.projectId,
576
+ projectAlias: this.args.projectAlias,
528
577
  isEmulator: true,
529
578
  };
530
579
  if (functionsEnv.hasUserEnvs(projectInfo)) {
@@ -543,11 +592,10 @@ class FunctionsEmulator {
543
592
  envs.K_REVISION = "1";
544
593
  envs.PORT = "80";
545
594
  if (trigger) {
546
- const service = trigger.name;
547
- const target = service.replace(/-/g, ".");
595
+ const target = trigger.entryPoint;
548
596
  envs.FUNCTION_TARGET = target;
549
597
  envs.FUNCTION_SIGNATURE_TYPE = (0, functionsEmulatorShared_1.getSignatureType)(trigger);
550
- envs.K_SERVICE = service;
598
+ envs.K_SERVICE = trigger.name;
551
599
  }
552
600
  return envs;
553
601
  }
@@ -579,6 +627,9 @@ class FunctionsEmulator {
579
627
  const pubsubHost = (0, functionsEmulatorShared_1.formatHost)(pubsubEmulator);
580
628
  process.env.PUBSUB_EMULATOR_HOST = pubsubHost;
581
629
  }
630
+ if (this.args.debugPort) {
631
+ envs["FUNCTION_DEBUG_MODE"] = "true";
632
+ }
582
633
  return envs;
583
634
  }
584
635
  getFirebaseConfig() {
@@ -612,44 +663,51 @@ class FunctionsEmulator {
612
663
  this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${LOCAL_SECRETS_FILE}: ${e.message}`);
613
664
  }
614
665
  }
615
- const secrets = trigger.secretEnvironmentVariables || [];
616
- const accesses = secrets
617
- .filter((s) => !secretEnvs[s.secret])
618
- .map(async (s) => {
619
- this.logger.logLabeled("INFO", "functions", `Trying to access secret ${s.key}@latest`);
620
- const value = await (0, secretManager_1.accessSecretVersion)(this.getProjectId(), s.key, "latest");
621
- return [s.secret, value];
622
- });
623
- const accessResults = await (0, utils_1.allSettled)(accesses);
624
- const errs = [];
625
- for (const result of accessResults) {
626
- if (result.status === "rejected") {
627
- errs.push(result.reason);
666
+ if (trigger) {
667
+ const secrets = trigger.secretEnvironmentVariables || [];
668
+ const accesses = secrets
669
+ .filter((s) => !secretEnvs[s.key])
670
+ .map(async (s) => {
671
+ var _a;
672
+ this.logger.logLabeled("INFO", "functions", `Trying to access secret ${s.secret}@latest`);
673
+ const value = await (0, secretManager_1.accessSecretVersion)(this.getProjectId(), s.secret, (_a = s.version) !== null && _a !== void 0 ? _a : "latest");
674
+ return [s.key, value];
675
+ });
676
+ const accessResults = await (0, utils_1.allSettled)(accesses);
677
+ const errs = [];
678
+ for (const result of accessResults) {
679
+ if (result.status === "rejected") {
680
+ errs.push(result.reason);
681
+ }
682
+ else {
683
+ const [k, v] = result.value;
684
+ secretEnvs[k] = v;
685
+ }
628
686
  }
629
- else {
630
- const [k, v] = result.value;
631
- secretEnvs[k] = v;
687
+ if (errs.length > 0) {
688
+ this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
689
+ "Make sure the credential used for the Functions Emulator have access " +
690
+ `or provide override values in ${LOCAL_SECRETS_FILE}:\n\t` +
691
+ errs.join("\n\t"));
632
692
  }
633
693
  }
634
- if (errs.length > 0) {
635
- this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
636
- "Make sure the credential used for the Functions Emulator have access " +
637
- `or provide override values in ${LOCAL_SECRETS_FILE}:\n\t` +
638
- errs.join("\n\t"));
639
- }
640
694
  return secretEnvs;
641
695
  }
642
696
  async invokeRuntime(backend, trigger, frb, opts) {
643
- if (this.workerPool.readyForWork(trigger.id)) {
644
- return this.workerPool.submitWork(trigger.id, frb, opts);
697
+ if (!this.workerPool.readyForWork(trigger.id)) {
698
+ await this.startRuntime(backend, opts, trigger);
645
699
  }
700
+ return this.workerPool.submitWork(trigger.id, frb, opts);
701
+ }
702
+ async startRuntime(backend, opts, trigger) {
703
+ var _a;
646
704
  const emitter = new events_1.EventEmitter();
647
705
  const args = [path.join(__dirname, "functionsEmulatorRuntime")];
648
706
  if (opts.ignore_warnings) {
649
707
  args.unshift("--no-warnings");
650
708
  }
651
709
  if (this.args.debugPort) {
652
- if (process.env.FIREPIT_VERSION && process.execPath == opts.nodeBinary) {
710
+ if (process.env.FIREPIT_VERSION && process.execPath === opts.nodeBinary) {
653
711
  const requestedMajorNodeVersion = this.getNodeBinary(backend);
654
712
  this.logger.log("WARN", `To enable function inspection, please run "${process.execPath} is:npm i node@${requestedMajorNodeVersion} --save-dev" in your functions directory`);
655
713
  }
@@ -711,8 +769,12 @@ class FunctionsEmulator {
711
769
  return childProcess.send(JSON.stringify(args));
712
770
  },
713
771
  };
714
- this.workerPool.addWorker(trigger.id, runtime);
715
- return this.workerPool.submitWork(trigger.id, frb, opts);
772
+ const extensionLogInfo = {
773
+ instanceId: backend.extensionInstanceId,
774
+ ref: (_a = backend.extensionVersion) === null || _a === void 0 ? void 0 : _a.ref,
775
+ };
776
+ this.workerPool.addWorker(trigger === null || trigger === void 0 ? void 0 : trigger.id, runtime, extensionLogInfo);
777
+ return;
716
778
  }
717
779
  async disableBackgroundTriggers() {
718
780
  Object.values(this.triggers).forEach((record) => {
@@ -738,7 +800,7 @@ class FunctionsEmulator {
738
800
  }
739
801
  const trigger = record.def;
740
802
  const service = (0, functionsEmulatorShared_1.getFunctionService)(trigger);
741
- const worker = await this.startFunctionRuntime(record.backend, trigger, proto);
803
+ const worker = await this.invokeTrigger(record.backend, trigger, proto);
742
804
  return new Promise((resolve, reject) => {
743
805
  if (projectId !== this.args.projectId) {
744
806
  if (service !== constants_1.Constants.SERVICE_REALTIME_DATABASE) {
@@ -757,7 +819,7 @@ class FunctionsEmulator {
757
819
  reject({ code: 500, body: el.text });
758
820
  }
759
821
  });
760
- track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
822
+ void track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
761
823
  worker.waitForDone().then(() => {
762
824
  resolve({ status: "acknowledged" });
763
825
  });
@@ -825,14 +887,14 @@ class FunctionsEmulator {
825
887
  req.headers[functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER] = encodeURIComponent(JSON.stringify(contextAuth));
826
888
  }
827
889
  }
828
- const worker = await this.startFunctionRuntime(record.backend, trigger);
890
+ const worker = await this.invokeTrigger(record.backend, trigger);
829
891
  worker.onLogs((el) => {
830
892
  if (el.level === "FATAL") {
831
893
  res.status(500).send(el.text);
832
894
  }
833
895
  });
834
896
  await worker.waitForSocketReady();
835
- track(EVENT_INVOKE, "https");
897
+ void track(EVENT_INVOKE, "https");
836
898
  this.logger.log("DEBUG", `[functions] Runtime ready! Sending request!`);
837
899
  if (!worker.lastArgs) {
838
900
  throw new error_1.FirebaseError("Cannot execute on a worker with no arguments");
@@ -10,9 +10,10 @@ const types_1 = require("./types");
10
10
  const constants_1 = require("./constants");
11
11
  const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
12
12
  const functionsEmulatorUtils_1 = require("./functionsEmulatorUtils");
13
- let functionTrigger;
13
+ let functionModule;
14
14
  let FUNCTION_TARGET_NAME;
15
15
  let FUNCTION_SIGNATURE;
16
+ let FUNCTION_DEBUG_MODE;
16
17
  let developerPkgJSON;
17
18
  const dynamicImport = new Function("modulePath", "return import(modulePath)");
18
19
  function isFeatureEnabled(frb, feature) {
@@ -484,7 +485,7 @@ async function initializeFunctionsConfigHelper(frb) {
484
485
  function rawBodySaver(req, res, buf) {
485
486
  req.rawBody = buf;
486
487
  }
487
- async function processHTTPS(frb) {
488
+ async function processHTTPS(trigger, frb) {
488
489
  const ephemeralServer = express();
489
490
  const functionRouter = express.Router();
490
491
  const socketPath = frb.socketPath;
@@ -506,7 +507,7 @@ async function processHTTPS(frb) {
506
507
  }
507
508
  });
508
509
  });
509
- await runHTTPS([req, res]);
510
+ await runHTTPS(trigger, [req, res]);
510
511
  }
511
512
  catch (err) {
512
513
  rejectEphemeralServer(err);
@@ -540,11 +541,11 @@ async function processHTTPS(frb) {
540
541
  instance.on("error", rejectEphemeralServer);
541
542
  });
542
543
  }
543
- async function processBackground(frb, signature) {
544
+ async function processBackground(trigger, frb, signature) {
544
545
  const proto = frb.proto;
545
546
  logDebug("ProcessBackground", proto);
546
547
  if (signature === "cloudevent") {
547
- return runCloudEvent(proto);
548
+ return runCloudEvent(trigger, proto);
548
549
  }
549
550
  const data = proto.data;
550
551
  delete proto.data;
@@ -555,7 +556,7 @@ async function processBackground(frb, signature) {
555
556
  context.resource = context.resource.name;
556
557
  }
557
558
  }
558
- await runBackground({ data, context });
559
+ await runBackground(trigger, { data, context });
559
560
  }
560
561
  async function runFunction(func) {
561
562
  let caughtErr;
@@ -570,24 +571,24 @@ async function runFunction(func) {
570
571
  throw caughtErr;
571
572
  }
572
573
  }
573
- async function runBackground(proto) {
574
+ async function runBackground(trigger, proto) {
574
575
  logDebug("RunBackground", proto);
575
576
  await runFunction(() => {
576
- return functionTrigger(proto.data, proto.context);
577
+ return trigger(proto.data, proto.context);
577
578
  });
578
579
  }
579
- async function runCloudEvent(event) {
580
+ async function runCloudEvent(trigger, event) {
580
581
  logDebug("RunCloudEvent", event);
581
582
  await runFunction(() => {
582
- return functionTrigger(event);
583
+ return trigger(event);
583
584
  });
584
585
  }
585
- async function runHTTPS(args) {
586
+ async function runHTTPS(trigger, args) {
586
587
  if (args.length < 2) {
587
588
  throw new Error("Function must be passed 2 args.");
588
589
  }
589
590
  await runFunction(() => {
590
- return functionTrigger(args[0], args[1]);
591
+ return trigger(args[0], args[1]);
591
592
  });
592
593
  }
593
594
  async function moduleResolutionDetective(frb, error) {
@@ -611,7 +612,7 @@ async function moduleResolutionDetective(frb, error) {
611
612
  function logDebug(msg, data) {
612
613
  new types_1.EmulatorLog("DEBUG", "runtime-status", `[${process.pid}] ${msg}`, data).log();
613
614
  }
614
- async function invokeTrigger(frb) {
615
+ async function invokeTrigger(trigger, frb) {
615
616
  new types_1.EmulatorLog("INFO", "runtime-status", `Beginning execution of "${FUNCTION_TARGET_NAME}"`, {
616
617
  frb,
617
618
  }).log();
@@ -636,10 +637,10 @@ async function invokeTrigger(frb) {
636
637
  switch (FUNCTION_SIGNATURE) {
637
638
  case "event":
638
639
  case "cloudevent":
639
- await processBackground(frb, FUNCTION_SIGNATURE);
640
+ await processBackground(trigger, frb, FUNCTION_SIGNATURE);
640
641
  break;
641
642
  case "http":
642
- await processHTTPS(frb);
643
+ await processHTTPS(trigger, frb);
643
644
  break;
644
645
  }
645
646
  if (timeoutId) {
@@ -649,15 +650,18 @@ async function invokeTrigger(frb) {
649
650
  new types_1.EmulatorLog("INFO", "runtime-status", `Finished "${FUNCTION_TARGET_NAME}" in ~${Math.max(seconds, 1)}s`).log();
650
651
  }
651
652
  async function initializeRuntime(frb) {
652
- FUNCTION_TARGET_NAME = process.env.FUNCTION_TARGET || "";
653
- if (!FUNCTION_TARGET_NAME) {
654
- new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_TARGET cannot be empty. This shouldn't happen.`).log();
655
- await flushAndExit(1);
656
- }
657
- FUNCTION_SIGNATURE = process.env.FUNCTION_SIGNATURE_TYPE || "";
658
- if (!FUNCTION_SIGNATURE) {
659
- new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_SIGNATURE_TYPE cannot be empty. This shouldn't happen.`).log();
660
- await flushAndExit(1);
653
+ FUNCTION_DEBUG_MODE = process.env.FUNCTION_DEBUG_MODE || "";
654
+ if (!FUNCTION_DEBUG_MODE) {
655
+ FUNCTION_TARGET_NAME = process.env.FUNCTION_TARGET || "";
656
+ if (!FUNCTION_TARGET_NAME) {
657
+ new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_TARGET cannot be empty. This shouldn't happen.`).log();
658
+ await flushAndExit(1);
659
+ }
660
+ FUNCTION_SIGNATURE = process.env.FUNCTION_SIGNATURE_TYPE || "";
661
+ if (!FUNCTION_SIGNATURE) {
662
+ new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_SIGNATURE_TYPE cannot be empty. This shouldn't happen.`).log();
663
+ await flushAndExit(1);
664
+ }
661
665
  }
662
666
  logDebug(`Disabled runtime features: ${JSON.stringify(frb.disabled_features)}`);
663
667
  const verified = await verifyDeveloperNodeModules(frb);
@@ -671,7 +675,7 @@ async function initializeRuntime(frb) {
671
675
  await initializeFirebaseFunctionsStubs(frb);
672
676
  await initializeFirebaseAdminStubs(frb);
673
677
  }
674
- async function loadTrigger(frb, functionTarget, serializedFunctionTrigger) {
678
+ async function loadTriggers(frb, serializedFunctionTrigger) {
675
679
  let triggerModule;
676
680
  if (serializedFunctionTrigger) {
677
681
  triggerModule = eval(serializedFunctionTrigger)();
@@ -690,13 +694,7 @@ async function loadTrigger(frb, functionTarget, serializedFunctionTrigger) {
690
694
  triggerModule = await dynamicImport(moduleURL);
691
695
  }
692
696
  }
693
- const maybeTrigger = functionTarget.split(".").reduce((mod, functionTargetPart) => {
694
- return mod === null || mod === void 0 ? void 0 : mod[functionTargetPart];
695
- }, triggerModule);
696
- if (!maybeTrigger) {
697
- throw new Error(`Failed to find function ${functionTarget} in the loaded module`);
698
- }
699
- return maybeTrigger;
697
+ return triggerModule;
700
698
  }
701
699
  async function flushAndExit(code) {
702
700
  await types_1.EmulatorLog.waitForFlush();
@@ -716,22 +714,32 @@ async function handleMessage(message) {
716
714
  await flushAndExit(1);
717
715
  return;
718
716
  }
719
- if (!functionTrigger) {
717
+ if (!functionModule) {
720
718
  try {
721
719
  await initializeRuntime(runtimeArgs.frb);
722
720
  const serializedTriggers = runtimeArgs.opts ? runtimeArgs.opts.serializedTriggers : undefined;
723
- functionTrigger = await loadTrigger(runtimeArgs.frb, FUNCTION_TARGET_NAME, serializedTriggers);
721
+ functionModule = await loadTriggers(runtimeArgs.frb, serializedTriggers);
724
722
  }
725
723
  catch (e) {
726
724
  logDebug(e);
727
- new types_1.EmulatorLog("FATAL", "runtime-status", `Failed to initialize and load trigger. This shouldn't happen: ${e.message}`).log();
725
+ new types_1.EmulatorLog("FATAL", "runtime-status", `Failed to initialize and load triggers. This shouldn't happen: ${e.message}`).log();
728
726
  await flushAndExit(1);
729
727
  return;
730
728
  }
731
729
  }
730
+ if (FUNCTION_DEBUG_MODE) {
731
+ FUNCTION_TARGET_NAME = runtimeArgs.frb.debug.functionTarget;
732
+ FUNCTION_SIGNATURE = runtimeArgs.frb.debug.functionSignature;
733
+ }
734
+ const trigger = FUNCTION_TARGET_NAME.split(".").reduce((mod, functionTargetPart) => {
735
+ return mod === null || mod === void 0 ? void 0 : mod[functionTargetPart];
736
+ }, functionModule);
737
+ if (!trigger) {
738
+ throw new Error(`Failed to find function ${FUNCTION_TARGET_NAME} in the loaded module`);
739
+ }
732
740
  logDebug(`Beginning invocation function ${FUNCTION_TARGET_NAME}!`);
733
741
  try {
734
- await invokeTrigger(runtimeArgs.frb);
742
+ await invokeTrigger(trigger, runtimeArgs.frb);
735
743
  if (runtimeArgs.opts && runtimeArgs.opts.serializedTriggers) {
736
744
  await flushAndExit(0);
737
745
  }
@@ -5,9 +5,10 @@ const _ = require("lodash");
5
5
  const os = require("os");
6
6
  const path = require("path");
7
7
  const fs = require("fs");
8
+ const backend = require("../deploy/functions/backend");
8
9
  const constants_1 = require("./constants");
9
- const backend_1 = require("../deploy/functions/backend");
10
10
  const proto_1 = require("../gcp/proto");
11
+ const logger_1 = require("../logger");
11
12
  const memoryLookup = {
12
13
  "128MB": 128,
13
14
  "256MB": 256,
@@ -60,30 +61,35 @@ function emulatedFunctionsFromEndpoints(endpoints) {
60
61
  id: `${endpoint.region}-${endpoint.id}`,
61
62
  };
62
63
  (0, proto_1.copyIfPresent)(def, endpoint, "timeout", "availableMemoryMb", "labels", "platform", "secretEnvironmentVariables");
63
- if ((0, backend_1.isHttpsTriggered)(endpoint)) {
64
+ if (backend.isHttpsTriggered(endpoint)) {
64
65
  def.httpsTrigger = endpoint.httpsTrigger;
65
66
  }
66
- else if ((0, backend_1.isEventTriggered)(endpoint)) {
67
+ else if (backend.isEventTriggered(endpoint)) {
67
68
  const eventTrigger = endpoint.eventTrigger;
68
69
  if (endpoint.platform === "gcfv1") {
70
+ const resourceFilter = backend.findEventFilter(endpoint, "resource");
71
+ if (!resourceFilter) {
72
+ logger_1.logger.debug(`Invalid event trigger ${JSON.stringify(endpoint)}, expected event filter with resource attribute. Skipping.`);
73
+ continue;
74
+ }
69
75
  def.eventTrigger = {
70
76
  eventType: eventTrigger.eventType,
71
- resource: eventTrigger.eventFilters.resource,
77
+ resource: resourceFilter.value,
72
78
  };
73
79
  }
74
80
  else {
75
- const { resource, topic, bucket } = endpoint.eventTrigger.eventFilters;
76
- const eventResource = resource || topic || bucket;
77
- if (!eventResource) {
81
+ const [eventFilter] = endpoint.eventTrigger.eventFilters;
82
+ if (!eventFilter) {
83
+ logger_1.logger.debug(`Invalid event trigger ${JSON.stringify(endpoint)}, expected at least one event filter. Skipping.`);
78
84
  continue;
79
85
  }
80
86
  def.eventTrigger = {
81
87
  eventType: eventTrigger.eventType,
82
- resource: eventResource,
88
+ resource: eventFilter.value,
83
89
  };
84
90
  }
85
91
  }
86
- else if ((0, backend_1.isScheduleTriggered)(endpoint)) {
92
+ else if (backend.isScheduleTriggered(endpoint)) {
87
93
  def.eventTrigger = { eventType: "pubsub", resource: "" };
88
94
  def.schedule = endpoint.scheduleTrigger;
89
95
  }
@@ -94,7 +100,7 @@ function emulatedFunctionsFromEndpoints(endpoints) {
94
100
  return regionDefinitions;
95
101
  }
96
102
  exports.emulatedFunctionsFromEndpoints = emulatedFunctionsFromEndpoints;
97
- function emulatedFunctionsByRegion(definitions) {
103
+ function emulatedFunctionsByRegion(definitions, secretEnvVariables = []) {
98
104
  const regionDefinitions = [];
99
105
  for (const def of definitions) {
100
106
  if (!def.regions) {
@@ -106,6 +112,7 @@ function emulatedFunctionsByRegion(definitions) {
106
112
  defDeepCopy.region = region;
107
113
  defDeepCopy.id = `${region}-${defDeepCopy.name}`;
108
114
  defDeepCopy.platform = defDeepCopy.platform || "gcfv1";
115
+ defDeepCopy.secretEnvironmentVariables = secretEnvVariables;
109
116
  regionDefinitions.push(defDeepCopy);
110
117
  }
111
118
  }
@@ -42,7 +42,7 @@ class FunctionsEmulatorShell {
42
42
  auth: opts.auth,
43
43
  data,
44
44
  };
45
- this.emu.startFunctionRuntime(this.backend, trigger, proto);
45
+ this.emu.invokeTrigger(this.backend, trigger, proto);
46
46
  }
47
47
  getTrigger(name) {
48
48
  const result = this.triggers.find((trigger) => {
@@ -52,7 +52,7 @@ function parseRuntimeVersion(runtime) {
52
52
  return undefined;
53
53
  }
54
54
  const runtimeRe = /(nodejs)?([0-9]+)/;
55
- const match = runtime.match(runtimeRe);
55
+ const match = runtimeRe.exec(runtime);
56
56
  if (match) {
57
57
  return Number.parseInt(match[2]);
58
58
  }
@@ -73,13 +73,13 @@ exports.parseVersionString = parseVersionString;
73
73
  function compareVersionStrings(a, b) {
74
74
  const versionA = parseVersionString(a);
75
75
  const versionB = parseVersionString(b);
76
- if (versionA.major != versionB.major) {
76
+ if (versionA.major !== versionB.major) {
77
77
  return versionA.major - versionB.major;
78
78
  }
79
- if (versionA.minor != versionB.minor) {
79
+ if (versionA.minor !== versionB.minor) {
80
80
  return versionA.minor - versionB.minor;
81
81
  }
82
- if (versionA.patch != versionB.patch) {
82
+ if (versionA.patch !== versionB.patch) {
83
83
  return versionA.patch - versionB.patch;
84
84
  }
85
85
  return 0;
@@ -170,14 +170,14 @@ class RuntimeWorkerPool {
170
170
  }
171
171
  return;
172
172
  }
173
- addWorker(triggerId, runtime) {
173
+ addWorker(triggerId, runtime, extensionLogInfo) {
174
174
  const worker = new RuntimeWorker(this.getKey(triggerId), runtime);
175
175
  this.log(`addWorker(${worker.key})`);
176
176
  const keyWorkers = this.getTriggerWorkers(triggerId);
177
177
  keyWorkers.push(worker);
178
178
  this.setTriggerWorkers(triggerId, keyWorkers);
179
179
  const logger = triggerId
180
- ? emulatorLogger_1.EmulatorLogger.forFunction(triggerId)
180
+ ? emulatorLogger_1.EmulatorLogger.forFunction(triggerId, extensionLogInfo)
181
181
  : emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
182
182
  worker.onLogs((log) => {
183
183
  logger.handleRuntimeLog(log);
@@ -25,9 +25,10 @@ class EmulatorHub {
25
25
  });
26
26
  this.hub.get(EmulatorHub.PATH_EMULATORS, (req, res) => {
27
27
  const body = {};
28
- registry_1.EmulatorRegistry.listRunning().forEach((name) => {
29
- body[name] = registry_1.EmulatorRegistry.get(name).getInfo();
30
- });
28
+ for (const emulator of registry_1.EmulatorRegistry.listRunning()) {
29
+ const info = registry_1.EmulatorRegistry.getInfo(emulator);
30
+ body[emulator] = info;
31
+ }
31
32
  res.json(body);
32
33
  });
33
34
  this.hub.post(EmulatorHub.PATH_EXPORT, async (req, res) => {
@@ -82,7 +82,7 @@ class WebSocketTransport extends TransportStream {
82
82
  };
83
83
  const splat = [info.message, ...(info[triple_beam_1.SPLAT] || [])]
84
84
  .map((value) => {
85
- if (typeof value == "string") {
85
+ if (typeof value === "string") {
86
86
  try {
87
87
  bundle.data = Object.assign(Object.assign({}, bundle.data), JSON.parse(value));
88
88
  return null;
@@ -88,7 +88,7 @@ class PubsubEmulator {
88
88
  this.subscriptionForTopic.set(topicName, sub);
89
89
  }
90
90
  ensureFunctionsClient() {
91
- if (this.client != undefined)
91
+ if (this.client !== undefined)
92
92
  return;
93
93
  const funcEmulator = registry_1.EmulatorRegistry.get(types_1.Emulators.FUNCTIONS);
94
94
  if (!funcEmulator) {