@vercube/serverless 0.0.22 → 0.0.24

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.
package/README.md CHANGED
@@ -3,10 +3,10 @@
3
3
  <br>
4
4
  <br>
5
5
 
6
- # @vercube/serverless
7
-
8
- Serverless deployment adapters for Vercube applications
9
-
6
+ # @vercube/serverless
7
+
8
+ Serverless deployment adapters for Vercube applications
9
+
10
10
  <a href="https://www.npmjs.com/package/@vercube/serverless">
11
11
  <img src="https://img.shields.io/npm/v/%40vercube%2Fserverless?style=for-the-badge&logo=npm&color=%23767eff" alt="npm"/>
12
12
  </a>
@@ -16,6 +16,9 @@
16
16
  <a href="https://github.com/vercube/vercube/blob/main/LICENSE" target="_blank">
17
17
  <img src="https://img.shields.io/npm/l/%40vercube%2Fserverless?style=for-the-badge&color=%23767eff" alt="License"/>
18
18
  </a>
19
+ <a href="https://codecov.io/gh/vercube/vercube" target="_blank">
20
+ <img src="https://img.shields.io/codecov/c/github/vercube/vercube?style=for-the-badge&color=%23767eff" alt="Coverage"/>
21
+ </a>
19
22
  <br/>
20
23
  <br/>
21
24
  </div>
@@ -31,10 +34,11 @@ The `@vercube/serverless` module provides unified, provider-agnostic adapters fo
31
34
  ### ✅ Key Features
32
35
 
33
36
  - **AWS Lambda Integration** - Full support for API Gateway v1 and v2
37
+ - **Azure Functions Integration** - Complete support for Azure Functions HTTP triggers
34
38
  - **Zero Configuration** - Works out-of-the-box with existing Vercube apps
35
39
  - **Type Safety** - Complete TypeScript support with proper type definitions
36
40
  - **Binary Support** - Automatic handling of binary content with base64 encoding
37
- - **Cookie Support** - Proper cookie handling for both API Gateway versions
41
+ - **Cookie Support** - Proper cookie handling for both API Gateway versions and Azure Functions
38
42
  - **Error Handling** - Robust error handling and validation
39
43
  - **Performance Optimized** - Efficient request/response conversion
40
44
 
@@ -63,6 +67,30 @@ const app = createApp();
63
67
  export const handler = toServerlessHandler(app);
64
68
  ```
65
69
 
70
+ ### Azure Functions Integration
71
+
72
+ Deploy your Vercube application to Azure Functions:
73
+
74
+ ```ts
75
+ // httpTrigger.ts
76
+ import { createApp } from '@vercube/core';
77
+ import { toServerlessHandler } from '@vercube/serverless/azure-functions';
78
+ import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';
79
+
80
+ const vercubeApp = createApp();
81
+ const handler = toServerlessHandler(vercubeApp);
82
+
83
+ export async function httpTrigger(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
84
+ return await handler(request);
85
+ }
86
+
87
+ app.http('httpTrigger', {
88
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],
89
+ authLevel: 'anonymous',
90
+ handler: httpTrigger,
91
+ });
92
+ ```
93
+
66
94
  ### Serverless Framework Configuration
67
95
 
68
96
  ```yaml
@@ -98,6 +126,16 @@ Full support for AWS Lambda with API Gateway integration:
98
126
  - **Cookies** - Proper cookie handling for both API Gateway versions
99
127
  - **Headers** - Complete header conversion and processing
100
128
 
129
+ ### Azure Functions
130
+
131
+ Complete support for Azure Functions HTTP triggers:
132
+
133
+ - **HTTP Triggers** - Full compatibility with Azure Functions HTTP triggers
134
+ - **Request/Response Conversion** - Seamless conversion between Azure Functions and web standards
135
+ - **Cookie Support** - Proper handling of Set-Cookie headers and cookie parsing
136
+ - **Headers** - Complete header conversion and processing
137
+ - **Streaming Support** - Efficient handling of request/response bodies with AsyncIterableIterator
138
+
101
139
  ---
102
140
 
103
141
  ## 📋 API Reference
@@ -107,13 +145,17 @@ Full support for AWS Lambda with API Gateway integration:
107
145
  Converts a Vercube App instance into a serverless handler function.
108
146
 
109
147
  **Parameters:**
148
+
110
149
  - `app` - The Vercube App instance that will handle requests
111
150
 
112
151
  **Returns:**
152
+
113
153
  - An async function that accepts serverless events and returns platform-specific responses
114
154
 
115
- **Example:**
155
+ **Examples:**
156
+
116
157
  ```ts
158
+ // AWS Lambda
117
159
  import { createApp } from '@vercube/core';
118
160
  import { toServerlessHandler } from '@vercube/serverless/aws-lambda';
119
161
 
@@ -121,6 +163,15 @@ const app = createApp();
121
163
  export const handler = toServerlessHandler(app);
122
164
  ```
123
165
 
166
+ ```ts
167
+ // Azure Functions
168
+ import { createApp } from '@vercube/core';
169
+ import { toServerlessHandler } from '@vercube/serverless/azure-functions';
170
+
171
+ const app = createApp();
172
+ export const handler = toServerlessHandler(app);
173
+ ```
174
+
124
175
  ---
125
176
 
126
177
  ## 🔄 Request/Response Conversion
@@ -128,16 +179,18 @@ export const handler = toServerlessHandler(app);
128
179
  The serverless adapters handle automatic conversion between platform-specific events and standard web requests:
129
180
 
130
181
  ### Request Conversion
182
+
131
183
  - **HTTP Method** - Extracted from event properties
132
184
  - **URL Construction** - Built from path, query parameters, and headers
133
185
  - **Headers** - Converted to standard Headers object
134
186
  - **Body** - Properly decoded and converted to Request body
135
187
 
136
188
  ### Response Conversion
189
+
137
190
  - **Status Code** - Mapped from Response status
138
191
  - **Headers** - Converted to platform-specific format
139
- - **Body** - Encoded appropriately (text vs binary)
140
- - **Cookies** - Handled for both API Gateway versions
192
+ - **Body** - Encoded appropriately (text vs binary for AWS, AsyncIterableIterator for Azure)
193
+ - **Cookies** - Handled for both API Gateway versions and Azure Functions
141
194
 
142
195
  ---
143
196
 
@@ -146,7 +199,8 @@ The serverless adapters handle automatic conversion between platform-specific ev
146
199
  - **Streaming Support** - Efficient handling of large request/response bodies
147
200
  - **Memory Optimization** - Minimal memory footprint for serverless environments
148
201
  - **Cold Start Optimization** - Fast initialization and request processing
149
- - **Binary Content** - Optimized base64 encoding for binary responses
202
+ - **Binary Content** - Optimized base64 encoding for binary responses (AWS Lambda)
203
+ - **AsyncIterableIterator** - Efficient streaming for Azure Functions responses
150
204
 
151
205
  ---
152
206
 
@@ -160,8 +214,8 @@ import { toServerlessHandler } from '@vercube/serverless/aws-lambda';
160
214
 
161
215
  const app = createApp({
162
216
  logger: {
163
- level: 'debug'
164
- }
217
+ level: 'debug',
218
+ },
165
219
  });
166
220
 
167
221
  export const handler = toServerlessHandler(app);
@@ -180,13 +234,13 @@ Explore guides, API references, and best practices to master Vercube serverless
180
234
 
181
235
  This module is inspired by:
182
236
 
183
- * [Nitro AWS Lambda Preset](https://nitro.build/presets/aws-lambda)
184
- * [Hono AWS Lambda Adapter](https://hono.dev/guides/aws-lambda)
185
- * [Vercel Serverless Functions](https://vercel.com/docs/functions)
237
+ - [Nitro AWS Lambda Preset](https://nitro.build/presets/aws-lambda)
238
+ - [Hono AWS Lambda Adapter](https://hono.dev/guides/aws-lambda)
239
+ - [Vercel Serverless Functions](https://vercel.com/docs/functions)
240
+ - [hono-azurefunc-adapter](https://github.com/Marplex/hono-azurefunc-adapter)
186
241
 
187
242
  ---
188
243
 
189
244
  ## 🪪 License
190
245
 
191
246
  [MIT License](https://github.com/vercube/vercube/blob/main/LICENSE)
192
-
@@ -1,4 +1,4 @@
1
- import { ServerlessHandler } from "../../ServerlessTypes-lXnSKHNA.mjs";
1
+ import { ServerlessHandler } from "../../ServerlessTypes-WMcCL6v7.mjs";
2
2
  import { App } from "@vercube/core";
3
3
  import { APIGatewayProxyEvent, APIGatewayProxyEventV2 } from "aws-lambda";
4
4
 
@@ -1,15 +1,52 @@
1
+ import { getHeaderValue, toBuffer } from "../../streams-jCOA2riB.mjs";
1
2
  import { stringifyQuery } from "ufo";
2
3
 
3
- //#region src/Adapters/aws-lambda/Utils/Request.ts
4
+ //#region src/Utils/content-type.ts
5
+ const DEFAULT_CONTENT_TYPE$1 = "";
6
+ const TEXT_CONTENT_TYPE_PATTERNS = [
7
+ /^text\//,
8
+ /^application\/(json|javascript|xml|xml\+text|x-www-form-urlencoded)$/,
9
+ /^application\/.*\+json$/,
10
+ /^application\/.*\+xml$/,
11
+ /utf-?8/
12
+ ];
13
+ /**
14
+ * Determines if a content type should be treated as text content.
15
+ *
16
+ * This function uses a set of patterns to identify text-based content types:
17
+ * - Content types starting with "text/" (e.g., text/plain, text/html)
18
+ * - JavaScript, JSON, or XML content types
19
+ * - Content types containing UTF-8 encoding specification
20
+ *
21
+ * The function performs case-insensitive matching to handle various content type
22
+ * formats and specifications.
23
+ *
24
+ * @param contentType - The content type string to evaluate (e.g., "text/plain", "application/json")
25
+ * @returns True if the content type should be treated as text, false otherwise
26
+ */
27
+ function isTextType(contentType = DEFAULT_CONTENT_TYPE$1) {
28
+ if (!contentType) return false;
29
+ return TEXT_CONTENT_TYPE_PATTERNS.some((pattern) => pattern.test(contentType));
30
+ }
31
+
32
+ //#endregion
33
+ //#region src/Utils/constants.ts
4
34
  const DEFAULT_METHOD = "GET";
5
35
  const DEFAULT_HOSTNAME = ".";
6
36
  const HTTP_PROTOCOL = "http";
7
37
  const HTTPS_PROTOCOL = "https";
38
+ const DEFAULT_BODY = "";
39
+ const DEFAULT_CONTENT_TYPE = "";
40
+ const UTF8_ENCODING = "utf8";
41
+ const BASE64_ENCODING = "base64";
8
42
  const HEADER_KEYS = {
9
43
  HOST: ["host", "Host"],
10
44
  X_FORWARDED_PROTO: ["X-Forwarded-Proto", "x-forwarded-proto"],
11
45
  COOKIE: "cookie"
12
46
  };
47
+
48
+ //#endregion
49
+ //#region src/Adapters/aws-lambda/Utils/Request.ts
13
50
  /**
14
51
  * Type guard to check if an event is APIGatewayProxyEventV2
15
52
  */
@@ -23,23 +60,12 @@ function isV1Event(event) {
23
60
  return "httpMethod" in event;
24
61
  }
25
62
  /**
26
- * Safely gets a header value with case-insensitive fallback
27
- */
28
- function getHeaderValue(headers, keys) {
29
- if (!headers) return void 0;
30
- for (const key of keys) {
31
- const value = headers[key];
32
- if (value !== void 0) return value;
33
- }
34
- return void 0;
35
- }
36
- /**
37
63
  * Extracts the HTTP method from an API Gateway event.
38
- *
64
+ *
39
65
  * Handles both v1 and v2 event formats:
40
66
  * - v1: Uses `httpMethod` property
41
67
  * - v2: Uses `requestContext.http.method` property
42
- *
68
+ *
43
69
  * @param event - The AWS API Gateway event object (v1 or v2 format)
44
70
  * @returns The HTTP method as a string, defaults to 'GET' if not found
45
71
  */
@@ -50,13 +76,13 @@ function getEventMethod(event) {
50
76
  }
51
77
  /**
52
78
  * Constructs a complete URL from an API Gateway event.
53
- *
79
+ *
54
80
  * Builds the URL by combining:
55
81
  * - Protocol (http/https based on X-Forwarded-Proto header)
56
82
  * - Hostname (from headers or requestContext)
57
83
  * - Path (from event path properties)
58
84
  * - Query string (from query parameters)
59
- *
85
+ *
60
86
  * @param event - The AWS API Gateway event object (v1 or v2 format)
61
87
  * @returns A complete URL object
62
88
  */
@@ -88,16 +114,15 @@ function getEventPath(event) {
88
114
  * Determines the protocol from the event headers
89
115
  */
90
116
  function getEventProtocol(event) {
91
- const forwardedProto = getHeaderValue(event.headers, HEADER_KEYS.X_FORWARDED_PROTO);
92
- return forwardedProto === HTTP_PROTOCOL ? HTTP_PROTOCOL : HTTPS_PROTOCOL;
117
+ return getHeaderValue(event.headers, HEADER_KEYS.X_FORWARDED_PROTO) === HTTP_PROTOCOL ? HTTP_PROTOCOL : HTTPS_PROTOCOL;
93
118
  }
94
119
  /**
95
120
  * Extracts and formats query parameters from an API Gateway event.
96
- *
121
+ *
97
122
  * Handles both v1 and v2 event formats:
98
123
  * - v2: Uses `rawQueryString` if available
99
124
  * - v1: Combines `queryStringParameters` and `multiValueQueryStringParameters`
100
- *
125
+ *
101
126
  * @param event - The AWS API Gateway event object (v1 or v2 format)
102
127
  * @returns A formatted query string (without the leading '?')
103
128
  */
@@ -113,11 +138,11 @@ function getEventQuery(event) {
113
138
  }
114
139
  /**
115
140
  * Converts API Gateway event headers to a standard Headers object.
116
- *
141
+ *
117
142
  * Processes all headers from the event and handles cookies specially:
118
143
  * - Sets all event headers in the Headers object
119
144
  * - Appends cookies from the event's cookies array (v2 format)
120
- *
145
+ *
121
146
  * @param event - The AWS API Gateway event object (v1 or v2 format)
122
147
  * @returns A Headers object with all event headers and cookies
123
148
  */
@@ -133,17 +158,17 @@ function getEventHeaders(event) {
133
158
  }
134
159
  /**
135
160
  * Extracts the request body from an API Gateway event.
136
- *
161
+ *
137
162
  * Handles different body formats:
138
163
  * - Returns undefined if no body is present
139
164
  * - Decodes base64-encoded bodies when `isBase64Encoded` is true
140
165
  * - Returns the raw body string for regular requests
141
- *
166
+ *
142
167
  * @param event - The AWS API Gateway event object (v1 or v2 format)
143
168
  * @returns The request body as BodyInit (string, Buffer, or undefined)
144
169
  */
145
170
  function getEventBody(event) {
146
- if (!event.body || event.body === null) return void 0;
171
+ if (!event.body || event.body === null) return;
147
172
  if (event.isBase64Encoded) try {
148
173
  return Buffer.from(event.body, "base64");
149
174
  } catch {
@@ -153,13 +178,13 @@ function getEventBody(event) {
153
178
  }
154
179
  /**
155
180
  * Converts an AWS API Gateway event to a standard Request object.
156
- *
181
+ *
157
182
  * This function handles both APIGatewayProxyEvent (v1) and APIGatewayProxyEventV2 (v2) formats,
158
183
  * extracting the HTTP method, URL, headers, and body to create a web-standard Request object.
159
- *
184
+ *
160
185
  * This file is highly inspired by the `awsRequest` from `nitro`
161
186
  * @see https://github.com/nitrojs/nitro/blob/v3/src/presets/aws-lambda/runtime/_utils.ts
162
- *
187
+ *
163
188
  * @param event - The AWS API Gateway event object (v1 or v2 format)
164
189
  * @returns A new Request object with the extracted event data
165
190
  * @throws {Error} If the event is invalid or missing required properties
@@ -179,29 +204,18 @@ function convertEventToRequest(event) {
179
204
 
180
205
  //#endregion
181
206
  //#region src/Adapters/aws-lambda/Utils/Response.ts
182
- const DEFAULT_BODY = "";
183
- const DEFAULT_CONTENT_TYPE = "";
184
- const UTF8_ENCODING = "utf8";
185
- const BASE64_ENCODING = "base64";
186
- const TEXT_CONTENT_TYPE_PATTERNS = [
187
- /^text\//,
188
- /^application\/(json|javascript|xml|xml\+text|x-www-form-urlencoded)$/,
189
- /^application\/.*\+json$/,
190
- /^application\/.*\+xml$/,
191
- /utf-?8/
192
- ];
193
207
  /**
194
208
  * Converts a standard web Response object to AWS API Gateway compatible response format.
195
- *
209
+ *
196
210
  * This function transforms the Response headers and cookies into the format expected by
197
211
  * AWS API Gateway proxy integrations. It handles both v1 and v2 API Gateway formats:
198
212
  * - v1: Uses `multiValueHeaders` for cookies
199
213
  * - v2: Uses `cookies` array for cookies
200
- *
214
+ *
201
215
  * The function processes all response headers, converting them to the appropriate format
202
216
  * for AWS Lambda integration. Headers with multiple values are joined with commas,
203
217
  * and cookies are handled specially for both API Gateway versions.
204
- *
218
+ *
205
219
  * @param response - The standard web Response object to convert
206
220
  * @returns An object containing headers and cookies in AWS API Gateway format
207
221
  * @throws {Error} If the response object is invalid or headers cannot be processed
@@ -224,21 +238,21 @@ function convertResponseToAWSResponse(response) {
224
238
  }
225
239
  /**
226
240
  * Converts a Response body to AWS API Gateway compatible format with proper encoding.
227
- *
241
+ *
228
242
  * AWS Lambda proxy integrations require special handling for binary content:
229
243
  * - Text content types are returned as UTF-8 strings
230
244
  * - Binary content types are base64 encoded with the `isBase64Encoded` flag set
231
- *
245
+ *
232
246
  * This function determines the appropriate encoding based on the response's content-type
233
247
  * header and converts the body accordingly. It supports both text and binary content
234
248
  * types, ensuring compatibility with API Gateway's payload encoding requirements.
235
- *
249
+ *
236
250
  * Binary media types should be configured as * in API Gateway settings.
237
251
  * @see https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html
238
- *
252
+ *
239
253
  * This function is heavily inspired by the `awsResponseBody` from `nitro`
240
254
  * @see https://github.com/nitrojs/nitro/blob/v3/src/presets/aws-lambda/runtime/_utils.ts
241
- *
255
+ *
242
256
  * @param response - The standard web Response object containing the body to convert
243
257
  * @returns A promise that resolves to an object with the encoded body and encoding flag
244
258
  * @throws {Error} If the response body cannot be read or converted
@@ -259,80 +273,30 @@ async function convertBodyToAWSResponse(response) {
259
273
  throw new Error(`Failed to convert response body: ${errorMessage}`);
260
274
  }
261
275
  }
262
- /**
263
- * Determines if a content type should be treated as text content.
264
- *
265
- * This function uses a set of patterns to identify text-based content types:
266
- * - Content types starting with "text/" (e.g., text/plain, text/html)
267
- * - JavaScript, JSON, or XML content types
268
- * - Content types containing UTF-8 encoding specification
269
- *
270
- * The function performs case-insensitive matching to handle various content type
271
- * formats and specifications.
272
- *
273
- * @param contentType - The content type string to evaluate (e.g., "text/plain", "application/json")
274
- * @returns True if the content type should be treated as text, false otherwise
275
- */
276
- function isTextType(contentType = DEFAULT_CONTENT_TYPE) {
277
- if (!contentType) return false;
278
- return TEXT_CONTENT_TYPE_PATTERNS.some((pattern) => pattern.test(contentType));
279
- }
280
- /**
281
- * Converts a ReadableStream to a Buffer using Web Streams API.
282
- *
283
- * This function reads all chunks from a ReadableStream and concatenates them
284
- * into a single Buffer. It uses the modern Web Streams API with WritableStream
285
- * to efficiently process the stream data without blocking.
286
- *
287
- * The function handles stream errors gracefully and provides proper cleanup
288
- * of stream resources. It's designed to work with Response.body streams
289
- * from fetch API responses.
290
- *
291
- * @param data - The ReadableStream to convert to a Buffer
292
- * @returns A promise that resolves to a Buffer containing all stream data
293
- * @throws {Error} If the stream cannot be read or an error occurs during processing
294
- */
295
- function toBuffer(data) {
296
- return new Promise((resolve, reject) => {
297
- const chunks = [];
298
- const writableStream = new WritableStream({
299
- write(chunk) {
300
- chunks.push(chunk);
301
- },
302
- close() {
303
- resolve(Buffer.concat(chunks));
304
- },
305
- abort(reason) {
306
- reject(/* @__PURE__ */ new Error("Stream aborted: " + String(reason)));
307
- }
308
- });
309
- data.pipeTo(writableStream).catch(reject);
310
- });
311
- }
312
276
 
313
277
  //#endregion
314
278
  //#region src/Adapters/aws-lambda/index.ts
315
279
  /**
316
280
  * Converts a Vercube App instance into an AWS Lambda handler function for API Gateway integration.
317
- *
281
+ *
318
282
  * This function creates a serverless handler that bridges between AWS API Gateway events and
319
283
  * the Vercube application framework. It handles the conversion of AWS Lambda events to standard
320
284
  * web Request objects, processes them through the Vercube app, and converts the responses back
321
285
  * to the format expected by AWS API Gateway.
322
- *
286
+ *
323
287
  * The handler supports both API Gateway v1 (APIGatewayProxyEvent) and v2 (APIGatewayProxyEventV2)
324
288
  * event formats, automatically detecting and handling the appropriate format. It processes:
325
289
  * - HTTP method, URL, headers, and body from the Lambda event
326
290
  * - Converts them to a standard web Request object
327
291
  * - Passes the request through the Vercube application
328
292
  * - Converts the Response back to AWS API Gateway format
329
- *
293
+ *
330
294
  * The returned handler function can be directly used as an AWS Lambda function handler,
331
295
  * providing seamless integration between AWS Lambda and Vercube applications.
332
- *
296
+ *
333
297
  * @param app - The Vercube App instance that will handle the requests
334
298
  * @returns An async function that accepts AWS API Gateway events and returns API Gateway responses
335
- *
299
+ *
336
300
  * @see {@link convertEventToRequest} For details on event to request conversion
337
301
  * @see {@link convertResponseToAWSResponse} For details on response header conversion
338
302
  * @see {@link convertBodyToAWSResponse} For details on response body conversion
@@ -0,0 +1,32 @@
1
+ import { ServerlessHandler } from "../../ServerlessTypes-WMcCL6v7.mjs";
2
+ import { App } from "@vercube/core";
3
+ import { HttpRequest } from "@azure/functions";
4
+
5
+ //#region src/Adapters/azure-functions/index.d.ts
6
+
7
+ /**
8
+ * Converts a Vercube App instance into an Azure Functions handler function for HTTP integration.
9
+ *
10
+ * This function creates a serverless handler that bridges between Azure Functions HTTP triggers
11
+ * and the Vercube application framework. It handles the conversion of Azure Functions HttpRequest
12
+ * objects to standard web Request objects, processes them through the Vercube app, and converts
13
+ * the responses back to the format expected by Azure Functions.
14
+ *
15
+ * The handler processes:
16
+ * - HTTP method, URL, headers, and body from the Azure Functions HttpRequest
17
+ * - Converts them to a standard web Request object
18
+ * - Passes the request through the Vercube application
19
+ * - Converts the Response back to Azure Functions HttpResponseInit format
20
+ *
21
+ * The returned handler function can be directly used as an Azure Functions HTTP trigger handler,
22
+ * providing seamless integration between Azure Functions and Vercube applications.
23
+ *
24
+ * @param app - The Vercube App instance that will handle the requests
25
+ * @returns An async function that accepts Azure Functions HttpRequest and returns HttpResponseInit
26
+ *
27
+ * @see {@link convertEventToRequest} For details on HttpRequest to Request conversion
28
+ * @see {@link convertResponseToAzureFunctionsResponse} For details on Response to HttpResponseInit conversion
29
+ */
30
+ declare function toServerlessHandler(app: App): ServerlessHandler<HttpRequest, any>;
31
+ //#endregion
32
+ export { toServerlessHandler };
@@ -0,0 +1,193 @@
1
+ import { headersToObject, streamToAsyncIterator } from "../../streams-jCOA2riB.mjs";
2
+
3
+ //#region src/Utils/cookies.ts
4
+ /**
5
+ * Parses a cookie string into a generic cookie object.
6
+ *
7
+ * This function parses a standard Set-Cookie header string and extracts all cookie attributes
8
+ * including name, value, path, sameSite, secure, httpOnly, domain, expires, and maxAge.
9
+ * It handles URL decoding of the cookie value and proper type conversion for boolean and
10
+ * numeric attributes.
11
+ *
12
+ * The function expects cookie strings in the format:
13
+ * "name=value; path=/; secure; httpOnly; sameSite=Strict; domain=example.com; expires=Wed, 09 Jun 2021 10:18:14 GMT; max-age=3600"
14
+ *
15
+ * @param cookieString - The Set-Cookie header string to parse
16
+ * @returns A GenericCookie object with all parsed attributes
17
+ * @throws {Error} If the cookie string format is invalid or cannot be parsed
18
+ */
19
+ function parseCookieString(cookieString) {
20
+ if (!cookieString || typeof cookieString !== "string") throw new Error("Invalid cookie string: must be a non-empty string");
21
+ const [nameValue, ...attributeParts] = cookieString.split(";");
22
+ const [nameRaw, encodedValue] = nameValue.split("=");
23
+ const name = nameRaw ? nameRaw.trim().toLowerCase() : "";
24
+ if (!name || encodedValue === void 0) throw new Error("Invalid cookie string: must contain a name and value separated by \"=\"");
25
+ const attributesArray = attributeParts.map((part) => part.split("=")).map(([key, value]) => [key.trim().toLowerCase(), value ?? "true"]);
26
+ const attrs = Object.fromEntries(attributesArray);
27
+ return {
28
+ name,
29
+ value: encodedValue ? decodeURIComponent(encodedValue) : "",
30
+ path: attrs["path"],
31
+ sameSite: attrs["samesite"],
32
+ secure: attrs["secure"] === "true",
33
+ httpOnly: attrs["httponly"] === "true",
34
+ domain: attrs["domain"],
35
+ expires: attrs["expires"] ? new Date(attrs["expires"]) : void 0,
36
+ maxAge: attrs["max-age"] ? Number.parseInt(attrs["max-age"], 10) : void 0
37
+ };
38
+ }
39
+ /**
40
+ * Extracts cookies from a Headers object and converts them to generic cookie format.
41
+ *
42
+ * This function processes the Set-Cookie headers from a standard web Response Headers object
43
+ * and converts them into generic cookie objects. It handles the case where no cookies are
44
+ * present by returning undefined.
45
+ *
46
+ * @param headers - The Headers object from a web Response
47
+ * @returns An array of GenericCookie objects, or undefined if no cookies are present
48
+ */
49
+ function cookiesFromHeaders$1(headers) {
50
+ const cookies = headers.getSetCookie();
51
+ if (cookies.length === 0) return void 0;
52
+ return cookies.map(parseCookieString);
53
+ }
54
+
55
+ //#endregion
56
+ //#region src/Adapters/azure-functions/Utils/Request.ts
57
+ /**
58
+ * Converts an Azure Functions HttpRequest to a standard web Request object.
59
+ *
60
+ * This function bridges the gap between Azure Functions HTTP triggers and the standard
61
+ * web Request API. It extracts the HTTP method, URL, headers, and body from the Azure
62
+ * Functions HttpRequest and creates a standard Request object that can be used with
63
+ * the Vercube application framework.
64
+ *
65
+ * The function handles:
66
+ * - HTTP method extraction from the request
67
+ * - URL preservation from the Azure Functions request
68
+ * - Header conversion from Azure Functions format to standard Headers
69
+ * - Body handling for non-GET/HEAD requests with proper duplex stream support
70
+ *
71
+ * This file is highly inspired by the `newRequestFromAzureFunctions` from `hono-azurefunc-adapter`
72
+ * @see https://github.com/Marplex/hono-azurefunc-adapter/blob/main/src/request.ts
73
+ *
74
+ * @param request - The Azure Functions HttpRequest object to convert
75
+ * @returns A standard web Request object compatible with the fetch API
76
+ * @throws {Error} If the request object is invalid or missing required properties
77
+ */
78
+ function convertEventToRequest(request) {
79
+ const hasBody = !["GET", "HEAD"].includes(request.method);
80
+ return new Request(request.url, {
81
+ method: request.method,
82
+ headers: headersToObject(request.headers),
83
+ ...hasBody ? {
84
+ body: request.body,
85
+ duplex: "half"
86
+ } : {}
87
+ });
88
+ }
89
+
90
+ //#endregion
91
+ //#region src/Adapters/azure-functions/Utils/Utils.ts
92
+ /**
93
+ * Extracts cookies from a Headers object and converts them to Azure Functions Cookie format.
94
+ *
95
+ * This function processes the Set-Cookie headers from a standard web Response Headers object
96
+ * and converts them into the Cookie format expected by Azure Functions. It handles the case
97
+ * where no cookies are present by returning undefined.
98
+ *
99
+ * @param headers - The Headers object from a web Response
100
+ * @returns An array of Cookie objects, or undefined if no cookies are present
101
+ */
102
+ function cookiesFromHeaders(headers) {
103
+ const genericCookies = cookiesFromHeaders$1(headers);
104
+ if (!genericCookies) return void 0;
105
+ return genericCookies.map(convertGenericCookieToAzure);
106
+ }
107
+ /**
108
+ * Converts a generic cookie object to Azure Functions Cookie format.
109
+ *
110
+ * @param genericCookie - The generic cookie object to convert
111
+ * @returns An Azure Functions Cookie object
112
+ */
113
+ function convertGenericCookieToAzure(genericCookie) {
114
+ return {
115
+ name: genericCookie.name,
116
+ value: genericCookie.value,
117
+ path: genericCookie.path,
118
+ sameSite: genericCookie.sameSite,
119
+ secure: genericCookie.secure,
120
+ httpOnly: genericCookie.httpOnly,
121
+ domain: genericCookie.domain,
122
+ expires: genericCookie.expires,
123
+ maxAge: genericCookie.maxAge
124
+ };
125
+ }
126
+
127
+ //#endregion
128
+ //#region src/Adapters/azure-functions/Utils/Response.ts
129
+ /**
130
+ * Converts a standard web Response object to Azure Functions HttpResponseInit format.
131
+ *
132
+ * This function transforms a standard web Response object into the format expected by
133
+ * Azure Functions HTTP responses. It handles the conversion of headers, cookies, status
134
+ * code, and body to ensure compatibility with Azure Functions runtime.
135
+ *
136
+ * The function processes:
137
+ * - Response headers conversion to plain object format
138
+ * - Set-Cookie headers extraction and conversion to Azure Functions Cookie format
139
+ * - HTTP status code preservation
140
+ * - Response body conversion to AsyncIterableIterator for Azure Functions compatibility
141
+ *
142
+ * This file is highly inspired by the `newAzureFunctionsResponse` from `hono-azurefunc-adapter`
143
+ * @see https://github.com/Marplex/hono-azurefunc-adapter/blob/main/src/response.ts
144
+ *
145
+ * @param response - The standard web Response object to convert
146
+ * @returns An HttpResponseInit object compatible with Azure Functions
147
+ * @throws {Error} If the response object is invalid or missing required properties
148
+ */
149
+ function convertResponseToAzureFunctionsResponse(response) {
150
+ const headers = headersToObject(response.headers);
151
+ return {
152
+ cookies: cookiesFromHeaders(response.headers),
153
+ headers,
154
+ status: response.status,
155
+ body: streamToAsyncIterator(response.body)
156
+ };
157
+ }
158
+
159
+ //#endregion
160
+ //#region src/Adapters/azure-functions/index.ts
161
+ /**
162
+ * Converts a Vercube App instance into an Azure Functions handler function for HTTP integration.
163
+ *
164
+ * This function creates a serverless handler that bridges between Azure Functions HTTP triggers
165
+ * and the Vercube application framework. It handles the conversion of Azure Functions HttpRequest
166
+ * objects to standard web Request objects, processes them through the Vercube app, and converts
167
+ * the responses back to the format expected by Azure Functions.
168
+ *
169
+ * The handler processes:
170
+ * - HTTP method, URL, headers, and body from the Azure Functions HttpRequest
171
+ * - Converts them to a standard web Request object
172
+ * - Passes the request through the Vercube application
173
+ * - Converts the Response back to Azure Functions HttpResponseInit format
174
+ *
175
+ * The returned handler function can be directly used as an Azure Functions HTTP trigger handler,
176
+ * providing seamless integration between Azure Functions and Vercube applications.
177
+ *
178
+ * @param app - The Vercube App instance that will handle the requests
179
+ * @returns An async function that accepts Azure Functions HttpRequest and returns HttpResponseInit
180
+ *
181
+ * @see {@link convertEventToRequest} For details on HttpRequest to Request conversion
182
+ * @see {@link convertResponseToAzureFunctionsResponse} For details on Response to HttpResponseInit conversion
183
+ */
184
+ function toServerlessHandler(app) {
185
+ return async (event) => {
186
+ const request = convertEventToRequest(event);
187
+ const response = await app.fetch(request);
188
+ return convertResponseToAzureFunctionsResponse(response);
189
+ };
190
+ }
191
+
192
+ //#endregion
193
+ export { toServerlessHandler };
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { ServerlessHandler } from "./ServerlessTypes-lXnSKHNA.mjs";
1
+ import { ServerlessHandler } from "./ServerlessTypes-WMcCL6v7.mjs";
2
2
  export { ServerlessHandler };
@@ -0,0 +1,108 @@
1
+ //#region src/Utils/headers.ts
2
+ /**
3
+ * Converts a loopable header object to a plain JavaScript object.
4
+ *
5
+ * This function is used to convert header objects (which have a forEach method)
6
+ * into standard JavaScript objects with string keys and values. This is necessary for
7
+ * compatibility with the standard web Headers API and other parts of the application.
8
+ *
9
+ * @param input - A header-like object with a forEach method
10
+ * @returns A plain object with string keys and string values representing the headers
11
+ */
12
+ function headersToObject(input) {
13
+ const headers = {};
14
+ input.forEach((value, key) => {
15
+ headers[key] = value;
16
+ });
17
+ return headers;
18
+ }
19
+ /**
20
+ * Safely gets a header value with case-insensitive fallback.
21
+ *
22
+ * This utility function searches through a headers object using multiple possible
23
+ * key variations to find a header value. It's useful for handling headers that
24
+ * might have different casing across different platforms.
25
+ *
26
+ * @param headers - The headers object to search in
27
+ * @param keys - Array of possible header keys to try (in order of preference)
28
+ * @returns The header value if found, undefined otherwise
29
+ */
30
+ function getHeaderValue(headers, keys) {
31
+ if (!headers) return;
32
+ for (const key of keys) {
33
+ const value = headers[key];
34
+ if (value !== void 0) return value;
35
+ }
36
+ }
37
+
38
+ //#endregion
39
+ //#region src/Utils/streams.ts
40
+ /**
41
+ * Converts a ReadableStream to a Buffer using Web Streams API.
42
+ *
43
+ * This function reads all chunks from a ReadableStream and concatenates them
44
+ * into a single Buffer. It uses the modern Web Streams API with WritableStream
45
+ * to efficiently process the stream data without blocking.
46
+ *
47
+ * The function handles stream errors gracefully and provides proper cleanup
48
+ * of stream resources. It's designed to work with Response.body streams
49
+ * from fetch API responses.
50
+ *
51
+ * @param data - The ReadableStream to convert to a Buffer
52
+ * @returns A promise that resolves to a Buffer containing all stream data
53
+ * @throws {Error} If the stream cannot be read or an error occurs during processing
54
+ */
55
+ function toBuffer(data) {
56
+ return new Promise((resolve, reject) => {
57
+ const chunks = [];
58
+ const writableStream = new WritableStream({
59
+ write(chunk) {
60
+ chunks.push(chunk);
61
+ },
62
+ close() {
63
+ resolve(Buffer.concat(chunks));
64
+ },
65
+ abort(reason) {
66
+ reject(/* @__PURE__ */ new Error("Stream aborted: " + String(reason)));
67
+ }
68
+ });
69
+ data.pipeTo(writableStream).catch(reject);
70
+ });
71
+ }
72
+ /**
73
+ * Converts a ReadableStream to an AsyncIterableIterator for platform compatibility.
74
+ *
75
+ * Some serverless platforms expect response bodies to be AsyncIterableIterator<Uint8Array>.
76
+ * This function wraps a standard web ReadableStream to provide the required interface
77
+ * for serverless HTTP responses.
78
+ *
79
+ * The returned iterator provides:
80
+ * - `next()` method that reads chunks from the stream
81
+ * - `return()` method that releases the stream reader lock
82
+ * - Symbol.asyncIterator for async iteration support
83
+ *
84
+ * @param readable - The ReadableStream from a Response body, or null/undefined
85
+ * @returns An AsyncIterableIterator<Uint8Array> or null if no readable stream provided
86
+ */
87
+ function streamToAsyncIterator(readable) {
88
+ if (readable == null) return null;
89
+ const reader = readable.getReader();
90
+ return {
91
+ next() {
92
+ return reader.read();
93
+ },
94
+ return() {
95
+ reader.releaseLock();
96
+ return Promise.resolve({
97
+ done: true,
98
+ value: void 0
99
+ });
100
+ },
101
+ [Symbol.asyncIterator]() {
102
+ return this;
103
+ }
104
+ };
105
+ }
106
+
107
+ //#endregion
108
+ export { getHeaderValue, headersToObject, streamToAsyncIterator, toBuffer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercube/serverless",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "Serverless module for Vercube framework",
5
5
  "repository": {
6
6
  "type": "git",
@@ -15,7 +15,8 @@
15
15
  "exports": {
16
16
  ".": "./dist/index.mjs",
17
17
  "./package.json": "./package.json",
18
- "./aws-lambda": "./dist/Adapters/aws-lambda/index.mjs"
18
+ "./aws-lambda": "./dist/Adapters/aws-lambda/index.mjs",
19
+ "./azure-functions": "./dist/Adapters/azure-functions/index.mjs"
19
20
  },
20
21
  "types": "./dist/index.d.mts",
21
22
  "files": [
@@ -35,12 +36,12 @@
35
36
  ],
36
37
  "dependencies": {
37
38
  "ufo": "1.6.1",
38
- "@vercube/core": "0.0.22",
39
- "@vercube/di": "0.0.22"
39
+ "@vercube/core": "0.0.24",
40
+ "@vercube/di": "0.0.24"
40
41
  },
41
42
  "devDependencies": {
42
- "@types/aws-lambda": "8.10.152",
43
- "@azure/functions": "4.7.2-preview"
43
+ "@azure/functions": "4.8.0",
44
+ "@types/aws-lambda": "8.10.152"
44
45
  },
45
46
  "publishConfig": {
46
47
  "access": "public"