@open-feishu-card/validator 0.1.1 → 0.1.3
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.cjs +92 -0
- package/dist/index.js +76 -93
- package/package.json +9 -6
- package/dist/index.js.map +0 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var parser = require('@open-feishu-card/parser');
|
|
4
|
+
var schema = require('@open-feishu-card/schema');
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
function findNodeAtPointer(ast, pointer) {
|
|
8
|
+
let node = ast;
|
|
9
|
+
for (const seg of pointer) {
|
|
10
|
+
if (node.type === "object") {
|
|
11
|
+
const prop = node.properties.find((p) => p.key.value === String(seg));
|
|
12
|
+
if (!prop) return void 0;
|
|
13
|
+
node = prop.value;
|
|
14
|
+
} else if (node.type === "array") {
|
|
15
|
+
const item = node.items[seg];
|
|
16
|
+
if (!item) return void 0;
|
|
17
|
+
node = item;
|
|
18
|
+
} else {
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return node;
|
|
23
|
+
}
|
|
24
|
+
function pointerToString(path) {
|
|
25
|
+
if (path.length === 0) return "/";
|
|
26
|
+
return "/" + path.map((s) => String(s).replace(/~/g, "~0").replace(/\//g, "~1")).join("/");
|
|
27
|
+
}
|
|
28
|
+
var DEPRECATED_PROPS = {
|
|
29
|
+
// 常见 1.0 → 2.0 迁移期废弃写法
|
|
30
|
+
i18n_elements: "2.0 \u8BF7\u4F7F\u7528 body.elements + config.locale",
|
|
31
|
+
card_link: "2.0 \u8BF7\u4F7F\u7528 elements \u4E2D\u7684 button/open_url \u884C\u4E3A"
|
|
32
|
+
};
|
|
33
|
+
function validateAst(result, options = {}) {
|
|
34
|
+
const diagnostics = [];
|
|
35
|
+
for (const e of result.errors) {
|
|
36
|
+
diagnostics.push({
|
|
37
|
+
message: `JSON \u8BED\u6CD5\u9519\u8BEF: ${e.message}`,
|
|
38
|
+
severity: "error",
|
|
39
|
+
range: e.range,
|
|
40
|
+
path: "/"
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (!result.ast || result.data === void 0) return diagnostics;
|
|
44
|
+
const checkResult = schema.FeishuCardV2Schema.safeParse(result.data);
|
|
45
|
+
if (!checkResult.success) {
|
|
46
|
+
for (const issue of checkResult.error.issues) {
|
|
47
|
+
const node = findNodeAtPointer(result.ast, issue.path);
|
|
48
|
+
const range = node ? node.range : { start: result.ast.range.start, end: result.ast.range.start };
|
|
49
|
+
diagnostics.push({
|
|
50
|
+
message: issue.message,
|
|
51
|
+
severity: "error",
|
|
52
|
+
range,
|
|
53
|
+
path: pointerToString(issue.path)
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const deprecated = { ...DEPRECATED_PROPS, ...options.deprecatedProps ?? {} };
|
|
58
|
+
for (const prop of result.ast.properties) {
|
|
59
|
+
const hint = deprecated[prop.key.value];
|
|
60
|
+
if (hint) {
|
|
61
|
+
diagnostics.push({
|
|
62
|
+
message: `\u5E9F\u5F03\u5C5E\u6027 "${prop.key.value}": ${hint}`,
|
|
63
|
+
severity: "warning",
|
|
64
|
+
range: prop.key.range,
|
|
65
|
+
path: "/" + prop.key.value,
|
|
66
|
+
code: "deprecated-prop"
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return diagnostics;
|
|
71
|
+
}
|
|
72
|
+
function validate(source, options) {
|
|
73
|
+
return validateAst(parser.parse(source), options);
|
|
74
|
+
}
|
|
75
|
+
function validateData(data) {
|
|
76
|
+
const checkResult = schema.FeishuCardV2Schema.safeParse(data);
|
|
77
|
+
if (checkResult.success) return [];
|
|
78
|
+
return checkResult.error.issues.map((issue) => ({
|
|
79
|
+
message: issue.message,
|
|
80
|
+
severity: "error",
|
|
81
|
+
range: {
|
|
82
|
+
start: { offset: 0, line: 1, column: 1 },
|
|
83
|
+
end: { offset: 0, line: 1, column: 1 }
|
|
84
|
+
},
|
|
85
|
+
path: pointerToString(issue.path)
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
exports.findNodeAtPointer = findNodeAtPointer;
|
|
90
|
+
exports.validate = validate;
|
|
91
|
+
exports.validateAst = validateAst;
|
|
92
|
+
exports.validateData = validateData;
|
package/dist/index.js
CHANGED
|
@@ -1,104 +1,87 @@
|
|
|
1
|
-
|
|
2
|
-
* @open-feishu-card/validator
|
|
3
|
-
*
|
|
4
|
-
* 把 Zod Schema 2.0 的校验结果映射回 parser 产出的 AST 节点,
|
|
5
|
-
* 让每条诊断都精确定位到源码的 Line/Column/Range(字符级)。
|
|
6
|
-
*/
|
|
7
|
-
import { parse, } from '@open-feishu-card/parser';
|
|
1
|
+
import { parse } from '@open-feishu-card/parser';
|
|
8
2
|
import { FeishuCardV2Schema } from '@open-feishu-card/schema';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
function findNodeAtPointer(ast, pointer) {
|
|
6
|
+
let node = ast;
|
|
7
|
+
for (const seg of pointer) {
|
|
8
|
+
if (node.type === "object") {
|
|
9
|
+
const prop = node.properties.find((p) => p.key.value === String(seg));
|
|
10
|
+
if (!prop) return void 0;
|
|
11
|
+
node = prop.value;
|
|
12
|
+
} else if (node.type === "array") {
|
|
13
|
+
const item = node.items[seg];
|
|
14
|
+
if (!item) return void 0;
|
|
15
|
+
node = item;
|
|
16
|
+
} else {
|
|
17
|
+
return void 0;
|
|
28
18
|
}
|
|
29
|
-
|
|
19
|
+
}
|
|
20
|
+
return node;
|
|
30
21
|
}
|
|
31
22
|
function pointerToString(path) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return '/' + path.map((s) => String(s).replace(/~/g, '~0').replace(/\//g, '~1')).join('/');
|
|
23
|
+
if (path.length === 0) return "/";
|
|
24
|
+
return "/" + path.map((s) => String(s).replace(/~/g, "~0").replace(/\//g, "~1")).join("/");
|
|
35
25
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
var DEPRECATED_PROPS = {
|
|
27
|
+
// 常见 1.0 → 2.0 迁移期废弃写法
|
|
28
|
+
i18n_elements: "2.0 \u8BF7\u4F7F\u7528 body.elements + config.locale",
|
|
29
|
+
card_link: "2.0 \u8BF7\u4F7F\u7528 elements \u4E2D\u7684 button/open_url \u884C\u4E3A"
|
|
40
30
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
severity: 'error',
|
|
64
|
-
range,
|
|
65
|
-
path: pointerToString(issue.path),
|
|
66
|
-
});
|
|
67
|
-
}
|
|
31
|
+
function validateAst(result, options = {}) {
|
|
32
|
+
const diagnostics = [];
|
|
33
|
+
for (const e of result.errors) {
|
|
34
|
+
diagnostics.push({
|
|
35
|
+
message: `JSON \u8BED\u6CD5\u9519\u8BEF: ${e.message}`,
|
|
36
|
+
severity: "error",
|
|
37
|
+
range: e.range,
|
|
38
|
+
path: "/"
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (!result.ast || result.data === void 0) return diagnostics;
|
|
42
|
+
const checkResult = FeishuCardV2Schema.safeParse(result.data);
|
|
43
|
+
if (!checkResult.success) {
|
|
44
|
+
for (const issue of checkResult.error.issues) {
|
|
45
|
+
const node = findNodeAtPointer(result.ast, issue.path);
|
|
46
|
+
const range = node ? node.range : { start: result.ast.range.start, end: result.ast.range.start };
|
|
47
|
+
diagnostics.push({
|
|
48
|
+
message: issue.message,
|
|
49
|
+
severity: "error",
|
|
50
|
+
range,
|
|
51
|
+
path: pointerToString(issue.path)
|
|
52
|
+
});
|
|
68
53
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
54
|
+
}
|
|
55
|
+
const deprecated = { ...DEPRECATED_PROPS, ...options.deprecatedProps ?? {} };
|
|
56
|
+
for (const prop of result.ast.properties) {
|
|
57
|
+
const hint = deprecated[prop.key.value];
|
|
58
|
+
if (hint) {
|
|
59
|
+
diagnostics.push({
|
|
60
|
+
message: `\u5E9F\u5F03\u5C5E\u6027 "${prop.key.value}": ${hint}`,
|
|
61
|
+
severity: "warning",
|
|
62
|
+
range: prop.key.range,
|
|
63
|
+
path: "/" + prop.key.value,
|
|
64
|
+
code: "deprecated-prop"
|
|
65
|
+
});
|
|
82
66
|
}
|
|
83
|
-
|
|
67
|
+
}
|
|
68
|
+
return diagnostics;
|
|
84
69
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return validateAst(parse(source), options);
|
|
70
|
+
function validate(source, options) {
|
|
71
|
+
return validateAst(parse(source), options);
|
|
88
72
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
path: pointerToString(issue.path),
|
|
102
|
-
}));
|
|
73
|
+
function validateData(data) {
|
|
74
|
+
const checkResult = FeishuCardV2Schema.safeParse(data);
|
|
75
|
+
if (checkResult.success) return [];
|
|
76
|
+
return checkResult.error.issues.map((issue) => ({
|
|
77
|
+
message: issue.message,
|
|
78
|
+
severity: "error",
|
|
79
|
+
range: {
|
|
80
|
+
start: { offset: 0, line: 1, column: 1 },
|
|
81
|
+
end: { offset: 0, line: 1, column: 1 }
|
|
82
|
+
},
|
|
83
|
+
path: pointerToString(issue.path)
|
|
84
|
+
}));
|
|
103
85
|
}
|
|
104
|
-
|
|
86
|
+
|
|
87
|
+
export { findNodeAtPointer, validate, validateAst, validateData };
|
package/package.json
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-feishu-card/validator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "对比 AST 与 Schema 2.0,输出精确定位到源码字符级的诊断信息",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
7
8
|
"types": "./dist/index.d.ts",
|
|
8
9
|
"exports": {
|
|
9
10
|
".": {
|
|
10
11
|
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
12
14
|
}
|
|
13
15
|
},
|
|
14
16
|
"files": [
|
|
15
17
|
"dist"
|
|
16
18
|
],
|
|
17
19
|
"dependencies": {
|
|
18
|
-
"@open-feishu-card/parser": "0.1.
|
|
19
|
-
"@open-feishu-card/schema": "0.1.
|
|
20
|
+
"@open-feishu-card/parser": "0.1.2",
|
|
21
|
+
"@open-feishu-card/schema": "0.1.3"
|
|
20
22
|
},
|
|
21
23
|
"devDependencies": {
|
|
24
|
+
"tsup": "^8.3.5",
|
|
22
25
|
"tsx": "^4.19.3",
|
|
23
26
|
"typescript": "^7.0.2",
|
|
24
27
|
"@types/node": "^22.20.1"
|
|
@@ -33,7 +36,7 @@
|
|
|
33
36
|
"directory": "packages/validator"
|
|
34
37
|
},
|
|
35
38
|
"scripts": {
|
|
36
|
-
"build": "tsc -p tsconfig.json",
|
|
39
|
+
"build": "tsup && tsc -p tsconfig.json --emitDeclarationOnly",
|
|
37
40
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
38
41
|
"test": "tsx --test test/*.test.ts"
|
|
39
42
|
}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|