firebase-functions
Advanced tools
Comparing version
@@ -89,2 +89,10 @@ /// <reference types="node" /> | ||
/** | ||
* CallableProxyResponse exposes subset of express.Response object | ||
* to allow writing partial, streaming responses back to the client. | ||
*/ | ||
export interface CallableProxyResponse { | ||
write: express.Response["write"]; | ||
acceptsStreaming: boolean; | ||
} | ||
/** | ||
* The set of Firebase Functions status codes. The codes are the same at the | ||
@@ -91,0 +99,0 @@ * ones exposed by {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md | gRPC}. |
@@ -388,4 +388,4 @@ "use strict"; | ||
/** @internal */ | ||
function onCallHandler(options, handler) { | ||
const wrapped = wrapOnCallHandler(options, handler); | ||
function onCallHandler(options, handler, version) { | ||
const wrapped = wrapOnCallHandler(options, handler, version); | ||
return (req, res) => { | ||
@@ -401,4 +401,7 @@ return new Promise((resolve) => { | ||
exports.onCallHandler = onCallHandler; | ||
function encodeSSE(data) { | ||
return `data: ${JSON.stringify(data)}\n`; | ||
} | ||
/** @internal */ | ||
function wrapOnCallHandler(options, handler) { | ||
function wrapOnCallHandler(options, handler, version) { | ||
return async (req, res) => { | ||
@@ -418,3 +421,3 @@ try { | ||
// and was not compatible with how monorepos separate out packages (see https://github.com/firebase/firebase-tools/issues/5210). | ||
if ((0, debug_1.isDebugFeatureEnabled)("skipTokenVerification") && handler.length === 2) { | ||
if ((0, debug_1.isDebugFeatureEnabled)("skipTokenVerification") && version === "gcfv1") { | ||
const authContext = context.rawRequest.header(exports.CALLABLE_AUTH_HEADER); | ||
@@ -458,5 +461,10 @@ if (authContext) { | ||
} | ||
const acceptsStreaming = req.header("accept") === "text/event-stream"; | ||
if (acceptsStreaming && version === "gcfv1") { | ||
// streaming responses are not supported in v1 callable | ||
throw new HttpsError("invalid-argument", "Unsupported Accept header 'text/event-stream'"); | ||
} | ||
const data = decode(req.body.data); | ||
let result; | ||
if (handler.length === 2) { | ||
if (version === "gcfv1") { | ||
result = await handler(data, context); | ||
@@ -469,5 +477,20 @@ } | ||
}; | ||
// TODO: set up optional heartbeat | ||
const responseProxy = { | ||
write(chunk) { | ||
if (acceptsStreaming) { | ||
const formattedData = encodeSSE({ message: chunk }); | ||
return res.write(formattedData); | ||
} | ||
// if client doesn't accept sse-protocol, response.write() is no-op. | ||
}, | ||
acceptsStreaming, | ||
}; | ||
if (acceptsStreaming) { | ||
// SSE always responds with 200 | ||
res.status(200); | ||
} | ||
// For some reason the type system isn't picking up that the handler | ||
// is a one argument function. | ||
result = await handler(arg); | ||
result = await handler(arg, responseProxy); | ||
} | ||
@@ -478,3 +501,9 @@ // Encode the result as JSON to preserve types like Dates. | ||
const responseBody = { result }; | ||
res.status(200).send(responseBody); | ||
if (acceptsStreaming) { | ||
res.write(encodeSSE(responseBody)); | ||
res.end(); | ||
} | ||
else { | ||
res.status(200).send(responseBody); | ||
} | ||
} | ||
@@ -490,5 +519,10 @@ catch (err) { | ||
const body = { error: httpErr.toJSON() }; | ||
res.status(status).send(body); | ||
if (version === "gcfv2" && req.header("accept") === "text/event-stream") { | ||
res.send(encodeSSE(body)); | ||
} | ||
else { | ||
res.status(status).send(body); | ||
} | ||
} | ||
}; | ||
} |
@@ -73,5 +73,4 @@ "use strict"; | ||
function _onCallWithOptions(handler, options) { | ||
// onCallHandler sniffs the function length of the passed-in callback | ||
// and the user could have only tried to listen to data. Wrap their handler | ||
// in another handler to avoid accidentally triggering the v2 API | ||
// fix the length of handler to make the call to handler consistent | ||
// in the onCallHandler | ||
const fixedLen = (data, context) => { | ||
@@ -84,3 +83,3 @@ return (0, onInit_1.withInit)(handler)(data, context); | ||
cors: { origin: true, methods: "POST" }, | ||
}, fixedLen)); | ||
}, fixedLen, "gcfv1")); | ||
func.__trigger = { | ||
@@ -87,0 +86,0 @@ labels: {}, |
import * as express from "express"; | ||
import { ResetValue } from "../../common/options"; | ||
import { CallableRequest, FunctionsErrorCode, HttpsError, Request } from "../../common/providers/https"; | ||
import { CallableRequest, CallableProxyResponse, FunctionsErrorCode, HttpsError, Request } from "../../common/providers/https"; | ||
import { ManifestEndpoint } from "../../runtime/manifest"; | ||
@@ -178,3 +178,3 @@ import { GlobalOptions, SupportedRegion } from "../options"; | ||
*/ | ||
export declare function onCall<T = any, Return = any | Promise<any>>(opts: CallableOptions, handler: (request: CallableRequest<T>) => Return): CallableFunction<T, Return extends Promise<unknown> ? Return : Promise<Return>>; | ||
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>>; | ||
/** | ||
@@ -185,2 +185,2 @@ * Declares a callable method for clients to call using a Firebase SDK. | ||
*/ | ||
export declare function onCall<T = any, Return = any | Promise<any>>(handler: (request: CallableRequest<T>) => Return): CallableFunction<T, Return extends Promise<unknown> ? Return : Promise<Return>>; | ||
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>>; |
@@ -133,5 +133,4 @@ "use strict"; | ||
} | ||
// onCallHandler sniffs the function length to determine which API to present. | ||
// fix the length to prevent api versions from being mismatched. | ||
const fixedLen = (req) => (0, onInit_1.withInit)(handler)(req); | ||
// fix the length of handler to make the call to handler consistent | ||
const fixedLen = (req, resp) => (0, onInit_1.withInit)(handler)(req, resp); | ||
let func = (0, https_1.onCallHandler)({ | ||
@@ -141,4 +140,4 @@ cors: { origin, methods: "POST" }, | ||
consumeAppCheckToken: opts.consumeAppCheckToken, | ||
}, fixedLen); | ||
func = (0, trace_1.wrapTraceContext)(func); | ||
}, fixedLen, "gcfv2"); | ||
func = (0, trace_1.wrapTraceContext)((0, onInit_1.withInit)(func)); | ||
Object.defineProperty(func, "__trigger", { | ||
@@ -145,0 +144,0 @@ get: () => { |
{ | ||
"name": "firebase-functions", | ||
"version": "6.1.0", | ||
"version": "6.1.1", | ||
"description": "Firebase SDK for Cloud Functions", | ||
@@ -302,3 +302,3 @@ "keywords": [ | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"firebase-admin": "^12.1.0", | ||
"firebase-admin": "^13.0.0", | ||
"js-yaml": "^3.13.1", | ||
@@ -323,3 +323,3 @@ "jsdom": "^16.2.1", | ||
"peerDependencies": { | ||
"firebase-admin": "^11.10.0 || ^12.0.0" | ||
"firebase-admin": "^11.10.0 || ^12.0.0 || ^13.0.0" | ||
}, | ||
@@ -326,0 +326,0 @@ "engines": { |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
891276
0.19%19355
0.21%