firebase-tools 15.10.0 → 15.11.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 (32) hide show
  1. package/lib/appdistribution/client.js +17 -0
  2. package/lib/apphosting/utils.js +14 -0
  3. package/lib/apptesting/parseTestFiles.js +25 -13
  4. package/lib/commands/apptesting.js +35 -16
  5. package/lib/commands/index.js +5 -5
  6. package/lib/commands/studio-export.js +2 -2
  7. package/lib/deploy/apphosting/util.js +1 -1
  8. package/lib/deploy/firestore/prepare.js +17 -0
  9. package/lib/deploy/functions/prepareFunctionsUpload.js +1 -1
  10. package/lib/deploy/functions/runtimes/dart/index.js +282 -0
  11. package/lib/deploy/functions/runtimes/index.js +1 -1
  12. package/lib/deploy/functions/runtimes/supported/index.js +4 -0
  13. package/lib/emulator/apphosting/serve.js +13 -15
  14. package/lib/emulator/downloadableEmulatorInfo.json +37 -37
  15. package/lib/emulator/functionsEmulator.js +103 -24
  16. package/lib/emulator/functionsRuntimeWorker.js +21 -18
  17. package/lib/firebase_studio/migrate.js +274 -95
  18. package/lib/init/features/functions/dart.js +31 -0
  19. package/lib/init/features/functions/index.js +14 -0
  20. package/lib/mcp/index.js +18 -6
  21. package/lib/tsconfig.publish.tsbuildinfo +1 -1
  22. package/package.json +1 -1
  23. package/schema/firebase-config.json +7 -0
  24. package/templates/firebase-studio-export/readme_template.md +13 -7
  25. package/templates/firebase-studio-export/system_instructions_template.md +14 -0
  26. package/templates/firebase-studio-export/workflows/cleanup.md +20 -0
  27. package/templates/init/apphosting/apphosting.yaml +1 -0
  28. package/templates/init/functions/dart/_gitignore +11 -0
  29. package/templates/init/functions/dart/pubspec.yaml +14 -0
  30. package/templates/init/functions/dart/server.dart +15 -0
  31. package/lib/deploy/functions/runtimes/dart.js +0 -42
  32. package/templates/firebase-studio-export/workflows/startup_workflow.md +0 -12
@@ -20,9 +20,10 @@ const utils_1 = require("../../utils");
20
20
  const apphosting = require("../../gcp/apphosting");
21
21
  const constants_2 = require("../constants");
22
22
  const fetchWebSetup_1 = require("../../fetchWebSetup");
23
- const apps_1 = require("../../management/apps");
24
23
  const child_process_1 = require("child_process");
25
24
  const semver_1 = require("semver");
25
+ const utils_2 = require("../../apphosting/utils");
26
+ const apps_1 = require("../../management/apps");
26
27
  const secretResourceRegex = /^projects\/([^/]+)\/secrets\/([^/]+)(?:\/versions\/((?:latest)|\d+))?$/;
27
28
  const secretShorthandRegex = /^([^/@]+)(?:@((?:latest)|\d+))?$/;
28
29
  async function loadSecret(project, name) {
@@ -81,6 +82,15 @@ async function start(options) {
81
82
  startCommand = await (0, developmentServer_1.detectPackageManagerStartCommand)(backendRoot);
82
83
  developmentServer_2.logger.logLabeled("BULLET", types_1.Emulators.APPHOSTING, `starting app with: '${startCommand}'`);
83
84
  }
85
+ const packageManager = await (0, developmentServer_1.detectPackageManager)(backendRoot).catch(() => undefined);
86
+ let autoinitEnvVars = {};
87
+ if (packageManager === "pnpm") {
88
+ (0, utils_1.logLabeledWarning)("apphosting", "Firebase JS SDK autoinit does not currently support PNPM.");
89
+ }
90
+ else {
91
+ const webappConfig = await getBackendAppConfig(options?.projectId, options?.backendId);
92
+ autoinitEnvVars = (0, utils_2.getAutoinitEnvVars)(webappConfig);
93
+ }
84
94
  const apphostingLocalConfig = await (0, config_1.getLocalAppHostingConfiguration)(backendRoot);
85
95
  const resolveEnv = Object.entries(apphostingLocalConfig.env).map(async ([key, value]) => [
86
96
  key,
@@ -88,6 +98,7 @@ async function start(options) {
88
98
  ]);
89
99
  const environmentVariablesToInject = {
90
100
  NODE_ENV: process.env.NODE_ENV,
101
+ ...autoinitEnvVars,
91
102
  ...getEmulatorEnvs(),
92
103
  ...Object.fromEntries(await Promise.all(resolveEnv)),
93
104
  FIREBASE_APP_HOSTING: "1",
@@ -96,20 +107,7 @@ async function start(options) {
96
107
  PROJECT_ID: options?.projectId,
97
108
  PORT: port.toString(),
98
109
  };
99
- const packageManager = await (0, developmentServer_1.detectPackageManager)(backendRoot).catch(() => undefined);
100
- if (packageManager === "pnpm") {
101
- (0, utils_1.logLabeledWarning)("apphosting", `Firebase JS SDK autoinit does not currently support PNPM.`);
102
- }
103
- else {
104
- const webappConfig = await getBackendAppConfig(options?.projectId, options?.backendId);
105
- if (webappConfig) {
106
- environmentVariablesToInject["FIREBASE_WEBAPP_CONFIG"] || (environmentVariablesToInject["FIREBASE_WEBAPP_CONFIG"] = JSON.stringify(webappConfig));
107
- environmentVariablesToInject["FIREBASE_CONFIG"] || (environmentVariablesToInject["FIREBASE_CONFIG"] = JSON.stringify({
108
- databaseURL: webappConfig.databaseURL,
109
- storageBucket: webappConfig.storageBucket,
110
- projectId: webappConfig.projectId,
111
- }));
112
- }
110
+ if (packageManager !== "pnpm") {
113
111
  await tripFirebasePostinstall(backendRoot, environmentVariablesToInject);
114
112
  }
115
113
  (0, spawn_1.spawnWithCommandString)(startCommand, backendRoot, environmentVariablesToInject)
@@ -8,12 +8,12 @@
8
8
  "downloadPathRelativeToCacheDir": "firebase-database-emulator-v4.11.2.jar"
9
9
  },
10
10
  "firestore": {
11
- "version": "1.20.2",
12
- "expectedSize": 123046947,
13
- "expectedChecksum": "3b04168138ff8ab17025fe7756e05dc9",
14
- "expectedChecksumSHA256": "4a117fc297b1441eac1b7756e80442e86ef88865b9e3caf6f59eabf83da574f8",
15
- "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.20.2.jar",
16
- "downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.20.2.jar"
11
+ "version": "1.20.4",
12
+ "expectedSize": 136551710,
13
+ "expectedChecksum": "8397dac992e02454914dec28d23b6ed8",
14
+ "expectedChecksumSHA256": "49ee7814741b481ae5becfdaa7b55693bdf7a03363cd184e35c36c1595a9643",
15
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.20.4.jar",
16
+ "downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.20.4.jar"
17
17
  },
18
18
  "storage": {
19
19
  "version": "1.1.3",
@@ -44,46 +44,46 @@
44
44
  }
45
45
  },
46
46
  "pubsub": {
47
- "version": "0.8.27",
48
- "expectedSize": 52924291,
49
- "expectedChecksum": "cb0d35db6aa1bb5e3f7e2a5a690c631d",
50
- "expectedChecksumSHA256": "0b793b420b608b68c200a0d15123c63967ac2863bbd9545ecb087d5b28871339",
51
- "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.27.zip",
52
- "downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.27.zip",
53
- "binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.27/pubsub-emulator/bin/cloud-pubsub-emulator"
47
+ "version": "0.8.29",
48
+ "expectedSize": 52906482,
49
+ "expectedChecksum": "8345b0a923dda5634dd56a25f913cf37",
50
+ "expectedChecksumSHA256": "31228112fb95a6818d13a88e801f4be6fb5ea5b601175b74528cc61823ea5507",
51
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.29.zip",
52
+ "downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.29.zip",
53
+ "binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.29/pubsub-emulator/bin/cloud-pubsub-emulator"
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "3.2.1",
58
- "expectedSize": 30880608,
59
- "expectedChecksum": "ef63443e5132e4ade7b89506fea60e29",
60
- "expectedChecksumSHA256": "0a9c05413cb3048a4bef1968e55109ca67eb607e6d77355741cb9a7dcae51607",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.2.1",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.2.1"
57
+ "version": "3.3.0",
58
+ "expectedSize": 32051312,
59
+ "expectedChecksum": "9c9d9fb375826d90f9a57ce7a1770afc",
60
+ "expectedChecksumSHA256": "ae2fe4f4b9a79b6bfd52ef227dcdb5e58c5b0e3a662a3874abc07c73d2e8f8e4",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.3.0",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0"
63
63
  },
64
64
  "darwin_arm64": {
65
- "version": "3.2.1",
66
- "expectedSize": 30339010,
67
- "expectedChecksum": "866989b28ae5cad1084403bd4404dcee",
68
- "expectedChecksumSHA256": "558e334c456c646b1ddf4b32ab43d976c5b03c191a4c92855e8c93cd32e1215a",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.2.1",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.2.1"
65
+ "version": "3.3.0",
66
+ "expectedSize": 30237490,
67
+ "expectedChecksum": "cd6e0b50e7614edd420a44cde1a6de54",
68
+ "expectedChecksumSHA256": "1524161c428f6c97ae1e4eb6371028bace2280f1090ebe89e73313afb30b7bdc",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.3.0",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0"
71
71
  },
72
72
  "win32": {
73
- "version": "3.2.1",
74
- "expectedSize": 31385088,
75
- "expectedChecksum": "cb46949156c889d5724f7fa80e8a613b",
76
- "expectedChecksumSHA256": "1dd1f9ef7910d8295baac61741fde29fac536d82ff841a2d5cefe25e98f7cd98",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.2.1",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.2.1.exe"
73
+ "version": "3.3.0",
74
+ "expectedSize": 32093696,
75
+ "expectedChecksum": "e2b6a90994609e613aae189fb6bb732b",
76
+ "expectedChecksumSHA256": "ddf44b362658a43ffb97c23e605e26d257e786adb9a71466ebd756b36c4dd212",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.3.0",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0.exe"
79
79
  },
80
80
  "linux": {
81
- "version": "3.2.1",
82
- "expectedSize": 30798008,
83
- "expectedChecksum": "8c29b6b526efcbf13de278bd3b5b598a",
84
- "expectedChecksumSHA256": "d7eb7f7afd98dd9018039c83782312f975b160c3708eb49f8fecdf7d27508d82",
85
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.2.1",
86
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.2.1"
81
+ "version": "3.3.0",
82
+ "expectedSize": 31211704,
83
+ "expectedChecksum": "48b40f31c1897afd39e3b256c356730a",
84
+ "expectedChecksumSHA256": "e6774287f3a3ea7ee240f524644e0b0441720dd5d55d6a970a6c4db9923b517f",
85
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.3.0",
86
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.3.0"
87
87
  }
88
88
  }
89
89
  }
@@ -35,6 +35,8 @@ const v1_1 = require("../functions/events/v1");
35
35
  const build_1 = require("../deploy/functions/build");
36
36
  const env_1 = require("./env");
37
37
  const python_1 = require("../functions/python");
38
+ const supported_1 = require("../deploy/functions/runtimes/supported");
39
+ const dart_1 = require("../deploy/functions/runtimes/dart");
38
40
  const EVENT_INVOKE_GA4 = "functions_invoke";
39
41
  const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
40
42
  class IPCConn {
@@ -83,6 +85,7 @@ class FunctionsEmulator {
83
85
  this.staticBackends = [];
84
86
  this.dynamicBackends = [];
85
87
  this.watchers = [];
88
+ this.watchCleanups = [];
86
89
  this.debugMode = false;
87
90
  this.staticBackends = args.emulatableBackends;
88
91
  emulatorLogger_1.EmulatorLogger.setVerbosity(this.args.verbosity ? emulatorLogger_1.Verbosity[this.args.verbosity] : emulatorLogger_1.Verbosity["DEBUG"]);
@@ -208,7 +211,7 @@ class FunctionsEmulator {
208
211
  async sendRequest(trigger, body) {
209
212
  const record = this.getTriggerRecordByKey(this.getTriggerKey(trigger));
210
213
  const pool = this.workerPools[record.backend.codebase];
211
- if (!pool.readyForWork(trigger.id)) {
214
+ if (!pool.readyForWork(trigger.id, record.backend.runtime)) {
212
215
  try {
213
216
  await this.startRuntime(record.backend, trigger);
214
217
  }
@@ -217,7 +220,7 @@ class FunctionsEmulator {
217
220
  return;
218
221
  }
219
222
  }
220
- const worker = pool.getIdleWorker(trigger.id);
223
+ const worker = pool.getIdleWorker(trigger.id, record.backend.runtime);
221
224
  if (this.debugMode) {
222
225
  await worker.sendDebugMsg({
223
226
  functionTarget: trigger.entryPoint,
@@ -229,10 +232,13 @@ class FunctionsEmulator {
229
232
  "Content-Type": "application/json",
230
233
  "Content-Length": `${reqBody.length}`,
231
234
  };
235
+ const isDart = (0, supported_1.isLanguageRuntime)(record.backend.runtime, "dart");
236
+ const path = isDart ? `/${trigger.entryPoint}` : `/`;
232
237
  return new Promise((resolve, reject) => {
233
238
  const req = http.request({
234
239
  ...worker.runtime.conn.httpReqOpts(),
235
- path: `/`,
240
+ method: "POST",
241
+ path: path,
236
242
  headers: headers,
237
243
  }, resolve);
238
244
  req.on("error", reject);
@@ -264,23 +270,43 @@ class FunctionsEmulator {
264
270
  async connect() {
265
271
  for (const backend of this.staticBackends) {
266
272
  this.logger.logLabeled("BULLET", "functions", `Watching "${backend.functionsDir}" for Cloud Functions...`);
267
- const watcher = chokidar.watch(backend.functionsDir, {
268
- ignored: [
269
- /.+?[\\\/]node_modules[\\\/].+?/,
270
- /(^|[\/\\])\../,
271
- /.+\.log/,
272
- /.+?[\\\/]venv[\\\/].+?/,
273
- ...(backend.ignore?.map((i) => `**/${i}`) ?? []),
274
- ],
275
- persistent: true,
276
- });
277
- this.watchers.push(watcher);
278
- const debouncedLoadTriggers = (0, utils_1.debounce)(() => this.loadTriggers(backend), 1000);
279
- watcher.on("change", (filePath) => {
280
- this.logger.log("DEBUG", `File ${filePath} changed, reloading triggers`);
281
- return debouncedLoadTriggers();
282
- });
283
273
  await this.loadTriggers(backend, true);
274
+ const isDart = (0, supported_1.isLanguageRuntime)(backend.runtime, "dart");
275
+ if (isDart) {
276
+ const runtimeDelegateContext = {
277
+ projectId: this.args.projectId,
278
+ projectDir: this.args.projectDir,
279
+ sourceDir: backend.functionsDir,
280
+ runtime: backend.runtime,
281
+ };
282
+ const delegate = await runtimes.getRuntimeDelegate(runtimeDelegateContext);
283
+ this.logger.logLabeled("BULLET", "functions", `Starting build_runner watch for Dart functions...`);
284
+ const debouncedLoadTriggers = (0, utils_1.debounce)(() => this.loadTriggers(backend), 1000);
285
+ const cleanup = await delegate.watch(() => {
286
+ this.logger.log("DEBUG", "build_runner rebuilt, reloading triggers");
287
+ debouncedLoadTriggers();
288
+ });
289
+ this.watchCleanups.push(cleanup);
290
+ this.logger.logLabeled("SUCCESS", "functions", `build_runner initial build completed`);
291
+ }
292
+ else {
293
+ const watcher = chokidar.watch(backend.functionsDir, {
294
+ ignored: [
295
+ /(^|[\/\\])\../,
296
+ /.+\.log/,
297
+ /.+?[\\\/]node_modules[\\\/].+?/,
298
+ /.+?[\\\/]venv[\\\/].+?/,
299
+ ...(backend.ignore?.map((i) => `**/${i}`) ?? []),
300
+ ],
301
+ persistent: true,
302
+ });
303
+ this.watchers.push(watcher);
304
+ const debouncedLoadTriggers = (0, utils_1.debounce)(() => this.loadTriggers(backend), 1000);
305
+ watcher.on("change", (filePath) => {
306
+ this.logger.log("DEBUG", `File ${filePath} changed, reloading triggers`);
307
+ return debouncedLoadTriggers();
308
+ });
309
+ }
284
310
  }
285
311
  await this.performPostLoadOperations();
286
312
  return;
@@ -300,6 +326,10 @@ class FunctionsEmulator {
300
326
  await watcher.close();
301
327
  }
302
328
  this.watchers = [];
329
+ for (const cleanup of this.watchCleanups) {
330
+ await cleanup();
331
+ }
332
+ this.watchCleanups = [];
303
333
  if (this.destroyServer) {
304
334
  await this.destroyServer();
305
335
  }
@@ -1098,13 +1128,52 @@ class FunctionsEmulator {
1098
1128
  conn: new TCPConn("127.0.0.1", port),
1099
1129
  };
1100
1130
  }
1131
+ async startDart(backend, envs) {
1132
+ if (this.debugMode) {
1133
+ this.logger.log("WARN", "--inspect-functions not supported for Dart functions. Ignored.");
1134
+ }
1135
+ const port = await portfinder.getPortPromise({
1136
+ port: 8081 + (0, utils_1.randomInt)(0, 1000),
1137
+ });
1138
+ const args = ["run", "--no-serve-devtools", dart_1.DART_ENTRY_POINT];
1139
+ const dartEnvs = { ...envs };
1140
+ delete dartEnvs.FUNCTION_TARGET;
1141
+ delete dartEnvs.FUNCTION_SIGNATURE_TYPE;
1142
+ const bin = backend.bin || "dart";
1143
+ logger_1.logger.debug(`Starting Dart runtime with args: ${args.join(" ")} on port ${port}`);
1144
+ const childProcess = spawn(bin, args, {
1145
+ cwd: backend.functionsDir,
1146
+ env: {
1147
+ ...process.env,
1148
+ ...dartEnvs,
1149
+ HOST: "127.0.0.1",
1150
+ PORT: port.toString(),
1151
+ },
1152
+ stdio: ["pipe", "pipe", "pipe"],
1153
+ });
1154
+ childProcess.stdout?.on("data", (chunk) => {
1155
+ this.logger.log("DEBUG", `[dart] ${chunk.toString("utf8")}`);
1156
+ });
1157
+ childProcess.stderr?.on("data", (chunk) => {
1158
+ this.logger.log("DEBUG", `[dart] ${chunk.toString("utf8")}`);
1159
+ });
1160
+ return {
1161
+ process: childProcess,
1162
+ events: new events_1.EventEmitter(),
1163
+ cwd: backend.functionsDir,
1164
+ conn: new TCPConn("127.0.0.1", port),
1165
+ };
1166
+ }
1101
1167
  async startRuntime(backend, trigger) {
1102
1168
  const runtimeEnv = this.getRuntimeEnvs(backend, trigger);
1103
1169
  const secretEnvs = await this.resolveSecretEnvs(backend, trigger);
1104
1170
  let runtime;
1105
- if (backend.runtime.startsWith("python")) {
1171
+ if ((0, supported_1.isLanguageRuntime)(backend.runtime, "python")) {
1106
1172
  runtime = await this.startPython(backend, { ...runtimeEnv, ...secretEnvs });
1107
1173
  }
1174
+ else if ((0, supported_1.isLanguageRuntime)(backend.runtime, "dart")) {
1175
+ runtime = await this.startDart(backend, { ...runtimeEnv, ...secretEnvs });
1176
+ }
1108
1177
  else {
1109
1178
  runtime = await this.startNode(backend, { ...runtimeEnv, ...secretEnvs });
1110
1179
  }
@@ -1113,7 +1182,7 @@ class FunctionsEmulator {
1113
1182
  ref: backend.extensionVersion?.ref,
1114
1183
  };
1115
1184
  const pool = this.workerPools[backend.codebase];
1116
- const worker = pool.addWorker(trigger, runtime, extensionLogInfo);
1185
+ const worker = pool.addWorker(trigger, runtime, extensionLogInfo, backend.runtime);
1117
1186
  await worker.waitForSocketReady();
1118
1187
  return worker;
1119
1188
  }
@@ -1172,10 +1241,20 @@ class FunctionsEmulator {
1172
1241
  });
1173
1242
  this.logger.log("DEBUG", `[functions] Runtime ready! Sending request!`);
1174
1243
  const url = new url_1.URL(`${req.protocol}://${req.hostname}${req.url}`);
1175
- const path = `${url.pathname}${url.search}`.replace(new RegExp(`\/${this.args.projectId}\/[^\/]*\/${req.params.trigger_name}\/?`), "/");
1244
+ let path = `${url.pathname}${url.search}`.replace(new RegExp(`\/${this.args.projectId}\/[^\/]*\/${req.params.trigger_name}\/?`), "/");
1245
+ const isDart = (0, supported_1.isLanguageRuntime)(record.backend.runtime, "dart");
1246
+ if (isDart) {
1247
+ const isBackgroundRoute = req.url.startsWith("/functions/projects/");
1248
+ if (isBackgroundRoute || path === "/") {
1249
+ path = `/${trigger.entryPoint}`;
1250
+ }
1251
+ else {
1252
+ path = `/${trigger.entryPoint}${path}`;
1253
+ }
1254
+ }
1176
1255
  this.logger.log("DEBUG", `[functions] Got req.url=${req.url}, mapping to path=${path}`);
1177
1256
  const pool = this.workerPools[record.backend.codebase];
1178
- if (!pool.readyForWork(trigger.id)) {
1257
+ if (!pool.readyForWork(trigger.id, record.backend.runtime)) {
1179
1258
  try {
1180
1259
  await this.startRuntime(record.backend, trigger);
1181
1260
  }
@@ -1196,7 +1275,7 @@ class FunctionsEmulator {
1196
1275
  method,
1197
1276
  path,
1198
1277
  headers: req.headers,
1199
- }, res, reqBody, debugBundle);
1278
+ }, res, reqBody, debugBundle, record.backend.runtime);
1200
1279
  }
1201
1280
  }
1202
1281
  exports.FunctionsEmulator = FunctionsEmulator;
@@ -8,6 +8,7 @@ const events_1 = require("events");
8
8
  const emulatorLogger_1 = require("./emulatorLogger");
9
9
  const error_1 = require("../error");
10
10
  const discovery_1 = require("../deploy/functions/runtimes/discovery");
11
+ const supported_1 = require("../deploy/functions/runtimes/supported");
11
12
  var RuntimeWorkerState;
12
13
  (function (RuntimeWorkerState) {
13
14
  RuntimeWorkerState["CREATED"] = "CREATED";
@@ -226,13 +227,14 @@ class RuntimeWorkerPool {
226
227
  this.mode = mode;
227
228
  this.workers = new Map();
228
229
  }
229
- getKey(triggerId) {
230
+ getKey(triggerId, runtime) {
230
231
  if (this.mode === types_1.FunctionsExecutionMode.SEQUENTIAL) {
231
232
  return "~shared~";
232
233
  }
233
- else {
234
- return triggerId || "~diagnostic~";
234
+ if ((0, supported_1.isLanguageRuntime)(runtime, "dart")) {
235
+ return "~dart-shared~";
235
236
  }
237
+ return triggerId || "~diagnostic~";
236
238
  }
237
239
  refresh() {
238
240
  for (const arr of this.workers.values()) {
@@ -261,13 +263,13 @@ class RuntimeWorkerPool {
261
263
  });
262
264
  }
263
265
  }
264
- readyForWork(triggerId) {
265
- const idleWorker = this.getIdleWorker(triggerId);
266
+ readyForWork(triggerId, runtime) {
267
+ const idleWorker = this.getIdleWorker(triggerId, runtime);
266
268
  return !!idleWorker;
267
269
  }
268
- async submitRequest(triggerId, req, resp, body, debug) {
270
+ async submitRequest(triggerId, req, resp, body, debug, runtime) {
269
271
  this.log(`submitRequest(triggerId=${triggerId})`);
270
- const worker = this.getIdleWorker(triggerId);
272
+ const worker = this.getIdleWorker(triggerId, runtime);
271
273
  if (!worker) {
272
274
  throw new error_1.FirebaseError("Internal Error: can't call submitRequest without checking for idle workers");
273
275
  }
@@ -276,11 +278,11 @@ class RuntimeWorkerPool {
276
278
  }
277
279
  return worker.request(req, resp, body, !!debug);
278
280
  }
279
- getIdleWorker(triggerId) {
281
+ getIdleWorker(triggerId, runtime) {
280
282
  this.cleanUpWorkers();
281
- const triggerWorkers = this.getTriggerWorkers(triggerId);
283
+ const triggerWorkers = this.getTriggerWorkers(triggerId, runtime);
282
284
  if (!triggerWorkers.length) {
283
- this.setTriggerWorkers(triggerId, []);
285
+ this.setTriggerWorkers(triggerId, [], runtime);
284
286
  return;
285
287
  }
286
288
  for (const worker of triggerWorkers) {
@@ -290,21 +292,22 @@ class RuntimeWorkerPool {
290
292
  }
291
293
  return;
292
294
  }
293
- addWorker(trigger, runtime, extensionLogInfo) {
294
- this.log(`addWorker(${this.getKey(trigger?.id)})`);
295
+ addWorker(trigger, runtime, extensionLogInfo, runtimeType) {
296
+ const key = this.getKey(trigger?.id, runtimeType);
297
+ this.log(`addWorker(${key})`);
295
298
  const disableTimeout = !trigger?.id || this.mode === types_1.FunctionsExecutionMode.SEQUENTIAL;
296
299
  const worker = new RuntimeWorker(trigger?.id, runtime, extensionLogInfo, disableTimeout ? undefined : trigger?.timeoutSeconds);
297
- const keyWorkers = this.getTriggerWorkers(trigger?.id);
300
+ const keyWorkers = this.getTriggerWorkers(trigger?.id, runtimeType);
298
301
  keyWorkers.push(worker);
299
- this.setTriggerWorkers(trigger?.id, keyWorkers);
302
+ this.setTriggerWorkers(trigger?.id, keyWorkers, runtimeType);
300
303
  this.log(`Adding worker with key ${worker.triggerKey}, total=${keyWorkers.length}`);
301
304
  return worker;
302
305
  }
303
- getTriggerWorkers(triggerId) {
304
- return this.workers.get(this.getKey(triggerId)) || [];
306
+ getTriggerWorkers(triggerId, runtime) {
307
+ return this.workers.get(this.getKey(triggerId, runtime)) || [];
305
308
  }
306
- setTriggerWorkers(triggerId, workers) {
307
- this.workers.set(this.getKey(triggerId), workers);
309
+ setTriggerWorkers(triggerId, workers, runtime) {
310
+ this.workers.set(this.getKey(triggerId, runtime), workers);
308
311
  }
309
312
  cleanUpWorkers() {
310
313
  for (const [key, keyWorkers] of this.workers.entries()) {