cdp-material-sdk 0.0.5
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/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/DataScope-B7ffH5qN.js +106 -0
- package/dist/components/core/types.d.ts +16 -0
- package/dist/components/data-scope/DataScope.d.ts +45 -0
- package/dist/constants/event.d.ts +22 -0
- package/dist/context/DataContainerRuntimeContext.d.ts +16 -0
- package/dist/context/DataScopeContext.d.ts +51 -0
- package/dist/context/PageContext.d.ts +26 -0
- package/dist/hooks/useConcurrentLoading.d.ts +16 -0
- package/dist/hooks/useDataContainer.d.ts +61 -0
- package/dist/hooks/useDualLoading.d.ts +22 -0
- package/dist/host-react.d.ts +15 -0
- package/dist/host-react.js +11 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +42 -0
- package/dist/plugin.d.ts +45 -0
- package/dist/portable.d.ts +20 -0
- package/dist/portable.js +33 -0
- package/dist/protocol/adapter.d.ts +50 -0
- package/dist/protocol/common-schemas.d.ts +64 -0
- package/dist/protocol/events.d.ts +48 -0
- package/dist/protocol/manifest-types.d.ts +10 -0
- package/dist/protocol/manifest.d.ts +144 -0
- package/dist/protocol/traits.d.ts +7 -0
- package/dist/types/category.d.ts +15 -0
- package/dist/utils/mergeInitialValue.d.ts +10 -0
- package/dist/validateManifest-CYqjvQe_.js +355 -0
- package/dist/validation/validateManifest.d.ts +59 -0
- package/docs/component-development/FAQ.md +203 -0
- package/docs/component-development/README.md +176 -0
- package/docs/component-development/getting-started/01-/345/210/233/345/273/272/346/210/226/346/216/245/345/205/245/347/273/204/344/273/266/345/272/223/345/267/245/347/250/213.md +172 -0
- package/docs/component-development/getting-started/02-/345/210/233/345/273/272/347/273/204/344/273/266/345/214/205/345/271/266/346/263/250/345/206/214.md +185 -0
- package/docs/component-development/getting-started/03-/345/274/200/345/217/221/346/234/200/345/260/217/345/217/257/350/277/220/350/241/214/347/273/204/344/273/266.md +196 -0
- package/docs/component-development/getting-started/04-/346/236/204/345/273/272/345/217/221/345/270/203/344/270/216/345/256/277/344/270/273/346/216/245/345/205/245.md +147 -0
- package/docs/component-development/getting-started/05-/350/207/252/346/243/200/344/270/216/346/216/222/351/224/231.md +186 -0
- package/docs/component-development/recipes//344/275/277/347/224/250Adapter/351/200/202/351/205/215/347/273/204/344/273/266API.md +131 -0
- package/docs/component-development/recipes//345/243/260/346/230/216/344/272/213/344/273/266.md +229 -0
- package/docs/component-development/recipes//345/243/260/346/230/216/345/212/250/344/275/234/344/270/216/347/212/266/346/200/201.md +188 -0
- package/docs/component-development/recipes//345/243/260/346/230/216/345/270/203/345/261/200/345/256/271/345/231/250/347/273/204/344/273/266.md +221 -0
- package/docs/component-development/recipes//345/243/260/346/230/216/346/217/222/346/247/275.md +141 -0
- package/docs/component-development/recipes//345/243/260/346/230/216/346/225/260/346/215/256/345/255/227/346/256/265/347/273/204/344/273/266.md +147 -0
- package/docs/component-development/recipes//345/243/260/346/230/216/346/225/260/346/215/256/345/256/271/345/231/250/347/273/204/344/273/266.md +258 -0
- package/docs/component-development/recipes//346/216/245/345/205/245/347/254/254/344/270/211/346/226/271React/347/273/204/344/273/266/345/272/223.md +209 -0
- package/docs/component-development/recipes//351/205/215/347/275/256DOM/346/240/271/350/212/202/347/202/271/346/263/250/345/205/245.md +222 -0
- package/docs/component-development/recipes//351/205/215/347/275/256Loading/347/255/226/347/225/245.md +198 -0
- package/docs/component-development/recipes//351/205/215/347/275/256/350/256/276/350/256/241/345/231/250/345/205/203/344/277/241/346/201/257.md +153 -0
- package/docs/component-development/reference/ActionsState/346/250/241/345/236/213.md +124 -0
- package/docs/component-development/reference/DOM/346/240/271/350/212/202/347/202/271/346/263/250/345/205/245/346/250/241/345/236/213.md +125 -0
- package/docs/component-development/reference/Events/346/250/241/345/236/213.md +170 -0
- package/docs/component-development/reference/Loading/347/255/226/347/225/245/346/250/241/345/236/213.md +140 -0
- package/docs/component-development/reference/Manifest/345/255/227/346/256/265/345/217/202/350/200/203.md +251 -0
- package/docs/component-development/reference/SDK/345/257/274/345/205/245/350/276/271/347/225/214.md +119 -0
- package/docs/component-development/reference/Slots/346/250/241/345/236/213.md +110 -0
- package/docs/component-development/reference/Traits/350/203/275/345/212/233/346/250/241/345/236/213.md +165 -0
- package/docs/component-development/reference/validateManifest/346/240/241/351/252/214/350/247/204/345/210/231.md +171 -0
- package/docs/component-development/reference//345/274/225/346/223/216/345/237/272/347/241/200/350/203/275/345/212/233/346/250/241/345/236/213.md +70 -0
- package/docs/component-development/reference//347/244/272/344/276/213/344/273/243/347/240/201/347/264/242/345/274/225.md +163 -0
- package/package.json +67 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { useState as m, useRef as A, useEffect as h, useCallback as u, useMemo as g } from "react";
|
|
2
|
+
const S = () => {
|
|
3
|
+
const [t, e] = m(0), [o, n] = m(void 0), c = A(0);
|
|
4
|
+
h(() => {
|
|
5
|
+
c.current = t;
|
|
6
|
+
}, [t]);
|
|
7
|
+
const a = u(() => c.current > 0, []), s = u((l) => (e((y) => y + 1), l && n(l), a()), [a]), p = u(() => (e((l) => {
|
|
8
|
+
const y = Math.max(0, l - 1);
|
|
9
|
+
return y === 0 && n(void 0), y;
|
|
10
|
+
}), a()), [a]), r = u(() => {
|
|
11
|
+
e(0), n(void 0);
|
|
12
|
+
}, []);
|
|
13
|
+
return g(() => ({
|
|
14
|
+
isLoading: t > 0,
|
|
15
|
+
loadingText: o,
|
|
16
|
+
startLoading: s,
|
|
17
|
+
stopLoading: p,
|
|
18
|
+
getLoading: a,
|
|
19
|
+
resetLoading: r
|
|
20
|
+
}), [a, t, o, r, s, p]);
|
|
21
|
+
}, V = () => {
|
|
22
|
+
const {
|
|
23
|
+
isLoading: t,
|
|
24
|
+
loadingText: e,
|
|
25
|
+
startLoading: o,
|
|
26
|
+
stopLoading: n,
|
|
27
|
+
getLoading: c,
|
|
28
|
+
resetLoading: a
|
|
29
|
+
} = S(), [s, p] = m(!1), r = g(() => t || s, [t, s]), l = A(!1);
|
|
30
|
+
return h(() => {
|
|
31
|
+
l.current = r;
|
|
32
|
+
}, [r]), g(() => ({
|
|
33
|
+
actionLoading: t,
|
|
34
|
+
actionLoadingText: e,
|
|
35
|
+
startActionLoading: o,
|
|
36
|
+
stopActionLoading: n,
|
|
37
|
+
getActionLoading: c,
|
|
38
|
+
resetActionLoading: a,
|
|
39
|
+
dataLoading: s,
|
|
40
|
+
setDataLoading: p,
|
|
41
|
+
isLoading: r,
|
|
42
|
+
isLoadingRef: l
|
|
43
|
+
}), [
|
|
44
|
+
t,
|
|
45
|
+
e,
|
|
46
|
+
s,
|
|
47
|
+
c,
|
|
48
|
+
r,
|
|
49
|
+
a,
|
|
50
|
+
o,
|
|
51
|
+
n
|
|
52
|
+
]);
|
|
53
|
+
};
|
|
54
|
+
function O(t) {
|
|
55
|
+
if (!t)
|
|
56
|
+
return [];
|
|
57
|
+
const e = [];
|
|
58
|
+
for (const [o, n] of Object.entries(t))
|
|
59
|
+
n && e.push({
|
|
60
|
+
type: o,
|
|
61
|
+
...n
|
|
62
|
+
});
|
|
63
|
+
return e;
|
|
64
|
+
}
|
|
65
|
+
function _(t) {
|
|
66
|
+
if (!t)
|
|
67
|
+
return [];
|
|
68
|
+
const e = [];
|
|
69
|
+
for (const [o, n] of Object.entries(t))
|
|
70
|
+
n && e.push({
|
|
71
|
+
type: o,
|
|
72
|
+
...n
|
|
73
|
+
});
|
|
74
|
+
return e;
|
|
75
|
+
}
|
|
76
|
+
function v(t) {
|
|
77
|
+
const e = O(t?.events), o = _(t?.customEvents);
|
|
78
|
+
return {
|
|
79
|
+
standard: e,
|
|
80
|
+
custom: o,
|
|
81
|
+
all: [...e, ...o],
|
|
82
|
+
allKeys: [...e.map((n) => n.type), ...o.map((n) => n.type)]
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const $ = {
|
|
86
|
+
type: "object",
|
|
87
|
+
properties: {
|
|
88
|
+
label: { type: "string", title: "显示名称" },
|
|
89
|
+
value: { type: "string", title: "选项值" },
|
|
90
|
+
disabled: { type: "boolean", title: "是否禁用" }
|
|
91
|
+
}
|
|
92
|
+
}, x = {
|
|
93
|
+
type: "array",
|
|
94
|
+
title: "选项列表",
|
|
95
|
+
items: $
|
|
96
|
+
}, J = {
|
|
97
|
+
type: "object",
|
|
98
|
+
title: "远程选项配置",
|
|
99
|
+
properties: {
|
|
100
|
+
panelCode: { type: "string", title: "面板代码" },
|
|
101
|
+
fieldName: { type: "string", title: "字段名称" },
|
|
102
|
+
condition: {
|
|
103
|
+
type: "object",
|
|
104
|
+
title: "查询条件",
|
|
105
|
+
description: "声明式查询条件,支持表达式绑定。",
|
|
106
|
+
additionalProperties: { type: "string" }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}, W = {
|
|
110
|
+
BEFORE: "before",
|
|
111
|
+
AFTER: "after",
|
|
112
|
+
BOTH: "both"
|
|
113
|
+
}, q = {
|
|
114
|
+
IGNORE: "ignore",
|
|
115
|
+
STOP: "stop"
|
|
116
|
+
}, C = {
|
|
117
|
+
MOUNT: "mount",
|
|
118
|
+
UNMOUNT: "unmount",
|
|
119
|
+
CLICK: "click",
|
|
120
|
+
FOCUS: "focus",
|
|
121
|
+
BLUR: "blur",
|
|
122
|
+
VALUE_CHANGE: "valueChange",
|
|
123
|
+
ITEM_CLICK: "itemClick",
|
|
124
|
+
ITEM_DOUBLE_CLICK: "itemDoubleClick",
|
|
125
|
+
ITEM_RIGHT_CLICK: "itemRightClick",
|
|
126
|
+
ITEM_LONG_PRESS: "itemLongPress",
|
|
127
|
+
DATA_FETCH: "dataFetch"
|
|
128
|
+
}, I = Object.values(C), L = new Set(I), R = /^[^:\s]+:[^:\s]+$/;
|
|
129
|
+
function f(t) {
|
|
130
|
+
return L.has(t);
|
|
131
|
+
}
|
|
132
|
+
function E(t) {
|
|
133
|
+
return R.test(t) && !L.has(t);
|
|
134
|
+
}
|
|
135
|
+
const Q = "x-dynamic-enum", X = "x-slot", P = "$root", Z = "__state";
|
|
136
|
+
function D(t) {
|
|
137
|
+
if (!t)
|
|
138
|
+
return [];
|
|
139
|
+
const e = [];
|
|
140
|
+
for (const [o, n] of Object.entries(t))
|
|
141
|
+
n && e.push({
|
|
142
|
+
type: o,
|
|
143
|
+
...n
|
|
144
|
+
});
|
|
145
|
+
return e;
|
|
146
|
+
}
|
|
147
|
+
function M(t) {
|
|
148
|
+
if (!t)
|
|
149
|
+
return [];
|
|
150
|
+
const e = [];
|
|
151
|
+
for (const [o, n] of Object.entries(t))
|
|
152
|
+
n && e.push({
|
|
153
|
+
type: o,
|
|
154
|
+
...n
|
|
155
|
+
});
|
|
156
|
+
return e;
|
|
157
|
+
}
|
|
158
|
+
function w(t) {
|
|
159
|
+
const e = D(t?.events), o = M(t?.customEvents);
|
|
160
|
+
return {
|
|
161
|
+
standard: e,
|
|
162
|
+
custom: o,
|
|
163
|
+
all: [...e, ...o],
|
|
164
|
+
allKeys: [...e.map((n) => n.type), ...o.map((n) => n.type)]
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const tt = {
|
|
168
|
+
NATIVE: "native",
|
|
169
|
+
WRAPPER: "wrapper",
|
|
170
|
+
NONE: "none"
|
|
171
|
+
}, et = {
|
|
172
|
+
SPIN: "spin",
|
|
173
|
+
SKELETON: "skeleton",
|
|
174
|
+
WAVE: "wave"
|
|
175
|
+
}, nt = "loading", T = {
|
|
176
|
+
DATA_FIELD: "Data.Field",
|
|
177
|
+
DATA_CONTAINER: "Data.Container",
|
|
178
|
+
LAYOUT_CONTAINER: "Layout.Container",
|
|
179
|
+
INTERACTION_CLICKABLE: "Interaction.Clickable"
|
|
180
|
+
}, d = {
|
|
181
|
+
GENERAL: "General",
|
|
182
|
+
FLOAT: "Float",
|
|
183
|
+
LAYOUT: "Layout",
|
|
184
|
+
DATA_ENTRY: "DataEntry",
|
|
185
|
+
DATA_DISPLAY: "DataDisplay",
|
|
186
|
+
CHART: "Chart",
|
|
187
|
+
BUSINESS: "Business",
|
|
188
|
+
SHELL: "Shell",
|
|
189
|
+
PAGE: "Page",
|
|
190
|
+
DEV: "Dev",
|
|
191
|
+
HTML_TEMPLATE: "HtmlTemplate"
|
|
192
|
+
}, ot = {
|
|
193
|
+
[d.GENERAL]: "通用",
|
|
194
|
+
[d.FLOAT]: "悬浮",
|
|
195
|
+
[d.LAYOUT]: "布局",
|
|
196
|
+
[d.DATA_ENTRY]: "录入",
|
|
197
|
+
[d.DATA_DISPLAY]: "数据展示",
|
|
198
|
+
[d.CHART]: "图表",
|
|
199
|
+
[d.BUSINESS]: "业务组件",
|
|
200
|
+
[d.SHELL]: "外壳",
|
|
201
|
+
[d.PAGE]: "页面",
|
|
202
|
+
[d.DEV]: "调试",
|
|
203
|
+
[d.HTML_TEMPLATE]: "HTML 模板"
|
|
204
|
+
}, b = "slotProps.root", Y = [T.DATA_FIELD, T.DATA_CONTAINER], j = [P, b], N = ["string", "number", "boolean", "array", "object", "integer", "null"];
|
|
205
|
+
function i(t, e, o, n, c) {
|
|
206
|
+
t.push({ type: e, field: o, message: n, severity: c });
|
|
207
|
+
}
|
|
208
|
+
function K(t, e, o) {
|
|
209
|
+
const n = t.type;
|
|
210
|
+
if (!t.meta)
|
|
211
|
+
return i(e, n, "meta", "meta 字段是必填的", "error"), !1;
|
|
212
|
+
if (t.meta.title || i(e, n, "meta.title", "title 是必填的", "error"), t.meta.category ? Object.values(d).includes(t.meta.category) || i(e, n, "meta.category", `无效的 category: ${t.meta.category}`, "error") : i(e, n, "meta.category", "category 是必填的", "error"), t.meta.valueSchema) {
|
|
213
|
+
const a = t.meta.valueSchema.type;
|
|
214
|
+
a && typeof a == "string" && !N.includes(a) && i(e, n, "meta.valueSchema.type", `无效的 valueSchema type: ${a}`, "error");
|
|
215
|
+
}
|
|
216
|
+
return t.traits?.some((a) => Y.includes(a)) && !t.meta.valueSchema && i(
|
|
217
|
+
o,
|
|
218
|
+
n,
|
|
219
|
+
"meta.valueSchema",
|
|
220
|
+
"声明了 DATA_FIELD 或 DATA_CONTAINER trait 但缺少 valueSchema——建议补充以供设计器和 AI 工具推断数据类型",
|
|
221
|
+
"warning"
|
|
222
|
+
), !0;
|
|
223
|
+
}
|
|
224
|
+
function G(t, e, o) {
|
|
225
|
+
const n = t.type, c = w(t), a = new Set(c.standard.map((p) => p.type)), s = new Set(c.custom.map((p) => p.type));
|
|
226
|
+
if (c.all.length > 0 && (c.standard.forEach((p, r) => {
|
|
227
|
+
p.type || i(e, n, `events[${r}].type`, "事件类型不能为空", "error"), p.type && !f(p.type) && i(e, n, `events[${r}].type`, `标准事件类型必须属于 EngineEventProtocol: ${p.type}`, "error");
|
|
228
|
+
}), c.custom.forEach((p) => {
|
|
229
|
+
p.type || i(e, n, "customEvents.type", "自定义事件类型不能为空", "error"), p.type && !E(p.type) && i(e, n, `customEvents.${p.type}`, `自定义事件类型必须是 namespaced 形态: ${p.type}`, "error"), p.payloadSchema || i(e, n, `customEvents.${p.type}.payloadSchema`, "自定义事件必须声明 payloadSchema", "error");
|
|
230
|
+
})), t.adapter) {
|
|
231
|
+
const p = v(t.adapter);
|
|
232
|
+
p.standard.forEach((r, l) => {
|
|
233
|
+
r.type || i(e, n, `adapter.events[${l}].type`, "Adapter 事件类型不能为空", "error"), r.type && !f(r.type) && i(e, n, `adapter.events[${l}].type`, `Adapter 标准事件类型必须属于 EngineEventProtocol: ${r.type}`, "error"), r.type && f(r.type) && !a.has(r.type) && i(e, n, `adapter.events[${l}].type`, `Adapter 标准事件必须先在 manifest.events 中声明: ${r.type}`, "error"), r.propName || i(e, n, `adapter.events[${l}].propName`, "Adapter propName 不能为空", "error");
|
|
234
|
+
}), p.custom.forEach((r) => {
|
|
235
|
+
r.type || i(e, n, "adapter.customEvents.type", "Adapter 自定义事件类型不能为空", "error"), r.type && !E(r.type) && i(e, n, `adapter.customEvents.${r.type}`, `Adapter 自定义事件类型必须是 namespaced 形态: ${r.type}`, "error"), r.type && E(r.type) && !s.has(r.type) && i(e, n, `adapter.customEvents.${r.type}`, `Adapter 自定义事件必须先在 manifest.customEvents 中声明: ${r.type}`, "error"), r.propName || i(e, n, `adapter.customEvents.${r.type}.propName`, "Adapter customEvents propName 不能为空", "error");
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function H(t, e, o) {
|
|
240
|
+
const n = t.type, c = t.actions;
|
|
241
|
+
if (c)
|
|
242
|
+
for (const [a, s] of Object.entries(c))
|
|
243
|
+
s && (s.title || i(e, n, `actions.${a}.title`, `动作 "${a}" 缺少 title`, "error"), s.returns || i(o, n, `actions.${a}.returns`, `动作 "${a}" 未声明 returns schema,建议补充`, "warning"), s.params && s.params.type !== "object" && i(e, n, `actions.${a}.params.type`, '动作参数 schema 的 type 必须为 "object"', "error"));
|
|
244
|
+
}
|
|
245
|
+
function U(t, e, o) {
|
|
246
|
+
const n = t.type, c = t.engine?.render?.injection?.rootPath;
|
|
247
|
+
c ? typeof c == "string" && !j.includes(c) && i(
|
|
248
|
+
o,
|
|
249
|
+
n,
|
|
250
|
+
"engine.render.injection.rootPath",
|
|
251
|
+
`rootPath "${c}":请确保组件内部将该路径对应的 props 透传到根 DOM 节点。若已按规则实现可忽略此提示`,
|
|
252
|
+
"warning"
|
|
253
|
+
) : i(
|
|
254
|
+
o,
|
|
255
|
+
n,
|
|
256
|
+
"engine.render.injection.rootPath",
|
|
257
|
+
"未声明 rootPath——引擎将在组件外层套 <div> 兜底。推荐声明 INJECT_PATH_SLOT_PROPS 并在组件根节点透传 slotProps.root",
|
|
258
|
+
"warning"
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
function k(t, e, o) {
|
|
262
|
+
const n = t.type, c = t.slots;
|
|
263
|
+
if (c)
|
|
264
|
+
for (const [a, s] of Object.entries(c))
|
|
265
|
+
s && (s.title || i(e, n, `slots.${a}.title`, `插槽 "${a}" 缺少 title`, "error"), s.dynamic && (s.dynamicSource || i(e, n, `slots.${a}.dynamicSource`, `动态插槽 "${a}" 缺少 dynamicSource`, "error"), s.dynamicKey || i(e, n, `slots.${a}.dynamicKey`, `动态插槽 "${a}" 缺少 dynamicKey`, "error")), s.scoped && !s.scopeDescription && i(
|
|
266
|
+
o,
|
|
267
|
+
n,
|
|
268
|
+
`slots.${a}.scopeDescription`,
|
|
269
|
+
`作用域插槽 "${a}" 建议声明 scopeDescription,帮助设计器用户理解作用域数据`,
|
|
270
|
+
"warning"
|
|
271
|
+
), s.allowedChildren !== void 0 && !Array.isArray(s.allowedChildren) && i(e, n, `slots.${a}.allowedChildren`, `插槽 "${a}" 的 allowedChildren 必须是字符串数组`, "error"));
|
|
272
|
+
}
|
|
273
|
+
function F(t, e, o) {
|
|
274
|
+
const n = t.type, c = t.state;
|
|
275
|
+
if (c)
|
|
276
|
+
for (const [a, s] of Object.entries(c))
|
|
277
|
+
s && (s.title || i(e, n, `state.${a}.title`, `状态 "${a}" 缺少 title`, "error"), s.schema ? s.schema.type && typeof s.schema.type == "string" && !N.includes(s.schema.type) && i(e, n, `state.${a}.schema.type`, `状态 "${a}" 的 schema.type 无效: ${s.schema.type}`, "error") : i(e, n, `state.${a}.schema`, `状态 "${a}" 缺少 schema`, "error"));
|
|
278
|
+
}
|
|
279
|
+
function z(t) {
|
|
280
|
+
const e = [], o = [];
|
|
281
|
+
return t.type || i(e, t.type || "Unknown", "type", "type 字段是必填的", "error"), K(t, e, o) ? (G(t, e), H(t, e, o), F(t, e), k(t, e, o), U(t, e, o), {
|
|
282
|
+
valid: e.length === 0,
|
|
283
|
+
errors: e,
|
|
284
|
+
warnings: o
|
|
285
|
+
}) : { valid: !1, errors: e, warnings: o };
|
|
286
|
+
}
|
|
287
|
+
function at(t) {
|
|
288
|
+
const e = [], o = [];
|
|
289
|
+
for (const n of t) {
|
|
290
|
+
const c = z(n);
|
|
291
|
+
e.push(...c.errors), o.push(...c.warnings);
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
valid: e.length === 0,
|
|
295
|
+
errors: e,
|
|
296
|
+
warnings: o
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function st(t, e) {
|
|
300
|
+
return !t || !e ? [] : t.filter((o) => typeof e[o] != "function");
|
|
301
|
+
}
|
|
302
|
+
function rt(t, e) {
|
|
303
|
+
return !t?.state || !e ? [] : Object.keys(t.state).filter((o) => !(o in e));
|
|
304
|
+
}
|
|
305
|
+
function it(t) {
|
|
306
|
+
t.valid ? console.log("✅ 所有 Manifest 验证通过") : console.error(`❌ 发现 ${t.errors.length} 个错误`), t.errors.length > 0 && (console.error(`
|
|
307
|
+
错误列表:`), t.errors.forEach((e) => {
|
|
308
|
+
console.error(` ❌ [${e.type}] ${e.field}: ${e.message}`);
|
|
309
|
+
})), t.warnings.length > 0 && (console.warn(`
|
|
310
|
+
⚠️ 发现 ${t.warnings.length} 个警告`), t.warnings.forEach((e) => {
|
|
311
|
+
console.warn(` ⚠️ [${e.type}] ${e.field}: ${e.message}`);
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
function ct(t, e = "[Registry]") {
|
|
315
|
+
if (!t.valid && t.errors.length > 0) {
|
|
316
|
+
const o = t.errors[0]?.type || "Unknown";
|
|
317
|
+
console.error(`${e} ❌ Manifest validation failed for '${o}':`), t.errors.forEach((n) => console.error(` - ${n.field}: ${n.message}`));
|
|
318
|
+
}
|
|
319
|
+
if (t.warnings.length > 0) {
|
|
320
|
+
const o = t.warnings[0]?.type || "Unknown";
|
|
321
|
+
console.warn(`${e} ⚠️ Manifest warnings for '${o}':`), t.warnings.forEach((n) => console.warn(` - ${n.field}: ${n.message}`));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
export {
|
|
325
|
+
ot as C,
|
|
326
|
+
Q as D,
|
|
327
|
+
C as E,
|
|
328
|
+
P as I,
|
|
329
|
+
nt as L,
|
|
330
|
+
q as N,
|
|
331
|
+
$ as O,
|
|
332
|
+
J as R,
|
|
333
|
+
X as S,
|
|
334
|
+
d as a,
|
|
335
|
+
Z as b,
|
|
336
|
+
T as c,
|
|
337
|
+
b as d,
|
|
338
|
+
tt as e,
|
|
339
|
+
et as f,
|
|
340
|
+
W as g,
|
|
341
|
+
x as h,
|
|
342
|
+
I as i,
|
|
343
|
+
st as j,
|
|
344
|
+
rt as k,
|
|
345
|
+
E as l,
|
|
346
|
+
f as m,
|
|
347
|
+
v as n,
|
|
348
|
+
w as o,
|
|
349
|
+
ct as p,
|
|
350
|
+
it as q,
|
|
351
|
+
V as r,
|
|
352
|
+
at as s,
|
|
353
|
+
S as u,
|
|
354
|
+
z as v
|
|
355
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest 校验工具
|
|
3
|
+
*
|
|
4
|
+
* 纯函数,零宿主依赖。第三方组件开发者可直接使用:
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { validateManifest } from 'cdp-material-sdk/portable';
|
|
8
|
+
* const result = validateManifest(myManifest);
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
import type { ComponentManifest } from '../protocol/manifest';
|
|
12
|
+
export interface ValidationError {
|
|
13
|
+
type: string;
|
|
14
|
+
field: string;
|
|
15
|
+
message: string;
|
|
16
|
+
severity: 'error' | 'warning';
|
|
17
|
+
}
|
|
18
|
+
export interface ValidationResult {
|
|
19
|
+
valid: boolean;
|
|
20
|
+
errors: ValidationError[];
|
|
21
|
+
warnings: ValidationError[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 验证单个 ComponentManifest 的完整性和正确性。
|
|
25
|
+
*
|
|
26
|
+
* 校验范围:
|
|
27
|
+
* - 必填字段(type, meta, meta.title, meta.category)
|
|
28
|
+
* - events / customEvents 合法性
|
|
29
|
+
* - adapter 与 events 对齐
|
|
30
|
+
* - actions 声明完整性(title 必填,returns 建议)
|
|
31
|
+
* - state 声明完整性(title + schema 必填)
|
|
32
|
+
* - slots 声明完整性(title 必填,动态/作用域规则)
|
|
33
|
+
* - engine.render.injection.rootPath 声明提示
|
|
34
|
+
*/
|
|
35
|
+
export declare function validateManifest(manifest: ComponentManifest): ValidationResult;
|
|
36
|
+
/**
|
|
37
|
+
* 批量验证多个 Manifest
|
|
38
|
+
*/
|
|
39
|
+
export declare function validateManifests(manifests: ComponentManifest[]): ValidationResult;
|
|
40
|
+
/**
|
|
41
|
+
* 比对 manifest.actions 中组件自声明的 action keys 与 ref 实际暴露的方法。
|
|
42
|
+
* 返回"声明了但未实现"的 action 名称列表。
|
|
43
|
+
*
|
|
44
|
+
* 用于宿主 DEV 模式下的挂载时诊断。
|
|
45
|
+
*/
|
|
46
|
+
export declare function diagnoseMissingActionImpls(componentActionKeys: readonly string[] | undefined, refValue: Record<string, any> | null | undefined): string[];
|
|
47
|
+
/**
|
|
48
|
+
* 比对 manifest.state 声明的 key 与 __state 实际暴露的 key。
|
|
49
|
+
* 返回"声明了但未暴露"的 state 名称列表。
|
|
50
|
+
*/
|
|
51
|
+
export declare function diagnoseMissingStateKeys(manifest: Pick<ComponentManifest, 'state'> | undefined, stateValue: Record<string, any> | null | undefined): string[];
|
|
52
|
+
/**
|
|
53
|
+
* 打印验证结果到控制台
|
|
54
|
+
*/
|
|
55
|
+
export declare function printValidationResult(result: ValidationResult): void;
|
|
56
|
+
/**
|
|
57
|
+
* 打印单个组件的验证结果(简洁格式)
|
|
58
|
+
*/
|
|
59
|
+
export declare function printSingleValidationResult(result: ValidationResult, prefix?: string): void;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: faq
|
|
3
|
+
capability: troubleshooting
|
|
4
|
+
related:
|
|
5
|
+
- README.md
|
|
6
|
+
- getting-started/05-自检与排错.md
|
|
7
|
+
- reference/validateManifest校验规则.md
|
|
8
|
+
---
|
|
9
|
+
# FAQ
|
|
10
|
+
|
|
11
|
+
本文收集 CDP 组件开发中的高频问题。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 我是第三方组件作者,应该先看哪份文档?
|
|
16
|
+
|
|
17
|
+
先看 [README](./README.md)。它会按你的场景分流。
|
|
18
|
+
|
|
19
|
+
第一次接入时,按 `getting-started/` 的顺序阅读。已有组件包新增组件时,直接看 [开发最小可运行组件](./getting-started/03-开发最小可运行组件.md) 和对应 Recipe。
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 这套文档应该怎么读?
|
|
24
|
+
|
|
25
|
+
组件开发现在统一按任务组织:
|
|
26
|
+
|
|
27
|
+
- `getting-started/`:完成最小接入闭环。
|
|
28
|
+
- `recipes/`:按需启用数据、事件、动作、状态、插槽等能力。
|
|
29
|
+
- `reference/`:查字段、查模型、查校验规则。
|
|
30
|
+
- `FAQ.md`:解决常见问题。
|
|
31
|
+
|
|
32
|
+
第一次接入时,建议按 `getting-started/` 顺序阅读;已有组件包新增组件时,直接查对应 Recipe 和 Reference。
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 事件、动作、状态都必须声明吗?
|
|
37
|
+
|
|
38
|
+
不是。
|
|
39
|
+
|
|
40
|
+
| 能力 | 是否必需 | 什么时候需要 |
|
|
41
|
+
|------|----------|--------------|
|
|
42
|
+
| events | 否 | 组件要通知宿主 |
|
|
43
|
+
| actions | 否 | 宿主要命令式调用组件 |
|
|
44
|
+
| state | 否 | 外部表达式要读取组件内部状态 |
|
|
45
|
+
| slots | 否 | 组件要承载子内容 |
|
|
46
|
+
| traits | 否 | 组件具备数据、布局或点击语义时声明 |
|
|
47
|
+
|
|
48
|
+
最小组件只需要 React 组件、`ComponentManifest`、组件包注册和自检。
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 我已经有组件包,只想新增组件,需要看工程搭建吗?
|
|
53
|
+
|
|
54
|
+
不需要。
|
|
55
|
+
|
|
56
|
+
建议阅读:
|
|
57
|
+
|
|
58
|
+
1. [开发最小可运行组件](./getting-started/03-开发最小可运行组件.md)
|
|
59
|
+
2. [创建组件包并注册](./getting-started/02-创建组件包并注册.md) 的「往已有组件包新增组件」小节
|
|
60
|
+
3. 对应能力 Recipe
|
|
61
|
+
4. [自检与排错](./getting-started/05-自检与排错.md)
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 我包装 Ant Design 或 ECharts 组件,文档覆盖吗?
|
|
66
|
+
|
|
67
|
+
覆盖。请从 [接入第三方 React 组件库](./recipes/接入第三方React组件库.md) 开始。
|
|
68
|
+
|
|
69
|
+
推荐方式是写一层 CDP wrapper,而不是直接裸注册第三方组件。
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 组件不是 React 写的怎么办?
|
|
74
|
+
|
|
75
|
+
当前 CDP 组件 runtime 是 React。非 React 组件需要自行提供 React wrapper。
|
|
76
|
+
|
|
77
|
+
例如 Web Component:
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { forwardRef } from 'react';
|
|
81
|
+
import type { BaseUIProps } from 'cdp-material-sdk/portable';
|
|
82
|
+
|
|
83
|
+
export const MyWebComponentWrapper = forwardRef<HTMLElement, BaseUIProps>((props, ref) => {
|
|
84
|
+
const { slotProps } = props;
|
|
85
|
+
return <my-web-component {...slotProps?.root} ref={ref} />;
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## `rootPath` 不声明可以吗?
|
|
92
|
+
|
|
93
|
+
可以。宿主会外层 `<div>` 兜底。
|
|
94
|
+
|
|
95
|
+
但正式组件建议显式声明 `engine.render.injection.rootPath`,并把对应 props 透传到真实 DOM 根节点。
|
|
96
|
+
|
|
97
|
+
推荐写法:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { INJECT_PATH_SLOT_PROPS } from 'cdp-material-sdk/portable';
|
|
101
|
+
|
|
102
|
+
engine: {
|
|
103
|
+
render: {
|
|
104
|
+
injection: {
|
|
105
|
+
rootPath: INJECT_PATH_SLOT_PROPS,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 自定义 rootPath 出现 warning,是不是一定有问题?
|
|
114
|
+
|
|
115
|
+
不是。
|
|
116
|
+
|
|
117
|
+
自定义 rootPath 是合法选择。warning 的含义是提醒作者确认该路径上的 props 已经透传到真实 DOM。如果已经按规则实现,可以忽略。
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Loading 应该用 native、wrapper 还是 none?
|
|
122
|
+
|
|
123
|
+
| 场景 | 推荐 |
|
|
124
|
+
|------|------|
|
|
125
|
+
| 组件已有 loading prop,且能阻断交互 | `native` |
|
|
126
|
+
| loading prop 名不是 `loading` | `native + propName` |
|
|
127
|
+
| 组件没有 loading prop,但可以整体遮罩 | `wrapper` |
|
|
128
|
+
| 组件内部 loading 很复杂 | `none` |
|
|
129
|
+
|
|
130
|
+
注意:正确路径是 `engine.render.loading`,不是 `engine.loading`。
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 为什么 native 模式下仍然能点击?
|
|
135
|
+
|
|
136
|
+
这是组件实现问题。`native` 模式只负责注入 loading prop;组件自己必须在 loading 为 true 时阻断用户交互。
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## DATA_FIELD 和 DATA_CONTAINER 有什么区别?
|
|
141
|
+
|
|
142
|
+
| Trait | 作用 |
|
|
143
|
+
|-------|------|
|
|
144
|
+
| `DATA_FIELD` | 单个数据字段,接收 `value` 并调用 `onChange` |
|
|
145
|
+
| `DATA_CONTAINER` | 数据容器,管理子字段的数据作用域 |
|
|
146
|
+
|
|
147
|
+
输入框、选择器用 `DATA_FIELD`。Form、Table、List 这类容器用 `DATA_CONTAINER`。
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 声明了 DATA_FIELD 但没有 valueSchema 可以吗?
|
|
152
|
+
|
|
153
|
+
可以运行,但不推荐。`validateManifest()` 会 warning。
|
|
154
|
+
|
|
155
|
+
`valueSchema` 能帮助设计器、表达式和 AI 工具理解组件 value 类型。
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Action 声明了但调用失败,怎么排查?
|
|
160
|
+
|
|
161
|
+
检查:
|
|
162
|
+
|
|
163
|
+
1. `manifest.actions` 中的 key 是否和 ref 方法名一致。
|
|
164
|
+
2. 组件是否使用 `forwardRef`。
|
|
165
|
+
3. 组件是否通过 `useImperativeHandle` 暴露方法。
|
|
166
|
+
4. 使用 `diagnoseMissingActionImpls()` 做运行时诊断。
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## State 读不到,怎么排查?
|
|
171
|
+
|
|
172
|
+
检查:
|
|
173
|
+
|
|
174
|
+
1. `manifest.state` 是否声明了对应 key。
|
|
175
|
+
2. 组件是否通过 `COMPONENT_STATE_KEY` 暴露状态。
|
|
176
|
+
3. 状态值是否加入 `useImperativeHandle` deps。
|
|
177
|
+
4. 使用 `diagnoseMissingStateKeys()` 做运行时诊断。
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 可以直接导入宿主内部模块吗?
|
|
182
|
+
|
|
183
|
+
不可以。
|
|
184
|
+
|
|
185
|
+
第三方作者只应依赖 `cdp-material-sdk/portable`,必要时使用 `cdp-material-sdk/host-react`。宿主内部模块不作为第三方兼容契约。
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## `cdp-material-sdk/host-react` 什么时候能用?
|
|
190
|
+
|
|
191
|
+
只有当外部 bundle 与宿主共享同一份 React runtime 和 Context 身份时才使用。
|
|
192
|
+
|
|
193
|
+
如果无法确认,请不要使用 `host-react`,优先通过 props、events、actions 与宿主交互。
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 关联文档
|
|
198
|
+
|
|
199
|
+
本文档只保留本层级职责内容:Recipe 提供任务步骤,Reference 提供稳定模型和规则,validateManifest 文档提供校验级别事实源。
|
|
200
|
+
|
|
201
|
+
- 关联文档:[README](./README.md)
|
|
202
|
+
- 自检与排错:[05-自检与排错](./getting-started/05-自检与排错.md)
|
|
203
|
+
- 校验规则:[validateManifest校验规则](./reference/validateManifest校验规则.md)
|