firebase-tools 11.14.1 → 11.14.2
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 +3 -3
- package/lib/commands/deploy.js +3 -3
- package/lib/commands/emulators-start.js +20 -16
- package/lib/deploy/functions/params.js +9 -1
- package/lib/deploy/functions/release/fabricator.js +1 -0
- package/lib/deploy/hosting/convertConfig.js +2 -1
- package/lib/deploy/index.js +10 -0
- package/lib/emulator/ExpressBasedEmulator.js +92 -0
- package/lib/emulator/auth/cloudFunctions.js +3 -12
- package/lib/emulator/auth/index.js +2 -2
- package/lib/emulator/auth/utils.js +1 -1
- package/lib/emulator/commandUtils.js +21 -44
- package/lib/emulator/constants.js +2 -9
- package/lib/emulator/controller.js +129 -130
- package/lib/emulator/databaseEmulator.js +3 -8
- package/lib/emulator/dns.js +49 -0
- package/lib/emulator/downloadableEmulators.js +19 -13
- package/lib/emulator/emulatorLogger.js +14 -7
- package/lib/emulator/env.js +35 -0
- package/lib/emulator/eventarcEmulator.js +2 -8
- package/lib/emulator/extensions/postinstall.js +8 -8
- package/lib/emulator/extensionsEmulator.js +7 -7
- package/lib/emulator/firestoreEmulator.js +3 -11
- package/lib/emulator/functionsEmulator.js +24 -60
- package/lib/emulator/functionsEmulatorShared.js +5 -3
- package/lib/emulator/functionsEmulatorShell.js +1 -1
- package/lib/emulator/hub.js +50 -58
- package/lib/emulator/hubClient.js +24 -10
- package/lib/emulator/hubExport.js +3 -11
- package/lib/emulator/portUtils.js +208 -29
- package/lib/emulator/pubsubEmulator.js +11 -12
- package/lib/emulator/registry.js +11 -20
- package/lib/emulator/storage/apis/gcloud.js +3 -6
- package/lib/emulator/storage/cloudFunctions.js +2 -7
- package/lib/emulator/storage/metadata.js +11 -5
- package/lib/emulator/storage/rules/runtime.js +1 -6
- package/lib/emulator/ui.js +6 -7
- package/lib/experiments.js +10 -1
- package/lib/extensions/displayExtensionInfo.js +43 -6
- package/lib/extensions/secretsUtils.js +2 -1
- package/lib/frameworks/angular/index.js +1 -1
- package/lib/frameworks/express/index.js +1 -1
- package/lib/frameworks/index.js +33 -17
- package/lib/frameworks/next/index.js +1 -1
- package/lib/frameworks/nuxt/index.js +1 -1
- package/lib/frameworks/vite/index.js +1 -1
- package/lib/functions/ensureTargeted.js +21 -0
- package/lib/functions/env.js +33 -19
- package/lib/functionsShellCommandAction.js +12 -5
- package/lib/gcp/run.js +0 -1
- package/lib/handlePreviewToggles.js +2 -2
- package/lib/hosting/functionsProxy.js +2 -3
- package/lib/hosting/implicitInit.js +2 -11
- package/lib/hosting/runTags.js +3 -4
- package/lib/serve/functions.js +8 -10
- package/lib/serve/hosting.js +10 -4
- package/lib/serve/index.js +2 -2
- package/lib/utils.js +14 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/templates/hosting/init.js +3 -2
- package/lib/emulator/emulatorServer.js +0 -29
|
@@ -7,7 +7,6 @@ const types_1 = require("./types");
|
|
|
7
7
|
const utils_1 = require("../utils");
|
|
8
8
|
const emulatorLogger_1 = require("./emulatorLogger");
|
|
9
9
|
const registry_1 = require("./registry");
|
|
10
|
-
const apiv2_1 = require("../apiv2");
|
|
11
10
|
const error_1 = require("../error");
|
|
12
11
|
class EventarcEmulator {
|
|
13
12
|
constructor(args) {
|
|
@@ -77,21 +76,16 @@ class EventarcEmulator {
|
|
|
77
76
|
return hub;
|
|
78
77
|
}
|
|
79
78
|
async triggerCustomEventFunction(channel, event) {
|
|
80
|
-
|
|
81
|
-
if (!functionsEmulator) {
|
|
79
|
+
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.FUNCTIONS)) {
|
|
82
80
|
this.logger.log("INFO", "Functions emulator not found. This should not happen.");
|
|
83
81
|
return Promise.reject();
|
|
84
82
|
}
|
|
85
83
|
const key = `${event.type}-${channel}`;
|
|
86
84
|
const triggers = this.customEvents[key] || [];
|
|
87
|
-
const apiClient = new apiv2_1.Client({
|
|
88
|
-
urlPrefix: `http://${registry_1.EmulatorRegistry.getInfoHostString(functionsEmulator.getInfo())}`,
|
|
89
|
-
auth: false,
|
|
90
|
-
});
|
|
91
85
|
return await Promise.all(triggers
|
|
92
86
|
.filter((trigger) => !trigger.eventTrigger.eventFilters ||
|
|
93
87
|
this.matchesAll(event, trigger.eventTrigger.eventFilters))
|
|
94
|
-
.map((trigger) =>
|
|
88
|
+
.map((trigger) => registry_1.EmulatorRegistry.client(types_1.Emulators.FUNCTIONS)
|
|
95
89
|
.request({
|
|
96
90
|
method: "POST",
|
|
97
91
|
path: `/functions/projects/${trigger.projectId}/triggers/${trigger.triggerName}`,
|
|
@@ -4,33 +4,33 @@ exports.replaceConsoleLinks = void 0;
|
|
|
4
4
|
const registry_1 = require("../registry");
|
|
5
5
|
const types_1 = require("../types");
|
|
6
6
|
function replaceConsoleLinks(postinstall) {
|
|
7
|
-
const
|
|
8
|
-
const uiUrl =
|
|
7
|
+
const uiRunning = registry_1.EmulatorRegistry.isRunning(types_1.Emulators.UI);
|
|
8
|
+
const uiUrl = uiRunning ? registry_1.EmulatorRegistry.url(types_1.Emulators.UI).toString() : "unknown";
|
|
9
9
|
let subbedPostinstall = postinstall;
|
|
10
10
|
const linkReplacements = new Map([
|
|
11
11
|
[
|
|
12
12
|
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/storage[A-Za-z0-9\/-]*(?=[\)\]\s])/,
|
|
13
|
-
`${uiUrl}
|
|
13
|
+
`${uiUrl}${types_1.Emulators.STORAGE}`,
|
|
14
14
|
],
|
|
15
15
|
[
|
|
16
16
|
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/firestore[A-Za-z0-9\/-]*(?=[\)\]\s])/,
|
|
17
|
-
`${uiUrl}
|
|
17
|
+
`${uiUrl}${types_1.Emulators.FIRESTORE}`,
|
|
18
18
|
],
|
|
19
19
|
[
|
|
20
20
|
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/database[A-Za-z0-9\/-]*(?=[\)\]\s])/,
|
|
21
|
-
`${uiUrl}
|
|
21
|
+
`${uiUrl}${types_1.Emulators.DATABASE}`,
|
|
22
22
|
],
|
|
23
23
|
[
|
|
24
24
|
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/authentication[A-Za-z0-9\/-]*(?=[\)\]\s])/,
|
|
25
|
-
`${uiUrl}
|
|
25
|
+
`${uiUrl}${types_1.Emulators.AUTH}`,
|
|
26
26
|
],
|
|
27
27
|
[
|
|
28
28
|
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/functions[A-Za-z0-9\/-]*(?=[\)\]\s])/,
|
|
29
|
-
`${uiUrl}
|
|
29
|
+
`${uiUrl}logs`,
|
|
30
30
|
],
|
|
31
31
|
[
|
|
32
32
|
/(http[s]?:\/\/)?console\.firebase\.google\.com\/(u\/[0-9]\/)?project\/[A-Za-z0-9-]+\/extensions[A-Za-z0-9\/-]*(?=[\)\]\s])/,
|
|
33
|
-
`${uiUrl}
|
|
33
|
+
`${uiUrl}${types_1.Emulators.EXTENSIONS}`,
|
|
34
34
|
],
|
|
35
35
|
]);
|
|
36
36
|
for (const [consoleLinkRegex, replacement] of linkReplacements) {
|
|
@@ -40,11 +40,11 @@ class ExtensionsEmulator {
|
|
|
40
40
|
return Promise.resolve();
|
|
41
41
|
}
|
|
42
42
|
getInfo() {
|
|
43
|
-
const
|
|
44
|
-
if (!
|
|
43
|
+
const functionsEmulator = registry_1.EmulatorRegistry.get(types_1.Emulators.FUNCTIONS);
|
|
44
|
+
if (!functionsEmulator) {
|
|
45
45
|
throw new error_1.FirebaseError("Extensions Emulator is running but Functions emulator is not. This should never happen.");
|
|
46
46
|
}
|
|
47
|
-
return
|
|
47
|
+
return functionsEmulator.getInfo();
|
|
48
48
|
}
|
|
49
49
|
getName() {
|
|
50
50
|
return types_1.Emulators.EXTENSIONS;
|
|
@@ -222,12 +222,12 @@ class ExtensionsEmulator {
|
|
|
222
222
|
return filteredBackends;
|
|
223
223
|
}
|
|
224
224
|
extensionDetailsUILink(backend) {
|
|
225
|
-
|
|
226
|
-
if (!uiInfo || !backend.extensionInstanceId) {
|
|
225
|
+
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.UI) || !backend.extensionInstanceId) {
|
|
227
226
|
return "";
|
|
228
227
|
}
|
|
229
|
-
const uiUrl = registry_1.EmulatorRegistry.
|
|
230
|
-
|
|
228
|
+
const uiUrl = registry_1.EmulatorRegistry.url(types_1.Emulators.UI);
|
|
229
|
+
uiUrl.pathname = `/${types_1.Emulators.EXTENSIONS}/${backend.extensionInstanceId}`;
|
|
230
|
+
return clc.underline(clc.bold(uiUrl.toString()));
|
|
231
231
|
}
|
|
232
232
|
extensionsInfoTable(options) {
|
|
233
233
|
var _a;
|
|
@@ -10,15 +10,13 @@ const downloadableEmulators = require("./downloadableEmulators");
|
|
|
10
10
|
const types_1 = require("../emulator/types");
|
|
11
11
|
const registry_1 = require("./registry");
|
|
12
12
|
const constants_1 = require("./constants");
|
|
13
|
-
const apiv2_1 = require("../apiv2");
|
|
14
13
|
class FirestoreEmulator {
|
|
15
14
|
constructor(args) {
|
|
16
15
|
this.args = args;
|
|
17
16
|
}
|
|
18
17
|
async start() {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.args.functions_emulator = registry_1.EmulatorRegistry.getInfoHostString(functionsInfo);
|
|
18
|
+
if (registry_1.EmulatorRegistry.isRunning(types_1.Emulators.FUNCTIONS)) {
|
|
19
|
+
this.args.functions_emulator = registry_1.EmulatorRegistry.url(types_1.Emulators.FUNCTIONS).host;
|
|
22
20
|
}
|
|
23
21
|
if (this.args.rules && this.args.project_id) {
|
|
24
22
|
const rulesPath = this.args.rules;
|
|
@@ -71,7 +69,6 @@ class FirestoreEmulator {
|
|
|
71
69
|
}
|
|
72
70
|
async updateRules(content) {
|
|
73
71
|
const projectId = this.args.project_id;
|
|
74
|
-
const info = this.getInfo();
|
|
75
72
|
const body = {
|
|
76
73
|
ignore_errors: true,
|
|
77
74
|
rules: {
|
|
@@ -83,11 +80,7 @@ class FirestoreEmulator {
|
|
|
83
80
|
],
|
|
84
81
|
},
|
|
85
82
|
};
|
|
86
|
-
const
|
|
87
|
-
urlPrefix: `http://${registry_1.EmulatorRegistry.getInfoHostString(info)}`,
|
|
88
|
-
auth: false,
|
|
89
|
-
});
|
|
90
|
-
const res = await client.put(`/emulator/v1/projects/${projectId}:securityRules`, body);
|
|
83
|
+
const res = await registry_1.EmulatorRegistry.client(types_1.Emulators.FIRESTORE).put(`/emulator/v1/projects/${projectId}:securityRules`, body);
|
|
91
84
|
if (res.body && Array.isArray(res.body.issues)) {
|
|
92
85
|
return res.body.issues;
|
|
93
86
|
}
|
|
@@ -101,4 +94,3 @@ class FirestoreEmulator {
|
|
|
101
94
|
}
|
|
102
95
|
}
|
|
103
96
|
exports.FirestoreEmulator = FirestoreEmulator;
|
|
104
|
-
FirestoreEmulator.FIRESTORE_EMULATOR_ENV_ALT = "FIREBASE_FIRESTORE_EMULATOR_ADDRESS";
|
|
@@ -31,8 +31,8 @@ const runtimes = require("../deploy/functions/runtimes");
|
|
|
31
31
|
const backend = require("../deploy/functions/backend");
|
|
32
32
|
const functionsEnv = require("../functions/env");
|
|
33
33
|
const v1_1 = require("../functions/events/v1");
|
|
34
|
-
const apiv2_1 = require("../apiv2");
|
|
35
34
|
const build_1 = require("../deploy/functions/build");
|
|
35
|
+
const env_1 = require("./env");
|
|
36
36
|
const EVENT_INVOKE = "functions:invoke";
|
|
37
37
|
const EVENT_INVOKE_GA4 = "functions_invoke";
|
|
38
38
|
const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
|
|
@@ -60,8 +60,16 @@ class FunctionsEmulator {
|
|
|
60
60
|
}
|
|
61
61
|
this.workQueue = new workQueue_1.WorkQueue(mode);
|
|
62
62
|
}
|
|
63
|
-
static getHttpFunctionUrl(
|
|
64
|
-
|
|
63
|
+
static getHttpFunctionUrl(projectId, name, region, info) {
|
|
64
|
+
let url;
|
|
65
|
+
if (info) {
|
|
66
|
+
url = new url_1.URL("http://" + (0, functionsEmulatorShared_1.formatHost)(info));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
url = registry_1.EmulatorRegistry.url(types_1.Emulators.FUNCTIONS);
|
|
70
|
+
}
|
|
71
|
+
url.pathname = `/${projectId}/${region}/${name}`;
|
|
72
|
+
return url.toString();
|
|
65
73
|
}
|
|
66
74
|
async getCredentialsEnvironment() {
|
|
67
75
|
const credentialEnv = {};
|
|
@@ -124,7 +132,7 @@ class FunctionsEmulator {
|
|
|
124
132
|
this.workQueue.submit(() => {
|
|
125
133
|
return new Promise((resolve, reject) => {
|
|
126
134
|
const trigReq = http.request({
|
|
127
|
-
host,
|
|
135
|
+
host: (0, utils_1.connectableHostname)(host),
|
|
128
136
|
port,
|
|
129
137
|
method: req.method,
|
|
130
138
|
path: `/functions/projects/${projectId}/triggers/${triggerId}`,
|
|
@@ -312,10 +320,9 @@ class FunctionsEmulator {
|
|
|
312
320
|
}
|
|
313
321
|
let added = false;
|
|
314
322
|
let url = undefined;
|
|
315
|
-
const { host, port } = this.getInfo();
|
|
316
323
|
if (definition.httpsTrigger) {
|
|
317
324
|
added = true;
|
|
318
|
-
url = FunctionsEmulator.getHttpFunctionUrl(
|
|
325
|
+
url = FunctionsEmulator.getHttpFunctionUrl(this.args.projectId, definition.name, definition.region);
|
|
319
326
|
}
|
|
320
327
|
else if (definition.eventTrigger) {
|
|
321
328
|
const service = (0, functionsEmulatorShared_1.getFunctionService)(definition);
|
|
@@ -346,8 +353,7 @@ class FunctionsEmulator {
|
|
|
346
353
|
}
|
|
347
354
|
}
|
|
348
355
|
else if (definition.blockingTrigger) {
|
|
349
|
-
|
|
350
|
-
url = FunctionsEmulator.getHttpFunctionUrl(host, port, this.args.projectId, definition.name, definition.region);
|
|
356
|
+
url = FunctionsEmulator.getHttpFunctionUrl(this.args.projectId, definition.name, definition.region);
|
|
351
357
|
added = this.addBlockingTrigger(url, definition.blockingTrigger);
|
|
352
358
|
}
|
|
353
359
|
else {
|
|
@@ -374,19 +380,14 @@ class FunctionsEmulator {
|
|
|
374
380
|
}
|
|
375
381
|
}
|
|
376
382
|
addEventarcTrigger(projectId, key, eventTrigger) {
|
|
377
|
-
|
|
378
|
-
if (!eventarcEmu) {
|
|
383
|
+
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EVENTARC)) {
|
|
379
384
|
return Promise.resolve(false);
|
|
380
385
|
}
|
|
381
386
|
const bundle = {
|
|
382
387
|
eventTrigger: Object.assign(Object.assign({}, eventTrigger), { service: "eventarc.googleapis.com" }),
|
|
383
388
|
};
|
|
384
389
|
logger_1.logger.debug(`addEventarcTrigger`, JSON.stringify(bundle));
|
|
385
|
-
|
|
386
|
-
urlPrefix: `http://${registry_1.EmulatorRegistry.getInfoHostString(eventarcEmu.getInfo())}`,
|
|
387
|
-
auth: false,
|
|
388
|
-
});
|
|
389
|
-
return client
|
|
390
|
+
return registry_1.EmulatorRegistry.client(types_1.Emulators.EVENTARC)
|
|
390
391
|
.post(`/emulator/v1/projects/${projectId}/triggers/${key}`, bundle)
|
|
391
392
|
.then(() => true)
|
|
392
393
|
.catch((err) => {
|
|
@@ -399,16 +400,12 @@ class FunctionsEmulator {
|
|
|
399
400
|
!this.blockingFunctionsConfig.forwardInboundCredentials) {
|
|
400
401
|
return;
|
|
401
402
|
}
|
|
402
|
-
|
|
403
|
-
if (!authEmu) {
|
|
403
|
+
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.AUTH)) {
|
|
404
404
|
return;
|
|
405
405
|
}
|
|
406
406
|
const path = `/identitytoolkit.googleapis.com/v2/projects/${this.getProjectId()}/config?updateMask=blockingFunctions`;
|
|
407
407
|
try {
|
|
408
|
-
const client =
|
|
409
|
-
urlPrefix: `http://${registry_1.EmulatorRegistry.getInfoHostString(authEmu.getInfo())}`,
|
|
410
|
-
auth: false,
|
|
411
|
-
});
|
|
408
|
+
const client = registry_1.EmulatorRegistry.client(types_1.Emulators.AUTH);
|
|
412
409
|
await client.patch(path, { blockingFunctions: this.blockingFunctionsConfig }, {
|
|
413
410
|
headers: { Authorization: "Bearer owner" },
|
|
414
411
|
});
|
|
@@ -461,18 +458,14 @@ class FunctionsEmulator {
|
|
|
461
458
|
return { bundle, apiPath, instance };
|
|
462
459
|
}
|
|
463
460
|
async addRealtimeDatabaseTrigger(projectId, key, eventTrigger, signature, region) {
|
|
464
|
-
|
|
465
|
-
if (!databaseEmu) {
|
|
461
|
+
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.DATABASE)) {
|
|
466
462
|
return false;
|
|
467
463
|
}
|
|
468
464
|
const { bundle, apiPath, instance } = signature === "cloudevent"
|
|
469
465
|
? this.getV2DatabaseApiAttributes(projectId, key, eventTrigger, region)
|
|
470
466
|
: this.getV1DatabaseApiAttributes(projectId, key, eventTrigger);
|
|
471
467
|
logger_1.logger.debug(`addRealtimeDatabaseTrigger[${instance}]`, JSON.stringify(bundle));
|
|
472
|
-
const client =
|
|
473
|
-
urlPrefix: `http://${registry_1.EmulatorRegistry.getInfoHostString(databaseEmu.getInfo())}`,
|
|
474
|
-
auth: false,
|
|
475
|
-
});
|
|
468
|
+
const client = registry_1.EmulatorRegistry.client(types_1.Emulators.DATABASE);
|
|
476
469
|
try {
|
|
477
470
|
await client.post(apiPath, bundle, { headers: { Authorization: "Bearer owner" } });
|
|
478
471
|
}
|
|
@@ -483,18 +476,14 @@ class FunctionsEmulator {
|
|
|
483
476
|
return true;
|
|
484
477
|
}
|
|
485
478
|
async addFirestoreTrigger(projectId, key, eventTrigger) {
|
|
486
|
-
|
|
487
|
-
if (!firestoreEmu) {
|
|
479
|
+
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.FIRESTORE)) {
|
|
488
480
|
return Promise.resolve(false);
|
|
489
481
|
}
|
|
490
482
|
const bundle = JSON.stringify({
|
|
491
483
|
eventTrigger: Object.assign(Object.assign({}, eventTrigger), { service: "firestore.googleapis.com" }),
|
|
492
484
|
});
|
|
493
485
|
logger_1.logger.debug(`addFirestoreTrigger`, JSON.stringify(bundle));
|
|
494
|
-
const client =
|
|
495
|
-
urlPrefix: `http://${registry_1.EmulatorRegistry.getInfoHostString(firestoreEmu.getInfo())}`,
|
|
496
|
-
auth: false,
|
|
497
|
-
});
|
|
486
|
+
const client = registry_1.EmulatorRegistry.client(types_1.Emulators.FIRESTORE);
|
|
498
487
|
try {
|
|
499
488
|
await client.put(`/emulator/v1/projects/${projectId}/triggers/${key}`, bundle);
|
|
500
489
|
}
|
|
@@ -718,32 +707,7 @@ class FunctionsEmulator {
|
|
|
718
707
|
skipTokenVerification: true,
|
|
719
708
|
enableCors: true,
|
|
720
709
|
});
|
|
721
|
-
|
|
722
|
-
if (firestoreEmulator != null) {
|
|
723
|
-
envs[constants_1.Constants.FIRESTORE_EMULATOR_HOST] = (0, functionsEmulatorShared_1.formatHost)(firestoreEmulator);
|
|
724
|
-
}
|
|
725
|
-
const databaseEmulator = this.getEmulatorInfo(types_1.Emulators.DATABASE);
|
|
726
|
-
if (databaseEmulator) {
|
|
727
|
-
envs[constants_1.Constants.FIREBASE_DATABASE_EMULATOR_HOST] = (0, functionsEmulatorShared_1.formatHost)(databaseEmulator);
|
|
728
|
-
}
|
|
729
|
-
const authEmulator = this.getEmulatorInfo(types_1.Emulators.AUTH);
|
|
730
|
-
if (authEmulator) {
|
|
731
|
-
envs[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST] = (0, functionsEmulatorShared_1.formatHost)(authEmulator);
|
|
732
|
-
}
|
|
733
|
-
const storageEmulator = this.getEmulatorInfo(types_1.Emulators.STORAGE);
|
|
734
|
-
if (storageEmulator) {
|
|
735
|
-
envs[constants_1.Constants.FIREBASE_STORAGE_EMULATOR_HOST] = (0, functionsEmulatorShared_1.formatHost)(storageEmulator);
|
|
736
|
-
envs[constants_1.Constants.CLOUD_STORAGE_EMULATOR_HOST] = `http://${(0, functionsEmulatorShared_1.formatHost)(storageEmulator)}`;
|
|
737
|
-
}
|
|
738
|
-
const pubsubEmulator = this.getEmulatorInfo(types_1.Emulators.PUBSUB);
|
|
739
|
-
if (pubsubEmulator) {
|
|
740
|
-
const pubsubHost = (0, functionsEmulatorShared_1.formatHost)(pubsubEmulator);
|
|
741
|
-
process.env.PUBSUB_EMULATOR_HOST = pubsubHost;
|
|
742
|
-
}
|
|
743
|
-
const eventarcEmulator = this.getEmulatorInfo(types_1.Emulators.EVENTARC);
|
|
744
|
-
if (eventarcEmulator) {
|
|
745
|
-
envs[constants_1.Constants.CLOUD_EVENTARC_EMULATOR_HOST] = `http://${(0, functionsEmulatorShared_1.formatHost)(eventarcEmulator)}`;
|
|
746
|
-
}
|
|
710
|
+
(0, env_1.setEnvVarsForEmulators)(envs);
|
|
747
711
|
if (this.args.debugPort) {
|
|
748
712
|
envs["FUNCTION_DEBUG_MODE"] = "true";
|
|
749
713
|
}
|
|
@@ -821,7 +785,7 @@ class FunctionsEmulator {
|
|
|
821
785
|
}
|
|
822
786
|
else {
|
|
823
787
|
const { host } = this.getInfo();
|
|
824
|
-
args.unshift(`--inspect=${host}:${this.args.debugPort}`);
|
|
788
|
+
args.unshift(`--inspect=${(0, utils_1.connectableHostname)(host)}:${this.args.debugPort}`);
|
|
825
789
|
}
|
|
826
790
|
}
|
|
827
791
|
const pnpPath = path.join(backend.functionsDir, ".pnp.js");
|
|
@@ -14,6 +14,7 @@ const postinstall_1 = require("./extensions/postinstall");
|
|
|
14
14
|
const services_1 = require("../deploy/functions/services");
|
|
15
15
|
const prepare_1 = require("../deploy/functions/prepare");
|
|
16
16
|
const events = require("../functions/events");
|
|
17
|
+
const utils_1 = require("../utils");
|
|
17
18
|
const V2_EVENTS = [
|
|
18
19
|
events.v2.PUBSUB_PUBLISH_EVENT,
|
|
19
20
|
...events.v2.STORAGE_EVENTS,
|
|
@@ -246,11 +247,12 @@ function findModuleRoot(moduleName, filepath) {
|
|
|
246
247
|
}
|
|
247
248
|
exports.findModuleRoot = findModuleRoot;
|
|
248
249
|
function formatHost(info) {
|
|
249
|
-
|
|
250
|
-
|
|
250
|
+
const host = (0, utils_1.connectableHostname)(info.host);
|
|
251
|
+
if (host.includes(":")) {
|
|
252
|
+
return `[${host}]:${info.port}`;
|
|
251
253
|
}
|
|
252
254
|
else {
|
|
253
|
-
return `${
|
|
255
|
+
return `${host}:${info.port}`;
|
|
254
256
|
}
|
|
255
257
|
}
|
|
256
258
|
exports.formatHost = formatHost;
|
|
@@ -16,7 +16,7 @@ class FunctionsEmulatorShell {
|
|
|
16
16
|
utils.logLabeledBullet("functions", `Loaded functions: ${entryPoints.join(", ")}`);
|
|
17
17
|
for (const trigger of this.triggers) {
|
|
18
18
|
if (trigger.httpsTrigger) {
|
|
19
|
-
this.urls[trigger.id] = functionsEmulator_1.FunctionsEmulator.getHttpFunctionUrl(this.emu.
|
|
19
|
+
this.urls[trigger.id] = functionsEmulator_1.FunctionsEmulator.getHttpFunctionUrl(this.emu.getProjectId(), trigger.name, trigger.region, this.emu.getInfo());
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
}
|
package/lib/emulator/hub.js
CHANGED
|
@@ -1,37 +1,58 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EmulatorHub = void 0;
|
|
4
|
-
const cors = require("cors");
|
|
5
|
-
const express = require("express");
|
|
6
4
|
const os = require("os");
|
|
7
5
|
const fs = require("fs");
|
|
8
6
|
const path = require("path");
|
|
9
|
-
const bodyParser = require("body-parser");
|
|
10
7
|
const utils = require("../utils");
|
|
11
8
|
const logger_1 = require("../logger");
|
|
12
|
-
const constants_1 = require("./constants");
|
|
13
9
|
const types_1 = require("./types");
|
|
14
10
|
const hubExport_1 = require("./hubExport");
|
|
15
11
|
const registry_1 = require("./registry");
|
|
12
|
+
const ExpressBasedEmulator_1 = require("./ExpressBasedEmulator");
|
|
16
13
|
const pkg = require("../../package.json");
|
|
17
|
-
class EmulatorHub {
|
|
14
|
+
class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
|
|
18
15
|
constructor(args) {
|
|
16
|
+
super({
|
|
17
|
+
listen: args.listen,
|
|
18
|
+
});
|
|
19
19
|
this.args = args;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
}
|
|
21
|
+
static readLocatorFile(projectId) {
|
|
22
|
+
const locatorPath = this.getLocatorFilePath(projectId);
|
|
23
|
+
if (!fs.existsSync(locatorPath)) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const data = fs.readFileSync(locatorPath, "utf8").toString();
|
|
27
|
+
const locator = JSON.parse(data);
|
|
28
|
+
if (locator.version !== this.CLI_VERSION) {
|
|
29
|
+
logger_1.logger.debug(`Found locator with mismatched version, ignoring: ${JSON.stringify(locator)}`);
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
return locator;
|
|
33
|
+
}
|
|
34
|
+
static getLocatorFilePath(projectId) {
|
|
35
|
+
const dir = os.tmpdir();
|
|
36
|
+
const filename = `hub-${projectId}.json`;
|
|
37
|
+
return path.join(dir, filename);
|
|
38
|
+
}
|
|
39
|
+
async start() {
|
|
40
|
+
await super.start();
|
|
41
|
+
await this.writeLocatorFile();
|
|
42
|
+
}
|
|
43
|
+
async createExpressApp() {
|
|
44
|
+
const app = await super.createExpressApp();
|
|
45
|
+
app.get("/", (req, res) => {
|
|
46
|
+
res.json(Object.assign(Object.assign({}, this.getLocator()), { host: utils.connectableHostname(this.args.listen[0].address), port: this.args.listen[0].port }));
|
|
25
47
|
});
|
|
26
|
-
|
|
48
|
+
app.get(EmulatorHub.PATH_EMULATORS, (req, res) => {
|
|
27
49
|
const body = {};
|
|
28
|
-
for (const
|
|
29
|
-
|
|
30
|
-
body[emulator] = info;
|
|
50
|
+
for (const info of registry_1.EmulatorRegistry.listRunningWithInfo()) {
|
|
51
|
+
body[info.name] = Object.assign({ listen: this.args.listenForEmulator[info.name] }, info);
|
|
31
52
|
}
|
|
32
53
|
res.json(body);
|
|
33
54
|
});
|
|
34
|
-
|
|
55
|
+
app.post(EmulatorHub.PATH_EXPORT, async (req, res) => {
|
|
35
56
|
const path = req.body.path;
|
|
36
57
|
const initiatedBy = req.body.initiatedBy || "unknown";
|
|
37
58
|
utils.logLabeledBullet("emulators", `Received export request. Exporting data to ${path}.`);
|
|
@@ -53,7 +74,7 @@ class EmulatorHub {
|
|
|
53
74
|
});
|
|
54
75
|
}
|
|
55
76
|
});
|
|
56
|
-
|
|
77
|
+
app.put(EmulatorHub.PATH_DISABLE_FUNCTIONS, async (req, res) => {
|
|
57
78
|
utils.logLabeledBullet("emulators", `Disabling Cloud Functions triggers, non-HTTP functions will not execute.`);
|
|
58
79
|
const instance = registry_1.EmulatorRegistry.get(types_1.Emulators.FUNCTIONS);
|
|
59
80
|
if (!instance) {
|
|
@@ -64,7 +85,7 @@ class EmulatorHub {
|
|
|
64
85
|
await emu.disableBackgroundTriggers();
|
|
65
86
|
res.status(200).json({ enabled: false });
|
|
66
87
|
});
|
|
67
|
-
|
|
88
|
+
app.put(EmulatorHub.PATH_ENABLE_FUNCTIONS, async (req, res) => {
|
|
68
89
|
utils.logLabeledBullet("emulators", `Enabling Cloud Functions triggers, non-HTTP functions will execute.`);
|
|
69
90
|
const instance = registry_1.EmulatorRegistry.get(types_1.Emulators.FUNCTIONS);
|
|
70
91
|
if (!instance) {
|
|
@@ -75,58 +96,29 @@ class EmulatorHub {
|
|
|
75
96
|
await emu.reloadTriggers();
|
|
76
97
|
res.status(200).json({ enabled: true });
|
|
77
98
|
});
|
|
78
|
-
|
|
79
|
-
static readLocatorFile(projectId) {
|
|
80
|
-
const locatorPath = this.getLocatorFilePath(projectId);
|
|
81
|
-
if (!fs.existsSync(locatorPath)) {
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
|
-
const data = fs.readFileSync(locatorPath, "utf8").toString();
|
|
85
|
-
const locator = JSON.parse(data);
|
|
86
|
-
if (locator.version !== this.CLI_VERSION) {
|
|
87
|
-
logger_1.logger.debug(`Found locator with mismatched version, ignoring: ${JSON.stringify(locator)}`);
|
|
88
|
-
return undefined;
|
|
89
|
-
}
|
|
90
|
-
return locator;
|
|
91
|
-
}
|
|
92
|
-
static getLocatorFilePath(projectId) {
|
|
93
|
-
const dir = os.tmpdir();
|
|
94
|
-
const filename = `hub-${projectId}.json`;
|
|
95
|
-
return path.join(dir, filename);
|
|
96
|
-
}
|
|
97
|
-
async start() {
|
|
98
|
-
const { host, port } = this.getInfo();
|
|
99
|
-
const server = this.hub.listen(port, host);
|
|
100
|
-
this.destroyServer = utils.createDestroyer(server);
|
|
101
|
-
await this.writeLocatorFile();
|
|
102
|
-
}
|
|
103
|
-
async connect() {
|
|
99
|
+
return app;
|
|
104
100
|
}
|
|
105
101
|
async stop() {
|
|
106
|
-
|
|
107
|
-
await this.destroyServer();
|
|
108
|
-
}
|
|
102
|
+
await super.stop();
|
|
109
103
|
await this.deleteLocatorFile();
|
|
110
104
|
}
|
|
111
|
-
getInfo() {
|
|
112
|
-
const host = this.args.host || constants_1.Constants.getDefaultHost();
|
|
113
|
-
const port = this.args.port || constants_1.Constants.getDefaultPort(types_1.Emulators.HUB);
|
|
114
|
-
return {
|
|
115
|
-
name: this.getName(),
|
|
116
|
-
host,
|
|
117
|
-
port,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
105
|
getName() {
|
|
121
106
|
return types_1.Emulators.HUB;
|
|
122
107
|
}
|
|
123
108
|
getLocator() {
|
|
124
|
-
const { host, port } = this.getInfo();
|
|
125
109
|
const version = pkg.version;
|
|
110
|
+
const origins = [];
|
|
111
|
+
for (const spec of this.args.listen) {
|
|
112
|
+
if (spec.family === "IPv6") {
|
|
113
|
+
origins.push(`http://[${utils.connectableHostname(spec.address)}]:${spec.port}`);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
origins.push(`http://${utils.connectableHostname(spec.address)}:${spec.port}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
126
119
|
return {
|
|
127
120
|
version,
|
|
128
|
-
|
|
129
|
-
port,
|
|
121
|
+
origins,
|
|
130
122
|
};
|
|
131
123
|
}
|
|
132
124
|
async writeLocatorFile() {
|
|
@@ -12,23 +12,37 @@ class EmulatorHubClient {
|
|
|
12
12
|
foundHub() {
|
|
13
13
|
return this.locator !== undefined;
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
getStatus() {
|
|
16
|
+
return this.tryOrigins(async (client, origin) => {
|
|
17
|
+
await client.get("/");
|
|
18
|
+
return origin;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async tryOrigins(task) {
|
|
22
|
+
const origins = this.assertLocator().origins;
|
|
23
|
+
let err = undefined;
|
|
24
|
+
for (const origin of origins) {
|
|
25
|
+
try {
|
|
26
|
+
const apiClient = new apiv2_1.Client({ urlPrefix: origin, auth: false });
|
|
27
|
+
return await task(apiClient, origin);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
if (!err) {
|
|
31
|
+
err = e;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
throw err !== null && err !== void 0 ? err : new Error("Cannot find working hub origin. Tried:" + origins.join(" "));
|
|
18
36
|
}
|
|
19
37
|
async getEmulators() {
|
|
20
|
-
const
|
|
21
|
-
const res = await apiClient.get(hub_1.EmulatorHub.PATH_EMULATORS);
|
|
38
|
+
const res = await this.tryOrigins((client) => client.get(hub_1.EmulatorHub.PATH_EMULATORS));
|
|
22
39
|
return res.body;
|
|
23
40
|
}
|
|
24
41
|
async postExport(options) {
|
|
25
|
-
const
|
|
42
|
+
const origin = await this.getStatus();
|
|
43
|
+
const apiClient = new apiv2_1.Client({ urlPrefix: origin, auth: false });
|
|
26
44
|
await apiClient.post(hub_1.EmulatorHub.PATH_EXPORT, options);
|
|
27
45
|
}
|
|
28
|
-
get origin() {
|
|
29
|
-
const locator = this.assertLocator();
|
|
30
|
-
return `http://${locator.host}:${locator.port}`;
|
|
31
|
-
}
|
|
32
46
|
assertLocator() {
|
|
33
47
|
if (this.locator === undefined) {
|
|
34
48
|
throw new error_1.FirebaseError(`Cannot contact the Emulator Hub for project ${this.projectId}`);
|
|
@@ -12,7 +12,6 @@ const error_1 = require("../error");
|
|
|
12
12
|
const hub_1 = require("./hub");
|
|
13
13
|
const downloadableEmulators_1 = require("./downloadableEmulators");
|
|
14
14
|
const rimraf = require("rimraf");
|
|
15
|
-
const apiv2_1 = require("../apiv2");
|
|
16
15
|
const track_1 = require("../track");
|
|
17
16
|
class HubExport {
|
|
18
17
|
constructor(projectId, options) {
|
|
@@ -83,20 +82,16 @@ class HubExport {
|
|
|
83
82
|
initiated_by: this.options.initiatedBy,
|
|
84
83
|
emulator_name: types_1.Emulators.FIRESTORE,
|
|
85
84
|
});
|
|
86
|
-
const firestoreInfo = registry_1.EmulatorRegistry.get(types_1.Emulators.FIRESTORE).getInfo();
|
|
87
|
-
const firestoreHost = `http://${registry_1.EmulatorRegistry.getInfoHostString(firestoreInfo)}`;
|
|
88
85
|
const firestoreExportBody = {
|
|
89
86
|
database: `projects/${this.projectId}/databases/(default)`,
|
|
90
87
|
export_directory: this.tmpDir,
|
|
91
88
|
export_name: metadata.firestore.path,
|
|
92
89
|
};
|
|
93
|
-
|
|
94
|
-
await client.post(`/emulator/v1/projects/${this.projectId}:export`, firestoreExportBody);
|
|
90
|
+
await registry_1.EmulatorRegistry.client(types_1.Emulators.FIRESTORE).post(`/emulator/v1/projects/${this.projectId}:export`, firestoreExportBody);
|
|
95
91
|
}
|
|
96
92
|
async exportDatabase(metadata) {
|
|
97
93
|
const databaseEmulator = registry_1.EmulatorRegistry.get(types_1.Emulators.DATABASE);
|
|
98
|
-
const
|
|
99
|
-
const client = new apiv2_1.Client({ urlPrefix: databaseAddr, auth: true });
|
|
94
|
+
const client = registry_1.EmulatorRegistry.client(types_1.Emulators.DATABASE, { auth: true });
|
|
100
95
|
const inspectURL = `/.inspect/databases.json`;
|
|
101
96
|
const inspectRes = await client.get(inspectURL, {
|
|
102
97
|
queryParams: { ns: this.projectId },
|
|
@@ -174,19 +169,16 @@ class HubExport {
|
|
|
174
169
|
}, configFile);
|
|
175
170
|
}
|
|
176
171
|
async exportStorage(metadata) {
|
|
177
|
-
const storageEmulator = registry_1.EmulatorRegistry.get(types_1.Emulators.STORAGE);
|
|
178
172
|
const storageExportPath = path.join(this.tmpDir, metadata.storage.path);
|
|
179
173
|
if (fs.existsSync(storageExportPath)) {
|
|
180
174
|
fse.removeSync(storageExportPath);
|
|
181
175
|
}
|
|
182
176
|
fs.mkdirSync(storageExportPath, { recursive: true });
|
|
183
|
-
const storageHost = `http://${registry_1.EmulatorRegistry.getInfoHostString(storageEmulator.getInfo())}`;
|
|
184
177
|
const storageExportBody = {
|
|
185
178
|
path: storageExportPath,
|
|
186
179
|
initiatedBy: this.options.initiatedBy,
|
|
187
180
|
};
|
|
188
|
-
const
|
|
189
|
-
const res = await client.request({
|
|
181
|
+
const res = await registry_1.EmulatorRegistry.client(types_1.Emulators.STORAGE).request({
|
|
190
182
|
method: "POST",
|
|
191
183
|
path: "/internal/export",
|
|
192
184
|
headers: { "Content-Type": "application/json" },
|