@djvlc/contracts-validators 1.3.1 → 1.4.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/dist/index.mjs CHANGED
@@ -7,200 +7,314 @@ import {
7
7
  actionSpecSchema,
8
8
  dataQuerySpecSchema
9
9
  } from "@djvlc/contracts-schemas";
10
- var ajv = new Ajv({
11
- allErrors: true,
12
- verbose: true,
13
- strict: false
14
- });
15
- addFormats(ajv);
16
- var pageValidator = ajv.compile(pageSchema);
17
- var componentMetaValidator = ajv.compile(componentMetaSchema);
18
- var actionSpecValidator = ajv.compile(actionSpecSchema);
19
- var dataQuerySpecValidator = ajv.compile(dataQuerySpecSchema);
20
- function convertAjvErrors(errors) {
10
+ import {
11
+ pageSchema as pageSchema2,
12
+ componentMetaSchema as componentMetaSchema2,
13
+ actionSpecSchema as actionSpecSchema2,
14
+ dataQuerySpecSchema as dataQuerySpecSchema2
15
+ } from "@djvlc/contracts-schemas";
16
+ function createAjv() {
17
+ const ajv2 = new Ajv({
18
+ strict: true,
19
+ allErrors: true,
20
+ verbose: true,
21
+ removeAdditional: false,
22
+ useDefaults: true,
23
+ coerceTypes: false,
24
+ allowUnionTypes: true
25
+ });
26
+ addFormats(ajv2);
27
+ ajv2.addFormat("semver", /^\d+\.\d+\.\d+$/);
28
+ ajv2.addFormat("sha384", /^sha384-[A-Za-z0-9+/=]+$/);
29
+ return ajv2;
30
+ }
31
+ var ajv = createAjv();
32
+ ajv.addSchema(pageSchema, pageSchema.$id);
33
+ ajv.addSchema(componentMetaSchema, componentMetaSchema.$id);
34
+ ajv.addSchema(actionSpecSchema, actionSpecSchema.$id);
35
+ ajv.addSchema(dataQuerySpecSchema, dataQuerySpecSchema.$id);
36
+ function toValidationErrors(errors) {
21
37
  if (!errors) return [];
22
38
  return errors.map((error) => ({
23
39
  path: error.instancePath || "/",
24
40
  message: error.message || "Unknown error",
25
41
  keyword: error.keyword,
26
- expected: error.params,
27
- actual: error.data
42
+ params: error.params,
43
+ schemaPath: error.schemaPath
28
44
  }));
29
45
  }
30
- function createResult(valid, errors) {
46
+ function validate(validator, data) {
47
+ const valid = validator(data);
31
48
  return {
32
49
  valid,
33
- errors: convertAjvErrors(errors)
50
+ errors: toValidationErrors(validator.errors)
34
51
  };
35
52
  }
36
- function validatePage(data) {
37
- const valid = pageValidator(data);
38
- return createResult(valid, pageValidator.errors);
39
- }
53
+ var _validatePageSchema = ajv.compile(pageSchema);
54
+ var _validateComponentMeta = ajv.compile(componentMetaSchema);
55
+ var _validateActionSpec = ajv.compile(actionSpecSchema);
56
+ var _validateDataQuerySpec = ajv.compile(dataQuerySpecSchema);
40
57
  function validatePageSchema(data) {
41
- const result = validatePage(data);
42
- if (result.valid) {
43
- return { ...result, data };
44
- }
45
- return result;
46
- }
47
- function assertPageSchema(data) {
48
- const result = validatePage(data);
49
- if (!result.valid) {
50
- throw new Error(`Invalid PageSchema: ${result.errors.map((e) => e.message).join(", ")}`);
51
- }
58
+ return validate(_validatePageSchema, data);
52
59
  }
53
60
  function validateComponentMeta(data) {
54
- const valid = componentMetaValidator(data);
55
- return createResult(valid, componentMetaValidator.errors);
61
+ return validate(_validateComponentMeta, data);
56
62
  }
57
- function assertComponentMeta(data) {
58
- const result = validateComponentMeta(data);
59
- if (!result.valid) {
60
- throw new Error(`Invalid ComponentMeta: ${result.errors.map((e) => e.message).join(", ")}`);
61
- }
63
+ function validateActionSpec(data) {
64
+ return validate(_validateActionSpec, data);
65
+ }
66
+ function validateDataQuerySpec(data) {
67
+ return validate(_validateDataQuerySpec, data);
62
68
  }
63
- function validateActionDefinition(data) {
64
- const valid = actionSpecValidator(data);
65
- return createResult(valid, actionSpecValidator.errors);
69
+ function batchValidate(validator, items) {
70
+ const results = items.map((item, index) => {
71
+ const result = validator(item);
72
+ return {
73
+ index,
74
+ valid: result.valid,
75
+ errors: result.errors
76
+ };
77
+ });
78
+ const passed = results.filter((r) => r.valid).length;
79
+ return {
80
+ total: items.length,
81
+ passed,
82
+ failed: items.length - passed,
83
+ results
84
+ };
66
85
  }
67
- function assertActionDefinition(data) {
68
- const result = validateActionDefinition(data);
86
+ function validateOrThrow(validator, data, errorMessage) {
87
+ const result = validator(data);
69
88
  if (!result.valid) {
70
- throw new Error(`Invalid ActionDefinition: ${result.errors.map((e) => e.message).join(", ")}`);
89
+ const message = errorMessage || "Validation failed";
90
+ const details = result.errors.map((e) => `${e.path}: ${e.message}`).join("; ");
91
+ throw new Error(`${message}: ${details}`);
71
92
  }
72
93
  }
73
- function validateActionRequest(data) {
94
+ function validateComponentReferences(pageSchema3, availableComponents) {
74
95
  const errors = [];
75
- if (!data || typeof data !== "object") {
76
- errors.push({ path: "/", message: "ActionExecuteRequest must be an object" });
77
- return { valid: false, errors };
78
- }
79
- const obj = data;
80
- if (typeof obj.actionType !== "string" || !obj.actionType) {
81
- errors.push({ path: "/actionType", message: "actionType is required and must be a string" });
82
- }
83
- if (obj.params === void 0 || typeof obj.params !== "object") {
84
- errors.push({ path: "/params", message: "params is required and must be an object" });
96
+ function traverse(node, path) {
97
+ const componentType = node.componentType;
98
+ const componentVersion = node.componentVersion;
99
+ if (componentType && componentVersion) {
100
+ const versions = availableComponents.get(componentType);
101
+ if (!versions) {
102
+ errors.push({
103
+ path: `${path}/componentType`,
104
+ message: `Component "${componentType}" not found in registry`,
105
+ keyword: "componentReference",
106
+ params: { componentType },
107
+ schemaPath: ""
108
+ });
109
+ } else if (!versions.has(componentVersion)) {
110
+ errors.push({
111
+ path: `${path}/componentVersion`,
112
+ message: `Version "${componentVersion}" of component "${componentType}" not found`,
113
+ keyword: "componentVersion",
114
+ params: { componentType, componentVersion },
115
+ schemaPath: ""
116
+ });
117
+ }
118
+ }
119
+ const children = node.children;
120
+ if (Array.isArray(children)) {
121
+ children.forEach((child, index) => {
122
+ traverse(child, `${path}/children/${index}`);
123
+ });
124
+ }
85
125
  }
86
- if (obj.context === void 0 || typeof obj.context !== "object") {
87
- errors.push({ path: "/context", message: "context is required and must be an object" });
126
+ const schema = pageSchema3;
127
+ const root = schema.root;
128
+ if (root) {
129
+ traverse(root, "/root");
88
130
  }
89
- return { valid: errors.length === 0, errors };
131
+ return {
132
+ valid: errors.length === 0,
133
+ errors
134
+ };
90
135
  }
91
- function assertActionRequest(data) {
92
- const result = validateActionRequest(data);
93
- if (!result.valid) {
94
- throw new Error(`Invalid ActionExecuteRequest: ${result.errors.map((e) => e.message).join(", ")}`);
136
+ function validateExpressionReferences(pageSchema3, availableFields) {
137
+ const errors = [];
138
+ function checkExpression(expr, path) {
139
+ const meta = expr.meta;
140
+ const dependencies = meta?.dependencies;
141
+ if (dependencies) {
142
+ for (const dep of dependencies) {
143
+ if (!availableFields.has(dep)) {
144
+ errors.push({
145
+ path,
146
+ message: `Expression references unknown field: "${dep}"`,
147
+ keyword: "expressionReference",
148
+ params: { field: dep },
149
+ schemaPath: ""
150
+ });
151
+ }
152
+ }
153
+ }
95
154
  }
155
+ function traverse(obj, path) {
156
+ if (!obj || typeof obj !== "object") return;
157
+ const record = obj;
158
+ if (record.type && record.value && ["state", "binding", "computed"].includes(record.type)) {
159
+ checkExpression(record, path);
160
+ }
161
+ for (const [key, value] of Object.entries(record)) {
162
+ if (Array.isArray(value)) {
163
+ value.forEach((item, index) => {
164
+ traverse(item, `${path}/${key}/${index}`);
165
+ });
166
+ } else if (typeof value === "object" && value !== null) {
167
+ traverse(value, `${path}/${key}`);
168
+ }
169
+ }
170
+ }
171
+ traverse(pageSchema3, "");
172
+ return {
173
+ valid: errors.length === 0,
174
+ errors
175
+ };
96
176
  }
97
- function validateDataQueryDefinition(data) {
98
- const valid = dataQuerySpecValidator(data);
99
- return createResult(valid, dataQuerySpecValidator.errors);
100
- }
101
- function assertDataQueryDefinition(data) {
102
- const result = validateDataQueryDefinition(data);
103
- if (!result.valid) {
104
- throw new Error(`Invalid DataQueryDefinition: ${result.errors.map((e) => e.message).join(", ")}`);
177
+ function validateDataBindingReferences(pageSchema3) {
178
+ const errors = [];
179
+ const schema = pageSchema3;
180
+ const state = schema.state;
181
+ const fields = state?.fields;
182
+ const stateFields = new Set(fields ? Object.keys(fields) : []);
183
+ const dataBindings = schema.dataBindings;
184
+ if (Array.isArray(dataBindings)) {
185
+ dataBindings.forEach((binding, index) => {
186
+ const targetState = binding.targetState;
187
+ if (targetState && !stateFields.has(targetState)) {
188
+ errors.push({
189
+ path: `/dataBindings/${index}/targetState`,
190
+ message: `Data binding targetState "${targetState}" not found in state.fields`,
191
+ keyword: "dataBindingReference",
192
+ params: { targetState },
193
+ schemaPath: ""
194
+ });
195
+ }
196
+ });
105
197
  }
198
+ return {
199
+ valid: errors.length === 0,
200
+ errors
201
+ };
106
202
  }
107
- function validateQueryRequest(data) {
203
+ function validateActionReferences(pageSchema3) {
108
204
  const errors = [];
109
- if (!data || typeof data !== "object") {
110
- errors.push({ path: "/", message: "DataQueryRequest must be an object" });
111
- return { valid: false, errors };
205
+ function checkActionRef(action, path) {
206
+ const hasDefinition = !!action.actionDefinitionVersionId;
207
+ const hasBuiltin = !!action.builtinAction;
208
+ if (!hasDefinition && !hasBuiltin) {
209
+ errors.push({
210
+ path,
211
+ message: "ActionRef must specify either actionDefinitionVersionId or builtinAction",
212
+ keyword: "actionReference",
213
+ params: {},
214
+ schemaPath: ""
215
+ });
216
+ }
217
+ const onSuccess = action.onSuccess;
218
+ if (Array.isArray(onSuccess)) {
219
+ onSuccess.forEach((a, i) => checkActionRef(a, `${path}/onSuccess/${i}`));
220
+ }
221
+ const onError = action.onError;
222
+ if (Array.isArray(onError)) {
223
+ onError.forEach((a, i) => checkActionRef(a, `${path}/onError/${i}`));
224
+ }
112
225
  }
113
- const obj = data;
114
- if (typeof obj.queryVersionId !== "string" || !obj.queryVersionId) {
115
- errors.push({ path: "/queryVersionId", message: "queryVersionId is required and must be a string" });
226
+ function traverseActions(obj, path) {
227
+ if (!obj || typeof obj !== "object") return;
228
+ const record = obj;
229
+ const eventHandlers = record.eventHandlers;
230
+ if (Array.isArray(eventHandlers)) {
231
+ eventHandlers.forEach((handler, hIndex) => {
232
+ const actions = handler.actions;
233
+ if (Array.isArray(actions)) {
234
+ actions.forEach((action, aIndex) => {
235
+ checkActionRef(action, `${path}/eventHandlers/${hIndex}/actions/${aIndex}`);
236
+ });
237
+ }
238
+ });
239
+ }
240
+ const lifecycle2 = record.lifecycle;
241
+ if (lifecycle2) {
242
+ for (const [hookName, actions] of Object.entries(lifecycle2)) {
243
+ if (Array.isArray(actions)) {
244
+ actions.forEach((action, index) => {
245
+ checkActionRef(action, `${path}/lifecycle/${hookName}/${index}`);
246
+ });
247
+ }
248
+ }
249
+ }
250
+ const children = record.children;
251
+ if (Array.isArray(children)) {
252
+ children.forEach((child, index) => {
253
+ traverseActions(child, `${path}/children/${index}`);
254
+ });
255
+ }
116
256
  }
117
- if (obj.params === void 0 || typeof obj.params !== "object") {
118
- errors.push({ path: "/params", message: "params is required and must be an object" });
257
+ const schema = pageSchema3;
258
+ const lifecycle = schema.lifecycle;
259
+ if (lifecycle) {
260
+ for (const [hookName, actions] of Object.entries(lifecycle)) {
261
+ if (Array.isArray(actions)) {
262
+ actions.forEach((action, index) => {
263
+ checkActionRef(action, `/lifecycle/${hookName}/${index}`);
264
+ });
265
+ }
266
+ }
119
267
  }
120
- return { valid: errors.length === 0, errors };
121
- }
122
- function assertQueryRequest(data) {
123
- const result = validateQueryRequest(data);
124
- if (!result.valid) {
125
- throw new Error(`Invalid DataQueryRequest: ${result.errors.map((e) => e.message).join(", ")}`);
268
+ const root = schema.root;
269
+ if (root) {
270
+ traverseActions(root, "/root");
126
271
  }
127
- }
128
- function isPageSchema(value) {
129
- return validatePage(value).valid;
130
- }
131
- function isComponentMeta(value) {
132
- return validateComponentMeta(value).valid;
133
- }
134
- function isActionDefinition(value) {
135
- return validateActionDefinition(value).valid;
136
- }
137
- function isDataQueryDefinition(value) {
138
- return validateDataQueryDefinition(value).valid;
139
- }
140
- function isActionRequest(value) {
141
- return validateActionRequest(value).valid;
142
- }
143
- function isQueryRequest(value) {
144
- return validateQueryRequest(value).valid;
145
- }
146
- function validatePages(items) {
147
- const results = items.map((item, index) => {
148
- const result = validatePage(item);
149
- return {
150
- index,
151
- valid: result.valid,
152
- data: result.valid ? item : void 0,
153
- errors: result.errors
154
- };
155
- });
156
- const validCount = results.filter((r) => r.valid).length;
157
272
  return {
158
- allValid: validCount === items.length,
159
- validCount,
160
- invalidCount: items.length - validCount,
161
- results
273
+ valid: errors.length === 0,
274
+ errors
162
275
  };
163
276
  }
164
- function validateComponentMetas(items) {
165
- const results = items.map((item, index) => {
166
- const result = validateComponentMeta(item);
167
- return {
168
- index,
169
- valid: result.valid,
170
- data: result.valid ? item : void 0,
171
- errors: result.errors
172
- };
173
- });
174
- const validCount = results.filter((r) => r.valid).length;
277
+ function validatePageSchemaFull(data, availableComponents, availableFields) {
278
+ const allErrors = [];
279
+ const structureResult = validatePageSchema(data);
280
+ allErrors.push(...structureResult.errors);
281
+ if (!structureResult.valid) {
282
+ return { valid: false, errors: allErrors };
283
+ }
284
+ if (availableComponents) {
285
+ const componentResult = validateComponentReferences(data, availableComponents);
286
+ allErrors.push(...componentResult.errors);
287
+ }
288
+ if (availableFields) {
289
+ const expressionResult = validateExpressionReferences(data, availableFields);
290
+ allErrors.push(...expressionResult.errors);
291
+ }
292
+ const bindingResult = validateDataBindingReferences(data);
293
+ allErrors.push(...bindingResult.errors);
294
+ const actionResult = validateActionReferences(data);
295
+ allErrors.push(...actionResult.errors);
175
296
  return {
176
- allValid: validCount === items.length,
177
- validCount,
178
- invalidCount: items.length - validCount,
179
- results
297
+ valid: allErrors.length === 0,
298
+ errors: allErrors
180
299
  };
181
300
  }
301
+ var VERSION = "2.0.0";
182
302
  export {
183
- ajv,
184
- assertActionDefinition,
185
- assertActionRequest,
186
- assertComponentMeta,
187
- assertDataQueryDefinition,
188
- assertPageSchema,
189
- assertQueryRequest,
190
- isActionDefinition,
191
- isActionRequest,
192
- isComponentMeta,
193
- isDataQueryDefinition,
194
- isPageSchema,
195
- isQueryRequest,
196
- validateActionDefinition,
197
- validateActionRequest,
303
+ VERSION,
304
+ actionSpecSchema2 as actionSpecSchema,
305
+ batchValidate,
306
+ componentMetaSchema2 as componentMetaSchema,
307
+ dataQuerySpecSchema2 as dataQuerySpecSchema,
308
+ pageSchema2 as pageSchema,
309
+ validateActionReferences,
310
+ validateActionSpec,
198
311
  validateComponentMeta,
199
- validateComponentMetas,
200
- validateDataQueryDefinition,
201
- validatePage,
312
+ validateComponentReferences,
313
+ validateDataBindingReferences,
314
+ validateDataQuerySpec,
315
+ validateExpressionReferences,
316
+ validateOrThrow,
202
317
  validatePageSchema,
203
- validatePages,
204
- validateQueryRequest
318
+ validatePageSchemaFull
205
319
  };
206
320
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @djvlc/contracts-validators - DJV 低代码平台预编译校验器包\n *\n * 提供预编译的 Ajv 校验器,避免各仓库自行编写校验逻辑导致分叉\n *\n * @packageDocumentation\n */\n\nimport Ajv from 'ajv';\nimport addFormats from 'ajv-formats';\nimport {\n pageSchema,\n componentMetaSchema,\n actionSpecSchema,\n dataQuerySpecSchema,\n} from '@djvlc/contracts-schemas';\nimport type {\n PageSchema,\n ComponentMeta,\n ActionDefinition,\n DataQueryDefinition,\n ActionExecuteRequest,\n DataQueryRequest,\n} from '@djvlc/contracts-types';\n\n// ==================== 校验结果类型 ====================\n\n/**\n * 校验结果\n */\nexport interface ValidationResult {\n /** 是否有效 */\n valid: boolean;\n /** 错误列表 */\n errors: ValidationError[];\n}\n\n/**\n * 校验错误\n */\nexport interface ValidationError {\n /** 错误路径 */\n path: string;\n /** 错误消息 */\n message: string;\n /** 错误关键字 */\n keyword?: string;\n /** 期望值 */\n expected?: unknown;\n /** 实际值 */\n actual?: unknown;\n}\n\n// ==================== Ajv 实例 ====================\n\nconst ajv = new Ajv({\n allErrors: true,\n verbose: true,\n strict: false,\n});\n\naddFormats(ajv);\n\n// 预编译 Schema\nconst pageValidator = ajv.compile(pageSchema);\nconst componentMetaValidator = ajv.compile(componentMetaSchema);\nconst actionSpecValidator = ajv.compile(actionSpecSchema);\nconst dataQuerySpecValidator = ajv.compile(dataQuerySpecSchema);\n\n// ==================== 工具函数 ====================\n\n/**\n * 将 Ajv 错误转换为标准格式\n */\nfunction convertAjvErrors(errors: typeof ajv.errors): ValidationError[] {\n if (!errors) return [];\n\n return errors.map((error) => ({\n path: error.instancePath || '/',\n message: error.message || 'Unknown error',\n keyword: error.keyword,\n expected: error.params,\n actual: error.data,\n }));\n}\n\n/**\n * 创建校验结果\n */\nfunction createResult(valid: boolean, errors: typeof ajv.errors): ValidationResult {\n return {\n valid,\n errors: convertAjvErrors(errors),\n };\n}\n\n// ==================== 页面校验 ====================\n\n/**\n * 校验 PageSchema\n * @param data 待校验数据\n * @returns 校验结果\n */\nexport function validatePage(data: unknown): ValidationResult {\n const valid = pageValidator(data);\n return createResult(valid, pageValidator.errors);\n}\n\n/**\n * 校验 PageSchema,返回类型安全的结果\n */\nexport function validatePageSchema(data: unknown): ValidationResult & { data?: PageSchema } {\n const result = validatePage(data);\n if (result.valid) {\n return { ...result, data: data as PageSchema };\n }\n return result;\n}\n\n/**\n * 断言为 PageSchema\n */\nexport function assertPageSchema(data: unknown): asserts data is PageSchema {\n const result = validatePage(data);\n if (!result.valid) {\n throw new Error(`Invalid PageSchema: ${result.errors.map((e) => e.message).join(', ')}`);\n }\n}\n\n// ==================== 组件元数据校验 ====================\n\n/**\n * 校验 ComponentMeta\n * @param data 待校验数据\n * @returns 校验结果\n */\nexport function validateComponentMeta(data: unknown): ValidationResult {\n const valid = componentMetaValidator(data);\n return createResult(valid, componentMetaValidator.errors);\n}\n\n/**\n * 断言为 ComponentMeta\n */\nexport function assertComponentMeta(data: unknown): asserts data is ComponentMeta {\n const result = validateComponentMeta(data);\n if (!result.valid) {\n throw new Error(`Invalid ComponentMeta: ${result.errors.map((e) => e.message).join(', ')}`);\n }\n}\n\n// ==================== Action 校验 ====================\n\n/**\n * 校验 ActionDefinition\n * @param data 待校验数据\n * @returns 校验结果\n */\nexport function validateActionDefinition(data: unknown): ValidationResult {\n const valid = actionSpecValidator(data);\n return createResult(valid, actionSpecValidator.errors);\n}\n\n/**\n * 断言为 ActionDefinition\n */\nexport function assertActionDefinition(data: unknown): asserts data is ActionDefinition {\n const result = validateActionDefinition(data);\n if (!result.valid) {\n throw new Error(`Invalid ActionDefinition: ${result.errors.map((e) => e.message).join(', ')}`);\n }\n}\n\n/**\n * 校验 ActionExecuteRequest\n */\nexport function validateActionRequest(data: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n\n if (!data || typeof data !== 'object') {\n errors.push({ path: '/', message: 'ActionExecuteRequest must be an object' });\n return { valid: false, errors };\n }\n\n const obj = data as Record<string, unknown>;\n\n if (typeof obj.actionType !== 'string' || !obj.actionType) {\n errors.push({ path: '/actionType', message: 'actionType is required and must be a string' });\n }\n\n if (obj.params === undefined || typeof obj.params !== 'object') {\n errors.push({ path: '/params', message: 'params is required and must be an object' });\n }\n\n if (obj.context === undefined || typeof obj.context !== 'object') {\n errors.push({ path: '/context', message: 'context is required and must be an object' });\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * 断言为 ActionExecuteRequest\n */\nexport function assertActionRequest(data: unknown): asserts data is ActionExecuteRequest {\n const result = validateActionRequest(data);\n if (!result.valid) {\n throw new Error(`Invalid ActionExecuteRequest: ${result.errors.map((e) => e.message).join(', ')}`);\n }\n}\n\n// ==================== Data Query 校验 ====================\n\n/**\n * 校验 DataQueryDefinition\n * @param data 待校验数据\n * @returns 校验结果\n */\nexport function validateDataQueryDefinition(data: unknown): ValidationResult {\n const valid = dataQuerySpecValidator(data);\n return createResult(valid, dataQuerySpecValidator.errors);\n}\n\n/**\n * 断言为 DataQueryDefinition\n */\nexport function assertDataQueryDefinition(data: unknown): asserts data is DataQueryDefinition {\n const result = validateDataQueryDefinition(data);\n if (!result.valid) {\n throw new Error(`Invalid DataQueryDefinition: ${result.errors.map((e) => e.message).join(', ')}`);\n }\n}\n\n/**\n * 校验 DataQueryRequest\n */\nexport function validateQueryRequest(data: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n\n if (!data || typeof data !== 'object') {\n errors.push({ path: '/', message: 'DataQueryRequest must be an object' });\n return { valid: false, errors };\n }\n\n const obj = data as Record<string, unknown>;\n\n if (typeof obj.queryVersionId !== 'string' || !obj.queryVersionId) {\n errors.push({ path: '/queryVersionId', message: 'queryVersionId is required and must be a string' });\n }\n\n if (obj.params === undefined || typeof obj.params !== 'object') {\n errors.push({ path: '/params', message: 'params is required and must be an object' });\n }\n\n return { valid: errors.length === 0, errors };\n}\n\n/**\n * 断言为 DataQueryRequest\n */\nexport function assertQueryRequest(data: unknown): asserts data is DataQueryRequest {\n const result = validateQueryRequest(data);\n if (!result.valid) {\n throw new Error(`Invalid DataQueryRequest: ${result.errors.map((e) => e.message).join(', ')}`);\n }\n}\n\n// ==================== 类型保护函数 ====================\n\n/**\n * 检查是否为有效的 PageSchema\n */\nexport function isPageSchema(value: unknown): value is PageSchema {\n return validatePage(value).valid;\n}\n\n/**\n * 检查是否为有效的 ComponentMeta\n */\nexport function isComponentMeta(value: unknown): value is ComponentMeta {\n return validateComponentMeta(value).valid;\n}\n\n/**\n * 检查是否为有效的 ActionDefinition\n */\nexport function isActionDefinition(value: unknown): value is ActionDefinition {\n return validateActionDefinition(value).valid;\n}\n\n/**\n * 检查是否为有效的 DataQueryDefinition\n */\nexport function isDataQueryDefinition(value: unknown): value is DataQueryDefinition {\n return validateDataQueryDefinition(value).valid;\n}\n\n/**\n * 检查是否为有效的 ActionExecuteRequest\n */\nexport function isActionRequest(value: unknown): value is ActionExecuteRequest {\n return validateActionRequest(value).valid;\n}\n\n/**\n * 检查是否为有效的 DataQueryRequest\n */\nexport function isQueryRequest(value: unknown): value is DataQueryRequest {\n return validateQueryRequest(value).valid;\n}\n\n// ==================== 批量校验 ====================\n\n/**\n * 批量校验结果\n */\nexport interface BatchValidationResult<T> {\n /** 全部有效 */\n allValid: boolean;\n /** 有效项数量 */\n validCount: number;\n /** 无效项数量 */\n invalidCount: number;\n /** 各项结果 */\n results: Array<{\n index: number;\n valid: boolean;\n data?: T;\n errors: ValidationError[];\n }>;\n}\n\n/**\n * 批量校验 PageSchema\n */\nexport function validatePages(items: unknown[]): BatchValidationResult<PageSchema> {\n const results = items.map((item, index) => {\n const result = validatePage(item);\n return {\n index,\n valid: result.valid,\n data: result.valid ? (item as PageSchema) : undefined,\n errors: result.errors,\n };\n });\n\n const validCount = results.filter((r) => r.valid).length;\n\n return {\n allValid: validCount === items.length,\n validCount,\n invalidCount: items.length - validCount,\n results,\n };\n}\n\n/**\n * 批量校验 ComponentMeta\n */\nexport function validateComponentMetas(items: unknown[]): BatchValidationResult<ComponentMeta> {\n const results = items.map((item, index) => {\n const result = validateComponentMeta(item);\n return {\n index,\n valid: result.valid,\n data: result.valid ? (item as ComponentMeta) : undefined,\n errors: result.errors,\n };\n });\n\n const validCount = results.filter((r) => r.valid).length;\n\n return {\n allValid: validCount === items.length,\n validCount,\n invalidCount: items.length - validCount,\n results,\n };\n}\n\n// ==================== 导出 Ajv 实例(供高级用法) ====================\n\nexport { ajv };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @djvlc/contracts-validators\n *\n * DJVLC 低代码平台预编译 JSON Schema 校验器\n *\n * 提供高性能的 Ajv 校验器,用于:\n * - 发布期校验(Editor 发布页面时)\n * - 运行时校验(Runtime 加载组件时)\n * - API 校验(Platform 接收请求时)\n *\n * V2 版本更新:\n * - 支持 PageSchema V2 Runtime Core 校验\n * - 新增表达式引用校验(支持 binding 类型)\n * - 优化错误信息格式\n */\n\nimport Ajv, { type ValidateFunction, type ErrorObject } from 'ajv';\nimport addFormats from 'ajv-formats';\nimport {\n pageSchema,\n componentMetaSchema,\n actionSpecSchema,\n dataQuerySpecSchema,\n} from '@djvlc/contracts-schemas';\n\n// ============================================================\n// 校验器实例\n// ============================================================\n\n/**\n * 创建配置好的 Ajv 实例\n */\nfunction createAjv(): Ajv {\n const ajv = new Ajv({\n strict: true,\n allErrors: true,\n verbose: true,\n removeAdditional: false,\n useDefaults: true,\n coerceTypes: false,\n allowUnionTypes: true,\n });\n\n // 添加格式支持\n addFormats(ajv);\n\n // 添加自定义格式\n ajv.addFormat('semver', /^\\d+\\.\\d+\\.\\d+$/);\n ajv.addFormat('sha384', /^sha384-[A-Za-z0-9+/=]+$/);\n\n return ajv;\n}\n\n// 单例 Ajv 实例\nconst ajv = createAjv();\n\n// 注册所有 Schema\najv.addSchema(pageSchema, pageSchema.$id);\najv.addSchema(componentMetaSchema, componentMetaSchema.$id);\najv.addSchema(actionSpecSchema, actionSpecSchema.$id);\najv.addSchema(dataQuerySpecSchema, dataQuerySpecSchema.$id);\n\n// ============================================================\n// 校验函数\n// ============================================================\n\n/**\n * 校验结果\n */\nexport interface ValidationResult {\n /** 是否有效 */\n valid: boolean;\n /** 错误列表 */\n errors: ValidationError[];\n}\n\n/**\n * 校验错误\n */\nexport interface ValidationError {\n /** 错误路径 */\n path: string;\n /** 错误消息 */\n message: string;\n /** JSON Schema 关键字 */\n keyword: string;\n /** 错误参数 */\n params: Record<string, unknown>;\n /** Schema 路径 */\n schemaPath: string;\n}\n\n/**\n * 将 Ajv 错误转换为校验错误\n */\nfunction toValidationErrors(errors: ErrorObject[] | null | undefined): ValidationError[] {\n if (!errors) return [];\n\n return errors.map(error => ({\n path: error.instancePath || '/',\n message: error.message || 'Unknown error',\n keyword: error.keyword,\n params: error.params as Record<string, unknown>,\n schemaPath: error.schemaPath,\n }));\n}\n\n/**\n * 通用校验函数\n */\nfunction validate(validator: ValidateFunction, data: unknown): ValidationResult {\n const valid = validator(data);\n return {\n valid,\n errors: toValidationErrors(validator.errors),\n };\n}\n\n// ============================================================\n// 导出的校验函数\n// ============================================================\n\n// 预编译校验函数\nconst _validatePageSchema = ajv.compile(pageSchema);\nconst _validateComponentMeta = ajv.compile(componentMetaSchema);\nconst _validateActionSpec = ajv.compile(actionSpecSchema);\nconst _validateDataQuerySpec = ajv.compile(dataQuerySpecSchema);\n\n/**\n * 校验页面 Schema(V2 Runtime Core)\n */\nexport function validatePageSchema(data: unknown): ValidationResult {\n return validate(_validatePageSchema, data);\n}\n\n/**\n * 校验组件元数据\n */\nexport function validateComponentMeta(data: unknown): ValidationResult {\n return validate(_validateComponentMeta, data);\n}\n\n/**\n * 校验动作规格\n */\nexport function validateActionSpec(data: unknown): ValidationResult {\n return validate(_validateActionSpec, data);\n}\n\n/**\n * 校验数据查询规格\n */\nexport function validateDataQuerySpec(data: unknown): ValidationResult {\n return validate(_validateDataQuerySpec, data);\n}\n\n// ============================================================\n// 高级校验功能\n// ============================================================\n\n/**\n * 批量校验结果\n */\nexport interface BatchValidationResult {\n /** 总数 */\n total: number;\n /** 通过数 */\n passed: number;\n /** 失败数 */\n failed: number;\n /** 详细结果 */\n results: {\n index: number;\n valid: boolean;\n errors: ValidationError[];\n }[];\n}\n\n/**\n * 批量校验\n */\nexport function batchValidate(\n validator: (data: unknown) => ValidationResult,\n items: unknown[],\n): BatchValidationResult {\n const results = items.map((item, index) => {\n const result = validator(item);\n return {\n index,\n valid: result.valid,\n errors: result.errors,\n };\n });\n\n const passed = results.filter(r => r.valid).length;\n\n return {\n total: items.length,\n passed,\n failed: items.length - passed,\n results,\n };\n}\n\n/**\n * 校验并抛出异常\n */\nexport function validateOrThrow(\n validator: (data: unknown) => ValidationResult,\n data: unknown,\n errorMessage?: string,\n): void {\n const result = validator(data);\n if (!result.valid) {\n const message = errorMessage || 'Validation failed';\n const details = result.errors.map(e => `${e.path}: ${e.message}`).join('; ');\n throw new Error(`${message}: ${details}`);\n }\n}\n\n// ============================================================\n// Schema 引用校验\n// ============================================================\n\n/**\n * 校验组件节点树中引用的组件版本\n */\nexport function validateComponentReferences(\n pageSchema: unknown,\n availableComponents: Map<string, Set<string>>,\n): ValidationResult {\n const errors: ValidationError[] = [];\n\n function traverse(node: Record<string, unknown>, path: string): void {\n const componentType = node.componentType as string;\n const componentVersion = node.componentVersion as string;\n\n if (componentType && componentVersion) {\n const versions = availableComponents.get(componentType);\n if (!versions) {\n errors.push({\n path: `${path}/componentType`,\n message: `Component \"${componentType}\" not found in registry`,\n keyword: 'componentReference',\n params: { componentType },\n schemaPath: '',\n });\n } else if (!versions.has(componentVersion)) {\n errors.push({\n path: `${path}/componentVersion`,\n message: `Version \"${componentVersion}\" of component \"${componentType}\" not found`,\n keyword: 'componentVersion',\n params: { componentType, componentVersion },\n schemaPath: '',\n });\n }\n }\n\n // 递归遍历子节点\n const children = node.children as Record<string, unknown>[] | undefined;\n if (Array.isArray(children)) {\n children.forEach((child, index) => {\n traverse(child, `${path}/children/${index}`);\n });\n }\n }\n\n const schema = pageSchema as Record<string, unknown>;\n const root = schema.root as Record<string, unknown>;\n if (root) {\n traverse(root, '/root');\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * 校验表达式引用\n *\n * @description\n * V2 版本更新:支持 state、binding、computed 三种表达式类型的引用校验\n */\nexport function validateExpressionReferences(\n pageSchema: unknown,\n availableFields: Set<string>,\n): ValidationResult {\n const errors: ValidationError[] = [];\n\n function checkExpression(expr: Record<string, unknown>, path: string): void {\n const meta = expr.meta as Record<string, unknown> | undefined;\n const dependencies = meta?.dependencies as string[] | undefined;\n\n if (dependencies) {\n for (const dep of dependencies) {\n if (!availableFields.has(dep)) {\n errors.push({\n path,\n message: `Expression references unknown field: \"${dep}\"`,\n keyword: 'expressionReference',\n params: { field: dep },\n schemaPath: '',\n });\n }\n }\n }\n }\n\n function traverse(obj: unknown, path: string): void {\n if (!obj || typeof obj !== 'object') return;\n\n const record = obj as Record<string, unknown>;\n\n // 检查是否是表达式对象(V2 版本支持 state, binding, computed)\n if (\n record.type &&\n record.value &&\n ['state', 'binding', 'computed'].includes(record.type as string)\n ) {\n checkExpression(record, path);\n }\n\n // 递归遍历\n for (const [key, value] of Object.entries(record)) {\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n traverse(item, `${path}/${key}/${index}`);\n });\n } else if (typeof value === 'object' && value !== null) {\n traverse(value, `${path}/${key}`);\n }\n }\n }\n\n traverse(pageSchema, '');\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * 校验数据绑定配置\n *\n * @description\n * V2 新增:校验 dataBindings 中的 targetState 是否在 state.fields 中定义\n */\nexport function validateDataBindingReferences(pageSchema: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const schema = pageSchema as Record<string, unknown>;\n\n // 获取所有状态字段\n const state = schema.state as Record<string, unknown> | undefined;\n const fields = state?.fields as Record<string, unknown> | undefined;\n const stateFields = new Set(fields ? Object.keys(fields) : []);\n\n // 校验数据绑定的 targetState\n const dataBindings = schema.dataBindings as Record<string, unknown>[] | undefined;\n if (Array.isArray(dataBindings)) {\n dataBindings.forEach((binding, index) => {\n const targetState = binding.targetState as string;\n if (targetState && !stateFields.has(targetState)) {\n errors.push({\n path: `/dataBindings/${index}/targetState`,\n message: `Data binding targetState \"${targetState}\" not found in state.fields`,\n keyword: 'dataBindingReference',\n params: { targetState },\n schemaPath: '',\n });\n }\n });\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * 校验动作引用\n *\n * @description\n * V2 新增:校验 ActionRef 必须指定 actionDefinitionVersionId 或 builtinAction 之一\n */\nexport function validateActionReferences(pageSchema: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n\n function checkActionRef(action: Record<string, unknown>, path: string): void {\n const hasDefinition = !!action.actionDefinitionVersionId;\n const hasBuiltin = !!action.builtinAction;\n\n if (!hasDefinition && !hasBuiltin) {\n errors.push({\n path,\n message: 'ActionRef must specify either actionDefinitionVersionId or builtinAction',\n keyword: 'actionReference',\n params: {},\n schemaPath: '',\n });\n }\n\n // 递归检查 onSuccess 和 onError\n const onSuccess = action.onSuccess as Record<string, unknown>[] | undefined;\n if (Array.isArray(onSuccess)) {\n onSuccess.forEach((a, i) => checkActionRef(a, `${path}/onSuccess/${i}`));\n }\n\n const onError = action.onError as Record<string, unknown>[] | undefined;\n if (Array.isArray(onError)) {\n onError.forEach((a, i) => checkActionRef(a, `${path}/onError/${i}`));\n }\n }\n\n function traverseActions(obj: unknown, path: string): void {\n if (!obj || typeof obj !== 'object') return;\n\n const record = obj as Record<string, unknown>;\n\n // 检查事件处理器中的动作\n const eventHandlers = record.eventHandlers as Record<string, unknown>[] | undefined;\n if (Array.isArray(eventHandlers)) {\n eventHandlers.forEach((handler, hIndex) => {\n const actions = handler.actions as Record<string, unknown>[] | undefined;\n if (Array.isArray(actions)) {\n actions.forEach((action, aIndex) => {\n checkActionRef(action, `${path}/eventHandlers/${hIndex}/actions/${aIndex}`);\n });\n }\n });\n }\n\n // 检查生命周期钩子中的动作\n const lifecycle = record.lifecycle as Record<string, unknown> | undefined;\n if (lifecycle) {\n for (const [hookName, actions] of Object.entries(lifecycle)) {\n if (Array.isArray(actions)) {\n (actions as Record<string, unknown>[]).forEach((action, index) => {\n checkActionRef(action, `${path}/lifecycle/${hookName}/${index}`);\n });\n }\n }\n }\n\n // 递归遍历子节点\n const children = record.children as Record<string, unknown>[] | undefined;\n if (Array.isArray(children)) {\n children.forEach((child, index) => {\n traverseActions(child, `${path}/children/${index}`);\n });\n }\n }\n\n const schema = pageSchema as Record<string, unknown>;\n\n // 从页面级生命周期开始检查\n const lifecycle = schema.lifecycle as Record<string, unknown> | undefined;\n if (lifecycle) {\n for (const [hookName, actions] of Object.entries(lifecycle)) {\n if (Array.isArray(actions)) {\n (actions as Record<string, unknown>[]).forEach((action, index) => {\n checkActionRef(action, `/lifecycle/${hookName}/${index}`);\n });\n }\n }\n }\n\n // 从根节点开始遍历组件树\n const root = schema.root as Record<string, unknown>;\n if (root) {\n traverseActions(root, '/root');\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * 完整校验(结构 + 引用)\n *\n * @description\n * 执行完整的页面 Schema 校验,包括:\n * 1. JSON Schema 结构校验\n * 2. 组件引用校验\n * 3. 表达式引用校验\n * 4. 数据绑定引用校验\n * 5. 动作引用校验\n */\nexport function validatePageSchemaFull(\n data: unknown,\n availableComponents?: Map<string, Set<string>>,\n availableFields?: Set<string>,\n): ValidationResult {\n const allErrors: ValidationError[] = [];\n\n // 1. 结构校验\n const structureResult = validatePageSchema(data);\n allErrors.push(...structureResult.errors);\n\n // 如果结构校验失败,后续校验可能不准确,直接返回\n if (!structureResult.valid) {\n return { valid: false, errors: allErrors };\n }\n\n // 2. 组件引用校验(如果提供了可用组件列表)\n if (availableComponents) {\n const componentResult = validateComponentReferences(data, availableComponents);\n allErrors.push(...componentResult.errors);\n }\n\n // 3. 表达式引用校验(如果提供了可用字段列表)\n if (availableFields) {\n const expressionResult = validateExpressionReferences(data, availableFields);\n allErrors.push(...expressionResult.errors);\n }\n\n // 4. 数据绑定引用校验\n const bindingResult = validateDataBindingReferences(data);\n allErrors.push(...bindingResult.errors);\n\n // 5. 动作引用校验\n const actionResult = validateActionReferences(data);\n allErrors.push(...actionResult.errors);\n\n return {\n valid: allErrors.length === 0,\n errors: allErrors,\n };\n}\n\n// ============================================================\n// 导出版本信息\n// ============================================================\n\nexport const VERSION = '2.0.0';\n\n// 重新导出 Schema\nexport {\n pageSchema,\n componentMetaSchema,\n actionSpecSchema,\n dataQuerySpecSchema,\n} from '@djvlc/contracts-schemas';\n"],"mappings":"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","names":["pageSchema","componentMetaSchema","actionSpecSchema","dataQuerySpecSchema","ajv","lifecycle"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djvlc/contracts-validators",
3
- "version": "1.3.1",
3
+ "version": "1.4.1",
4
4
  "description": "DJV Low-code Platform - 预编译的 Ajv 校验器包",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -18,16 +18,13 @@
18
18
  "scripts": {
19
19
  "build": "tsup src/index.ts --format cjs,esm --dts --clean",
20
20
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
21
- "test": "vitest run",
22
- "test:watch": "vitest",
23
- "test:coverage": "vitest run --coverage",
24
21
  "lint": "eslint src/ --ext .ts",
25
22
  "typecheck": "tsc --noEmit",
26
23
  "clean": "rimraf dist"
27
24
  },
28
25
  "dependencies": {
29
- "@djvlc/contracts-types": "1.5.0",
30
- "@djvlc/contracts-schemas": "1.3.0",
26
+ "@djvlc/contracts-types": "1.7.0",
27
+ "@djvlc/contracts-schemas": "1.4.0",
31
28
  "ajv": "^8.12.0",
32
29
  "ajv-formats": "^3.0.1"
33
30
  },
@@ -49,17 +46,17 @@
49
46
  "ajv",
50
47
  "djvlc"
51
48
  ],
52
- "author": "DJV Team",
49
+ "author": "DJVLC Team",
53
50
  "license": "MIT",
54
- "repository": {
55
- "type": "git",
56
- "url": "git+https://github.com/djvlc/contracts.git",
57
- "directory": "packages/contracts-validators"
58
- },
59
51
  "publishConfig": {
60
52
  "access": "public",
61
53
  "registry": "https://registry.npmjs.org/"
62
54
  },
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "git+https://github.com/djvlc/contracts.git",
58
+ "directory": "packages/validators"
59
+ },
63
60
  "engines": {
64
61
  "node": ">=18.0.0"
65
62
  }