@xpert-ai/plugin-live-dashboard 0.5.0 → 0.6.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/.xpertai-plugin/plugin.json +1 -1
- package/README.md +4 -2
- package/dist/index.js +3 -1
- package/dist/lib/agent.schema.d.ts +9 -9
- package/dist/lib/agent.schema.js +2 -1
- package/dist/lib/archive.service.d.ts +26 -2
- package/dist/lib/archive.service.js +17 -3
- package/dist/lib/bounded-map.d.ts +2 -0
- package/dist/lib/bounded-map.js +25 -0
- package/dist/lib/capacity.d.ts +14 -0
- package/dist/lib/capacity.js +16 -0
- package/dist/lib/dashboard-archive.d.ts +1 -1
- package/dist/lib/dashboard-queries.d.ts +1 -1
- package/dist/lib/dashboard-queries.js +11 -6
- package/dist/lib/dashboard-receipt.js +1 -1
- package/dist/lib/dashboard-sources.js +3 -2
- package/dist/lib/dashboard-view.schema.d.ts +20 -20
- package/dist/lib/dashboard.schema.d.ts +18 -18
- package/dist/lib/dashboard.schema.js +2 -1
- package/dist/lib/dashboard.service.d.ts +24 -5
- package/dist/lib/dashboard.service.js +58 -8
- package/dist/lib/definition-tools.d.ts +4 -4
- package/dist/lib/document-host.js +1 -1
- package/dist/lib/document-package.d.ts +1 -1
- package/dist/lib/editing-patch.js +2 -1
- package/dist/lib/editing.d.ts +11 -10
- package/dist/lib/editing.js +5 -1
- package/dist/lib/filter-evidence.d.ts +28 -0
- package/dist/lib/filter-evidence.js +3 -2
- package/dist/lib/html-guidance.d.ts +1 -0
- package/dist/lib/html-guidance.js +2 -1
- package/dist/lib/html-validation.js +1 -1
- package/dist/lib/middleware.d.ts +3 -1
- package/dist/lib/middleware.js +89 -11
- package/dist/lib/plugin.js +2 -1
- package/dist/lib/presentation.provider.d.ts +1 -1
- package/dist/lib/preview-retry.d.ts +4 -3
- package/dist/lib/preview-retry.js +13 -7
- package/dist/lib/prompt-workflows.js +1 -1
- package/dist/lib/query-session.d.ts +9 -0
- package/dist/lib/query-session.js +64 -0
- package/dist/lib/save-requests.d.ts +118 -0
- package/dist/lib/save-requests.js +199 -0
- package/dist/lib/source-executions.d.ts +243 -2
- package/dist/lib/source-executions.js +29 -5
- package/dist/lib/view.provider.d.ts +38 -3
- package/dist/lib/view.provider.js +20 -4
- package/dist/remote/app.js +114 -65
- package/dist/remote/build-manifest.json +37 -32
- package/dist/sandbox-actions/action.json +1 -1
- package/dist/sandbox-actions/bundle/host.js +63 -14
- package/dist/sandbox-actions/bundle/validate.mjs +2 -2
- package/dist/validation/host-entry.d.ts +5 -1
- package/dist/validation/host-entry.js +21 -4
- package/docs/docs.json +2 -0
- package/docs/large-dashboards-acceptance.mdx +74 -0
- package/docs/large-dashboards.mdx +80 -0
- package/package.json +5 -5
|
@@ -128,16 +128,16 @@ export declare const dashboardSchema: z.ZodEffects<z.ZodObject<{
|
|
|
128
128
|
y: z.ZodString;
|
|
129
129
|
color: z.ZodOptional<z.ZodString>;
|
|
130
130
|
}, "strict", z.ZodTypeAny, {
|
|
131
|
-
title: string;
|
|
132
131
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
132
|
+
title: string;
|
|
133
133
|
id: string;
|
|
134
134
|
datasetId: string;
|
|
135
135
|
x: string;
|
|
136
136
|
y: string;
|
|
137
137
|
color?: string | undefined;
|
|
138
138
|
}, {
|
|
139
|
-
title: string;
|
|
140
139
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
140
|
+
title: string;
|
|
141
141
|
id: string;
|
|
142
142
|
datasetId: string;
|
|
143
143
|
x: string;
|
|
@@ -173,8 +173,8 @@ export declare const dashboardSchema: z.ZodEffects<z.ZodObject<{
|
|
|
173
173
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
174
174
|
}[];
|
|
175
175
|
charts: {
|
|
176
|
-
title: string;
|
|
177
176
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
177
|
+
title: string;
|
|
178
178
|
id: string;
|
|
179
179
|
datasetId: string;
|
|
180
180
|
x: string;
|
|
@@ -220,8 +220,8 @@ export declare const dashboardSchema: z.ZodEffects<z.ZodObject<{
|
|
|
220
220
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
221
221
|
}[];
|
|
222
222
|
charts: {
|
|
223
|
-
title: string;
|
|
224
223
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
224
|
+
title: string;
|
|
225
225
|
id: string;
|
|
226
226
|
datasetId: string;
|
|
227
227
|
x: string;
|
|
@@ -273,8 +273,8 @@ export declare const dashboardSchema: z.ZodEffects<z.ZodObject<{
|
|
|
273
273
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
274
274
|
}[];
|
|
275
275
|
charts: {
|
|
276
|
-
title: string;
|
|
277
276
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
277
|
+
title: string;
|
|
278
278
|
id: string;
|
|
279
279
|
datasetId: string;
|
|
280
280
|
x: string;
|
|
@@ -320,8 +320,8 @@ export declare const dashboardSchema: z.ZodEffects<z.ZodObject<{
|
|
|
320
320
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
321
321
|
}[];
|
|
322
322
|
charts: {
|
|
323
|
-
title: string;
|
|
324
323
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
324
|
+
title: string;
|
|
325
325
|
id: string;
|
|
326
326
|
datasetId: string;
|
|
327
327
|
x: string;
|
|
@@ -493,16 +493,16 @@ export declare const dashboardChangesSchema: z.ZodObject<{
|
|
|
493
493
|
y: z.ZodString;
|
|
494
494
|
color: z.ZodOptional<z.ZodString>;
|
|
495
495
|
}, "strict", z.ZodTypeAny, {
|
|
496
|
-
title: string;
|
|
497
496
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
497
|
+
title: string;
|
|
498
498
|
id: string;
|
|
499
499
|
datasetId: string;
|
|
500
500
|
x: string;
|
|
501
501
|
y: string;
|
|
502
502
|
color?: string | undefined;
|
|
503
503
|
}, {
|
|
504
|
-
title: string;
|
|
505
504
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
505
|
+
title: string;
|
|
506
506
|
id: string;
|
|
507
507
|
datasetId: string;
|
|
508
508
|
x: string;
|
|
@@ -556,8 +556,8 @@ export declare const dashboardChangesSchema: z.ZodObject<{
|
|
|
556
556
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
557
557
|
}[] | undefined;
|
|
558
558
|
charts?: {
|
|
559
|
-
title: string;
|
|
560
559
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
560
|
+
title: string;
|
|
561
561
|
id: string;
|
|
562
562
|
datasetId: string;
|
|
563
563
|
x: string;
|
|
@@ -607,8 +607,8 @@ export declare const dashboardChangesSchema: z.ZodObject<{
|
|
|
607
607
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
608
608
|
}[] | undefined;
|
|
609
609
|
charts?: {
|
|
610
|
-
title: string;
|
|
611
610
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
611
|
+
title: string;
|
|
612
612
|
id: string;
|
|
613
613
|
datasetId: string;
|
|
614
614
|
x: string;
|
|
@@ -754,16 +754,16 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
754
754
|
y: z.ZodString;
|
|
755
755
|
color: z.ZodOptional<z.ZodString>;
|
|
756
756
|
}, "strict", z.ZodTypeAny, {
|
|
757
|
-
title: string;
|
|
758
757
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
758
|
+
title: string;
|
|
759
759
|
id: string;
|
|
760
760
|
datasetId: string;
|
|
761
761
|
x: string;
|
|
762
762
|
y: string;
|
|
763
763
|
color?: string | undefined;
|
|
764
764
|
}, {
|
|
765
|
-
title: string;
|
|
766
765
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
766
|
+
title: string;
|
|
767
767
|
id: string;
|
|
768
768
|
datasetId: string;
|
|
769
769
|
x: string;
|
|
@@ -799,8 +799,8 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
799
799
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
800
800
|
}[];
|
|
801
801
|
charts: {
|
|
802
|
-
title: string;
|
|
803
802
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
803
|
+
title: string;
|
|
804
804
|
id: string;
|
|
805
805
|
datasetId: string;
|
|
806
806
|
x: string;
|
|
@@ -846,8 +846,8 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
846
846
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
847
847
|
}[];
|
|
848
848
|
charts: {
|
|
849
|
-
title: string;
|
|
850
849
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
850
|
+
title: string;
|
|
851
851
|
id: string;
|
|
852
852
|
datasetId: string;
|
|
853
853
|
x: string;
|
|
@@ -899,8 +899,8 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
899
899
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
900
900
|
}[];
|
|
901
901
|
charts: {
|
|
902
|
-
title: string;
|
|
903
902
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
903
|
+
title: string;
|
|
904
904
|
id: string;
|
|
905
905
|
datasetId: string;
|
|
906
906
|
x: string;
|
|
@@ -946,8 +946,8 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
946
946
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
947
947
|
}[];
|
|
948
948
|
charts: {
|
|
949
|
-
title: string;
|
|
950
949
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
950
|
+
title: string;
|
|
951
951
|
id: string;
|
|
952
952
|
datasetId: string;
|
|
953
953
|
x: string;
|
|
@@ -1003,8 +1003,8 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
1003
1003
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
1004
1004
|
}[];
|
|
1005
1005
|
charts: {
|
|
1006
|
-
title: string;
|
|
1007
1006
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
1007
|
+
title: string;
|
|
1008
1008
|
id: string;
|
|
1009
1009
|
datasetId: string;
|
|
1010
1010
|
x: string;
|
|
@@ -1054,8 +1054,8 @@ export declare const saveSchema: z.ZodObject<{
|
|
|
1054
1054
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
1055
1055
|
}[];
|
|
1056
1056
|
charts: {
|
|
1057
|
-
title: string;
|
|
1058
1057
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
1058
|
+
title: string;
|
|
1059
1059
|
id: string;
|
|
1060
1060
|
datasetId: string;
|
|
1061
1061
|
x: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.queryBindingSchema = exports.querySchema = exports.saveSchema = exports.controlsSchema = exports.dashboardChangesSchema = exports.dashboardSchema = void 0;
|
|
4
|
+
const capacity_1 = require("./capacity");
|
|
4
5
|
const v3_1 = require("zod/v3");
|
|
5
6
|
const analytics_1 = require("@xpert-ai/plugin-sdk/analytics");
|
|
6
7
|
const artifact_document_1 = require("@xpert-ai/plugin-sdk/artifact-document");
|
|
@@ -8,7 +9,7 @@ const dashboard_sources_1 = require("./dashboard-sources");
|
|
|
8
9
|
const id = v3_1.z.string().regex(/^[A-Za-z][A-Za-z0-9_.-]{0,79}$/);
|
|
9
10
|
exports.dashboardSchema = v3_1.z.object({
|
|
10
11
|
schema: v3_1.z.literal('xpert.live-dashboard'), id: v3_1.z.string().uuid(), title: v3_1.z.string().min(1).max(160),
|
|
11
|
-
bindings: v3_1.z.array(analytics_1.analyticsBindingSchema).max(
|
|
12
|
+
bindings: v3_1.z.array(analytics_1.analyticsBindingSchema).max(capacity_1.MAX_DASHBOARD_BINDINGS),
|
|
12
13
|
sourceMetadata: dashboard_sources_1.sourceMetadataSchema.optional(),
|
|
13
14
|
components: dashboard_sources_1.dashboardComponentsSchema.optional(),
|
|
14
15
|
presentation: v3_1.z.object({ type: v3_1.z.literal('html'), html: artifact_document_1.artifactDocumentHtml, dependencies: artifact_document_1.artifactDocumentDependencyLocks.optional() }).strict().optional(),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type DashboardConfig } from './capacity';
|
|
1
2
|
import type { AnalyticsValue, AnalyticsDiscoveryRequest, AnalyticsQueryRequest, XpertResolvedViewHostContext } from '@xpert-ai/contracts';
|
|
2
3
|
import { type RuntimeCapabilityRegistry } from '@xpert-ai/plugin-sdk';
|
|
3
4
|
import { type DashboardManifest, type DashboardChanges } from './dashboard.schema';
|
|
@@ -5,10 +6,18 @@ import { type ArtifactDocumentDependencyAsset } from '@xpert-ai/plugin-sdk/artif
|
|
|
5
6
|
import { HtmlDashboardValidation } from './html-validation';
|
|
6
7
|
import type { DashboardAccess, DashboardMode } from './project';
|
|
7
8
|
export declare const PLUGIN_NAME = "@xpert-ai/plugin-live-dashboard";
|
|
9
|
+
export type SaveProgress = (stage: string, completed: number, total: number) => Promise<void>;
|
|
10
|
+
export type SaveOptions = {
|
|
11
|
+
requestId: string;
|
|
12
|
+
progress: SaveProgress;
|
|
13
|
+
};
|
|
8
14
|
export declare class DashboardService {
|
|
9
15
|
private readonly runtime;
|
|
10
16
|
private readonly htmlValidation;
|
|
11
|
-
|
|
17
|
+
private readonly config?;
|
|
18
|
+
constructor(runtime: RuntimeCapabilityRegistry, htmlValidation: HtmlDashboardValidation, config?: DashboardConfig | undefined);
|
|
19
|
+
get maxBindings(): number;
|
|
20
|
+
assertCapacity(count: number, previousCount?: number): void;
|
|
12
21
|
sources(context: XpertResolvedViewHostContext): Promise<import("@xpert-ai/contracts").AnalyticsSourceSummary[]>;
|
|
13
22
|
discover(context: XpertResolvedViewHostContext, input: AnalyticsDiscoveryRequest): Promise<import("@xpert-ai/contracts").AnalyticsDiscoveryResult>;
|
|
14
23
|
preview(context: XpertResolvedViewHostContext, input: AnalyticsQueryRequest): Promise<import("@xpert-ai/contracts").AnalyticalDataset>;
|
|
@@ -64,7 +73,17 @@ export declare class DashboardService {
|
|
|
64
73
|
trash(context: XpertResolvedViewHostContext, artifactId: string, expectedRevision: number): Promise<{
|
|
65
74
|
artifactId: string;
|
|
66
75
|
}>;
|
|
67
|
-
|
|
76
|
+
previousBindingCount(context: XpertResolvedViewHostContext, manifest: DashboardManifest): Promise<number>;
|
|
77
|
+
/** Read-only reconciliation of a completed CAS after a worker/status-store interruption. */
|
|
78
|
+
savedRequestReceipt(context: XpertResolvedViewHostContext, manifestId: string, requestId: string, expectedRevision: number): Promise<{
|
|
79
|
+
artifactId: string;
|
|
80
|
+
draftId: string;
|
|
81
|
+
draftRevision: number;
|
|
82
|
+
baseVersionId: string | null;
|
|
83
|
+
status: "draft";
|
|
84
|
+
title: string;
|
|
85
|
+
} | null>;
|
|
86
|
+
save(context: XpertResolvedViewHostContext, manifest: DashboardManifest, expectedVersionId: string | null, expectedRevision?: number, importedDependencies?: ArtifactDocumentDependencyAsset[], task?: SaveOptions): Promise<{
|
|
68
87
|
artifactId: string;
|
|
69
88
|
draftId: string;
|
|
70
89
|
draftRevision: number;
|
|
@@ -86,8 +105,8 @@ export declare class DashboardService {
|
|
|
86
105
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
87
106
|
}[];
|
|
88
107
|
charts: {
|
|
89
|
-
title: string;
|
|
90
108
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
109
|
+
title: string;
|
|
91
110
|
id: string;
|
|
92
111
|
datasetId: string;
|
|
93
112
|
x: string;
|
|
@@ -147,8 +166,8 @@ export declare class DashboardService {
|
|
|
147
166
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
148
167
|
}[];
|
|
149
168
|
charts: {
|
|
150
|
-
title: string;
|
|
151
169
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
170
|
+
title: string;
|
|
152
171
|
id: string;
|
|
153
172
|
datasetId: string;
|
|
154
173
|
x: string;
|
|
@@ -293,8 +312,8 @@ export declare class DashboardService {
|
|
|
293
312
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
294
313
|
}[];
|
|
295
314
|
charts: {
|
|
296
|
-
title: string;
|
|
297
315
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
316
|
+
title: string;
|
|
298
317
|
id: string;
|
|
299
318
|
datasetId: string;
|
|
300
319
|
x: string;
|
|
@@ -14,6 +14,8 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.DashboardService = exports.PLUGIN_NAME = void 0;
|
|
16
16
|
exports.projectId = projectId;
|
|
17
|
+
const capacity_1 = require("./capacity");
|
|
18
|
+
const bounded_map_1 = require("./bounded-map");
|
|
17
19
|
const common_1 = require("@nestjs/common");
|
|
18
20
|
const node_crypto_1 = require("node:crypto");
|
|
19
21
|
const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
|
|
@@ -30,9 +32,19 @@ exports.PLUGIN_NAME = '@xpert-ai/plugin-live-dashboard';
|
|
|
30
32
|
const RESOURCE_TYPE = 'live-dashboard';
|
|
31
33
|
const MIME_TYPE = 'application/json';
|
|
32
34
|
let DashboardService = class DashboardService {
|
|
33
|
-
constructor(runtime, htmlValidation) {
|
|
35
|
+
constructor(runtime, htmlValidation, config) {
|
|
34
36
|
this.runtime = runtime;
|
|
35
37
|
this.htmlValidation = htmlValidation;
|
|
38
|
+
this.config = config;
|
|
39
|
+
}
|
|
40
|
+
get maxBindings() { return this.config?.maxBindings ?? capacity_1.DEFAULT_DASHBOARD_BINDINGS; }
|
|
41
|
+
assertCapacity(count, previousCount = 0) {
|
|
42
|
+
try {
|
|
43
|
+
(0, capacity_1.assertBindingCapacity)(count, previousCount, this.maxBindings);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new common_1.BadRequestException(error instanceof Error ? error.message : 'DASHBOARD_BINDING_CAPACITY');
|
|
47
|
+
}
|
|
36
48
|
}
|
|
37
49
|
async sources(context) { await this.authorizeProject(context); return this.runtime.require(plugin_sdk_1.AnalyticsRuntimeCapability).list(actor(context)); }
|
|
38
50
|
async discover(context, input) { await this.authorizeProject(context); return this.runtime.require(plugin_sdk_1.AnalyticsRuntimeCapability).discover(actor(context), input); }
|
|
@@ -100,7 +112,31 @@ let DashboardService = class DashboardService {
|
|
|
100
112
|
await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).setRecycled({ artifactId, expectedRevision, recycled: true });
|
|
101
113
|
return { artifactId };
|
|
102
114
|
}
|
|
103
|
-
async
|
|
115
|
+
async previousBindingCount(context, manifest) {
|
|
116
|
+
await this.authorizeProject(context, 'write');
|
|
117
|
+
const existing = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).findArtifactBySource({ pluginName: exports.PLUGIN_NAME, resourceType: RESOURCE_TYPE, resourceId: `${hostPrefix(context)}${manifest.id}` });
|
|
118
|
+
if (!existing)
|
|
119
|
+
return 0;
|
|
120
|
+
assertOwned(context, existing);
|
|
121
|
+
const draft = (await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).list([existing.id]))[0];
|
|
122
|
+
return existing.currentVersionId || draft ? (await this.open(context, existing.id)).manifest.bindings.length : 0;
|
|
123
|
+
}
|
|
124
|
+
/** Read-only reconciliation of a completed CAS after a worker/status-store interruption. */
|
|
125
|
+
async savedRequestReceipt(context, manifestId, requestId, expectedRevision) {
|
|
126
|
+
await this.authorizeProject(context, 'write');
|
|
127
|
+
const artifact = await this.runtime.require(plugin_sdk_1.ArtifactsRuntimeCapability).findArtifactBySource({ pluginName: exports.PLUGIN_NAME, resourceType: RESOURCE_TYPE, resourceId: `${hostPrefix(context)}${manifestId}` });
|
|
128
|
+
if (!artifact)
|
|
129
|
+
return null;
|
|
130
|
+
assertOwned(context, artifact);
|
|
131
|
+
if (artifact.status !== 'active')
|
|
132
|
+
throw new common_1.ForbiddenException('DASHBOARD_ARCHIVED');
|
|
133
|
+
const draft = await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).get(artifact.id);
|
|
134
|
+
if (draft?.metadata?.saveRequestId !== requestId || draft.revision !== expectedRevision + 1)
|
|
135
|
+
return null;
|
|
136
|
+
return { artifactId: artifact.id, draftId: draft.id, draftRevision: draft.revision, baseVersionId: draft.baseVersionId, status: 'draft', title: draft.title ?? artifact.title ?? '' };
|
|
137
|
+
}
|
|
138
|
+
async save(context, manifest, expectedVersionId, expectedRevision = 0, importedDependencies, task) {
|
|
139
|
+
await task?.progress('authorization', 0, manifest.bindings.length);
|
|
104
140
|
await this.authorizeProject(context, 'write');
|
|
105
141
|
const scopedActor = actor(context);
|
|
106
142
|
const parsed = dashboard_schema_1.dashboardSchema.parse(manifest);
|
|
@@ -115,6 +151,7 @@ let DashboardService = class DashboardService {
|
|
|
115
151
|
}
|
|
116
152
|
// A failed first upload can leave an Artifact with no saved content; revision 0 initializes it.
|
|
117
153
|
const previous = existing && (existing.currentVersionId || expectedRevision > 0) ? await this.openDocument(context, existing.id) : null;
|
|
154
|
+
this.assertCapacity(parsed.bindings.length, previous?.manifest.bindings.length ?? 0);
|
|
118
155
|
if (importedDependencies && existing)
|
|
119
156
|
throw new common_1.BadRequestException('DASHBOARD_IMPORT_NEW_ID_REQUIRED');
|
|
120
157
|
const dependencies = importedDependencies ? (0, artifact_document_1.verifyArtifactDocumentDependencies)(importedDependencies, parsed.presentation?.dependencies ?? [])
|
|
@@ -123,21 +160,31 @@ let DashboardService = class DashboardService {
|
|
|
123
160
|
if (buffer.byteLength > document_package_1.MAX_DASHBOARD_PACKAGE_BYTES)
|
|
124
161
|
throw new common_1.BadRequestException('DASHBOARD_MANIFEST_TOO_LARGE');
|
|
125
162
|
const digest = (0, node_crypto_1.createHash)('sha256').update(buffer).digest('hex');
|
|
163
|
+
if (existing && task) {
|
|
164
|
+
const draft = await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).get(existing.id);
|
|
165
|
+
if (draft?.metadata?.saveRequestId === task.requestId && draft.sha256 === digest && draft.revision === expectedRevision + 1)
|
|
166
|
+
return { artifactId: existing.id, draftId: draft.id, draftRevision: draft.revision, baseVersionId: draft.baseVersionId, status: 'draft', title: parsed.title };
|
|
167
|
+
}
|
|
126
168
|
if ((existing?.currentVersionId ?? null) !== expectedVersionId)
|
|
127
169
|
throw new common_1.ConflictException('DASHBOARD_VERSION_CONFLICT');
|
|
128
170
|
// Recheck current data access even for an idempotent retry; the platform enforces the final CAS under a row lock.
|
|
129
|
-
const preview = await this.
|
|
171
|
+
const preview = await (0, dashboard_queries_1.queryDashboard)(this.runtime, actor(context), parsed, Object.fromEntries(parsed.controls.filter((control) => control.defaultValue !== undefined).map((control) => [control.controlId, control.defaultValue ?? null])), task ? completed => task.progress('queries', completed, parsed.bindings.length) : undefined);
|
|
130
172
|
if (!parsed.presentation)
|
|
131
173
|
(0, renderer_1.toDataAnalyticsPayload)(parsed, preview);
|
|
132
|
-
|
|
133
|
-
|
|
174
|
+
let completedFilters = 0;
|
|
175
|
+
await (0, bounded_map_1.boundedMap)(parsed.bindings, async (binding) => {
|
|
176
|
+
await this.probeControls(context, binding, Object.fromEntries(parsed.controls.filter((control) => control.defaultValue !== undefined).map((control) => [control.controlId, control.defaultValue ?? null])), task ? () => task.progress('filters', completedFilters, parsed.bindings.length) : undefined);
|
|
177
|
+
await task?.progress('filters', ++completedFilters, parsed.bindings.length);
|
|
178
|
+
});
|
|
179
|
+
await task?.progress('html', 0, 1);
|
|
134
180
|
const htmlValidation = await this.htmlValidation.validate(context, parsed, preview, dependencies);
|
|
181
|
+
await task?.progress('save', 0, 1);
|
|
135
182
|
const artifact = existing ?? await artifacts.createArtifact({ source, type: { pluginName: exports.PLUGIN_NAME, resourceType: RESOURCE_TYPE }, kind: 'file', title: parsed.title,
|
|
136
183
|
scope: { ...scopedActor, ...(projectId(context) ? { projectId: projectId(context), userId: null } : { xpertId: context.hostId }) } });
|
|
137
184
|
const reference = await this.runtime.require(plugin_sdk_1.ArtifactContentRuntimeCapability).write({ artifactId: artifact.id, buffer });
|
|
138
185
|
const result = await this.runtime.require(plugin_sdk_1.ArtifactDraftsRuntimeCapability).save({ artifactId: artifact.id, expectedRevision,
|
|
139
186
|
expectedCurrentVersionId: expectedVersionId, mimeType: MIME_TYPE, title: parsed.title, fileName: 'dashboard.json', sha256: digest,
|
|
140
|
-
...(htmlValidation ? { metadata: { htmlValidation } } : {}), workspaceFileRef: reference });
|
|
187
|
+
...((htmlValidation || task) ? { metadata: { ...(htmlValidation ? { htmlValidation } : {}), ...(task ? { saveRequestId: task.requestId } : {}) } } : {}), workspaceFileRef: reference });
|
|
141
188
|
return { artifactId: artifact.id, draftId: result.id, draftRevision: result.revision, baseVersionId: result.baseVersionId, status: 'draft', title: parsed.title };
|
|
142
189
|
}
|
|
143
190
|
async open(context, artifactId, versionId, draftRevision, mode = 'draft') {
|
|
@@ -242,7 +289,7 @@ let DashboardService = class DashboardService {
|
|
|
242
289
|
execute(context, manifest, input) {
|
|
243
290
|
return (0, dashboard_queries_1.queryDashboard)(this.runtime, actor(context), manifest, input);
|
|
244
291
|
}
|
|
245
|
-
async probeControls(context, binding, input) {
|
|
292
|
+
async probeControls(context, binding, input, checkpoint) {
|
|
246
293
|
const controls = Object.fromEntries(binding.parameters.map((parameter) => [parameter.controlId,
|
|
247
294
|
Object.hasOwn(input, parameter.controlId) ? input[parameter.controlId] : parameter.defaultValue ?? null]));
|
|
248
295
|
// A null default can bypass a native predicate entirely. Verify a real selection too,
|
|
@@ -253,6 +300,7 @@ let DashboardService = class DashboardService {
|
|
|
253
300
|
const samples = [...(alternative !== undefined ? [alternative] : []),
|
|
254
301
|
...(current !== null && parameter.allowedValues?.includes(null) ? [null] : [])];
|
|
255
302
|
for (const value of samples) {
|
|
303
|
+
await checkpoint?.();
|
|
256
304
|
// Analytics already redacts provider failures. Preserve its actionable validation
|
|
257
305
|
// errors: a duplicate grain is not a malformed filter and needs a different repair.
|
|
258
306
|
await this.runtime.require(plugin_sdk_1.AnalyticsRuntimeCapability).query(actor(context), { binding, controls: { ...controls, [parameter.controlId]: value } });
|
|
@@ -264,7 +312,9 @@ exports.DashboardService = DashboardService;
|
|
|
264
312
|
exports.DashboardService = DashboardService = __decorate([
|
|
265
313
|
(0, common_1.Injectable)(),
|
|
266
314
|
__param(0, (0, common_1.Inject)(plugin_sdk_1.XPERT_RUNTIME_CAPABILITIES_TOKEN)),
|
|
267
|
-
|
|
315
|
+
__param(2, (0, common_1.Optional)()),
|
|
316
|
+
__param(2, (0, common_1.Inject)(capacity_1.DASHBOARD_CONFIG)),
|
|
317
|
+
__metadata("design:paramtypes", [Object, html_validation_1.HtmlDashboardValidation, Object])
|
|
268
318
|
], DashboardService);
|
|
269
319
|
function actor(context) {
|
|
270
320
|
if (!context.tenantId || !context.organizationId || !context.userId || !['agent', 'project'].includes(context.hostType))
|
|
@@ -31,26 +31,26 @@ export declare function createDefinitionTools(context: IAgentMiddlewareContext,
|
|
|
31
31
|
definitionJson: z.ZodString;
|
|
32
32
|
}, "strict", z.ZodTypeAny, {
|
|
33
33
|
name: string;
|
|
34
|
+
requestId: string;
|
|
34
35
|
providerId: string;
|
|
35
36
|
connectionKey: string;
|
|
36
|
-
requestId: string;
|
|
37
37
|
definitionJson: string;
|
|
38
38
|
}, {
|
|
39
39
|
name: string;
|
|
40
|
+
requestId: string;
|
|
40
41
|
providerId: string;
|
|
41
42
|
connectionKey: string;
|
|
42
|
-
requestId: string;
|
|
43
43
|
definitionJson: string;
|
|
44
44
|
}>, {
|
|
45
45
|
name: string;
|
|
46
|
+
requestId: string;
|
|
46
47
|
providerId: string;
|
|
47
48
|
connectionKey: string;
|
|
48
|
-
requestId: string;
|
|
49
49
|
definitionJson: string;
|
|
50
50
|
}, {
|
|
51
51
|
name: string;
|
|
52
|
+
requestId: string;
|
|
52
53
|
providerId: string;
|
|
53
54
|
connectionKey: string;
|
|
54
|
-
requestId: string;
|
|
55
55
|
definitionJson: string;
|
|
56
56
|
}, string>)[];
|
|
@@ -6,7 +6,7 @@ const artifact_document_1 = require("@xpert-ai/plugin-sdk/artifact-document");
|
|
|
6
6
|
const document_network_1 = require("./document-network");
|
|
7
7
|
/** The HTML never supplies or overrides its network permissions. */
|
|
8
8
|
function mountDashboardDocument(frame, options) {
|
|
9
|
-
return (0, artifact_document_1.mountArtifactDocument)(frame, { ...options, networkPolicy: document_network_1.DASHBOARD_NETWORK_POLICY });
|
|
9
|
+
return (0, artifact_document_1.mountArtifactDocument)(frame, { ...options, dispatchesPerMinute: 60, networkPolicy: document_network_1.DASHBOARD_NETWORK_POLICY });
|
|
10
10
|
}
|
|
11
11
|
function isDashboardResourceAllowed(resourceType, url) {
|
|
12
12
|
return (0, artifact_document_1.isArtifactDocumentResourceAllowed)(document_network_1.DASHBOARD_NETWORK_POLICY, resourceType, url);
|
|
@@ -17,8 +17,8 @@ export declare function decodeDashboardPackage(bytes: Buffer): {
|
|
|
17
17
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
18
18
|
}[];
|
|
19
19
|
charts: {
|
|
20
|
-
title: string;
|
|
21
20
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
21
|
+
title: string;
|
|
22
22
|
id: string;
|
|
23
23
|
datasetId: string;
|
|
24
24
|
x: string;
|
|
@@ -4,10 +4,11 @@ exports.removalsSchema = void 0;
|
|
|
4
4
|
exports.upsertComponents = upsertComponents;
|
|
5
5
|
exports.htmlElementIds = htmlElementIds;
|
|
6
6
|
exports.assertHtmlPreserved = assertHtmlPreserved;
|
|
7
|
+
const capacity_1 = require("./capacity");
|
|
7
8
|
const parse5_1 = require("parse5");
|
|
8
9
|
const v3_1 = require("zod/v3");
|
|
9
10
|
exports.removalsSchema = v3_1.z.object({
|
|
10
|
-
bindingIds: v3_1.z.array(v3_1.z.string()).max(
|
|
11
|
+
bindingIds: v3_1.z.array(v3_1.z.string()).max(capacity_1.MAX_DASHBOARD_BINDINGS).default([]), controlIds: v3_1.z.array(v3_1.z.string()).max(24).default([]),
|
|
11
12
|
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
13
|
componentIds: v3_1.z.array(v3_1.z.string()).max(160).default([])
|
|
13
14
|
}).strict().optional().describe('Only explicitly requested removals. Arrays of changed components otherwise upsert by stable ID and preserve omitted components.');
|
package/dist/lib/editing.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DashboardSaveRequests } from './save-requests';
|
|
1
2
|
import { z } from 'zod/v3';
|
|
2
3
|
import type { AnalyticsBinding, XpertResolvedViewHostContext } from '@xpert-ai/contracts';
|
|
3
4
|
import { type IAgentMiddlewareContext } from '@xpert-ai/plugin-sdk';
|
|
@@ -101,24 +102,24 @@ export declare const updateToolSchema: z.ZodObject<{
|
|
|
101
102
|
y: z.ZodString;
|
|
102
103
|
color: z.ZodOptional<z.ZodString>;
|
|
103
104
|
}, "strict", z.ZodTypeAny, {
|
|
104
|
-
title: string;
|
|
105
105
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
106
|
+
title: string;
|
|
106
107
|
id: string;
|
|
107
108
|
datasetId: string;
|
|
108
109
|
x: string;
|
|
109
110
|
y: string;
|
|
110
111
|
color?: string | undefined;
|
|
111
112
|
}, {
|
|
112
|
-
title: string;
|
|
113
113
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
114
|
+
title: string;
|
|
114
115
|
id: string;
|
|
115
116
|
datasetId: string;
|
|
116
117
|
x: string;
|
|
117
118
|
y: string;
|
|
118
119
|
color?: string | undefined;
|
|
119
120
|
}>, "many">, {
|
|
120
|
-
title: string;
|
|
121
121
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
122
|
+
title: string;
|
|
122
123
|
id: string;
|
|
123
124
|
datasetId: string;
|
|
124
125
|
x: string;
|
|
@@ -202,8 +203,8 @@ export declare const updateToolSchema: z.ZodObject<{
|
|
|
202
203
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
203
204
|
}[] | undefined;
|
|
204
205
|
charts?: {
|
|
205
|
-
title: string;
|
|
206
206
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
207
|
+
title: string;
|
|
207
208
|
id: string;
|
|
208
209
|
datasetId: string;
|
|
209
210
|
x: string;
|
|
@@ -251,7 +252,7 @@ export declare const updateToolSchema: z.ZodObject<{
|
|
|
251
252
|
}>;
|
|
252
253
|
export declare const EDITING_GUIDANCE = "Opening or reading a dashboard does not modify it. A selection_required result is normal: show its message to the user and wait for an explicit selection; do not retry, infer a target from history, or create a replacement. Do not save an unchanged editing copy or reopen/update merely to confirm a successful save; use the mutation receipt. For existing dashboards, call dashboard_open first and inspect presentation.type. For standard dashboards, update definition fields directly: a rename needs only title, editId, expectedRevision and expectedCurrentVersionId. No HTML file exists; omit htmlPath and libraries. For HTML dashboards only, open prepares a unique editing copy at htmlPath. Read with sandbox_read_file. If a long HTML line is truncated, do not repeat the same read: use dashboard_read_html with editId and follow nextOffset until null to inspect the saved source, then apply exact replacements to htmlPath. Never replace a file with a truncated preview. Edit with sandbox_edit_file/multi_edit_file (sandbox_write_file is create-only), then call dashboard_update with that exact htmlPath and read revisions. If the user requests a rename, include the new title in this same update; editing the HTML heading alone does not rename the Artifact. Save each batch automatically without human confirmation. Files are not dashboard saves. Preserve Binding semantics, stable component IDs and dependency locks unless requested. To add filters to an existing Binding, copy its complete dashboard_open.bindings[] entry, retain binding.id and existing parameters[].controlId, and call dashboard_preview_binding after changing payload/parameters/defaults. Binding ID is independent of output.datasetId; simplified dashboard_preview derives the ID from datasetId and can accidentally add a new Binding instead of updating the old one. Use the stable Binding ID in HTML query/subscribe calls. Submit every intended changed/new Binding previewId and matching controls with htmlPath in the same dashboard_update. Update arrays upsert by ID, preserving omitted components; removals must name explicitly requested deletions. On preflight failure, compare expected/requested Binding IDs, repair the HTML or previews, and retain all intended previewIds, controls, title and htmlPath when retrying. Dropping previewIds does not register new HTML queries. Do not remove an existing Binding merely to suppress validation. HTML updates retain existing element IDs. For new HTML, assign stable IDs to every KPI, chart, detail table and filter. Read and patch the current file instead of rebuilding unrelated sections. On conflict retain the editing file, open the latest draft and reconcile; never silently overwrite. Historical versions are read-only references. All Agent saves are drafts; only the Workbench human publish action creates formal versions. Read the live-dashboard skill for the complete workflow.";
|
|
253
254
|
/** Run-local read receipts carry planning versions; authorization and CAS remain in the domain service. */
|
|
254
|
-
export declare function createEditingTools(service: DashboardService, scope: XpertResolvedViewHostContext, context: IAgentMiddlewareContext, resolvePreviews: (ids: string[]) => AnalyticsBinding[], validateFinal: (manifest: DashboardManifest, requirePreviews: boolean, changedBindingIds?: string[]) => void, onOpen?: (path: string, artifactId: string) => void, currentTarget?: (config: unknown) => DashboardSelection | null | undefined): (import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
|
|
255
|
+
export declare function createEditingTools(service: DashboardService, scope: XpertResolvedViewHostContext, context: IAgentMiddlewareContext, resolvePreviews: (ids: string[]) => AnalyticsBinding[], validateFinal: (manifest: DashboardManifest, requirePreviews: boolean, changedBindingIds?: string[]) => void, onOpen?: (path: string, artifactId: string) => void, currentTarget?: (config: unknown) => DashboardSelection | null | undefined, saveRequests?: DashboardSaveRequests): (import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
|
|
255
256
|
page: z.ZodDefault<z.ZodNumber>;
|
|
256
257
|
}, "strict", z.ZodTypeAny, {
|
|
257
258
|
page: number;
|
|
@@ -394,24 +395,24 @@ export declare function createEditingTools(service: DashboardService, scope: Xpe
|
|
|
394
395
|
y: z.ZodString;
|
|
395
396
|
color: z.ZodOptional<z.ZodString>;
|
|
396
397
|
}, "strict", z.ZodTypeAny, {
|
|
397
|
-
title: string;
|
|
398
398
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
399
|
+
title: string;
|
|
399
400
|
id: string;
|
|
400
401
|
datasetId: string;
|
|
401
402
|
x: string;
|
|
402
403
|
y: string;
|
|
403
404
|
color?: string | undefined;
|
|
404
405
|
}, {
|
|
405
|
-
title: string;
|
|
406
406
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
407
|
+
title: string;
|
|
407
408
|
id: string;
|
|
408
409
|
datasetId: string;
|
|
409
410
|
x: string;
|
|
410
411
|
y: string;
|
|
411
412
|
color?: string | undefined;
|
|
412
413
|
}>, "many">, {
|
|
413
|
-
title: string;
|
|
414
414
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
415
|
+
title: string;
|
|
415
416
|
id: string;
|
|
416
417
|
datasetId: string;
|
|
417
418
|
x: string;
|
|
@@ -495,8 +496,8 @@ export declare function createEditingTools(service: DashboardService, scope: Xpe
|
|
|
495
496
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
496
497
|
}[] | undefined;
|
|
497
498
|
charts?: {
|
|
498
|
-
title: string;
|
|
499
499
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
500
|
+
title: string;
|
|
500
501
|
id: string;
|
|
501
502
|
datasetId: string;
|
|
502
503
|
x: string;
|
|
@@ -569,8 +570,8 @@ export declare function createEditingTools(service: DashboardService, scope: Xpe
|
|
|
569
570
|
allowedValues?: (string | number | boolean | null)[] | undefined;
|
|
570
571
|
}[] | undefined;
|
|
571
572
|
charts?: {
|
|
572
|
-
title: string;
|
|
573
573
|
type: "bar" | "line" | "area" | "pie" | "scatter";
|
|
574
|
+
title: string;
|
|
574
575
|
id: string;
|
|
575
576
|
datasetId: string;
|
|
576
577
|
x: string;
|
package/dist/lib/editing.js
CHANGED
|
@@ -25,7 +25,7 @@ const readHtmlSchema = v3_1.z.object({ editId: v3_1.z.string().uuid(), versionId
|
|
|
25
25
|
length: v3_1.z.number().int().min(1).max(24000).default(16000) }).strict();
|
|
26
26
|
exports.EDITING_GUIDANCE = `Opening or reading a dashboard does not modify it. A selection_required result is normal: show its message to the user and wait for an explicit selection; do not retry, infer a target from history, or create a replacement. Do not save an unchanged editing copy or reopen/update merely to confirm a successful save; use the mutation receipt. For existing dashboards, call dashboard_open first and inspect presentation.type. For standard dashboards, update definition fields directly: a rename needs only title, editId, expectedRevision and expectedCurrentVersionId. No HTML file exists; omit htmlPath and libraries. For HTML dashboards only, open prepares a unique editing copy at htmlPath. Read with sandbox_read_file. If a long HTML line is truncated, do not repeat the same read: use dashboard_read_html with editId and follow nextOffset until null to inspect the saved source, then apply exact replacements to htmlPath. Never replace a file with a truncated preview. Edit with sandbox_edit_file/multi_edit_file (sandbox_write_file is create-only), then call dashboard_update with that exact htmlPath and read revisions. If the user requests a rename, include the new title in this same update; editing the HTML heading alone does not rename the Artifact. Save each batch automatically without human confirmation. Files are not dashboard saves. Preserve Binding semantics, stable component IDs and dependency locks unless requested. To add filters to an existing Binding, copy its complete dashboard_open.bindings[] entry, retain binding.id and existing parameters[].controlId, and call dashboard_preview_binding after changing payload/parameters/defaults. Binding ID is independent of output.datasetId; simplified dashboard_preview derives the ID from datasetId and can accidentally add a new Binding instead of updating the old one. Use the stable Binding ID in HTML query/subscribe calls. Submit every intended changed/new Binding previewId and matching controls with htmlPath in the same dashboard_update. Update arrays upsert by ID, preserving omitted components; removals must name explicitly requested deletions. On preflight failure, compare expected/requested Binding IDs, repair the HTML or previews, and retain all intended previewIds, controls, title and htmlPath when retrying. Dropping previewIds does not register new HTML queries. Do not remove an existing Binding merely to suppress validation. HTML updates retain existing element IDs. For new HTML, assign stable IDs to every KPI, chart, detail table and filter. Read and patch the current file instead of rebuilding unrelated sections. On conflict retain the editing file, open the latest draft and reconcile; never silently overwrite. Historical versions are read-only references. All Agent saves are drafts; only the Workbench human publish action creates formal versions. Read the live-dashboard skill for the complete workflow.`;
|
|
27
27
|
/** Run-local read receipts carry planning versions; authorization and CAS remain in the domain service. */
|
|
28
|
-
function createEditingTools(service, scope, context, resolvePreviews, validateFinal, onOpen = () => undefined, currentTarget = editing_session_1.selectionFromConfig) {
|
|
28
|
+
function createEditingTools(service, scope, context, resolvePreviews, validateFinal, onOpen = () => undefined, currentTarget = editing_session_1.selectionFromConfig, saveRequests) {
|
|
29
29
|
const target = (input, config) => {
|
|
30
30
|
const selected = currentTarget(config);
|
|
31
31
|
if (input.artifactId)
|
|
@@ -119,6 +119,10 @@ function createEditingTools(service, scope, context, resolvePreviews, validateFi
|
|
|
119
119
|
...(sourceMetadata.length || saved.manifest.sourceMetadata ? { sourceMetadata } : {}), ...(components.length || saved.manifest.components ? { components } : {}),
|
|
120
120
|
...(presentation ? { presentation } : {}) });
|
|
121
121
|
validateFinal(manifest, previewIds !== undefined, changedBindings?.map(item => item.id));
|
|
122
|
+
if (saveRequests && manifest.bindings.length > 8) {
|
|
123
|
+
const key = (0, node_crypto_1.createHash)('sha256').update(JSON.stringify([editId, expectedRevision, expectedCurrentVersionId, manifest, sourceFile])).digest('hex');
|
|
124
|
+
return JSON.stringify(await saveRequests.submit(scope, manifest, expectedCurrentVersionId, expectedRevision, key, sourceFile));
|
|
125
|
+
}
|
|
122
126
|
const { schema, id, ...update } = manifest;
|
|
123
127
|
const result = await service.update(scope, editId, expectedRevision, update, expectedCurrentVersionId);
|
|
124
128
|
const resourceCard = await (0, resource_card_1.emitDashboardCard)(scope, { ...result, state: 'draft', revision: result.draftRevision }, config);
|
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import { z } from 'zod/v3';
|
|
2
2
|
import type { AnalyticsBinding, AnalyticsValue, XpertResolvedViewHostContext } from '@xpert-ai/contracts';
|
|
3
3
|
import type { DashboardService } from './dashboard.service';
|
|
4
|
+
export declare const filterTestSchema: z.ZodObject<{
|
|
5
|
+
previewId: z.ZodString;
|
|
6
|
+
controls: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>, Record<string, string | number | boolean | null>, unknown>;
|
|
7
|
+
expectations: z.ZodEffects<z.ZodArray<z.ZodObject<{
|
|
8
|
+
fieldId: z.ZodString;
|
|
9
|
+
oneOf: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>, "many">;
|
|
10
|
+
}, "strict", z.ZodTypeAny, {
|
|
11
|
+
fieldId: string;
|
|
12
|
+
oneOf: (string | number | boolean | null)[];
|
|
13
|
+
}, {
|
|
14
|
+
fieldId: string;
|
|
15
|
+
oneOf: (string | number | boolean | null)[];
|
|
16
|
+
}>, "many">, {
|
|
17
|
+
fieldId: string;
|
|
18
|
+
oneOf: (string | number | boolean | null)[];
|
|
19
|
+
}[], unknown>;
|
|
20
|
+
}, "strict", z.ZodTypeAny, {
|
|
21
|
+
controls: Record<string, string | number | boolean | null>;
|
|
22
|
+
previewId: string;
|
|
23
|
+
expectations: {
|
|
24
|
+
fieldId: string;
|
|
25
|
+
oneOf: (string | number | boolean | null)[];
|
|
26
|
+
}[];
|
|
27
|
+
}, {
|
|
28
|
+
previewId: string;
|
|
29
|
+
controls?: unknown;
|
|
30
|
+
expectations?: unknown;
|
|
31
|
+
}>;
|
|
4
32
|
/** Model quality evidence, not authorization: every query still goes through Analytics. */
|
|
5
33
|
export declare function createFilterEvidence(service: DashboardService, scope: XpertResolvedViewHostContext, previews: Map<string, AnalyticsBinding>): {
|
|
6
34
|
requirements(binding: AnalyticsBinding, previewId: string, rows: Record<string, AnalyticsValue>[]): {
|