acrylblends 0.7.2 → 0.7.4

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 (88) hide show
  1. package/docs/framework/README.md +4 -3
  2. package/docs/framework/UPSTREAM.md +1 -1
  3. package/docs/framework/getting-started/aws-lambda.md +115 -0
  4. package/docs/framework/getting-started/bun.md +1 -3
  5. package/docs/framework/getting-started/cloudflare-pages.md +53 -0
  6. package/docs/framework/getting-started/cloudflare-workers.md +67 -0
  7. package/docs/framework/getting-started/lambda-edge.md +41 -0
  8. package/docs/framework/getting-started/netlify.md +25 -0
  9. package/docs/framework/getting-started/other-runtimes.md +20 -9
  10. package/docs/framework/getting-started/service-worker.md +31 -0
  11. package/docs/framework/getting-started/vercel.md +28 -0
  12. package/docs/framework/guides/client-modules.md +34 -0
  13. package/docs/framework/guides/create-acrylblends.md +40 -2
  14. package/docs/framework/guides/faq.md +2 -2
  15. package/docs/framework/helpers/conninfo.md +1 -1
  16. package/docs/framework/helpers/ssg.md +1 -1
  17. package/docs/framework/helpers/streaming.md +120 -0
  18. package/docs/framework/helpers/websocket.md +2 -2
  19. package/docs/framework/middleware/builtin/ip-restriction.md +1 -1
  20. package/docs/framework/upstream.json +3 -3
  21. package/lib/aws-lambda.d.ts +324 -0
  22. package/lib/aws-lambda.js +357 -0
  23. package/lib/bun.d.ts +75 -0
  24. package/lib/bun.js +134 -0
  25. package/lib/cloudflare-pages.d.ts +58 -0
  26. package/lib/cloudflare-pages.js +78 -0
  27. package/lib/cloudflare-workers.d.ts +21 -0
  28. package/lib/cloudflare-workers.js +80 -0
  29. package/lib/index-CseujaYI.d.ts +31 -0
  30. package/lib/index-DaJtL7ci.d.ts +156 -0
  31. package/lib/lambda-edge.d.ts +92 -0
  32. package/lib/lambda-edge.js +105 -0
  33. package/lib/netlify.d.ts +29 -0
  34. package/lib/netlify.js +30 -0
  35. package/lib/node-server/register.js +1 -1
  36. package/lib/node-server/serve-static.js +1 -1
  37. package/lib/node-server.js +3 -3
  38. package/lib/serve-static-Cp5adzGo.js +77 -0
  39. package/lib/serve-static.d.ts +1 -30
  40. package/lib/serve-static.js +1 -76
  41. package/lib/service-worker.d.ts +48 -0
  42. package/lib/service-worker.js +38 -0
  43. package/lib/ssg.d.ts +1 -155
  44. package/lib/vercel.d.ts +10 -0
  45. package/lib/vercel.js +9 -0
  46. package/package.json +33 -1
  47. package/plugins/acryl-inspect/upstream.json +1 -1
  48. package/plugins/acryl-node-server/upstream.json +1 -1
  49. package/plugins/acryl-router/src/core/adapter/aws-lambda/conninfo.ts +73 -0
  50. package/plugins/acryl-router/src/core/adapter/aws-lambda/handler.ts +682 -0
  51. package/plugins/acryl-router/src/core/adapter/aws-lambda/index.ts +14 -0
  52. package/plugins/acryl-router/src/core/adapter/aws-lambda/types.ts +173 -0
  53. package/plugins/acryl-router/src/core/adapter/bun/conninfo.ts +43 -0
  54. package/plugins/acryl-router/src/core/adapter/bun/index.ts +11 -0
  55. package/plugins/acryl-router/src/core/adapter/bun/serve-static.ts +32 -0
  56. package/plugins/acryl-router/src/core/adapter/bun/server.ts +14 -0
  57. package/plugins/acryl-router/src/core/adapter/bun/ssg.ts +26 -0
  58. package/plugins/acryl-router/src/core/adapter/bun/websocket.ts +113 -0
  59. package/plugins/acryl-router/src/core/adapter/cloudflare-pages/conninfo.ts +26 -0
  60. package/plugins/acryl-router/src/core/adapter/cloudflare-pages/handler.ts +122 -0
  61. package/plugins/acryl-router/src/core/adapter/cloudflare-pages/index.ts +11 -0
  62. package/plugins/acryl-router/src/core/adapter/cloudflare-workers/conninfo.ts +7 -0
  63. package/plugins/acryl-router/src/core/adapter/cloudflare-workers/index.ts +8 -0
  64. package/plugins/acryl-router/src/core/adapter/cloudflare-workers/serve-static-module.ts +12 -0
  65. package/plugins/acryl-router/src/core/adapter/cloudflare-workers/serve-static.ts +41 -0
  66. package/plugins/acryl-router/src/core/adapter/cloudflare-workers/utils.ts +50 -0
  67. package/plugins/acryl-router/src/core/adapter/cloudflare-workers/websocket.ts +52 -0
  68. package/plugins/acryl-router/src/core/adapter/lambda-edge/conninfo.ts +15 -0
  69. package/plugins/acryl-router/src/core/adapter/lambda-edge/handler.ts +239 -0
  70. package/plugins/acryl-router/src/core/adapter/lambda-edge/index.ts +14 -0
  71. package/plugins/acryl-router/src/core/adapter/netlify/conninfo.ts +57 -0
  72. package/plugins/acryl-router/src/core/adapter/netlify/handler.ts +10 -0
  73. package/plugins/acryl-router/src/core/adapter/netlify/index.ts +6 -0
  74. package/plugins/acryl-router/src/core/adapter/netlify/mod.ts +2 -0
  75. package/plugins/acryl-router/src/core/adapter/service-worker/handler.ts +37 -0
  76. package/plugins/acryl-router/src/core/adapter/service-worker/index.ts +36 -0
  77. package/plugins/acryl-router/src/core/adapter/service-worker/types.ts +14 -0
  78. package/plugins/acryl-router/src/core/adapter/vercel/conninfo.ts +8 -0
  79. package/plugins/acryl-router/src/core/adapter/vercel/handler.ts +8 -0
  80. package/plugins/acryl-router/src/core/adapter/vercel/index.ts +7 -0
  81. package/plugins/acryl-router/upstream.json +2 -2
  82. package/plugins/acryl-router-http/upstream.json +1 -1
  83. package/plugins/acryl-state/upstream.json +1 -1
  84. package/plugins/desktop/upstream.json +1 -1
  85. package/plugins/desktop-web/upstream.json +1 -1
  86. package/templates/todo/upstream.json +1 -1
  87. package/lib/{serve-static-B14MF1Va.js → serve-static-B849HlX2.js} +1 -1
  88. package/lib/{transpile-CTnWof4z.js → transpile-QUK9b3eG.js} +1 -1
@@ -81,6 +81,126 @@ app.get('/sse', async (c) => {
81
81
  })
82
82
  ```
83
83
 
84
+ ## Cancelling work when the client leaves
85
+
86
+ A reader that closes the tab, loses the network or presses a Stop button should not leave your server working for nobody (a request to a paid model, a database scan). There are two signals, and they are not the same:
87
+
88
+ - **`c.req.raw.signal`** is the request's `AbortSignal`. It aborts when the client goes away. Pass it to anything that takes a `signal`, such as `fetch`, and the work stops with the client. This is the one to use for cancelling work.
89
+ - **`stream.onAbort(callback)`** and **`stream.aborted`** belong to the response stream. They report that the *reader of the response* was cancelled. Measured: when you call `app.request()` with a `signal` and abort it, `c.req.raw.signal` aborts and `stream.aborted` stays `false`; when the reader of the response body is cancelled, `stream.onAbort` fires and `c.req.raw.signal` does not. On a real connection the request signal is what fired when a browser aborted its `fetch`. Use `c.req.raw.signal` for the work, and `stream.onAbort` for cleanup of the stream itself.
90
+
91
+ An abort event that has already fired does not fire again, so check `signal.aborted` before you wait for it:
92
+
93
+ ```ts
94
+ const waitForAbort = (signal: AbortSignal) =>
95
+ signal.aborted
96
+ ? Promise.resolve()
97
+ : new Promise<void>((resolve) => signal.addEventListener('abort', () => resolve(), { once: true }))
98
+ ```
99
+
100
+ Keep what you have when the client leaves by saving it in a `finally` block: the stream callback runs to the end whether or not the client is still there.
101
+
102
+ ## Streaming an answer from a model
103
+
104
+ A chat answer that arrives token by token is a `streamSSE` response that reads a streaming upstream. This server route calls an OpenAI-compatible chat completions API (`stream: true`), forwards each piece as an event, and cancels the upstream request when the client leaves. The key comes from the environment and is never written anywhere.
105
+
106
+ ```ts
107
+ import { Acryl } from 'acrylblends'
108
+ import { streamSSE } from 'acrylblends/streaming'
109
+
110
+ /** The pieces of an OpenAI-compatible streaming answer: each `data:` line holds a JSON chunk, and `[DONE]` ends it. */
111
+ async function* modelPieces(messages: { role: string; content: string }[], signal: AbortSignal): AsyncGenerator<string> {
112
+ const key = process.env.DEEPSEEK_API_KEY
113
+ if (!key) throw new Error('DEEPSEEK_API_KEY is not set')
114
+ const base = process.env.DEEPSEEK_BASE_URL ?? 'https://api.deepseek.com'
115
+ const response = await fetch(`${base}/chat/completions`, {
116
+ method: 'POST',
117
+ headers: { 'content-type': 'application/json', authorization: `Bearer ${key}` },
118
+ body: JSON.stringify({ model: process.env.DEEPSEEK_MODEL ?? 'deepseek-chat', messages, stream: true }),
119
+ signal,
120
+ })
121
+ if (!response.ok || !response.body) throw new Error(`the model service answered ${response.status}`)
122
+ const decoder = new TextDecoder()
123
+ let buffer = ''
124
+ for await (const chunk of response.body) {
125
+ buffer += decoder.decode(chunk, { stream: true })
126
+ const lines = buffer.split('\n')
127
+ buffer = lines.pop() ?? ''
128
+ for (const line of lines) {
129
+ if (!line.startsWith('data:')) continue
130
+ const data = line.slice(5).trim()
131
+ if (data === '[DONE]') return
132
+ const piece = (JSON.parse(data) as { choices?: { delta?: { content?: string } }[] }).choices?.[0]?.delta?.content
133
+ if (piece) yield piece
134
+ }
135
+ }
136
+ }
137
+
138
+ const app = new Acryl()
139
+
140
+ app.post('/api/chat', async (c) => {
141
+ const { message } = await c.req.json<{ message: string }>()
142
+ const signal = c.req.raw.signal // aborts when the client goes away
143
+ return streamSSE(c, async (stream) => {
144
+ let answer = ''
145
+ try {
146
+ for await (const piece of modelPieces([{ role: 'user', content: message }], signal)) {
147
+ answer += piece
148
+ // JSON-encode the piece: a piece may contain a newline, which would end an SSE `data:` line.
149
+ await stream.writeSSE({ event: 'token', data: JSON.stringify(piece) })
150
+ }
151
+ await stream.writeSSE({ event: 'done', data: '{}' })
152
+ } catch (error) {
153
+ if (!signal.aborted) await stream.writeSSE({ event: 'error', data: JSON.stringify('The model could not answer.') })
154
+ } finally {
155
+ console.log(`kept ${answer.length} characters`) // save what arrived, also when the client pressed Stop
156
+ }
157
+ })
158
+ })
159
+ ```
160
+
161
+ ## Reading a POST event stream in the browser
162
+
163
+ `EventSource` can only send a GET request. For a POST (a chat message), read the response body yourself; this function calls `on` for each event as it arrives:
164
+
165
+ ```ts
166
+ async function readEvents(body: ReadableStream<Uint8Array>, on: (event: string, data: string) => void): Promise<void> {
167
+ const reader = body.getReader()
168
+ const decoder = new TextDecoder()
169
+ let buffer = ''
170
+ for (;;) {
171
+ const { done, value } = await reader.read()
172
+ if (done) return
173
+ // Line endings may be CRLF; a \r at the end of a chunk is completed by the next chunk, so normalise the whole buffer.
174
+ buffer = (buffer + decoder.decode(value, { stream: true })).replaceAll('\r\n', '\n')
175
+ const blocks = buffer.split('\n\n')
176
+ buffer = blocks.pop() ?? ''
177
+ for (const block of blocks) {
178
+ let event = 'message'
179
+ const data: string[] = []
180
+ for (const line of block.split('\n')) {
181
+ if (line.startsWith('event:')) event = line.slice(6).trim()
182
+ else if (line.startsWith('data:')) data.push(line.slice(5).replace(/^ /, '')) // the SSE spec strips one leading space only
183
+ }
184
+ on(event, data.join('\n'))
185
+ }
186
+ }
187
+ }
188
+
189
+ const controller = new AbortController() // controller.abort() is the Stop button
190
+ const response = await fetch('/api/chat', {
191
+ method: 'POST',
192
+ headers: { 'content-type': 'application/json' },
193
+ body: JSON.stringify({ message: 'Hello' }),
194
+ signal: controller.signal,
195
+ })
196
+ let answer = ''
197
+ await readEvents(response.body!, (event, data) => {
198
+ if (event === 'token') answer += JSON.parse(data) as string
199
+ })
200
+ ```
201
+
202
+ A complete chat component built on this is in the `chat` template of `create-acrylblends` (`npm create acrylblends@latest my-chat -- --template chat`).
203
+
84
204
  ## Error Handling
85
205
 
86
206
  The third argument of the streaming helper is an error handler.
@@ -1,11 +1,11 @@
1
1
  # WebSocket Helper
2
2
 
3
3
  WebSocket Helper is a helper for server-side WebSockets in Acryl applications.
4
- The Node.js adapter is available; the others are not yet available.
4
+ The Node.js, Cloudflare Workers and Bun adapters are available.
5
5
 
6
6
  ## Import
7
7
 
8
- Only the Node.js adapter, `acrylblends/node-server`, is available today. The adapters for the other platforms (Cloudflare Workers, Bun, Vercel, AWS Lambda, Netlify and Lambda@Edge) are **not yet available**; their imports are shown for completeness and are not checked by the docs tests.
8
+ The Node.js adapter (`acrylblends/node-server`), the Cloudflare Workers adapter (`acrylblends/cloudflare-workers`) and the Bun adapter (`acrylblends/bun`) provide `upgradeWebSocket`. The other platforms have no WebSocket support in their adapters. The Bun adapter is checked on Bun in the framework's release check; its examples below are not type-checked by the docs tests, which run without Bun's types.
9
9
 
10
10
  **Cloudflare Workers**
11
11
 
@@ -31,7 +31,7 @@ app.use(
31
31
  app.get('/', (c) => c.text('Acryl!'))
32
32
  ```
33
33
 
34
- Pass the `getConninfo` from the [ConnInfo helper](../../helpers/conninfo.md) appropriate for your environment as the first argument of `ipRestriction`. For a runtime whose adapter is not yet available there is no `getConnInfo` to pass yet.
34
+ Pass the `getConninfo` from the [ConnInfo helper](../../helpers/conninfo.md) appropriate for your environment as the first argument of `ipRestriction`. Every adapter has one (`acrylblends/bun`, `acrylblends/aws-lambda`, `acrylblends/node-server` and the others).
35
35
 
36
36
  ## Rules
37
37
 
@@ -2,8 +2,8 @@
2
2
  "source": {
3
3
  "repo": "acryldev/acryl",
4
4
  "path": "docs/framework",
5
- "commit": "cdc083e1bbaedaeafa7494fe646bdb202ce86d09"
5
+ "commit": "eada6fb06a5cafb6c2c92a97effeba567dbd44ba"
6
6
  },
7
- "files": 91,
8
- "digest": "sha256:964e5ff58d487ce53394469d359a251265a3ef093354f55b6b4c47c55051774b"
7
+ "files": 98,
8
+ "digest": "sha256:396f882ac7b612455edc59961c52472fadc5f95ea1a778208e7edd860e4e6727"
9
9
  }
@@ -0,0 +1,324 @@
1
+ import { Y as Schema, v as Env } from "./context-DS3fYyG-.js";
2
+ import { r as GetConnInfo } from "./types-Dv_nV2cv.js";
3
+ import { t as Acryl } from "./app-D8riatvv.js";
4
+
5
+ //#region ../../plugins/acryl-router/src/core/adapter/aws-lambda/types.d.ts
6
+ interface CognitoIdentity {
7
+ cognitoIdentityId: string;
8
+ cognitoIdentityPoolId: string;
9
+ }
10
+ interface ClientContext {
11
+ client: ClientContextClient;
12
+ Custom?: any;
13
+ env: ClientContextEnv;
14
+ }
15
+ interface ClientContextClient {
16
+ installationId: string;
17
+ appTitle: string;
18
+ appVersionName: string;
19
+ appVersionCode: string;
20
+ appPackageName: string;
21
+ }
22
+ interface ClientContextEnv {
23
+ platformVersion: string;
24
+ platform: string;
25
+ make: string;
26
+ model: string;
27
+ locale: string;
28
+ }
29
+ /**
30
+ * {@link Handler} context parameter.
31
+ * See {@link https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html AWS documentation}.
32
+ */
33
+ interface LambdaContext {
34
+ callbackWaitsForEmptyEventLoop: boolean;
35
+ functionName: string;
36
+ functionVersion: string;
37
+ invokedFunctionArn: string;
38
+ memoryLimitInMB: string;
39
+ awsRequestId: string;
40
+ logGroupName: string;
41
+ logStreamName: string;
42
+ identity?: CognitoIdentity | undefined;
43
+ clientContext?: ClientContext | undefined;
44
+ getRemainingTimeInMillis(): number;
45
+ }
46
+ type Callback<TResult = any> = (error?: Error | string | null, result?: TResult) => void;
47
+ type Handler<TEvent = any, TResult = any> = (event: TEvent, context: LambdaContext, callback: Callback<TResult>) => void | Promise<TResult>;
48
+ interface ClientCert {
49
+ clientCertPem: string;
50
+ subjectDN: string;
51
+ issuerDN: string;
52
+ serialNumber: string;
53
+ validity: {
54
+ notBefore: string;
55
+ notAfter: string;
56
+ };
57
+ }
58
+ interface Identity {
59
+ accessKey?: string;
60
+ accountId?: string;
61
+ caller?: string;
62
+ cognitoAuthenticationProvider?: string;
63
+ cognitoAuthenticationType?: string;
64
+ cognitoIdentityId?: string;
65
+ cognitoIdentityPoolId?: string;
66
+ principalOrgId?: string;
67
+ sourceIp: string;
68
+ user?: string;
69
+ userAgent: string;
70
+ userArn?: string;
71
+ clientCert?: ClientCert;
72
+ }
73
+ interface ApiGatewayRequestContext {
74
+ accountId: string;
75
+ apiId: string;
76
+ authorizer: {
77
+ claims?: unknown;
78
+ scopes?: unknown;
79
+ };
80
+ domainName: string;
81
+ domainPrefix: string;
82
+ extendedRequestId: string;
83
+ httpMethod: string;
84
+ identity: Identity;
85
+ path: string;
86
+ protocol: string;
87
+ requestId: string;
88
+ requestTime: string;
89
+ requestTimeEpoch: number;
90
+ resourceId?: string;
91
+ resourcePath: string;
92
+ stage: string;
93
+ }
94
+ interface Authorizer {
95
+ iam?: {
96
+ accessKey: string;
97
+ accountId: string;
98
+ callerId: string;
99
+ cognitoIdentity: null;
100
+ principalOrgId: null;
101
+ userArn: string;
102
+ userId: string;
103
+ };
104
+ jwt?: {
105
+ claims: Record<string, string | number | boolean | string[]>;
106
+ scopes: string[] | null;
107
+ };
108
+ /**
109
+ * The `context` object returned by a Lambda (REQUEST) authorizer.
110
+ * It is `null` when the authorizer returns no context.
111
+ */
112
+ lambda?: Record<string, unknown> | null;
113
+ }
114
+ interface ApiGatewayRequestContextV2 {
115
+ accountId: string;
116
+ apiId: string;
117
+ authentication: null;
118
+ authorizer: Authorizer;
119
+ domainName: string;
120
+ domainPrefix: string;
121
+ http: {
122
+ method: string;
123
+ path: string;
124
+ protocol: string;
125
+ sourceIp: string;
126
+ userAgent: string;
127
+ };
128
+ requestId: string;
129
+ routeKey: string;
130
+ stage: string;
131
+ time: string;
132
+ timeEpoch: number;
133
+ }
134
+ interface ALBRequestContext {
135
+ elb: {
136
+ targetGroupArn: string;
137
+ };
138
+ }
139
+ interface LatticeRequestContextV2 {
140
+ serviceNetworkArn: string;
141
+ serviceArn: string;
142
+ targetGroupArn: string;
143
+ region: string;
144
+ timeEpoch: string;
145
+ identity: {
146
+ sourceVpcArn?: string;
147
+ type?: string;
148
+ principal?: string;
149
+ principalOrgID?: string;
150
+ sessionName?: string;
151
+ x509IssuerOu?: string;
152
+ x509SanDns?: string;
153
+ x509SanNameCn?: string;
154
+ x509SanUri?: string;
155
+ x509SubjectCn?: string;
156
+ };
157
+ }
158
+ //#endregion
159
+ //#region ../../plugins/acryl-router/src/core/adapter/aws-lambda/handler.d.ts
160
+ type LambdaEvent = APIGatewayProxyEvent | APIGatewayProxyEventV2 | ALBProxyEvent | LatticeProxyEventV2;
161
+ interface LatticeProxyEventV2 {
162
+ version: string;
163
+ path: string;
164
+ method: string;
165
+ headers: Record<string, string[] | undefined>;
166
+ queryStringParameters: Record<string, string[] | undefined>;
167
+ body: string | null;
168
+ isBase64Encoded: boolean;
169
+ requestContext: LatticeRequestContextV2;
170
+ }
171
+ interface APIGatewayProxyEventV2 {
172
+ version: string;
173
+ routeKey: string;
174
+ headers: Record<string, string | undefined>;
175
+ multiValueHeaders?: undefined;
176
+ cookies?: string[];
177
+ rawPath: string;
178
+ rawQueryString: string;
179
+ body: string | null;
180
+ isBase64Encoded: boolean;
181
+ requestContext: ApiGatewayRequestContextV2;
182
+ queryStringParameters?: {
183
+ [name: string]: string | undefined;
184
+ };
185
+ pathParameters?: {
186
+ [name: string]: string | undefined;
187
+ };
188
+ stageVariables?: {
189
+ [name: string]: string | undefined;
190
+ };
191
+ }
192
+ interface APIGatewayProxyEvent {
193
+ version: string;
194
+ httpMethod: string;
195
+ headers: Record<string, string | undefined>;
196
+ multiValueHeaders?: {
197
+ [headerKey: string]: string[];
198
+ };
199
+ path: string;
200
+ body: string | null;
201
+ isBase64Encoded: boolean;
202
+ queryStringParameters?: Record<string, string | undefined>;
203
+ requestContext: ApiGatewayRequestContext;
204
+ resource: string;
205
+ multiValueQueryStringParameters?: {
206
+ [parameterKey: string]: string[];
207
+ };
208
+ pathParameters?: Record<string, string>;
209
+ stageVariables?: Record<string, string>;
210
+ }
211
+ interface ALBProxyEvent {
212
+ httpMethod: string;
213
+ headers?: Record<string, string | undefined>;
214
+ multiValueHeaders?: Record<string, string[] | undefined>;
215
+ path: string;
216
+ body: string | null;
217
+ isBase64Encoded: boolean;
218
+ queryStringParameters?: Record<string, string | undefined>;
219
+ multiValueQueryStringParameters?: {
220
+ [parameterKey: string]: string[];
221
+ };
222
+ requestContext: ALBRequestContext;
223
+ }
224
+ type WithHeaders = {
225
+ headers: Record<string, string | string[]>;
226
+ multiValueHeaders?: undefined;
227
+ };
228
+ type WithMultiValueHeaders = {
229
+ headers?: undefined;
230
+ multiValueHeaders: Record<string, string[]>;
231
+ };
232
+ type APIGatewayProxyResult = {
233
+ statusCode: number;
234
+ statusDescription?: string;
235
+ body: string;
236
+ cookies?: string[];
237
+ isBase64Encoded: boolean;
238
+ } & (WithHeaders | WithMultiValueHeaders);
239
+ declare const streamHandle: <E extends Env = Env, S extends Schema = {}, BasePath extends string = "/">(app: Acryl<E, S, BasePath>) => Handler;
240
+ type HandleOptions = {
241
+ isContentTypeBinary: ((contentType: string) => boolean) | undefined;
242
+ };
243
+ /**
244
+ * Converts a Acryl application to an AWS Lambda handler.
245
+ *
246
+ * Accepts events from API Gateway (v1 and v2), Application Load Balancer (ALB),
247
+ * and Lambda Function URLs.
248
+ *
249
+ * @param app - The Acryl application instance
250
+ * @param options - Optional configuration
251
+ * @param options.isContentTypeBinary - A function to determine if the content type is binary.
252
+ * If not provided, the default function will be used.
253
+ * @returns Lambda handler function
254
+ *
255
+ * @example
256
+ * ```js
257
+ * import { Acryl } from 'acrylblends'
258
+ * import { handle } from 'acrylblends/aws-lambda'
259
+ *
260
+ * const app = new Acryl()
261
+ *
262
+ * app.get('/', (c) => c.text('Hello from Lambda'))
263
+ * app.get('/json', (c) => c.json({ message: 'Hello JSON' }))
264
+ *
265
+ * export const handler = handle(app)
266
+ * ```
267
+ *
268
+ * @example
269
+ * ```js
270
+ * // With custom binary content type detection
271
+ * import { handle, defaultIsContentTypeBinary } from 'acrylblends/aws-lambda'
272
+ * export const handler = handle(app, {
273
+ * isContentTypeBinary: (contentType) => {
274
+ * if (defaultIsContentTypeBinary(contentType)) {
275
+ * // default logic same as prior to v4.8.4
276
+ * return true
277
+ * }
278
+ * return contentType.startsWith('image/') || contentType === 'application/pdf'
279
+ * }
280
+ * })
281
+ * ```
282
+ */
283
+ declare const handle: <E extends Env = Env, S extends Schema = {}, BasePath extends string = "/">(app: Acryl<E, S, BasePath>, {
284
+ isContentTypeBinary
285
+ }?: HandleOptions) => (<L extends LambdaEvent>(event: L, lambdaContext?: LambdaContext) => Promise<APIGatewayProxyResult & (L extends {
286
+ multiValueHeaders: Record<string, string[]>;
287
+ } ? WithMultiValueHeaders : WithHeaders)>);
288
+ /**
289
+ * Check if the given content type is binary.
290
+ * This is a default function and may be overwritten by the user via `isContentTypeBinary` option in handler().
291
+ * @param contentType The content type to check.
292
+ * @returns True if the content type is binary, false otherwise.
293
+ */
294
+ declare const defaultIsContentTypeBinary: (contentType: string) => boolean;
295
+ //#endregion
296
+ //#region ../../plugins/acryl-router/src/core/adapter/aws-lambda/conninfo.d.ts
297
+ /**
298
+ * Get connection information from AWS Lambda
299
+ *
300
+ * Extracts client IP from various Lambda event sources:
301
+ * - API Gateway v1 (REST API): requestContext.identity.sourceIp
302
+ * - API Gateway v2 (HTTP API/Function URLs): requestContext.http.sourceIp
303
+ * - ALB: Falls back to x-forwarded-for header
304
+ *
305
+ * @param c - Context
306
+ * @returns Connection information including remote address
307
+ * @example
308
+ * ```ts
309
+ * import { Acryl } from 'acrylblends'
310
+ * import { handle, getConnInfo } from 'acrylblends/aws-lambda'
311
+ *
312
+ * const app = new Acryl()
313
+ *
314
+ * app.get('/', (c) => {
315
+ * const info = getConnInfo(c)
316
+ * return c.text(`Your IP: ${info.remote.address}`)
317
+ * })
318
+ *
319
+ * export const handler = handle(app)
320
+ * ```
321
+ */
322
+ declare const getConnInfo: GetConnInfo;
323
+ //#endregion
324
+ export { type ALBRequestContext, type APIGatewayProxyResult, type ApiGatewayRequestContext, type ApiGatewayRequestContextV2, type LambdaContext, type LambdaEvent, defaultIsContentTypeBinary, getConnInfo, handle, streamHandle };