firebase-tools 11.2.1 → 11.4.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 +5 -0
- package/lib/checkValidTargetFilters.js +3 -2
- package/lib/commands/ext-export.js +2 -0
- package/lib/database/rulesConfig.js +35 -8
- package/lib/deploy/extensions/planner.js +1 -0
- package/lib/deploy/extensions/prepare.js +18 -1
- package/lib/deploy/extensions/release.js +4 -0
- package/lib/deploy/functions/build.js +43 -52
- package/lib/deploy/functions/params.js +189 -0
- package/lib/deploy/functions/prepare.js +1 -1
- package/lib/deploy/functions/release/index.js +4 -0
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +3 -0
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +1 -1
- package/lib/deploy/hosting/convertConfig.js +76 -16
- package/lib/deploy/index.js +1 -1
- package/lib/deploy/lifecycleHooks.js +1 -1
- package/lib/deploy/storage/prepare.js +29 -6
- package/lib/downloadUtils.js +1 -1
- package/lib/emulator/controller.js +0 -1
- package/lib/emulator/downloadableEmulators.js +9 -9
- package/lib/emulator/functionsEmulator.js +9 -1
- package/lib/emulator/functionsEmulatorRuntime.js +68 -58
- package/lib/emulator/functionsRuntimeWorker.js +35 -9
- package/lib/emulator/storage/apis/gcloud.js +1 -1
- package/lib/extensions/etags.js +28 -0
- package/lib/extensions/warnings.js +7 -1
- package/lib/functions/env.js +51 -2
- package/lib/functionsConfig.js +1 -1
- package/lib/gcp/iam.js +20 -17
- package/lib/gcp/rules.js +2 -3
- package/lib/gcp/runtimeconfig.js +1 -1
- package/lib/hosting/api.js +9 -11
- package/lib/hosting/expireUtils.js +2 -2
- package/lib/management/projects.js +6 -7
- package/lib/profileReport.js +16 -14
- package/lib/rc.js +14 -10
- package/lib/requireConfig.js +6 -6
- package/lib/rulesDeploy.js +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -1
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.convertConfig = void 0;
|
|
4
4
|
const error_1 = require("../../error");
|
|
5
5
|
const backend_1 = require("../functions/backend");
|
|
6
|
+
const backend = require("../functions/backend");
|
|
6
7
|
function has(obj, k) {
|
|
7
8
|
return obj[k] !== undefined;
|
|
8
9
|
}
|
|
@@ -39,24 +40,66 @@ async function convertConfig(context, payload, config, finalize) {
|
|
|
39
40
|
if (!config) {
|
|
40
41
|
return out;
|
|
41
42
|
}
|
|
42
|
-
const
|
|
43
|
-
|
|
43
|
+
const endpointFromBackend = (targetBackend, functionsEndpointInfo) => {
|
|
44
|
+
const backendsForId = backend.allEndpoints(targetBackend).filter((endpoint) => {
|
|
45
|
+
return endpoint.id === functionsEndpointInfo.serviceId;
|
|
46
|
+
});
|
|
47
|
+
const matchingBackends = backendsForId.filter((endpoint) => {
|
|
48
|
+
return ((!functionsEndpointInfo.region || endpoint.region === functionsEndpointInfo.region) &&
|
|
49
|
+
(!functionsEndpointInfo.platform || endpoint.platform === functionsEndpointInfo.platform));
|
|
50
|
+
});
|
|
51
|
+
if (matchingBackends.length > 1) {
|
|
52
|
+
throw new error_1.FirebaseError(`More than one backend found for function name: ${functionsEndpointInfo.serviceId}. If the function is deployed in multiple regions, you must specify a region.`);
|
|
53
|
+
}
|
|
54
|
+
if (matchingBackends.length === 1) {
|
|
55
|
+
const endpoint = matchingBackends[0];
|
|
56
|
+
if (endpoint && (0, backend_1.isHttpsTriggered)(endpoint)) {
|
|
57
|
+
return endpoint;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
};
|
|
62
|
+
const endpointBeingDeployed = (functionsEndpointInfo) => {
|
|
44
63
|
for (const { wantBackend } of Object.values(payload.functions || {})) {
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
if (!wantBackend) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const endpoint = endpointFromBackend(wantBackend, functionsEndpointInfo);
|
|
68
|
+
if (endpoint) {
|
|
47
69
|
return endpoint;
|
|
70
|
+
}
|
|
48
71
|
}
|
|
49
|
-
return
|
|
72
|
+
return;
|
|
50
73
|
};
|
|
51
|
-
const matchingEndpoint = async (
|
|
52
|
-
const pendingEndpoint = endpointBeingDeployed(
|
|
74
|
+
const matchingEndpoint = async (functionsEndpointInfo) => {
|
|
75
|
+
const pendingEndpoint = endpointBeingDeployed(functionsEndpointInfo);
|
|
53
76
|
if (pendingEndpoint)
|
|
54
77
|
return pendingEndpoint;
|
|
55
78
|
const backend = await (0, backend_1.existingBackend)(context);
|
|
56
79
|
return (0, backend_1.allEndpoints)(backend).find((it) => (0, backend_1.isHttpsTriggered)(it) &&
|
|
57
|
-
it.
|
|
58
|
-
it.
|
|
59
|
-
it.region === region);
|
|
80
|
+
it.id === functionsEndpointInfo.serviceId &&
|
|
81
|
+
(!functionsEndpointInfo.platform || it.platform === functionsEndpointInfo.platform) &&
|
|
82
|
+
(!functionsEndpointInfo.region || it.region === functionsEndpointInfo.region));
|
|
83
|
+
};
|
|
84
|
+
const findEndpointWithValidRegion = async (rewrite, context) => {
|
|
85
|
+
if ("function" in rewrite) {
|
|
86
|
+
const foundEndpointToBeDeployed = endpointBeingDeployed({
|
|
87
|
+
serviceId: rewrite.function,
|
|
88
|
+
region: rewrite.region,
|
|
89
|
+
});
|
|
90
|
+
if (foundEndpointToBeDeployed) {
|
|
91
|
+
return foundEndpointToBeDeployed;
|
|
92
|
+
}
|
|
93
|
+
const existingBackend = await backend.existingBackend(context);
|
|
94
|
+
const endpointAlreadyDeployed = endpointFromBackend(existingBackend, {
|
|
95
|
+
serviceId: rewrite.function,
|
|
96
|
+
region: rewrite.region,
|
|
97
|
+
});
|
|
98
|
+
if (endpointAlreadyDeployed) {
|
|
99
|
+
return endpointAlreadyDeployed;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
60
103
|
};
|
|
61
104
|
if (Array.isArray(config.rewrites)) {
|
|
62
105
|
out.rewrites = [];
|
|
@@ -66,19 +109,30 @@ async function convertConfig(context, payload, config, finalize) {
|
|
|
66
109
|
vRewrite.path = rewrite.destination;
|
|
67
110
|
}
|
|
68
111
|
else if ("function" in rewrite) {
|
|
69
|
-
if (!finalize &&
|
|
112
|
+
if (!finalize &&
|
|
113
|
+
endpointBeingDeployed({
|
|
114
|
+
serviceId: rewrite.function,
|
|
115
|
+
platform: "gcfv2",
|
|
116
|
+
region: rewrite.region,
|
|
117
|
+
})) {
|
|
70
118
|
continue;
|
|
71
|
-
|
|
119
|
+
}
|
|
120
|
+
const endpoint = await matchingEndpoint({
|
|
121
|
+
serviceId: rewrite.function,
|
|
122
|
+
platform: "gcfv2",
|
|
123
|
+
region: rewrite.region,
|
|
124
|
+
});
|
|
72
125
|
if (endpoint) {
|
|
73
126
|
vRewrite.run = { serviceId: endpoint.id, region: endpoint.region };
|
|
74
127
|
}
|
|
75
128
|
else {
|
|
76
129
|
vRewrite.function = rewrite.function;
|
|
77
|
-
|
|
78
|
-
|
|
130
|
+
const foundEndpoint = await findEndpointWithValidRegion(rewrite, context);
|
|
131
|
+
if (foundEndpoint) {
|
|
132
|
+
vRewrite.functionRegion = foundEndpoint.region;
|
|
79
133
|
}
|
|
80
134
|
else {
|
|
81
|
-
|
|
135
|
+
throw new error_1.FirebaseError(`Unable to find a valid endpoint for function ${vRewrite.function}`);
|
|
82
136
|
}
|
|
83
137
|
}
|
|
84
138
|
}
|
|
@@ -86,8 +140,14 @@ async function convertConfig(context, payload, config, finalize) {
|
|
|
86
140
|
vRewrite.dynamicLinks = rewrite.dynamicLinks;
|
|
87
141
|
}
|
|
88
142
|
else if ("run" in rewrite) {
|
|
89
|
-
if (!finalize &&
|
|
143
|
+
if (!finalize &&
|
|
144
|
+
endpointBeingDeployed({
|
|
145
|
+
serviceId: rewrite.run.serviceId,
|
|
146
|
+
platform: "gcfv2",
|
|
147
|
+
region: rewrite.run.region,
|
|
148
|
+
})) {
|
|
90
149
|
continue;
|
|
150
|
+
}
|
|
91
151
|
vRewrite.run = Object.assign({ region: "us-central1" }, rewrite.run);
|
|
92
152
|
}
|
|
93
153
|
out.rewrites.push(vRewrite);
|
package/lib/deploy/index.js
CHANGED
|
@@ -52,7 +52,7 @@ const deploy = async function (targetNames, options, customContext = {}) {
|
|
|
52
52
|
for (const targetName of targetNames) {
|
|
53
53
|
const target = TARGETS[targetName];
|
|
54
54
|
if (!target) {
|
|
55
|
-
return Promise.reject(new error_1.FirebaseError((0, cli_color_1.bold)(targetName)
|
|
55
|
+
return Promise.reject(new error_1.FirebaseError(`${(0, cli_color_1.bold)(targetName)} is not a valid deploy target`));
|
|
56
56
|
}
|
|
57
57
|
predeploys.push((0, lifecycleHooks_1.lifecycleHooks)(targetName, "predeploy"));
|
|
58
58
|
prepares.push(target.prepare);
|
|
@@ -31,7 +31,7 @@ function runCommand(command, childOptions) {
|
|
|
31
31
|
reject(new Error("Command terminated with signal " + signal));
|
|
32
32
|
}
|
|
33
33
|
else if (code !== 0) {
|
|
34
|
-
reject(new Error("Command terminated with non-zero exit code" + code));
|
|
34
|
+
reject(new Error("Command terminated with non-zero exit code " + code));
|
|
35
35
|
}
|
|
36
36
|
else {
|
|
37
37
|
resolve();
|
|
@@ -3,25 +3,48 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const _ = require("lodash");
|
|
4
4
|
const gcp = require("../../gcp");
|
|
5
5
|
const rulesDeploy_1 = require("../../rulesDeploy");
|
|
6
|
+
const error_1 = require("../../error");
|
|
6
7
|
async function default_1(context, options) {
|
|
7
8
|
let rulesConfig = options.config.get("storage");
|
|
8
9
|
if (!rulesConfig) {
|
|
9
10
|
return;
|
|
10
11
|
}
|
|
12
|
+
const onlyTargets = new Set();
|
|
13
|
+
let allStorage = !options.only;
|
|
14
|
+
if (options.only) {
|
|
15
|
+
const split = options.only.split(",");
|
|
16
|
+
if (split.includes("storage")) {
|
|
17
|
+
allStorage = true;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
for (const value of split) {
|
|
21
|
+
if (value.startsWith("storage:")) {
|
|
22
|
+
onlyTargets.add(value.split(":")[1]);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
11
27
|
_.set(context, "storage.rules", rulesConfig);
|
|
12
28
|
const rulesDeploy = new rulesDeploy_1.RulesDeploy(options, rulesDeploy_1.RulesetServiceType.FIREBASE_STORAGE);
|
|
13
29
|
_.set(context, "storage.rulesDeploy", rulesDeploy);
|
|
14
|
-
if (
|
|
30
|
+
if (!Array.isArray(rulesConfig)) {
|
|
15
31
|
const defaultBucket = await gcp.storage.getDefaultBucket(options.project);
|
|
16
32
|
rulesConfig = [Object.assign(rulesConfig, { bucket: defaultBucket })];
|
|
17
33
|
_.set(context, "storage.rules", rulesConfig);
|
|
18
34
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
35
|
+
for (const ruleConfig of rulesConfig) {
|
|
36
|
+
const target = ruleConfig.target;
|
|
37
|
+
if (target) {
|
|
38
|
+
options.rc.requireTarget(context.projectId, "storage", target);
|
|
22
39
|
}
|
|
23
|
-
|
|
24
|
-
|
|
40
|
+
if (allStorage || onlyTargets.has(target)) {
|
|
41
|
+
rulesDeploy.addFile(ruleConfig.rules);
|
|
42
|
+
onlyTargets.delete(target);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (!allStorage && onlyTargets.size !== 0) {
|
|
46
|
+
throw new error_1.FirebaseError(`Could not find rules for the following storage targets: ${[...onlyTargets].join(", ")}`);
|
|
47
|
+
}
|
|
25
48
|
await rulesDeploy.compile();
|
|
26
49
|
}
|
|
27
50
|
exports.default = default_1;
|
package/lib/downloadUtils.js
CHANGED
|
@@ -20,7 +20,7 @@ async function downloadToTmp(remoteUrl) {
|
|
|
20
20
|
resolveOnHTTPError: true,
|
|
21
21
|
});
|
|
22
22
|
if (res.status !== 200) {
|
|
23
|
-
throw new error_1.FirebaseError(`download failed, status ${res.status}
|
|
23
|
+
throw new error_1.FirebaseError(`download failed, status ${res.status}: ${await res.response.text()}`);
|
|
24
24
|
}
|
|
25
25
|
const total = parseInt(res.response.headers.get("content-length") || "0", 10);
|
|
26
26
|
const totalMb = Math.ceil(total / 1000000);
|
|
@@ -301,7 +301,6 @@ async function startAll(options, showUI = true) {
|
|
|
301
301
|
const extensionsBackends = await extensionEmulator.getExtensionBackends();
|
|
302
302
|
const filteredExtensionsBackends = extensionEmulator.filterUnemulatedTriggers(options, extensionsBackends);
|
|
303
303
|
emulatableBackends.push(...filteredExtensionsBackends);
|
|
304
|
-
void (0, track_1.track)("Emulator Run", types_1.Emulators.EXTENSIONS);
|
|
305
304
|
await startEmulator(extensionEmulator);
|
|
306
305
|
}
|
|
307
306
|
if (emulatableBackends.length) {
|
|
@@ -55,7 +55,7 @@ exports.DownloadDetails = {
|
|
|
55
55
|
version: "SNAPSHOT",
|
|
56
56
|
downloadPath: path.join(CACHE_DIR, "ui-vSNAPSHOT.zip"),
|
|
57
57
|
unzipDir: path.join(CACHE_DIR, "ui-vSNAPSHOT"),
|
|
58
|
-
binaryPath: path.join(CACHE_DIR, "ui-vSNAPSHOT", "server.
|
|
58
|
+
binaryPath: path.join(CACHE_DIR, "ui-vSNAPSHOT", "server", "server.js"),
|
|
59
59
|
opts: {
|
|
60
60
|
cacheDir: CACHE_DIR,
|
|
61
61
|
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-vSNAPSHOT.zip",
|
|
@@ -67,15 +67,15 @@ exports.DownloadDetails = {
|
|
|
67
67
|
},
|
|
68
68
|
}
|
|
69
69
|
: {
|
|
70
|
-
version: "1.
|
|
71
|
-
downloadPath: path.join(CACHE_DIR, "ui-v1.
|
|
72
|
-
unzipDir: path.join(CACHE_DIR, "ui-v1.
|
|
73
|
-
binaryPath: path.join(CACHE_DIR, "ui-v1.
|
|
70
|
+
version: "1.8.1",
|
|
71
|
+
downloadPath: path.join(CACHE_DIR, "ui-v1.8.1.zip"),
|
|
72
|
+
unzipDir: path.join(CACHE_DIR, "ui-v1.8.1"),
|
|
73
|
+
binaryPath: path.join(CACHE_DIR, "ui-v1.8.1", "server", "server.js"),
|
|
74
74
|
opts: {
|
|
75
75
|
cacheDir: CACHE_DIR,
|
|
76
|
-
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.
|
|
77
|
-
expectedSize:
|
|
78
|
-
expectedChecksum: "
|
|
76
|
+
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.8.1.zip",
|
|
77
|
+
expectedSize: 3056552,
|
|
78
|
+
expectedChecksum: "92590fdda20f9883588438d9551111b5",
|
|
79
79
|
namePrefix: "ui",
|
|
80
80
|
},
|
|
81
81
|
},
|
|
@@ -164,7 +164,7 @@ const Commands = {
|
|
|
164
164
|
},
|
|
165
165
|
ui: {
|
|
166
166
|
binary: "node",
|
|
167
|
-
args: [
|
|
167
|
+
args: [getExecPath(types_1.Emulators.UI)],
|
|
168
168
|
optionalArgs: [],
|
|
169
169
|
joinArgs: false,
|
|
170
170
|
},
|
|
@@ -272,8 +272,13 @@ class FunctionsEmulator {
|
|
|
272
272
|
await runtimeDelegate.build();
|
|
273
273
|
logger_1.logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
|
|
274
274
|
const environment = Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), emulatableBackend.env);
|
|
275
|
+
const userEnvOpt = {
|
|
276
|
+
functionsSource: emulatableBackend.functionsDir,
|
|
277
|
+
projectId: this.args.projectId,
|
|
278
|
+
projectAlias: this.args.projectAlias,
|
|
279
|
+
};
|
|
275
280
|
const discoveredBuild = await runtimeDelegate.discoverBuild(runtimeConfig, environment);
|
|
276
|
-
const discoveredBackend = (0, build_1.resolveBackend)(discoveredBuild, environment);
|
|
281
|
+
const discoveredBackend = await (0, build_1.resolveBackend)(discoveredBuild, userEnvOpt, environment);
|
|
277
282
|
const endpoints = backend.allEndpoints(discoveredBackend);
|
|
278
283
|
(0, functionsEmulatorShared_1.prepareEndpoints)(endpoints);
|
|
279
284
|
for (const e of endpoints) {
|
|
@@ -641,6 +646,9 @@ class FunctionsEmulator {
|
|
|
641
646
|
envs.GCLOUD_PROJECT = this.args.projectId;
|
|
642
647
|
envs.K_REVISION = "1";
|
|
643
648
|
envs.PORT = "80";
|
|
649
|
+
if (trigger === null || trigger === void 0 ? void 0 : trigger.timeoutSeconds) {
|
|
650
|
+
envs.FUNCTIONS_EMULATOR_TIMEOUT_SECONDS = trigger.timeoutSeconds.toString();
|
|
651
|
+
}
|
|
644
652
|
if (trigger) {
|
|
645
653
|
const target = trigger.entryPoint;
|
|
646
654
|
envs.FUNCTION_TARGET = target;
|
|
@@ -104,7 +104,7 @@ class Proxied {
|
|
|
104
104
|
return this.proxy;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
async function resolveDeveloperNodeModule(
|
|
107
|
+
async function resolveDeveloperNodeModule(name) {
|
|
108
108
|
const pkg = requirePackageJson();
|
|
109
109
|
if (!pkg) {
|
|
110
110
|
new types_1.EmulatorLog("SYSTEM", "missing-package-json", "").log();
|
|
@@ -130,20 +130,20 @@ async function resolveDeveloperNodeModule(frb, name) {
|
|
|
130
130
|
logDebug(`Resolved module ${name}`, moduleResolution);
|
|
131
131
|
return moduleResolution;
|
|
132
132
|
}
|
|
133
|
-
async function assertResolveDeveloperNodeModule(
|
|
134
|
-
const resolution = await resolveDeveloperNodeModule(
|
|
133
|
+
async function assertResolveDeveloperNodeModule(name) {
|
|
134
|
+
const resolution = await resolveDeveloperNodeModule(name);
|
|
135
135
|
if (!(resolution.installed && resolution.declared && resolution.resolution && resolution.version)) {
|
|
136
136
|
throw new Error(`Assertion failure: could not fully resolve ${name}: ${JSON.stringify(resolution)}`);
|
|
137
137
|
}
|
|
138
138
|
return resolution;
|
|
139
139
|
}
|
|
140
|
-
async function verifyDeveloperNodeModules(
|
|
140
|
+
async function verifyDeveloperNodeModules() {
|
|
141
141
|
const modBundles = [
|
|
142
142
|
{ name: "firebase-admin", isDev: false, minVersion: "8.9.0" },
|
|
143
143
|
{ name: "firebase-functions", isDev: false, minVersion: "3.13.1" },
|
|
144
144
|
];
|
|
145
145
|
for (const modBundle of modBundles) {
|
|
146
|
-
const resolution = await resolveDeveloperNodeModule(
|
|
146
|
+
const resolution = await resolveDeveloperNodeModule(modBundle.name);
|
|
147
147
|
if (!resolution.declared) {
|
|
148
148
|
new types_1.EmulatorLog("SYSTEM", "missing-module", "", modBundle).log();
|
|
149
149
|
return false;
|
|
@@ -240,8 +240,8 @@ function initializeNetworkFiltering() {
|
|
|
240
240
|
});
|
|
241
241
|
logDebug("Outgoing network have been stubbed.", results);
|
|
242
242
|
}
|
|
243
|
-
async function initializeFirebaseFunctionsStubs(
|
|
244
|
-
const firebaseFunctionsResolution = await assertResolveDeveloperNodeModule(
|
|
243
|
+
async function initializeFirebaseFunctionsStubs() {
|
|
244
|
+
const firebaseFunctionsResolution = await assertResolveDeveloperNodeModule("firebase-functions");
|
|
245
245
|
const firebaseFunctionsRoot = (0, functionsEmulatorShared_1.findModuleRoot)("firebase-functions", firebaseFunctionsResolution.resolution);
|
|
246
246
|
const httpsProviderResolution = path.join(firebaseFunctionsRoot, "lib/providers/https");
|
|
247
247
|
const httpsProviderV1Resolution = path.join(firebaseFunctionsRoot, "lib/v1/providers/https");
|
|
@@ -343,10 +343,10 @@ function initializeRuntimeConfig() {
|
|
|
343
343
|
}
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
|
-
async function initializeFirebaseAdminStubs(
|
|
347
|
-
const adminResolution = await assertResolveDeveloperNodeModule(
|
|
346
|
+
async function initializeFirebaseAdminStubs() {
|
|
347
|
+
const adminResolution = await assertResolveDeveloperNodeModule("firebase-admin");
|
|
348
348
|
const localAdminModule = require(adminResolution.resolution);
|
|
349
|
-
const functionsResolution = await assertResolveDeveloperNodeModule(
|
|
349
|
+
const functionsResolution = await assertResolveDeveloperNodeModule("firebase-functions");
|
|
350
350
|
const localFunctionsModule = require(functionsResolution.resolution);
|
|
351
351
|
const defaultConfig = getDefaultConfig();
|
|
352
352
|
const adminModuleProxy = new Proxied(localAdminModule);
|
|
@@ -360,7 +360,7 @@ async function initializeFirebaseAdminStubs(frb) {
|
|
|
360
360
|
new types_1.EmulatorLog("SYSTEM", "default-admin-app-used", `config=${defaultAppOptions}`, {
|
|
361
361
|
opts: defaultAppOptions,
|
|
362
362
|
}).log();
|
|
363
|
-
const defaultApp = makeProxiedFirebaseApp(
|
|
363
|
+
const defaultApp = makeProxiedFirebaseApp(adminModuleTarget.initializeApp(defaultAppOptions));
|
|
364
364
|
logDebug("initializeApp(DEFAULT)", defaultAppOptions);
|
|
365
365
|
localFunctionsModule.app.setEmulatedAdminApp(defaultApp);
|
|
366
366
|
if (process.env[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST]) {
|
|
@@ -405,7 +405,7 @@ async function initializeFirebaseAdminStubs(frb) {
|
|
|
405
405
|
adminResolution,
|
|
406
406
|
});
|
|
407
407
|
}
|
|
408
|
-
function makeProxiedFirebaseApp(
|
|
408
|
+
function makeProxiedFirebaseApp(original) {
|
|
409
409
|
const appProxy = new Proxied(original);
|
|
410
410
|
return appProxy
|
|
411
411
|
.when("firestore", (target) => {
|
|
@@ -450,8 +450,8 @@ function warnAboutStorageProd() {
|
|
|
450
450
|
}
|
|
451
451
|
new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Firebase Storage emulator is not running, so calls to Firebase Storage will affect production.").log();
|
|
452
452
|
}
|
|
453
|
-
async function initializeFunctionsConfigHelper(
|
|
454
|
-
const functionsResolution = await assertResolveDeveloperNodeModule(
|
|
453
|
+
async function initializeFunctionsConfigHelper() {
|
|
454
|
+
const functionsResolution = await assertResolveDeveloperNodeModule("firebase-functions");
|
|
455
455
|
const localFunctionsModule = require(functionsResolution.resolution);
|
|
456
456
|
logDebug("Checked functions.config()", {
|
|
457
457
|
config: localFunctionsModule.config(),
|
|
@@ -487,27 +487,7 @@ function rawBodySaver(req, res, buf) {
|
|
|
487
487
|
}
|
|
488
488
|
async function processHTTPS(trigger) {
|
|
489
489
|
const ephemeralServer = express();
|
|
490
|
-
const functionRouter = express.Router();
|
|
491
490
|
await new Promise((resolveEphemeralServer, rejectEphemeralServer) => {
|
|
492
|
-
const handler = async (req, res) => {
|
|
493
|
-
try {
|
|
494
|
-
logDebug(`Ephemeral server handling ${req.method} request`);
|
|
495
|
-
res.on("finish", () => {
|
|
496
|
-
instance.close((err) => {
|
|
497
|
-
if (err) {
|
|
498
|
-
rejectEphemeralServer(err);
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
resolveEphemeralServer();
|
|
502
|
-
}
|
|
503
|
-
});
|
|
504
|
-
});
|
|
505
|
-
await runHTTPS(trigger, [req, res]);
|
|
506
|
-
}
|
|
507
|
-
catch (err) {
|
|
508
|
-
rejectEphemeralServer(err);
|
|
509
|
-
}
|
|
510
|
-
};
|
|
511
491
|
ephemeralServer.enable("trust proxy");
|
|
512
492
|
ephemeralServer.use(bodyParser.json({
|
|
513
493
|
limit: "10mb",
|
|
@@ -527,14 +507,39 @@ async function processHTTPS(trigger) {
|
|
|
527
507
|
limit: "10mb",
|
|
528
508
|
verify: rawBodySaver,
|
|
529
509
|
}));
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
510
|
+
let server;
|
|
511
|
+
function closeServer() {
|
|
512
|
+
if (server) {
|
|
513
|
+
server.close((err) => {
|
|
514
|
+
if (err) {
|
|
515
|
+
rejectEphemeralServer(err);
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
resolveEphemeralServer();
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
ephemeralServer.get("/__/health", (req, res) => {
|
|
524
|
+
res.status(200).send();
|
|
536
525
|
});
|
|
537
|
-
|
|
526
|
+
ephemeralServer.all("/favicon.ico|/robots.txt", (req, res) => {
|
|
527
|
+
res.on("finish", closeServer);
|
|
528
|
+
res.status(404).send();
|
|
529
|
+
});
|
|
530
|
+
ephemeralServer.all(`/*`, async (req, res) => {
|
|
531
|
+
try {
|
|
532
|
+
logDebug(`Ephemeral server handling ${req.method} request`);
|
|
533
|
+
res.on("finish", closeServer);
|
|
534
|
+
await runHTTPS(trigger, [req, res]);
|
|
535
|
+
}
|
|
536
|
+
catch (err) {
|
|
537
|
+
rejectEphemeralServer(err);
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
server = ephemeralServer.listen(process.env.PORT);
|
|
541
|
+
logDebug(`Listening to port: ${process.env.PORT}`);
|
|
542
|
+
server.on("error", rejectEphemeralServer);
|
|
538
543
|
});
|
|
539
544
|
}
|
|
540
545
|
async function processBackground(trigger, frb, signature) {
|
|
@@ -587,7 +592,7 @@ async function runHTTPS(trigger, args) {
|
|
|
587
592
|
return trigger(args[0], args[1]);
|
|
588
593
|
});
|
|
589
594
|
}
|
|
590
|
-
async function moduleResolutionDetective(
|
|
595
|
+
async function moduleResolutionDetective(error) {
|
|
591
596
|
const clues = {
|
|
592
597
|
tsconfigJSON: await requireAsync("./tsconfig.json", { paths: [process.cwd()] }).catch(noOp),
|
|
593
598
|
packageJSON: await requireAsync("./package.json", { paths: [process.cwd()] }).catch(noOp),
|
|
@@ -645,7 +650,7 @@ async function invokeTrigger(trigger, frb) {
|
|
|
645
650
|
clearInterval(timerId);
|
|
646
651
|
new types_1.EmulatorLog("INFO", "runtime-status", `Finished "${FUNCTION_TARGET_NAME}" in ~${Math.max(seconds, 1)}s`).log();
|
|
647
652
|
}
|
|
648
|
-
async function initializeRuntime(
|
|
653
|
+
async function initializeRuntime() {
|
|
649
654
|
FUNCTION_DEBUG_MODE = process.env.FUNCTION_DEBUG_MODE || "";
|
|
650
655
|
if (!FUNCTION_DEBUG_MODE) {
|
|
651
656
|
FUNCTION_TARGET_NAME = process.env.FUNCTION_TARGET || "";
|
|
@@ -659,19 +664,18 @@ async function initializeRuntime(frb) {
|
|
|
659
664
|
await flushAndExit(1);
|
|
660
665
|
}
|
|
661
666
|
}
|
|
662
|
-
|
|
663
|
-
const verified = await verifyDeveloperNodeModules(frb);
|
|
667
|
+
const verified = await verifyDeveloperNodeModules();
|
|
664
668
|
if (!verified) {
|
|
665
669
|
new types_1.EmulatorLog("INFO", "runtime-status", `Your functions could not be parsed due to an issue with your node_modules (see above)`).log();
|
|
666
670
|
return;
|
|
667
671
|
}
|
|
668
672
|
initializeRuntimeConfig();
|
|
669
673
|
initializeNetworkFiltering();
|
|
670
|
-
await initializeFunctionsConfigHelper(
|
|
671
|
-
await initializeFirebaseFunctionsStubs(
|
|
672
|
-
await initializeFirebaseAdminStubs(
|
|
674
|
+
await initializeFunctionsConfigHelper();
|
|
675
|
+
await initializeFirebaseFunctionsStubs();
|
|
676
|
+
await initializeFirebaseAdminStubs();
|
|
673
677
|
}
|
|
674
|
-
async function loadTriggers(
|
|
678
|
+
async function loadTriggers(serializedFunctionTrigger) {
|
|
675
679
|
let triggerModule;
|
|
676
680
|
if (serializedFunctionTrigger) {
|
|
677
681
|
triggerModule = eval(serializedFunctionTrigger)();
|
|
@@ -682,7 +686,7 @@ async function loadTriggers(frb, serializedFunctionTrigger) {
|
|
|
682
686
|
}
|
|
683
687
|
catch (err) {
|
|
684
688
|
if (err.code !== "ERR_REQUIRE_ESM") {
|
|
685
|
-
await moduleResolutionDetective(
|
|
689
|
+
await moduleResolutionDetective(err);
|
|
686
690
|
throw err;
|
|
687
691
|
}
|
|
688
692
|
const modulePath = require.resolve(process.cwd());
|
|
@@ -712,9 +716,8 @@ async function handleMessage(message) {
|
|
|
712
716
|
}
|
|
713
717
|
if (!functionModule) {
|
|
714
718
|
try {
|
|
715
|
-
await initializeRuntime(runtimeArgs.frb);
|
|
716
719
|
const serializedTriggers = runtimeArgs.opts ? runtimeArgs.opts.serializedTriggers : undefined;
|
|
717
|
-
functionModule = await loadTriggers(
|
|
720
|
+
functionModule = await loadTriggers(serializedTriggers);
|
|
718
721
|
}
|
|
719
722
|
catch (e) {
|
|
720
723
|
logDebug(e);
|
|
@@ -748,7 +751,7 @@ async function handleMessage(message) {
|
|
|
748
751
|
await flushAndExit(1);
|
|
749
752
|
}
|
|
750
753
|
}
|
|
751
|
-
function main() {
|
|
754
|
+
async function main() {
|
|
752
755
|
let lastSignal = new Date().getTime();
|
|
753
756
|
let signalCount = 0;
|
|
754
757
|
process.on("SIGINT", () => {
|
|
@@ -762,10 +765,7 @@ function main() {
|
|
|
762
765
|
process.exit(1);
|
|
763
766
|
}
|
|
764
767
|
});
|
|
765
|
-
|
|
766
|
-
cwd: process.cwd(),
|
|
767
|
-
node_version: process.versions.node,
|
|
768
|
-
});
|
|
768
|
+
await initializeRuntime();
|
|
769
769
|
let messageHandlePromise = Promise.resolve();
|
|
770
770
|
process.on("message", (message) => {
|
|
771
771
|
messageHandlePromise = messageHandlePromise
|
|
@@ -780,5 +780,15 @@ function main() {
|
|
|
780
780
|
});
|
|
781
781
|
}
|
|
782
782
|
if (require.main === module) {
|
|
783
|
-
main()
|
|
783
|
+
main()
|
|
784
|
+
.then(() => {
|
|
785
|
+
logDebug("Functions runtime initialized.", {
|
|
786
|
+
cwd: process.cwd(),
|
|
787
|
+
node_version: process.versions.node,
|
|
788
|
+
});
|
|
789
|
+
})
|
|
790
|
+
.catch((err) => {
|
|
791
|
+
new types_1.EmulatorLog("FATAL", "runtime-error", err.message || err, err).log();
|
|
792
|
+
return flushAndExit(1);
|
|
793
|
+
});
|
|
784
794
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RuntimeWorkerPool = exports.RuntimeWorker = exports.RuntimeWorkerState = void 0;
|
|
4
|
+
const http = require("http");
|
|
4
5
|
const uuid = require("uuid");
|
|
5
6
|
const types_1 = require("./types");
|
|
6
7
|
const events_1 = require("events");
|
|
@@ -49,17 +50,11 @@ class RuntimeWorker {
|
|
|
49
50
|
return this._state;
|
|
50
51
|
}
|
|
51
52
|
set state(state) {
|
|
52
|
-
if (state === RuntimeWorkerState.BUSY) {
|
|
53
|
-
this.socketReady = types_1.EmulatorLog.waitForLog(this.runtime.events, "SYSTEM", "runtime-status", (el) => {
|
|
54
|
-
return el.data.state === "ready";
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
53
|
if (state === RuntimeWorkerState.IDLE) {
|
|
58
54
|
for (const l of this.logListeners) {
|
|
59
55
|
this.runtime.events.removeListener("log", l);
|
|
60
56
|
}
|
|
61
57
|
this.logListeners = [];
|
|
62
|
-
this.socketReady = undefined;
|
|
63
58
|
}
|
|
64
59
|
if (state === RuntimeWorkerState.FINISHED) {
|
|
65
60
|
this.runtime.events.removeAllListeners();
|
|
@@ -88,9 +83,40 @@ class RuntimeWorker {
|
|
|
88
83
|
this.stateEvents.once(RuntimeWorkerState.FINISHED, listener);
|
|
89
84
|
});
|
|
90
85
|
}
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
|
|
86
|
+
isSocketReady() {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
const req = http
|
|
89
|
+
.request({
|
|
90
|
+
method: "GET",
|
|
91
|
+
path: "/__/health",
|
|
92
|
+
socketPath: this.runtime.socketPath,
|
|
93
|
+
}, () => resolve())
|
|
94
|
+
.end();
|
|
95
|
+
req.on("error", (error) => {
|
|
96
|
+
reject(error);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async waitForSocketReady() {
|
|
101
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
102
|
+
const timeout = new Promise((resolve, reject) => {
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
reject(new error_1.FirebaseError("Failed to load function."));
|
|
105
|
+
}, 7000);
|
|
106
|
+
});
|
|
107
|
+
while (true) {
|
|
108
|
+
try {
|
|
109
|
+
await Promise.race([this.isSocketReady(), timeout]);
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
if (["ECONNREFUSED", "ENOENT"].includes(err === null || err === void 0 ? void 0 : err.code)) {
|
|
114
|
+
await sleep(100);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
94
120
|
}
|
|
95
121
|
log(msg) {
|
|
96
122
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).log("DEBUG", `[worker-${this.key}-${this.id}]: ${msg}`);
|
|
@@ -324,7 +324,7 @@ function sendFileBytes(md, data, req, res) {
|
|
|
324
324
|
res.setHeader("Accept-Ranges", "bytes");
|
|
325
325
|
res.setHeader("Content-Type", md.contentType);
|
|
326
326
|
res.setHeader("Content-Disposition", md.contentDisposition);
|
|
327
|
-
res.setHeader("Content-Encoding", md.contentEncoding);
|
|
327
|
+
res.setHeader("Content-Encoding", isGZipped ? "identity" : md.contentEncoding);
|
|
328
328
|
res.setHeader("ETag", md.etag);
|
|
329
329
|
res.setHeader("Cache-Control", md.cacheControl);
|
|
330
330
|
res.setHeader("x-goog-generation", `${md.generation}`);
|