firebase-functions 6.1.0 → 6.1.1
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.
|
@@ -85,6 +85,14 @@ export interface CallableRequest<T = any> {
|
|
|
85
85
|
*/
|
|
86
86
|
rawRequest: Request;
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* CallableProxyResponse exposes subset of express.Response object
|
|
90
|
+
* to allow writing partial, streaming responses back to the client.
|
|
91
|
+
*/
|
|
92
|
+
export interface CallableProxyResponse {
|
|
93
|
+
write: express.Response["write"];
|
|
94
|
+
acceptsStreaming: boolean;
|
|
95
|
+
}
|
|
88
96
|
/**
|
|
89
97
|
* The set of Firebase Functions status codes. The codes are the same at the
|
|
90
98
|
* ones exposed by {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md | gRPC}.
|
|
@@ -385,8 +385,8 @@ async function checkAppCheckToken(req, ctx, options) {
|
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
/** @internal */
|
|
388
|
-
function onCallHandler(options, handler) {
|
|
389
|
-
const wrapped = wrapOnCallHandler(options, handler);
|
|
388
|
+
function onCallHandler(options, handler, version) {
|
|
389
|
+
const wrapped = wrapOnCallHandler(options, handler, version);
|
|
390
390
|
return (req, res) => {
|
|
391
391
|
return new Promise((resolve) => {
|
|
392
392
|
res.on("finish", resolve);
|
|
@@ -397,8 +397,11 @@ function onCallHandler(options, handler) {
|
|
|
397
397
|
};
|
|
398
398
|
}
|
|
399
399
|
exports.onCallHandler = onCallHandler;
|
|
400
|
+
function encodeSSE(data) {
|
|
401
|
+
return `data: ${JSON.stringify(data)}\n`;
|
|
402
|
+
}
|
|
400
403
|
/** @internal */
|
|
401
|
-
function wrapOnCallHandler(options, handler) {
|
|
404
|
+
function wrapOnCallHandler(options, handler, version) {
|
|
402
405
|
return async (req, res) => {
|
|
403
406
|
try {
|
|
404
407
|
if (!isValidRequest(req)) {
|
|
@@ -413,7 +416,7 @@ function wrapOnCallHandler(options, handler) {
|
|
|
413
416
|
// The original monkey-patched code lived in the functionsEmulatorRuntime
|
|
414
417
|
// (link: https://github.com/firebase/firebase-tools/blob/accea7abda3cc9fa6bb91368e4895faf95281c60/src/emulator/functionsEmulatorRuntime.ts#L480)
|
|
415
418
|
// and was not compatible with how monorepos separate out packages (see https://github.com/firebase/firebase-tools/issues/5210).
|
|
416
|
-
if ((0, debug_1.isDebugFeatureEnabled)("skipTokenVerification") &&
|
|
419
|
+
if ((0, debug_1.isDebugFeatureEnabled)("skipTokenVerification") && version === "gcfv1") {
|
|
417
420
|
const authContext = context.rawRequest.header(exports.CALLABLE_AUTH_HEADER);
|
|
418
421
|
if (authContext) {
|
|
419
422
|
logger.debug("Callable functions auth override", {
|
|
@@ -452,9 +455,14 @@ function wrapOnCallHandler(options, handler) {
|
|
|
452
455
|
// pushes with FCM. In that case, the FCM APIs will validate the token.
|
|
453
456
|
context.instanceIdToken = req.header("Firebase-Instance-ID-Token");
|
|
454
457
|
}
|
|
458
|
+
const acceptsStreaming = req.header("accept") === "text/event-stream";
|
|
459
|
+
if (acceptsStreaming && version === "gcfv1") {
|
|
460
|
+
// streaming responses are not supported in v1 callable
|
|
461
|
+
throw new HttpsError("invalid-argument", "Unsupported Accept header 'text/event-stream'");
|
|
462
|
+
}
|
|
455
463
|
const data = decode(req.body.data);
|
|
456
464
|
let result;
|
|
457
|
-
if (
|
|
465
|
+
if (version === "gcfv1") {
|
|
458
466
|
result = await handler(data, context);
|
|
459
467
|
}
|
|
460
468
|
else {
|
|
@@ -462,15 +470,36 @@ function wrapOnCallHandler(options, handler) {
|
|
|
462
470
|
...context,
|
|
463
471
|
data,
|
|
464
472
|
};
|
|
473
|
+
// TODO: set up optional heartbeat
|
|
474
|
+
const responseProxy = {
|
|
475
|
+
write(chunk) {
|
|
476
|
+
if (acceptsStreaming) {
|
|
477
|
+
const formattedData = encodeSSE({ message: chunk });
|
|
478
|
+
return res.write(formattedData);
|
|
479
|
+
}
|
|
480
|
+
// if client doesn't accept sse-protocol, response.write() is no-op.
|
|
481
|
+
},
|
|
482
|
+
acceptsStreaming,
|
|
483
|
+
};
|
|
484
|
+
if (acceptsStreaming) {
|
|
485
|
+
// SSE always responds with 200
|
|
486
|
+
res.status(200);
|
|
487
|
+
}
|
|
465
488
|
// For some reason the type system isn't picking up that the handler
|
|
466
489
|
// is a one argument function.
|
|
467
|
-
result = await handler(arg);
|
|
490
|
+
result = await handler(arg, responseProxy);
|
|
468
491
|
}
|
|
469
492
|
// Encode the result as JSON to preserve types like Dates.
|
|
470
493
|
result = encode(result);
|
|
471
494
|
// If there was some result, encode it in the body.
|
|
472
495
|
const responseBody = { result };
|
|
473
|
-
|
|
496
|
+
if (acceptsStreaming) {
|
|
497
|
+
res.write(encodeSSE(responseBody));
|
|
498
|
+
res.end();
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
res.status(200).send(responseBody);
|
|
502
|
+
}
|
|
474
503
|
}
|
|
475
504
|
catch (err) {
|
|
476
505
|
let httpErr = err;
|
|
@@ -481,7 +510,12 @@ function wrapOnCallHandler(options, handler) {
|
|
|
481
510
|
}
|
|
482
511
|
const { status } = httpErr.httpErrorCode;
|
|
483
512
|
const body = { error: httpErr.toJSON() };
|
|
484
|
-
|
|
513
|
+
if (version === "gcfv2" && req.header("accept") === "text/event-stream") {
|
|
514
|
+
res.send(encodeSSE(body));
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
res.status(status).send(body);
|
|
518
|
+
}
|
|
485
519
|
}
|
|
486
520
|
};
|
|
487
521
|
}
|
|
@@ -70,9 +70,8 @@ function _onRequestWithOptions(handler, options) {
|
|
|
70
70
|
exports._onRequestWithOptions = _onRequestWithOptions;
|
|
71
71
|
/** @internal */
|
|
72
72
|
function _onCallWithOptions(handler, options) {
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
// in another handler to avoid accidentally triggering the v2 API
|
|
73
|
+
// fix the length of handler to make the call to handler consistent
|
|
74
|
+
// in the onCallHandler
|
|
76
75
|
const fixedLen = (data, context) => {
|
|
77
76
|
return (0, onInit_1.withInit)(handler)(data, context);
|
|
78
77
|
};
|
|
@@ -80,7 +79,7 @@ function _onCallWithOptions(handler, options) {
|
|
|
80
79
|
enforceAppCheck: options.enforceAppCheck,
|
|
81
80
|
consumeAppCheckToken: options.consumeAppCheckToken,
|
|
82
81
|
cors: { origin: true, methods: "POST" },
|
|
83
|
-
}, fixedLen));
|
|
82
|
+
}, fixedLen, "gcfv1"));
|
|
84
83
|
func.__trigger = {
|
|
85
84
|
labels: {},
|
|
86
85
|
...(0, cloud_functions_1.optionsToTrigger)(options),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as express from "express";
|
|
2
2
|
import { ResetValue } from "../../common/options";
|
|
3
|
-
import { CallableRequest, FunctionsErrorCode, HttpsError, Request } from "../../common/providers/https";
|
|
3
|
+
import { CallableRequest, CallableProxyResponse, FunctionsErrorCode, HttpsError, Request } from "../../common/providers/https";
|
|
4
4
|
import { ManifestEndpoint } from "../../runtime/manifest";
|
|
5
5
|
import { GlobalOptions, SupportedRegion } from "../options";
|
|
6
6
|
import { Expression } from "../../params";
|
|
@@ -175,10 +175,10 @@ export declare function onRequest(handler: (request: Request, response: express.
|
|
|
175
175
|
* @param handler - A function that takes a {@link https.CallableRequest}.
|
|
176
176
|
* @returns A function that you can export and deploy.
|
|
177
177
|
*/
|
|
178
|
-
export declare function onCall<T = any, Return = any | Promise<any>>(opts: CallableOptions, handler: (request: CallableRequest<T
|
|
178
|
+
export declare function onCall<T = any, Return = any | Promise<any>>(opts: CallableOptions, handler: (request: CallableRequest<T>, response?: CallableProxyResponse) => Return): CallableFunction<T, Return extends Promise<unknown> ? Return : Promise<Return>>;
|
|
179
179
|
/**
|
|
180
180
|
* Declares a callable method for clients to call using a Firebase SDK.
|
|
181
181
|
* @param handler - A function that takes a {@link https.CallableRequest}.
|
|
182
182
|
* @returns A function that you can export and deploy.
|
|
183
183
|
*/
|
|
184
|
-
export declare function onCall<T = any, Return = any | Promise<any>>(handler: (request: CallableRequest<T
|
|
184
|
+
export declare function onCall<T = any, Return = any | Promise<any>>(handler: (request: CallableRequest<T>, response?: CallableProxyResponse) => Return): CallableFunction<T, Return extends Promise<unknown> ? Return : Promise<Return>>;
|
|
@@ -130,15 +130,14 @@ function onCall(optsOrHandler, handler) {
|
|
|
130
130
|
if (Array.isArray(origin) && origin.length === 1) {
|
|
131
131
|
origin = origin[0];
|
|
132
132
|
}
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
const fixedLen = (req) => (0, onInit_1.withInit)(handler)(req);
|
|
133
|
+
// fix the length of handler to make the call to handler consistent
|
|
134
|
+
const fixedLen = (req, resp) => (0, onInit_1.withInit)(handler)(req, resp);
|
|
136
135
|
let func = (0, https_1.onCallHandler)({
|
|
137
136
|
cors: { origin, methods: "POST" },
|
|
138
137
|
enforceAppCheck: (_a = opts.enforceAppCheck) !== null && _a !== void 0 ? _a : options.getGlobalOptions().enforceAppCheck,
|
|
139
138
|
consumeAppCheckToken: opts.consumeAppCheckToken,
|
|
140
|
-
}, fixedLen);
|
|
141
|
-
func = (0, trace_1.wrapTraceContext)(func);
|
|
139
|
+
}, fixedLen, "gcfv2");
|
|
140
|
+
func = (0, trace_1.wrapTraceContext)((0, onInit_1.withInit)(func));
|
|
142
141
|
Object.defineProperty(func, "__trigger", {
|
|
143
142
|
get: () => {
|
|
144
143
|
const baseOpts = options.optionsToTriggerAnnotations(options.getGlobalOptions());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-functions",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.1",
|
|
4
4
|
"description": "Firebase SDK for Cloud Functions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"firebase",
|
|
@@ -299,7 +299,7 @@
|
|
|
299
299
|
"eslint-config-prettier": "^8.3.0",
|
|
300
300
|
"eslint-plugin-jsdoc": "^39.2.9",
|
|
301
301
|
"eslint-plugin-prettier": "^4.0.0",
|
|
302
|
-
"firebase-admin": "^
|
|
302
|
+
"firebase-admin": "^13.0.0",
|
|
303
303
|
"js-yaml": "^3.13.1",
|
|
304
304
|
"jsdom": "^16.2.1",
|
|
305
305
|
"jsonwebtoken": "^9.0.0",
|
|
@@ -319,7 +319,7 @@
|
|
|
319
319
|
"yargs": "^15.3.1"
|
|
320
320
|
},
|
|
321
321
|
"peerDependencies": {
|
|
322
|
-
"firebase-admin": "^11.10.0 || ^12.0.0"
|
|
322
|
+
"firebase-admin": "^11.10.0 || ^12.0.0 || ^13.0.0"
|
|
323
323
|
},
|
|
324
324
|
"engines": {
|
|
325
325
|
"node": ">=14.10.0"
|