firebase-tools 15.18.0 → 15.19.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.
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONCURRENCY = void 0;
4
+ exports.checkGoogleAppID = checkGoogleAppID;
5
+ exports.getAppVersion = getAppVersion;
6
+ exports.getGitCommit = getGitCommit;
7
+ exports.getPackageVersion = getPackageVersion;
8
+ exports.upsertBucket = upsertBucket;
9
+ exports.findSourceMapMappings = findSourceMapMappings;
10
+ exports.getLinkedSourceMapPath = getLinkedSourceMapPath;
11
+ exports.uploadSourceMaps = uploadSourceMaps;
12
+ exports.uploadMap = uploadMap;
13
+ exports.normalizeFileName = normalizeFileName;
14
+ exports.registerSourceMap = registerSourceMap;
15
+ const fs = require("fs");
16
+ const path = require("path");
17
+ const node_child_process_1 = require("node:child_process");
18
+ const pLimit = require("p-limit");
19
+ const apiv2_1 = require("../apiv2");
20
+ const error_1 = require("../error");
21
+ const logger_1 = require("../logger");
22
+ const utils_1 = require("../utils");
23
+ const gcs = require("../gcp/storage");
24
+ const archiveFile_1 = require("../archiveFile");
25
+ exports.CONCURRENCY = 25;
26
+ function checkGoogleAppID(options) {
27
+ if (!options.app) {
28
+ throw new error_1.FirebaseError("set --app <appId> to a valid Firebase application id, e.g. 1:00000000:android:0000000");
29
+ }
30
+ }
31
+ function getAppVersion(options) {
32
+ if (options.appVersion) {
33
+ return options.appVersion;
34
+ }
35
+ const gitCommit = getGitCommit();
36
+ if (gitCommit) {
37
+ (0, utils_1.logLabeledBullet)("crashlytics", `Using git commit as app version: ${gitCommit}`);
38
+ return gitCommit;
39
+ }
40
+ const packageVersion = getPackageVersion();
41
+ if (packageVersion) {
42
+ (0, utils_1.logLabeledBullet)("crashlytics", `Using package version as app version: ${packageVersion}`);
43
+ return packageVersion;
44
+ }
45
+ return "unset";
46
+ }
47
+ function getGitCommit() {
48
+ if (!(0, utils_1.commandExistsSync)("git")) {
49
+ return undefined;
50
+ }
51
+ try {
52
+ return (0, node_child_process_1.execSync)("git rev-parse HEAD").toString().trim();
53
+ }
54
+ catch (error) {
55
+ return undefined;
56
+ }
57
+ }
58
+ function getPackageVersion() {
59
+ if (!(0, utils_1.commandExistsSync)("npm")) {
60
+ return undefined;
61
+ }
62
+ try {
63
+ return (0, node_child_process_1.execSync)("npm pkg get version").toString().trim().replaceAll('"', "");
64
+ }
65
+ catch (error) {
66
+ return undefined;
67
+ }
68
+ }
69
+ async function upsertBucket(projectId, projectNumber, options) {
70
+ let loc = "US-CENTRAL1";
71
+ if (options.bucketLocation) {
72
+ loc = options.bucketLocation.toUpperCase();
73
+ }
74
+ else {
75
+ (0, utils_1.logLabeledBullet)("crashlytics", "No Google Cloud Storage bucket location specified. Defaulting to US-CENTRAL1.");
76
+ }
77
+ const baseName = `firebasecrashlytics-sourcemaps-${projectNumber}-${loc.toLowerCase()}`;
78
+ return await gcs.upsertBucket({
79
+ product: "crashlytics",
80
+ createMessage: `Creating Cloud Storage bucket in ${loc} to store Crashlytics source maps at ${baseName}...`,
81
+ projectId,
82
+ req: {
83
+ baseName,
84
+ purposeLabel: `crashlytics-sourcemaps-${loc.toLowerCase()}`,
85
+ location: loc,
86
+ lifecycle: {
87
+ rule: [
88
+ {
89
+ action: {
90
+ type: "Delete",
91
+ },
92
+ condition: {
93
+ age: 30,
94
+ },
95
+ },
96
+ ],
97
+ },
98
+ },
99
+ });
100
+ }
101
+ async function findSourceMapMappings(files, rootDir) {
102
+ const jsFiles = files.filter((f) => f.name.endsWith(".js"));
103
+ const mapFiles = files.filter((f) => f.name.endsWith(".js.map"));
104
+ const mappings = [];
105
+ const mapFilePathsSet = new Set(mapFiles.map((f) => f.name));
106
+ const mapFilesLinkedInJsComment = new Set();
107
+ const limit = pLimit(exports.CONCURRENCY);
108
+ const results = await Promise.all(jsFiles.map((jsFile) => limit(async () => {
109
+ const mapFilePath = await getLinkedSourceMapPath(jsFile.name);
110
+ return { jsFile, mapFilePath };
111
+ })));
112
+ for (const { jsFile, mapFilePath } of results) {
113
+ if (mapFilePath && mapFilePathsSet.has(mapFilePath)) {
114
+ mappings.push({
115
+ mapFilePath,
116
+ obfuscatedFilePath: path.relative(rootDir, path.resolve(jsFile.name)),
117
+ });
118
+ mapFilesLinkedInJsComment.add(mapFilePath);
119
+ }
120
+ }
121
+ for (const mapFile of mapFiles) {
122
+ if (!mapFilesLinkedInJsComment.has(mapFile.name)) {
123
+ mappings.push({
124
+ mapFilePath: mapFile.name,
125
+ obfuscatedFilePath: path.relative(rootDir, path.resolve(mapFile.name)),
126
+ });
127
+ }
128
+ }
129
+ return mappings;
130
+ }
131
+ async function getLinkedSourceMapPath(jsFilePath) {
132
+ let fileHandle;
133
+ try {
134
+ const stat = await fs.promises.stat(jsFilePath);
135
+ const size = stat.size;
136
+ const bufferSize = Math.min(size, 4096);
137
+ if (bufferSize === 0) {
138
+ return undefined;
139
+ }
140
+ fileHandle = await fs.promises.open(jsFilePath, "r");
141
+ const buffer = Buffer.alloc(bufferSize);
142
+ const { bytesRead } = await fileHandle.read(buffer, 0, bufferSize, size - bufferSize);
143
+ const tail = buffer.toString("utf-8", 0, bytesRead);
144
+ const regex = /^\/\/\s*[#@]\s*sourceMappingURL=(?<sourceMappingURL>.+)\s*$/m;
145
+ const match = regex.exec(tail);
146
+ const sourceMappingURL = match?.groups?.sourceMappingURL?.trim();
147
+ if (sourceMappingURL) {
148
+ return path.join(path.dirname(jsFilePath), sourceMappingURL);
149
+ }
150
+ }
151
+ catch (e) {
152
+ logger_1.logger.debug(`Error reading sourceMappingURL from ${jsFilePath}: ${e instanceof Error ? e.message : String(e)}`);
153
+ }
154
+ finally {
155
+ if (fileHandle) {
156
+ try {
157
+ await fileHandle.close();
158
+ }
159
+ catch (e) {
160
+ }
161
+ }
162
+ }
163
+ return undefined;
164
+ }
165
+ async function uploadSourceMaps(mappings, request) {
166
+ const { projectId, bucketName, appVersion, options } = request;
167
+ const limit = pLimit(exports.CONCURRENCY);
168
+ const results = await Promise.all(mappings.map((mapping) => limit(async () => {
169
+ const uploadRequest = {
170
+ projectId,
171
+ mappingFile: mapping.mapFilePath,
172
+ obfuscatedFilePath: mapping.obfuscatedFilePath,
173
+ bucketName,
174
+ appVersion,
175
+ options,
176
+ };
177
+ let success = await uploadMap(uploadRequest, 1);
178
+ if (!success) {
179
+ await new Promise((res) => setTimeout(res, options.retryDelay || 5000));
180
+ success = await uploadMap(uploadRequest);
181
+ }
182
+ return success;
183
+ })));
184
+ let successCount = 0;
185
+ const failedFiles = [];
186
+ for (const [i, success] of results.entries()) {
187
+ if (success) {
188
+ successCount++;
189
+ }
190
+ else {
191
+ failedFiles.push(mappings[i].mapFilePath);
192
+ }
193
+ }
194
+ return {
195
+ successCount,
196
+ failedFiles,
197
+ };
198
+ }
199
+ async function uploadMap(request, attemptsRemaining = 0) {
200
+ const { projectId, mappingFile, obfuscatedFilePath, bucketName, appVersion, options } = request;
201
+ const filePath = path.relative(options.projectRoot ?? process.cwd(), mappingFile);
202
+ const obfuscatedPath = obfuscatedFilePath
203
+ .split(path.sep)
204
+ .map((p) => (p === ".next" ? "_next" : p))
205
+ .filter((p) => p !== "dev")
206
+ .join("/");
207
+ const tmpArchive = await (0, archiveFile_1.archiveFile)(filePath, { archivedFileName: "mapping.js.map" });
208
+ const appId = options.app || "";
209
+ const gcsFile = `${appId}-${appVersion}-${normalizeFileName(obfuscatedPath)}.zip`;
210
+ const uid = (0, utils_1.murmurHashV3)(`${appId}-${appVersion}-${obfuscatedPath}`);
211
+ const name = `projects/${projectId}/locations/global/mappingFiles/${uid}`;
212
+ const stream = fs.createReadStream(tmpArchive);
213
+ stream.on("error", (err) => {
214
+ logger_1.logger.debug(`Stream error on tmpArchive: ${err instanceof Error ? err.message : String(err)}`);
215
+ });
216
+ try {
217
+ const { bucket, object } = await gcs.uploadObject({
218
+ file: gcsFile,
219
+ stream,
220
+ }, bucketName);
221
+ const fileUri = `gs://${bucket}/${object}`;
222
+ logger_1.logger.debug(`Uploaded mapping file ${filePath} to ${fileUri}`);
223
+ await registerSourceMap({
224
+ name,
225
+ appId,
226
+ version: appVersion,
227
+ obfuscatedFilePath: `/${obfuscatedPath}`,
228
+ fileUri,
229
+ });
230
+ logger_1.logger.debug(`Registered mapping file ${filePath}`);
231
+ return true;
232
+ }
233
+ catch (e) {
234
+ if (attemptsRemaining === 0) {
235
+ (0, utils_1.logLabeledWarning)("crashlytics", `Failed to upload mapping file ${filePath}:\n${e instanceof Error ? e.message : String(e)}`);
236
+ }
237
+ return false;
238
+ }
239
+ finally {
240
+ stream.destroy();
241
+ try {
242
+ fs.rmSync(tmpArchive, { force: true });
243
+ }
244
+ catch (err) {
245
+ logger_1.logger.debug(`Failed to delete temporary archive ${tmpArchive}: ${err instanceof Error ? err.message : String(err)}`);
246
+ }
247
+ }
248
+ }
249
+ function normalizeFileName(fileName) {
250
+ return fileName.replaceAll(/\//g, "-");
251
+ }
252
+ async function registerSourceMap(sourceMap) {
253
+ const client = new apiv2_1.Client({
254
+ urlPrefix: "https://firebasetelemetryadmin.googleapis.com",
255
+ auth: true,
256
+ apiVersion: "v1",
257
+ });
258
+ try {
259
+ await client.patch(sourceMap.name, sourceMap, { queryParams: { allowMissing: "true" } });
260
+ logger_1.logger.debug(`Registered source map ${sourceMap.obfuscatedFilePath} with Firebase Telemetry service`);
261
+ }
262
+ catch (e) {
263
+ if (e instanceof error_1.FirebaseError) {
264
+ if (e.status === 409) {
265
+ return;
266
+ }
267
+ }
268
+ throw new error_1.FirebaseError(`Failed to register source map ${sourceMap.obfuscatedFilePath} with Firebase Telemetry service:\n${e instanceof Error ? e.message : String(e)}`);
269
+ }
270
+ }
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ensureApis = ensureApis;
4
- exports.ensureGIFApiTos = ensureGIFApiTos;
5
4
  const api = require("../api");
6
- const configstore_1 = require("../configstore");
7
5
  const ensureApiEnabled_1 = require("../ensureApiEnabled");
8
6
  const prefix = "dataconnect";
9
7
  async function ensureApis(projectId, silent = false) {
@@ -12,14 +10,3 @@ async function ensureApis(projectId, silent = false) {
12
10
  (0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix, silent),
13
11
  ]);
14
12
  }
15
- async function ensureGIFApiTos(projectId) {
16
- if (configstore_1.configstore.get("gemini")) {
17
- await (0, ensureApiEnabled_1.ensure)(projectId, api.cloudAiCompanionOrigin(), "");
18
- }
19
- else {
20
- if (!(await (0, ensureApiEnabled_1.check)(projectId, api.cloudAiCompanionOrigin(), ""))) {
21
- return false;
22
- }
23
- }
24
- return true;
25
- }
@@ -10,6 +10,7 @@ const projectUtils_1 = require("../../projectUtils");
10
10
  const utils_1 = require("../../utils");
11
11
  const util = require("./util");
12
12
  const experiments = require("../../experiments");
13
+ const logger_1 = require("../../logger");
13
14
  async function default_1(context, options) {
14
15
  if (Object.entries(context.backendConfigs).length === 0) {
15
16
  return;
@@ -52,31 +53,46 @@ async function default_1(context, options) {
52
53
  }));
53
54
  await Promise.all(Object.values(context.backendConfigs).map(async (cfg) => {
54
55
  const rootDir = options.projectRoot ?? process.cwd();
55
- let builtAppDir;
56
- const isLocalBuild = !!cfg.localBuild;
57
- if (isLocalBuild) {
58
- experiments.assertEnabled("apphostinglocalbuilds", "App Hosting local builds");
59
- builtAppDir = context.backendLocalBuilds[cfg.backendId].buildDir;
60
- if (!builtAppDir) {
61
- throw new error_1.FirebaseError(`No local build dir found for ${cfg.backendId}`);
56
+ let localBuildScratchDir;
57
+ try {
58
+ const isLocalBuild = cfg.localBuild;
59
+ let outputFiles;
60
+ if (isLocalBuild) {
61
+ experiments.assertEnabled("apphostinglocalbuilds", "App Hosting local builds");
62
+ const localBuild = context.backendLocalBuilds[cfg.backendId];
63
+ outputFiles = localBuild?.outputFiles;
64
+ localBuildScratchDir = localBuild?.localBuildScratchDir;
65
+ if (!outputFiles || !localBuildScratchDir) {
66
+ throw new error_1.FirebaseError(`No local build output files found for ${cfg.backendId}`);
67
+ }
62
68
  }
69
+ const zippedSourcePath = isLocalBuild
70
+ ? await util.createLocalBuildTarArchive(cfg, localBuildScratchDir, outputFiles ?? [])
71
+ : await util.createSourceDeployArchive(cfg, rootDir);
72
+ (0, utils_1.logLabeledBullet)("apphosting", `Zipped ${isLocalBuild ? "built app" : "source"} for backend ${cfg.backendId}`);
73
+ const backendLocation = context.backendLocations[cfg.backendId];
74
+ if (!backendLocation) {
75
+ throw new error_1.FirebaseError(`Failed to find location for backend ${cfg.backendId}. Please contact support with the contents of your firebase-debug.log to report your issue.`);
76
+ }
77
+ (0, utils_1.logLabeledBullet)("apphosting", `Uploading ${isLocalBuild ? "built app" : "source"} for backend ${cfg.backendId}...`);
78
+ const bucketName = bucketsPerLocation[backendLocation];
79
+ const { bucket, object } = await gcs.uploadObject({
80
+ file: zippedSourcePath,
81
+ stream: fs.createReadStream(zippedSourcePath),
82
+ }, bucketName, isLocalBuild ? gcs.ContentType.TAR : gcs.ContentType.ZIP);
83
+ (0, utils_1.logLabeledBullet)("apphosting", `Uploaded at gs://${bucket}/${object}`);
84
+ context.backendStorageUris[cfg.backendId] =
85
+ `gs://${bucketName}/${path.basename(zippedSourcePath)}`;
63
86
  }
64
- const zippedSourcePath = isLocalBuild
65
- ? await util.createLocalBuildTarArchive(cfg, rootDir, builtAppDir)
66
- : await util.createSourceDeployArchive(cfg, rootDir);
67
- (0, utils_1.logLabeledBullet)("apphosting", `Zipped ${isLocalBuild ? "built app" : "source"} for backend ${cfg.backendId}`);
68
- const backendLocation = context.backendLocations[cfg.backendId];
69
- if (!backendLocation) {
70
- throw new error_1.FirebaseError(`Failed to find location for backend ${cfg.backendId}. Please contact support with the contents of your firebase-debug.log to report your issue.`);
87
+ finally {
88
+ if (localBuildScratchDir && fs.existsSync(localBuildScratchDir)) {
89
+ try {
90
+ fs.rmSync(localBuildScratchDir, { recursive: true, force: true });
91
+ }
92
+ catch (err) {
93
+ logger_1.logger.debug(`Failed to clean up local build directory ${localBuildScratchDir}: ${err}`);
94
+ }
95
+ }
71
96
  }
72
- (0, utils_1.logLabeledBullet)("apphosting", `Uploading ${isLocalBuild ? "built app" : "source"} for backend ${cfg.backendId}...`);
73
- const bucketName = bucketsPerLocation[backendLocation];
74
- const { bucket, object } = await gcs.uploadObject({
75
- file: zippedSourcePath,
76
- stream: fs.createReadStream(zippedSourcePath),
77
- }, bucketName, isLocalBuild ? gcs.ContentType.TAR : gcs.ContentType.ZIP);
78
- (0, utils_1.logLabeledBullet)("apphosting", `Uploaded at gs://${bucket}/${object}`);
79
- context.backendStorageUris[cfg.backendId] =
80
- `gs://${bucketName}/${path.basename(zippedSourcePath)}`;
81
97
  }));
82
98
  }
@@ -4,7 +4,12 @@ exports.default = default_1;
4
4
  exports.injectEnvVarsFromApphostingConfig = injectEnvVarsFromApphostingConfig;
5
5
  exports.injectAutoInitEnvVars = injectAutoInitEnvVars;
6
6
  exports.getBackendConfigs = getBackendConfigs;
7
+ const crypto = require("crypto");
8
+ const fs = require("fs");
9
+ const os = require("os");
7
10
  const path = require("path");
11
+ const fsAsync = require("../../fsAsync");
12
+ const util_1 = require("./util");
8
13
  const backend_1 = require("../../apphosting/backend");
9
14
  const apphosting_1 = require("../../gcp/apphosting");
10
15
  const yaml_1 = require("../../apphosting/yaml");
@@ -132,21 +137,22 @@ async function default_1(context, options) {
132
137
  (0, utils_1.logLabeledBullet)("apphosting", `Starting local build for backend ${cfg.backendId}`);
133
138
  await injectEnvVarsFromApphostingConfig(configs.filter((c) => c.backendId === cfg.backendId), options, buildEnv, runtimeEnv);
134
139
  await injectAutoInitEnvVars(cfg, backends, buildEnv, runtimeEnv);
140
+ const rootDir = path.resolve(options.projectRoot || process.cwd());
141
+ const pathHash = crypto.createHash("md5").update(rootDir).digest("hex").substring(0, 8);
142
+ const localBuildScratchDir = path.join(os.tmpdir(), `apphosting-local-build-${cfg.backendId}-${pathHash}`);
135
143
  try {
136
- const { outputFiles, annotations, buildConfig } = await (0, localbuilds_1.localBuild)(projectId, options.projectRoot || "./", "nextjs", buildEnv[cfg.backendId] || {}, {
144
+ await prepareLocalBuildScratchDirectory(rootDir, localBuildScratchDir, cfg);
145
+ const { outputFiles, buildConfig } = await (0, localbuilds_1.localBuild)(projectId, localBuildScratchDir, buildEnv[cfg.backendId] || {}, {
137
146
  nonInteractive: options.nonInteractive,
138
147
  allowLocalBuildSecrets: !!options.allowLocalBuildSecrets,
139
148
  });
140
- if (outputFiles.length !== 1) {
141
- throw new error_1.FirebaseError(`Local build for backend ${cfg.backendId} failed: No output files found.`);
142
- }
143
149
  context.backendLocalBuilds[cfg.backendId] = {
144
- buildDir: outputFiles[0],
150
+ outputFiles,
151
+ localBuildScratchDir,
145
152
  buildConfig: {
146
153
  ...buildConfig,
147
154
  env: mergeEnvVars(buildConfig.env || [], runtimeEnv[cfg.backendId] || {}),
148
155
  },
149
- annotations,
150
156
  };
151
157
  }
152
158
  catch (e) {
@@ -244,3 +250,19 @@ async function ensureAppHostingServiceAgentRoles(projectId, projectNumber) {
244
250
  (0, utils_1.logLabeledWarning)("apphosting", `Unable to verify App Hosting service agent permissions for ${p4saEmail}. If you encounter a PERMISSION_DENIED error during rollout, please ensure the service agent has the "Storage Object Viewer" role.`);
245
251
  }
246
252
  }
253
+ async function prepareLocalBuildScratchDirectory(rootDir, localBuildScratchDir, cfg) {
254
+ const ignore = (0, util_1.resolveIgnorePatterns)(cfg);
255
+ fs.rmSync(localBuildScratchDir, { recursive: true, force: true });
256
+ fs.mkdirSync(localBuildScratchDir, { recursive: true });
257
+ const filesToCopy = await fsAsync.readdirRecursive({
258
+ path: rootDir,
259
+ ignoreStrings: ignore,
260
+ supportGitIgnore: true,
261
+ });
262
+ for (const file of filesToCopy) {
263
+ const relativePath = path.relative(rootDir, file.name);
264
+ const destPath = path.join(localBuildScratchDir, relativePath);
265
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
266
+ fs.copyFileSync(file.name, destPath);
267
+ }
268
+ }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createLocalBuildTarArchive = createLocalBuildTarArchive;
4
4
  exports.createSourceDeployArchive = createSourceDeployArchive;
5
+ exports.resolveIgnorePatterns = resolveIgnorePatterns;
5
6
  const archiver = require("archiver");
6
7
  const fs = require("fs");
7
8
  const path = require("path");
@@ -9,25 +10,28 @@ const tar = require("tar");
9
10
  const tmp = require("tmp");
10
11
  const error_1 = require("../../error");
11
12
  const fsAsync = require("../../fsAsync");
12
- const config_1 = require("../../apphosting/config");
13
- async function createLocalBuildTarArchive(config, rootDir, targetSubDir) {
13
+ const utils_1 = require("../../utils");
14
+ async function createLocalBuildTarArchive(config, rootDir, outputFiles) {
14
15
  const tmpFile = tmp.fileSync({ prefix: `${config.backendId}-`, postfix: ".tar.gz" }).name;
15
- const targetDir = targetSubDir ? path.join(rootDir, targetSubDir) : rootDir;
16
- const ignore = ["firebase-debug.log", "firebase-debug.*.log", ".git"];
17
- const rdrFiles = await fsAsync.readdirRecursive({
18
- path: targetDir,
19
- ignoreStrings: ignore,
20
- supportGitIgnore: true,
21
- });
22
- const allFiles = rdrFiles.map((rdrf) => path.relative(rootDir, rdrf.name));
23
- if (targetSubDir) {
24
- const defaultFiles = fs.readdirSync(rootDir).filter((file) => {
25
- return config_1.APPHOSTING_YAML_FILE_REGEX.test(file);
26
- });
27
- for (const file of defaultFiles) {
28
- if (!allFiles.includes(file)) {
29
- allFiles.push(file);
30
- }
16
+ const filesToPackage = outputFiles.length > 0 ? outputFiles : ["."];
17
+ const allFiles = [];
18
+ for (const fileOrDir of filesToPackage) {
19
+ const absolutePath = path.join(rootDir, fileOrDir);
20
+ if (!fs.existsSync(absolutePath)) {
21
+ (0, utils_1.logLabeledWarning)("apphosting", `Expected build output file or directory not found: ${fileOrDir}`);
22
+ continue;
23
+ }
24
+ const stat = fs.statSync(absolutePath);
25
+ if (stat.isDirectory()) {
26
+ const rdrFiles = await fsAsync.readdirRecursive({
27
+ path: absolutePath,
28
+ ignoreStrings: ["firebase-debug.log", "firebase-debug.*.log"],
29
+ supportGitIgnore: false,
30
+ });
31
+ allFiles.push(...rdrFiles.map((rdrf) => path.relative(rootDir, rdrf.name)));
32
+ }
33
+ else {
34
+ allFiles.push(path.relative(rootDir, absolutePath));
31
35
  }
32
36
  }
33
37
  try {
@@ -58,8 +62,7 @@ async function createSourceDeployArchive(config, rootDir, targetSubDir) {
58
62
  });
59
63
  const archive = archiver("zip");
60
64
  const targetDir = targetSubDir ? path.join(rootDir, targetSubDir) : rootDir;
61
- const ignore = config.ignore || ["node_modules", ".git"];
62
- ignore.push("firebase-debug.log", "firebase-debug.*.log");
65
+ const ignore = resolveIgnorePatterns(config);
63
66
  try {
64
67
  const files = await fsAsync.readdirRecursive({
65
68
  path: targetDir,
@@ -76,10 +79,20 @@ async function createSourceDeployArchive(config, rootDir, targetSubDir) {
76
79
  await pipeAsync(archive, fileStream);
77
80
  }
78
81
  catch (err) {
79
- throw new error_1.FirebaseError(`Could not read source directory. Remove links and shortcuts and try again. Original: ${err}`, { original: err, exit: 1 });
82
+ throw new error_1.FirebaseError(`Could not read source directory. Remove links and shortcuts and try again. Original: ${String(err)}`, { original: err, exit: 1 });
80
83
  }
81
84
  return tmpFile;
82
85
  }
86
+ function resolveIgnorePatterns(config, skipDefaultNodeModules = false) {
87
+ const ignore = config.ignore
88
+ ? [...config.ignore]
89
+ : skipDefaultNodeModules
90
+ ? [".git"]
91
+ : ["node_modules", ".git"];
92
+ ignore.push("firebase-debug.log", "firebase-debug.*.log");
93
+ ignore.push(".local_build_*");
94
+ return ignore;
95
+ }
83
96
  async function pipeAsync(from, to) {
84
97
  from.pipe(to);
85
98
  await from.finalize();
@@ -249,12 +249,11 @@ async function resolveDefaultRegionsForBuild(buildObj, have) {
249
249
  }
250
250
  else {
251
251
  try {
252
- const fullEndpoint = { ...endpoint, id };
253
252
  if (build.isBlockingTriggered(endpoint)) {
254
- resolvedRegion = resolveRegionForBlockingTrigger(fullEndpoint);
253
+ resolvedRegion = resolveRegionForBlockingTrigger(endpoint.blockingTrigger);
255
254
  }
256
255
  else if (build.isEventTriggered(endpoint)) {
257
- resolvedRegion = await resolveRegionForEventTrigger(fullEndpoint);
256
+ resolvedRegion = await resolveRegionForEventTrigger(endpoint.project, endpoint.eventTrigger);
258
257
  }
259
258
  }
260
259
  catch (err) {
@@ -265,18 +264,17 @@ async function resolveDefaultRegionsForBuild(buildObj, have) {
265
264
  }
266
265
  }
267
266
  }
268
- function resolveRegionForBlockingTrigger(endpoint) {
269
- const eventType = endpoint.blockingTrigger.eventType;
267
+ function resolveRegionForBlockingTrigger(blockingTrigger) {
268
+ const eventType = blockingTrigger.eventType;
270
269
  if (events.AUTH_BLOCKING_EVENTS.includes(eventType)) {
271
270
  return "us-east1";
272
271
  }
273
- if ((0, ailogic_1.isGlobalAILogicEndpoint)(endpoint)) {
272
+ if ((0, ailogic_1.isGlobalAILogicTrigger)(blockingTrigger)) {
274
273
  return "us-east1";
275
274
  }
276
275
  return exports.DEFAULT_FUNCTION_REGION;
277
276
  }
278
- async function resolveRegionForEventTrigger(endpoint) {
279
- const eventTrigger = endpoint.eventTrigger;
277
+ async function resolveRegionForEventTrigger(project, eventTrigger) {
280
278
  const eventType = eventTrigger.eventType;
281
279
  if (eventType.startsWith("google.cloud.pubsub.") ||
282
280
  eventType.startsWith("providers/cloud.auth/eventTypes/") ||
@@ -289,7 +287,7 @@ async function resolveRegionForEventTrigger(endpoint) {
289
287
  if (eventType.startsWith("google.cloud.firestore.")) {
290
288
  try {
291
289
  const databaseId = eventTrigger.eventFilters?.database || "(default)";
292
- const db = await (0, firestore_1.getDatabase)(endpoint.project, databaseId);
290
+ const db = await (0, firestore_1.getDatabase)(project, databaseId);
293
291
  const locationId = db.locationId.toLowerCase();
294
292
  if (locationId === "nam5" || locationId === "nam7")
295
293
  return "us-central1";
@@ -326,7 +324,7 @@ async function resolveRegionForEventTrigger(endpoint) {
326
324
  try {
327
325
  const instanceName = eventTrigger.eventFilters?.instance;
328
326
  if (instanceName) {
329
- const details = await (0, database_1.getDatabaseInstanceDetails)(endpoint.project, instanceName);
327
+ const details = await (0, database_1.getDatabaseInstanceDetails)(project, instanceName);
330
328
  if (details.location && details.location !== "-") {
331
329
  return details.location.toLowerCase();
332
330
  }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AILogicService = exports.AI_LOGIC_EVENTS = exports.AI_LOGIC_AFTER_GENERATE_CONTENT = exports.AI_LOGIC_BEFORE_GENERATE_CONTENT = void 0;
4
4
  exports.isAILogicEvent = isAILogicEvent;
5
- exports.isGlobalAILogicEndpoint = isGlobalAILogicEndpoint;
5
+ exports.isGlobalAILogicTrigger = isGlobalAILogicTrigger;
6
6
  const backend = require("../backend");
7
7
  const error_1 = require("../../../error");
8
8
  const ailogicApi = require("../../../gcp/ailogic");
@@ -20,11 +20,9 @@ function isAILogicEvent(endpoint) {
20
20
  }
21
21
  return exports.AI_LOGIC_EVENTS.includes(endpoint.blockingTrigger.eventType);
22
22
  }
23
- function isGlobalAILogicEndpoint(endpoint) {
24
- if (!isAILogicEvent(endpoint)) {
25
- return false;
26
- }
27
- return !endpoint.blockingTrigger.options?.regionalWebhook;
23
+ function isGlobalAILogicTrigger(blockingTrigger) {
24
+ return (exports.AI_LOGIC_EVENTS.includes(blockingTrigger.eventType) &&
25
+ !blockingTrigger.options?.regionalWebhook);
28
26
  }
29
27
  class AILogicService {
30
28
  constructor() {