@finos/legend-extension-dsl-data-space 10.4.248 → 10.4.249

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.
Files changed (37) hide show
  1. package/lib/components/DataSpaceLegendAIIntegration.d.ts +1 -18
  2. package/lib/components/DataSpaceLegendAIIntegration.d.ts.map +1 -1
  3. package/lib/components/DataSpaceLegendAIIntegration.js +9 -350
  4. package/lib/components/DataSpaceLegendAIIntegration.js.map +1 -1
  5. package/lib/components/DataSpaceLegendAISchema.d.ts +43 -0
  6. package/lib/components/DataSpaceLegendAISchema.d.ts.map +1 -0
  7. package/lib/components/DataSpaceLegendAISchema.js +397 -0
  8. package/lib/components/DataSpaceLegendAISchema.js.map +1 -0
  9. package/lib/components/index.d.ts +1 -0
  10. package/lib/components/index.d.ts.map +1 -1
  11. package/lib/components/index.js +1 -0
  12. package/lib/components/index.js.map +1 -1
  13. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.d.ts +5 -0
  14. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.d.ts.map +1 -1
  15. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.js +5 -0
  16. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.js.map +1 -1
  17. package/lib/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.d.ts.map +1 -1
  18. package/lib/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.js +10 -10
  19. package/lib/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.js.map +1 -1
  20. package/lib/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.d.ts.map +1 -1
  21. package/lib/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.js +18 -5
  22. package/lib/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.js.map +1 -1
  23. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.d.ts +8 -0
  24. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.d.ts.map +1 -1
  25. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.js +13 -3
  26. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.js.map +1 -1
  27. package/lib/index.css +1 -1
  28. package/lib/package.json +1 -1
  29. package/package.json +6 -6
  30. package/src/components/DataSpaceLegendAIIntegration.tsx +18 -482
  31. package/src/components/DataSpaceLegendAISchema.ts +562 -0
  32. package/src/components/index.ts +1 -0
  33. package/src/graph-manager/action/analytics/DataSpaceAnalysis.ts +6 -0
  34. package/src/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.ts +16 -13
  35. package/src/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.ts +35 -14
  36. package/src/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.ts +33 -17
  37. package/tsconfig.json +1 -0
@@ -0,0 +1,562 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import {
18
+ Enumeration,
19
+ PRIMITIVE_TYPE,
20
+ RelationalDatabaseTableSpecification,
21
+ type MappingModelCoverageAnalysisResult,
22
+ type FunctionAnalysisInfo,
23
+ type GraphManagerState,
24
+ } from '@finos/legend-graph';
25
+ import {
26
+ extractParameterSchemas,
27
+ buildPropertyDocIndex,
28
+ enrichColumnsFromElementDocs,
29
+ inferServiceRelationshipsFromAssociations,
30
+ extractServicePreFilters,
31
+ extractModelContext,
32
+ type TDSColumnSchema,
33
+ type TDSServiceSchema,
34
+ type LegendAIProductMetadata,
35
+ type LegendAIServiceSummary,
36
+ type LegendAIModelContext,
37
+ type LegendAIModelEntity,
38
+ type LegendAIModelProperty,
39
+ type LegendAIModelAssociation,
40
+ type LegendAIExecutableInfo,
41
+ type LegendAIColumnPropertyMapping,
42
+ type LegendAIParameterInfo,
43
+ type LegendAIFunctionInfo,
44
+ } from '@finos/legend-lego/legend-ai';
45
+ import { type DiagramAnalysisResult } from '@finos/legend-extension-dsl-diagram';
46
+ import type { DataSpaceViewerState } from '../stores/DataSpaceViewerState.js';
47
+ import {
48
+ DataSpaceServiceExecutableInfo,
49
+ DataSpaceMultiExecutionServiceExecutableInfo,
50
+ DataSpaceExecutableTDSResult,
51
+ type DataSpaceExecutableAnalysisResult,
52
+ type DataSpaceAnalysisResult,
53
+ type DataSpaceExecutionContextAnalysisResult,
54
+ } from '../graph-manager/action/analytics/DataSpaceAnalysis.js';
55
+ import {
56
+ DataSpaceSupportEmail,
57
+ DataSpaceSupportCombinedInfo,
58
+ } from '../graph/metamodel/pure/model/packageableElements/dataSpace/DSL_DataSpace_DataSpace.js';
59
+
60
+ const MAX_QUERY_SCAN_LENGTH = 5_000;
61
+ const MAX_QUERY_TEMPLATE_LENGTH = 1000;
62
+ const MAX_DATASPACE_FUNCTIONS = 20;
63
+
64
+ const ENTITY_PATH_PATTERN =
65
+ /(?<entityPath>[A-Za-z_]\w{0,63}(?:::[A-Za-z_]\w{0,63}){0,10})\.all\(\)/g;
66
+
67
+ // ────────────────────────────────────────────────────────────────────────────
68
+ // Diagram-based model context extraction (fallback when elementDocs is sparse)
69
+ // ────────────────────────────────────────────────────────────────────────────
70
+
71
+ /**
72
+ * Extracts a {@link LegendAIModelContext} from diagram metamodel objects.
73
+ * Works even when elementDocs documentation is empty or incomplete.
74
+ */
75
+ function extractModelContextFromDiagrams(
76
+ diagrams: DiagramAnalysisResult[],
77
+ ): LegendAIModelContext {
78
+ const entityMap = new Map<string, LegendAIModelEntity>();
79
+ const associations: LegendAIModelAssociation[] = [];
80
+ const enumPaths = new Map<
81
+ string,
82
+ { path: string; name: string; values: string[] }
83
+ >();
84
+
85
+ for (const { diagram } of diagrams) {
86
+ for (const classView of diagram.classViews) {
87
+ const cls = classView.class.value;
88
+ if (entityMap.has(cls.path)) {
89
+ continue;
90
+ }
91
+ const properties: LegendAIModelProperty[] = cls.properties.map((prop) => {
92
+ const rawType = prop.genericType.value.rawType;
93
+ const typePath = rawType.path;
94
+ if (rawType instanceof Enumeration && !enumPaths.has(typePath)) {
95
+ enumPaths.set(typePath, {
96
+ path: typePath,
97
+ name: rawType.name,
98
+ values: rawType.values.map((v) => v.name),
99
+ });
100
+ }
101
+ return {
102
+ name: prop.name,
103
+ type: typePath,
104
+ isCollection:
105
+ prop.multiplicity.upperBound === undefined ||
106
+ prop.multiplicity.upperBound > 1,
107
+ isOptional: prop.multiplicity.lowerBound === 0,
108
+ };
109
+ });
110
+ const entity: LegendAIModelEntity = {
111
+ path: cls.path,
112
+ name: cls.name,
113
+ properties,
114
+ };
115
+ entityMap.set(cls.path, entity);
116
+ }
117
+
118
+ for (const assocView of diagram.associationViews) {
119
+ const assoc = assocView.association.value;
120
+ const [propA, propB] = assoc.properties;
121
+ associations.push({
122
+ name: assoc.name,
123
+ leftEntity: assocView.from.classView.value.class.value.path,
124
+ leftProperty: propA.name,
125
+ rightEntity: assocView.to.classView.value.class.value.path,
126
+ rightProperty: propB.name,
127
+ });
128
+ }
129
+ }
130
+
131
+ const result: LegendAIModelContext = {
132
+ entities: Array.from(entityMap.values()),
133
+ associations,
134
+ };
135
+ if (enumPaths.size > 0) {
136
+ result.enumerations = Array.from(enumPaths.values());
137
+ }
138
+ return result;
139
+ }
140
+
141
+ /**
142
+ * Enriches a {@link LegendAIModelContext} with mapping coverage data.
143
+ * Marks root-mapped entities so entity resolution can prefer them.
144
+ */
145
+ function enrichModelContextWithMappingCoverage(
146
+ modelContext: LegendAIModelContext,
147
+ mappingCoverage: MappingModelCoverageAnalysisResult | undefined,
148
+ ): void {
149
+ if (!mappingCoverage) {
150
+ return;
151
+ }
152
+ for (const entity of modelContext.entities) {
153
+ const mapped = mappingCoverage.mappedEntities.find(
154
+ (me) => me.path === entity.path,
155
+ );
156
+ if (mapped?.info?.isRootEntity) {
157
+ entity.isRootMapped = true;
158
+ }
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Builds column-to-property mappings by matching TDS result column names
164
+ * to root-entity property names (case-insensitive, separator-normalized).
165
+ */
166
+ function buildColumnPropertyMappings(
167
+ columns: string[],
168
+ rootEntity: LegendAIModelEntity | undefined,
169
+ ): LegendAIColumnPropertyMapping[] | undefined {
170
+ if (!rootEntity || rootEntity.properties.length === 0) {
171
+ return undefined;
172
+ }
173
+
174
+ const normalizeName = (name: string): string =>
175
+ name.toLowerCase().replaceAll(/[\s_]/g, '');
176
+
177
+ const propLookup = new Map<string, string>();
178
+ for (const prop of rootEntity.properties) {
179
+ propLookup.set(normalizeName(prop.name), prop.name);
180
+ }
181
+
182
+ const mappings: LegendAIColumnPropertyMapping[] = [];
183
+ for (const colName of columns) {
184
+ const propName = propLookup.get(normalizeName(colName));
185
+ if (propName && propName !== colName) {
186
+ mappings.push({ columnName: colName, propertyPath: propName });
187
+ }
188
+ }
189
+
190
+ return mappings.length > 0 ? mappings : undefined;
191
+ }
192
+
193
+ // Scans a query string for referenced entity paths; resets the shared regex's
194
+ // lastIndex first since ENTITY_PATH_PATTERN is module-scoped and reused.
195
+ function collectReferencedEntityPaths(queryStr: string): string[] {
196
+ const safeQuery = queryStr.slice(0, MAX_QUERY_SCAN_LENGTH);
197
+ ENTITY_PATH_PATTERN.lastIndex = 0;
198
+ const paths: string[] = [];
199
+ const seen = new Set<string>();
200
+ let match: RegExpExecArray | null;
201
+ while ((match = ENTITY_PATH_PATTERN.exec(safeQuery)) !== null) {
202
+ const path = match.groups?.entityPath;
203
+ if (path && !seen.has(path)) {
204
+ seen.add(path);
205
+ paths.push(path);
206
+ }
207
+ }
208
+ return paths;
209
+ }
210
+
211
+ function buildExecutableInfo(
212
+ exec: DataSpaceExecutableAnalysisResult,
213
+ entityMap: Map<string, LegendAIModelEntity>,
214
+ serviceParameters: Map<string, LegendAIParameterInfo[]>,
215
+ ): LegendAIExecutableInfo | undefined {
216
+ if (
217
+ !(
218
+ exec.info instanceof DataSpaceServiceExecutableInfo ||
219
+ exec.info instanceof DataSpaceMultiExecutionServiceExecutableInfo
220
+ )
221
+ ) {
222
+ return undefined;
223
+ }
224
+ const queryStr = exec.info.query;
225
+ const referencedEntityPaths = collectReferencedEntityPaths(queryStr);
226
+ const rootEntityPath = referencedEntityPaths[0];
227
+ if (!rootEntityPath) {
228
+ return undefined;
229
+ }
230
+ const info: LegendAIExecutableInfo = {
231
+ title: exec.title,
232
+ rootEntityPath,
233
+ queryTemplate:
234
+ queryStr.length > MAX_QUERY_TEMPLATE_LENGTH
235
+ ? `${queryStr.slice(0, MAX_QUERY_TEMPLATE_LENGTH)}...`
236
+ : queryStr,
237
+ };
238
+ if (referencedEntityPaths.length > 1) {
239
+ info.referencedEntityPaths = referencedEntityPaths;
240
+ }
241
+ if (exec.description !== undefined) {
242
+ info.description = exec.description;
243
+ }
244
+ const reqParams = serviceParameters.get(exec.title);
245
+ if (reqParams && reqParams.length > 0) {
246
+ info.requiredParameters = reqParams;
247
+ }
248
+ if (exec.result instanceof DataSpaceExecutableTDSResult) {
249
+ const columnNames = exec.result.columns.map((c) => c.name);
250
+ const colMappings = buildColumnPropertyMappings(
251
+ columnNames,
252
+ entityMap.get(rootEntityPath),
253
+ );
254
+ if (colMappings) {
255
+ info.columnPropertyMappings = colMappings;
256
+ }
257
+ }
258
+ return info;
259
+ }
260
+
261
+ export function extractExecutableInfo(
262
+ executables: DataSpaceExecutableAnalysisResult[],
263
+ modelContext: LegendAIModelContext,
264
+ services: TDSServiceSchema[],
265
+ ): LegendAIExecutableInfo[] {
266
+ const entityMap = new Map(modelContext.entities.map((e) => [e.path, e]));
267
+ const serviceParameters = new Map<string, LegendAIParameterInfo[]>();
268
+ for (const svc of services) {
269
+ if (svc.parameterSchemas && svc.parameterSchemas.length > 0) {
270
+ serviceParameters.set(
271
+ svc.title,
272
+ svc.parameterSchemas.map((p) => ({
273
+ name: p.name,
274
+ type: p.type ?? PRIMITIVE_TYPE.STRING,
275
+ })),
276
+ );
277
+ }
278
+ }
279
+ const result: LegendAIExecutableInfo[] = [];
280
+ for (const exec of executables) {
281
+ const info = buildExecutableInfo(exec, entityMap, serviceParameters);
282
+ if (!info) {
283
+ continue;
284
+ }
285
+ result.push(info);
286
+ const queryableEntityPaths = info.referencedEntityPaths ?? [
287
+ info.rootEntityPath,
288
+ ];
289
+ for (const entityPath of queryableEntityPaths) {
290
+ const entity = entityMap.get(entityPath);
291
+ if (entity) {
292
+ entity.isQueryable = true;
293
+ }
294
+ }
295
+ }
296
+ return result;
297
+ }
298
+
299
+ /**
300
+ * Everything data space extraction needs, with no UI state involved. The
301
+ * execution context supplies the mapping and datasets the viewer would project.
302
+ */
303
+ export interface DataSpaceSchemaSource {
304
+ analysisResult: DataSpaceAnalysisResult;
305
+ executionContext: DataSpaceExecutionContextAnalysisResult;
306
+ graphManagerState: GraphManagerState;
307
+ }
308
+
309
+ export async function extractTDSServicesFromDataSpaceSource(
310
+ source: DataSpaceSchemaSource,
311
+ ): Promise<TDSServiceSchema[]> {
312
+ const executables = source.analysisResult.executables;
313
+ const graphManager = source.graphManagerState.graphManager;
314
+
315
+ const nested = await Promise.all(
316
+ executables.map(async (exec): Promise<TDSServiceSchema[]> => {
317
+ if (
318
+ !(
319
+ exec.info instanceof DataSpaceServiceExecutableInfo ||
320
+ exec.info instanceof DataSpaceMultiExecutionServiceExecutableInfo
321
+ ) ||
322
+ !(exec.result instanceof DataSpaceExecutableTDSResult)
323
+ ) {
324
+ return [];
325
+ }
326
+ const info = exec.info;
327
+ const tdsResult = exec.result;
328
+
329
+ const { parameters, parameterSchemas, parameterExtractionFailed } =
330
+ await extractParameterSchemas(
331
+ info.query,
332
+ graphManager,
333
+ source.graphManagerState,
334
+ );
335
+
336
+ const preFilters = await extractServicePreFilters(
337
+ info.query,
338
+ graphManager,
339
+ );
340
+
341
+ const entry: TDSServiceSchema = {
342
+ title: exec.title,
343
+ pattern: info.pattern,
344
+ columns: tdsResult.columns.map((col) => {
345
+ const column: TDSColumnSchema = { name: col.name };
346
+ if (col.type !== undefined) {
347
+ column.type = col.type;
348
+ }
349
+ if (col.relationalType !== undefined) {
350
+ column.relationalType = col.relationalType;
351
+ }
352
+ if (col.documentation !== undefined) {
353
+ column.documentation = col.documentation;
354
+ }
355
+ if (col.sampleValues !== undefined) {
356
+ column.sampleValues = col.sampleValues;
357
+ }
358
+ return column;
359
+ }),
360
+ parameters,
361
+ ...(parameterSchemas.length > 0 ? { parameterSchemas } : {}),
362
+ ...(parameterExtractionFailed
363
+ ? { parameterExtractionFailed: true }
364
+ : {}),
365
+ ...(preFilters ? { preFilters } : {}),
366
+ };
367
+ if (exec.description !== undefined) {
368
+ entry.description = exec.description;
369
+ }
370
+ return [entry];
371
+ }),
372
+ );
373
+ const services = nested.flat();
374
+
375
+ const propIndex = buildPropertyDocIndex(source.analysisResult.elementDocs);
376
+ if (propIndex.size > 0) {
377
+ for (const svc of services) {
378
+ enrichColumnsFromElementDocs(svc.columns, propIndex);
379
+ }
380
+ }
381
+
382
+ return services;
383
+ }
384
+
385
+ export function extractMetadataFromDataSpaceAnalysis(
386
+ result: DataSpaceAnalysisResult,
387
+ coordinates: string,
388
+ services: TDSServiceSchema[],
389
+ ): LegendAIProductMetadata {
390
+ let supportInfoText: string | undefined;
391
+ if (result.supportInfo instanceof DataSpaceSupportEmail) {
392
+ supportInfoText = result.supportInfo.address;
393
+ } else if (result.supportInfo instanceof DataSpaceSupportCombinedInfo) {
394
+ supportInfoText = (result.supportInfo.emails ?? []).join(', ');
395
+ }
396
+
397
+ const metadata: LegendAIProductMetadata = {
398
+ name: result.title ?? result.name,
399
+ coordinates,
400
+ serviceSummaries: result.executables.map((exec) => {
401
+ const summary: LegendAIServiceSummary = {
402
+ title: exec.title,
403
+ };
404
+ if (exec.description !== undefined) {
405
+ summary.description = exec.description;
406
+ }
407
+ if (exec.result instanceof DataSpaceExecutableTDSResult) {
408
+ summary.columnNames = exec.result.columns.map((col) => col.name);
409
+ }
410
+ return summary;
411
+ }),
412
+ tags: result.taggedValues.map((tv) => ({
413
+ profile: tv.profile,
414
+ value: tv.value,
415
+ })),
416
+ };
417
+ if (result.description !== undefined) {
418
+ metadata.description = result.description;
419
+ }
420
+ if (supportInfoText !== undefined) {
421
+ metadata.supportInfo = supportInfoText;
422
+ }
423
+ if (services.length >= 2) {
424
+ const serviceRels = inferServiceRelationshipsFromAssociations(
425
+ services,
426
+ result.elementDocs,
427
+ );
428
+ if (serviceRels.length > 0) {
429
+ metadata.serviceRelationships = serviceRels;
430
+ }
431
+ }
432
+ return metadata;
433
+ }
434
+
435
+ // Maps the dataspace's own callable functions to model-context function hints.
436
+ function extractFunctionInfo(
437
+ functionInfos: Map<string, FunctionAnalysisInfo> | undefined,
438
+ ): LegendAIFunctionInfo[] {
439
+ if (!functionInfos || functionInfos.size === 0) {
440
+ return [];
441
+ }
442
+ const functions: LegendAIFunctionInfo[] = [];
443
+ for (const fn of functionInfos.values()) {
444
+ if (functions.length >= MAX_DATASPACE_FUNCTIONS) {
445
+ break;
446
+ }
447
+ functions.push({
448
+ name: fn.functionName,
449
+ functionPath: fn.functionPath,
450
+ returnType: fn.returnType,
451
+ parameters: fn.parameterInfoList.map((p) => ({
452
+ name: p.name,
453
+ type: p.type,
454
+ })),
455
+ });
456
+ }
457
+ return functions;
458
+ }
459
+
460
+ /**
461
+ * Builds the orchestrator model context for a dataspace from element docs or
462
+ * diagrams, enriched with mapping coverage, executables, datasets, and functions.
463
+ */
464
+ export function buildDataSpaceModelContextFromSource(
465
+ source: DataSpaceSchemaSource,
466
+ services: TDSServiceSchema[],
467
+ ): LegendAIModelContext | undefined {
468
+ const result = source.analysisResult;
469
+ let ctx: LegendAIModelContext | undefined;
470
+ if (result.elementDocs.length > 0) {
471
+ const docsCtx = extractModelContext(result.elementDocs);
472
+ if (docsCtx.entities.length > 0) {
473
+ ctx = docsCtx;
474
+ }
475
+ }
476
+ if (!ctx && result.diagrams.length > 0) {
477
+ const diagramCtx = extractModelContextFromDiagrams(result.diagrams);
478
+ if (diagramCtx.entities.length > 0) {
479
+ ctx = diagramCtx;
480
+ }
481
+ }
482
+ if (!ctx) {
483
+ return undefined;
484
+ }
485
+ const mappingPath = source.executionContext.mapping.path;
486
+ enrichModelContextWithMappingCoverage(
487
+ ctx,
488
+ result.mappingToMappingCoverageResult?.get(mappingPath),
489
+ );
490
+ if (result.executables.length > 0) {
491
+ const execInfos = extractExecutableInfo(result.executables, ctx, services);
492
+ if (execInfos.length > 0) {
493
+ ctx.executables = execInfos;
494
+ }
495
+ }
496
+ const functions = extractFunctionInfo(result.functionInfos);
497
+ if (functions.length > 0) {
498
+ ctx.functions = functions;
499
+ }
500
+ const physicalDatasets = source.executionContext.datasets;
501
+ if (physicalDatasets.length > 0) {
502
+ ctx.datasets = physicalDatasets.map((ds) =>
503
+ ds instanceof RelationalDatabaseTableSpecification
504
+ ? {
505
+ name: ds.name,
506
+ database: ds.database,
507
+ schema: ds.schema,
508
+ table: ds.table,
509
+ }
510
+ : { name: ds.name },
511
+ );
512
+ }
513
+ if (result.description) {
514
+ ctx.dataspaceDescription = result.description;
515
+ }
516
+ return ctx;
517
+ }
518
+
519
+ /** Adapts an open data space viewer to {@link extractTDSServicesFromDataSpaceSource}. */
520
+ export async function extractTDSServicesFromDataSpace(
521
+ viewerState: DataSpaceViewerState,
522
+ ): Promise<TDSServiceSchema[]> {
523
+ return extractTDSServicesFromDataSpaceSource(
524
+ buildDataSpaceSchemaSource(viewerState),
525
+ );
526
+ }
527
+
528
+ /** Adapts an open data space viewer to {@link extractMetadataFromDataSpaceAnalysis}. */
529
+ export function extractMetadataFromDataSpace(
530
+ viewerState: DataSpaceViewerState,
531
+ coordinates: string,
532
+ services: TDSServiceSchema[],
533
+ ): LegendAIProductMetadata {
534
+ return extractMetadataFromDataSpaceAnalysis(
535
+ viewerState.dataSpaceAnalysisResult,
536
+ coordinates,
537
+ services,
538
+ );
539
+ }
540
+
541
+ /** Adapts an open data space viewer to {@link buildDataSpaceModelContextFromSource}. */
542
+ export function buildDataSpaceModelContext(
543
+ viewerState: DataSpaceViewerState,
544
+ services: TDSServiceSchema[],
545
+ ): LegendAIModelContext | undefined {
546
+ return buildDataSpaceModelContextFromSource(
547
+ buildDataSpaceSchemaSource(viewerState),
548
+ services,
549
+ );
550
+ }
551
+
552
+ // The viewer's execution context is observable, so it is read per call rather
553
+ // than captured once.
554
+ function buildDataSpaceSchemaSource(
555
+ viewerState: DataSpaceViewerState,
556
+ ): DataSpaceSchemaSource {
557
+ return {
558
+ analysisResult: viewerState.dataSpaceAnalysisResult,
559
+ executionContext: viewerState.currentExecutionContext,
560
+ graphManagerState: viewerState.graphManagerState,
561
+ };
562
+ }
@@ -34,3 +34,4 @@ export {
34
34
  export * from '../stores/shared/DataSpaceUtils.js';
35
35
 
36
36
  export * from './shared/DSL_DataSpace_Icon.js';
37
+ export * from './DataSpaceLegendAISchema.js';
@@ -32,6 +32,7 @@ export class DataSpaceExecutionContextAnalysisResult {
32
32
  title?: string | undefined;
33
33
  description?: string | undefined;
34
34
  mapping!: Mapping;
35
+ mappingProvider?: DataSpaceMappingProviderAnalysisResult;
35
36
  defaultRuntime!: PackageableRuntime;
36
37
  compatibleRuntimes!: PackageableRuntime[];
37
38
  datasets: DatasetSpecification[] = [];
@@ -44,6 +45,11 @@ export class DataSpaceExecutionContextRuntimeMetadata {
44
45
  connectionType?: string;
45
46
  }
46
47
 
48
+ export class DataSpaceMappingProviderAnalysisResult {
49
+ element!: string;
50
+ keys: string[] = [];
51
+ }
52
+
47
53
  export class DataSpaceTaggedValueInfo {
48
54
  readonly _UUID = uuid();
49
55
 
@@ -691,21 +691,24 @@ export class DSL_DataSpace_PureProtocolProcessorPlugin
691
691
  );
692
692
  if (v1DataSpace) {
693
693
  const executionContexts = v1DataSpace.executionContexts ?? [];
694
- if (executionContexts.length === 1) {
695
- const exec = guaranteeNonNullable(executionContexts[0]);
696
- return {
697
- mapping: exec.mapping?.path ?? '',
698
- runtime: exec.defaultRuntime?.path ?? '',
699
- };
694
+ const resolvedExec =
695
+ executionContexts.length === 1
696
+ ? executionContexts[0]
697
+ : executionContexts.find(
698
+ (e) =>
699
+ e.name ===
700
+ (queryExec.executionKey ??
701
+ v1DataSpace.defaultExecutionContext),
702
+ );
703
+ if (
704
+ !resolvedExec?.mapping?.path ||
705
+ !resolvedExec.defaultRuntime?.path
706
+ ) {
707
+ return undefined;
700
708
  }
701
- const resvoled =
702
- queryExec.executionKey ?? v1DataSpace.defaultExecutionContext;
703
- const resolvedExec = guaranteeNonNullable(
704
- executionContexts.find((e) => e.name === resvoled),
705
- );
706
709
  return {
707
- mapping: resolvedExec.mapping?.path ?? '',
708
- runtime: resolvedExec.defaultRuntime?.path ?? '',
710
+ mapping: resolvedExec.mapping.path,
711
+ runtime: resolvedExec.defaultRuntime.path,
709
712
  };
710
713
  }
711
714
  }