firebase-tools 13.6.1 → 13.7.1

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.
package/lib/api.js CHANGED
@@ -62,7 +62,7 @@ const cloudbuildOrigin = () => utils.envOverride("FIREBASE_CLOUDBUILD_URL", "htt
62
62
  exports.cloudbuildOrigin = cloudbuildOrigin;
63
63
  const developerConnectOrigin = () => utils.envOverride("FIREBASE_DEVELOPERCONNECT_URL", "https://developerconnect.googleapis.com");
64
64
  exports.developerConnectOrigin = developerConnectOrigin;
65
- const developerConnectP4SAOrigin = () => utils.envOverride("FIREBASE_DEVELOPERCONNECT_P4SA_URL", "gcp-sa-developerconnect.iam.gserviceaccount.com");
65
+ const developerConnectP4SAOrigin = () => utils.envOverride("FIREBASE_DEVELOPERCONNECT_P4SA_URL", "gcp-sa-devconnect.iam.gserviceaccount.com");
66
66
  exports.developerConnectP4SAOrigin = developerConnectP4SAOrigin;
67
67
  const cloudschedulerOrigin = () => utils.envOverride("FIREBASE_CLOUDSCHEDULER_URL", "https://cloudscheduler.googleapis.com");
68
68
  exports.cloudschedulerOrigin = cloudschedulerOrigin;
@@ -58,7 +58,6 @@ async function linkGitHubRepository(projectId, location) {
58
58
  const oauthConn = await getOrCreateOauthConnection(projectId, location);
59
59
  const existingConns = await listAppHostingConnections(projectId);
60
60
  if (existingConns.length === 0) {
61
- utils.logBullet("no connections exist");
62
61
  existingConns.push(await createFullyInstalledConnection(projectId, location, generateConnectionId(), oauthConn));
63
62
  }
64
63
  let repoCloneUri;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const logger_1 = require("../logger");
6
+ const projectUtils_1 = require("../projectUtils");
7
+ const secretManager_1 = require("../gcp/secretManager");
8
+ const requireAuth_1 = require("../requireAuth");
9
+ const secretManager = require("../gcp/secretManager");
10
+ const requirePermissions_1 = require("../requirePermissions");
11
+ exports.command = new command_1.Command("apphosting:secrets:access <secretName>[@version]")
12
+ .description("Access secret value given secret and its version. Defaults to accessing the latest version.")
13
+ .before(requireAuth_1.requireAuth)
14
+ .before(secretManager.ensureApi)
15
+ .before(requirePermissions_1.requirePermissions, ["secretmanager.versions.access"])
16
+ .action(async (key, options) => {
17
+ const projectId = (0, projectUtils_1.needProjectId)(options);
18
+ let [name, version] = key.split("@");
19
+ if (!version) {
20
+ version = "latest";
21
+ }
22
+ const value = await (0, secretManager_1.accessSecretVersion)(projectId, name, version);
23
+ logger_1.logger.info(value);
24
+ });
@@ -35,9 +35,10 @@ exports.command = new command_1.Command("apphosting:secrets:set <secretName>")
35
35
  .action(async (secretName, options) => {
36
36
  var _a;
37
37
  const howToAccess = `You can access the contents of the secret's latest value with ${clc.bold(`firebase apphosting:secrets:access ${secretName}`)}`;
38
+ const grantAccess = `To use this secret in your backend, you must grant access. You can do so in the future with ${clc.bold("firebase apphosting:secrets:grantAccess")}`;
38
39
  const projectId = (0, projectUtils_1.needProjectId)(options);
39
40
  const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
40
- const created = secrets.upsertSecret(projectId, secretName, options.location);
41
+ const created = await secrets.upsertSecret(projectId, secretName, options.location);
41
42
  if (created === null) {
42
43
  return;
43
44
  }
@@ -55,17 +56,19 @@ exports.command = new command_1.Command("apphosting:secrets:set <secretName>")
55
56
  }
56
57
  secretValue = fs.readFileSync(dataFile, "utf-8");
57
58
  }
59
+ if (created) {
60
+ (0, utils_1.logSuccess)(`Created new secret projects/${projectId}/secrets/${secretName}`);
61
+ }
62
+ const version = await gcsm.addVersion(projectId, secretName, secretValue);
63
+ (0, utils_1.logSuccess)(`Created new secret version ${gcsm.toSecretVersionResourceName(version)}`);
64
+ (0, utils_1.logSuccess)(howToAccess);
58
65
  if (!created) {
59
- const version = await gcsm.addVersion(projectId, secretName, secretValue);
60
- (0, utils_1.logSuccess)(`Created new secret version ${gcsm.toSecretVersionResourceName(version)}`);
61
- (0, utils_1.logSuccess)(howToAccess);
66
+ (0, utils_1.logWarning)(grantAccess);
62
67
  return;
63
68
  }
64
- (0, utils_1.logSuccess)(`Created new secret projects/${projectId}/secrets/${secretName}`);
65
- (0, utils_1.logSuccess)(howToAccess);
66
69
  const accounts = await dialogs.selectBackendServiceAccounts(projectNumber, projectId, options);
67
70
  if (!accounts.buildServiceAccounts.length && !accounts.runServiceAccounts.length) {
68
- (0, utils_1.logWarning)(`To use this secret your backend, you must grant access. You can do so in the future with ${clc.bold("firebase apphosting:secrets:grantAccess")}`);
71
+ (0, utils_1.logWarning)(grantAccess);
69
72
  }
70
73
  else {
71
74
  await secrets.grantSecretAccess(projectId, secretName, accounts);
@@ -169,6 +169,7 @@ function load(client) {
169
169
  client.apphosting.secrets.set = loadCommand("apphosting-secrets-set");
170
170
  client.apphosting.secrets.grantaccess = loadCommand("apphosting-secrets-grantaccess");
171
171
  client.apphosting.secrets.describe = loadCommand("apphosting-secrets-describe");
172
+ client.apphosting.secrets.access = loadCommand("apphosting-secrets-access");
172
173
  client.apphosting.rollouts = {};
173
174
  client.apphosting.rollouts.create = loadCommand("apphosting-rollouts-create");
174
175
  client.apphosting.rollouts.list = loadCommand("apphosting-rollouts-list");
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.promptForMinInstances = exports.promptForFunctionDeletion = exports.promptForFailurePolicies = void 0;
3
+ exports.promptForMinInstances = exports.promptForUnsafeMigration = exports.promptForFunctionDeletion = exports.promptForFailurePolicies = void 0;
4
4
  const clc = require("colorette");
5
5
  const functionsDeployHelper_1 = require("./functionsDeployHelper");
6
6
  const error_1 = require("../../error");
@@ -49,16 +49,16 @@ async function promptForFailurePolicies(options, want, have) {
49
49
  }
50
50
  }
51
51
  exports.promptForFailurePolicies = promptForFailurePolicies;
52
- async function promptForFunctionDeletion(functionsToDelete, force, nonInteractive) {
52
+ async function promptForFunctionDeletion(functionsToDelete, options) {
53
53
  let shouldDeleteFns = true;
54
- if (functionsToDelete.length === 0 || force) {
54
+ if (functionsToDelete.length === 0 || options.force) {
55
55
  return true;
56
56
  }
57
57
  const deleteList = functionsToDelete
58
58
  .sort(backend.compareFunctions)
59
59
  .map((fn) => "\t" + (0, functionsDeployHelper_1.getFunctionLabel)(fn))
60
60
  .join("\n");
61
- if (nonInteractive) {
61
+ if (options.nonInteractive) {
62
62
  const deleteCommands = functionsToDelete
63
63
  .map((func) => {
64
64
  return "\tfirebase functions:delete " + func.id + " --region " + func.region;
@@ -75,9 +75,7 @@ async function promptForFunctionDeletion(functionsToDelete, force, nonInteractiv
75
75
  "\n\nIf you are renaming a function or changing its region, it is recommended that you create the new " +
76
76
  "function first before deleting the old one to prevent event loss. For more info, visit " +
77
77
  clc.underline("https://firebase.google.com/docs/functions/manage-functions#modify" + "\n"));
78
- shouldDeleteFns = await (0, prompt_1.promptOnce)({
79
- type: "confirm",
80
- name: "confirm",
78
+ shouldDeleteFns = await (0, prompt_1.confirm)({
81
79
  default: false,
82
80
  message: "Would you like to proceed with deletion? Selecting no will continue the rest of the deployments.",
83
81
  });
@@ -85,6 +83,40 @@ async function promptForFunctionDeletion(functionsToDelete, force, nonInteractiv
85
83
  return shouldDeleteFns;
86
84
  }
87
85
  exports.promptForFunctionDeletion = promptForFunctionDeletion;
86
+ async function promptForUnsafeMigration(fnsToUpdate, options) {
87
+ const unsafeUpdates = fnsToUpdate.filter((eu) => eu.unsafe);
88
+ if (unsafeUpdates.length === 0 || options.force) {
89
+ return fnsToUpdate;
90
+ }
91
+ const warnMessage = "The following functions are unsafely changing event types: " +
92
+ clc.bold(unsafeUpdates
93
+ .map((eu) => eu.endpoint)
94
+ .sort(backend.compareFunctions)
95
+ .map(functionsDeployHelper_1.getFunctionLabel)
96
+ .join(", ")) +
97
+ ". " +
98
+ "While automatic migration is allowed for these functions, updating the underlying event type may result in data loss. " +
99
+ "To avoid this, consider the best practices outlined in the migration guide: https://firebase.google.com/docs/functions/manage-functions?gen=2nd#modify-trigger";
100
+ utils.logLabeledWarning("functions", warnMessage);
101
+ const safeUpdates = fnsToUpdate.filter((eu) => !eu.unsafe);
102
+ if (options.nonInteractive) {
103
+ utils.logLabeledWarning("functions", "Skipping updates for functions that may be unsafe to update. To update these functions anyway, deploy again in interactive mode or use the --force option.");
104
+ return safeUpdates;
105
+ }
106
+ for (const eu of unsafeUpdates) {
107
+ const shouldUpdate = await (0, prompt_1.promptOnce)({
108
+ type: "confirm",
109
+ name: "confirm",
110
+ default: false,
111
+ message: `[${(0, functionsDeployHelper_1.getFunctionLabel)(eu.endpoint)}] Would you like to proceed with the unsafe migration?`,
112
+ });
113
+ if (shouldUpdate) {
114
+ safeUpdates.push(eu);
115
+ }
116
+ }
117
+ return safeUpdates;
118
+ }
119
+ exports.promptForUnsafeMigration = promptForUnsafeMigration;
88
120
  async function promptForMinInstances(options, want, have) {
89
121
  if (options.force) {
90
122
  return;
@@ -11,6 +11,7 @@ const fabricator = require("./fabricator");
11
11
  const reporter = require("./reporter");
12
12
  const executor = require("./executor");
13
13
  const prompts = require("../prompts");
14
+ const experiments = require("../../../experiments");
14
15
  const functionsConfig_1 = require("../../../functionsConfig");
15
16
  const functionsDeployHelper_1 = require("../functionsDeployHelper");
16
17
  const error_1 = require("../../../error");
@@ -37,12 +38,24 @@ async function release(context, options, payload) {
37
38
  const fnsToDelete = Object.values(plan)
38
39
  .map((regionalChanges) => regionalChanges.endpointsToDelete)
39
40
  .reduce(functional_1.reduceFlat, []);
40
- const shouldDelete = await prompts.promptForFunctionDeletion(fnsToDelete, options.force, options.nonInteractive);
41
+ const shouldDelete = await prompts.promptForFunctionDeletion(fnsToDelete, options);
41
42
  if (!shouldDelete) {
42
43
  for (const change of Object.values(plan)) {
43
44
  change.endpointsToDelete = [];
44
45
  }
45
46
  }
47
+ const fnsToUpdate = Object.values(plan)
48
+ .map((regionalChanges) => regionalChanges.endpointsToUpdate)
49
+ .reduce(functional_1.reduceFlat, []);
50
+ const fnsToUpdateSafe = await prompts.promptForUnsafeMigration(fnsToUpdate, options);
51
+ for (const key of Object.keys(plan)) {
52
+ plan[key].endpointsToUpdate = [];
53
+ }
54
+ for (const eu of fnsToUpdateSafe) {
55
+ const e = eu.endpoint;
56
+ const key = `${e.codebase || ""}-${e.region}-${e.availableMemoryMb || "default"}`;
57
+ plan[key].endpointsToUpdate.push(eu);
58
+ }
46
59
  const throttlerOptions = {
47
60
  retries: 30,
48
61
  backoff: 20000,
@@ -65,7 +78,9 @@ async function release(context, options, payload) {
65
78
  const deletedEndpoints = Object.values(plan)
66
79
  .map((r) => r.endpointsToDelete)
67
80
  .reduce(functional_1.reduceFlat, []);
68
- await containerCleaner.cleanupBuildImages(haveEndpoints, deletedEndpoints);
81
+ if (experiments.isEnabled("automaticallydeletegcfartifacts")) {
82
+ await containerCleaner.cleanupBuildImages(haveEndpoints, deletedEndpoints);
83
+ }
69
84
  const allErrors = summary.results.filter((r) => r.error).map((r) => r.error);
70
85
  if (allErrors.length) {
71
86
  const opts = allErrors.length === 1 ? { original: allErrors[0] } : { children: allErrors };
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkForV2Upgrade = exports.checkForIllegalUpdate = exports.upgradedScheduleFromV1ToV2 = exports.changedV2PubSubTopic = exports.changedTriggerRegion = exports.upgradedToGCFv2WithoutSettingConcurrency = exports.createDeploymentPlan = exports.calculateUpdate = exports.calculateChangesets = void 0;
4
- const clc = require("colorette");
3
+ exports.checkForV2Upgrade = exports.checkForIllegalUpdate = exports.checkForUnsafeUpdate = exports.upgradedScheduleFromV1ToV2 = exports.changedV2PubSubTopic = exports.changedTriggerRegion = exports.upgradedToGCFv2WithoutSettingConcurrency = exports.createDeploymentPlan = exports.calculateUpdate = exports.calculateChangesets = void 0;
5
4
  const functionsDeployHelper_1 = require("../functionsDeployHelper");
6
5
  const deploymentTool_1 = require("../../../deploymentTool");
7
6
  const error_1 = require("../../../error");
8
7
  const utils = require("../../../utils");
9
8
  const backend = require("../backend");
10
9
  const v2events = require("../../../functions/events/v2");
10
+ const v2_1 = require("../../../functions/events/v2");
11
11
  function calculateChangesets(want, have, keyFn, deleteAll) {
12
12
  const toCreate = utils.groupBy(Object.keys(want)
13
13
  .filter((id) => !have[id])
@@ -29,7 +29,7 @@ function calculateChangesets(want, have, keyFn, deleteAll) {
29
29
  }, {});
30
30
  const toSkip = utils.groupBy(Object.values(toSkipEndpointsMap), keyFn);
31
31
  if (Object.keys(toSkip).length) {
32
- utils.logLabeledBullet("functions", `Skipping the deploy of unchanged functions with ${clc.bold("experimental")} support for skipdeployingnoopfunctions`);
32
+ utils.logLabeledBullet("functions", "Skipping the deploy of unchanged functions.");
33
33
  }
34
34
  const toUpdate = utils.groupBy(Object.keys(want)
35
35
  .filter((id) => have[id])
@@ -57,6 +57,7 @@ function calculateUpdate(want, have) {
57
57
  checkForIllegalUpdate(want, have);
58
58
  const update = {
59
59
  endpoint: want,
60
+ unsafe: checkForUnsafeUpdate(want, have),
60
61
  };
61
62
  const needsDelete = changedTriggerRegion(want, have) ||
62
63
  changedV2PubSubTopic(want, have) ||
@@ -164,6 +165,13 @@ function upgradedScheduleFromV1ToV2(want, have) {
164
165
  return true;
165
166
  }
166
167
  exports.upgradedScheduleFromV1ToV2 = upgradedScheduleFromV1ToV2;
168
+ function checkForUnsafeUpdate(want, have) {
169
+ return (backend.isEventTriggered(want) &&
170
+ v2_1.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX.test(want.eventTrigger.eventType) &&
171
+ backend.isEventTriggered(have) &&
172
+ v2_1.FIRESTORE_EVENT_REGEX.test(have.eventTrigger.eventType));
173
+ }
174
+ exports.checkForUnsafeUpdate = checkForUnsafeUpdate;
167
175
  function checkForIllegalUpdate(want, have) {
168
176
  const triggerType = (e) => {
169
177
  if (backend.isHttpsTriggered(e)) {
@@ -104,6 +104,10 @@ const EVENT_SERVICE_MAPPING = {
104
104
  "google.cloud.firestore.document.v1.created": firestoreService,
105
105
  "google.cloud.firestore.document.v1.updated": firestoreService,
106
106
  "google.cloud.firestore.document.v1.deleted": firestoreService,
107
+ "google.cloud.firestore.document.v1.written.withAuthContext": firestoreService,
108
+ "google.cloud.firestore.document.v1.created.withAuthContext": firestoreService,
109
+ "google.cloud.firestore.document.v1.updated.withAuthContext": firestoreService,
110
+ "google.cloud.firestore.document.v1.deleted.withAuthContext": firestoreService,
107
111
  };
108
112
  function serviceForEndpoint(endpoint) {
109
113
  if (backend.isEventTriggered(endpoint)) {
@@ -309,11 +309,8 @@ async function startAll(options, showUI = true, runningTestScript = false) {
309
309
  utils.assertIsStringOrUndefined(options.extDevDir);
310
310
  for (const cfg of functionsCfg) {
311
311
  const functionsDir = path.join(projectDir, cfg.source);
312
- let runtime = ((_e = options.extDevRuntime) !== null && _e !== void 0 ? _e : cfg.runtime);
313
- if (!runtime) {
314
- runtime = (0, supported_1.latest)("nodejs");
315
- }
316
- if (!(0, supported_1.isRuntime)(runtime)) {
312
+ const runtime = ((_e = options.extDevRuntime) !== null && _e !== void 0 ? _e : cfg.runtime);
313
+ if (runtime && !(0, supported_1.isRuntime)(runtime)) {
317
314
  throw new error_1.FirebaseError(`Cannot load functions from ${functionsDir} because it has invalid runtime ${runtime}`);
318
315
  }
319
316
  emulatableBackends.push({
@@ -39,6 +39,18 @@ exports.ALL_EXPERIMENTS = experiments({
39
39
  "of how that image was created.",
40
40
  public: true,
41
41
  },
42
+ automaticallydeletegcfartifacts: {
43
+ shortDescription: "Control whether functions cleans up images after deploys",
44
+ fullDescription: "To control costs, Firebase defaults to automatically deleting containers " +
45
+ "created during the build process. This has the side-effect of preventing " +
46
+ "users from rolling back to previous revisions using the Run API. To change " +
47
+ `this behavior, call ${(0, colorette_1.bold)("experiments:disable deletegcfartifactsondeploy")} ` +
48
+ `consider also calling ${(0, colorette_1.bold)("experiments:enable deletegcfartifacts")} ` +
49
+ `to enable the new command ${(0, colorette_1.bold)("functions:deletegcfartifacts")} which` +
50
+ "lets you clean up images manually",
51
+ public: true,
52
+ default: true,
53
+ },
42
54
  emulatoruisnapshot: {
43
55
  shortDescription: "Load pre-release versions of the emulator UI",
44
56
  },
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WEBPACK_LAYERS = exports.ESBUILD_VERSION = exports.SERVER_REFERENCE_MANIFEST = exports.APP_PATHS_MANIFEST = exports.ROUTES_MANIFEST = exports.PRERENDER_MANIFEST = exports.PAGES_MANIFEST = exports.MIDDLEWARE_MANIFEST = exports.IMAGES_MANIFEST = exports.EXPORT_MARKER = exports.APP_PATH_ROUTES_MANIFEST = void 0;
3
+ exports.WEBPACK_LAYERS = exports.ESBUILD_VERSION = exports.CONFIG_FILES = exports.SERVER_REFERENCE_MANIFEST = exports.APP_PATHS_MANIFEST = exports.ROUTES_MANIFEST = exports.PRERENDER_MANIFEST = exports.PAGES_MANIFEST = exports.MIDDLEWARE_MANIFEST = exports.IMAGES_MANIFEST = exports.EXPORT_MARKER = exports.APP_PATH_ROUTES_MANIFEST = void 0;
4
4
  exports.APP_PATH_ROUTES_MANIFEST = "app-path-routes-manifest.json";
5
5
  exports.EXPORT_MARKER = "export-marker.json";
6
6
  exports.IMAGES_MANIFEST = "images-manifest.json";
@@ -10,6 +10,7 @@ exports.PRERENDER_MANIFEST = "prerender-manifest.json";
10
10
  exports.ROUTES_MANIFEST = "routes-manifest.json";
11
11
  exports.APP_PATHS_MANIFEST = "app-paths-manifest.json";
12
12
  exports.SERVER_REFERENCE_MANIFEST = "server-reference-manifest.json";
13
+ exports.CONFIG_FILES = ["next.config.js", "next.config.mjs"];
13
14
  exports.ESBUILD_VERSION = "0.19.2";
14
15
  const WEBPACK_LAYERS_NAMES = {
15
16
  shared: "shared",
@@ -7,7 +7,6 @@ const promises_1 = require("fs/promises");
7
7
  const path_1 = require("path");
8
8
  const fs_extra_1 = require("fs-extra");
9
9
  const url_1 = require("url");
10
- const fs_1 = require("fs");
11
10
  const semver_1 = require("semver");
12
11
  const clc = require("colorette");
13
12
  const stream_chain_1 = require("stream-chain");
@@ -40,7 +39,7 @@ async function discover(dir) {
40
39
  if (!(await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, "package.json"))))
41
40
  return;
42
41
  const version = (0, utils_2.getNextVersion)(dir);
43
- if (!(await (0, fs_extra_1.pathExists)("next.config.js")) && !version)
42
+ if (!(await (0, utils_2.whichNextConfigFile)(dir)) && !version)
44
43
  return;
45
44
  return { mayWantBackend: true, publicDirectory: (0, path_1.join)(dir, PUBLIC_DIR), version };
46
45
  }
@@ -336,7 +335,8 @@ exports.ɵcodegenPublicDirectory = ɵcodegenPublicDirectory;
336
335
  async function ɵcodegenFunctionsDirectory(sourceDir, destDir, target, context) {
337
336
  const { distDir } = await getConfig(sourceDir);
338
337
  const packageJson = await (0, utils_1.readJSON)((0, path_1.join)(sourceDir, "package.json"));
339
- if ((0, fs_1.existsSync)((0, path_1.join)(sourceDir, "next.config.js"))) {
338
+ const configFile = await (0, utils_2.whichNextConfigFile)(sourceDir);
339
+ if (configFile) {
340
340
  try {
341
341
  const productionDeps = await new Promise((resolve) => {
342
342
  const dependencies = [];
@@ -361,8 +361,14 @@ async function ɵcodegenFunctionsDirectory(sourceDir, destDir, target, context)
361
361
  });
362
362
  const esbuildArgs = productionDeps
363
363
  .map((it) => `--external:${it}`)
364
- .concat("--bundle", "--platform=node", `--target=node${constants_1.NODE_VERSION}`, `--outdir=${destDir}`, "--log-level=error");
365
- const bundle = (0, cross_spawn_1.sync)("npx", ["--yes", `esbuild@${constants_2.ESBUILD_VERSION}`, "next.config.js", ...esbuildArgs], {
364
+ .concat("--bundle", "--platform=node", `--target=node${constants_1.NODE_VERSION}`, "--log-level=error");
365
+ if (configFile === "next.config.mjs") {
366
+ esbuildArgs.push(...[`--outfile=${(0, path_1.join)(destDir, configFile)}`, "--format=esm"]);
367
+ }
368
+ else {
369
+ esbuildArgs.push(`--outfile=${(0, path_1.join)(destDir, configFile)}`);
370
+ }
371
+ const bundle = (0, cross_spawn_1.sync)("npx", ["--yes", `esbuild@${constants_2.ESBUILD_VERSION}`, configFile, ...esbuildArgs], {
366
372
  cwd: sourceDir,
367
373
  timeout: BUNDLE_NEXT_CONFIG_TIMEOUT,
368
374
  });
@@ -371,9 +377,9 @@ async function ɵcodegenFunctionsDirectory(sourceDir, destDir, target, context)
371
377
  }
372
378
  }
373
379
  catch (e) {
374
- console.warn("Unable to bundle next.config.js for use in Cloud Functions, proceeding with deploy but problems may be enountered.");
380
+ console.warn(`Unable to bundle ${configFile} for use in Cloud Functions, proceeding with deploy but problems may be encountered.`);
375
381
  console.error(e.message || e);
376
- (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, "next.config.js"), (0, path_1.join)(destDir, "next.config.js"));
382
+ await (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, configFile), (0, path_1.join)(destDir, configFile));
377
383
  }
378
384
  }
379
385
  if (await (0, fs_extra_1.pathExists)((0, path_1.join)(sourceDir, "public"))) {
@@ -390,8 +396,11 @@ async function ɵcodegenFunctionsDirectory(sourceDir, destDir, target, context)
390
396
  dotEnv["VERCEL_URL"] = deploymentDomain;
391
397
  }
392
398
  }
393
- await (0, fs_extra_1.mkdirp)((0, path_1.join)(destDir, distDir));
394
- await (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, distDir), (0, path_1.join)(destDir, distDir));
399
+ const [productionDistDirfiles] = await Promise.all([
400
+ (0, utils_2.getProductionDistDirFiles)(sourceDir, distDir),
401
+ (0, fs_extra_1.mkdirp)((0, path_1.join)(destDir, distDir)),
402
+ ]);
403
+ await Promise.all(productionDistDirfiles.map((file) => (0, fs_extra_1.copy)(file, file.replace(sourceDir, destDir), { recursive: true })));
395
404
  return { packageJson, frameworksEntry: "next.js", dotEnv };
396
405
  }
397
406
  exports.ɵcodegenFunctionsDirectory = ɵcodegenFunctionsDirectory;
@@ -422,7 +431,8 @@ async function getConfig(dir) {
422
431
  var _a;
423
432
  var _b;
424
433
  let config = {};
425
- if ((0, fs_1.existsSync)((0, path_1.join)(dir, "next.config.js"))) {
434
+ const configFile = await (0, utils_2.whichNextConfigFile)(dir);
435
+ if (configFile) {
426
436
  const version = (0, utils_2.getNextVersion)(dir);
427
437
  if (!version)
428
438
  throw new Error("Unable to find the next dep, try NPM installing?");
@@ -435,10 +445,10 @@ async function getConfig(dir) {
435
445
  }
436
446
  else {
437
447
  try {
438
- config = await (_a = (0, url_1.pathToFileURL)((0, path_1.join)(dir, "next.config.js")).toString(), Promise.resolve().then(() => require(_a)));
448
+ config = await (_a = (0, url_1.pathToFileURL)((0, path_1.join)(dir, configFile)).toString(), Promise.resolve().then(() => require(_a)));
439
449
  }
440
450
  catch (e) {
441
- throw new Error("Unable to load next.config.js.");
451
+ throw new Error(`Unable to load ${configFile}.`);
442
452
  }
443
453
  }
444
454
  }
@@ -1,15 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getRoutesWithServerAction = exports.hasStaticAppNotFoundComponent = exports.getNextVersion = exports.getBuildId = exports.getHeadersFromMetaFiles = exports.getNonStaticServerComponents = exports.getNonStaticRoutes = exports.getMiddlewareMatcherRegexes = exports.allDependencyNames = exports.isUsingAppDirectory = exports.isUsingNextImageInAppDirectory = exports.isUsingImageOptimization = exports.isUsingMiddleware = exports.hasUnoptimizedImage = exports.usesNextImage = exports.usesAppDirRouter = exports.getNextjsRewritesToUse = exports.isHeaderSupportedByHosting = exports.isRedirectSupportedByHosting = exports.isRewriteSupportedByHosting = exports.cleanI18n = exports.cleanCustomRouteI18n = exports.cleanEscapedChars = exports.I18N_SOURCE = void 0;
3
+ exports.whichNextConfigFile = exports.getProductionDistDirFiles = exports.getRoutesWithServerAction = exports.hasStaticAppNotFoundComponent = exports.getNextVersion = exports.getBuildId = exports.getHeadersFromMetaFiles = exports.getNonStaticServerComponents = exports.getNonStaticRoutes = exports.getMiddlewareMatcherRegexes = exports.allDependencyNames = exports.isUsingAppDirectory = exports.isUsingNextImageInAppDirectory = exports.isUsingImageOptimization = exports.isUsingMiddleware = exports.hasUnoptimizedImage = exports.usesNextImage = exports.usesAppDirRouter = exports.getNextjsRewritesToUse = exports.isHeaderSupportedByHosting = exports.isRedirectSupportedByHosting = exports.isRewriteSupportedByHosting = exports.cleanI18n = exports.cleanCustomRouteI18n = exports.cleanEscapedChars = exports.I18N_SOURCE = void 0;
4
4
  const fs_1 = require("fs");
5
5
  const fs_extra_1 = require("fs-extra");
6
6
  const path_1 = require("path");
7
7
  const promises_1 = require("fs/promises");
8
8
  const glob_1 = require("glob");
9
+ const glob = require("glob");
9
10
  const semver_1 = require("semver");
10
11
  const utils_1 = require("../utils");
11
12
  const constants_1 = require("./constants");
12
13
  const fsutils_1 = require("../../fsutils");
14
+ const utils_2 = require("../../utils");
13
15
  exports.I18N_SOURCE = /\/:nextInternalLocale(\([^\)]+\))?/;
14
16
  function cleanEscapedChars(path) {
15
17
  return path.replace(/\\([(){}:+?*])/g, (a, b) => b);
@@ -101,10 +103,15 @@ async function isUsingImageOptimization(projectDir, distDir) {
101
103
  }
102
104
  exports.isUsingImageOptimization = isUsingImageOptimization;
103
105
  async function isUsingNextImageInAppDirectory(projectDir, nextDir) {
106
+ const nextImagePath = ["node_modules", "next", "dist", "client", "image"];
107
+ const nextImageString = utils_2.IS_WINDOWS
108
+ ?
109
+ nextImagePath.join(path_1.sep + path_1.sep)
110
+ : (0, path_1.join)(...nextImagePath);
104
111
  const files = (0, glob_1.sync)((0, path_1.join)(projectDir, nextDir, "server", "**", "*client-reference-manifest.js"));
105
112
  for (const filepath of files) {
106
- const fileContents = await (0, promises_1.readFile)(filepath);
107
- if (fileContents.includes("node_modules/next/dist/client/image")) {
113
+ const fileContents = await (0, promises_1.readFile)(filepath, "utf-8");
114
+ if (fileContents.includes(nextImageString)) {
108
115
  return true;
109
116
  }
110
117
  }
@@ -218,3 +225,25 @@ function getRoutesWithServerAction(serverReferenceManifest, appPathRoutesManifes
218
225
  return Array.from(routesWithServerAction);
219
226
  }
220
227
  exports.getRoutesWithServerAction = getRoutesWithServerAction;
228
+ async function getProductionDistDirFiles(sourceDir, distDir) {
229
+ const productionDistDirFiles = await new Promise((resolve, reject) => glob("**", {
230
+ ignore: [(0, path_1.join)("cache", "webpack", "*-development", "**"), (0, path_1.join)("cache", "eslint", "**")],
231
+ cwd: (0, path_1.join)(sourceDir, distDir),
232
+ nodir: true,
233
+ absolute: true,
234
+ }, (err, matches) => {
235
+ if (err)
236
+ reject(err);
237
+ resolve(matches);
238
+ }));
239
+ return productionDistDirFiles;
240
+ }
241
+ exports.getProductionDistDirFiles = getProductionDistDirFiles;
242
+ async function whichNextConfigFile(dir) {
243
+ for (const file of constants_1.CONFIG_FILES) {
244
+ if (await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, file)))
245
+ return file;
246
+ }
247
+ return null;
248
+ }
249
+ exports.whichNextConfigFile = whichNextConfigFile;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FIRESTORE_EVENTS = exports.TEST_LAB_EVENT = exports.REMOTE_CONFIG_EVENT = exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
3
+ exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = exports.FIRESTORE_EVENT_REGEX = exports.FIRESTORE_EVENTS = exports.TEST_LAB_EVENT = exports.REMOTE_CONFIG_EVENT = exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
4
4
  exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
5
5
  exports.STORAGE_EVENTS = [
6
6
  "google.cloud.storage.object.v1.finalized",
@@ -22,4 +22,10 @@ exports.FIRESTORE_EVENTS = [
22
22
  "google.cloud.firestore.document.v1.created",
23
23
  "google.cloud.firestore.document.v1.updated",
24
24
  "google.cloud.firestore.document.v1.deleted",
25
+ "google.cloud.firestore.document.v1.written.withAuthContext",
26
+ "google.cloud.firestore.document.v1.created.withAuthContext",
27
+ "google.cloud.firestore.document.v1.updated.withAuthContext",
28
+ "google.cloud.firestore.document.v1.deleted.withAuthContext",
25
29
  ];
30
+ exports.FIRESTORE_EVENT_REGEX = /^google\.cloud\.firestore\.document\.v1\.[^\.]*$/;
31
+ exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = /^google\.cloud\.firestore\.document\.v1\..*\.withAuthContext$/;
package/lib/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getHostnameFromUrl = exports.openInBrowserPopup = exports.openInBrowser = exports.connectableHostname = exports.randomInt = exports.debounce = exports.last = exports.cloneDeep = exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isVSCodeExtension = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.promiseWithSpinner = exports.setupLoggers = exports.tryParse = exports.tryStringify = exports.promiseProps = exports.withTimeout = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledError = exports.logLabeledWarning = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.getInheritedOption = exports.consoleUrl = exports.envOverrides = void 0;
3
+ exports.getHostnameFromUrl = exports.openInBrowserPopup = exports.openInBrowser = exports.connectableHostname = exports.randomInt = exports.debounce = exports.last = exports.cloneDeep = exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isVSCodeExtension = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.promiseWithSpinner = exports.setupLoggers = exports.tryParse = exports.tryStringify = exports.promiseProps = exports.withTimeout = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledError = exports.logLabeledWarning = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.getInheritedOption = exports.consoleUrl = exports.envOverrides = exports.IS_WINDOWS = void 0;
4
4
  const fs = require("node:fs");
5
5
  const path = require("node:path");
6
6
  const _ = require("lodash");
@@ -19,10 +19,10 @@ const portfinder_1 = require("portfinder");
19
19
  const configstore_1 = require("./configstore");
20
20
  const error_1 = require("./error");
21
21
  const logger_1 = require("./logger");
22
- const IS_WINDOWS = process.platform === "win32";
23
- const SUCCESS_CHAR = IS_WINDOWS ? "+" : "✔";
24
- const WARNING_CHAR = IS_WINDOWS ? "!" : "⚠";
25
- const ERROR_CHAR = IS_WINDOWS ? "!!" : "⬢";
22
+ exports.IS_WINDOWS = process.platform === "win32";
23
+ const SUCCESS_CHAR = exports.IS_WINDOWS ? "+" : "✔";
24
+ const WARNING_CHAR = exports.IS_WINDOWS ? "!" : "⚠";
25
+ const ERROR_CHAR = exports.IS_WINDOWS ? "!!" : "⬢";
26
26
  const THIRTY_DAYS_IN_MILLISECONDS = 30 * 24 * 60 * 60 * 1000;
27
27
  exports.envOverrides = [];
28
28
  function consoleUrl(project, path) {
@@ -148,7 +148,7 @@ function allSettled(promises) {
148
148
  }
149
149
  exports.allSettled = allSettled;
150
150
  function explainStdin() {
151
- if (IS_WINDOWS) {
151
+ if (exports.IS_WINDOWS) {
152
152
  throw new error_1.FirebaseError("STDIN input is not available on Windows.", {
153
153
  exit: 1,
154
154
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "13.6.1",
3
+ "version": "13.7.1",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {