@sylphx/lens-server 1.3.2 → 1.5.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/README.md +35 -0
- package/dist/index.d.ts +23 -109
- package/dist/index.js +58 -38
- package/package.json +37 -36
- package/src/e2e/server.test.ts +56 -45
- package/src/index.ts +26 -29
- package/src/server/create.test.ts +997 -20
- package/src/server/create.ts +82 -85
- package/src/sse/handler.ts +1 -1
- package/src/state/graph-state-manager.test.ts +566 -10
- package/src/state/graph-state-manager.ts +38 -13
- package/src/state/index.ts +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @sylphx/lens-server
|
|
2
|
+
|
|
3
|
+
Server runtime for the Lens API framework with WebSocket support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @sylphx/lens-server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createServer } from "@sylphx/lens-server";
|
|
15
|
+
import { appRouter } from "./router";
|
|
16
|
+
|
|
17
|
+
const server = createServer({ router: appRouter });
|
|
18
|
+
|
|
19
|
+
// Handle WebSocket connections
|
|
20
|
+
Bun.serve({
|
|
21
|
+
port: 3000,
|
|
22
|
+
fetch: server.fetch,
|
|
23
|
+
websocket: server.websocket,
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
|
|
29
|
+
MIT
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
Built with [@sylphx/lens-core](https://github.com/SylphxAI/Lens).
|
|
34
|
+
|
|
35
|
+
✨ Powered by Sylphx
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ContextValue, EntityDef,
|
|
1
|
+
import { InferRouterContext as InferRouterContext2, MutationDef as MutationDef2, mutation, QueryDef as QueryDef2, query, ResolverContext, ResolverFn, RouterDef as RouterDef2, RouterRoutes, router } from "@sylphx/lens-core";
|
|
2
|
+
import { ContextValue, EntityDef, InferRouterContext, MutationDef, QueryDef, Resolvers, RouterDef } from "@sylphx/lens-core";
|
|
3
3
|
import { ArrayOperation, EmitCommand, EntityKey, InternalFieldUpdate, Update } from "@sylphx/lens-core";
|
|
4
4
|
/** Client connection interface */
|
|
5
5
|
interface StateClient {
|
|
@@ -217,17 +217,21 @@ type EntitiesMap = Record<string, EntityDef<string, any>>;
|
|
|
217
217
|
type QueriesMap = Record<string, QueryDef<unknown, unknown>>;
|
|
218
218
|
/** Mutations map type */
|
|
219
219
|
type MutationsMap = Record<string, MutationDef<unknown, unknown>>;
|
|
220
|
-
/** Relations array type */
|
|
221
|
-
type RelationsArray = RelationDef<EntityDef<string, EntityDefinition>, Record<string, RelationTypeWithForeignKey>>[];
|
|
222
220
|
/** Operation metadata for handshake */
|
|
223
221
|
interface OperationMeta {
|
|
224
|
-
type: "query" | "mutation";
|
|
222
|
+
type: "query" | "mutation" | "subscription";
|
|
225
223
|
optimistic?: unknown;
|
|
226
224
|
}
|
|
227
225
|
/** Nested operations structure for handshake */
|
|
228
226
|
type OperationsMap = {
|
|
229
227
|
[key: string]: OperationMeta | OperationsMap;
|
|
230
228
|
};
|
|
229
|
+
/** Logger interface for server */
|
|
230
|
+
interface LensLogger {
|
|
231
|
+
info?: (message: string, ...args: unknown[]) => void;
|
|
232
|
+
warn?: (message: string, ...args: unknown[]) => void;
|
|
233
|
+
error?: (message: string, ...args: unknown[]) => void;
|
|
234
|
+
}
|
|
231
235
|
/** Server configuration */
|
|
232
236
|
interface LensServerConfig<
|
|
233
237
|
TContext extends ContextValue = ContextValue,
|
|
@@ -235,16 +239,16 @@ interface LensServerConfig<
|
|
|
235
239
|
> {
|
|
236
240
|
/** Entity definitions */
|
|
237
241
|
entities?: EntitiesMap;
|
|
238
|
-
/** Relation definitions */
|
|
239
|
-
relations?: RelationsArray;
|
|
240
242
|
/** Router definition (namespaced operations) - context type is inferred */
|
|
241
243
|
router?: TRouter;
|
|
242
244
|
/** Query definitions (flat, legacy) */
|
|
243
245
|
queries?: QueriesMap;
|
|
244
246
|
/** Mutation definitions (flat, legacy) */
|
|
245
247
|
mutations?: MutationsMap;
|
|
246
|
-
/**
|
|
247
|
-
resolvers?:
|
|
248
|
+
/** Field resolvers array (use lens() factory to create) */
|
|
249
|
+
resolvers?: Resolvers;
|
|
250
|
+
/** Logger for server messages (default: silent) */
|
|
251
|
+
logger?: LensLogger;
|
|
248
252
|
/** Context factory - must return the context type expected by the router */
|
|
249
253
|
context?: (req?: unknown) => TContext | Promise<TContext>;
|
|
250
254
|
/** Server version */
|
|
@@ -308,96 +312,6 @@ interface WebSocketLike {
|
|
|
308
312
|
onclose?: (() => void) | null;
|
|
309
313
|
onerror?: ((error: unknown) => void) | null;
|
|
310
314
|
}
|
|
311
|
-
declare class LensServerImpl<
|
|
312
|
-
Q extends QueriesMap = QueriesMap,
|
|
313
|
-
M extends MutationsMap = MutationsMap,
|
|
314
|
-
TContext extends ContextValue = ContextValue
|
|
315
|
-
> implements LensServer {
|
|
316
|
-
private queries;
|
|
317
|
-
private mutations;
|
|
318
|
-
private entities;
|
|
319
|
-
private resolvers?;
|
|
320
|
-
private contextFactory;
|
|
321
|
-
private version;
|
|
322
|
-
private ctx;
|
|
323
|
-
/** GraphStateManager for per-client state tracking */
|
|
324
|
-
private stateManager;
|
|
325
|
-
/** DataLoaders for N+1 batching (per-request) */
|
|
326
|
-
private loaders;
|
|
327
|
-
/** Client connections */
|
|
328
|
-
private connections;
|
|
329
|
-
private connectionCounter;
|
|
330
|
-
/** Server instance */
|
|
331
|
-
private server;
|
|
332
|
-
constructor(config: LensServerConfig<TContext> & {
|
|
333
|
-
queries?: Q;
|
|
334
|
-
mutations?: M;
|
|
335
|
-
});
|
|
336
|
-
getStateManager(): GraphStateManager;
|
|
337
|
-
/**
|
|
338
|
-
* Get server metadata for transport handshake.
|
|
339
|
-
* Used by inProcess transport for direct access.
|
|
340
|
-
*/
|
|
341
|
-
getMetadata(): ServerMetadata;
|
|
342
|
-
/**
|
|
343
|
-
* Execute operation - auto-detects query vs mutation from registered operations.
|
|
344
|
-
* Used by inProcess transport for direct server calls.
|
|
345
|
-
*/
|
|
346
|
-
execute(op: LensOperation): Promise<LensResult>;
|
|
347
|
-
/**
|
|
348
|
-
* Build nested operations map for handshake response
|
|
349
|
-
* Converts flat "user.get", "user.create" into nested { user: { get: {...}, create: {...} } }
|
|
350
|
-
*/
|
|
351
|
-
private buildOperationsMap;
|
|
352
|
-
handleWebSocket(ws: WebSocketLike): void;
|
|
353
|
-
private handleMessage;
|
|
354
|
-
private handleHandshake;
|
|
355
|
-
private handleSubscribe;
|
|
356
|
-
private executeSubscription;
|
|
357
|
-
private handleUpdateFields;
|
|
358
|
-
private handleUnsubscribe;
|
|
359
|
-
private handleQuery;
|
|
360
|
-
private handleMutation;
|
|
361
|
-
private handleDisconnect;
|
|
362
|
-
executeQuery<
|
|
363
|
-
TInput,
|
|
364
|
-
TOutput
|
|
365
|
-
>(name: string, input?: TInput): Promise<TOutput>;
|
|
366
|
-
executeMutation<
|
|
367
|
-
TInput,
|
|
368
|
-
TOutput
|
|
369
|
-
>(name: string, input: TInput): Promise<TOutput>;
|
|
370
|
-
handleRequest(req: Request): Promise<Response>;
|
|
371
|
-
listen(port: number): Promise<void>;
|
|
372
|
-
close(): Promise<void>;
|
|
373
|
-
private findConnectionByWs;
|
|
374
|
-
private getEntityNameFromOutput;
|
|
375
|
-
private getEntityNameFromMutation;
|
|
376
|
-
private extractEntities;
|
|
377
|
-
private applySelection;
|
|
378
|
-
private applySelectionToObject;
|
|
379
|
-
/**
|
|
380
|
-
* Execute entity resolvers for nested data.
|
|
381
|
-
* Processes the selection object and resolves relation fields.
|
|
382
|
-
*/
|
|
383
|
-
private executeEntityResolvers;
|
|
384
|
-
/**
|
|
385
|
-
* Get target entity name for a relation field.
|
|
386
|
-
*/
|
|
387
|
-
private getRelationTargetEntity;
|
|
388
|
-
/**
|
|
389
|
-
* Serialize entity data for transport.
|
|
390
|
-
* Auto-calls serialize() on field types (Date → ISO string, etc.)
|
|
391
|
-
*/
|
|
392
|
-
private serializeEntity;
|
|
393
|
-
/**
|
|
394
|
-
* Process query result: execute entity resolvers, apply selection, serialize
|
|
395
|
-
*/
|
|
396
|
-
private processQueryResult;
|
|
397
|
-
private computeUpdates;
|
|
398
|
-
private deepEqual;
|
|
399
|
-
private clearLoaders;
|
|
400
|
-
}
|
|
401
315
|
/**
|
|
402
316
|
* Infer input type from a query/mutation definition
|
|
403
317
|
*/
|
|
@@ -421,10 +335,9 @@ type InferOutput<T> = T extends QueryDef<unknown, infer O> ? O : T extends Mutat
|
|
|
421
335
|
* const client = createClient<Api>({ links: [...] });
|
|
422
336
|
* ```
|
|
423
337
|
*/
|
|
424
|
-
type InferApi<T
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
} : never;
|
|
338
|
+
type InferApi<T> = T extends {
|
|
339
|
+
_types: infer Types;
|
|
340
|
+
} ? Types : never;
|
|
428
341
|
/**
|
|
429
342
|
* Config helper type that infers context from router
|
|
430
343
|
*/
|
|
@@ -434,11 +347,11 @@ type ServerConfigWithInferredContext<
|
|
|
434
347
|
M extends MutationsMap = MutationsMap
|
|
435
348
|
> = {
|
|
436
349
|
entities?: EntitiesMap;
|
|
437
|
-
relations?: RelationsArray;
|
|
438
350
|
router: TRouter;
|
|
439
351
|
queries?: Q;
|
|
440
352
|
mutations?: M;
|
|
441
|
-
resolvers
|
|
353
|
+
/** Field resolvers array */
|
|
354
|
+
resolvers?: Resolvers;
|
|
442
355
|
/** Context factory - type is inferred from router's procedures */
|
|
443
356
|
context?: (req?: unknown) => InferRouterContext<TRouter> | Promise<InferRouterContext<TRouter>>;
|
|
444
357
|
version?: string;
|
|
@@ -452,11 +365,11 @@ type ServerConfigLegacy<
|
|
|
452
365
|
M extends MutationsMap = MutationsMap
|
|
453
366
|
> = {
|
|
454
367
|
entities?: EntitiesMap;
|
|
455
|
-
relations?: RelationsArray;
|
|
456
368
|
router?: undefined;
|
|
457
369
|
queries?: Q;
|
|
458
370
|
mutations?: M;
|
|
459
|
-
resolvers
|
|
371
|
+
/** Field resolvers array */
|
|
372
|
+
resolvers?: Resolvers;
|
|
460
373
|
context?: (req?: unknown) => TContext | Promise<TContext>;
|
|
461
374
|
version?: string;
|
|
462
375
|
};
|
|
@@ -484,6 +397,7 @@ declare function createServer<
|
|
|
484
397
|
M extends MutationsMap = MutationsMap
|
|
485
398
|
>(config: ServerConfigWithInferredContext<TRouter, Q, M>): LensServer & {
|
|
486
399
|
_types: {
|
|
400
|
+
router: TRouter;
|
|
487
401
|
queries: Q;
|
|
488
402
|
mutations: M;
|
|
489
403
|
context: InferRouterContext<TRouter>;
|
|
@@ -544,7 +458,7 @@ declare class SSEHandler {
|
|
|
544
458
|
* Handle new SSE connection
|
|
545
459
|
* Returns a Response with SSE stream
|
|
546
460
|
*/
|
|
547
|
-
handleConnection(
|
|
461
|
+
handleConnection(_req?: Request): Response;
|
|
548
462
|
/**
|
|
549
463
|
* Remove client and cleanup
|
|
550
464
|
*/
|
|
@@ -570,4 +484,4 @@ declare class SSEHandler {
|
|
|
570
484
|
* Create SSE handler (transport adapter)
|
|
571
485
|
*/
|
|
572
486
|
declare function createSSEHandler(config: SSEHandlerConfig): SSEHandler;
|
|
573
|
-
export { router, query, mutation, createServer, createSSEHandler, createGraphStateManager, WebSocketLike, Subscription, StateUpdateMessage, StateFullMessage, StateClient, ServerMetadata, LensServerConfig as ServerConfig, SelectionObject, SSEHandlerConfig, SSEHandler, SSEClientInfo, RouterRoutes, RouterDef2 as RouterDef, ResolverFn, ResolverContext,
|
|
487
|
+
export { router, query, mutation, createServer, createSSEHandler, createGraphStateManager, WebSocketLike, Subscription, StateUpdateMessage, StateFullMessage, StateClient, ServerMetadata, LensServerConfig as ServerConfig, SelectionObject, SSEHandlerConfig, SSEHandler, SSEClientInfo, RouterRoutes, RouterDef2 as RouterDef, ResolverFn, ResolverContext, QueryDef2 as QueryDef, QueriesMap, OperationsMap, OperationMeta, MutationsMap, MutationDef2 as MutationDef, LensServer, LensResult, LensOperation, InferRouterContext2 as InferRouterContext, InferOutput, InferInput, InferApi, GraphStateManagerConfig, GraphStateManager, EntityKey, EntitiesMap };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import {
|
|
3
|
-
query,
|
|
4
3
|
mutation,
|
|
4
|
+
query,
|
|
5
5
|
router
|
|
6
6
|
} from "@sylphx/lens-core";
|
|
7
7
|
|
|
@@ -11,15 +11,16 @@ import {
|
|
|
11
11
|
createEmit,
|
|
12
12
|
createUpdate as createUpdate2,
|
|
13
13
|
flattenRouter,
|
|
14
|
-
isBatchResolver,
|
|
15
14
|
isMutationDef,
|
|
16
15
|
isQueryDef,
|
|
17
|
-
runWithContext
|
|
16
|
+
runWithContext,
|
|
17
|
+
toResolverMap
|
|
18
18
|
} from "@sylphx/lens-core";
|
|
19
19
|
|
|
20
20
|
// src/state/graph-state-manager.ts
|
|
21
21
|
import {
|
|
22
22
|
applyUpdate,
|
|
23
|
+
computeArrayDiff,
|
|
23
24
|
createUpdate,
|
|
24
25
|
makeEntityKey
|
|
25
26
|
} from "@sylphx/lens-core";
|
|
@@ -241,14 +242,35 @@ class GraphStateManager {
|
|
|
241
242
|
if (JSON.stringify(lastState) === JSON.stringify(newArray)) {
|
|
242
243
|
return;
|
|
243
244
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
245
|
+
const diff = computeArrayDiff(lastState, newArray);
|
|
246
|
+
if (diff === null || diff.length === 0) {
|
|
247
|
+
client.send({
|
|
248
|
+
type: "update",
|
|
249
|
+
entity,
|
|
250
|
+
id,
|
|
251
|
+
updates: {
|
|
252
|
+
_items: { strategy: "value", data: newArray }
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
} else if (diff.length === 1 && diff[0].op === "replace") {
|
|
256
|
+
client.send({
|
|
257
|
+
type: "update",
|
|
258
|
+
entity,
|
|
259
|
+
id,
|
|
260
|
+
updates: {
|
|
261
|
+
_items: { strategy: "value", data: newArray }
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
} else {
|
|
265
|
+
client.send({
|
|
266
|
+
type: "update",
|
|
267
|
+
entity,
|
|
268
|
+
id,
|
|
269
|
+
updates: {
|
|
270
|
+
_items: { strategy: "array", data: diff }
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
252
274
|
clientArrayState.lastState = [...newArray];
|
|
253
275
|
}
|
|
254
276
|
getArrayState(entity, id) {
|
|
@@ -471,11 +493,13 @@ class DataLoader {
|
|
|
471
493
|
keys.forEach((key, index) => {
|
|
472
494
|
const callbacks = batch.get(key);
|
|
473
495
|
const result = results[index] ?? null;
|
|
474
|
-
|
|
496
|
+
for (const { resolve } of callbacks)
|
|
497
|
+
resolve(result);
|
|
475
498
|
});
|
|
476
499
|
} catch (error) {
|
|
477
500
|
for (const callbacks of batch.values()) {
|
|
478
|
-
|
|
501
|
+
for (const { reject } of callbacks)
|
|
502
|
+
reject(error);
|
|
479
503
|
}
|
|
480
504
|
}
|
|
481
505
|
}
|
|
@@ -483,14 +507,16 @@ class DataLoader {
|
|
|
483
507
|
this.batch.clear();
|
|
484
508
|
}
|
|
485
509
|
}
|
|
510
|
+
var noopLogger = {};
|
|
486
511
|
|
|
487
512
|
class LensServerImpl {
|
|
488
513
|
queries;
|
|
489
514
|
mutations;
|
|
490
515
|
entities;
|
|
491
|
-
|
|
516
|
+
resolverMap;
|
|
492
517
|
contextFactory;
|
|
493
518
|
version;
|
|
519
|
+
logger;
|
|
494
520
|
ctx = createContext();
|
|
495
521
|
stateManager;
|
|
496
522
|
loaders = new Map;
|
|
@@ -513,9 +539,10 @@ class LensServerImpl {
|
|
|
513
539
|
this.queries = queries;
|
|
514
540
|
this.mutations = mutations;
|
|
515
541
|
this.entities = config.entities ?? {};
|
|
516
|
-
this.
|
|
542
|
+
this.resolverMap = config.resolvers ? toResolverMap(config.resolvers) : undefined;
|
|
517
543
|
this.contextFactory = config.context ?? (() => ({}));
|
|
518
544
|
this.version = config.version ?? "1.0.0";
|
|
545
|
+
this.logger = config.logger ?? noopLogger;
|
|
519
546
|
for (const [name, def] of Object.entries(this.entities)) {
|
|
520
547
|
if (def && typeof def === "object" && !def._name) {
|
|
521
548
|
def._name = name;
|
|
@@ -594,7 +621,7 @@ class LensServerImpl {
|
|
|
594
621
|
}
|
|
595
622
|
current[parts[parts.length - 1]] = meta;
|
|
596
623
|
};
|
|
597
|
-
for (const [name,
|
|
624
|
+
for (const [name, _def] of Object.entries(this.queries)) {
|
|
598
625
|
setNested(name, { type: "query" });
|
|
599
626
|
}
|
|
600
627
|
for (const [name, def] of Object.entries(this.mutations)) {
|
|
@@ -820,7 +847,7 @@ class LensServerImpl {
|
|
|
820
847
|
try {
|
|
821
848
|
cleanup();
|
|
822
849
|
} catch (e) {
|
|
823
|
-
|
|
850
|
+
this.logger.error?.("Cleanup error:", e);
|
|
824
851
|
}
|
|
825
852
|
}
|
|
826
853
|
for (const entityKey of sub.entityKeys) {
|
|
@@ -877,7 +904,7 @@ class LensServerImpl {
|
|
|
877
904
|
try {
|
|
878
905
|
cleanup();
|
|
879
906
|
} catch (e) {
|
|
880
|
-
|
|
907
|
+
this.logger.error?.("Cleanup error:", e);
|
|
881
908
|
}
|
|
882
909
|
}
|
|
883
910
|
}
|
|
@@ -1031,7 +1058,7 @@ class LensServerImpl {
|
|
|
1031
1058
|
}
|
|
1032
1059
|
}
|
|
1033
1060
|
});
|
|
1034
|
-
|
|
1061
|
+
this.logger.info?.(`Lens server listening on port ${port}`);
|
|
1035
1062
|
}
|
|
1036
1063
|
async close() {
|
|
1037
1064
|
if (this.server && typeof this.server.stop === "function") {
|
|
@@ -1144,27 +1171,20 @@ class LensServerImpl {
|
|
|
1144
1171
|
return result;
|
|
1145
1172
|
}
|
|
1146
1173
|
async executeEntityResolvers(entityName, data, select) {
|
|
1147
|
-
if (!data || !select || !this.
|
|
1174
|
+
if (!data || !select || !this.resolverMap)
|
|
1175
|
+
return data;
|
|
1176
|
+
const resolverDef = this.resolverMap.get(entityName);
|
|
1177
|
+
if (!resolverDef)
|
|
1148
1178
|
return data;
|
|
1149
1179
|
const result = { ...data };
|
|
1180
|
+
const context = await this.contextFactory();
|
|
1150
1181
|
for (const [fieldName, fieldSelect] of Object.entries(select)) {
|
|
1151
1182
|
if (fieldSelect === false || fieldSelect === true)
|
|
1152
1183
|
continue;
|
|
1153
|
-
|
|
1154
|
-
if (!resolver)
|
|
1184
|
+
if (!resolverDef.hasField(fieldName))
|
|
1155
1185
|
continue;
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
if (!this.loaders.has(loaderKey)) {
|
|
1159
|
-
this.loaders.set(loaderKey, new DataLoader(async (parents) => {
|
|
1160
|
-
return resolver.batch(parents);
|
|
1161
|
-
}));
|
|
1162
|
-
}
|
|
1163
|
-
const loader = this.loaders.get(loaderKey);
|
|
1164
|
-
result[fieldName] = await loader.load(data);
|
|
1165
|
-
} else {
|
|
1166
|
-
result[fieldName] = await resolver(data);
|
|
1167
|
-
}
|
|
1186
|
+
const fieldArgs = typeof fieldSelect === "object" && fieldSelect !== null && "args" in fieldSelect ? fieldSelect.args ?? {} : {};
|
|
1187
|
+
result[fieldName] = await resolverDef.resolveField(fieldName, data, fieldArgs, context);
|
|
1168
1188
|
const nestedSelect = fieldSelect.select;
|
|
1169
1189
|
if (nestedSelect && result[fieldName]) {
|
|
1170
1190
|
const relationData = result[fieldName];
|
|
@@ -1228,7 +1248,7 @@ class LensServerImpl {
|
|
|
1228
1248
|
try {
|
|
1229
1249
|
result[fieldName] = fieldType.serialize(value);
|
|
1230
1250
|
} catch (error) {
|
|
1231
|
-
|
|
1251
|
+
this.logger.warn?.(`Failed to serialize field ${entityName}.${fieldName}:`, error);
|
|
1232
1252
|
result[fieldName] = value;
|
|
1233
1253
|
}
|
|
1234
1254
|
} else {
|
|
@@ -1245,7 +1265,7 @@ class LensServerImpl {
|
|
|
1245
1265
|
if (Array.isArray(data)) {
|
|
1246
1266
|
const processedItems = await Promise.all(data.map(async (item) => {
|
|
1247
1267
|
let result2 = item;
|
|
1248
|
-
if (select && this.
|
|
1268
|
+
if (select && this.resolverMap) {
|
|
1249
1269
|
result2 = await this.executeEntityResolvers(entityName, item, select);
|
|
1250
1270
|
}
|
|
1251
1271
|
if (select) {
|
|
@@ -1259,7 +1279,7 @@ class LensServerImpl {
|
|
|
1259
1279
|
return processedItems;
|
|
1260
1280
|
}
|
|
1261
1281
|
let result = data;
|
|
1262
|
-
if (select && this.
|
|
1282
|
+
if (select && this.resolverMap) {
|
|
1263
1283
|
result = await this.executeEntityResolvers(entityName, data, select);
|
|
1264
1284
|
}
|
|
1265
1285
|
if (select) {
|
|
@@ -1330,7 +1350,7 @@ class SSEHandler {
|
|
|
1330
1350
|
this.stateManager = config.stateManager;
|
|
1331
1351
|
this.heartbeatInterval = config.heartbeatInterval ?? 30000;
|
|
1332
1352
|
}
|
|
1333
|
-
handleConnection(
|
|
1353
|
+
handleConnection(_req) {
|
|
1334
1354
|
const clientId = `sse_${++this.clientCounter}_${Date.now()}`;
|
|
1335
1355
|
const encoder = new TextEncoder;
|
|
1336
1356
|
const stream = new ReadableStream({
|
package/package.json
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
2
|
+
"name": "@sylphx/lens-server",
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"description": "Server runtime for Lens API framework",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "bunup",
|
|
16
|
+
"typecheck": "tsc --noEmit",
|
|
17
|
+
"test": "bun test",
|
|
18
|
+
"prepack": "bun run build"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"lens",
|
|
26
|
+
"server",
|
|
27
|
+
"resolvers",
|
|
28
|
+
"graphql"
|
|
29
|
+
],
|
|
30
|
+
"author": "SylphxAI",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@sylphx/lens-core": "^1.9.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"typescript": "^5.9.3",
|
|
37
|
+
"zod": "^4.1.13"
|
|
38
|
+
}
|
|
38
39
|
}
|