@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 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
- /** 按 Zod issue 的 path 在 AST 中查找对应节点 */
10
- export function findNodeAtPointer(ast, pointer) {
11
- let node = ast;
12
- for (const seg of pointer) {
13
- if (node.type === 'object') {
14
- const prop = node.properties.find((p) => p.key.value === String(seg));
15
- if (!prop)
16
- return undefined;
17
- node = prop.value;
18
- }
19
- else if (node.type === 'array') {
20
- const item = node.items[seg];
21
- if (!item)
22
- return undefined;
23
- node = item;
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
- return node;
19
+ }
20
+ return node;
30
21
  }
31
22
  function pointerToString(path) {
32
- if (path.length === 0)
33
- return '/';
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
- const DEPRECATED_PROPS = {
37
- // 常见 1.0 → 2.0 迁移期废弃写法
38
- i18n_elements: '2.0 请使用 body.elements + config.locale',
39
- card_link: '2.0 请使用 elements 中的 button/open_url 行为',
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
- /** 对已解析结果做 Schema 校验并生成诊断 */
42
- export function validateAst(result, options = {}) {
43
- const diagnostics = [];
44
- for (const e of result.errors) {
45
- diagnostics.push({
46
- message: `JSON 语法错误: ${e.message}`,
47
- severity: 'error',
48
- range: e.range,
49
- path: '/',
50
- });
51
- }
52
- if (!result.ast || result.data === undefined)
53
- return diagnostics;
54
- const checkResult = FeishuCardV2Schema.safeParse(result.data);
55
- if (!checkResult.success) {
56
- for (const issue of checkResult.error.issues) {
57
- const node = findNodeAtPointer(result.ast, issue.path);
58
- const range = node
59
- ? node.range
60
- : { start: result.ast.range.start, end: result.ast.range.start };
61
- diagnostics.push({
62
- message: issue.message,
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
- // 废弃属性检查(warning 级别,只针对卡片根级字段)
70
- const deprecated = { ...DEPRECATED_PROPS, ...(options.deprecatedProps ?? {}) };
71
- for (const prop of result.ast.properties) {
72
- const hint = deprecated[prop.key.value];
73
- if (hint) {
74
- diagnostics.push({
75
- message: `废弃属性 "${prop.key.value}": ${hint}`,
76
- severity: 'warning',
77
- range: prop.key.range,
78
- path: '/' + prop.key.value,
79
- code: 'deprecated-prop',
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
- return diagnostics;
67
+ }
68
+ return diagnostics;
84
69
  }
85
- /** 一步到位:源码 → 解析 + 校验 */
86
- export function validate(source, options) {
87
- return validateAst(parse(source), options);
70
+ function validate(source, options) {
71
+ return validateAst(parse(source), options);
88
72
  }
89
- /** 校验普通 JS 对象(无位置信息时 range 退化为 {0,0}) */
90
- export function validateData(data) {
91
- const checkResult = FeishuCardV2Schema.safeParse(data);
92
- if (checkResult.success)
93
- return [];
94
- return checkResult.error.issues.map((issue) => ({
95
- message: issue.message,
96
- severity: 'error',
97
- range: {
98
- start: { offset: 0, line: 1, column: 1 },
99
- end: { offset: 0, line: 1, column: 1 },
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
- //# sourceMappingURL=index.js.map
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.1",
3
+ "version": "0.1.3",
4
4
  "description": "对比 AST 与 Schema 2.0,输出精确定位到源码字符级的诊断信息",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
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.1",
19
- "@open-feishu-card/schema": "0.1.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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"}