firebase-tools 10.1.5 → 10.2.2
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 +1 -0
- package/lib/apiv2.js +3 -0
- package/lib/appdistribution/options-parser-util.js +1 -1
- package/lib/auth.js +62 -25
- package/lib/command.js +1 -1
- package/lib/commands/apps-android-sha-create.js +2 -2
- package/lib/commands/apps-sdkconfig.js +1 -1
- package/lib/commands/auth-import.js +1 -1
- package/lib/commands/database-rules-list.js +2 -2
- package/lib/commands/emulators-start.js +1 -1
- package/lib/commands/ext-configure.js +1 -0
- package/lib/commands/ext-dev-init.js +49 -49
- package/lib/commands/ext-export.js +12 -2
- package/lib/commands/ext-install.js +104 -103
- package/lib/commands/ext-uninstall.js +9 -8
- package/lib/commands/ext-update.js +10 -9
- package/lib/commands/functions-config-clone.js +1 -1
- package/lib/commands/functions-config-export.js +1 -1
- package/lib/commands/functions-secrets-access.js +17 -0
- package/lib/commands/functions-secrets-destroy.js +40 -0
- package/lib/commands/functions-secrets-get.js +21 -0
- package/lib/commands/functions-secrets-prune.js +50 -0
- package/lib/commands/functions-secrets-set.js +46 -0
- package/lib/commands/hosting-clone.js +3 -3
- package/lib/commands/index.js +7 -3
- package/lib/commands/login.js +1 -1
- package/lib/commands/remoteconfig-get.js +1 -1
- package/lib/deploy/extensions/deploymentSummary.js +3 -3
- package/lib/deploy/extensions/params.js +3 -0
- package/lib/deploy/extensions/planner.js +2 -1
- package/lib/deploy/extensions/tasks.js +1 -1
- package/lib/deploy/functions/backend.js +20 -5
- package/lib/deploy/functions/checkIam.js +1 -1
- package/lib/deploy/functions/containerCleaner.js +3 -3
- package/lib/deploy/functions/ensure.js +112 -0
- package/lib/deploy/functions/ensureCloudBuildEnabled.js +0 -49
- package/lib/deploy/functions/functionsDeployHelper.js +2 -2
- package/lib/deploy/functions/prepare.js +15 -20
- package/lib/deploy/functions/pricing.js +1 -1
- package/lib/deploy/functions/prompts.js +2 -2
- package/lib/deploy/functions/release/fabricator.js +3 -3
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/release/planner.js +11 -8
- package/lib/deploy/functions/release/reporter.js +3 -0
- package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
- package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +17 -11
- package/lib/deploy/functions/runtimes/golang/index.js +2 -2
- package/lib/deploy/functions/runtimes/node/index.js +26 -0
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +40 -7
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/validate.js +58 -3
- package/lib/deploy/hosting/client.js +9 -0
- package/lib/deploy/hosting/convertConfig.js +6 -0
- package/lib/deploy/hosting/deploy.js +2 -2
- package/lib/deploy/hosting/hashcache.js +21 -19
- package/lib/deploy/hosting/index.js +5 -5
- package/lib/deploy/hosting/prepare.js +25 -25
- package/lib/deploy/hosting/release.js +21 -24
- package/lib/deploy/hosting/uploader.js +5 -5
- package/lib/deploy/remoteconfig/functions.js +2 -2
- package/lib/emulator/auth/cloudFunctions.js +1 -1
- package/lib/emulator/auth/operations.js +1 -1
- package/lib/emulator/commandUtils.js +5 -1
- package/lib/emulator/constants.js +3 -0
- package/lib/emulator/controller.js +48 -18
- package/lib/emulator/download.js +18 -1
- package/lib/emulator/downloadableEmulators.js +30 -13
- package/lib/emulator/emulatorLogger.js +19 -1
- package/lib/emulator/extensions/validation.js +35 -0
- package/lib/emulator/extensionsEmulator.js +140 -0
- package/lib/emulator/functionsEmulator.js +175 -86
- package/lib/emulator/functionsEmulatorRuntime.js +108 -83
- package/lib/emulator/functionsEmulatorShared.js +51 -1
- package/lib/emulator/functionsEmulatorShell.js +1 -2
- package/lib/emulator/functionsEmulatorUtils.js +4 -4
- package/lib/emulator/functionsRuntimeWorker.js +3 -3
- package/lib/emulator/hub.js +4 -3
- package/lib/emulator/loggingEmulator.js +1 -1
- package/lib/emulator/pubsubEmulator.js +1 -1
- package/lib/emulator/registry.js +10 -2
- package/lib/emulator/storage/apis/firebase.js +31 -26
- package/lib/emulator/storage/apis/gcloud.js +7 -12
- package/lib/emulator/storage/files.js +36 -34
- package/lib/emulator/storage/index.js +2 -2
- package/lib/emulator/storage/metadata.js +2 -2
- package/lib/emulator/storage/rules/runtime.js +8 -7
- package/lib/emulator/types.js +3 -0
- package/lib/ensureApiEnabled.js +5 -1
- package/lib/error.js +1 -1
- package/lib/extensions/askUserForParam.js +2 -2
- package/lib/extensions/changelog.js +3 -1
- package/lib/extensions/checkProjectBilling.js +1 -1
- package/lib/extensions/diagnose.js +56 -0
- package/lib/extensions/displayExtensionInfo.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +24 -8
- package/lib/extensions/emulator/specHelper.js +10 -23
- package/lib/extensions/export.js +1 -51
- package/lib/extensions/extensionsApi.js +1 -1
- package/lib/extensions/extensionsHelper.js +23 -10
- package/lib/extensions/listExtensions.js +2 -0
- package/lib/extensions/manifest.js +48 -0
- package/lib/extensions/metricsUtils.js +4 -4
- package/lib/extensions/paramHelper.js +4 -4
- package/lib/extensions/refs.js +1 -1
- package/lib/extensions/secretsUtils.js +4 -4
- package/lib/functional.js +1 -1
- package/lib/functions/env.js +7 -8
- package/lib/functions/secrets.js +112 -0
- package/lib/gcp/cloudfunctions.js +24 -5
- package/lib/gcp/cloudfunctionsv2.js +18 -5
- package/lib/gcp/cloudtasks.js +1 -1
- package/lib/gcp/docker.js +2 -2
- package/lib/gcp/run.js +2 -2
- package/lib/gcp/secretManager.js +128 -46
- package/lib/gcp/storage.js +1 -0
- package/lib/hosting/api.js +1 -1
- package/lib/hosting/functionsProxy.js +15 -5
- package/lib/hosting/proxy.js +2 -2
- package/lib/init/features/account.js +1 -1
- package/lib/management/database.js +1 -1
- package/lib/previews.js +1 -1
- package/lib/responseToError.js +16 -7
- package/lib/serve/functions.js +2 -2
- package/lib/serve/hosting.js +1 -1
- package/lib/utils.js +7 -2
- package/npm-shrinkwrap.json +904 -412
- package/package.json +3 -3
- package/schema/firebase-config.json +32 -0
- package/templates/init/functions/javascript/package.lint.json +3 -3
- package/templates/init/functions/javascript/package.nolint.json +2 -2
- package/templates/init/functions/typescript/package.lint.json +7 -7
- package/templates/init/functions/typescript/package.nolint.json +3 -3
|
@@ -8,7 +8,9 @@ const express = require("express");
|
|
|
8
8
|
const clc = require("cli-color");
|
|
9
9
|
const http = require("http");
|
|
10
10
|
const jwt = require("jsonwebtoken");
|
|
11
|
+
const cors = require("cors");
|
|
11
12
|
const url_1 = require("url");
|
|
13
|
+
const events_1 = require("events");
|
|
12
14
|
const api = require("../api");
|
|
13
15
|
const logger_1 = require("../logger");
|
|
14
16
|
const track = require("../track");
|
|
@@ -19,7 +21,6 @@ const spawn = require("cross-spawn");
|
|
|
19
21
|
const child_process_1 = require("child_process");
|
|
20
22
|
const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
|
|
21
23
|
const registry_1 = require("./registry");
|
|
22
|
-
const events_1 = require("events");
|
|
23
24
|
const emulatorLogger_1 = require("./emulatorLogger");
|
|
24
25
|
const functionsRuntimeWorker_1 = require("./functionsRuntimeWorker");
|
|
25
26
|
const error_1 = require("../error");
|
|
@@ -27,10 +28,14 @@ const workQueue_1 = require("./workQueue");
|
|
|
27
28
|
const utils_1 = require("../utils");
|
|
28
29
|
const defaultCredentials_1 = require("../defaultCredentials");
|
|
29
30
|
const adminSdkConfig_1 = require("./adminSdkConfig");
|
|
30
|
-
const functionsEnv = require("../functions/env");
|
|
31
31
|
const types_2 = require("./events/types");
|
|
32
32
|
const validate_1 = require("../deploy/functions/validate");
|
|
33
|
+
const secretManager_1 = require("../gcp/secretManager");
|
|
34
|
+
const runtimes = require("../deploy/functions/runtimes");
|
|
35
|
+
const backend = require("../deploy/functions/backend");
|
|
36
|
+
const functionsEnv = require("../functions/env");
|
|
33
37
|
const EVENT_INVOKE = "functions:invoke";
|
|
38
|
+
const LOCAL_SECRETS_FILE = ".secret.local";
|
|
34
39
|
const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
|
|
35
40
|
class FunctionsEmulator {
|
|
36
41
|
constructor(args) {
|
|
@@ -44,9 +49,7 @@ class FunctionsEmulator {
|
|
|
44
49
|
this.args.disabledRuntimeFeatures = this.args.disabledRuntimeFeatures || {};
|
|
45
50
|
this.args.disabledRuntimeFeatures.timeout = true;
|
|
46
51
|
}
|
|
47
|
-
this.adminSdkConfig = {
|
|
48
|
-
projectId: this.args.projectId,
|
|
49
|
-
};
|
|
52
|
+
this.adminSdkConfig = Object.assign(Object.assign({}, this.args.adminSdkConfig), { projectId: this.args.projectId });
|
|
50
53
|
const mode = this.args.debugPort
|
|
51
54
|
? types_1.FunctionsExecutionMode.SEQUENTIAL
|
|
52
55
|
: types_1.FunctionsExecutionMode.AUTO;
|
|
@@ -76,6 +79,7 @@ class FunctionsEmulator {
|
|
|
76
79
|
createHubServer() {
|
|
77
80
|
this.workQueue.start();
|
|
78
81
|
const hub = express();
|
|
82
|
+
hub.use(cors({ origin: true }));
|
|
79
83
|
const dataMiddleware = (req, res, next) => {
|
|
80
84
|
const chunks = [];
|
|
81
85
|
req.on("data", (chunk) => {
|
|
@@ -90,6 +94,7 @@ class FunctionsEmulator {
|
|
|
90
94
|
const httpsFunctionRoute = `/${this.args.projectId}/:region/:trigger_name`;
|
|
91
95
|
const multicastFunctionRoute = `/functions/projects/:project_id/trigger_multicast`;
|
|
92
96
|
const httpsFunctionRoutes = [httpsFunctionRoute, `${httpsFunctionRoute}/*`];
|
|
97
|
+
const listBackendsRoute = `/backends`;
|
|
93
98
|
const backgroundHandler = (req, res) => {
|
|
94
99
|
var _a;
|
|
95
100
|
const region = req.params.region;
|
|
@@ -150,6 +155,10 @@ class FunctionsEmulator {
|
|
|
150
155
|
});
|
|
151
156
|
res.json({ status: "multicast_acknowledged" });
|
|
152
157
|
};
|
|
158
|
+
const listBackendsHandler = (req, res) => {
|
|
159
|
+
res.json({ backends: this.getBackendInfo() });
|
|
160
|
+
};
|
|
161
|
+
hub.get(listBackendsRoute, dataMiddleware, listBackendsHandler);
|
|
153
162
|
hub.post(backgroundFunctionRoute, dataMiddleware, backgroundHandler);
|
|
154
163
|
hub.post(multicastFunctionRoute, dataMiddleware, multicastHandler);
|
|
155
164
|
hub.all(httpsFunctionRoutes, dataMiddleware, httpsHandler);
|
|
@@ -159,25 +168,23 @@ class FunctionsEmulator {
|
|
|
159
168
|
});
|
|
160
169
|
return hub;
|
|
161
170
|
}
|
|
162
|
-
|
|
163
|
-
const bundleTemplate = this.getBaseBundle(
|
|
164
|
-
const runtimeBundle = Object.assign(Object.assign({}, bundleTemplate), {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
triggerId,
|
|
172
|
-
targetName });
|
|
171
|
+
async invokeTrigger(backend, trigger, proto, runtimeOpts) {
|
|
172
|
+
const bundleTemplate = this.getBaseBundle();
|
|
173
|
+
const runtimeBundle = Object.assign(Object.assign({}, bundleTemplate), { proto });
|
|
174
|
+
if (this.args.debugPort) {
|
|
175
|
+
runtimeBundle.debug = {
|
|
176
|
+
functionTarget: trigger.entryPoint,
|
|
177
|
+
functionSignature: (0, functionsEmulatorShared_1.getSignatureType)(trigger),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
173
180
|
if (!backend.nodeBinary) {
|
|
174
|
-
throw new error_1.FirebaseError(`No node binary for ${
|
|
181
|
+
throw new error_1.FirebaseError(`No node binary for ${trigger.id}. This should never happen.`);
|
|
175
182
|
}
|
|
176
183
|
const opts = runtimeOpts || {
|
|
177
184
|
nodeBinary: backend.nodeBinary,
|
|
178
185
|
extensionTriggers: backend.predefinedTriggers,
|
|
179
186
|
};
|
|
180
|
-
const worker = this.invokeRuntime(
|
|
187
|
+
const worker = await this.invokeRuntime(backend, trigger, runtimeBundle, opts);
|
|
181
188
|
return worker;
|
|
182
189
|
}
|
|
183
190
|
async start() {
|
|
@@ -188,13 +195,15 @@ class FunctionsEmulator {
|
|
|
188
195
|
for (const e of this.args.emulatableBackends) {
|
|
189
196
|
e.env = Object.assign(Object.assign({}, credentialEnv), e.env);
|
|
190
197
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
+
if (Object.keys(this.adminSdkConfig || {}).length <= 1) {
|
|
199
|
+
const adminSdkConfig = await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(this.args.projectId);
|
|
200
|
+
if (adminSdkConfig) {
|
|
201
|
+
this.adminSdkConfig = adminSdkConfig;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
this.logger.logLabeled("WARN", "functions", "Unable to fetch project Admin SDK configuration, Admin SDK behavior in Cloud Functions emulator may be incorrect.");
|
|
205
|
+
this.adminSdkConfig = (0, adminSdkConfig_1.constructDefaultAdminSdkConfig)(this.args.projectId);
|
|
206
|
+
}
|
|
198
207
|
}
|
|
199
208
|
const { host, port } = this.getInfo();
|
|
200
209
|
this.workQueue.start();
|
|
@@ -242,14 +251,30 @@ class FunctionsEmulator {
|
|
|
242
251
|
if (!emulatableBackend.nodeBinary) {
|
|
243
252
|
throw new error_1.FirebaseError(`No node binary for ${emulatableBackend.functionsDir}. This should never happen.`);
|
|
244
253
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
254
|
+
let triggerDefinitions;
|
|
255
|
+
if (emulatableBackend.predefinedTriggers) {
|
|
256
|
+
triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
const runtimeConfig = this.getRuntimeConfig(emulatableBackend);
|
|
260
|
+
const runtimeDelegateContext = {
|
|
261
|
+
projectId: this.args.projectId,
|
|
262
|
+
projectDir: this.args.projectDir,
|
|
263
|
+
sourceDir: emulatableBackend.functionsDir,
|
|
264
|
+
};
|
|
265
|
+
if (emulatableBackend.nodeMajorVersion) {
|
|
266
|
+
runtimeDelegateContext.runtime = `nodejs${emulatableBackend.nodeMajorVersion}`;
|
|
267
|
+
}
|
|
268
|
+
const runtimeDelegate = await runtimes.getRuntimeDelegate(runtimeDelegateContext);
|
|
269
|
+
logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
|
|
270
|
+
await runtimeDelegate.validate();
|
|
271
|
+
logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
|
|
272
|
+
await runtimeDelegate.build();
|
|
273
|
+
logger_1.logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
|
|
274
|
+
const discoveredBackend = await runtimeDelegate.discoverSpec(runtimeConfig, Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), emulatableBackend.env));
|
|
275
|
+
const endpoints = backend.allEndpoints(discoveredBackend);
|
|
276
|
+
triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsFromEndpoints)(endpoints);
|
|
277
|
+
}
|
|
253
278
|
const toSetup = triggerDefinitions.filter((definition) => {
|
|
254
279
|
if (force) {
|
|
255
280
|
return true;
|
|
@@ -305,7 +330,7 @@ class FunctionsEmulator {
|
|
|
305
330
|
}
|
|
306
331
|
}
|
|
307
332
|
else {
|
|
308
|
-
this.logger.log("WARN", `
|
|
333
|
+
this.logger.log("WARN", `Unsupported function type on ${definition.name}. Expected either httpsTrigger or eventTrigger.`);
|
|
309
334
|
}
|
|
310
335
|
const ignored = !added;
|
|
311
336
|
this.addTriggerRecord(definition, { backend: emulatableBackend, ignored, url });
|
|
@@ -323,6 +348,9 @@ class FunctionsEmulator {
|
|
|
323
348
|
this.logger.logLabeled("SUCCESS", `functions[${definition.id}]`, msg);
|
|
324
349
|
}
|
|
325
350
|
}
|
|
351
|
+
if (this.args.debugPort) {
|
|
352
|
+
this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
|
|
353
|
+
}
|
|
326
354
|
}
|
|
327
355
|
addRealtimeDatabaseTrigger(projectId, key, eventTrigger) {
|
|
328
356
|
const databaseEmu = registry_1.EmulatorRegistry.get(types_1.Emulators.DATABASE);
|
|
@@ -331,7 +359,7 @@ class FunctionsEmulator {
|
|
|
331
359
|
}
|
|
332
360
|
const result = DATABASE_PATH_PATTERN.exec(eventTrigger.resource);
|
|
333
361
|
if (result === null || result.length !== 3) {
|
|
334
|
-
this.logger.log("WARN", `Event
|
|
362
|
+
this.logger.log("WARN", `Event function "${key}" has malformed "resource" member. ` + `${eventTrigger.resource}`);
|
|
335
363
|
return Promise.reject();
|
|
336
364
|
}
|
|
337
365
|
const instance = result[1];
|
|
@@ -347,7 +375,7 @@ class FunctionsEmulator {
|
|
|
347
375
|
setTriggersPath += `?ns=${instance}`;
|
|
348
376
|
}
|
|
349
377
|
else {
|
|
350
|
-
this.logger.log("WARN", `No project in use. Registering function
|
|
378
|
+
this.logger.log("WARN", `No project in use. Registering function for sentinel namespace '${constants_1.Constants.DEFAULT_DATABASE_EMULATOR_NAMESPACE}'`);
|
|
351
379
|
}
|
|
352
380
|
return api
|
|
353
381
|
.request("POST", setTriggersPath, {
|
|
@@ -362,7 +390,7 @@ class FunctionsEmulator {
|
|
|
362
390
|
return true;
|
|
363
391
|
})
|
|
364
392
|
.catch((err) => {
|
|
365
|
-
this.logger.log("WARN", "Error adding
|
|
393
|
+
this.logger.log("WARN", "Error adding Realtime Database function: " + err);
|
|
366
394
|
throw err;
|
|
367
395
|
});
|
|
368
396
|
}
|
|
@@ -371,7 +399,9 @@ class FunctionsEmulator {
|
|
|
371
399
|
if (!firestoreEmu) {
|
|
372
400
|
return Promise.resolve(false);
|
|
373
401
|
}
|
|
374
|
-
const bundle = JSON.stringify({
|
|
402
|
+
const bundle = JSON.stringify({
|
|
403
|
+
eventTrigger: Object.assign(Object.assign({}, eventTrigger), { service: "firestore.googleapis.com" }),
|
|
404
|
+
});
|
|
375
405
|
logger_1.logger.debug(`addFirestoreTrigger`, JSON.stringify(bundle));
|
|
376
406
|
return api
|
|
377
407
|
.request("PUT", `/emulator/v1/projects/${projectId}/triggers/${key}`, {
|
|
@@ -383,7 +413,7 @@ class FunctionsEmulator {
|
|
|
383
413
|
return true;
|
|
384
414
|
})
|
|
385
415
|
.catch((err) => {
|
|
386
|
-
this.logger.log("WARN", "Error adding
|
|
416
|
+
this.logger.log("WARN", "Error adding firestore function: " + err);
|
|
387
417
|
throw err;
|
|
388
418
|
});
|
|
389
419
|
}
|
|
@@ -452,15 +482,29 @@ class FunctionsEmulator {
|
|
|
452
482
|
const record = this.triggers[triggerKey];
|
|
453
483
|
if (!record) {
|
|
454
484
|
logger_1.logger.debug(`Could not find key=${triggerKey} in ${JSON.stringify(this.triggers)}`);
|
|
455
|
-
throw new error_1.FirebaseError(`No
|
|
485
|
+
throw new error_1.FirebaseError(`No function with key ${triggerKey}`);
|
|
456
486
|
}
|
|
457
487
|
return record;
|
|
458
488
|
}
|
|
459
489
|
getTriggerKey(def) {
|
|
460
490
|
return def.eventTrigger ? `${def.id}-${this.triggerGeneration}` : def.id;
|
|
461
491
|
}
|
|
462
|
-
|
|
463
|
-
|
|
492
|
+
getBackendInfo() {
|
|
493
|
+
const cf3Triggers = Object.values(this.triggers)
|
|
494
|
+
.filter((t) => !t.backend.extensionInstanceId)
|
|
495
|
+
.map((t) => t.def);
|
|
496
|
+
return this.args.emulatableBackends.map((e) => {
|
|
497
|
+
var _a;
|
|
498
|
+
return {
|
|
499
|
+
directory: e.functionsDir,
|
|
500
|
+
env: e.env,
|
|
501
|
+
extensionInstanceId: e.extensionInstanceId,
|
|
502
|
+
extension: e.extension,
|
|
503
|
+
extensionVersion: e.extensionVersion,
|
|
504
|
+
extensionSpec: e.extensionSpec,
|
|
505
|
+
functionTriggers: (_a = e.predefinedTriggers) !== null && _a !== void 0 ? _a : cf3Triggers,
|
|
506
|
+
};
|
|
507
|
+
});
|
|
464
508
|
}
|
|
465
509
|
addTriggerRecord(def, opts) {
|
|
466
510
|
const key = this.getTriggerKey(def);
|
|
@@ -475,30 +519,12 @@ class FunctionsEmulator {
|
|
|
475
519
|
setTriggersForTesting(triggers, backend) {
|
|
476
520
|
triggers.forEach((def) => this.addTriggerRecord(def, { backend, ignored: false }));
|
|
477
521
|
}
|
|
478
|
-
getBaseBundle(
|
|
522
|
+
getBaseBundle() {
|
|
479
523
|
return {
|
|
480
|
-
|
|
481
|
-
projectId: this.args.projectId,
|
|
482
|
-
triggerId: "",
|
|
483
|
-
targetName: "",
|
|
484
|
-
emulators: {
|
|
485
|
-
firestore: registry_1.EmulatorRegistry.getInfo(types_1.Emulators.FIRESTORE),
|
|
486
|
-
database: registry_1.EmulatorRegistry.getInfo(types_1.Emulators.DATABASE),
|
|
487
|
-
pubsub: registry_1.EmulatorRegistry.getInfo(types_1.Emulators.PUBSUB),
|
|
488
|
-
auth: registry_1.EmulatorRegistry.getInfo(types_1.Emulators.AUTH),
|
|
489
|
-
storage: registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE),
|
|
490
|
-
},
|
|
491
|
-
adminSdkConfig: {
|
|
492
|
-
databaseURL: this.adminSdkConfig.databaseURL,
|
|
493
|
-
storageBucket: this.adminSdkConfig.storageBucket,
|
|
494
|
-
},
|
|
524
|
+
proto: {},
|
|
495
525
|
disabled_features: this.args.disabledRuntimeFeatures,
|
|
496
526
|
};
|
|
497
527
|
}
|
|
498
|
-
getRequestedNodeRuntimeVersion(frb) {
|
|
499
|
-
const pkg = require(path.join(frb.cwd, "package.json"));
|
|
500
|
-
return frb.nodeMajorVersion || (pkg.engines && pkg.engines.node);
|
|
501
|
-
}
|
|
502
528
|
getNodeBinary(backend) {
|
|
503
529
|
const pkg = require(path.join(backend.functionsDir, "package.json"));
|
|
504
530
|
if ((!pkg.engines || !pkg.engines.node) && !backend.nodeMajorVersion) {
|
|
@@ -530,6 +556,16 @@ class FunctionsEmulator {
|
|
|
530
556
|
}
|
|
531
557
|
return process.execPath;
|
|
532
558
|
}
|
|
559
|
+
getRuntimeConfig(backend) {
|
|
560
|
+
const configPath = `${backend.functionsDir}/.runtimeconfig.json`;
|
|
561
|
+
try {
|
|
562
|
+
const configContent = fs.readFileSync(configPath, "utf8");
|
|
563
|
+
return JSON.parse(configContent.toString());
|
|
564
|
+
}
|
|
565
|
+
catch (e) {
|
|
566
|
+
}
|
|
567
|
+
return {};
|
|
568
|
+
}
|
|
533
569
|
getUserEnvs(backend) {
|
|
534
570
|
const projectInfo = {
|
|
535
571
|
functionsSource: backend.functionsDir,
|
|
@@ -546,17 +582,16 @@ class FunctionsEmulator {
|
|
|
546
582
|
}
|
|
547
583
|
return {};
|
|
548
584
|
}
|
|
549
|
-
getSystemEnvs(
|
|
585
|
+
getSystemEnvs(trigger) {
|
|
550
586
|
const envs = {};
|
|
551
587
|
envs.GCLOUD_PROJECT = this.args.projectId;
|
|
552
588
|
envs.K_REVISION = "1";
|
|
553
589
|
envs.PORT = "80";
|
|
554
|
-
if (
|
|
555
|
-
const
|
|
556
|
-
const target = service.replace(/-/g, ".");
|
|
590
|
+
if (trigger) {
|
|
591
|
+
const target = trigger.entryPoint;
|
|
557
592
|
envs.FUNCTION_TARGET = target;
|
|
558
|
-
envs.FUNCTION_SIGNATURE_TYPE =
|
|
559
|
-
envs.K_SERVICE =
|
|
593
|
+
envs.FUNCTION_SIGNATURE_TYPE = (0, functionsEmulatorShared_1.getSignatureType)(trigger);
|
|
594
|
+
envs.K_SERVICE = trigger.name;
|
|
560
595
|
}
|
|
561
596
|
return envs;
|
|
562
597
|
}
|
|
@@ -588,6 +623,9 @@ class FunctionsEmulator {
|
|
|
588
623
|
const pubsubHost = (0, functionsEmulatorShared_1.formatHost)(pubsubEmulator);
|
|
589
624
|
process.env.PUBSUB_EMULATOR_HOST = pubsubHost;
|
|
590
625
|
}
|
|
626
|
+
if (this.args.debugPort) {
|
|
627
|
+
envs["FUNCTION_DEBUG_MODE"] = "true";
|
|
628
|
+
}
|
|
591
629
|
return envs;
|
|
592
630
|
}
|
|
593
631
|
getFirebaseConfig() {
|
|
@@ -607,21 +645,65 @@ class FunctionsEmulator {
|
|
|
607
645
|
projectId: this.args.projectId,
|
|
608
646
|
});
|
|
609
647
|
}
|
|
610
|
-
getRuntimeEnvs(backend,
|
|
611
|
-
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, this.getUserEnvs(backend)), this.getSystemEnvs(
|
|
648
|
+
getRuntimeEnvs(backend, trigger) {
|
|
649
|
+
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, this.getUserEnvs(backend)), this.getSystemEnvs(trigger)), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), backend.env);
|
|
612
650
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
651
|
+
async resolveSecretEnvs(backend, trigger) {
|
|
652
|
+
let secretEnvs = {};
|
|
653
|
+
try {
|
|
654
|
+
const data = fs.readFileSync(path.join(backend.functionsDir, LOCAL_SECRETS_FILE), "utf8");
|
|
655
|
+
secretEnvs = functionsEnv.parseStrict(data);
|
|
656
|
+
}
|
|
657
|
+
catch (e) {
|
|
658
|
+
if (e.code !== "ENOENT") {
|
|
659
|
+
this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${LOCAL_SECRETS_FILE}: ${e.message}`);
|
|
660
|
+
}
|
|
616
661
|
}
|
|
662
|
+
if (trigger) {
|
|
663
|
+
const secrets = trigger.secretEnvironmentVariables || [];
|
|
664
|
+
const accesses = secrets
|
|
665
|
+
.filter((s) => !secretEnvs[s.secret])
|
|
666
|
+
.map(async (s) => {
|
|
667
|
+
this.logger.logLabeled("INFO", "functions", `Trying to access secret ${s.key}@latest`);
|
|
668
|
+
const value = await (0, secretManager_1.accessSecretVersion)(this.getProjectId(), s.key, "latest");
|
|
669
|
+
return [s.secret, value];
|
|
670
|
+
});
|
|
671
|
+
const accessResults = await (0, utils_1.allSettled)(accesses);
|
|
672
|
+
const errs = [];
|
|
673
|
+
for (const result of accessResults) {
|
|
674
|
+
if (result.status === "rejected") {
|
|
675
|
+
errs.push(result.reason);
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
const [k, v] = result.value;
|
|
679
|
+
secretEnvs[k] = v;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
if (errs.length > 0) {
|
|
683
|
+
this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
|
|
684
|
+
"Make sure the credential used for the Functions Emulator have access " +
|
|
685
|
+
`or provide override values in ${LOCAL_SECRETS_FILE}:\n\t` +
|
|
686
|
+
errs.join("\n\t"));
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return secretEnvs;
|
|
690
|
+
}
|
|
691
|
+
async invokeRuntime(backend, trigger, frb, opts) {
|
|
692
|
+
if (!this.workerPool.readyForWork(trigger.id)) {
|
|
693
|
+
await this.startRuntime(backend, opts, trigger);
|
|
694
|
+
}
|
|
695
|
+
return this.workerPool.submitWork(trigger.id, frb, opts);
|
|
696
|
+
}
|
|
697
|
+
async startRuntime(backend, opts, trigger) {
|
|
698
|
+
var _a;
|
|
617
699
|
const emitter = new events_1.EventEmitter();
|
|
618
700
|
const args = [path.join(__dirname, "functionsEmulatorRuntime")];
|
|
619
701
|
if (opts.ignore_warnings) {
|
|
620
702
|
args.unshift("--no-warnings");
|
|
621
703
|
}
|
|
622
704
|
if (this.args.debugPort) {
|
|
623
|
-
if (process.env.FIREPIT_VERSION && process.execPath
|
|
624
|
-
const requestedMajorNodeVersion = this.
|
|
705
|
+
if (process.env.FIREPIT_VERSION && process.execPath === opts.nodeBinary) {
|
|
706
|
+
const requestedMajorNodeVersion = this.getNodeBinary(backend);
|
|
625
707
|
this.logger.log("WARN", `To enable function inspection, please run "${process.execPath} is:npm i node@${requestedMajorNodeVersion} --save-dev" in your functions directory`);
|
|
626
708
|
}
|
|
627
709
|
else {
|
|
@@ -629,15 +711,17 @@ class FunctionsEmulator {
|
|
|
629
711
|
args.unshift(`--inspect=${host}:${this.args.debugPort}`);
|
|
630
712
|
}
|
|
631
713
|
}
|
|
632
|
-
const pnpPath = path.join(
|
|
714
|
+
const pnpPath = path.join(backend.functionsDir, ".pnp.js");
|
|
633
715
|
if (fs.existsSync(pnpPath)) {
|
|
634
716
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).logLabeled("WARN_ONCE", "functions", "Detected yarn@2 with PnP. " +
|
|
635
717
|
"Cloud Functions for Firebase requires a node_modules folder to work correctly and is therefore incompatible with PnP. " +
|
|
636
718
|
"See https://yarnpkg.com/getting-started/migration#step-by-step for more information.");
|
|
637
719
|
}
|
|
720
|
+
const runtimeEnv = this.getRuntimeEnvs(backend, trigger);
|
|
721
|
+
const secretEnvs = await this.resolveSecretEnvs(backend, trigger);
|
|
638
722
|
const childProcess = spawn(opts.nodeBinary, args, {
|
|
639
|
-
|
|
640
|
-
|
|
723
|
+
cwd: backend.functionsDir,
|
|
724
|
+
env: Object.assign(Object.assign(Object.assign({ node: opts.nodeBinary }, process.env), runtimeEnv), secretEnvs),
|
|
641
725
|
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
642
726
|
});
|
|
643
727
|
if (!childProcess.stderr) {
|
|
@@ -668,6 +752,7 @@ class FunctionsEmulator {
|
|
|
668
752
|
childProcess.on("exit", resolve);
|
|
669
753
|
}),
|
|
670
754
|
events: emitter,
|
|
755
|
+
cwd: backend.functionsDir,
|
|
671
756
|
shutdown: () => {
|
|
672
757
|
childProcess.kill();
|
|
673
758
|
},
|
|
@@ -679,8 +764,12 @@ class FunctionsEmulator {
|
|
|
679
764
|
return childProcess.send(JSON.stringify(args));
|
|
680
765
|
},
|
|
681
766
|
};
|
|
682
|
-
|
|
683
|
-
|
|
767
|
+
const extensionLogInfo = {
|
|
768
|
+
instanceId: backend.extensionInstanceId,
|
|
769
|
+
ref: (_a = backend.extensionVersion) === null || _a === void 0 ? void 0 : _a.ref,
|
|
770
|
+
};
|
|
771
|
+
this.workerPool.addWorker(trigger === null || trigger === void 0 ? void 0 : trigger.id, runtime, extensionLogInfo);
|
|
772
|
+
return;
|
|
684
773
|
}
|
|
685
774
|
async disableBackgroundTriggers() {
|
|
686
775
|
Object.values(this.triggers).forEach((record) => {
|
|
@@ -706,7 +795,7 @@ class FunctionsEmulator {
|
|
|
706
795
|
}
|
|
707
796
|
const trigger = record.def;
|
|
708
797
|
const service = (0, functionsEmulatorShared_1.getFunctionService)(trigger);
|
|
709
|
-
const worker = this.
|
|
798
|
+
const worker = await this.invokeTrigger(record.backend, trigger, proto);
|
|
710
799
|
return new Promise((resolve, reject) => {
|
|
711
800
|
if (projectId !== this.args.projectId) {
|
|
712
801
|
if (service !== constants_1.Constants.SERVICE_REALTIME_DATABASE) {
|
|
@@ -725,7 +814,7 @@ class FunctionsEmulator {
|
|
|
725
814
|
reject({ code: 500, body: el.text });
|
|
726
815
|
}
|
|
727
816
|
});
|
|
728
|
-
track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
|
|
817
|
+
void track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
|
|
729
818
|
worker.waitForDone().then(() => {
|
|
730
819
|
resolve({ status: "acknowledged" });
|
|
731
820
|
});
|
|
@@ -740,7 +829,7 @@ class FunctionsEmulator {
|
|
|
740
829
|
return registry_1.EmulatorRegistry.getInfo(emulator);
|
|
741
830
|
}
|
|
742
831
|
tokenFromAuthHeader(authHeader) {
|
|
743
|
-
const match =
|
|
832
|
+
const match = /^Bearer (.*)$/.exec(authHeader);
|
|
744
833
|
if (!match) {
|
|
745
834
|
return;
|
|
746
835
|
}
|
|
@@ -772,7 +861,7 @@ class FunctionsEmulator {
|
|
|
772
861
|
if (!this.triggers[triggerId]) {
|
|
773
862
|
res
|
|
774
863
|
.status(404)
|
|
775
|
-
.send(`Function ${triggerId} does not exist, valid
|
|
864
|
+
.send(`Function ${triggerId} does not exist, valid functions are: ${Object.keys(this.triggers).join(", ")}`);
|
|
776
865
|
return;
|
|
777
866
|
}
|
|
778
867
|
const record = this.getTriggerRecordByKey(triggerId);
|
|
@@ -793,14 +882,14 @@ class FunctionsEmulator {
|
|
|
793
882
|
req.headers[functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER] = encodeURIComponent(JSON.stringify(contextAuth));
|
|
794
883
|
}
|
|
795
884
|
}
|
|
796
|
-
const worker = this.
|
|
885
|
+
const worker = await this.invokeTrigger(record.backend, trigger);
|
|
797
886
|
worker.onLogs((el) => {
|
|
798
887
|
if (el.level === "FATAL") {
|
|
799
888
|
res.status(500).send(el.text);
|
|
800
889
|
}
|
|
801
890
|
});
|
|
802
891
|
await worker.waitForSocketReady();
|
|
803
|
-
track(EVENT_INVOKE, "https");
|
|
892
|
+
void track(EVENT_INVOKE, "https");
|
|
804
893
|
this.logger.log("DEBUG", `[functions] Runtime ready! Sending request!`);
|
|
805
894
|
if (!worker.lastArgs) {
|
|
806
895
|
throw new error_1.FirebaseError("Cannot execute on a worker with no arguments");
|