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