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
|
@@ -71,7 +71,7 @@ class Delegate {
|
|
|
71
71
|
},
|
|
72
72
|
stdio: ["ignore", "pipe", "pipe"],
|
|
73
73
|
});
|
|
74
|
-
if (genBinary.status
|
|
74
|
+
if (genBinary.status !== 0) {
|
|
75
75
|
throw new error_1.FirebaseError("Failed to run codegen", {
|
|
76
76
|
children: [new Error(genBinary.stderr.toString())],
|
|
77
77
|
});
|
|
@@ -96,7 +96,7 @@ class Delegate {
|
|
|
96
96
|
childProcess.once("exit", resolve);
|
|
97
97
|
childProcess.once("error", reject);
|
|
98
98
|
});
|
|
99
|
-
await (0, node_fetch_1.default)(`http://localhost:${adminPort}/quitquitquit`);
|
|
99
|
+
await (0, node_fetch_1.default)(`http://localhost:${adminPort}/__/quitquitquit`);
|
|
100
100
|
setTimeout(() => {
|
|
101
101
|
if (!childProcess.killed) {
|
|
102
102
|
childProcess.kill("SIGKILL");
|
|
@@ -4,12 +4,20 @@ exports.Delegate = exports.tryCreateDelegate = void 0;
|
|
|
4
4
|
const util_1 = require("util");
|
|
5
5
|
const fs = require("fs");
|
|
6
6
|
const path = require("path");
|
|
7
|
+
const portfinder = require("portfinder");
|
|
8
|
+
const semver = require("semver");
|
|
9
|
+
const spawn = require("cross-spawn");
|
|
10
|
+
const node_fetch_1 = require("node-fetch");
|
|
7
11
|
const error_1 = require("../../../../error");
|
|
8
12
|
const parseRuntimeAndValidateSDK_1 = require("./parseRuntimeAndValidateSDK");
|
|
9
13
|
const logger_1 = require("../../../../logger");
|
|
14
|
+
const previews_1 = require("../../../../previews");
|
|
15
|
+
const utils_1 = require("../../../../utils");
|
|
16
|
+
const discovery = require("../discovery");
|
|
10
17
|
const validate = require("./validate");
|
|
11
18
|
const versioning = require("./versioning");
|
|
12
19
|
const parseTriggers = require("./parseTriggers");
|
|
20
|
+
const MIN_FUNCTIONS_SDK_VERSION = "3.19.0";
|
|
13
21
|
async function tryCreateDelegate(context) {
|
|
14
22
|
const packageJsonPath = path.join(context.sourceDir, "package.json");
|
|
15
23
|
if (!(await (0, util_1.promisify)(fs.exists)(packageJsonPath))) {
|
|
@@ -50,7 +58,52 @@ class Delegate {
|
|
|
50
58
|
watch() {
|
|
51
59
|
return Promise.resolve(() => Promise.resolve());
|
|
52
60
|
}
|
|
61
|
+
serve(port, envs) {
|
|
62
|
+
var _a;
|
|
63
|
+
const childProcess = spawn("./node_modules/.bin/firebase-functions", [this.sourceDir], {
|
|
64
|
+
env: Object.assign(Object.assign({}, envs), { PORT: port.toString(), FUNCTIONS_CONTROL_API: "true", HOME: process.env.HOME, PATH: process.env.PATH }),
|
|
65
|
+
cwd: this.sourceDir,
|
|
66
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
67
|
+
});
|
|
68
|
+
(_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (chunk) => {
|
|
69
|
+
logger_1.logger.debug(chunk.toString());
|
|
70
|
+
});
|
|
71
|
+
return Promise.resolve(async () => {
|
|
72
|
+
const p = new Promise((resolve, reject) => {
|
|
73
|
+
childProcess.once("exit", resolve);
|
|
74
|
+
childProcess.once("error", reject);
|
|
75
|
+
});
|
|
76
|
+
await (0, node_fetch_1.default)(`http://localhost:${port}/__/quitquitquit`);
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
if (!childProcess.killed) {
|
|
79
|
+
childProcess.kill("SIGKILL");
|
|
80
|
+
}
|
|
81
|
+
}, 10000);
|
|
82
|
+
return p;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
53
85
|
async discoverSpec(config, env) {
|
|
86
|
+
if (previews_1.previews.functionsv2) {
|
|
87
|
+
if (semver.lt(this.sdkVersion, MIN_FUNCTIONS_SDK_VERSION)) {
|
|
88
|
+
(0, utils_1.logLabeledWarning)("functions", `You are using an old version of firebase-functions SDK (${this.sdkVersion}). ` +
|
|
89
|
+
`Please update firebase-functions SDK to >=${MIN_FUNCTIONS_SDK_VERSION}`);
|
|
90
|
+
return parseTriggers.discoverBackend(this.projectId, this.sourceDir, this.runtime, config, env);
|
|
91
|
+
}
|
|
92
|
+
let discovered = await discovery.detectFromYaml(this.sourceDir, this.projectId, this.runtime);
|
|
93
|
+
if (!discovered) {
|
|
94
|
+
const getPort = (0, util_1.promisify)(portfinder.getPort);
|
|
95
|
+
const port = await getPort();
|
|
96
|
+
const kill = await this.serve(port, env);
|
|
97
|
+
try {
|
|
98
|
+
discovered = await discovery.detectFromPort(port, this.projectId, this.runtime);
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
await kill();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
discovered.environmentVariables = env;
|
|
105
|
+
return discovered;
|
|
106
|
+
}
|
|
54
107
|
return parseTriggers.discoverBackend(this.projectId, this.sourceDir, this.runtime, config, env);
|
|
55
108
|
}
|
|
56
109
|
}
|
|
@@ -46,11 +46,11 @@ function getRuntimeChoice(sourceDir, runtimeFromConfig) {
|
|
|
46
46
|
? exports.UNSUPPORTED_NODE_VERSION_FIREBASE_JSON_MSG
|
|
47
47
|
: exports.UNSUPPORTED_NODE_VERSION_PACKAGE_JSON_MSG) + exports.DEPRECATED_NODE_VERSION_INFO;
|
|
48
48
|
if (!runtime || !ENGINE_RUNTIMES_NAMES.includes(runtime)) {
|
|
49
|
-
track("functions_runtime_notices", "package_missing_runtime");
|
|
49
|
+
void track("functions_runtime_notices", "package_missing_runtime");
|
|
50
50
|
throw new error_1.FirebaseError(errorMessage, { exit: 1 });
|
|
51
51
|
}
|
|
52
52
|
if (runtimes.isDeprecatedRuntime(runtime) || !runtimes.isValidRuntime(runtime)) {
|
|
53
|
-
track("functions_runtime_notices", `${runtime}_deploy_prohibited`);
|
|
53
|
+
void track("functions_runtime_notices", `${runtime}_deploy_prohibited`);
|
|
54
54
|
throw new error_1.FirebaseError(errorMessage, { exit: 1 });
|
|
55
55
|
}
|
|
56
56
|
return runtime;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.addResourcesToBackend = exports.discoverBackend = exports.useStrategy = void 0;
|
|
3
|
+
exports.addResourcesToBackend = exports.mergeRequiredAPIs = exports.discoverBackend = exports.useStrategy = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const _ = require("lodash");
|
|
6
6
|
const child_process_1 = require("child_process");
|
|
@@ -9,7 +9,7 @@ const logger_1 = require("../../../../logger");
|
|
|
9
9
|
const backend = require("../../backend");
|
|
10
10
|
const api = require("../../../../api");
|
|
11
11
|
const proto = require("../../../../gcp/proto");
|
|
12
|
-
const
|
|
12
|
+
const v2events = require("../../../../functions/events/v2");
|
|
13
13
|
const TRIGGER_PARSER = path.resolve(__dirname, "./triggerParser.js");
|
|
14
14
|
function removeInspectOptions(options) {
|
|
15
15
|
return options.filter((opt) => !opt.startsWith("--inspect"));
|
|
@@ -58,45 +58,87 @@ async function discoverBackend(projectId, sourceDir, runtime, configValues, envs
|
|
|
58
58
|
return want;
|
|
59
59
|
}
|
|
60
60
|
exports.discoverBackend = discoverBackend;
|
|
61
|
+
function mergeRequiredAPIs(backend) {
|
|
62
|
+
const apiToReasons = {};
|
|
63
|
+
for (const { api, reason } of backend.requiredAPIs) {
|
|
64
|
+
const reasons = apiToReasons[api] || new Set();
|
|
65
|
+
reasons.add(reason);
|
|
66
|
+
apiToReasons[api] = reasons;
|
|
67
|
+
}
|
|
68
|
+
const merged = [];
|
|
69
|
+
for (const [api, reasons] of Object.entries(apiToReasons)) {
|
|
70
|
+
merged.push({ api, reason: Array.from(reasons).join(" ") });
|
|
71
|
+
}
|
|
72
|
+
backend.requiredAPIs = merged;
|
|
73
|
+
}
|
|
74
|
+
exports.mergeRequiredAPIs = mergeRequiredAPIs;
|
|
61
75
|
function addResourcesToBackend(projectId, runtime, annotation, want) {
|
|
76
|
+
var _a;
|
|
62
77
|
Object.freeze(annotation);
|
|
63
78
|
for (const region of annotation.regions || [api.functionsDefaultRegion]) {
|
|
64
79
|
let triggered;
|
|
65
80
|
const triggerCount = +!!annotation.httpsTrigger + +!!annotation.eventTrigger + +!!annotation.taskQueueTrigger;
|
|
66
|
-
if (triggerCount
|
|
81
|
+
if (triggerCount !== 1) {
|
|
67
82
|
throw new error_1.FirebaseError("Unexpected annotation generated by the Firebase Functions SDK. This should never happen.");
|
|
68
83
|
}
|
|
69
84
|
if (annotation.taskQueueTrigger) {
|
|
70
85
|
triggered = { taskQueueTrigger: annotation.taskQueueTrigger };
|
|
71
|
-
want.requiredAPIs
|
|
86
|
+
want.requiredAPIs.push({
|
|
87
|
+
api: "cloudtasks.googleapis.com",
|
|
88
|
+
reason: "Needed for task queue functions.",
|
|
89
|
+
});
|
|
72
90
|
}
|
|
73
91
|
else if (annotation.httpsTrigger) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
92
|
+
if ((_a = annotation.labels) === null || _a === void 0 ? void 0 : _a["deployment-callable"]) {
|
|
93
|
+
delete annotation.labels["deployment-callable"];
|
|
94
|
+
triggered = { callableTrigger: {} };
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const trigger = {};
|
|
98
|
+
if (annotation.failurePolicy) {
|
|
99
|
+
logger_1.logger.warn(`Ignoring retry policy for HTTPS function ${annotation.name}`);
|
|
100
|
+
}
|
|
101
|
+
proto.copyIfPresent(trigger, annotation.httpsTrigger, "invoker");
|
|
102
|
+
triggered = { httpsTrigger: trigger };
|
|
77
103
|
}
|
|
78
|
-
proto.copyIfPresent(trigger, annotation.httpsTrigger, "invoker");
|
|
79
|
-
triggered = { httpsTrigger: trigger };
|
|
80
104
|
}
|
|
81
105
|
else if (annotation.schedule) {
|
|
82
|
-
want.requiredAPIs
|
|
83
|
-
|
|
106
|
+
want.requiredAPIs.push({
|
|
107
|
+
api: "cloudscheduler.googleapis.com",
|
|
108
|
+
reason: "Needed for scheduled functions.",
|
|
109
|
+
});
|
|
84
110
|
triggered = { scheduleTrigger: annotation.schedule };
|
|
85
111
|
}
|
|
86
112
|
else {
|
|
87
113
|
triggered = {
|
|
88
114
|
eventTrigger: {
|
|
89
115
|
eventType: annotation.eventTrigger.eventType,
|
|
90
|
-
eventFilters:
|
|
91
|
-
|
|
92
|
-
|
|
116
|
+
eventFilters: [
|
|
117
|
+
{
|
|
118
|
+
attribute: "resource",
|
|
119
|
+
value: annotation.eventTrigger.resource,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
93
122
|
retry: !!annotation.failurePolicy,
|
|
94
123
|
},
|
|
95
124
|
};
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
125
|
+
if (annotation.platform === "gcfv2") {
|
|
126
|
+
if (annotation.eventTrigger.eventType === v2events.PUBSUB_PUBLISH_EVENT) {
|
|
127
|
+
triggered.eventTrigger.eventFilters = [
|
|
128
|
+
{
|
|
129
|
+
attribute: "topic",
|
|
130
|
+
value: annotation.eventTrigger.resource,
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
if (v2events.STORAGE_EVENTS.find((event) => { var _a; return event === (((_a = annotation.eventTrigger) === null || _a === void 0 ? void 0 : _a.eventType) || ""); })) {
|
|
135
|
+
triggered.eventTrigger.eventFilters = [
|
|
136
|
+
{
|
|
137
|
+
attribute: "bucket",
|
|
138
|
+
value: annotation.eventTrigger.resource,
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
}
|
|
100
142
|
}
|
|
101
143
|
}
|
|
102
144
|
const endpoint = Object.assign({ platform: annotation.platform || "gcfv1", id: annotation.name, region: region, project: projectId, entryPoint: annotation.entryPoint, runtime: runtime }, triggered);
|
|
@@ -105,7 +147,8 @@ function addResourcesToBackend(projectId, runtime, annotation, want) {
|
|
|
105
147
|
if (maybeId && !maybeId.includes("/")) {
|
|
106
148
|
maybeId = `projects/${projectId}/locations/${region}/connectors/${maybeId}`;
|
|
107
149
|
}
|
|
108
|
-
endpoint.
|
|
150
|
+
endpoint.vpc = { connector: maybeId };
|
|
151
|
+
proto.renameIfPresent(endpoint.vpc, annotation, "egressSettings", "vpcConnectorEgressSettings");
|
|
109
152
|
}
|
|
110
153
|
if (annotation.secrets) {
|
|
111
154
|
const secretEnvs = [];
|
|
@@ -119,9 +162,10 @@ function addResourcesToBackend(projectId, runtime, annotation, want) {
|
|
|
119
162
|
}
|
|
120
163
|
endpoint.secretEnvironmentVariables = secretEnvs;
|
|
121
164
|
}
|
|
122
|
-
proto.copyIfPresent(endpoint, annotation, "concurrency", "serviceAccountEmail", "labels", "
|
|
165
|
+
proto.copyIfPresent(endpoint, annotation, "concurrency", "serviceAccountEmail", "labels", "ingressSettings", "timeout", "maxInstances", "minInstances", "availableMemoryMb");
|
|
123
166
|
want.endpoints[region] = want.endpoints[region] || {};
|
|
124
167
|
want.endpoints[region][endpoint.id] = endpoint;
|
|
168
|
+
mergeRequiredAPIs(want);
|
|
125
169
|
}
|
|
126
170
|
}
|
|
127
171
|
exports.addResourcesToBackend = addResourcesToBackend;
|
|
@@ -52,7 +52,7 @@ exports.getLatestSDKVersion = getLatestSDKVersion;
|
|
|
52
52
|
function checkFunctionsSDKVersion(currentVersion) {
|
|
53
53
|
try {
|
|
54
54
|
if (semver.lt(currentVersion, MIN_SDK_VERSION)) {
|
|
55
|
-
track("functions_runtime_notices", "functions_sdk_too_old");
|
|
55
|
+
void track("functions_runtime_notices", "functions_sdk_too_old");
|
|
56
56
|
utils.logWarning(exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING);
|
|
57
57
|
}
|
|
58
58
|
const latest = exports.getLatestSDKVersion();
|
|
@@ -63,7 +63,7 @@ function checkFunctionsSDKVersion(currentVersion) {
|
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
65
|
utils.logWarning(clc.bold.yellow("functions: ") +
|
|
66
|
-
"package.json indicates an outdated version of firebase-functions
|
|
66
|
+
"package.json indicates an outdated version of firebase-functions. Please upgrade using " +
|
|
67
67
|
clc.bold("npm install --save firebase-functions@latest") +
|
|
68
68
|
" in your functions directory.");
|
|
69
69
|
if (semver.major(currentVersion) < semver.major(latest)) {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureFirebaseAlertsTriggerRegion = exports.obtainFirebaseAlertsBindings = exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = void 0;
|
|
4
|
+
const error_1 = require("../../../error");
|
|
5
|
+
exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = "roles/iam.serviceAccountTokenCreator";
|
|
6
|
+
function obtainFirebaseAlertsBindings(projectNumber, existingPolicy) {
|
|
7
|
+
const pubsubServiceAgent = `serviceAccount:service-${projectNumber}@gcp-sa-pubsub.iam.gserviceaccount.com`;
|
|
8
|
+
let pubsubBinding = existingPolicy.bindings.find((b) => b.role === exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE);
|
|
9
|
+
if (!pubsubBinding) {
|
|
10
|
+
pubsubBinding = {
|
|
11
|
+
role: exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE,
|
|
12
|
+
members: [],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
if (!pubsubBinding.members.find((m) => m === pubsubServiceAgent)) {
|
|
16
|
+
pubsubBinding.members.push(pubsubServiceAgent);
|
|
17
|
+
}
|
|
18
|
+
return Promise.resolve([pubsubBinding]);
|
|
19
|
+
}
|
|
20
|
+
exports.obtainFirebaseAlertsBindings = obtainFirebaseAlertsBindings;
|
|
21
|
+
function ensureFirebaseAlertsTriggerRegion(endpoint) {
|
|
22
|
+
if (!endpoint.eventTrigger.region) {
|
|
23
|
+
endpoint.eventTrigger.region = "global";
|
|
24
|
+
}
|
|
25
|
+
if (endpoint.eventTrigger.region !== "global") {
|
|
26
|
+
throw new error_1.FirebaseError("A firebase alerts trigger must specify 'global' trigger location");
|
|
27
|
+
}
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
exports.ensureFirebaseAlertsTriggerRegion = ensureFirebaseAlertsTriggerRegion;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.serviceForEndpoint = exports.EVENT_SERVICE_MAPPING = exports.StorageService = exports.PubSubService = exports.NoOpService = void 0;
|
|
3
|
+
exports.serviceForEndpoint = exports.EVENT_SERVICE_MAPPING = exports.FirebaseAlertsService = exports.StorageService = exports.PubSubService = exports.NoOpService = void 0;
|
|
4
4
|
const backend = require("../backend");
|
|
5
5
|
const storage_1 = require("./storage");
|
|
6
|
+
const firebaseAlerts_1 = require("./firebaseAlerts");
|
|
6
7
|
const noop = () => Promise.resolve();
|
|
7
8
|
exports.NoOpService = {
|
|
8
9
|
name: "noop",
|
|
@@ -22,12 +23,19 @@ exports.StorageService = {
|
|
|
22
23
|
requiredProjectBindings: storage_1.obtainStorageBindings,
|
|
23
24
|
ensureTriggerRegion: storage_1.ensureStorageTriggerRegion,
|
|
24
25
|
};
|
|
26
|
+
exports.FirebaseAlertsService = {
|
|
27
|
+
name: "firebasealerts",
|
|
28
|
+
api: "logging.googleapis.com",
|
|
29
|
+
requiredProjectBindings: firebaseAlerts_1.obtainFirebaseAlertsBindings,
|
|
30
|
+
ensureTriggerRegion: firebaseAlerts_1.ensureFirebaseAlertsTriggerRegion,
|
|
31
|
+
};
|
|
25
32
|
exports.EVENT_SERVICE_MAPPING = {
|
|
26
33
|
"google.cloud.pubsub.topic.v1.messagePublished": exports.PubSubService,
|
|
27
34
|
"google.cloud.storage.object.v1.finalized": exports.StorageService,
|
|
28
35
|
"google.cloud.storage.object.v1.archived": exports.StorageService,
|
|
29
36
|
"google.cloud.storage.object.v1.deleted": exports.StorageService,
|
|
30
37
|
"google.cloud.storage.object.v1.metadataUpdated": exports.StorageService,
|
|
38
|
+
"google.firebase.firebasealerts.alerts.v1.published": exports.FirebaseAlertsService,
|
|
31
39
|
};
|
|
32
40
|
function serviceForEndpoint(endpoint) {
|
|
33
41
|
if (!backend.isEventTriggered(endpoint)) {
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ensureStorageTriggerRegion = exports.obtainStorageBindings = void 0;
|
|
4
4
|
const storage = require("../../../gcp/storage");
|
|
5
|
+
const backend = require("../backend");
|
|
5
6
|
const logger_1 = require("../../../logger");
|
|
6
7
|
const error_1 = require("../../../error");
|
|
7
8
|
const location_1 = require("../../../gcp/location");
|
|
8
9
|
const PUBSUB_PUBLISHER_ROLE = "roles/pubsub.publisher";
|
|
9
|
-
async function obtainStorageBindings(
|
|
10
|
-
const storageResponse = await storage.getServiceAccount(
|
|
10
|
+
async function obtainStorageBindings(projectNumber, existingPolicy) {
|
|
11
|
+
const storageResponse = await storage.getServiceAccount(projectNumber);
|
|
11
12
|
const storageServiceAgent = `serviceAccount:${storageResponse.email_address}`;
|
|
12
13
|
let pubsubBinding = existingPolicy.bindings.find((b) => b.role === PUBSUB_PUBLISHER_ROLE);
|
|
13
14
|
if (!pubsubBinding) {
|
|
@@ -22,11 +23,16 @@ async function obtainStorageBindings(projectId, existingPolicy) {
|
|
|
22
23
|
return [pubsubBinding];
|
|
23
24
|
}
|
|
24
25
|
exports.obtainStorageBindings = obtainStorageBindings;
|
|
25
|
-
async function ensureStorageTriggerRegion(endpoint
|
|
26
|
+
async function ensureStorageTriggerRegion(endpoint) {
|
|
27
|
+
const { eventTrigger } = endpoint;
|
|
26
28
|
if (!eventTrigger.region) {
|
|
27
29
|
logger_1.logger.debug("Looking up bucket region for the storage event trigger");
|
|
30
|
+
const bucketFilter = backend.findEventFilter(endpoint, "bucket");
|
|
31
|
+
if (!bucketFilter) {
|
|
32
|
+
throw new error_1.FirebaseError("Storage event trigger unexpectedly missing event filter with bucket attribute.");
|
|
33
|
+
}
|
|
28
34
|
try {
|
|
29
|
-
const bucket = await storage.getBucket(
|
|
35
|
+
const bucket = await storage.getBucket(bucketFilter.value);
|
|
30
36
|
eventTrigger.region = bucket.location.toLowerCase();
|
|
31
37
|
logger_1.logger.debug("Setting the event trigger region to", eventTrigger.region, ".");
|
|
32
38
|
}
|
|
@@ -9,7 +9,7 @@ async function ensureTriggerRegions(want) {
|
|
|
9
9
|
if (ep.platform === "gcfv1" || !backend.isEventTriggered(ep)) {
|
|
10
10
|
continue;
|
|
11
11
|
}
|
|
12
|
-
regionLookups.push((0, services_1.serviceForEndpoint)(ep).ensureTriggerRegion(ep
|
|
12
|
+
regionLookups.push((0, services_1.serviceForEndpoint)(ep).ensureTriggerRegion(ep));
|
|
13
13
|
}
|
|
14
14
|
await Promise.all(regionLookups);
|
|
15
15
|
}
|
|
@@ -14,7 +14,7 @@ function endpointsAreValid(wantBackend) {
|
|
|
14
14
|
functionIdsAreValid(backend.allEndpoints(wantBackend));
|
|
15
15
|
const gcfV1WithConcurrency = backend
|
|
16
16
|
.allEndpoints(wantBackend)
|
|
17
|
-
.filter((endpoint) => (endpoint.concurrency || 1)
|
|
17
|
+
.filter((endpoint) => (endpoint.concurrency || 1) !== 1 && endpoint.platform === "gcfv1")
|
|
18
18
|
.map((endpoint) => endpoint.id);
|
|
19
19
|
if (gcfV1WithConcurrency.length) {
|
|
20
20
|
const msg = `Cannot set concurrency on the functions ${gcfV1WithConcurrency.join(",")} because they are GCF gen 1`;
|
|
@@ -23,7 +23,7 @@ function endpointsAreValid(wantBackend) {
|
|
|
23
23
|
const tooSmallForConcurrency = backend
|
|
24
24
|
.allEndpoints(wantBackend)
|
|
25
25
|
.filter((endpoint) => {
|
|
26
|
-
if ((endpoint.concurrency || 1)
|
|
26
|
+
if ((endpoint.concurrency || 1) === 1) {
|
|
27
27
|
return false;
|
|
28
28
|
}
|
|
29
29
|
const mem = endpoint.availableMemoryMb || backend.DEFAULT_MEMORY;
|
|
@@ -98,7 +98,7 @@ async function validateSecretVersions(projectId, endpoints) {
|
|
|
98
98
|
for (const result of results) {
|
|
99
99
|
if (result.status === "fulfilled") {
|
|
100
100
|
const sv = result.value;
|
|
101
|
-
if (sv.state
|
|
101
|
+
if (sv.state !== "ENABLED") {
|
|
102
102
|
errs.push(new error_1.FirebaseError(`Expected secret ${sv.secret.name}@${sv.versionId} to be in state ENABLED not ${sv.state}.`));
|
|
103
103
|
}
|
|
104
104
|
secretVersions[sv.secret.name] = sv;
|
|
@@ -53,7 +53,7 @@ async function deploy(context, options) {
|
|
|
53
53
|
await uploader.start();
|
|
54
54
|
}
|
|
55
55
|
catch (err) {
|
|
56
|
-
track("Hosting Deploy", "failure");
|
|
56
|
+
void track("Hosting Deploy", "failure");
|
|
57
57
|
throw err;
|
|
58
58
|
}
|
|
59
59
|
finally {
|
|
@@ -65,7 +65,7 @@ async function deploy(context, options) {
|
|
|
65
65
|
(0, utils_1.logLabeledSuccess)("hosting[" + deploy.site + "]", "file upload complete");
|
|
66
66
|
const dt = Date.now() - t0;
|
|
67
67
|
logger_1.logger.debug("[hosting] deploy completed after " + dt + "ms");
|
|
68
|
-
track("Hosting Deploy", "success", dt);
|
|
68
|
+
void track("Hosting Deploy", "success", dt);
|
|
69
69
|
return runDeploys(deploys, debugging);
|
|
70
70
|
}
|
|
71
71
|
const debugging = !!(options.debug || options.nonInteractive);
|
|
@@ -1,46 +1,48 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dump = exports.load = void 0;
|
|
2
4
|
const fs = require("fs-extra");
|
|
3
5
|
const path = require("path");
|
|
4
|
-
const
|
|
6
|
+
const logger_1 = require("../../logger");
|
|
5
7
|
function cachePath(cwd, name) {
|
|
6
|
-
return path.resolve(cwd,
|
|
8
|
+
return path.resolve(cwd, `.firebase/hosting.${name}.cache`);
|
|
7
9
|
}
|
|
8
|
-
|
|
10
|
+
function load(cwd, name) {
|
|
9
11
|
try {
|
|
10
|
-
const out =
|
|
11
|
-
const lines = fs.readFileSync(cachePath(cwd, name),
|
|
12
|
-
|
|
13
|
-
});
|
|
14
|
-
lines.split("\n").forEach(function (line) {
|
|
12
|
+
const out = new Map();
|
|
13
|
+
const lines = fs.readFileSync(cachePath(cwd, name), "utf8");
|
|
14
|
+
for (const line of lines.split("\n")) {
|
|
15
15
|
const d = line.split(",");
|
|
16
16
|
if (d.length === 3) {
|
|
17
|
-
out
|
|
17
|
+
out.set(d[0], { mtime: parseInt(d[1]), hash: d[2] });
|
|
18
18
|
}
|
|
19
|
-
}
|
|
19
|
+
}
|
|
20
20
|
return out;
|
|
21
21
|
}
|
|
22
22
|
catch (e) {
|
|
23
23
|
if (e.code === "ENOENT") {
|
|
24
|
-
logger.debug(
|
|
24
|
+
logger_1.logger.debug(`[hosting] hash cache [${name}] not populated`);
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
27
|
-
logger.debug(
|
|
27
|
+
logger_1.logger.debug(`[hosting] hash cache [${name}] load error: ${e.message}`);
|
|
28
28
|
}
|
|
29
|
-
return
|
|
29
|
+
return new Map();
|
|
30
30
|
}
|
|
31
|
-
}
|
|
32
|
-
exports.
|
|
31
|
+
}
|
|
32
|
+
exports.load = load;
|
|
33
|
+
function dump(cwd, name, data) {
|
|
33
34
|
let st = "";
|
|
34
35
|
let count = 0;
|
|
35
36
|
for (const [path, d] of data) {
|
|
36
37
|
count++;
|
|
37
|
-
st += path
|
|
38
|
+
st += `${path},${d.mtime},${d.hash}\n`;
|
|
38
39
|
}
|
|
39
40
|
try {
|
|
40
41
|
fs.outputFileSync(cachePath(cwd, name), st, { encoding: "utf8" });
|
|
41
|
-
logger.debug(
|
|
42
|
+
logger_1.logger.debug(`[hosting] hash cache [${name}] stored for ${count} files`);
|
|
42
43
|
}
|
|
43
44
|
catch (e) {
|
|
44
|
-
logger.debug(
|
|
45
|
+
logger_1.logger.debug(`[hosting] unable to store hash cache [${name}]: ${e.stack}`);
|
|
45
46
|
}
|
|
46
|
-
}
|
|
47
|
+
}
|
|
48
|
+
exports.dump = dump;
|
|
@@ -11,7 +11,7 @@ const zlib = require("zlib");
|
|
|
11
11
|
const apiv2_1 = require("../../apiv2");
|
|
12
12
|
const queue_1 = require("../../throttler/queue");
|
|
13
13
|
const api_1 = require("../../api");
|
|
14
|
-
const
|
|
14
|
+
const hashcache_1 = require("./hashcache");
|
|
15
15
|
const logger_1 = require("../../logger");
|
|
16
16
|
const error_1 = require("../../error");
|
|
17
17
|
const MIN_UPLOAD_TIMEOUT = 30000;
|
|
@@ -54,7 +54,7 @@ class Uploader {
|
|
|
54
54
|
this.public = options.public || this.cwd;
|
|
55
55
|
this.files = options.files;
|
|
56
56
|
this.fileCount = this.files.length;
|
|
57
|
-
this.cache =
|
|
57
|
+
this.cache = (0, hashcache_1.load)(this.projectRoot, this.hashcacheName());
|
|
58
58
|
this.cacheNew = new Map();
|
|
59
59
|
this.sizeMap = {};
|
|
60
60
|
this.hashMap = {};
|
|
@@ -78,7 +78,7 @@ class Uploader {
|
|
|
78
78
|
.wait()
|
|
79
79
|
.then(this.queuePopulate.bind(this))
|
|
80
80
|
.then(() => {
|
|
81
|
-
|
|
81
|
+
(0, hashcache_1.dump)(this.projectRoot, this.hashcacheName(), this.cacheNew);
|
|
82
82
|
logger_1.logger.debug("[hosting][hash queue][FINAL]", this.hashQueue.stats());
|
|
83
83
|
this.populateQueue.close();
|
|
84
84
|
return this.populateQueue.wait();
|
|
@@ -91,7 +91,7 @@ class Uploader {
|
|
|
91
91
|
this.uploadQueue.wait().catch((err) => {
|
|
92
92
|
if (err.message.includes("content hash")) {
|
|
93
93
|
logger_1.logger.debug("[hosting][upload queue] upload failed with content hash error. Deleting hash cache");
|
|
94
|
-
|
|
94
|
+
(0, hashcache_1.dump)(this.projectRoot, this.hashcacheName(), new Map());
|
|
95
95
|
}
|
|
96
96
|
});
|
|
97
97
|
const fin = (err) => {
|
|
@@ -123,7 +123,7 @@ class Uploader {
|
|
|
123
123
|
const stats = fs.statSync(path.resolve(this.public, filePath));
|
|
124
124
|
const mtime = stats.mtime.getTime();
|
|
125
125
|
this.sizeMap[filePath] = stats.size;
|
|
126
|
-
const cached = this.cache
|
|
126
|
+
const cached = this.cache.get(filePath);
|
|
127
127
|
if (cached && cached.mtime === mtime) {
|
|
128
128
|
this.cacheNew.set(filePath, cached);
|
|
129
129
|
this.addHash(filePath, cached.hash);
|
|
@@ -20,10 +20,10 @@ async function getEtag(projectNumber, versionNumber) {
|
|
|
20
20
|
exports.getEtag = getEtag;
|
|
21
21
|
function validateInputRemoteConfigTemplate(template) {
|
|
22
22
|
const templateCopy = JSON.parse(JSON.stringify(template));
|
|
23
|
-
if (!templateCopy || templateCopy
|
|
23
|
+
if (!templateCopy || templateCopy === "null" || templateCopy === "undefined") {
|
|
24
24
|
throw new error_1.FirebaseError(`Invalid Remote Config template: ${JSON.stringify(templateCopy)}`);
|
|
25
25
|
}
|
|
26
|
-
if (typeof templateCopy.etag !== "string" || templateCopy.etag
|
|
26
|
+
if (typeof templateCopy.etag !== "string" || templateCopy.etag === "") {
|
|
27
27
|
throw new error_1.FirebaseError("ETag must be a non-empty string");
|
|
28
28
|
}
|
|
29
29
|
if (templateCopy.conditions && !Array.isArray(templateCopy.conditions)) {
|
|
@@ -35,7 +35,7 @@ class AuthCloudFunction {
|
|
|
35
35
|
catch (e) {
|
|
36
36
|
err = e;
|
|
37
37
|
}
|
|
38
|
-
if (err || (res === null || res === void 0 ? void 0 : res.status)
|
|
38
|
+
if (err || (res === null || res === void 0 ? void 0 : res.status) !== 200) {
|
|
39
39
|
this.logger.logLabeled("WARN", "functions", `Firebase Authentication function was not triggered due to emulation error. Please file a bug.`);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -1378,7 +1378,7 @@ function mfaSignInFinalize(state, reqBody) {
|
|
|
1378
1378
|
(0, errors_1.assert)(sessionInfo, "MISSING_SESSION_INFO");
|
|
1379
1379
|
const phoneNumber = verifyPhoneNumber(state, sessionInfo, code);
|
|
1380
1380
|
let { user, signInProvider } = parsePendingCredential(state, reqBody.mfaPendingCredential);
|
|
1381
|
-
const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => enrollment.unobfuscatedPhoneInfo
|
|
1381
|
+
const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber);
|
|
1382
1382
|
(0, errors_1.assert)(enrollment && enrollment.mfaEnrollmentId, "MFA_ENROLLMENT_NOT_FOUND");
|
|
1383
1383
|
user = state.updateUserByLocalId(user.localId, { lastLoginAt: Date.now().toString() });
|
|
1384
1384
|
(0, errors_1.assert)(!user.disabled, "USER_DISABLED");
|
|
@@ -8,6 +8,7 @@ const DEFAULT_PORTS = {
|
|
|
8
8
|
logging: 4500,
|
|
9
9
|
hosting: 5000,
|
|
10
10
|
functions: 5001,
|
|
11
|
+
extensions: 5001,
|
|
11
12
|
firestore: 8080,
|
|
12
13
|
pubsub: 8085,
|
|
13
14
|
database: 9000,
|
|
@@ -25,6 +26,7 @@ exports.FIND_AVAILBLE_PORT_BY_DEFAULT = {
|
|
|
25
26
|
pubsub: false,
|
|
26
27
|
auth: false,
|
|
27
28
|
storage: false,
|
|
29
|
+
extensions: false,
|
|
28
30
|
};
|
|
29
31
|
exports.EMULATOR_DESCRIPTION = {
|
|
30
32
|
ui: "Emulator UI",
|
|
@@ -37,6 +39,7 @@ exports.EMULATOR_DESCRIPTION = {
|
|
|
37
39
|
pubsub: "Pub/Sub Emulator",
|
|
38
40
|
auth: "Authentication Emulator",
|
|
39
41
|
storage: "Storage Emulator",
|
|
42
|
+
extensions: "Extensions Emulator",
|
|
40
43
|
};
|
|
41
44
|
const DEFAULT_HOST = "localhost";
|
|
42
45
|
class Constants {
|
|
@@ -87,6 +90,7 @@ class Constants {
|
|
|
87
90
|
}
|
|
88
91
|
exports.Constants = Constants;
|
|
89
92
|
Constants.FAKE_PROJECT_ID_PREFIX = "demo-";
|
|
93
|
+
Constants.FAKE_PROJECT_NUMBER = "0";
|
|
90
94
|
Constants.DEFAULT_DATABASE_EMULATOR_NAMESPACE = "fake-server";
|
|
91
95
|
Constants.FIRESTORE_EMULATOR_HOST = "FIRESTORE_EMULATOR_HOST";
|
|
92
96
|
Constants.FIREBASE_DATABASE_EMULATOR_HOST = "FIREBASE_DATABASE_EMULATOR_HOST";
|