@sylphx/lens-server 2.8.0 → 2.8.1

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 CHANGED
@@ -708,11 +708,16 @@ class LensServerImpl {
708
708
  return (fieldPath) => {
709
709
  if (!fieldPath)
710
710
  return;
711
- return (newValue) => {
712
- const state = getCurrentState();
713
- if (!state || typeof state !== "object")
711
+ const state = getCurrentState();
712
+ const currentFieldValue = state ? this.getFieldByPath(state, fieldPath) : undefined;
713
+ const isArray = Array.isArray(currentFieldValue);
714
+ const emitHandler = (command) => {
715
+ const fullState = getCurrentState();
716
+ if (!fullState || typeof fullState !== "object")
714
717
  return;
715
- const updatedState = this.setFieldByPath(state, fieldPath, newValue);
718
+ const fieldValue = this.getFieldByPath(fullState, fieldPath);
719
+ const newFieldValue = this.applyEmitCommand(command, fieldValue);
720
+ const updatedState = this.setFieldByPath(fullState, fieldPath, newFieldValue);
716
721
  setCurrentState(updatedState);
717
722
  (async () => {
718
723
  try {
@@ -725,8 +730,21 @@ class LensServerImpl {
725
730
  }
726
731
  })();
727
732
  };
733
+ return createEmit(emitHandler, isArray);
728
734
  };
729
735
  }
736
+ getFieldByPath(obj, path) {
737
+ if (!obj || typeof obj !== "object")
738
+ return;
739
+ const parts = path.split(".");
740
+ let current = obj;
741
+ for (const part of parts) {
742
+ if (!current || typeof current !== "object")
743
+ return;
744
+ current = current[part];
745
+ }
746
+ return current;
747
+ }
730
748
  setFieldByPath(obj, path, value) {
731
749
  const parts = path.split(".");
732
750
  if (parts.length === 1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylphx/lens-server",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
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.6.0"
33
+ "@sylphx/lens-core": "^2.6.1"
34
34
  },
35
35
  "devDependencies": {
36
36
  "typescript": "^5.9.3",
@@ -19,6 +19,7 @@ import {
19
19
  type ContextValue,
20
20
  createEmit,
21
21
  createResolverFromEntity,
22
+ type Emit,
22
23
  type EmitCommand,
23
24
  type EntityDef,
24
25
  flattenRouter,
@@ -764,7 +765,7 @@ class LensServerImpl<
764
765
 
765
766
  /**
766
767
  * Factory type for creating field-level emit handlers.
767
- * Each field gets its own emit that updates just that field path.
768
+ * Each field gets its own emit with full Emit<T> API (.delta, .patch, .push, etc).
768
769
  */
769
770
  private createFieldEmitFactory(
770
771
  getCurrentState: () => unknown,
@@ -773,19 +774,29 @@ class LensServerImpl<
773
774
  select: SelectionObject | undefined,
774
775
  context: TContext | undefined,
775
776
  onCleanup: ((fn: () => void) => void) | undefined,
776
- ): (fieldPath: string) => ((value: unknown) => void) | undefined {
777
+ ): (fieldPath: string) => Emit<unknown> | undefined {
777
778
  return (fieldPath: string) => {
778
779
  if (!fieldPath) return undefined;
779
780
 
780
- return (newValue: unknown) => {
781
- // Get current state and update the field at the given path
782
- const state = getCurrentState();
783
- if (!state || typeof state !== "object") return;
781
+ // Determine if field value is an array (check current state)
782
+ const state = getCurrentState();
783
+ const currentFieldValue = state ? this.getFieldByPath(state, fieldPath) : undefined;
784
+ const isArray = Array.isArray(currentFieldValue);
784
785
 
786
+ // Create emit handler that applies commands to the field's value
787
+ const emitHandler = (command: EmitCommand) => {
788
+ const fullState = getCurrentState();
789
+ if (!fullState || typeof fullState !== "object") return;
790
+
791
+ // Get current field value and apply command to it
792
+ const fieldValue = this.getFieldByPath(fullState, fieldPath);
793
+ const newFieldValue = this.applyEmitCommand(command, fieldValue);
794
+
795
+ // Update state with new field value
785
796
  const updatedState = this.setFieldByPath(
786
- state as Record<string, unknown>,
797
+ fullState as Record<string, unknown>,
787
798
  fieldPath,
788
- newValue,
799
+ newFieldValue,
789
800
  );
790
801
  setCurrentState(updatedState);
791
802
 
@@ -816,9 +827,25 @@ class LensServerImpl<
816
827
  }
817
828
  })();
818
829
  };
830
+
831
+ return createEmit<unknown>(emitHandler, isArray);
819
832
  };
820
833
  }
821
834
 
835
+ /**
836
+ * Get a value at a nested path in an object.
837
+ */
838
+ private getFieldByPath(obj: unknown, path: string): unknown {
839
+ if (!obj || typeof obj !== "object") return undefined;
840
+ const parts = path.split(".");
841
+ let current: unknown = obj;
842
+ for (const part of parts) {
843
+ if (!current || typeof current !== "object") return undefined;
844
+ current = (current as Record<string, unknown>)[part];
845
+ }
846
+ return current;
847
+ }
848
+
822
849
  /**
823
850
  * Set a value at a nested path in an object.
824
851
  * Creates a shallow copy at each level.
@@ -850,7 +877,7 @@ class LensServerImpl<
850
877
  select?: SelectionObject,
851
878
  context?: TContext,
852
879
  onCleanup?: (fn: () => void) => void,
853
- createFieldEmit?: (fieldPath: string) => ((value: unknown) => void) | undefined,
880
+ createFieldEmit?: (fieldPath: string) => Emit<unknown> | undefined,
854
881
  ): Promise<T> {
855
882
  if (!data) return data;
856
883
 
@@ -888,7 +915,7 @@ class LensServerImpl<
888
915
  context?: TContext,
889
916
  fieldPath = "",
890
917
  onCleanup?: (fn: () => void) => void,
891
- createFieldEmit?: (fieldPath: string) => ((value: unknown) => void) | undefined,
918
+ createFieldEmit?: (fieldPath: string) => Emit<unknown> | undefined,
892
919
  ): Promise<T> {
893
920
  if (!data || !this.resolverMap) return data;
894
921