firebase-tools 10.6.0 → 10.7.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/command.js +4 -4
- package/lib/commands/deploy.js +1 -1
- package/lib/commands/emulators-start.js +7 -2
- package/lib/commands/ext-configure.js +15 -5
- package/lib/commands/ext-export.js +6 -5
- package/lib/commands/ext-install.js +28 -44
- package/lib/commands/ext-update.js +9 -1
- package/lib/commands/functions-delete.js +7 -3
- package/lib/commands/hosting-channel-deploy.js +2 -2
- package/lib/deploy/database/deploy.js +4 -0
- package/lib/deploy/database/index.js +1 -0
- package/lib/deploy/extensions/deploy.js +4 -4
- package/lib/deploy/extensions/deploymentSummary.js +8 -5
- package/lib/deploy/extensions/planner.js +36 -9
- package/lib/deploy/extensions/prepare.js +1 -1
- package/lib/deploy/extensions/secrets.js +2 -2
- package/lib/deploy/extensions/tasks.js +60 -21
- package/lib/deploy/functions/backend.js +37 -2
- package/lib/deploy/functions/build.js +173 -0
- package/lib/deploy/functions/checkIam.js +11 -14
- package/lib/deploy/functions/containerCleaner.js +8 -7
- package/lib/deploy/functions/deploy.js +49 -28
- package/lib/deploy/functions/ensure.js +4 -4
- package/lib/deploy/functions/functionsDeployHelper.js +99 -24
- package/lib/deploy/functions/prepare.js +129 -71
- package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
- package/lib/deploy/functions/pricing.js +6 -3
- package/lib/deploy/functions/prompts.js +1 -7
- package/lib/deploy/functions/release/executor.js +1 -1
- package/lib/deploy/functions/release/fabricator.js +69 -25
- package/lib/deploy/functions/release/index.js +20 -6
- package/lib/deploy/functions/release/planner.js +18 -10
- package/lib/deploy/functions/release/reporter.js +14 -11
- package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +50 -3
- package/lib/deploy/functions/runtimes/golang/index.js +3 -0
- package/lib/deploy/functions/runtimes/node/index.js +7 -0
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +132 -6
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/services/auth.js +95 -0
- package/lib/deploy/functions/services/index.js +41 -21
- package/lib/deploy/functions/validate.js +33 -7
- package/lib/deploy/hosting/args.js +2 -0
- package/lib/deploy/hosting/convertConfig.js +39 -8
- package/lib/deploy/hosting/deploy.js +3 -3
- package/lib/deploy/hosting/prepare.js +2 -2
- package/lib/deploy/hosting/release.js +6 -2
- package/lib/deploy/index.js +82 -93
- package/lib/deploy/remoteconfig/deploy.js +4 -0
- package/lib/deploy/remoteconfig/index.js +3 -1
- package/lib/emulator/auth/cloudFunctions.js +6 -2
- package/lib/emulator/auth/operations.js +5 -1
- package/lib/emulator/auth/server.js +8 -1
- package/lib/emulator/auth/state.js +27 -24
- package/lib/emulator/auth/utils.js +3 -25
- package/lib/emulator/controller.js +17 -14
- package/lib/emulator/databaseEmulator.js +36 -3
- package/lib/emulator/downloadableEmulators.js +39 -23
- package/lib/emulator/extensions/validation.js +2 -2
- package/lib/emulator/extensionsEmulator.js +85 -21
- package/lib/emulator/functionsEmulator.js +89 -15
- package/lib/emulator/functionsEmulatorRuntime.js +1 -1
- package/lib/emulator/functionsEmulatorShared.js +25 -2
- package/lib/emulator/functionsEmulatorShell.js +2 -3
- package/lib/emulator/functionsEmulatorUtils.js +5 -1
- package/lib/emulator/pubsubEmulator.js +13 -9
- package/lib/emulator/registry.js +34 -12
- package/lib/emulator/storage/apis/firebase.js +33 -6
- package/lib/emulator/storage/apis/gcloud.js +6 -3
- package/lib/emulator/storage/files.js +9 -1
- package/lib/ensureApiEnabled.js +8 -4
- package/lib/extensions/changelog.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +4 -3
- package/lib/extensions/emulator/specHelper.js +7 -1
- package/lib/extensions/extensionsHelper.js +30 -24
- package/lib/extensions/manifest.js +27 -7
- package/lib/extensions/paramHelper.js +7 -5
- package/lib/extensions/provisioningHelper.js +2 -2
- package/lib/extensions/warnings.js +11 -4
- package/lib/functions/events/index.js +7 -0
- package/lib/functions/events/v1.js +6 -0
- package/lib/functions/projectConfig.js +32 -6
- package/lib/functionsShellCommandAction.js +1 -1
- package/lib/gcp/cloudfunctions.js +38 -5
- package/lib/gcp/cloudfunctionsv2.js +46 -7
- package/lib/gcp/identityPlatform.js +44 -0
- package/lib/gcp/secretManager.js +1 -1
- package/lib/metaprogramming.js +2 -0
- package/lib/previews.js +1 -1
- package/lib/serve/functions.js +16 -19
- package/lib/serve/hosting.js +25 -12
- package/lib/serve/index.js +6 -0
- package/lib/track.js +15 -21
- package/npm-shrinkwrap.json +256 -527
- package/package.json +6 -3
- package/schema/firebase-config.json +6 -0
|
@@ -13,7 +13,7 @@ const url_1 = require("url");
|
|
|
13
13
|
const events_1 = require("events");
|
|
14
14
|
const api = require("../api");
|
|
15
15
|
const logger_1 = require("../logger");
|
|
16
|
-
const
|
|
16
|
+
const track_1 = require("../track");
|
|
17
17
|
const constants_1 = require("./constants");
|
|
18
18
|
const types_1 = require("./types");
|
|
19
19
|
const chokidar = require("chokidar");
|
|
@@ -34,6 +34,7 @@ const secretManager_1 = require("../gcp/secretManager");
|
|
|
34
34
|
const runtimes = require("../deploy/functions/runtimes");
|
|
35
35
|
const backend = require("../deploy/functions/backend");
|
|
36
36
|
const functionsEnv = require("../functions/env");
|
|
37
|
+
const v1_1 = require("../functions/events/v1");
|
|
37
38
|
const EVENT_INVOKE = "functions:invoke";
|
|
38
39
|
const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
|
|
39
40
|
class FunctionsEmulator {
|
|
@@ -54,6 +55,16 @@ class FunctionsEmulator {
|
|
|
54
55
|
: types_1.FunctionsExecutionMode.AUTO;
|
|
55
56
|
this.workerPool = new functionsRuntimeWorker_1.RuntimeWorkerPool(mode);
|
|
56
57
|
this.workQueue = new workQueue_1.WorkQueue(mode);
|
|
58
|
+
this.blockingFunctionsConfig = {
|
|
59
|
+
triggers: {
|
|
60
|
+
beforeCreate: {
|
|
61
|
+
functionUri: "",
|
|
62
|
+
},
|
|
63
|
+
beforeSignIn: {
|
|
64
|
+
functionUri: "",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
57
68
|
}
|
|
58
69
|
static getHttpFunctionUrl(host, port, projectId, name, region) {
|
|
59
70
|
return `http://${host}:${port}/${projectId}/${region}/${name}`;
|
|
@@ -166,7 +177,9 @@ class FunctionsEmulator {
|
|
|
166
177
|
});
|
|
167
178
|
return hub;
|
|
168
179
|
}
|
|
169
|
-
async invokeTrigger(
|
|
180
|
+
async invokeTrigger(trigger, proto, runtimeOpts) {
|
|
181
|
+
const record = this.getTriggerRecordByKey(this.getTriggerKey(trigger));
|
|
182
|
+
const backend = record.backend;
|
|
170
183
|
const bundleTemplate = this.getBaseBundle();
|
|
171
184
|
const runtimeBundle = Object.assign(Object.assign({}, bundleTemplate), { proto });
|
|
172
185
|
if (this.args.debugPort) {
|
|
@@ -210,7 +223,6 @@ class FunctionsEmulator {
|
|
|
210
223
|
return Promise.resolve();
|
|
211
224
|
}
|
|
212
225
|
async connect() {
|
|
213
|
-
const loadTriggerPromises = [];
|
|
214
226
|
for (const backend of this.args.emulatableBackends) {
|
|
215
227
|
this.logger.logLabeled("BULLET", "functions", `Watching "${backend.functionsDir}" for Cloud Functions...`);
|
|
216
228
|
const watcher = chokidar.watch(backend.functionsDir, {
|
|
@@ -226,9 +238,9 @@ class FunctionsEmulator {
|
|
|
226
238
|
this.logger.log("DEBUG", `File ${filePath} changed, reloading triggers`);
|
|
227
239
|
return debouncedLoadTriggers();
|
|
228
240
|
});
|
|
229
|
-
|
|
241
|
+
await this.loadTriggers(backend, true);
|
|
230
242
|
}
|
|
231
|
-
await
|
|
243
|
+
await this.performPostLoadOperations();
|
|
232
244
|
return;
|
|
233
245
|
}
|
|
234
246
|
async stop() {
|
|
@@ -271,6 +283,10 @@ class FunctionsEmulator {
|
|
|
271
283
|
logger_1.logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
|
|
272
284
|
const discoveredBackend = await runtimeDelegate.discoverSpec(runtimeConfig, Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: this.getFirebaseConfig() }), emulatableBackend.env));
|
|
273
285
|
const endpoints = backend.allEndpoints(discoveredBackend);
|
|
286
|
+
(0, functionsEmulatorShared_1.prepareEndpoints)(endpoints);
|
|
287
|
+
for (const e of endpoints) {
|
|
288
|
+
e.codebase = emulatableBackend.codebase;
|
|
289
|
+
}
|
|
274
290
|
triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsFromEndpoints)(endpoints);
|
|
275
291
|
}
|
|
276
292
|
const toSetup = triggerDefinitions.filter((definition) => {
|
|
@@ -327,6 +343,11 @@ class FunctionsEmulator {
|
|
|
327
343
|
break;
|
|
328
344
|
}
|
|
329
345
|
}
|
|
346
|
+
else if (definition.blockingTrigger) {
|
|
347
|
+
const { host, port } = this.getInfo();
|
|
348
|
+
url = FunctionsEmulator.getHttpFunctionUrl(host, port, this.args.projectId, definition.name, definition.region);
|
|
349
|
+
added = this.addBlockingTrigger(url, definition.blockingTrigger);
|
|
350
|
+
}
|
|
330
351
|
else {
|
|
331
352
|
this.logger.log("WARN", `Unsupported function type on ${definition.name}. Expected either httpsTrigger or eventTrigger.`);
|
|
332
353
|
}
|
|
@@ -350,6 +371,34 @@ class FunctionsEmulator {
|
|
|
350
371
|
this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
|
|
351
372
|
}
|
|
352
373
|
}
|
|
374
|
+
async performPostLoadOperations() {
|
|
375
|
+
var _a, _b, _c, _d;
|
|
376
|
+
if (((_b = (_a = this.blockingFunctionsConfig.triggers) === null || _a === void 0 ? void 0 : _a.beforeCreate) === null || _b === void 0 ? void 0 : _b.functionUri) === "" &&
|
|
377
|
+
((_d = (_c = this.blockingFunctionsConfig.triggers) === null || _c === void 0 ? void 0 : _c.beforeSignIn) === null || _d === void 0 ? void 0 : _d.functionUri) === "") {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const authEmu = registry_1.EmulatorRegistry.get(types_1.Emulators.AUTH);
|
|
381
|
+
if (!authEmu) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const path = `/identitytoolkit.googleapis.com/v2/projects/${this.getProjectId()}/config?updateMask=blockingFunctions`;
|
|
385
|
+
try {
|
|
386
|
+
await api.request("PATCH", path, {
|
|
387
|
+
origin: `http://${registry_1.EmulatorRegistry.getInfoHostString(authEmu.getInfo())}`,
|
|
388
|
+
headers: {
|
|
389
|
+
Authorization: "Bearer owner",
|
|
390
|
+
},
|
|
391
|
+
data: {
|
|
392
|
+
blockingFunctions: this.blockingFunctionsConfig,
|
|
393
|
+
},
|
|
394
|
+
json: true,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
catch (err) {
|
|
398
|
+
this.logger.log("WARN", "Error updating blocking functions config to the auth emulator: " + err);
|
|
399
|
+
throw err;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
353
402
|
addRealtimeDatabaseTrigger(projectId, key, eventTrigger) {
|
|
354
403
|
const databaseEmu = registry_1.EmulatorRegistry.get(types_1.Emulators.DATABASE);
|
|
355
404
|
if (!databaseEmu) {
|
|
@@ -416,11 +465,10 @@ class FunctionsEmulator {
|
|
|
416
465
|
});
|
|
417
466
|
}
|
|
418
467
|
async addPubsubTrigger(triggerName, key, eventTrigger, signatureType, schedule) {
|
|
419
|
-
const
|
|
420
|
-
if (!
|
|
468
|
+
const pubsubEmulator = registry_1.EmulatorRegistry.get(types_1.Emulators.PUBSUB);
|
|
469
|
+
if (!pubsubEmulator) {
|
|
421
470
|
return false;
|
|
422
471
|
}
|
|
423
|
-
const pubsubEmulator = registry_1.EmulatorRegistry.get(types_1.Emulators.PUBSUB);
|
|
424
472
|
logger_1.logger.debug(`addPubsubTrigger`, JSON.stringify({ eventTrigger }));
|
|
425
473
|
const resource = eventTrigger.resource;
|
|
426
474
|
let topic;
|
|
@@ -458,6 +506,31 @@ class FunctionsEmulator {
|
|
|
458
506
|
this.multicastTriggers[eventTriggerId] = triggers;
|
|
459
507
|
return true;
|
|
460
508
|
}
|
|
509
|
+
addBlockingTrigger(url, blockingTrigger) {
|
|
510
|
+
logger_1.logger.debug(`addBlockingTrigger`, JSON.stringify({ blockingTrigger }));
|
|
511
|
+
const eventType = blockingTrigger.eventType;
|
|
512
|
+
if (v1_1.AUTH_BLOCKING_EVENTS.includes(eventType)) {
|
|
513
|
+
if (blockingTrigger.eventType === v1_1.BEFORE_CREATE_EVENT) {
|
|
514
|
+
this.blockingFunctionsConfig.triggers = Object.assign(Object.assign({}, this.blockingFunctionsConfig.triggers), { beforeCreate: {
|
|
515
|
+
functionUri: url,
|
|
516
|
+
} });
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
this.blockingFunctionsConfig.triggers = Object.assign(Object.assign({}, this.blockingFunctionsConfig.triggers), { beforeSignIn: {
|
|
520
|
+
functionUri: url,
|
|
521
|
+
} });
|
|
522
|
+
}
|
|
523
|
+
this.blockingFunctionsConfig.forwardInboundCredentials = {
|
|
524
|
+
accessToken: blockingTrigger.options.accessToken,
|
|
525
|
+
idToken: blockingTrigger.options.idToken,
|
|
526
|
+
refreshToken: blockingTrigger.options.refreshToken,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
461
534
|
getProjectId() {
|
|
462
535
|
return this.args.projectId;
|
|
463
536
|
}
|
|
@@ -509,6 +582,7 @@ class FunctionsEmulator {
|
|
|
509
582
|
};
|
|
510
583
|
}
|
|
511
584
|
setTriggersForTesting(triggers, backend) {
|
|
585
|
+
this.triggers = {};
|
|
512
586
|
triggers.forEach((def) => this.addTriggerRecord(def, { backend, ignored: false }));
|
|
513
587
|
}
|
|
514
588
|
getBaseBundle() {
|
|
@@ -777,11 +851,11 @@ class FunctionsEmulator {
|
|
|
777
851
|
}
|
|
778
852
|
async reloadTriggers() {
|
|
779
853
|
this.triggerGeneration++;
|
|
780
|
-
const loadTriggerPromises = [];
|
|
781
854
|
for (const backend of this.args.emulatableBackends) {
|
|
782
|
-
|
|
855
|
+
await this.loadTriggers(backend);
|
|
783
856
|
}
|
|
784
|
-
|
|
857
|
+
await this.performPostLoadOperations();
|
|
858
|
+
return;
|
|
785
859
|
}
|
|
786
860
|
async handleBackgroundTrigger(projectId, triggerKey, proto) {
|
|
787
861
|
const record = this.getTriggerRecordByKey(triggerKey);
|
|
@@ -790,7 +864,7 @@ class FunctionsEmulator {
|
|
|
790
864
|
}
|
|
791
865
|
const trigger = record.def;
|
|
792
866
|
const service = (0, functionsEmulatorShared_1.getFunctionService)(trigger);
|
|
793
|
-
const worker = await this.invokeTrigger(
|
|
867
|
+
const worker = await this.invokeTrigger(trigger, proto);
|
|
794
868
|
return new Promise((resolve, reject) => {
|
|
795
869
|
if (projectId !== this.args.projectId) {
|
|
796
870
|
if (service !== constants_1.Constants.SERVICE_REALTIME_DATABASE) {
|
|
@@ -809,7 +883,7 @@ class FunctionsEmulator {
|
|
|
809
883
|
reject({ code: 500, body: el.text });
|
|
810
884
|
}
|
|
811
885
|
});
|
|
812
|
-
void track(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
|
|
886
|
+
void (0, track_1.track)(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
|
|
813
887
|
worker.waitForDone().then(() => {
|
|
814
888
|
resolve({ status: "acknowledged" });
|
|
815
889
|
});
|
|
@@ -877,14 +951,14 @@ class FunctionsEmulator {
|
|
|
877
951
|
req.headers[functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER] = encodeURIComponent(JSON.stringify(contextAuth));
|
|
878
952
|
}
|
|
879
953
|
}
|
|
880
|
-
const worker = await this.invokeTrigger(
|
|
954
|
+
const worker = await this.invokeTrigger(trigger);
|
|
881
955
|
worker.onLogs((el) => {
|
|
882
956
|
if (el.level === "FATAL") {
|
|
883
957
|
res.status(500).send(el.text);
|
|
884
958
|
}
|
|
885
959
|
});
|
|
886
960
|
await worker.waitForSocketReady();
|
|
887
|
-
void track(EVENT_INVOKE, "https");
|
|
961
|
+
void (0, track_1.track)(EVENT_INVOKE, "https");
|
|
888
962
|
this.logger.log("DEBUG", `[functions] Runtime ready! Sending request!`);
|
|
889
963
|
if (!worker.lastArgs) {
|
|
890
964
|
throw new error_1.FirebaseError("Cannot execute on a worker with no arguments");
|
|
@@ -213,7 +213,7 @@ function initializeNetworkFiltering(frb) {
|
|
|
213
213
|
})
|
|
214
214
|
.filter((v) => v);
|
|
215
215
|
const href = (hrefs.length && hrefs[0]) || "";
|
|
216
|
-
if (href && !history[href] && !
|
|
216
|
+
if (href && !history[href] && !(0, functionsEmulatorUtils_1.isLocalHost)(href)) {
|
|
217
217
|
history[href] = true;
|
|
218
218
|
if (href.indexOf("googleapis.com") !== -1) {
|
|
219
219
|
new types_1.EmulatorLog("SYSTEM", "googleapis-network-access", "", {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toBackendInfo = exports.getSecretLocalPath = exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
|
|
3
|
+
exports.toBackendInfo = exports.getSecretLocalPath = exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.prepareEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const os = require("os");
|
|
6
6
|
const path = require("path");
|
|
@@ -11,6 +11,8 @@ const proto_1 = require("../gcp/proto");
|
|
|
11
11
|
const manifest_1 = require("../extensions/manifest");
|
|
12
12
|
const extensionsHelper_1 = require("../extensions/extensionsHelper");
|
|
13
13
|
const postinstall_1 = require("./extensions/postinstall");
|
|
14
|
+
const services_1 = require("../deploy/functions/services");
|
|
15
|
+
const prepare_1 = require("../deploy/functions/prepare");
|
|
14
16
|
const memoryLookup = {
|
|
15
17
|
"128MB": 128,
|
|
16
18
|
"256MB": 256,
|
|
@@ -44,6 +46,14 @@ class EmulatedTrigger {
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
exports.EmulatedTrigger = EmulatedTrigger;
|
|
49
|
+
function prepareEndpoints(endpoints) {
|
|
50
|
+
const bkend = backend.of(...endpoints);
|
|
51
|
+
for (const ep of endpoints) {
|
|
52
|
+
(0, services_1.serviceForEndpoint)(ep).validateTrigger(ep, bkend);
|
|
53
|
+
}
|
|
54
|
+
(0, prepare_1.inferBlockingDetails)(bkend);
|
|
55
|
+
}
|
|
56
|
+
exports.prepareEndpoints = prepareEndpoints;
|
|
47
57
|
function emulatedFunctionsFromEndpoints(endpoints) {
|
|
48
58
|
const regionDefinitions = [];
|
|
49
59
|
for (const endpoint of endpoints) {
|
|
@@ -56,6 +66,7 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
56
66
|
region: endpoint.region,
|
|
57
67
|
name: endpoint.id,
|
|
58
68
|
id: `${endpoint.region}-${endpoint.id}`,
|
|
69
|
+
codebase: endpoint.codebase,
|
|
59
70
|
};
|
|
60
71
|
(0, proto_1.copyIfPresent)(def, endpoint, "availableMemoryMb", "labels", "timeoutSeconds", "platform", "secretEnvironmentVariables");
|
|
61
72
|
if (backend.isHttpsTriggered(endpoint)) {
|
|
@@ -89,6 +100,15 @@ function emulatedFunctionsFromEndpoints(endpoints) {
|
|
|
89
100
|
def.eventTrigger = { eventType: "pubsub", resource: "" };
|
|
90
101
|
def.schedule = endpoint.scheduleTrigger;
|
|
91
102
|
}
|
|
103
|
+
else if (backend.isBlockingTriggered(endpoint)) {
|
|
104
|
+
def.blockingTrigger = {
|
|
105
|
+
eventType: endpoint.blockingTrigger.eventType,
|
|
106
|
+
options: endpoint.blockingTrigger.options || {},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
110
|
+
def.httpsTrigger = {};
|
|
111
|
+
}
|
|
92
112
|
else {
|
|
93
113
|
}
|
|
94
114
|
regionDefinitions.push(def);
|
|
@@ -136,6 +156,9 @@ function getFunctionService(def) {
|
|
|
136
156
|
if (def.eventTrigger) {
|
|
137
157
|
return (_a = def.eventTrigger.service) !== null && _a !== void 0 ? _a : getServiceFromEventType(def.eventTrigger.eventType);
|
|
138
158
|
}
|
|
159
|
+
if (def.blockingTrigger) {
|
|
160
|
+
return def.blockingTrigger.eventType;
|
|
161
|
+
}
|
|
139
162
|
return "unknown";
|
|
140
163
|
}
|
|
141
164
|
exports.getFunctionService = getFunctionService;
|
|
@@ -260,7 +283,7 @@ function toBackendInfo(e, cf3Triggers) {
|
|
|
260
283
|
extension: e.extension,
|
|
261
284
|
extensionVersion: extensionVersion,
|
|
262
285
|
extensionSpec: extensionSpec,
|
|
263
|
-
functionTriggers: (_b = e.predefinedTriggers) !== null && _b !== void 0 ? _b : cf3Triggers,
|
|
286
|
+
functionTriggers: (_b = e.predefinedTriggers) !== null && _b !== void 0 ? _b : cf3Triggers.filter((t) => t.codebase === e.codebase),
|
|
264
287
|
}));
|
|
265
288
|
}
|
|
266
289
|
exports.toBackendInfo = toBackendInfo;
|
|
@@ -7,9 +7,8 @@ const utils = require("../utils");
|
|
|
7
7
|
const logger_1 = require("../logger");
|
|
8
8
|
const error_1 = require("../error");
|
|
9
9
|
class FunctionsEmulatorShell {
|
|
10
|
-
constructor(emu
|
|
10
|
+
constructor(emu) {
|
|
11
11
|
this.emu = emu;
|
|
12
|
-
this.backend = backend;
|
|
13
12
|
this.urls = {};
|
|
14
13
|
this.triggers = emu.getTriggerDefinitions();
|
|
15
14
|
this.emulatedFunctions = this.triggers.map((t) => t.id);
|
|
@@ -42,7 +41,7 @@ class FunctionsEmulatorShell {
|
|
|
42
41
|
auth: opts.auth,
|
|
43
42
|
data,
|
|
44
43
|
};
|
|
45
|
-
this.emu.invokeTrigger(
|
|
44
|
+
this.emu.invokeTrigger(trigger, proto);
|
|
46
45
|
}
|
|
47
46
|
getTrigger(name) {
|
|
48
47
|
const result = this.triggers.find((trigger) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.compareVersionStrings = exports.parseVersionString = exports.parseRuntimeVersion = exports.removePathSegments = exports.trimSlashes = exports.isValidWildcardMatch = exports.extractParamsFromPath = void 0;
|
|
3
|
+
exports.isLocalHost = exports.compareVersionStrings = exports.parseVersionString = exports.parseRuntimeVersion = exports.removePathSegments = exports.trimSlashes = exports.isValidWildcardMatch = exports.extractParamsFromPath = void 0;
|
|
4
4
|
const wildcardRegex = new RegExp("{[^/{}]*}");
|
|
5
5
|
const wildcardKeyRegex = new RegExp("^{(.+)}$");
|
|
6
6
|
function extractParamsFromPath(wildcardPath, snapshotPath) {
|
|
@@ -85,3 +85,7 @@ function compareVersionStrings(a, b) {
|
|
|
85
85
|
return 0;
|
|
86
86
|
}
|
|
87
87
|
exports.compareVersionStrings = compareVersionStrings;
|
|
88
|
+
function isLocalHost(href) {
|
|
89
|
+
return !!href.match(/^(http(s)?:\/\/)?(localhost|127.0.0.1|\[::1])/);
|
|
90
|
+
}
|
|
91
|
+
exports.isLocalHost = isLocalHost;
|
|
@@ -44,14 +44,7 @@ class PubsubEmulator {
|
|
|
44
44
|
getName() {
|
|
45
45
|
return types_1.Emulators.PUBSUB;
|
|
46
46
|
}
|
|
47
|
-
async
|
|
48
|
-
this.logger.logLabeled("DEBUG", "pubsub", `addTrigger(${topicName}, ${triggerKey}, ${signatureType})`);
|
|
49
|
-
const triggers = this.triggersForTopic.get(topicName) || [];
|
|
50
|
-
if (triggers.some((t) => t.triggerKey === triggerKey) &&
|
|
51
|
-
this.subscriptionForTopic.has(topicName)) {
|
|
52
|
-
this.logger.logLabeled("DEBUG", "pubsub", "Trigger already exists");
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
47
|
+
async maybeCreateTopicAndSub(topicName) {
|
|
55
48
|
const topic = this.pubsub.topic(topicName);
|
|
56
49
|
try {
|
|
57
50
|
this.logger.logLabeled("DEBUG", "pubsub", `Creating topic: ${topicName}`);
|
|
@@ -74,7 +67,7 @@ class PubsubEmulator {
|
|
|
74
67
|
catch (e) {
|
|
75
68
|
if (e && e.code === 6) {
|
|
76
69
|
this.logger.logLabeled("DEBUG", "pubsub", `Sub for ${topicName} exists`);
|
|
77
|
-
sub = topic.subscription(
|
|
70
|
+
sub = topic.subscription(subName);
|
|
78
71
|
}
|
|
79
72
|
else {
|
|
80
73
|
throw new error_1.FirebaseError(`Could not create sub ${subName}`, { original: e });
|
|
@@ -83,6 +76,17 @@ class PubsubEmulator {
|
|
|
83
76
|
sub.on("message", (message) => {
|
|
84
77
|
this.onMessage(topicName, message);
|
|
85
78
|
});
|
|
79
|
+
return sub;
|
|
80
|
+
}
|
|
81
|
+
async addTrigger(topicName, triggerKey, signatureType) {
|
|
82
|
+
this.logger.logLabeled("DEBUG", "pubsub", `addTrigger(${topicName}, ${triggerKey}, ${signatureType})`);
|
|
83
|
+
const sub = await this.maybeCreateTopicAndSub(topicName);
|
|
84
|
+
const triggers = this.triggersForTopic.get(topicName) || [];
|
|
85
|
+
if (triggers.some((t) => t.triggerKey === triggerKey) &&
|
|
86
|
+
this.subscriptionForTopic.has(topicName)) {
|
|
87
|
+
this.logger.logLabeled("DEBUG", "pubsub", "Trigger already exists");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
86
90
|
triggers.push({ triggerKey, signatureType });
|
|
87
91
|
this.triggersForTopic.set(topicName, triggers);
|
|
88
92
|
this.subscriptionForTopic.set(topicName, sub);
|
package/lib/emulator/registry.js
CHANGED
|
@@ -14,11 +14,10 @@ class EmulatorRegistry {
|
|
|
14
14
|
}
|
|
15
15
|
this.set(instance.getName(), instance);
|
|
16
16
|
await instance.start();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.extensionsEmulatorRegistered = true;
|
|
17
|
+
if (instance.getName() !== types_1.Emulators.EXTENSIONS) {
|
|
18
|
+
const info = instance.getInfo();
|
|
19
|
+
await portUtils.waitForPortClosed(info.port, info.host);
|
|
20
|
+
}
|
|
22
21
|
}
|
|
23
22
|
static async stop(name) {
|
|
24
23
|
emulatorLogger_1.EmulatorLogger.forEmulator(name).logLabeled("BULLET", name, `Stopping ${constants_1.Constants.description(name)}`);
|
|
@@ -57,7 +56,7 @@ class EmulatorRegistry {
|
|
|
57
56
|
}
|
|
58
57
|
static isRunning(emulator) {
|
|
59
58
|
if (emulator === types_1.Emulators.EXTENSIONS) {
|
|
60
|
-
return this.
|
|
59
|
+
return this.INSTANCES.get(emulator) !== undefined && this.isRunning(types_1.Emulators.FUNCTIONS);
|
|
61
60
|
}
|
|
62
61
|
const instance = this.INSTANCES.get(emulator);
|
|
63
62
|
return instance !== undefined;
|
|
@@ -89,12 +88,36 @@ class EmulatorRegistry {
|
|
|
89
88
|
return `${host}:${port}`;
|
|
90
89
|
}
|
|
91
90
|
}
|
|
92
|
-
static
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
95
|
-
|
|
91
|
+
static url(emulator, req) {
|
|
92
|
+
const url = new URL("http://unknown/");
|
|
93
|
+
if (req) {
|
|
94
|
+
url.protocol = req.protocol;
|
|
95
|
+
const host = req.headers.host;
|
|
96
|
+
if (host) {
|
|
97
|
+
url.host = host;
|
|
98
|
+
return url;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const info = EmulatorRegistry.getInfo(emulator);
|
|
102
|
+
if (info) {
|
|
103
|
+
if (info.host === "0.0.0.0") {
|
|
104
|
+
url.hostname = "127.0.0.1";
|
|
105
|
+
}
|
|
106
|
+
else if (info.host === "::") {
|
|
107
|
+
url.hostname = "[::1]";
|
|
108
|
+
}
|
|
109
|
+
else if (info.host.includes(":")) {
|
|
110
|
+
url.hostname = `[${info.host}]`;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
url.hostname = info.host;
|
|
114
|
+
}
|
|
115
|
+
url.port = info.port.toString();
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.warn(`Cannot determine host and port of ${emulator}`);
|
|
96
119
|
}
|
|
97
|
-
return
|
|
120
|
+
return url;
|
|
98
121
|
}
|
|
99
122
|
static set(emulator, instance) {
|
|
100
123
|
this.INSTANCES.set(emulator, instance);
|
|
@@ -104,5 +127,4 @@ class EmulatorRegistry {
|
|
|
104
127
|
}
|
|
105
128
|
}
|
|
106
129
|
exports.EmulatorRegistry = EmulatorRegistry;
|
|
107
|
-
EmulatorRegistry.extensionsEmulatorRegistered = false;
|
|
108
130
|
EmulatorRegistry.INSTANCES = new Map();
|
|
@@ -118,7 +118,7 @@ function createFirebaseEndpoints(emulator) {
|
|
|
118
118
|
return res.json(new metadata_1.OutgoingFirebaseMetadata(metadata));
|
|
119
119
|
});
|
|
120
120
|
firebaseStorageAPI.get("/b/:bucketId/o", async (req, res) => {
|
|
121
|
-
var _a, _b, _c, _d
|
|
121
|
+
var _a, _b, _c, _d;
|
|
122
122
|
const maxResults = (_a = req.query.maxResults) === null || _a === void 0 ? void 0 : _a.toString();
|
|
123
123
|
let listResponse;
|
|
124
124
|
try {
|
|
@@ -144,10 +144,12 @@ function createFirebaseEndpoints(emulator) {
|
|
|
144
144
|
}
|
|
145
145
|
return res.status(200).json({
|
|
146
146
|
nextPageToken: listResponse.nextPageToken,
|
|
147
|
-
prefixes: (_c = listResponse.prefixes) !== null && _c !== void 0 ? _c : [],
|
|
148
|
-
items: (
|
|
147
|
+
prefixes: ((_c = listResponse.prefixes) !== null && _c !== void 0 ? _c : []).filter(isValidPrefix),
|
|
148
|
+
items: ((_d = listResponse.items) !== null && _d !== void 0 ? _d : [])
|
|
149
|
+
.filter((item) => isValidNonEncodedPathString(item.name))
|
|
150
|
+
.map((item) => {
|
|
149
151
|
return { name: item.name, bucket: item.bucket };
|
|
150
|
-
})
|
|
152
|
+
}),
|
|
151
153
|
});
|
|
152
154
|
});
|
|
153
155
|
const handleUpload = async (req, res) => {
|
|
@@ -219,9 +221,13 @@ function createFirebaseEndpoints(emulator) {
|
|
|
219
221
|
res.header("x-goog-upload-chunk-granularity", "10000");
|
|
220
222
|
res.header("x-goog-upload-control-url", "");
|
|
221
223
|
res.header("x-goog-upload-status", "active");
|
|
222
|
-
const emulatorInfo = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE);
|
|
223
|
-
res.header("x-goog-upload-url", `http://${req.hostname}:${emulatorInfo === null || emulatorInfo === void 0 ? void 0 : emulatorInfo.port}/v0/b/${bucketId}/o?name=${objectId}&upload_id=${upload.id}&upload_protocol=resumable`);
|
|
224
224
|
res.header("x-gupload-uploadid", upload.id);
|
|
225
|
+
const uploadUrl = registry_1.EmulatorRegistry.url(types_1.Emulators.STORAGE, req);
|
|
226
|
+
uploadUrl.pathname = `/v0/b/${bucketId}/o`;
|
|
227
|
+
uploadUrl.searchParams.set("name", objectId);
|
|
228
|
+
uploadUrl.searchParams.set("upload_id", upload.id);
|
|
229
|
+
uploadUrl.searchParams.set("upload_protocol", "resumable");
|
|
230
|
+
res.header("x-goog-upload-url", uploadUrl.toString());
|
|
225
231
|
return res.sendStatus(200);
|
|
226
232
|
}
|
|
227
233
|
if (!req.query.upload_id) {
|
|
@@ -306,6 +312,7 @@ function createFirebaseEndpoints(emulator) {
|
|
|
306
312
|
}
|
|
307
313
|
throw err;
|
|
308
314
|
}
|
|
315
|
+
res.header("x-goog-upload-status", "final");
|
|
309
316
|
storedMetadata.addDownloadToken(false);
|
|
310
317
|
return res.status(200).json(new metadata_1.OutgoingFirebaseMetadata(storedMetadata));
|
|
311
318
|
}
|
|
@@ -462,3 +469,23 @@ function setObjectHeaders(res, metadata, headerOverride = { "Content-Encoding":
|
|
|
462
469
|
res.setHeader("Content-Language", metadata.contentLanguage);
|
|
463
470
|
}
|
|
464
471
|
}
|
|
472
|
+
function isValidPrefix(prefix) {
|
|
473
|
+
return isValidNonEncodedPathString(removeAtMostOneTrailingSlash(prefix));
|
|
474
|
+
}
|
|
475
|
+
function isValidNonEncodedPathString(path) {
|
|
476
|
+
if (path.startsWith("/")) {
|
|
477
|
+
path = path.substring(1);
|
|
478
|
+
}
|
|
479
|
+
if (!path) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
for (const pathSegment of path.split("/")) {
|
|
483
|
+
if (!pathSegment) {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
function removeAtMostOneTrailingSlash(path) {
|
|
490
|
+
return path.replace(/\/$/, "");
|
|
491
|
+
}
|
|
@@ -200,9 +200,12 @@ function createCloudEndpoints(emulator) {
|
|
|
200
200
|
metadataRaw: JSON.stringify(req.body),
|
|
201
201
|
authorization: req.header("authorization"),
|
|
202
202
|
});
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
const uploadUrl = registry_1.EmulatorRegistry.url(types_1.Emulators.STORAGE, req);
|
|
204
|
+
uploadUrl.pathname = `/upload/storage/v1/b/${req.params.bucketId}/o`;
|
|
205
|
+
uploadUrl.searchParams.set("name", name);
|
|
206
|
+
uploadUrl.searchParams.set("uploadType", "resumable");
|
|
207
|
+
uploadUrl.searchParams.set("upload_id", upload.id);
|
|
208
|
+
return res.header("location", uploadUrl.toString()).sendStatus(200);
|
|
206
209
|
}
|
|
207
210
|
let metadataRaw;
|
|
208
211
|
let dataRaw;
|
|
@@ -348,7 +348,11 @@ class StorageLayer {
|
|
|
348
348
|
logger_1.logger.warn(`Could not find file "${blobPath}" in storage export.`);
|
|
349
349
|
continue;
|
|
350
350
|
}
|
|
351
|
-
|
|
351
|
+
let decodedBlobPath = decodeURIComponent(blobPath);
|
|
352
|
+
const decodedBlobPathSep = getPathSep(decodedBlobPath);
|
|
353
|
+
if (decodedBlobPathSep !== path.sep) {
|
|
354
|
+
decodedBlobPath = decodedBlobPath.split(decodedBlobPathSep).join(path.sep);
|
|
355
|
+
}
|
|
352
356
|
const blobDiskPath = this._persistence.getDiskPath(decodedBlobPath);
|
|
353
357
|
const file = new StoredFile(metadata, blobDiskPath);
|
|
354
358
|
this._files.set(decodedBlobPath, file);
|
|
@@ -369,3 +373,7 @@ class StorageLayer {
|
|
|
369
373
|
}
|
|
370
374
|
}
|
|
371
375
|
exports.StorageLayer = StorageLayer;
|
|
376
|
+
function getPathSep(decodedPath) {
|
|
377
|
+
const firstSepIndex = decodedPath.search(/[^a-z0-9-_.]/g);
|
|
378
|
+
return decodedPath[firstSepIndex];
|
|
379
|
+
}
|
package/lib/ensureApiEnabled.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.enableApiURI = exports.ensure = exports.check = exports.POLL_SETTINGS = void 0;
|
|
4
4
|
const cli_color_1 = require("cli-color");
|
|
5
|
-
const
|
|
5
|
+
const track_1 = require("./track");
|
|
6
6
|
const api_1 = require("./api");
|
|
7
7
|
const apiv2_1 = require("./apiv2");
|
|
8
8
|
const utils = require("./utils");
|
|
@@ -16,7 +16,9 @@ const apiClient = new apiv2_1.Client({
|
|
|
16
16
|
apiVersion: "v1",
|
|
17
17
|
});
|
|
18
18
|
async function check(projectId, apiName, prefix, silent = false) {
|
|
19
|
-
const res = await apiClient.get(`/projects/${projectId}/services/${apiName}
|
|
19
|
+
const res = await apiClient.get(`/projects/${projectId}/services/${apiName}`, {
|
|
20
|
+
skipLog: { resBody: true },
|
|
21
|
+
});
|
|
20
22
|
const isEnabled = res.body.state === "ENABLED";
|
|
21
23
|
if (isEnabled && !silent) {
|
|
22
24
|
utils.logLabeledSuccess(prefix, `required API ${(0, cli_color_1.bold)(apiName)} is enabled`);
|
|
@@ -26,7 +28,9 @@ async function check(projectId, apiName, prefix, silent = false) {
|
|
|
26
28
|
exports.check = check;
|
|
27
29
|
async function enable(projectId, apiName) {
|
|
28
30
|
try {
|
|
29
|
-
await apiClient.post(`/projects/${projectId}/services/${apiName}:enable
|
|
31
|
+
await apiClient.post(`/projects/${projectId}/services/${apiName}:enable`, undefined, {
|
|
32
|
+
skipLog: { resBody: true },
|
|
33
|
+
});
|
|
30
34
|
}
|
|
31
35
|
catch (err) {
|
|
32
36
|
if ((0, error_1.isBillingError)(err)) {
|
|
@@ -46,7 +50,7 @@ async function pollCheckEnabled(projectId, apiName, prefix, silent, enablementRe
|
|
|
46
50
|
});
|
|
47
51
|
const isEnabled = await check(projectId, apiName, prefix, silent);
|
|
48
52
|
if (isEnabled) {
|
|
49
|
-
void track("api_enabled", apiName);
|
|
53
|
+
void (0, track_1.track)("api_enabled", apiName);
|
|
50
54
|
return;
|
|
51
55
|
}
|
|
52
56
|
if (!silent) {
|
|
@@ -16,7 +16,7 @@ marked.setOptions({
|
|
|
16
16
|
renderer: new TerminalRenderer(),
|
|
17
17
|
});
|
|
18
18
|
const EXTENSIONS_CHANGELOG = "CHANGELOG.md";
|
|
19
|
-
const VERSION_LINE_REGEX = /##.*(\d+\.\d+\.\d+).*/;
|
|
19
|
+
const VERSION_LINE_REGEX = /##.*(\d+\.\d+\.\d+(?:-((\d+|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(\d+|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?).*/;
|
|
20
20
|
async function getReleaseNotesForUpdate(args) {
|
|
21
21
|
const releaseNotes = {};
|
|
22
22
|
const filter = `id<="${args.toVersion}" AND id>"${args.fromVersion}"`;
|
|
@@ -9,6 +9,7 @@ const localHelper = require("../localHelper");
|
|
|
9
9
|
const triggerHelper = require("./triggerHelper");
|
|
10
10
|
const extensionsApi_1 = require("../extensionsApi");
|
|
11
11
|
const extensionsHelper = require("../extensionsHelper");
|
|
12
|
+
const planner = require("../../deploy/extensions/planner");
|
|
12
13
|
const config_1 = require("../../config");
|
|
13
14
|
const error_1 = require("../../error");
|
|
14
15
|
const emulatorLogger_1 = require("../../emulator/emulatorLogger");
|
|
@@ -35,13 +36,13 @@ async function buildOptions(options) {
|
|
|
35
36
|
return options;
|
|
36
37
|
}
|
|
37
38
|
exports.buildOptions = buildOptions;
|
|
38
|
-
async function getExtensionFunctionInfo(
|
|
39
|
-
const spec = await
|
|
39
|
+
async function getExtensionFunctionInfo(instance, paramValues) {
|
|
40
|
+
const spec = await planner.getExtensionSpec(instance);
|
|
40
41
|
const functionResources = specHelper.getFunctionResourcesWithParamSubstitution(spec, paramValues);
|
|
41
42
|
const extensionTriggers = functionResources
|
|
42
43
|
.map((r) => triggerHelper.functionResourceToEmulatedTriggerDefintion(r))
|
|
43
44
|
.map((trigger) => {
|
|
44
|
-
trigger.name = `ext-${instanceId}-${trigger.name}`;
|
|
45
|
+
trigger.name = `ext-${instance.instanceId}-${trigger.name}`;
|
|
45
46
|
return trigger;
|
|
46
47
|
});
|
|
47
48
|
const nodeMajorVersion = specHelper.getNodeVersion(functionResources);
|