ai.matey.http 0.2.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.
- package/LICENSE +21 -0
- package/dist/cjs/deno/adapter.js +173 -0
- package/dist/cjs/deno/adapter.js.map +1 -0
- package/dist/cjs/deno/handler.js +78 -0
- package/dist/cjs/deno/handler.js.map +1 -0
- package/dist/cjs/deno/index.js +26 -0
- package/dist/cjs/deno/index.js.map +1 -0
- package/dist/cjs/express/adapter.js +150 -0
- package/dist/cjs/express/adapter.js.map +1 -0
- package/dist/cjs/express/index.js +26 -0
- package/dist/cjs/express/index.js.map +1 -0
- package/dist/cjs/express/middleware.js +63 -0
- package/dist/cjs/express/middleware.js.map +1 -0
- package/dist/cjs/fastify/adapter.js +149 -0
- package/dist/cjs/fastify/adapter.js.map +1 -0
- package/dist/cjs/fastify/handler.js +54 -0
- package/dist/cjs/fastify/handler.js.map +1 -0
- package/dist/cjs/fastify/index.js +26 -0
- package/dist/cjs/fastify/index.js.map +1 -0
- package/dist/cjs/hono/adapter.js +172 -0
- package/dist/cjs/hono/adapter.js.map +1 -0
- package/dist/cjs/hono/index.js +26 -0
- package/dist/cjs/hono/index.js.map +1 -0
- package/dist/cjs/hono/middleware.js +75 -0
- package/dist/cjs/hono/middleware.js.map +1 -0
- package/dist/cjs/index.js +37 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/koa/adapter.js +153 -0
- package/dist/cjs/koa/adapter.js.map +1 -0
- package/dist/cjs/koa/index.js +26 -0
- package/dist/cjs/koa/index.js.map +1 -0
- package/dist/cjs/koa/middleware.js +58 -0
- package/dist/cjs/koa/middleware.js.map +1 -0
- package/dist/cjs/node/adapter.js +170 -0
- package/dist/cjs/node/adapter.js.map +1 -0
- package/dist/cjs/node/index.js +26 -0
- package/dist/cjs/node/index.js.map +1 -0
- package/dist/cjs/node/listener.js +98 -0
- package/dist/cjs/node/listener.js.map +1 -0
- package/dist/esm/deno/adapter.js +168 -0
- package/dist/esm/deno/adapter.js.map +1 -0
- package/dist/esm/deno/handler.js +75 -0
- package/dist/esm/deno/handler.js.map +1 -0
- package/dist/esm/deno/index.js +10 -0
- package/dist/esm/deno/index.js.map +1 -0
- package/dist/esm/express/adapter.js +145 -0
- package/dist/esm/express/adapter.js.map +1 -0
- package/dist/esm/express/index.js +10 -0
- package/dist/esm/express/index.js.map +1 -0
- package/dist/esm/express/middleware.js +60 -0
- package/dist/esm/express/middleware.js.map +1 -0
- package/dist/esm/fastify/adapter.js +144 -0
- package/dist/esm/fastify/adapter.js.map +1 -0
- package/dist/esm/fastify/handler.js +51 -0
- package/dist/esm/fastify/handler.js.map +1 -0
- package/dist/esm/fastify/index.js +10 -0
- package/dist/esm/fastify/index.js.map +1 -0
- package/dist/esm/hono/adapter.js +167 -0
- package/dist/esm/hono/adapter.js.map +1 -0
- package/dist/esm/hono/index.js +10 -0
- package/dist/esm/hono/index.js.map +1 -0
- package/dist/esm/hono/middleware.js +72 -0
- package/dist/esm/hono/middleware.js.map +1 -0
- package/dist/esm/index.js +21 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/koa/adapter.js +148 -0
- package/dist/esm/koa/adapter.js.map +1 -0
- package/dist/esm/koa/index.js +10 -0
- package/dist/esm/koa/index.js.map +1 -0
- package/dist/esm/koa/middleware.js +55 -0
- package/dist/esm/koa/middleware.js.map +1 -0
- package/dist/esm/node/adapter.js +165 -0
- package/dist/esm/node/adapter.js.map +1 -0
- package/dist/esm/node/index.js +10 -0
- package/dist/esm/node/index.js.map +1 -0
- package/dist/esm/node/listener.js +92 -0
- package/dist/esm/node/listener.js.map +1 -0
- package/dist/types/deno/adapter.d.ts +56 -0
- package/dist/types/deno/adapter.d.ts.map +1 -0
- package/dist/types/deno/handler.d.ts +38 -0
- package/dist/types/deno/handler.d.ts.map +1 -0
- package/dist/types/deno/index.d.ts +10 -0
- package/dist/types/deno/index.d.ts.map +1 -0
- package/dist/types/express/adapter.d.ts +46 -0
- package/dist/types/express/adapter.d.ts.map +1 -0
- package/dist/types/express/index.d.ts +10 -0
- package/dist/types/express/index.d.ts.map +1 -0
- package/dist/types/express/middleware.d.ts +39 -0
- package/dist/types/express/middleware.d.ts.map +1 -0
- package/dist/types/fastify/adapter.d.ts +46 -0
- package/dist/types/fastify/adapter.d.ts.map +1 -0
- package/dist/types/fastify/handler.d.ts +36 -0
- package/dist/types/fastify/handler.d.ts.map +1 -0
- package/dist/types/fastify/index.d.ts +10 -0
- package/dist/types/fastify/index.d.ts.map +1 -0
- package/dist/types/hono/adapter.d.ts +47 -0
- package/dist/types/hono/adapter.d.ts.map +1 -0
- package/dist/types/hono/index.d.ts +10 -0
- package/dist/types/hono/index.d.ts.map +1 -0
- package/dist/types/hono/middleware.d.ts +36 -0
- package/dist/types/hono/middleware.d.ts.map +1 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/koa/adapter.d.ts +46 -0
- package/dist/types/koa/adapter.d.ts.map +1 -0
- package/dist/types/koa/index.d.ts +10 -0
- package/dist/types/koa/index.d.ts.map +1 -0
- package/dist/types/koa/middleware.d.ts +40 -0
- package/dist/types/koa/middleware.d.ts.map +1 -0
- package/dist/types/node/adapter.d.ts +57 -0
- package/dist/types/node/adapter.d.ts.map +1 -0
- package/dist/types/node/index.d.ts +10 -0
- package/dist/types/node/index.d.ts.map +1 -0
- package/dist/types/node/listener.d.ts +51 -0
- package/dist/types/node/listener.d.ts.map +1 -0
- package/package.json +160 -0
- package/readme.md +93 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deno HTTP Adapter
|
|
3
|
+
*
|
|
4
|
+
* Converts Deno Request/Response to GenericRequest/GenericResponse
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Adapter that converts Deno Request to GenericRequest
|
|
10
|
+
*/
|
|
11
|
+
export class DenoRequestAdapter {
|
|
12
|
+
request;
|
|
13
|
+
connInfo;
|
|
14
|
+
_body = null;
|
|
15
|
+
constructor(request, connInfo) {
|
|
16
|
+
this.request = request;
|
|
17
|
+
this.connInfo = connInfo;
|
|
18
|
+
}
|
|
19
|
+
get method() {
|
|
20
|
+
return this.request.method || 'GET';
|
|
21
|
+
}
|
|
22
|
+
get url() {
|
|
23
|
+
return this.request.url || '/';
|
|
24
|
+
}
|
|
25
|
+
get headers() {
|
|
26
|
+
const headers = {};
|
|
27
|
+
this.request.headers.forEach((value, key) => {
|
|
28
|
+
headers[key.toLowerCase()] = value;
|
|
29
|
+
});
|
|
30
|
+
return headers;
|
|
31
|
+
}
|
|
32
|
+
get body() {
|
|
33
|
+
// Return parsed body (must be set externally via setBody)
|
|
34
|
+
return this._body;
|
|
35
|
+
}
|
|
36
|
+
get params() {
|
|
37
|
+
// Params would need to be provided by routing layer
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
get query() {
|
|
41
|
+
// Parse query string from URL
|
|
42
|
+
const url = new URL(this.request.url);
|
|
43
|
+
const query = {};
|
|
44
|
+
url.searchParams.forEach((value, key) => {
|
|
45
|
+
query[key] = value;
|
|
46
|
+
});
|
|
47
|
+
return Object.keys(query).length > 0 ? query : undefined;
|
|
48
|
+
}
|
|
49
|
+
get ip() {
|
|
50
|
+
// Try to get IP from connection info or headers
|
|
51
|
+
if (this.connInfo?.remoteAddr?.hostname) {
|
|
52
|
+
return this.connInfo.remoteAddr.hostname;
|
|
53
|
+
}
|
|
54
|
+
const forwarded = this.request.headers.get('x-forwarded-for');
|
|
55
|
+
if (forwarded) {
|
|
56
|
+
return forwarded.split(',')[0]?.trim();
|
|
57
|
+
}
|
|
58
|
+
const realIp = this.request.headers.get('x-real-ip');
|
|
59
|
+
if (realIp) {
|
|
60
|
+
return realIp;
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Set the parsed body (must be called before passing to handler)
|
|
66
|
+
*/
|
|
67
|
+
setBody(body) {
|
|
68
|
+
this._body = body;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get the underlying Deno request object
|
|
72
|
+
*/
|
|
73
|
+
get raw() {
|
|
74
|
+
return this.request;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Adapter that converts GenericResponse methods to Deno Response
|
|
79
|
+
*/
|
|
80
|
+
export class DenoResponseAdapter {
|
|
81
|
+
_statusCode = 200;
|
|
82
|
+
_headers = new Headers();
|
|
83
|
+
_sent = false;
|
|
84
|
+
_response = null;
|
|
85
|
+
constructor() { }
|
|
86
|
+
status(code) {
|
|
87
|
+
if (!this._sent) {
|
|
88
|
+
this._statusCode = code;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
header(name, value) {
|
|
92
|
+
if (!this._sent) {
|
|
93
|
+
this._headers.set(name, value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
send(data) {
|
|
97
|
+
if (this._sent) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this._sent = true;
|
|
101
|
+
// Clone headers to ensure they're properly set
|
|
102
|
+
const headers = new Headers(this._headers);
|
|
103
|
+
// Handle empty string (e.g., for CORS preflight 204 responses)
|
|
104
|
+
if (data === '') {
|
|
105
|
+
// 204 No Content responses cannot have a body (not even empty string)
|
|
106
|
+
// Use null for 204, empty string for other status codes
|
|
107
|
+
this._response = new Response(this._statusCode === 204 ? null : '', {
|
|
108
|
+
status: this._statusCode,
|
|
109
|
+
headers,
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Create JSON response
|
|
114
|
+
headers.set('Content-Type', 'application/json');
|
|
115
|
+
this._response = new Response(JSON.stringify(data), {
|
|
116
|
+
status: this._statusCode,
|
|
117
|
+
headers,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
stream(generator) {
|
|
121
|
+
if (this._sent) {
|
|
122
|
+
return Promise.resolve();
|
|
123
|
+
}
|
|
124
|
+
this._sent = true;
|
|
125
|
+
// Create SSE stream
|
|
126
|
+
const stream = new ReadableStream({
|
|
127
|
+
async start(controller) {
|
|
128
|
+
try {
|
|
129
|
+
for await (const chunk of generator) {
|
|
130
|
+
const data = `data: ${JSON.stringify(chunk)}\n\n`;
|
|
131
|
+
controller.enqueue(new TextEncoder().encode(data));
|
|
132
|
+
}
|
|
133
|
+
// Send done marker
|
|
134
|
+
const done = 'data: [DONE]\n\n';
|
|
135
|
+
controller.enqueue(new TextEncoder().encode(done));
|
|
136
|
+
controller.close();
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.error('Streaming error:', error);
|
|
140
|
+
controller.error(error);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
// Set SSE headers
|
|
145
|
+
this._headers.set('Content-Type', 'text/event-stream');
|
|
146
|
+
this._headers.set('Cache-Control', 'no-cache');
|
|
147
|
+
this._headers.set('Connection', 'keep-alive');
|
|
148
|
+
this._response = new Response(stream, {
|
|
149
|
+
status: this._statusCode,
|
|
150
|
+
headers: this._headers,
|
|
151
|
+
});
|
|
152
|
+
return Promise.resolve();
|
|
153
|
+
}
|
|
154
|
+
isWritable() {
|
|
155
|
+
return !this._sent;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get the generated Response object
|
|
159
|
+
*/
|
|
160
|
+
getResponse() {
|
|
161
|
+
if (!this._response) {
|
|
162
|
+
// If no response was generated, create an error response
|
|
163
|
+
return new Response('Internal Server Error', { status: 500 });
|
|
164
|
+
}
|
|
165
|
+
return this._response;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/deno/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deno Handler
|
|
3
|
+
*
|
|
4
|
+
* HTTP request handler for Deno that uses the core handler.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { CoreHTTPHandler } from 'ai.matey.http.core';
|
|
9
|
+
import { DenoRequestAdapter, DenoResponseAdapter } from './adapter.js';
|
|
10
|
+
/**
|
|
11
|
+
* Create Deno HTTP handler for handling AI chat requests
|
|
12
|
+
*
|
|
13
|
+
* @param bridge - Bridge instance
|
|
14
|
+
* @param options - HTTP listener options
|
|
15
|
+
* @returns Deno HTTP handler function
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { DenoHandler } from 'ai.matey.http/deno';
|
|
20
|
+
*
|
|
21
|
+
* const bridge = new Bridge(frontend, backend);
|
|
22
|
+
*
|
|
23
|
+
* // Create handler
|
|
24
|
+
* const handler = DenoHandler(bridge, {
|
|
25
|
+
* cors: true,
|
|
26
|
+
* streaming: true,
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Use with Deno.serve
|
|
30
|
+
* Deno.serve({ port: 3000 }, handler);
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function DenoHandler(bridge, options = {}) {
|
|
34
|
+
// Create core handler with all business logic
|
|
35
|
+
const { cors, ...restOptions } = options;
|
|
36
|
+
const coreHandler = new CoreHTTPHandler({
|
|
37
|
+
bridge,
|
|
38
|
+
cors: cors === false || cors === undefined ? undefined : cors === true ? {} : cors,
|
|
39
|
+
...restOptions, // HTTPListenerOptions types are compatible with CoreHandlerOptions at runtime
|
|
40
|
+
});
|
|
41
|
+
// Return Deno HTTP handler
|
|
42
|
+
return async (request, connInfo) => {
|
|
43
|
+
try {
|
|
44
|
+
// Parse JSON body if present (but not for OPTIONS requests)
|
|
45
|
+
const contentType = request.headers.get('content-type') || '';
|
|
46
|
+
const method = request.method;
|
|
47
|
+
let body = null;
|
|
48
|
+
if (method !== 'OPTIONS' && contentType.includes('application/json')) {
|
|
49
|
+
try {
|
|
50
|
+
body = await request.json();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// If JSON parsing fails, leave body as null
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Create adapters
|
|
57
|
+
const genericReq = new DenoRequestAdapter(request, connInfo);
|
|
58
|
+
genericReq.setBody(body);
|
|
59
|
+
const genericRes = new DenoResponseAdapter();
|
|
60
|
+
// Handle request through core handler
|
|
61
|
+
await coreHandler.handle(genericReq, genericRes);
|
|
62
|
+
// Return the response generated by the adapter
|
|
63
|
+
return genericRes.getResponse();
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
// Return error response
|
|
67
|
+
console.error('Deno handler error:', error);
|
|
68
|
+
return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
|
|
69
|
+
status: 500,
|
|
70
|
+
headers: { 'Content-Type': 'application/json' },
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/deno/handler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,WAAW,CACzB,MAAc,EACd,UAA+B,EAAE;IAEjC,8CAA8C;IAC9C,MAAM,EAAE,IAAI,EAAE,GAAG,WAAW,EAAE,GAAG,OAAO,CAAC;IAEzC,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC;QACtC,MAAM;QACN,IAAI,EAAE,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;QAClF,GAAI,WAAmB,EAAE,8EAA8E;KACxG,CAAC,CAAC;IAEH,2BAA2B;IAC3B,OAAO,KAAK,EACV,OAAgB,EAChB,QAAiD,EAC9B,EAAE;QACrB,IAAI,CAAC;YACH,4DAA4D;YAC5D,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;YAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC9B,IAAI,IAAI,GAAG,IAAI,CAAC;YAEhB,IAAI,MAAM,KAAK,SAAS,IAAI,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBACrE,IAAI,CAAC;oBACH,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;gBAC9B,CAAC;gBAAC,MAAM,CAAC;oBACP,4CAA4C;gBAC9C,CAAC;YACH,CAAC;YAED,kBAAkB;YAClB,MAAM,UAAU,GAAG,IAAI,kBAAkB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC7D,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAEzB,MAAM,UAAU,GAAG,IAAI,mBAAmB,EAAE,CAAC;YAE7C,sCAAsC;YACtC,MAAM,WAAW,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YAEjD,+CAA+C;YAC/C,OAAO,UAAU,CAAC,WAAW,EAAE,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,wBAAwB;YACxB,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;YAC5C,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,EAAE;gBACtE,MAAM,EAAE,GAAG;gBACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;aAChD,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/deno/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express HTTP Adapter
|
|
3
|
+
*
|
|
4
|
+
* Converts Express Request/Response to GenericRequest/GenericResponse
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { sendSSEHeaders, sendSSEChunk, sendSSEDone } from 'ai.matey.http.core';
|
|
9
|
+
/**
|
|
10
|
+
* Adapter that converts Express Request to GenericRequest
|
|
11
|
+
*/
|
|
12
|
+
export class ExpressRequestAdapter {
|
|
13
|
+
req;
|
|
14
|
+
constructor(req) {
|
|
15
|
+
this.req = req;
|
|
16
|
+
}
|
|
17
|
+
get method() {
|
|
18
|
+
return this.req.method || 'GET';
|
|
19
|
+
}
|
|
20
|
+
get url() {
|
|
21
|
+
return this.req.originalUrl || this.req.url || '/';
|
|
22
|
+
}
|
|
23
|
+
get headers() {
|
|
24
|
+
const headers = {};
|
|
25
|
+
for (const [key, value] of Object.entries(this.req.headers)) {
|
|
26
|
+
if (typeof value === 'string') {
|
|
27
|
+
headers[key.toLowerCase()] = value;
|
|
28
|
+
}
|
|
29
|
+
else if (Array.isArray(value)) {
|
|
30
|
+
headers[key.toLowerCase()] = value.join(', ');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return headers;
|
|
34
|
+
}
|
|
35
|
+
get body() {
|
|
36
|
+
// Express body is already parsed if body-parser middleware is used
|
|
37
|
+
return this.req.body ?? null;
|
|
38
|
+
}
|
|
39
|
+
get params() {
|
|
40
|
+
return this.req.params;
|
|
41
|
+
}
|
|
42
|
+
get query() {
|
|
43
|
+
// Express query can have arrays, so we need to stringify them
|
|
44
|
+
const query = {};
|
|
45
|
+
for (const [key, value] of Object.entries(this.req.query)) {
|
|
46
|
+
if (typeof value === 'string') {
|
|
47
|
+
query[key] = value;
|
|
48
|
+
}
|
|
49
|
+
else if (Array.isArray(value)) {
|
|
50
|
+
const primitives = value.filter((v) => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean');
|
|
51
|
+
query[key] = primitives.map((v) => v.toString()).join(',');
|
|
52
|
+
}
|
|
53
|
+
else if (value !== undefined && typeof value !== 'object') {
|
|
54
|
+
query[key] = String(value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return Object.keys(query).length > 0 ? query : undefined;
|
|
58
|
+
}
|
|
59
|
+
get ip() {
|
|
60
|
+
// Express provides multiple IP sources
|
|
61
|
+
return this.req.ip || this.req.socket?.remoteAddress;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get the underlying Express request object
|
|
65
|
+
*/
|
|
66
|
+
get raw() {
|
|
67
|
+
return this.req;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Adapter that converts GenericResponse methods to Express Response
|
|
72
|
+
*/
|
|
73
|
+
export class ExpressResponseAdapter {
|
|
74
|
+
res;
|
|
75
|
+
_statusCode = 200;
|
|
76
|
+
_headersSent = false;
|
|
77
|
+
constructor(res) {
|
|
78
|
+
this.res = res;
|
|
79
|
+
}
|
|
80
|
+
status(code) {
|
|
81
|
+
this._statusCode = code;
|
|
82
|
+
if (!this._headersSent) {
|
|
83
|
+
this.res.status(code);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
header(name, value) {
|
|
87
|
+
if (!this._headersSent) {
|
|
88
|
+
this.res.setHeader(name, value);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
send(data) {
|
|
92
|
+
if (!this.isWritable()) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this._headersSent = true;
|
|
96
|
+
// Handle empty string (e.g., for CORS preflight 204 responses)
|
|
97
|
+
if (data === '') {
|
|
98
|
+
this.res.end();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Express has a built-in json() method
|
|
102
|
+
this.res.status(this._statusCode).json(data);
|
|
103
|
+
}
|
|
104
|
+
async stream(generator) {
|
|
105
|
+
if (!this.isWritable()) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this._headersSent = true;
|
|
109
|
+
// Get the underlying Node.js response for SSE
|
|
110
|
+
const nodeRes = this.res;
|
|
111
|
+
// Set SSE headers
|
|
112
|
+
sendSSEHeaders(nodeRes, {});
|
|
113
|
+
try {
|
|
114
|
+
// Stream chunks
|
|
115
|
+
for await (const chunk of generator) {
|
|
116
|
+
if (!this.isWritable()) {
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
sendSSEChunk(nodeRes, chunk);
|
|
120
|
+
}
|
|
121
|
+
// Send done marker
|
|
122
|
+
if (this.isWritable()) {
|
|
123
|
+
sendSSEDone(nodeRes);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
// If error occurs during streaming, we can't change status code
|
|
128
|
+
// Just log and close
|
|
129
|
+
console.error('Streaming error:', error);
|
|
130
|
+
if (this.isWritable()) {
|
|
131
|
+
nodeRes.end();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
isWritable() {
|
|
136
|
+
return !this.res.headersSent && this.res.writable;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get the underlying Express response object
|
|
140
|
+
*/
|
|
141
|
+
get raw() {
|
|
142
|
+
return this.res;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/express/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/express/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express Middleware
|
|
3
|
+
*
|
|
4
|
+
* HTTP request middleware for Express that uses the core handler.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { CoreHTTPHandler } from 'ai.matey.http.core';
|
|
9
|
+
import { ExpressRequestAdapter, ExpressResponseAdapter } from './adapter.js';
|
|
10
|
+
/**
|
|
11
|
+
* Create Express middleware for handling AI chat requests
|
|
12
|
+
*
|
|
13
|
+
* @param bridge - Bridge instance
|
|
14
|
+
* @param options - HTTP listener options
|
|
15
|
+
* @returns Express middleware function
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import express from 'express';
|
|
20
|
+
* import { ExpressMiddleware } from 'ai.matey.http/express';
|
|
21
|
+
*
|
|
22
|
+
* const app = express();
|
|
23
|
+
* const bridge = new Bridge(frontend, backend);
|
|
24
|
+
*
|
|
25
|
+
* // Use body parser middleware first
|
|
26
|
+
* app.use(express.json());
|
|
27
|
+
*
|
|
28
|
+
* // Add AI chat middleware
|
|
29
|
+
* app.use('/v1/messages', ExpressMiddleware(bridge, {
|
|
30
|
+
* cors: true,
|
|
31
|
+
* streaming: true,
|
|
32
|
+
* }));
|
|
33
|
+
*
|
|
34
|
+
* app.listen(3000);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function ExpressMiddleware(bridge, options = {}) {
|
|
38
|
+
// Create core handler with all business logic
|
|
39
|
+
const { cors, ...restOptions } = options;
|
|
40
|
+
const coreHandler = new CoreHTTPHandler({
|
|
41
|
+
bridge,
|
|
42
|
+
cors: cors === false || cors === undefined ? undefined : cors === true ? {} : cors,
|
|
43
|
+
...restOptions, // HTTPListenerOptions types are compatible with CoreHandlerOptions at runtime
|
|
44
|
+
});
|
|
45
|
+
// Return Express middleware
|
|
46
|
+
return async (req, res, next) => {
|
|
47
|
+
try {
|
|
48
|
+
// Create adapters
|
|
49
|
+
const genericReq = new ExpressRequestAdapter(req);
|
|
50
|
+
const genericRes = new ExpressResponseAdapter(res);
|
|
51
|
+
// Handle request through core handler
|
|
52
|
+
await coreHandler.handle(genericReq, genericRes);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
// Pass error to Express error handling middleware
|
|
56
|
+
next(error);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/express/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAc,EACd,UAA+B,EAAE;IAEjC,8CAA8C;IAC9C,MAAM,EAAE,IAAI,EAAE,GAAG,WAAW,EAAE,GAAG,OAAO,CAAC;IAEzC,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC;QACtC,MAAM;QACN,IAAI,EAAE,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;QAClF,GAAI,WAAmB,EAAE,8EAA8E;KACxG,CAAC,CAAC;IAEH,4BAA4B;IAC5B,OAAO,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAiB,EAAE;QAC9E,IAAI,CAAC;YACH,kBAAkB;YAClB,MAAM,UAAU,GAAG,IAAI,qBAAqB,CAAC,GAAG,CAAC,CAAC;YAClD,MAAM,UAAU,GAAG,IAAI,sBAAsB,CAAC,GAAG,CAAC,CAAC;YAEnD,sCAAsC;YACtC,MAAM,WAAW,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,kDAAkD;YAClD,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fastify HTTP Adapter
|
|
3
|
+
*
|
|
4
|
+
* Converts Fastify Request/Reply to GenericRequest/GenericResponse
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { sendSSEHeaders, sendSSEChunk, sendSSEDone } from 'ai.matey.http.core';
|
|
9
|
+
/**
|
|
10
|
+
* Adapter that converts Fastify Request to GenericRequest
|
|
11
|
+
*/
|
|
12
|
+
export class FastifyRequestAdapter {
|
|
13
|
+
request;
|
|
14
|
+
constructor(request) {
|
|
15
|
+
this.request = request;
|
|
16
|
+
}
|
|
17
|
+
get method() {
|
|
18
|
+
return this.request.method || 'GET';
|
|
19
|
+
}
|
|
20
|
+
get url() {
|
|
21
|
+
return this.request.url || '/';
|
|
22
|
+
}
|
|
23
|
+
get headers() {
|
|
24
|
+
const headers = {};
|
|
25
|
+
for (const [key, value] of Object.entries(this.request.headers)) {
|
|
26
|
+
if (typeof value === 'string') {
|
|
27
|
+
headers[key.toLowerCase()] = value;
|
|
28
|
+
}
|
|
29
|
+
else if (Array.isArray(value)) {
|
|
30
|
+
headers[key.toLowerCase()] = value.join(', ');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return headers;
|
|
34
|
+
}
|
|
35
|
+
get body() {
|
|
36
|
+
// Fastify automatically parses JSON body
|
|
37
|
+
return this.request.body ?? null;
|
|
38
|
+
}
|
|
39
|
+
get params() {
|
|
40
|
+
return this.request.params;
|
|
41
|
+
}
|
|
42
|
+
get query() {
|
|
43
|
+
// Fastify query can have arrays, so we need to stringify them
|
|
44
|
+
const query = {};
|
|
45
|
+
for (const [key, value] of Object.entries(this.request.query || {})) {
|
|
46
|
+
if (typeof value === 'string') {
|
|
47
|
+
query[key] = value;
|
|
48
|
+
}
|
|
49
|
+
else if (Array.isArray(value)) {
|
|
50
|
+
query[key] = value.join(',');
|
|
51
|
+
}
|
|
52
|
+
else if (value !== undefined) {
|
|
53
|
+
query[key] = String(value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return Object.keys(query).length > 0 ? query : undefined;
|
|
57
|
+
}
|
|
58
|
+
get ip() {
|
|
59
|
+
// Fastify provides IP through request.ip
|
|
60
|
+
return this.request.ip || this.request.socket?.remoteAddress;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get the underlying Fastify request object
|
|
64
|
+
*/
|
|
65
|
+
get raw() {
|
|
66
|
+
return this.request;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Adapter that converts GenericResponse methods to Fastify Reply
|
|
71
|
+
*/
|
|
72
|
+
export class FastifyResponseAdapter {
|
|
73
|
+
reply;
|
|
74
|
+
_statusCode = 200;
|
|
75
|
+
_headersSent = false;
|
|
76
|
+
constructor(reply) {
|
|
77
|
+
this.reply = reply;
|
|
78
|
+
}
|
|
79
|
+
status(code) {
|
|
80
|
+
this._statusCode = code;
|
|
81
|
+
if (!this._headersSent) {
|
|
82
|
+
this.reply.code(code);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
header(name, value) {
|
|
86
|
+
if (!this._headersSent) {
|
|
87
|
+
this.reply.header(name, value);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
send(data) {
|
|
91
|
+
if (!this.isWritable()) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this._headersSent = true;
|
|
95
|
+
// Handle empty string (e.g., for CORS preflight 204 responses)
|
|
96
|
+
if (data === '') {
|
|
97
|
+
this.reply.code(this._statusCode).send('');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Fastify automatically JSON-stringifies objects
|
|
101
|
+
this.reply.code(this._statusCode).send(data);
|
|
102
|
+
}
|
|
103
|
+
async stream(generator) {
|
|
104
|
+
if (!this.isWritable()) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this._headersSent = true;
|
|
108
|
+
// Get the underlying Node.js response for SSE
|
|
109
|
+
const nodeRes = this.reply.raw;
|
|
110
|
+
// Set SSE headers
|
|
111
|
+
sendSSEHeaders(nodeRes, {});
|
|
112
|
+
try {
|
|
113
|
+
// Stream chunks
|
|
114
|
+
for await (const chunk of generator) {
|
|
115
|
+
if (!this.isWritable()) {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
sendSSEChunk(nodeRes, chunk);
|
|
119
|
+
}
|
|
120
|
+
// Send done marker
|
|
121
|
+
if (this.isWritable()) {
|
|
122
|
+
sendSSEDone(nodeRes);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
// If error occurs during streaming, we can't change status code
|
|
127
|
+
// Just log and close
|
|
128
|
+
console.error('Streaming error:', error);
|
|
129
|
+
if (this.isWritable()) {
|
|
130
|
+
nodeRes.end();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
isWritable() {
|
|
135
|
+
return !this.reply.sent && !this._headersSent;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get the underlying Fastify reply object
|
|
139
|
+
*/
|
|
140
|
+
get raw() {
|
|
141
|
+
return this.reply;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/fastify/adapter.ts"],"names":[],"mappings":"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"}
|