@djvlc/contracts-validators 1.3.1 → 1.4.0

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.js CHANGED
@@ -30,228 +30,331 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- ajv: () => ajv,
34
- assertActionDefinition: () => assertActionDefinition,
35
- assertActionRequest: () => assertActionRequest,
36
- assertComponentMeta: () => assertComponentMeta,
37
- assertDataQueryDefinition: () => assertDataQueryDefinition,
38
- assertPageSchema: () => assertPageSchema,
39
- assertQueryRequest: () => assertQueryRequest,
40
- isActionDefinition: () => isActionDefinition,
41
- isActionRequest: () => isActionRequest,
42
- isComponentMeta: () => isComponentMeta,
43
- isDataQueryDefinition: () => isDataQueryDefinition,
44
- isPageSchema: () => isPageSchema,
45
- isQueryRequest: () => isQueryRequest,
46
- validateActionDefinition: () => validateActionDefinition,
47
- validateActionRequest: () => validateActionRequest,
33
+ VERSION: () => VERSION,
34
+ actionSpecSchema: () => import_contracts_schemas2.actionSpecSchema,
35
+ batchValidate: () => batchValidate,
36
+ componentMetaSchema: () => import_contracts_schemas2.componentMetaSchema,
37
+ dataQuerySpecSchema: () => import_contracts_schemas2.dataQuerySpecSchema,
38
+ pageSchema: () => import_contracts_schemas2.pageSchema,
39
+ validateActionReferences: () => validateActionReferences,
40
+ validateActionSpec: () => validateActionSpec,
48
41
  validateComponentMeta: () => validateComponentMeta,
49
- validateComponentMetas: () => validateComponentMetas,
50
- validateDataQueryDefinition: () => validateDataQueryDefinition,
51
- validatePage: () => validatePage,
42
+ validateComponentReferences: () => validateComponentReferences,
43
+ validateDataBindingReferences: () => validateDataBindingReferences,
44
+ validateDataQuerySpec: () => validateDataQuerySpec,
45
+ validateExpressionReferences: () => validateExpressionReferences,
46
+ validateOrThrow: () => validateOrThrow,
52
47
  validatePageSchema: () => validatePageSchema,
53
- validatePages: () => validatePages,
54
- validateQueryRequest: () => validateQueryRequest
48
+ validatePageSchemaFull: () => validatePageSchemaFull
55
49
  });
56
50
  module.exports = __toCommonJS(index_exports);
57
51
  var import_ajv = __toESM(require("ajv"));
58
52
  var import_ajv_formats = __toESM(require("ajv-formats"));
59
53
  var import_contracts_schemas = require("@djvlc/contracts-schemas");
60
- var ajv = new import_ajv.default({
61
- allErrors: true,
62
- verbose: true,
63
- strict: false
64
- });
65
- (0, import_ajv_formats.default)(ajv);
66
- var pageValidator = ajv.compile(import_contracts_schemas.pageSchema);
67
- var componentMetaValidator = ajv.compile(import_contracts_schemas.componentMetaSchema);
68
- var actionSpecValidator = ajv.compile(import_contracts_schemas.actionSpecSchema);
69
- var dataQuerySpecValidator = ajv.compile(import_contracts_schemas.dataQuerySpecSchema);
70
- function convertAjvErrors(errors) {
54
+ var import_contracts_schemas2 = require("@djvlc/contracts-schemas");
55
+ function createAjv() {
56
+ const ajv2 = new import_ajv.default({
57
+ strict: true,
58
+ allErrors: true,
59
+ verbose: true,
60
+ removeAdditional: false,
61
+ useDefaults: true,
62
+ coerceTypes: false,
63
+ allowUnionTypes: true
64
+ });
65
+ (0, import_ajv_formats.default)(ajv2);
66
+ ajv2.addFormat("semver", /^\d+\.\d+\.\d+$/);
67
+ ajv2.addFormat("sha384", /^sha384-[A-Za-z0-9+/=]+$/);
68
+ return ajv2;
69
+ }
70
+ var ajv = createAjv();
71
+ ajv.addSchema(import_contracts_schemas.pageSchema, import_contracts_schemas.pageSchema.$id);
72
+ ajv.addSchema(import_contracts_schemas.componentMetaSchema, import_contracts_schemas.componentMetaSchema.$id);
73
+ ajv.addSchema(import_contracts_schemas.actionSpecSchema, import_contracts_schemas.actionSpecSchema.$id);
74
+ ajv.addSchema(import_contracts_schemas.dataQuerySpecSchema, import_contracts_schemas.dataQuerySpecSchema.$id);
75
+ function toValidationErrors(errors) {
71
76
  if (!errors) return [];
72
77
  return errors.map((error) => ({
73
78
  path: error.instancePath || "/",
74
79
  message: error.message || "Unknown error",
75
80
  keyword: error.keyword,
76
- expected: error.params,
77
- actual: error.data
81
+ params: error.params,
82
+ schemaPath: error.schemaPath
78
83
  }));
79
84
  }
80
- function createResult(valid, errors) {
85
+ function validate(validator, data) {
86
+ const valid = validator(data);
81
87
  return {
82
88
  valid,
83
- errors: convertAjvErrors(errors)
89
+ errors: toValidationErrors(validator.errors)
84
90
  };
85
91
  }
86
- function validatePage(data) {
87
- const valid = pageValidator(data);
88
- return createResult(valid, pageValidator.errors);
89
- }
92
+ var _validatePageSchema = ajv.compile(import_contracts_schemas.pageSchema);
93
+ var _validateComponentMeta = ajv.compile(import_contracts_schemas.componentMetaSchema);
94
+ var _validateActionSpec = ajv.compile(import_contracts_schemas.actionSpecSchema);
95
+ var _validateDataQuerySpec = ajv.compile(import_contracts_schemas.dataQuerySpecSchema);
90
96
  function validatePageSchema(data) {
91
- const result = validatePage(data);
92
- if (result.valid) {
93
- return { ...result, data };
94
- }
95
- return result;
96
- }
97
- function assertPageSchema(data) {
98
- const result = validatePage(data);
99
- if (!result.valid) {
100
- throw new Error(`Invalid PageSchema: ${result.errors.map((e) => e.message).join(", ")}`);
101
- }
97
+ return validate(_validatePageSchema, data);
102
98
  }
103
99
  function validateComponentMeta(data) {
104
- const valid = componentMetaValidator(data);
105
- return createResult(valid, componentMetaValidator.errors);
100
+ return validate(_validateComponentMeta, data);
106
101
  }
107
- function assertComponentMeta(data) {
108
- const result = validateComponentMeta(data);
109
- if (!result.valid) {
110
- throw new Error(`Invalid ComponentMeta: ${result.errors.map((e) => e.message).join(", ")}`);
111
- }
102
+ function validateActionSpec(data) {
103
+ return validate(_validateActionSpec, data);
112
104
  }
113
- function validateActionDefinition(data) {
114
- const valid = actionSpecValidator(data);
115
- return createResult(valid, actionSpecValidator.errors);
105
+ function validateDataQuerySpec(data) {
106
+ return validate(_validateDataQuerySpec, data);
107
+ }
108
+ function batchValidate(validator, items) {
109
+ const results = items.map((item, index) => {
110
+ const result = validator(item);
111
+ return {
112
+ index,
113
+ valid: result.valid,
114
+ errors: result.errors
115
+ };
116
+ });
117
+ const passed = results.filter((r) => r.valid).length;
118
+ return {
119
+ total: items.length,
120
+ passed,
121
+ failed: items.length - passed,
122
+ results
123
+ };
116
124
  }
117
- function assertActionDefinition(data) {
118
- const result = validateActionDefinition(data);
125
+ function validateOrThrow(validator, data, errorMessage) {
126
+ const result = validator(data);
119
127
  if (!result.valid) {
120
- throw new Error(`Invalid ActionDefinition: ${result.errors.map((e) => e.message).join(", ")}`);
128
+ const message = errorMessage || "Validation failed";
129
+ const details = result.errors.map((e) => `${e.path}: ${e.message}`).join("; ");
130
+ throw new Error(`${message}: ${details}`);
121
131
  }
122
132
  }
123
- function validateActionRequest(data) {
133
+ function validateComponentReferences(pageSchema3, availableComponents) {
124
134
  const errors = [];
125
- if (!data || typeof data !== "object") {
126
- errors.push({ path: "/", message: "ActionExecuteRequest must be an object" });
127
- return { valid: false, errors };
128
- }
129
- const obj = data;
130
- if (typeof obj.actionType !== "string" || !obj.actionType) {
131
- errors.push({ path: "/actionType", message: "actionType is required and must be a string" });
135
+ function traverse(node, path) {
136
+ const componentType = node.componentType;
137
+ const componentVersion = node.componentVersion;
138
+ if (componentType && componentVersion) {
139
+ const versions = availableComponents.get(componentType);
140
+ if (!versions) {
141
+ errors.push({
142
+ path: `${path}/componentType`,
143
+ message: `Component "${componentType}" not found in registry`,
144
+ keyword: "componentReference",
145
+ params: { componentType },
146
+ schemaPath: ""
147
+ });
148
+ } else if (!versions.has(componentVersion)) {
149
+ errors.push({
150
+ path: `${path}/componentVersion`,
151
+ message: `Version "${componentVersion}" of component "${componentType}" not found`,
152
+ keyword: "componentVersion",
153
+ params: { componentType, componentVersion },
154
+ schemaPath: ""
155
+ });
156
+ }
157
+ }
158
+ const children = node.children;
159
+ if (Array.isArray(children)) {
160
+ children.forEach((child, index) => {
161
+ traverse(child, `${path}/children/${index}`);
162
+ });
163
+ }
132
164
  }
133
- if (obj.params === void 0 || typeof obj.params !== "object") {
134
- errors.push({ path: "/params", message: "params is required and must be an object" });
165
+ const schema = pageSchema3;
166
+ const root = schema.root;
167
+ if (root) {
168
+ traverse(root, "/root");
135
169
  }
136
- if (obj.context === void 0 || typeof obj.context !== "object") {
137
- errors.push({ path: "/context", message: "context is required and must be an object" });
138
- }
139
- return { valid: errors.length === 0, errors };
170
+ return {
171
+ valid: errors.length === 0,
172
+ errors
173
+ };
140
174
  }
141
- function assertActionRequest(data) {
142
- const result = validateActionRequest(data);
143
- if (!result.valid) {
144
- throw new Error(`Invalid ActionExecuteRequest: ${result.errors.map((e) => e.message).join(", ")}`);
175
+ function validateExpressionReferences(pageSchema3, availableFields) {
176
+ const errors = [];
177
+ function checkExpression(expr, path) {
178
+ const meta = expr.meta;
179
+ const dependencies = meta?.dependencies;
180
+ if (dependencies) {
181
+ for (const dep of dependencies) {
182
+ if (!availableFields.has(dep)) {
183
+ errors.push({
184
+ path,
185
+ message: `Expression references unknown field: "${dep}"`,
186
+ keyword: "expressionReference",
187
+ params: { field: dep },
188
+ schemaPath: ""
189
+ });
190
+ }
191
+ }
192
+ }
145
193
  }
194
+ function traverse(obj, path) {
195
+ if (!obj || typeof obj !== "object") return;
196
+ const record = obj;
197
+ if (record.type && record.value && ["state", "binding", "computed"].includes(record.type)) {
198
+ checkExpression(record, path);
199
+ }
200
+ for (const [key, value] of Object.entries(record)) {
201
+ if (Array.isArray(value)) {
202
+ value.forEach((item, index) => {
203
+ traverse(item, `${path}/${key}/${index}`);
204
+ });
205
+ } else if (typeof value === "object" && value !== null) {
206
+ traverse(value, `${path}/${key}`);
207
+ }
208
+ }
209
+ }
210
+ traverse(pageSchema3, "");
211
+ return {
212
+ valid: errors.length === 0,
213
+ errors
214
+ };
146
215
  }
147
- function validateDataQueryDefinition(data) {
148
- const valid = dataQuerySpecValidator(data);
149
- return createResult(valid, dataQuerySpecValidator.errors);
150
- }
151
- function assertDataQueryDefinition(data) {
152
- const result = validateDataQueryDefinition(data);
153
- if (!result.valid) {
154
- throw new Error(`Invalid DataQueryDefinition: ${result.errors.map((e) => e.message).join(", ")}`);
216
+ function validateDataBindingReferences(pageSchema3) {
217
+ const errors = [];
218
+ const schema = pageSchema3;
219
+ const state = schema.state;
220
+ const fields = state?.fields;
221
+ const stateFields = new Set(fields ? Object.keys(fields) : []);
222
+ const dataBindings = schema.dataBindings;
223
+ if (Array.isArray(dataBindings)) {
224
+ dataBindings.forEach((binding, index) => {
225
+ const targetState = binding.targetState;
226
+ if (targetState && !stateFields.has(targetState)) {
227
+ errors.push({
228
+ path: `/dataBindings/${index}/targetState`,
229
+ message: `Data binding targetState "${targetState}" not found in state.fields`,
230
+ keyword: "dataBindingReference",
231
+ params: { targetState },
232
+ schemaPath: ""
233
+ });
234
+ }
235
+ });
155
236
  }
237
+ return {
238
+ valid: errors.length === 0,
239
+ errors
240
+ };
156
241
  }
157
- function validateQueryRequest(data) {
242
+ function validateActionReferences(pageSchema3) {
158
243
  const errors = [];
159
- if (!data || typeof data !== "object") {
160
- errors.push({ path: "/", message: "DataQueryRequest must be an object" });
161
- return { valid: false, errors };
244
+ function checkActionRef(action, path) {
245
+ const hasDefinition = !!action.actionDefinitionVersionId;
246
+ const hasBuiltin = !!action.builtinAction;
247
+ if (!hasDefinition && !hasBuiltin) {
248
+ errors.push({
249
+ path,
250
+ message: "ActionRef must specify either actionDefinitionVersionId or builtinAction",
251
+ keyword: "actionReference",
252
+ params: {},
253
+ schemaPath: ""
254
+ });
255
+ }
256
+ const onSuccess = action.onSuccess;
257
+ if (Array.isArray(onSuccess)) {
258
+ onSuccess.forEach((a, i) => checkActionRef(a, `${path}/onSuccess/${i}`));
259
+ }
260
+ const onError = action.onError;
261
+ if (Array.isArray(onError)) {
262
+ onError.forEach((a, i) => checkActionRef(a, `${path}/onError/${i}`));
263
+ }
162
264
  }
163
- const obj = data;
164
- if (typeof obj.queryVersionId !== "string" || !obj.queryVersionId) {
165
- errors.push({ path: "/queryVersionId", message: "queryVersionId is required and must be a string" });
265
+ function traverseActions(obj, path) {
266
+ if (!obj || typeof obj !== "object") return;
267
+ const record = obj;
268
+ const eventHandlers = record.eventHandlers;
269
+ if (Array.isArray(eventHandlers)) {
270
+ eventHandlers.forEach((handler, hIndex) => {
271
+ const actions = handler.actions;
272
+ if (Array.isArray(actions)) {
273
+ actions.forEach((action, aIndex) => {
274
+ checkActionRef(action, `${path}/eventHandlers/${hIndex}/actions/${aIndex}`);
275
+ });
276
+ }
277
+ });
278
+ }
279
+ const lifecycle2 = record.lifecycle;
280
+ if (lifecycle2) {
281
+ for (const [hookName, actions] of Object.entries(lifecycle2)) {
282
+ if (Array.isArray(actions)) {
283
+ actions.forEach((action, index) => {
284
+ checkActionRef(action, `${path}/lifecycle/${hookName}/${index}`);
285
+ });
286
+ }
287
+ }
288
+ }
289
+ const children = record.children;
290
+ if (Array.isArray(children)) {
291
+ children.forEach((child, index) => {
292
+ traverseActions(child, `${path}/children/${index}`);
293
+ });
294
+ }
166
295
  }
167
- if (obj.params === void 0 || typeof obj.params !== "object") {
168
- errors.push({ path: "/params", message: "params is required and must be an object" });
296
+ const schema = pageSchema3;
297
+ const lifecycle = schema.lifecycle;
298
+ if (lifecycle) {
299
+ for (const [hookName, actions] of Object.entries(lifecycle)) {
300
+ if (Array.isArray(actions)) {
301
+ actions.forEach((action, index) => {
302
+ checkActionRef(action, `/lifecycle/${hookName}/${index}`);
303
+ });
304
+ }
305
+ }
169
306
  }
170
- return { valid: errors.length === 0, errors };
171
- }
172
- function assertQueryRequest(data) {
173
- const result = validateQueryRequest(data);
174
- if (!result.valid) {
175
- throw new Error(`Invalid DataQueryRequest: ${result.errors.map((e) => e.message).join(", ")}`);
307
+ const root = schema.root;
308
+ if (root) {
309
+ traverseActions(root, "/root");
176
310
  }
177
- }
178
- function isPageSchema(value) {
179
- return validatePage(value).valid;
180
- }
181
- function isComponentMeta(value) {
182
- return validateComponentMeta(value).valid;
183
- }
184
- function isActionDefinition(value) {
185
- return validateActionDefinition(value).valid;
186
- }
187
- function isDataQueryDefinition(value) {
188
- return validateDataQueryDefinition(value).valid;
189
- }
190
- function isActionRequest(value) {
191
- return validateActionRequest(value).valid;
192
- }
193
- function isQueryRequest(value) {
194
- return validateQueryRequest(value).valid;
195
- }
196
- function validatePages(items) {
197
- const results = items.map((item, index) => {
198
- const result = validatePage(item);
199
- return {
200
- index,
201
- valid: result.valid,
202
- data: result.valid ? item : void 0,
203
- errors: result.errors
204
- };
205
- });
206
- const validCount = results.filter((r) => r.valid).length;
207
311
  return {
208
- allValid: validCount === items.length,
209
- validCount,
210
- invalidCount: items.length - validCount,
211
- results
312
+ valid: errors.length === 0,
313
+ errors
212
314
  };
213
315
  }
214
- function validateComponentMetas(items) {
215
- const results = items.map((item, index) => {
216
- const result = validateComponentMeta(item);
217
- return {
218
- index,
219
- valid: result.valid,
220
- data: result.valid ? item : void 0,
221
- errors: result.errors
222
- };
223
- });
224
- const validCount = results.filter((r) => r.valid).length;
316
+ function validatePageSchemaFull(data, availableComponents, availableFields) {
317
+ const allErrors = [];
318
+ const structureResult = validatePageSchema(data);
319
+ allErrors.push(...structureResult.errors);
320
+ if (!structureResult.valid) {
321
+ return { valid: false, errors: allErrors };
322
+ }
323
+ if (availableComponents) {
324
+ const componentResult = validateComponentReferences(data, availableComponents);
325
+ allErrors.push(...componentResult.errors);
326
+ }
327
+ if (availableFields) {
328
+ const expressionResult = validateExpressionReferences(data, availableFields);
329
+ allErrors.push(...expressionResult.errors);
330
+ }
331
+ const bindingResult = validateDataBindingReferences(data);
332
+ allErrors.push(...bindingResult.errors);
333
+ const actionResult = validateActionReferences(data);
334
+ allErrors.push(...actionResult.errors);
225
335
  return {
226
- allValid: validCount === items.length,
227
- validCount,
228
- invalidCount: items.length - validCount,
229
- results
336
+ valid: allErrors.length === 0,
337
+ errors: allErrors
230
338
  };
231
339
  }
340
+ var VERSION = "2.0.0";
232
341
  // Annotate the CommonJS export names for ESM import in node:
233
342
  0 && (module.exports = {
234
- ajv,
235
- assertActionDefinition,
236
- assertActionRequest,
237
- assertComponentMeta,
238
- assertDataQueryDefinition,
239
- assertPageSchema,
240
- assertQueryRequest,
241
- isActionDefinition,
242
- isActionRequest,
243
- isComponentMeta,
244
- isDataQueryDefinition,
245
- isPageSchema,
246
- isQueryRequest,
247
- validateActionDefinition,
248
- validateActionRequest,
343
+ VERSION,
344
+ actionSpecSchema,
345
+ batchValidate,
346
+ componentMetaSchema,
347
+ dataQuerySpecSchema,
348
+ pageSchema,
349
+ validateActionReferences,
350
+ validateActionSpec,
249
351
  validateComponentMeta,
250
- validateComponentMetas,
251
- validateDataQueryDefinition,
252
- validatePage,
352
+ validateComponentReferences,
353
+ validateDataBindingReferences,
354
+ validateDataQuerySpec,
355
+ validateExpressionReferences,
356
+ validateOrThrow,
253
357
  validatePageSchema,
254
- validatePages,
255
- validateQueryRequest
358
+ validatePageSchemaFull
256
359
  });
257
360
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -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":["Ajv","addFormats"]}
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":["import_contracts_schemas","ajv","Ajv","addFormats","pageSchema","lifecycle"]}