@sylphx/lens-server 4.1.2 → 4.1.3
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/dist/index.d.ts +1 -65
- package/dist/index.js +1 -134
- package/package.json +1 -1
- package/src/handlers/framework.ts +5 -279
- package/src/handlers/index.ts +2 -9
- package/src/index.ts +2 -9
- package/src/server/create.ts +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -735,70 +735,6 @@ declare function createApp<
|
|
|
735
735
|
*/
|
|
736
736
|
declare function createServerClientProxy(server: LensServer): unknown;
|
|
737
737
|
/**
|
|
738
|
-
* Handle a query request using standard Web Request/Response API.
|
|
739
|
-
*
|
|
740
|
-
* Expects input in URL search params as JSON string.
|
|
741
|
-
*
|
|
742
|
-
* @example
|
|
743
|
-
* ```typescript
|
|
744
|
-
* // GET /api/lens/user.get?input={"id":"123"}
|
|
745
|
-
* const response = await handleWebQuery(server, 'user.get', url);
|
|
746
|
-
* ```
|
|
747
|
-
*/
|
|
748
|
-
declare function handleWebQuery(server: LensServer, path: string, url: URL): Promise<Response>;
|
|
749
|
-
/**
|
|
750
|
-
* Handle a mutation request using standard Web Request/Response API.
|
|
751
|
-
*
|
|
752
|
-
* Expects input in request body as JSON.
|
|
753
|
-
*
|
|
754
|
-
* @example
|
|
755
|
-
* ```typescript
|
|
756
|
-
* // POST /api/lens/user.create with body { "input": { "name": "John" } }
|
|
757
|
-
* const response = await handleWebMutation(server, 'user.create', request);
|
|
758
|
-
* ```
|
|
759
|
-
*/
|
|
760
|
-
declare function handleWebMutation(server: LensServer, path: string, request: Request): Promise<Response>;
|
|
761
|
-
/**
|
|
762
|
-
* Handle an SSE subscription request using standard Web Request/Response API.
|
|
763
|
-
*
|
|
764
|
-
* Creates a ReadableStream that emits SSE events from the subscription.
|
|
765
|
-
*
|
|
766
|
-
* @example
|
|
767
|
-
* ```typescript
|
|
768
|
-
* // GET /api/lens/events.stream with Accept: text/event-stream
|
|
769
|
-
* const response = handleWebSSE(server, 'events.stream', url, request.signal);
|
|
770
|
-
* ```
|
|
771
|
-
*/
|
|
772
|
-
declare function handleWebSSE(server: LensServer, path: string, url: URL, signal?: AbortSignal): Response;
|
|
773
|
-
/**
|
|
774
|
-
* Options for creating a framework handler.
|
|
775
|
-
*/
|
|
776
|
-
interface FrameworkHandlerOptions {
|
|
777
|
-
/** Base path to strip from request URLs */
|
|
778
|
-
basePath?: string;
|
|
779
|
-
}
|
|
780
|
-
/**
|
|
781
|
-
* Create a complete request handler for Web standard Request/Response.
|
|
782
|
-
*
|
|
783
|
-
* Handles:
|
|
784
|
-
* - GET requests → Query execution
|
|
785
|
-
* - POST requests → Mutation execution
|
|
786
|
-
* - SSE requests (Accept: text/event-stream) → Subscriptions
|
|
787
|
-
*
|
|
788
|
-
* @example
|
|
789
|
-
* ```typescript
|
|
790
|
-
* const handler = createFrameworkHandler(server, { basePath: '/api/lens' });
|
|
791
|
-
*
|
|
792
|
-
* // In Next.js App Router:
|
|
793
|
-
* const GET = handler;
|
|
794
|
-
* const POST = handler;
|
|
795
|
-
*
|
|
796
|
-
* // In Fresh:
|
|
797
|
-
* const handler = { GET: lensHandler, POST: lensHandler };
|
|
798
|
-
* ```
|
|
799
|
-
*/
|
|
800
|
-
declare function createFrameworkHandler(server: LensServer, options?: FrameworkHandlerOptions): (request: Request) => Promise<Response>;
|
|
801
|
-
/**
|
|
802
738
|
* @sylphx/lens-server - SSE Handler
|
|
803
739
|
*
|
|
804
740
|
* Pure transport handler for Server-Sent Events.
|
|
@@ -1627,4 +1563,4 @@ declare function toBasicLogger(structuredLogger: StructuredLogger): {
|
|
|
1627
1563
|
warn: (message: string, ...args: unknown[]) => void;
|
|
1628
1564
|
error: (message: string, ...args: unknown[]) => void;
|
|
1629
1565
|
};
|
|
1630
|
-
export { useContext, tryUseContext, toBasicLogger, runWithContextAsync, runWithContext, router, query, prettyOutput, optimisticPlugin, opLog, mutation, memoryStorage, jsonOutput, isOptimisticPlugin, isOpLogPlugin, hasContext,
|
|
1566
|
+
export { useContext, tryUseContext, toBasicLogger, runWithContextAsync, runWithContext, router, query, prettyOutput, optimisticPlugin, opLog, mutation, memoryStorage, jsonOutput, isOptimisticPlugin, isOpLogPlugin, hasContext, extendContext, estimatePatchSize, createWSHandler, createStructuredLogger, createServerClientProxy, createSSEHandler, createPluginManager, createContext, createApp, coalescePatches, WebSocketLike, WebSocketContext, WSHandlerOptions, WSHandlerConfig, WSHandler, UnsubscribeContext, SubscribeContext, StructuredLoggerOptions, StructuredLogger, StoredPatchEntry, StoredEntityState, ServerPlugin, ServerMetadata, LensServerConfig as ServerConfig, SelectionObject, SSEHandlerConfig as SSEHandlerOptions, SSEHandlerConfig, SSEHandler, SSEClient, RouterRoutes, RouterDef3 as RouterDef, RequestContext, QueryDef2 as QueryDef, QueriesMap, PluginManager, PerformanceContext, OptimisticPluginOptions, OperationsMap, OperationMeta, OperationLog, OpLogStorageConfig, OpLogStorage, OpLogPlugin, OpLogOptions, MutationsMap, MutationDef2 as MutationDef, LogOutput, LogLevel, LogEntry, LogContext, LensServer, LensResult, LensOperation, InferRouterContext3 as InferRouterContext, InferOutput, InferInput, InferApi, ErrorContext, EntitiesMap, EnhanceOperationMetaContext, EmitResult, DisconnectContext, DEFAULT_WS_HANDLER_CONFIG, DEFAULT_STORAGE_CONFIG, ConnectContext, ClientSendFn, BroadcastResult, BeforeSendContext, BeforeMutationContext, AfterSendContext, AfterMutationContext };
|
package/dist/index.js
CHANGED
|
@@ -1045,7 +1045,7 @@ class LensServerImpl {
|
|
|
1045
1045
|
headers: baseHeaders
|
|
1046
1046
|
});
|
|
1047
1047
|
}
|
|
1048
|
-
if (request.method === "POST"
|
|
1048
|
+
if (request.method === "POST") {
|
|
1049
1049
|
let body;
|
|
1050
1050
|
try {
|
|
1051
1051
|
body = await request.json();
|
|
@@ -1126,135 +1126,6 @@ function createServerClientProxy(server) {
|
|
|
1126
1126
|
}
|
|
1127
1127
|
return createProxy("");
|
|
1128
1128
|
}
|
|
1129
|
-
async function handleWebQuery(server, path, url) {
|
|
1130
|
-
try {
|
|
1131
|
-
const inputParam = url.searchParams.get("input");
|
|
1132
|
-
const input = inputParam ? JSON.parse(inputParam) : undefined;
|
|
1133
|
-
const result = await firstValueFrom2(server.execute({ path, input }));
|
|
1134
|
-
if (isError2(result)) {
|
|
1135
|
-
return Response.json({ error: result.error }, { status: 400 });
|
|
1136
|
-
}
|
|
1137
|
-
if (isSnapshot2(result)) {
|
|
1138
|
-
return Response.json(result);
|
|
1139
|
-
}
|
|
1140
|
-
return Response.json(result);
|
|
1141
|
-
} catch (error) {
|
|
1142
|
-
return Response.json({ error: error instanceof Error ? error.message : "Unknown error" }, { status: 500 });
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
async function handleWebMutation(server, path, request) {
|
|
1146
|
-
try {
|
|
1147
|
-
const body = await request.json();
|
|
1148
|
-
const input = body.input;
|
|
1149
|
-
const result = await firstValueFrom2(server.execute({ path, input }));
|
|
1150
|
-
if (isError2(result)) {
|
|
1151
|
-
return Response.json({ error: result.error }, { status: 400 });
|
|
1152
|
-
}
|
|
1153
|
-
if (isSnapshot2(result)) {
|
|
1154
|
-
return Response.json(result);
|
|
1155
|
-
}
|
|
1156
|
-
return Response.json(result);
|
|
1157
|
-
} catch (error) {
|
|
1158
|
-
return Response.json({ error: error instanceof Error ? error.message : "Unknown error" }, { status: 500 });
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
function handleWebSSE(server, path, url, signal) {
|
|
1162
|
-
const inputParam = url.searchParams.get("input");
|
|
1163
|
-
let input;
|
|
1164
|
-
if (inputParam) {
|
|
1165
|
-
try {
|
|
1166
|
-
input = JSON.parse(inputParam);
|
|
1167
|
-
} catch (parseError) {
|
|
1168
|
-
const encoder = new TextEncoder;
|
|
1169
|
-
const errorStream = new ReadableStream({
|
|
1170
|
-
start(controller) {
|
|
1171
|
-
const errMsg = parseError instanceof Error ? parseError.message : "Invalid JSON";
|
|
1172
|
-
const data = `event: error
|
|
1173
|
-
data: ${JSON.stringify({ error: `Invalid input JSON: ${errMsg}` })}
|
|
1174
|
-
|
|
1175
|
-
`;
|
|
1176
|
-
controller.enqueue(encoder.encode(data));
|
|
1177
|
-
controller.close();
|
|
1178
|
-
}
|
|
1179
|
-
});
|
|
1180
|
-
return new Response(errorStream, {
|
|
1181
|
-
headers: {
|
|
1182
|
-
"Content-Type": "text/event-stream",
|
|
1183
|
-
"Cache-Control": "no-cache",
|
|
1184
|
-
Connection: "keep-alive"
|
|
1185
|
-
}
|
|
1186
|
-
});
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
const stream = new ReadableStream({
|
|
1190
|
-
start(controller) {
|
|
1191
|
-
const encoder = new TextEncoder;
|
|
1192
|
-
try {
|
|
1193
|
-
const result = server.execute({ path, input });
|
|
1194
|
-
if (result && typeof result === "object" && "subscribe" in result) {
|
|
1195
|
-
const observable = result;
|
|
1196
|
-
const subscription = observable.subscribe({
|
|
1197
|
-
next: (value) => {
|
|
1198
|
-
const data = `data: ${JSON.stringify(value)}
|
|
1199
|
-
|
|
1200
|
-
`;
|
|
1201
|
-
controller.enqueue(encoder.encode(data));
|
|
1202
|
-
},
|
|
1203
|
-
error: (err) => {
|
|
1204
|
-
const data = `event: error
|
|
1205
|
-
data: ${JSON.stringify({ error: err.message })}
|
|
1206
|
-
|
|
1207
|
-
`;
|
|
1208
|
-
controller.enqueue(encoder.encode(data));
|
|
1209
|
-
controller.close();
|
|
1210
|
-
},
|
|
1211
|
-
complete: () => {
|
|
1212
|
-
controller.close();
|
|
1213
|
-
}
|
|
1214
|
-
});
|
|
1215
|
-
if (signal) {
|
|
1216
|
-
signal.addEventListener("abort", () => {
|
|
1217
|
-
subscription.unsubscribe();
|
|
1218
|
-
controller.close();
|
|
1219
|
-
});
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
} catch (execError) {
|
|
1223
|
-
const errMsg = execError instanceof Error ? execError.message : "Internal error";
|
|
1224
|
-
const data = `event: error
|
|
1225
|
-
data: ${JSON.stringify({ error: errMsg })}
|
|
1226
|
-
|
|
1227
|
-
`;
|
|
1228
|
-
controller.enqueue(encoder.encode(data));
|
|
1229
|
-
controller.close();
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
});
|
|
1233
|
-
return new Response(stream, {
|
|
1234
|
-
headers: {
|
|
1235
|
-
"Content-Type": "text/event-stream",
|
|
1236
|
-
"Cache-Control": "no-cache",
|
|
1237
|
-
Connection: "keep-alive"
|
|
1238
|
-
}
|
|
1239
|
-
});
|
|
1240
|
-
}
|
|
1241
|
-
function createFrameworkHandler(server, options = {}) {
|
|
1242
|
-
const basePath = options.basePath ?? "";
|
|
1243
|
-
return async (request) => {
|
|
1244
|
-
const url = new URL(request.url);
|
|
1245
|
-
const path = url.pathname.replace(basePath, "").replace(/^\//, "");
|
|
1246
|
-
if (request.headers.get("accept") === "text/event-stream") {
|
|
1247
|
-
return handleWebSSE(server, path, url, request.signal);
|
|
1248
|
-
}
|
|
1249
|
-
if (request.method === "GET") {
|
|
1250
|
-
return handleWebQuery(server, path, url);
|
|
1251
|
-
}
|
|
1252
|
-
if (request.method === "POST") {
|
|
1253
|
-
return handleWebMutation(server, path, request);
|
|
1254
|
-
}
|
|
1255
|
-
return new Response("Method not allowed", { status: 405 });
|
|
1256
|
-
};
|
|
1257
|
-
}
|
|
1258
1129
|
// src/sse/handler.ts
|
|
1259
1130
|
class SSEHandler {
|
|
1260
1131
|
heartbeatInterval;
|
|
@@ -2818,9 +2689,6 @@ export {
|
|
|
2818
2689
|
isOptimisticPlugin,
|
|
2819
2690
|
isOpLogPlugin,
|
|
2820
2691
|
hasContext,
|
|
2821
|
-
handleWebSSE,
|
|
2822
|
-
handleWebQuery,
|
|
2823
|
-
handleWebMutation,
|
|
2824
2692
|
extendContext,
|
|
2825
2693
|
estimatePatchSize,
|
|
2826
2694
|
createWSHandler,
|
|
@@ -2828,7 +2696,6 @@ export {
|
|
|
2828
2696
|
createServerClientProxy,
|
|
2829
2697
|
createSSEHandler,
|
|
2830
2698
|
createPluginManager,
|
|
2831
|
-
createFrameworkHandler,
|
|
2832
2699
|
createContext,
|
|
2833
2700
|
createApp,
|
|
2834
2701
|
coalescePatches,
|
package/package.json
CHANGED
|
@@ -1,39 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @sylphx/lens-server - Framework
|
|
2
|
+
* @sylphx/lens-server - Framework Utilities
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* These provide common implementations that framework packages can use instead of
|
|
6
|
-
* duplicating the same logic.
|
|
4
|
+
* Utilities for framework integrations (Next.js, Nuxt, SolidStart, Fresh, etc.).
|
|
7
5
|
*
|
|
8
6
|
* @example
|
|
9
7
|
* ```typescript
|
|
10
|
-
*
|
|
11
|
-
* import {
|
|
12
|
-
* createServerClientProxy,
|
|
13
|
-
* handleWebQuery,
|
|
14
|
-
* handleWebMutation,
|
|
15
|
-
* handleWebSSE,
|
|
16
|
-
* } from '@sylphx/lens-server';
|
|
8
|
+
* import { createServerClientProxy } from '@sylphx/lens-server';
|
|
17
9
|
*
|
|
10
|
+
* // Create a typed proxy for direct server-side calls
|
|
18
11
|
* const serverClient = createServerClientProxy(server);
|
|
19
|
-
*
|
|
20
|
-
* function createHandler(server, basePath) {
|
|
21
|
-
* return async (request: Request) => {
|
|
22
|
-
* const url = new URL(request.url);
|
|
23
|
-
* const path = url.pathname.replace(basePath, '').replace(/^\//, '');
|
|
24
|
-
*
|
|
25
|
-
* if (request.headers.get('accept') === 'text/event-stream') {
|
|
26
|
-
* return handleWebSSE(server, path, url, request.signal);
|
|
27
|
-
* }
|
|
28
|
-
* if (request.method === 'GET') {
|
|
29
|
-
* return handleWebQuery(server, path, url);
|
|
30
|
-
* }
|
|
31
|
-
* if (request.method === 'POST') {
|
|
32
|
-
* return handleWebMutation(server, path, request);
|
|
33
|
-
* }
|
|
34
|
-
* return new Response('Method not allowed', { status: 405 });
|
|
35
|
-
* };
|
|
36
|
-
* }
|
|
12
|
+
* const users = await serverClient.user.list();
|
|
37
13
|
* ```
|
|
38
14
|
*/
|
|
39
15
|
|
|
@@ -93,253 +69,3 @@ export function createServerClientProxy(server: LensServer): unknown {
|
|
|
93
69
|
|
|
94
70
|
return createProxy("");
|
|
95
71
|
}
|
|
96
|
-
|
|
97
|
-
// =============================================================================
|
|
98
|
-
// Web Request Handlers
|
|
99
|
-
// =============================================================================
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Handle a query request using standard Web Request/Response API.
|
|
103
|
-
*
|
|
104
|
-
* Expects input in URL search params as JSON string.
|
|
105
|
-
*
|
|
106
|
-
* @example
|
|
107
|
-
* ```typescript
|
|
108
|
-
* // GET /api/lens/user.get?input={"id":"123"}
|
|
109
|
-
* const response = await handleWebQuery(server, 'user.get', url);
|
|
110
|
-
* ```
|
|
111
|
-
*/
|
|
112
|
-
export async function handleWebQuery(
|
|
113
|
-
server: LensServer,
|
|
114
|
-
path: string,
|
|
115
|
-
url: URL,
|
|
116
|
-
): Promise<Response> {
|
|
117
|
-
try {
|
|
118
|
-
const inputParam = url.searchParams.get("input");
|
|
119
|
-
const input = inputParam ? JSON.parse(inputParam) : undefined;
|
|
120
|
-
|
|
121
|
-
const result = await firstValueFrom(server.execute({ path, input }));
|
|
122
|
-
|
|
123
|
-
if (isError(result)) {
|
|
124
|
-
return Response.json({ error: result.error }, { status: 400 });
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (isSnapshot(result)) {
|
|
128
|
-
return Response.json(result);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// ops message - forward as-is
|
|
132
|
-
return Response.json(result);
|
|
133
|
-
} catch (error) {
|
|
134
|
-
return Response.json(
|
|
135
|
-
{ error: error instanceof Error ? error.message : "Unknown error" },
|
|
136
|
-
{ status: 500 },
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Handle a mutation request using standard Web Request/Response API.
|
|
143
|
-
*
|
|
144
|
-
* Expects input in request body as JSON.
|
|
145
|
-
*
|
|
146
|
-
* @example
|
|
147
|
-
* ```typescript
|
|
148
|
-
* // POST /api/lens/user.create with body { "input": { "name": "John" } }
|
|
149
|
-
* const response = await handleWebMutation(server, 'user.create', request);
|
|
150
|
-
* ```
|
|
151
|
-
*/
|
|
152
|
-
export async function handleWebMutation(
|
|
153
|
-
server: LensServer,
|
|
154
|
-
path: string,
|
|
155
|
-
request: Request,
|
|
156
|
-
): Promise<Response> {
|
|
157
|
-
try {
|
|
158
|
-
const body = (await request.json()) as { input?: unknown };
|
|
159
|
-
const input = body.input;
|
|
160
|
-
|
|
161
|
-
const result = await firstValueFrom(server.execute({ path, input }));
|
|
162
|
-
|
|
163
|
-
if (isError(result)) {
|
|
164
|
-
return Response.json({ error: result.error }, { status: 400 });
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (isSnapshot(result)) {
|
|
168
|
-
return Response.json(result);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// ops message - forward as-is
|
|
172
|
-
return Response.json(result);
|
|
173
|
-
} catch (error) {
|
|
174
|
-
return Response.json(
|
|
175
|
-
{ error: error instanceof Error ? error.message : "Unknown error" },
|
|
176
|
-
{ status: 500 },
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Handle an SSE subscription request using standard Web Request/Response API.
|
|
183
|
-
*
|
|
184
|
-
* Creates a ReadableStream that emits SSE events from the subscription.
|
|
185
|
-
*
|
|
186
|
-
* @example
|
|
187
|
-
* ```typescript
|
|
188
|
-
* // GET /api/lens/events.stream with Accept: text/event-stream
|
|
189
|
-
* const response = handleWebSSE(server, 'events.stream', url, request.signal);
|
|
190
|
-
* ```
|
|
191
|
-
*/
|
|
192
|
-
export function handleWebSSE(
|
|
193
|
-
server: LensServer,
|
|
194
|
-
path: string,
|
|
195
|
-
url: URL,
|
|
196
|
-
signal?: AbortSignal,
|
|
197
|
-
): Response {
|
|
198
|
-
const inputParam = url.searchParams.get("input");
|
|
199
|
-
|
|
200
|
-
// Parse input with error handling
|
|
201
|
-
let input: unknown;
|
|
202
|
-
if (inputParam) {
|
|
203
|
-
try {
|
|
204
|
-
input = JSON.parse(inputParam);
|
|
205
|
-
} catch (parseError) {
|
|
206
|
-
// Return error response for malformed JSON
|
|
207
|
-
const encoder = new TextEncoder();
|
|
208
|
-
const errorStream = new ReadableStream({
|
|
209
|
-
start(controller) {
|
|
210
|
-
const errMsg = parseError instanceof Error ? parseError.message : "Invalid JSON";
|
|
211
|
-
const data = `event: error\ndata: ${JSON.stringify({ error: `Invalid input JSON: ${errMsg}` })}\n\n`;
|
|
212
|
-
controller.enqueue(encoder.encode(data));
|
|
213
|
-
controller.close();
|
|
214
|
-
},
|
|
215
|
-
});
|
|
216
|
-
return new Response(errorStream, {
|
|
217
|
-
headers: {
|
|
218
|
-
"Content-Type": "text/event-stream",
|
|
219
|
-
"Cache-Control": "no-cache",
|
|
220
|
-
Connection: "keep-alive",
|
|
221
|
-
},
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const stream = new ReadableStream({
|
|
227
|
-
start(controller) {
|
|
228
|
-
const encoder = new TextEncoder();
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
const result = server.execute({ path, input });
|
|
232
|
-
|
|
233
|
-
if (result && typeof result === "object" && "subscribe" in result) {
|
|
234
|
-
const observable = result as {
|
|
235
|
-
subscribe: (handlers: {
|
|
236
|
-
next: (value: unknown) => void;
|
|
237
|
-
error: (err: Error) => void;
|
|
238
|
-
complete: () => void;
|
|
239
|
-
}) => { unsubscribe: () => void };
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const subscription = observable.subscribe({
|
|
243
|
-
next: (value) => {
|
|
244
|
-
// Send full LensResult envelope (e.g. { "$": "snapshot", "data": ... })
|
|
245
|
-
// Client expects the complete message, not just the inner data
|
|
246
|
-
const data = `data: ${JSON.stringify(value)}\n\n`;
|
|
247
|
-
controller.enqueue(encoder.encode(data));
|
|
248
|
-
},
|
|
249
|
-
error: (err) => {
|
|
250
|
-
const data = `event: error\ndata: ${JSON.stringify({ error: err.message })}\n\n`;
|
|
251
|
-
controller.enqueue(encoder.encode(data));
|
|
252
|
-
controller.close();
|
|
253
|
-
},
|
|
254
|
-
complete: () => {
|
|
255
|
-
controller.close();
|
|
256
|
-
},
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
// Clean up on abort
|
|
260
|
-
if (signal) {
|
|
261
|
-
signal.addEventListener("abort", () => {
|
|
262
|
-
subscription.unsubscribe();
|
|
263
|
-
controller.close();
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
} catch (execError) {
|
|
268
|
-
// Handle synchronous errors from server.execute() or subscribe()
|
|
269
|
-
const errMsg = execError instanceof Error ? execError.message : "Internal error";
|
|
270
|
-
const data = `event: error\ndata: ${JSON.stringify({ error: errMsg })}\n\n`;
|
|
271
|
-
controller.enqueue(encoder.encode(data));
|
|
272
|
-
controller.close();
|
|
273
|
-
}
|
|
274
|
-
},
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
return new Response(stream, {
|
|
278
|
-
headers: {
|
|
279
|
-
"Content-Type": "text/event-stream",
|
|
280
|
-
"Cache-Control": "no-cache",
|
|
281
|
-
Connection: "keep-alive",
|
|
282
|
-
},
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// =============================================================================
|
|
287
|
-
// Full Handler Factory
|
|
288
|
-
// =============================================================================
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Options for creating a framework handler.
|
|
292
|
-
*/
|
|
293
|
-
export interface FrameworkHandlerOptions {
|
|
294
|
-
/** Base path to strip from request URLs */
|
|
295
|
-
basePath?: string;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Create a complete request handler for Web standard Request/Response.
|
|
300
|
-
*
|
|
301
|
-
* Handles:
|
|
302
|
-
* - GET requests → Query execution
|
|
303
|
-
* - POST requests → Mutation execution
|
|
304
|
-
* - SSE requests (Accept: text/event-stream) → Subscriptions
|
|
305
|
-
*
|
|
306
|
-
* @example
|
|
307
|
-
* ```typescript
|
|
308
|
-
* const handler = createFrameworkHandler(server, { basePath: '/api/lens' });
|
|
309
|
-
*
|
|
310
|
-
* // In Next.js App Router:
|
|
311
|
-
* export const GET = handler;
|
|
312
|
-
* export const POST = handler;
|
|
313
|
-
*
|
|
314
|
-
* // In Fresh:
|
|
315
|
-
* export const handler = { GET: lensHandler, POST: lensHandler };
|
|
316
|
-
* ```
|
|
317
|
-
*/
|
|
318
|
-
export function createFrameworkHandler(
|
|
319
|
-
server: LensServer,
|
|
320
|
-
options: FrameworkHandlerOptions = {},
|
|
321
|
-
): (request: Request) => Promise<Response> {
|
|
322
|
-
const basePath = options.basePath ?? "";
|
|
323
|
-
|
|
324
|
-
return async (request: Request): Promise<Response> => {
|
|
325
|
-
const url = new URL(request.url);
|
|
326
|
-
const path = url.pathname.replace(basePath, "").replace(/^\//, "");
|
|
327
|
-
|
|
328
|
-
// Handle SSE subscription
|
|
329
|
-
if (request.headers.get("accept") === "text/event-stream") {
|
|
330
|
-
return handleWebSSE(server, path, url, request.signal);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Handle query (GET)
|
|
334
|
-
if (request.method === "GET") {
|
|
335
|
-
return handleWebQuery(server, path, url);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Handle mutation (POST)
|
|
339
|
-
if (request.method === "POST") {
|
|
340
|
-
return handleWebMutation(server, path, request);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return new Response("Method not allowed", { status: 405 });
|
|
344
|
-
};
|
|
345
|
-
}
|
package/src/handlers/index.ts
CHANGED
|
@@ -6,17 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// =============================================================================
|
|
9
|
-
//
|
|
9
|
+
// Server Client Proxy (for framework packages)
|
|
10
10
|
// =============================================================================
|
|
11
11
|
|
|
12
|
-
export {
|
|
13
|
-
createFrameworkHandler,
|
|
14
|
-
createServerClientProxy,
|
|
15
|
-
type FrameworkHandlerOptions,
|
|
16
|
-
handleWebMutation,
|
|
17
|
-
handleWebQuery,
|
|
18
|
-
handleWebSSE,
|
|
19
|
-
} from "./framework.js";
|
|
12
|
+
export { createServerClientProxy } from "./framework.js";
|
|
20
13
|
|
|
21
14
|
// =============================================================================
|
|
22
15
|
// SSE Handler
|
package/src/index.ts
CHANGED
|
@@ -93,17 +93,10 @@ export {
|
|
|
93
93
|
} from "./handlers/index.js";
|
|
94
94
|
|
|
95
95
|
// =============================================================================
|
|
96
|
-
//
|
|
96
|
+
// Server Client Proxy (for framework packages)
|
|
97
97
|
// =============================================================================
|
|
98
98
|
|
|
99
|
-
export {
|
|
100
|
-
createFrameworkHandler,
|
|
101
|
-
createServerClientProxy,
|
|
102
|
-
type FrameworkHandlerOptions,
|
|
103
|
-
handleWebMutation,
|
|
104
|
-
handleWebQuery,
|
|
105
|
-
handleWebSSE,
|
|
106
|
-
} from "./handlers/index.js";
|
|
99
|
+
export { createServerClientProxy } from "./handlers/index.js";
|
|
107
100
|
|
|
108
101
|
// =============================================================================
|
|
109
102
|
// Plugin System
|
package/src/server/create.ts
CHANGED
|
@@ -1276,8 +1276,8 @@ class LensServerImpl<
|
|
|
1276
1276
|
});
|
|
1277
1277
|
}
|
|
1278
1278
|
|
|
1279
|
-
// Operations: POST
|
|
1280
|
-
if (request.method === "POST"
|
|
1279
|
+
// Operations: POST to any path (client sends path in body)
|
|
1280
|
+
if (request.method === "POST") {
|
|
1281
1281
|
let body: { path?: string; input?: unknown };
|
|
1282
1282
|
try {
|
|
1283
1283
|
body = (await request.json()) as typeof body;
|