@xpert-ai/plugin-analytics-data-xpert 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/dist/lib/client.d.ts +2 -49
- package/dist/lib/client.js +41 -26
- package/dist/lib/diagnostic.d.ts +10 -0
- package/dist/lib/diagnostic.js +58 -0
- package/dist/lib/discovery.js +3 -1
- package/dist/lib/import-targets.d.ts +164 -0
- package/dist/lib/import-targets.js +30 -0
- package/dist/lib/payload.d.ts +50 -0
- package/dist/lib/payload.js +17 -0
- package/dist/lib/result-schema.d.ts +101 -0
- package/dist/lib/result-schema.js +44 -0
- package/docs/verification.mdx +4 -3
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -18,4 +18,28 @@ Run `corepack pnpm exec nx run @xpert-ai/plugin-analytics-data-xpert:test` with
|
|
|
18
18
|
|
|
19
19
|
Discovery preserves each adapter's `definition` (identity, version, native metadata and instructions) and each Action's input schema. Semantic Model, OData and future registered read-only adapters share this transport without sharing query syntax.
|
|
20
20
|
|
|
21
|
+
Before constructing a dashboard binding, call `dashboard_discover` with the selected source/provider and `requestJson` encoding:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{"kind":"query-schema","payload":{"resourceId":"<discovered-resource>","actionTypeCode":"<discovered-read-action>","target":{"entityRef":"<canonical-target>"},"params":{},"parameterPaths":{}},"parameters":{}}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Omit `datasetId` to let the provider generate a valid local output key from the native payload, including its projection. An optional `datasetId` accepts a nonempty string of up to 512 characters: valid output keys (a letter followed by up to 79 letters, digits, `_`, `.` or `-`) are preserved; native references or other aliases are replaced with a generated key. Native resource IDs and targets are unchanged. Copy the returned `output.datasetId` as well as its fields.
|
|
28
|
+
|
|
29
|
+
Use the Action's actual bounded query arguments in `params`. This plugin-owned discovery request executes the same authorized `/agent-workbench/analytics/query` path as preview, with the current actor, host row limit and cancellation signal. It does not require a new DataXpert discovery endpoint. Copy `items[0].metadata.output` into the preview binding and use the same native payload and parameter values. The response includes no business rows and is not a preview receipt; a successful `dashboard_preview` is still required before saving.
|
|
30
|
+
|
|
31
|
+
Output fields retain exact native keys and declared types, roles and nullability, including empty results. Decimal/int64 cells remain exact strings. Stable field IDs are derived from native keys; duplicate semantic selectors use exact keys instead. Grain is left empty until query semantics establish uniqueness. Missing declarations fail with `DATAX_RESULT_SCHEMA_UNAVAILABLE`; duplicate native keys fail with `DATAX_RESULT_SCHEMA_AMBIGUOUS`. Legacy authored bindings can still execute without these optional declarations. Rediscover after changing the projection; existing incorrect saved bindings must be corrected and previewed before saving.
|
|
32
|
+
|
|
33
|
+
Release compatibility: this path uses existing exports from the published SDK/contracts 3.26.1 and does not require a new host capability. The companion SDK change only adds optional column metadata to its TypeScript contract. Merge and publish this plugin's Changeset, then update the installed plugin; merging source alone does not update an existing deployment. Schema authoring requires DataXpert to supply declared column metadata (including the decimal fix in data-xpert PR #108). SAP query errors, authentication, and model-generated malformed tool calls remain separate acceptance checks.
|
|
34
|
+
|
|
35
|
+
After building this plugin and Live Dashboard, run `node plugin-dev-harness/verify-datax-dashboard-schema.mjs` from the repository root. It invokes the real discovery and preview tools, provider and HTTP parser with published SDKs. Its local HTTP source and host-service fixtures do not establish customer SSO, SAP or browser acceptance.
|
|
36
|
+
|
|
21
37
|
See [architecture and Binding usage](docs/index.mdx) and [compatibility and verification](docs/verification.mdx).
|
|
38
|
+
# Public query diagnostics
|
|
39
|
+
|
|
40
|
+
Non-success HTTP responses may contain the shared `xpert.analytics.diagnostic` v1 envelope.
|
|
41
|
+
Only validated public causes and opaque audit references cross into `AnalyticsProviderError`.
|
|
42
|
+
Malformed, sensitive, oversized or unfinished bodies use the existing status-only fallback.
|
|
43
|
+
Parameter mapping failures use provider-authored repair guidance without echoing values.
|
|
44
|
+
|
|
45
|
+
Structured diagnostics require `@xpert-ai/contracts` and `@xpert-ai/plugin-sdk` 3.32.0 or a compatible later 3.x release.
|
package/dist/lib/client.d.ts
CHANGED
|
@@ -1,61 +1,14 @@
|
|
|
1
|
-
import { z } from 'zod/v3';
|
|
2
1
|
import type { AnalyticsBinding, AnalyticsValue, JSONValue, AnalyticsDiscoveryResult } from '@xpert-ai/contracts';
|
|
2
|
+
export { dataXpertPayloadSchema } from './payload';
|
|
3
3
|
import { type AnalyticsProviderContext, type AnalyticsProviderResult, type RuntimeCapabilityRegistry } from '@xpert-ai/plugin-sdk';
|
|
4
4
|
export type DataXpertClientContext = Pick<AnalyticsProviderContext, 'actor' | 'options' | 'signal' | 'deadlineAt' | 'maxRows'>;
|
|
5
|
-
/** Native Action arguments stay plugin-owned; the host only interprets Dataset/Binding. */
|
|
6
|
-
export declare const dataXpertPayloadSchema: z.ZodObject<{
|
|
7
|
-
resourceId: z.ZodString;
|
|
8
|
-
actionTypeCode: z.ZodString;
|
|
9
|
-
target: z.ZodEffects<z.ZodObject<{
|
|
10
|
-
entityId: z.ZodOptional<z.ZodString>;
|
|
11
|
-
entityTypeCode: z.ZodOptional<z.ZodString>;
|
|
12
|
-
entityRef: z.ZodOptional<z.ZodString>;
|
|
13
|
-
}, "strict", z.ZodTypeAny, {
|
|
14
|
-
entityTypeCode?: string | undefined;
|
|
15
|
-
entityRef?: string | undefined;
|
|
16
|
-
entityId?: string | undefined;
|
|
17
|
-
}, {
|
|
18
|
-
entityTypeCode?: string | undefined;
|
|
19
|
-
entityRef?: string | undefined;
|
|
20
|
-
entityId?: string | undefined;
|
|
21
|
-
}>, {
|
|
22
|
-
entityTypeCode?: string | undefined;
|
|
23
|
-
entityRef?: string | undefined;
|
|
24
|
-
entityId?: string | undefined;
|
|
25
|
-
}, {
|
|
26
|
-
entityTypeCode?: string | undefined;
|
|
27
|
-
entityRef?: string | undefined;
|
|
28
|
-
entityId?: string | undefined;
|
|
29
|
-
}>;
|
|
30
|
-
params: z.ZodRecord<z.ZodString, z.ZodType<JSONValue, z.ZodTypeDef, JSONValue>>;
|
|
31
|
-
parameterPaths: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
|
|
32
|
-
}, "strict", z.ZodTypeAny, {
|
|
33
|
-
params: Record<string, JSONValue>;
|
|
34
|
-
resourceId: string;
|
|
35
|
-
target: {
|
|
36
|
-
entityTypeCode?: string | undefined;
|
|
37
|
-
entityRef?: string | undefined;
|
|
38
|
-
entityId?: string | undefined;
|
|
39
|
-
};
|
|
40
|
-
actionTypeCode: string;
|
|
41
|
-
parameterPaths: Record<string, string[]>;
|
|
42
|
-
}, {
|
|
43
|
-
params: Record<string, JSONValue>;
|
|
44
|
-
resourceId: string;
|
|
45
|
-
target: {
|
|
46
|
-
entityTypeCode?: string | undefined;
|
|
47
|
-
entityRef?: string | undefined;
|
|
48
|
-
entityId?: string | undefined;
|
|
49
|
-
};
|
|
50
|
-
actionTypeCode: string;
|
|
51
|
-
parameterPaths?: Record<string, string[]> | undefined;
|
|
52
|
-
}>;
|
|
53
5
|
export declare class DataXpertAnalyticsClient {
|
|
54
6
|
private readonly runtime;
|
|
55
7
|
constructor(runtime: RuntimeCapabilityRegistry);
|
|
56
8
|
discover(request: JSONValue, context: DataXpertClientContext): Promise<AnalyticsDiscoveryResult>;
|
|
57
9
|
private request;
|
|
58
10
|
execute(binding: AnalyticsBinding, parameters: Record<string, AnalyticsValue>, context: DataXpertClientContext): Promise<AnalyticsProviderResult>;
|
|
11
|
+
private query;
|
|
59
12
|
}
|
|
60
13
|
/** Typed paths replace whole parameter values, never splice text into SQL/MDX/OData. */
|
|
61
14
|
export declare function bindParameters(params: Record<string, JSONValue>, paths: Record<string, string[]>, parameters: Record<string, AnalyticsValue>): Record<string, JSONValue>;
|
package/dist/lib/client.js
CHANGED
|
@@ -17,19 +17,18 @@ exports.bindParameters = bindParameters;
|
|
|
17
17
|
const common_1 = require("@nestjs/common");
|
|
18
18
|
const v3_1 = require("zod/v3");
|
|
19
19
|
const discovery_1 = require("./discovery");
|
|
20
|
+
const diagnostic_1 = require("./diagnostic");
|
|
21
|
+
const payload_1 = require("./payload");
|
|
22
|
+
var payload_2 = require("./payload");
|
|
23
|
+
Object.defineProperty(exports, "dataXpertPayloadSchema", { enumerable: true, get: function () { return payload_2.dataXpertPayloadSchema; } });
|
|
24
|
+
const import_targets_1 = require("./import-targets");
|
|
25
|
+
const result_schema_1 = require("./result-schema");
|
|
20
26
|
const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
|
|
21
27
|
const scalar = v3_1.z.union([v3_1.z.string(), v3_1.z.number().finite(), v3_1.z.boolean(), v3_1.z.null()]);
|
|
22
28
|
const json = v3_1.z.lazy(() => v3_1.z.union([scalar, v3_1.z.array(json), v3_1.z.record(json)]));
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
target: v3_1.z.object({
|
|
27
|
-
entityId: v3_1.z.string().trim().min(1).max(256).optional(),
|
|
28
|
-
entityTypeCode: v3_1.z.string().trim().min(1).max(256).optional(),
|
|
29
|
-
entityRef: v3_1.z.string().trim().min(1).max(512).optional()
|
|
30
|
-
}).strict().refine(target => Boolean(target.entityId || target.entityRef), 'Target reference is required'),
|
|
31
|
-
params: v3_1.z.record(json),
|
|
32
|
-
parameterPaths: v3_1.z.record(v3_1.z.array(v3_1.z.string().min(1)).min(1).max(8)).default({})
|
|
29
|
+
const querySchemaRequestSchema = v3_1.z.object({
|
|
30
|
+
kind: v3_1.z.literal('query-schema'), datasetId: v3_1.z.string().min(1).max(512).optional(),
|
|
31
|
+
payload: payload_1.dataXpertPayloadSchema, parameters: v3_1.z.record(scalar).default({})
|
|
33
32
|
}).strict();
|
|
34
33
|
/** Trusted DataSource configuration; never merge it with payloads, controls or discovery requests. */
|
|
35
34
|
const optionsSchema = v3_1.z.object({
|
|
@@ -38,7 +37,7 @@ const optionsSchema = v3_1.z.object({
|
|
|
38
37
|
}).strict();
|
|
39
38
|
const responseSchema = v3_1.z.object({
|
|
40
39
|
effect: v3_1.z.object({ version: v3_1.z.literal(2), kind: v3_1.z.enum(['rows', 'series', 'entity']),
|
|
41
|
-
columns: v3_1.z.array(
|
|
40
|
+
columns: v3_1.z.array(result_schema_1.dataXpertResultColumnSchema),
|
|
42
41
|
rows: v3_1.z.array(v3_1.z.record(scalar)),
|
|
43
42
|
summary: v3_1.z.object({ rowCount: v3_1.z.number().int().nonnegative(), returnedRowCount: v3_1.z.number().int().nonnegative(), truncated: v3_1.z.boolean() }) }),
|
|
44
43
|
fetchedAt: v3_1.z.string().datetime(), executionId: v3_1.z.string(), auditRef: v3_1.z.string(), fromCache: v3_1.z.literal(false)
|
|
@@ -48,6 +47,19 @@ let DataXpertAnalyticsClient = class DataXpertAnalyticsClient {
|
|
|
48
47
|
this.runtime = runtime;
|
|
49
48
|
}
|
|
50
49
|
async discover(request, context) {
|
|
50
|
+
const importing = import_targets_1.importTargetRequest.safeParse(request);
|
|
51
|
+
if (importing.success)
|
|
52
|
+
return (0, import_targets_1.discoverImportTargets)(importing.data, browse => this.discover(browse, context));
|
|
53
|
+
if (request && typeof request === 'object' && !Array.isArray(request) && request.kind === 'query-schema') {
|
|
54
|
+
const parsed = querySchemaRequestSchema.parse(request);
|
|
55
|
+
const datasetId = (0, result_schema_1.createDataXpertDatasetId)(parsed.payload, parsed.datasetId);
|
|
56
|
+
const result = await this.query(datasetId, parsed.payload, parsed.parameters, context);
|
|
57
|
+
return plugin_sdk_1.analyticsDiscoveryResultSchema.parse({
|
|
58
|
+
items: [{ id: datasetId, name: datasetId, kind: 'dataset',
|
|
59
|
+
metadata: { output: (0, result_schema_1.createDataXpertOutput)(datasetId, result.effect.columns) } }],
|
|
60
|
+
queryContract: discovery_1.dataXpertQueryContract
|
|
61
|
+
});
|
|
62
|
+
}
|
|
51
63
|
const empty = request && typeof request === 'object' && !Array.isArray(request) && Object.keys(request).length === 0;
|
|
52
64
|
const parsed = discovery_1.dataXpertDiscoveryRequestSchema.parse(empty ? { kind: 'resources' } : request);
|
|
53
65
|
const response = await this.request('discover', context, { request: parsed });
|
|
@@ -69,25 +81,28 @@ let DataXpertAnalyticsClient = class DataXpertAnalyticsClient {
|
|
|
69
81
|
body: JSON.stringify({ assistant: { code: options.assistantCode, xpertId: options.assistantXpertId, workspaceId: options.workspaceId }, ...body })
|
|
70
82
|
});
|
|
71
83
|
if (!response.ok) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
throw new Error(`DATAX_QUERY_HTTP_${response.status}`);
|
|
84
|
+
const error = await (0, diagnostic_1.readAnalyticsHttpError)(response, context.signal, context.deadlineAt);
|
|
85
|
+
context.signal.throwIfAborted();
|
|
86
|
+
throw error ?? new Error(`DATAX_QUERY_HTTP_${response.status}`);
|
|
75
87
|
}
|
|
76
88
|
return response;
|
|
77
89
|
}
|
|
78
90
|
async execute(binding, parameters, context) {
|
|
79
|
-
const payload =
|
|
80
|
-
const
|
|
81
|
-
binding: { id: binding.id, resourceId: payload.resourceId, actionTypeCode: payload.actionTypeCode, target: payload.target,
|
|
82
|
-
params: bindParameters(payload.params, payload.parameterPaths, parameters) }, maxRows: context.maxRows
|
|
83
|
-
});
|
|
84
|
-
const result = responseSchema.parse(await boundedJson(response));
|
|
91
|
+
const payload = payload_1.dataXpertPayloadSchema.parse(binding.payload);
|
|
92
|
+
const result = await this.query(binding.id, payload, parameters, context);
|
|
85
93
|
return { columns: result.effect.columns, rows: result.effect.rows,
|
|
86
94
|
completeness: { returnedRows: result.effect.summary.returnedRowCount, totalRows: result.effect.summary.rowCount,
|
|
87
95
|
totalAccuracy: result.effect.summary.truncated ? 'unknown' : 'exact', scope: result.effect.summary.truncated ? 'page' : 'full_query_result',
|
|
88
96
|
truncated: result.effect.summary.truncated }, sourceFetchedAt: result.fetchedAt,
|
|
89
97
|
upstreamExecutionId: result.executionId, auditRef: result.auditRef };
|
|
90
98
|
}
|
|
99
|
+
async query(id, payload, parameters, context) {
|
|
100
|
+
const response = await this.request('query', context, {
|
|
101
|
+
binding: { id, resourceId: payload.resourceId, actionTypeCode: payload.actionTypeCode, target: payload.target,
|
|
102
|
+
params: bindParameters(payload.params, payload.parameterPaths, parameters) }, maxRows: context.maxRows
|
|
103
|
+
});
|
|
104
|
+
return responseSchema.parse(await boundedJson(response));
|
|
105
|
+
}
|
|
91
106
|
};
|
|
92
107
|
exports.DataXpertAnalyticsClient = DataXpertAnalyticsClient;
|
|
93
108
|
exports.DataXpertAnalyticsClient = DataXpertAnalyticsClient = __decorate([
|
|
@@ -99,21 +114,21 @@ exports.DataXpertAnalyticsClient = DataXpertAnalyticsClient = __decorate([
|
|
|
99
114
|
function bindParameters(params, paths, parameters) {
|
|
100
115
|
const result = structuredClone(params);
|
|
101
116
|
if (Object.keys(parameters).some((name) => !Object.hasOwn(paths, name)) || Object.keys(paths).some((name) => !Object.hasOwn(parameters, name)))
|
|
102
|
-
throw
|
|
117
|
+
throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_MAPPING_INVALID');
|
|
103
118
|
if (new Set(Object.values(paths).map((path) => JSON.stringify(path))).size !== Object.keys(paths).length)
|
|
104
|
-
throw
|
|
119
|
+
throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_MAPPING_INVALID');
|
|
105
120
|
for (const [name, path] of Object.entries(paths)) {
|
|
106
121
|
let current = result;
|
|
107
122
|
for (const [index, key] of path.entries()) {
|
|
108
123
|
if (['__proto__', 'prototype', 'constructor'].includes(key) || !current || typeof current !== 'object' || !Object.hasOwn(current, key))
|
|
109
|
-
throw
|
|
124
|
+
throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_PATH_INVALID');
|
|
110
125
|
if (Array.isArray(current)) {
|
|
111
126
|
if (!/^(0|[1-9]\d*)$/.test(key) || Number(key) >= current.length)
|
|
112
|
-
throw
|
|
127
|
+
throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_PATH_INVALID');
|
|
113
128
|
const position = Number(key);
|
|
114
129
|
if (index === path.length - 1) {
|
|
115
130
|
if (current[position] !== null && typeof current[position] === 'object')
|
|
116
|
-
throw
|
|
131
|
+
throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_PATH_NOT_SCALAR');
|
|
117
132
|
current[position] = parameters[name];
|
|
118
133
|
}
|
|
119
134
|
else
|
|
@@ -121,7 +136,7 @@ function bindParameters(params, paths, parameters) {
|
|
|
121
136
|
}
|
|
122
137
|
else if (index === path.length - 1) {
|
|
123
138
|
if (current[key] !== null && typeof current[key] === 'object')
|
|
124
|
-
throw
|
|
139
|
+
throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_PATH_NOT_SCALAR');
|
|
125
140
|
current[key] = parameters[name];
|
|
126
141
|
}
|
|
127
142
|
else
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AnalyticsProviderError } from '@xpert-ai/plugin-sdk';
|
|
2
|
+
declare const parameterMessages: {
|
|
3
|
+
DATAX_PARAMETER_MAPPING_INVALID: string;
|
|
4
|
+
DATAX_PARAMETER_PATH_INVALID: string;
|
|
5
|
+
DATAX_PARAMETER_PATH_NOT_SCALAR: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function dataXpertParameterError(code: keyof typeof parameterMessages): AnalyticsProviderError;
|
|
8
|
+
/** Error responses get a smaller byte/time budget than results. Never retain or echo an unknown body. */
|
|
9
|
+
export declare function readAnalyticsHttpError(response: Response, signal: AbortSignal, deadlineAt?: number): Promise<AnalyticsProviderError | undefined>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dataXpertParameterError = dataXpertParameterError;
|
|
4
|
+
exports.readAnalyticsHttpError = readAnalyticsHttpError;
|
|
5
|
+
const contracts_1 = require("@xpert-ai/contracts");
|
|
6
|
+
const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
|
|
7
|
+
const parameterMessages = {
|
|
8
|
+
DATAX_PARAMETER_MAPPING_INVALID: 'Parameter names must match parameterPaths exactly, with a distinct target path for each parameter.',
|
|
9
|
+
DATAX_PARAMETER_PATH_INVALID: 'parameterPaths are relative to params. Omit the leading params segment and target an existing scalar, for example ["slicers","1","value"].',
|
|
10
|
+
DATAX_PARAMETER_PATH_NOT_SCALAR: 'A parameter path must target a scalar value, not an entire object or array. Select the existing filter value field.'
|
|
11
|
+
};
|
|
12
|
+
function dataXpertParameterError(code) {
|
|
13
|
+
return new plugin_sdk_1.AnalyticsProviderError({ schema: 'xpert.analytics.diagnostic', version: 1,
|
|
14
|
+
code, stage: 'validation', retryable: false, message: parameterMessages[code] });
|
|
15
|
+
}
|
|
16
|
+
/** Error responses get a smaller byte/time budget than results. Never retain or echo an unknown body. */
|
|
17
|
+
async function readAnalyticsHttpError(response, signal, deadlineAt) {
|
|
18
|
+
if (!/^application\/json(?:\s*;|$)/i.test(response.headers.get('content-type') ?? '') || !response.body) {
|
|
19
|
+
await response.body?.cancel().catch(() => undefined);
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const reader = response.body.getReader();
|
|
23
|
+
const budget = Math.min(1000, Math.max(0, (deadlineAt ?? Infinity) - Date.now()));
|
|
24
|
+
let timer;
|
|
25
|
+
let abort = () => undefined;
|
|
26
|
+
const cancelled = new Promise(resolve => {
|
|
27
|
+
abort = () => resolve(undefined);
|
|
28
|
+
timer = setTimeout(abort, budget);
|
|
29
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
30
|
+
});
|
|
31
|
+
const read = async () => {
|
|
32
|
+
const chunks = [];
|
|
33
|
+
let size = 0;
|
|
34
|
+
for (;;) {
|
|
35
|
+
const chunk = await reader.read();
|
|
36
|
+
if (chunk.done)
|
|
37
|
+
break;
|
|
38
|
+
size += chunk.value.byteLength;
|
|
39
|
+
if (size > 16_384)
|
|
40
|
+
return undefined;
|
|
41
|
+
chunks.push(chunk.value);
|
|
42
|
+
}
|
|
43
|
+
const diagnostic = (0, contracts_1.parseAnalyticsDiagnostic)(JSON.parse(Buffer.concat(chunks).toString('utf8')));
|
|
44
|
+
return diagnostic && new plugin_sdk_1.AnalyticsProviderError(diagnostic);
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
signal.throwIfAborted();
|
|
48
|
+
return await Promise.race([read(), cancelled]);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
signal.removeEventListener('abort', abort);
|
|
56
|
+
await reader.cancel().catch(() => undefined);
|
|
57
|
+
}
|
|
58
|
+
}
|
package/dist/lib/discovery.js
CHANGED
|
@@ -22,6 +22,7 @@ exports.dataXpertDiscoveryRequestSchema = v3_1.z.discriminatedUnion('kind', [
|
|
|
22
22
|
exports.dataXpertQueryContract = {
|
|
23
23
|
operation: 'action', contractVersion: 1,
|
|
24
24
|
payloadSchema: {
|
|
25
|
+
'x-xpert-import': { version: 1, request: { kind: 'import-targets' } },
|
|
25
26
|
type: 'object', additionalProperties: false,
|
|
26
27
|
required: ['resourceId', 'actionTypeCode', 'target', 'params', 'parameterPaths'],
|
|
27
28
|
properties: {
|
|
@@ -47,6 +48,7 @@ exports.dataXpertQueryContract = {
|
|
|
47
48
|
'Entity metadata.definition contains adapterId, contractVersion, metadata and instructions. Follow that adapter-specific definition and the discovered Action inputSchema/inputHint. Preserve canonical targets, resource IDs and exact field names. Do not infer adapter types from names or apply one adapter\'s query, filter or time rules to another.',
|
|
48
49
|
'Use only published metadata and authorized previews to establish business metrics, time axes, supported filters and complete aggregation. Missing metadata does not authorize guessing; obtain sufficient evidence before generating a time filter.',
|
|
49
50
|
'Use parameterPaths relative to params to replace an existing scalar value, for example {region:["filters","0","value"]}. Paths must be unique. Never interpolate values into query text. Controls supply scalar values; arrays remain static unless the native Action explicitly supports another declared scalar mapping. Null handling and filter operators are defined by the Action schema and adapter instructions.',
|
|
50
|
-
'
|
|
51
|
+
'Before authoring a binding, use dashboard_discover with the same source/providerId and requestJson encoding {kind:"query-schema",payload:<native Action envelope>,parameters:<mapped scalar values, or {}>}. This runs that bounded read-only Action under the current viewer policy; respect the native Action limits and filters. It returns items[0].metadata.output without business rows. Omit datasetId to let the provider generate a valid output key. Copy this output into the dashboard_preview binding, preserving datasetId, field IDs, sourceField, logicalType, role and nullable. Decimal/int64 remain exact strings; do not infer types from sample cells or names. If declarations are missing, obtain source metadata instead of guessing.',
|
|
52
|
+
'The discovered output has empty grain because column roles alone do not prove uniqueness. Declare grain only when supported by query semantics. Always run dashboard_preview with the same payload and parameter values to validate the binding and obtain a preview receipt before saving; schema discovery is not a preview receipt. Re-discover after changing the query projection. A partial page is not a complete business aggregate.'
|
|
51
53
|
].join(' ')
|
|
52
54
|
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { z } from 'zod/v3';
|
|
2
|
+
import type { AnalyticsDiscoveryResult, JSONValue } from '@xpert-ai/contracts';
|
|
3
|
+
/** Provider-owned reference migration. Native Action arguments and output contracts remain unchanged. */
|
|
4
|
+
export declare const importTargetRequest: z.ZodObject<{
|
|
5
|
+
kind: z.ZodLiteral<"import-targets">;
|
|
6
|
+
payload: z.ZodObject<{
|
|
7
|
+
resourceId: z.ZodString;
|
|
8
|
+
actionTypeCode: z.ZodString;
|
|
9
|
+
target: z.ZodEffects<z.ZodObject<{
|
|
10
|
+
entityId: z.ZodOptional<z.ZodString>;
|
|
11
|
+
entityTypeCode: z.ZodOptional<z.ZodString>;
|
|
12
|
+
entityRef: z.ZodOptional<z.ZodString>;
|
|
13
|
+
}, "strict", z.ZodTypeAny, {
|
|
14
|
+
entityTypeCode?: string | undefined;
|
|
15
|
+
entityRef?: string | undefined;
|
|
16
|
+
entityId?: string | undefined;
|
|
17
|
+
}, {
|
|
18
|
+
entityTypeCode?: string | undefined;
|
|
19
|
+
entityRef?: string | undefined;
|
|
20
|
+
entityId?: string | undefined;
|
|
21
|
+
}>, {
|
|
22
|
+
entityTypeCode?: string | undefined;
|
|
23
|
+
entityRef?: string | undefined;
|
|
24
|
+
entityId?: string | undefined;
|
|
25
|
+
}, {
|
|
26
|
+
entityTypeCode?: string | undefined;
|
|
27
|
+
entityRef?: string | undefined;
|
|
28
|
+
entityId?: string | undefined;
|
|
29
|
+
}>;
|
|
30
|
+
params: z.ZodRecord<z.ZodString, z.ZodType<JSONValue, z.ZodTypeDef, JSONValue>>;
|
|
31
|
+
parameterPaths: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
|
|
32
|
+
}, "strict", z.ZodTypeAny, {
|
|
33
|
+
params: Record<string, JSONValue>;
|
|
34
|
+
resourceId: string;
|
|
35
|
+
target: {
|
|
36
|
+
entityTypeCode?: string | undefined;
|
|
37
|
+
entityRef?: string | undefined;
|
|
38
|
+
entityId?: string | undefined;
|
|
39
|
+
};
|
|
40
|
+
actionTypeCode: string;
|
|
41
|
+
parameterPaths: Record<string, string[]>;
|
|
42
|
+
}, {
|
|
43
|
+
params: Record<string, JSONValue>;
|
|
44
|
+
resourceId: string;
|
|
45
|
+
target: {
|
|
46
|
+
entityTypeCode?: string | undefined;
|
|
47
|
+
entityRef?: string | undefined;
|
|
48
|
+
entityId?: string | undefined;
|
|
49
|
+
};
|
|
50
|
+
actionTypeCode: string;
|
|
51
|
+
parameterPaths?: Record<string, string[]> | undefined;
|
|
52
|
+
}>;
|
|
53
|
+
browse: z.ZodDefault<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
|
|
54
|
+
kind: z.ZodLiteral<"resources">;
|
|
55
|
+
after: z.ZodOptional<z.ZodString>;
|
|
56
|
+
}, "strict", z.ZodTypeAny, {
|
|
57
|
+
kind: "resources";
|
|
58
|
+
after?: string | undefined;
|
|
59
|
+
}, {
|
|
60
|
+
kind: "resources";
|
|
61
|
+
after?: string | undefined;
|
|
62
|
+
}>, z.ZodObject<{
|
|
63
|
+
kind: z.ZodLiteral<"entities">;
|
|
64
|
+
resourceId: z.ZodString;
|
|
65
|
+
entityTypeCode: z.ZodOptional<z.ZodString>;
|
|
66
|
+
query: z.ZodOptional<z.ZodString>;
|
|
67
|
+
}, "strict", z.ZodTypeAny, {
|
|
68
|
+
kind: "entities";
|
|
69
|
+
resourceId: string;
|
|
70
|
+
entityTypeCode?: string | undefined;
|
|
71
|
+
query?: string | undefined;
|
|
72
|
+
}, {
|
|
73
|
+
kind: "entities";
|
|
74
|
+
resourceId: string;
|
|
75
|
+
entityTypeCode?: string | undefined;
|
|
76
|
+
query?: string | undefined;
|
|
77
|
+
}>, z.ZodObject<{
|
|
78
|
+
kind: z.ZodLiteral<"actions">;
|
|
79
|
+
resourceId: z.ZodString;
|
|
80
|
+
target: z.ZodObject<{
|
|
81
|
+
entityTypeCode: z.ZodString;
|
|
82
|
+
entityRef: z.ZodString;
|
|
83
|
+
}, "strict", z.ZodTypeAny, {
|
|
84
|
+
entityTypeCode: string;
|
|
85
|
+
entityRef: string;
|
|
86
|
+
}, {
|
|
87
|
+
entityTypeCode: string;
|
|
88
|
+
entityRef: string;
|
|
89
|
+
}>;
|
|
90
|
+
}, "strict", z.ZodTypeAny, {
|
|
91
|
+
kind: "actions";
|
|
92
|
+
resourceId: string;
|
|
93
|
+
target: {
|
|
94
|
+
entityTypeCode: string;
|
|
95
|
+
entityRef: string;
|
|
96
|
+
};
|
|
97
|
+
}, {
|
|
98
|
+
kind: "actions";
|
|
99
|
+
resourceId: string;
|
|
100
|
+
target: {
|
|
101
|
+
entityTypeCode: string;
|
|
102
|
+
entityRef: string;
|
|
103
|
+
};
|
|
104
|
+
}>]>>;
|
|
105
|
+
}, "strict", z.ZodTypeAny, {
|
|
106
|
+
kind: "import-targets";
|
|
107
|
+
payload: {
|
|
108
|
+
params: Record<string, JSONValue>;
|
|
109
|
+
resourceId: string;
|
|
110
|
+
target: {
|
|
111
|
+
entityTypeCode?: string | undefined;
|
|
112
|
+
entityRef?: string | undefined;
|
|
113
|
+
entityId?: string | undefined;
|
|
114
|
+
};
|
|
115
|
+
actionTypeCode: string;
|
|
116
|
+
parameterPaths: Record<string, string[]>;
|
|
117
|
+
};
|
|
118
|
+
browse: {
|
|
119
|
+
kind: "resources";
|
|
120
|
+
after?: string | undefined;
|
|
121
|
+
} | {
|
|
122
|
+
kind: "entities";
|
|
123
|
+
resourceId: string;
|
|
124
|
+
entityTypeCode?: string | undefined;
|
|
125
|
+
query?: string | undefined;
|
|
126
|
+
} | {
|
|
127
|
+
kind: "actions";
|
|
128
|
+
resourceId: string;
|
|
129
|
+
target: {
|
|
130
|
+
entityTypeCode: string;
|
|
131
|
+
entityRef: string;
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
}, {
|
|
135
|
+
kind: "import-targets";
|
|
136
|
+
payload: {
|
|
137
|
+
params: Record<string, JSONValue>;
|
|
138
|
+
resourceId: string;
|
|
139
|
+
target: {
|
|
140
|
+
entityTypeCode?: string | undefined;
|
|
141
|
+
entityRef?: string | undefined;
|
|
142
|
+
entityId?: string | undefined;
|
|
143
|
+
};
|
|
144
|
+
actionTypeCode: string;
|
|
145
|
+
parameterPaths?: Record<string, string[]> | undefined;
|
|
146
|
+
};
|
|
147
|
+
browse?: {
|
|
148
|
+
kind: "resources";
|
|
149
|
+
after?: string | undefined;
|
|
150
|
+
} | {
|
|
151
|
+
kind: "entities";
|
|
152
|
+
resourceId: string;
|
|
153
|
+
entityTypeCode?: string | undefined;
|
|
154
|
+
query?: string | undefined;
|
|
155
|
+
} | {
|
|
156
|
+
kind: "actions";
|
|
157
|
+
resourceId: string;
|
|
158
|
+
target: {
|
|
159
|
+
entityTypeCode: string;
|
|
160
|
+
entityRef: string;
|
|
161
|
+
};
|
|
162
|
+
} | undefined;
|
|
163
|
+
}>;
|
|
164
|
+
export declare function discoverImportTargets(input: z.infer<typeof importTargetRequest>, discover: (request: JSONValue) => Promise<AnalyticsDiscoveryResult>): Promise<AnalyticsDiscoveryResult>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.importTargetRequest = void 0;
|
|
4
|
+
exports.discoverImportTargets = discoverImportTargets;
|
|
5
|
+
const v3_1 = require("zod/v3");
|
|
6
|
+
const payload_1 = require("./payload");
|
|
7
|
+
const discovery_1 = require("./discovery");
|
|
8
|
+
/** Provider-owned reference migration. Native Action arguments and output contracts remain unchanged. */
|
|
9
|
+
exports.importTargetRequest = v3_1.z.object({
|
|
10
|
+
kind: v3_1.z.literal('import-targets'), payload: payload_1.dataXpertPayloadSchema,
|
|
11
|
+
browse: discovery_1.dataXpertDiscoveryRequestSchema.default({ kind: 'resources' })
|
|
12
|
+
}).strict();
|
|
13
|
+
async function discoverImportTargets(input, discover) {
|
|
14
|
+
const result = await discover(input.browse);
|
|
15
|
+
const wrap = (browse) => ({ kind: 'import-targets', payload: { ...input.payload, params: {}, parameterPaths: {} }, browse });
|
|
16
|
+
return { ...result,
|
|
17
|
+
items: result.items.flatMap(item => {
|
|
18
|
+
const next = discovery_1.dataXpertDiscoveryRequestSchema.safeParse(item.inspectRequest);
|
|
19
|
+
if (next.success && next.data.kind === 'actions') {
|
|
20
|
+
const target = next.data.target;
|
|
21
|
+
if (input.payload.target.entityTypeCode && target.entityTypeCode !== input.payload.target.entityTypeCode)
|
|
22
|
+
return [];
|
|
23
|
+
return [{ id: item.id, name: item.name, kind: 'dataset', description: item.description,
|
|
24
|
+
metadata: { schema: 'xpert.analytics.import-target', version: 1,
|
|
25
|
+
payloadPatch: { resourceId: next.data.resourceId, target } } }];
|
|
26
|
+
}
|
|
27
|
+
return item.inspectRequest ? [{ ...item, inspectRequest: wrap(item.inspectRequest) }] : [];
|
|
28
|
+
}),
|
|
29
|
+
...(result.nextRequest ? { nextRequest: wrap(result.nextRequest) } : {}) };
|
|
30
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod/v3';
|
|
2
|
+
import type { JSONValue } from '@xpert-ai/contracts';
|
|
3
|
+
/** Native Action arguments stay plugin-owned; the host only interprets Dataset/Binding. */
|
|
4
|
+
export declare const dataXpertPayloadSchema: z.ZodObject<{
|
|
5
|
+
resourceId: z.ZodString;
|
|
6
|
+
actionTypeCode: z.ZodString;
|
|
7
|
+
target: z.ZodEffects<z.ZodObject<{
|
|
8
|
+
entityId: z.ZodOptional<z.ZodString>;
|
|
9
|
+
entityTypeCode: z.ZodOptional<z.ZodString>;
|
|
10
|
+
entityRef: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, "strict", z.ZodTypeAny, {
|
|
12
|
+
entityTypeCode?: string | undefined;
|
|
13
|
+
entityRef?: string | undefined;
|
|
14
|
+
entityId?: string | undefined;
|
|
15
|
+
}, {
|
|
16
|
+
entityTypeCode?: string | undefined;
|
|
17
|
+
entityRef?: string | undefined;
|
|
18
|
+
entityId?: string | undefined;
|
|
19
|
+
}>, {
|
|
20
|
+
entityTypeCode?: string | undefined;
|
|
21
|
+
entityRef?: string | undefined;
|
|
22
|
+
entityId?: string | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
entityTypeCode?: string | undefined;
|
|
25
|
+
entityRef?: string | undefined;
|
|
26
|
+
entityId?: string | undefined;
|
|
27
|
+
}>;
|
|
28
|
+
params: z.ZodRecord<z.ZodString, z.ZodType<JSONValue, z.ZodTypeDef, JSONValue>>;
|
|
29
|
+
parameterPaths: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
|
|
30
|
+
}, "strict", z.ZodTypeAny, {
|
|
31
|
+
params: Record<string, JSONValue>;
|
|
32
|
+
resourceId: string;
|
|
33
|
+
target: {
|
|
34
|
+
entityTypeCode?: string | undefined;
|
|
35
|
+
entityRef?: string | undefined;
|
|
36
|
+
entityId?: string | undefined;
|
|
37
|
+
};
|
|
38
|
+
actionTypeCode: string;
|
|
39
|
+
parameterPaths: Record<string, string[]>;
|
|
40
|
+
}, {
|
|
41
|
+
params: Record<string, JSONValue>;
|
|
42
|
+
resourceId: string;
|
|
43
|
+
target: {
|
|
44
|
+
entityTypeCode?: string | undefined;
|
|
45
|
+
entityRef?: string | undefined;
|
|
46
|
+
entityId?: string | undefined;
|
|
47
|
+
};
|
|
48
|
+
actionTypeCode: string;
|
|
49
|
+
parameterPaths?: Record<string, string[]> | undefined;
|
|
50
|
+
}>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dataXpertPayloadSchema = void 0;
|
|
4
|
+
const v3_1 = require("zod/v3");
|
|
5
|
+
const scalar = v3_1.z.union([v3_1.z.string(), v3_1.z.number().finite(), v3_1.z.boolean(), v3_1.z.null()]);
|
|
6
|
+
const json = v3_1.z.lazy(() => v3_1.z.union([scalar, v3_1.z.array(json), v3_1.z.record(json)]));
|
|
7
|
+
/** Native Action arguments stay plugin-owned; the host only interprets Dataset/Binding. */
|
|
8
|
+
exports.dataXpertPayloadSchema = v3_1.z.object({
|
|
9
|
+
resourceId: v3_1.z.string().min(1).max(160), actionTypeCode: v3_1.z.string().min(1).max(160),
|
|
10
|
+
target: v3_1.z.object({
|
|
11
|
+
entityId: v3_1.z.string().trim().min(1).max(256).optional(),
|
|
12
|
+
entityTypeCode: v3_1.z.string().trim().min(1).max(256).optional(),
|
|
13
|
+
entityRef: v3_1.z.string().trim().min(1).max(512).optional()
|
|
14
|
+
}).strict().refine(target => Boolean(target.entityId || target.entityRef), 'Target reference is required'),
|
|
15
|
+
params: v3_1.z.record(json),
|
|
16
|
+
parameterPaths: v3_1.z.record(v3_1.z.array(v3_1.z.string().min(1)).min(1).max(8)).default({})
|
|
17
|
+
}).strict();
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { z } from 'zod/v3';
|
|
2
|
+
import type { JSONValue } from '@xpert-ai/contracts';
|
|
3
|
+
export declare const dataXpertResultColumnSchema: z.ZodObject<{
|
|
4
|
+
label: z.ZodOptional<z.ZodString>;
|
|
5
|
+
logicalType: z.ZodOptional<z.ZodEnum<["string", "boolean", "int64", "float64", "decimal", "date", "timestamp"]>>;
|
|
6
|
+
nullable: z.ZodOptional<z.ZodBoolean>;
|
|
7
|
+
role: z.ZodOptional<z.ZodEnum<["dimension", "measure", "time", "identifier"]>>;
|
|
8
|
+
semanticRef: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
9
|
+
unit: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
10
|
+
} & {
|
|
11
|
+
key: z.ZodString;
|
|
12
|
+
}, "strip", z.ZodTypeAny, {
|
|
13
|
+
key: string;
|
|
14
|
+
label?: string | undefined;
|
|
15
|
+
logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp" | undefined;
|
|
16
|
+
nullable?: boolean | undefined;
|
|
17
|
+
role?: "dimension" | "measure" | "time" | "identifier" | undefined;
|
|
18
|
+
semanticRef?: string | undefined;
|
|
19
|
+
unit?: string | undefined;
|
|
20
|
+
}, {
|
|
21
|
+
key: string;
|
|
22
|
+
label?: string | undefined;
|
|
23
|
+
logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp" | undefined;
|
|
24
|
+
nullable?: boolean | undefined;
|
|
25
|
+
role?: "dimension" | "measure" | "time" | "identifier" | undefined;
|
|
26
|
+
semanticRef?: string | undefined;
|
|
27
|
+
unit?: string | undefined;
|
|
28
|
+
}>;
|
|
29
|
+
export declare const dataXpertOutputSchema: z.ZodObject<{
|
|
30
|
+
datasetId: z.ZodString;
|
|
31
|
+
fields: z.ZodArray<z.ZodObject<{
|
|
32
|
+
id: z.ZodString;
|
|
33
|
+
sourceField: z.ZodString;
|
|
34
|
+
label: z.ZodString;
|
|
35
|
+
logicalType: z.ZodEnum<["string", "boolean", "int64", "float64", "decimal", "date", "timestamp"]>;
|
|
36
|
+
nullable: z.ZodBoolean;
|
|
37
|
+
role: z.ZodEnum<["dimension", "measure", "time", "identifier"]>;
|
|
38
|
+
semanticRef: z.ZodOptional<z.ZodString>;
|
|
39
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
40
|
+
}, "strict", z.ZodTypeAny, {
|
|
41
|
+
id?: string;
|
|
42
|
+
sourceField?: string;
|
|
43
|
+
label?: string;
|
|
44
|
+
logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp";
|
|
45
|
+
nullable?: boolean;
|
|
46
|
+
role?: "dimension" | "measure" | "time" | "identifier";
|
|
47
|
+
semanticRef?: string;
|
|
48
|
+
unit?: string;
|
|
49
|
+
}, {
|
|
50
|
+
id?: string;
|
|
51
|
+
sourceField?: string;
|
|
52
|
+
label?: string;
|
|
53
|
+
logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp";
|
|
54
|
+
nullable?: boolean;
|
|
55
|
+
role?: "dimension" | "measure" | "time" | "identifier";
|
|
56
|
+
semanticRef?: string;
|
|
57
|
+
unit?: string;
|
|
58
|
+
}>, "many">;
|
|
59
|
+
grain: z.ZodArray<z.ZodString, "many">;
|
|
60
|
+
}, "strict", z.ZodTypeAny, {
|
|
61
|
+
datasetId?: string;
|
|
62
|
+
fields?: {
|
|
63
|
+
id?: string;
|
|
64
|
+
sourceField?: string;
|
|
65
|
+
label?: string;
|
|
66
|
+
logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp";
|
|
67
|
+
nullable?: boolean;
|
|
68
|
+
role?: "dimension" | "measure" | "time" | "identifier";
|
|
69
|
+
semanticRef?: string;
|
|
70
|
+
unit?: string;
|
|
71
|
+
}[];
|
|
72
|
+
grain?: string[];
|
|
73
|
+
}, {
|
|
74
|
+
datasetId?: string;
|
|
75
|
+
fields?: {
|
|
76
|
+
id?: string;
|
|
77
|
+
sourceField?: string;
|
|
78
|
+
label?: string;
|
|
79
|
+
logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp";
|
|
80
|
+
nullable?: boolean;
|
|
81
|
+
role?: "dimension" | "measure" | "time" | "identifier";
|
|
82
|
+
semanticRef?: string;
|
|
83
|
+
unit?: string;
|
|
84
|
+
}[];
|
|
85
|
+
grain?: string[];
|
|
86
|
+
}>;
|
|
87
|
+
export declare function createDataXpertDatasetId(payload: JSONValue, requestedId?: string): string;
|
|
88
|
+
export declare function createDataXpertOutput(datasetId: string, columns: z.output<typeof dataXpertResultColumnSchema>[]): {
|
|
89
|
+
datasetId?: string;
|
|
90
|
+
fields?: {
|
|
91
|
+
id?: string;
|
|
92
|
+
sourceField?: string;
|
|
93
|
+
label?: string;
|
|
94
|
+
logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp";
|
|
95
|
+
nullable?: boolean;
|
|
96
|
+
role?: "dimension" | "measure" | "time" | "identifier";
|
|
97
|
+
semanticRef?: string;
|
|
98
|
+
unit?: string;
|
|
99
|
+
}[];
|
|
100
|
+
grain?: string[];
|
|
101
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dataXpertOutputSchema = exports.dataXpertResultColumnSchema = void 0;
|
|
4
|
+
exports.createDataXpertDatasetId = createDataXpertDatasetId;
|
|
5
|
+
exports.createDataXpertOutput = createDataXpertOutput;
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
const v3_1 = require("zod/v3");
|
|
8
|
+
const analytics_1 = require("@xpert-ai/plugin-sdk/analytics");
|
|
9
|
+
// Optional on legacy responses; schema authoring requires explicit declarations.
|
|
10
|
+
exports.dataXpertResultColumnSchema = analytics_1.analyticsFieldSchema
|
|
11
|
+
.omit({ id: true, sourceField: true })
|
|
12
|
+
.partial()
|
|
13
|
+
.extend({ key: v3_1.z.string() })
|
|
14
|
+
.strip();
|
|
15
|
+
exports.dataXpertOutputSchema = analytics_1.analyticsBindingObjectSchema.shape.output;
|
|
16
|
+
function createDataXpertDatasetId(payload, requestedId) {
|
|
17
|
+
const parsed = exports.dataXpertOutputSchema.shape.datasetId.safeParse(requestedId);
|
|
18
|
+
if (parsed.success)
|
|
19
|
+
return parsed.data;
|
|
20
|
+
// Native references are not output keys. Include the query projection to separate bindings.
|
|
21
|
+
return `dataset_${(0, node_crypto_1.createHash)('sha256').update(JSON.stringify(payload)).digest('hex')}`;
|
|
22
|
+
}
|
|
23
|
+
function createDataXpertOutput(datasetId, columns) {
|
|
24
|
+
if (columns.length === 0 || columns.length > 80)
|
|
25
|
+
throw new Error('DATAX_RESULT_SCHEMA_UNAVAILABLE: expected 1 to 80 declared columns');
|
|
26
|
+
if (new Set(columns.map(column => column.key)).size !== columns.length)
|
|
27
|
+
throw new Error('DATAX_RESULT_SCHEMA_AMBIGUOUS');
|
|
28
|
+
const fields = columns.map(column => {
|
|
29
|
+
if (column.logicalType === undefined || column.role === undefined || column.nullable === undefined)
|
|
30
|
+
throw new Error('DATAX_RESULT_SCHEMA_UNAVAILABLE');
|
|
31
|
+
// Repeated semantic selectors cannot identify a column; its unique native key can.
|
|
32
|
+
const semanticRef = columns.filter(item => item.semanticRef === column.semanticRef).length === 1
|
|
33
|
+
? column.semanticRef : undefined;
|
|
34
|
+
return {
|
|
35
|
+
id: `field_${(0, node_crypto_1.createHash)('sha256').update(column.key).digest('hex')}`,
|
|
36
|
+
sourceField: column.key, label: column.label ?? column.key.slice(0, 160),
|
|
37
|
+
logicalType: column.logicalType, role: column.role, nullable: column.nullable,
|
|
38
|
+
...(semanticRef === undefined ? {} : { semanticRef }),
|
|
39
|
+
...(column.unit === undefined ? {} : { unit: column.unit })
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
// Column roles do not establish row uniqueness or business aggregation grain.
|
|
43
|
+
return exports.dataXpertOutputSchema.parse({ datasetId, fields, grain: [] });
|
|
44
|
+
}
|
package/docs/verification.mdx
CHANGED
|
@@ -5,9 +5,10 @@ description: "Separate transport-contract tests from real SSO and end-to-end das
|
|
|
5
5
|
|
|
6
6
|
## Compatibility
|
|
7
7
|
|
|
8
|
-
Development dependencies pin
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Development dependencies pin `@xpert-ai/contracts` and `@xpert-ai/plugin-sdk` to 3.32.0.
|
|
9
|
+
Both peer dependencies require `^3.32.0`. The plugin validates upstream error responses with
|
|
10
|
+
`parseAnalyticsDiagnostic` and returns accepted diagnostics through `AnalyticsProviderError`.
|
|
11
|
+
The host must provide the Analytics and Actor Token runtime capabilities.
|
|
11
12
|
|
|
12
13
|
Data-xpert must expose `POST /agent-workbench/analytics/discover` and
|
|
13
14
|
`POST /agent-workbench/analytics/query` under the configured API base. Discovery returns
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xpert-ai/plugin-analytics-data-xpert",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Governed data-xpert ontology analytics provider",
|
|
5
5
|
"main": "./index.cjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -27,16 +27,16 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@xpert-ai/plugin-sdk": "^3.
|
|
31
|
-
"@xpert-ai/contracts": "^3.
|
|
30
|
+
"@xpert-ai/plugin-sdk": "^3.32.0",
|
|
31
|
+
"@xpert-ai/contracts": "^3.32.0",
|
|
32
32
|
"@nestjs/common": "^11.1.6",
|
|
33
33
|
"@nestjs/core": "^11.1.6",
|
|
34
34
|
"zod": "3.25.67",
|
|
35
35
|
"@nestjs/cqrs": "^11.0.3"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@xpert-ai/plugin-sdk": "3.
|
|
39
|
-
"@xpert-ai/contracts": "3.
|
|
38
|
+
"@xpert-ai/plugin-sdk": "3.32.0",
|
|
39
|
+
"@xpert-ai/contracts": "3.32.0",
|
|
40
40
|
"@nestjs/common": "^11.1.6",
|
|
41
41
|
"@nestjs/core": "^11.1.6",
|
|
42
42
|
"zod": "3.25.67",
|