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.
- package/lib/appdistribution/client.js +17 -0
- package/lib/apphosting/utils.js +14 -0
- package/lib/apptesting/parseTestFiles.js +25 -13
- package/lib/commands/apptesting.js +35 -16
- package/lib/commands/index.js +5 -5
- package/lib/commands/studio-export.js +2 -2
- package/lib/deploy/apphosting/util.js +1 -1
- package/lib/deploy/firestore/prepare.js +17 -0
- package/lib/deploy/functions/prepareFunctionsUpload.js +1 -1
- package/lib/deploy/functions/runtimes/dart/index.js +282 -0
- package/lib/deploy/functions/runtimes/index.js +1 -1
- package/lib/deploy/functions/runtimes/supported/index.js +4 -0
- package/lib/emulator/apphosting/serve.js +13 -15
- package/lib/emulator/downloadableEmulatorInfo.json +37 -37
- package/lib/emulator/functionsEmulator.js +103 -24
- package/lib/emulator/functionsRuntimeWorker.js +21 -18
- package/lib/firebase_studio/migrate.js +274 -95
- package/lib/init/features/functions/dart.js +31 -0
- package/lib/init/features/functions/index.js +14 -0
- package/lib/mcp/index.js +18 -6
- package/lib/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/schema/firebase-config.json +7 -0
- package/templates/firebase-studio-export/readme_template.md +13 -7
- package/templates/firebase-studio-export/system_instructions_template.md +14 -0
- package/templates/firebase-studio-export/workflows/cleanup.md +20 -0
- package/templates/init/apphosting/apphosting.yaml +1 -0
- package/templates/init/functions/dart/_gitignore +11 -0
- package/templates/init/functions/dart/pubspec.yaml +14 -0
- package/templates/init/functions/dart/server.dart +15 -0
- package/lib/deploy/functions/runtimes/dart.js +0 -42
- 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
|
-
|
|
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.
|
|
12
|
-
"expectedSize":
|
|
13
|
-
"expectedChecksum": "
|
|
14
|
-
"expectedChecksumSHA256": "
|
|
15
|
-
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.20.
|
|
16
|
-
"downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.20.
|
|
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.
|
|
48
|
-
"expectedSize":
|
|
49
|
-
"expectedChecksum": "
|
|
50
|
-
"expectedChecksumSHA256": "
|
|
51
|
-
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.
|
|
52
|
-
"downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.
|
|
53
|
-
"binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.
|
|
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.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
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.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
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.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
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.
|
|
82
|
-
"expectedSize":
|
|
83
|
-
"expectedChecksum": "
|
|
84
|
-
"expectedChecksumSHA256": "
|
|
85
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.
|
|
86
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
234
|
-
return
|
|
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.
|
|
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()) {
|