@77-zhike/app-sdk 0.2.0-d7b1.10
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 +251 -0
- package/package.json +71 -0
- package/src/bin.js +15 -0
- package/src/cli/api-client.js +249 -0
- package/src/cli/browser-auth.js +212 -0
- package/src/cli/credential-store.js +101 -0
- package/src/cli/development-app.js +77 -0
- package/src/cli/development-logs.js +183 -0
- package/src/cli/development-watch.js +205 -0
- package/src/cli/index.js +256 -0
- package/src/cli/local-commands.js +80 -0
- package/src/cli/logs.js +56 -0
- package/src/cli/project-init.js +259 -0
- package/src/cli/upload-bundle.js +73 -0
- package/src/client/analysis.d.ts +4 -0
- package/src/client/analysis.js +12 -0
- package/src/client/destinations.d.ts +25 -0
- package/src/client/destinations.js +70 -0
- package/src/client/external-url.js +9 -0
- package/src/client/index.d.ts +249 -0
- package/src/client/index.js +72 -0
- package/src/client/interactions.d.ts +23 -0
- package/src/client/interactions.js +62 -0
- package/src/client/points.generated.js +67 -0
- package/src/config.js +68 -0
- package/src/development-workspace.js +154 -0
- package/src/errors.js +20 -0
- package/src/forms/array-contract.d.ts +32 -0
- package/src/forms/array-contract.js +138 -0
- package/src/forms/components.generated.js +4 -0
- package/src/forms/factory.js +41 -0
- package/src/forms/field-array.d.ts +16 -0
- package/src/forms/field-array.js +52 -0
- package/src/forms/index.d.ts +118 -0
- package/src/forms/index.js +5 -0
- package/src/forms/normalize-options.js +19 -0
- package/src/forms/options.d.ts +30 -0
- package/src/forms/options.js +55 -0
- package/src/forms/record-options.d.ts +28 -0
- package/src/forms/record-options.js +92 -0
- package/src/forms/schema.d.ts +82 -0
- package/src/forms/schema.js +128 -0
- package/src/forms/state.d.ts +30 -0
- package/src/forms/state.js +82 -0
- package/src/forms/validation.d.ts +9 -0
- package/src/forms/validation.js +81 -0
- package/src/forms/values.d.ts +5 -0
- package/src/forms/values.js +38 -0
- package/src/index.d.ts +13 -0
- package/src/index.js +2 -0
- package/src/jsx-dev-runtime.d.ts +2 -0
- package/src/jsx-dev-runtime.js +1 -0
- package/src/jsx-runtime.d.ts +2 -0
- package/src/jsx-runtime.js +1 -0
- package/src/project-binding.js +84 -0
- package/src/server/index.d.ts +318 -0
- package/src/server/index.js +242 -0
- package/src/server/steps/index.d.ts +71 -0
- package/src/server/steps/index.js +15 -0
- package/src/server/triggers/index.d.ts +68 -0
- package/src/server/triggers/index.js +27 -0
- package/src/server/workflow-blocks/index.d.ts +72 -0
- package/src/server/workflow-blocks/index.js +32 -0
- package/src/toolchain/allowed-packages.json +7 -0
- package/src/toolchain/canonical-json.js +17 -0
- package/src/toolchain/dependency-policy.js +181 -0
- package/src/toolchain/index.js +15 -0
- package/src/toolchain/real-build.js +459 -0
- package/src/toolchain/source-discovery.js +919 -0
- package/src/toolchain/transport-tar.js +168 -0
- package/src/toolchain/webhook-response.js +50 -0
- package/src/toolchain/workflow-blocks/discovery.js +133 -0
- package/src/toolchain/workflow-steps/manifest.js +99 -0
- package/src/toolchain/workflow-triggers/manifest.js +47 -0
- package/src/ui/components.generated.js +5 -0
- package/src/ui/events.d.ts +10 -0
- package/src/ui/factory.js +70 -0
- package/src/ui/icons.generated.js +2 -0
- package/src/ui/index.d.ts +343 -0
- package/src/ui/index.js +4 -0
- package/src/ui/jsx.generated.d.ts +359 -0
- package/src/ui/native-dom.d.ts +49 -0
- package/src/ui/native-dom.js +44 -0
- package/src/ui/style.d.ts +5 -0
- package/src/ui/style.generated.js +2 -0
- package/src/ui/style.js +15 -0
- package/src/ui/validation.js +86 -0
package/src/ui/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// 此文件由 scripts/generate-components.mjs 生成,请修改产品能力声明。
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { createUiComponents } from "./factory.js";
|
|
4
|
+
export const { NativeDOM, Icon, Stack, SplitLayout, SplitLayoutMain, SplitLayoutAside, SplitLayoutActions, Section, Banner, Badge, LoadingState, Skeleton, Card, Grid, EmptyState, ZhikeLink, Button, SearchInput, FilterSelect, Switch, LookupCell, Tabs, TabsList, TabsTrigger, TabsContent, Table, TableHeader, TableBody, TableRow, TableCell, Avatar, DescriptionList, DescriptionListItem, DescriptionListMenu, DescriptionListMenuItem, Divider, Chart, DashboardSurface, DashboardLayout, DashboardPanel, MetricReadout, AnalysisView, Json, Link, StatusBadge, TextBlock, TypographyTitle, TypographyBody, TypographyCaption, TableHeaderCell, WidgetTextWidget, WidgetTitle, WidgetTextPrimary, WidgetTextSecondary, WidgetLoading, WidgetDecoration, Typography, Widget } = createUiComponents(React);
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
// 此文件由 scripts/generate-components.mjs 生成,请修改产品能力声明。
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import type { AppStyle } from "./style.js";
|
|
5
|
+
import type { AppKeyboardEvent } from "./events.js";
|
|
6
|
+
import type { NativeDOMIntrinsicElements } from "./native-dom.js";
|
|
7
|
+
export namespace JSX {
|
|
8
|
+
type Element = React.JSX.Element;
|
|
9
|
+
type ElementType = keyof IntrinsicElements | React.JSXElementConstructor<any>;
|
|
10
|
+
interface ElementClass extends React.JSX.ElementClass {}
|
|
11
|
+
interface ElementAttributesProperty extends React.JSX.ElementAttributesProperty {}
|
|
12
|
+
interface ElementChildrenAttribute extends React.JSX.ElementChildrenAttribute {}
|
|
13
|
+
type LibraryManagedAttributes<C, P> = React.JSX.LibraryManagedAttributes<C, P>;
|
|
14
|
+
interface IntrinsicAttributes extends React.Attributes {}
|
|
15
|
+
interface IntrinsicClassAttributes<T> extends React.ClassAttributes<T> {}
|
|
16
|
+
interface IntrinsicElements {
|
|
17
|
+
div: {
|
|
18
|
+
title?: string;
|
|
19
|
+
style?: AppStyle;
|
|
20
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
21
|
+
"aria-label"?: string;
|
|
22
|
+
"aria-hidden"?: boolean;
|
|
23
|
+
tabIndex?: -1 | 0;
|
|
24
|
+
onClick?: () => void | Promise<void>;
|
|
25
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
26
|
+
onFocus?: () => void | Promise<void>;
|
|
27
|
+
onBlur?: () => void | Promise<void>;
|
|
28
|
+
children?: ReactNode;
|
|
29
|
+
} & React.Attributes | NativeDOMIntrinsicElements["div"];
|
|
30
|
+
section: {
|
|
31
|
+
title?: string;
|
|
32
|
+
style?: AppStyle;
|
|
33
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
34
|
+
"aria-label"?: string;
|
|
35
|
+
"aria-hidden"?: boolean;
|
|
36
|
+
tabIndex?: -1 | 0;
|
|
37
|
+
onClick?: () => void | Promise<void>;
|
|
38
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
39
|
+
onFocus?: () => void | Promise<void>;
|
|
40
|
+
onBlur?: () => void | Promise<void>;
|
|
41
|
+
children?: ReactNode;
|
|
42
|
+
} & React.Attributes | NativeDOMIntrinsicElements["section"];
|
|
43
|
+
article: {
|
|
44
|
+
title?: string;
|
|
45
|
+
style?: AppStyle;
|
|
46
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
47
|
+
"aria-label"?: string;
|
|
48
|
+
"aria-hidden"?: boolean;
|
|
49
|
+
tabIndex?: -1 | 0;
|
|
50
|
+
onClick?: () => void | Promise<void>;
|
|
51
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
52
|
+
onFocus?: () => void | Promise<void>;
|
|
53
|
+
onBlur?: () => void | Promise<void>;
|
|
54
|
+
children?: ReactNode;
|
|
55
|
+
} & React.Attributes | NativeDOMIntrinsicElements["article"];
|
|
56
|
+
header: {
|
|
57
|
+
title?: string;
|
|
58
|
+
style?: AppStyle;
|
|
59
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
60
|
+
"aria-label"?: string;
|
|
61
|
+
"aria-hidden"?: boolean;
|
|
62
|
+
tabIndex?: -1 | 0;
|
|
63
|
+
onClick?: () => void | Promise<void>;
|
|
64
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
65
|
+
onFocus?: () => void | Promise<void>;
|
|
66
|
+
onBlur?: () => void | Promise<void>;
|
|
67
|
+
children?: ReactNode;
|
|
68
|
+
} & React.Attributes | NativeDOMIntrinsicElements["header"];
|
|
69
|
+
footer: {
|
|
70
|
+
title?: string;
|
|
71
|
+
style?: AppStyle;
|
|
72
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
73
|
+
"aria-label"?: string;
|
|
74
|
+
"aria-hidden"?: boolean;
|
|
75
|
+
tabIndex?: -1 | 0;
|
|
76
|
+
onClick?: () => void | Promise<void>;
|
|
77
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
78
|
+
onFocus?: () => void | Promise<void>;
|
|
79
|
+
onBlur?: () => void | Promise<void>;
|
|
80
|
+
children?: ReactNode;
|
|
81
|
+
} & React.Attributes | NativeDOMIntrinsicElements["footer"];
|
|
82
|
+
aside: {
|
|
83
|
+
title?: string;
|
|
84
|
+
style?: AppStyle;
|
|
85
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
86
|
+
"aria-label"?: string;
|
|
87
|
+
"aria-hidden"?: boolean;
|
|
88
|
+
tabIndex?: -1 | 0;
|
|
89
|
+
onClick?: () => void | Promise<void>;
|
|
90
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
91
|
+
onFocus?: () => void | Promise<void>;
|
|
92
|
+
onBlur?: () => void | Promise<void>;
|
|
93
|
+
children?: ReactNode;
|
|
94
|
+
} & React.Attributes | NativeDOMIntrinsicElements["aside"];
|
|
95
|
+
span: {
|
|
96
|
+
title?: string;
|
|
97
|
+
style?: AppStyle;
|
|
98
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
99
|
+
"aria-label"?: string;
|
|
100
|
+
"aria-hidden"?: boolean;
|
|
101
|
+
tabIndex?: -1 | 0;
|
|
102
|
+
onClick?: () => void | Promise<void>;
|
|
103
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
104
|
+
onFocus?: () => void | Promise<void>;
|
|
105
|
+
onBlur?: () => void | Promise<void>;
|
|
106
|
+
children?: ReactNode;
|
|
107
|
+
} & React.Attributes | NativeDOMIntrinsicElements["span"];
|
|
108
|
+
p: {
|
|
109
|
+
title?: string;
|
|
110
|
+
style?: AppStyle;
|
|
111
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
112
|
+
"aria-label"?: string;
|
|
113
|
+
"aria-hidden"?: boolean;
|
|
114
|
+
tabIndex?: -1 | 0;
|
|
115
|
+
onClick?: () => void | Promise<void>;
|
|
116
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
117
|
+
onFocus?: () => void | Promise<void>;
|
|
118
|
+
onBlur?: () => void | Promise<void>;
|
|
119
|
+
children?: ReactNode;
|
|
120
|
+
} & React.Attributes | NativeDOMIntrinsicElements["p"];
|
|
121
|
+
h1: {
|
|
122
|
+
title?: string;
|
|
123
|
+
style?: AppStyle;
|
|
124
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
125
|
+
"aria-label"?: string;
|
|
126
|
+
"aria-hidden"?: boolean;
|
|
127
|
+
tabIndex?: -1 | 0;
|
|
128
|
+
onClick?: () => void | Promise<void>;
|
|
129
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
130
|
+
onFocus?: () => void | Promise<void>;
|
|
131
|
+
onBlur?: () => void | Promise<void>;
|
|
132
|
+
children?: ReactNode;
|
|
133
|
+
} & React.Attributes | NativeDOMIntrinsicElements["h1"];
|
|
134
|
+
h2: {
|
|
135
|
+
title?: string;
|
|
136
|
+
style?: AppStyle;
|
|
137
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
138
|
+
"aria-label"?: string;
|
|
139
|
+
"aria-hidden"?: boolean;
|
|
140
|
+
tabIndex?: -1 | 0;
|
|
141
|
+
onClick?: () => void | Promise<void>;
|
|
142
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
143
|
+
onFocus?: () => void | Promise<void>;
|
|
144
|
+
onBlur?: () => void | Promise<void>;
|
|
145
|
+
children?: ReactNode;
|
|
146
|
+
} & React.Attributes | NativeDOMIntrinsicElements["h2"];
|
|
147
|
+
h3: {
|
|
148
|
+
title?: string;
|
|
149
|
+
style?: AppStyle;
|
|
150
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
151
|
+
"aria-label"?: string;
|
|
152
|
+
"aria-hidden"?: boolean;
|
|
153
|
+
tabIndex?: -1 | 0;
|
|
154
|
+
onClick?: () => void | Promise<void>;
|
|
155
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
156
|
+
onFocus?: () => void | Promise<void>;
|
|
157
|
+
onBlur?: () => void | Promise<void>;
|
|
158
|
+
children?: ReactNode;
|
|
159
|
+
} & React.Attributes | NativeDOMIntrinsicElements["h3"];
|
|
160
|
+
h4: {
|
|
161
|
+
title?: string;
|
|
162
|
+
style?: AppStyle;
|
|
163
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
164
|
+
"aria-label"?: string;
|
|
165
|
+
"aria-hidden"?: boolean;
|
|
166
|
+
tabIndex?: -1 | 0;
|
|
167
|
+
onClick?: () => void | Promise<void>;
|
|
168
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
169
|
+
onFocus?: () => void | Promise<void>;
|
|
170
|
+
onBlur?: () => void | Promise<void>;
|
|
171
|
+
children?: ReactNode;
|
|
172
|
+
} & React.Attributes | NativeDOMIntrinsicElements["h4"];
|
|
173
|
+
h5: {
|
|
174
|
+
title?: string;
|
|
175
|
+
style?: AppStyle;
|
|
176
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
177
|
+
"aria-label"?: string;
|
|
178
|
+
"aria-hidden"?: boolean;
|
|
179
|
+
tabIndex?: -1 | 0;
|
|
180
|
+
onClick?: () => void | Promise<void>;
|
|
181
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
182
|
+
onFocus?: () => void | Promise<void>;
|
|
183
|
+
onBlur?: () => void | Promise<void>;
|
|
184
|
+
children?: ReactNode;
|
|
185
|
+
} & React.Attributes | NativeDOMIntrinsicElements["h5"];
|
|
186
|
+
h6: {
|
|
187
|
+
title?: string;
|
|
188
|
+
style?: AppStyle;
|
|
189
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
190
|
+
"aria-label"?: string;
|
|
191
|
+
"aria-hidden"?: boolean;
|
|
192
|
+
tabIndex?: -1 | 0;
|
|
193
|
+
onClick?: () => void | Promise<void>;
|
|
194
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
195
|
+
onFocus?: () => void | Promise<void>;
|
|
196
|
+
onBlur?: () => void | Promise<void>;
|
|
197
|
+
children?: ReactNode;
|
|
198
|
+
} & React.Attributes | NativeDOMIntrinsicElements["h6"];
|
|
199
|
+
strong: {
|
|
200
|
+
title?: string;
|
|
201
|
+
style?: AppStyle;
|
|
202
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
203
|
+
"aria-label"?: string;
|
|
204
|
+
"aria-hidden"?: boolean;
|
|
205
|
+
tabIndex?: -1 | 0;
|
|
206
|
+
onClick?: () => void | Promise<void>;
|
|
207
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
208
|
+
onFocus?: () => void | Promise<void>;
|
|
209
|
+
onBlur?: () => void | Promise<void>;
|
|
210
|
+
children?: ReactNode;
|
|
211
|
+
} & React.Attributes | NativeDOMIntrinsicElements["strong"];
|
|
212
|
+
em: {
|
|
213
|
+
title?: string;
|
|
214
|
+
style?: AppStyle;
|
|
215
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
216
|
+
"aria-label"?: string;
|
|
217
|
+
"aria-hidden"?: boolean;
|
|
218
|
+
tabIndex?: -1 | 0;
|
|
219
|
+
onClick?: () => void | Promise<void>;
|
|
220
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
221
|
+
onFocus?: () => void | Promise<void>;
|
|
222
|
+
onBlur?: () => void | Promise<void>;
|
|
223
|
+
children?: ReactNode;
|
|
224
|
+
} & React.Attributes | NativeDOMIntrinsicElements["em"];
|
|
225
|
+
small: {
|
|
226
|
+
title?: string;
|
|
227
|
+
style?: AppStyle;
|
|
228
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
229
|
+
"aria-label"?: string;
|
|
230
|
+
"aria-hidden"?: boolean;
|
|
231
|
+
tabIndex?: -1 | 0;
|
|
232
|
+
onClick?: () => void | Promise<void>;
|
|
233
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
234
|
+
onFocus?: () => void | Promise<void>;
|
|
235
|
+
onBlur?: () => void | Promise<void>;
|
|
236
|
+
children?: ReactNode;
|
|
237
|
+
} & React.Attributes | NativeDOMIntrinsicElements["small"];
|
|
238
|
+
ul: {
|
|
239
|
+
title?: string;
|
|
240
|
+
style?: AppStyle;
|
|
241
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
242
|
+
"aria-label"?: string;
|
|
243
|
+
"aria-hidden"?: boolean;
|
|
244
|
+
tabIndex?: -1 | 0;
|
|
245
|
+
onClick?: () => void | Promise<void>;
|
|
246
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
247
|
+
onFocus?: () => void | Promise<void>;
|
|
248
|
+
onBlur?: () => void | Promise<void>;
|
|
249
|
+
children?: ReactNode;
|
|
250
|
+
} & React.Attributes | NativeDOMIntrinsicElements["ul"];
|
|
251
|
+
ol: {
|
|
252
|
+
title?: string;
|
|
253
|
+
style?: AppStyle;
|
|
254
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
255
|
+
"aria-label"?: string;
|
|
256
|
+
"aria-hidden"?: boolean;
|
|
257
|
+
tabIndex?: -1 | 0;
|
|
258
|
+
onClick?: () => void | Promise<void>;
|
|
259
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
260
|
+
onFocus?: () => void | Promise<void>;
|
|
261
|
+
onBlur?: () => void | Promise<void>;
|
|
262
|
+
children?: ReactNode;
|
|
263
|
+
} & React.Attributes | NativeDOMIntrinsicElements["ol"];
|
|
264
|
+
li: {
|
|
265
|
+
title?: string;
|
|
266
|
+
style?: AppStyle;
|
|
267
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
268
|
+
"aria-label"?: string;
|
|
269
|
+
"aria-hidden"?: boolean;
|
|
270
|
+
tabIndex?: -1 | 0;
|
|
271
|
+
onClick?: () => void | Promise<void>;
|
|
272
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
273
|
+
onFocus?: () => void | Promise<void>;
|
|
274
|
+
onBlur?: () => void | Promise<void>;
|
|
275
|
+
children?: ReactNode;
|
|
276
|
+
} & React.Attributes | NativeDOMIntrinsicElements["li"];
|
|
277
|
+
br: {
|
|
278
|
+
title?: string;
|
|
279
|
+
style?: AppStyle;
|
|
280
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
281
|
+
"aria-label"?: string;
|
|
282
|
+
"aria-hidden"?: boolean;
|
|
283
|
+
tabIndex?: -1 | 0;
|
|
284
|
+
onClick?: () => void | Promise<void>;
|
|
285
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
286
|
+
onFocus?: () => void | Promise<void>;
|
|
287
|
+
onBlur?: () => void | Promise<void>;
|
|
288
|
+
children?: ReactNode;
|
|
289
|
+
} & React.Attributes | NativeDOMIntrinsicElements["br"];
|
|
290
|
+
hr: {
|
|
291
|
+
title?: string;
|
|
292
|
+
style?: AppStyle;
|
|
293
|
+
role?: "group" | "region" | "note" | "heading" | "list" | "listitem";
|
|
294
|
+
"aria-label"?: string;
|
|
295
|
+
"aria-hidden"?: boolean;
|
|
296
|
+
tabIndex?: -1 | 0;
|
|
297
|
+
onClick?: () => void | Promise<void>;
|
|
298
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
299
|
+
onFocus?: () => void | Promise<void>;
|
|
300
|
+
onBlur?: () => void | Promise<void>;
|
|
301
|
+
children?: ReactNode;
|
|
302
|
+
} & React.Attributes | NativeDOMIntrinsicElements["hr"];
|
|
303
|
+
a: NativeDOMIntrinsicElements["a"];
|
|
304
|
+
abbr: NativeDOMIntrinsicElements["abbr"];
|
|
305
|
+
b: NativeDOMIntrinsicElements["b"];
|
|
306
|
+
blockquote: NativeDOMIntrinsicElements["blockquote"];
|
|
307
|
+
button: NativeDOMIntrinsicElements["button"];
|
|
308
|
+
caption: NativeDOMIntrinsicElements["caption"];
|
|
309
|
+
circle: NativeDOMIntrinsicElements["circle"];
|
|
310
|
+
clipPath: NativeDOMIntrinsicElements["clipPath"];
|
|
311
|
+
code: NativeDOMIntrinsicElements["code"];
|
|
312
|
+
col: NativeDOMIntrinsicElements["col"];
|
|
313
|
+
colgroup: NativeDOMIntrinsicElements["colgroup"];
|
|
314
|
+
dd: NativeDOMIntrinsicElements["dd"];
|
|
315
|
+
defs: NativeDOMIntrinsicElements["defs"];
|
|
316
|
+
details: NativeDOMIntrinsicElements["details"];
|
|
317
|
+
dialog: NativeDOMIntrinsicElements["dialog"];
|
|
318
|
+
dl: NativeDOMIntrinsicElements["dl"];
|
|
319
|
+
dt: NativeDOMIntrinsicElements["dt"];
|
|
320
|
+
ellipse: NativeDOMIntrinsicElements["ellipse"];
|
|
321
|
+
fieldset: NativeDOMIntrinsicElements["fieldset"];
|
|
322
|
+
figcaption: NativeDOMIntrinsicElements["figcaption"];
|
|
323
|
+
figure: NativeDOMIntrinsicElements["figure"];
|
|
324
|
+
form: NativeDOMIntrinsicElements["form"];
|
|
325
|
+
g: NativeDOMIntrinsicElements["g"];
|
|
326
|
+
i: NativeDOMIntrinsicElements["i"];
|
|
327
|
+
input: NativeDOMIntrinsicElements["input"];
|
|
328
|
+
label: NativeDOMIntrinsicElements["label"];
|
|
329
|
+
legend: NativeDOMIntrinsicElements["legend"];
|
|
330
|
+
line: NativeDOMIntrinsicElements["line"];
|
|
331
|
+
main: NativeDOMIntrinsicElements["main"];
|
|
332
|
+
mark: NativeDOMIntrinsicElements["mark"];
|
|
333
|
+
nav: NativeDOMIntrinsicElements["nav"];
|
|
334
|
+
optgroup: NativeDOMIntrinsicElements["optgroup"];
|
|
335
|
+
option: NativeDOMIntrinsicElements["option"];
|
|
336
|
+
path: NativeDOMIntrinsicElements["path"];
|
|
337
|
+
polygon: NativeDOMIntrinsicElements["polygon"];
|
|
338
|
+
polyline: NativeDOMIntrinsicElements["polyline"];
|
|
339
|
+
pre: NativeDOMIntrinsicElements["pre"];
|
|
340
|
+
progress: NativeDOMIntrinsicElements["progress"];
|
|
341
|
+
rect: NativeDOMIntrinsicElements["rect"];
|
|
342
|
+
select: NativeDOMIntrinsicElements["select"];
|
|
343
|
+
sub: NativeDOMIntrinsicElements["sub"];
|
|
344
|
+
summary: NativeDOMIntrinsicElements["summary"];
|
|
345
|
+
sup: NativeDOMIntrinsicElements["sup"];
|
|
346
|
+
svg: NativeDOMIntrinsicElements["svg"];
|
|
347
|
+
table: NativeDOMIntrinsicElements["table"];
|
|
348
|
+
tbody: NativeDOMIntrinsicElements["tbody"];
|
|
349
|
+
td: NativeDOMIntrinsicElements["td"];
|
|
350
|
+
text: NativeDOMIntrinsicElements["text"];
|
|
351
|
+
textarea: NativeDOMIntrinsicElements["textarea"];
|
|
352
|
+
th: NativeDOMIntrinsicElements["th"];
|
|
353
|
+
thead: NativeDOMIntrinsicElements["thead"];
|
|
354
|
+
time: NativeDOMIntrinsicElements["time"];
|
|
355
|
+
title: NativeDOMIntrinsicElements["title"];
|
|
356
|
+
tr: NativeDOMIntrinsicElements["tr"];
|
|
357
|
+
tspan: NativeDOMIntrinsicElements["tspan"];
|
|
358
|
+
}
|
|
359
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AppKeyboardEvent } from "./events.js";
|
|
2
|
+
import type { JSX as ReactJSX, DOMAttributes, CSSProperties, ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
/** 原生元素事件只传送数据快照,不携带 DOM 节点或同步事件方法。 */
|
|
5
|
+
export interface NativeDOMEvent extends AppKeyboardEvent {
|
|
6
|
+
type: string;
|
|
7
|
+
target: NativeDOMEventTarget;
|
|
8
|
+
currentTarget: NativeDOMEventTarget;
|
|
9
|
+
key: string;
|
|
10
|
+
code: string;
|
|
11
|
+
altKey: boolean;
|
|
12
|
+
ctrlKey: boolean;
|
|
13
|
+
metaKey: boolean;
|
|
14
|
+
shiftKey: boolean;
|
|
15
|
+
button: number;
|
|
16
|
+
}
|
|
17
|
+
export interface NativeDOMEventTarget {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
value: string;
|
|
21
|
+
checked: boolean;
|
|
22
|
+
dataset: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
export const nativeDOMTags: readonly string[];
|
|
25
|
+
export const nativeDOMEvents: readonly string[];
|
|
26
|
+
export function isNativeDOMTag(type: string): boolean;
|
|
27
|
+
export function assertNativeStyles(value: unknown, maxLength?: number): string;
|
|
28
|
+
export function assertNativeDOMNode(node: { type: string; props: Record<string, unknown>; children: string[] }): void;
|
|
29
|
+
|
|
30
|
+
/** HTML/CSS/JSX 可声明;自由属性的作用域由 Host 树校验,事件仍为 Worker 快照。 */
|
|
31
|
+
export type NativeDOMIntrinsicElements = {
|
|
32
|
+
[Tag in keyof ReactJSX.IntrinsicElements]: Omit<ReactJSX.IntrinsicElements[Tag],
|
|
33
|
+
keyof DOMAttributes<unknown> | 'ref' | 'href' | 'src' | 'action' | 'formAction' | 'style'> & {
|
|
34
|
+
children?: ReactNode;
|
|
35
|
+
style?: CSSProperties;
|
|
36
|
+
ref?: never;
|
|
37
|
+
dangerouslySetInnerHTML?: never;
|
|
38
|
+
onClick?: (event: NativeDOMEvent) => void | Promise<void>;
|
|
39
|
+
onChange?: (event: NativeDOMEvent) => void | Promise<void>;
|
|
40
|
+
onInput?: (event: NativeDOMEvent) => void | Promise<void>;
|
|
41
|
+
onSubmit?: (event: NativeDOMEvent) => void | Promise<void>;
|
|
42
|
+
onKeyDown?: (event: AppKeyboardEvent) => void | Promise<void>;
|
|
43
|
+
onKeyUp?: (event: NativeDOMEvent) => void | Promise<void>;
|
|
44
|
+
onFocus?: (event: NativeDOMEvent) => void | Promise<void>;
|
|
45
|
+
onBlur?: (event: NativeDOMEvent) => void | Promise<void>;
|
|
46
|
+
onPointerDown?: (event: NativeDOMEvent) => void | Promise<void>;
|
|
47
|
+
onPointerUp?: (event: NativeDOMEvent) => void | Promise<void>;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { assertJsonValue } from './validation.js';
|
|
2
|
+
|
|
3
|
+
const fail = () => { throw new Error('runtime_ui_contract_invalid'); };
|
|
4
|
+
export const nativeDOMTags = Object.freeze(('a abbr article aside b blockquote br button caption circle clipPath code col colgroup dd defs details dialog div dl dt ellipse em fieldset figcaption figure footer form g h1 h2 h3 h4 h5 h6 header hr i input label legend li line main mark nav ol optgroup option p path polygon polyline pre progress rect section select small span strong sub summary sup svg table tbody td text textarea th thead time title tr tspan ul').split(' '));
|
|
5
|
+
export const nativeDOMEvents = Object.freeze(['onClick', 'onChange', 'onInput', 'onSubmit', 'onKeyDown', 'onKeyUp', 'onFocus', 'onBlur', 'onPointerDown', 'onPointerUp']);
|
|
6
|
+
const tags = new Set(nativeDOMTags);
|
|
7
|
+
const attributes = new Set(('id className title role name placeholder value defaultValue checked defaultChecked disabled readOnly required multiple selected open hidden tabIndex type min max step minLength maxLength rows cols colSpan rowSpan scope htmlFor form noValidate autoComplete autoFocus spellCheck width height viewBox preserveAspectRatio d points x y x1 x2 y1 y2 cx cy r rx ry dx dy fill stroke strokeWidth strokeLinecap strokeLinejoin strokeDasharray fillRule clipRule clipPath opacity transform offset fontSize textAnchor dominantBaseline dateTime').split(' '));
|
|
8
|
+
const booleans = new Set(('checked defaultChecked disabled readOnly required multiple selected open hidden noValidate autoFocus').split(' '));
|
|
9
|
+
|
|
10
|
+
/** 只开放呈现;远端资源、CSS 逃逸编码和主动文档节点不属于此合同。 */
|
|
11
|
+
export function assertNativeStyles(value, maxLength = 65536) {
|
|
12
|
+
if (typeof value !== 'string' || value.length > maxLength || /[\\<\u0000-\u0008\u000b\u000c\u000e-\u001f]/u.test(value)) fail();
|
|
13
|
+
const normalized = value.replace(/\/\*[\s\S]*?\*\//gu, '');
|
|
14
|
+
if (/@(?:import|font-face|namespace)\b|(?:image-set|expression)\s*\(|(?:behavior|-moz-binding)\s*:/iu.test(normalized)) fail();
|
|
15
|
+
const withoutFragments = normalized.replace(/url\(\s*['"]?#[a-zA-Z][\w-]*['"]?\s*\)/gu, '');
|
|
16
|
+
if (/url\s*\(/iu.test(withoutFragments)) fail();
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isNativeDOMTag(type) { return tags.has(type); }
|
|
21
|
+
export function assertNativeDOMNode(node) {
|
|
22
|
+
if (!isNativeDOMTag(node.type)) fail();
|
|
23
|
+
for (const [key, value] of Object.entries(node.props)) {
|
|
24
|
+
if (nativeDOMEvents.includes(key)) {
|
|
25
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).join() !== 'callbackId'
|
|
26
|
+
|| typeof value.callbackId !== 'string' || !value.callbackId || value.callbackId.length > 160) fail();
|
|
27
|
+
} else if (key === 'style') {
|
|
28
|
+
assertJsonValue(value, { maxBytes: 16384, maxEntries: 100 });
|
|
29
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) fail();
|
|
30
|
+
for (const [property, item] of Object.entries(value)) {
|
|
31
|
+
if (!/^(?:[a-zA-Z][a-zA-Z0-9]*|--[a-zA-Z][\w-]*)$/u.test(property) || !['string', 'number'].includes(typeof item)) fail();
|
|
32
|
+
assertNativeStyles(`${property}:${item}`, 4096);
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
if (!attributes.has(key) && !/^(?:aria|data)-[a-z][a-z0-9-]{0,60}$/u.test(key)) fail();
|
|
36
|
+
if (!['string', 'number', 'boolean'].includes(typeof value) || typeof value === 'number' && !Number.isFinite(value)
|
|
37
|
+
|| typeof value === 'string' && value.length > 4096 || booleans.has(key) && typeof value !== 'boolean') fail();
|
|
38
|
+
if (['fill', 'stroke', 'clipPath'].includes(key)) assertNativeStyles(String(value), 4096);
|
|
39
|
+
if (key === 'type' && !['button', 'submit', 'reset', 'text', 'search', 'email', 'tel', 'url', 'number', 'date', 'datetime-local', 'time', 'checkbox', 'radio', 'range', 'hidden'].includes(value)) fail();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// 输入与 SVG 叶节点不能暗藏子树。
|
|
43
|
+
if (['input', 'br', 'hr', 'col'].includes(node.type) && node.children.length) fail();
|
|
44
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// 此文件由 scripts/generate-components.mjs 生成,请修改产品能力声明。
|
|
2
|
+
import type { CSSProperties } from "react";
|
|
3
|
+
export type AllowedStyleKey = "display" | "flexDirection" | "flexWrap" | "alignItems" | "alignSelf" | "justifyContent" | "flexGrow" | "flexShrink" | "flexBasis" | "gap" | "rowGap" | "columnGap" | "width" | "height" | "minWidth" | "minHeight" | "maxWidth" | "maxHeight" | "boxSizing" | "margin" | "marginTop" | "marginRight" | "marginBottom" | "marginLeft" | "padding" | "paddingTop" | "paddingRight" | "paddingBottom" | "paddingLeft" | "fontSize" | "fontWeight" | "lineHeight" | "textAlign" | "whiteSpace" | "overflowWrap" | "wordBreak" | "color" | "backgroundColor" | "borderWidth" | "borderStyle" | "borderColor" | "borderRadius" | "opacity" | "overflow" | "overflowX" | "overflowY";
|
|
4
|
+
export type AppStyle = Pick<CSSProperties, AllowedStyleKey>;
|
|
5
|
+
export function assertAppStyle(value: unknown): AppStyle;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// 此文件由 scripts/generate-components.mjs 生成,请修改产品能力声明。
|
|
2
|
+
export const allowedStyleKeys = Object.freeze(["display","flexDirection","flexWrap","alignItems","alignSelf","justifyContent","flexGrow","flexShrink","flexBasis","gap","rowGap","columnGap","width","height","minWidth","minHeight","maxWidth","maxHeight","boxSizing","margin","marginTop","marginRight","marginBottom","marginLeft","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","fontSize","fontWeight","lineHeight","textAlign","whiteSpace","overflowWrap","wordBreak","color","backgroundColor","borderWidth","borderStyle","borderColor","borderRadius","opacity","overflow","overflowX","overflowY"]);
|
package/src/ui/style.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { allowedStyleKeys } from './style.generated.js';
|
|
2
|
+
import { assertJsonValue } from './validation.js';
|
|
3
|
+
|
|
4
|
+
const allowed = new Set(allowedStyleKeys);
|
|
5
|
+
/** 复制已经通过 JSON 数据边界的样式,不解释 CSS 值。 */
|
|
6
|
+
export function assertAppStyle(value) {
|
|
7
|
+
assertJsonValue(value);
|
|
8
|
+
if (!value || Array.isArray(value) || typeof value !== 'object') throw new Error('runtime_ui_contract_invalid');
|
|
9
|
+
const result = {};
|
|
10
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
11
|
+
if (!allowed.has(key) || !['string', 'number'].includes(typeof entry)) throw new Error('runtime_ui_contract_invalid');
|
|
12
|
+
result[key] = entry;
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const fail = () => { throw new Error('runtime_ui_contract_invalid'); };
|
|
2
|
+
|
|
3
|
+
/** 在序列化之前校验,避免 Date、非有限数字或 undefined 被 JSON 静默改写。 */
|
|
4
|
+
export function assertJsonValue(value, { maxBytes = 32 * 1024, maxEntries = 1000, maxDepth = 8 } = {}) {
|
|
5
|
+
const ancestors = new Set();
|
|
6
|
+
let entries = 0;
|
|
7
|
+
function visit(item, depth) {
|
|
8
|
+
if (depth > maxDepth) fail();
|
|
9
|
+
if (item === null || typeof item === 'string' || typeof item === 'boolean') return;
|
|
10
|
+
if (typeof item === 'number') { if (!Number.isFinite(item)) fail(); return; }
|
|
11
|
+
if (typeof item !== 'object' || ancestors.has(item)) fail();
|
|
12
|
+
const array = Array.isArray(item);
|
|
13
|
+
if (!array && ![Object.prototype, null].includes(Object.getPrototypeOf(item))) fail();
|
|
14
|
+
const keys = Reflect.ownKeys(item).filter(key => !array || key !== 'length');
|
|
15
|
+
if (keys.some(key => typeof key !== 'string') || (array && keys.length !== item.length)) fail();
|
|
16
|
+
entries += keys.length;
|
|
17
|
+
if (entries > maxEntries) fail();
|
|
18
|
+
ancestors.add(item);
|
|
19
|
+
for (const key of keys) {
|
|
20
|
+
const descriptor = Object.getOwnPropertyDescriptor(item, key);
|
|
21
|
+
if (!descriptor?.enumerable || !Object.hasOwn(descriptor, 'value') || (array && !/^(0|[1-9]\d*)$/.test(key))) fail();
|
|
22
|
+
visit(descriptor.value, depth + 1);
|
|
23
|
+
}
|
|
24
|
+
ancestors.delete(item);
|
|
25
|
+
}
|
|
26
|
+
visit(value, 0);
|
|
27
|
+
if (new TextEncoder().encode(JSON.stringify(value)).byteLength > maxBytes) fail();
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function assertBannerActions(actions) {
|
|
32
|
+
if (!Array.isArray(actions) || !actions.length || actions.length > 3) fail();
|
|
33
|
+
for (const action of actions) {
|
|
34
|
+
if (!action || typeof action !== 'object' || Object.keys(action).some(key => !['label', 'variant'].includes(key))
|
|
35
|
+
|| typeof action.label !== 'string' || !action.label.trim() || action.label.length > 200
|
|
36
|
+
|| !['primary', 'secondary'].includes(action.variant)) fail();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 有界的纯数据图表;不接受脚本、HTML、任意样式或补零。 */
|
|
41
|
+
export function assertChartData(data) {
|
|
42
|
+
assertJsonValue(data, { maxEntries: 1200 });
|
|
43
|
+
const only = (item, keys) => item && typeof item === 'object' && !Array.isArray(item) && Object.keys(item).every(key => keys.includes(key));
|
|
44
|
+
const label = value => typeof value === 'string' && value.trim().length > 0 && value.length <= 200;
|
|
45
|
+
if (!only(data, ['labels', 'series']) || !Array.isArray(data.labels) || data.labels.length > 200 || !data.labels.every(label)
|
|
46
|
+
|| !Array.isArray(data.series) || !data.series.length || data.series.length > 4) fail();
|
|
47
|
+
const ids = new Set();
|
|
48
|
+
for (const series of data.series) {
|
|
49
|
+
if (!only(series, ['id', 'label', 'values']) || !label(series.id) || !label(series.label) || ids.has(series.id)
|
|
50
|
+
|| !Array.isArray(series.values) || series.values.length !== data.labels.length
|
|
51
|
+
|| series.values.some(value => value !== null && (typeof value !== 'number' || !Number.isFinite(value)))) fail();
|
|
52
|
+
ids.add(series.id);
|
|
53
|
+
}
|
|
54
|
+
return data;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
export const ANALYSIS_VIEW_LIMITS = Object.freeze({ maxBytes: 256 * 1024, maxEntries: 20000, maxDepth: 24 });
|
|
59
|
+
/** 结果只携带有界纯数据;身份、网络和执行代码均不是呈现合同。 */
|
|
60
|
+
export function assertAnalysisViewData(asset, result) {
|
|
61
|
+
assertJsonValue(asset, ANALYSIS_VIEW_LIMITS);
|
|
62
|
+
assertJsonValue(result, ANALYSIS_VIEW_LIMITS);
|
|
63
|
+
const object = x => x !== null && typeof x === 'object' && !Array.isArray(x);
|
|
64
|
+
const label = x => typeof x === 'string' && x.length > 0 && x.length <= 200;
|
|
65
|
+
const cells = items => Array.isArray(items) && items.length <= 2400 && items.every(cell => object(cell) && label(cell.status) && (cell.value === null || typeof cell.value === 'string' && /^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(cell.value) && Number.isFinite(Number(cell.value))));
|
|
66
|
+
if (!object(asset) || !object(asset.definition) || !label(asset.definition.id) || !label(asset.definition.name) || !object(asset.capability)
|
|
67
|
+
|| !object(result) || !['scalar', 'state_analysis'].includes(result.kind) || !object(result.query) || !Array.isArray(result.query.metrics)
|
|
68
|
+
|| result.query.metrics.length !== 1 || result.query.metrics[0]?.metricId !== asset.definition.id || !object(result.query.timeRange)) fail();
|
|
69
|
+
if (result.kind === 'state_analysis') {
|
|
70
|
+
const state = result.analysisResult;
|
|
71
|
+
if (!object(state) || !Array.isArray(state.measures) || !state.measures.length || state.measures.length > 24
|
|
72
|
+
|| state.measures.some(m => !object(m) || !label(m.slotId) || !label(m.label) || !['count', 'ratio', 'number'].includes(m.unit))
|
|
73
|
+
|| new Set(state.measures.map(m => m.slotId)).size !== state.measures.length
|
|
74
|
+
|| !Array.isArray(state.dimensions) || !Array.isArray(state.rows) || state.rows.length > 400 || !cells(state.totals)
|
|
75
|
+
|| state.rows.some(row => !object(row) || !Array.isArray(row.groups) || !cells(row.cells))) fail();
|
|
76
|
+
} else if (!cells(result.cells) || !cells(result.totals) || [...result.cells, ...result.totals].some(c => !Array.isArray(c.groups))) fail();
|
|
77
|
+
return { asset, result };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** TreeMap 的面积只能使用单系列非负数;选择始终指向原始标签索引。 */
|
|
81
|
+
export function assertChartSelection(props) {
|
|
82
|
+
if (props.selectedIndex !== undefined && (!Number.isInteger(props.selectedIndex) || props.selectedIndex < 0 || props.selectedIndex >= props.data.labels.length)) fail();
|
|
83
|
+
if (props.dimmedIndices !== undefined && (!Array.isArray(props.dimmedIndices) || props.dimmedIndices.length > props.data.labels.length || new Set(props.dimmedIndices).size !== props.dimmedIndices.length || props.dimmedIndices.some(index => !Number.isInteger(index) || index < 0 || index >= props.data.labels.length))) fail();
|
|
84
|
+
if (props.detailLabels !== undefined && (!Array.isArray(props.detailLabels) || props.detailLabels.length !== props.data.labels.length || props.detailLabels.some(label => typeof label !== 'string' || label.length > 200))) fail();
|
|
85
|
+
if (props.variant === 'treemap' && (props.data.series.length !== 1 || props.data.series[0].values.some(value => value !== null && value < 0))) fail();
|
|
86
|
+
}
|