freesail 0.0.1 → 0.1.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 +190 -5
- package/docs/A2UX_Protocol.md +183 -0
- package/docs/Agents.md +218 -0
- package/docs/Architecture.md +285 -0
- package/docs/CatalogReference.md +377 -0
- package/docs/GettingStarted.md +230 -0
- package/examples/demo/package.json +21 -0
- package/examples/demo/public/index.html +381 -0
- package/examples/demo/server.js +253 -0
- package/package.json +38 -5
- package/packages/core/package.json +48 -0
- package/packages/core/src/functions.ts +403 -0
- package/packages/core/src/index.ts +214 -0
- package/packages/core/src/parser.ts +270 -0
- package/packages/core/src/protocol.ts +254 -0
- package/packages/core/src/store.ts +452 -0
- package/packages/core/src/transport.ts +439 -0
- package/packages/core/src/types.ts +209 -0
- package/packages/core/tsconfig.json +10 -0
- package/packages/lit-ui/package.json +44 -0
- package/packages/lit-ui/src/catalogs/standard/catalog.json +405 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Badge.ts +96 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Button.ts +147 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Card.ts +78 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Checkbox.ts +94 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Column.ts +66 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Divider.ts +59 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Image.ts +54 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Input.ts +125 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Progress.ts +79 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Row.ts +68 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Select.ts +110 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Spacer.ts +37 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Spinner.ts +76 -0
- package/packages/lit-ui/src/catalogs/standard/elements/Text.ts +86 -0
- package/packages/lit-ui/src/catalogs/standard/elements/index.ts +18 -0
- package/packages/lit-ui/src/catalogs/standard/index.ts +17 -0
- package/packages/lit-ui/src/index.ts +84 -0
- package/packages/lit-ui/src/renderer.ts +211 -0
- package/packages/lit-ui/src/types.ts +49 -0
- package/packages/lit-ui/src/utils/define-props.ts +157 -0
- package/packages/lit-ui/src/utils/index.ts +2 -0
- package/packages/lit-ui/src/utils/registry.ts +139 -0
- package/packages/lit-ui/tsconfig.json +11 -0
- package/packages/server/package.json +61 -0
- package/packages/server/src/adapters/index.ts +5 -0
- package/packages/server/src/adapters/langchain.ts +175 -0
- package/packages/server/src/adapters/openai.ts +209 -0
- package/packages/server/src/catalog-loader.ts +311 -0
- package/packages/server/src/index.ts +142 -0
- package/packages/server/src/stream.ts +329 -0
- package/packages/server/tsconfig.json +11 -0
- package/tsconfig.base.json +23 -0
- package/index.js +0 -3
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surface Store
|
|
3
|
+
*
|
|
4
|
+
* Reactive state management for A2UX surfaces.
|
|
5
|
+
* Manages component trees, data models, and subscriptions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { A2UXComponent, WatchSurfaceResponseMessage } from './types.js';
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Types
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
export interface Surface {
|
|
15
|
+
/** Surface unique identifier */
|
|
16
|
+
surfaceId: string;
|
|
17
|
+
/** Catalog ID for component definitions */
|
|
18
|
+
catalogId: string;
|
|
19
|
+
/** Component tree (flat adjacency list) */
|
|
20
|
+
components: Map<string, A2UXComponent>;
|
|
21
|
+
/** Data model state */
|
|
22
|
+
dataModel: Record<string, unknown>;
|
|
23
|
+
/** Creation timestamp */
|
|
24
|
+
createdAt: number;
|
|
25
|
+
/** Last update timestamp */
|
|
26
|
+
updatedAt: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface StoreEventMap {
|
|
30
|
+
'surfaceCreated': { surfaceId: string; catalogId: string };
|
|
31
|
+
'surfaceDeleted': { surfaceId: string };
|
|
32
|
+
'componentsUpdated': { surfaceId: string; componentIds: string[] };
|
|
33
|
+
'dataModelUpdated': { surfaceId: string; path: string; value: unknown };
|
|
34
|
+
'watchCallback': WatchSurfaceResponseMessage;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type StoreEventHandler<K extends keyof StoreEventMap> =
|
|
38
|
+
(data: StoreEventMap[K]) => void;
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// JSON Pointer Utilities
|
|
42
|
+
// =============================================================================
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse a JSON Pointer path into an array of keys
|
|
46
|
+
*/
|
|
47
|
+
function parseJsonPointer(pointer: string): string[] {
|
|
48
|
+
if (!pointer || pointer === '/') return [];
|
|
49
|
+
|
|
50
|
+
return pointer
|
|
51
|
+
.split('/')
|
|
52
|
+
.slice(1) // Remove leading empty string from initial /
|
|
53
|
+
.map(segment => segment.replace(/~1/g, '/').replace(/~0/g, '~'));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get a value from an object using a JSON Pointer path
|
|
58
|
+
*/
|
|
59
|
+
function getByPointer(obj: Record<string, unknown>, pointer: string): unknown {
|
|
60
|
+
const keys = parseJsonPointer(pointer);
|
|
61
|
+
let current: unknown = obj;
|
|
62
|
+
|
|
63
|
+
for (const key of keys) {
|
|
64
|
+
if (current === null || current === undefined) return undefined;
|
|
65
|
+
current = (current as Record<string, unknown>)[key];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return current;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Set a value in an object using a JSON Pointer path
|
|
73
|
+
*/
|
|
74
|
+
function setByPointer(
|
|
75
|
+
obj: Record<string, unknown>,
|
|
76
|
+
pointer: string,
|
|
77
|
+
value: unknown
|
|
78
|
+
): void {
|
|
79
|
+
const keys = parseJsonPointer(pointer);
|
|
80
|
+
|
|
81
|
+
if (keys.length === 0) {
|
|
82
|
+
// Replace entire object
|
|
83
|
+
Object.keys(obj).forEach(key => delete obj[key]);
|
|
84
|
+
Object.assign(obj, value);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let current: Record<string, unknown> = obj;
|
|
89
|
+
|
|
90
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
91
|
+
const key = keys[i];
|
|
92
|
+
if (!(key in current) || current[key] === null || typeof current[key] !== 'object') {
|
|
93
|
+
current[key] = {};
|
|
94
|
+
}
|
|
95
|
+
current = current[key] as Record<string, unknown>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
current[keys[keys.length - 1]] = value;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Remove a value from an object using a JSON Pointer path
|
|
103
|
+
*/
|
|
104
|
+
function removeByPointer(obj: Record<string, unknown>, pointer: string): void {
|
|
105
|
+
const keys = parseJsonPointer(pointer);
|
|
106
|
+
|
|
107
|
+
if (keys.length === 0) {
|
|
108
|
+
Object.keys(obj).forEach(key => delete obj[key]);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let current: Record<string, unknown> = obj;
|
|
113
|
+
|
|
114
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
115
|
+
const key = keys[i];
|
|
116
|
+
if (!(key in current)) return;
|
|
117
|
+
current = current[key] as Record<string, unknown>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
delete current[keys[keys.length - 1]];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// =============================================================================
|
|
124
|
+
// Surface Store
|
|
125
|
+
// =============================================================================
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* SurfaceStore manages the state of all A2UX surfaces.
|
|
129
|
+
* It provides reactive subscriptions for state changes.
|
|
130
|
+
*/
|
|
131
|
+
export class SurfaceStore {
|
|
132
|
+
private surfaces: Map<string, Surface> = new Map();
|
|
133
|
+
private listeners: Map<keyof StoreEventMap, Set<StoreEventHandler<keyof StoreEventMap>>> = new Map();
|
|
134
|
+
private watchCallbacks: Map<string, () => void> = new Map();
|
|
135
|
+
|
|
136
|
+
// ===========================================================================
|
|
137
|
+
// Surface Management
|
|
138
|
+
// ===========================================================================
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Create a new surface
|
|
142
|
+
*/
|
|
143
|
+
createSurface(surfaceId: string, catalogId: string): Surface {
|
|
144
|
+
const surface: Surface = {
|
|
145
|
+
surfaceId,
|
|
146
|
+
catalogId,
|
|
147
|
+
components: new Map(),
|
|
148
|
+
dataModel: {},
|
|
149
|
+
createdAt: Date.now(),
|
|
150
|
+
updatedAt: Date.now(),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
this.surfaces.set(surfaceId, surface);
|
|
154
|
+
this.emit('surfaceCreated', { surfaceId, catalogId });
|
|
155
|
+
|
|
156
|
+
return surface;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get a surface by ID
|
|
161
|
+
*/
|
|
162
|
+
getSurface(surfaceId: string): Surface | undefined {
|
|
163
|
+
return this.surfaces.get(surfaceId);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if a surface exists
|
|
168
|
+
*/
|
|
169
|
+
hasSurface(surfaceId: string): boolean {
|
|
170
|
+
return this.surfaces.has(surfaceId);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get all surface IDs
|
|
175
|
+
*/
|
|
176
|
+
getSurfaceIds(): string[] {
|
|
177
|
+
return Array.from(this.surfaces.keys());
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Delete a surface
|
|
182
|
+
*/
|
|
183
|
+
deleteSurface(surfaceId: string): boolean {
|
|
184
|
+
const deleted = this.surfaces.delete(surfaceId);
|
|
185
|
+
if (deleted) {
|
|
186
|
+
this.watchCallbacks.delete(surfaceId);
|
|
187
|
+
this.emit('surfaceDeleted', { surfaceId });
|
|
188
|
+
}
|
|
189
|
+
return deleted;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Clear all surfaces
|
|
194
|
+
*/
|
|
195
|
+
clear(): void {
|
|
196
|
+
const surfaceIds = this.getSurfaceIds();
|
|
197
|
+
surfaceIds.forEach(id => this.deleteSurface(id));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ===========================================================================
|
|
201
|
+
// Component Management
|
|
202
|
+
// ===========================================================================
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Update components in a surface
|
|
206
|
+
*/
|
|
207
|
+
updateComponents(surfaceId: string, components: A2UXComponent[]): void {
|
|
208
|
+
const surface = this.surfaces.get(surfaceId);
|
|
209
|
+
if (!surface) {
|
|
210
|
+
console.warn(`[SurfaceStore] Surface not found: ${surfaceId}`);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const updatedIds: string[] = [];
|
|
215
|
+
|
|
216
|
+
for (const component of components) {
|
|
217
|
+
surface.components.set(component.id, component);
|
|
218
|
+
updatedIds.push(component.id);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
surface.updatedAt = Date.now();
|
|
222
|
+
this.emit('componentsUpdated', { surfaceId, componentIds: updatedIds });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get a component by ID
|
|
227
|
+
*/
|
|
228
|
+
getComponent(surfaceId: string, componentId: string): A2UXComponent | undefined {
|
|
229
|
+
return this.surfaces.get(surfaceId)?.components.get(componentId);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Get all components in a surface
|
|
234
|
+
*/
|
|
235
|
+
getComponents(surfaceId: string): A2UXComponent[] {
|
|
236
|
+
const surface = this.surfaces.get(surfaceId);
|
|
237
|
+
return surface ? Array.from(surface.components.values()) : [];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get the root component of a surface
|
|
242
|
+
*/
|
|
243
|
+
getRootComponent(surfaceId: string): A2UXComponent | undefined {
|
|
244
|
+
return this.getComponent(surfaceId, 'root');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Build component tree from flat list
|
|
249
|
+
*/
|
|
250
|
+
getComponentTree(surfaceId: string): A2UXComponent | null {
|
|
251
|
+
const root = this.getRootComponent(surfaceId);
|
|
252
|
+
if (!root) return null;
|
|
253
|
+
|
|
254
|
+
const buildTree = (component: A2UXComponent): A2UXComponent => {
|
|
255
|
+
const result = { ...component };
|
|
256
|
+
|
|
257
|
+
if (component.children && Array.isArray(component.children)) {
|
|
258
|
+
result.children = component.children.map(childId => {
|
|
259
|
+
const child = this.getComponent(surfaceId, childId as string);
|
|
260
|
+
return child ? buildTree(child) : childId;
|
|
261
|
+
}) as unknown as string[];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return result;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
return buildTree(root);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ===========================================================================
|
|
271
|
+
// Data Model Management
|
|
272
|
+
// ===========================================================================
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Update the data model of a surface
|
|
276
|
+
*/
|
|
277
|
+
updateDataModel(
|
|
278
|
+
surfaceId: string,
|
|
279
|
+
path: string = '/',
|
|
280
|
+
op: 'add' | 'replace' | 'remove' = 'replace',
|
|
281
|
+
value?: unknown
|
|
282
|
+
): void {
|
|
283
|
+
const surface = this.surfaces.get(surfaceId);
|
|
284
|
+
if (!surface) {
|
|
285
|
+
console.warn(`[SurfaceStore] Surface not found: ${surfaceId}`);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
switch (op) {
|
|
290
|
+
case 'add':
|
|
291
|
+
case 'replace':
|
|
292
|
+
setByPointer(surface.dataModel, path, value);
|
|
293
|
+
break;
|
|
294
|
+
case 'remove':
|
|
295
|
+
removeByPointer(surface.dataModel, path);
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
surface.updatedAt = Date.now();
|
|
300
|
+
this.emit('dataModelUpdated', { surfaceId, path, value });
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Get a value from the data model using JSON Pointer
|
|
305
|
+
*/
|
|
306
|
+
getDataValue(surfaceId: string, path: string): unknown {
|
|
307
|
+
const surface = this.surfaces.get(surfaceId);
|
|
308
|
+
if (!surface) return undefined;
|
|
309
|
+
return getByPointer(surface.dataModel, path);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get the entire data model
|
|
314
|
+
*/
|
|
315
|
+
getDataModel(surfaceId: string): Record<string, unknown> | undefined {
|
|
316
|
+
return this.surfaces.get(surfaceId)?.dataModel;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Get flattened data model with JSON Pointer paths as keys
|
|
321
|
+
*/
|
|
322
|
+
getFlatDataModel(surfaceId: string): Record<string, unknown> {
|
|
323
|
+
const surface = this.surfaces.get(surfaceId);
|
|
324
|
+
if (!surface) return {};
|
|
325
|
+
|
|
326
|
+
const result: Record<string, unknown> = {};
|
|
327
|
+
|
|
328
|
+
const flatten = (obj: Record<string, unknown>, prefix: string = ''): void => {
|
|
329
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
330
|
+
const path = `${prefix}/${key}`;
|
|
331
|
+
|
|
332
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
333
|
+
flatten(value as Record<string, unknown>, path);
|
|
334
|
+
} else {
|
|
335
|
+
result[path] = value;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
flatten(surface.dataModel);
|
|
341
|
+
return result;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ===========================================================================
|
|
345
|
+
// Watch Callbacks
|
|
346
|
+
// ===========================================================================
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Register a callback for watch responses
|
|
350
|
+
*/
|
|
351
|
+
setWatchCallback(surfaceId: string, callback: () => void): void {
|
|
352
|
+
this.watchCallbacks.set(surfaceId, callback);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Trigger the watch callback for a surface
|
|
357
|
+
*/
|
|
358
|
+
triggerWatchCallback(surfaceId: string): void {
|
|
359
|
+
const callback = this.watchCallbacks.get(surfaceId);
|
|
360
|
+
if (callback) {
|
|
361
|
+
callback();
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Also emit a watchCallback event with the current state
|
|
365
|
+
const flatData = this.getFlatDataModel(surfaceId);
|
|
366
|
+
this.emit('watchCallback', {
|
|
367
|
+
watchSurfaceResponse: {
|
|
368
|
+
surfaceId,
|
|
369
|
+
data: flatData,
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ===========================================================================
|
|
375
|
+
// Event Handling
|
|
376
|
+
// ===========================================================================
|
|
377
|
+
|
|
378
|
+
on<K extends keyof StoreEventMap>(
|
|
379
|
+
event: K,
|
|
380
|
+
handler: StoreEventHandler<K>
|
|
381
|
+
): () => void {
|
|
382
|
+
if (!this.listeners.has(event)) {
|
|
383
|
+
this.listeners.set(event, new Set());
|
|
384
|
+
}
|
|
385
|
+
this.listeners.get(event)!.add(handler as StoreEventHandler<keyof StoreEventMap>);
|
|
386
|
+
|
|
387
|
+
return () => this.off(event, handler);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
off<K extends keyof StoreEventMap>(
|
|
391
|
+
event: K,
|
|
392
|
+
handler: StoreEventHandler<K>
|
|
393
|
+
): void {
|
|
394
|
+
this.listeners.get(event)?.delete(handler as StoreEventHandler<keyof StoreEventMap>);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private emit<K extends keyof StoreEventMap>(event: K, data: StoreEventMap[K]): void {
|
|
398
|
+
this.listeners.get(event)?.forEach(handler => {
|
|
399
|
+
try {
|
|
400
|
+
(handler as StoreEventHandler<K>)(data);
|
|
401
|
+
} catch (error) {
|
|
402
|
+
console.error(`Error in store event handler for ${event}:`, error);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// ===========================================================================
|
|
408
|
+
// Subscriptions (Reactive)
|
|
409
|
+
// ===========================================================================
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Subscribe to changes in a specific surface
|
|
413
|
+
*/
|
|
414
|
+
subscribeSurface(
|
|
415
|
+
surfaceId: string,
|
|
416
|
+
callback: (surface: Surface) => void
|
|
417
|
+
): () => void {
|
|
418
|
+
const unsubComponents = this.on('componentsUpdated', ({ surfaceId: id }) => {
|
|
419
|
+
if (id === surfaceId) {
|
|
420
|
+
const surface = this.getSurface(surfaceId);
|
|
421
|
+
if (surface) callback(surface);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
const unsubData = this.on('dataModelUpdated', ({ surfaceId: id }) => {
|
|
426
|
+
if (id === surfaceId) {
|
|
427
|
+
const surface = this.getSurface(surfaceId);
|
|
428
|
+
if (surface) callback(surface);
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
return () => {
|
|
433
|
+
unsubComponents();
|
|
434
|
+
unsubData();
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Subscribe to a specific data path in a surface
|
|
440
|
+
*/
|
|
441
|
+
subscribeData(
|
|
442
|
+
surfaceId: string,
|
|
443
|
+
path: string,
|
|
444
|
+
callback: (value: unknown) => void
|
|
445
|
+
): () => void {
|
|
446
|
+
return this.on('dataModelUpdated', ({ surfaceId: id, path: updatedPath }) => {
|
|
447
|
+
if (id === surfaceId && (updatedPath === path || updatedPath.startsWith(path))) {
|
|
448
|
+
callback(this.getDataValue(surfaceId, path));
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
}
|