@sylphx/lens-server 1.0.3 → 1.2.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.js CHANGED
@@ -1,286 +1,28 @@
1
- // ../core/dist/index.js
2
- import { AsyncLocalStorage } from "node:async_hooks";
3
- var ENTITY_SYMBOL = Symbol("lens:entity");
4
- var valueStrategy = {
5
- name: "value",
6
- encode(_prev, next) {
7
- return { strategy: "value", data: next };
8
- },
9
- decode(_current, update) {
10
- return update.data;
11
- },
12
- estimateSize(update) {
13
- return JSON.stringify(update.data).length;
14
- }
15
- };
16
- var deltaStrategy = {
17
- name: "delta",
18
- encode(prev, next) {
19
- const operations = computeStringDiff(prev, next);
20
- const diffSize = JSON.stringify(operations).length;
21
- const valueSize = next.length + 20;
22
- if (diffSize >= valueSize) {
23
- return { strategy: "value", data: next };
24
- }
25
- return { strategy: "delta", data: operations };
26
- },
27
- decode(current, update) {
28
- if (update.strategy === "value") {
29
- return update.data;
30
- }
31
- const operations = update.data;
32
- return applyStringDiff(current, operations);
33
- },
34
- estimateSize(update) {
35
- return JSON.stringify(update.data).length;
36
- }
37
- };
38
- function computeStringDiff(prev, next) {
39
- const operations = [];
40
- let prefixLen = 0;
41
- const minLen = Math.min(prev.length, next.length);
42
- while (prefixLen < minLen && prev[prefixLen] === next[prefixLen]) {
43
- prefixLen++;
44
- }
45
- let suffixLen = 0;
46
- const remainingPrev = prev.length - prefixLen;
47
- const remainingNext = next.length - prefixLen;
48
- const maxSuffix = Math.min(remainingPrev, remainingNext);
49
- while (suffixLen < maxSuffix && prev[prev.length - 1 - suffixLen] === next[next.length - 1 - suffixLen]) {
50
- suffixLen++;
51
- }
52
- const deleteCount = prev.length - prefixLen - suffixLen;
53
- const insertText = next.slice(prefixLen, next.length - suffixLen || undefined);
54
- if (deleteCount > 0 || insertText.length > 0) {
55
- operations.push({
56
- position: prefixLen,
57
- ...deleteCount > 0 ? { delete: deleteCount } : {},
58
- ...insertText.length > 0 ? { insert: insertText } : {}
59
- });
60
- }
61
- return operations;
62
- }
63
- function applyStringDiff(current, operations) {
64
- let result = current;
65
- const sortedOps = [...operations].sort((a, b) => b.position - a.position);
66
- for (const op of sortedOps) {
67
- const before = result.slice(0, op.position);
68
- const after = result.slice(op.position + (op.delete ?? 0));
69
- result = before + (op.insert ?? "") + after;
70
- }
71
- return result;
72
- }
73
- var patchStrategy = {
74
- name: "patch",
75
- encode(prev, next) {
76
- const operations = computeJsonPatch(prev, next);
77
- const patchSize = JSON.stringify(operations).length;
78
- const valueSize = JSON.stringify(next).length + 20;
79
- if (patchSize >= valueSize) {
80
- return { strategy: "value", data: next };
81
- }
82
- return { strategy: "patch", data: operations };
83
- },
84
- decode(current, update) {
85
- if (update.strategy === "value") {
86
- return update.data;
87
- }
88
- const operations = update.data;
89
- return applyJsonPatch(current, operations);
90
- },
91
- estimateSize(update) {
92
- return JSON.stringify(update.data).length;
93
- }
94
- };
95
- function computeJsonPatch(prev, next, basePath = "") {
96
- const operations = [];
97
- const prevObj = prev;
98
- const nextObj = next;
99
- for (const key of Object.keys(prevObj)) {
100
- if (!(key in nextObj)) {
101
- operations.push({ op: "remove", path: `${basePath}/${escapeJsonPointer(key)}` });
102
- }
103
- }
104
- for (const [key, nextValue] of Object.entries(nextObj)) {
105
- const path = `${basePath}/${escapeJsonPointer(key)}`;
106
- const prevValue = prevObj[key];
107
- if (!(key in prevObj)) {
108
- operations.push({ op: "add", path, value: nextValue });
109
- } else if (!deepEqual(prevValue, nextValue)) {
110
- if (isPlainObject(prevValue) && isPlainObject(nextValue) && !Array.isArray(prevValue) && !Array.isArray(nextValue)) {
111
- operations.push(...computeJsonPatch(prevValue, nextValue, path));
112
- } else {
113
- operations.push({ op: "replace", path, value: nextValue });
114
- }
115
- }
116
- }
117
- return operations;
118
- }
119
- function applyJsonPatch(current, operations) {
120
- const result = structuredClone(current);
121
- for (const op of operations) {
122
- const pathParts = parseJsonPointer(op.path);
123
- switch (op.op) {
124
- case "add":
125
- case "replace":
126
- setValueAtPath(result, pathParts, op.value);
127
- break;
128
- case "remove":
129
- removeValueAtPath(result, pathParts);
130
- break;
131
- case "move":
132
- if (op.from) {
133
- const fromParts = parseJsonPointer(op.from);
134
- const value = getValueAtPath(result, fromParts);
135
- removeValueAtPath(result, fromParts);
136
- setValueAtPath(result, pathParts, value);
137
- }
138
- break;
139
- case "copy":
140
- if (op.from) {
141
- const fromParts = parseJsonPointer(op.from);
142
- const value = structuredClone(getValueAtPath(result, fromParts));
143
- setValueAtPath(result, pathParts, value);
144
- }
145
- break;
146
- case "test":
147
- break;
148
- }
149
- }
150
- return result;
151
- }
152
- var THRESHOLDS = {
153
- STRING_DELTA_MIN: 100,
154
- OBJECT_PATCH_MIN: 50
155
- };
156
- function selectStrategy(prev, next) {
157
- if (typeof prev === "string" && typeof next === "string") {
158
- if (next.length >= THRESHOLDS.STRING_DELTA_MIN) {
159
- return deltaStrategy;
160
- }
161
- return valueStrategy;
162
- }
163
- if (typeof next !== "object" || next === null) {
164
- return valueStrategy;
165
- }
166
- if (isPlainObject(prev) && isPlainObject(next)) {
167
- const prevSize = JSON.stringify(prev).length;
168
- if (prevSize >= THRESHOLDS.OBJECT_PATCH_MIN) {
169
- return patchStrategy;
170
- }
171
- }
172
- return valueStrategy;
173
- }
174
- function createUpdate(prev, next) {
175
- const strategy = selectStrategy(prev, next);
176
- return strategy.encode(prev, next);
177
- }
178
- function isPlainObject(value) {
179
- return typeof value === "object" && value !== null && !Array.isArray(value);
180
- }
181
- function deepEqual(a, b) {
182
- if (a === b)
183
- return true;
184
- if (typeof a !== typeof b)
185
- return false;
186
- if (typeof a !== "object" || a === null || b === null)
187
- return false;
188
- const aKeys = Object.keys(a);
189
- const bKeys = Object.keys(b);
190
- if (aKeys.length !== bKeys.length)
191
- return false;
192
- for (const key of aKeys) {
193
- if (!deepEqual(a[key], b[key])) {
194
- return false;
195
- }
196
- }
197
- return true;
198
- }
199
- function escapeJsonPointer(str) {
200
- return str.replace(/~/g, "~0").replace(/\//g, "~1");
201
- }
202
- function parseJsonPointer(path) {
203
- if (!path || path === "/")
204
- return [];
205
- return path.slice(1).split("/").map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"));
206
- }
207
- function getValueAtPath(obj, path) {
208
- let current = obj;
209
- for (const key of path) {
210
- if (current === null || typeof current !== "object")
211
- return;
212
- current = current[key];
213
- }
214
- return current;
215
- }
216
- function setValueAtPath(obj, path, value) {
217
- if (path.length === 0)
218
- return;
219
- let current = obj;
220
- for (let i = 0;i < path.length - 1; i++) {
221
- const key = path[i];
222
- if (!(key in current) || typeof current[key] !== "object") {
223
- current[key] = {};
224
- }
225
- current = current[key];
226
- }
227
- current[path[path.length - 1]] = value;
228
- }
229
- function removeValueAtPath(obj, path) {
230
- if (path.length === 0)
231
- return;
232
- let current = obj;
233
- for (let i = 0;i < path.length - 1; i++) {
234
- const key = path[i];
235
- if (!(key in current) || typeof current[key] !== "object")
236
- return;
237
- current = current[key];
238
- }
239
- delete current[path[path.length - 1]];
240
- }
241
- function isQueryDef(value) {
242
- return typeof value === "object" && value !== null && value._type === "query";
243
- }
244
- function isMutationDef(value) {
245
- return typeof value === "object" && value !== null && value._type === "mutation";
246
- }
247
- function isRouterDef(value) {
248
- return typeof value === "object" && value !== null && value._type === "router";
249
- }
250
- function flattenRouter(routerDef, prefix = "") {
251
- const result = new Map;
252
- for (const [key, value] of Object.entries(routerDef._routes)) {
253
- const path = prefix ? `${prefix}.${key}` : key;
254
- if (isRouterDef(value)) {
255
- const nested = flattenRouter(value, path);
256
- for (const [nestedPath, procedure] of nested) {
257
- result.set(nestedPath, procedure);
258
- }
259
- } else {
260
- result.set(path, value);
261
- }
262
- }
263
- return result;
264
- }
265
- function isBatchResolver(resolver) {
266
- return typeof resolver === "object" && resolver !== null && "batch" in resolver;
267
- }
268
- var globalContextStore = new AsyncLocalStorage;
269
- function createContext() {
270
- return globalContextStore;
271
- }
272
- function runWithContext(_context, value, fn) {
273
- return globalContextStore.run(value, fn);
274
- }
275
- function makeEntityKey(entity2, id) {
276
- return `${entity2}:${id}`;
277
- }
1
+ // src/server/create.ts
2
+ import {
3
+ createContext,
4
+ createEmit,
5
+ createUpdate as createUpdate2,
6
+ flattenRouter,
7
+ isBatchResolver,
8
+ isMutationDef,
9
+ isQueryDef,
10
+ runWithContext
11
+ } from "@sylphx/lens-core";
278
12
 
279
13
  // src/state/graph-state-manager.ts
14
+ import {
15
+ createUpdate,
16
+ applyUpdate,
17
+ makeEntityKey
18
+ } from "@sylphx/lens-core";
19
+
280
20
  class GraphStateManager {
281
21
  clients = new Map;
282
22
  canonical = new Map;
23
+ canonicalArrays = new Map;
283
24
  clientStates = new Map;
25
+ clientArrayStates = new Map;
284
26
  entitySubscribers = new Map;
285
27
  config;
286
28
  constructor(config = {}) {
@@ -289,6 +31,7 @@ class GraphStateManager {
289
31
  addClient(client) {
290
32
  this.clients.set(client.id, client);
291
33
  this.clientStates.set(client.id, new Map);
34
+ this.clientArrayStates.set(client.id, new Map);
292
35
  }
293
36
  removeClient(clientId) {
294
37
  for (const [key, subscribers] of this.entitySubscribers) {
@@ -299,6 +42,7 @@ class GraphStateManager {
299
42
  }
300
43
  this.clients.delete(clientId);
301
44
  this.clientStates.delete(clientId);
45
+ this.clientArrayStates.delete(clientId);
302
46
  }
303
47
  subscribe(clientId, entity, id, fields = "*") {
304
48
  const key = this.makeKey(entity, id);
@@ -361,6 +105,148 @@ class GraphStateManager {
361
105
  this.pushToClient(clientId, entity, id, key, currentCanonical);
362
106
  }
363
107
  }
108
+ emitField(entity, id, field, update) {
109
+ const key = this.makeKey(entity, id);
110
+ let currentCanonical = this.canonical.get(key);
111
+ if (!currentCanonical) {
112
+ currentCanonical = {};
113
+ }
114
+ const oldValue = currentCanonical[field];
115
+ const newValue = applyUpdate(oldValue, update);
116
+ currentCanonical = { ...currentCanonical, [field]: newValue };
117
+ this.canonical.set(key, currentCanonical);
118
+ const subscribers = this.entitySubscribers.get(key);
119
+ if (!subscribers)
120
+ return;
121
+ for (const clientId of subscribers) {
122
+ this.pushFieldToClient(clientId, entity, id, key, field, newValue);
123
+ }
124
+ }
125
+ emitBatch(entity, id, updates) {
126
+ const key = this.makeKey(entity, id);
127
+ let currentCanonical = this.canonical.get(key);
128
+ if (!currentCanonical) {
129
+ currentCanonical = {};
130
+ }
131
+ const changedFields = [];
132
+ for (const { field, update } of updates) {
133
+ const oldValue = currentCanonical[field];
134
+ const newValue = applyUpdate(oldValue, update);
135
+ currentCanonical[field] = newValue;
136
+ changedFields.push(field);
137
+ }
138
+ this.canonical.set(key, currentCanonical);
139
+ const subscribers = this.entitySubscribers.get(key);
140
+ if (!subscribers)
141
+ return;
142
+ for (const clientId of subscribers) {
143
+ this.pushFieldsToClient(clientId, entity, id, key, changedFields, currentCanonical);
144
+ }
145
+ }
146
+ processCommand(entity, id, command) {
147
+ switch (command.type) {
148
+ case "full":
149
+ this.emit(entity, id, command.data, {
150
+ replace: command.replace
151
+ });
152
+ break;
153
+ case "field":
154
+ this.emitField(entity, id, command.field, command.update);
155
+ break;
156
+ case "batch":
157
+ this.emitBatch(entity, id, command.updates);
158
+ break;
159
+ case "array":
160
+ this.emitArrayOperation(entity, id, command.operation);
161
+ break;
162
+ }
163
+ }
164
+ emitArray(entity, id, items) {
165
+ const key = this.makeKey(entity, id);
166
+ this.canonicalArrays.set(key, [...items]);
167
+ const subscribers = this.entitySubscribers.get(key);
168
+ if (!subscribers)
169
+ return;
170
+ for (const clientId of subscribers) {
171
+ this.pushArrayToClient(clientId, entity, id, key, items);
172
+ }
173
+ }
174
+ emitArrayOperation(entity, id, operation) {
175
+ const key = this.makeKey(entity, id);
176
+ let currentArray = this.canonicalArrays.get(key);
177
+ if (!currentArray) {
178
+ currentArray = [];
179
+ }
180
+ const newArray = this.applyArrayOperation([...currentArray], operation);
181
+ this.canonicalArrays.set(key, newArray);
182
+ const subscribers = this.entitySubscribers.get(key);
183
+ if (!subscribers)
184
+ return;
185
+ for (const clientId of subscribers) {
186
+ this.pushArrayToClient(clientId, entity, id, key, newArray);
187
+ }
188
+ }
189
+ applyArrayOperation(array, operation) {
190
+ switch (operation.op) {
191
+ case "push":
192
+ return [...array, operation.item];
193
+ case "unshift":
194
+ return [operation.item, ...array];
195
+ case "insert":
196
+ return [
197
+ ...array.slice(0, operation.index),
198
+ operation.item,
199
+ ...array.slice(operation.index)
200
+ ];
201
+ case "remove":
202
+ return [...array.slice(0, operation.index), ...array.slice(operation.index + 1)];
203
+ case "removeById": {
204
+ const idx = array.findIndex((item) => typeof item === "object" && item !== null && ("id" in item) && item.id === operation.id);
205
+ if (idx === -1)
206
+ return array;
207
+ return [...array.slice(0, idx), ...array.slice(idx + 1)];
208
+ }
209
+ case "update":
210
+ return array.map((item, i) => i === operation.index ? operation.item : item);
211
+ case "updateById":
212
+ return array.map((item) => typeof item === "object" && item !== null && ("id" in item) && item.id === operation.id ? operation.item : item);
213
+ case "merge":
214
+ return array.map((item, i) => i === operation.index && typeof item === "object" && item !== null ? { ...item, ...operation.partial } : item);
215
+ case "mergeById":
216
+ return array.map((item) => typeof item === "object" && item !== null && ("id" in item) && item.id === operation.id ? { ...item, ...operation.partial } : item);
217
+ default:
218
+ return array;
219
+ }
220
+ }
221
+ pushArrayToClient(clientId, entity, id, key, newArray) {
222
+ const client = this.clients.get(clientId);
223
+ if (!client)
224
+ return;
225
+ const clientArrayStateMap = this.clientArrayStates.get(clientId);
226
+ if (!clientArrayStateMap)
227
+ return;
228
+ let clientArrayState = clientArrayStateMap.get(key);
229
+ if (!clientArrayState) {
230
+ clientArrayState = { lastState: [] };
231
+ clientArrayStateMap.set(key, clientArrayState);
232
+ }
233
+ const { lastState } = clientArrayState;
234
+ if (JSON.stringify(lastState) === JSON.stringify(newArray)) {
235
+ return;
236
+ }
237
+ client.send({
238
+ type: "update",
239
+ entity,
240
+ id,
241
+ updates: {
242
+ _items: { strategy: "value", data: newArray }
243
+ }
244
+ });
245
+ clientArrayState.lastState = [...newArray];
246
+ }
247
+ getArrayState(entity, id) {
248
+ return this.canonicalArrays.get(this.makeKey(entity, id));
249
+ }
364
250
  getState(entity, id) {
365
251
  return this.canonical.get(this.makeKey(entity, id));
366
252
  }
@@ -408,6 +294,77 @@ class GraphStateManager {
408
294
  }
409
295
  }
410
296
  }
297
+ pushFieldToClient(clientId, entity, id, key, field, newValue) {
298
+ const client = this.clients.get(clientId);
299
+ if (!client)
300
+ return;
301
+ const clientStateMap = this.clientStates.get(clientId);
302
+ if (!clientStateMap)
303
+ return;
304
+ const clientEntityState = clientStateMap.get(key);
305
+ if (!clientEntityState)
306
+ return;
307
+ const { lastState, fields } = clientEntityState;
308
+ if (fields !== "*" && !fields.has(field)) {
309
+ return;
310
+ }
311
+ const oldValue = lastState[field];
312
+ if (oldValue === newValue)
313
+ return;
314
+ if (typeof oldValue === "object" && typeof newValue === "object" && JSON.stringify(oldValue) === JSON.stringify(newValue)) {
315
+ return;
316
+ }
317
+ const update = createUpdate(oldValue, newValue);
318
+ client.send({
319
+ type: "update",
320
+ entity,
321
+ id,
322
+ updates: { [field]: update }
323
+ });
324
+ clientEntityState.lastState[field] = newValue;
325
+ }
326
+ pushFieldsToClient(clientId, entity, id, key, changedFields, newState) {
327
+ const client = this.clients.get(clientId);
328
+ if (!client)
329
+ return;
330
+ const clientStateMap = this.clientStates.get(clientId);
331
+ if (!clientStateMap)
332
+ return;
333
+ const clientEntityState = clientStateMap.get(key);
334
+ if (!clientEntityState)
335
+ return;
336
+ const { lastState, fields } = clientEntityState;
337
+ const updates = {};
338
+ let hasChanges = false;
339
+ for (const field of changedFields) {
340
+ if (fields !== "*" && !fields.has(field)) {
341
+ continue;
342
+ }
343
+ const oldValue = lastState[field];
344
+ const newValue = newState[field];
345
+ if (oldValue === newValue)
346
+ continue;
347
+ if (typeof oldValue === "object" && typeof newValue === "object" && JSON.stringify(oldValue) === JSON.stringify(newValue)) {
348
+ continue;
349
+ }
350
+ const update = createUpdate(oldValue, newValue);
351
+ updates[field] = update;
352
+ hasChanges = true;
353
+ }
354
+ if (!hasChanges)
355
+ return;
356
+ client.send({
357
+ type: "update",
358
+ entity,
359
+ id,
360
+ updates
361
+ });
362
+ for (const field of changedFields) {
363
+ if (newState[field] !== undefined) {
364
+ clientEntityState.lastState[field] = newState[field];
365
+ }
366
+ }
367
+ }
411
368
  sendInitialData(clientId, entity, id, state, fields) {
412
369
  const client = this.clients.get(clientId);
413
370
  if (!client)
@@ -460,7 +417,9 @@ class GraphStateManager {
460
417
  clear() {
461
418
  this.clients.clear();
462
419
  this.canonical.clear();
420
+ this.canonicalArrays.clear();
463
421
  this.clientStates.clear();
422
+ this.clientArrayStates.clear();
464
423
  this.entitySubscribers.clear();
465
424
  }
466
425
  }
@@ -770,21 +729,31 @@ class LensServerImpl {
770
729
  if (!resolver) {
771
730
  throw new Error(`Query ${sub.operation} has no resolver`);
772
731
  }
773
- const contextWithHelpers = {
774
- ...context,
775
- emit: emitData,
776
- onCleanup: (fn) => {
777
- sub.cleanups.push(fn);
778
- return () => {
779
- const idx = sub.cleanups.indexOf(fn);
780
- if (idx >= 0)
781
- sub.cleanups.splice(idx, 1);
782
- };
732
+ const emit = createEmit((command) => {
733
+ const entityName = this.getEntityNameFromOutput(queryDef._output);
734
+ if (entityName) {
735
+ const entities = this.extractEntities(entityName, command.type === "full" ? command.data : {});
736
+ for (const { entity, id } of entities) {
737
+ this.stateManager.processCommand(entity, id, command);
738
+ }
783
739
  }
740
+ if (command.type === "full") {
741
+ emitData(command.data);
742
+ }
743
+ });
744
+ const onCleanup = (fn) => {
745
+ sub.cleanups.push(fn);
746
+ return () => {
747
+ const idx = sub.cleanups.indexOf(fn);
748
+ if (idx >= 0)
749
+ sub.cleanups.splice(idx, 1);
750
+ };
784
751
  };
785
752
  const result = resolver({
786
753
  input: sub.input,
787
- ctx: contextWithHelpers
754
+ ctx: context,
755
+ emit,
756
+ onCleanup
788
757
  });
789
758
  if (isAsyncIterable(result)) {
790
759
  for await (const value of result) {
@@ -933,13 +902,14 @@ class LensServerImpl {
933
902
  if (!resolver) {
934
903
  throw new Error(`Query ${name} has no resolver`);
935
904
  }
936
- const resolverCtx = {
905
+ const emit = createEmit(() => {});
906
+ const onCleanup = () => () => {};
907
+ const result = resolver({
937
908
  input: cleanInput,
938
909
  ctx: context,
939
- emit: () => {},
940
- onCleanup: () => () => {}
941
- };
942
- const result = resolver(resolverCtx);
910
+ emit,
911
+ onCleanup
912
+ });
943
913
  let data;
944
914
  if (isAsyncIterable(result)) {
945
915
  for await (const value of result) {
@@ -976,9 +946,13 @@ class LensServerImpl {
976
946
  if (!resolver) {
977
947
  throw new Error(`Mutation ${name} has no resolver`);
978
948
  }
949
+ const emit = createEmit(() => {});
950
+ const onCleanup = () => () => {};
979
951
  const result = await resolver({
980
952
  input,
981
- ctx: context
953
+ ctx: context,
954
+ emit,
955
+ onCleanup
982
956
  });
983
957
  const entityName = this.getEntityNameFromMutation(name);
984
958
  const entities = this.extractEntities(entityName, result);
@@ -1301,7 +1275,7 @@ class LensServerImpl {
1301
1275
  const oldValue = oldObj[key];
1302
1276
  const newValue = newObj[key];
1303
1277
  if (!this.deepEqual(oldValue, newValue)) {
1304
- updates[key] = createUpdate(oldValue, newValue);
1278
+ updates[key] = createUpdate2(oldValue, newValue);
1305
1279
  }
1306
1280
  }
1307
1281
  return Object.keys(updates).length > 0 ? updates : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylphx/lens-server",
3
- "version": "1.0.3",
3
+ "version": "1.2.0",
4
4
  "description": "Server runtime for Lens API framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -12,8 +12,7 @@
12
12
  }
13
13
  },
14
14
  "scripts": {
15
- "build": "bun build ./src/index.ts --outdir ./dist --target node && bun run build:types",
16
- "build:types": "tsc --emitDeclarationOnly --outDir ./dist",
15
+ "build": "bunup",
17
16
  "typecheck": "tsc --noEmit",
18
17
  "test": "bun test"
19
18
  },
@@ -30,7 +29,7 @@
30
29
  "author": "SylphxAI",
31
30
  "license": "MIT",
32
31
  "dependencies": {
33
- "@sylphx/lens-core": "^1.0.3"
32
+ "@sylphx/lens-core": "^1.2.0"
34
33
  },
35
34
  "devDependencies": {
36
35
  "typescript": "^5.9.3",