@sylphx/lens-server 2.1.0 → 2.3.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/dist/index.d.ts +14 -3
- package/dist/index.js +299 -100
- package/package.json +2 -2
- package/src/e2e/server.test.ts +81 -61
- package/src/handlers/framework.ts +4 -3
- package/src/handlers/http.ts +7 -4
- package/src/handlers/ws.ts +18 -10
- package/src/server/create.test.ts +701 -47
- package/src/server/create.ts +492 -105
- package/src/server/selection.test.ts +253 -0
- package/src/server/types.ts +15 -2
package/dist/index.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ declare function extendContext<
|
|
|
41
41
|
E extends ContextValue
|
|
42
42
|
>(current: T, extension: E): T & E;
|
|
43
43
|
import { ContextValue as ContextValue3, InferRouterContext as InferRouterContext2, RouterDef as RouterDef2 } from "@sylphx/lens-core";
|
|
44
|
-
import { ContextValue as ContextValue2, EntityDef, InferRouterContext, MutationDef, OptimisticDSL, QueryDef, Resolvers, RouterDef } from "@sylphx/lens-core";
|
|
44
|
+
import { ContextValue as ContextValue2, EntityDef, InferRouterContext, MutationDef, Observable, OptimisticDSL, QueryDef, Resolvers, RouterDef } from "@sylphx/lens-core";
|
|
45
45
|
/**
|
|
46
46
|
* @sylphx/lens-server - Plugin System Types
|
|
47
47
|
*
|
|
@@ -555,8 +555,19 @@ interface WebSocketLike {
|
|
|
555
555
|
interface LensServer {
|
|
556
556
|
/** Get server metadata for transport handshake */
|
|
557
557
|
getMetadata(): ServerMetadata;
|
|
558
|
-
/**
|
|
559
|
-
|
|
558
|
+
/**
|
|
559
|
+
* Execute operation - auto-detects query vs mutation.
|
|
560
|
+
*
|
|
561
|
+
* Always returns Observable<LensResult>:
|
|
562
|
+
* - One-shot: emits once, then completes
|
|
563
|
+
* - Streaming: emits multiple times (AsyncIterable or emit-based)
|
|
564
|
+
*
|
|
565
|
+
* Usage:
|
|
566
|
+
* - HTTP: `await firstValueFrom(server.execute(op))`
|
|
567
|
+
* - WS/SSE: `server.execute(op).subscribe(...)`
|
|
568
|
+
* - direct: pass through Observable directly
|
|
569
|
+
*/
|
|
570
|
+
execute(op: LensOperation): Observable<LensResult>;
|
|
560
571
|
/**
|
|
561
572
|
* Register a client connection.
|
|
562
573
|
* Call when a client connects via WebSocket/SSE.
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,8 @@ import {
|
|
|
40
40
|
isEntityDef,
|
|
41
41
|
isMutationDef,
|
|
42
42
|
isQueryDef,
|
|
43
|
-
toResolverMap
|
|
43
|
+
toResolverMap,
|
|
44
|
+
valuesEqual
|
|
44
45
|
} from "@sylphx/lens-core";
|
|
45
46
|
|
|
46
47
|
// src/plugin/types.ts
|
|
@@ -215,6 +216,9 @@ class DataLoader {
|
|
|
215
216
|
}
|
|
216
217
|
|
|
217
218
|
// src/server/selection.ts
|
|
219
|
+
function isNestedSelection(value) {
|
|
220
|
+
return typeof value === "object" && value !== null && (("input" in value) || ("select" in value)) && !Array.isArray(value);
|
|
221
|
+
}
|
|
218
222
|
function extractSelect(value) {
|
|
219
223
|
if (value === true)
|
|
220
224
|
return null;
|
|
@@ -257,6 +261,25 @@ function applySelection(data, select) {
|
|
|
257
261
|
}
|
|
258
262
|
return result;
|
|
259
263
|
}
|
|
264
|
+
function extractNestedInputs(select, prefix = "") {
|
|
265
|
+
const inputs = new Map;
|
|
266
|
+
for (const [key, value] of Object.entries(select)) {
|
|
267
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
268
|
+
if (isNestedSelection(value) && value.input) {
|
|
269
|
+
inputs.set(path, value.input);
|
|
270
|
+
}
|
|
271
|
+
if (typeof value === "object" && value !== null) {
|
|
272
|
+
const nestedSelect = extractSelect(value);
|
|
273
|
+
if (nestedSelect) {
|
|
274
|
+
const nestedInputs = extractNestedInputs(nestedSelect, path);
|
|
275
|
+
for (const [nestedPath, nestedInput] of nestedInputs) {
|
|
276
|
+
inputs.set(nestedPath, nestedInput);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return inputs;
|
|
282
|
+
}
|
|
260
283
|
|
|
261
284
|
// src/server/create.ts
|
|
262
285
|
function isAsyncIterable(value) {
|
|
@@ -337,106 +360,253 @@ class LensServerImpl {
|
|
|
337
360
|
operations: this.buildOperationsMap()
|
|
338
361
|
};
|
|
339
362
|
}
|
|
340
|
-
|
|
363
|
+
execute(op) {
|
|
341
364
|
const { path, input } = op;
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
} catch (error) {
|
|
353
|
-
return { error: error instanceof Error ? error : new Error(String(error)) };
|
|
365
|
+
const isQuery = !!this.queries[path];
|
|
366
|
+
const isMutation = !!this.mutations[path];
|
|
367
|
+
if (!isQuery && !isMutation) {
|
|
368
|
+
return {
|
|
369
|
+
subscribe: (observer) => {
|
|
370
|
+
observer.next?.({ error: new Error(`Operation not found: ${path}`) });
|
|
371
|
+
observer.complete?.();
|
|
372
|
+
return { unsubscribe: () => {} };
|
|
373
|
+
}
|
|
374
|
+
};
|
|
354
375
|
}
|
|
376
|
+
return this.executeAsObservable(path, input, isQuery);
|
|
355
377
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
378
|
+
executeAsObservable(path, input, isQuery) {
|
|
379
|
+
return {
|
|
380
|
+
subscribe: (observer) => {
|
|
381
|
+
let cancelled = false;
|
|
382
|
+
let currentState;
|
|
383
|
+
let lastEmittedResult;
|
|
384
|
+
const cleanups = [];
|
|
385
|
+
const emitIfChanged = (data) => {
|
|
386
|
+
if (cancelled)
|
|
387
|
+
return;
|
|
388
|
+
if (valuesEqual(data, lastEmittedResult))
|
|
389
|
+
return;
|
|
390
|
+
lastEmittedResult = data;
|
|
391
|
+
observer.next?.({ data });
|
|
392
|
+
};
|
|
393
|
+
(async () => {
|
|
394
|
+
try {
|
|
395
|
+
const def = isQuery ? this.queries[path] : this.mutations[path];
|
|
396
|
+
if (!def) {
|
|
397
|
+
observer.next?.({ error: new Error(`Operation not found: ${path}`) });
|
|
398
|
+
observer.complete?.();
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
let select;
|
|
402
|
+
let cleanInput = input;
|
|
403
|
+
if (isQuery && input && typeof input === "object" && "$select" in input) {
|
|
404
|
+
const { $select, ...rest } = input;
|
|
405
|
+
select = $select;
|
|
406
|
+
cleanInput = Object.keys(rest).length > 0 ? rest : undefined;
|
|
407
|
+
}
|
|
408
|
+
if (def._input && cleanInput !== undefined) {
|
|
409
|
+
const result = def._input.safeParse(cleanInput);
|
|
410
|
+
if (!result.success) {
|
|
411
|
+
observer.next?.({
|
|
412
|
+
error: new Error(`Invalid input: ${JSON.stringify(result.error)}`)
|
|
413
|
+
});
|
|
414
|
+
observer.complete?.();
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
const context = await this.contextFactory();
|
|
419
|
+
await runWithContext(this.ctx, context, async () => {
|
|
420
|
+
const resolver = def._resolve;
|
|
421
|
+
if (!resolver) {
|
|
422
|
+
observer.next?.({ error: new Error(`Operation ${path} has no resolver`) });
|
|
423
|
+
observer.complete?.();
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
let emitProcessing = false;
|
|
427
|
+
const emitQueue = [];
|
|
428
|
+
const processEmitQueue = async () => {
|
|
429
|
+
if (emitProcessing || cancelled)
|
|
430
|
+
return;
|
|
431
|
+
emitProcessing = true;
|
|
432
|
+
while (emitQueue.length > 0 && !cancelled) {
|
|
433
|
+
const command = emitQueue.shift();
|
|
434
|
+
currentState = this.applyEmitCommand(command, currentState);
|
|
435
|
+
const fieldEmitFactory = isQuery ? this.createFieldEmitFactory(() => currentState, (state) => {
|
|
436
|
+
currentState = state;
|
|
437
|
+
}, emitIfChanged, select, context, onCleanup) : undefined;
|
|
438
|
+
const processed = isQuery ? await this.processQueryResult(path, currentState, select, context, onCleanup, fieldEmitFactory) : currentState;
|
|
439
|
+
emitIfChanged(processed);
|
|
440
|
+
}
|
|
441
|
+
emitProcessing = false;
|
|
442
|
+
};
|
|
443
|
+
const emitHandler = (command) => {
|
|
444
|
+
if (cancelled)
|
|
445
|
+
return;
|
|
446
|
+
emitQueue.push(command);
|
|
447
|
+
processEmitQueue().catch((err) => {
|
|
448
|
+
if (!cancelled) {
|
|
449
|
+
observer.next?.({ error: err instanceof Error ? err : new Error(String(err)) });
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
};
|
|
453
|
+
const emit = createEmit(emitHandler);
|
|
454
|
+
const onCleanup = (fn) => {
|
|
455
|
+
cleanups.push(fn);
|
|
456
|
+
return () => {
|
|
457
|
+
const idx = cleanups.indexOf(fn);
|
|
458
|
+
if (idx >= 0)
|
|
459
|
+
cleanups.splice(idx, 1);
|
|
460
|
+
};
|
|
461
|
+
};
|
|
462
|
+
const createFieldEmit = isQuery ? this.createFieldEmitFactory(() => currentState, (state) => {
|
|
463
|
+
currentState = state;
|
|
464
|
+
}, emitIfChanged, select, context, onCleanup) : undefined;
|
|
465
|
+
const lensContext = { ...context, emit, onCleanup };
|
|
466
|
+
const result = resolver({ input: cleanInput, ctx: lensContext });
|
|
467
|
+
if (isAsyncIterable(result)) {
|
|
468
|
+
for await (const value of result) {
|
|
469
|
+
if (cancelled)
|
|
470
|
+
break;
|
|
471
|
+
currentState = value;
|
|
472
|
+
const processed = await this.processQueryResult(path, value, select, context, onCleanup, createFieldEmit);
|
|
473
|
+
emitIfChanged(processed);
|
|
474
|
+
}
|
|
475
|
+
if (!cancelled) {
|
|
476
|
+
observer.complete?.();
|
|
477
|
+
}
|
|
478
|
+
} else {
|
|
479
|
+
const value = await result;
|
|
480
|
+
currentState = value;
|
|
481
|
+
const processed = isQuery ? await this.processQueryResult(path, value, select, context, onCleanup, createFieldEmit) : value;
|
|
482
|
+
emitIfChanged(processed);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
} catch (error) {
|
|
486
|
+
if (!cancelled) {
|
|
487
|
+
observer.next?.({ error: error instanceof Error ? error : new Error(String(error)) });
|
|
488
|
+
observer.complete?.();
|
|
489
|
+
}
|
|
490
|
+
} finally {
|
|
491
|
+
this.clearLoaders();
|
|
388
492
|
}
|
|
389
|
-
|
|
390
|
-
|
|
493
|
+
})();
|
|
494
|
+
return {
|
|
495
|
+
unsubscribe: () => {
|
|
496
|
+
cancelled = true;
|
|
497
|
+
for (const fn of cleanups) {
|
|
498
|
+
fn();
|
|
499
|
+
}
|
|
391
500
|
}
|
|
392
|
-
}
|
|
393
|
-
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
applyEmitCommand(command, state) {
|
|
506
|
+
switch (command.type) {
|
|
507
|
+
case "full":
|
|
508
|
+
if (command.replace) {
|
|
509
|
+
return command.data;
|
|
394
510
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
511
|
+
if (state && typeof state === "object" && typeof command.data === "object") {
|
|
512
|
+
return { ...state, ...command.data };
|
|
513
|
+
}
|
|
514
|
+
return command.data;
|
|
515
|
+
case "field":
|
|
516
|
+
if (state && typeof state === "object") {
|
|
517
|
+
return {
|
|
518
|
+
...state,
|
|
519
|
+
[command.field]: command.update.data
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
return { [command.field]: command.update.data };
|
|
523
|
+
case "batch":
|
|
524
|
+
if (state && typeof state === "object") {
|
|
525
|
+
const result = { ...state };
|
|
526
|
+
for (const update of command.updates) {
|
|
527
|
+
result[update.field] = update.update.data;
|
|
528
|
+
}
|
|
529
|
+
return result;
|
|
530
|
+
}
|
|
531
|
+
return state;
|
|
532
|
+
case "array": {
|
|
533
|
+
const arr = Array.isArray(state) ? [...state] : [];
|
|
534
|
+
const op = command.operation;
|
|
535
|
+
switch (op.op) {
|
|
536
|
+
case "push":
|
|
537
|
+
return [...arr, op.item];
|
|
538
|
+
case "unshift":
|
|
539
|
+
return [op.item, ...arr];
|
|
540
|
+
case "insert":
|
|
541
|
+
arr.splice(op.index, 0, op.item);
|
|
542
|
+
return arr;
|
|
543
|
+
case "remove":
|
|
544
|
+
arr.splice(op.index, 1);
|
|
545
|
+
return arr;
|
|
546
|
+
case "update":
|
|
547
|
+
arr[op.index] = op.item;
|
|
548
|
+
return arr;
|
|
549
|
+
default:
|
|
550
|
+
return arr;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
default:
|
|
554
|
+
return state;
|
|
399
555
|
}
|
|
400
556
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
557
|
+
createFieldEmitFactory(getCurrentState, setCurrentState, notifyObserver, select, context, onCleanup) {
|
|
558
|
+
return (fieldPath) => {
|
|
559
|
+
if (!fieldPath)
|
|
560
|
+
return;
|
|
561
|
+
return (newValue) => {
|
|
562
|
+
const state = getCurrentState();
|
|
563
|
+
if (!state || typeof state !== "object")
|
|
564
|
+
return;
|
|
565
|
+
const updatedState = this.setFieldByPath(state, fieldPath, newValue);
|
|
566
|
+
setCurrentState(updatedState);
|
|
567
|
+
(async () => {
|
|
568
|
+
try {
|
|
569
|
+
const nestedInputs = select ? extractNestedInputs(select) : undefined;
|
|
570
|
+
const processed = await this.resolveEntityFields(updatedState, nestedInputs, context, "", onCleanup, this.createFieldEmitFactory(getCurrentState, setCurrentState, notifyObserver, select, context, onCleanup));
|
|
571
|
+
const result = select ? applySelection(processed, select) : processed;
|
|
572
|
+
notifyObserver(result);
|
|
573
|
+
} catch (err) {
|
|
574
|
+
console.error(`Field emit error at path "${fieldPath}":`, err);
|
|
575
|
+
}
|
|
576
|
+
})();
|
|
577
|
+
};
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
setFieldByPath(obj, path, value) {
|
|
581
|
+
const parts = path.split(".");
|
|
582
|
+
if (parts.length === 1) {
|
|
583
|
+
return { ...obj, [path]: value };
|
|
410
584
|
}
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const onCleanup = () => () => {};
|
|
419
|
-
const lensContext = { ...context, emit, onCleanup };
|
|
420
|
-
return await resolver({ input, ctx: lensContext });
|
|
421
|
-
});
|
|
422
|
-
} finally {
|
|
423
|
-
this.clearLoaders();
|
|
585
|
+
const [first, ...rest] = parts;
|
|
586
|
+
const nested = obj[first];
|
|
587
|
+
if (nested && typeof nested === "object") {
|
|
588
|
+
return {
|
|
589
|
+
...obj,
|
|
590
|
+
[first]: this.setFieldByPath(nested, rest.join("."), value)
|
|
591
|
+
};
|
|
424
592
|
}
|
|
593
|
+
return obj;
|
|
425
594
|
}
|
|
426
|
-
async processQueryResult(_operationName, data, select) {
|
|
595
|
+
async processQueryResult(_operationName, data, select, context, onCleanup, createFieldEmit) {
|
|
427
596
|
if (!data)
|
|
428
597
|
return data;
|
|
429
|
-
const
|
|
598
|
+
const nestedInputs = select ? extractNestedInputs(select) : undefined;
|
|
599
|
+
const processed = await this.resolveEntityFields(data, nestedInputs, context, "", onCleanup, createFieldEmit);
|
|
430
600
|
if (select) {
|
|
431
601
|
return applySelection(processed, select);
|
|
432
602
|
}
|
|
433
603
|
return processed;
|
|
434
604
|
}
|
|
435
|
-
async resolveEntityFields(data) {
|
|
605
|
+
async resolveEntityFields(data, nestedInputs, context, fieldPath = "", onCleanup, createFieldEmit) {
|
|
436
606
|
if (!data || !this.resolverMap)
|
|
437
607
|
return data;
|
|
438
608
|
if (Array.isArray(data)) {
|
|
439
|
-
return Promise.all(data.map((item) => this.resolveEntityFields(item)));
|
|
609
|
+
return Promise.all(data.map((item) => this.resolveEntityFields(item, nestedInputs, context, fieldPath, onCleanup, createFieldEmit)));
|
|
440
610
|
}
|
|
441
611
|
if (typeof data !== "object")
|
|
442
612
|
return data;
|
|
@@ -452,15 +622,31 @@ class LensServerImpl {
|
|
|
452
622
|
const field = String(fieldName);
|
|
453
623
|
if (resolverDef.isExposed(field))
|
|
454
624
|
continue;
|
|
625
|
+
const currentPath = fieldPath ? `${fieldPath}.${field}` : field;
|
|
626
|
+
const args = nestedInputs?.get(currentPath) ?? {};
|
|
627
|
+
const hasArgs = Object.keys(args).length > 0;
|
|
455
628
|
const existingValue = result[field];
|
|
456
629
|
if (existingValue !== undefined) {
|
|
457
|
-
result[field] = await this.resolveEntityFields(existingValue);
|
|
630
|
+
result[field] = await this.resolveEntityFields(existingValue, nestedInputs, context, currentPath, onCleanup, createFieldEmit);
|
|
458
631
|
continue;
|
|
459
632
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
633
|
+
if (hasArgs || context) {
|
|
634
|
+
try {
|
|
635
|
+
const extendedCtx = {
|
|
636
|
+
...context ?? {},
|
|
637
|
+
emit: createFieldEmit(currentPath),
|
|
638
|
+
onCleanup
|
|
639
|
+
};
|
|
640
|
+
result[field] = await resolverDef.resolveField(field, obj, args, extendedCtx);
|
|
641
|
+
} catch {
|
|
642
|
+
result[field] = null;
|
|
643
|
+
}
|
|
644
|
+
} else {
|
|
645
|
+
const loaderKey = `${typeName}.${field}`;
|
|
646
|
+
const loader = this.getOrCreateLoaderForField(loaderKey, resolverDef, field);
|
|
647
|
+
result[field] = await loader.load(obj);
|
|
648
|
+
}
|
|
649
|
+
result[field] = await this.resolveEntityFields(result[field], nestedInputs, context, currentPath, onCleanup, createFieldEmit);
|
|
464
650
|
}
|
|
465
651
|
return result;
|
|
466
652
|
}
|
|
@@ -469,15 +655,23 @@ class LensServerImpl {
|
|
|
469
655
|
return obj.__typename;
|
|
470
656
|
if ("_type" in obj)
|
|
471
657
|
return obj._type;
|
|
658
|
+
let bestMatch;
|
|
472
659
|
for (const [name, def] of Object.entries(this.entities)) {
|
|
473
|
-
if (isEntityDef(def)
|
|
474
|
-
|
|
660
|
+
if (!isEntityDef(def))
|
|
661
|
+
continue;
|
|
662
|
+
const score = this.getEntityMatchScore(obj, def);
|
|
663
|
+
if (score >= 0.5 && (!bestMatch || score > bestMatch.score)) {
|
|
664
|
+
bestMatch = { name, score };
|
|
475
665
|
}
|
|
476
666
|
}
|
|
477
|
-
return;
|
|
667
|
+
return bestMatch?.name;
|
|
478
668
|
}
|
|
479
|
-
|
|
480
|
-
|
|
669
|
+
getEntityMatchScore(obj, entityDef) {
|
|
670
|
+
const fieldNames = Object.keys(entityDef.fields);
|
|
671
|
+
if (fieldNames.length === 0)
|
|
672
|
+
return 0;
|
|
673
|
+
const matchingFields = fieldNames.filter((field) => (field in obj));
|
|
674
|
+
return matchingFields.length / fieldNames.length;
|
|
481
675
|
}
|
|
482
676
|
getOrCreateLoaderForField(loaderKey, resolverDef, fieldName) {
|
|
483
677
|
let loader = this.loaders.get(loaderKey);
|
|
@@ -716,6 +910,7 @@ function createSSEHandler(config = {}) {
|
|
|
716
910
|
}
|
|
717
911
|
|
|
718
912
|
// src/handlers/http.ts
|
|
913
|
+
import { firstValueFrom } from "@sylphx/lens-core";
|
|
719
914
|
function createHTTPHandler(server, options = {}) {
|
|
720
915
|
const { pathPrefix = "", cors } = options;
|
|
721
916
|
const corsHeaders = {
|
|
@@ -755,10 +950,10 @@ function createHTTPHandler(server, options = {}) {
|
|
|
755
950
|
}
|
|
756
951
|
});
|
|
757
952
|
}
|
|
758
|
-
const result2 = await server.execute({
|
|
953
|
+
const result2 = await firstValueFrom(server.execute({
|
|
759
954
|
path: operationPath2,
|
|
760
955
|
input: body.input
|
|
761
|
-
});
|
|
956
|
+
}));
|
|
762
957
|
if (result2.error) {
|
|
763
958
|
return new Response(JSON.stringify({ error: result2.error.message }), {
|
|
764
959
|
status: 500,
|
|
@@ -826,6 +1021,7 @@ function createHandler(server, options = {}) {
|
|
|
826
1021
|
return result;
|
|
827
1022
|
}
|
|
828
1023
|
// src/handlers/framework.ts
|
|
1024
|
+
import { firstValueFrom as firstValueFrom2 } from "@sylphx/lens-core";
|
|
829
1025
|
function createServerClientProxy(server) {
|
|
830
1026
|
function createProxy(path) {
|
|
831
1027
|
return new Proxy(() => {}, {
|
|
@@ -839,7 +1035,7 @@ function createServerClientProxy(server) {
|
|
|
839
1035
|
},
|
|
840
1036
|
async apply(_, __, args) {
|
|
841
1037
|
const input = args[0];
|
|
842
|
-
const result = await server.execute({ path, input });
|
|
1038
|
+
const result = await firstValueFrom2(server.execute({ path, input }));
|
|
843
1039
|
if (result.error) {
|
|
844
1040
|
throw result.error;
|
|
845
1041
|
}
|
|
@@ -853,7 +1049,7 @@ async function handleWebQuery(server, path, url) {
|
|
|
853
1049
|
try {
|
|
854
1050
|
const inputParam = url.searchParams.get("input");
|
|
855
1051
|
const input = inputParam ? JSON.parse(inputParam) : undefined;
|
|
856
|
-
const result = await server.execute({ path, input });
|
|
1052
|
+
const result = await firstValueFrom2(server.execute({ path, input }));
|
|
857
1053
|
if (result.error) {
|
|
858
1054
|
return Response.json({ error: result.error.message }, { status: 400 });
|
|
859
1055
|
}
|
|
@@ -866,7 +1062,7 @@ async function handleWebMutation(server, path, request) {
|
|
|
866
1062
|
try {
|
|
867
1063
|
const body = await request.json();
|
|
868
1064
|
const input = body.input;
|
|
869
|
-
const result = await server.execute({ path, input });
|
|
1065
|
+
const result = await firstValueFrom2(server.execute({ path, input }));
|
|
870
1066
|
if (result.error) {
|
|
871
1067
|
return Response.json({ error: result.error.message }, { status: 400 });
|
|
872
1068
|
}
|
|
@@ -938,6 +1134,9 @@ function createFrameworkHandler(server, options = {}) {
|
|
|
938
1134
|
};
|
|
939
1135
|
}
|
|
940
1136
|
// src/handlers/ws.ts
|
|
1137
|
+
import {
|
|
1138
|
+
firstValueFrom as firstValueFrom3
|
|
1139
|
+
} from "@sylphx/lens-core";
|
|
941
1140
|
function createWSHandler(server, options = {}) {
|
|
942
1141
|
const { logger = {} } = options;
|
|
943
1142
|
const connections = new Map;
|
|
@@ -1014,7 +1213,7 @@ function createWSHandler(server, options = {}) {
|
|
|
1014
1213
|
const { id, operation, input, fields } = message;
|
|
1015
1214
|
let result;
|
|
1016
1215
|
try {
|
|
1017
|
-
result = await server.execute({ path: operation, input });
|
|
1216
|
+
result = await firstValueFrom3(server.execute({ path: operation, input }));
|
|
1018
1217
|
if (result.error) {
|
|
1019
1218
|
conn.ws.send(JSON.stringify({
|
|
1020
1219
|
type: "error",
|
|
@@ -1140,10 +1339,10 @@ function createWSHandler(server, options = {}) {
|
|
|
1140
1339
|
}
|
|
1141
1340
|
async function handleQuery(conn, message) {
|
|
1142
1341
|
try {
|
|
1143
|
-
const result = await server.execute({
|
|
1342
|
+
const result = await firstValueFrom3(server.execute({
|
|
1144
1343
|
path: message.operation,
|
|
1145
1344
|
input: message.input
|
|
1146
|
-
});
|
|
1345
|
+
}));
|
|
1147
1346
|
if (result.error) {
|
|
1148
1347
|
conn.ws.send(JSON.stringify({
|
|
1149
1348
|
type: "error",
|
|
@@ -1168,10 +1367,10 @@ function createWSHandler(server, options = {}) {
|
|
|
1168
1367
|
}
|
|
1169
1368
|
async function handleMutation(conn, message) {
|
|
1170
1369
|
try {
|
|
1171
|
-
const result = await server.execute({
|
|
1370
|
+
const result = await firstValueFrom3(server.execute({
|
|
1172
1371
|
path: message.operation,
|
|
1173
1372
|
input: message.input
|
|
1174
|
-
});
|
|
1373
|
+
}));
|
|
1175
1374
|
if (result.error) {
|
|
1176
1375
|
conn.ws.send(JSON.stringify({
|
|
1177
1376
|
type: "error",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylphx/lens-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Server runtime for Lens API framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"author": "SylphxAI",
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@sylphx/lens-core": "^2.0
|
|
33
|
+
"@sylphx/lens-core": "^2.2.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"typescript": "^5.9.3",
|