@dbx-tools/appkit-mastra 0.3.35 → 0.3.36
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/package.json +9 -9
- package/src/chart.ts +10 -2
- package/test/chart.test.ts +63 -0
package/package.json
CHANGED
|
@@ -28,21 +28,21 @@
|
|
|
28
28
|
"express": "^5.1.0",
|
|
29
29
|
"pg": "^8.22.0",
|
|
30
30
|
"zod": "4.3.6",
|
|
31
|
-
"@dbx-tools/appkit": "0.3.
|
|
32
|
-
"@dbx-tools/core": "0.3.
|
|
33
|
-
"@dbx-tools/genie": "0.3.
|
|
34
|
-
"@dbx-tools/model": "0.3.
|
|
35
|
-
"@dbx-tools/shared-core": "0.3.
|
|
36
|
-
"@dbx-tools/shared-
|
|
37
|
-
"@dbx-tools/shared-
|
|
38
|
-
"@dbx-tools/shared-
|
|
31
|
+
"@dbx-tools/appkit": "0.3.36",
|
|
32
|
+
"@dbx-tools/core": "0.3.36",
|
|
33
|
+
"@dbx-tools/genie": "0.3.36",
|
|
34
|
+
"@dbx-tools/model": "0.3.36",
|
|
35
|
+
"@dbx-tools/shared-core": "0.3.36",
|
|
36
|
+
"@dbx-tools/shared-genie": "0.3.36",
|
|
37
|
+
"@dbx-tools/shared-mastra": "0.3.36",
|
|
38
|
+
"@dbx-tools/shared-model": "0.3.36"
|
|
39
39
|
},
|
|
40
40
|
"main": "index.ts",
|
|
41
41
|
"license": "UNLICENSED",
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
|
-
"version": "0.3.
|
|
45
|
+
"version": "0.3.36",
|
|
46
46
|
"types": "index.ts",
|
|
47
47
|
"type": "module",
|
|
48
48
|
"exports": {
|
package/src/chart.ts
CHANGED
|
@@ -122,7 +122,15 @@ const chartDataPointSchema = z
|
|
|
122
122
|
z.union([
|
|
123
123
|
z.number(),
|
|
124
124
|
z.null(),
|
|
125
|
-
|
|
125
|
+
// `[x, y]` scatter point. Modelled as a length-constrained
|
|
126
|
+
// homogeneous array rather than `z.tuple`: Zod emits a tuple as
|
|
127
|
+
// JSON Schema 2020-12 `prefixItems`, and Databricks' Gemini
|
|
128
|
+
// endpoints reject a `response_json_schema` containing it
|
|
129
|
+
// ("must be a boolean or an object", since `items` is absent).
|
|
130
|
+
// `z.array(z.number()).length(2)` emits `items` + `minItems` /
|
|
131
|
+
// `maxItems`, which every provider understands, and validates
|
|
132
|
+
// the same two-number shape.
|
|
133
|
+
z.array(z.number()).length(2),
|
|
126
134
|
z.object({ name: z.string(), value: z.number() }),
|
|
127
135
|
]),
|
|
128
136
|
)
|
|
@@ -138,7 +146,7 @@ const chartDataPointSchema = z
|
|
|
138
146
|
* and keeps animation / tooltip / styling defaults consistent
|
|
139
147
|
* across charts.
|
|
140
148
|
*/
|
|
141
|
-
const chartPlanSchema = z.object({
|
|
149
|
+
export const chartPlanSchema = z.object({
|
|
142
150
|
chartType: wire.ChartTypeSchema,
|
|
143
151
|
title: z
|
|
144
152
|
.string()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { chartPlanSchema } from "../src/chart";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Walk every nested schema node, yielding each object so a test can
|
|
9
|
+
* assert on keywords wherever they appear in the tree.
|
|
10
|
+
*/
|
|
11
|
+
function* nodes(schema: unknown): Generator<Record<string, unknown>> {
|
|
12
|
+
if (Array.isArray(schema)) {
|
|
13
|
+
for (const item of schema) yield* nodes(item);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (typeof schema !== "object" || schema === null) return;
|
|
17
|
+
const record = schema as Record<string, unknown>;
|
|
18
|
+
yield record;
|
|
19
|
+
for (const value of Object.values(record)) yield* nodes(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("chart plan JSON schema", () => {
|
|
23
|
+
// Databricks' Gemini serving endpoints validate `response_json_schema`
|
|
24
|
+
// and reject JSON Schema 2020-12 `prefixItems` with "schema at
|
|
25
|
+
// properties.series.items.properties.data.items.anyOf.2.items must be a
|
|
26
|
+
// boolean or an object". Zod emits `prefixItems` for `z.tuple`, so the
|
|
27
|
+
// scatter `[x, y]` point must stay a length-constrained number array.
|
|
28
|
+
it("emits no prefixItems, so Gemini accepts it as a response schema", () => {
|
|
29
|
+
const jsonSchema = z.toJSONSchema(chartPlanSchema);
|
|
30
|
+
const offenders = [...nodes(jsonSchema)].filter((node) => "prefixItems" in node);
|
|
31
|
+
assert.deepEqual(offenders, []);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("types the scatter point as a two-number array", () => {
|
|
35
|
+
const jsonSchema = z.toJSONSchema(chartPlanSchema) as Record<string, any>;
|
|
36
|
+
const variants = jsonSchema.properties.series.items.properties.data.items.anyOf;
|
|
37
|
+
const arrayVariant = variants.find((v: Record<string, unknown>) => v.type === "array");
|
|
38
|
+
assert.deepEqual(arrayVariant, {
|
|
39
|
+
type: "array",
|
|
40
|
+
items: { type: "number" },
|
|
41
|
+
minItems: 2,
|
|
42
|
+
maxItems: 2,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("chart data point coercion", () => {
|
|
48
|
+
const dataPoint = chartPlanSchema.shape.series.element.shape.data.element;
|
|
49
|
+
|
|
50
|
+
it("keeps the shapes a SQL row set produces", () => {
|
|
51
|
+
assert.equal(dataPoint.parse(12.5), 12.5);
|
|
52
|
+
assert.equal(dataPoint.parse("12.5"), 12.5);
|
|
53
|
+
assert.equal(dataPoint.parse(null), null);
|
|
54
|
+
assert.deepEqual(dataPoint.parse([1, 2]), [1, 2]);
|
|
55
|
+
assert.deepEqual(dataPoint.parse({ name: "ATL", value: 3 }), { name: "ATL", value: 3 });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("degrades unusable values to null instead of failing the plan", () => {
|
|
59
|
+
assert.equal(dataPoint.parse("not a number"), null);
|
|
60
|
+
assert.equal(dataPoint.parse(Number.POSITIVE_INFINITY), null);
|
|
61
|
+
assert.equal(dataPoint.parse({ nope: true }), null);
|
|
62
|
+
});
|
|
63
|
+
});
|