@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 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, handleWebSSE, handleWebQuery, handleWebMutation, extendContext, estimatePatchSize, createWSHandler, createStructuredLogger, createServerClientProxy, createSSEHandler, createPluginManager, createFrameworkHandler, 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, FrameworkHandlerOptions, ErrorContext, EntitiesMap, EnhanceOperationMetaContext, EmitResult, DisconnectContext, DEFAULT_WS_HANDLER_CONFIG, DEFAULT_STORAGE_CONFIG, ConnectContext, ClientSendFn, BroadcastResult, BeforeSendContext, BeforeMutationContext, AfterSendContext, AfterMutationContext };
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" && (pathname === "/" || pathname === "")) {
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylphx/lens-server",
3
- "version": "4.1.2",
3
+ "version": "4.1.3",
4
4
  "description": "Server runtime for Lens API framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,39 +1,15 @@
1
1
  /**
2
- * @sylphx/lens-server - Framework Handler Utilities
2
+ * @sylphx/lens-server - Framework Utilities
3
3
  *
4
- * Shared utilities for framework integrations (Next.js, Nuxt, SolidStart, Fresh, etc.).
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
- * // In a framework integration package
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
- }
@@ -6,17 +6,10 @@
6
6
  */
7
7
 
8
8
  // =============================================================================
9
- // Framework Handler Utilities (for framework packages)
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
- // Framework Integration Utilities (internal use)
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
@@ -1276,8 +1276,8 @@ class LensServerImpl<
1276
1276
  });
1277
1277
  }
1278
1278
 
1279
- // Operations: POST /
1280
- if (request.method === "POST" && (pathname === "/" || pathname === "")) {
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;