firebase-tools 10.1.5 → 10.2.2

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 (134) hide show
  1. package/lib/api.js +1 -0
  2. package/lib/apiv2.js +3 -0
  3. package/lib/appdistribution/options-parser-util.js +1 -1
  4. package/lib/auth.js +62 -25
  5. package/lib/command.js +1 -1
  6. package/lib/commands/apps-android-sha-create.js +2 -2
  7. package/lib/commands/apps-sdkconfig.js +1 -1
  8. package/lib/commands/auth-import.js +1 -1
  9. package/lib/commands/database-rules-list.js +2 -2
  10. package/lib/commands/emulators-start.js +1 -1
  11. package/lib/commands/ext-configure.js +1 -0
  12. package/lib/commands/ext-dev-init.js +49 -49
  13. package/lib/commands/ext-export.js +12 -2
  14. package/lib/commands/ext-install.js +104 -103
  15. package/lib/commands/ext-uninstall.js +9 -8
  16. package/lib/commands/ext-update.js +10 -9
  17. package/lib/commands/functions-config-clone.js +1 -1
  18. package/lib/commands/functions-config-export.js +1 -1
  19. package/lib/commands/functions-secrets-access.js +17 -0
  20. package/lib/commands/functions-secrets-destroy.js +40 -0
  21. package/lib/commands/functions-secrets-get.js +21 -0
  22. package/lib/commands/functions-secrets-prune.js +50 -0
  23. package/lib/commands/functions-secrets-set.js +46 -0
  24. package/lib/commands/hosting-clone.js +3 -3
  25. package/lib/commands/index.js +7 -3
  26. package/lib/commands/login.js +1 -1
  27. package/lib/commands/remoteconfig-get.js +1 -1
  28. package/lib/deploy/extensions/deploymentSummary.js +3 -3
  29. package/lib/deploy/extensions/params.js +3 -0
  30. package/lib/deploy/extensions/planner.js +2 -1
  31. package/lib/deploy/extensions/tasks.js +1 -1
  32. package/lib/deploy/functions/backend.js +20 -5
  33. package/lib/deploy/functions/checkIam.js +1 -1
  34. package/lib/deploy/functions/containerCleaner.js +3 -3
  35. package/lib/deploy/functions/ensure.js +112 -0
  36. package/lib/deploy/functions/ensureCloudBuildEnabled.js +0 -49
  37. package/lib/deploy/functions/functionsDeployHelper.js +2 -2
  38. package/lib/deploy/functions/prepare.js +15 -20
  39. package/lib/deploy/functions/pricing.js +1 -1
  40. package/lib/deploy/functions/prompts.js +2 -2
  41. package/lib/deploy/functions/release/fabricator.js +3 -3
  42. package/lib/deploy/functions/release/index.js +1 -1
  43. package/lib/deploy/functions/release/planner.js +11 -8
  44. package/lib/deploy/functions/release/reporter.js +3 -0
  45. package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
  46. package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
  47. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +17 -11
  48. package/lib/deploy/functions/runtimes/golang/index.js +2 -2
  49. package/lib/deploy/functions/runtimes/node/index.js +26 -0
  50. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
  51. package/lib/deploy/functions/runtimes/node/parseTriggers.js +40 -7
  52. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  53. package/lib/deploy/functions/validate.js +58 -3
  54. package/lib/deploy/hosting/client.js +9 -0
  55. package/lib/deploy/hosting/convertConfig.js +6 -0
  56. package/lib/deploy/hosting/deploy.js +2 -2
  57. package/lib/deploy/hosting/hashcache.js +21 -19
  58. package/lib/deploy/hosting/index.js +5 -5
  59. package/lib/deploy/hosting/prepare.js +25 -25
  60. package/lib/deploy/hosting/release.js +21 -24
  61. package/lib/deploy/hosting/uploader.js +5 -5
  62. package/lib/deploy/remoteconfig/functions.js +2 -2
  63. package/lib/emulator/auth/cloudFunctions.js +1 -1
  64. package/lib/emulator/auth/operations.js +1 -1
  65. package/lib/emulator/commandUtils.js +5 -1
  66. package/lib/emulator/constants.js +3 -0
  67. package/lib/emulator/controller.js +48 -18
  68. package/lib/emulator/download.js +18 -1
  69. package/lib/emulator/downloadableEmulators.js +30 -13
  70. package/lib/emulator/emulatorLogger.js +19 -1
  71. package/lib/emulator/extensions/validation.js +35 -0
  72. package/lib/emulator/extensionsEmulator.js +140 -0
  73. package/lib/emulator/functionsEmulator.js +175 -86
  74. package/lib/emulator/functionsEmulatorRuntime.js +108 -83
  75. package/lib/emulator/functionsEmulatorShared.js +51 -1
  76. package/lib/emulator/functionsEmulatorShell.js +1 -2
  77. package/lib/emulator/functionsEmulatorUtils.js +4 -4
  78. package/lib/emulator/functionsRuntimeWorker.js +3 -3
  79. package/lib/emulator/hub.js +4 -3
  80. package/lib/emulator/loggingEmulator.js +1 -1
  81. package/lib/emulator/pubsubEmulator.js +1 -1
  82. package/lib/emulator/registry.js +10 -2
  83. package/lib/emulator/storage/apis/firebase.js +31 -26
  84. package/lib/emulator/storage/apis/gcloud.js +7 -12
  85. package/lib/emulator/storage/files.js +36 -34
  86. package/lib/emulator/storage/index.js +2 -2
  87. package/lib/emulator/storage/metadata.js +2 -2
  88. package/lib/emulator/storage/rules/runtime.js +8 -7
  89. package/lib/emulator/types.js +3 -0
  90. package/lib/ensureApiEnabled.js +5 -1
  91. package/lib/error.js +1 -1
  92. package/lib/extensions/askUserForParam.js +2 -2
  93. package/lib/extensions/changelog.js +3 -1
  94. package/lib/extensions/checkProjectBilling.js +1 -1
  95. package/lib/extensions/diagnose.js +56 -0
  96. package/lib/extensions/displayExtensionInfo.js +1 -1
  97. package/lib/extensions/emulator/optionsHelper.js +24 -8
  98. package/lib/extensions/emulator/specHelper.js +10 -23
  99. package/lib/extensions/export.js +1 -51
  100. package/lib/extensions/extensionsApi.js +1 -1
  101. package/lib/extensions/extensionsHelper.js +23 -10
  102. package/lib/extensions/listExtensions.js +2 -0
  103. package/lib/extensions/manifest.js +48 -0
  104. package/lib/extensions/metricsUtils.js +4 -4
  105. package/lib/extensions/paramHelper.js +4 -4
  106. package/lib/extensions/refs.js +1 -1
  107. package/lib/extensions/secretsUtils.js +4 -4
  108. package/lib/functional.js +1 -1
  109. package/lib/functions/env.js +7 -8
  110. package/lib/functions/secrets.js +112 -0
  111. package/lib/gcp/cloudfunctions.js +24 -5
  112. package/lib/gcp/cloudfunctionsv2.js +18 -5
  113. package/lib/gcp/cloudtasks.js +1 -1
  114. package/lib/gcp/docker.js +2 -2
  115. package/lib/gcp/run.js +2 -2
  116. package/lib/gcp/secretManager.js +128 -46
  117. package/lib/gcp/storage.js +1 -0
  118. package/lib/hosting/api.js +1 -1
  119. package/lib/hosting/functionsProxy.js +15 -5
  120. package/lib/hosting/proxy.js +2 -2
  121. package/lib/init/features/account.js +1 -1
  122. package/lib/management/database.js +1 -1
  123. package/lib/previews.js +1 -1
  124. package/lib/responseToError.js +16 -7
  125. package/lib/serve/functions.js +2 -2
  126. package/lib/serve/hosting.js +1 -1
  127. package/lib/utils.js +7 -2
  128. package/npm-shrinkwrap.json +904 -412
  129. package/package.json +3 -3
  130. package/schema/firebase-config.json +32 -0
  131. package/templates/init/functions/javascript/package.lint.json +3 -3
  132. package/templates/init/functions/javascript/package.nolint.json +2 -2
  133. package/templates/init/functions/typescript/package.lint.json +7 -7
  134. package/templates/init/functions/typescript/package.nolint.json +3 -3
@@ -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");
@@ -27,10 +28,14 @@ const workQueue_1 = require("./workQueue");
27
28
  const utils_1 = require("../utils");
28
29
  const defaultCredentials_1 = require("../defaultCredentials");
29
30
  const adminSdkConfig_1 = require("./adminSdkConfig");
30
- const functionsEnv = require("../functions/env");
31
31
  const types_2 = require("./events/types");
32
32
  const validate_1 = require("../deploy/functions/validate");
33
+ const secretManager_1 = require("../gcp/secretManager");
34
+ const runtimes = require("../deploy/functions/runtimes");
35
+ const backend = require("../deploy/functions/backend");
36
+ const functionsEnv = require("../functions/env");
33
37
  const EVENT_INVOKE = "functions:invoke";
38
+ const LOCAL_SECRETS_FILE = ".secret.local";
34
39
  const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
35
40
  class FunctionsEmulator {
36
41
  constructor(args) {
@@ -44,9 +49,7 @@ class FunctionsEmulator {
44
49
  this.args.disabledRuntimeFeatures = this.args.disabledRuntimeFeatures || {};
45
50
  this.args.disabledRuntimeFeatures.timeout = true;
46
51
  }
47
- this.adminSdkConfig = {
48
- projectId: this.args.projectId,
49
- };
52
+ this.adminSdkConfig = Object.assign(Object.assign({}, this.args.adminSdkConfig), { projectId: this.args.projectId });
50
53
  const mode = this.args.debugPort
51
54
  ? types_1.FunctionsExecutionMode.SEQUENTIAL
52
55
  : types_1.FunctionsExecutionMode.AUTO;
@@ -76,6 +79,7 @@ class FunctionsEmulator {
76
79
  createHubServer() {
77
80
  this.workQueue.start();
78
81
  const hub = express();
82
+ hub.use(cors({ origin: true }));
79
83
  const dataMiddleware = (req, res, next) => {
80
84
  const chunks = [];
81
85
  req.on("data", (chunk) => {
@@ -90,6 +94,7 @@ class FunctionsEmulator {
90
94
  const httpsFunctionRoute = `/${this.args.projectId}/:region/:trigger_name`;
91
95
  const multicastFunctionRoute = `/functions/projects/:project_id/trigger_multicast`;
92
96
  const httpsFunctionRoutes = [httpsFunctionRoute, `${httpsFunctionRoute}/*`];
97
+ const listBackendsRoute = `/backends`;
93
98
  const backgroundHandler = (req, res) => {
94
99
  var _a;
95
100
  const region = req.params.region;
@@ -150,6 +155,10 @@ class FunctionsEmulator {
150
155
  });
151
156
  res.json({ status: "multicast_acknowledged" });
152
157
  };
158
+ const listBackendsHandler = (req, res) => {
159
+ res.json({ backends: this.getBackendInfo() });
160
+ };
161
+ hub.get(listBackendsRoute, dataMiddleware, listBackendsHandler);
153
162
  hub.post(backgroundFunctionRoute, dataMiddleware, backgroundHandler);
154
163
  hub.post(multicastFunctionRoute, dataMiddleware, multicastHandler);
155
164
  hub.all(httpsFunctionRoutes, dataMiddleware, httpsHandler);
@@ -159,25 +168,23 @@ class FunctionsEmulator {
159
168
  });
160
169
  return hub;
161
170
  }
162
- startFunctionRuntime(backend, triggerId, targetName, signatureType, proto, runtimeOpts) {
163
- const bundleTemplate = this.getBaseBundle(backend);
164
- const runtimeBundle = Object.assign(Object.assign({}, bundleTemplate), { emulators: {
165
- firestore: this.getEmulatorInfo(types_1.Emulators.FIRESTORE),
166
- database: this.getEmulatorInfo(types_1.Emulators.DATABASE),
167
- pubsub: this.getEmulatorInfo(types_1.Emulators.PUBSUB),
168
- auth: this.getEmulatorInfo(types_1.Emulators.AUTH),
169
- storage: this.getEmulatorInfo(types_1.Emulators.STORAGE),
170
- }, nodeMajorVersion: backend.nodeMajorVersion, proto,
171
- triggerId,
172
- targetName });
171
+ async invokeTrigger(backend, trigger, proto, runtimeOpts) {
172
+ const bundleTemplate = this.getBaseBundle();
173
+ const runtimeBundle = Object.assign(Object.assign({}, bundleTemplate), { proto });
174
+ if (this.args.debugPort) {
175
+ runtimeBundle.debug = {
176
+ functionTarget: trigger.entryPoint,
177
+ functionSignature: (0, functionsEmulatorShared_1.getSignatureType)(trigger),
178
+ };
179
+ }
173
180
  if (!backend.nodeBinary) {
174
- throw new error_1.FirebaseError(`No node binary for ${triggerId}. This should never happen.`);
181
+ throw new error_1.FirebaseError(`No node binary for ${trigger.id}. This should never happen.`);
175
182
  }
176
183
  const opts = runtimeOpts || {
177
184
  nodeBinary: backend.nodeBinary,
178
185
  extensionTriggers: backend.predefinedTriggers,
179
186
  };
180
- const worker = this.invokeRuntime(runtimeBundle, opts, this.getRuntimeEnvs(backend, { targetName, signatureType }));
187
+ const worker = await this.invokeRuntime(backend, trigger, runtimeBundle, opts);
181
188
  return worker;
182
189
  }
183
190
  async start() {
@@ -188,13 +195,15 @@ class FunctionsEmulator {
188
195
  for (const e of this.args.emulatableBackends) {
189
196
  e.env = Object.assign(Object.assign({}, credentialEnv), e.env);
190
197
  }
191
- const adminSdkConfig = await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(this.args.projectId);
192
- if (adminSdkConfig) {
193
- this.adminSdkConfig = adminSdkConfig;
194
- }
195
- else {
196
- this.logger.logLabeled("WARN", "functions", "Unable to fetch project Admin SDK configuration, Admin SDK behavior in Cloud Functions emulator may be incorrect.");
197
- this.adminSdkConfig = (0, adminSdkConfig_1.constructDefaultAdminSdkConfig)(this.args.projectId);
198
+ if (Object.keys(this.adminSdkConfig || {}).length <= 1) {
199
+ const adminSdkConfig = await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(this.args.projectId);
200
+ if (adminSdkConfig) {
201
+ this.adminSdkConfig = adminSdkConfig;
202
+ }
203
+ else {
204
+ this.logger.logLabeled("WARN", "functions", "Unable to fetch project Admin SDK configuration, Admin SDK behavior in Cloud Functions emulator may be incorrect.");
205
+ this.adminSdkConfig = (0, adminSdkConfig_1.constructDefaultAdminSdkConfig)(this.args.projectId);
206
+ }
198
207
  }
199
208
  const { host, port } = this.getInfo();
200
209
  this.workQueue.start();
@@ -242,14 +251,30 @@ class FunctionsEmulator {
242
251
  if (!emulatableBackend.nodeBinary) {
243
252
  throw new error_1.FirebaseError(`No node binary for ${emulatableBackend.functionsDir}. This should never happen.`);
244
253
  }
245
- const worker = this.invokeRuntime(this.getBaseBundle(emulatableBackend), {
246
- nodeBinary: emulatableBackend.nodeBinary,
247
- extensionTriggers: emulatableBackend.predefinedTriggers,
248
- }, Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), emulatableBackend.env));
249
- const triggerParseEvent = await types_1.EmulatorLog.waitForLog(worker.runtime.events, "SYSTEM", "triggers-parsed");
250
- const parsedDefinitions = triggerParseEvent.data
251
- .triggerDefinitions;
252
- const triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(parsedDefinitions);
254
+ let triggerDefinitions;
255
+ if (emulatableBackend.predefinedTriggers) {
256
+ triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers);
257
+ }
258
+ else {
259
+ const runtimeConfig = this.getRuntimeConfig(emulatableBackend);
260
+ const runtimeDelegateContext = {
261
+ projectId: this.args.projectId,
262
+ projectDir: this.args.projectDir,
263
+ sourceDir: emulatableBackend.functionsDir,
264
+ };
265
+ if (emulatableBackend.nodeMajorVersion) {
266
+ runtimeDelegateContext.runtime = `nodejs${emulatableBackend.nodeMajorVersion}`;
267
+ }
268
+ const runtimeDelegate = await runtimes.getRuntimeDelegate(runtimeDelegateContext);
269
+ logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
270
+ await runtimeDelegate.validate();
271
+ logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
272
+ await runtimeDelegate.build();
273
+ logger_1.logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
274
+ const discoveredBackend = await runtimeDelegate.discoverSpec(runtimeConfig, Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), emulatableBackend.env));
275
+ const endpoints = backend.allEndpoints(discoveredBackend);
276
+ triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsFromEndpoints)(endpoints);
277
+ }
253
278
  const toSetup = triggerDefinitions.filter((definition) => {
254
279
  if (force) {
255
280
  return true;
@@ -305,7 +330,7 @@ class FunctionsEmulator {
305
330
  }
306
331
  }
307
332
  else {
308
- this.logger.log("WARN", `Trigger trigger "${definition.name}" has has neither "httpsTrigger" or "eventTrigger" member`);
333
+ this.logger.log("WARN", `Unsupported function type on ${definition.name}. Expected either httpsTrigger or eventTrigger.`);
309
334
  }
310
335
  const ignored = !added;
311
336
  this.addTriggerRecord(definition, { backend: emulatableBackend, ignored, url });
@@ -323,6 +348,9 @@ class FunctionsEmulator {
323
348
  this.logger.logLabeled("SUCCESS", `functions[${definition.id}]`, msg);
324
349
  }
325
350
  }
351
+ if (this.args.debugPort) {
352
+ this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
353
+ }
326
354
  }
327
355
  addRealtimeDatabaseTrigger(projectId, key, eventTrigger) {
328
356
  const databaseEmu = registry_1.EmulatorRegistry.get(types_1.Emulators.DATABASE);
@@ -331,7 +359,7 @@ class FunctionsEmulator {
331
359
  }
332
360
  const result = DATABASE_PATH_PATTERN.exec(eventTrigger.resource);
333
361
  if (result === null || result.length !== 3) {
334
- this.logger.log("WARN", `Event trigger "${key}" has malformed "resource" member. ` + `${eventTrigger.resource}`);
362
+ this.logger.log("WARN", `Event function "${key}" has malformed "resource" member. ` + `${eventTrigger.resource}`);
335
363
  return Promise.reject();
336
364
  }
337
365
  const instance = result[1];
@@ -347,7 +375,7 @@ class FunctionsEmulator {
347
375
  setTriggersPath += `?ns=${instance}`;
348
376
  }
349
377
  else {
350
- this.logger.log("WARN", `No project in use. Registering function trigger for sentinel namespace '${constants_1.Constants.DEFAULT_DATABASE_EMULATOR_NAMESPACE}'`);
378
+ this.logger.log("WARN", `No project in use. Registering function for sentinel namespace '${constants_1.Constants.DEFAULT_DATABASE_EMULATOR_NAMESPACE}'`);
351
379
  }
352
380
  return api
353
381
  .request("POST", setTriggersPath, {
@@ -362,7 +390,7 @@ class FunctionsEmulator {
362
390
  return true;
363
391
  })
364
392
  .catch((err) => {
365
- this.logger.log("WARN", "Error adding trigger: " + err);
393
+ this.logger.log("WARN", "Error adding Realtime Database function: " + err);
366
394
  throw err;
367
395
  });
368
396
  }
@@ -371,7 +399,9 @@ class FunctionsEmulator {
371
399
  if (!firestoreEmu) {
372
400
  return Promise.resolve(false);
373
401
  }
374
- const bundle = JSON.stringify({ eventTrigger });
402
+ const bundle = JSON.stringify({
403
+ eventTrigger: Object.assign(Object.assign({}, eventTrigger), { service: "firestore.googleapis.com" }),
404
+ });
375
405
  logger_1.logger.debug(`addFirestoreTrigger`, JSON.stringify(bundle));
376
406
  return api
377
407
  .request("PUT", `/emulator/v1/projects/${projectId}/triggers/${key}`, {
@@ -383,7 +413,7 @@ class FunctionsEmulator {
383
413
  return true;
384
414
  })
385
415
  .catch((err) => {
386
- this.logger.log("WARN", "Error adding trigger: " + err);
416
+ this.logger.log("WARN", "Error adding firestore function: " + err);
387
417
  throw err;
388
418
  });
389
419
  }
@@ -452,15 +482,29 @@ class FunctionsEmulator {
452
482
  const record = this.triggers[triggerKey];
453
483
  if (!record) {
454
484
  logger_1.logger.debug(`Could not find key=${triggerKey} in ${JSON.stringify(this.triggers)}`);
455
- throw new error_1.FirebaseError(`No trigger with key ${triggerKey}`);
485
+ throw new error_1.FirebaseError(`No function with key ${triggerKey}`);
456
486
  }
457
487
  return record;
458
488
  }
459
489
  getTriggerKey(def) {
460
490
  return def.eventTrigger ? `${def.id}-${this.triggerGeneration}` : def.id;
461
491
  }
462
- getBackends() {
463
- return this.args.emulatableBackends;
492
+ getBackendInfo() {
493
+ const cf3Triggers = Object.values(this.triggers)
494
+ .filter((t) => !t.backend.extensionInstanceId)
495
+ .map((t) => t.def);
496
+ return this.args.emulatableBackends.map((e) => {
497
+ var _a;
498
+ return {
499
+ directory: e.functionsDir,
500
+ env: e.env,
501
+ extensionInstanceId: e.extensionInstanceId,
502
+ extension: e.extension,
503
+ extensionVersion: e.extensionVersion,
504
+ extensionSpec: e.extensionSpec,
505
+ functionTriggers: (_a = e.predefinedTriggers) !== null && _a !== void 0 ? _a : cf3Triggers,
506
+ };
507
+ });
464
508
  }
465
509
  addTriggerRecord(def, opts) {
466
510
  const key = this.getTriggerKey(def);
@@ -475,30 +519,12 @@ class FunctionsEmulator {
475
519
  setTriggersForTesting(triggers, backend) {
476
520
  triggers.forEach((def) => this.addTriggerRecord(def, { backend, ignored: false }));
477
521
  }
478
- getBaseBundle(backend) {
522
+ getBaseBundle() {
479
523
  return {
480
- cwd: backend.functionsDir,
481
- projectId: this.args.projectId,
482
- triggerId: "",
483
- targetName: "",
484
- emulators: {
485
- firestore: registry_1.EmulatorRegistry.getInfo(types_1.Emulators.FIRESTORE),
486
- database: registry_1.EmulatorRegistry.getInfo(types_1.Emulators.DATABASE),
487
- pubsub: registry_1.EmulatorRegistry.getInfo(types_1.Emulators.PUBSUB),
488
- auth: registry_1.EmulatorRegistry.getInfo(types_1.Emulators.AUTH),
489
- storage: registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE),
490
- },
491
- adminSdkConfig: {
492
- databaseURL: this.adminSdkConfig.databaseURL,
493
- storageBucket: this.adminSdkConfig.storageBucket,
494
- },
524
+ proto: {},
495
525
  disabled_features: this.args.disabledRuntimeFeatures,
496
526
  };
497
527
  }
498
- getRequestedNodeRuntimeVersion(frb) {
499
- const pkg = require(path.join(frb.cwd, "package.json"));
500
- return frb.nodeMajorVersion || (pkg.engines && pkg.engines.node);
501
- }
502
528
  getNodeBinary(backend) {
503
529
  const pkg = require(path.join(backend.functionsDir, "package.json"));
504
530
  if ((!pkg.engines || !pkg.engines.node) && !backend.nodeMajorVersion) {
@@ -530,6 +556,16 @@ class FunctionsEmulator {
530
556
  }
531
557
  return process.execPath;
532
558
  }
559
+ getRuntimeConfig(backend) {
560
+ const configPath = `${backend.functionsDir}/.runtimeconfig.json`;
561
+ try {
562
+ const configContent = fs.readFileSync(configPath, "utf8");
563
+ return JSON.parse(configContent.toString());
564
+ }
565
+ catch (e) {
566
+ }
567
+ return {};
568
+ }
533
569
  getUserEnvs(backend) {
534
570
  const projectInfo = {
535
571
  functionsSource: backend.functionsDir,
@@ -546,17 +582,16 @@ class FunctionsEmulator {
546
582
  }
547
583
  return {};
548
584
  }
549
- getSystemEnvs(triggerDef) {
585
+ getSystemEnvs(trigger) {
550
586
  const envs = {};
551
587
  envs.GCLOUD_PROJECT = this.args.projectId;
552
588
  envs.K_REVISION = "1";
553
589
  envs.PORT = "80";
554
- if (triggerDef) {
555
- const service = triggerDef.targetName;
556
- const target = service.replace(/-/g, ".");
590
+ if (trigger) {
591
+ const target = trigger.entryPoint;
557
592
  envs.FUNCTION_TARGET = target;
558
- envs.FUNCTION_SIGNATURE_TYPE = triggerDef.signatureType;
559
- envs.K_SERVICE = service;
593
+ envs.FUNCTION_SIGNATURE_TYPE = (0, functionsEmulatorShared_1.getSignatureType)(trigger);
594
+ envs.K_SERVICE = trigger.name;
560
595
  }
561
596
  return envs;
562
597
  }
@@ -588,6 +623,9 @@ class FunctionsEmulator {
588
623
  const pubsubHost = (0, functionsEmulatorShared_1.formatHost)(pubsubEmulator);
589
624
  process.env.PUBSUB_EMULATOR_HOST = pubsubHost;
590
625
  }
626
+ if (this.args.debugPort) {
627
+ envs["FUNCTION_DEBUG_MODE"] = "true";
628
+ }
591
629
  return envs;
592
630
  }
593
631
  getFirebaseConfig() {
@@ -607,21 +645,65 @@ class FunctionsEmulator {
607
645
  projectId: this.args.projectId,
608
646
  });
609
647
  }
610
- getRuntimeEnvs(backend, triggerDef) {
611
- return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, this.getUserEnvs(backend)), this.getSystemEnvs(triggerDef)), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), backend.env);
648
+ getRuntimeEnvs(backend, trigger) {
649
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, this.getUserEnvs(backend)), this.getSystemEnvs(trigger)), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), backend.env);
612
650
  }
613
- invokeRuntime(frb, opts, runtimeEnv) {
614
- if (this.workerPool.readyForWork(frb.triggerId)) {
615
- return this.workerPool.submitWork(frb.triggerId, frb, opts);
651
+ async resolveSecretEnvs(backend, trigger) {
652
+ let secretEnvs = {};
653
+ try {
654
+ const data = fs.readFileSync(path.join(backend.functionsDir, LOCAL_SECRETS_FILE), "utf8");
655
+ secretEnvs = functionsEnv.parseStrict(data);
656
+ }
657
+ catch (e) {
658
+ if (e.code !== "ENOENT") {
659
+ this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${LOCAL_SECRETS_FILE}: ${e.message}`);
660
+ }
616
661
  }
662
+ if (trigger) {
663
+ const secrets = trigger.secretEnvironmentVariables || [];
664
+ const accesses = secrets
665
+ .filter((s) => !secretEnvs[s.secret])
666
+ .map(async (s) => {
667
+ this.logger.logLabeled("INFO", "functions", `Trying to access secret ${s.key}@latest`);
668
+ const value = await (0, secretManager_1.accessSecretVersion)(this.getProjectId(), s.key, "latest");
669
+ return [s.secret, value];
670
+ });
671
+ const accessResults = await (0, utils_1.allSettled)(accesses);
672
+ const errs = [];
673
+ for (const result of accessResults) {
674
+ if (result.status === "rejected") {
675
+ errs.push(result.reason);
676
+ }
677
+ else {
678
+ const [k, v] = result.value;
679
+ secretEnvs[k] = v;
680
+ }
681
+ }
682
+ if (errs.length > 0) {
683
+ this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
684
+ "Make sure the credential used for the Functions Emulator have access " +
685
+ `or provide override values in ${LOCAL_SECRETS_FILE}:\n\t` +
686
+ errs.join("\n\t"));
687
+ }
688
+ }
689
+ return secretEnvs;
690
+ }
691
+ async invokeRuntime(backend, trigger, frb, opts) {
692
+ if (!this.workerPool.readyForWork(trigger.id)) {
693
+ await this.startRuntime(backend, opts, trigger);
694
+ }
695
+ return this.workerPool.submitWork(trigger.id, frb, opts);
696
+ }
697
+ async startRuntime(backend, opts, trigger) {
698
+ var _a;
617
699
  const emitter = new events_1.EventEmitter();
618
700
  const args = [path.join(__dirname, "functionsEmulatorRuntime")];
619
701
  if (opts.ignore_warnings) {
620
702
  args.unshift("--no-warnings");
621
703
  }
622
704
  if (this.args.debugPort) {
623
- if (process.env.FIREPIT_VERSION && process.execPath == opts.nodeBinary) {
624
- const requestedMajorNodeVersion = this.getRequestedNodeRuntimeVersion(frb);
705
+ if (process.env.FIREPIT_VERSION && process.execPath === opts.nodeBinary) {
706
+ const requestedMajorNodeVersion = this.getNodeBinary(backend);
625
707
  this.logger.log("WARN", `To enable function inspection, please run "${process.execPath} is:npm i node@${requestedMajorNodeVersion} --save-dev" in your functions directory`);
626
708
  }
627
709
  else {
@@ -629,15 +711,17 @@ class FunctionsEmulator {
629
711
  args.unshift(`--inspect=${host}:${this.args.debugPort}`);
630
712
  }
631
713
  }
632
- const pnpPath = path.join(frb.cwd, ".pnp.js");
714
+ const pnpPath = path.join(backend.functionsDir, ".pnp.js");
633
715
  if (fs.existsSync(pnpPath)) {
634
716
  emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).logLabeled("WARN_ONCE", "functions", "Detected yarn@2 with PnP. " +
635
717
  "Cloud Functions for Firebase requires a node_modules folder to work correctly and is therefore incompatible with PnP. " +
636
718
  "See https://yarnpkg.com/getting-started/migration#step-by-step for more information.");
637
719
  }
720
+ const runtimeEnv = this.getRuntimeEnvs(backend, trigger);
721
+ const secretEnvs = await this.resolveSecretEnvs(backend, trigger);
638
722
  const childProcess = spawn(opts.nodeBinary, args, {
639
- env: Object.assign(Object.assign({ node: opts.nodeBinary }, process.env), (runtimeEnv !== null && runtimeEnv !== void 0 ? runtimeEnv : {})),
640
- cwd: frb.cwd,
723
+ cwd: backend.functionsDir,
724
+ env: Object.assign(Object.assign(Object.assign({ node: opts.nodeBinary }, process.env), runtimeEnv), secretEnvs),
641
725
  stdio: ["pipe", "pipe", "pipe", "ipc"],
642
726
  });
643
727
  if (!childProcess.stderr) {
@@ -668,6 +752,7 @@ class FunctionsEmulator {
668
752
  childProcess.on("exit", resolve);
669
753
  }),
670
754
  events: emitter,
755
+ cwd: backend.functionsDir,
671
756
  shutdown: () => {
672
757
  childProcess.kill();
673
758
  },
@@ -679,8 +764,12 @@ class FunctionsEmulator {
679
764
  return childProcess.send(JSON.stringify(args));
680
765
  },
681
766
  };
682
- this.workerPool.addWorker(frb.triggerId, runtime);
683
- return this.workerPool.submitWork(frb.triggerId, frb, opts);
767
+ const extensionLogInfo = {
768
+ instanceId: backend.extensionInstanceId,
769
+ ref: (_a = backend.extensionVersion) === null || _a === void 0 ? void 0 : _a.ref,
770
+ };
771
+ this.workerPool.addWorker(trigger === null || trigger === void 0 ? void 0 : trigger.id, runtime, extensionLogInfo);
772
+ return;
684
773
  }
685
774
  async disableBackgroundTriggers() {
686
775
  Object.values(this.triggers).forEach((record) => {
@@ -706,7 +795,7 @@ class FunctionsEmulator {
706
795
  }
707
796
  const trigger = record.def;
708
797
  const service = (0, functionsEmulatorShared_1.getFunctionService)(trigger);
709
- const worker = this.startFunctionRuntime(record.backend, trigger.id, trigger.name, (0, functionsEmulatorShared_1.getSignatureType)(trigger), proto);
798
+ const worker = await this.invokeTrigger(record.backend, trigger, proto);
710
799
  return new Promise((resolve, reject) => {
711
800
  if (projectId !== this.args.projectId) {
712
801
  if (service !== constants_1.Constants.SERVICE_REALTIME_DATABASE) {
@@ -725,7 +814,7 @@ class FunctionsEmulator {
725
814
  reject({ code: 500, body: el.text });
726
815
  }
727
816
  });
728
- track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
817
+ void track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
729
818
  worker.waitForDone().then(() => {
730
819
  resolve({ status: "acknowledged" });
731
820
  });
@@ -740,7 +829,7 @@ class FunctionsEmulator {
740
829
  return registry_1.EmulatorRegistry.getInfo(emulator);
741
830
  }
742
831
  tokenFromAuthHeader(authHeader) {
743
- const match = authHeader.match(/^Bearer (.*)$/);
832
+ const match = /^Bearer (.*)$/.exec(authHeader);
744
833
  if (!match) {
745
834
  return;
746
835
  }
@@ -772,7 +861,7 @@ class FunctionsEmulator {
772
861
  if (!this.triggers[triggerId]) {
773
862
  res
774
863
  .status(404)
775
- .send(`Function ${triggerId} does not exist, valid triggers are: ${Object.keys(this.triggers).join(", ")}`);
864
+ .send(`Function ${triggerId} does not exist, valid functions are: ${Object.keys(this.triggers).join(", ")}`);
776
865
  return;
777
866
  }
778
867
  const record = this.getTriggerRecordByKey(triggerId);
@@ -793,14 +882,14 @@ class FunctionsEmulator {
793
882
  req.headers[functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER] = encodeURIComponent(JSON.stringify(contextAuth));
794
883
  }
795
884
  }
796
- const worker = this.startFunctionRuntime(record.backend, trigger.id, trigger.name, "http", undefined);
885
+ const worker = await this.invokeTrigger(record.backend, trigger);
797
886
  worker.onLogs((el) => {
798
887
  if (el.level === "FATAL") {
799
888
  res.status(500).send(el.text);
800
889
  }
801
890
  });
802
891
  await worker.waitForSocketReady();
803
- track(EVENT_INVOKE, "https");
892
+ void track(EVENT_INVOKE, "https");
804
893
  this.logger.log("DEBUG", `[functions] Runtime ready! Sending request!`);
805
894
  if (!worker.lastArgs) {
806
895
  throw new error_1.FirebaseError("Cannot execute on a worker with no arguments");