@svadmin/surface 0.6.2 → 0.7.1
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 +19 -0
- package/compatibility.json +1 -1
- package/dist/agent.d.ts +23 -0
- package/dist/agent.js +78 -0
- package/dist/builtin-schemas.d.ts +5 -139
- package/dist/builtin-schemas.js +7 -43
- package/dist/catalog.d.ts +4 -122
- package/dist/catalog.js +4 -3
- package/dist/components/BarChartWidget.svelte +2 -1
- package/dist/components/LineChartWidget.svelte +2 -1
- package/dist/components/MetricWidget.svelte +2 -1
- package/dist/components/ResourceTableWidget.svelte +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +1 -6
- package/dist/validation.js +0 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,6 +78,25 @@ import {
|
|
|
78
78
|
|
|
79
79
|
When rendered inside `AdminApp`, the renderer resolves the configured provider for each resource. A trusted host may instead pass `dataProvider`, which is still narrowed to `getList` and `getOne`.
|
|
80
80
|
|
|
81
|
+
## AI proposals
|
|
82
|
+
|
|
83
|
+
The DOM-free root entry also exposes an opt-in Agent protocol. `buildSurfaceAgentPrompt()` constrains a model to return a proposal envelope and includes the host's widget/resource/field allowlists. `parseSurfaceAgentProposal()` parses and validates the complete `SurfaceSpec` against the same catalog and policy without querying a provider:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import {
|
|
87
|
+
buildSurfaceAgentPrompt,
|
|
88
|
+
parseSurfaceAgentProposal,
|
|
89
|
+
} from '@svadmin/surface';
|
|
90
|
+
|
|
91
|
+
const prompt = buildSurfaceAgentPrompt('Generate an inventory dashboard', catalog, policy);
|
|
92
|
+
const proposal = parseSurfaceAgentProposal(modelText, catalog, policy);
|
|
93
|
+
if (proposal.ok) {
|
|
94
|
+
// Preview proposal.value.spec, then require explicit user approval before rendering.
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The adapter is deliberately proposal-only. Persistence, revision history, audit records, and the final apply decision belong to the host application. It never executes generated code or mutation actions.
|
|
99
|
+
|
|
81
100
|
## Built-in catalog
|
|
82
101
|
|
|
83
102
|
| Type | Binding | Purpose |
|
package/compatibility.json
CHANGED
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SurfaceCatalog, SurfacePolicy, SurfaceSpec, SurfaceValidationIssue } from './types.js';
|
|
2
|
+
/** Wire version for model-produced, human-reviewable Surface proposals. */
|
|
3
|
+
export declare const SURFACE_AGENT_SCHEMA_VERSION: "surface-agent/v1";
|
|
4
|
+
export interface SurfaceAgentProposal {
|
|
5
|
+
readonly schemaVersion: typeof SURFACE_AGENT_SCHEMA_VERSION;
|
|
6
|
+
readonly action: 'propose';
|
|
7
|
+
readonly summary?: string;
|
|
8
|
+
readonly spec: SurfaceSpec;
|
|
9
|
+
}
|
|
10
|
+
export type SurfaceAgentValidationResult = {
|
|
11
|
+
readonly ok: true;
|
|
12
|
+
readonly value: SurfaceAgentProposal;
|
|
13
|
+
} | {
|
|
14
|
+
readonly ok: false;
|
|
15
|
+
readonly issues: readonly SurfaceValidationIssue[];
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Parse and fully validate an AI-generated proposal before a host previews it.
|
|
19
|
+
* This function has no side effects and never queries a provider.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseSurfaceAgentProposal(input: unknown, catalog: SurfaceCatalog, policy: SurfacePolicy): SurfaceAgentValidationResult;
|
|
22
|
+
/** Build a model instruction that keeps generation inside the Surface contract. */
|
|
23
|
+
export declare function buildSurfaceAgentPrompt(request: string, catalog: SurfaceCatalog, policy: SurfacePolicy): string;
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Type } from '@sinclair/typebox';
|
|
2
|
+
import { TypeCompiler } from '@sinclair/typebox/compiler';
|
|
3
|
+
import { jsonPointer } from './json.js';
|
|
4
|
+
import { validateSurfaceSpec } from './validation.js';
|
|
5
|
+
/** Wire version for model-produced, human-reviewable Surface proposals. */
|
|
6
|
+
export const SURFACE_AGENT_SCHEMA_VERSION = 'surface-agent/v1';
|
|
7
|
+
const proposalSchema = Type.Object({
|
|
8
|
+
schemaVersion: Type.Literal(SURFACE_AGENT_SCHEMA_VERSION),
|
|
9
|
+
action: Type.Literal('propose'),
|
|
10
|
+
summary: Type.Optional(Type.String({ minLength: 1, maxLength: 240 })),
|
|
11
|
+
spec: Type.Unknown(),
|
|
12
|
+
}, { additionalProperties: false });
|
|
13
|
+
const compiledProposalSchema = TypeCompiler.Compile(proposalSchema);
|
|
14
|
+
function parseCandidate(value) {
|
|
15
|
+
if (typeof value !== 'string')
|
|
16
|
+
return value;
|
|
17
|
+
const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(value);
|
|
18
|
+
const source = fenced?.[1] ?? value.trim();
|
|
19
|
+
return JSON.parse(source);
|
|
20
|
+
}
|
|
21
|
+
function proposalIssue(message) {
|
|
22
|
+
return {
|
|
23
|
+
code: 'invalid_json',
|
|
24
|
+
path: '/',
|
|
25
|
+
message,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function proposalSchemaIssues(errors) {
|
|
29
|
+
return [...errors].map((issue) => ({
|
|
30
|
+
code: 'invalid_json',
|
|
31
|
+
path: issue.path || jsonPointer([]),
|
|
32
|
+
message: issue.message,
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Parse and fully validate an AI-generated proposal before a host previews it.
|
|
37
|
+
* This function has no side effects and never queries a provider.
|
|
38
|
+
*/
|
|
39
|
+
export function parseSurfaceAgentProposal(input, catalog, policy) {
|
|
40
|
+
let candidate;
|
|
41
|
+
try {
|
|
42
|
+
candidate = parseCandidate(input);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return { ok: false, issues: [proposalIssue('Agent proposal must be valid JSON')] };
|
|
46
|
+
}
|
|
47
|
+
if (!compiledProposalSchema.Check(candidate)) {
|
|
48
|
+
return { ok: false, issues: proposalSchemaIssues(compiledProposalSchema.Errors(candidate)) };
|
|
49
|
+
}
|
|
50
|
+
const parsed = candidate;
|
|
51
|
+
const surface = validateSurfaceSpec(parsed.spec, catalog, policy);
|
|
52
|
+
if (!surface.ok)
|
|
53
|
+
return surface;
|
|
54
|
+
return {
|
|
55
|
+
ok: true,
|
|
56
|
+
value: {
|
|
57
|
+
schemaVersion: SURFACE_AGENT_SCHEMA_VERSION,
|
|
58
|
+
action: 'propose',
|
|
59
|
+
...(parsed.summary === undefined ? {} : { summary: parsed.summary }),
|
|
60
|
+
spec: surface.value,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Build a model instruction that keeps generation inside the Surface contract. */
|
|
65
|
+
export function buildSurfaceAgentPrompt(request, catalog, policy) {
|
|
66
|
+
const widgetTypes = catalog.widgets.map((widget) => widget.type).join(', ') || '(none)';
|
|
67
|
+
const resources = Object.entries(policy.resources).map(([resource, resourcePolicy]) => {
|
|
68
|
+
const permissions = [
|
|
69
|
+
`read=${resourcePolicy.readFields.join(',') || '(none)'}`,
|
|
70
|
+
`filter=${resourcePolicy.filterFields?.join(',') || '(none)'}`,
|
|
71
|
+
`sort=${resourcePolicy.sortFields?.join(',') || '(none)'}`,
|
|
72
|
+
`getOne=${resourcePolicy.allowGetOne === true}`,
|
|
73
|
+
`maxPageSize=${resourcePolicy.maxPageSize ?? 'default'}`,
|
|
74
|
+
];
|
|
75
|
+
return `${resource}(${permissions.join(';')})`;
|
|
76
|
+
}).join(' | ') || '(none)';
|
|
77
|
+
return `${request}\n\n[svadmin surface agent protocol]\nReturn only a human-reviewable fenced JSON proposal. Never generate or execute Svelte, HTML, CSS, JavaScript, SQL, URLs, event handlers, or mutations. The envelope must be {"schemaVersion":"${SURFACE_AGENT_SCHEMA_VERSION}","action":"propose","summary":"...","spec":{...}}. The spec must use schemaVersion "surface/v1" and catalogVersion "${catalog.version}". Allowed widget types: ${widgetTypes}. Resource policy: ${resources}. Use only catalog widgets and policy-authorized resources and fields. If the request cannot be represented safely, explain the limitation without inventing fields or capabilities.`;
|
|
78
|
+
}
|
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
import { type Static
|
|
2
|
-
export declare function createParsedSchema<T extends TSchema>(schema: T): T & {
|
|
3
|
-
parse: (value: unknown) => Static<T>;
|
|
4
|
-
safeParse: (value: unknown) => {
|
|
5
|
-
success: true;
|
|
6
|
-
data: Static<T>;
|
|
7
|
-
} | {
|
|
8
|
-
success: false;
|
|
9
|
-
error: {
|
|
10
|
-
issues: Array<{
|
|
11
|
-
path: string[];
|
|
12
|
-
message: string;
|
|
13
|
-
}>;
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
Check: (value: unknown) => boolean;
|
|
17
|
-
};
|
|
1
|
+
import { type Static } from "@sinclair/typebox";
|
|
18
2
|
export declare const metricPropsSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TObject<{
|
|
19
3
|
label: import("@sinclair/typebox").TString;
|
|
20
4
|
format: import("@sinclair/typebox").TLiteral<"currency">;
|
|
@@ -25,42 +9,7 @@ export declare const metricPropsSchema: import("@sinclair/typebox").TUnion<[impo
|
|
|
25
9
|
format: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"number">, import("@sinclair/typebox").TLiteral<"percent">]>;
|
|
26
10
|
currency: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNever>;
|
|
27
11
|
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
28
|
-
}>]
|
|
29
|
-
parse: (value: unknown) => {
|
|
30
|
-
description?: string | undefined;
|
|
31
|
-
currency: string;
|
|
32
|
-
label: string;
|
|
33
|
-
format: "currency";
|
|
34
|
-
} | {
|
|
35
|
-
description?: string | undefined;
|
|
36
|
-
currency?: undefined;
|
|
37
|
-
label: string;
|
|
38
|
-
format: "number" | "percent";
|
|
39
|
-
};
|
|
40
|
-
safeParse: (value: unknown) => {
|
|
41
|
-
success: false;
|
|
42
|
-
error: {
|
|
43
|
-
issues: Array<{
|
|
44
|
-
path: string[];
|
|
45
|
-
message: string;
|
|
46
|
-
}>;
|
|
47
|
-
};
|
|
48
|
-
} | {
|
|
49
|
-
success: true;
|
|
50
|
-
data: {
|
|
51
|
-
description?: string | undefined;
|
|
52
|
-
currency: string;
|
|
53
|
-
label: string;
|
|
54
|
-
format: "currency";
|
|
55
|
-
} | {
|
|
56
|
-
description?: string | undefined;
|
|
57
|
-
currency?: undefined;
|
|
58
|
-
label: string;
|
|
59
|
-
format: "number" | "percent";
|
|
60
|
-
};
|
|
61
|
-
};
|
|
62
|
-
Check: (value: unknown) => boolean;
|
|
63
|
-
};
|
|
12
|
+
}>]>;
|
|
64
13
|
export declare const resourceTablePropsSchema: import("@sinclair/typebox").TObject<{
|
|
65
14
|
title: import("@sinclair/typebox").TString;
|
|
66
15
|
emptyLabel: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
@@ -69,103 +18,20 @@ export declare const resourceTablePropsSchema: import("@sinclair/typebox").TObje
|
|
|
69
18
|
label: import("@sinclair/typebox").TString;
|
|
70
19
|
format: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"text">, import("@sinclair/typebox").TLiteral<"number">, import("@sinclair/typebox").TLiteral<"date">, import("@sinclair/typebox").TLiteral<"boolean">]>>;
|
|
71
20
|
}>>;
|
|
72
|
-
}
|
|
73
|
-
parse: (value: unknown) => {
|
|
74
|
-
emptyLabel?: string | undefined;
|
|
75
|
-
title: string;
|
|
76
|
-
columns: {
|
|
77
|
-
format?: "number" | "boolean" | "text" | "date" | undefined;
|
|
78
|
-
field: string;
|
|
79
|
-
label: string;
|
|
80
|
-
}[];
|
|
81
|
-
};
|
|
82
|
-
safeParse: (value: unknown) => {
|
|
83
|
-
success: false;
|
|
84
|
-
error: {
|
|
85
|
-
issues: Array<{
|
|
86
|
-
path: string[];
|
|
87
|
-
message: string;
|
|
88
|
-
}>;
|
|
89
|
-
};
|
|
90
|
-
} | {
|
|
91
|
-
success: true;
|
|
92
|
-
data: {
|
|
93
|
-
emptyLabel?: string | undefined;
|
|
94
|
-
title: string;
|
|
95
|
-
columns: {
|
|
96
|
-
format?: "number" | "boolean" | "text" | "date" | undefined;
|
|
97
|
-
field: string;
|
|
98
|
-
label: string;
|
|
99
|
-
}[];
|
|
100
|
-
};
|
|
101
|
-
};
|
|
102
|
-
Check: (value: unknown) => boolean;
|
|
103
|
-
};
|
|
21
|
+
}>;
|
|
104
22
|
export declare const barChartPropsSchema: import("@sinclair/typebox").TObject<{
|
|
105
23
|
title: import("@sinclair/typebox").TString;
|
|
106
24
|
labelField: import("@sinclair/typebox").TString;
|
|
107
25
|
valueField: import("@sinclair/typebox").TString;
|
|
108
26
|
showValues: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
109
|
-
}
|
|
110
|
-
parse: (value: unknown) => {
|
|
111
|
-
showValues?: boolean | undefined;
|
|
112
|
-
title: string;
|
|
113
|
-
labelField: string;
|
|
114
|
-
valueField: string;
|
|
115
|
-
};
|
|
116
|
-
safeParse: (value: unknown) => {
|
|
117
|
-
success: false;
|
|
118
|
-
error: {
|
|
119
|
-
issues: Array<{
|
|
120
|
-
path: string[];
|
|
121
|
-
message: string;
|
|
122
|
-
}>;
|
|
123
|
-
};
|
|
124
|
-
} | {
|
|
125
|
-
success: true;
|
|
126
|
-
data: {
|
|
127
|
-
showValues?: boolean | undefined;
|
|
128
|
-
title: string;
|
|
129
|
-
labelField: string;
|
|
130
|
-
valueField: string;
|
|
131
|
-
};
|
|
132
|
-
};
|
|
133
|
-
Check: (value: unknown) => boolean;
|
|
134
|
-
};
|
|
27
|
+
}>;
|
|
135
28
|
export declare const lineChartPropsSchema: import("@sinclair/typebox").TObject<{
|
|
136
29
|
title: import("@sinclair/typebox").TString;
|
|
137
30
|
labelField: import("@sinclair/typebox").TString;
|
|
138
31
|
valueField: import("@sinclair/typebox").TString;
|
|
139
32
|
showDots: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
140
33
|
fill: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
141
|
-
}
|
|
142
|
-
parse: (value: unknown) => {
|
|
143
|
-
fill?: boolean | undefined;
|
|
144
|
-
showDots?: boolean | undefined;
|
|
145
|
-
title: string;
|
|
146
|
-
labelField: string;
|
|
147
|
-
valueField: string;
|
|
148
|
-
};
|
|
149
|
-
safeParse: (value: unknown) => {
|
|
150
|
-
success: false;
|
|
151
|
-
error: {
|
|
152
|
-
issues: Array<{
|
|
153
|
-
path: string[];
|
|
154
|
-
message: string;
|
|
155
|
-
}>;
|
|
156
|
-
};
|
|
157
|
-
} | {
|
|
158
|
-
success: true;
|
|
159
|
-
data: {
|
|
160
|
-
fill?: boolean | undefined;
|
|
161
|
-
showDots?: boolean | undefined;
|
|
162
|
-
title: string;
|
|
163
|
-
labelField: string;
|
|
164
|
-
valueField: string;
|
|
165
|
-
};
|
|
166
|
-
};
|
|
167
|
-
Check: (value: unknown) => boolean;
|
|
168
|
-
};
|
|
34
|
+
}>;
|
|
169
35
|
export type MetricProps = Static<typeof metricPropsSchema>;
|
|
170
36
|
export type ResourceTableProps = Static<typeof resourceTablePropsSchema>;
|
|
171
37
|
export type BarChartProps = Static<typeof barChartPropsSchema>;
|
package/dist/builtin-schemas.js
CHANGED
|
@@ -1,40 +1,4 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
|
3
|
-
export function createParsedSchema(schema) {
|
|
4
|
-
const compiled = TypeCompiler.Compile(schema);
|
|
5
|
-
const parse = (value) => {
|
|
6
|
-
if (compiled.Check(value))
|
|
7
|
-
return value;
|
|
8
|
-
const errors = [...compiled.Errors(value)];
|
|
9
|
-
const message = errors.map((e) => (e.path ? e.path + ": " : "") + e.message).join(", ") || "Validation error";
|
|
10
|
-
const err = new Error(message);
|
|
11
|
-
err.issues = errors.map((e) => ({
|
|
12
|
-
path: e.path ? (e.path.startsWith("/") ? e.path.slice(1) : e.path).split("/") : [],
|
|
13
|
-
message: e.message,
|
|
14
|
-
}));
|
|
15
|
-
throw err;
|
|
16
|
-
};
|
|
17
|
-
const safeParse = (value) => {
|
|
18
|
-
if (compiled.Check(value)) {
|
|
19
|
-
return { success: true, data: value };
|
|
20
|
-
}
|
|
21
|
-
const errors = [...compiled.Errors(value)];
|
|
22
|
-
return {
|
|
23
|
-
success: false,
|
|
24
|
-
error: {
|
|
25
|
-
issues: errors.map((e) => ({
|
|
26
|
-
path: e.path ? (e.path.startsWith("/") ? e.path.slice(1) : e.path).split("/") : [],
|
|
27
|
-
message: e.message,
|
|
28
|
-
})),
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
return Object.assign(schema, {
|
|
33
|
-
parse,
|
|
34
|
-
safeParse,
|
|
35
|
-
Check: (val) => compiled.Check(val),
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
2
|
const catalogFieldSchema = Type.String({
|
|
39
3
|
minLength: 1,
|
|
40
4
|
maxLength: 64,
|
|
@@ -52,8 +16,8 @@ const otherMetric = Type.Object({
|
|
|
52
16
|
currency: Type.Optional(Type.Never()),
|
|
53
17
|
description: Type.Optional(Type.String({ minLength: 1, maxLength: 160 })),
|
|
54
18
|
}, { additionalProperties: false });
|
|
55
|
-
export const metricPropsSchema =
|
|
56
|
-
export const resourceTablePropsSchema =
|
|
19
|
+
export const metricPropsSchema = Type.Union([currencyMetric, otherMetric]);
|
|
20
|
+
export const resourceTablePropsSchema = Type.Object({
|
|
57
21
|
title: Type.String({ minLength: 1, maxLength: 80 }),
|
|
58
22
|
emptyLabel: Type.Optional(Type.String({ minLength: 1, maxLength: 80 })),
|
|
59
23
|
columns: Type.Array(Type.Object({
|
|
@@ -66,17 +30,17 @@ export const resourceTablePropsSchema = createParsedSchema(Type.Object({
|
|
|
66
30
|
Type.Literal("boolean"),
|
|
67
31
|
])),
|
|
68
32
|
}, { additionalProperties: false }), { minItems: 1, maxItems: 8 }),
|
|
69
|
-
}, { additionalProperties: false })
|
|
70
|
-
export const barChartPropsSchema =
|
|
33
|
+
}, { additionalProperties: false });
|
|
34
|
+
export const barChartPropsSchema = Type.Object({
|
|
71
35
|
title: Type.String({ minLength: 1, maxLength: 80 }),
|
|
72
36
|
labelField: catalogFieldSchema,
|
|
73
37
|
valueField: catalogFieldSchema,
|
|
74
38
|
showValues: Type.Optional(Type.Boolean()),
|
|
75
|
-
}, { additionalProperties: false })
|
|
76
|
-
export const lineChartPropsSchema =
|
|
39
|
+
}, { additionalProperties: false });
|
|
40
|
+
export const lineChartPropsSchema = Type.Object({
|
|
77
41
|
title: Type.String({ minLength: 1, maxLength: 80 }),
|
|
78
42
|
labelField: catalogFieldSchema,
|
|
79
43
|
valueField: catalogFieldSchema,
|
|
80
44
|
showDots: Type.Optional(Type.Boolean()),
|
|
81
45
|
fill: Type.Optional(Type.Boolean()),
|
|
82
|
-
}, { additionalProperties: false })
|
|
46
|
+
}, { additionalProperties: false });
|
package/dist/catalog.d.ts
CHANGED
|
@@ -31,42 +31,7 @@ export declare const defaultSurfaceCatalog: {
|
|
|
31
31
|
format: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"number">, import("@sinclair/typebox").TLiteral<"percent">]>;
|
|
32
32
|
currency: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNever>;
|
|
33
33
|
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
34
|
-
}>]
|
|
35
|
-
parse: (value: unknown) => {
|
|
36
|
-
description?: string | undefined;
|
|
37
|
-
currency: string;
|
|
38
|
-
label: string;
|
|
39
|
-
format: "currency";
|
|
40
|
-
} | {
|
|
41
|
-
description?: string | undefined;
|
|
42
|
-
currency?: undefined;
|
|
43
|
-
label: string;
|
|
44
|
-
format: "number" | "percent";
|
|
45
|
-
};
|
|
46
|
-
safeParse: (value: unknown) => {
|
|
47
|
-
success: false;
|
|
48
|
-
error: {
|
|
49
|
-
issues: Array<{
|
|
50
|
-
path: string[];
|
|
51
|
-
message: string;
|
|
52
|
-
}>;
|
|
53
|
-
};
|
|
54
|
-
} | {
|
|
55
|
-
success: true;
|
|
56
|
-
data: {
|
|
57
|
-
description?: string | undefined;
|
|
58
|
-
currency: string;
|
|
59
|
-
label: string;
|
|
60
|
-
format: "currency";
|
|
61
|
-
} | {
|
|
62
|
-
description?: string | undefined;
|
|
63
|
-
currency?: undefined;
|
|
64
|
-
label: string;
|
|
65
|
-
format: "number" | "percent";
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
Check: (value: unknown) => boolean;
|
|
69
|
-
};
|
|
34
|
+
}>]>;
|
|
70
35
|
readonly component: Component<SurfaceWidgetRendererProps, {}, "">;
|
|
71
36
|
}, {
|
|
72
37
|
readonly type: "resource-table";
|
|
@@ -79,38 +44,7 @@ export declare const defaultSurfaceCatalog: {
|
|
|
79
44
|
label: import("@sinclair/typebox").TString;
|
|
80
45
|
format: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"text">, import("@sinclair/typebox").TLiteral<"number">, import("@sinclair/typebox").TLiteral<"date">, import("@sinclair/typebox").TLiteral<"boolean">]>>;
|
|
81
46
|
}>>;
|
|
82
|
-
}
|
|
83
|
-
parse: (value: unknown) => {
|
|
84
|
-
emptyLabel?: string | undefined;
|
|
85
|
-
title: string;
|
|
86
|
-
columns: {
|
|
87
|
-
format?: "number" | "boolean" | "text" | "date" | undefined;
|
|
88
|
-
field: string;
|
|
89
|
-
label: string;
|
|
90
|
-
}[];
|
|
91
|
-
};
|
|
92
|
-
safeParse: (value: unknown) => {
|
|
93
|
-
success: false;
|
|
94
|
-
error: {
|
|
95
|
-
issues: Array<{
|
|
96
|
-
path: string[];
|
|
97
|
-
message: string;
|
|
98
|
-
}>;
|
|
99
|
-
};
|
|
100
|
-
} | {
|
|
101
|
-
success: true;
|
|
102
|
-
data: {
|
|
103
|
-
emptyLabel?: string | undefined;
|
|
104
|
-
title: string;
|
|
105
|
-
columns: {
|
|
106
|
-
format?: "number" | "boolean" | "text" | "date" | undefined;
|
|
107
|
-
field: string;
|
|
108
|
-
label: string;
|
|
109
|
-
}[];
|
|
110
|
-
};
|
|
111
|
-
};
|
|
112
|
-
Check: (value: unknown) => boolean;
|
|
113
|
-
};
|
|
47
|
+
}>;
|
|
114
48
|
readonly getReferencedFields: typeof tableFields;
|
|
115
49
|
readonly component: Component<SurfaceWidgetRendererProps, {}, "">;
|
|
116
50
|
}, {
|
|
@@ -121,32 +55,7 @@ export declare const defaultSurfaceCatalog: {
|
|
|
121
55
|
labelField: import("@sinclair/typebox").TString;
|
|
122
56
|
valueField: import("@sinclair/typebox").TString;
|
|
123
57
|
showValues: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
124
|
-
}
|
|
125
|
-
parse: (value: unknown) => {
|
|
126
|
-
showValues?: boolean | undefined;
|
|
127
|
-
title: string;
|
|
128
|
-
labelField: string;
|
|
129
|
-
valueField: string;
|
|
130
|
-
};
|
|
131
|
-
safeParse: (value: unknown) => {
|
|
132
|
-
success: false;
|
|
133
|
-
error: {
|
|
134
|
-
issues: Array<{
|
|
135
|
-
path: string[];
|
|
136
|
-
message: string;
|
|
137
|
-
}>;
|
|
138
|
-
};
|
|
139
|
-
} | {
|
|
140
|
-
success: true;
|
|
141
|
-
data: {
|
|
142
|
-
showValues?: boolean | undefined;
|
|
143
|
-
title: string;
|
|
144
|
-
labelField: string;
|
|
145
|
-
valueField: string;
|
|
146
|
-
};
|
|
147
|
-
};
|
|
148
|
-
Check: (value: unknown) => boolean;
|
|
149
|
-
};
|
|
58
|
+
}>;
|
|
150
59
|
readonly getReferencedFields: typeof barChartFields;
|
|
151
60
|
readonly component: Component<SurfaceWidgetRendererProps, {}, "">;
|
|
152
61
|
}, {
|
|
@@ -158,34 +67,7 @@ export declare const defaultSurfaceCatalog: {
|
|
|
158
67
|
valueField: import("@sinclair/typebox").TString;
|
|
159
68
|
showDots: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
160
69
|
fill: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
161
|
-
}
|
|
162
|
-
parse: (value: unknown) => {
|
|
163
|
-
fill?: boolean | undefined;
|
|
164
|
-
showDots?: boolean | undefined;
|
|
165
|
-
title: string;
|
|
166
|
-
labelField: string;
|
|
167
|
-
valueField: string;
|
|
168
|
-
};
|
|
169
|
-
safeParse: (value: unknown) => {
|
|
170
|
-
success: false;
|
|
171
|
-
error: {
|
|
172
|
-
issues: Array<{
|
|
173
|
-
path: string[];
|
|
174
|
-
message: string;
|
|
175
|
-
}>;
|
|
176
|
-
};
|
|
177
|
-
} | {
|
|
178
|
-
success: true;
|
|
179
|
-
data: {
|
|
180
|
-
fill?: boolean | undefined;
|
|
181
|
-
showDots?: boolean | undefined;
|
|
182
|
-
title: string;
|
|
183
|
-
labelField: string;
|
|
184
|
-
valueField: string;
|
|
185
|
-
};
|
|
186
|
-
};
|
|
187
|
-
Check: (value: unknown) => boolean;
|
|
188
|
-
};
|
|
70
|
+
}>;
|
|
189
71
|
readonly getReferencedFields: typeof lineChartFields;
|
|
190
72
|
readonly component: Component<SurfaceWidgetRendererProps, {}, "">;
|
|
191
73
|
}];
|
package/dist/catalog.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Value } from '@sinclair/typebox/value';
|
|
1
2
|
import BarChartWidget from './components/BarChartWidget.svelte';
|
|
2
3
|
import LineChartWidget from './components/LineChartWidget.svelte';
|
|
3
4
|
import MetricWidget from './components/MetricWidget.svelte';
|
|
@@ -16,14 +17,14 @@ export function defineSurfaceCatalog(catalog) {
|
|
|
16
17
|
return catalog;
|
|
17
18
|
}
|
|
18
19
|
function tableFields(props) {
|
|
19
|
-
return
|
|
20
|
+
return Value.Decode(resourceTablePropsSchema, props).columns.map((column) => column.field);
|
|
20
21
|
}
|
|
21
22
|
function barChartFields(props) {
|
|
22
|
-
const chartProps =
|
|
23
|
+
const chartProps = Value.Decode(barChartPropsSchema, props);
|
|
23
24
|
return [chartProps.labelField, chartProps.valueField];
|
|
24
25
|
}
|
|
25
26
|
function lineChartFields(props) {
|
|
26
|
-
const chartProps =
|
|
27
|
+
const chartProps = Value.Decode(lineChartPropsSchema, props);
|
|
27
28
|
return [chartProps.labelField, chartProps.valueField];
|
|
28
29
|
}
|
|
29
30
|
export const defaultSurfaceCatalog = defineSurfaceCatalog({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { Value } from '@sinclair/typebox/value';
|
|
2
3
|
import BarChart from '@svadmin/ui/components/charts/BarChart.svelte';
|
|
3
4
|
import CardContent from '@svadmin/ui/components/ui/card/card-content.svelte';
|
|
4
5
|
import CardHeader from '@svadmin/ui/components/ui/card/card-header.svelte';
|
|
@@ -10,7 +11,7 @@
|
|
|
10
11
|
|
|
11
12
|
let { props, data }: SurfaceWidgetRendererProps = $props();
|
|
12
13
|
|
|
13
|
-
const chartProps = $derived(
|
|
14
|
+
const chartProps = $derived(Value.Decode(barChartPropsSchema, props));
|
|
14
15
|
const points = $derived(data.status === 'ready'
|
|
15
16
|
? asChartPoints(data.value, chartProps.labelField, chartProps.valueField)
|
|
16
17
|
: null);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { Value } from '@sinclair/typebox/value';
|
|
2
3
|
import LineChart from '@svadmin/ui/components/charts/LineChart.svelte';
|
|
3
4
|
import CardContent from '@svadmin/ui/components/ui/card/card-content.svelte';
|
|
4
5
|
import CardHeader from '@svadmin/ui/components/ui/card/card-header.svelte';
|
|
@@ -10,7 +11,7 @@
|
|
|
10
11
|
|
|
11
12
|
let { props, data }: SurfaceWidgetRendererProps = $props();
|
|
12
13
|
|
|
13
|
-
const chartProps = $derived(
|
|
14
|
+
const chartProps = $derived(Value.Decode(lineChartPropsSchema, props));
|
|
14
15
|
const points = $derived(data.status === 'ready'
|
|
15
16
|
? asChartPoints(data.value, chartProps.labelField, chartProps.valueField)
|
|
16
17
|
: null);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { Value } from '@sinclair/typebox/value';
|
|
2
3
|
import StatsCard from '@svadmin/ui/components/StatsCard.svelte';
|
|
3
4
|
import { metricPropsSchema } from '../builtin-schemas.js';
|
|
4
5
|
import type { SurfaceWidgetRendererProps } from '../catalog.js';
|
|
5
6
|
|
|
6
7
|
let { widgetId, props, data }: SurfaceWidgetRendererProps = $props();
|
|
7
8
|
|
|
8
|
-
const metricProps = $derived(
|
|
9
|
+
const metricProps = $derived(Value.Decode(metricPropsSchema, props));
|
|
9
10
|
const formattedValue = $derived.by(() => {
|
|
10
11
|
if (data.status !== 'ready') return '—';
|
|
11
12
|
const value = data.value;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { Value } from '@sinclair/typebox/value';
|
|
2
3
|
import CardContent from '@svadmin/ui/components/ui/card/card-content.svelte';
|
|
3
4
|
import CardHeader from '@svadmin/ui/components/ui/card/card-header.svelte';
|
|
4
5
|
import CardTitle from '@svadmin/ui/components/ui/card/card-title.svelte';
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
|
|
16
17
|
let { props, data }: SurfaceWidgetRendererProps = $props();
|
|
17
18
|
|
|
18
|
-
const tableProps = $derived(
|
|
19
|
+
const tableProps = $derived(Value.Decode(resourceTablePropsSchema, props));
|
|
19
20
|
const records = $derived(data.status === 'ready' ? asRecordArray(data.value) : null);
|
|
20
21
|
</script>
|
|
21
22
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { SURFACE_LIMITS, SURFACE_SCHEMA_VERSION } from './types.js';
|
|
2
2
|
export type { JsonObject, JsonPrimitive, JsonValue, ResourceListDataSource, ResourceListSource, ResourceOneDataSource, ResourceOneSource, SurfaceBinding, SurfaceCatalog, SurfaceCatalogDataKind, SurfaceDataError, SurfaceDataProvider, SurfaceDataSource, SurfaceFilter, SurfaceGridLayout, SurfaceGridSpan, SurfacePolicy, SurfaceResourcePolicy, SurfaceSort, SurfaceSpec, SurfaceValidationCode, SurfaceValidationIssue, SurfaceValidationResult, SurfaceWidget, SurfaceWidgetDataState, SurfaceWidgetDefinition, } from './types.js';
|
|
3
3
|
export { validateSurfaceSpec } from './validation.js';
|
|
4
|
+
export { SURFACE_AGENT_SCHEMA_VERSION, buildSurfaceAgentPrompt, parseSurfaceAgentProposal, } from './agent.js';
|
|
5
|
+
export type { SurfaceAgentProposal, SurfaceAgentValidationResult, } from './agent.js';
|
package/dist/index.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -92,12 +92,7 @@ export type SurfaceCatalogDataKind = 'none' | 'scalar' | 'items';
|
|
|
92
92
|
export interface SurfaceWidgetDefinition {
|
|
93
93
|
readonly type: string;
|
|
94
94
|
readonly dataKind: SurfaceCatalogDataKind;
|
|
95
|
-
readonly propsSchema: TSchema
|
|
96
|
-
Check?: (data: unknown) => boolean;
|
|
97
|
-
safeParse?: (data: unknown) => {
|
|
98
|
-
success: boolean;
|
|
99
|
-
};
|
|
100
|
-
};
|
|
95
|
+
readonly propsSchema: TSchema;
|
|
101
96
|
/** 组件从绑定记录中读取的字段,由可信 Catalog 提供。 */
|
|
102
97
|
readonly getReferencedFields?: (props: JsonObject) => readonly string[];
|
|
103
98
|
}
|
package/dist/validation.js
CHANGED
|
@@ -234,9 +234,6 @@ function checkPropsSchema(schema, data) {
|
|
|
234
234
|
if ("Check" in schema && typeof schema.Check === "function") {
|
|
235
235
|
return schema.Check(data);
|
|
236
236
|
}
|
|
237
|
-
if ("safeParse" in schema && typeof schema.safeParse === "function") {
|
|
238
|
-
return schema.safeParse(data).success;
|
|
239
|
-
}
|
|
240
237
|
return Value.Check(schema, data);
|
|
241
238
|
}
|
|
242
239
|
function widgetIssue(widget, widgetIndex, definition, sources, policy) {
|