firebase-tools 11.4.1 → 11.4.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.
@@ -1,15 +1,4 @@
1
1
  "use strict";
2
- var __rest = (this && this.__rest) || function (s, e) {
3
- var t = {};
4
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
- t[p] = s[p];
6
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
- t[p[i]] = s[p[i]];
10
- }
11
- return t;
12
- };
13
2
  Object.defineProperty(exports, "__esModule", { value: true });
14
3
  exports.backendFromV1Alpha1 = exports.buildFromV1Alpha1 = void 0;
15
4
  const backend = require("../../backend");
@@ -17,6 +6,7 @@ const build = require("../../build");
17
6
  const proto_1 = require("../../../../gcp/proto");
18
7
  const parsing_1 = require("./parsing");
19
8
  const error_1 = require("../../../../error");
9
+ const functional_1 = require("../../../../functional");
20
10
  const CHANNEL_NAME_REGEX = new RegExp("(projects\\/" +
21
11
  "(?<project>(?:\\d+)|(?:[A-Za-z]+[A-Za-z\\d-]*[A-Za-z\\d]?))\\/)?" +
22
12
  "locations\\/" +
@@ -82,29 +72,30 @@ function assertManifestEndpoint(ep, id) {
82
72
  region: "array",
83
73
  platform: (platform) => backend.AllFunctionsPlatforms.includes(platform),
84
74
  entryPoint: "string",
85
- availableMemoryMb: (mem) => backend.AllMemoryOptions.includes(mem),
86
- maxInstances: "number",
87
- minInstances: "number",
88
- concurrency: "number",
89
- serviceAccountEmail: "string",
90
- timeoutSeconds: "number",
91
- vpc: "object",
92
- labels: "object",
93
- ingressSettings: (setting) => backend.AllIngressSettings.includes(setting),
94
- environmentVariables: "object",
95
- secretEnvironmentVariables: "array",
75
+ availableMemoryMb: (mem) => mem === null || backend.isValidMemoryOption(mem),
76
+ maxInstances: "number?",
77
+ minInstances: "number?",
78
+ concurrency: "number?",
79
+ serviceAccount: "string?",
80
+ serviceAccountEmail: "string?",
81
+ timeoutSeconds: "number?",
82
+ vpc: "object?",
83
+ labels: "object?",
84
+ ingressSettings: (setting) => setting === null || backend.AllIngressSettings.includes(setting),
85
+ environmentVariables: "object?",
86
+ secretEnvironmentVariables: "array?",
96
87
  httpsTrigger: "object",
97
88
  callableTrigger: "object",
98
89
  eventTrigger: "object",
99
90
  scheduleTrigger: "object",
100
91
  taskQueueTrigger: "object",
101
92
  blockingTrigger: "object",
102
- cpu: (cpu) => typeof cpu === "number" || cpu === "gcf_gen1",
93
+ cpu: (cpu) => cpu === null || typeof cpu === "number" || cpu === "gcf_gen1",
103
94
  });
104
95
  if (ep.vpc) {
105
96
  (0, parsing_1.assertKeyTypes)(prefix + ".vpc", ep.vpc, {
106
97
  connector: "string",
107
- egressSettings: (setting) => backend.AllVpcEgressSettings.includes(setting),
98
+ egressSettings: (setting) => setting === null || backend.AllVpcEgressSettings.includes(setting),
108
99
  });
109
100
  (0, parsing_1.requireKeys)(prefix + ".vpc", ep.vpc, "connector");
110
101
  }
@@ -141,13 +132,14 @@ function assertManifestEndpoint(ep, id) {
141
132
  eventType: "string",
142
133
  retry: "boolean",
143
134
  region: "string",
144
- serviceAccountEmail: "string",
135
+ serviceAccount: "string?",
136
+ serviceAccountEmail: "string?",
145
137
  channel: "string",
146
138
  });
147
139
  }
148
140
  else if (backend.isHttpsTriggered(ep)) {
149
141
  (0, parsing_1.assertKeyTypes)(prefix + ".httpsTrigger", ep.httpsTrigger, {
150
- invoker: "array",
142
+ invoker: "array?",
151
143
  });
152
144
  }
153
145
  else if (backend.isCallableTriggered(ep)) {
@@ -155,36 +147,39 @@ function assertManifestEndpoint(ep, id) {
155
147
  else if (backend.isScheduleTriggered(ep)) {
156
148
  (0, parsing_1.assertKeyTypes)(prefix + ".scheduleTrigger", ep.scheduleTrigger, {
157
149
  schedule: "string",
158
- timeZone: "string",
159
- retryConfig: "object",
150
+ timeZone: "string?",
151
+ retryConfig: "object?",
160
152
  });
161
- (0, parsing_1.assertKeyTypes)(prefix + ".scheduleTrigger.retryConfig", ep.scheduleTrigger.retryConfig, {
162
- retryCount: "number",
163
- maxDoublings: "number",
164
- minBackoffDuration: "string",
165
- maxBackoffDuration: "string",
166
- maxRetryDuration: "string",
153
+ (0, parsing_1.assertKeyTypes)(prefix + ".scheduleTrigger.retryConfig", ep.scheduleTrigger.retryConfig || {}, {
154
+ retryCount: "number?",
155
+ maxDoublings: "number?",
156
+ minBackoffSeconds: "number?",
157
+ maxBackoffSeconds: "number?",
158
+ maxRetrySeconds: "number?",
159
+ minBackoffDuration: "string?",
160
+ maxBackoffDuration: "string?",
161
+ maxRetryDuration: "string?",
167
162
  });
168
163
  }
169
164
  else if (backend.isTaskQueueTriggered(ep)) {
170
165
  (0, parsing_1.assertKeyTypes)(prefix + ".taskQueueTrigger", ep.taskQueueTrigger, {
171
- rateLimits: "object",
172
- retryConfig: "object",
173
- invoker: "array",
166
+ rateLimits: "object?",
167
+ retryConfig: "object?",
168
+ invoker: "array?",
174
169
  });
175
170
  if (ep.taskQueueTrigger.rateLimits) {
176
171
  (0, parsing_1.assertKeyTypes)(prefix + ".taskQueueTrigger.rateLimits", ep.taskQueueTrigger.rateLimits, {
177
- maxConcurrentDispatches: "number",
178
- maxDispatchesPerSecond: "number",
172
+ maxConcurrentDispatches: "number?",
173
+ maxDispatchesPerSecond: "number?",
179
174
  });
180
175
  }
181
176
  if (ep.taskQueueTrigger.retryConfig) {
182
177
  (0, parsing_1.assertKeyTypes)(prefix + ".taskQueueTrigger.retryConfig", ep.taskQueueTrigger.retryConfig, {
183
- maxAttempts: "number",
184
- maxRetrySeconds: "number",
185
- minBackoffSeconds: "number",
186
- maxBackoffSeconds: "number",
187
- maxDoublings: "number",
178
+ maxAttempts: "number?",
179
+ maxRetrySeconds: "number?",
180
+ minBackoffSeconds: "number?",
181
+ maxBackoffSeconds: "number?",
182
+ maxDoublings: "number?",
188
183
  });
189
184
  }
190
185
  }
@@ -201,18 +196,24 @@ function assertManifestEndpoint(ep, id) {
201
196
  }
202
197
  }
203
198
  function parseEndpointForBuild(id, ep, project, defaultRegion, runtime) {
199
+ var _a;
204
200
  let triggered;
205
201
  if (backend.isEventTriggered(ep)) {
206
- const newTrigger = __rest(ep.eventTrigger, []);
207
- delete newTrigger.serviceAccountEmail;
208
- triggered = { eventTrigger: newTrigger };
209
- triggered.eventTrigger.serviceAccount = ep.eventTrigger.serviceAccountEmail;
210
- (0, proto_1.renameIfPresent)(triggered.eventTrigger, ep.eventTrigger, "channel", "channel", (c) => resolveChannelName(project, c, defaultRegion));
211
- for (const [k, v] of Object.entries(triggered.eventTrigger.eventFilters)) {
212
- if (k === "topic" && !v.startsWith("projects/")) {
213
- triggered.eventTrigger.eventFilters[k] = `projects/${project}/topics/${v}`;
202
+ const eventTrigger = {
203
+ eventType: ep.eventTrigger.eventType,
204
+ retry: ep.eventTrigger.retry,
205
+ };
206
+ (0, proto_1.renameIfPresent)(eventTrigger, ep.eventTrigger, "serviceAccount", "serviceAccountEmail");
207
+ (0, proto_1.copyIfPresent)(eventTrigger, ep.eventTrigger, "serviceAccount", "eventFilterPathPatterns", "region");
208
+ (0, proto_1.convertIfPresent)(eventTrigger, ep.eventTrigger, "channel", (c) => resolveChannelName(project, c, defaultRegion));
209
+ (0, proto_1.convertIfPresent)(eventTrigger, ep.eventTrigger, "eventFilters", (filters) => {
210
+ const copy = Object.assign({}, filters);
211
+ if (copy["topic"] && !copy["topic"].startsWith("projects/")) {
212
+ copy["topic"] = `projects/${project}/topics/${copy["topic"]}`;
214
213
  }
215
- }
214
+ return copy;
215
+ });
216
+ triggered = { eventTrigger };
216
217
  }
217
218
  else if (backend.isHttpsTriggered(ep)) {
218
219
  triggered = { httpsTrigger: {} };
@@ -224,39 +225,39 @@ function parseEndpointForBuild(id, ep, project, defaultRegion, runtime) {
224
225
  else if (backend.isScheduleTriggered(ep)) {
225
226
  const st = {
226
227
  schedule: ep.scheduleTrigger.schedule || "",
227
- timeZone: ep.scheduleTrigger.timeZone || "",
228
- retryConfig: {},
228
+ timeZone: (_a = ep.scheduleTrigger.timeZone) !== null && _a !== void 0 ? _a : null,
229
229
  };
230
230
  if (ep.scheduleTrigger.retryConfig) {
231
- st.retryConfig = {
232
- retryCount: ep.scheduleTrigger.retryConfig.retryCount,
233
- maxDoublings: ep.scheduleTrigger.retryConfig.maxDoublings,
234
- };
235
- if (ep.scheduleTrigger.retryConfig.maxRetryDuration) {
236
- st.retryConfig.maxRetrySeconds = (0, proto_1.secondsFromDuration)(ep.scheduleTrigger.retryConfig.maxRetryDuration);
237
- }
238
- if (ep.scheduleTrigger.retryConfig.maxBackoffDuration) {
239
- st.retryConfig.maxBackoffSeconds = (0, proto_1.secondsFromDuration)(ep.scheduleTrigger.retryConfig.maxBackoffDuration);
240
- }
241
- if (ep.scheduleTrigger.retryConfig.minBackoffDuration) {
242
- st.retryConfig.minBackoffSeconds = (0, proto_1.secondsFromDuration)(ep.scheduleTrigger.retryConfig.minBackoffDuration);
243
- }
231
+ st.retryConfig = {};
232
+ (0, proto_1.copyIfPresent)(st.retryConfig, ep.scheduleTrigger.retryConfig, "retryCount", "minBackoffSeconds", "maxBackoffSeconds", "maxRetrySeconds", "maxDoublings");
233
+ (0, proto_1.convertIfPresent)(st.retryConfig, ep.scheduleTrigger.retryConfig, "minBackoffSeconds", "minBackoffDuration", (0, functional_1.nullsafeVisitor)(proto_1.secondsFromDuration));
234
+ (0, proto_1.convertIfPresent)(st.retryConfig, ep.scheduleTrigger.retryConfig, "maxBackoffSeconds", "maxBackoffDuration", (0, functional_1.nullsafeVisitor)(proto_1.secondsFromDuration));
235
+ (0, proto_1.convertIfPresent)(st.retryConfig, ep.scheduleTrigger.retryConfig, "maxRetrySeconds", "maxRetryDuration", (0, functional_1.nullsafeVisitor)(proto_1.secondsFromDuration));
236
+ }
237
+ else if (ep.scheduleTrigger.retryConfig === null) {
238
+ st.retryConfig = null;
244
239
  }
245
240
  triggered = { scheduleTrigger: st };
246
241
  }
247
242
  else if (backend.isTaskQueueTriggered(ep)) {
248
- const tq = {
249
- invoker: ep.taskQueueTrigger.invoker,
250
- rateLimits: ep.taskQueueTrigger.rateLimits,
251
- };
243
+ const tq = {};
244
+ if (ep.taskQueueTrigger.invoker) {
245
+ tq.invoker = ep.taskQueueTrigger.invoker;
246
+ }
247
+ else if (ep.taskQueueTrigger.invoker === null) {
248
+ tq.invoker = null;
249
+ }
252
250
  if (ep.taskQueueTrigger.retryConfig) {
253
- tq.retryConfig = {
254
- maxRetryDurationSeconds: ep.taskQueueTrigger.retryConfig.maxRetrySeconds,
255
- maxBackoffSeconds: ep.taskQueueTrigger.retryConfig.maxBackoffSeconds,
256
- minBackoffSeconds: ep.taskQueueTrigger.retryConfig.minBackoffSeconds,
257
- maxDoublings: ep.taskQueueTrigger.retryConfig.maxDoublings,
258
- maxAttempts: ep.taskQueueTrigger.retryConfig.maxAttempts,
259
- };
251
+ tq.retryConfig = Object.assign({}, ep.taskQueueTrigger.retryConfig);
252
+ }
253
+ else if (ep.taskQueueTrigger.retryConfig === null) {
254
+ tq.retryConfig = null;
255
+ }
256
+ if (ep.taskQueueTrigger.rateLimits) {
257
+ tq.rateLimits = Object.assign({}, ep.taskQueueTrigger.rateLimits);
258
+ }
259
+ else if (ep.taskQueueTrigger.rateLimits === null) {
260
+ tq.rateLimits = null;
260
261
  }
261
262
  triggered = { taskQueueTrigger: tq };
262
263
  }
@@ -268,18 +269,16 @@ function parseEndpointForBuild(id, ep, project, defaultRegion, runtime) {
268
269
  "firebase-tools with npm install -g firebase-tools@latest");
269
270
  }
270
271
  const parsed = Object.assign({ platform: ep.platform || "gcfv2", region: ep.region || [defaultRegion], project,
271
- runtime, entryPoint: ep.entryPoint, serviceAccount: ep.serviceAccountEmail || null }, triggered);
272
- (0, proto_1.copyIfPresent)(parsed, ep, "availableMemoryMb", "maxInstances", "minInstances", "concurrency", "timeoutSeconds", "vpc", "labels", "ingressSettings", "environmentVariables");
273
- (0, proto_1.renameIfPresent)(parsed, ep, "secretEnvironmentVariables", "secretEnvironmentVariables", (senvs) => {
274
- const secretEnvironmentVariables = [];
275
- for (const { key, secret } of senvs) {
276
- secretEnvironmentVariables.push({
277
- key,
278
- secret: secret || key,
279
- projectId: project,
280
- });
272
+ runtime, entryPoint: ep.entryPoint }, triggered);
273
+ (0, proto_1.renameIfPresent)(parsed, ep, "serviceAccount", "serviceAccountEmail");
274
+ (0, proto_1.copyIfPresent)(parsed, ep, "availableMemoryMb", "cpu", "maxInstances", "minInstances", "concurrency", "timeoutSeconds", "vpc", "labels", "ingressSettings", "environmentVariables", "serviceAccount");
275
+ (0, proto_1.convertIfPresent)(parsed, ep, "secretEnvironmentVariables", (senvs) => {
276
+ if (!senvs) {
277
+ return null;
281
278
  }
282
- return secretEnvironmentVariables;
279
+ return senvs.map(({ key, secret }) => {
280
+ return { key, secret: secret || key, projectId: project };
281
+ });
283
282
  });
284
283
  return parsed;
285
284
  }
@@ -287,17 +286,25 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
287
286
  const allParsed = [];
288
287
  const prefix = `endpoints[${id}]`;
289
288
  const ep = manifest.endpoints[id];
290
- assertManifestEndpoint(ep, prefix);
289
+ assertManifestEndpoint(ep, id);
291
290
  for (const region of ep.region || [defaultRegion]) {
292
291
  let triggered;
293
292
  if (backend.isEventTriggered(ep)) {
294
- triggered = { eventTrigger: ep.eventTrigger };
295
- (0, proto_1.renameIfPresent)(triggered.eventTrigger, ep.eventTrigger, "channel", "channel", (c) => resolveChannelName(project, c, defaultRegion));
296
- for (const [k, v] of Object.entries(triggered.eventTrigger.eventFilters)) {
297
- if (k === "topic" && !v.startsWith("projects/")) {
298
- triggered.eventTrigger.eventFilters[k] = `projects/${project}/topics/${v}`;
293
+ const eventTrigger = {
294
+ eventType: ep.eventTrigger.eventType,
295
+ retry: false,
296
+ };
297
+ (0, proto_1.renameIfPresent)(eventTrigger, ep.eventTrigger, "serviceAccount", "serviceAccountEmail");
298
+ (0, proto_1.copyIfPresent)(eventTrigger, ep.eventTrigger, "eventFilterPathPatterns", "retry", "serviceAccount", "region");
299
+ (0, proto_1.convertIfPresent)(eventTrigger, ep.eventTrigger, "channel", (c) => resolveChannelName(project, c, defaultRegion));
300
+ (0, proto_1.convertIfPresent)(eventTrigger, ep.eventTrigger, "eventFilters", (filters) => {
301
+ const copy = Object.assign({}, filters);
302
+ if (copy["topic"] && !copy["topic"].startsWith("projects/")) {
303
+ copy["topic"] = `projects/${project}/topics/${copy["topic"]}`;
299
304
  }
300
- }
305
+ return copy;
306
+ });
307
+ triggered = { eventTrigger };
301
308
  }
302
309
  else if (backend.isHttpsTriggered(ep)) {
303
310
  triggered = { httpsTrigger: {} };
@@ -324,17 +331,15 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
324
331
  region,
325
332
  project,
326
333
  runtime, entryPoint: ep.entryPoint }, triggered);
327
- (0, proto_1.copyIfPresent)(parsed, ep, "availableMemoryMb", "maxInstances", "minInstances", "concurrency", "serviceAccountEmail", "timeoutSeconds", "vpc", "labels", "ingressSettings", "environmentVariables", "cpu");
328
- (0, proto_1.renameIfPresent)(parsed, ep, "secretEnvironmentVariables", "secretEnvironmentVariables", (senvs) => {
329
- const secretEnvironmentVariables = [];
330
- for (const { key, secret } of senvs) {
331
- secretEnvironmentVariables.push({
332
- key,
333
- secret: secret || key,
334
- projectId: project,
335
- });
334
+ (0, proto_1.renameIfPresent)(parsed, ep, "serviceAccount", "serviceAccountEmail");
335
+ (0, proto_1.copyIfPresent)(parsed, ep, "availableMemoryMb", "maxInstances", "minInstances", "concurrency", "serviceAccount", "timeoutSeconds", "vpc", "labels", "ingressSettings", "environmentVariables", "cpu");
336
+ (0, proto_1.convertIfPresent)(parsed, ep, "secretEnvironmentVariables", (senvs) => {
337
+ if (!senvs) {
338
+ return null;
336
339
  }
337
- return secretEnvironmentVariables;
340
+ return senvs.map(({ key, secret }) => {
341
+ return { key, secret: secret || key, projectId: project };
342
+ });
338
343
  });
339
344
  allParsed.push(parsed);
340
345
  }
@@ -10,6 +10,7 @@ const backend = require("../../backend");
10
10
  const api = require("../../../../api");
11
11
  const proto = require("../../../../gcp/proto");
12
12
  const events = require("../../../../functions/events");
13
+ const functional_1 = require("../../../../functional");
13
14
  const TRIGGER_PARSER = path.resolve(__dirname, "./triggerParser.js");
14
15
  function removeInspectOptions(options) {
15
16
  return options.filter((opt) => !opt.startsWith("--inspect"));
@@ -88,8 +89,9 @@ function mergeRequiredAPIs(backend) {
88
89
  }
89
90
  exports.mergeRequiredAPIs = mergeRequiredAPIs;
90
91
  function addResourcesToBuild(projectId, runtime, annotation, want) {
91
- var _a;
92
+ var _a, _b;
92
93
  Object.freeze(annotation);
94
+ const toSeconds = (0, functional_1.nullsafeVisitor)(proto.secondsFromDuration);
93
95
  const regions = annotation.regions || [api.functionsDefaultRegion];
94
96
  let triggered;
95
97
  const triggerCount = +!!annotation.httpsTrigger +
@@ -110,9 +112,11 @@ function addResourcesToBuild(projectId, runtime, annotation, want) {
110
112
  proto.copyIfPresent(triggered.taskQueueTrigger, annotation.taskQueueTrigger, "invoker");
111
113
  proto.copyIfPresent(triggered.taskQueueTrigger, annotation.taskQueueTrigger, "rateLimits");
112
114
  if (annotation.taskQueueTrigger.retryConfig) {
113
- triggered.taskQueueTrigger.retryConfig = Object.assign(annotation.taskQueueTrigger.retryConfig, {
114
- maxRetryDurationSeconds: proto.secondsFromDuration(annotation.taskQueueTrigger.retryConfig.maxRetryDuration || "0"),
115
- });
115
+ triggered.taskQueueTrigger.retryConfig = {};
116
+ proto.copyIfPresent(triggered.taskQueueTrigger.retryConfig, annotation.taskQueueTrigger.retryConfig, "maxAttempts", "maxDoublings");
117
+ proto.convertIfPresent(triggered.taskQueueTrigger.retryConfig, annotation.taskQueueTrigger.retryConfig, "minBackoffSeconds", "minBackoff", toSeconds);
118
+ proto.convertIfPresent(triggered.taskQueueTrigger.retryConfig, annotation.taskQueueTrigger.retryConfig, "maxBackoffSeconds", "maxBackoff", toSeconds);
119
+ proto.convertIfPresent(triggered.taskQueueTrigger.retryConfig, annotation.taskQueueTrigger.retryConfig, "maxRetryDurationSeconds", "maxRetryDuration", toSeconds);
116
120
  }
117
121
  }
118
122
  else if (annotation.httpsTrigger) {
@@ -139,21 +143,16 @@ function addResourcesToBuild(projectId, runtime, annotation, want) {
139
143
  triggered = {
140
144
  scheduleTrigger: {
141
145
  schedule: annotation.schedule.schedule,
142
- timeZone: annotation.schedule.timeZone || "America/Los_Angeles",
146
+ timeZone: (_b = annotation.schedule.timeZone) !== null && _b !== void 0 ? _b : null,
143
147
  retryConfig: {},
144
148
  },
145
149
  };
146
150
  if (annotation.schedule.retryConfig) {
147
- if (annotation.schedule.retryConfig.maxBackoffDuration) {
148
- triggered.scheduleTrigger.retryConfig.maxBackoffSeconds = proto.secondsFromDuration(annotation.schedule.retryConfig.maxBackoffDuration);
149
- }
150
- if (annotation.schedule.retryConfig.minBackoffDuration) {
151
- triggered.scheduleTrigger.retryConfig.minBackoffSeconds = proto.secondsFromDuration(annotation.schedule.retryConfig.minBackoffDuration);
152
- }
153
- if (annotation.schedule.retryConfig.maxRetryDuration) {
154
- triggered.scheduleTrigger.retryConfig.maxRetrySeconds = proto.secondsFromDuration(annotation.schedule.retryConfig.maxRetryDuration);
155
- }
156
- proto.copyIfPresent(triggered.scheduleTrigger.retryConfig, annotation.schedule.retryConfig, "maxDoublings", "retryCount");
151
+ triggered.scheduleTrigger.retryConfig = {};
152
+ proto.copyIfPresent(triggered.scheduleTrigger.retryConfig, annotation.schedule.retryConfig, "retryCount", "maxDoublings");
153
+ proto.convertIfPresent(triggered.scheduleTrigger.retryConfig, annotation.schedule.retryConfig, "maxRetrySeconds", "maxRetryDuration", toSeconds);
154
+ proto.convertIfPresent(triggered.scheduleTrigger.retryConfig, annotation.schedule.retryConfig, "minBackoffSeconds", "minBackoffDuration", toSeconds);
155
+ proto.convertIfPresent(triggered.scheduleTrigger.retryConfig, annotation.schedule.retryConfig, "maxBackoffSeconds", "maxBackoffDuration", toSeconds);
157
156
  }
158
157
  }
159
158
  else if (annotation.blockingTrigger) {
@@ -169,7 +168,7 @@ function addResourcesToBuild(projectId, runtime, annotation, want) {
169
168
  },
170
169
  };
171
170
  }
172
- else {
171
+ else if (annotation.eventTrigger) {
173
172
  triggered = {
174
173
  eventTrigger: {
175
174
  eventType: annotation.eventTrigger.eventType,
@@ -178,8 +177,13 @@ function addResourcesToBuild(projectId, runtime, annotation, want) {
178
177
  },
179
178
  };
180
179
  }
180
+ else {
181
+ throw new error_1.FirebaseError("Do not understand Cloud Function annotation without a trigger" +
182
+ JSON.stringify(annotation, null, 2));
183
+ }
181
184
  const endpointId = annotation.name;
182
- const endpoint = Object.assign({ platform: annotation.platform || "gcfv1", region: regions, project: projectId, entryPoint: annotation.entryPoint, runtime: runtime, serviceAccount: annotation.serviceAccountEmail || null }, triggered);
185
+ const endpoint = Object.assign({ platform: annotation.platform || "gcfv1", region: regions, project: projectId, entryPoint: annotation.entryPoint, runtime: runtime }, triggered);
186
+ proto.renameIfPresent(endpoint, annotation, "serviceAccount", "serviceAccountEmail");
183
187
  if (annotation.vpcConnector != null) {
184
188
  let maybeId = annotation.vpcConnector;
185
189
  if (maybeId && !maybeId.includes("/")) {
@@ -188,8 +192,17 @@ function addResourcesToBuild(projectId, runtime, annotation, want) {
188
192
  endpoint.vpc = { connector: maybeId };
189
193
  proto.renameIfPresent(endpoint.vpc, annotation, "egressSettings", "vpcConnectorEgressSettings");
190
194
  }
191
- proto.copyIfPresent(endpoint, annotation, "concurrency", "labels", "ingressSettings", "maxInstances", "minInstances", "availableMemoryMb");
192
- proto.renameIfPresent(endpoint, annotation, "timeoutSeconds", "timeout", proto.secondsFromDuration);
195
+ proto.copyIfPresent(endpoint, annotation, "concurrency", "labels", "maxInstances", "minInstances", "availableMemoryMb");
196
+ proto.convertIfPresent(endpoint, annotation, "ingressSettings", (str) => {
197
+ if (str === null) {
198
+ return null;
199
+ }
200
+ if (!backend.AllIngressSettings.includes(str)) {
201
+ throw new Error(`Invalid ingress setting ${str}`);
202
+ }
203
+ return str;
204
+ });
205
+ proto.convertIfPresent(endpoint, annotation, "timeoutSeconds", "timeout", proto.secondsFromDuration);
193
206
  want.endpoints[endpointId] = endpoint;
194
207
  }
195
208
  exports.addResourcesToBuild = addResourcesToBuild;
@@ -285,8 +298,27 @@ function addResourcesToBackend(projectId, runtime, annotation, want) {
285
298
  }
286
299
  endpoint.secretEnvironmentVariables = secretEnvs;
287
300
  }
288
- proto.copyIfPresent(endpoint, annotation, "concurrency", "serviceAccountEmail", "labels", "ingressSettings", "maxInstances", "minInstances", "availableMemoryMb");
289
- proto.renameIfPresent(endpoint, annotation, "timeoutSeconds", "timeout", proto.secondsFromDuration);
301
+ proto.copyIfPresent(endpoint, annotation, "concurrency", "labels", "maxInstances", "minInstances");
302
+ proto.renameIfPresent(endpoint, annotation, "serviceAccount", "serviceAccountEmail");
303
+ proto.convertIfPresent(endpoint, annotation, "ingressSettings", (ingress) => {
304
+ if (ingress == null) {
305
+ return null;
306
+ }
307
+ if (!backend.AllIngressSettings.includes(ingress)) {
308
+ throw new error_1.FirebaseError(`Invalid ingress setting ${ingress}`);
309
+ }
310
+ return ingress;
311
+ });
312
+ proto.convertIfPresent(endpoint, annotation, "availableMemoryMb", (mem) => {
313
+ if (mem === null) {
314
+ return null;
315
+ }
316
+ if (!backend.isValidMemoryOption(mem)) {
317
+ throw new error_1.FirebaseError(`This version of firebase-tools does not know about the memory option ${mem}. Is an upgrade necessary?`);
318
+ }
319
+ return mem;
320
+ });
321
+ proto.convertIfPresent(endpoint, annotation, "timeoutSeconds", "timeout", proto.secondsFromDuration);
290
322
  want.endpoints[region] = want.endpoints[region] || {};
291
323
  want.endpoints[region][endpoint.id] = endpoint;
292
324
  mergeRequiredAPIs(want);
@@ -17,8 +17,14 @@ async function obtainStorageBindings(projectNumber) {
17
17
  }
18
18
  exports.obtainStorageBindings = obtainStorageBindings;
19
19
  async function ensureStorageTriggerRegion(endpoint) {
20
+ var _a;
20
21
  const { eventTrigger } = endpoint;
21
22
  if (!eventTrigger.region) {
23
+ logger_1.logger.debug("Looking up bucket region for the storage event trigger");
24
+ if (!((_a = eventTrigger.eventFilters) === null || _a === void 0 ? void 0 : _a.bucket)) {
25
+ throw new error_1.FirebaseError("Error: storage event trigger is missing bucket filter: " +
26
+ JSON.stringify(eventTrigger, null, 2));
27
+ }
22
28
  logger_1.logger.debug(`Looking up bucket region for the storage event trigger on bucket ${eventTrigger.eventFilters.bucket}`);
23
29
  try {
24
30
  const bucket = await storage.getBucket(eventTrigger.eventFilters.bucket);
@@ -50,11 +50,18 @@ async function convertConfig(context, payload, config, finalize) {
50
50
  (!functionsEndpointInfo.platform || endpoint.platform === functionsEndpointInfo.platform));
51
51
  });
52
52
  if (matchingBackends.length > 1) {
53
+ for (const endpoint of matchingBackends) {
54
+ if (endpoint.region === "us-central1") {
55
+ (0, utils_1.logLabeledBullet)(`hosting[${config.site}]`, `Function \`${functionsEndpointInfo.serviceId}\` found in multiple regions, defaulting to \`us-central1\`. ` +
56
+ `To rewrite to a different region, specify a \`region\` for the rewrite in \`firebase.json\`.`);
57
+ return endpoint;
58
+ }
59
+ }
53
60
  throw new error_1.FirebaseError(`More than one backend found for function name: ${functionsEndpointInfo.serviceId}. If the function is deployed in multiple regions, you must specify a region.`);
54
61
  }
55
62
  if (matchingBackends.length === 1) {
56
63
  const endpoint = matchingBackends[0];
57
- if (endpoint && (0, backend_1.isHttpsTriggered)(endpoint)) {
64
+ if (endpoint && ((0, backend_1.isHttpsTriggered)(endpoint) || (0, backend_1.isCallableTriggered)(endpoint))) {
58
65
  return endpoint;
59
66
  }
60
67
  }
@@ -8,20 +8,11 @@ const crypto_1 = require("crypto");
8
8
  const _ = require("lodash");
9
9
  const backend = require("../deploy/functions/backend");
10
10
  const constants_1 = require("./constants");
11
- const proto_1 = require("../gcp/proto");
12
11
  const manifest_1 = require("../extensions/manifest");
13
12
  const extensionsHelper_1 = require("../extensions/extensionsHelper");
14
13
  const postinstall_1 = require("./extensions/postinstall");
15
14
  const services_1 = require("../deploy/functions/services");
16
15
  const prepare_1 = require("../deploy/functions/prepare");
17
- const memoryLookup = {
18
- "128MB": 128,
19
- "256MB": 256,
20
- "512MB": 512,
21
- "1GB": 1024,
22
- "2GB": 2048,
23
- "4GB": 4096,
24
- };
25
16
  class HttpConstants {
26
17
  }
27
18
  exports.HttpConstants = HttpConstants;
@@ -33,7 +24,7 @@ class EmulatedTrigger {
33
24
  this.module = module;
34
25
  }
35
26
  get memoryLimitBytes() {
36
- return memoryLookup[this.definition.availableMemoryMb || "128MB"] * 1024 * 1024;
27
+ return (this.definition.availableMemoryMb || 128) * 1024 * 1024;
37
28
  }
38
29
  get timeoutMs() {
39
30
  return (this.definition.timeoutSeconds || 60) * 1000;
@@ -69,7 +60,11 @@ function emulatedFunctionsFromEndpoints(endpoints) {
69
60
  id: `${endpoint.region}-${endpoint.id}`,
70
61
  codebase: endpoint.codebase,
71
62
  };
72
- (0, proto_1.copyIfPresent)(def, endpoint, "availableMemoryMb", "labels", "timeoutSeconds", "platform", "secretEnvironmentVariables");
63
+ def.availableMemoryMb = endpoint.availableMemoryMb || 256;
64
+ def.labels = endpoint.labels || {};
65
+ def.timeoutSeconds = endpoint.timeoutSeconds || 60;
66
+ def.secretEnvironmentVariables = endpoint.secretEnvironmentVariables || [];
67
+ def.platform = endpoint.platform;
73
68
  if (backend.isHttpsTriggered(endpoint)) {
74
69
  def.httpsTrigger = endpoint.httpsTrigger;
75
70
  }
@@ -327,6 +327,10 @@ class StorageLayer {
327
327
  }
328
328
  const metadataDir = path.join(storageExportPath, "metadata");
329
329
  const blobsDir = path.join(storageExportPath, "blobs");
330
+ if (!(0, fs_1.existsSync)(metadataDir) || !(0, fs_1.existsSync)(blobsDir)) {
331
+ logger_1.logger.warn(`Could not find metadata directory at "${metadataDir}" and/or blobs directory at "${blobsDir}".`);
332
+ return;
333
+ }
330
334
  const metadataList = this.walkDirSync(metadataDir);
331
335
  const dotJson = ".json";
332
336
  for (const f of metadataList) {
@@ -287,15 +287,15 @@ class CloudStorageObjectMetadata {
287
287
  }
288
288
  exports.CloudStorageObjectMetadata = CloudStorageObjectMetadata;
289
289
  function toSerializedDate(d) {
290
- const day = `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, "0")}-${d
291
- .getDate()
290
+ const day = `${d.getUTCFullYear()}-${(d.getUTCMonth() + 1).toString().padStart(2, "0")}-${d
291
+ .getUTCDate()
292
292
  .toString()
293
293
  .padStart(2, "0")}`;
294
- const time = `${d.getHours().toString().padStart(2, "0")}:${d
295
- .getMinutes()
294
+ const time = `${d.getUTCHours().toString().padStart(2, "0")}:${d
295
+ .getUTCMinutes()
296
296
  .toString()
297
- .padStart(2, "0")}:${d.getSeconds().toString().padStart(2, "0")}.${d
298
- .getMilliseconds()
297
+ .padStart(2, "0")}:${d.getUTCSeconds().toString().padStart(2, "0")}.${d
298
+ .getUTCMilliseconds()
299
299
  .toString()
300
300
  .padStart(3, "0")}`;
301
301
  return `${day}T${time}Z`;