@xpert-ai/plugin-live-dashboard 0.4.1 → 0.5.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.
Files changed (50) hide show
  1. package/.xpertai-plugin/plugin.json +1 -1
  2. package/README.md +4 -0
  3. package/dist/lib/agent.schema.d.ts +179 -25
  4. package/dist/lib/agent.schema.js +10 -9
  5. package/dist/lib/archive.service.d.ts +145 -0
  6. package/dist/lib/archive.service.js +133 -0
  7. package/dist/lib/content.provider.d.ts +1 -1
  8. package/dist/lib/dashboard-archive.d.ts +70 -0
  9. package/dist/lib/dashboard-archive.js +118 -0
  10. package/dist/lib/dashboard-sources.d.ts +69 -0
  11. package/dist/lib/dashboard-sources.js +37 -0
  12. package/dist/lib/dashboard-view.schema.d.ts +422 -88
  13. package/dist/lib/dashboard.schema.d.ts +379 -58
  14. package/dist/lib/dashboard.schema.js +18 -1
  15. package/dist/lib/dashboard.service.d.ts +60 -14
  16. package/dist/lib/dashboard.service.js +8 -2
  17. package/dist/lib/document-package.d.ts +19 -4
  18. package/dist/lib/editing-patch.d.ts +3 -0
  19. package/dist/lib/editing-patch.js +2 -1
  20. package/dist/lib/editing-session.d.ts +4 -4
  21. package/dist/lib/editing.d.ts +202 -19
  22. package/dist/lib/editing.js +5 -2
  23. package/dist/lib/html-validation-feedback.d.ts +7 -0
  24. package/dist/lib/html-validation-feedback.js +19 -0
  25. package/dist/lib/html-validation.js +2 -1
  26. package/dist/lib/inspection-host.d.ts +7 -0
  27. package/dist/lib/inspection-host.js +48 -0
  28. package/dist/lib/inspection-runtime.d.ts +2 -0
  29. package/dist/lib/inspection-runtime.js +77 -0
  30. package/dist/lib/json-input.d.ts +4 -0
  31. package/dist/lib/json-input.js +21 -0
  32. package/dist/lib/middleware.js +3 -2
  33. package/dist/lib/plugin.js +2 -1
  34. package/dist/lib/presentation.provider.d.ts +24 -9
  35. package/dist/lib/presentation.provider.js +2 -1
  36. package/dist/lib/source-executions.d.ts +196 -0
  37. package/dist/lib/source-executions.js +25 -0
  38. package/dist/lib/source-validation.d.ts +2 -0
  39. package/dist/lib/source-validation.js +21 -0
  40. package/dist/lib/view.provider.d.ts +178 -8
  41. package/dist/lib/view.provider.js +30 -4
  42. package/dist/remote/app.css +1 -1
  43. package/dist/remote/app.js +100 -27
  44. package/dist/remote/build-manifest.json +37 -23
  45. package/docs/docs.json +3 -1
  46. package/docs/sources-inspect-portability-acceptance.mdx +67 -0
  47. package/docs/sources-inspect-portability.mdx +115 -0
  48. package/package.json +6 -5
  49. package/skills/live-dashboard/SKILL.md +5 -4
  50. package/skills/live-dashboard/references/source-inspection.md +17 -0
@@ -4,10 +4,13 @@ exports.queryBindingSchema = exports.querySchema = exports.saveSchema = exports.
4
4
  const v3_1 = require("zod/v3");
5
5
  const analytics_1 = require("@xpert-ai/plugin-sdk/analytics");
6
6
  const artifact_document_1 = require("@xpert-ai/plugin-sdk/artifact-document");
7
+ const dashboard_sources_1 = require("./dashboard-sources");
7
8
  const id = v3_1.z.string().regex(/^[A-Za-z][A-Za-z0-9_.-]{0,79}$/);
8
9
  exports.dashboardSchema = v3_1.z.object({
9
10
  schema: v3_1.z.literal('xpert.live-dashboard'), id: v3_1.z.string().uuid(), title: v3_1.z.string().min(1).max(160),
10
11
  bindings: v3_1.z.array(analytics_1.analyticsBindingSchema).max(8),
12
+ sourceMetadata: dashboard_sources_1.sourceMetadataSchema.optional(),
13
+ components: dashboard_sources_1.dashboardComponentsSchema.optional(),
11
14
  presentation: v3_1.z.object({ type: v3_1.z.literal('html'), html: artifact_document_1.artifactDocumentHtml, dependencies: artifact_document_1.artifactDocumentDependencyLocks.optional() }).strict().optional(),
12
15
  controls: v3_1.z.array(v3_1.z.object({ controlId: id, type: v3_1.z.enum(['string', 'number', 'boolean', 'date']),
13
16
  defaultValue: v3_1.z.union([v3_1.z.string(), v3_1.z.number(), v3_1.z.boolean(), v3_1.z.null()]).optional(),
@@ -25,6 +28,20 @@ exports.dashboardSchema = v3_1.z.object({
25
28
  ctx.addIssue({ code: 'custom', message: 'An unconfigured draft must contain no views or controls' });
26
29
  unique(manifest.bindings.map((binding) => binding.id), 'binding');
27
30
  unique(manifest.bindings.map((binding) => binding.output.datasetId), 'dataset');
31
+ unique((manifest.sourceMetadata ?? []).map(item => item.bindingId), 'source metadata');
32
+ unique((manifest.components ?? []).map(item => item.id), 'component');
33
+ unique((manifest.components ?? []).map(item => item.elementId), 'component element');
34
+ for (const metadata of manifest.sourceMetadata ?? [])
35
+ if (!manifest.bindings.some(binding => binding.id === metadata.bindingId))
36
+ ctx.addIssue({ code: 'custom', message: 'Source metadata references an undeclared Binding' });
37
+ for (const component of manifest.components ?? []) {
38
+ unique(component.sources.map(source => source.bindingId), 'component source');
39
+ for (const source of component.sources) {
40
+ const binding = manifest.bindings.find(item => item.id === source.bindingId);
41
+ if (!binding || source.fieldIds?.some(field => !binding.output.fields.some(item => item.id === field)))
42
+ ctx.addIssue({ code: 'custom', message: 'Component references an undeclared Binding or field' });
43
+ }
44
+ }
28
45
  unique(manifest.controls.map((control) => control.controlId), 'control');
29
46
  unique([...manifest.charts, ...manifest.tables].map((view) => view.id), 'view');
30
47
  if (manifest.bindings.length && !manifest.presentation && !manifest.charts.length && !manifest.tables.length)
@@ -68,7 +85,7 @@ exports.dashboardSchema = v3_1.z.object({
68
85
  });
69
86
  /** Updates never choose identity or source scope; those come from the authorized saved version. */
70
87
  exports.dashboardChangesSchema = exports.dashboardSchema.innerType().pick({ title: true, bindings: true,
71
- controls: true, charts: true, tables: true, presentation: true }).partial().strict();
88
+ controls: true, charts: true, tables: true, presentation: true, sourceMetadata: true, components: true }).partial().strict();
72
89
  exports.controlsSchema = v3_1.z.record(v3_1.z.union([v3_1.z.string().max(4000), v3_1.z.number().finite(), v3_1.z.boolean(), v3_1.z.null()]));
73
90
  exports.saveSchema = v3_1.z.object({ manifest: exports.dashboardSchema, expectedVersionId: v3_1.z.string().uuid().nullable(), expectedRevision: v3_1.z.number().int().nonnegative().default(0) }).strict();
74
91
  exports.querySchema = v3_1.z.object({ artifactId: v3_1.z.string().uuid(), versionId: v3_1.z.string().uuid().optional(), draftRevision: v3_1.z.number().int().positive().optional(), controls: exports.controlsSchema.default({}) }).strict();
@@ -1,6 +1,7 @@
1
1
  import type { AnalyticsValue, AnalyticsDiscoveryRequest, AnalyticsQueryRequest, XpertResolvedViewHostContext } from '@xpert-ai/contracts';
2
2
  import { type RuntimeCapabilityRegistry } from '@xpert-ai/plugin-sdk';
3
3
  import { type DashboardManifest, type DashboardChanges } from './dashboard.schema';
4
+ import { type ArtifactDocumentDependencyAsset } from '@xpert-ai/plugin-sdk/artifact-document';
4
5
  import { HtmlDashboardValidation } from './html-validation';
5
6
  import type { DashboardAccess, DashboardMode } from './project';
6
7
  export declare const PLUGIN_NAME = "@xpert-ai/plugin-live-dashboard";
@@ -63,7 +64,7 @@ export declare class DashboardService {
63
64
  trash(context: XpertResolvedViewHostContext, artifactId: string, expectedRevision: number): Promise<{
64
65
  artifactId: string;
65
66
  }>;
66
- save(context: XpertResolvedViewHostContext, manifest: DashboardManifest, expectedVersionId: string | null, expectedRevision?: number): Promise<{
67
+ save(context: XpertResolvedViewHostContext, manifest: DashboardManifest, expectedVersionId: string | null, expectedRevision?: number, importedDependencies?: ArtifactDocumentDependencyAsset[]): Promise<{
67
68
  artifactId: string;
68
69
  draftId: string;
69
70
  draftRevision: number;
@@ -73,9 +74,9 @@ export declare class DashboardService {
73
74
  }>;
74
75
  open(context: XpertResolvedViewHostContext, artifactId: string, versionId?: string, draftRevision?: number, mode?: DashboardMode): Promise<{
75
76
  manifest: {
76
- schema: "xpert.live-dashboard";
77
- id: string;
78
77
  title: string;
78
+ id: string;
79
+ schema: "xpert.live-dashboard";
79
80
  bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
80
81
  controls: {
81
82
  type: "string" | "number" | "boolean" | "date";
@@ -85,20 +86,35 @@ export declare class DashboardService {
85
86
  allowedValues?: (string | number | boolean | null)[] | undefined;
86
87
  }[];
87
88
  charts: {
89
+ title: string;
88
90
  type: "bar" | "line" | "area" | "pie" | "scatter";
89
91
  id: string;
90
- title: string;
91
92
  datasetId: string;
92
93
  x: string;
93
94
  y: string;
94
95
  color?: string | undefined;
95
96
  }[];
96
97
  tables: {
97
- id: string;
98
98
  title: string;
99
+ id: string;
99
100
  datasetId: string;
100
101
  fields: string[];
101
102
  }[];
103
+ sourceMetadata?: {
104
+ bindingId: string;
105
+ title: string;
106
+ description?: string | undefined;
107
+ }[] | undefined;
108
+ components?: {
109
+ title: string;
110
+ id: string;
111
+ elementId: string;
112
+ kind: "filter" | "metric" | "chart" | "table" | "text";
113
+ sources: {
114
+ bindingId: string;
115
+ fieldIds?: string[] | undefined;
116
+ }[];
117
+ }[] | undefined;
102
118
  presentation?: {
103
119
  type: "html";
104
120
  html: string;
@@ -119,9 +135,9 @@ export declare class DashboardService {
119
135
  /** Trusted View transport only. Agent read tools receive locks, never megabytes of library code. */
120
136
  openDocument(context: XpertResolvedViewHostContext, artifactId: string, versionId?: string, draftRevision?: number, mode?: DashboardMode): Promise<{
121
137
  manifest: {
122
- schema: "xpert.live-dashboard";
123
- id: string;
124
138
  title: string;
139
+ id: string;
140
+ schema: "xpert.live-dashboard";
125
141
  bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
126
142
  controls: {
127
143
  type: "string" | "number" | "boolean" | "date";
@@ -131,27 +147,42 @@ export declare class DashboardService {
131
147
  allowedValues?: (string | number | boolean | null)[] | undefined;
132
148
  }[];
133
149
  charts: {
150
+ title: string;
134
151
  type: "bar" | "line" | "area" | "pie" | "scatter";
135
152
  id: string;
136
- title: string;
137
153
  datasetId: string;
138
154
  x: string;
139
155
  y: string;
140
156
  color?: string | undefined;
141
157
  }[];
142
158
  tables: {
143
- id: string;
144
159
  title: string;
160
+ id: string;
145
161
  datasetId: string;
146
162
  fields: string[];
147
163
  }[];
164
+ sourceMetadata?: {
165
+ bindingId: string;
166
+ title: string;
167
+ description?: string | undefined;
168
+ }[] | undefined;
169
+ components?: {
170
+ title: string;
171
+ id: string;
172
+ elementId: string;
173
+ kind: "filter" | "metric" | "chart" | "table" | "text";
174
+ sources: {
175
+ bindingId: string;
176
+ fieldIds?: string[] | undefined;
177
+ }[];
178
+ }[] | undefined;
148
179
  presentation?: {
149
180
  type: "html";
150
181
  html: string;
151
182
  dependencies?: import("@xpert-ai/plugin-sdk/artifact-document").ArtifactDocumentDependencyLock[] | undefined;
152
183
  } | undefined;
153
184
  };
154
- dependencies: import("@xpert-ai/plugin-sdk/artifact-document").ArtifactDocumentDependencyAsset[];
185
+ dependencies: ArtifactDocumentDependencyAsset[];
155
186
  contentHash: string;
156
187
  draftId?: string | undefined;
157
188
  draftRevision?: number | undefined;
@@ -250,9 +281,9 @@ export declare class DashboardService {
250
281
  reason: "unavailable";
251
282
  }[];
252
283
  manifest: {
253
- schema: "xpert.live-dashboard";
254
- id: string;
255
284
  title: string;
285
+ id: string;
286
+ schema: "xpert.live-dashboard";
256
287
  bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
257
288
  controls: {
258
289
  type: "string" | "number" | "boolean" | "date";
@@ -262,20 +293,35 @@ export declare class DashboardService {
262
293
  allowedValues?: (string | number | boolean | null)[] | undefined;
263
294
  }[];
264
295
  charts: {
296
+ title: string;
265
297
  type: "bar" | "line" | "area" | "pie" | "scatter";
266
298
  id: string;
267
- title: string;
268
299
  datasetId: string;
269
300
  x: string;
270
301
  y: string;
271
302
  color?: string | undefined;
272
303
  }[];
273
304
  tables: {
274
- id: string;
275
305
  title: string;
306
+ id: string;
276
307
  datasetId: string;
277
308
  fields: string[];
278
309
  }[];
310
+ sourceMetadata?: {
311
+ bindingId: string;
312
+ title: string;
313
+ description?: string | undefined;
314
+ }[] | undefined;
315
+ components?: {
316
+ title: string;
317
+ id: string;
318
+ elementId: string;
319
+ kind: "filter" | "metric" | "chart" | "table" | "text";
320
+ sources: {
321
+ bindingId: string;
322
+ fieldIds?: string[] | undefined;
323
+ }[];
324
+ }[] | undefined;
279
325
  presentation?: {
280
326
  type: "html";
281
327
  html: string;
@@ -18,6 +18,8 @@ const common_1 = require("@nestjs/common");
18
18
  const node_crypto_1 = require("node:crypto");
19
19
  const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
20
20
  const dashboard_schema_1 = require("./dashboard.schema");
21
+ const source_validation_1 = require("./source-validation");
22
+ const artifact_document_1 = require("@xpert-ai/plugin-sdk/artifact-document");
21
23
  const renderer_1 = require("./renderer");
22
24
  const dashboard_queries_1 = require("./dashboard-queries");
23
25
  const html_validation_1 = require("./html-validation");
@@ -98,10 +100,11 @@ let DashboardService = class DashboardService {
98
100
  await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).setRecycled({ artifactId, expectedRevision, recycled: true });
99
101
  return { artifactId };
100
102
  }
101
- async save(context, manifest, expectedVersionId, expectedRevision = 0) {
103
+ async save(context, manifest, expectedVersionId, expectedRevision = 0, importedDependencies) {
102
104
  await this.authorizeProject(context, 'write');
103
105
  const scopedActor = actor(context);
104
106
  const parsed = dashboard_schema_1.dashboardSchema.parse(manifest);
107
+ (0, source_validation_1.assertComponentElements)(parsed);
105
108
  const artifacts = this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability);
106
109
  const source = { pluginName: exports.PLUGIN_NAME, resourceType: RESOURCE_TYPE, resourceId: `${hostPrefix(context)}${parsed.id}` };
107
110
  const existing = await artifacts.findArtifactBySource(source);
@@ -112,7 +115,10 @@ let DashboardService = class DashboardService {
112
115
  }
113
116
  // A failed first upload can leave an Artifact with no saved content; revision 0 initializes it.
114
117
  const previous = existing && (existing.currentVersionId || expectedRevision > 0) ? await this.openDocument(context, existing.id) : null;
115
- const dependencies = await (0, document_libraries_1.resolveLibraryAssets)(parsed.presentation?.dependencies ?? [], previous?.dependencies);
118
+ if (importedDependencies && existing)
119
+ throw new common_1.BadRequestException('DASHBOARD_IMPORT_NEW_ID_REQUIRED');
120
+ const dependencies = importedDependencies ? (0, artifact_document_1.verifyArtifactDocumentDependencies)(importedDependencies, parsed.presentation?.dependencies ?? [])
121
+ : await (0, document_libraries_1.resolveLibraryAssets)(parsed.presentation?.dependencies ?? [], previous?.dependencies);
116
122
  const buffer = (0, document_package_1.encodeDashboardPackage)(parsed, dependencies);
117
123
  if (buffer.byteLength > document_package_1.MAX_DASHBOARD_PACKAGE_BYTES)
118
124
  throw new common_1.BadRequestException('DASHBOARD_MANIFEST_TOO_LARGE');
@@ -5,9 +5,9 @@ export declare const MAX_DASHBOARD_PACKAGE_BYTES = 9000000;
5
5
  export declare function encodeDashboardPackage(manifest: DashboardManifest, dependencies: ArtifactDocumentDependencyAsset[]): Buffer<ArrayBuffer>;
6
6
  export declare function decodeDashboardPackage(bytes: Buffer): {
7
7
  manifest: {
8
- schema: "xpert.live-dashboard";
9
- id: string;
10
8
  title: string;
9
+ id: string;
10
+ schema: "xpert.live-dashboard";
11
11
  bindings: import("@xpert-ai/contracts").AnalyticsBinding[];
12
12
  controls: {
13
13
  type: "string" | "number" | "boolean" | "date";
@@ -17,20 +17,35 @@ export declare function decodeDashboardPackage(bytes: Buffer): {
17
17
  allowedValues?: (string | number | boolean | null)[] | undefined;
18
18
  }[];
19
19
  charts: {
20
+ title: string;
20
21
  type: "bar" | "line" | "area" | "pie" | "scatter";
21
22
  id: string;
22
- title: string;
23
23
  datasetId: string;
24
24
  x: string;
25
25
  y: string;
26
26
  color?: string | undefined;
27
27
  }[];
28
28
  tables: {
29
- id: string;
30
29
  title: string;
30
+ id: string;
31
31
  datasetId: string;
32
32
  fields: string[];
33
33
  }[];
34
+ sourceMetadata?: {
35
+ bindingId: string;
36
+ title: string;
37
+ description?: string | undefined;
38
+ }[] | undefined;
39
+ components?: {
40
+ title: string;
41
+ id: string;
42
+ elementId: string;
43
+ kind: "filter" | "metric" | "chart" | "table" | "text";
44
+ sources: {
45
+ bindingId: string;
46
+ fieldIds?: string[] | undefined;
47
+ }[];
48
+ }[] | undefined;
34
49
  presentation?: {
35
50
  type: "html";
36
51
  html: string;
@@ -5,16 +5,19 @@ export declare const removalsSchema: z.ZodOptional<z.ZodObject<{
5
5
  controlIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
6
6
  viewIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
7
7
  htmlElementIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
8
+ componentIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
8
9
  }, "strict", z.ZodTypeAny, {
9
10
  bindingIds: string[];
10
11
  controlIds: string[];
11
12
  viewIds: string[];
12
13
  htmlElementIds: string[];
14
+ componentIds: string[];
13
15
  }, {
14
16
  bindingIds?: string[] | undefined;
15
17
  controlIds?: string[] | undefined;
16
18
  viewIds?: string[] | undefined;
17
19
  htmlElementIds?: string[] | undefined;
20
+ componentIds?: string[] | undefined;
18
21
  }>>;
19
22
  export declare function upsertComponents<T>(existing: T[], changes: T[] | undefined, id: (item: T) => string, removed?: string[]): T[];
20
23
  export declare function htmlElementIds(html: string): Set<string>;
@@ -8,7 +8,8 @@ const parse5_1 = require("parse5");
8
8
  const v3_1 = require("zod/v3");
9
9
  exports.removalsSchema = v3_1.z.object({
10
10
  bindingIds: v3_1.z.array(v3_1.z.string()).max(8).default([]), controlIds: v3_1.z.array(v3_1.z.string()).max(24).default([]),
11
- viewIds: v3_1.z.array(v3_1.z.string()).max(40).default([]), htmlElementIds: v3_1.z.array(v3_1.z.string()).max(500).default([])
11
+ viewIds: v3_1.z.array(v3_1.z.string()).max(40).default([]), htmlElementIds: v3_1.z.array(v3_1.z.string()).max(500).default([]),
12
+ componentIds: v3_1.z.array(v3_1.z.string()).max(160).default([])
12
13
  }).strict().optional().describe('Only explicitly requested removals. Arrays of changed components otherwise upsert by stable ID and preserve omitted components.');
13
14
  function upsertComponents(existing, changes, id, removed = []) {
14
15
  if (new Set(changes?.map(id)).size !== (changes?.length ?? 0))
@@ -84,41 +84,41 @@ export declare const dashboardStateSchema: z.ZodObject<{
84
84
  draftRevision?: number | undefined;
85
85
  }>>;
86
86
  }, "strip", z.ZodTypeAny, {
87
+ scope: string;
87
88
  target: {
88
89
  artifactId: string;
89
90
  schemaVersion: 1;
90
91
  versionId?: string | undefined;
91
92
  draftRevision?: number | undefined;
92
93
  } | null;
93
- scope: string;
94
94
  }, {
95
+ scope: string;
95
96
  target: {
96
97
  artifactId: string;
97
98
  schemaVersion: 1;
98
99
  versionId?: string | undefined;
99
100
  draftRevision?: number | undefined;
100
101
  } | null;
101
- scope: string;
102
102
  }>>>;
103
103
  }, "strip", z.ZodTypeAny, {
104
104
  dashboardEditing?: {
105
+ scope: string;
105
106
  target: {
106
107
  artifactId: string;
107
108
  schemaVersion: 1;
108
109
  versionId?: string | undefined;
109
110
  draftRevision?: number | undefined;
110
111
  } | null;
111
- scope: string;
112
112
  } | null | undefined;
113
113
  }, {
114
114
  dashboardEditing?: {
115
+ scope: string;
115
116
  target: {
116
117
  artifactId: string;
117
118
  schemaVersion: 1;
118
119
  versionId?: string | undefined;
119
120
  draftRevision?: number | undefined;
120
121
  } | null;
121
- scope: string;
122
122
  } | null | undefined;
123
123
  }>;
124
124
  /** Checkpoints belong to an actor and conversation. Copied state must never choose another user's target. */