firebase-tools 10.2.0 → 10.3.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/apiv2.js +3 -0
- 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/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 +58 -4
- package/lib/commands/ext-dev-init.js +49 -49
- package/lib/commands/ext-export.js +7 -2
- package/lib/commands/ext-install.js +163 -104
- package/lib/commands/ext-uninstall.js +17 -8
- package/lib/commands/ext-update.js +64 -11
- package/lib/commands/functions-config-clone.js +1 -1
- 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 +52 -15
- 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/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 +4 -0
- package/lib/emulator/controller.js +54 -24
- package/lib/emulator/download.js +18 -1
- package/lib/emulator/downloadableEmulators.js +30 -13
- 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 +106 -44
- package/lib/emulator/functionsEmulatorRuntime.js +44 -36
- package/lib/emulator/functionsEmulatorShared.js +17 -10
- 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 +1 -1
- 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/listExtensions.js +2 -0
- package/lib/extensions/manifest.js +144 -0
- package/lib/extensions/metricsUtils.js +4 -4
- package/lib/extensions/paramHelper.js +9 -8
- 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 +42 -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/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 -1
- package/lib/serve/hosting.js +1 -1
- package/lib/utils.js +15 -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
- 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,10 +30,10 @@ 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
38
|
const LOCAL_SECRETS_FILE = ".secret.local";
|
|
38
39
|
const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
|
|
@@ -92,6 +93,7 @@ class FunctionsEmulator {
|
|
|
92
93
|
const httpsFunctionRoute = `/${this.args.projectId}/:region/:trigger_name`;
|
|
93
94
|
const multicastFunctionRoute = `/functions/projects/:project_id/trigger_multicast`;
|
|
94
95
|
const httpsFunctionRoutes = [httpsFunctionRoute, `${httpsFunctionRoute}/*`];
|
|
96
|
+
const listBackendsRoute = `/backends`;
|
|
95
97
|
const backgroundHandler = (req, res) => {
|
|
96
98
|
var _a;
|
|
97
99
|
const region = req.params.region;
|
|
@@ -152,6 +154,10 @@ class FunctionsEmulator {
|
|
|
152
154
|
});
|
|
153
155
|
res.json({ status: "multicast_acknowledged" });
|
|
154
156
|
};
|
|
157
|
+
const listBackendsHandler = (req, res) => {
|
|
158
|
+
res.json({ backends: this.getBackendInfo() });
|
|
159
|
+
};
|
|
160
|
+
hub.get(listBackendsRoute, cors({ origin: true }), listBackendsHandler);
|
|
155
161
|
hub.post(backgroundFunctionRoute, dataMiddleware, backgroundHandler);
|
|
156
162
|
hub.post(multicastFunctionRoute, dataMiddleware, multicastHandler);
|
|
157
163
|
hub.all(httpsFunctionRoutes, dataMiddleware, httpsHandler);
|
|
@@ -161,9 +167,15 @@ class FunctionsEmulator {
|
|
|
161
167
|
});
|
|
162
168
|
return hub;
|
|
163
169
|
}
|
|
164
|
-
async
|
|
170
|
+
async invokeTrigger(backend, trigger, proto, runtimeOpts) {
|
|
165
171
|
const bundleTemplate = this.getBaseBundle();
|
|
166
172
|
const runtimeBundle = Object.assign(Object.assign({}, bundleTemplate), { proto });
|
|
173
|
+
if (this.args.debugPort) {
|
|
174
|
+
runtimeBundle.debug = {
|
|
175
|
+
functionTarget: trigger.entryPoint,
|
|
176
|
+
functionSignature: (0, functionsEmulatorShared_1.getSignatureType)(trigger),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
167
179
|
if (!backend.nodeBinary) {
|
|
168
180
|
throw new error_1.FirebaseError(`No node binary for ${trigger.id}. This should never happen.`);
|
|
169
181
|
}
|
|
@@ -240,20 +252,25 @@ class FunctionsEmulator {
|
|
|
240
252
|
}
|
|
241
253
|
let triggerDefinitions;
|
|
242
254
|
if (emulatableBackend.predefinedTriggers) {
|
|
243
|
-
triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers);
|
|
255
|
+
triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers, emulatableBackend.secretEnv);
|
|
244
256
|
}
|
|
245
257
|
else {
|
|
246
|
-
const
|
|
258
|
+
const runtimeConfig = this.getRuntimeConfig(emulatableBackend);
|
|
259
|
+
const runtimeDelegateContext = {
|
|
247
260
|
projectId: this.args.projectId,
|
|
248
261
|
projectDir: this.args.projectDir,
|
|
249
262
|
sourceDir: emulatableBackend.functionsDir,
|
|
250
|
-
}
|
|
263
|
+
};
|
|
264
|
+
if (emulatableBackend.nodeMajorVersion) {
|
|
265
|
+
runtimeDelegateContext.runtime = `nodejs${emulatableBackend.nodeMajorVersion}`;
|
|
266
|
+
}
|
|
267
|
+
const runtimeDelegate = await runtimes.getRuntimeDelegate(runtimeDelegateContext);
|
|
251
268
|
logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
|
|
252
269
|
await runtimeDelegate.validate();
|
|
253
270
|
logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
|
|
254
271
|
await runtimeDelegate.build();
|
|
255
272
|
logger_1.logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
|
|
256
|
-
const discoveredBackend = await runtimeDelegate.discoverSpec(
|
|
273
|
+
const discoveredBackend = await runtimeDelegate.discoverSpec(runtimeConfig, Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), emulatableBackend.env));
|
|
257
274
|
const endpoints = backend.allEndpoints(discoveredBackend);
|
|
258
275
|
triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsFromEndpoints)(endpoints);
|
|
259
276
|
}
|
|
@@ -330,6 +347,9 @@ class FunctionsEmulator {
|
|
|
330
347
|
this.logger.logLabeled("SUCCESS", `functions[${definition.id}]`, msg);
|
|
331
348
|
}
|
|
332
349
|
}
|
|
350
|
+
if (this.args.debugPort) {
|
|
351
|
+
this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
|
|
352
|
+
}
|
|
333
353
|
}
|
|
334
354
|
addRealtimeDatabaseTrigger(projectId, key, eventTrigger) {
|
|
335
355
|
const databaseEmu = registry_1.EmulatorRegistry.get(types_1.Emulators.DATABASE);
|
|
@@ -468,8 +488,26 @@ class FunctionsEmulator {
|
|
|
468
488
|
getTriggerKey(def) {
|
|
469
489
|
return def.eventTrigger ? `${def.id}-${this.triggerGeneration}` : def.id;
|
|
470
490
|
}
|
|
471
|
-
|
|
472
|
-
|
|
491
|
+
getBackendInfo() {
|
|
492
|
+
const cf3Triggers = Object.values(this.triggers)
|
|
493
|
+
.filter((t) => !t.backend.extensionInstanceId)
|
|
494
|
+
.map((t) => t.def);
|
|
495
|
+
return this.args.emulatableBackends.map((e) => {
|
|
496
|
+
var _a;
|
|
497
|
+
const envWithSecrets = Object.assign({}, e.env);
|
|
498
|
+
for (const s of e.secretEnv) {
|
|
499
|
+
envWithSecrets[s.key] = backend.secretVersionName(s);
|
|
500
|
+
}
|
|
501
|
+
return {
|
|
502
|
+
directory: e.functionsDir,
|
|
503
|
+
env: envWithSecrets,
|
|
504
|
+
extensionInstanceId: e.extensionInstanceId,
|
|
505
|
+
extension: e.extension,
|
|
506
|
+
extensionVersion: e.extensionVersion,
|
|
507
|
+
extensionSpec: e.extensionSpec,
|
|
508
|
+
functionTriggers: (_a = e.predefinedTriggers) !== null && _a !== void 0 ? _a : cf3Triggers,
|
|
509
|
+
};
|
|
510
|
+
});
|
|
473
511
|
}
|
|
474
512
|
addTriggerRecord(def, opts) {
|
|
475
513
|
const key = this.getTriggerKey(def);
|
|
@@ -521,10 +559,21 @@ class FunctionsEmulator {
|
|
|
521
559
|
}
|
|
522
560
|
return process.execPath;
|
|
523
561
|
}
|
|
562
|
+
getRuntimeConfig(backend) {
|
|
563
|
+
const configPath = `${backend.functionsDir}/.runtimeconfig.json`;
|
|
564
|
+
try {
|
|
565
|
+
const configContent = fs.readFileSync(configPath, "utf8");
|
|
566
|
+
return JSON.parse(configContent.toString());
|
|
567
|
+
}
|
|
568
|
+
catch (e) {
|
|
569
|
+
}
|
|
570
|
+
return {};
|
|
571
|
+
}
|
|
524
572
|
getUserEnvs(backend) {
|
|
525
573
|
const projectInfo = {
|
|
526
574
|
functionsSource: backend.functionsDir,
|
|
527
575
|
projectId: this.args.projectId,
|
|
576
|
+
projectAlias: this.args.projectAlias,
|
|
528
577
|
isEmulator: true,
|
|
529
578
|
};
|
|
530
579
|
if (functionsEnv.hasUserEnvs(projectInfo)) {
|
|
@@ -543,11 +592,10 @@ class FunctionsEmulator {
|
|
|
543
592
|
envs.K_REVISION = "1";
|
|
544
593
|
envs.PORT = "80";
|
|
545
594
|
if (trigger) {
|
|
546
|
-
const
|
|
547
|
-
const target = service.replace(/-/g, ".");
|
|
595
|
+
const target = trigger.entryPoint;
|
|
548
596
|
envs.FUNCTION_TARGET = target;
|
|
549
597
|
envs.FUNCTION_SIGNATURE_TYPE = (0, functionsEmulatorShared_1.getSignatureType)(trigger);
|
|
550
|
-
envs.K_SERVICE =
|
|
598
|
+
envs.K_SERVICE = trigger.name;
|
|
551
599
|
}
|
|
552
600
|
return envs;
|
|
553
601
|
}
|
|
@@ -579,6 +627,9 @@ class FunctionsEmulator {
|
|
|
579
627
|
const pubsubHost = (0, functionsEmulatorShared_1.formatHost)(pubsubEmulator);
|
|
580
628
|
process.env.PUBSUB_EMULATOR_HOST = pubsubHost;
|
|
581
629
|
}
|
|
630
|
+
if (this.args.debugPort) {
|
|
631
|
+
envs["FUNCTION_DEBUG_MODE"] = "true";
|
|
632
|
+
}
|
|
582
633
|
return envs;
|
|
583
634
|
}
|
|
584
635
|
getFirebaseConfig() {
|
|
@@ -612,44 +663,51 @@ class FunctionsEmulator {
|
|
|
612
663
|
this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${LOCAL_SECRETS_FILE}: ${e.message}`);
|
|
613
664
|
}
|
|
614
665
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
+
}
|
|
628
686
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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 ${LOCAL_SECRETS_FILE}:\n\t` +
|
|
691
|
+
errs.join("\n\t"));
|
|
632
692
|
}
|
|
633
693
|
}
|
|
634
|
-
if (errs.length > 0) {
|
|
635
|
-
this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
|
|
636
|
-
"Make sure the credential used for the Functions Emulator have access " +
|
|
637
|
-
`or provide override values in ${LOCAL_SECRETS_FILE}:\n\t` +
|
|
638
|
-
errs.join("\n\t"));
|
|
639
|
-
}
|
|
640
694
|
return secretEnvs;
|
|
641
695
|
}
|
|
642
696
|
async invokeRuntime(backend, trigger, frb, opts) {
|
|
643
|
-
if (this.workerPool.readyForWork(trigger.id)) {
|
|
644
|
-
|
|
697
|
+
if (!this.workerPool.readyForWork(trigger.id)) {
|
|
698
|
+
await this.startRuntime(backend, opts, trigger);
|
|
645
699
|
}
|
|
700
|
+
return this.workerPool.submitWork(trigger.id, frb, opts);
|
|
701
|
+
}
|
|
702
|
+
async startRuntime(backend, opts, trigger) {
|
|
703
|
+
var _a;
|
|
646
704
|
const emitter = new events_1.EventEmitter();
|
|
647
705
|
const args = [path.join(__dirname, "functionsEmulatorRuntime")];
|
|
648
706
|
if (opts.ignore_warnings) {
|
|
649
707
|
args.unshift("--no-warnings");
|
|
650
708
|
}
|
|
651
709
|
if (this.args.debugPort) {
|
|
652
|
-
if (process.env.FIREPIT_VERSION && process.execPath
|
|
710
|
+
if (process.env.FIREPIT_VERSION && process.execPath === opts.nodeBinary) {
|
|
653
711
|
const requestedMajorNodeVersion = this.getNodeBinary(backend);
|
|
654
712
|
this.logger.log("WARN", `To enable function inspection, please run "${process.execPath} is:npm i node@${requestedMajorNodeVersion} --save-dev" in your functions directory`);
|
|
655
713
|
}
|
|
@@ -711,8 +769,12 @@ class FunctionsEmulator {
|
|
|
711
769
|
return childProcess.send(JSON.stringify(args));
|
|
712
770
|
},
|
|
713
771
|
};
|
|
714
|
-
|
|
715
|
-
|
|
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;
|
|
716
778
|
}
|
|
717
779
|
async disableBackgroundTriggers() {
|
|
718
780
|
Object.values(this.triggers).forEach((record) => {
|
|
@@ -738,7 +800,7 @@ class FunctionsEmulator {
|
|
|
738
800
|
}
|
|
739
801
|
const trigger = record.def;
|
|
740
802
|
const service = (0, functionsEmulatorShared_1.getFunctionService)(trigger);
|
|
741
|
-
const worker = await this.
|
|
803
|
+
const worker = await this.invokeTrigger(record.backend, trigger, proto);
|
|
742
804
|
return new Promise((resolve, reject) => {
|
|
743
805
|
if (projectId !== this.args.projectId) {
|
|
744
806
|
if (service !== constants_1.Constants.SERVICE_REALTIME_DATABASE) {
|
|
@@ -757,7 +819,7 @@ class FunctionsEmulator {
|
|
|
757
819
|
reject({ code: 500, body: el.text });
|
|
758
820
|
}
|
|
759
821
|
});
|
|
760
|
-
track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
|
|
822
|
+
void track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
|
|
761
823
|
worker.waitForDone().then(() => {
|
|
762
824
|
resolve({ status: "acknowledged" });
|
|
763
825
|
});
|
|
@@ -825,14 +887,14 @@ class FunctionsEmulator {
|
|
|
825
887
|
req.headers[functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER] = encodeURIComponent(JSON.stringify(contextAuth));
|
|
826
888
|
}
|
|
827
889
|
}
|
|
828
|
-
const worker = await this.
|
|
890
|
+
const worker = await this.invokeTrigger(record.backend, trigger);
|
|
829
891
|
worker.onLogs((el) => {
|
|
830
892
|
if (el.level === "FATAL") {
|
|
831
893
|
res.status(500).send(el.text);
|
|
832
894
|
}
|
|
833
895
|
});
|
|
834
896
|
await worker.waitForSocketReady();
|
|
835
|
-
track(EVENT_INVOKE, "https");
|
|
897
|
+
void track(EVENT_INVOKE, "https");
|
|
836
898
|
this.logger.log("DEBUG", `[functions] Runtime ready! Sending request!`);
|
|
837
899
|
if (!worker.lastArgs) {
|
|
838
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
|
}
|
|
@@ -5,9 +5,10 @@ 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");
|
|
11
12
|
const memoryLookup = {
|
|
12
13
|
"128MB": 128,
|
|
13
14
|
"256MB": 256,
|
|
@@ -60,30 +61,35 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
60
61
|
id: `${endpoint.region}-${endpoint.id}`,
|
|
61
62
|
};
|
|
62
63
|
(0, proto_1.copyIfPresent)(def, endpoint, "timeout", "availableMemoryMb", "labels", "platform", "secretEnvironmentVariables");
|
|
63
|
-
if (
|
|
64
|
+
if (backend.isHttpsTriggered(endpoint)) {
|
|
64
65
|
def.httpsTrigger = endpoint.httpsTrigger;
|
|
65
66
|
}
|
|
66
|
-
else if (
|
|
67
|
+
else if (backend.isEventTriggered(endpoint)) {
|
|
67
68
|
const eventTrigger = endpoint.eventTrigger;
|
|
68
69
|
if (endpoint.platform === "gcfv1") {
|
|
70
|
+
const resourceFilter = backend.findEventFilter(endpoint, "resource");
|
|
71
|
+
if (!resourceFilter) {
|
|
72
|
+
logger_1.logger.debug(`Invalid event trigger ${JSON.stringify(endpoint)}, expected event filter with resource attribute. Skipping.`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
69
75
|
def.eventTrigger = {
|
|
70
76
|
eventType: eventTrigger.eventType,
|
|
71
|
-
resource:
|
|
77
|
+
resource: resourceFilter.value,
|
|
72
78
|
};
|
|
73
79
|
}
|
|
74
80
|
else {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
const [eventFilter] = endpoint.eventTrigger.eventFilters;
|
|
82
|
+
if (!eventFilter) {
|
|
83
|
+
logger_1.logger.debug(`Invalid event trigger ${JSON.stringify(endpoint)}, expected at least one event filter. Skipping.`);
|
|
78
84
|
continue;
|
|
79
85
|
}
|
|
80
86
|
def.eventTrigger = {
|
|
81
87
|
eventType: eventTrigger.eventType,
|
|
82
|
-
resource:
|
|
88
|
+
resource: eventFilter.value,
|
|
83
89
|
};
|
|
84
90
|
}
|
|
85
91
|
}
|
|
86
|
-
else if (
|
|
92
|
+
else if (backend.isScheduleTriggered(endpoint)) {
|
|
87
93
|
def.eventTrigger = { eventType: "pubsub", resource: "" };
|
|
88
94
|
def.schedule = endpoint.scheduleTrigger;
|
|
89
95
|
}
|
|
@@ -94,7 +100,7 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
94
100
|
return regionDefinitions;
|
|
95
101
|
}
|
|
96
102
|
exports.emulatedFunctionsFromEndpoints = emulatedFunctionsFromEndpoints;
|
|
97
|
-
function emulatedFunctionsByRegion(definitions) {
|
|
103
|
+
function emulatedFunctionsByRegion(definitions, secretEnvVariables = []) {
|
|
98
104
|
const regionDefinitions = [];
|
|
99
105
|
for (const def of definitions) {
|
|
100
106
|
if (!def.regions) {
|
|
@@ -106,6 +112,7 @@ function emulatedFunctionsByRegion(definitions) {
|
|
|
106
112
|
defDeepCopy.region = region;
|
|
107
113
|
defDeepCopy.id = `${region}-${defDeepCopy.name}`;
|
|
108
114
|
defDeepCopy.platform = defDeepCopy.platform || "gcfv1";
|
|
115
|
+
defDeepCopy.secretEnvironmentVariables = secretEnvVariables;
|
|
109
116
|
regionDefinitions.push(defDeepCopy);
|
|
110
117
|
}
|
|
111
118
|
}
|
|
@@ -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) {
|