firebase-tools 10.7.1 → 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.
@@ -1,11 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveBackend = void 0;
3
+ exports.resolveBackend = exports.of = exports.empty = void 0;
4
4
  const backend = require("./backend");
5
5
  const proto = require("../../gcp/proto");
6
6
  const api = require("../../.../../api");
7
7
  const error_1 = require("../../error");
8
8
  const functional_1 = require("../../functional");
9
+ function empty() {
10
+ return {
11
+ requiredAPIs: [],
12
+ endpoints: {},
13
+ params: [],
14
+ };
15
+ }
16
+ exports.empty = empty;
17
+ function of(endpoints) {
18
+ const build = empty();
19
+ build.endpoints = endpoints;
20
+ return build;
21
+ }
22
+ exports.of = of;
9
23
  function resolveInt(from) {
10
24
  if (from == null) {
11
25
  return 0;
@@ -59,23 +73,20 @@ function resolveBackend(build) {
59
73
  else {
60
74
  timeout = 60;
61
75
  }
62
- const bkEndpoint = Object.assign({ id: endpointId, project: "", region: region, entryPoint: endpoint.entryPoint, platform: endpoint.platform, runtime: "", labels: endpoint.labels, environmentVariables: endpoint.environmentVariables, secretEnvironmentVariables: undefined, availableMemoryMb: endpoint.availableMemoryMb, timeoutSeconds: timeout }, trigger);
76
+ const bkEndpoint = Object.assign({ id: endpointId, project: endpoint.project, region: region, entryPoint: endpoint.entryPoint, platform: endpoint.platform, runtime: endpoint.runtime, timeoutSeconds: timeout }, trigger);
63
77
  proto.renameIfPresent(bkEndpoint, endpoint, "maxInstances", "maxInstances", resolveInt);
64
78
  proto.renameIfPresent(bkEndpoint, endpoint, "minInstances", "minInstances", resolveInt);
65
79
  proto.renameIfPresent(bkEndpoint, endpoint, "concurrency", "concurrency", resolveInt);
66
- proto.copyIfPresent(bkEndpoint, endpoint, "ingressSettings");
80
+ proto.copyIfPresent(bkEndpoint, endpoint, "ingressSettings", "availableMemoryMb", "environmentVariables", "labels");
67
81
  if (endpoint.vpc) {
68
82
  bkEndpoint.vpc = {
69
- connector: resolveString(endpoint.vpc.connector),
70
- egressSettings: endpoint.vpc.egressSettings,
83
+ connector: resolveString(endpoint.vpc.connector).replace("$REGION", region),
71
84
  };
85
+ proto.copyIfPresent(bkEndpoint.vpc, endpoint.vpc, "egressSettings");
72
86
  }
73
87
  if (endpoint.serviceAccount) {
74
88
  bkEndpoint.serviceAccountEmail = endpoint.serviceAccount;
75
89
  }
76
- else {
77
- bkEndpoint.serviceAccountEmail = "default";
78
- }
79
90
  bkEndpoints.push(bkEndpoint);
80
91
  }
81
92
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureServiceAgentRoles = exports.mergeBindings = exports.obtainEventarcServiceAgentBindings = exports.obtainDefaultComputeServiceAgentBindings = exports.obtainPubSubServiceAgentBindings = exports.obtainBinding = exports.checkHttpIam = exports.checkServiceAccountIam = exports.EVENTARC_SERVICE_AGENT_ROLE = exports.EVENTARC_EVENT_RECEIVER_ROLE = exports.RUN_INVOKER_ROLE = exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = void 0;
3
+ exports.ensureServiceAgentRoles = exports.mergeBindings = exports.obtainDefaultComputeServiceAgentBindings = exports.obtainPubSubServiceAgentBindings = exports.obtainBinding = exports.checkHttpIam = exports.checkServiceAccountIam = exports.EVENTARC_EVENT_RECEIVER_ROLE = exports.RUN_INVOKER_ROLE = exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = void 0;
4
4
  const cli_color_1 = require("cli-color");
5
5
  const logger_1 = require("../../logger");
6
6
  const functionsDeployHelper_1 = require("./functionsDeployHelper");
@@ -16,7 +16,6 @@ const PERMISSION = "cloudfunctions.functions.setIamPolicy";
16
16
  exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = "roles/iam.serviceAccountTokenCreator";
17
17
  exports.RUN_INVOKER_ROLE = "roles/run.invoker";
18
18
  exports.EVENTARC_EVENT_RECEIVER_ROLE = "roles/eventarc.eventReceiver";
19
- exports.EVENTARC_SERVICE_AGENT_ROLE = "roles/eventarc.serviceAgent";
20
19
  async function checkServiceAccountIam(projectId) {
21
20
  const saEmail = `${projectId}@appspot.gserviceaccount.com`;
22
21
  let passed = false;
@@ -101,11 +100,6 @@ function obtainDefaultComputeServiceAgentBindings(projectNumber, existingPolicy)
101
100
  return [invokerBinding, eventReceiverBinding];
102
101
  }
103
102
  exports.obtainDefaultComputeServiceAgentBindings = obtainDefaultComputeServiceAgentBindings;
104
- function obtainEventarcServiceAgentBindings(projectNumber, existingPolicy) {
105
- const eventarcServiceAgent = `serviceAccount:service-${projectNumber}@gcp-sa-eventarc.iam.gserviceaccount.com`;
106
- return [obtainBinding(existingPolicy, eventarcServiceAgent, exports.EVENTARC_SERVICE_AGENT_ROLE)];
107
- }
108
- exports.obtainEventarcServiceAgentBindings = obtainEventarcServiceAgentBindings;
109
103
  function mergeBindings(policy, allRequiredBindings) {
110
104
  for (const requiredBindings of allRequiredBindings) {
111
105
  if (requiredBindings.length === 0) {
@@ -149,7 +143,6 @@ async function ensureServiceAgentRoles(projectNumber, want, have) {
149
143
  if (haveServices.length === 0) {
150
144
  allRequiredBindings.push(obtainPubSubServiceAgentBindings(projectNumber, policy));
151
145
  allRequiredBindings.push(obtainDefaultComputeServiceAgentBindings(projectNumber, policy));
152
- allRequiredBindings.push(obtainEventarcServiceAgentBindings(projectNumber, policy));
153
146
  }
154
147
  if (!allRequiredBindings.find((bindings) => bindings.length > 0)) {
155
148
  return;
@@ -251,13 +251,14 @@ class DockerHelper {
251
251
  this.client = new docker.Client(origin);
252
252
  }
253
253
  async ls(path) {
254
- if (!this.cache[path]) {
255
- const raw = await retry(() => this.client.listTags(path));
256
- this.cache[path] = {
257
- tags: raw.tags,
258
- digests: Object.keys(raw.manifest),
259
- children: raw.child,
260
- };
254
+ if (!(path in this.cache)) {
255
+ this.cache[path] = retry(() => this.client.listTags(path)).then((res) => {
256
+ return {
257
+ tags: res.tags,
258
+ digests: Object.keys(res.manifest),
259
+ children: res.child,
260
+ };
261
+ });
261
262
  }
262
263
  return this.cache[path];
263
264
  }
@@ -13,7 +13,7 @@ async function handler(op) {
13
13
  ((_b = (_a = err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) ||
14
14
  ((_c = err.original) === null || _c === void 0 ? void 0 : _c.code) ||
15
15
  ((_f = (_e = (_d = err.original) === null || _d === void 0 ? void 0 : _d.context) === null || _e === void 0 ? void 0 : _e.response) === null || _f === void 0 ? void 0 : _f.statusCode);
16
- if (code === 429 || code === 409) {
16
+ if (code === 429 || code === 409 || code === 503) {
17
17
  throw err;
18
18
  }
19
19
  op.error = err;
@@ -105,6 +105,9 @@ class Delegate {
105
105
  return p;
106
106
  });
107
107
  }
108
+ async discoverBuild(configValues, envs) {
109
+ return { requiredAPIs: [], endpoints: {}, params: [] };
110
+ }
108
111
  async discoverSpec(configValues, envs) {
109
112
  let discovered = await discovery.detectFromYaml(this.sourceDir, this.projectId, this.runtime);
110
113
  if (!discovered) {
@@ -84,6 +84,10 @@ class Delegate {
84
84
  }
85
85
  async discoverSpec(config, env) {
86
86
  if (previews_1.previews.functionsv2) {
87
+ if (!semver.valid(this.sdkVersion)) {
88
+ logger_1.logger.debug(`Could not parse firebase-functions version '${this.sdkVersion}' into semver. Falling back to parseTriggers.`);
89
+ return parseTriggers.discoverBackend(this.projectId, this.sourceDir, this.runtime, config, env);
90
+ }
87
91
  if (semver.lt(this.sdkVersion, MIN_FUNCTIONS_SDK_VERSION)) {
88
92
  (0, utils_1.logLabeledWarning)("functions", `You are using an old version of firebase-functions SDK (${this.sdkVersion}). ` +
89
93
  `Please update firebase-functions SDK to >=${MIN_FUNCTIONS_SDK_VERSION}`);
@@ -106,5 +110,8 @@ class Delegate {
106
110
  }
107
111
  return parseTriggers.discoverBackend(this.projectId, this.sourceDir, this.runtime, config, env);
108
112
  }
113
+ async discoverBuild(config, env) {
114
+ return parseTriggers.discoverBuild(this.projectId, this.sourceDir, this.runtime, config, env);
115
+ }
109
116
  }
110
117
  exports.Delegate = Delegate;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.addResourcesToBackend = exports.mergeRequiredAPIs = exports.discoverBackend = exports.useStrategy = void 0;
3
+ exports.addResourcesToBackend = exports.addResourcesToBuild = exports.mergeRequiredAPIs = exports.discoverBackend = exports.discoverBuild = exports.useStrategy = void 0;
4
4
  const path = require("path");
5
5
  const _ = require("lodash");
6
6
  const child_process_1 = require("child_process");
@@ -49,6 +49,19 @@ function useStrategy(context) {
49
49
  return Promise.resolve(true);
50
50
  }
51
51
  exports.useStrategy = useStrategy;
52
+ async function discoverBuild(projectId, sourceDir, runtime, configValues, envs) {
53
+ const triggerAnnotations = await parseTriggers(projectId, sourceDir, configValues, envs);
54
+ const want = {
55
+ requiredAPIs: [],
56
+ endpoints: {},
57
+ params: [],
58
+ };
59
+ for (const annotation of triggerAnnotations) {
60
+ addResourcesToBuild(projectId, runtime, annotation, want);
61
+ }
62
+ return want;
63
+ }
64
+ exports.discoverBuild = discoverBuild;
52
65
  async function discoverBackend(projectId, sourceDir, runtime, configValues, envs) {
53
66
  const triggerAnnotations = await parseTriggers(projectId, sourceDir, configValues, envs);
54
67
  const want = Object.assign(Object.assign({}, backend.empty()), { environmentVariables: envs });
@@ -74,6 +87,100 @@ function mergeRequiredAPIs(backend) {
74
87
  backend.requiredAPIs = merged;
75
88
  }
76
89
  exports.mergeRequiredAPIs = mergeRequiredAPIs;
90
+ function addResourcesToBuild(projectId, runtime, annotation, want) {
91
+ var _a;
92
+ Object.freeze(annotation);
93
+ const regions = annotation.regions || [api.functionsDefaultRegion];
94
+ let triggered;
95
+ const triggerCount = +!!annotation.httpsTrigger +
96
+ +!!annotation.eventTrigger +
97
+ +!!annotation.taskQueueTrigger +
98
+ +!!annotation.blockingTrigger;
99
+ if (triggerCount !== 1) {
100
+ throw new error_1.FirebaseError("Unexpected annotation generated by the Firebase Functions SDK. This should never happen.");
101
+ }
102
+ if (annotation.taskQueueTrigger) {
103
+ want.requiredAPIs.push({
104
+ api: "cloudtasks.googleapis.com",
105
+ reason: "Needed for task queue functions.",
106
+ });
107
+ triggered = {
108
+ taskQueueTrigger: {},
109
+ };
110
+ proto.copyIfPresent(triggered.taskQueueTrigger, annotation.taskQueueTrigger, "invoker");
111
+ proto.copyIfPresent(triggered.taskQueueTrigger, annotation.taskQueueTrigger, "rateLimits");
112
+ if (annotation.taskQueueTrigger.retryConfig) {
113
+ triggered.taskQueueTrigger.retryConfig = Object.assign(annotation.taskQueueTrigger.retryConfig, {
114
+ maxRetryDurationSeconds: proto.secondsFromDuration(annotation.taskQueueTrigger.retryConfig.maxRetryDuration || "0"),
115
+ });
116
+ }
117
+ }
118
+ else if (annotation.httpsTrigger) {
119
+ if ((_a = annotation.labels) === null || _a === void 0 ? void 0 : _a["deployment-callable"]) {
120
+ delete annotation.labels["deployment-callable"];
121
+ triggered = { callableTrigger: {} };
122
+ }
123
+ else {
124
+ const trigger = {};
125
+ if (annotation.failurePolicy) {
126
+ logger_1.logger.warn(`Ignoring retry policy for HTTPS function ${annotation.name}`);
127
+ }
128
+ if (annotation.httpsTrigger.invoker) {
129
+ trigger.invoker = annotation.httpsTrigger.invoker[0];
130
+ }
131
+ triggered = { httpsTrigger: trigger };
132
+ }
133
+ }
134
+ else if (annotation.schedule) {
135
+ want.requiredAPIs.push({
136
+ api: "cloudscheduler.googleapis.com",
137
+ reason: "Needed for scheduled functions.",
138
+ });
139
+ triggered = {
140
+ scheduleTrigger: {
141
+ schedule: annotation.schedule.schedule,
142
+ timeZone: annotation.schedule.timeZone || "what's the default timezone?",
143
+ retryConfig: annotation.schedule.retryConfig || {},
144
+ },
145
+ };
146
+ }
147
+ else if (annotation.blockingTrigger) {
148
+ if (events.v1.AUTH_BLOCKING_EVENTS.includes(annotation.blockingTrigger.eventType)) {
149
+ want.requiredAPIs.push({
150
+ api: "identitytoolkit.googleapis.com",
151
+ reason: "Needed for auth blocking functions.",
152
+ });
153
+ }
154
+ triggered = {
155
+ blockingTrigger: {
156
+ eventType: annotation.blockingTrigger.eventType,
157
+ },
158
+ };
159
+ }
160
+ else {
161
+ triggered = {
162
+ eventTrigger: {
163
+ eventType: annotation.eventTrigger.eventType,
164
+ eventFilters: { resource: annotation.eventTrigger.resource },
165
+ retry: !!annotation.failurePolicy,
166
+ },
167
+ };
168
+ }
169
+ const endpointId = annotation.name;
170
+ const endpoint = Object.assign({ platform: annotation.platform || "gcfv1", region: regions, project: projectId, entryPoint: annotation.entryPoint, runtime: runtime, serviceAccount: annotation.serviceAccountEmail || "default" }, triggered);
171
+ if (annotation.vpcConnector != null) {
172
+ let maybeId = annotation.vpcConnector;
173
+ if (maybeId && !maybeId.includes("/")) {
174
+ maybeId = `projects/${projectId}/locations/$REGION/connectors/${maybeId}`;
175
+ }
176
+ endpoint.vpc = { connector: maybeId };
177
+ proto.renameIfPresent(endpoint.vpc, annotation, "egressSettings", "vpcConnectorEgressSettings");
178
+ }
179
+ proto.copyIfPresent(endpoint, annotation, "concurrency", "labels", "ingressSettings", "maxInstances", "minInstances", "availableMemoryMb");
180
+ proto.renameIfPresent(endpoint, annotation, "timeoutSeconds", "timeout", proto.secondsFromDuration);
181
+ want.endpoints[endpointId] = endpoint;
182
+ }
183
+ exports.addResourcesToBuild = addResourcesToBuild;
77
184
  function addResourcesToBackend(projectId, runtime, annotation, want) {
78
185
  var _a;
79
186
  Object.freeze(annotation);
@@ -86,7 +86,7 @@ function functionIdsAreValid(functions) {
86
86
  return fn.platform === "gcfv2" && !v2FunctionName.test(fn.id);
87
87
  });
88
88
  if (invalidV2Ids.length !== 0) {
89
- const msg = `${invalidV2Ids.map((f) => f.id).join(", ")} v2 function name(s) can only contin lower ` +
89
+ const msg = `${invalidV2Ids.map((f) => f.id).join(", ")} v2 function name(s) can only contain lower ` +
90
90
  `case letters, numbers, hyphens, and not exceed 62 characters in length`;
91
91
  throw new error_1.FirebaseError(msg);
92
92
  }
@@ -62,8 +62,12 @@ class AuthCloudFunction {
62
62
  phoneNumber: user.phoneNumber,
63
63
  disabled: user.disabled,
64
64
  metadata: {
65
- creationTime: user.createdAt,
66
- lastSignInTime: user.lastLoginAt,
65
+ creationTime: user.createdAt
66
+ ? new Date(parseInt(user.createdAt, 10)).toISOString()
67
+ : undefined,
68
+ lastSignInTime: user.lastLoginAt
69
+ ? new Date(parseInt(user.lastLoginAt, 10)).toISOString()
70
+ : undefined,
67
71
  },
68
72
  customClaims: JSON.parse(user.customAttributes || "{}"),
69
73
  providerData: user.providerUserInfo,
@@ -1210,7 +1210,6 @@ function grantToken(state, reqBody) {
1210
1210
  (0, errors_1.assert)(reqBody.grantType === "refresh_token", "INVALID_GRANT_TYPE");
1211
1211
  (0, errors_1.assert)(reqBody.refreshToken, "MISSING_REFRESH_TOKEN");
1212
1212
  const refreshTokenRecord = state.validateRefreshToken(reqBody.refreshToken);
1213
- (0, errors_1.assert)(refreshTokenRecord, "INVALID_REFRESH_TOKEN");
1214
1213
  (0, errors_1.assert)(!refreshTokenRecord.user.disabled, "USER_DISABLED");
1215
1214
  const tokens = issueTokens(state, refreshTokenRecord.user, refreshTokenRecord.provider, {
1216
1215
  extraClaims: refreshTokenRecord.extraClaims,
@@ -342,7 +342,7 @@ function toExegesisController(ops, getProjectStateById) {
342
342
  }
343
343
  function toExegesisOperation(operation) {
344
344
  return (ctx) => {
345
- var _a, _b, _c, _d, _e, _f, _g;
345
+ var _a, _b, _c, _d, _e, _f, _g, _h;
346
346
  let targetProjectId = ctx.params.path.targetProjectId || ((_a = ctx.requestBody) === null || _a === void 0 ? void 0 : _a.targetProjectId);
347
347
  if (targetProjectId) {
348
348
  if ((_b = ctx.api.operationObject.security) === null || _b === void 0 ? void 0 : _b.some((sec) => sec.Oauth2)) {
@@ -365,6 +365,13 @@ function toExegesisController(ops, getProjectStateById) {
365
365
  }
366
366
  targetTenantId = targetTenantId || (decoded === null || decoded === void 0 ? void 0 : decoded.payload.firebase.tenant);
367
367
  }
368
+ if ((_h = ctx.requestBody) === null || _h === void 0 ? void 0 : _h.refreshToken) {
369
+ const refreshTokenRecord = (0, state_1.decodeRefreshToken)(ctx.requestBody.refreshToken);
370
+ if (refreshTokenRecord.tenantId && targetTenantId) {
371
+ (0, errors_2.assert)(refreshTokenRecord.tenantId === targetTenantId, "TENANT_ID_MISMATCH: ((Refresh token tenant ID does not match target tenant ID.))");
372
+ }
373
+ targetTenantId = targetTenantId || refreshTokenRecord.tenantId;
374
+ }
368
375
  return operation(getProjectStateById(targetProjectId, targetTenantId), ctx.requestBody, ctx);
369
376
  };
370
377
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BlockingFunctionEvents = exports.UsageMode = exports.TenantProjectState = exports.AgentProjectState = exports.ProjectState = exports.SIGNIN_METHOD_EMAIL_LINK = exports.PROVIDER_GAME_CENTER = exports.PROVIDER_CUSTOM = exports.PROVIDER_ANONYMOUS = exports.PROVIDER_PHONE = exports.PROVIDER_PASSWORD = void 0;
3
+ exports.decodeRefreshToken = exports.encodeRefreshToken = exports.BlockingFunctionEvents = exports.UsageMode = exports.TenantProjectState = exports.AgentProjectState = exports.ProjectState = exports.SIGNIN_METHOD_EMAIL_LINK = exports.PROVIDER_GAME_CENTER = exports.PROVIDER_CUSTOM = exports.PROVIDER_ANONYMOUS = exports.PROVIDER_PHONE = exports.PROVIDER_PASSWORD = void 0;
4
4
  const utils_1 = require("./utils");
5
5
  const cloudFunctions_1 = require("./cloudFunctions");
6
6
  const errors_1 = require("./errors");
@@ -19,8 +19,6 @@ class ProjectState {
19
19
  this.localIdForPhoneNumber = new Map();
20
20
  this.localIdsForProviderEmail = new Map();
21
21
  this.userIdForProviderRawId = new Map();
22
- this.refreshTokens = new Map();
23
- this.refreshTokensForLocalId = new Map();
24
22
  this.oobs = new Map();
25
23
  this.verificationCodes = new Map();
26
24
  this.temporaryProofs = new Map();
@@ -73,13 +71,6 @@ class ProjectState {
73
71
  deleteUser(user) {
74
72
  this.users.delete(user.localId);
75
73
  this.removeUserFromIndex(user);
76
- const refreshTokens = this.refreshTokensForLocalId.get(user.localId);
77
- if (refreshTokens) {
78
- this.refreshTokensForLocalId.delete(user.localId);
79
- for (const refreshToken of refreshTokens) {
80
- this.refreshTokens.delete(refreshToken);
81
- }
82
- }
83
74
  this.authCloudFunction.dispatch("delete", user);
84
75
  }
85
76
  updateUserByLocalId(localId, fields, options = {}) {
@@ -282,26 +273,23 @@ class ProjectState {
282
273
  }
283
274
  createRefreshTokenFor(userInfo, provider, { extraClaims = {}, secondFactor, } = {}) {
284
275
  const localId = userInfo.localId;
285
- const refreshToken = (0, utils_1.randomBase64UrlStr)(204);
286
- this.refreshTokens.set(refreshToken, {
276
+ const refreshTokenRecord = {
277
+ _AuthEmulatorRefreshToken: "DO NOT MODIFY",
287
278
  localId,
288
279
  provider,
289
280
  extraClaims,
281
+ projectId: this.projectId,
290
282
  secondFactor,
291
283
  tenantId: userInfo.tenantId,
292
- });
293
- let refreshTokens = this.refreshTokensForLocalId.get(localId);
294
- if (!refreshTokens) {
295
- refreshTokens = new Set();
296
- this.refreshTokensForLocalId.set(localId, refreshTokens);
297
- }
298
- refreshTokens.add(refreshToken);
284
+ };
285
+ const refreshToken = encodeRefreshToken(refreshTokenRecord);
299
286
  return refreshToken;
300
287
  }
301
288
  validateRefreshToken(refreshToken) {
302
- const record = this.refreshTokens.get(refreshToken);
303
- if (!record) {
304
- return undefined;
289
+ const record = decodeRefreshToken(refreshToken);
290
+ (0, errors_1.assert)(record.projectId === this.projectId, "INVALID_REFRESH_TOKEN");
291
+ if (this instanceof TenantProjectState) {
292
+ (0, errors_1.assert)(record.tenantId === this.tenantId, "TENANT_ID_MISMATCH");
305
293
  }
306
294
  return {
307
295
  user: this.getUserByLocalIdAssertingExists(record.localId),
@@ -356,8 +344,6 @@ class ProjectState {
356
344
  this.localIdForPhoneNumber.clear();
357
345
  this.localIdsForProviderEmail.clear();
358
346
  this.userIdForProviderRawId.clear();
359
- this.refreshTokens.clear();
360
- this.refreshTokensForLocalId.clear();
361
347
  }
362
348
  getUserCount() {
363
349
  return this.users.size;
@@ -617,6 +603,23 @@ var BlockingFunctionEvents;
617
603
  BlockingFunctionEvents["BEFORE_CREATE"] = "beforeCreate";
618
604
  BlockingFunctionEvents["BEFORE_SIGN_IN"] = "beforeSignIn";
619
605
  })(BlockingFunctionEvents = exports.BlockingFunctionEvents || (exports.BlockingFunctionEvents = {}));
606
+ function encodeRefreshToken(refreshTokenRecord) {
607
+ return Buffer.from(JSON.stringify(refreshTokenRecord), "utf8").toString("base64");
608
+ }
609
+ exports.encodeRefreshToken = encodeRefreshToken;
610
+ function decodeRefreshToken(refreshTokenString) {
611
+ let refreshTokenRecord;
612
+ try {
613
+ const json = Buffer.from(refreshTokenString, "base64").toString("utf8");
614
+ refreshTokenRecord = JSON.parse(json);
615
+ }
616
+ catch (_a) {
617
+ throw new errors_1.BadRequestError("INVALID_REFRESH_TOKEN");
618
+ }
619
+ (0, errors_1.assert)(refreshTokenRecord._AuthEmulatorRefreshToken, "INVALID_REFRESH_TOKEN");
620
+ return refreshTokenRecord;
621
+ }
622
+ exports.decodeRefreshToken = decodeRefreshToken;
620
623
  function getProviderEmailsForUser(user) {
621
624
  var _a;
622
625
  const emails = new Set();
@@ -56,7 +56,15 @@ class DatabaseEmulator {
56
56
  if (!c.instance) {
57
57
  continue;
58
58
  }
59
- await this.updateRules(c.instance, c.rules);
59
+ try {
60
+ await this.updateRules(c.instance, c.rules);
61
+ }
62
+ catch (e) {
63
+ const rulesError = this.prettyPrintRulesError(c.rules, e);
64
+ this.logger.logLabeled("WARN", "database", rulesError);
65
+ this.logger.logLabeled("WARN", "database", "Failed to update rules");
66
+ throw new error_1.FirebaseError(`Failed to load initial ${constants_1.Constants.description(this.getName())} rules:\n${rulesError}`);
67
+ }
60
68
  }
61
69
  }
62
70
  }
@@ -114,6 +122,7 @@ class DatabaseEmulator {
114
122
  });
115
123
  }
116
124
  async updateRules(instance, rulesPath) {
125
+ var _a;
117
126
  const rulesExt = path.extname(rulesPath);
118
127
  const content = rulesExt === ".bolt"
119
128
  ? parseBoltRules(rulesPath).toString()
@@ -131,12 +140,36 @@ class DatabaseEmulator {
131
140
  if (e.context && e.context.body) {
132
141
  throw e.context.body.error;
133
142
  }
134
- throw e.original;
143
+ throw (_a = e.original) !== null && _a !== void 0 ? _a : e;
135
144
  }
136
145
  }
137
146
  prettyPrintRulesError(filePath, error) {
147
+ let errStr;
148
+ switch (typeof error) {
149
+ case "string":
150
+ errStr = error;
151
+ break;
152
+ case "object":
153
+ if (error != null && "message" in error) {
154
+ const message = error.message;
155
+ errStr = `${message}`;
156
+ if (typeof message === "string") {
157
+ try {
158
+ const parsed = JSON.parse(message);
159
+ if (typeof parsed === "object" && parsed.error) {
160
+ errStr = `${parsed.error}`;
161
+ }
162
+ }
163
+ catch (_) {
164
+ }
165
+ }
166
+ break;
167
+ }
168
+ default:
169
+ errStr = `Unknown error: ${JSON.stringify(error)}`;
170
+ }
138
171
  const relativePath = path.relative(process.cwd(), filePath);
139
- return `${clc.cyan(relativePath)}:${error.trim()}`;
172
+ return `${clc.cyan(relativePath)}:${errStr.trim()}`;
140
173
  }
141
174
  }
142
175
  exports.DatabaseEmulator = DatabaseEmulator;
@@ -83,15 +83,15 @@ exports.DownloadDetails = {
83
83
  },
84
84
  }
85
85
  : {
86
- version: "1.6.5",
87
- downloadPath: path.join(CACHE_DIR, "ui-v1.6.5.zip"),
88
- unzipDir: path.join(CACHE_DIR, "ui-v1.6.5"),
89
- binaryPath: path.join(CACHE_DIR, "ui-v1.6.5", "server.bundle.js"),
86
+ version: "1.6.6",
87
+ downloadPath: path.join(CACHE_DIR, "ui-v1.6.6.zip"),
88
+ unzipDir: path.join(CACHE_DIR, "ui-v1.6.6"),
89
+ binaryPath: path.join(CACHE_DIR, "ui-v1.6.6", "server.bundle.js"),
90
90
  opts: {
91
91
  cacheDir: CACHE_DIR,
92
- remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.6.5.zip",
93
- expectedSize: 3816994,
94
- expectedChecksum: "92dfff4b2ef8ab616e8a60cc93e0a00b",
92
+ remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.6.6.zip",
93
+ expectedSize: 3817247,
94
+ expectedChecksum: "c80a3f0ae1e3f682ace0a18a9cdd2861",
95
95
  namePrefix: "ui",
96
96
  },
97
97
  },
@@ -223,7 +223,6 @@ class FunctionsEmulator {
223
223
  return Promise.resolve();
224
224
  }
225
225
  async connect() {
226
- const loadTriggerPromises = [];
227
226
  for (const backend of this.args.emulatableBackends) {
228
227
  this.logger.logLabeled("BULLET", "functions", `Watching "${backend.functionsDir}" for Cloud Functions...`);
229
228
  const watcher = chokidar.watch(backend.functionsDir, {
@@ -239,9 +238,8 @@ class FunctionsEmulator {
239
238
  this.logger.log("DEBUG", `File ${filePath} changed, reloading triggers`);
240
239
  return debouncedLoadTriggers();
241
240
  });
242
- loadTriggerPromises.push(this.loadTriggers(backend, true));
241
+ await this.loadTriggers(backend, true);
243
242
  }
244
- await Promise.all(loadTriggerPromises);
245
243
  await this.performPostLoadOperations();
246
244
  return;
247
245
  }
@@ -853,11 +851,9 @@ class FunctionsEmulator {
853
851
  }
854
852
  async reloadTriggers() {
855
853
  this.triggerGeneration++;
856
- const loadTriggerPromises = [];
857
854
  for (const backend of this.args.emulatableBackends) {
858
- loadTriggerPromises.push(this.loadTriggers(backend));
855
+ await this.loadTriggers(backend);
859
856
  }
860
- await Promise.all(loadTriggerPromises);
861
857
  await this.performPostLoadOperations();
862
858
  return;
863
859
  }
@@ -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] && !href.startsWith("http://localhost")) {
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", "", {
@@ -106,6 +106,9 @@ function emulatedFunctionsFromEndpoints(endpoints) {
106
106
  options: endpoint.blockingTrigger.options || {},
107
107
  };
108
108
  }
109
+ else if (backend.isTaskQueueTriggered(endpoint)) {
110
+ def.httpsTrigger = {};
111
+ }
109
112
  else {
110
113
  }
111
114
  regionDefinitions.push(def);
@@ -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;
@@ -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, _e;
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: (_e = (_d = listResponse.items) === null || _d === void 0 ? void 0 : _d.map((item) => {
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
- })) !== null && _e !== void 0 ? _e : [],
152
+ }),
151
153
  });
152
154
  });
153
155
  const handleUpload = async (req, res) => {
@@ -467,3 +469,23 @@ function setObjectHeaders(res, metadata, headerOverride = { "Content-Encoding":
467
469
  res.setHeader("Content-Language", metadata.contentLanguage);
468
470
  }
469
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
+ }