firebase-tools 10.4.2 → 10.7.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 (108) hide show
  1. package/lib/bin/firebase.js +1 -1
  2. package/lib/command.js +4 -4
  3. package/lib/commands/deploy.js +1 -1
  4. package/lib/commands/emulators-start.js +13 -3
  5. package/lib/commands/ext-configure.js +15 -5
  6. package/lib/commands/ext-dev-emulators-start.js +5 -1
  7. package/lib/commands/ext-export.js +6 -5
  8. package/lib/commands/ext-install.js +28 -44
  9. package/lib/commands/ext-update.js +9 -1
  10. package/lib/commands/functions-delete.js +2 -5
  11. package/lib/commands/functions-secrets-destroy.js +23 -3
  12. package/lib/commands/functions-secrets-prune.js +15 -12
  13. package/lib/commands/functions-secrets-set.js +51 -4
  14. package/lib/commands/hosting-channel-deploy.js +2 -2
  15. package/lib/deploy/database/deploy.js +4 -0
  16. package/lib/deploy/database/index.js +1 -0
  17. package/lib/deploy/extensions/deploy.js +4 -4
  18. package/lib/deploy/extensions/deploymentSummary.js +8 -5
  19. package/lib/deploy/extensions/planner.js +36 -9
  20. package/lib/deploy/extensions/prepare.js +1 -1
  21. package/lib/deploy/extensions/secrets.js +2 -2
  22. package/lib/deploy/extensions/tasks.js +60 -21
  23. package/lib/deploy/functions/backend.js +17 -6
  24. package/lib/deploy/functions/build.js +162 -0
  25. package/lib/deploy/functions/checkIam.js +6 -5
  26. package/lib/deploy/functions/deploy.js +14 -15
  27. package/lib/deploy/functions/ensure.js +4 -4
  28. package/lib/deploy/functions/functionsDeployHelper.js +54 -23
  29. package/lib/deploy/functions/prepare.js +92 -39
  30. package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
  31. package/lib/deploy/functions/pricing.js +6 -3
  32. package/lib/deploy/functions/prompts.js +1 -7
  33. package/lib/deploy/functions/release/fabricator.js +44 -5
  34. package/lib/deploy/functions/release/index.js +31 -6
  35. package/lib/deploy/functions/release/planner.js +10 -8
  36. package/lib/deploy/functions/release/reporter.js +14 -11
  37. package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
  38. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +61 -13
  39. package/lib/deploy/functions/runtimes/node/index.js +1 -1
  40. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
  41. package/lib/deploy/functions/runtimes/node/parseTriggers.js +29 -24
  42. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  43. package/lib/deploy/functions/services/auth.js +95 -0
  44. package/lib/deploy/functions/services/index.js +41 -21
  45. package/lib/deploy/functions/services/storage.js +1 -6
  46. package/lib/deploy/functions/validate.js +8 -5
  47. package/lib/deploy/hosting/args.js +2 -0
  48. package/lib/deploy/hosting/convertConfig.js +37 -8
  49. package/lib/deploy/hosting/deploy.js +3 -3
  50. package/lib/deploy/hosting/prepare.js +2 -2
  51. package/lib/deploy/hosting/release.js +6 -2
  52. package/lib/deploy/index.js +82 -93
  53. package/lib/deploy/remoteconfig/deploy.js +4 -0
  54. package/lib/deploy/remoteconfig/index.js +3 -1
  55. package/lib/emulator/auth/operations.js +26 -20
  56. package/lib/emulator/auth/state.js +79 -43
  57. package/lib/emulator/auth/utils.js +3 -25
  58. package/lib/emulator/commandUtils.js +72 -2
  59. package/lib/emulator/controller.js +14 -5
  60. package/lib/emulator/downloadableEmulators.js +47 -24
  61. package/lib/emulator/extensions/postinstall.js +41 -0
  62. package/lib/emulator/extensions/validation.js +2 -2
  63. package/lib/emulator/extensionsEmulator.js +85 -21
  64. package/lib/emulator/functionsEmulator.js +79 -7
  65. package/lib/emulator/functionsEmulatorShared.js +36 -21
  66. package/lib/emulator/registry.js +34 -12
  67. package/lib/emulator/shared/request.js +19 -0
  68. package/lib/emulator/storage/apis/firebase.js +32 -35
  69. package/lib/emulator/storage/apis/gcloud.js +84 -66
  70. package/lib/emulator/storage/files.js +56 -52
  71. package/lib/emulator/storage/index.js +23 -3
  72. package/lib/emulator/storage/metadata.js +18 -8
  73. package/lib/emulator/storage/rules/manager.js +7 -17
  74. package/lib/emulator/storage/rules/utils.js +11 -3
  75. package/lib/emulator/storage/server.js +38 -12
  76. package/lib/ensureApiEnabled.js +8 -4
  77. package/lib/extensions/askUserForParam.js +14 -11
  78. package/lib/extensions/changelog.js +1 -1
  79. package/lib/extensions/emulator/optionsHelper.js +9 -10
  80. package/lib/extensions/emulator/specHelper.js +7 -1
  81. package/lib/extensions/emulator/triggerHelper.js +11 -14
  82. package/lib/extensions/extensionsApi.js +2 -1
  83. package/lib/extensions/extensionsHelper.js +30 -24
  84. package/lib/extensions/manifest.js +28 -8
  85. package/lib/extensions/paramHelper.js +19 -13
  86. package/lib/extensions/provisioningHelper.js +2 -2
  87. package/lib/extensions/warnings.js +3 -3
  88. package/lib/functions/env.js +10 -2
  89. package/lib/functions/events/index.js +7 -0
  90. package/lib/functions/events/v1.js +6 -0
  91. package/lib/functions/projectConfig.js +24 -3
  92. package/lib/functions/runtimeConfigExport.js +10 -6
  93. package/lib/functions/secrets.js +99 -6
  94. package/lib/gcp/cloudfunctions.js +37 -18
  95. package/lib/gcp/cloudfunctionsv2.js +41 -25
  96. package/lib/gcp/cloudtasks.js +5 -3
  97. package/lib/gcp/identityPlatform.js +44 -0
  98. package/lib/gcp/secretManager.js +2 -2
  99. package/lib/metaprogramming.js +2 -0
  100. package/lib/previews.js +1 -1
  101. package/lib/serve/hosting.js +25 -12
  102. package/lib/serve/index.js +6 -0
  103. package/lib/track.js +15 -21
  104. package/lib/utils.js +30 -1
  105. package/npm-shrinkwrap.json +44 -2
  106. package/package.json +4 -1
  107. package/schema/firebase-config.json +6 -0
  108. package/lib/emulator/storage/list.js +0 -18
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.emulatorExec = exports.shutdownWhenKilled = exports.setExportOnExitOptions = exports.parseInspectionPort = exports.beforeEmulatorCommand = exports.warnEmulatorNotSupported = exports.printNoticeIfEmulated = exports.DESC_TEST_PARAMS = exports.FLAG_TEST_PARAMS = exports.DESC_TEST_CONFIG = exports.FLAG_TEST_CONFIG = exports.DESC_UI = exports.FLAG_UI = exports.EXPORT_ON_EXIT_CWD_DANGER = exports.EXPORT_ON_EXIT_USAGE_ERROR = exports.DESC_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT_NAME = exports.DESC_IMPORT = exports.FLAG_IMPORT = exports.DESC_INSPECT_FUNCTIONS = exports.FLAG_INSPECT_FUNCTIONS = exports.DESC_ONLY = exports.FLAG_ONLY = void 0;
3
+ exports.JAVA_DEPRECATION_WARNING = exports.checkJavaSupported = exports.emulatorExec = exports.shutdownWhenKilled = exports.setExportOnExitOptions = exports.parseInspectionPort = exports.beforeEmulatorCommand = exports.warnEmulatorNotSupported = exports.printNoticeIfEmulated = exports.DESC_TEST_PARAMS = exports.FLAG_TEST_PARAMS = exports.DESC_TEST_CONFIG = exports.FLAG_TEST_CONFIG = exports.DESC_UI = exports.FLAG_UI = exports.EXPORT_ON_EXIT_CWD_DANGER = exports.EXPORT_ON_EXIT_USAGE_ERROR = exports.DESC_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT_NAME = exports.DESC_IMPORT = exports.FLAG_IMPORT = exports.DESC_INSPECT_FUNCTIONS = exports.FLAG_INSPECT_FUNCTIONS = exports.DESC_ONLY = exports.FLAG_ONLY = void 0;
4
4
  const clc = require("cli-color");
5
5
  const childProcess = require("child_process");
6
6
  const controller = require("../emulator/controller");
@@ -287,15 +287,19 @@ async function emulatorExec(script, options) {
287
287
  extraEnv.GCLOUD_PROJECT = projectId;
288
288
  }
289
289
  let exitCode = 0;
290
+ let deprecationNotices;
290
291
  try {
291
292
  const showUI = !!options.ui;
292
- await controller.startAll(options, showUI);
293
+ ({ deprecationNotices } = await controller.startAll(options, showUI));
293
294
  exitCode = await runScript(script, extraEnv);
294
295
  await (0, controller_1.onExit)(options);
295
296
  }
296
297
  finally {
297
298
  await controller.cleanShutdown();
298
299
  }
300
+ for (const notice of deprecationNotices) {
301
+ utils.logLabeledWarning("emulators", notice, "warn");
302
+ }
299
303
  if (exitCode !== 0) {
300
304
  throw new error_1.FirebaseError(`Script "${clc.bold(script)}" exited with code ${exitCode}`, {
301
305
  exit: exitCode,
@@ -303,3 +307,69 @@ async function emulatorExec(script, options) {
303
307
  }
304
308
  }
305
309
  exports.emulatorExec = emulatorExec;
310
+ const JAVA_VERSION_REGEX = /version "([1-9][0-9]*)/;
311
+ const MIN_SUPPORTED_JAVA_MAJOR_VERSION = 11;
312
+ const JAVA_HINT = "Please make sure Java is installed and on your system PATH.";
313
+ async function checkJavaSupported() {
314
+ return new Promise((resolve, reject) => {
315
+ var _a, _b;
316
+ let child;
317
+ try {
318
+ child = childProcess.spawn("java", ["-Duser.language=en", "-Dfile.encoding=UTF-8", "-version"], {
319
+ stdio: ["inherit", "pipe", "pipe"],
320
+ });
321
+ }
322
+ catch (err) {
323
+ return reject(new error_1.FirebaseError(`Could not spawn \`java -version\`. ${JAVA_HINT}`, { original: err }));
324
+ }
325
+ let output = "";
326
+ let error = "";
327
+ (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (data) => {
328
+ const str = data.toString("utf8");
329
+ logger_1.logger.debug(str);
330
+ output += str;
331
+ });
332
+ (_b = child.stderr) === null || _b === void 0 ? void 0 : _b.on("data", (data) => {
333
+ const str = data.toString("utf8");
334
+ logger_1.logger.debug(str);
335
+ error += str;
336
+ });
337
+ child.once("error", (err) => {
338
+ reject(new error_1.FirebaseError(`Could not spawn \`java -version\`. ${JAVA_HINT}`, { original: err }));
339
+ });
340
+ child.once("exit", (code, signal) => {
341
+ if (signal) {
342
+ reject(new error_1.FirebaseError(`Process \`java -version\` was killed by signal ${signal}.`));
343
+ }
344
+ else if (code && code !== 0) {
345
+ reject(new error_1.FirebaseError(`Process \`java -version\` has exited with code ${code}. ${JAVA_HINT}\n` +
346
+ `-----Original stdout-----\n${output}` +
347
+ `-----Original stderr-----\n${error}`));
348
+ }
349
+ else {
350
+ resolve(`${output}\n${error}`);
351
+ }
352
+ });
353
+ }).then((output) => {
354
+ const match = output.match(JAVA_VERSION_REGEX);
355
+ if (match) {
356
+ const version = match[1];
357
+ const versionInt = parseInt(version, 10);
358
+ if (!versionInt) {
359
+ utils.logLabeledWarning("emulators", `Failed to parse Java version. Got "${match[0]}".`, "warn");
360
+ }
361
+ else {
362
+ logger_1.logger.debug(`Parsed Java major version: ${versionInt}`);
363
+ return versionInt >= MIN_SUPPORTED_JAVA_MAJOR_VERSION;
364
+ }
365
+ }
366
+ else {
367
+ logger_1.logger.debug("java -version outputs:", output);
368
+ logger_1.logger.warn(`Failed to parse Java version.`);
369
+ }
370
+ return false;
371
+ });
372
+ }
373
+ exports.checkJavaSupported = checkJavaSupported;
374
+ exports.JAVA_DEPRECATION_WARNING = "Support for Java version <= 10 will be dropped soon in firebase-tools@11. " +
375
+ "Please upgrade to Java version 11 or above to continue using the emulators.";
@@ -6,7 +6,7 @@ const clc = require("cli-color");
6
6
  const fs = require("fs");
7
7
  const path = require("path");
8
8
  const logger_1 = require("../logger");
9
- const track = require("../track");
9
+ const track_1 = require("../track");
10
10
  const utils = require("../utils");
11
11
  const registry_1 = require("./registry");
12
12
  const types_1 = require("./types");
@@ -39,6 +39,7 @@ const auth_2 = require("../auth");
39
39
  const extensionsEmulator_1 = require("./extensionsEmulator");
40
40
  const previews_1 = require("../previews");
41
41
  const projectConfig_1 = require("../functions/projectConfig");
42
+ const downloadableEmulators_1 = require("./downloadableEmulators");
42
43
  const START_LOGGING_EMULATOR = utils.envOverride("START_LOGGING_EMULATOR", "false", (val) => val === "true");
43
44
  async function getAndCheckAddress(emulator, options) {
44
45
  var _a, _b, _c, _d;
@@ -94,7 +95,7 @@ async function getAndCheckAddress(emulator, options) {
94
95
  }
95
96
  async function startEmulator(instance) {
96
97
  const name = instance.getName();
97
- void track("Emulator Run", name);
98
+ void (0, track_1.track)("Emulator Run", name);
98
99
  await registry_1.EmulatorRegistry.start(instance);
99
100
  }
100
101
  exports.startEmulator = startEmulator;
@@ -216,6 +217,13 @@ async function startAll(options, showUI = true) {
216
217
  if (targets.length === 0) {
217
218
  throw new error_1.FirebaseError(`No emulators to start, run ${clc.bold("firebase init emulators")} to get started.`);
218
219
  }
220
+ const deprecationNotices = [];
221
+ if (targets.some(downloadableEmulators_1.requiresJava)) {
222
+ if (!(await commandUtils.checkJavaSupported())) {
223
+ utils.logLabeledWarning("emulators", commandUtils_1.JAVA_DEPRECATION_WARNING, "warn");
224
+ deprecationNotices.push(commandUtils_1.JAVA_DEPRECATION_WARNING);
225
+ }
226
+ }
219
227
  const hubLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.HUB);
220
228
  hubLogger.logLabeled("BULLET", "emulators", `Starting emulators: ${targets.join(", ")}`);
221
229
  const projectId = (0, projectUtils_1.getProjectId)(options) || "";
@@ -240,7 +248,7 @@ async function startAll(options, showUI = true) {
240
248
  if (shouldStart(options, types_1.Emulators.HUB)) {
241
249
  const hubAddr = await getAndCheckAddress(types_1.Emulators.HUB, options);
242
250
  const hub = new hub_1.EmulatorHub(Object.assign({ projectId }, hubAddr));
243
- void track("emulators:start", "hub");
251
+ void (0, track_1.track)("emulators:start", "hub");
244
252
  await startEmulator(hub);
245
253
  }
246
254
  let exportMetadata = {
@@ -286,8 +294,8 @@ async function startAll(options, showUI = true) {
286
294
  const extensionsBackends = await extensionEmulator.getExtensionBackends();
287
295
  const filteredExtensionsBackends = extensionEmulator.filterUnemulatedTriggers(options, extensionsBackends);
288
296
  emulatableBackends.push(...filteredExtensionsBackends);
289
- void track("Emulator Run", types_1.Emulators.EXTENSIONS);
290
- registry_1.EmulatorRegistry.registerExtensionsEmulator();
297
+ void (0, track_1.track)("Emulator Run", types_1.Emulators.EXTENSIONS);
298
+ await startEmulator(extensionEmulator);
291
299
  }
292
300
  if (emulatableBackends.length) {
293
301
  const functionsLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
@@ -479,6 +487,7 @@ async function startAll(options, showUI = true) {
479
487
  await instance.connect();
480
488
  }
481
489
  }
490
+ return { deprecationNotices };
482
491
  }
483
492
  exports.startAll = startAll;
484
493
  async function exportEmulatorData(exportPath, options) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.start = exports.downloadIfNecessary = exports.stop = exports.getPID = exports.get = exports.getDownloadDetails = exports.handleEmulatorProcessError = exports._getCommand = exports.getLogFileName = exports.DownloadDetails = void 0;
3
+ exports.start = exports.downloadIfNecessary = exports.stop = exports.getPID = exports.get = exports.getDownloadDetails = exports.requiresJava = exports.handleEmulatorProcessError = exports._getCommand = exports.getLogFileName = exports.DownloadDetails = void 0;
4
4
  const types_1 = require("./types");
5
5
  const constants_1 = require("./constants");
6
6
  const error_1 = require("../error");
@@ -29,13 +29,13 @@ exports.DownloadDetails = {
29
29
  },
30
30
  },
31
31
  firestore: {
32
- downloadPath: path.join(CACHE_DIR, "cloud-firestore-emulator-v1.13.1.jar"),
33
- version: "1.13.1",
32
+ downloadPath: path.join(CACHE_DIR, "cloud-firestore-emulator-v1.14.3.jar"),
33
+ version: "1.14.3",
34
34
  opts: {
35
35
  cacheDir: CACHE_DIR,
36
- remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.13.1.jar",
37
- expectedSize: 60486708,
38
- expectedChecksum: "e0590880408eacb790874643147c0081",
36
+ remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.14.3.jar",
37
+ expectedSize: 60442855,
38
+ expectedChecksum: "63517534875818689639ee5dee57dd52",
39
39
  namePrefix: "cloud-firestore-emulator",
40
40
  },
41
41
  },
@@ -50,15 +50,15 @@ exports.DownloadDetails = {
50
50
  namePrefix: "cloud-storage-rules-emulator",
51
51
  },
52
52
  },
53
- ui: previews_1.previews.emulatoruisnapshot
53
+ ui: previews_1.previews.extensionsemulator
54
54
  ? {
55
- version: "SNAPSHOT",
56
- downloadPath: path.join(CACHE_DIR, "ui-vSNAPSHOT.zip"),
57
- unzipDir: path.join(CACHE_DIR, "ui-vSNAPSHOT"),
58
- binaryPath: path.join(CACHE_DIR, "ui-vSNAPSHOT", "server.bundle.js"),
55
+ version: "EXTENSIONS",
56
+ downloadPath: path.join(CACHE_DIR, "ui-vEXTENSIONS.zip"),
57
+ unzipDir: path.join(CACHE_DIR, "ui-vEXTENSIONS"),
58
+ binaryPath: path.join(CACHE_DIR, "ui-vEXTENSIONS", "server.bundle.js"),
59
59
  opts: {
60
60
  cacheDir: CACHE_DIR,
61
- remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-vSNAPSHOT.zip",
61
+ remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-vEXTENSIONS.zip",
62
62
  expectedSize: -1,
63
63
  expectedChecksum: "",
64
64
  skipCache: true,
@@ -66,19 +66,35 @@ exports.DownloadDetails = {
66
66
  namePrefix: "ui",
67
67
  },
68
68
  }
69
- : {
70
- version: "1.6.5",
71
- downloadPath: path.join(CACHE_DIR, "ui-v1.6.5.zip"),
72
- unzipDir: path.join(CACHE_DIR, "ui-v1.6.5"),
73
- binaryPath: path.join(CACHE_DIR, "ui-v1.6.5", "server.bundle.js"),
74
- opts: {
75
- cacheDir: CACHE_DIR,
76
- remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.6.5.zip",
77
- expectedSize: 3816994,
78
- expectedChecksum: "92dfff4b2ef8ab616e8a60cc93e0a00b",
79
- namePrefix: "ui",
69
+ : previews_1.previews.emulatoruisnapshot
70
+ ? {
71
+ version: "SNAPSHOT",
72
+ downloadPath: path.join(CACHE_DIR, "ui-vSNAPSHOT.zip"),
73
+ unzipDir: path.join(CACHE_DIR, "ui-vSNAPSHOT"),
74
+ binaryPath: path.join(CACHE_DIR, "ui-vSNAPSHOT", "server.bundle.js"),
75
+ opts: {
76
+ cacheDir: CACHE_DIR,
77
+ remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-vSNAPSHOT.zip",
78
+ expectedSize: -1,
79
+ expectedChecksum: "",
80
+ skipCache: true,
81
+ skipChecksumAndSize: true,
82
+ namePrefix: "ui",
83
+ },
84
+ }
85
+ : {
86
+ version: "1.6.5",
87
+ downloadPath: path.join(CACHE_DIR, "ui-v1.6.5.zip"),
88
+ unzipDir: path.join(CACHE_DIR, "ui-v1.6.5"),
89
+ binaryPath: path.join(CACHE_DIR, "ui-v1.6.5", "server.bundle.js"),
90
+ opts: {
91
+ cacheDir: CACHE_DIR,
92
+ remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.6.5.zip",
93
+ expectedSize: 3816994,
94
+ expectedChecksum: "92dfff4b2ef8ab616e8a60cc93e0a00b",
95
+ namePrefix: "ui",
96
+ },
80
97
  },
81
- },
82
98
  pubsub: {
83
99
  downloadPath: path.join(CACHE_DIR, "pubsub-emulator-0.1.0.zip"),
84
100
  version: "0.1.0",
@@ -236,6 +252,13 @@ async function handleEmulatorProcessError(emulator, err) {
236
252
  }
237
253
  }
238
254
  exports.handleEmulatorProcessError = handleEmulatorProcessError;
255
+ function requiresJava(emulator) {
256
+ if (emulator in Commands) {
257
+ return Commands[emulator].binary === "java";
258
+ }
259
+ return false;
260
+ }
261
+ exports.requiresJava = requiresJava;
239
262
  async function _runBinary(emulator, command, extraEnv) {
240
263
  return new Promise((resolve) => {
241
264
  var _a, _b;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.replaceConsoleLinks = void 0;
4
+ const registry_1 = require("../registry");
5
+ const types_1 = require("../types");
6
+ function replaceConsoleLinks(postinstall) {
7
+ const uiInfo = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.UI);
8
+ const uiUrl = uiInfo ? `http://${registry_1.EmulatorRegistry.getInfoHostString(uiInfo)}` : "unknown";
9
+ let subbedPostinstall = postinstall;
10
+ const linkReplacements = new Map([
11
+ [
12
+ /(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/storage[A-Za-z0-9\/-]*(?=[\)\]\s])/,
13
+ `${uiUrl}/${types_1.Emulators.STORAGE}`,
14
+ ],
15
+ [
16
+ /(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/firestore[A-Za-z0-9\/-]*(?=[\)\]\s])/,
17
+ `${uiUrl}/${types_1.Emulators.FIRESTORE}`,
18
+ ],
19
+ [
20
+ /(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/database[A-Za-z0-9\/-]*(?=[\)\]\s])/,
21
+ `${uiUrl}/${types_1.Emulators.DATABASE}`,
22
+ ],
23
+ [
24
+ /(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/authentication[A-Za-z0-9\/-]*(?=[\)\]\s])/,
25
+ `${uiUrl}/${types_1.Emulators.AUTH}`,
26
+ ],
27
+ [
28
+ /(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/functions[A-Za-z0-9\/-]*(?=[\)\]\s])/,
29
+ `${uiUrl}/logs`,
30
+ ],
31
+ [
32
+ /(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/extensions[A-Za-z0-9\/-]*(?=[\)\]\s])/,
33
+ `${uiUrl}/${types_1.Emulators.EXTENSIONS}`,
34
+ ],
35
+ ]);
36
+ for (const [consoleLinkRegex, replacement] of linkReplacements) {
37
+ subbedPostinstall = subbedPostinstall.replace(consoleLinkRegex, replacement);
38
+ }
39
+ return subbedPostinstall;
40
+ }
41
+ exports.replaceConsoleLinks = replaceConsoleLinks;
@@ -17,8 +17,8 @@ async function getUnemulatedAPIs(projectId, instances) {
17
17
  var _a;
18
18
  const unemulatedAPIs = {};
19
19
  for (const i of instances) {
20
- const extensionVersion = await planner.getExtensionVersion(i);
21
- for (const api of (_a = extensionVersion.spec.apis) !== null && _a !== void 0 ? _a : []) {
20
+ const extensionSpec = await planner.getExtensionSpec(i);
21
+ for (const api of (_a = extensionSpec.apis) !== null && _a !== void 0 ? _a : []) {
22
22
  if (!EMULATED_APIS.includes(api.apiName)) {
23
23
  if (unemulatedAPIs[api.apiName]) {
24
24
  unemulatedAPIs[api.apiName].instanceIds.push(i.instanceId);
@@ -18,13 +18,37 @@ const validation_1 = require("./extensions/validation");
18
18
  const ensureApiEnabled_1 = require("../ensureApiEnabled");
19
19
  const shortenUrl_1 = require("../shortenUrl");
20
20
  const constants_1 = require("./constants");
21
+ const registry_1 = require("./registry");
21
22
  class ExtensionsEmulator {
22
23
  constructor(args) {
23
24
  this.want = [];
25
+ this.backends = [];
24
26
  this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.EXTENSIONS);
25
27
  this.pendingDownloads = new Map();
26
28
  this.args = args;
27
29
  }
30
+ start() {
31
+ this.logger.logLabeled("DEBUG", "Extensions", "Started Extensions emulator, this is a noop.");
32
+ return Promise.resolve();
33
+ }
34
+ stop() {
35
+ this.logger.logLabeled("DEBUG", "Extensions", "Stopping Extensions emulator, this is a noop.");
36
+ return Promise.resolve();
37
+ }
38
+ connect() {
39
+ this.logger.logLabeled("DEBUG", "Extensions", "Connecting Extensions emulator, this is a noop.");
40
+ return Promise.resolve();
41
+ }
42
+ getInfo() {
43
+ const info = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.FUNCTIONS);
44
+ if (!info) {
45
+ throw new error_1.FirebaseError("Extensions Emulator is running but Functions emulator is not. This should never happen.");
46
+ }
47
+ return info;
48
+ }
49
+ getName() {
50
+ return types_1.Emulators.EXTENSIONS;
51
+ }
28
52
  async readManifest() {
29
53
  var _a;
30
54
  this.want = await planner.want({
@@ -37,22 +61,30 @@ class ExtensionsEmulator {
37
61
  });
38
62
  }
39
63
  async ensureSourceCode(instance) {
40
- if (!instance.ref) {
41
- throw new error_1.FirebaseError(`No ref found for ${instance.instanceId}. Emulating local extensions is not yet supported.`);
64
+ if (instance.localPath) {
65
+ if (!this.hasValidSource({ path: instance.localPath, extTarget: instance.localPath })) {
66
+ throw new error_1.FirebaseError(`Tried to emulate local extension at ${instance.localPath}, but it was missing required files.`);
67
+ }
68
+ return instance.localPath;
42
69
  }
43
- const ref = (0, refs_1.toExtensionVersionRef)(instance.ref);
44
- const cacheDir = process.env.FIREBASE_EXTENSIONS_CACHE_PATH ||
45
- path.join(os.homedir(), ".cache", "firebase", "extensions");
46
- const sourceCodePath = path.join(cacheDir, ref);
47
- if (this.pendingDownloads.get(ref)) {
48
- await this.pendingDownloads.get(ref);
70
+ else if (instance.ref) {
71
+ const ref = (0, refs_1.toExtensionVersionRef)(instance.ref);
72
+ const cacheDir = process.env.FIREBASE_EXTENSIONS_CACHE_PATH ||
73
+ path.join(os.homedir(), ".cache", "firebase", "extensions");
74
+ const sourceCodePath = path.join(cacheDir, ref);
75
+ if (this.pendingDownloads.get(ref)) {
76
+ await this.pendingDownloads.get(ref);
77
+ }
78
+ if (!this.hasValidSource({ path: sourceCodePath, extTarget: ref })) {
79
+ const promise = this.downloadSource(instance, ref, sourceCodePath);
80
+ this.pendingDownloads.set(ref, promise);
81
+ await promise;
82
+ }
83
+ return sourceCodePath;
49
84
  }
50
- if (!this.hasValidSource({ path: sourceCodePath, extRef: ref })) {
51
- const promise = this.downloadSource(instance, ref, sourceCodePath);
52
- this.pendingDownloads.set(ref, promise);
53
- await promise;
85
+ else {
86
+ throw new error_1.FirebaseError("Tried to emulate an extension instance without a ref or localPath. This should never happen.");
54
87
  }
55
- return sourceCodePath;
56
88
  }
57
89
  async downloadSource(instance, ref, sourceCodePath) {
58
90
  const extensionVersion = await planner.getExtensionVersion(instance);
@@ -71,7 +103,7 @@ class ExtensionsEmulator {
71
103
  for (const requiredFile of requiredFiles) {
72
104
  const f = path.join(args.path, requiredFile);
73
105
  if (!fs.existsSync(f)) {
74
- emulatorLogger_1.EmulatorLogger.forExtension({ ref: args.extRef }).logLabeled("BULLET", "extensions", `Detected invalid source code for ${args.extRef}, expected to find ${f}`);
106
+ emulatorLogger_1.EmulatorLogger.forExtension({ ref: args.extTarget }).logLabeled("BULLET", "extensions", `Detected invalid source code for ${args.extTarget}, expected to find ${f}`);
75
107
  return false;
76
108
  }
77
109
  }
@@ -92,27 +124,32 @@ class ExtensionsEmulator {
92
124
  async getExtensionBackends() {
93
125
  await this.readManifest();
94
126
  await this.checkAndWarnAPIs(this.want);
95
- return Promise.all(this.want.map((i) => {
127
+ this.backends = await Promise.all(this.want.map((i) => {
96
128
  return this.toEmulatableBackend(i);
97
129
  }));
130
+ return this.backends;
98
131
  }
99
132
  async toEmulatableBackend(instance) {
100
133
  const extensionDir = await this.ensureSourceCode(instance);
101
134
  const functionsDir = path.join(extensionDir, "functions");
102
135
  const env = Object.assign(this.autoPopulatedParams(instance), instance.params);
103
- const { extensionTriggers, nodeMajorVersion, nonSecretEnv, secretEnvVariables } = await (0, optionsHelper_1.getExtensionFunctionInfo)(extensionDir, instance.instanceId, env);
104
- const extension = await planner.getExtension(instance);
105
- const extensionVersion = await planner.getExtensionVersion(instance);
106
- return {
136
+ const { extensionTriggers, nodeMajorVersion, nonSecretEnv, secretEnvVariables } = await (0, optionsHelper_1.getExtensionFunctionInfo)(instance, env);
137
+ const emulatableBackend = {
107
138
  functionsDir,
108
139
  env: nonSecretEnv,
109
140
  secretEnv: secretEnvVariables,
110
141
  predefinedTriggers: extensionTriggers,
111
142
  nodeMajorVersion: nodeMajorVersion,
112
143
  extensionInstanceId: instance.instanceId,
113
- extension,
114
- extensionVersion,
115
144
  };
145
+ if (instance.ref) {
146
+ emulatableBackend.extension = await planner.getExtension(instance);
147
+ emulatableBackend.extensionVersion = await planner.getExtensionVersion(instance);
148
+ }
149
+ else if (instance.localPath) {
150
+ emulatableBackend.extensionSpec = await planner.getExtensionSpec(instance);
151
+ }
152
+ return emulatableBackend;
116
153
  }
117
154
  autoPopulatedParams(instance) {
118
155
  const projectId = this.args.projectId;
@@ -174,5 +211,32 @@ class ExtensionsEmulator {
174
211
  }
175
212
  return filteredBackends;
176
213
  }
214
+ extensionDetailsUILink(backend) {
215
+ const uiInfo = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.UI);
216
+ if (!uiInfo || !backend.extensionInstanceId) {
217
+ return "";
218
+ }
219
+ const uiUrl = registry_1.EmulatorRegistry.getInfoHostString(uiInfo);
220
+ return clc.underline(clc.bold(`http://${uiUrl}/${types_1.Emulators.EXTENSIONS}/${backend.extensionInstanceId}`));
221
+ }
222
+ extensionsInfoTable(options) {
223
+ var _a;
224
+ const filtedBackends = this.filterUnemulatedTriggers(options, this.backends);
225
+ const uiRunning = registry_1.EmulatorRegistry.isRunning(types_1.Emulators.UI);
226
+ const tableHead = ["Extension Instance Name", "Extension Ref"];
227
+ if (uiRunning) {
228
+ tableHead.push("View in Emulator UI");
229
+ }
230
+ const table = new Table({ head: tableHead, style: { head: ["yellow"] } });
231
+ for (const b of filtedBackends) {
232
+ if (b.extensionInstanceId) {
233
+ const tableEntry = [b.extensionInstanceId, ((_a = b.extensionVersion) === null || _a === void 0 ? void 0 : _a.ref) || "Local Extension"];
234
+ if (uiRunning)
235
+ tableEntry.push(this.extensionDetailsUILink(b));
236
+ table.push(tableEntry);
237
+ }
238
+ }
239
+ return table.toString();
240
+ }
177
241
  }
178
242
  exports.ExtensionsEmulator = ExtensionsEmulator;
@@ -13,7 +13,7 @@ const url_1 = require("url");
13
13
  const events_1 = require("events");
14
14
  const api = require("../api");
15
15
  const logger_1 = require("../logger");
16
- const track = require("../track");
16
+ const track_1 = require("../track");
17
17
  const constants_1 = require("./constants");
18
18
  const types_1 = require("./types");
19
19
  const chokidar = require("chokidar");
@@ -34,6 +34,7 @@ const secretManager_1 = require("../gcp/secretManager");
34
34
  const runtimes = require("../deploy/functions/runtimes");
35
35
  const backend = require("../deploy/functions/backend");
36
36
  const functionsEnv = require("../functions/env");
37
+ const v1_1 = require("../functions/events/v1");
37
38
  const EVENT_INVOKE = "functions:invoke";
38
39
  const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
39
40
  class FunctionsEmulator {
@@ -54,6 +55,16 @@ class FunctionsEmulator {
54
55
  : types_1.FunctionsExecutionMode.AUTO;
55
56
  this.workerPool = new functionsRuntimeWorker_1.RuntimeWorkerPool(mode);
56
57
  this.workQueue = new workQueue_1.WorkQueue(mode);
58
+ this.blockingFunctionsConfig = {
59
+ triggers: {
60
+ beforeCreate: {
61
+ functionUri: "",
62
+ },
63
+ beforeSignIn: {
64
+ functionUri: "",
65
+ },
66
+ },
67
+ };
57
68
  }
58
69
  static getHttpFunctionUrl(host, port, projectId, name, region) {
59
70
  return `http://${host}:${port}/${projectId}/${region}/${name}`;
@@ -229,6 +240,7 @@ class FunctionsEmulator {
229
240
  loadTriggerPromises.push(this.loadTriggers(backend, true));
230
241
  }
231
242
  await Promise.all(loadTriggerPromises);
243
+ await this.performPostLoadOperations();
232
244
  return;
233
245
  }
234
246
  async stop() {
@@ -271,6 +283,7 @@ class FunctionsEmulator {
271
283
  logger_1.logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
272
284
  const discoveredBackend = await runtimeDelegate.discoverSpec(runtimeConfig, Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), emulatableBackend.env));
273
285
  const endpoints = backend.allEndpoints(discoveredBackend);
286
+ (0, functionsEmulatorShared_1.prepareEndpoints)(endpoints);
274
287
  triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsFromEndpoints)(endpoints);
275
288
  }
276
289
  const toSetup = triggerDefinitions.filter((definition) => {
@@ -327,6 +340,11 @@ class FunctionsEmulator {
327
340
  break;
328
341
  }
329
342
  }
343
+ else if (definition.blockingTrigger) {
344
+ const { host, port } = this.getInfo();
345
+ url = FunctionsEmulator.getHttpFunctionUrl(host, port, this.args.projectId, definition.name, definition.region);
346
+ added = this.addBlockingTrigger(url, definition.blockingTrigger);
347
+ }
330
348
  else {
331
349
  this.logger.log("WARN", `Unsupported function type on ${definition.name}. Expected either httpsTrigger or eventTrigger.`);
332
350
  }
@@ -350,6 +368,34 @@ class FunctionsEmulator {
350
368
  this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
351
369
  }
352
370
  }
371
+ async performPostLoadOperations() {
372
+ var _a, _b, _c, _d;
373
+ if (((_b = (_a = this.blockingFunctionsConfig.triggers) === null || _a === void 0 ? void 0 : _a.beforeCreate) === null || _b === void 0 ? void 0 : _b.functionUri) === "" &&
374
+ ((_d = (_c = this.blockingFunctionsConfig.triggers) === null || _c === void 0 ? void 0 : _c.beforeSignIn) === null || _d === void 0 ? void 0 : _d.functionUri) === "") {
375
+ return;
376
+ }
377
+ const authEmu = registry_1.EmulatorRegistry.get(types_1.Emulators.AUTH);
378
+ if (!authEmu) {
379
+ return;
380
+ }
381
+ const path = `/identitytoolkit.googleapis.com/v2/projects/${this.getProjectId()}/config?updateMask=blockingFunctions`;
382
+ try {
383
+ await api.request("PATCH", path, {
384
+ origin: `http://${registry_1.EmulatorRegistry.getInfoHostString(authEmu.getInfo())}`,
385
+ headers: {
386
+ Authorization: "Bearer owner",
387
+ },
388
+ data: {
389
+ blockingFunctions: this.blockingFunctionsConfig,
390
+ },
391
+ json: true,
392
+ });
393
+ }
394
+ catch (err) {
395
+ this.logger.log("WARN", "Error updating blocking functions config to the auth emulator: " + err);
396
+ throw err;
397
+ }
398
+ }
353
399
  addRealtimeDatabaseTrigger(projectId, key, eventTrigger) {
354
400
  const databaseEmu = registry_1.EmulatorRegistry.get(types_1.Emulators.DATABASE);
355
401
  if (!databaseEmu) {
@@ -416,11 +462,10 @@ class FunctionsEmulator {
416
462
  });
417
463
  }
418
464
  async addPubsubTrigger(triggerName, key, eventTrigger, signatureType, schedule) {
419
- const pubsubPort = registry_1.EmulatorRegistry.getPort(types_1.Emulators.PUBSUB);
420
- if (!pubsubPort) {
465
+ const pubsubEmulator = registry_1.EmulatorRegistry.get(types_1.Emulators.PUBSUB);
466
+ if (!pubsubEmulator) {
421
467
  return false;
422
468
  }
423
- const pubsubEmulator = registry_1.EmulatorRegistry.get(types_1.Emulators.PUBSUB);
424
469
  logger_1.logger.debug(`addPubsubTrigger`, JSON.stringify({ eventTrigger }));
425
470
  const resource = eventTrigger.resource;
426
471
  let topic;
@@ -458,6 +503,31 @@ class FunctionsEmulator {
458
503
  this.multicastTriggers[eventTriggerId] = triggers;
459
504
  return true;
460
505
  }
506
+ addBlockingTrigger(url, blockingTrigger) {
507
+ logger_1.logger.debug(`addBlockingTrigger`, JSON.stringify({ blockingTrigger }));
508
+ const eventType = blockingTrigger.eventType;
509
+ if (v1_1.AUTH_BLOCKING_EVENTS.includes(eventType)) {
510
+ if (blockingTrigger.eventType === v1_1.BEFORE_CREATE_EVENT) {
511
+ this.blockingFunctionsConfig.triggers = Object.assign(Object.assign({}, this.blockingFunctionsConfig.triggers), { beforeCreate: {
512
+ functionUri: url,
513
+ } });
514
+ }
515
+ else {
516
+ this.blockingFunctionsConfig.triggers = Object.assign(Object.assign({}, this.blockingFunctionsConfig.triggers), { beforeSignIn: {
517
+ functionUri: url,
518
+ } });
519
+ }
520
+ this.blockingFunctionsConfig.forwardInboundCredentials = {
521
+ accessToken: blockingTrigger.options.accessToken,
522
+ idToken: blockingTrigger.options.idToken,
523
+ refreshToken: blockingTrigger.options.refreshToken,
524
+ };
525
+ }
526
+ else {
527
+ return false;
528
+ }
529
+ return true;
530
+ }
461
531
  getProjectId() {
462
532
  return this.args.projectId;
463
533
  }
@@ -781,7 +851,9 @@ class FunctionsEmulator {
781
851
  for (const backend of this.args.emulatableBackends) {
782
852
  loadTriggerPromises.push(this.loadTriggers(backend));
783
853
  }
784
- return Promise.all(loadTriggerPromises);
854
+ await Promise.all(loadTriggerPromises);
855
+ await this.performPostLoadOperations();
856
+ return;
785
857
  }
786
858
  async handleBackgroundTrigger(projectId, triggerKey, proto) {
787
859
  const record = this.getTriggerRecordByKey(triggerKey);
@@ -809,7 +881,7 @@ class FunctionsEmulator {
809
881
  reject({ code: 500, body: el.text });
810
882
  }
811
883
  });
812
- void track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
884
+ void (0, track_1.track)(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
813
885
  worker.waitForDone().then(() => {
814
886
  resolve({ status: "acknowledged" });
815
887
  });
@@ -884,7 +956,7 @@ class FunctionsEmulator {
884
956
  }
885
957
  });
886
958
  await worker.waitForSocketReady();
887
- void track(EVENT_INVOKE, "https");
959
+ void (0, track_1.track)(EVENT_INVOKE, "https");
888
960
  this.logger.log("DEBUG", `[functions] Runtime ready! Sending request!`);
889
961
  if (!worker.lastArgs) {
890
962
  throw new error_1.FirebaseError("Cannot execute on a worker with no arguments");