firebase-tools 11.20.0 → 11.22.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/bin/firebase.js +1 -1
- package/lib/commands/appdistribution-distribute.js +3 -0
- package/lib/commands/ext-configure.js +2 -1
- package/lib/commands/ext-install.js +2 -2
- package/lib/deploy/extensions/planner.js +16 -9
- package/lib/deploy/extensions/tasks.js +6 -0
- package/lib/deploy/functions/prepareFunctionsUpload.js +13 -2
- package/lib/deploy/functions/runtimes/discovery/index.js +1 -1
- package/lib/deploy/functions/runtimes/index.js +5 -3
- package/lib/deploy/functions/runtimes/node/index.js +71 -26
- package/lib/deploy/functions/runtimes/node/versioning.js +4 -2
- package/lib/deploy/functions/runtimes/python/index.js +119 -0
- package/lib/emulator/auth/apiSpec.js +4 -0
- package/lib/emulator/auth/operations.js +3 -3
- package/lib/emulator/controller.js +5 -5
- package/lib/emulator/downloadableEmulators.js +3 -3
- package/lib/emulator/extensionsEmulator.js +3 -2
- package/lib/emulator/functionsEmulator.js +107 -72
- package/lib/emulator/functionsRuntimeWorker.js +4 -14
- package/lib/emulator/storage/apis/firebase.js +1 -0
- package/lib/emulator/storage/rules/manager.js +1 -1
- package/lib/emulator/storage/server.js +1 -1
- package/lib/emulator/storage/upload.js +3 -3
- package/lib/extensions/askUserForParam.js +32 -1
- package/lib/extensions/emulator/optionsHelper.js +3 -3
- package/lib/extensions/emulator/specHelper.js +17 -16
- package/lib/extensions/extensionsApi.js +16 -3
- package/lib/extensions/paramHelper.js +6 -1
- package/lib/firestore/checkDatabaseType.js +3 -3
- package/lib/frameworks/index.js +9 -8
- package/lib/frameworks/next/index.js +6 -4
- package/lib/frameworks/nuxt/index.js +18 -26
- package/lib/frameworks/nuxt/interfaces.js +2 -0
- package/lib/frameworks/nuxt/utils.js +13 -0
- package/lib/frameworks/nuxt2/index.js +91 -0
- package/lib/functional.js +10 -3
- package/lib/functions/env.js +1 -1
- package/lib/functions/python.js +21 -0
- package/lib/init/features/firestore/index.js +1 -3
- package/lib/serve/functions.js +1 -3
- package/npm-shrinkwrap.json +1805 -1171
- package/package.json +3 -3
- package/lib/deploy/functions/runtimes/golang/gomod.js +0 -74
- package/lib/deploy/functions/runtimes/golang/index.js +0 -106
- package/lib/init/features/functions/golang.js +0 -57
- package/templates/init/functions/golang/_gitignore +0 -12
- package/templates/init/functions/golang/functions.go +0 -38
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FunctionsEmulator = void 0;
|
|
3
|
+
exports.FunctionsEmulator = exports.TCPConn = exports.IPCConn = void 0;
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const express = require("express");
|
|
@@ -8,6 +8,7 @@ const clc = require("colorette");
|
|
|
8
8
|
const http = require("http");
|
|
9
9
|
const jwt = require("jsonwebtoken");
|
|
10
10
|
const cors = require("cors");
|
|
11
|
+
const semver = require("semver");
|
|
11
12
|
const url_1 = require("url");
|
|
12
13
|
const events_1 = require("events");
|
|
13
14
|
const logger_1 = require("../logger");
|
|
@@ -15,6 +16,7 @@ const track_1 = require("../track");
|
|
|
15
16
|
const constants_1 = require("./constants");
|
|
16
17
|
const types_1 = require("./types");
|
|
17
18
|
const chokidar = require("chokidar");
|
|
19
|
+
const portfinder = require("portfinder");
|
|
18
20
|
const spawn = require("cross-spawn");
|
|
19
21
|
const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
|
|
20
22
|
const registry_1 = require("./registry");
|
|
@@ -33,9 +35,34 @@ const functionsEnv = require("../functions/env");
|
|
|
33
35
|
const v1_1 = require("../functions/events/v1");
|
|
34
36
|
const build_1 = require("../deploy/functions/build");
|
|
35
37
|
const env_1 = require("./env");
|
|
38
|
+
const python_1 = require("../functions/python");
|
|
36
39
|
const EVENT_INVOKE = "functions:invoke";
|
|
37
40
|
const EVENT_INVOKE_GA4 = "functions_invoke";
|
|
38
41
|
const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
|
|
42
|
+
class IPCConn {
|
|
43
|
+
constructor(socketPath) {
|
|
44
|
+
this.socketPath = socketPath;
|
|
45
|
+
}
|
|
46
|
+
httpReqOpts() {
|
|
47
|
+
return {
|
|
48
|
+
socketPath: this.socketPath,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.IPCConn = IPCConn;
|
|
53
|
+
class TCPConn {
|
|
54
|
+
constructor(host, port) {
|
|
55
|
+
this.host = host;
|
|
56
|
+
this.port = port;
|
|
57
|
+
}
|
|
58
|
+
httpReqOpts() {
|
|
59
|
+
return {
|
|
60
|
+
host: this.host,
|
|
61
|
+
port: this.port,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.TCPConn = TCPConn;
|
|
39
66
|
class FunctionsEmulator {
|
|
40
67
|
constructor(args) {
|
|
41
68
|
this.args = args;
|
|
@@ -168,7 +195,13 @@ class FunctionsEmulator {
|
|
|
168
195
|
const record = this.getTriggerRecordByKey(this.getTriggerKey(trigger));
|
|
169
196
|
const pool = this.workerPools[record.backend.codebase];
|
|
170
197
|
if (!pool.readyForWork(trigger.id)) {
|
|
171
|
-
|
|
198
|
+
try {
|
|
199
|
+
await this.startRuntime(record.backend, trigger);
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
202
|
+
this.logger.logLabeled("ERROR", `Failed to start runtime for ${trigger.id}: ${e}`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
172
205
|
}
|
|
173
206
|
const worker = pool.getIdleWorker(trigger.id);
|
|
174
207
|
const reqBody = JSON.stringify(body);
|
|
@@ -177,20 +210,13 @@ class FunctionsEmulator {
|
|
|
177
210
|
"Content-Length": `${reqBody.length}`,
|
|
178
211
|
};
|
|
179
212
|
return new Promise((resolve, reject) => {
|
|
180
|
-
const req = http.request({
|
|
181
|
-
path: `/`,
|
|
182
|
-
socketPath: worker.runtime.socketPath,
|
|
183
|
-
headers: headers,
|
|
184
|
-
}, resolve);
|
|
213
|
+
const req = http.request(Object.assign(Object.assign({}, worker.runtime.conn.httpReqOpts()), { path: `/`, headers: headers }), resolve);
|
|
185
214
|
req.on("error", reject);
|
|
186
215
|
req.write(reqBody);
|
|
187
216
|
req.end();
|
|
188
217
|
});
|
|
189
218
|
}
|
|
190
219
|
async start() {
|
|
191
|
-
for (const backend of this.args.emulatableBackends) {
|
|
192
|
-
backend.nodeBinary = this.getNodeBinary(backend);
|
|
193
|
-
}
|
|
194
220
|
const credentialEnv = await this.getCredentialsEnvironment();
|
|
195
221
|
for (const e of this.args.emulatableBackends) {
|
|
196
222
|
e.env = Object.assign(Object.assign({}, credentialEnv), e.env);
|
|
@@ -219,6 +245,7 @@ class FunctionsEmulator {
|
|
|
219
245
|
/.+?[\\\/]node_modules[\\\/].+?/,
|
|
220
246
|
/(^|[\/\\])\../,
|
|
221
247
|
/.+\.log/,
|
|
248
|
+
/.+?[\\\/]venv[\\\/].+?/,
|
|
222
249
|
],
|
|
223
250
|
persistent: true,
|
|
224
251
|
});
|
|
@@ -257,21 +284,22 @@ class FunctionsEmulator {
|
|
|
257
284
|
projectId: this.args.projectId,
|
|
258
285
|
projectDir: this.args.projectDir,
|
|
259
286
|
sourceDir: emulatableBackend.functionsDir,
|
|
287
|
+
runtime: emulatableBackend.runtime,
|
|
260
288
|
};
|
|
261
|
-
if (emulatableBackend.nodeMajorVersion) {
|
|
262
|
-
runtimeDelegateContext.runtime = `nodejs${emulatableBackend.nodeMajorVersion}`;
|
|
263
|
-
}
|
|
264
289
|
const runtimeDelegate = await runtimes.getRuntimeDelegate(runtimeDelegateContext);
|
|
265
290
|
logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
|
|
266
291
|
await runtimeDelegate.validate();
|
|
267
292
|
logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
|
|
268
293
|
await runtimeDelegate.build();
|
|
294
|
+
emulatableBackend.runtime = runtimeDelegate.runtime;
|
|
295
|
+
emulatableBackend.bin = runtimeDelegate.bin;
|
|
269
296
|
const firebaseConfig = this.getFirebaseConfig();
|
|
270
297
|
const environment = Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: firebaseConfig }), emulatableBackend.env);
|
|
271
298
|
const userEnvOpt = {
|
|
272
299
|
functionsSource: emulatableBackend.functionsDir,
|
|
273
300
|
projectId: this.args.projectId,
|
|
274
301
|
projectAlias: this.args.projectAlias,
|
|
302
|
+
isEmulator: true,
|
|
275
303
|
};
|
|
276
304
|
const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
|
|
277
305
|
const discoveredBuild = await runtimeDelegate.discoverBuild(runtimeConfig, environment);
|
|
@@ -286,9 +314,7 @@ class FunctionsEmulator {
|
|
|
286
314
|
}
|
|
287
315
|
}
|
|
288
316
|
async loadTriggers(emulatableBackend, force = false) {
|
|
289
|
-
|
|
290
|
-
throw new error_1.FirebaseError(`No node binary for ${emulatableBackend.functionsDir}. This should never happen.`);
|
|
291
|
-
}
|
|
317
|
+
var _a;
|
|
292
318
|
let triggerDefinitions = [];
|
|
293
319
|
try {
|
|
294
320
|
triggerDefinitions = await this.discoverTriggers(emulatableBackend);
|
|
@@ -381,13 +407,23 @@ class FunctionsEmulator {
|
|
|
381
407
|
}
|
|
382
408
|
}
|
|
383
409
|
if (this.args.debugPort) {
|
|
384
|
-
emulatableBackend.
|
|
385
|
-
|
|
386
|
-
|
|
410
|
+
if (!((_a = emulatableBackend.bin) === null || _a === void 0 ? void 0 : _a.startsWith("node"))) {
|
|
411
|
+
this.logger.log("WARN", "--inspect-functions only supported for Node.js runtimes.");
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
emulatableBackend.secretEnv = Object.values(triggerDefinitions.reduce((acc, curr) => {
|
|
415
|
+
for (const secret of curr.secretEnvironmentVariables || []) {
|
|
416
|
+
acc[secret.key] = secret;
|
|
417
|
+
}
|
|
418
|
+
return acc;
|
|
419
|
+
}, {}));
|
|
420
|
+
try {
|
|
421
|
+
await this.startRuntime(emulatableBackend);
|
|
387
422
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
423
|
+
catch (e) {
|
|
424
|
+
this.logger.logLabeled("ERROR", `Failed to start functions in ${emulatableBackend.functionsDir}: ${e}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
391
427
|
}
|
|
392
428
|
}
|
|
393
429
|
addEventarcTrigger(projectId, key, eventTrigger) {
|
|
@@ -632,43 +668,6 @@ class FunctionsEmulator {
|
|
|
632
668
|
this.triggers = {};
|
|
633
669
|
triggers.forEach((def) => this.addTriggerRecord(def, { backend, ignored: false }));
|
|
634
670
|
}
|
|
635
|
-
getNodeBinary(backend) {
|
|
636
|
-
const pkg = require(path.join(backend.functionsDir, "package.json"));
|
|
637
|
-
if ((!pkg.engines || !pkg.engines.node) && !backend.nodeMajorVersion) {
|
|
638
|
-
this.logger.log("WARN", `Your functions directory ${backend.functionsDir} does not specify a Node version.\n ` +
|
|
639
|
-
"- Learn more at https://firebase.google.com/docs/functions/manage-functions#set_runtime_options");
|
|
640
|
-
return process.execPath;
|
|
641
|
-
}
|
|
642
|
-
const hostMajorVersion = process.versions.node.split(".")[0];
|
|
643
|
-
const requestedMajorVersion = backend.nodeMajorVersion
|
|
644
|
-
? `${backend.nodeMajorVersion}`
|
|
645
|
-
: pkg.engines.node;
|
|
646
|
-
let localMajorVersion = "0";
|
|
647
|
-
const localNodePath = path.join(backend.functionsDir, "node_modules/.bin/node");
|
|
648
|
-
try {
|
|
649
|
-
const localNodeOutput = spawn.sync(localNodePath, ["--version"]).stdout.toString();
|
|
650
|
-
localMajorVersion = localNodeOutput.slice(1).split(".")[0];
|
|
651
|
-
}
|
|
652
|
-
catch (err) {
|
|
653
|
-
}
|
|
654
|
-
if (requestedMajorVersion === localMajorVersion) {
|
|
655
|
-
this.logger.logLabeled("SUCCESS", "functions", `Using node@${requestedMajorVersion} from local cache.`);
|
|
656
|
-
return localNodePath;
|
|
657
|
-
}
|
|
658
|
-
if (requestedMajorVersion === hostMajorVersion) {
|
|
659
|
-
this.logger.logLabeled("SUCCESS", "functions", `Using node@${requestedMajorVersion} from host.`);
|
|
660
|
-
}
|
|
661
|
-
else {
|
|
662
|
-
if (process.env.FIREPIT_VERSION) {
|
|
663
|
-
this.logger.log("WARN", `You've requested "node" version "${requestedMajorVersion}", but the standalone Firebase CLI comes with bundled Node "${hostMajorVersion}".`);
|
|
664
|
-
this.logger.log("INFO", `To use a different Node.js version, consider removing the standalone Firebase CLI and switching to "firebase-tools" on npm.`);
|
|
665
|
-
}
|
|
666
|
-
else {
|
|
667
|
-
this.logger.log("WARN", `Your requested "node" version "${requestedMajorVersion}" doesn't match your global version "${hostMajorVersion}". Using node@${hostMajorVersion} from host.`);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
return process.execPath;
|
|
671
|
-
}
|
|
672
671
|
getRuntimeConfig(backend) {
|
|
673
672
|
const configPath = `${backend.functionsDir}/.runtimeconfig.json`;
|
|
674
673
|
try {
|
|
@@ -791,13 +790,11 @@ class FunctionsEmulator {
|
|
|
791
790
|
}
|
|
792
791
|
return secretEnvs;
|
|
793
792
|
}
|
|
794
|
-
async
|
|
795
|
-
var _a;
|
|
796
|
-
const emitter = new events_1.EventEmitter();
|
|
793
|
+
async startNode(backend, envs) {
|
|
797
794
|
const args = [path.join(__dirname, "functionsEmulatorRuntime")];
|
|
798
795
|
if (this.args.debugPort) {
|
|
799
|
-
if (process.env.FIREPIT_VERSION
|
|
800
|
-
this.logger.log("WARN", `To enable function inspection, please run "
|
|
796
|
+
if (process.env.FIREPIT_VERSION) {
|
|
797
|
+
this.logger.log("WARN", `To enable function inspection, please run "npm i node@${semver.coerce(backend.runtime || "18.0.0")} --save-dev" in your functions directory`);
|
|
801
798
|
}
|
|
802
799
|
else {
|
|
803
800
|
const { host } = this.getInfo();
|
|
@@ -810,20 +807,51 @@ class FunctionsEmulator {
|
|
|
810
807
|
"Cloud Functions for Firebase requires a node_modules folder to work correctly and is therefore incompatible with PnP. " +
|
|
811
808
|
"See https://yarnpkg.com/getting-started/migration#step-by-step for more information.");
|
|
812
809
|
}
|
|
813
|
-
const
|
|
814
|
-
|
|
810
|
+
const bin = backend.bin;
|
|
811
|
+
if (!bin) {
|
|
812
|
+
throw new Error(`No binary associated with ${backend.functionsDir}. ` +
|
|
813
|
+
"Make sure function runtime is configured correctly in firebase.json.");
|
|
814
|
+
}
|
|
815
815
|
const socketPath = (0, functionsEmulatorShared_1.getTemporarySocketPath)();
|
|
816
|
-
const childProcess = spawn(
|
|
816
|
+
const childProcess = spawn(bin, args, {
|
|
817
817
|
cwd: backend.functionsDir,
|
|
818
|
-
env: Object.assign(Object.assign(Object.assign(
|
|
818
|
+
env: Object.assign(Object.assign(Object.assign({ node: backend.bin }, process.env), envs), { PORT: socketPath }),
|
|
819
819
|
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
820
820
|
});
|
|
821
|
-
|
|
821
|
+
return Promise.resolve({
|
|
822
|
+
process: childProcess,
|
|
823
|
+
events: new events_1.EventEmitter(),
|
|
824
|
+
cwd: backend.functionsDir,
|
|
825
|
+
conn: new IPCConn(socketPath),
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
async startPython(backend, envs) {
|
|
829
|
+
const args = ["functions-framework"];
|
|
830
|
+
if (this.args.debugPort) {
|
|
831
|
+
this.logger.log("WARN", "--inspect-functions not supported for Python functions. Ignored.");
|
|
832
|
+
}
|
|
833
|
+
const port = await portfinder.getPortPromise({
|
|
834
|
+
port: 8081 + (0, utils_1.randomInt)(0, 1000),
|
|
835
|
+
});
|
|
836
|
+
const childProcess = (0, python_1.runWithVirtualEnv)(args, backend.functionsDir, Object.assign(Object.assign(Object.assign({}, process.env), envs), { PYTHONUNBUFFERED: "1", DEBUG: "False", HOST: "127.0.0.1", PORT: port.toString() }));
|
|
837
|
+
return {
|
|
822
838
|
process: childProcess,
|
|
823
|
-
events:
|
|
839
|
+
events: new events_1.EventEmitter(),
|
|
824
840
|
cwd: backend.functionsDir,
|
|
825
|
-
|
|
841
|
+
conn: new TCPConn("127.0.0.1", port),
|
|
826
842
|
};
|
|
843
|
+
}
|
|
844
|
+
async startRuntime(backend, trigger) {
|
|
845
|
+
var _a;
|
|
846
|
+
const runtimeEnv = this.getRuntimeEnvs(backend, trigger);
|
|
847
|
+
const secretEnvs = await this.resolveSecretEnvs(backend, trigger);
|
|
848
|
+
let runtime;
|
|
849
|
+
if (backend.runtime.startsWith("python")) {
|
|
850
|
+
runtime = await this.startPython(backend, Object.assign(Object.assign({}, runtimeEnv), secretEnvs));
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
runtime = await this.startNode(backend, Object.assign(Object.assign({}, runtimeEnv), secretEnvs));
|
|
854
|
+
}
|
|
827
855
|
const extensionLogInfo = {
|
|
828
856
|
instanceId: backend.extensionInstanceId,
|
|
829
857
|
ref: (_a = backend.extensionVersion) === null || _a === void 0 ? void 0 : _a.ref,
|
|
@@ -927,7 +955,14 @@ class FunctionsEmulator {
|
|
|
927
955
|
this.logger.log("DEBUG", `[functions] Got req.url=${req.url}, mapping to path=${path}`);
|
|
928
956
|
const pool = this.workerPools[record.backend.codebase];
|
|
929
957
|
if (!pool.readyForWork(trigger.id)) {
|
|
930
|
-
|
|
958
|
+
try {
|
|
959
|
+
await this.startRuntime(record.backend, trigger);
|
|
960
|
+
}
|
|
961
|
+
catch (e) {
|
|
962
|
+
this.logger.logLabeled("ERROR", `Failed to handle request for function ${trigger.id}`);
|
|
963
|
+
this.logger.logLabeled("ERROR", `Failed to start functions in ${record.backend.functionsDir}: ${e}`);
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
931
966
|
}
|
|
932
967
|
const debugBundle = this.args.debugPort
|
|
933
968
|
? {
|
|
@@ -86,12 +86,7 @@ class RuntimeWorker {
|
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
88
|
return new Promise((resolve) => {
|
|
89
|
-
const proxy = http.request({
|
|
90
|
-
method: req.method,
|
|
91
|
-
path: req.path,
|
|
92
|
-
headers: req.headers,
|
|
93
|
-
socketPath: this.runtime.socketPath,
|
|
94
|
-
}, (_resp) => {
|
|
89
|
+
const proxy = http.request(Object.assign(Object.assign({}, this.runtime.conn.httpReqOpts()), { method: req.method, path: req.path, headers: req.headers }), (_resp) => {
|
|
95
90
|
resp.writeHead(_resp.statusCode || 200, _resp.headers);
|
|
96
91
|
const piped = _resp.pipe(resp);
|
|
97
92
|
piped.on("finish", () => {
|
|
@@ -137,16 +132,11 @@ class RuntimeWorker {
|
|
|
137
132
|
}
|
|
138
133
|
isSocketReady() {
|
|
139
134
|
return new Promise((resolve, reject) => {
|
|
140
|
-
const req = http
|
|
141
|
-
.request({
|
|
142
|
-
method: "GET",
|
|
143
|
-
path: "/__/health",
|
|
144
|
-
socketPath: this.runtime.socketPath,
|
|
145
|
-
}, () => {
|
|
135
|
+
const req = http.request(Object.assign(Object.assign({}, this.runtime.conn.httpReqOpts()), { method: "GET", path: "/__/health" }), () => {
|
|
146
136
|
this.readyForWork();
|
|
147
137
|
resolve();
|
|
148
|
-
})
|
|
149
|
-
|
|
138
|
+
});
|
|
139
|
+
req.end();
|
|
150
140
|
req.on("error", (error) => {
|
|
151
141
|
reject(error);
|
|
152
142
|
});
|
|
@@ -229,6 +229,7 @@ function createFirebaseEndpoints(emulator) {
|
|
|
229
229
|
throw err;
|
|
230
230
|
}
|
|
231
231
|
res.header("X-Goog-Upload-Size-Received", upload.size.toString());
|
|
232
|
+
res.header("x-goog-upload-status", upload.status);
|
|
232
233
|
return res.sendStatus(200);
|
|
233
234
|
}
|
|
234
235
|
if (uploadCommand === "cancel") {
|
|
@@ -52,7 +52,7 @@ class DefaultStorageRulesManager {
|
|
|
52
52
|
this._logger.log("WARN", `${parsedIssue.description_.replace(/\.$/, "")} in ${parsedIssue.sourcePosition_.fileName_}:${parsedIssue.sourcePosition_.line_}`);
|
|
53
53
|
}
|
|
54
54
|
catch (_a) {
|
|
55
|
-
this._logger.
|
|
55
|
+
this._logger.logLabeled("WARN", "storage", issue);
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
58
|
return issues;
|
|
@@ -24,8 +24,8 @@ function createApp(defaultProjectId, emulator) {
|
|
|
24
24
|
exposedHeaders: [
|
|
25
25
|
"content-type",
|
|
26
26
|
"x-firebase-storage-version",
|
|
27
|
+
"X-Goog-Upload-Size-Received",
|
|
27
28
|
"x-goog-upload-url",
|
|
28
|
-
"x-goog-upload-status",
|
|
29
29
|
"x-goog-upload-command",
|
|
30
30
|
"x-gupload-uploadid",
|
|
31
31
|
"x-goog-upload-header-content-length",
|
|
@@ -11,9 +11,9 @@ var UploadType;
|
|
|
11
11
|
})(UploadType = exports.UploadType || (exports.UploadType = {}));
|
|
12
12
|
var UploadStatus;
|
|
13
13
|
(function (UploadStatus) {
|
|
14
|
-
UploadStatus[
|
|
15
|
-
UploadStatus[
|
|
16
|
-
UploadStatus[
|
|
14
|
+
UploadStatus["ACTIVE"] = "active";
|
|
15
|
+
UploadStatus["CANCELLED"] = "cancelled";
|
|
16
|
+
UploadStatus["FINISHED"] = "final";
|
|
17
17
|
})(UploadStatus = exports.UploadStatus || (exports.UploadStatus = {}));
|
|
18
18
|
class UploadNotActiveError extends Error {
|
|
19
19
|
}
|
|
@@ -13,6 +13,7 @@ const logger_1 = require("../logger");
|
|
|
13
13
|
const prompt_1 = require("../prompt");
|
|
14
14
|
const utils = require("../utils");
|
|
15
15
|
const projectUtils_1 = require("../projectUtils");
|
|
16
|
+
const functional_1 = require("../functional");
|
|
16
17
|
var SecretLocation;
|
|
17
18
|
(function (SecretLocation) {
|
|
18
19
|
SecretLocation[SecretLocation["CLOUD"] = 1] = "CLOUD";
|
|
@@ -67,8 +68,9 @@ async function ask(args) {
|
|
|
67
68
|
}
|
|
68
69
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, "answer the questions below to configure your extension:");
|
|
69
70
|
const substituted = (0, extensionsHelper_1.substituteParams)(args.paramSpecs, args.firebaseProjectParams);
|
|
71
|
+
const [advancedParams, standardParams] = (0, functional_1.partition)(substituted, (p) => { var _a; return (_a = p.advanced) !== null && _a !== void 0 ? _a : false; });
|
|
70
72
|
const result = {};
|
|
71
|
-
const promises =
|
|
73
|
+
const promises = standardParams.map((paramSpec) => {
|
|
72
74
|
return async () => {
|
|
73
75
|
result[paramSpec.param] = await askForParam({
|
|
74
76
|
projectId: args.projectId,
|
|
@@ -78,6 +80,35 @@ async function ask(args) {
|
|
|
78
80
|
});
|
|
79
81
|
};
|
|
80
82
|
});
|
|
83
|
+
if (advancedParams.length) {
|
|
84
|
+
promises.push(async () => {
|
|
85
|
+
const shouldPrompt = await (0, prompt_1.promptOnce)({
|
|
86
|
+
type: "confirm",
|
|
87
|
+
message: "Do you want to configure any advanced parameters for this instance?",
|
|
88
|
+
default: false,
|
|
89
|
+
});
|
|
90
|
+
if (shouldPrompt) {
|
|
91
|
+
const advancedPromises = advancedParams.map((paramSpec) => {
|
|
92
|
+
return async () => {
|
|
93
|
+
result[paramSpec.param] = await askForParam({
|
|
94
|
+
projectId: args.projectId,
|
|
95
|
+
instanceId: args.instanceId,
|
|
96
|
+
paramSpec: paramSpec,
|
|
97
|
+
reconfiguring: args.reconfiguring,
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
await advancedPromises.reduce((prev, cur) => prev.then(cur), Promise.resolve());
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
for (const paramSpec of advancedParams) {
|
|
105
|
+
if (paramSpec.required && paramSpec.default) {
|
|
106
|
+
result[paramSpec.param] = { baseValue: paramSpec.default };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
81
112
|
await promises.reduce((prev, cur) => prev.then(cur), Promise.resolve());
|
|
82
113
|
logger_1.logger.info();
|
|
83
114
|
return result;
|
|
@@ -32,7 +32,7 @@ async function buildOptions(options) {
|
|
|
32
32
|
options.extDevEnv = params;
|
|
33
33
|
const functionEmuTriggerDefs = functionResources.map((r) => triggerHelper.functionResourceToEmulatedTriggerDefintion(r));
|
|
34
34
|
options.extDevTriggers = functionEmuTriggerDefs;
|
|
35
|
-
options.
|
|
35
|
+
options.extDevRuntime = specHelper.getRuntime(functionResources);
|
|
36
36
|
return options;
|
|
37
37
|
}
|
|
38
38
|
exports.buildOptions = buildOptions;
|
|
@@ -45,12 +45,12 @@ async function getExtensionFunctionInfo(instance, paramValues) {
|
|
|
45
45
|
trigger.name = `ext-${instance.instanceId}-${trigger.name}`;
|
|
46
46
|
return trigger;
|
|
47
47
|
});
|
|
48
|
-
const
|
|
48
|
+
const runtime = specHelper.getRuntime(functionResources);
|
|
49
49
|
const nonSecretEnv = getNonSecretEnv(spec.params, paramValues);
|
|
50
50
|
const secretEnvVariables = getSecretEnvVars(spec.params, paramValues);
|
|
51
51
|
return {
|
|
52
52
|
extensionTriggers,
|
|
53
|
-
|
|
53
|
+
runtime,
|
|
54
54
|
nonSecretEnv,
|
|
55
55
|
secretEnvVariables,
|
|
56
56
|
};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getRuntime = exports.DEFAULT_RUNTIME = exports.getFunctionProperties = exports.getFunctionResourcesWithParamSubstitution = exports.readFileFromDirectory = exports.readPostinstall = exports.readExtensionYaml = void 0;
|
|
4
4
|
const yaml = require("js-yaml");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const fs = require("fs-extra");
|
|
7
7
|
const error_1 = require("../../error");
|
|
8
8
|
const extensionsHelper_1 = require("../extensionsHelper");
|
|
9
9
|
const utils_1 = require("../utils");
|
|
10
|
-
const functionsEmulatorUtils_1 = require("../../emulator/functionsEmulatorUtils");
|
|
11
10
|
const SPEC_FILE = "extension.yaml";
|
|
12
11
|
const POSTINSTALL_FILE = "POSTINSTALL.md";
|
|
13
12
|
const validFunctionTypes = [
|
|
@@ -67,24 +66,26 @@ function getFunctionProperties(resources) {
|
|
|
67
66
|
return resources.map((r) => r.properties);
|
|
68
67
|
}
|
|
69
68
|
exports.getFunctionProperties = getFunctionProperties;
|
|
70
|
-
|
|
69
|
+
exports.DEFAULT_RUNTIME = "nodejs14";
|
|
70
|
+
function getRuntime(resources) {
|
|
71
|
+
if (resources.length === 0) {
|
|
72
|
+
return exports.DEFAULT_RUNTIME;
|
|
73
|
+
}
|
|
71
74
|
const invalidRuntimes = [];
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return runtime;
|
|
81
|
-
}
|
|
75
|
+
const runtimes = resources.map((r) => {
|
|
76
|
+
const runtime = (0, utils_1.getResourceRuntime)(r);
|
|
77
|
+
if (!runtime) {
|
|
78
|
+
return exports.DEFAULT_RUNTIME;
|
|
79
|
+
}
|
|
80
|
+
if (!/^(nodejs)?([0-9]+)/.test(runtime)) {
|
|
81
|
+
invalidRuntimes.push(runtime);
|
|
82
|
+
return exports.DEFAULT_RUNTIME;
|
|
82
83
|
}
|
|
83
|
-
return
|
|
84
|
+
return runtime;
|
|
84
85
|
});
|
|
85
86
|
if (invalidRuntimes.length) {
|
|
86
87
|
throw new error_1.FirebaseError(`The following runtimes are not supported by the Emulator Suite: ${invalidRuntimes.join(", ")}. \n Only Node runtimes are supported.`);
|
|
87
88
|
}
|
|
88
|
-
return
|
|
89
|
+
return runtimes.sort()[runtimes.length - 1];
|
|
89
90
|
}
|
|
90
|
-
exports.
|
|
91
|
+
exports.getRuntime = getRuntime;
|
|
@@ -34,9 +34,10 @@ async function createInstanceHelper(projectId, instanceId, config, validateOnly
|
|
|
34
34
|
return pollRes;
|
|
35
35
|
}
|
|
36
36
|
async function createInstance(args) {
|
|
37
|
-
var _a, _b;
|
|
37
|
+
var _a, _b, _c;
|
|
38
38
|
const config = {
|
|
39
39
|
params: args.params,
|
|
40
|
+
systemParams: (_a = args.systemParams) !== null && _a !== void 0 ? _a : {},
|
|
40
41
|
allowedEventTypes: args.allowedEventTypes,
|
|
41
42
|
eventarcChannel: args.eventarcChannel,
|
|
42
43
|
};
|
|
@@ -44,12 +45,12 @@ async function createInstance(args) {
|
|
|
44
45
|
throw new error_1.FirebaseError("ExtensionSource and ExtensionVersion both provided, but only one should be.");
|
|
45
46
|
}
|
|
46
47
|
else if (args.extensionSource) {
|
|
47
|
-
config.source = { name: (
|
|
48
|
+
config.source = { name: (_b = args.extensionSource) === null || _b === void 0 ? void 0 : _b.name };
|
|
48
49
|
}
|
|
49
50
|
else if (args.extensionVersionRef) {
|
|
50
51
|
const ref = refs.parse(args.extensionVersionRef);
|
|
51
52
|
config.extensionRef = refs.toExtensionRef(ref);
|
|
52
|
-
config.extensionVersion = (
|
|
53
|
+
config.extensionVersion = (_c = ref.version) !== null && _c !== void 0 ? _c : "";
|
|
53
54
|
}
|
|
54
55
|
else {
|
|
55
56
|
throw new error_1.FirebaseError("No ExtensionVersion or ExtensionSource provided but one is required.");
|
|
@@ -128,6 +129,10 @@ async function configureInstance(args) {
|
|
|
128
129
|
reqBody.data.config.eventarcChannel = args.eventarcChannel;
|
|
129
130
|
}
|
|
130
131
|
reqBody.updateMask += ",config.allowed_event_types,config.eventarc_channel";
|
|
132
|
+
if (args.systemParams) {
|
|
133
|
+
reqBody.data.config.systemParams = args.systemParams;
|
|
134
|
+
reqBody.updateMask += ",config.system_params";
|
|
135
|
+
}
|
|
131
136
|
return patchInstance(reqBody);
|
|
132
137
|
}
|
|
133
138
|
exports.configureInstance = configureInstance;
|
|
@@ -143,6 +148,10 @@ async function updateInstance(args) {
|
|
|
143
148
|
body.config.params = args.params;
|
|
144
149
|
updateMask += ",config.params";
|
|
145
150
|
}
|
|
151
|
+
if (args.systemParams) {
|
|
152
|
+
body.config.systemParams = args.systemParams;
|
|
153
|
+
updateMask += ",config.system_params";
|
|
154
|
+
}
|
|
146
155
|
if (args.canEmitEvents) {
|
|
147
156
|
if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
|
|
148
157
|
throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
|
|
@@ -174,6 +183,10 @@ async function updateInstanceFromRegistry(args) {
|
|
|
174
183
|
body.config.params = args.params;
|
|
175
184
|
updateMask += ",config.params";
|
|
176
185
|
}
|
|
186
|
+
if (args.systemParams) {
|
|
187
|
+
body.config.systemParams = args.systemParams;
|
|
188
|
+
updateMask += ",config.system_params";
|
|
189
|
+
}
|
|
177
190
|
if (args.canEmitEvents) {
|
|
178
191
|
if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
|
|
179
192
|
throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readEnvFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = exports.setNewDefaults = exports.buildBindingOptionsWithBaseValue = exports.getBaseParamBindings = void 0;
|
|
3
|
+
exports.isSystemParam = exports.readEnvFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = exports.setNewDefaults = exports.buildBindingOptionsWithBaseValue = exports.getBaseParamBindings = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const clc = require("colorette");
|
|
6
6
|
const fs = require("fs-extra");
|
|
@@ -172,3 +172,8 @@ function readEnvFile(envPath) {
|
|
|
172
172
|
return result.envs;
|
|
173
173
|
}
|
|
174
174
|
exports.readEnvFile = readEnvFile;
|
|
175
|
+
function isSystemParam(paramName) {
|
|
176
|
+
const regex = /^firebaseextensions\.[a-zA-Z0-9\.]*\//;
|
|
177
|
+
return regex.test(paramName);
|
|
178
|
+
}
|
|
179
|
+
exports.isSystemParam = isSystemParam;
|
|
@@ -6,9 +6,9 @@ const apiv2_1 = require("../apiv2");
|
|
|
6
6
|
const logger_1 = require("../logger");
|
|
7
7
|
async function checkDatabaseType(projectId) {
|
|
8
8
|
try {
|
|
9
|
-
const client = new apiv2_1.Client({ urlPrefix: api_1.
|
|
10
|
-
const resp = await client.get(`/
|
|
11
|
-
return resp.body.
|
|
9
|
+
const client = new apiv2_1.Client({ urlPrefix: api_1.firestoreOrigin, apiVersion: "v1" });
|
|
10
|
+
const resp = await client.get(`/projects/${projectId}/databases/(default)`);
|
|
11
|
+
return resp.body.type;
|
|
12
12
|
}
|
|
13
13
|
catch (err) {
|
|
14
14
|
logger_1.logger.debug("error getting database type", err);
|
package/lib/frameworks/index.js
CHANGED
|
@@ -120,8 +120,8 @@ function findDependency(name, options = {}) {
|
|
|
120
120
|
}
|
|
121
121
|
exports.findDependency = findDependency;
|
|
122
122
|
async function prepareFrameworks(targetNames, context, options, emulators = []) {
|
|
123
|
-
var _a;
|
|
124
|
-
var
|
|
123
|
+
var _a, _b;
|
|
124
|
+
var _c, _d, _e, _f;
|
|
125
125
|
const nodeVersion = process.version;
|
|
126
126
|
if (!semver.satisfies(nodeVersion, ">=16.0.0")) {
|
|
127
127
|
throw new error_1.FirebaseError(`The frameworks awareness feature requires Node.JS >= 16 and npm >= 8 in order to work correctly, due to some of the downstream dependencies. Please upgrade your version of Node.JS, reinstall firebase-tools, and give it another go.`);
|
|
@@ -133,7 +133,7 @@ async function prepareFrameworks(targetNames, context, options, emulators = [])
|
|
|
133
133
|
try {
|
|
134
134
|
await (0, requireHostingSite_1.requireHostingSite)(options);
|
|
135
135
|
}
|
|
136
|
-
catch (
|
|
136
|
+
catch (_g) {
|
|
137
137
|
options.site = project;
|
|
138
138
|
}
|
|
139
139
|
}
|
|
@@ -243,10 +243,11 @@ async function prepareFrameworks(targetNames, context, options, emulators = [])
|
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
else {
|
|
246
|
-
const { wantsBackend = false, rewrites = [], redirects = [], headers = [], } = (await build(getProjectPath())) || {};
|
|
246
|
+
const { wantsBackend = false, rewrites = [], redirects = [], headers = [], trailingSlash, } = (await build(getProjectPath())) || {};
|
|
247
247
|
config.rewrites.push(...rewrites);
|
|
248
248
|
config.redirects.push(...redirects);
|
|
249
249
|
config.headers.push(...headers);
|
|
250
|
+
(_b = config.trailingSlash) !== null && _b !== void 0 ? _b : (config.trailingSlash = trailingSlash);
|
|
250
251
|
if (await (0, fs_extra_1.pathExists)(hostingDist))
|
|
251
252
|
await (0, promises_1.rm)(hostingDist, { recursive: true });
|
|
252
253
|
await (0, fs_extra_1.mkdirp)(hostingDist);
|
|
@@ -319,11 +320,11 @@ async function prepareFrameworks(targetNames, context, options, emulators = [])
|
|
|
319
320
|
packageJson.main = "server.js";
|
|
320
321
|
delete packageJson.devDependencies;
|
|
321
322
|
packageJson.dependencies || (packageJson.dependencies = {});
|
|
322
|
-
(
|
|
323
|
-
(
|
|
324
|
-
(
|
|
323
|
+
(_c = packageJson.dependencies)["firebase-frameworks"] || (_c["firebase-frameworks"] = exports.FIREBASE_FRAMEWORKS_VERSION);
|
|
324
|
+
(_d = packageJson.dependencies)["firebase-functions"] || (_d["firebase-functions"] = exports.FIREBASE_FUNCTIONS_VERSION);
|
|
325
|
+
(_e = packageJson.dependencies)["firebase-admin"] || (_e["firebase-admin"] = exports.FIREBASE_ADMIN_VERSION);
|
|
325
326
|
packageJson.engines || (packageJson.engines = {});
|
|
326
|
-
(
|
|
327
|
+
(_f = packageJson.engines).node || (_f.node = exports.NODE_VERSION);
|
|
327
328
|
await (0, promises_1.writeFile)((0, path_1.join)(functionsDist, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
328
329
|
await (0, promises_1.writeFile)((0, path_1.join)(functionsDist, ".env"), `__FIREBASE_FRAMEWORKS_ENTRY__=${frameworksEntry}
|
|
329
330
|
${firebaseDefaults ? `__FIREBASE_DEFAULTS__=${JSON.stringify(firebaseDefaults)}\n` : ""}`);
|