@hulla/api-google-cloud-functions 0.0.0-stage → 2.0.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.
@@ -0,0 +1,30 @@
1
+ import { FetchServerErrorInput } from "@hulla/api/fetch";
2
+ import { Awaitable, ServerAdapter, ServerContextInput, ServerExecutableFor } from "@hulla/api/server";
3
+ import { HttpFunction, Request as Request$1, Response as Response$1 } from "@google-cloud/functions-framework";
4
+ import { Contract } from "@hulla/api";
5
+ //#region src/index.d.ts
6
+ type GoogleCloudFunction = HttpFunction;
7
+ type GoogleCloudFunctionsRequest = Request$1;
8
+ type GoogleCloudFunctionsResponse = Response$1;
9
+ type GoogleCloudFunctionsAdapterContext = {
10
+ /** The Web request consumed by the shared Fetch executor. */
11
+ readonly request: Request;
12
+ /** The native Functions Framework request, including `rawBody` and execution metadata. */
13
+ readonly googleRequest: GoogleCloudFunctionsRequest;
14
+ readonly response: GoogleCloudFunctionsResponse;
15
+ };
16
+ type GoogleCloudFunctionsContextInput<ContractType extends Contract> = ServerContextInput<ContractType> & GoogleCloudFunctionsAdapterContext;
17
+ type GoogleCloudFunctionsServerErrorInput = Omit<FetchServerErrorInput<GoogleCloudFunctionsAdapterContext>, 'handlerContext'> & GoogleCloudFunctionsAdapterContext;
18
+ type GoogleCloudFunctionsAdapterOptions = {
19
+ readonly onError?: (input: GoogleCloudFunctionsServerErrorInput) => Awaitable<Response | undefined | void>;
20
+ };
21
+ type GoogleCloudFunctionsMountOptions = GoogleCloudFunctionsAdapterOptions;
22
+ type GoogleCloudFunctionsMount = {
23
+ <const ContractType extends Contract, const Context extends object>(implementation: ServerExecutableFor<ContractType, Context, 'google-cloud-functions', GoogleCloudFunctionsAdapterContext>, options?: GoogleCloudFunctionsMountOptions): GoogleCloudFunction;
24
+ };
25
+ type GoogleCloudFunctionsAdapter = ServerAdapter<'google-cloud-functions', GoogleCloudFunctionsAdapterContext> & {
26
+ readonly mount: GoogleCloudFunctionsMount;
27
+ };
28
+ declare function googleCloudFunctionsAdapter(options?: GoogleCloudFunctionsAdapterOptions): GoogleCloudFunctionsAdapter;
29
+ //#endregion
30
+ export { GoogleCloudFunction, GoogleCloudFunctionsAdapter, GoogleCloudFunctionsAdapterContext, GoogleCloudFunctionsAdapterOptions, GoogleCloudFunctionsContextInput, GoogleCloudFunctionsMountOptions, GoogleCloudFunctionsRequest, GoogleCloudFunctionsResponse, GoogleCloudFunctionsServerErrorInput, googleCloudFunctionsAdapter };
package/dist/index.js ADDED
@@ -0,0 +1,135 @@
1
+ import { createFetchHandler } from "@hulla/api/fetch";
2
+ import { createServerAdapter } from "@hulla/api/server";
3
+
4
+ //#region src/index.ts
5
+ function getFirstHeaderValue(value) {
6
+ return typeof value === "string" ? value : value?.[0];
7
+ }
8
+ function createRequestHeaders(request) {
9
+ const headers = new Headers();
10
+ for (const [name, value] of Object.entries(request.headers)) {
11
+ if (name.startsWith(":")) continue;
12
+ if (Array.isArray(value)) for (const item of value) headers.append(name, item);
13
+ else if (value !== void 0) headers.set(name, value);
14
+ }
15
+ return headers;
16
+ }
17
+ function createRequestUrl(request) {
18
+ const originalUrl = request.originalUrl || request.url || "/";
19
+ if (/^https?:\/\//i.test(originalUrl)) return originalUrl;
20
+ return `${getFirstHeaderValue(request.headers["x-forwarded-proto"])?.split(",")[0]?.trim() || request.protocol || "http"}://${request.get?.("host") || request.headers.host || "localhost"}${originalUrl.startsWith("/") ? originalUrl : `/${originalUrl}`}`;
21
+ }
22
+ function getRequestBody(request) {
23
+ if (request.rawBody !== void 0) return request.rawBody;
24
+ const body = request.body;
25
+ if (body === void 0 || body === null) return void 0;
26
+ if (typeof body === "string" || body instanceof ArrayBuffer || body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams || body instanceof Uint8Array) return body;
27
+ return JSON.stringify(body);
28
+ }
29
+ function createWebRequest(request, method) {
30
+ const headers = createRequestHeaders(request);
31
+ const body = method === "GET" || method === "HEAD" ? void 0 : getRequestBody(request);
32
+ if (request.rawBody === void 0 && body !== void 0) headers.delete("content-length");
33
+ const init = {
34
+ method,
35
+ headers,
36
+ ...body === void 0 ? {} : { body },
37
+ ...request.abortController === void 0 ? {} : { signal: request.abortController.signal }
38
+ };
39
+ return new Request(createRequestUrl(request), init);
40
+ }
41
+ function waitForDrain(response) {
42
+ return new Promise((resolve, reject) => {
43
+ const onDrain = () => {
44
+ cleanup();
45
+ resolve();
46
+ };
47
+ const onClose = () => {
48
+ cleanup();
49
+ reject(/* @__PURE__ */ new Error("The Google Cloud Functions response closed early."));
50
+ };
51
+ const cleanup = () => {
52
+ response.off("drain", onDrain);
53
+ response.off("close", onClose);
54
+ };
55
+ response.once("drain", onDrain);
56
+ response.once("close", onClose);
57
+ });
58
+ }
59
+ function setResponseHeaders(source, response) {
60
+ const setCookies = source.getSetCookie();
61
+ source.forEach((value, name) => {
62
+ if (name !== "set-cookie") response.setHeader(name, value);
63
+ });
64
+ if (setCookies.length > 0) response.setHeader("set-cookie", setCookies);
65
+ }
66
+ async function writeWebResponse(source, response, suppressBody) {
67
+ response.statusCode = source.status;
68
+ setResponseHeaders(source.headers, response);
69
+ if (suppressBody || source.body === null) {
70
+ await source.body?.cancel();
71
+ response.end();
72
+ return;
73
+ }
74
+ const reader = source.body.getReader();
75
+ try {
76
+ while (true) {
77
+ const chunk = await reader.read();
78
+ if (chunk.done) break;
79
+ if (!response.write(chunk.value)) await waitForDrain(response);
80
+ }
81
+ response.end();
82
+ } catch (error) {
83
+ await reader.cancel(error).catch(() => void 0);
84
+ throw error;
85
+ } finally {
86
+ reader.releaseLock();
87
+ }
88
+ }
89
+ function failTransport(error, response) {
90
+ if (response.writableEnded) return;
91
+ if (response.headersSent) {
92
+ response.destroy(error instanceof Error ? error : void 0);
93
+ return;
94
+ }
95
+ response.statusCode = 500;
96
+ response.setHeader("content-type", "text/plain; charset=utf-8");
97
+ response.end("Internal Server Error");
98
+ }
99
+ function createGoogleCloudFunctionsHandler(implementation, options) {
100
+ const handler = createFetchHandler(implementation, {
101
+ contextAdapter: "google-cloud-functions",
102
+ contextInput: (_request, context) => context,
103
+ ...options.onError === void 0 ? {} : { onError: ({ handlerContext, ...input }) => options.onError?.({
104
+ ...input,
105
+ googleRequest: handlerContext.googleRequest,
106
+ response: handlerContext.response
107
+ }) }
108
+ });
109
+ return async (request, response) => {
110
+ try {
111
+ const webRequest = createWebRequest(request, request.method === "HEAD" ? "GET" : request.method);
112
+ await writeWebResponse(await handler(webRequest, {
113
+ googleRequest: request,
114
+ response
115
+ }), response, request.method === "HEAD");
116
+ } catch (error) {
117
+ failTransport(error, response);
118
+ }
119
+ };
120
+ }
121
+ let googleCloudFunctionsAdapterValue;
122
+ function createGoogleCloudFunctionsAdapter(defaults) {
123
+ return createServerAdapter("google-cloud-functions", { mount: (implementation, options) => createGoogleCloudFunctionsHandler(implementation, options === void 0 ? defaults : {
124
+ ...defaults,
125
+ ...options
126
+ }) });
127
+ }
128
+ function googleCloudFunctionsAdapter(options) {
129
+ if (options !== void 0) return createGoogleCloudFunctionsAdapter({ ...options });
130
+ googleCloudFunctionsAdapterValue ??= createGoogleCloudFunctionsAdapter({});
131
+ return googleCloudFunctionsAdapterValue;
132
+ }
133
+
134
+ //#endregion
135
+ export { googleCloudFunctionsAdapter };
package/package.json CHANGED
@@ -1,6 +1,46 @@
1
1
  {
2
2
  "name": "@hulla/api-google-cloud-functions",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "2.0.0",
4
+ "description": "Google Cloud Run functions HTTP adapter for @hulla/api",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/hulladev/api.git",
9
+ "directory": "packages/google-cloud-functions"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "type": "module",
15
+ "sideEffects": false,
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ }
21
+ },
22
+ "publishConfig": {
23
+ "access": "public",
24
+ "registry": "https://registry.npmjs.org/"
25
+ },
26
+ "scripts": {
27
+ "build": "tsdown",
28
+ "clean": "rm -rf dist coverage",
29
+ "test": "bun run build && vitest run",
30
+ "test:coverage": "bun run build && vitest run --coverage",
31
+ "test:watch": "vitest",
32
+ "typecheck": "tsc --noEmit"
33
+ },
34
+ "devDependencies": {
35
+ "@google-cloud/functions-framework": "^5.0.5",
36
+ "@hulla/api": "2.0.0",
37
+ "zod": "^4.4.3"
38
+ },
39
+ "peerDependencies": {
40
+ "@google-cloud/functions-framework": "^5.0.0",
41
+ "@hulla/api": "^2.0.0"
42
+ },
43
+ "engines": {
44
+ "node": ">=22"
45
+ }
46
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.