firebase-tools 11.16.1 → 11.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/deploy/extensions/planner.js +2 -1
- package/lib/deploy/functions/deploy.js +5 -2
- package/lib/deploy/functions/runtimes/index.js +2 -1
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +1 -0
- package/lib/deploy/lifecycleHooks.js +2 -1
- package/lib/emulator/auth/apiSpec.js +549 -201
- package/lib/emulator/auth/operations.js +4 -2
- package/lib/emulator/auth/server.js +19 -8
- package/lib/emulator/eventarcEmulator.js +2 -1
- package/lib/emulator/eventarcEmulatorUtils.js +60 -0
- package/lib/emulator/extensions/validation.js +2 -0
- package/lib/emulator/extensionsEmulator.js +1 -1
- package/lib/emulator/functionsEmulator.js +40 -29
- package/lib/extensions/billingMigrationHelper.js +2 -1
- package/lib/extensions/displayExtensionInfo.js +2 -1
- package/lib/extensions/emulator/specHelper.js +4 -3
- package/lib/extensions/emulator/triggerHelper.js +64 -20
- package/lib/extensions/extensionsHelper.js +9 -9
- package/lib/extensions/types.js +2 -1
- package/lib/extensions/utils.js +14 -1
- package/lib/firestore/indexes-api.js +7 -1
- package/lib/firestore/indexes-sort.js +4 -0
- package/lib/firestore/indexes.js +31 -5
- package/lib/firestore/util.js +5 -1
- package/lib/firestore/validator.js +7 -1
- package/lib/frameworks/angular/index.js +3 -0
- package/lib/frameworks/next/index.js +59 -16
- package/lib/frameworks/next/interfaces.js +2 -0
- package/lib/frameworks/next/utils.js +34 -0
- package/lib/frameworks/nuxt/index.js +3 -0
- package/lib/frameworks/utils.js +24 -0
- package/lib/frameworks/vite/index.js +4 -1
- package/lib/init/features/emulators.js +1 -1
- package/lib/utils.js +1 -1
- package/npm-shrinkwrap.json +38 -72
- package/package.json +1 -1
- package/schema/firebase-config.json +4 -2
|
@@ -1126,7 +1126,8 @@ async function signInWithIdp(state, reqBody) {
|
|
|
1126
1126
|
oauthExpiresIn: coercePrimitiveToString(response.oauthExpireIn),
|
|
1127
1127
|
};
|
|
1128
1128
|
if (response.isNewUser) {
|
|
1129
|
-
|
|
1129
|
+
const timestamp = new Date();
|
|
1130
|
+
let updates = Object.assign(Object.assign({}, accountUpdates.fields), { createdAt: timestamp.getTime().toString(), lastLoginAt: timestamp.getTime().toString(), providerUserInfo: [providerUserInfo], tenantId: state instanceof state_1.TenantProjectState ? state.tenantId : undefined });
|
|
1130
1131
|
const localId = state.generateLocalId();
|
|
1131
1132
|
const userBeforeCreate = Object.assign({ localId }, updates);
|
|
1132
1133
|
const blockingResponse = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_CREATE, userBeforeCreate, {
|
|
@@ -1187,7 +1188,8 @@ async function signInWithIdp(state, reqBody) {
|
|
|
1187
1188
|
async function signInWithPassword(state, reqBody) {
|
|
1188
1189
|
(0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
|
|
1189
1190
|
(0, errors_1.assert)(state.allowPasswordSignup, "PASSWORD_LOGIN_DISABLED");
|
|
1190
|
-
(0, errors_1.assert)(reqBody.email, "MISSING_EMAIL");
|
|
1191
|
+
(0, errors_1.assert)(reqBody.email !== undefined, "MISSING_EMAIL");
|
|
1192
|
+
(0, errors_1.assert)((0, utils_1.isValidEmailAddress)(reqBody.email), "INVALID_EMAIL");
|
|
1191
1193
|
(0, errors_1.assert)(reqBody.password, "MISSING_PASSWORD");
|
|
1192
1194
|
if (reqBody.captchaResponse || reqBody.captchaChallenge) {
|
|
1193
1195
|
throw new errors_1.NotImplementedError("captcha unimplemented");
|
|
@@ -97,14 +97,24 @@ async function createApp(defaultProjectId, singleProjectMode = index_1.SinglePro
|
|
|
97
97
|
registerLegacyRoutes(app);
|
|
98
98
|
(0, handlers_1.registerHandlers)(app, (apiKey, tenantId) => getProjectStateById(getProjectIdByApiKey(apiKey), tenantId));
|
|
99
99
|
const apiKeyAuthenticator = (ctx, info) => {
|
|
100
|
-
if (info.in !== "query") {
|
|
101
|
-
throw new Error('apiKey must be defined as in: "query" in API spec.');
|
|
102
|
-
}
|
|
103
100
|
if (!info.name) {
|
|
104
|
-
throw new Error("apiKey param name is undefined in API spec.");
|
|
101
|
+
throw new Error("apiKey param/header name is undefined in API spec.");
|
|
102
|
+
}
|
|
103
|
+
let key;
|
|
104
|
+
const req = ctx.req;
|
|
105
|
+
switch (info.in) {
|
|
106
|
+
case "header":
|
|
107
|
+
key = req.get(info.name);
|
|
108
|
+
break;
|
|
109
|
+
case "query": {
|
|
110
|
+
const q = req.query[info.name];
|
|
111
|
+
key = typeof q === "string" ? q : undefined;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
default:
|
|
115
|
+
throw new Error('apiKey must be defined as in: "query" or "header" in API spec.');
|
|
105
116
|
}
|
|
106
|
-
|
|
107
|
-
if (typeof key === "string" && key.length > 0) {
|
|
117
|
+
if (key) {
|
|
108
118
|
return { type: "success", user: getProjectIdByApiKey(key) };
|
|
109
119
|
}
|
|
110
120
|
else {
|
|
@@ -138,7 +148,8 @@ async function createApp(defaultProjectId, singleProjectMode = index_1.SinglePro
|
|
|
138
148
|
const apis = await exegesisExpress.middleware(specForRouter(), {
|
|
139
149
|
controllers: { auth: toExegesisController(operations_1.authOperations, getProjectStateById) },
|
|
140
150
|
authenticators: {
|
|
141
|
-
|
|
151
|
+
apiKeyQuery: apiKeyAuthenticator,
|
|
152
|
+
apiKeyHeader: apiKeyAuthenticator,
|
|
142
153
|
Oauth2: oauth2Authenticator,
|
|
143
154
|
},
|
|
144
155
|
autoHandleHttpErrors(err) {
|
|
@@ -206,7 +217,7 @@ async function createApp(defaultProjectId, singleProjectMode = index_1.SinglePro
|
|
|
206
217
|
postController(ctx) {
|
|
207
218
|
if (ctx.res.statusCode === 401) {
|
|
208
219
|
const requirements = ctx.api.operationObject.security;
|
|
209
|
-
if (requirements === null || requirements === void 0 ? void 0 : requirements.some((req) => req.
|
|
220
|
+
if (requirements === null || requirements === void 0 ? void 0 : requirements.some((req) => req.apiKeyQuery || req.apiKeyHeader)) {
|
|
210
221
|
throw new errors_2.PermissionDeniedError("The request is missing a valid API key.");
|
|
211
222
|
}
|
|
212
223
|
else {
|
|
@@ -8,6 +8,7 @@ const utils_1 = require("../utils");
|
|
|
8
8
|
const emulatorLogger_1 = require("./emulatorLogger");
|
|
9
9
|
const registry_1 = require("./registry");
|
|
10
10
|
const error_1 = require("../error");
|
|
11
|
+
const eventarcEmulatorUtils_1 = require("./eventarcEmulatorUtils");
|
|
11
12
|
class EventarcEmulator {
|
|
12
13
|
constructor(args) {
|
|
13
14
|
this.args = args;
|
|
@@ -89,7 +90,7 @@ class EventarcEmulator {
|
|
|
89
90
|
.request({
|
|
90
91
|
method: "POST",
|
|
91
92
|
path: `/functions/projects/${trigger.projectId}/triggers/${trigger.triggerName}`,
|
|
92
|
-
body: JSON.stringify(event),
|
|
93
|
+
body: JSON.stringify((0, eventarcEmulatorUtils_1.cloudEventFromProtoToJson)(event)),
|
|
93
94
|
responseType: "stream",
|
|
94
95
|
resolveOnHTTPError: true,
|
|
95
96
|
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cloudEventFromProtoToJson = void 0;
|
|
4
|
+
const error_1 = require("../error");
|
|
5
|
+
const BUILT_IN_ATTRS = ["time", "datacontenttype", "subject"];
|
|
6
|
+
function cloudEventFromProtoToJson(ce) {
|
|
7
|
+
if (ce["id"] === undefined) {
|
|
8
|
+
throw new error_1.FirebaseError("CloudEvent 'id' is required.");
|
|
9
|
+
}
|
|
10
|
+
if (ce["type"] === undefined) {
|
|
11
|
+
throw new error_1.FirebaseError("CloudEvent 'type' is required.");
|
|
12
|
+
}
|
|
13
|
+
if (ce["specVersion"] === undefined) {
|
|
14
|
+
throw new error_1.FirebaseError("CloudEvent 'specVersion' is required.");
|
|
15
|
+
}
|
|
16
|
+
if (ce["source"] === undefined) {
|
|
17
|
+
throw new error_1.FirebaseError("CloudEvent 'source' is required.");
|
|
18
|
+
}
|
|
19
|
+
const out = {
|
|
20
|
+
id: ce["id"],
|
|
21
|
+
type: ce["type"],
|
|
22
|
+
specversion: ce["specVersion"],
|
|
23
|
+
source: ce["source"],
|
|
24
|
+
subject: getOptionalAttribute(ce, "subject", "ceString"),
|
|
25
|
+
time: getRequiredAttribute(ce, "time", "ceTimestamp"),
|
|
26
|
+
data: getData(ce),
|
|
27
|
+
datacontenttype: getRequiredAttribute(ce, "datacontenttype", "ceString"),
|
|
28
|
+
};
|
|
29
|
+
for (const attr in ce["attributes"]) {
|
|
30
|
+
if (BUILT_IN_ATTRS.includes(attr)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
out[attr] = getRequiredAttribute(ce, attr, "ceString");
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
exports.cloudEventFromProtoToJson = cloudEventFromProtoToJson;
|
|
38
|
+
function getOptionalAttribute(ce, attr, type) {
|
|
39
|
+
return ce["attributes"][attr][type];
|
|
40
|
+
}
|
|
41
|
+
function getRequiredAttribute(ce, attr, type) {
|
|
42
|
+
const val = ce["attributes"][attr][type];
|
|
43
|
+
if (val === undefined) {
|
|
44
|
+
throw new error_1.FirebaseError("CloudEvent must contain " + attr + " attribute");
|
|
45
|
+
}
|
|
46
|
+
return val;
|
|
47
|
+
}
|
|
48
|
+
function getData(ce) {
|
|
49
|
+
const contentType = getRequiredAttribute(ce, "datacontenttype", "ceString");
|
|
50
|
+
switch (contentType) {
|
|
51
|
+
case "application/json":
|
|
52
|
+
return JSON.parse(ce["textData"]);
|
|
53
|
+
case "text/plain":
|
|
54
|
+
return ce["textData"];
|
|
55
|
+
case undefined:
|
|
56
|
+
return undefined;
|
|
57
|
+
default:
|
|
58
|
+
throw new error_1.FirebaseError("Unsupported content type: " + contentType);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -59,6 +59,8 @@ function checkForUnemulatedTriggerTypes(backend, options) {
|
|
|
59
59
|
return !(0, controller_1.shouldStart)(options, types_1.Emulators.AUTH);
|
|
60
60
|
case constants_1.Constants.SERVICE_STORAGE:
|
|
61
61
|
return !(0, controller_1.shouldStart)(options, types_1.Emulators.STORAGE);
|
|
62
|
+
case constants_1.Constants.SERVICE_EVENTARC:
|
|
63
|
+
return !(0, controller_1.shouldStart)(options, types_1.Emulators.EVENTARC);
|
|
62
64
|
default:
|
|
63
65
|
return true;
|
|
64
66
|
}
|
|
@@ -142,7 +142,7 @@ class ExtensionsEmulator {
|
|
|
142
142
|
const emulatableBackend = {
|
|
143
143
|
functionsDir,
|
|
144
144
|
env: nonSecretEnv,
|
|
145
|
-
codebase:
|
|
145
|
+
codebase: instance.instanceId,
|
|
146
146
|
secretEnv: secretEnvVariables,
|
|
147
147
|
predefinedTriggers: extensionTriggers,
|
|
148
148
|
nodeMajorVersion: nodeMajorVersion,
|
|
@@ -334,7 +334,7 @@ class FunctionsEmulator {
|
|
|
334
334
|
added = await this.addFirestoreTrigger(this.args.projectId, key, definition.eventTrigger);
|
|
335
335
|
break;
|
|
336
336
|
case constants_1.Constants.SERVICE_REALTIME_DATABASE:
|
|
337
|
-
added = await this.addRealtimeDatabaseTrigger(this.args.projectId, key, definition.eventTrigger, signature, definition.region);
|
|
337
|
+
added = await this.addRealtimeDatabaseTrigger(this.args.projectId, definition.id, key, definition.eventTrigger, signature, definition.region);
|
|
338
338
|
break;
|
|
339
339
|
case constants_1.Constants.SERVICE_PUBSUB:
|
|
340
340
|
added = await this.addPubsubTrigger(definition.name, key, definition.eventTrigger, signature, definition.schedule);
|
|
@@ -377,6 +377,12 @@ class FunctionsEmulator {
|
|
|
377
377
|
}
|
|
378
378
|
}
|
|
379
379
|
if (this.args.debugPort) {
|
|
380
|
+
emulatableBackend.secretEnv = Object.values(toSetup.reduce((acc, curr) => {
|
|
381
|
+
for (const secret of curr.secretEnvironmentVariables || []) {
|
|
382
|
+
acc[secret.key] = secret;
|
|
383
|
+
}
|
|
384
|
+
return acc;
|
|
385
|
+
}, {}));
|
|
380
386
|
await this.startRuntime(emulatableBackend);
|
|
381
387
|
}
|
|
382
388
|
}
|
|
@@ -438,7 +444,7 @@ class FunctionsEmulator {
|
|
|
438
444
|
}
|
|
439
445
|
return { bundle, apiPath, instance };
|
|
440
446
|
}
|
|
441
|
-
getV2DatabaseApiAttributes(projectId, key, eventTrigger, region) {
|
|
447
|
+
getV2DatabaseApiAttributes(projectId, id, key, eventTrigger, region) {
|
|
442
448
|
var _a, _b, _c;
|
|
443
449
|
const instance = ((_a = eventTrigger.eventFilters) === null || _a === void 0 ? void 0 : _a.instance) || ((_b = eventTrigger.eventFilterPathPatterns) === null || _b === void 0 ? void 0 : _b.instance);
|
|
444
450
|
if (!instance) {
|
|
@@ -448,6 +454,9 @@ class FunctionsEmulator {
|
|
|
448
454
|
if (!ref) {
|
|
449
455
|
throw new error_1.FirebaseError("A database reference must be supplied.");
|
|
450
456
|
}
|
|
457
|
+
if (region !== "us-central1") {
|
|
458
|
+
this.logger.logLabeled("WARN", `functions[${id}]`, `function region is defined outside the database region, will not trigger.`);
|
|
459
|
+
}
|
|
451
460
|
const bundle = JSON.stringify({
|
|
452
461
|
name: `projects/${projectId}/locations/${region}/triggers/${key}`,
|
|
453
462
|
path: ref,
|
|
@@ -458,12 +467,12 @@ class FunctionsEmulator {
|
|
|
458
467
|
const apiPath = "/.settings/functionTriggers.json";
|
|
459
468
|
return { bundle, apiPath, instance };
|
|
460
469
|
}
|
|
461
|
-
async addRealtimeDatabaseTrigger(projectId, key, eventTrigger, signature, region) {
|
|
470
|
+
async addRealtimeDatabaseTrigger(projectId, id, key, eventTrigger, signature, region) {
|
|
462
471
|
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.DATABASE)) {
|
|
463
472
|
return false;
|
|
464
473
|
}
|
|
465
474
|
const { bundle, apiPath, instance } = signature === "cloudevent"
|
|
466
|
-
? this.getV2DatabaseApiAttributes(projectId, key, eventTrigger, region)
|
|
475
|
+
? this.getV2DatabaseApiAttributes(projectId, id, key, eventTrigger, region)
|
|
467
476
|
: this.getV1DatabaseApiAttributes(projectId, key, eventTrigger);
|
|
468
477
|
logger_1.logger.debug(`addRealtimeDatabaseTrigger[${instance}]`, JSON.stringify(bundle));
|
|
469
478
|
const client = registry_1.EmulatorRegistry.client(types_1.Emulators.DATABASE);
|
|
@@ -746,34 +755,32 @@ class FunctionsEmulator {
|
|
|
746
755
|
this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${secretPath}: ${e.message}`);
|
|
747
756
|
}
|
|
748
757
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
errs.push(result.reason);
|
|
764
|
-
}
|
|
765
|
-
else {
|
|
766
|
-
const [k, v] = result.value;
|
|
767
|
-
secretEnvs[k] = v;
|
|
768
|
-
}
|
|
758
|
+
const secrets = (trigger === null || trigger === void 0 ? void 0 : trigger.secretEnvironmentVariables) || backend.secretEnv;
|
|
759
|
+
const accesses = secrets
|
|
760
|
+
.filter((s) => !secretEnvs[s.key])
|
|
761
|
+
.map(async (s) => {
|
|
762
|
+
var _a;
|
|
763
|
+
this.logger.logLabeled("INFO", "functions", `Trying to access secret ${s.secret}@latest`);
|
|
764
|
+
const value = await (0, secretManager_1.accessSecretVersion)(this.getProjectId(), s.secret, (_a = s.version) !== null && _a !== void 0 ? _a : "latest");
|
|
765
|
+
return [s.key, value];
|
|
766
|
+
});
|
|
767
|
+
const accessResults = await (0, utils_1.allSettled)(accesses);
|
|
768
|
+
const errs = [];
|
|
769
|
+
for (const result of accessResults) {
|
|
770
|
+
if (result.status === "rejected") {
|
|
771
|
+
errs.push(result.reason);
|
|
769
772
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
`or provide override values in ${secretPath}:\n\t` +
|
|
774
|
-
errs.join("\n\t"));
|
|
773
|
+
else {
|
|
774
|
+
const [k, v] = result.value;
|
|
775
|
+
secretEnvs[k] = v;
|
|
775
776
|
}
|
|
776
777
|
}
|
|
778
|
+
if (errs.length > 0) {
|
|
779
|
+
this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
|
|
780
|
+
"Make sure the credential used for the Functions Emulator have access " +
|
|
781
|
+
`or provide override values in ${secretPath}:\n\t` +
|
|
782
|
+
errs.join("\n\t"));
|
|
783
|
+
}
|
|
777
784
|
return secretEnvs;
|
|
778
785
|
}
|
|
779
786
|
async startRuntime(backend, trigger) {
|
|
@@ -881,6 +888,10 @@ class FunctionsEmulator {
|
|
|
881
888
|
return;
|
|
882
889
|
}
|
|
883
890
|
const record = this.getTriggerRecordByKey(triggerId);
|
|
891
|
+
if (!record.enabled) {
|
|
892
|
+
res.status(204).send("Background triggers are currently disabled.");
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
884
895
|
const trigger = record.def;
|
|
885
896
|
logger_1.logger.debug(`Accepted request ${method} ${req.url} --> ${triggerId}`);
|
|
886
897
|
const reqBody = req.rawBody;
|
|
@@ -7,6 +7,7 @@ const error_1 = require("../error");
|
|
|
7
7
|
const extensionsHelper_1 = require("./extensionsHelper");
|
|
8
8
|
const prompt_1 = require("../prompt");
|
|
9
9
|
const utils = require("../utils");
|
|
10
|
+
const utils_1 = require("./utils");
|
|
10
11
|
marked.setOptions({
|
|
11
12
|
renderer: new TerminalRenderer(),
|
|
12
13
|
});
|
|
@@ -34,7 +35,7 @@ function hasRuntime(spec, runtime) {
|
|
|
34
35
|
const specVersion = spec.specVersion || defaultSpecVersion;
|
|
35
36
|
const defaultRuntime = defaultRuntimes[specVersion];
|
|
36
37
|
const resources = spec.resources || [];
|
|
37
|
-
return resources.some((r) =>
|
|
38
|
+
return resources.some((r) => runtime === ((0, utils_1.getResourceRuntime)(r) || defaultRuntime));
|
|
38
39
|
}
|
|
39
40
|
function displayNode10UpdateBillingNotice(curSpec, newSpec) {
|
|
40
41
|
if (hasRuntime(curSpec, "nodejs8") && hasRuntime(newSpec, "nodejs10")) {
|
|
@@ -8,6 +8,7 @@ const utils = require("../utils");
|
|
|
8
8
|
const extensionsHelper_1 = require("./extensionsHelper");
|
|
9
9
|
const logger_1 = require("../logger");
|
|
10
10
|
const error_1 = require("../error");
|
|
11
|
+
const types_1 = require("./types");
|
|
11
12
|
const iam = require("../gcp/iam");
|
|
12
13
|
const secretsUtils_1 = require("./secretsUtils");
|
|
13
14
|
marked.setOptions({
|
|
@@ -80,7 +81,7 @@ function displayApis(apis) {
|
|
|
80
81
|
return "**APIs used by this Extension**:\n" + lines.join("\n");
|
|
81
82
|
}
|
|
82
83
|
function usesTasks(spec) {
|
|
83
|
-
return spec.resources.some((r) => { var _a; return ((_a = r.properties) === null || _a === void 0 ? void 0 : _a.taskQueueTrigger) !== undefined; });
|
|
84
|
+
return spec.resources.some((r) => { var _a; return r.type === types_1.FUNCTIONS_RESOURCE_TYPE && ((_a = r.properties) === null || _a === void 0 ? void 0 : _a.taskQueueTrigger) !== undefined; });
|
|
84
85
|
}
|
|
85
86
|
function impliedRoles(spec) {
|
|
86
87
|
var _a, _b, _c;
|
|
@@ -6,11 +6,13 @@ const path = require("path");
|
|
|
6
6
|
const fs = require("fs-extra");
|
|
7
7
|
const error_1 = require("../../error");
|
|
8
8
|
const extensionsHelper_1 = require("../extensionsHelper");
|
|
9
|
+
const utils_1 = require("../utils");
|
|
9
10
|
const functionsEmulatorUtils_1 = require("../../emulator/functionsEmulatorUtils");
|
|
10
11
|
const SPEC_FILE = "extension.yaml";
|
|
11
12
|
const POSTINSTALL_FILE = "POSTINSTALL.md";
|
|
12
13
|
const validFunctionTypes = [
|
|
13
14
|
"firebaseextensions.v1beta.function",
|
|
15
|
+
"firebaseextensions.v1beta.v2function",
|
|
14
16
|
"firebaseextensions.v1beta.scheduledFunction",
|
|
15
17
|
];
|
|
16
18
|
function wrappedSafeLoad(source) {
|
|
@@ -68,9 +70,8 @@ exports.getFunctionProperties = getFunctionProperties;
|
|
|
68
70
|
function getNodeVersion(resources) {
|
|
69
71
|
const invalidRuntimes = [];
|
|
70
72
|
const versions = resources.map((r) => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const runtimeName = (_b = r.properties) === null || _b === void 0 ? void 0 : _b.runtime;
|
|
73
|
+
if ((0, utils_1.getResourceRuntime)(r)) {
|
|
74
|
+
const runtimeName = (0, utils_1.getResourceRuntime)(r);
|
|
74
75
|
const runtime = (0, functionsEmulatorUtils_1.parseRuntimeVersion)(runtimeName);
|
|
75
76
|
if (!runtime) {
|
|
76
77
|
invalidRuntimes.push(runtimeName);
|
|
@@ -4,30 +4,74 @@ exports.functionResourceToEmulatedTriggerDefintion = void 0;
|
|
|
4
4
|
const functionsEmulatorShared_1 = require("../../emulator/functionsEmulatorShared");
|
|
5
5
|
const emulatorLogger_1 = require("../../emulator/emulatorLogger");
|
|
6
6
|
const types_1 = require("../../emulator/types");
|
|
7
|
+
const types_2 = require("../../extensions/types");
|
|
7
8
|
const proto = require("../../gcp/proto");
|
|
9
|
+
const error_1 = require("../../error");
|
|
8
10
|
function functionResourceToEmulatedTriggerDefintion(resource) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
proto.convertIfPresent(etd, properties, "timeoutSeconds", "timeout", proto.secondsFromDuration);
|
|
16
|
-
proto.convertIfPresent(etd, properties, "regions", "location", (str) => [str]);
|
|
17
|
-
proto.copyIfPresent(etd, properties, "availableMemoryMb");
|
|
18
|
-
if (properties.httpsTrigger) {
|
|
19
|
-
etd.httpsTrigger = properties.httpsTrigger;
|
|
20
|
-
}
|
|
21
|
-
if (properties.eventTrigger) {
|
|
22
|
-
etd.eventTrigger = {
|
|
23
|
-
eventType: properties.eventTrigger.eventType,
|
|
24
|
-
resource: properties.eventTrigger.resource,
|
|
25
|
-
service: (0, functionsEmulatorShared_1.getServiceFromEventType)(properties.eventTrigger.eventType),
|
|
11
|
+
const resourceType = resource.type;
|
|
12
|
+
if (resource.type === types_2.FUNCTIONS_RESOURCE_TYPE) {
|
|
13
|
+
const etd = {
|
|
14
|
+
name: resource.name,
|
|
15
|
+
entryPoint: resource.name,
|
|
16
|
+
platform: "gcfv1",
|
|
26
17
|
};
|
|
18
|
+
const properties = resource.properties || {};
|
|
19
|
+
proto.convertIfPresent(etd, properties, "timeoutSeconds", "timeout", proto.secondsFromDuration);
|
|
20
|
+
proto.convertIfPresent(etd, properties, "regions", "location", (str) => [str]);
|
|
21
|
+
proto.copyIfPresent(etd, properties, "availableMemoryMb");
|
|
22
|
+
if (properties.httpsTrigger) {
|
|
23
|
+
etd.httpsTrigger = properties.httpsTrigger;
|
|
24
|
+
}
|
|
25
|
+
if (properties.eventTrigger) {
|
|
26
|
+
etd.eventTrigger = {
|
|
27
|
+
eventType: properties.eventTrigger.eventType,
|
|
28
|
+
resource: properties.eventTrigger.resource,
|
|
29
|
+
service: (0, functionsEmulatorShared_1.getServiceFromEventType)(properties.eventTrigger.eventType),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).log("WARN", `Function '${resource.name} is missing a trigger in extension.yaml. Please add one, as triggers defined in code are ignored.`);
|
|
34
|
+
}
|
|
35
|
+
return etd;
|
|
27
36
|
}
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
if (resource.type === types_2.FUNCTIONS_V2_RESOURCE_TYPE) {
|
|
38
|
+
const etd = {
|
|
39
|
+
name: resource.name,
|
|
40
|
+
entryPoint: resource.name,
|
|
41
|
+
platform: "gcfv2",
|
|
42
|
+
};
|
|
43
|
+
const properties = resource.properties || {};
|
|
44
|
+
proto.convertIfPresent(etd, properties, "regions", "location", (str) => [str]);
|
|
45
|
+
if (properties.serviceConfig) {
|
|
46
|
+
proto.copyIfPresent(etd, properties.serviceConfig, "timeoutSeconds");
|
|
47
|
+
proto.convertIfPresent(etd, properties.serviceConfig, "availableMemoryMb", "availableMemory", (mem) => parseInt(mem));
|
|
48
|
+
}
|
|
49
|
+
if (properties.eventTrigger) {
|
|
50
|
+
etd.eventTrigger = {
|
|
51
|
+
eventType: properties.eventTrigger.eventType,
|
|
52
|
+
service: (0, functionsEmulatorShared_1.getServiceFromEventType)(properties.eventTrigger.eventType),
|
|
53
|
+
};
|
|
54
|
+
proto.copyIfPresent(etd.eventTrigger, properties.eventTrigger, "channel");
|
|
55
|
+
if (properties.eventTrigger.eventFilters) {
|
|
56
|
+
const eventFilters = {};
|
|
57
|
+
const eventFilterPathPatterns = {};
|
|
58
|
+
for (const filter of properties.eventTrigger.eventFilters) {
|
|
59
|
+
if (filter.operator === undefined) {
|
|
60
|
+
eventFilters[filter.attribute] = filter.value;
|
|
61
|
+
}
|
|
62
|
+
else if (filter.operator === "match-path-pattern") {
|
|
63
|
+
eventFilterPathPatterns[filter.attribute] = filter.value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
etd.eventTrigger.eventFilters = eventFilters;
|
|
67
|
+
etd.eventTrigger.eventFilterPathPatterns = eventFilterPathPatterns;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).log("WARN", `Function '${resource.name} is missing a trigger in extension.yaml. Please add one, as triggers defined in code are ignored.`);
|
|
72
|
+
}
|
|
73
|
+
return etd;
|
|
30
74
|
}
|
|
31
|
-
|
|
75
|
+
throw new error_1.FirebaseError("Unexpected resource type " + resourceType);
|
|
32
76
|
}
|
|
33
77
|
exports.functionResourceToEmulatedTriggerDefintion = functionResourceToEmulatedTriggerDefintion;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.canonicalizeRefInput = exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.incrementPrereleaseVersion = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.
|
|
3
|
+
exports.canonicalizeRefInput = exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.incrementPrereleaseVersion = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const ora = require("ora");
|
|
6
6
|
const semver = require("semver");
|
|
@@ -66,9 +66,6 @@ exports.AUTOPOULATED_PARAM_PLACEHOLDERS = {
|
|
|
66
66
|
DATABASE_INSTANCE: "project-id-default-rtdb",
|
|
67
67
|
DATABASE_URL: "https://project-id-default-rtdb.firebaseio.com",
|
|
68
68
|
};
|
|
69
|
-
exports.resourceTypeToNiceName = {
|
|
70
|
-
"firebaseextensions.v1beta.function": "Cloud Function",
|
|
71
|
-
};
|
|
72
69
|
function getDBInstanceFromURL(databaseUrl = "") {
|
|
73
70
|
const instanceRegex = new RegExp("(?:https://)(.*)(?:.firebaseio.com)");
|
|
74
71
|
const matches = instanceRegex.exec(databaseUrl);
|
|
@@ -558,12 +555,15 @@ async function diagnoseAndFixProject(options) {
|
|
|
558
555
|
}
|
|
559
556
|
}
|
|
560
557
|
exports.diagnoseAndFixProject = diagnoseAndFixProject;
|
|
561
|
-
async function canonicalizeRefInput(
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
558
|
+
async function canonicalizeRefInput(refInput) {
|
|
559
|
+
let inferredRef = refInput;
|
|
560
|
+
if (refInput.split("/").length < 2) {
|
|
561
|
+
inferredRef = `firebase/${inferredRef}`;
|
|
562
|
+
}
|
|
563
|
+
if (refInput.split("@").length < 2) {
|
|
564
|
+
inferredRef = `${inferredRef}@latest`;
|
|
565
565
|
}
|
|
566
|
-
const ref = refs.parse(
|
|
566
|
+
const ref = refs.parse(inferredRef);
|
|
567
567
|
ref.version = await (0, planner_1.resolveVersion)(ref);
|
|
568
568
|
return refs.toExtensionVersionRef(ref);
|
|
569
569
|
}
|
package/lib/extensions/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ParamType = exports.FUNCTIONS_RESOURCE_TYPE = exports.Visibility = exports.RegistryLaunchStage = void 0;
|
|
3
|
+
exports.ParamType = exports.FUNCTIONS_V2_RESOURCE_TYPE = exports.FUNCTIONS_RESOURCE_TYPE = exports.Visibility = exports.RegistryLaunchStage = void 0;
|
|
4
4
|
var RegistryLaunchStage;
|
|
5
5
|
(function (RegistryLaunchStage) {
|
|
6
6
|
RegistryLaunchStage["EXPERIMENTAL"] = "EXPERIMENTAL";
|
|
@@ -15,6 +15,7 @@ var Visibility;
|
|
|
15
15
|
Visibility["PUBLIC"] = "public";
|
|
16
16
|
})(Visibility = exports.Visibility || (exports.Visibility = {}));
|
|
17
17
|
exports.FUNCTIONS_RESOURCE_TYPE = "firebaseextensions.v1beta.function";
|
|
18
|
+
exports.FUNCTIONS_V2_RESOURCE_TYPE = "firebaseextensions.v1beta.v2function";
|
|
18
19
|
var ParamType;
|
|
19
20
|
(function (ParamType) {
|
|
20
21
|
ParamType["STRING"] = "STRING";
|
package/lib/extensions/utils.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.formatTimestamp = exports.getRandomString = exports.convertOfficialExtensionsToList = exports.convertExtensionOptionToLabeledList = exports.onceWithJoin = void 0;
|
|
3
|
+
exports.getResourceRuntime = exports.formatTimestamp = exports.getRandomString = exports.convertOfficialExtensionsToList = exports.convertExtensionOptionToLabeledList = exports.onceWithJoin = void 0;
|
|
4
4
|
const prompt_1 = require("../prompt");
|
|
5
|
+
const types_1 = require("./types");
|
|
5
6
|
async function onceWithJoin(question) {
|
|
6
7
|
const response = await (0, prompt_1.promptOnce)(question);
|
|
7
8
|
if (Array.isArray(response)) {
|
|
@@ -48,3 +49,15 @@ function formatTimestamp(timestamp) {
|
|
|
48
49
|
return withoutMs.replace("T", " ");
|
|
49
50
|
}
|
|
50
51
|
exports.formatTimestamp = formatTimestamp;
|
|
52
|
+
function getResourceRuntime(resource) {
|
|
53
|
+
var _a, _b, _c;
|
|
54
|
+
switch (resource.type) {
|
|
55
|
+
case types_1.FUNCTIONS_RESOURCE_TYPE:
|
|
56
|
+
return (_a = resource.properties) === null || _a === void 0 ? void 0 : _a.runtime;
|
|
57
|
+
case types_1.FUNCTIONS_V2_RESOURCE_TYPE:
|
|
58
|
+
return (_c = (_b = resource.properties) === null || _b === void 0 ? void 0 : _b.buildConfig) === null || _c === void 0 ? void 0 : _c.runtime;
|
|
59
|
+
default:
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.getResourceRuntime = getResourceRuntime;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.State = exports.ArrayConfig = exports.Order = exports.QueryScope = exports.Mode = void 0;
|
|
3
|
+
exports.StateTtl = exports.State = exports.ArrayConfig = exports.Order = exports.QueryScope = exports.Mode = void 0;
|
|
4
4
|
var Mode;
|
|
5
5
|
(function (Mode) {
|
|
6
6
|
Mode["ASCENDING"] = "ASCENDING";
|
|
@@ -27,3 +27,9 @@ var State;
|
|
|
27
27
|
State["READY"] = "READY";
|
|
28
28
|
State["NEEDS_REPAIR"] = "NEEDS_REPAIR";
|
|
29
29
|
})(State = exports.State || (exports.State = {}));
|
|
30
|
+
var StateTtl;
|
|
31
|
+
(function (StateTtl) {
|
|
32
|
+
StateTtl["CREATING"] = "CREATING";
|
|
33
|
+
StateTtl["ACTIVE"] = "ACTIVE";
|
|
34
|
+
StateTtl["NEEDS_REPAIR"] = "NEEDS_REPAIR";
|
|
35
|
+
})(StateTtl = exports.StateTtl || (exports.StateTtl = {}));
|
|
@@ -50,6 +50,10 @@ function compareFieldOverride(a, b) {
|
|
|
50
50
|
if (a.collectionGroup !== b.collectionGroup) {
|
|
51
51
|
return a.collectionGroup.localeCompare(b.collectionGroup);
|
|
52
52
|
}
|
|
53
|
+
const compareTtl = Number(!!a.ttl) - Number(!!b.ttl);
|
|
54
|
+
if (compareTtl) {
|
|
55
|
+
return compareTtl;
|
|
56
|
+
}
|
|
53
57
|
if (a.fieldPath !== b.fieldPath) {
|
|
54
58
|
return a.fieldPath.localeCompare(b.fieldPath);
|
|
55
59
|
}
|