@sylphx/lens-server 4.1.3 → 4.1.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.
- package/dist/index.js +82 -0
- package/package.json +1 -1
- package/src/server/create.ts +95 -0
package/dist/index.js
CHANGED
|
@@ -1045,6 +1045,88 @@ class LensServerImpl {
|
|
|
1045
1045
|
headers: baseHeaders
|
|
1046
1046
|
});
|
|
1047
1047
|
}
|
|
1048
|
+
const isSseRequest = url.searchParams.get("_sse") === "1" || request.headers.get("accept") === "text/event-stream";
|
|
1049
|
+
if (request.method === "GET" && isSseRequest) {
|
|
1050
|
+
const path = pathname.replace(/^\//, "");
|
|
1051
|
+
const inputParam = url.searchParams.get("input");
|
|
1052
|
+
let input;
|
|
1053
|
+
if (inputParam) {
|
|
1054
|
+
try {
|
|
1055
|
+
input = JSON.parse(inputParam);
|
|
1056
|
+
} catch {
|
|
1057
|
+
const encoder = new TextEncoder;
|
|
1058
|
+
const errorStream = new ReadableStream({
|
|
1059
|
+
start(controller) {
|
|
1060
|
+
const data = `event: error
|
|
1061
|
+
data: ${JSON.stringify({ error: "Invalid input JSON" })}
|
|
1062
|
+
|
|
1063
|
+
`;
|
|
1064
|
+
controller.enqueue(encoder.encode(data));
|
|
1065
|
+
controller.close();
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
return new Response(errorStream, {
|
|
1069
|
+
headers: {
|
|
1070
|
+
"Content-Type": "text/event-stream",
|
|
1071
|
+
"Cache-Control": "no-cache",
|
|
1072
|
+
Connection: "keep-alive"
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
const self = this;
|
|
1078
|
+
const stream = new ReadableStream({
|
|
1079
|
+
start(controller) {
|
|
1080
|
+
const encoder = new TextEncoder;
|
|
1081
|
+
try {
|
|
1082
|
+
const result = self.execute({ path, input });
|
|
1083
|
+
if (result && typeof result === "object" && "subscribe" in result) {
|
|
1084
|
+
const observable = result;
|
|
1085
|
+
const subscription = observable.subscribe({
|
|
1086
|
+
next: (value) => {
|
|
1087
|
+
const data = `data: ${JSON.stringify(value)}
|
|
1088
|
+
|
|
1089
|
+
`;
|
|
1090
|
+
controller.enqueue(encoder.encode(data));
|
|
1091
|
+
},
|
|
1092
|
+
error: (err) => {
|
|
1093
|
+
const data = `event: error
|
|
1094
|
+
data: ${JSON.stringify({ error: err.message })}
|
|
1095
|
+
|
|
1096
|
+
`;
|
|
1097
|
+
controller.enqueue(encoder.encode(data));
|
|
1098
|
+
controller.close();
|
|
1099
|
+
},
|
|
1100
|
+
complete: () => {
|
|
1101
|
+
controller.close();
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
if (request.signal) {
|
|
1105
|
+
request.signal.addEventListener("abort", () => {
|
|
1106
|
+
subscription.unsubscribe();
|
|
1107
|
+
controller.close();
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
} catch (execError) {
|
|
1112
|
+
const errMsg = execError instanceof Error ? execError.message : "Internal error";
|
|
1113
|
+
const data = `event: error
|
|
1114
|
+
data: ${JSON.stringify({ error: errMsg })}
|
|
1115
|
+
|
|
1116
|
+
`;
|
|
1117
|
+
controller.enqueue(encoder.encode(data));
|
|
1118
|
+
controller.close();
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1122
|
+
return new Response(stream, {
|
|
1123
|
+
headers: {
|
|
1124
|
+
"Content-Type": "text/event-stream",
|
|
1125
|
+
"Cache-Control": "no-cache",
|
|
1126
|
+
Connection: "keep-alive"
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1048
1130
|
if (request.method === "POST") {
|
|
1049
1131
|
let body;
|
|
1050
1132
|
try {
|
package/package.json
CHANGED
package/src/server/create.ts
CHANGED
|
@@ -1276,6 +1276,101 @@ class LensServerImpl<
|
|
|
1276
1276
|
});
|
|
1277
1277
|
}
|
|
1278
1278
|
|
|
1279
|
+
// SSE: GET /{path}?_sse=1&input={...}
|
|
1280
|
+
// Client uses EventSource which sends GET requests with path in URL
|
|
1281
|
+
const isSseRequest =
|
|
1282
|
+
url.searchParams.get("_sse") === "1" || request.headers.get("accept") === "text/event-stream";
|
|
1283
|
+
|
|
1284
|
+
if (request.method === "GET" && isSseRequest) {
|
|
1285
|
+
// Extract path from URL (strip leading slash)
|
|
1286
|
+
const path = pathname.replace(/^\//, "");
|
|
1287
|
+
|
|
1288
|
+
// Parse input from query params
|
|
1289
|
+
const inputParam = url.searchParams.get("input");
|
|
1290
|
+
let input: unknown;
|
|
1291
|
+
if (inputParam) {
|
|
1292
|
+
try {
|
|
1293
|
+
input = JSON.parse(inputParam);
|
|
1294
|
+
} catch {
|
|
1295
|
+
// Return SSE error for malformed JSON
|
|
1296
|
+
const encoder = new TextEncoder();
|
|
1297
|
+
const errorStream = new ReadableStream({
|
|
1298
|
+
start(controller) {
|
|
1299
|
+
const data = `event: error\ndata: ${JSON.stringify({ error: "Invalid input JSON" })}\n\n`;
|
|
1300
|
+
controller.enqueue(encoder.encode(data));
|
|
1301
|
+
controller.close();
|
|
1302
|
+
},
|
|
1303
|
+
});
|
|
1304
|
+
return new Response(errorStream, {
|
|
1305
|
+
headers: {
|
|
1306
|
+
"Content-Type": "text/event-stream",
|
|
1307
|
+
"Cache-Control": "no-cache",
|
|
1308
|
+
Connection: "keep-alive",
|
|
1309
|
+
},
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// Create SSE stream
|
|
1315
|
+
const self = this;
|
|
1316
|
+
const stream = new ReadableStream({
|
|
1317
|
+
start(controller) {
|
|
1318
|
+
const encoder = new TextEncoder();
|
|
1319
|
+
|
|
1320
|
+
try {
|
|
1321
|
+
const result = self.execute({ path, input });
|
|
1322
|
+
|
|
1323
|
+
if (result && typeof result === "object" && "subscribe" in result) {
|
|
1324
|
+
const observable = result as {
|
|
1325
|
+
subscribe: (handlers: {
|
|
1326
|
+
next: (value: unknown) => void;
|
|
1327
|
+
error: (err: Error) => void;
|
|
1328
|
+
complete: () => void;
|
|
1329
|
+
}) => { unsubscribe: () => void };
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
const subscription = observable.subscribe({
|
|
1333
|
+
next: (value) => {
|
|
1334
|
+
// Send full LensResult envelope
|
|
1335
|
+
const data = `data: ${JSON.stringify(value)}\n\n`;
|
|
1336
|
+
controller.enqueue(encoder.encode(data));
|
|
1337
|
+
},
|
|
1338
|
+
error: (err) => {
|
|
1339
|
+
const data = `event: error\ndata: ${JSON.stringify({ error: err.message })}\n\n`;
|
|
1340
|
+
controller.enqueue(encoder.encode(data));
|
|
1341
|
+
controller.close();
|
|
1342
|
+
},
|
|
1343
|
+
complete: () => {
|
|
1344
|
+
controller.close();
|
|
1345
|
+
},
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
// Clean up on abort
|
|
1349
|
+
if (request.signal) {
|
|
1350
|
+
request.signal.addEventListener("abort", () => {
|
|
1351
|
+
subscription.unsubscribe();
|
|
1352
|
+
controller.close();
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
} catch (execError) {
|
|
1357
|
+
const errMsg = execError instanceof Error ? execError.message : "Internal error";
|
|
1358
|
+
const data = `event: error\ndata: ${JSON.stringify({ error: errMsg })}\n\n`;
|
|
1359
|
+
controller.enqueue(encoder.encode(data));
|
|
1360
|
+
controller.close();
|
|
1361
|
+
}
|
|
1362
|
+
},
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
return new Response(stream, {
|
|
1366
|
+
headers: {
|
|
1367
|
+
"Content-Type": "text/event-stream",
|
|
1368
|
+
"Cache-Control": "no-cache",
|
|
1369
|
+
Connection: "keep-alive",
|
|
1370
|
+
},
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1279
1374
|
// Operations: POST to any path (client sends path in body)
|
|
1280
1375
|
if (request.method === "POST") {
|
|
1281
1376
|
let body: { path?: string; input?: unknown };
|