firebase-tools 10.7.2 → 10.9.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/commands/ext-configure.js +26 -15
- package/lib/commands/ext-export.js +14 -5
- package/lib/commands/ext-install.js +31 -2
- package/lib/commands/ext-update.js +17 -10
- package/lib/commands/functions-list.js +12 -20
- package/lib/commands/functions-secrets-set.js +1 -13
- package/lib/deploy/extensions/planner.js +12 -0
- package/lib/deploy/extensions/tasks.js +13 -0
- package/lib/deploy/functions/backend.js +47 -14
- package/lib/deploy/functions/build.js +9 -1
- package/lib/deploy/functions/checkIam.js +65 -46
- package/lib/deploy/functions/functionsDeployHelper.js +1 -1
- package/lib/deploy/functions/prepare.js +42 -15
- package/lib/deploy/functions/pricing.js +2 -2
- package/lib/deploy/functions/release/fabricator.js +66 -11
- package/lib/deploy/functions/release/index.js +0 -21
- package/lib/deploy/functions/runtimes/discovery/index.js +2 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +13 -1
- package/lib/deploy/functions/runtimes/node/index.js +26 -26
- package/lib/deploy/functions/services/storage.js +6 -12
- package/lib/deploy/functions/validate.js +79 -15
- package/lib/deploy/index.js +2 -1
- package/lib/emulator/controller.js +10 -5
- package/lib/emulator/downloadableEmulators.js +18 -34
- package/lib/emulator/extensionsEmulator.js +4 -1
- package/lib/emulator/functionsEmulator.js +4 -1
- package/lib/extensions/askUserForEventsConfig.js +97 -0
- package/lib/extensions/export.js +7 -0
- package/lib/extensions/extensionsApi.js +47 -7
- package/lib/extensions/manifest.js +1 -1
- package/lib/extensions/updateHelper.js +7 -1
- package/lib/extensions/warnings.js +3 -3
- package/lib/frameworks/index.js +121 -0
- package/lib/functions/functionslog.js +4 -9
- package/lib/gcp/cloudfunctions.js +1 -1
- package/lib/gcp/cloudfunctionsv2.js +9 -11
- package/lib/gcp/serviceusage.js +24 -0
- package/lib/hosting/normalizedHostingConfigs.js +3 -0
- package/lib/previews.js +1 -1
- package/lib/serve/index.js +2 -1
- package/lib/throttler/throttler.js +2 -1
- package/npm-shrinkwrap.json +103 -9
- package/package.json +2 -2
- package/schema/firebase-config.json +9 -0
- package/templates/extensions/javascript/package.lint.json +5 -5
- package/templates/extensions/javascript/package.nolint.json +3 -3
- package/templates/extensions/typescript/package.lint.json +8 -7
- package/templates/extensions/typescript/package.nolint.json +2 -1
- package/templates/init/functions/typescript/package.lint.json +1 -0
- package/templates/init/functions/typescript/package.nolint.json +5 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.secretsAreValid = exports.functionIdsAreValid = exports.functionsDirectoryExists = exports.endpointsAreUnique = exports.endpointsAreValid = void 0;
|
|
3
|
+
exports.secretsAreValid = exports.functionIdsAreValid = exports.functionsDirectoryExists = exports.endpointsAreUnique = exports.cpuConfigIsValid = exports.endpointsAreValid = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const error_1 = require("../../error");
|
|
@@ -11,34 +11,98 @@ const backend = require("./backend");
|
|
|
11
11
|
const utils = require("../../utils");
|
|
12
12
|
const secrets = require("../../functions/secrets");
|
|
13
13
|
const services_1 = require("./services");
|
|
14
|
+
function matchingIds(endpoints, filter) {
|
|
15
|
+
return endpoints
|
|
16
|
+
.filter(filter)
|
|
17
|
+
.map((endpoint) => endpoint.id)
|
|
18
|
+
.join(",");
|
|
19
|
+
}
|
|
20
|
+
const mem = (endpoint) => endpoint.availableMemoryMb || backend.DEFAULT_MEMORY;
|
|
21
|
+
const cpu = (endpoint) => {
|
|
22
|
+
var _a;
|
|
23
|
+
return endpoint.cpu === "gcf_gen1"
|
|
24
|
+
? backend.memoryToGen1Cpu(mem(endpoint))
|
|
25
|
+
: (_a = endpoint.cpu) !== null && _a !== void 0 ? _a : backend.memoryToGen2Cpu(mem(endpoint));
|
|
26
|
+
};
|
|
14
27
|
function endpointsAreValid(wantBackend) {
|
|
15
28
|
const endpoints = backend.allEndpoints(wantBackend);
|
|
16
29
|
functionIdsAreValid(endpoints);
|
|
17
30
|
for (const ep of endpoints) {
|
|
18
31
|
(0, services_1.serviceForEndpoint)(ep).validateTrigger(ep, wantBackend);
|
|
19
32
|
}
|
|
20
|
-
const gcfV1WithConcurrency = endpoints
|
|
21
|
-
.filter((endpoint) => (endpoint.concurrency || 1) !== 1 && endpoint.platform === "gcfv1")
|
|
22
|
-
.map((endpoint) => endpoint.id);
|
|
33
|
+
const gcfV1WithConcurrency = matchingIds(endpoints, (endpoint) => (endpoint.concurrency || 1) !== 1 && endpoint.platform === "gcfv1");
|
|
23
34
|
if (gcfV1WithConcurrency.length) {
|
|
24
|
-
const msg = `Cannot set concurrency on the functions ${gcfV1WithConcurrency
|
|
35
|
+
const msg = `Cannot set concurrency on the functions ${gcfV1WithConcurrency} because they are GCF gen 1`;
|
|
25
36
|
throw new error_1.FirebaseError(msg);
|
|
26
37
|
}
|
|
27
|
-
const tooSmallForConcurrency = endpoints
|
|
28
|
-
.filter((endpoint) => {
|
|
38
|
+
const tooSmallForConcurrency = matchingIds(endpoints, (endpoint) => {
|
|
29
39
|
if ((endpoint.concurrency || 1) === 1) {
|
|
30
40
|
return false;
|
|
31
41
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
})
|
|
35
|
-
.map((endpoint) => endpoint.id);
|
|
42
|
+
return cpu(endpoint) < backend.MIN_CPU_FOR_CONCURRENCY;
|
|
43
|
+
});
|
|
36
44
|
if (tooSmallForConcurrency.length) {
|
|
37
|
-
const msg =
|
|
45
|
+
const msg = "The following functions are configured to allow concurrent " +
|
|
46
|
+
"execution and less than one full CPU. This is not supported: " +
|
|
47
|
+
tooSmallForConcurrency;
|
|
38
48
|
throw new error_1.FirebaseError(msg);
|
|
39
49
|
}
|
|
50
|
+
cpuConfigIsValid(endpoints);
|
|
40
51
|
}
|
|
41
52
|
exports.endpointsAreValid = endpointsAreValid;
|
|
53
|
+
function cpuConfigIsValid(endpoints) {
|
|
54
|
+
const gcfV1WithCPU = matchingIds(endpoints, (endpoint) => endpoint.platform === "gcfv1" && typeof endpoint["cpu"] !== "undefined");
|
|
55
|
+
if (gcfV1WithCPU.length) {
|
|
56
|
+
const msg = `Cannot set CPU on the functions ${gcfV1WithCPU} because they are GCF gen 1`;
|
|
57
|
+
throw new error_1.FirebaseError(msg);
|
|
58
|
+
}
|
|
59
|
+
const invalidCPU = matchingIds(endpoints, (endpoint) => {
|
|
60
|
+
const c = cpu(endpoint);
|
|
61
|
+
if (c < 0.08) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
if (c < 1) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return ![1, 2, 4, 6, 8].includes(c);
|
|
68
|
+
});
|
|
69
|
+
if (invalidCPU.length) {
|
|
70
|
+
const msg = `The following functions have invalid CPU settings ${invalidCPU}. Valid CPU options are (0.08, 1], 2, 4, 6, 8, or "gcf_gen1"`;
|
|
71
|
+
throw new error_1.FirebaseError(msg);
|
|
72
|
+
}
|
|
73
|
+
const smallCPURegions = ["australia-southeast2", "asia-northeast3", "asia-south2"];
|
|
74
|
+
const tooBigCPUForRegion = matchingIds(endpoints, (endpoint) => smallCPURegions.includes(endpoint.region) && cpu(endpoint) > 4);
|
|
75
|
+
if (tooBigCPUForRegion) {
|
|
76
|
+
const msg = `The functions ${tooBigCPUForRegion} have > 4 CPU in a region that supports a maximum 4 CPU`;
|
|
77
|
+
throw new error_1.FirebaseError(msg);
|
|
78
|
+
}
|
|
79
|
+
const tooSmallCPUSmall = matchingIds(endpoints, (endpoint) => mem(endpoint) > 512 && cpu(endpoint) < 0.5);
|
|
80
|
+
if (tooSmallCPUSmall) {
|
|
81
|
+
const msg = `The functions ${tooSmallCPUSmall} have too little CPU for their memory allocation. A minimum of 0.5 CPU is needed to set a memory limit greater than 512MiB`;
|
|
82
|
+
throw new error_1.FirebaseError(msg);
|
|
83
|
+
}
|
|
84
|
+
const tooSmallCPUBig = matchingIds(endpoints, (endpoint) => mem(endpoint) > 1024 && cpu(endpoint) < 1);
|
|
85
|
+
if (tooSmallCPUBig) {
|
|
86
|
+
const msg = `The functions ${tooSmallCPUSmall} have too little CPU for their memory allocation. A minimum of 1 CPU is needed to set a memory limit greater than 1GiB`;
|
|
87
|
+
throw new error_1.FirebaseError(msg);
|
|
88
|
+
}
|
|
89
|
+
const tooSmallMemory4CPU = matchingIds(endpoints, (endpoint) => cpu(endpoint) === 4 && mem(endpoint) < 2 << 10);
|
|
90
|
+
if (tooSmallMemory4CPU) {
|
|
91
|
+
const msg = `The functions ${tooSmallMemory4CPU} have too little memory for their CPU. Functions with 4 CPU require at least 2GiB`;
|
|
92
|
+
throw new error_1.FirebaseError(msg);
|
|
93
|
+
}
|
|
94
|
+
const tooSmallMemory6CPU = matchingIds(endpoints, (endpoint) => cpu(endpoint) === 6 && mem(endpoint) < 3 << 10);
|
|
95
|
+
if (tooSmallMemory6CPU) {
|
|
96
|
+
const msg = `The functions ${tooSmallMemory6CPU} have too little memory for their CPU. Functions with 6 CPU require at least 3GiB`;
|
|
97
|
+
throw new error_1.FirebaseError(msg);
|
|
98
|
+
}
|
|
99
|
+
const tooSmallMemory8CPU = matchingIds(endpoints, (endpoint) => cpu(endpoint) === 8 && mem(endpoint) < 4 << 10);
|
|
100
|
+
if (tooSmallMemory8CPU) {
|
|
101
|
+
const msg = `The functions ${tooSmallMemory8CPU} have too little memory for their CPU. Functions with 8 CPU require at least 4GiB`;
|
|
102
|
+
throw new error_1.FirebaseError(msg);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.cpuConfigIsValid = cpuConfigIsValid;
|
|
42
106
|
function endpointsAreUnique(backends) {
|
|
43
107
|
const endpointToCodebases = {};
|
|
44
108
|
for (const [codebase, b] of Object.entries(backends)) {
|
|
@@ -100,13 +164,13 @@ async function secretsAreValid(projectId, wantBackend) {
|
|
|
100
164
|
await validateSecretVersions(projectId, endpoints);
|
|
101
165
|
}
|
|
102
166
|
exports.secretsAreValid = secretsAreValid;
|
|
167
|
+
const secretsSupportedPlatforms = ["gcfv1", "gcfv2"];
|
|
103
168
|
function validatePlatformTargets(endpoints) {
|
|
104
|
-
const
|
|
105
|
-
const unsupported = endpoints.filter((e) => !supportedPlatforms.includes(e.platform));
|
|
169
|
+
const unsupported = endpoints.filter((e) => !secretsSupportedPlatforms.includes(e.platform));
|
|
106
170
|
if (unsupported.length > 0) {
|
|
107
171
|
const errs = unsupported.map((e) => `${e.id}[platform=${e.platform}]`);
|
|
108
172
|
throw new error_1.FirebaseError(`Tried to set secret environment variables on ${errs.join(", ")}. ` +
|
|
109
|
-
`Only ${
|
|
173
|
+
`Only ${secretsSupportedPlatforms.join(", ")} support secret environments.`);
|
|
110
174
|
}
|
|
111
175
|
}
|
|
112
176
|
async function validateSecretVersions(projectId, endpoints) {
|
package/lib/deploy/index.js
CHANGED
|
@@ -18,6 +18,7 @@ const FunctionsTarget = require("./functions");
|
|
|
18
18
|
const StorageTarget = require("./storage");
|
|
19
19
|
const RemoteConfigTarget = require("./remoteconfig");
|
|
20
20
|
const ExtensionsTarget = require("./extensions");
|
|
21
|
+
const frameworks_1 = require("../frameworks");
|
|
21
22
|
const TARGETS = {
|
|
22
23
|
hosting: HostingTarget,
|
|
23
24
|
database: DatabaseTarget,
|
|
@@ -45,7 +46,7 @@ const deploy = async function (targetNames, options, customContext = {}) {
|
|
|
45
46
|
if (previews_1.previews.frameworkawareness && targetNames.includes("hosting")) {
|
|
46
47
|
const config = options.config.get("hosting");
|
|
47
48
|
if (Array.isArray(config) ? config.some((it) => it.source) : config.source) {
|
|
48
|
-
await
|
|
49
|
+
await (0, frameworks_1.prepareFrameworks)(targetNames, context, options);
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
for (const targetName of targetNames) {
|
|
@@ -37,9 +37,10 @@ const config_1 = require("./storage/rules/config");
|
|
|
37
37
|
const getDefaultDatabaseInstance_1 = require("../getDefaultDatabaseInstance");
|
|
38
38
|
const auth_2 = require("../auth");
|
|
39
39
|
const extensionsEmulator_1 = require("./extensionsEmulator");
|
|
40
|
-
const previews_1 = require("../previews");
|
|
41
40
|
const projectConfig_1 = require("../functions/projectConfig");
|
|
42
41
|
const downloadableEmulators_1 = require("./downloadableEmulators");
|
|
42
|
+
const frameworks_1 = require("../frameworks");
|
|
43
|
+
const previews_1 = require("../previews");
|
|
43
44
|
const START_LOGGING_EMULATOR = utils.envOverride("START_LOGGING_EMULATOR", "false", (val) => val === "true");
|
|
44
45
|
async function getAndCheckAddress(emulator, options) {
|
|
45
46
|
var _a, _b, _c, _d;
|
|
@@ -125,9 +126,7 @@ async function cleanShutdown() {
|
|
|
125
126
|
exports.cleanShutdown = cleanShutdown;
|
|
126
127
|
function filterEmulatorTargets(options) {
|
|
127
128
|
let targets = [...types_1.ALL_SERVICE_EMULATORS];
|
|
128
|
-
|
|
129
|
-
targets.push(types_1.Emulators.EXTENSIONS);
|
|
130
|
-
}
|
|
129
|
+
targets.push(types_1.Emulators.EXTENSIONS);
|
|
131
130
|
targets = targets.filter((e) => {
|
|
132
131
|
return options.config.has(e) || options.config.has(`emulators.${e}`);
|
|
133
132
|
});
|
|
@@ -245,6 +244,12 @@ async function startAll(options, showUI = true) {
|
|
|
245
244
|
}
|
|
246
245
|
}
|
|
247
246
|
}
|
|
247
|
+
if (previews_1.previews.frameworkawareness) {
|
|
248
|
+
const config = options.config.get("hosting");
|
|
249
|
+
if (Array.isArray(config) ? config.some((it) => it.source) : config.source) {
|
|
250
|
+
await (0, frameworks_1.prepareFrameworks)(targets, options, options);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
248
253
|
if (shouldStart(options, types_1.Emulators.HUB)) {
|
|
249
254
|
const hubAddr = await getAndCheckAddress(types_1.Emulators.HUB, options);
|
|
250
255
|
const hub = new hub_1.EmulatorHub(Object.assign({ projectId }, hubAddr));
|
|
@@ -282,7 +287,7 @@ async function startAll(options, showUI = true) {
|
|
|
282
287
|
});
|
|
283
288
|
}
|
|
284
289
|
}
|
|
285
|
-
if (shouldStart(options, types_1.Emulators.EXTENSIONS)
|
|
290
|
+
if (shouldStart(options, types_1.Emulators.EXTENSIONS)) {
|
|
286
291
|
const projectNumber = constants_1.Constants.isDemoProject(projectId)
|
|
287
292
|
? constants_1.Constants.FAKE_PROJECT_NUMBER
|
|
288
293
|
: await (0, projectUtils_1.needProjectNumber)(options);
|
|
@@ -50,15 +50,15 @@ exports.DownloadDetails = {
|
|
|
50
50
|
namePrefix: "cloud-storage-rules-emulator",
|
|
51
51
|
},
|
|
52
52
|
},
|
|
53
|
-
ui: previews_1.previews.
|
|
53
|
+
ui: previews_1.previews.emulatoruisnapshot
|
|
54
54
|
? {
|
|
55
|
-
version: "
|
|
56
|
-
downloadPath: path.join(CACHE_DIR, "ui-
|
|
57
|
-
unzipDir: path.join(CACHE_DIR, "ui-
|
|
58
|
-
binaryPath: path.join(CACHE_DIR, "ui-
|
|
55
|
+
version: "SNAPSHOT",
|
|
56
|
+
downloadPath: path.join(CACHE_DIR, "ui-vSNAPSHOT.zip"),
|
|
57
|
+
unzipDir: path.join(CACHE_DIR, "ui-vSNAPSHOT"),
|
|
58
|
+
binaryPath: path.join(CACHE_DIR, "ui-vSNAPSHOT", "server.bundle.js"),
|
|
59
59
|
opts: {
|
|
60
60
|
cacheDir: CACHE_DIR,
|
|
61
|
-
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-
|
|
61
|
+
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-vSNAPSHOT.zip",
|
|
62
62
|
expectedSize: -1,
|
|
63
63
|
expectedChecksum: "",
|
|
64
64
|
skipCache: true,
|
|
@@ -66,35 +66,19 @@ exports.DownloadDetails = {
|
|
|
66
66
|
namePrefix: "ui",
|
|
67
67
|
},
|
|
68
68
|
}
|
|
69
|
-
:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
skipCache: true,
|
|
81
|
-
skipChecksumAndSize: true,
|
|
82
|
-
namePrefix: "ui",
|
|
83
|
-
},
|
|
84
|
-
}
|
|
85
|
-
: {
|
|
86
|
-
version: "1.6.6",
|
|
87
|
-
downloadPath: path.join(CACHE_DIR, "ui-v1.6.6.zip"),
|
|
88
|
-
unzipDir: path.join(CACHE_DIR, "ui-v1.6.6"),
|
|
89
|
-
binaryPath: path.join(CACHE_DIR, "ui-v1.6.6", "server.bundle.js"),
|
|
90
|
-
opts: {
|
|
91
|
-
cacheDir: CACHE_DIR,
|
|
92
|
-
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.6.6.zip",
|
|
93
|
-
expectedSize: 3817247,
|
|
94
|
-
expectedChecksum: "c80a3f0ae1e3f682ace0a18a9cdd2861",
|
|
95
|
-
namePrefix: "ui",
|
|
96
|
-
},
|
|
69
|
+
: {
|
|
70
|
+
version: "1.7.0",
|
|
71
|
+
downloadPath: path.join(CACHE_DIR, "ui-v1.7.0.zip"),
|
|
72
|
+
unzipDir: path.join(CACHE_DIR, "ui-v1.7.0"),
|
|
73
|
+
binaryPath: path.join(CACHE_DIR, "ui-v1.7.0", "server.bundle.js"),
|
|
74
|
+
opts: {
|
|
75
|
+
cacheDir: CACHE_DIR,
|
|
76
|
+
remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.7.0.zip",
|
|
77
|
+
expectedSize: 4053708,
|
|
78
|
+
expectedChecksum: "aea9ae19091df5974a88a8847aaf127c",
|
|
79
|
+
namePrefix: "ui",
|
|
97
80
|
},
|
|
81
|
+
},
|
|
98
82
|
pubsub: {
|
|
99
83
|
downloadPath: path.join(CACHE_DIR, "pubsub-emulator-0.1.0.zip"),
|
|
100
84
|
version: "0.1.0",
|
|
@@ -65,7 +65,7 @@ class ExtensionsEmulator {
|
|
|
65
65
|
if (!this.hasValidSource({ path: instance.localPath, extTarget: instance.localPath })) {
|
|
66
66
|
throw new error_1.FirebaseError(`Tried to emulate local extension at ${instance.localPath}, but it was missing required files.`);
|
|
67
67
|
}
|
|
68
|
-
return instance.localPath;
|
|
68
|
+
return path.resolve(instance.localPath);
|
|
69
69
|
}
|
|
70
70
|
else if (instance.ref) {
|
|
71
71
|
const ref = (0, refs_1.toExtensionVersionRef)(instance.ref);
|
|
@@ -152,6 +152,7 @@ class ExtensionsEmulator {
|
|
|
152
152
|
return emulatableBackend;
|
|
153
153
|
}
|
|
154
154
|
autoPopulatedParams(instance) {
|
|
155
|
+
var _a;
|
|
155
156
|
const projectId = this.args.projectId;
|
|
156
157
|
return {
|
|
157
158
|
PROJECT_ID: projectId !== null && projectId !== void 0 ? projectId : "",
|
|
@@ -159,6 +160,8 @@ class ExtensionsEmulator {
|
|
|
159
160
|
DATABASE_INSTANCE: projectId !== null && projectId !== void 0 ? projectId : "",
|
|
160
161
|
DATABASE_URL: `https://${projectId}.firebaseio.com`,
|
|
161
162
|
STORAGE_BUCKET: `${projectId}.appspot.com`,
|
|
163
|
+
ALLOWED_EVENT_TYPES: instance.allowedEventTypes ? instance.allowedEventTypes.join(",") : "",
|
|
164
|
+
EVENTARC_CHANNEL: (_a = instance.eventarcChannel) !== null && _a !== void 0 ? _a : "",
|
|
162
165
|
};
|
|
163
166
|
}
|
|
164
167
|
async checkAndWarnAPIs(instances) {
|
|
@@ -667,7 +667,10 @@ class FunctionsEmulator {
|
|
|
667
667
|
envs.FUNCTIONS_EMULATOR = "true";
|
|
668
668
|
envs.TZ = "UTC";
|
|
669
669
|
envs.FIREBASE_DEBUG_MODE = "true";
|
|
670
|
-
envs.FIREBASE_DEBUG_FEATURES = JSON.stringify({
|
|
670
|
+
envs.FIREBASE_DEBUG_FEATURES = JSON.stringify({
|
|
671
|
+
skipTokenVerification: true,
|
|
672
|
+
enableCors: true,
|
|
673
|
+
});
|
|
671
674
|
const firestoreEmulator = this.getEmulatorInfo(types_1.Emulators.FIRESTORE);
|
|
672
675
|
if (firestoreEmulator != null) {
|
|
673
676
|
envs[constants_1.Constants.FIRESTORE_EMULATOR_HOST] = (0, functionsEmulatorShared_1.formatHost)(firestoreEmulator);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.askForEventArcLocation = exports.askShouldCollectEventsConfig = exports.askForAllowedEventTypes = exports.askForEventsConfig = exports.checkAllowedEventTypesResponse = void 0;
|
|
4
|
+
const prompt_1 = require("../prompt");
|
|
5
|
+
const extensionsApi = require("../extensions/extensionsApi");
|
|
6
|
+
const utils = require("../utils");
|
|
7
|
+
const clc = require("cli-color");
|
|
8
|
+
const logger_1 = require("../logger");
|
|
9
|
+
const { marked } = require("marked");
|
|
10
|
+
function checkAllowedEventTypesResponse(response, validEvents) {
|
|
11
|
+
const validEventTypes = validEvents.map((e) => e.type);
|
|
12
|
+
if (response.length === 0) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
for (const e of response) {
|
|
16
|
+
if (!validEventTypes.includes(e)) {
|
|
17
|
+
utils.logWarning(`Unexpected event type '${e}' was configured to be emitted. This event type is not part of the extension spec.`);
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
exports.checkAllowedEventTypesResponse = checkAllowedEventTypesResponse;
|
|
24
|
+
async function askForEventsConfig(events, projectId, instanceId) {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
logger_1.logger.info(`\n${clc.bold("Enable Events")}: ${marked("If you enable events, you can write custom event handlers ([https://firebase.google.com/docs/extensions/install-extensions#eventarc](https://firebase.google.com/docs/extensions/install-extensions#eventarc)) that respond to these events.\n\nYou can always enable or disable events later. Events will be emitted via Eventarc. Fees apply ([https://cloud.google.com/eventarc/pricing](https://cloud.google.com/eventarc/pricing)).")}`);
|
|
27
|
+
if (!(await askShouldCollectEventsConfig())) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
let existingInstance;
|
|
31
|
+
try {
|
|
32
|
+
existingInstance = instanceId
|
|
33
|
+
? await extensionsApi.getInstance(projectId, instanceId)
|
|
34
|
+
: undefined;
|
|
35
|
+
}
|
|
36
|
+
catch (_c) {
|
|
37
|
+
}
|
|
38
|
+
const preselectedTypes = (_a = existingInstance === null || existingInstance === void 0 ? void 0 : existingInstance.config.allowedEventTypes) !== null && _a !== void 0 ? _a : [];
|
|
39
|
+
const oldLocation = (_b = existingInstance === null || existingInstance === void 0 ? void 0 : existingInstance.config.eventarcChannel) === null || _b === void 0 ? void 0 : _b.split("/")[3];
|
|
40
|
+
const location = await askForEventArcLocation(oldLocation);
|
|
41
|
+
const channel = `projects/${projectId}/locations/${location}/channels/firebase`;
|
|
42
|
+
const allowedEventTypes = await askForAllowedEventTypes(events, preselectedTypes);
|
|
43
|
+
return { channel, allowedEventTypes };
|
|
44
|
+
}
|
|
45
|
+
exports.askForEventsConfig = askForEventsConfig;
|
|
46
|
+
async function askForAllowedEventTypes(eventDescriptors, preselectedTypes) {
|
|
47
|
+
let valid = false;
|
|
48
|
+
let response = [];
|
|
49
|
+
const eventTypes = eventDescriptors.map((e, index) => ({
|
|
50
|
+
checked: false,
|
|
51
|
+
name: `${index + 1}. ${e.type}\n ${e.description}`,
|
|
52
|
+
value: e.type,
|
|
53
|
+
}));
|
|
54
|
+
while (!valid) {
|
|
55
|
+
response = await (0, prompt_1.promptOnce)({
|
|
56
|
+
name: "selectedEventTypesInput",
|
|
57
|
+
type: "checkbox",
|
|
58
|
+
default: preselectedTypes !== null && preselectedTypes !== void 0 ? preselectedTypes : [],
|
|
59
|
+
message: `Please select the events [${eventTypes.length} types total] that this extension is permitted to emit. ` +
|
|
60
|
+
"You can implement your own handlers that trigger when these events are emitted to customize the extension's behavior. ",
|
|
61
|
+
choices: eventTypes,
|
|
62
|
+
pageSize: 20,
|
|
63
|
+
});
|
|
64
|
+
valid = checkAllowedEventTypesResponse(response, eventDescriptors);
|
|
65
|
+
}
|
|
66
|
+
return response.filter((e) => e !== "");
|
|
67
|
+
}
|
|
68
|
+
exports.askForAllowedEventTypes = askForAllowedEventTypes;
|
|
69
|
+
async function askShouldCollectEventsConfig() {
|
|
70
|
+
return (0, prompt_1.promptOnce)({
|
|
71
|
+
type: "confirm",
|
|
72
|
+
name: "shouldCollectEvents",
|
|
73
|
+
message: `Would you like to enable events?`,
|
|
74
|
+
default: false,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
exports.askShouldCollectEventsConfig = askShouldCollectEventsConfig;
|
|
78
|
+
async function askForEventArcLocation(preselectedLocation) {
|
|
79
|
+
let valid = false;
|
|
80
|
+
const allowedRegions = ["us-central1", "us-west1", "europe-west4", "asia-northeast1"];
|
|
81
|
+
let location = "";
|
|
82
|
+
while (!valid) {
|
|
83
|
+
location = await (0, prompt_1.promptOnce)({
|
|
84
|
+
name: "input",
|
|
85
|
+
type: "list",
|
|
86
|
+
default: preselectedLocation !== null && preselectedLocation !== void 0 ? preselectedLocation : "us-central1",
|
|
87
|
+
message: "Which location would you like the Eventarc channel to live in? We recommend using the default option. A channel location that differs from the extension's Cloud Functions location can incur egress cost.",
|
|
88
|
+
choices: allowedRegions.map((e) => ({ checked: false, value: e })),
|
|
89
|
+
});
|
|
90
|
+
valid = allowedRegions.includes(location);
|
|
91
|
+
if (!valid) {
|
|
92
|
+
utils.logWarning(`Unexpected EventArc region '${location}' was specified. Allowed regions: ${allowedRegions.join(", ")}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return location;
|
|
96
|
+
}
|
|
97
|
+
exports.askForEventArcLocation = askForEventArcLocation;
|
package/lib/extensions/export.js
CHANGED
|
@@ -45,6 +45,7 @@ function displayExportInfo(withRef, withoutRef) {
|
|
|
45
45
|
}
|
|
46
46
|
exports.displayExportInfo = displayExportInfo;
|
|
47
47
|
function displaySpecs(specs) {
|
|
48
|
+
var _a;
|
|
48
49
|
for (let i = 0; i < specs.length; i++) {
|
|
49
50
|
const spec = specs[i];
|
|
50
51
|
logger_1.logger.info(`${i + 1}. ${(0, deploymentSummary_1.humanReadable)(spec)}`);
|
|
@@ -52,6 +53,12 @@ function displaySpecs(specs) {
|
|
|
52
53
|
for (const p of Object.entries(spec.params)) {
|
|
53
54
|
logger_1.logger.info(`\t${p[0]}=${p[1]}`);
|
|
54
55
|
}
|
|
56
|
+
if ((_a = spec.allowedEventTypes) === null || _a === void 0 ? void 0 : _a.length) {
|
|
57
|
+
logger_1.logger.info(`\tALLOWED_EVENTS=${spec.allowedEventTypes}`);
|
|
58
|
+
}
|
|
59
|
+
if (spec.eventarcChannel) {
|
|
60
|
+
logger_1.logger.info(`\tEVENTARC_CHANNEL=${spec.eventarcChannel}`);
|
|
61
|
+
}
|
|
55
62
|
logger_1.logger.info("");
|
|
56
63
|
}
|
|
57
64
|
}
|
|
@@ -58,6 +58,8 @@ async function createInstance(args) {
|
|
|
58
58
|
var _a, _b;
|
|
59
59
|
const config = {
|
|
60
60
|
params: args.params,
|
|
61
|
+
allowedEventTypes: args.allowedEventTypes,
|
|
62
|
+
eventarcChannel: args.eventarcChannel,
|
|
61
63
|
};
|
|
62
64
|
if (args.extensionSource && args.extensionVersionRef) {
|
|
63
65
|
throw new error_1.FirebaseError("ExtensionSource and ExtensionVersion both provided, but only one should be.");
|
|
@@ -73,6 +75,12 @@ async function createInstance(args) {
|
|
|
73
75
|
else {
|
|
74
76
|
throw new error_1.FirebaseError("No ExtensionVersion or ExtensionSource provided but one is required.");
|
|
75
77
|
}
|
|
78
|
+
if (args.allowedEventTypes) {
|
|
79
|
+
config.allowedEventTypes = args.allowedEventTypes;
|
|
80
|
+
}
|
|
81
|
+
if (args.eventarcChannel) {
|
|
82
|
+
config.eventarcChannel = args.eventarcChannel;
|
|
83
|
+
}
|
|
76
84
|
return createInstanceHelper(args.projectId, args.instanceId, config, args.validateOnly);
|
|
77
85
|
}
|
|
78
86
|
exports.createInstance = createInstance;
|
|
@@ -88,8 +96,16 @@ async function deleteInstance(projectId, instanceId) {
|
|
|
88
96
|
}
|
|
89
97
|
exports.deleteInstance = deleteInstance;
|
|
90
98
|
async function getInstance(projectId, instanceId) {
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
try {
|
|
100
|
+
const res = await apiClient.get(`/projects/${projectId}/instances/${instanceId}`);
|
|
101
|
+
return res.body;
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
if (err.status === 404) {
|
|
105
|
+
throw new error_1.FirebaseError(`Extension instance '${clc.bold(instanceId)}' not found in project '${clc.bold(projectId)}'.`, { status: 404 });
|
|
106
|
+
}
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
93
109
|
}
|
|
94
110
|
exports.getInstance = getInstance;
|
|
95
111
|
async function listInstances(projectId) {
|
|
@@ -114,7 +130,7 @@ async function listInstances(projectId) {
|
|
|
114
130
|
exports.listInstances = listInstances;
|
|
115
131
|
async function configureInstance(args) {
|
|
116
132
|
var _a;
|
|
117
|
-
const
|
|
133
|
+
const reqBody = {
|
|
118
134
|
projectId: args.projectId,
|
|
119
135
|
instanceId: args.instanceId,
|
|
120
136
|
updateMask: "config.params",
|
|
@@ -124,8 +140,16 @@ async function configureInstance(args) {
|
|
|
124
140
|
params: args.params,
|
|
125
141
|
},
|
|
126
142
|
},
|
|
127
|
-
}
|
|
128
|
-
|
|
143
|
+
};
|
|
144
|
+
if (args.canEmitEvents) {
|
|
145
|
+
if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
|
|
146
|
+
throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
|
|
147
|
+
}
|
|
148
|
+
reqBody.data.config.allowedEventTypes = args.allowedEventTypes;
|
|
149
|
+
reqBody.data.config.eventarcChannel = args.eventarcChannel;
|
|
150
|
+
}
|
|
151
|
+
reqBody.updateMask += ",config.allowed_event_types,config.eventarc_channel";
|
|
152
|
+
return patchInstance(reqBody);
|
|
129
153
|
}
|
|
130
154
|
exports.configureInstance = configureInstance;
|
|
131
155
|
async function updateInstance(args) {
|
|
@@ -140,7 +164,15 @@ async function updateInstance(args) {
|
|
|
140
164
|
body.config.params = args.params;
|
|
141
165
|
updateMask += ",config.params";
|
|
142
166
|
}
|
|
143
|
-
|
|
167
|
+
if (args.canEmitEvents) {
|
|
168
|
+
if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
|
|
169
|
+
throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
|
|
170
|
+
}
|
|
171
|
+
body.config.allowedEventTypes = args.allowedEventTypes;
|
|
172
|
+
body.config.eventarcChannel = args.eventarcChannel;
|
|
173
|
+
}
|
|
174
|
+
updateMask += ",config.allowed_event_types,config.eventarc_channel";
|
|
175
|
+
return patchInstance({
|
|
144
176
|
projectId: args.projectId,
|
|
145
177
|
instanceId: args.instanceId,
|
|
146
178
|
updateMask,
|
|
@@ -163,7 +195,15 @@ async function updateInstanceFromRegistry(args) {
|
|
|
163
195
|
body.config.params = args.params;
|
|
164
196
|
updateMask += ",config.params";
|
|
165
197
|
}
|
|
166
|
-
|
|
198
|
+
if (args.canEmitEvents) {
|
|
199
|
+
if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
|
|
200
|
+
throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
|
|
201
|
+
}
|
|
202
|
+
body.config.allowedEventTypes = args.allowedEventTypes;
|
|
203
|
+
body.config.eventarcChannel = args.eventarcChannel;
|
|
204
|
+
}
|
|
205
|
+
updateMask += ",config.allowed_event_types,config.eventarc_channel";
|
|
206
|
+
return patchInstance({
|
|
167
207
|
projectId: args.projectId,
|
|
168
208
|
instanceId: args.instanceId,
|
|
169
209
|
updateMask,
|
|
@@ -48,7 +48,7 @@ async function writeLocalSecrets(specs, config, force) {
|
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
50
|
const writeBuffer = {};
|
|
51
|
-
const locallyOverridenSecretParams = extensionSpec.params.filter((p) => p.type === extensionsApi_1.ParamType.SECRET && spec.params[p.param].local);
|
|
51
|
+
const locallyOverridenSecretParams = extensionSpec.params.filter((p) => { var _a; return p.type === extensionsApi_1.ParamType.SECRET && ((_a = spec.params[p.param]) === null || _a === void 0 ? void 0 : _a.local); });
|
|
52
52
|
for (const paramSpec of locallyOverridenSecretParams) {
|
|
53
53
|
const key = paramSpec.param;
|
|
54
54
|
const localValue = spec.params[key].local;
|
|
@@ -59,13 +59,16 @@ async function displayChanges(args) {
|
|
|
59
59
|
}
|
|
60
60
|
exports.displayChanges = displayChanges;
|
|
61
61
|
async function update(updateOptions) {
|
|
62
|
-
const { projectId, instanceId, source, extRef, params } = updateOptions;
|
|
62
|
+
const { projectId, instanceId, source, extRef, params, canEmitEvents, allowedEventTypes, eventarcChannel, } = updateOptions;
|
|
63
63
|
if (extRef) {
|
|
64
64
|
return await extensionsApi.updateInstanceFromRegistry({
|
|
65
65
|
projectId,
|
|
66
66
|
instanceId,
|
|
67
67
|
extRef,
|
|
68
68
|
params,
|
|
69
|
+
canEmitEvents,
|
|
70
|
+
allowedEventTypes,
|
|
71
|
+
eventarcChannel,
|
|
69
72
|
});
|
|
70
73
|
}
|
|
71
74
|
else if (source) {
|
|
@@ -74,6 +77,9 @@ async function update(updateOptions) {
|
|
|
74
77
|
instanceId,
|
|
75
78
|
extensionSource: source,
|
|
76
79
|
params,
|
|
80
|
+
canEmitEvents,
|
|
81
|
+
allowedEventTypes,
|
|
82
|
+
eventarcChannel,
|
|
77
83
|
});
|
|
78
84
|
}
|
|
79
85
|
throw new error_1.FirebaseError(`Neither a source nor a version of the extension was supplied for ${instanceId}. Please make sure this is a valid extension and try again.`);
|
|
@@ -58,13 +58,13 @@ async function displayWarningsForDeploy(instancesToCreate) {
|
|
|
58
58
|
const experimental = nonEapExtensions.filter((i) => i.extension.registryLaunchStage === extensionsApi_1.RegistryLaunchStage.EXPERIMENTAL);
|
|
59
59
|
if (experimental.length) {
|
|
60
60
|
const humanReadableList = experimental.map((i) => `\t${(0, deploymentSummary_1.humanReadable)(i)}`).join("\n");
|
|
61
|
-
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`The following are instances of ${clc.bold("experimental")} extensions.They may not be production-ready. Their functionality may change in backward-incompatible ways before their official release, or they may be discontinued.\n${humanReadableList}\n
|
|
61
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`The following are instances of ${clc.bold("experimental")} extensions.They may not be production-ready. Their functionality may change in backward-incompatible ways before their official release, or they may be discontinued.\n${humanReadableList}\n`, { gfm: false }));
|
|
62
62
|
}
|
|
63
63
|
if (eapExtensions.length) {
|
|
64
64
|
const humanReadableList = eapExtensions.map(toListEntry).join("\n");
|
|
65
|
-
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`These extensions are in preview and are built by a developer in the Extensions Publisher Early Access Program (http://bit.ly/firex-provider. Their functionality might change in backwards-incompatible ways. Since these extensions aren't built by Firebase, reach out to their publisher with questions about them.` +
|
|
65
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`These extensions are in preview and are built by a developer in the Extensions Publisher Early Access Program (http://bit.ly/firex-provider). Their functionality might change in backwards-incompatible ways. Since these extensions aren't built by Firebase, reach out to their publisher with questions about them.` +
|
|
66
66
|
` They are provided “AS IS”, without any warranty, express or implied, from Google.` +
|
|
67
|
-
` Google disclaims all liability for any damages, direct or indirect, resulting from the use of these extensions\n${humanReadableList}
|
|
67
|
+
` Google disclaims all liability for any damages, direct or indirect, resulting from the use of these extensions\n${humanReadableList}`, { gfm: false }));
|
|
68
68
|
}
|
|
69
69
|
return experimental.length > 0 || eapExtensions.length > 0;
|
|
70
70
|
}
|