firebase-tools 10.2.1 → 10.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/appdistribution/options-parser-util.js +1 -1
- package/lib/auth.js +3 -3
- 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/database-rules-list.js +2 -2
- package/lib/commands/emulators-start.js +1 -1
- package/lib/commands/ext-configure.js +67 -7
- package/lib/commands/ext-dev-init.js +49 -49
- package/lib/commands/ext-export.js +7 -2
- package/lib/commands/ext-install.js +173 -109
- package/lib/commands/ext-uninstall.js +17 -8
- package/lib/commands/ext-update.js +67 -12
- package/lib/commands/functions-config-export.js +1 -1
- package/lib/commands/hosting-clone.js +3 -3
- package/lib/commands/remoteconfig-get.js +1 -1
- package/lib/config.js +6 -3
- package/lib/deploy/extensions/deploymentSummary.js +3 -3
- package/lib/deploy/extensions/planner.js +7 -6
- package/lib/deploy/extensions/tasks.js +1 -1
- package/lib/deploy/functions/backend.js +21 -5
- package/lib/deploy/functions/checkIam.js +5 -5
- package/lib/deploy/functions/containerCleaner.js +3 -3
- package/lib/deploy/functions/ensure.js +3 -3
- package/lib/deploy/functions/functionsDeployHelper.js +2 -2
- package/lib/deploy/functions/prepare.js +5 -3
- package/lib/deploy/functions/pricing.js +1 -1
- package/lib/deploy/functions/prompts.js +2 -2
- package/lib/deploy/functions/release/fabricator.js +7 -7
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/release/planner.js +43 -26
- 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 +22 -12
- package/lib/deploy/functions/runtimes/golang/index.js +2 -2
- package/lib/deploy/functions/runtimes/node/index.js +53 -0
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +64 -20
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
- package/lib/deploy/functions/services/index.js +9 -1
- package/lib/deploy/functions/services/storage.js +10 -4
- package/lib/deploy/functions/triggerRegionHelper.js +1 -1
- package/lib/deploy/functions/validate.js +3 -3
- package/lib/deploy/hosting/deploy.js +2 -2
- package/lib/deploy/hosting/hashcache.js +21 -19
- 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/constants.js +4 -0
- package/lib/emulator/controller.js +54 -24
- package/lib/emulator/download.js +18 -1
- package/lib/emulator/downloadableEmulators.js +1 -1
- package/lib/emulator/emulatorLogger.js +12 -1
- package/lib/emulator/extensions/validation.js +70 -0
- package/lib/emulator/extensionsEmulator.js +175 -0
- package/lib/emulator/functionsEmulator.js +95 -43
- package/lib/emulator/functionsEmulatorRuntime.js +44 -36
- package/lib/emulator/functionsEmulatorShared.js +30 -11
- package/lib/emulator/functionsEmulatorShell.js +1 -1
- package/lib/emulator/functionsEmulatorUtils.js +4 -4
- package/lib/emulator/functionsRuntimeWorker.js +2 -2
- 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 +314 -332
- package/lib/emulator/storage/apis/gcloud.js +241 -121
- package/lib/emulator/storage/crc.js +5 -1
- package/lib/emulator/storage/errors.js +9 -0
- package/lib/emulator/storage/files.js +159 -300
- package/lib/emulator/storage/index.js +27 -73
- package/lib/emulator/storage/metadata.js +65 -51
- package/lib/emulator/storage/multipart.js +62 -0
- package/lib/emulator/storage/persistence.js +78 -0
- package/lib/emulator/storage/rules/config.js +33 -0
- package/lib/emulator/storage/rules/manager.js +81 -0
- package/lib/emulator/storage/rules/runtime.js +8 -7
- package/lib/emulator/storage/rules/utils.js +48 -0
- package/lib/emulator/storage/server.js +2 -2
- package/lib/emulator/storage/upload.js +106 -0
- package/lib/emulator/types.js +3 -0
- package/lib/ensureApiEnabled.js +5 -1
- package/lib/error.js +1 -1
- package/lib/extensions/askUserForParam.js +31 -25
- package/lib/extensions/changelog.js +3 -1
- package/lib/extensions/checkProjectBilling.js +1 -1
- package/lib/extensions/displayExtensionInfo.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +56 -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 +32 -19
- package/lib/extensions/manifest.js +144 -0
- package/lib/extensions/metricsUtils.js +4 -4
- package/lib/extensions/paramHelper.js +34 -12
- package/lib/extensions/refs.js +1 -1
- package/lib/extensions/secretsUtils.js +3 -3
- package/lib/functional.js +1 -1
- package/lib/functions/env.js +6 -7
- package/lib/functions/events/v2.js +11 -0
- package/lib/gcp/cloudfunctions.js +43 -11
- package/lib/gcp/cloudfunctionsv2.js +48 -17
- package/lib/gcp/cloudtasks.js +1 -1
- package/lib/gcp/docker.js +2 -2
- package/lib/gcp/resourceManager.js +4 -4
- package/lib/gcp/run.js +2 -2
- package/lib/hosting/api.js +1 -1
- 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/serve/functions.js +2 -1
- package/lib/utils.js +15 -2
- package/npm-shrinkwrap.json +786 -393
- package/package.json +1 -1
- package/schema/firebase-config.json +5 -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
- package/lib/deploy/extensions/params.js +0 -39
- package/lib/deploy/functions/eventTypes.js +0 -10
|
@@ -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");
|
|
@@ -29,12 +30,11 @@ const defaultCredentials_1 = require("../defaultCredentials");
|
|
|
29
30
|
const adminSdkConfig_1 = require("./adminSdkConfig");
|
|
30
31
|
const types_2 = require("./events/types");
|
|
31
32
|
const validate_1 = require("../deploy/functions/validate");
|
|
32
|
-
const
|
|
33
|
+
const secretManager_1 = require("../gcp/secretManager");
|
|
34
|
+
const runtimes = require("../deploy/functions/runtimes");
|
|
33
35
|
const backend = require("../deploy/functions/backend");
|
|
34
36
|
const functionsEnv = require("../functions/env");
|
|
35
|
-
const secretManager_1 = require("../gcp/secretManager");
|
|
36
37
|
const EVENT_INVOKE = "functions:invoke";
|
|
37
|
-
const LOCAL_SECRETS_FILE = ".secret.local";
|
|
38
38
|
const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
|
|
39
39
|
class FunctionsEmulator {
|
|
40
40
|
constructor(args) {
|
|
@@ -92,6 +92,7 @@ class FunctionsEmulator {
|
|
|
92
92
|
const httpsFunctionRoute = `/${this.args.projectId}/:region/:trigger_name`;
|
|
93
93
|
const multicastFunctionRoute = `/functions/projects/:project_id/trigger_multicast`;
|
|
94
94
|
const httpsFunctionRoutes = [httpsFunctionRoute, `${httpsFunctionRoute}/*`];
|
|
95
|
+
const listBackendsRoute = `/backends`;
|
|
95
96
|
const backgroundHandler = (req, res) => {
|
|
96
97
|
var _a;
|
|
97
98
|
const region = req.params.region;
|
|
@@ -152,6 +153,10 @@ class FunctionsEmulator {
|
|
|
152
153
|
});
|
|
153
154
|
res.json({ status: "multicast_acknowledged" });
|
|
154
155
|
};
|
|
156
|
+
const listBackendsHandler = (req, res) => {
|
|
157
|
+
res.json({ backends: this.getBackendInfo() });
|
|
158
|
+
};
|
|
159
|
+
hub.get(listBackendsRoute, cors({ origin: true }), listBackendsHandler);
|
|
155
160
|
hub.post(backgroundFunctionRoute, dataMiddleware, backgroundHandler);
|
|
156
161
|
hub.post(multicastFunctionRoute, dataMiddleware, multicastHandler);
|
|
157
162
|
hub.all(httpsFunctionRoutes, dataMiddleware, httpsHandler);
|
|
@@ -161,9 +166,15 @@ class FunctionsEmulator {
|
|
|
161
166
|
});
|
|
162
167
|
return hub;
|
|
163
168
|
}
|
|
164
|
-
async
|
|
169
|
+
async invokeTrigger(backend, trigger, proto, runtimeOpts) {
|
|
165
170
|
const bundleTemplate = this.getBaseBundle();
|
|
166
171
|
const runtimeBundle = Object.assign(Object.assign({}, bundleTemplate), { proto });
|
|
172
|
+
if (this.args.debugPort) {
|
|
173
|
+
runtimeBundle.debug = {
|
|
174
|
+
functionTarget: trigger.entryPoint,
|
|
175
|
+
functionSignature: (0, functionsEmulatorShared_1.getSignatureType)(trigger),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
167
178
|
if (!backend.nodeBinary) {
|
|
168
179
|
throw new error_1.FirebaseError(`No node binary for ${trigger.id}. This should never happen.`);
|
|
169
180
|
}
|
|
@@ -240,15 +251,19 @@ class FunctionsEmulator {
|
|
|
240
251
|
}
|
|
241
252
|
let triggerDefinitions;
|
|
242
253
|
if (emulatableBackend.predefinedTriggers) {
|
|
243
|
-
triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers);
|
|
254
|
+
triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers, emulatableBackend.secretEnv);
|
|
244
255
|
}
|
|
245
256
|
else {
|
|
246
257
|
const runtimeConfig = this.getRuntimeConfig(emulatableBackend);
|
|
247
|
-
const
|
|
258
|
+
const runtimeDelegateContext = {
|
|
248
259
|
projectId: this.args.projectId,
|
|
249
260
|
projectDir: this.args.projectDir,
|
|
250
261
|
sourceDir: emulatableBackend.functionsDir,
|
|
251
|
-
}
|
|
262
|
+
};
|
|
263
|
+
if (emulatableBackend.nodeMajorVersion) {
|
|
264
|
+
runtimeDelegateContext.runtime = `nodejs${emulatableBackend.nodeMajorVersion}`;
|
|
265
|
+
}
|
|
266
|
+
const runtimeDelegate = await runtimes.getRuntimeDelegate(runtimeDelegateContext);
|
|
252
267
|
logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
|
|
253
268
|
await runtimeDelegate.validate();
|
|
254
269
|
logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
|
|
@@ -331,6 +346,9 @@ class FunctionsEmulator {
|
|
|
331
346
|
this.logger.logLabeled("SUCCESS", `functions[${definition.id}]`, msg);
|
|
332
347
|
}
|
|
333
348
|
}
|
|
349
|
+
if (this.args.debugPort) {
|
|
350
|
+
this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
|
|
351
|
+
}
|
|
334
352
|
}
|
|
335
353
|
addRealtimeDatabaseTrigger(projectId, key, eventTrigger) {
|
|
336
354
|
const databaseEmu = registry_1.EmulatorRegistry.get(types_1.Emulators.DATABASE);
|
|
@@ -469,8 +487,26 @@ class FunctionsEmulator {
|
|
|
469
487
|
getTriggerKey(def) {
|
|
470
488
|
return def.eventTrigger ? `${def.id}-${this.triggerGeneration}` : def.id;
|
|
471
489
|
}
|
|
472
|
-
|
|
473
|
-
|
|
490
|
+
getBackendInfo() {
|
|
491
|
+
const cf3Triggers = Object.values(this.triggers)
|
|
492
|
+
.filter((t) => !t.backend.extensionInstanceId)
|
|
493
|
+
.map((t) => t.def);
|
|
494
|
+
return this.args.emulatableBackends.map((e) => {
|
|
495
|
+
var _a;
|
|
496
|
+
const envWithSecrets = Object.assign({}, e.env);
|
|
497
|
+
for (const s of e.secretEnv) {
|
|
498
|
+
envWithSecrets[s.key] = backend.secretVersionName(s);
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
directory: e.functionsDir,
|
|
502
|
+
env: envWithSecrets,
|
|
503
|
+
extensionInstanceId: e.extensionInstanceId,
|
|
504
|
+
extension: e.extension,
|
|
505
|
+
extensionVersion: e.extensionVersion,
|
|
506
|
+
extensionSpec: e.extensionSpec,
|
|
507
|
+
functionTriggers: (_a = e.predefinedTriggers) !== null && _a !== void 0 ? _a : cf3Triggers,
|
|
508
|
+
};
|
|
509
|
+
});
|
|
474
510
|
}
|
|
475
511
|
addTriggerRecord(def, opts) {
|
|
476
512
|
const key = this.getTriggerKey(def);
|
|
@@ -536,6 +572,7 @@ class FunctionsEmulator {
|
|
|
536
572
|
const projectInfo = {
|
|
537
573
|
functionsSource: backend.functionsDir,
|
|
538
574
|
projectId: this.args.projectId,
|
|
575
|
+
projectAlias: this.args.projectAlias,
|
|
539
576
|
isEmulator: true,
|
|
540
577
|
};
|
|
541
578
|
if (functionsEnv.hasUserEnvs(projectInfo)) {
|
|
@@ -589,6 +626,9 @@ class FunctionsEmulator {
|
|
|
589
626
|
const pubsubHost = (0, functionsEmulatorShared_1.formatHost)(pubsubEmulator);
|
|
590
627
|
process.env.PUBSUB_EMULATOR_HOST = pubsubHost;
|
|
591
628
|
}
|
|
629
|
+
if (this.args.debugPort) {
|
|
630
|
+
envs["FUNCTION_DEBUG_MODE"] = "true";
|
|
631
|
+
}
|
|
592
632
|
return envs;
|
|
593
633
|
}
|
|
594
634
|
getFirebaseConfig() {
|
|
@@ -613,53 +653,61 @@ class FunctionsEmulator {
|
|
|
613
653
|
}
|
|
614
654
|
async resolveSecretEnvs(backend, trigger) {
|
|
615
655
|
let secretEnvs = {};
|
|
656
|
+
const secretPath = (0, functionsEmulatorShared_1.getSecretLocalPath)(backend, this.args.projectDir);
|
|
616
657
|
try {
|
|
617
|
-
const data = fs.readFileSync(
|
|
658
|
+
const data = fs.readFileSync(secretPath, "utf8");
|
|
618
659
|
secretEnvs = functionsEnv.parseStrict(data);
|
|
619
660
|
}
|
|
620
661
|
catch (e) {
|
|
621
662
|
if (e.code !== "ENOENT") {
|
|
622
|
-
this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${
|
|
663
|
+
this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${secretPath}: ${e.message}`);
|
|
623
664
|
}
|
|
624
665
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
666
|
+
if (trigger) {
|
|
667
|
+
const secrets = trigger.secretEnvironmentVariables || [];
|
|
668
|
+
const accesses = secrets
|
|
669
|
+
.filter((s) => !secretEnvs[s.key])
|
|
670
|
+
.map(async (s) => {
|
|
671
|
+
var _a;
|
|
672
|
+
this.logger.logLabeled("INFO", "functions", `Trying to access secret ${s.secret}@latest`);
|
|
673
|
+
const value = await (0, secretManager_1.accessSecretVersion)(this.getProjectId(), s.secret, (_a = s.version) !== null && _a !== void 0 ? _a : "latest");
|
|
674
|
+
return [s.key, value];
|
|
675
|
+
});
|
|
676
|
+
const accessResults = await (0, utils_1.allSettled)(accesses);
|
|
677
|
+
const errs = [];
|
|
678
|
+
for (const result of accessResults) {
|
|
679
|
+
if (result.status === "rejected") {
|
|
680
|
+
errs.push(result.reason);
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
const [k, v] = result.value;
|
|
684
|
+
secretEnvs[k] = v;
|
|
685
|
+
}
|
|
638
686
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
687
|
+
if (errs.length > 0) {
|
|
688
|
+
this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
|
|
689
|
+
"Make sure the credential used for the Functions Emulator have access " +
|
|
690
|
+
`or provide override values in ${secretPath}:\n\t` +
|
|
691
|
+
errs.join("\n\t"));
|
|
642
692
|
}
|
|
643
693
|
}
|
|
644
|
-
if (errs.length > 0) {
|
|
645
|
-
this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
|
|
646
|
-
"Make sure the credential used for the Functions Emulator have access " +
|
|
647
|
-
`or provide override values in ${LOCAL_SECRETS_FILE}:\n\t` +
|
|
648
|
-
errs.join("\n\t"));
|
|
649
|
-
}
|
|
650
694
|
return secretEnvs;
|
|
651
695
|
}
|
|
652
696
|
async invokeRuntime(backend, trigger, frb, opts) {
|
|
653
|
-
if (this.workerPool.readyForWork(trigger.id)) {
|
|
654
|
-
|
|
697
|
+
if (!this.workerPool.readyForWork(trigger.id)) {
|
|
698
|
+
await this.startRuntime(backend, opts, trigger);
|
|
655
699
|
}
|
|
700
|
+
return this.workerPool.submitWork(trigger.id, frb, opts);
|
|
701
|
+
}
|
|
702
|
+
async startRuntime(backend, opts, trigger) {
|
|
703
|
+
var _a;
|
|
656
704
|
const emitter = new events_1.EventEmitter();
|
|
657
705
|
const args = [path.join(__dirname, "functionsEmulatorRuntime")];
|
|
658
706
|
if (opts.ignore_warnings) {
|
|
659
707
|
args.unshift("--no-warnings");
|
|
660
708
|
}
|
|
661
709
|
if (this.args.debugPort) {
|
|
662
|
-
if (process.env.FIREPIT_VERSION && process.execPath
|
|
710
|
+
if (process.env.FIREPIT_VERSION && process.execPath === opts.nodeBinary) {
|
|
663
711
|
const requestedMajorNodeVersion = this.getNodeBinary(backend);
|
|
664
712
|
this.logger.log("WARN", `To enable function inspection, please run "${process.execPath} is:npm i node@${requestedMajorNodeVersion} --save-dev" in your functions directory`);
|
|
665
713
|
}
|
|
@@ -721,8 +769,12 @@ class FunctionsEmulator {
|
|
|
721
769
|
return childProcess.send(JSON.stringify(args));
|
|
722
770
|
},
|
|
723
771
|
};
|
|
724
|
-
|
|
725
|
-
|
|
772
|
+
const extensionLogInfo = {
|
|
773
|
+
instanceId: backend.extensionInstanceId,
|
|
774
|
+
ref: (_a = backend.extensionVersion) === null || _a === void 0 ? void 0 : _a.ref,
|
|
775
|
+
};
|
|
776
|
+
this.workerPool.addWorker(trigger === null || trigger === void 0 ? void 0 : trigger.id, runtime, extensionLogInfo);
|
|
777
|
+
return;
|
|
726
778
|
}
|
|
727
779
|
async disableBackgroundTriggers() {
|
|
728
780
|
Object.values(this.triggers).forEach((record) => {
|
|
@@ -748,7 +800,7 @@ class FunctionsEmulator {
|
|
|
748
800
|
}
|
|
749
801
|
const trigger = record.def;
|
|
750
802
|
const service = (0, functionsEmulatorShared_1.getFunctionService)(trigger);
|
|
751
|
-
const worker = await this.
|
|
803
|
+
const worker = await this.invokeTrigger(record.backend, trigger, proto);
|
|
752
804
|
return new Promise((resolve, reject) => {
|
|
753
805
|
if (projectId !== this.args.projectId) {
|
|
754
806
|
if (service !== constants_1.Constants.SERVICE_REALTIME_DATABASE) {
|
|
@@ -767,7 +819,7 @@ class FunctionsEmulator {
|
|
|
767
819
|
reject({ code: 500, body: el.text });
|
|
768
820
|
}
|
|
769
821
|
});
|
|
770
|
-
track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
|
|
822
|
+
void track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
|
|
771
823
|
worker.waitForDone().then(() => {
|
|
772
824
|
resolve({ status: "acknowledged" });
|
|
773
825
|
});
|
|
@@ -835,14 +887,14 @@ class FunctionsEmulator {
|
|
|
835
887
|
req.headers[functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER] = encodeURIComponent(JSON.stringify(contextAuth));
|
|
836
888
|
}
|
|
837
889
|
}
|
|
838
|
-
const worker = await this.
|
|
890
|
+
const worker = await this.invokeTrigger(record.backend, trigger);
|
|
839
891
|
worker.onLogs((el) => {
|
|
840
892
|
if (el.level === "FATAL") {
|
|
841
893
|
res.status(500).send(el.text);
|
|
842
894
|
}
|
|
843
895
|
});
|
|
844
896
|
await worker.waitForSocketReady();
|
|
845
|
-
track(EVENT_INVOKE, "https");
|
|
897
|
+
void track(EVENT_INVOKE, "https");
|
|
846
898
|
this.logger.log("DEBUG", `[functions] Runtime ready! Sending request!`);
|
|
847
899
|
if (!worker.lastArgs) {
|
|
848
900
|
throw new error_1.FirebaseError("Cannot execute on a worker with no arguments");
|
|
@@ -10,9 +10,10 @@ const types_1 = require("./types");
|
|
|
10
10
|
const constants_1 = require("./constants");
|
|
11
11
|
const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
|
|
12
12
|
const functionsEmulatorUtils_1 = require("./functionsEmulatorUtils");
|
|
13
|
-
let
|
|
13
|
+
let functionModule;
|
|
14
14
|
let FUNCTION_TARGET_NAME;
|
|
15
15
|
let FUNCTION_SIGNATURE;
|
|
16
|
+
let FUNCTION_DEBUG_MODE;
|
|
16
17
|
let developerPkgJSON;
|
|
17
18
|
const dynamicImport = new Function("modulePath", "return import(modulePath)");
|
|
18
19
|
function isFeatureEnabled(frb, feature) {
|
|
@@ -484,7 +485,7 @@ async function initializeFunctionsConfigHelper(frb) {
|
|
|
484
485
|
function rawBodySaver(req, res, buf) {
|
|
485
486
|
req.rawBody = buf;
|
|
486
487
|
}
|
|
487
|
-
async function processHTTPS(frb) {
|
|
488
|
+
async function processHTTPS(trigger, frb) {
|
|
488
489
|
const ephemeralServer = express();
|
|
489
490
|
const functionRouter = express.Router();
|
|
490
491
|
const socketPath = frb.socketPath;
|
|
@@ -506,7 +507,7 @@ async function processHTTPS(frb) {
|
|
|
506
507
|
}
|
|
507
508
|
});
|
|
508
509
|
});
|
|
509
|
-
await runHTTPS([req, res]);
|
|
510
|
+
await runHTTPS(trigger, [req, res]);
|
|
510
511
|
}
|
|
511
512
|
catch (err) {
|
|
512
513
|
rejectEphemeralServer(err);
|
|
@@ -540,11 +541,11 @@ async function processHTTPS(frb) {
|
|
|
540
541
|
instance.on("error", rejectEphemeralServer);
|
|
541
542
|
});
|
|
542
543
|
}
|
|
543
|
-
async function processBackground(frb, signature) {
|
|
544
|
+
async function processBackground(trigger, frb, signature) {
|
|
544
545
|
const proto = frb.proto;
|
|
545
546
|
logDebug("ProcessBackground", proto);
|
|
546
547
|
if (signature === "cloudevent") {
|
|
547
|
-
return runCloudEvent(proto);
|
|
548
|
+
return runCloudEvent(trigger, proto);
|
|
548
549
|
}
|
|
549
550
|
const data = proto.data;
|
|
550
551
|
delete proto.data;
|
|
@@ -555,7 +556,7 @@ async function processBackground(frb, signature) {
|
|
|
555
556
|
context.resource = context.resource.name;
|
|
556
557
|
}
|
|
557
558
|
}
|
|
558
|
-
await runBackground({ data, context });
|
|
559
|
+
await runBackground(trigger, { data, context });
|
|
559
560
|
}
|
|
560
561
|
async function runFunction(func) {
|
|
561
562
|
let caughtErr;
|
|
@@ -570,24 +571,24 @@ async function runFunction(func) {
|
|
|
570
571
|
throw caughtErr;
|
|
571
572
|
}
|
|
572
573
|
}
|
|
573
|
-
async function runBackground(proto) {
|
|
574
|
+
async function runBackground(trigger, proto) {
|
|
574
575
|
logDebug("RunBackground", proto);
|
|
575
576
|
await runFunction(() => {
|
|
576
|
-
return
|
|
577
|
+
return trigger(proto.data, proto.context);
|
|
577
578
|
});
|
|
578
579
|
}
|
|
579
|
-
async function runCloudEvent(event) {
|
|
580
|
+
async function runCloudEvent(trigger, event) {
|
|
580
581
|
logDebug("RunCloudEvent", event);
|
|
581
582
|
await runFunction(() => {
|
|
582
|
-
return
|
|
583
|
+
return trigger(event);
|
|
583
584
|
});
|
|
584
585
|
}
|
|
585
|
-
async function runHTTPS(args) {
|
|
586
|
+
async function runHTTPS(trigger, args) {
|
|
586
587
|
if (args.length < 2) {
|
|
587
588
|
throw new Error("Function must be passed 2 args.");
|
|
588
589
|
}
|
|
589
590
|
await runFunction(() => {
|
|
590
|
-
return
|
|
591
|
+
return trigger(args[0], args[1]);
|
|
591
592
|
});
|
|
592
593
|
}
|
|
593
594
|
async function moduleResolutionDetective(frb, error) {
|
|
@@ -611,7 +612,7 @@ async function moduleResolutionDetective(frb, error) {
|
|
|
611
612
|
function logDebug(msg, data) {
|
|
612
613
|
new types_1.EmulatorLog("DEBUG", "runtime-status", `[${process.pid}] ${msg}`, data).log();
|
|
613
614
|
}
|
|
614
|
-
async function invokeTrigger(frb) {
|
|
615
|
+
async function invokeTrigger(trigger, frb) {
|
|
615
616
|
new types_1.EmulatorLog("INFO", "runtime-status", `Beginning execution of "${FUNCTION_TARGET_NAME}"`, {
|
|
616
617
|
frb,
|
|
617
618
|
}).log();
|
|
@@ -636,10 +637,10 @@ async function invokeTrigger(frb) {
|
|
|
636
637
|
switch (FUNCTION_SIGNATURE) {
|
|
637
638
|
case "event":
|
|
638
639
|
case "cloudevent":
|
|
639
|
-
await processBackground(frb, FUNCTION_SIGNATURE);
|
|
640
|
+
await processBackground(trigger, frb, FUNCTION_SIGNATURE);
|
|
640
641
|
break;
|
|
641
642
|
case "http":
|
|
642
|
-
await processHTTPS(frb);
|
|
643
|
+
await processHTTPS(trigger, frb);
|
|
643
644
|
break;
|
|
644
645
|
}
|
|
645
646
|
if (timeoutId) {
|
|
@@ -649,15 +650,18 @@ async function invokeTrigger(frb) {
|
|
|
649
650
|
new types_1.EmulatorLog("INFO", "runtime-status", `Finished "${FUNCTION_TARGET_NAME}" in ~${Math.max(seconds, 1)}s`).log();
|
|
650
651
|
}
|
|
651
652
|
async function initializeRuntime(frb) {
|
|
652
|
-
|
|
653
|
-
if (!
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
653
|
+
FUNCTION_DEBUG_MODE = process.env.FUNCTION_DEBUG_MODE || "";
|
|
654
|
+
if (!FUNCTION_DEBUG_MODE) {
|
|
655
|
+
FUNCTION_TARGET_NAME = process.env.FUNCTION_TARGET || "";
|
|
656
|
+
if (!FUNCTION_TARGET_NAME) {
|
|
657
|
+
new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_TARGET cannot be empty. This shouldn't happen.`).log();
|
|
658
|
+
await flushAndExit(1);
|
|
659
|
+
}
|
|
660
|
+
FUNCTION_SIGNATURE = process.env.FUNCTION_SIGNATURE_TYPE || "";
|
|
661
|
+
if (!FUNCTION_SIGNATURE) {
|
|
662
|
+
new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_SIGNATURE_TYPE cannot be empty. This shouldn't happen.`).log();
|
|
663
|
+
await flushAndExit(1);
|
|
664
|
+
}
|
|
661
665
|
}
|
|
662
666
|
logDebug(`Disabled runtime features: ${JSON.stringify(frb.disabled_features)}`);
|
|
663
667
|
const verified = await verifyDeveloperNodeModules(frb);
|
|
@@ -671,7 +675,7 @@ async function initializeRuntime(frb) {
|
|
|
671
675
|
await initializeFirebaseFunctionsStubs(frb);
|
|
672
676
|
await initializeFirebaseAdminStubs(frb);
|
|
673
677
|
}
|
|
674
|
-
async function
|
|
678
|
+
async function loadTriggers(frb, serializedFunctionTrigger) {
|
|
675
679
|
let triggerModule;
|
|
676
680
|
if (serializedFunctionTrigger) {
|
|
677
681
|
triggerModule = eval(serializedFunctionTrigger)();
|
|
@@ -690,13 +694,7 @@ async function loadTrigger(frb, functionTarget, serializedFunctionTrigger) {
|
|
|
690
694
|
triggerModule = await dynamicImport(moduleURL);
|
|
691
695
|
}
|
|
692
696
|
}
|
|
693
|
-
|
|
694
|
-
return mod === null || mod === void 0 ? void 0 : mod[functionTargetPart];
|
|
695
|
-
}, triggerModule);
|
|
696
|
-
if (!maybeTrigger) {
|
|
697
|
-
throw new Error(`Failed to find function ${functionTarget} in the loaded module`);
|
|
698
|
-
}
|
|
699
|
-
return maybeTrigger;
|
|
697
|
+
return triggerModule;
|
|
700
698
|
}
|
|
701
699
|
async function flushAndExit(code) {
|
|
702
700
|
await types_1.EmulatorLog.waitForFlush();
|
|
@@ -716,22 +714,32 @@ async function handleMessage(message) {
|
|
|
716
714
|
await flushAndExit(1);
|
|
717
715
|
return;
|
|
718
716
|
}
|
|
719
|
-
if (!
|
|
717
|
+
if (!functionModule) {
|
|
720
718
|
try {
|
|
721
719
|
await initializeRuntime(runtimeArgs.frb);
|
|
722
720
|
const serializedTriggers = runtimeArgs.opts ? runtimeArgs.opts.serializedTriggers : undefined;
|
|
723
|
-
|
|
721
|
+
functionModule = await loadTriggers(runtimeArgs.frb, serializedTriggers);
|
|
724
722
|
}
|
|
725
723
|
catch (e) {
|
|
726
724
|
logDebug(e);
|
|
727
|
-
new types_1.EmulatorLog("FATAL", "runtime-status", `Failed to initialize and load
|
|
725
|
+
new types_1.EmulatorLog("FATAL", "runtime-status", `Failed to initialize and load triggers. This shouldn't happen: ${e.message}`).log();
|
|
728
726
|
await flushAndExit(1);
|
|
729
727
|
return;
|
|
730
728
|
}
|
|
731
729
|
}
|
|
730
|
+
if (FUNCTION_DEBUG_MODE) {
|
|
731
|
+
FUNCTION_TARGET_NAME = runtimeArgs.frb.debug.functionTarget;
|
|
732
|
+
FUNCTION_SIGNATURE = runtimeArgs.frb.debug.functionSignature;
|
|
733
|
+
}
|
|
734
|
+
const trigger = FUNCTION_TARGET_NAME.split(".").reduce((mod, functionTargetPart) => {
|
|
735
|
+
return mod === null || mod === void 0 ? void 0 : mod[functionTargetPart];
|
|
736
|
+
}, functionModule);
|
|
737
|
+
if (!trigger) {
|
|
738
|
+
throw new Error(`Failed to find function ${FUNCTION_TARGET_NAME} in the loaded module`);
|
|
739
|
+
}
|
|
732
740
|
logDebug(`Beginning invocation function ${FUNCTION_TARGET_NAME}!`);
|
|
733
741
|
try {
|
|
734
|
-
await invokeTrigger(runtimeArgs.frb);
|
|
742
|
+
await invokeTrigger(trigger, runtimeArgs.frb);
|
|
735
743
|
if (runtimeArgs.opts && runtimeArgs.opts.serializedTriggers) {
|
|
736
744
|
await flushAndExit(0);
|
|
737
745
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
|
|
3
|
+
exports.getSecretLocalPath = exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const os = require("os");
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const fs = require("fs");
|
|
8
|
+
const backend = require("../deploy/functions/backend");
|
|
8
9
|
const constants_1 = require("./constants");
|
|
9
|
-
const backend_1 = require("../deploy/functions/backend");
|
|
10
10
|
const proto_1 = require("../gcp/proto");
|
|
11
|
+
const logger_1 = require("../logger");
|
|
12
|
+
const manifest_1 = require("../extensions/manifest");
|
|
11
13
|
const memoryLookup = {
|
|
12
14
|
"128MB": 128,
|
|
13
15
|
"256MB": 256,
|
|
@@ -60,30 +62,35 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
60
62
|
id: `${endpoint.region}-${endpoint.id}`,
|
|
61
63
|
};
|
|
62
64
|
(0, proto_1.copyIfPresent)(def, endpoint, "timeout", "availableMemoryMb", "labels", "platform", "secretEnvironmentVariables");
|
|
63
|
-
if (
|
|
65
|
+
if (backend.isHttpsTriggered(endpoint)) {
|
|
64
66
|
def.httpsTrigger = endpoint.httpsTrigger;
|
|
65
67
|
}
|
|
66
|
-
else if (
|
|
68
|
+
else if (backend.isEventTriggered(endpoint)) {
|
|
67
69
|
const eventTrigger = endpoint.eventTrigger;
|
|
68
70
|
if (endpoint.platform === "gcfv1") {
|
|
71
|
+
const resourceFilter = backend.findEventFilter(endpoint, "resource");
|
|
72
|
+
if (!resourceFilter) {
|
|
73
|
+
logger_1.logger.debug(`Invalid event trigger ${JSON.stringify(endpoint)}, expected event filter with resource attribute. Skipping.`);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
69
76
|
def.eventTrigger = {
|
|
70
77
|
eventType: eventTrigger.eventType,
|
|
71
|
-
resource:
|
|
78
|
+
resource: resourceFilter.value,
|
|
72
79
|
};
|
|
73
80
|
}
|
|
74
81
|
else {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
const [eventFilter] = endpoint.eventTrigger.eventFilters;
|
|
83
|
+
if (!eventFilter) {
|
|
84
|
+
logger_1.logger.debug(`Invalid event trigger ${JSON.stringify(endpoint)}, expected at least one event filter. Skipping.`);
|
|
78
85
|
continue;
|
|
79
86
|
}
|
|
80
87
|
def.eventTrigger = {
|
|
81
88
|
eventType: eventTrigger.eventType,
|
|
82
|
-
resource:
|
|
89
|
+
resource: eventFilter.value,
|
|
83
90
|
};
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
|
-
else if (
|
|
93
|
+
else if (backend.isScheduleTriggered(endpoint)) {
|
|
87
94
|
def.eventTrigger = { eventType: "pubsub", resource: "" };
|
|
88
95
|
def.schedule = endpoint.scheduleTrigger;
|
|
89
96
|
}
|
|
@@ -94,7 +101,7 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
94
101
|
return regionDefinitions;
|
|
95
102
|
}
|
|
96
103
|
exports.emulatedFunctionsFromEndpoints = emulatedFunctionsFromEndpoints;
|
|
97
|
-
function emulatedFunctionsByRegion(definitions) {
|
|
104
|
+
function emulatedFunctionsByRegion(definitions, secretEnvVariables = []) {
|
|
98
105
|
const regionDefinitions = [];
|
|
99
106
|
for (const def of definitions) {
|
|
100
107
|
if (!def.regions) {
|
|
@@ -106,6 +113,7 @@ function emulatedFunctionsByRegion(definitions) {
|
|
|
106
113
|
defDeepCopy.region = region;
|
|
107
114
|
defDeepCopy.id = `${region}-${defDeepCopy.name}`;
|
|
108
115
|
defDeepCopy.platform = defDeepCopy.platform || "gcfv1";
|
|
116
|
+
defDeepCopy.secretEnvironmentVariables = secretEnvVariables;
|
|
109
117
|
regionDefinitions.push(defDeepCopy);
|
|
110
118
|
}
|
|
111
119
|
}
|
|
@@ -219,3 +227,14 @@ function getSignatureType(def) {
|
|
|
219
227
|
return def.platform === "gcfv2" ? "cloudevent" : "event";
|
|
220
228
|
}
|
|
221
229
|
exports.getSignatureType = getSignatureType;
|
|
230
|
+
const LOCAL_SECRETS_FILE = ".secret.local";
|
|
231
|
+
function getSecretLocalPath(backend, projectDir) {
|
|
232
|
+
const secretsFile = backend.extensionInstanceId
|
|
233
|
+
? `${backend.extensionInstanceId}${LOCAL_SECRETS_FILE}`
|
|
234
|
+
: LOCAL_SECRETS_FILE;
|
|
235
|
+
const secretDirectory = backend.extensionInstanceId
|
|
236
|
+
? path.join(projectDir, manifest_1.ENV_DIRECTORY)
|
|
237
|
+
: backend.functionsDir;
|
|
238
|
+
return path.join(secretDirectory, secretsFile);
|
|
239
|
+
}
|
|
240
|
+
exports.getSecretLocalPath = getSecretLocalPath;
|
|
@@ -42,7 +42,7 @@ class FunctionsEmulatorShell {
|
|
|
42
42
|
auth: opts.auth,
|
|
43
43
|
data,
|
|
44
44
|
};
|
|
45
|
-
this.emu.
|
|
45
|
+
this.emu.invokeTrigger(this.backend, trigger, proto);
|
|
46
46
|
}
|
|
47
47
|
getTrigger(name) {
|
|
48
48
|
const result = this.triggers.find((trigger) => {
|
|
@@ -52,7 +52,7 @@ function parseRuntimeVersion(runtime) {
|
|
|
52
52
|
return undefined;
|
|
53
53
|
}
|
|
54
54
|
const runtimeRe = /(nodejs)?([0-9]+)/;
|
|
55
|
-
const match =
|
|
55
|
+
const match = runtimeRe.exec(runtime);
|
|
56
56
|
if (match) {
|
|
57
57
|
return Number.parseInt(match[2]);
|
|
58
58
|
}
|
|
@@ -73,13 +73,13 @@ exports.parseVersionString = parseVersionString;
|
|
|
73
73
|
function compareVersionStrings(a, b) {
|
|
74
74
|
const versionA = parseVersionString(a);
|
|
75
75
|
const versionB = parseVersionString(b);
|
|
76
|
-
if (versionA.major
|
|
76
|
+
if (versionA.major !== versionB.major) {
|
|
77
77
|
return versionA.major - versionB.major;
|
|
78
78
|
}
|
|
79
|
-
if (versionA.minor
|
|
79
|
+
if (versionA.minor !== versionB.minor) {
|
|
80
80
|
return versionA.minor - versionB.minor;
|
|
81
81
|
}
|
|
82
|
-
if (versionA.patch
|
|
82
|
+
if (versionA.patch !== versionB.patch) {
|
|
83
83
|
return versionA.patch - versionB.patch;
|
|
84
84
|
}
|
|
85
85
|
return 0;
|
|
@@ -170,14 +170,14 @@ class RuntimeWorkerPool {
|
|
|
170
170
|
}
|
|
171
171
|
return;
|
|
172
172
|
}
|
|
173
|
-
addWorker(triggerId, runtime) {
|
|
173
|
+
addWorker(triggerId, runtime, extensionLogInfo) {
|
|
174
174
|
const worker = new RuntimeWorker(this.getKey(triggerId), runtime);
|
|
175
175
|
this.log(`addWorker(${worker.key})`);
|
|
176
176
|
const keyWorkers = this.getTriggerWorkers(triggerId);
|
|
177
177
|
keyWorkers.push(worker);
|
|
178
178
|
this.setTriggerWorkers(triggerId, keyWorkers);
|
|
179
179
|
const logger = triggerId
|
|
180
|
-
? emulatorLogger_1.EmulatorLogger.forFunction(triggerId)
|
|
180
|
+
? emulatorLogger_1.EmulatorLogger.forFunction(triggerId, extensionLogInfo)
|
|
181
181
|
: emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
|
|
182
182
|
worker.onLogs((log) => {
|
|
183
183
|
logger.handleRuntimeLog(log);
|
package/lib/emulator/hub.js
CHANGED
|
@@ -25,9 +25,10 @@ class EmulatorHub {
|
|
|
25
25
|
});
|
|
26
26
|
this.hub.get(EmulatorHub.PATH_EMULATORS, (req, res) => {
|
|
27
27
|
const body = {};
|
|
28
|
-
registry_1.EmulatorRegistry.listRunning()
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
for (const emulator of registry_1.EmulatorRegistry.listRunning()) {
|
|
29
|
+
const info = registry_1.EmulatorRegistry.getInfo(emulator);
|
|
30
|
+
body[emulator] = info;
|
|
31
|
+
}
|
|
31
32
|
res.json(body);
|
|
32
33
|
});
|
|
33
34
|
this.hub.post(EmulatorHub.PATH_EXPORT, async (req, res) => {
|
|
@@ -82,7 +82,7 @@ class WebSocketTransport extends TransportStream {
|
|
|
82
82
|
};
|
|
83
83
|
const splat = [info.message, ...(info[triple_beam_1.SPLAT] || [])]
|
|
84
84
|
.map((value) => {
|
|
85
|
-
if (typeof value
|
|
85
|
+
if (typeof value === "string") {
|
|
86
86
|
try {
|
|
87
87
|
bundle.data = Object.assign(Object.assign({}, bundle.data), JSON.parse(value));
|
|
88
88
|
return null;
|
|
@@ -88,7 +88,7 @@ class PubsubEmulator {
|
|
|
88
88
|
this.subscriptionForTopic.set(topicName, sub);
|
|
89
89
|
}
|
|
90
90
|
ensureFunctionsClient() {
|
|
91
|
-
if (this.client
|
|
91
|
+
if (this.client !== undefined)
|
|
92
92
|
return;
|
|
93
93
|
const funcEmulator = registry_1.EmulatorRegistry.get(types_1.Emulators.FUNCTIONS);
|
|
94
94
|
if (!funcEmulator) {
|