pdd-skills 3.0.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/README.md +1478 -0
- package/bin/pdd.js +354 -0
- package/config/bpmn-rules.yaml +166 -0
- package/config/checkstyle.xml +105 -0
- package/config/eslint.config.js +48 -0
- package/config/pmd.xml +91 -0
- package/config/prd-rules.yaml +113 -0
- package/config/ruff.toml +45 -0
- package/config/sqlfluff.cfg +82 -0
- package/hooks/hook-executor.js +332 -0
- package/index.js +43 -0
- package/lib/api-routes.js +750 -0
- package/lib/api-server.js +408 -0
- package/lib/cache/cache-config.js +209 -0
- package/lib/cache/system-cache.js +852 -0
- package/lib/config-manager.js +373 -0
- package/lib/generate.js +528 -0
- package/lib/grpc/grpc-routes.js +1134 -0
- package/lib/grpc/grpc-server.js +912 -0
- package/lib/grpc/proto-definitions.js +1033 -0
- package/lib/init.js +172 -0
- package/lib/iteration/auto-fixer.js +1025 -0
- package/lib/iteration/auto-reviewer.js +923 -0
- package/lib/iteration/controller.js +577 -0
- package/lib/list.js +130 -0
- package/lib/mcp-server.js +548 -0
- package/lib/openclaw/api-integration.js +535 -0
- package/lib/openclaw/cli-integration.js +567 -0
- package/lib/openclaw/data-sync.js +845 -0
- package/lib/openclaw/openclaw-adapter.js +783 -0
- package/lib/plugin/example-plugins/code-stats/index.js +332 -0
- package/lib/plugin/example-plugins/code-stats/plugin.json +1 -0
- package/lib/plugin/example-plugins/custom-linter/index.js +472 -0
- package/lib/plugin/example-plugins/custom-linter/plugin.json +1 -0
- package/lib/plugin/example-plugins/hello-world/index.js +86 -0
- package/lib/plugin/example-plugins/hello-world/plugin.json +1 -0
- package/lib/plugin/plugin-manager.js +655 -0
- package/lib/plugin/plugin-sdk.js +565 -0
- package/lib/plugin/sandbox.js +627 -0
- package/lib/quality/rules/maintainability.js +418 -0
- package/lib/quality/rules/performance.js +498 -0
- package/lib/quality/rules/readability.js +441 -0
- package/lib/quality/rules/robustness.js +504 -0
- package/lib/quality/rules/security.js +444 -0
- package/lib/quality/scorer.js +576 -0
- package/lib/report.js +669 -0
- package/lib/sdk-base.js +301 -0
- package/lib/sdk-js.js +446 -0
- package/lib/sdk-python/README.md +546 -0
- package/lib/sdk-python/examples/basic_usage.py +450 -0
- package/lib/sdk-python/pdd_sdk/__init__.py +180 -0
- package/lib/sdk-python/pdd_sdk/client.py +1170 -0
- package/lib/sdk-python/pdd_sdk/events.py +423 -0
- package/lib/sdk-python/pdd_sdk/exceptions.py +158 -0
- package/lib/sdk-python/pdd_sdk/models.py +518 -0
- package/lib/sdk-python/pdd_sdk/utils.py +759 -0
- package/lib/token/budget-alert.js +367 -0
- package/lib/token/budget-manager.js +485 -0
- package/lib/update.js +54 -0
- package/lib/utils/logger.js +88 -0
- package/lib/verify.js +741 -0
- package/lib/version.js +52 -0
- package/lib/vm/README.md +102 -0
- package/lib/vm/dashboard/api-routes.js +669 -0
- package/lib/vm/dashboard/server.js +391 -0
- package/lib/vm/dashboard/sse.js +358 -0
- package/lib/vm/dashboard/static/css/dashboard.css +1378 -0
- package/lib/vm/dashboard/static/index.html +118 -0
- package/lib/vm/dashboard/static/js/app.js +949 -0
- package/lib/vm/dashboard/static/js/charts.js +913 -0
- package/lib/vm/dashboard/static/js/kanban-view.js +1053 -0
- package/lib/vm/dashboard/static/js/pipeline-view.js +463 -0
- package/lib/vm/dashboard/static/js/quality-view.js +598 -0
- package/lib/vm/dashboard/static/js/system-view.js +1021 -0
- package/lib/vm/data-provider.js +1191 -0
- package/lib/vm/event-bus.js +402 -0
- package/lib/vm/hooks/extract-hook.js +307 -0
- package/lib/vm/hooks/generate-hook.js +374 -0
- package/lib/vm/hooks/hook-interface.js +458 -0
- package/lib/vm/hooks/report-hook.js +331 -0
- package/lib/vm/hooks/verify-hook.js +454 -0
- package/lib/vm/models.js +1003 -0
- package/lib/vm/reconciler.js +855 -0
- package/lib/vm/scanner.js +988 -0
- package/lib/vm/state-schema.js +955 -0
- package/lib/vm/state-store.js +733 -0
- package/lib/vm/tui/components/card.js +339 -0
- package/lib/vm/tui/components/progress-bar.js +368 -0
- package/lib/vm/tui/components/sparkline.js +327 -0
- package/lib/vm/tui/components/status-light.js +294 -0
- package/lib/vm/tui/components/table.js +370 -0
- package/lib/vm/tui/input.js +335 -0
- package/lib/vm/tui/renderer.js +548 -0
- package/lib/vm/tui/screens/kanban-screen.js +397 -0
- package/lib/vm/tui/screens/overview-screen.js +357 -0
- package/lib/vm/tui/screens/quality-screen.js +336 -0
- package/lib/vm/tui/screens/system-screen.js +379 -0
- package/lib/vm/tui/tui.js +805 -0
- package/package.json +1 -0
- package/scripts/cso-analyzer.js +198 -0
- package/scripts/eval-runner.js +359 -0
- package/scripts/i18n-checker.js +109 -0
- package/scripts/linter/activiti-linter.js +272 -0
- package/scripts/linter/prd-linter.js +162 -0
- package/scripts/linter/report-generator.js +207 -0
- package/scripts/linter/run-linters.js +285 -0
- package/scripts/linter/sql-linter.js +166 -0
- package/scripts/token-analyzer.js +162 -0
- package/scripts/vm-test.js +180 -0
- package/skills/core/official-doc-writer/LICENSE +21 -0
- package/skills/core/official-doc-writer/README.md +232 -0
- package/skills/core/official-doc-writer/SKILL.md +475 -0
- package/skills/core/official-doc-writer/_meta.json +1 -0
- package/skills/core/official-doc-writer/document_generator.py +580 -0
- package/skills/core/official-doc-writer/evals/default-evals.json +1 -0
- package/skills/core/official-doc-writer/examples.md +150 -0
- package/skills/core/official-doc-writer/fonts/FONTS_LIST.md +45 -0
- package/skills/core/official-doc-writer/fonts/README.md +141 -0
- package/skills/core/official-doc-writer/fonts/SIMFANG.TTF +0 -0
- package/skills/core/official-doc-writer/fonts/SIMHEI.TTF +0 -0
- package/skills/core/official-doc-writer/fonts/SIMKAI.TTF +0 -0
- package/skills/core/official-doc-writer/fonts/SIMSUN.TTC +0 -0
- package/skills/core/official-doc-writer/fonts//346/226/271/346/255/243/345/260/217/346/240/207/345/256/213GBK.TTF +0 -0
- package/skills/core/official-doc-writer/references/GBT_9704-2012_/345/205/232/346/224/277/346/234/272/345/205/263/345/205/254/346/226/207/346/240/274/345/274/217.md +422 -0
- package/skills/core/official-doc-writer/scripts/__pycache__/generate_official_doc.cpython-313.pyc +0 -0
- package/skills/core/official-doc-writer/scripts/dialog_manager.py +564 -0
- package/skills/core/official-doc-writer/scripts/generate_official_doc.py +252 -0
- package/skills/core/official-doc-writer/scripts/install_fonts.py +390 -0
- package/skills/core/official-doc-writer/scripts/smart_prompts.py +363 -0
- package/skills/core/pdd-ba/SKILL.md +305 -0
- package/skills/core/pdd-ba/_meta.json +1 -0
- package/skills/core/pdd-ba/evals/default-evals.json +1 -0
- package/skills/core/pdd-code-reviewer/SKILL.md +378 -0
- package/skills/core/pdd-code-reviewer/_meta.json +1 -0
- package/skills/core/pdd-code-reviewer/evals/default-evals.json +1 -0
- package/skills/core/pdd-doc-change/SKILL.md +350 -0
- package/skills/core/pdd-doc-change/_meta.json +1 -0
- package/skills/core/pdd-doc-change/evals/default-evals.json +1 -0
- package/skills/core/pdd-doc-gardener/SKILL.md +248 -0
- package/skills/core/pdd-doc-gardener/_meta.json +1 -0
- package/skills/core/pdd-doc-gardener/evals/default-evals.json +1 -0
- package/skills/core/pdd-entropy-reduction/SKILL.md +360 -0
- package/skills/core/pdd-entropy-reduction/_meta.json +1 -0
- package/skills/core/pdd-entropy-reduction/evals/default-evals.json +1 -0
- package/skills/core/pdd-entropy-reduction/references/entropy-report-template.md +287 -0
- package/skills/core/pdd-entropy-reduction/references/golden-principles.md +573 -0
- package/skills/core/pdd-entropy-reduction/scripts/entropy_scan.py +712 -0
- package/skills/core/pdd-extract-features/SKILL.md +320 -0
- package/skills/core/pdd-extract-features/_meta.json +1 -0
- package/skills/core/pdd-extract-features/evals/default-evals.json +1 -0
- package/skills/core/pdd-generate-spec/SKILL.md +418 -0
- package/skills/core/pdd-generate-spec/_meta.json +1 -0
- package/skills/core/pdd-generate-spec/evals/default-evals.json +1 -0
- package/skills/core/pdd-implement-feature/SKILL.md +332 -0
- package/skills/core/pdd-implement-feature/_meta.json +1 -0
- package/skills/core/pdd-implement-feature/evals/default-evals.json +1 -0
- package/skills/core/pdd-main/SKILL.md +540 -0
- package/skills/core/pdd-main/_meta.json +1 -0
- package/skills/core/pdd-main/evals/default-evals.json +1 -0
- package/skills/core/pdd-main/evals/evals.json +215 -0
- package/skills/core/pdd-verify-feature/SKILL.md +474 -0
- package/skills/core/pdd-verify-feature/_meta.json +1 -0
- package/skills/core/pdd-verify-feature/evals/default-evals.json +1 -0
- package/skills/core/pdd-vm/evals/default-evals.json +1 -0
- package/skills/core/traffic-accident-assessor/LICENSE +29 -0
- package/skills/core/traffic-accident-assessor/SKILL.md +439 -0
- package/skills/core/traffic-accident-assessor/evals/evals.json +1 -0
- package/skills/core/traffic-accident-assessor/references/accident-types.md +369 -0
- package/skills/core/traffic-accident-assessor/references/liability-rules.md +287 -0
- package/skills/core/traffic-accident-assessor/references/traffic-laws.md +226 -0
- package/skills/core/traffic-accident-assessor/references//351/253/230/345/260/224/345/244/253/350/257/264/346/230/216/344/271/246.pdf +32576 -106
- package/skills/core/traffic-accident-assessor/scripts/generate_official_statement.py +588 -0
- package/skills/core/traffic-accident-assessor/scripts/generate_report.py +495 -0
- package/skills/core/traffic-accident-assessor/scripts/generate_statement.py +528 -0
- package/skills/core/traffic-accident-assessor.zip +0 -0
- package/skills/entropy/expert-arch-enforcer/SKILL.md +292 -0
- package/skills/entropy/expert-arch-enforcer/_meta.json +1 -0
- package/skills/entropy/expert-arch-enforcer/evals/default-evals.json +1 -0
- package/skills/entropy/expert-auto-refactor/SKILL.md +327 -0
- package/skills/entropy/expert-auto-refactor/_meta.json +1 -0
- package/skills/entropy/expert-auto-refactor/evals/default-evals.json +1 -0
- package/skills/entropy/expert-code-quality/SKILL.md +468 -0
- package/skills/entropy/expert-code-quality/_meta.json +1 -0
- package/skills/entropy/expert-code-quality/evals/default-evals.json +1 -0
- package/skills/entropy/expert-code-quality/evals/evals.json +109 -0
- package/skills/entropy/expert-code-quality/references/code-smells.md +605 -0
- package/skills/entropy/expert-code-quality/references/design-patterns.md +1111 -0
- package/skills/entropy/expert-code-quality/references/refactoring-catalog.md +1281 -0
- package/skills/entropy/expert-code-quality/references/solid-principles.md +524 -0
- package/skills/entropy/expert-entropy-auditor/SKILL.md +276 -0
- package/skills/entropy/expert-entropy-auditor/_meta.json +1 -0
- package/skills/entropy/expert-entropy-auditor/evals/default-evals.json +1 -0
- package/skills/expert/expert-activiti/SKILL.md +497 -0
- package/skills/expert/expert-activiti/_meta.json +1 -0
- package/skills/expert/expert-mysql/SKILL.md +832 -0
- package/skills/expert/expert-mysql/_meta.json +1 -0
- package/skills/expert/expert-performance/SKILL.md +379 -0
- package/skills/expert/expert-performance/_meta.json +1 -0
- package/skills/expert/expert-performance/evals/default-evals.json +1 -0
- package/skills/expert/expert-ruoyi/SKILL.md +472 -0
- package/skills/expert/expert-ruoyi/_meta.json +1 -0
- package/skills/expert/expert-security/SKILL.md +1341 -0
- package/skills/expert/expert-security/_meta.json +1 -0
- package/skills/expert/expert-security/evals/default-evals.json +1 -0
- package/skills/expert/software-architect/SKILL.md +350 -0
- package/skills/expert/software-architect/_meta.json +1 -0
- package/skills/expert/software-engineer/SKILL.md +437 -0
- package/skills/expert/software-engineer/_meta.json +1 -0
- package/skills/expert/software-engineer/architecture.md +130 -0
- package/skills/expert/software-engineer/patterns.md +151 -0
- package/skills/expert/software-engineer/testing.md +135 -0
- package/skills/expert/system-architect/SKILL.md +628 -0
- package/skills/expert/system-architect/_meta.json +1 -0
- package/skills/expert/system-architect/assets/templates/ARCHITECTURE.md +25 -0
- package/skills/expert/system-architect/assets/templates/README.md +44 -0
- package/skills/expert/system-architect/references/js-ts-standards.md +18 -0
- package/skills/expert/system-architect/references/python-standards.md +19 -0
- package/skills/expert/system-architect/references/scaffolding.md +61 -0
- package/skills/expert/system-architect/references/security-checklist.md +21 -0
- package/skills/openspec/openspec-apply-change/SKILL.md +156 -0
- package/skills/openspec/openspec-apply-change/_meta.json +1 -0
- package/skills/openspec/openspec-archive-change/SKILL.md +114 -0
- package/skills/openspec/openspec-archive-change/_meta.json +1 -0
- package/skills/openspec/openspec-bulk-archive-change/SKILL.md +246 -0
- package/skills/openspec/openspec-bulk-archive-change/_meta.json +1 -0
- package/skills/openspec/openspec-continue-change/SKILL.md +118 -0
- package/skills/openspec/openspec-continue-change/_meta.json +1 -0
- package/skills/openspec/openspec-explore/SKILL.md +288 -0
- package/skills/openspec/openspec-explore/_meta.json +1 -0
- package/skills/openspec/openspec-ff-change/SKILL.md +101 -0
- package/skills/openspec/openspec-ff-change/_meta.json +1 -0
- package/skills/openspec/openspec-new-change/SKILL.md +74 -0
- package/skills/openspec/openspec-new-change/_meta.json +1 -0
- package/skills/openspec/openspec-onboard/SKILL.md +554 -0
- package/skills/openspec/openspec-onboard/_meta.json +1 -0
- package/skills/openspec/openspec-sync-specs/SKILL.md +138 -0
- package/skills/openspec/openspec-sync-specs/_meta.json +1 -0
- package/skills/openspec/openspec-verify-change/SKILL.md +168 -0
- package/skills/openspec/openspec-verify-change/_meta.json +1 -0
- package/skills/pr/pdd-multi-review/SKILL.md +534 -0
- package/skills/pr/pdd-multi-review/_meta.json +1 -0
- package/skills/pr/pdd-pr-batch/SKILL.md +303 -0
- package/skills/pr/pdd-pr-batch/_meta.json +1 -0
- package/skills/pr/pdd-pr-create/SKILL.md +344 -0
- package/skills/pr/pdd-pr-create/_meta.json +1 -0
- package/skills/pr/pdd-pr-merge/SKILL.md +286 -0
- package/skills/pr/pdd-pr-merge/_meta.json +1 -0
- package/skills/pr/pdd-pr-review/SKILL.md +217 -0
- package/skills/pr/pdd-pr-review/_meta.json +1 -0
- package/skills/pr/pdd-task-manager/SKILL.md +636 -0
- package/skills/pr/pdd-task-manager/_meta.json +1 -0
- package/skills/pr/pdd-template-engine/SKILL.md +306 -0
- package/skills/pr/pdd-template-engine/_meta.json +1 -0
- package/templates/behavior-shaping/iron-law-template.md +87 -0
- package/templates/behavior-shaping/rationalization-template.md +62 -0
- package/templates/behavior-shaping/red-flags-template.md +70 -0
- package/templates/bilingual-template.md +139 -0
- package/templates/config/default.yaml +47 -0
- package/templates/project/default/README.md +31 -0
- package/templates/project/frontend/README.md +46 -0
- package/templates/project/java/README.md +48 -0
|
@@ -0,0 +1,1033 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDD gRPC Proto Definitions
|
|
3
|
+
* Protocol Buffer 类型定义和编解码器(proto3 JSON mapping)
|
|
4
|
+
*
|
|
5
|
+
* 本模块实现轻量级的 Protocol Buffer 支持,无需外部依赖。
|
|
6
|
+
* 遵循 proto3 JSON Mapping 规范:
|
|
7
|
+
* https://protobuf.dev/programming-guides/proto3/#json
|
|
8
|
+
*
|
|
9
|
+
* 支持的字段类型:
|
|
10
|
+
* - 标量类型: string, int32, int64, bool, float, double, bytes
|
|
11
|
+
* - 复合类型: message, enum
|
|
12
|
+
* - 容器类型: repeated (数组), map (键值对)
|
|
13
|
+
*
|
|
14
|
+
* @module lib/grpc/proto-definitions
|
|
15
|
+
* @author PDD-Skills Team
|
|
16
|
+
* @version 3.0.0
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// ==================== gRPC 状态码定义 ====================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* gRPC 标准状态码
|
|
23
|
+
* 参考: https://grpc.github.io/grpc/core/md_doc_statuscodes.html
|
|
24
|
+
*/
|
|
25
|
+
export const GrpcStatus = {
|
|
26
|
+
/** OK - 操作成功完成 */
|
|
27
|
+
OK: 0,
|
|
28
|
+
/** CANCELLED - 操作被调用方取消 */
|
|
29
|
+
CANCELLED: 1,
|
|
30
|
+
/** UNKNOWN - 未知错误 */
|
|
31
|
+
UNKNOWN: 2,
|
|
32
|
+
/** INVALID_ARGUMENT - 客户端指定了无效参数 */
|
|
33
|
+
INVALID_ARGUMENT: 3,
|
|
34
|
+
/** DEADLINE_EXCEEDED - 截止时间在操作完成前已过 */
|
|
35
|
+
DEADLINE_EXCEEDED: 4,
|
|
36
|
+
/** NOT_FOUND - 未找到请求的实体 */
|
|
37
|
+
NOT_FOUND: 5,
|
|
38
|
+
/** ALREADY_EXISTS - 尝试创建的实体已存在 */
|
|
39
|
+
ALREADY_EXISTS: 6,
|
|
40
|
+
/** PERMISSION_DENIED - 调用方没有权限执行指定操作 */
|
|
41
|
+
PERMISSION_DENIED: 7,
|
|
42
|
+
/** RESOURCE_EXHAUSTED - 资源已耗尽(如配额不足) */
|
|
43
|
+
RESOURCE_EXHAUSTED: 8,
|
|
44
|
+
/** FAILED_PRECONDITION - 系统未处于操作执行所需的状态 */
|
|
45
|
+
FAILED_PRECONDITION: 9,
|
|
46
|
+
/** ABORTED - 操作被中止 */
|
|
47
|
+
ABORTED: 10,
|
|
48
|
+
/** OUT_OF_RANGE - 操作尝试超出有效范围 */
|
|
49
|
+
OUT_OF_RANGE: 11,
|
|
50
|
+
/** UNIMPLEMENTED - 操作未实现或不支持/启用 */
|
|
51
|
+
UNIMPLEMENTED: 12,
|
|
52
|
+
/** INTERNAL - 内部错误 */
|
|
53
|
+
INTERNAL: 13,
|
|
54
|
+
/** UNAVAILABLE - 服务当前不可用 */
|
|
55
|
+
UNAVAILABLE: 14,
|
|
56
|
+
/** DATA_LOSS - 不可恢复的数据丢失或损坏 */
|
|
57
|
+
DATA_LOSS: 15,
|
|
58
|
+
/** UNAUTHENTICATED - 请求没有有效的身份验证凭据 */
|
|
59
|
+
UNAUTHENTICATED: 16
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 状态码到描述信息的映射
|
|
64
|
+
*/
|
|
65
|
+
export const StatusMessages = {
|
|
66
|
+
[GrpcStatus.OK]: 'OK',
|
|
67
|
+
[GrpcStatus.CANCELLED]: 'Cancelled',
|
|
68
|
+
[GrpcStatus.UNKNOWN]: 'Unknown error',
|
|
69
|
+
[GrpcStatus.INVALID_ARGUMENT]: 'Invalid argument',
|
|
70
|
+
[GrpcStatus.DEADLINE_EXCEEDED]: 'Deadline exceeded',
|
|
71
|
+
[GrpcStatus.NOT_FOUND]: 'Not found',
|
|
72
|
+
[GrpcStatus.ALREADY_EXISTS]: 'Already exists',
|
|
73
|
+
[GrpcStatus.PERMISSION_DENIED]: 'Permission denied',
|
|
74
|
+
[GrpcStatus.RESOURCE_EXHAUSTED]: 'Resource exhausted',
|
|
75
|
+
[GrpcStatus.FAILED_PRECONDITION]: 'Failed precondition',
|
|
76
|
+
[GrpcStatus.ABORTED]: 'Aborted',
|
|
77
|
+
[GrpcStatus.OUT_OF_RANGE]: 'Out of range',
|
|
78
|
+
[GrpcStatus.UNIMPLEMENTED]: 'Unimplemented',
|
|
79
|
+
[GrpcStatus.INTERNAL]: 'Internal error',
|
|
80
|
+
[GrpcStatus.UNAVAILABLE]: 'Unavailable',
|
|
81
|
+
[GrpcStatus.DATA_LOSS]: 'Data loss',
|
|
82
|
+
[GrpcStatus.UNAUTHENTICATED]: 'Unauthenticated'
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// ==================== Proto3 默认值表 ====================
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Proto3 各类型的默认值
|
|
89
|
+
* @private
|
|
90
|
+
*/
|
|
91
|
+
const DEFAULT_VALUES = {
|
|
92
|
+
string: '',
|
|
93
|
+
int32: 0,
|
|
94
|
+
int64: '0', // JSON 中 int64 使用字符串表示
|
|
95
|
+
uint32: 0,
|
|
96
|
+
uint64: '0',
|
|
97
|
+
sint32: 0,
|
|
98
|
+
sint64: '0',
|
|
99
|
+
bool: false,
|
|
100
|
+
float: 0.0,
|
|
101
|
+
double: 0.0,
|
|
102
|
+
bytes: '', // Base64 编码字符串
|
|
103
|
+
enum: 0, // 枚举默认值为第一个值(数值 0)
|
|
104
|
+
message: null // 消息默认值为 null
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// ==================== Schema 定义工具函数 ====================
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 创建字段 Schema
|
|
111
|
+
* @param {string} type - 字段类型
|
|
112
|
+
* @param {number} fieldNumber - 字段编号(proto 规范要求)
|
|
113
|
+
* @param {Object} options - 可选配置
|
|
114
|
+
* @returns {Object} 字段 Schema 对象
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
function field(type, fieldNumber, options = {}) {
|
|
118
|
+
return {
|
|
119
|
+
type,
|
|
120
|
+
fieldNumber,
|
|
121
|
+
repeated: options.repeated || false,
|
|
122
|
+
mapKey: options.mapKey || null,
|
|
123
|
+
mapValue: options.mapValue || null,
|
|
124
|
+
required: options.required || false,
|
|
125
|
+
defaultValue: options.defaultValue !== undefined ? options.defaultValue : DEFAULT_VALUES[type]
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 创建枚举 Schema
|
|
131
|
+
* @param {string} name - 枚举名称
|
|
132
|
+
* @param {Object} values - 枚举值映射 { NAME: number }
|
|
133
|
+
* @returns {Object} 枚举 Schema
|
|
134
|
+
* @private
|
|
135
|
+
*/
|
|
136
|
+
function enumSchema(name, values) {
|
|
137
|
+
return {
|
|
138
|
+
kind: 'enum',
|
|
139
|
+
name,
|
|
140
|
+
values,
|
|
141
|
+
// 确保存在值为 0 的枚举项(proto3 要求)
|
|
142
|
+
defaultName: Object.entries(values).find(([, v]) => v === 0)?.[name] ||
|
|
143
|
+
Object.keys(values)[0]
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 创建消息 Schema
|
|
149
|
+
* @param {string} name - 消息名称
|
|
150
|
+
* @param {Object} fields - 字段定义 { fieldName: fieldSchema }
|
|
151
|
+
* @returns {Object} 消息 Schema
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
function messageSchema(name, fields) {
|
|
155
|
+
return {
|
|
156
|
+
kind: 'message',
|
|
157
|
+
name,
|
|
158
|
+
fields
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ==================== PDD gRPC Service Schema 定义 ====================
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* SpecService 相关 Schema
|
|
166
|
+
*/
|
|
167
|
+
export const SpecSchemas = {
|
|
168
|
+
/**
|
|
169
|
+
* GenerateSpec 请求消息
|
|
170
|
+
* 用于基于 PRD 生成开发规格文档
|
|
171
|
+
*/
|
|
172
|
+
SpecRequest: messageSchema('SpecRequest', {
|
|
173
|
+
prdPath: field('string', 1),
|
|
174
|
+
outputDir: field('string', 2),
|
|
175
|
+
template: field('string', 3),
|
|
176
|
+
featureId: field('string', 4),
|
|
177
|
+
options: field('message', 5, { mapValue: 'string' })
|
|
178
|
+
}),
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* GetSpec 请求消息
|
|
182
|
+
* 获取已生成的规格文档
|
|
183
|
+
*/
|
|
184
|
+
GetSpecRequest: messageSchema('GetSpecRequest', {
|
|
185
|
+
specId: field('string', 1),
|
|
186
|
+
specPath: field('string', 2)
|
|
187
|
+
}),
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* ListSpecs 请求消息
|
|
191
|
+
* 列出所有规格文档
|
|
192
|
+
*/
|
|
193
|
+
ListSpecsRequest: messageSchema('ListSpecsRequest', {
|
|
194
|
+
pageToken: field('string', 1),
|
|
195
|
+
pageSize: field('int32', 2),
|
|
196
|
+
filter: field('message', 3)
|
|
197
|
+
}),
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Spec 响应消息(通用)
|
|
201
|
+
*/
|
|
202
|
+
SpecResponse: messageSchema('SpecResponse', {
|
|
203
|
+
success: field('bool', 1),
|
|
204
|
+
specId: field('string', 2),
|
|
205
|
+
specPath: field('string', 3),
|
|
206
|
+
content: field('string', 4),
|
|
207
|
+
featuresCount: field('int32', 5),
|
|
208
|
+
generatedAt: field('string', 6), // RFC3339 格式时间戳
|
|
209
|
+
metadata: field('message', 7, { mapValue: 'string' })
|
|
210
|
+
}),
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* ListSpecs 响应消息
|
|
214
|
+
*/
|
|
215
|
+
ListSpecsResponse: messageSchema('ListSpecsResponse', {
|
|
216
|
+
specs: field('message', 1, { repeated: true }),
|
|
217
|
+
nextPageToken: field('string', 2),
|
|
218
|
+
totalSize: field('int32', 3)
|
|
219
|
+
}),
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 过滤条件消息
|
|
223
|
+
*/
|
|
224
|
+
SpecFilter: messageSchema('SpecFilter', {
|
|
225
|
+
status: field('string', 1),
|
|
226
|
+
featureId: field('string', 2),
|
|
227
|
+
dateFrom: field('string', 3),
|
|
228
|
+
dateTo: field('string', 4)
|
|
229
|
+
})
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* CodeService 相关 Schema
|
|
234
|
+
*/
|
|
235
|
+
export const CodeSchemas = {
|
|
236
|
+
/**
|
|
237
|
+
* GenerateCode 请求消息
|
|
238
|
+
*/
|
|
239
|
+
CodeRequest: messageSchema('CodeRequest', {
|
|
240
|
+
specPath: field('string', 1),
|
|
241
|
+
outputDir: field('string', 2),
|
|
242
|
+
feature: field('string', 3), // 可选:只生成特定功能
|
|
243
|
+
dryRun: field('bool', 4), // 预览模式
|
|
244
|
+
overwrite: field('bool', 5), // 是否覆盖已有文件
|
|
245
|
+
language: field('string', 6), // 目标语言
|
|
246
|
+
framework: field('string', 7) // 目标框架
|
|
247
|
+
}),
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Code 响应消息
|
|
251
|
+
*/
|
|
252
|
+
CodeResponse: messageSchema('CodeResponse', {
|
|
253
|
+
success: field('bool', 1),
|
|
254
|
+
outputDir: field('string', 2),
|
|
255
|
+
generatedFiles: field('message', 3, { repeated: true }),
|
|
256
|
+
featuresProcessed: field('int32', 4),
|
|
257
|
+
dryRun: field('bool', 5),
|
|
258
|
+
generatedAt: field('string', 6),
|
|
259
|
+
errors: field('message', 7, { repeated: true }),
|
|
260
|
+
warnings: field('message', 8, { repeated: true })
|
|
261
|
+
}),
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 生成的文件信息
|
|
265
|
+
*/
|
|
266
|
+
GeneratedFile: messageSchema('GeneratedFile', {
|
|
267
|
+
path: field('string', 1),
|
|
268
|
+
size: field('int64', 2),
|
|
269
|
+
language: field('string', 3),
|
|
270
|
+
lines: field('int32', 4)
|
|
271
|
+
}),
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 错误信息
|
|
275
|
+
*/
|
|
276
|
+
ErrorInfo: messageSchema('ErrorInfo', {
|
|
277
|
+
code: field('string', 1),
|
|
278
|
+
message: field('string', 2),
|
|
279
|
+
file: field('string', 3),
|
|
280
|
+
line: field('int32', 4)
|
|
281
|
+
})
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* VerifyService 相关 Schema
|
|
286
|
+
*/
|
|
287
|
+
export const VerifySchemas = {
|
|
288
|
+
/**
|
|
289
|
+
* VerifyFeature 请求消息
|
|
290
|
+
*/
|
|
291
|
+
VerifyRequest: messageSchema('VerifyRequest', {
|
|
292
|
+
specPath: field('string', 1),
|
|
293
|
+
codePath: field('string', 2),
|
|
294
|
+
verbose: field('bool', 3),
|
|
295
|
+
dimensions: field('string', 4, { repeated: true }) // 验证维度列表
|
|
296
|
+
}),
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Verify 响应消息
|
|
300
|
+
*/
|
|
301
|
+
VerifyResponse: messageSchema('VerifyResponse', {
|
|
302
|
+
success: field('bool', 1),
|
|
303
|
+
passed: field('bool', 2),
|
|
304
|
+
score: field('float', 3), // 总体评分 0-100
|
|
305
|
+
verifiedAt: field('string', 4),
|
|
306
|
+
results: field('message', 5, { repeated: true }),
|
|
307
|
+
summary: field('message', 6)
|
|
308
|
+
}),
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* 单个维度的验证结果
|
|
312
|
+
*/
|
|
313
|
+
DimensionResult: messageSchema('DimensionResult', {
|
|
314
|
+
dimension: field('string', 1),
|
|
315
|
+
passed: field('bool', 2),
|
|
316
|
+
score: field('float', 3),
|
|
317
|
+
details: field('string', 4),
|
|
318
|
+
issues: field('message', 5, { repeated: true })
|
|
319
|
+
}),
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* 验证摘要
|
|
323
|
+
*/
|
|
324
|
+
VerifySummary: messageSchema('VerifySummary', {
|
|
325
|
+
totalDimensions: field('int32', 1),
|
|
326
|
+
passedDimensions: field('int32', 2),
|
|
327
|
+
totalIssues: field('int32', 3),
|
|
328
|
+
criticalIssues: field('int32', 4),
|
|
329
|
+
passRate: field('float', 5)
|
|
330
|
+
})
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* ReportService 相关 Schema
|
|
335
|
+
*/
|
|
336
|
+
export const ReportSchemas = {
|
|
337
|
+
/**
|
|
338
|
+
* GenerateReport 请求消息
|
|
339
|
+
*/
|
|
340
|
+
ReportRequest: messageSchema('ReportRequest', {
|
|
341
|
+
type: field('string', 1), // 输出格式: md, json, html, pdf
|
|
342
|
+
outputPath: field('string', 2),
|
|
343
|
+
includeStats: field('bool', 3),
|
|
344
|
+
includeCharts: field('bool', 4),
|
|
345
|
+
projectDir: field('string', 5)
|
|
346
|
+
}),
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Report 响应消息
|
|
350
|
+
*/
|
|
351
|
+
ReportResponse: messageSchema('ReportResponse', {
|
|
352
|
+
success: field('bool', 1),
|
|
353
|
+
reportPath: field('string', 2),
|
|
354
|
+
format: field('string', 3),
|
|
355
|
+
generatedAt: field('string', 4),
|
|
356
|
+
preview: field('message', 5),
|
|
357
|
+
stats: field('message', 6)
|
|
358
|
+
}),
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* 报告预览摘要
|
|
362
|
+
*/
|
|
363
|
+
ReportPreview: messageSchema('ReportPreview', {
|
|
364
|
+
totalFiles: field('int32', 1),
|
|
365
|
+
skillsCount: field('int32', 2),
|
|
366
|
+
specsCount: field('int32', 3),
|
|
367
|
+
testsCount: field('int32', 4),
|
|
368
|
+
codeLines: field('int64', 5)
|
|
369
|
+
}),
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* 项目统计信息
|
|
373
|
+
*/
|
|
374
|
+
ProjectStats: messageSchema('ProjectStats', {
|
|
375
|
+
name: field('string', 1),
|
|
376
|
+
version: field('string', 2),
|
|
377
|
+
fileTypes: field('message', 3, { mapValue: 'int32' }),
|
|
378
|
+
quickCounts: field('message', 4, { mapValue: 'int32' })
|
|
379
|
+
})
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* SkillService 相关 Schema
|
|
384
|
+
*/
|
|
385
|
+
export const SkillSchemas = {
|
|
386
|
+
/**
|
|
387
|
+
* ListSkills 请求消息
|
|
388
|
+
*/
|
|
389
|
+
ListSkillsRequest: messageSchema('ListSkillsRequest', {
|
|
390
|
+
category: field('string', 1), // 可选过滤分类
|
|
391
|
+
pageToken: field('string', 2),
|
|
392
|
+
pageSize: field('int32', 3)
|
|
393
|
+
}),
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* GetSkillInfo 请求消息
|
|
397
|
+
*/
|
|
398
|
+
GetSkillInfoRequest: messageSchema('GetSkillInfoRequest', {
|
|
399
|
+
category: field('string', 1),
|
|
400
|
+
name: field('string', 2)
|
|
401
|
+
}),
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* ListSkills 响应消息
|
|
405
|
+
*/
|
|
406
|
+
ListSkillsResponse: messageSchema('ListSkillsResponse', {
|
|
407
|
+
skills: field('message', 1, { repeated: true }),
|
|
408
|
+
nextPageToken: field('string', 2),
|
|
409
|
+
totalSize: field('int32', 3)
|
|
410
|
+
}),
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Skill 信息响应
|
|
414
|
+
*/
|
|
415
|
+
SkillInfoResponse: messageSchema('SkillInfoResponse', {
|
|
416
|
+
name: field('string', 1),
|
|
417
|
+
category: field('string', 2),
|
|
418
|
+
title: field('string', 3),
|
|
419
|
+
description: field('string', 4),
|
|
420
|
+
path: field('string', 5),
|
|
421
|
+
triggers: field('string', 6, { repeated: true }),
|
|
422
|
+
contentLength: field('int32', 7),
|
|
423
|
+
lastModified: field('string', 8)
|
|
424
|
+
}),
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Skill 条目(用于列表)
|
|
428
|
+
*/
|
|
429
|
+
SkillEntry: messageSchema('SkillEntry', {
|
|
430
|
+
name: field('string', 1),
|
|
431
|
+
category: field('string', 2),
|
|
432
|
+
path: field('string', 3)
|
|
433
|
+
})
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Health Check Service Schema(gRPC 标准健康检查协议)
|
|
438
|
+
*/
|
|
439
|
+
export const HealthSchemas = {
|
|
440
|
+
/**
|
|
441
|
+
* HealthCheck 请求
|
|
442
|
+
*/
|
|
443
|
+
HealthCheckRequest: messageSchema('HealthCheckRequest', {
|
|
444
|
+
service: field('string', 1) // 要检查的服务名
|
|
445
|
+
}),
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* HealthCheck 响应
|
|
449
|
+
*/
|
|
450
|
+
HealthCheckResponse: messageSchema('HealthCheckResponse', {
|
|
451
|
+
status: field('enum', 1) // ServingStatus 枚举值
|
|
452
|
+
}),
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* ServingStatus 枚举
|
|
456
|
+
*/
|
|
457
|
+
ServingStatus: enumSchema('ServingStatus', {
|
|
458
|
+
UNKNOWN: 0,
|
|
459
|
+
SERVING: 1,
|
|
460
|
+
NOT_SERVING: 2,
|
|
461
|
+
SERVICE_UNKNOWN: 3 // 仅用于 Watch 的响应
|
|
462
|
+
})
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* 通用错误响应 Schema
|
|
467
|
+
*/
|
|
468
|
+
export const CommonSchemas = {
|
|
469
|
+
/**
|
|
470
|
+
* Google RPC Status 消息(标准错误响应格式)
|
|
471
|
+
*/
|
|
472
|
+
GoogleRpcStatus: messageSchema('GoogleRpcStatus', {
|
|
473
|
+
code: field('int32', 1),
|
|
474
|
+
message: field('string', 2),
|
|
475
|
+
details: field('message', 3, { repeated: true })
|
|
476
|
+
}),
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Any 类型包装器(用于 details 字段)
|
|
480
|
+
*/
|
|
481
|
+
Any: messageSchema('Any', {
|
|
482
|
+
typeUrl: field('string', 1),
|
|
483
|
+
value: field('string', 2) // Base64 编码
|
|
484
|
+
}),
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* 空消息(用于无参数 RPC)
|
|
488
|
+
*/
|
|
489
|
+
Empty: messageSchema('Empty', {})
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// ==================== 所有 Schema 注册表 ====================
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* 全局 Schema 注册表
|
|
496
|
+
* 将消息名称映射到其 Schema 定义
|
|
497
|
+
*/
|
|
498
|
+
export const SchemaRegistry = {
|
|
499
|
+
...SpecSchemas,
|
|
500
|
+
...CodeSchemas,
|
|
501
|
+
...VerifySchemas,
|
|
502
|
+
...ReportSchemas,
|
|
503
|
+
...SkillSchemas,
|
|
504
|
+
...HealthSchemas,
|
|
505
|
+
...CommonSchemas
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// ==================== 编解码器核心实现 ====================
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* 将 JavaScript 对象编码为 proto3 JSON 格式
|
|
512
|
+
*
|
|
513
|
+
* 编码规则(遵循 proto3 JSON Mapping):
|
|
514
|
+
* - null/undefined 值不输出(使用默认值)
|
|
515
|
+
* - int64 类型输出为字符串
|
|
516
|
+
* - enum 输出为名称字符串或数字
|
|
517
|
+
* - bytes 输出为 base64 字符串
|
|
518
|
+
* - 忽略未知字段
|
|
519
|
+
*
|
|
520
|
+
* @param {Object} data - 要编码的数据对象
|
|
521
|
+
* @param {Object} schema - 消息 Schema 定义
|
|
522
|
+
* @returns {Object} 编码后的 JSON 兼容对象
|
|
523
|
+
* @example
|
|
524
|
+
* encode({ prdPath: './prd.md' }, SpecSchemas.SpecRequest)
|
|
525
|
+
* // => { prdPath: './prd.md' }
|
|
526
|
+
*/
|
|
527
|
+
export function encode(data, schema) {
|
|
528
|
+
if (!schema || schema.kind !== 'message') {
|
|
529
|
+
throw new Error(`Invalid schema: expected message schema, got ${schema?.kind}`);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (!data || typeof data !== 'object') {
|
|
533
|
+
return {};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const result = {};
|
|
537
|
+
const { fields } = schema;
|
|
538
|
+
|
|
539
|
+
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
540
|
+
const value = data[fieldName];
|
|
541
|
+
|
|
542
|
+
// 跳过 undefined 和 null(proto3 默认行为)
|
|
543
|
+
if (value === undefined || value === null) {
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// 处理各类型
|
|
548
|
+
if (fieldDef.repeated) {
|
|
549
|
+
// repeated 字段
|
|
550
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
551
|
+
result[fieldName] = value.map(item => _encodeFieldValue(item, fieldDef));
|
|
552
|
+
}
|
|
553
|
+
// 空数组不输出
|
|
554
|
+
} else if (fieldDef.mapValue !== null && !fieldDef.repeated) {
|
|
555
|
+
// map 字段
|
|
556
|
+
if (value && typeof value === 'object') {
|
|
557
|
+
result[fieldName] = {};
|
|
558
|
+
for (const [k, v] of Object.entries(value)) {
|
|
559
|
+
result[fieldName][k] = _encodeFieldValue(v, {
|
|
560
|
+
type: fieldDef.mapValue
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
} else {
|
|
565
|
+
// 标量或消息字段
|
|
566
|
+
const encoded = _encodeFieldValue(value, fieldDef);
|
|
567
|
+
// 只有非默认值才输出(可选优化)
|
|
568
|
+
result[fieldName] = encoded;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return result;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* 编码单个字段值
|
|
577
|
+
* @private
|
|
578
|
+
*/
|
|
579
|
+
function _encodeFieldValue(value, fieldDef) {
|
|
580
|
+
switch (fieldDef.type) {
|
|
581
|
+
case 'int64':
|
|
582
|
+
case 'uint64':
|
|
583
|
+
case 'sint64':
|
|
584
|
+
// int64 在 JSON 中必须表示为字符串
|
|
585
|
+
return String(value);
|
|
586
|
+
|
|
587
|
+
case 'bytes':
|
|
588
|
+
// bytes 在 JSON 中表示为 base64 字符串
|
|
589
|
+
if (typeof value === 'string') {
|
|
590
|
+
return value; // 假设已经是 base64
|
|
591
|
+
}
|
|
592
|
+
return Buffer.from(value).toString('base64');
|
|
593
|
+
|
|
594
|
+
case 'bool':
|
|
595
|
+
return Boolean(value);
|
|
596
|
+
|
|
597
|
+
case 'float':
|
|
598
|
+
case 'double':
|
|
599
|
+
return Number(value);
|
|
600
|
+
|
|
601
|
+
case 'message':
|
|
602
|
+
// 嵌套消息需要递归编码
|
|
603
|
+
if (value === null || value === undefined) {
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
// 查找嵌套消息的 schema
|
|
607
|
+
const nestedSchema = SchemaRegistry[value.__schema] || findSchemaByContext(fieldDef);
|
|
608
|
+
if (nestedSchema) {
|
|
609
|
+
return encode(value, nestedSchema);
|
|
610
|
+
}
|
|
611
|
+
// 无法确定 schema 时直接返回
|
|
612
|
+
return value;
|
|
613
|
+
|
|
614
|
+
case 'enum':
|
|
615
|
+
// enum 可以是数字或字符串名称
|
|
616
|
+
return value;
|
|
617
|
+
|
|
618
|
+
default:
|
|
619
|
+
// string, int32, uint32, sint32 等
|
|
620
|
+
return value;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* 从 proto3 JSON 格式解码为 JavaScript 对象
|
|
626
|
+
*
|
|
627
|
+
* 解码规则:
|
|
628
|
+
* - 缺失字段填充默认值
|
|
629
|
+
* - int64 字符串保持为字符串
|
|
630
|
+
* - 未知字段保留(可选)
|
|
631
|
+
*
|
|
632
|
+
* @param {Object|string} json - JSON 对象或 JSON 字符串
|
|
633
|
+
* @param {Object} schema - 消息 Schema 定义
|
|
634
|
+
* @returns {Object} 解码后的 JavaScript 对象
|
|
635
|
+
* @example
|
|
636
|
+
* decode('{ "prdPath": "./prd.md" }', SpecSchemas.SpecRequest)
|
|
637
|
+
* // => { prdPath: './prd.md', outputDir: '', template: '', ... }
|
|
638
|
+
*/
|
|
639
|
+
export function decode(json, schema) {
|
|
640
|
+
if (!schema || schema.kind !== 'message') {
|
|
641
|
+
throw new Error(`Invalid schema: expected message schema, got ${schema?.kind}`);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// 如果传入的是字符串,先解析为对象
|
|
645
|
+
let data = json;
|
|
646
|
+
if (typeof json === 'string') {
|
|
647
|
+
try {
|
|
648
|
+
data = JSON.parse(json);
|
|
649
|
+
} catch (e) {
|
|
650
|
+
throw new Error(`Invalid JSON: ${e.message}`);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// 确保 data 是对象
|
|
655
|
+
if (!data || typeof data !== 'object') {
|
|
656
|
+
data = {};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const result = {};
|
|
660
|
+
const { fields } = schema;
|
|
661
|
+
|
|
662
|
+
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
663
|
+
const value = data[fieldName];
|
|
664
|
+
|
|
665
|
+
if (fieldDef.repeated) {
|
|
666
|
+
// repeated 字段:缺失时为空数组
|
|
667
|
+
if (Array.isArray(value)) {
|
|
668
|
+
result[fieldName] = value.map(item =>
|
|
669
|
+
_decodeFieldValue(item, fieldDef)
|
|
670
|
+
);
|
|
671
|
+
} else {
|
|
672
|
+
result[fieldName] = [];
|
|
673
|
+
}
|
|
674
|
+
} else if (fieldDef.mapValue !== null && !fieldDef.repeated) {
|
|
675
|
+
// map 字段:缺失时为空对象
|
|
676
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
677
|
+
const mapResult = {};
|
|
678
|
+
for (const [k, v] of Object.entries(value)) {
|
|
679
|
+
mapResult[k] = _decodeFieldValue(v, {
|
|
680
|
+
type: fieldDef.mapValue
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
result[fieldName] = mapResult;
|
|
684
|
+
} else {
|
|
685
|
+
result[fieldName] = {};
|
|
686
|
+
}
|
|
687
|
+
} else {
|
|
688
|
+
// 标量或消息字段:缺失时使用默认值
|
|
689
|
+
if (value === undefined || value === null) {
|
|
690
|
+
result[fieldName] = _getDefaultValue(fieldDef.type);
|
|
691
|
+
} else {
|
|
692
|
+
result[fieldName] = _decodeFieldValue(value, fieldDef);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
return result;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* 解码单个字段值
|
|
702
|
+
* @private
|
|
703
|
+
*/
|
|
704
|
+
function _decodeFieldValue(value, fieldDef) {
|
|
705
|
+
if (value === null || value === undefined) {
|
|
706
|
+
return _getDefaultValue(fieldDef.type);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
switch (fieldDef.type) {
|
|
710
|
+
case 'int32':
|
|
711
|
+
case 'uint32':
|
|
712
|
+
case 'sint32':
|
|
713
|
+
return parseInt(value, 10) || 0;
|
|
714
|
+
|
|
715
|
+
case 'int64':
|
|
716
|
+
case 'uint64':
|
|
717
|
+
case 'sint64':
|
|
718
|
+
// 保持为字符串以避免精度丢失
|
|
719
|
+
return String(value);
|
|
720
|
+
|
|
721
|
+
case 'float':
|
|
722
|
+
case 'double':
|
|
723
|
+
return parseFloat(value) || 0.0;
|
|
724
|
+
|
|
725
|
+
case 'bool':
|
|
726
|
+
// 接受 "true"/"false" 字符串和布尔值
|
|
727
|
+
if (typeof value === 'string') {
|
|
728
|
+
return value.toLowerCase() === 'true';
|
|
729
|
+
}
|
|
730
|
+
return Boolean(value);
|
|
731
|
+
|
|
732
|
+
case 'bytes':
|
|
733
|
+
// 返回 base64 字符串(实际解码由使用者决定)
|
|
734
|
+
return String(value);
|
|
735
|
+
|
|
736
|
+
case 'message':
|
|
737
|
+
// 嵌套消息递归解码
|
|
738
|
+
if (typeof value === 'object' && value !== null) {
|
|
739
|
+
const nestedSchema = findSchemaByContext(fieldDef);
|
|
740
|
+
if (nestedSchema) {
|
|
741
|
+
return decode(value, nestedSchema);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return value;
|
|
745
|
+
|
|
746
|
+
case 'enum':
|
|
747
|
+
// enum 可以是数字或字符串
|
|
748
|
+
if (typeof value === 'string') {
|
|
749
|
+
const num = parseInt(value, 10);
|
|
750
|
+
if (!isNaN(num)) return num;
|
|
751
|
+
return value; // 保持为字符串名称
|
|
752
|
+
}
|
|
753
|
+
return parseInt(value, 10) || 0;
|
|
754
|
+
|
|
755
|
+
default:
|
|
756
|
+
// string 和其他类型
|
|
757
|
+
return String(value);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* 获取类型的默认值
|
|
763
|
+
* @private
|
|
764
|
+
*/
|
|
765
|
+
function _getDefaultValue(type) {
|
|
766
|
+
if (type in DEFAULT_VALUES) {
|
|
767
|
+
return DEFAULT_VALUES[type];
|
|
768
|
+
}
|
|
769
|
+
return null; // 未知类型返回 null
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* 根据上下文查找嵌套消息的 Schema
|
|
774
|
+
* 这是一个简化的实现,实际场景中可能需要更复杂的查找逻辑
|
|
775
|
+
* @private
|
|
776
|
+
*/
|
|
777
|
+
function findSchemaByContext(fieldDef) {
|
|
778
|
+
// 这里可以通过字段命名约定或其他方式推断 schema
|
|
779
|
+
// 当前简化实现:返回 null 让调用者处理
|
|
780
|
+
return null;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// ==================== 验证器 ====================
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* 验证消息是否符合 Schema 定义
|
|
787
|
+
*
|
|
788
|
+
* @param {Object} data - 要验证的数据
|
|
789
|
+
* @param {Object} schema - 消息 Schema 定义
|
|
790
|
+
* @returns {{ valid: boolean, errors: Array<string> }} 验证结果
|
|
791
|
+
* @example
|
|
792
|
+
* validate({ prdPath: './prd.md' }, SpecSchemas.SpecRequest)
|
|
793
|
+
* // => { valid: true, errors: [] }
|
|
794
|
+
*/
|
|
795
|
+
export function validate(data, schema) {
|
|
796
|
+
const errors = [];
|
|
797
|
+
|
|
798
|
+
if (!schema || schema.kind !== 'message') {
|
|
799
|
+
return { valid: false, errors: ['Invalid schema definition'] };
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (!data || typeof data !== 'object') {
|
|
803
|
+
return { valid: false, errors: ['Data must be an object'] };
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
_validateMessage(data, schema.fields, '', errors);
|
|
807
|
+
|
|
808
|
+
return {
|
|
809
|
+
valid: errors.length === 0,
|
|
810
|
+
errors
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* 递归验证消息字段
|
|
816
|
+
* @private
|
|
817
|
+
*/
|
|
818
|
+
function _validateMessage(data, fields, prefix, errors) {
|
|
819
|
+
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
820
|
+
const value = data[fieldName];
|
|
821
|
+
const fullPath = prefix ? `${prefix}.${fieldName}` : fieldName;
|
|
822
|
+
|
|
823
|
+
// 检查必填字段
|
|
824
|
+
if (fieldDef.required && (value === undefined || value === null)) {
|
|
825
|
+
errors.push(`${fullPath}: Required field is missing`);
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// 跳过可选且缺失的字段
|
|
830
|
+
if (value === undefined || value === null) {
|
|
831
|
+
if (fieldDef.repeated) {
|
|
832
|
+
// repeated 字段的 null 应该视为空数组,但这里报错
|
|
833
|
+
errors.push(`${fullPath}: Expected array, got null`);
|
|
834
|
+
}
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// 类型检查
|
|
839
|
+
if (fieldDef.repeated) {
|
|
840
|
+
if (!Array.isArray(value)) {
|
|
841
|
+
errors.push(`${fullPath}: Expected array, got ${typeof value}`);
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
// 验证数组元素
|
|
845
|
+
value.forEach((item, index) => {
|
|
846
|
+
_validateFieldItem(item, fieldDef, `${fullPath}[${index}]`, errors);
|
|
847
|
+
});
|
|
848
|
+
} else if (fieldDef.mapValue !== null) {
|
|
849
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
850
|
+
errors.push(`${fullPath}: Expected object, got ${typeof value}`);
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
// 验证 map 值
|
|
854
|
+
for (const [k, v] of Object.entries(value)) {
|
|
855
|
+
_validateFieldItem(v, { type: fieldDef.mapValue }, `${fullPath}.${k}`, errors);
|
|
856
|
+
}
|
|
857
|
+
} else {
|
|
858
|
+
_validateFieldItem(value, fieldDef, fullPath, errors);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* 验证单个字段值的类型
|
|
865
|
+
* @private
|
|
866
|
+
*/
|
|
867
|
+
function _validateFieldItem(value, fieldDef, path, errors) {
|
|
868
|
+
const { type } = fieldDef;
|
|
869
|
+
|
|
870
|
+
switch (type) {
|
|
871
|
+
case 'string':
|
|
872
|
+
if (typeof value !== 'string') {
|
|
873
|
+
errors.push(`${path}: Expected string, got ${typeof value}`);
|
|
874
|
+
}
|
|
875
|
+
break;
|
|
876
|
+
|
|
877
|
+
case 'int32':
|
|
878
|
+
case 'uint32':
|
|
879
|
+
case 'sint32':
|
|
880
|
+
case 'int64':
|
|
881
|
+
case 'uint64':
|
|
882
|
+
case 'sint64':
|
|
883
|
+
if (typeof value !== 'number' && typeof value !== 'string') {
|
|
884
|
+
errors.push(`${path}: Expected integer, got ${typeof value}`);
|
|
885
|
+
} else if (typeof value === 'number' && !Number.isInteger(value)) {
|
|
886
|
+
errors.push(`${path}: Expected integer, got float`);
|
|
887
|
+
}
|
|
888
|
+
break;
|
|
889
|
+
|
|
890
|
+
case 'bool':
|
|
891
|
+
if (typeof value !== 'boolean') {
|
|
892
|
+
errors.push(`${path}: Expected boolean, got ${typeof value}`);
|
|
893
|
+
}
|
|
894
|
+
break;
|
|
895
|
+
|
|
896
|
+
case 'float':
|
|
897
|
+
case 'double':
|
|
898
|
+
if (typeof value !== 'number') {
|
|
899
|
+
errors.push(`${path}: Expected number, got ${typeof value}`);
|
|
900
|
+
}
|
|
901
|
+
break;
|
|
902
|
+
|
|
903
|
+
case 'message':
|
|
904
|
+
if (value !== null && typeof value !== 'object') {
|
|
905
|
+
errors.push(`${path}: Expected object, got ${typeof value}`);
|
|
906
|
+
}
|
|
907
|
+
// 递归验证嵌套消息(如果有 schema)
|
|
908
|
+
const nestedSchema = SchemaRegistry[type] || findSchemaForType(type);
|
|
909
|
+
if (nestedSchema && value !== null) {
|
|
910
|
+
_validateMessage(value, nestedSchema.fields, path, errors);
|
|
911
|
+
}
|
|
912
|
+
break;
|
|
913
|
+
|
|
914
|
+
case 'enum':
|
|
915
|
+
// enum 可以是数字或字符串
|
|
916
|
+
if (typeof value !== 'number' && typeof value !== 'string') {
|
|
917
|
+
errors.push(`${path}: Expected enum (number or string), got ${typeof value}`);
|
|
918
|
+
}
|
|
919
|
+
break;
|
|
920
|
+
|
|
921
|
+
default:
|
|
922
|
+
// 未知类型跳过验证
|
|
923
|
+
break;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* 根据类型名查找 Schema
|
|
929
|
+
* @private
|
|
930
|
+
*/
|
|
931
|
+
function findSchemaForType(typeName) {
|
|
932
|
+
// 尝试从注册表中查找
|
|
933
|
+
// 这里的 typeName 可能是引用其他消息类型的名称
|
|
934
|
+
return SchemaRegistry[typeName] || null;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// ==================== 导出汇总 ====================
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* 所有可用的 gRPC Service 及其方法定义
|
|
941
|
+
* 用于服务注册和反射
|
|
942
|
+
*/
|
|
943
|
+
export const ServiceDefinitions = {
|
|
944
|
+
SpecService: {
|
|
945
|
+
name: 'pdd.SpecService',
|
|
946
|
+
methods: {
|
|
947
|
+
GenerateSpec: {
|
|
948
|
+
requestType: 'SpecRequest',
|
|
949
|
+
responseType: 'SpecResponse',
|
|
950
|
+
requestStream: false,
|
|
951
|
+
responseStream: false
|
|
952
|
+
},
|
|
953
|
+
GetSpec: {
|
|
954
|
+
requestType: 'GetSpecRequest',
|
|
955
|
+
responseType: 'SpecResponse',
|
|
956
|
+
requestStream: false,
|
|
957
|
+
responseStream: false
|
|
958
|
+
},
|
|
959
|
+
ListSpecs: {
|
|
960
|
+
requestType: 'ListSpecsRequest',
|
|
961
|
+
responseType: 'ListSpecsResponse',
|
|
962
|
+
requestStream: false,
|
|
963
|
+
responseStream: false
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
},
|
|
967
|
+
|
|
968
|
+
CodeService: {
|
|
969
|
+
name: 'pdd.CodeService',
|
|
970
|
+
methods: {
|
|
971
|
+
GenerateCode: {
|
|
972
|
+
requestType: 'CodeRequest',
|
|
973
|
+
responseType: 'CodeResponse',
|
|
974
|
+
requestStream: false,
|
|
975
|
+
responseStream: false
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
|
|
980
|
+
VerifyService: {
|
|
981
|
+
name: 'pdd.VerifyService',
|
|
982
|
+
methods: {
|
|
983
|
+
VerifyFeature: {
|
|
984
|
+
requestType: 'VerifyRequest',
|
|
985
|
+
responseType: 'VerifyResponse',
|
|
986
|
+
requestStream: false,
|
|
987
|
+
responseStream: false
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
|
|
992
|
+
ReportService: {
|
|
993
|
+
name: 'pdd.ReportService',
|
|
994
|
+
methods: {
|
|
995
|
+
GenerateReport: {
|
|
996
|
+
requestType: 'ReportRequest',
|
|
997
|
+
responseType: 'ReportResponse',
|
|
998
|
+
requestStream: false,
|
|
999
|
+
responseStream: false
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
},
|
|
1003
|
+
|
|
1004
|
+
SkillService: {
|
|
1005
|
+
name: 'pdd.SkillService',
|
|
1006
|
+
methods: {
|
|
1007
|
+
ListSkills: {
|
|
1008
|
+
requestType: 'ListSkillsRequest',
|
|
1009
|
+
responseType: 'ListSkillsResponse',
|
|
1010
|
+
requestStream: false,
|
|
1011
|
+
responseStream: false
|
|
1012
|
+
},
|
|
1013
|
+
GetSkillInfo: {
|
|
1014
|
+
requestType: 'GetSkillInfoRequest',
|
|
1015
|
+
responseType: 'SkillInfoResponse',
|
|
1016
|
+
requestStream: false,
|
|
1017
|
+
responseStream: false
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
},
|
|
1021
|
+
|
|
1022
|
+
HealthService: {
|
|
1023
|
+
name: 'grpc.health.v1.Health',
|
|
1024
|
+
methods: {
|
|
1025
|
+
Check: {
|
|
1026
|
+
requestType: 'HealthCheckRequest',
|
|
1027
|
+
responseType: 'HealthCheckResponse',
|
|
1028
|
+
requestStream: false,
|
|
1029
|
+
responseStream: false
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
};
|