firebase-functions 6.5.0 → 7.0.0-rc.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.
Files changed (157) hide show
  1. package/lib/_virtual/rolldown_runtime.js +34 -0
  2. package/lib/bin/firebase-functions.js +78 -103
  3. package/lib/common/app.js +35 -55
  4. package/lib/common/change.js +54 -75
  5. package/lib/common/config.js +41 -41
  6. package/lib/common/debug.js +23 -47
  7. package/lib/common/encoding.js +59 -82
  8. package/lib/common/onInit.js +26 -28
  9. package/lib/common/options.js +22 -42
  10. package/lib/common/params.d.ts +6 -6
  11. package/lib/common/params.js +0 -23
  12. package/lib/common/providers/database.js +270 -300
  13. package/lib/common/providers/firestore.js +66 -92
  14. package/lib/common/providers/https.d.ts +0 -1
  15. package/lib/common/providers/https.js +537 -539
  16. package/lib/common/providers/identity.js +393 -444
  17. package/lib/common/providers/tasks.js +64 -98
  18. package/lib/common/timezone.js +544 -542
  19. package/lib/common/trace.d.ts +0 -1
  20. package/lib/common/trace.js +63 -55
  21. package/lib/common/utilities/assertions.d.ts +11 -0
  22. package/lib/common/utilities/assertions.js +18 -0
  23. package/lib/common/utilities/encoder.js +20 -37
  24. package/lib/common/utilities/path-pattern.js +106 -132
  25. package/lib/common/utilities/path.js +28 -27
  26. package/lib/common/utilities/utils.js +23 -45
  27. package/lib/esm/_virtual/rolldown_runtime.mjs +16 -0
  28. package/lib/esm/bin/firebase-functions.mjs +91 -0
  29. package/lib/esm/common/app.mjs +39 -0
  30. package/lib/esm/common/change.mjs +57 -0
  31. package/lib/esm/common/config.mjs +45 -0
  32. package/lib/esm/common/debug.mjs +28 -0
  33. package/lib/esm/common/encoding.mjs +69 -0
  34. package/lib/esm/common/onInit.mjs +33 -0
  35. package/lib/esm/common/options.mjs +22 -0
  36. package/lib/esm/common/params.mjs +1 -0
  37. package/lib/esm/common/providers/database.mjs +269 -0
  38. package/lib/esm/common/providers/firestore.mjs +78 -0
  39. package/lib/esm/common/providers/https.mjs +573 -0
  40. package/lib/esm/common/providers/identity.mjs +428 -0
  41. package/lib/esm/common/providers/tasks.mjs +67 -0
  42. package/lib/esm/common/timezone.mjs +544 -0
  43. package/lib/esm/common/trace.mjs +73 -0
  44. package/lib/esm/common/utilities/assertions.mjs +17 -0
  45. package/lib/esm/common/utilities/encoder.mjs +21 -0
  46. package/lib/esm/common/utilities/path-pattern.mjs +116 -0
  47. package/lib/esm/common/utilities/path.mjs +35 -0
  48. package/lib/esm/common/utilities/utils.mjs +29 -0
  49. package/lib/esm/function-configuration.mjs +1 -0
  50. package/lib/esm/logger/common.mjs +23 -0
  51. package/lib/esm/logger/compat.mjs +25 -0
  52. package/lib/esm/logger/index.mjs +131 -0
  53. package/lib/esm/params/index.mjs +160 -0
  54. package/lib/esm/params/types.mjs +400 -0
  55. package/lib/esm/runtime/loader.mjs +132 -0
  56. package/lib/esm/runtime/manifest.mjs +134 -0
  57. package/lib/esm/types/global.d.mjs +1 -0
  58. package/lib/esm/v1/cloud-functions.mjs +206 -0
  59. package/lib/esm/v1/config.mjs +14 -0
  60. package/lib/esm/v1/function-builder.mjs +252 -0
  61. package/lib/esm/v1/function-configuration.mjs +72 -0
  62. package/lib/esm/v1/index.mjs +27 -0
  63. package/lib/esm/v1/providers/analytics.mjs +212 -0
  64. package/lib/esm/v1/providers/auth.mjs +156 -0
  65. package/lib/esm/v1/providers/database.mjs +243 -0
  66. package/lib/esm/v1/providers/firestore.mjs +131 -0
  67. package/lib/esm/v1/providers/https.mjs +82 -0
  68. package/lib/esm/v1/providers/pubsub.mjs +175 -0
  69. package/lib/esm/v1/providers/remoteConfig.mjs +64 -0
  70. package/lib/esm/v1/providers/storage.mjs +163 -0
  71. package/lib/esm/v1/providers/tasks.mjs +63 -0
  72. package/lib/esm/v1/providers/testLab.mjs +94 -0
  73. package/lib/esm/v2/core.mjs +4 -0
  74. package/lib/esm/v2/index.mjs +28 -0
  75. package/lib/esm/v2/options.mjs +102 -0
  76. package/lib/esm/v2/providers/alerts/alerts.mjs +85 -0
  77. package/lib/esm/v2/providers/alerts/appDistribution.mjs +75 -0
  78. package/lib/esm/v2/providers/alerts/billing.mjs +51 -0
  79. package/lib/esm/v2/providers/alerts/crashlytics.mjs +122 -0
  80. package/lib/esm/v2/providers/alerts/index.mjs +22 -0
  81. package/lib/esm/v2/providers/alerts/performance.mjs +66 -0
  82. package/lib/esm/v2/providers/database.mjs +197 -0
  83. package/lib/esm/v2/providers/dataconnect.mjs +130 -0
  84. package/lib/esm/v2/providers/eventarc.mjs +51 -0
  85. package/lib/esm/v2/providers/firestore.mjs +294 -0
  86. package/lib/esm/v2/providers/https.mjs +210 -0
  87. package/lib/esm/v2/providers/identity.mjs +103 -0
  88. package/lib/esm/v2/providers/pubsub.mjs +148 -0
  89. package/lib/esm/v2/providers/remoteConfig.mjs +52 -0
  90. package/lib/esm/v2/providers/scheduler.mjs +84 -0
  91. package/lib/esm/v2/providers/storage.mjs +155 -0
  92. package/lib/esm/v2/providers/tasks.mjs +65 -0
  93. package/lib/esm/v2/providers/testLab.mjs +53 -0
  94. package/lib/esm/v2/trace.mjs +20 -0
  95. package/lib/function-configuration.d.ts +0 -0
  96. package/lib/function-configuration.js +0 -0
  97. package/lib/logger/common.js +21 -41
  98. package/lib/logger/compat.js +18 -33
  99. package/lib/logger/index.js +119 -130
  100. package/lib/params/index.d.ts +19 -4
  101. package/lib/params/index.js +153 -129
  102. package/lib/params/types.d.ts +17 -0
  103. package/lib/params/types.js +390 -382
  104. package/lib/runtime/loader.js +114 -148
  105. package/lib/runtime/manifest.js +106 -126
  106. package/lib/types/global.d.js +0 -0
  107. package/lib/v1/cloud-functions.d.ts +2 -2
  108. package/lib/v1/cloud-functions.js +193 -241
  109. package/lib/v1/config.d.ts +4 -7
  110. package/lib/v1/config.js +13 -75
  111. package/lib/v1/function-builder.js +239 -368
  112. package/lib/v1/function-configuration.js +70 -63
  113. package/lib/v1/index.js +118 -73
  114. package/lib/v1/providers/analytics.js +189 -210
  115. package/lib/v1/providers/auth.d.ts +2 -1
  116. package/lib/v1/providers/auth.js +159 -164
  117. package/lib/v1/providers/database.js +237 -242
  118. package/lib/v1/providers/firestore.js +131 -130
  119. package/lib/v1/providers/https.d.ts +2 -1
  120. package/lib/v1/providers/https.js +79 -86
  121. package/lib/v1/providers/pubsub.js +175 -172
  122. package/lib/v1/providers/remoteConfig.js +64 -68
  123. package/lib/v1/providers/storage.js +161 -163
  124. package/lib/v1/providers/tasks.d.ts +1 -1
  125. package/lib/v1/providers/tasks.js +65 -80
  126. package/lib/v1/providers/testLab.js +94 -94
  127. package/lib/v2/core.d.ts +1 -1
  128. package/lib/v2/core.js +5 -32
  129. package/lib/v2/index.d.ts +6 -3
  130. package/lib/v2/index.js +123 -75
  131. package/lib/v2/options.js +88 -114
  132. package/lib/v2/providers/alerts/alerts.js +76 -95
  133. package/lib/v2/providers/alerts/appDistribution.js +73 -78
  134. package/lib/v2/providers/alerts/billing.js +49 -53
  135. package/lib/v2/providers/alerts/crashlytics.js +110 -102
  136. package/lib/v2/providers/alerts/index.js +56 -53
  137. package/lib/v2/providers/alerts/performance.js +64 -74
  138. package/lib/v2/providers/database.js +177 -180
  139. package/lib/v2/providers/dataconnect.d.ts +95 -0
  140. package/lib/v2/providers/dataconnect.js +137 -0
  141. package/lib/v2/providers/eventarc.js +55 -77
  142. package/lib/v2/providers/firestore.js +262 -260
  143. package/lib/v2/providers/https.d.ts +3 -2
  144. package/lib/v2/providers/https.js +210 -247
  145. package/lib/v2/providers/identity.d.ts +2 -1
  146. package/lib/v2/providers/identity.js +96 -105
  147. package/lib/v2/providers/pubsub.js +149 -167
  148. package/lib/v2/providers/remoteConfig.js +54 -63
  149. package/lib/v2/providers/scheduler.js +84 -96
  150. package/lib/v2/providers/storage.js +147 -162
  151. package/lib/v2/providers/tasks.d.ts +1 -1
  152. package/lib/v2/providers/tasks.js +68 -95
  153. package/lib/v2/providers/testLab.js +55 -64
  154. package/lib/v2/trace.js +18 -19
  155. package/package.json +290 -226
  156. package/protos/compiledFirestore.mjs +3512 -0
  157. package/protos/update.sh +28 -7
@@ -1,591 +1,589 @@
1
- "use strict";
2
- // The MIT License (MIT)
3
- //
4
- // Copyright (c) 2021 Firebase
5
- //
6
- // Permission is hereby granted, free of charge, to any person obtaining a copy
7
- // of this software and associated documentation files (the "Software"), to deal
8
- // in the Software without restriction, including without limitation the rights
9
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- // copies of the Software, and to permit persons to whom the Software is
11
- // furnished to do so, subject to the following conditions:
12
- //
13
- // The above copyright notice and this permission notice shall be included in all
14
- // copies or substantial portions of the Software.
15
- //
16
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
- // SOFTWARE.
23
- Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.onCallHandler = exports.checkAuthToken = exports.unsafeDecodeAppCheckToken = exports.unsafeDecodeIdToken = exports.unsafeDecodeToken = exports.decode = exports.encode = exports.isValidRequest = exports.HttpsError = exports.DEFAULT_HEARTBEAT_SECONDS = exports.ORIGINAL_AUTH_HEADER = exports.CALLABLE_AUTH_HEADER = void 0;
25
- const cors = require("cors");
26
- const logger = require("../../logger");
27
- // TODO(inlined): Decide whether we want to un-version apps or whether we want a
28
- // different strategy
29
- const app_check_1 = require("firebase-admin/app-check");
30
- const auth_1 = require("firebase-admin/auth");
31
- const app_1 = require("../app");
32
- const debug_1 = require("../debug");
1
+ const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.js');
2
+ const require_logger_index = require('../../logger/index.js');
3
+ const require_common_app = require('../app.js');
4
+ const require_common_debug = require('../debug.js');
5
+ let firebase_admin_auth = require("firebase-admin/auth");
6
+ firebase_admin_auth = require_rolldown_runtime.__toESM(firebase_admin_auth);
7
+ let cors = require("cors");
8
+ cors = require_rolldown_runtime.__toESM(cors);
9
+ let firebase_admin_app_check = require("firebase-admin/app-check");
10
+ firebase_admin_app_check = require_rolldown_runtime.__toESM(firebase_admin_app_check);
11
+
12
+ //#region src/common/providers/https.ts
33
13
  const JWT_REGEX = /^[a-zA-Z0-9\-_=]+?\.[a-zA-Z0-9\-_=]+?\.([a-zA-Z0-9\-_=]+)?$/;
34
14
  /** @internal */
35
- exports.CALLABLE_AUTH_HEADER = "x-callable-context-auth";
15
+ const CALLABLE_AUTH_HEADER = "x-callable-context-auth";
36
16
  /** @internal */
37
- exports.ORIGINAL_AUTH_HEADER = "x-original-auth";
17
+ const ORIGINAL_AUTH_HEADER = "x-original-auth";
38
18
  /** @internal */
39
- exports.DEFAULT_HEARTBEAT_SECONDS = 30;
19
+ const DEFAULT_HEARTBEAT_SECONDS = 30;
40
20
  /**
41
- * Standard error codes and HTTP statuses for different ways a request can fail,
42
- * as defined by:
43
- * https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
44
- *
45
- * This map is used primarily to convert from a client error code string to
46
- * to the HTTP format error code string and status, and make sure it's in the
47
- * supported set.
48
- */
21
+ * Standard error codes and HTTP statuses for different ways a request can fail,
22
+ * as defined by:
23
+ * https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
24
+ *
25
+ * This map is used primarily to convert from a client error code string to
26
+ * to the HTTP format error code string and status, and make sure it's in the
27
+ * supported set.
28
+ */
49
29
  const errorCodeMap = {
50
- ok: { canonicalName: "OK", status: 200 },
51
- cancelled: { canonicalName: "CANCELLED", status: 499 },
52
- unknown: { canonicalName: "UNKNOWN", status: 500 },
53
- "invalid-argument": { canonicalName: "INVALID_ARGUMENT", status: 400 },
54
- "deadline-exceeded": { canonicalName: "DEADLINE_EXCEEDED", status: 504 },
55
- "not-found": { canonicalName: "NOT_FOUND", status: 404 },
56
- "already-exists": { canonicalName: "ALREADY_EXISTS", status: 409 },
57
- "permission-denied": { canonicalName: "PERMISSION_DENIED", status: 403 },
58
- unauthenticated: { canonicalName: "UNAUTHENTICATED", status: 401 },
59
- "resource-exhausted": { canonicalName: "RESOURCE_EXHAUSTED", status: 429 },
60
- "failed-precondition": { canonicalName: "FAILED_PRECONDITION", status: 400 },
61
- aborted: { canonicalName: "ABORTED", status: 409 },
62
- "out-of-range": { canonicalName: "OUT_OF_RANGE", status: 400 },
63
- unimplemented: { canonicalName: "UNIMPLEMENTED", status: 501 },
64
- internal: { canonicalName: "INTERNAL", status: 500 },
65
- unavailable: { canonicalName: "UNAVAILABLE", status: 503 },
66
- "data-loss": { canonicalName: "DATA_LOSS", status: 500 },
30
+ ok: {
31
+ canonicalName: "OK",
32
+ status: 200
33
+ },
34
+ cancelled: {
35
+ canonicalName: "CANCELLED",
36
+ status: 499
37
+ },
38
+ unknown: {
39
+ canonicalName: "UNKNOWN",
40
+ status: 500
41
+ },
42
+ "invalid-argument": {
43
+ canonicalName: "INVALID_ARGUMENT",
44
+ status: 400
45
+ },
46
+ "deadline-exceeded": {
47
+ canonicalName: "DEADLINE_EXCEEDED",
48
+ status: 504
49
+ },
50
+ "not-found": {
51
+ canonicalName: "NOT_FOUND",
52
+ status: 404
53
+ },
54
+ "already-exists": {
55
+ canonicalName: "ALREADY_EXISTS",
56
+ status: 409
57
+ },
58
+ "permission-denied": {
59
+ canonicalName: "PERMISSION_DENIED",
60
+ status: 403
61
+ },
62
+ unauthenticated: {
63
+ canonicalName: "UNAUTHENTICATED",
64
+ status: 401
65
+ },
66
+ "resource-exhausted": {
67
+ canonicalName: "RESOURCE_EXHAUSTED",
68
+ status: 429
69
+ },
70
+ "failed-precondition": {
71
+ canonicalName: "FAILED_PRECONDITION",
72
+ status: 400
73
+ },
74
+ aborted: {
75
+ canonicalName: "ABORTED",
76
+ status: 409
77
+ },
78
+ "out-of-range": {
79
+ canonicalName: "OUT_OF_RANGE",
80
+ status: 400
81
+ },
82
+ unimplemented: {
83
+ canonicalName: "UNIMPLEMENTED",
84
+ status: 501
85
+ },
86
+ internal: {
87
+ canonicalName: "INTERNAL",
88
+ status: 500
89
+ },
90
+ unavailable: {
91
+ canonicalName: "UNAVAILABLE",
92
+ status: 503
93
+ },
94
+ "data-loss": {
95
+ canonicalName: "DATA_LOSS",
96
+ status: 500
97
+ }
67
98
  };
68
99
  /**
69
- * An explicit error that can be thrown from a handler to send an error to the
70
- * client that called the function.
71
- */
72
- class HttpsError extends Error {
73
- constructor(code, message, details) {
74
- super(message);
75
- // A sanity check for non-TypeScript consumers.
76
- if (code in errorCodeMap === false) {
77
- throw new Error(`Unknown error code: ${code}.`);
78
- }
79
- this.code = code;
80
- this.details = details;
81
- this.httpErrorCode = errorCodeMap[code];
82
- }
83
- /**
84
- * Returns a JSON-serializable representation of this object.
85
- */
86
- toJSON() {
87
- const { details, httpErrorCode: { canonicalName: status }, message, } = this;
88
- return {
89
- ...(details === undefined ? {} : { details }),
90
- message,
91
- status,
92
- };
93
- }
94
- }
95
- exports.HttpsError = HttpsError;
100
+ * An explicit error that can be thrown from a handler to send an error to the
101
+ * client that called the function.
102
+ */
103
+ var HttpsError = class extends Error {
104
+ constructor(code, message, details) {
105
+ super(message);
106
+ if (code in errorCodeMap === false) {
107
+ throw new Error(`Unknown error code: ${code}.`);
108
+ }
109
+ this.code = code;
110
+ this.details = details;
111
+ this.httpErrorCode = errorCodeMap[code];
112
+ }
113
+ /**
114
+ * Returns a JSON-serializable representation of this object.
115
+ */
116
+ toJSON() {
117
+ const { details, httpErrorCode: { canonicalName: status }, message } = this;
118
+ return {
119
+ ...details === undefined ? {} : { details },
120
+ message,
121
+ status
122
+ };
123
+ }
124
+ };
96
125
  /** @hidden */
97
- // Returns true if req is a properly formatted callable request.
98
126
  function isValidRequest(req) {
99
- // The body must not be empty.
100
- if (!req.body) {
101
- logger.warn("Request is missing body.");
102
- return false;
103
- }
104
- // Make sure it's a POST.
105
- if (req.method !== "POST") {
106
- logger.warn("Request has invalid method.", req.method);
107
- return false;
108
- }
109
- // Check that the Content-Type is JSON.
110
- let contentType = (req.header("Content-Type") || "").toLowerCase();
111
- // If it has a charset, just ignore it for now.
112
- const semiColon = contentType.indexOf(";");
113
- if (semiColon >= 0) {
114
- contentType = contentType.slice(0, semiColon).trim();
115
- }
116
- if (contentType !== "application/json") {
117
- logger.warn("Request has incorrect Content-Type.", contentType);
118
- return false;
119
- }
120
- // The body must have data.
121
- if (typeof req.body.data === "undefined") {
122
- logger.warn("Request body is missing data.", req.body);
123
- return false;
124
- }
125
- // TODO(klimt): Allow only specific http headers.
126
- // Verify that the body does not have any extra fields.
127
- const extraKeys = Object.keys(req.body).filter((field) => field !== "data");
128
- if (extraKeys.length !== 0) {
129
- logger.warn("Request body has extra fields: ", extraKeys.join(", "));
130
- return false;
131
- }
132
- return true;
127
+ if (!req.body) {
128
+ require_logger_index.warn("Request is missing body.");
129
+ return false;
130
+ }
131
+ if (req.method !== "POST") {
132
+ require_logger_index.warn("Request has invalid method.", req.method);
133
+ return false;
134
+ }
135
+ let contentType = (req.header("Content-Type") || "").toLowerCase();
136
+ const semiColon = contentType.indexOf(";");
137
+ if (semiColon >= 0) {
138
+ contentType = contentType.slice(0, semiColon).trim();
139
+ }
140
+ if (contentType !== "application/json") {
141
+ require_logger_index.warn("Request has incorrect Content-Type.", contentType);
142
+ return false;
143
+ }
144
+ if (typeof req.body.data === "undefined") {
145
+ require_logger_index.warn("Request body is missing data.", req.body);
146
+ return false;
147
+ }
148
+ const extraKeys = Object.keys(req.body).filter((field) => field !== "data");
149
+ if (extraKeys.length !== 0) {
150
+ require_logger_index.warn("Request body has extra fields: ", extraKeys.join(", "));
151
+ return false;
152
+ }
153
+ return true;
133
154
  }
134
- exports.isValidRequest = isValidRequest;
135
155
  /** @hidden */
136
156
  const LONG_TYPE = "type.googleapis.com/google.protobuf.Int64Value";
137
157
  /** @hidden */
138
158
  const UNSIGNED_LONG_TYPE = "type.googleapis.com/google.protobuf.UInt64Value";
139
159
  /**
140
- * Encodes arbitrary data in our special format for JSON.
141
- * This is exposed only for testing.
142
- */
160
+ * Encodes arbitrary data in our special format for JSON.
161
+ * This is exposed only for testing.
162
+ */
143
163
  /** @hidden */
144
164
  function encode(data) {
145
- if (data === null || typeof data === "undefined") {
146
- return null;
147
- }
148
- if (data instanceof Number) {
149
- data = data.valueOf();
150
- }
151
- if (Number.isFinite(data)) {
152
- // Any number in JS is safe to put directly in JSON and parse as a double
153
- // without any loss of precision.
154
- return data;
155
- }
156
- if (typeof data === "boolean") {
157
- return data;
158
- }
159
- if (typeof data === "string") {
160
- return data;
161
- }
162
- if (Array.isArray(data)) {
163
- return data.map(encode);
164
- }
165
- if (typeof data === "object" || typeof data === "function") {
166
- // Sadly we don't have Object.fromEntries in Node 10, so we can't use a single
167
- // list comprehension
168
- const obj = {};
169
- for (const [k, v] of Object.entries(data)) {
170
- obj[k] = encode(v);
171
- }
172
- return obj;
173
- }
174
- // If we got this far, the data is not encodable.
175
- logger.error("Data cannot be encoded in JSON.", data);
176
- throw new Error(`Data cannot be encoded in JSON: ${data}`);
165
+ if (data === null || typeof data === "undefined") {
166
+ return null;
167
+ }
168
+ if (data instanceof Number) {
169
+ data = data.valueOf();
170
+ }
171
+ if (Number.isFinite(data)) {
172
+ return data;
173
+ }
174
+ if (typeof data === "boolean") {
175
+ return data;
176
+ }
177
+ if (typeof data === "string") {
178
+ return data;
179
+ }
180
+ if (Array.isArray(data)) {
181
+ return data.map(encode);
182
+ }
183
+ if (typeof data === "object" || typeof data === "function") {
184
+ const obj = {};
185
+ for (const [k, v] of Object.entries(data)) {
186
+ obj[k] = encode(v);
187
+ }
188
+ return obj;
189
+ }
190
+ require_logger_index.error("Data cannot be encoded in JSON.", data);
191
+ throw new Error(`Data cannot be encoded in JSON: ${data}`);
177
192
  }
178
- exports.encode = encode;
179
193
  /**
180
- * Decodes our special format for JSON into native types.
181
- * This is exposed only for testing.
182
- */
194
+ * Decodes our special format for JSON into native types.
195
+ * This is exposed only for testing.
196
+ */
183
197
  /** @hidden */
184
198
  function decode(data) {
185
- if (data === null) {
186
- return data;
187
- }
188
- if (data["@type"]) {
189
- switch (data["@type"]) {
190
- case LONG_TYPE:
191
- // Fall through and handle this the same as unsigned.
192
- case UNSIGNED_LONG_TYPE: {
193
- // Technically, this could work return a valid number for malformed
194
- // data if there was a number followed by garbage. But it's just not
195
- // worth all the extra code to detect that case.
196
- const value = parseFloat(data.value);
197
- if (isNaN(value)) {
198
- logger.error("Data cannot be decoded from JSON.", data);
199
- throw new Error(`Data cannot be decoded from JSON: ${data}`);
200
- }
201
- return value;
202
- }
203
- default: {
204
- logger.error("Data cannot be decoded from JSON.", data);
205
- throw new Error(`Data cannot be decoded from JSON: ${data}`);
206
- }
207
- }
208
- }
209
- if (Array.isArray(data)) {
210
- return data.map(decode);
211
- }
212
- if (typeof data === "object") {
213
- const obj = {};
214
- for (const [k, v] of Object.entries(data)) {
215
- obj[k] = decode(v);
216
- }
217
- return obj;
218
- }
219
- // Anything else is safe to return.
220
- return data;
199
+ if (data === null) {
200
+ return data;
201
+ }
202
+ if (data["@type"]) {
203
+ switch (data["@type"]) {
204
+ case LONG_TYPE:
205
+ case UNSIGNED_LONG_TYPE: {
206
+ const value = parseFloat(data.value);
207
+ if (isNaN(value)) {
208
+ require_logger_index.error("Data cannot be decoded from JSON.", data);
209
+ throw new Error(`Data cannot be decoded from JSON: ${data}`);
210
+ }
211
+ return value;
212
+ }
213
+ default: {
214
+ require_logger_index.error("Data cannot be decoded from JSON.", data);
215
+ throw new Error(`Data cannot be decoded from JSON: ${data}`);
216
+ }
217
+ }
218
+ }
219
+ if (Array.isArray(data)) {
220
+ return data.map(decode);
221
+ }
222
+ if (typeof data === "object") {
223
+ const obj = {};
224
+ for (const [k, v] of Object.entries(data)) {
225
+ obj[k] = decode(v);
226
+ }
227
+ return obj;
228
+ }
229
+ return data;
221
230
  }
222
- exports.decode = decode;
223
231
  /** @internal */
224
232
  function unsafeDecodeToken(token) {
225
- if (!JWT_REGEX.test(token)) {
226
- return {};
227
- }
228
- const components = token.split(".").map((s) => Buffer.from(s, "base64").toString());
229
- let payload = components[1];
230
- if (typeof payload === "string") {
231
- try {
232
- const obj = JSON.parse(payload);
233
- if (typeof obj === "object") {
234
- payload = obj;
235
- }
236
- }
237
- catch (e) {
238
- // ignore error
239
- }
240
- }
241
- return payload;
233
+ if (!JWT_REGEX.test(token)) {
234
+ return {};
235
+ }
236
+ const components = token.split(".").map((s) => Buffer.from(s, "base64").toString());
237
+ let payload = components[1];
238
+ if (typeof payload === "string") {
239
+ try {
240
+ const obj = JSON.parse(payload);
241
+ if (typeof obj === "object") {
242
+ payload = obj;
243
+ }
244
+ } catch (_e) {}
245
+ }
246
+ return payload;
242
247
  }
243
- exports.unsafeDecodeToken = unsafeDecodeToken;
244
248
  /**
245
- * Decode, but not verify, a Auth ID token.
246
- *
247
- * Do not use in production. Token should always be verified using the Admin SDK.
248
- *
249
- * This is exposed only for testing.
250
- */
249
+ * Decode, but not verify, a Auth ID token.
250
+ *
251
+ * Do not use in production. Token should always be verified using the Admin SDK.
252
+ *
253
+ * This is exposed only for testing.
254
+ */
251
255
  /** @internal */
252
256
  function unsafeDecodeIdToken(token) {
253
- const decoded = unsafeDecodeToken(token);
254
- decoded.uid = decoded.sub;
255
- return decoded;
257
+ const decoded = unsafeDecodeToken(token);
258
+ decoded.uid = decoded.sub;
259
+ return decoded;
256
260
  }
257
- exports.unsafeDecodeIdToken = unsafeDecodeIdToken;
258
261
  /**
259
- * Decode, but not verify, an App Check token.
260
- *
261
- * Do not use in production. Token should always be verified using the Admin SDK.
262
- *
263
- * This is exposed only for testing.
264
- */
262
+ * Decode, but not verify, an App Check token.
263
+ *
264
+ * Do not use in production. Token should always be verified using the Admin SDK.
265
+ *
266
+ * This is exposed only for testing.
267
+ */
265
268
  /** @internal */
266
269
  function unsafeDecodeAppCheckToken(token) {
267
- const decoded = unsafeDecodeToken(token);
268
- decoded.app_id = decoded.sub;
269
- return decoded;
270
+ const decoded = unsafeDecodeToken(token);
271
+ decoded.app_id = decoded.sub;
272
+ return decoded;
270
273
  }
271
- exports.unsafeDecodeAppCheckToken = unsafeDecodeAppCheckToken;
272
274
  /**
273
- * Check and verify tokens included in the requests. Once verified, tokens
274
- * are injected into the callable context.
275
- *
276
- * @param {Request} req - Request sent to the Callable function.
277
- * @param {CallableContext} ctx - Context to be sent to callable function handler.
278
- * @returns {CallableTokenStatus} Status of the token verifications.
279
- */
275
+ * Check and verify tokens included in the requests. Once verified, tokens
276
+ * are injected into the callable context.
277
+ *
278
+ * @param {Request} req - Request sent to the Callable function.
279
+ * @param {CallableContext} ctx - Context to be sent to callable function handler.
280
+ * @returns {CallableTokenStatus} Status of the token verifications.
281
+ */
280
282
  /** @internal */
281
283
  async function checkTokens(req, ctx, options) {
282
- const verifications = {
283
- app: "INVALID",
284
- auth: "INVALID",
285
- };
286
- [verifications.auth, verifications.app] = await Promise.all([
287
- checkAuthToken(req, ctx),
288
- checkAppCheckToken(req, ctx, options),
289
- ]);
290
- const logPayload = {
291
- verifications,
292
- "logging.googleapis.com/labels": {
293
- "firebase-log-type": "callable-request-verification",
294
- },
295
- };
296
- const errs = [];
297
- if (verifications.app === "INVALID") {
298
- errs.push("AppCheck token was rejected.");
299
- }
300
- if (verifications.auth === "INVALID") {
301
- errs.push("Auth token was rejected.");
302
- }
303
- if (errs.length === 0) {
304
- logger.debug("Callable request verification passed", logPayload);
305
- }
306
- else {
307
- logger.warn(`Callable request verification failed: ${errs.join(" ")}`, logPayload);
308
- }
309
- return verifications;
284
+ const verifications = {
285
+ app: "INVALID",
286
+ auth: "INVALID"
287
+ };
288
+ [verifications.auth, verifications.app] = await Promise.all([checkAuthToken(req, ctx), checkAppCheckToken(req, ctx, options)]);
289
+ const logPayload = {
290
+ verifications,
291
+ "logging.googleapis.com/labels": { "firebase-log-type": "callable-request-verification" }
292
+ };
293
+ const errs = [];
294
+ if (verifications.app === "INVALID") {
295
+ errs.push("AppCheck token was rejected.");
296
+ }
297
+ if (verifications.auth === "INVALID") {
298
+ errs.push("Auth token was rejected.");
299
+ }
300
+ if (errs.length === 0) {
301
+ require_logger_index.debug("Callable request verification passed", logPayload);
302
+ } else {
303
+ require_logger_index.warn(`Callable request verification failed: ${errs.join(" ")}`, logPayload);
304
+ }
305
+ return verifications;
310
306
  }
311
307
  /** @interanl */
312
308
  async function checkAuthToken(req, ctx) {
313
- const authorization = req.header("Authorization");
314
- if (!authorization) {
315
- return "MISSING";
316
- }
317
- const match = authorization.match(/^Bearer (.*)$/i);
318
- if (!match) {
319
- return "INVALID";
320
- }
321
- const idToken = match[1];
322
- try {
323
- let authToken;
324
- if ((0, debug_1.isDebugFeatureEnabled)("skipTokenVerification")) {
325
- authToken = unsafeDecodeIdToken(idToken);
326
- }
327
- else {
328
- authToken = await (0, auth_1.getAuth)((0, app_1.getApp)()).verifyIdToken(idToken);
329
- }
330
- ctx.auth = {
331
- uid: authToken.uid,
332
- token: authToken,
333
- rawToken: idToken,
334
- };
335
- return "VALID";
336
- }
337
- catch (err) {
338
- logger.warn("Failed to validate auth token.", err);
339
- return "INVALID";
340
- }
309
+ const authorization = req.header("Authorization");
310
+ if (!authorization) {
311
+ return "MISSING";
312
+ }
313
+ const match = authorization.match(/^Bearer (.*)$/i);
314
+ if (!match) {
315
+ return "INVALID";
316
+ }
317
+ const idToken = match[1];
318
+ try {
319
+ let authToken;
320
+ if (require_common_debug.isDebugFeatureEnabled("skipTokenVerification")) {
321
+ authToken = unsafeDecodeIdToken(idToken);
322
+ } else {
323
+ authToken = await (0, firebase_admin_auth.getAuth)(require_common_app.getApp()).verifyIdToken(idToken);
324
+ }
325
+ ctx.auth = {
326
+ uid: authToken.uid,
327
+ token: authToken,
328
+ rawToken: idToken
329
+ };
330
+ return "VALID";
331
+ } catch (err) {
332
+ require_logger_index.warn("Failed to validate auth token.", err);
333
+ return "INVALID";
334
+ }
341
335
  }
342
- exports.checkAuthToken = checkAuthToken;
343
336
  /** @internal */
344
337
  async function checkAppCheckToken(req, ctx, options) {
345
- var _a;
346
- const appCheckToken = req.header("X-Firebase-AppCheck");
347
- if (!appCheckToken) {
348
- return "MISSING";
349
- }
350
- try {
351
- let appCheckData;
352
- if ((0, debug_1.isDebugFeatureEnabled)("skipTokenVerification")) {
353
- const decodedToken = unsafeDecodeAppCheckToken(appCheckToken);
354
- appCheckData = { appId: decodedToken.app_id, token: decodedToken };
355
- if (options.consumeAppCheckToken) {
356
- appCheckData.alreadyConsumed = false;
357
- }
358
- }
359
- else {
360
- const appCheck = (0, app_check_1.getAppCheck)((0, app_1.getApp)());
361
- if (options.consumeAppCheckToken) {
362
- if (((_a = appCheck.verifyToken) === null || _a === void 0 ? void 0 : _a.length) === 1) {
363
- const errorMsg = "Unsupported version of the Admin SDK." +
364
- " App Check token will not be consumed." +
365
- " Please upgrade the firebase-admin to the latest version.";
366
- logger.error(errorMsg);
367
- throw new HttpsError("internal", "Internal Error");
368
- }
369
- appCheckData = await (0, app_check_1.getAppCheck)((0, app_1.getApp)()).verifyToken(appCheckToken, { consume: true });
370
- }
371
- else {
372
- appCheckData = await (0, app_check_1.getAppCheck)((0, app_1.getApp)()).verifyToken(appCheckToken);
373
- }
374
- }
375
- ctx.app = appCheckData;
376
- return "VALID";
377
- }
378
- catch (err) {
379
- logger.warn("Failed to validate AppCheck token.", err);
380
- if (err instanceof HttpsError) {
381
- throw err;
382
- }
383
- return "INVALID";
384
- }
338
+ const appCheckToken = req.header("X-Firebase-AppCheck");
339
+ if (!appCheckToken) {
340
+ return "MISSING";
341
+ }
342
+ try {
343
+ let appCheckData;
344
+ if (require_common_debug.isDebugFeatureEnabled("skipTokenVerification")) {
345
+ const decodedToken = unsafeDecodeAppCheckToken(appCheckToken);
346
+ appCheckData = {
347
+ appId: decodedToken.app_id,
348
+ token: decodedToken
349
+ };
350
+ if (options.consumeAppCheckToken) {
351
+ appCheckData.alreadyConsumed = false;
352
+ }
353
+ } else {
354
+ const appCheck = (0, firebase_admin_app_check.getAppCheck)(require_common_app.getApp());
355
+ if (options.consumeAppCheckToken) {
356
+ if (appCheck.verifyToken?.length === 1) {
357
+ const errorMsg = "Unsupported version of the Admin SDK." + " App Check token will not be consumed." + " Please upgrade the firebase-admin to the latest version.";
358
+ require_logger_index.error(errorMsg);
359
+ throw new HttpsError("internal", "Internal Error");
360
+ }
361
+ appCheckData = await (0, firebase_admin_app_check.getAppCheck)(require_common_app.getApp()).verifyToken(appCheckToken, { consume: true });
362
+ } else {
363
+ appCheckData = await (0, firebase_admin_app_check.getAppCheck)(require_common_app.getApp()).verifyToken(appCheckToken);
364
+ }
365
+ }
366
+ ctx.app = appCheckData;
367
+ return "VALID";
368
+ } catch (err) {
369
+ require_logger_index.warn("Failed to validate AppCheck token.", err);
370
+ if (err instanceof HttpsError) {
371
+ throw err;
372
+ }
373
+ return "INVALID";
374
+ }
385
375
  }
386
376
  /** @internal */
387
377
  function onCallHandler(options, handler, version) {
388
- const wrapped = wrapOnCallHandler(options, handler, version);
389
- return (req, res) => {
390
- return new Promise((resolve) => {
391
- res.on("finish", resolve);
392
- cors(options.cors)(req, res, () => {
393
- resolve(wrapped(req, res));
394
- });
395
- });
396
- };
378
+ const wrapped = wrapOnCallHandler(options, handler, version);
379
+ return (req, res) => {
380
+ return new Promise((resolve) => {
381
+ res.on("finish", resolve);
382
+ (0, cors.default)(options.cors)(req, res, () => {
383
+ resolve(wrapped(req, res));
384
+ });
385
+ });
386
+ };
397
387
  }
398
- exports.onCallHandler = onCallHandler;
399
388
  function encodeSSE(data) {
400
- return `data: ${JSON.stringify(data)}\n\n`;
389
+ return `data: ${JSON.stringify(data)}\n\n`;
401
390
  }
402
391
  /** @internal */
403
392
  function wrapOnCallHandler(options, handler, version) {
404
- return async (req, res) => {
405
- var _a;
406
- const abortController = new AbortController();
407
- let heartbeatInterval = null;
408
- const heartbeatSeconds = options.heartbeatSeconds === undefined ? exports.DEFAULT_HEARTBEAT_SECONDS : options.heartbeatSeconds;
409
- const clearScheduledHeartbeat = () => {
410
- if (heartbeatInterval) {
411
- clearTimeout(heartbeatInterval);
412
- heartbeatInterval = null;
413
- }
414
- };
415
- const scheduleHeartbeat = () => {
416
- clearScheduledHeartbeat();
417
- if (!abortController.signal.aborted) {
418
- heartbeatInterval = setTimeout(() => {
419
- if (!abortController.signal.aborted) {
420
- res.write(": ping\n\n");
421
- scheduleHeartbeat();
422
- }
423
- }, heartbeatSeconds * 1000);
424
- }
425
- };
426
- res.on("close", () => {
427
- clearScheduledHeartbeat();
428
- abortController.abort();
429
- });
430
- try {
431
- if (!isValidRequest(req)) {
432
- logger.error("Invalid request, unable to process.");
433
- throw new HttpsError("invalid-argument", "Bad Request");
434
- }
435
- const context = { rawRequest: req };
436
- // TODO(colerogers): yank this when we release a breaking change of the CLI that removes
437
- // our monkey-patching code referenced below and increases the minimum supported SDK version.
438
- //
439
- // Note: This code is needed to fix v1 callable functions in the emulator with a monorepo setup.
440
- // The original monkey-patched code lived in the functionsEmulatorRuntime
441
- // (link: https://github.com/firebase/firebase-tools/blob/accea7abda3cc9fa6bb91368e4895faf95281c60/src/emulator/functionsEmulatorRuntime.ts#L480)
442
- // and was not compatible with how monorepos separate out packages (see https://github.com/firebase/firebase-tools/issues/5210).
443
- if ((0, debug_1.isDebugFeatureEnabled)("skipTokenVerification") && version === "gcfv1") {
444
- const authContext = context.rawRequest.header(exports.CALLABLE_AUTH_HEADER);
445
- if (authContext) {
446
- logger.debug("Callable functions auth override", {
447
- key: exports.CALLABLE_AUTH_HEADER,
448
- value: authContext,
449
- });
450
- context.auth = JSON.parse(decodeURIComponent(authContext));
451
- delete context.rawRequest.headers[exports.CALLABLE_AUTH_HEADER];
452
- }
453
- const originalAuth = context.rawRequest.header(exports.ORIGINAL_AUTH_HEADER);
454
- if (originalAuth) {
455
- context.rawRequest.headers["authorization"] = originalAuth;
456
- delete context.rawRequest.headers[exports.ORIGINAL_AUTH_HEADER];
457
- }
458
- }
459
- const tokenStatus = await checkTokens(req, context, options);
460
- if (tokenStatus.auth === "INVALID") {
461
- throw new HttpsError("unauthenticated", "Unauthenticated");
462
- }
463
- if (tokenStatus.app === "INVALID") {
464
- if (options.enforceAppCheck) {
465
- throw new HttpsError("unauthenticated", "Unauthenticated");
466
- }
467
- else {
468
- logger.warn("Allowing request with invalid AppCheck token because enforcement is disabled");
469
- }
470
- }
471
- if (tokenStatus.app === "MISSING" && options.enforceAppCheck) {
472
- throw new HttpsError("unauthenticated", "Unauthenticated");
473
- }
474
- const instanceId = req.header("Firebase-Instance-ID-Token");
475
- if (instanceId) {
476
- // Validating the token requires an http request, so we don't do it.
477
- // If the user wants to use it for something, it will be validated then.
478
- // Currently, the only real use case for this token is for sending
479
- // pushes with FCM. In that case, the FCM APIs will validate the token.
480
- context.instanceIdToken = req.header("Firebase-Instance-ID-Token");
481
- }
482
- const acceptsStreaming = req.header("accept") === "text/event-stream";
483
- if (acceptsStreaming && version === "gcfv1") {
484
- // streaming responses are not supported in v1 callable
485
- throw new HttpsError("invalid-argument", "Unsupported Accept header 'text/event-stream'");
486
- }
487
- const data = decode(req.body.data);
488
- if (options.authPolicy) {
489
- const authorized = await options.authPolicy((_a = context.auth) !== null && _a !== void 0 ? _a : null, data);
490
- if (!authorized) {
491
- throw new HttpsError("permission-denied", "Permission Denied");
492
- }
493
- }
494
- let result;
495
- if (version === "gcfv1") {
496
- result = await handler(data, context);
497
- }
498
- else {
499
- const arg = {
500
- ...context,
501
- data,
502
- acceptsStreaming,
503
- };
504
- const responseProxy = {
505
- sendChunk(chunk) {
506
- // if client doesn't accept sse-protocol, response.write() is no-op.
507
- if (!acceptsStreaming) {
508
- return Promise.resolve(false);
509
- }
510
- // if connection is already closed, response.write() is no-op.
511
- if (abortController.signal.aborted) {
512
- return Promise.resolve(false);
513
- }
514
- const formattedData = encodeSSE({ message: chunk });
515
- let resolve;
516
- let reject;
517
- const p = new Promise((res, rej) => {
518
- resolve = res;
519
- reject = rej;
520
- });
521
- const wrote = res.write(formattedData, (error) => {
522
- if (error) {
523
- reject(error);
524
- return;
525
- }
526
- resolve(wrote);
527
- });
528
- // Reset heartbeat timer after successful write
529
- if (wrote && heartbeatInterval !== null && heartbeatSeconds > 0) {
530
- scheduleHeartbeat();
531
- }
532
- return p;
533
- },
534
- signal: abortController.signal,
535
- };
536
- if (acceptsStreaming) {
537
- // SSE always responds with 200
538
- res.status(200);
539
- if (heartbeatSeconds !== null && heartbeatSeconds > 0) {
540
- scheduleHeartbeat();
541
- }
542
- }
543
- // For some reason the type system isn't picking up that the handler
544
- // is a one argument function.
545
- result = await handler(arg, responseProxy);
546
- clearScheduledHeartbeat();
547
- }
548
- if (!abortController.signal.aborted) {
549
- // Encode the result as JSON to preserve types like Dates.
550
- result = encode(result);
551
- // If there was some result, encode it in the body.
552
- const responseBody = { result };
553
- if (acceptsStreaming) {
554
- res.write(encodeSSE(responseBody));
555
- res.end();
556
- }
557
- else {
558
- res.status(200).send(responseBody);
559
- }
560
- }
561
- else {
562
- res.end();
563
- }
564
- }
565
- catch (err) {
566
- if (!abortController.signal.aborted) {
567
- let httpErr = err;
568
- if (!(err instanceof HttpsError)) {
569
- // This doesn't count as an 'explicit' error.
570
- logger.error("Unhandled error", err);
571
- httpErr = new HttpsError("internal", "INTERNAL");
572
- }
573
- const { status } = httpErr.httpErrorCode;
574
- const body = { error: httpErr.toJSON() };
575
- if (version === "gcfv2" && req.header("accept") === "text/event-stream") {
576
- res.write(encodeSSE(body));
577
- res.end();
578
- }
579
- else {
580
- res.status(status).send(body);
581
- }
582
- }
583
- else {
584
- res.end();
585
- }
586
- }
587
- finally {
588
- clearScheduledHeartbeat();
589
- }
590
- };
393
+ return async (req, res) => {
394
+ const abortController = new AbortController();
395
+ let heartbeatInterval = null;
396
+ const heartbeatSeconds = options.heartbeatSeconds === undefined ? DEFAULT_HEARTBEAT_SECONDS : options.heartbeatSeconds;
397
+ const clearScheduledHeartbeat = () => {
398
+ if (heartbeatInterval) {
399
+ clearTimeout(heartbeatInterval);
400
+ heartbeatInterval = null;
401
+ }
402
+ };
403
+ const scheduleHeartbeat = () => {
404
+ clearScheduledHeartbeat();
405
+ if (!abortController.signal.aborted) {
406
+ heartbeatInterval = setTimeout(() => {
407
+ if (!abortController.signal.aborted) {
408
+ res.write(": ping\n\n");
409
+ scheduleHeartbeat();
410
+ }
411
+ }, heartbeatSeconds * 1e3);
412
+ }
413
+ };
414
+ res.on("close", () => {
415
+ clearScheduledHeartbeat();
416
+ abortController.abort();
417
+ });
418
+ try {
419
+ if (!isValidRequest(req)) {
420
+ require_logger_index.error("Invalid request, unable to process.");
421
+ throw new HttpsError("invalid-argument", "Bad Request");
422
+ }
423
+ const context = { rawRequest: req };
424
+ if (require_common_debug.isDebugFeatureEnabled("skipTokenVerification") && version === "gcfv1") {
425
+ const authContext = context.rawRequest.header(CALLABLE_AUTH_HEADER);
426
+ if (authContext) {
427
+ require_logger_index.debug("Callable functions auth override", {
428
+ key: CALLABLE_AUTH_HEADER,
429
+ value: authContext
430
+ });
431
+ context.auth = JSON.parse(decodeURIComponent(authContext));
432
+ delete context.rawRequest.headers[CALLABLE_AUTH_HEADER];
433
+ }
434
+ const originalAuth = context.rawRequest.header(ORIGINAL_AUTH_HEADER);
435
+ if (originalAuth) {
436
+ context.rawRequest.headers["authorization"] = originalAuth;
437
+ delete context.rawRequest.headers[ORIGINAL_AUTH_HEADER];
438
+ }
439
+ }
440
+ const tokenStatus = await checkTokens(req, context, options);
441
+ if (tokenStatus.auth === "INVALID") {
442
+ throw new HttpsError("unauthenticated", "Unauthenticated");
443
+ }
444
+ if (tokenStatus.app === "INVALID") {
445
+ if (options.enforceAppCheck) {
446
+ throw new HttpsError("unauthenticated", "Unauthenticated");
447
+ } else {
448
+ require_logger_index.warn("Allowing request with invalid AppCheck token because enforcement is disabled");
449
+ }
450
+ }
451
+ if (tokenStatus.app === "MISSING" && options.enforceAppCheck) {
452
+ throw new HttpsError("unauthenticated", "Unauthenticated");
453
+ }
454
+ const instanceId = req.header("Firebase-Instance-ID-Token");
455
+ if (instanceId) {
456
+ context.instanceIdToken = req.header("Firebase-Instance-ID-Token");
457
+ }
458
+ const acceptsStreaming = req.header("accept") === "text/event-stream";
459
+ if (acceptsStreaming && version === "gcfv1") {
460
+ throw new HttpsError("invalid-argument", "Unsupported Accept header 'text/event-stream'");
461
+ }
462
+ const data = decode(req.body.data);
463
+ if (options.authPolicy) {
464
+ const authorized = await options.authPolicy(context.auth ?? null, data);
465
+ if (!authorized) {
466
+ throw new HttpsError("permission-denied", "Permission Denied");
467
+ }
468
+ }
469
+ let result;
470
+ if (version === "gcfv1") {
471
+ result = await handler(data, context);
472
+ } else {
473
+ const arg = {
474
+ ...context,
475
+ data,
476
+ acceptsStreaming
477
+ };
478
+ const responseProxy = {
479
+ sendChunk(chunk) {
480
+ if (!acceptsStreaming) {
481
+ return Promise.resolve(false);
482
+ }
483
+ if (abortController.signal.aborted) {
484
+ return Promise.resolve(false);
485
+ }
486
+ const formattedData = encodeSSE({ message: chunk });
487
+ let resolve;
488
+ let reject;
489
+ const p = new Promise((res$1, rej) => {
490
+ resolve = res$1;
491
+ reject = rej;
492
+ });
493
+ const wrote = res.write(formattedData, (error$1) => {
494
+ if (error$1) {
495
+ reject(error$1);
496
+ return;
497
+ }
498
+ resolve(wrote);
499
+ });
500
+ if (wrote && heartbeatInterval !== null && heartbeatSeconds > 0) {
501
+ scheduleHeartbeat();
502
+ }
503
+ return p;
504
+ },
505
+ signal: abortController.signal
506
+ };
507
+ if (acceptsStreaming) {
508
+ res.status(200);
509
+ if (heartbeatSeconds !== null && heartbeatSeconds > 0) {
510
+ scheduleHeartbeat();
511
+ }
512
+ }
513
+ result = await handler(arg, responseProxy);
514
+ clearScheduledHeartbeat();
515
+ }
516
+ if (!abortController.signal.aborted) {
517
+ result = encode(result);
518
+ const responseBody = { result };
519
+ if (acceptsStreaming) {
520
+ res.write(encodeSSE(responseBody));
521
+ res.end();
522
+ } else {
523
+ res.status(200).send(responseBody);
524
+ }
525
+ } else {
526
+ res.end();
527
+ }
528
+ } catch (err) {
529
+ if (!abortController.signal.aborted) {
530
+ let httpErr = err;
531
+ if (!(err instanceof HttpsError)) {
532
+ require_logger_index.error("Unhandled error", err);
533
+ httpErr = new HttpsError("internal", "INTERNAL");
534
+ }
535
+ const { status } = httpErr.httpErrorCode;
536
+ const body = { error: httpErr.toJSON() };
537
+ if (version === "gcfv2" && req.header("accept") === "text/event-stream") {
538
+ res.write(encodeSSE(body));
539
+ res.end();
540
+ } else {
541
+ res.status(status).send(body);
542
+ }
543
+ } else {
544
+ res.end();
545
+ }
546
+ } finally {
547
+ clearScheduledHeartbeat();
548
+ }
549
+ };
591
550
  }
551
+ /**
552
+ * Wraps an HTTP handler with a safety net for unhandled errors.
553
+ *
554
+ * This wrapper catches both synchronous errors and rejected Promises from `async` handlers.
555
+ * Without this, an unhandled error in an `async` handler would cause the request to hang
556
+ * until the platform timeout, as Express (v4) does not await handlers.
557
+ *
558
+ * It logs the error and returns a 500 Internal Server Error to the client if the response
559
+ * headers have not yet been sent.
560
+ *
561
+ * @internal
562
+ */
563
+ function withErrorHandler(handler) {
564
+ return async (req, res) => {
565
+ try {
566
+ await handler(req, res);
567
+ } catch (err) {
568
+ require_logger_index.error("Unhandled error", err);
569
+ if (!res.headersSent) {
570
+ res.status(500).send("Internal Server Error");
571
+ }
572
+ }
573
+ };
574
+ }
575
+
576
+ //#endregion
577
+ exports.CALLABLE_AUTH_HEADER = CALLABLE_AUTH_HEADER;
578
+ exports.DEFAULT_HEARTBEAT_SECONDS = DEFAULT_HEARTBEAT_SECONDS;
579
+ exports.HttpsError = HttpsError;
580
+ exports.ORIGINAL_AUTH_HEADER = ORIGINAL_AUTH_HEADER;
581
+ exports.checkAuthToken = checkAuthToken;
582
+ exports.decode = decode;
583
+ exports.encode = encode;
584
+ exports.isValidRequest = isValidRequest;
585
+ exports.onCallHandler = onCallHandler;
586
+ exports.unsafeDecodeAppCheckToken = unsafeDecodeAppCheckToken;
587
+ exports.unsafeDecodeIdToken = unsafeDecodeIdToken;
588
+ exports.unsafeDecodeToken = unsafeDecodeToken;
589
+ exports.withErrorHandler = withErrorHandler;