@ai-setting/roy-agent-core 1.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/dist/index.js +99145 -0
- package/package.json +114 -0
- package/src/config/config-component.test.ts +627 -0
- package/src/config/config-component.ts +906 -0
- package/src/config/config-parser.test.ts +319 -0
- package/src/config/config-parser.ts +203 -0
- package/src/config/decentralized-config.test.ts +740 -0
- package/src/config/env-key.ts +210 -0
- package/src/config/env-source.test.ts +252 -0
- package/src/config/env-source.ts +301 -0
- package/src/config/file-source.test.ts +357 -0
- package/src/config/file-source.ts +421 -0
- package/src/config/index.ts +24 -0
- package/src/config/protocol-resolver.test.ts +217 -0
- package/src/config/protocol-resolver.ts +228 -0
- package/src/env/agent/agent-component.abort.test.ts +511 -0
- package/src/env/agent/agent-component.record-session.test.ts +349 -0
- package/src/env/agent/agent-component.test.ts +1389 -0
- package/src/env/agent/agent-component.tool-error.test.ts +327 -0
- package/src/env/agent/agent-component.ts +1711 -0
- package/src/env/agent/agent-config-registration.test.ts +226 -0
- package/src/env/agent/agent-config-registration.ts +46 -0
- package/src/env/agent/agent-reminder-plugin.integration.test.ts +243 -0
- package/src/env/agent/index.ts +10 -0
- package/src/env/agent/summary-agent.parse-hint.test.ts +360 -0
- package/src/env/agent/summary-agent.ts +508 -0
- package/src/env/agent/types.ts +536 -0
- package/src/env/commands/commands-component.test.ts +364 -0
- package/src/env/commands/commands-component.ts +604 -0
- package/src/env/commands/commands-config-registration.test.ts +198 -0
- package/src/env/commands/commands-config-registration.ts +38 -0
- package/src/env/commands/index.ts +21 -0
- package/src/env/commands/parser.test.ts +203 -0
- package/src/env/commands/parser.ts +115 -0
- package/src/env/commands/types.ts +184 -0
- package/src/env/commands-prompt-integration.test.ts +243 -0
- package/src/env/component-env.test.ts +119 -0
- package/src/env/component.ts +335 -0
- package/src/env/constants.test.ts +72 -0
- package/src/env/constants.ts +123 -0
- package/src/env/debug/debug-component.test.ts +114 -0
- package/src/env/debug/debug-component.ts +547 -0
- package/src/env/debug/formatters/index.ts +9 -0
- package/src/env/debug/formatters/repl-formatter.test.ts +139 -0
- package/src/env/debug/formatters/repl-formatter.ts +358 -0
- package/src/env/debug/formatters/trace-formatter.test.ts +119 -0
- package/src/env/debug/formatters/trace-formatter.ts +191 -0
- package/src/env/debug/formatters/tree-formatter.test.ts +107 -0
- package/src/env/debug/formatters/tree-formatter.ts +325 -0
- package/src/env/debug/index.ts +38 -0
- package/src/env/debug/parser/regex-parser.test.ts +201 -0
- package/src/env/debug/parser/regex-parser.ts +196 -0
- package/src/env/debug/parser/span-builder.test.ts +241 -0
- package/src/env/debug/parser/span-builder.ts +386 -0
- package/src/env/debug/reader/log-reader.test.ts +170 -0
- package/src/env/debug/reader/log-reader.ts +186 -0
- package/src/env/debug/reader/span-db-reader.test.ts +118 -0
- package/src/env/debug/reader/span-db-reader.ts +201 -0
- package/src/env/debug/types.test.ts +187 -0
- package/src/env/debug/types.ts +171 -0
- package/src/env/environment-init.test.ts +183 -0
- package/src/env/environment-lifecycle.test.ts +516 -0
- package/src/env/environment-service.test.ts +332 -0
- package/src/env/environment.handle-query.test.ts +96 -0
- package/src/env/environment.test.ts +232 -0
- package/src/env/environment.ts +708 -0
- package/src/env/errors.test.ts +165 -0
- package/src/env/errors.ts +157 -0
- package/src/env/event-source/event-source-agent-handler.test.ts +193 -0
- package/src/env/event-source/event-source-agent-handler.ts +111 -0
- package/src/env/event-source/event-source-component.process-cleanup.test.ts +236 -0
- package/src/env/event-source/event-source-component.stop.test.ts +346 -0
- package/src/env/event-source/event-source-component.test.ts +1207 -0
- package/src/env/event-source/event-source-component.ts +1379 -0
- package/src/env/event-source/event-source-config-registration.test.ts +242 -0
- package/src/env/event-source/event-source-config-registration.ts +37 -0
- package/src/env/event-source/event-source-integration.test.ts +320 -0
- package/src/env/event-source/event-source-platform.test.ts +630 -0
- package/src/env/event-source/types.ts +298 -0
- package/src/env/hook/global-hook-manager.ts +162 -0
- package/src/env/hook/hook-manager.test.ts +374 -0
- package/src/env/hook/hook-manager.ts +309 -0
- package/src/env/hook/index.ts +38 -0
- package/src/env/hook/types.ts +138 -0
- package/src/env/index.ts +144 -0
- package/src/env/interface.ts +203 -0
- package/src/env/llm/hooks.test.ts +293 -0
- package/src/env/llm/hooks.ts +316 -0
- package/src/env/llm/index.ts +61 -0
- package/src/env/llm/invoke-threshold-check.test.ts +88 -0
- package/src/env/llm/invoke-timeout.test.ts +54 -0
- package/src/env/llm/invoke.test.ts +71 -0
- package/src/env/llm/invoke.ts +1039 -0
- package/src/env/llm/llm-config.test.ts +523 -0
- package/src/env/llm/llm.test.ts +233 -0
- package/src/env/llm/llm.ts +568 -0
- package/src/env/llm/provider.test.ts +182 -0
- package/src/env/llm/provider.ts +108 -0
- package/src/env/llm/transform.test.ts +251 -0
- package/src/env/llm/transform.ts +286 -0
- package/src/env/llm/types.test.ts +580 -0
- package/src/env/llm/types.ts +424 -0
- package/src/env/log-trace/decorator-otel.test.ts +182 -0
- package/src/env/log-trace/decorator.ts +230 -0
- package/src/env/log-trace/index.ts +79 -0
- package/src/env/log-trace/log-trace-component.test.ts +242 -0
- package/src/env/log-trace/log-trace-component.ts +497 -0
- package/src/env/log-trace/log-trace-config-registration.test.ts +348 -0
- package/src/env/log-trace/log-trace-config-registration.ts +45 -0
- package/src/env/log-trace/logger.test.ts +149 -0
- package/src/env/log-trace/logger.ts +522 -0
- package/src/env/log-trace/opentelemetry/cli-propagation.test.ts +147 -0
- package/src/env/log-trace/opentelemetry/cli-propagation.ts +194 -0
- package/src/env/log-trace/opentelemetry/integration.test.ts +668 -0
- package/src/env/log-trace/opentelemetry/mod.ts +25 -0
- package/src/env/log-trace/opentelemetry/propagation-env.test.ts +181 -0
- package/src/env/log-trace/opentelemetry/propagation-env.ts +136 -0
- package/src/env/log-trace/opentelemetry/propagation.test.ts +259 -0
- package/src/env/log-trace/opentelemetry/propagation.ts +215 -0
- package/src/env/log-trace/opentelemetry/tracer-provider-context.test.ts +166 -0
- package/src/env/log-trace/opentelemetry/tracer-provider.test.ts +379 -0
- package/src/env/log-trace/opentelemetry/tracer-provider.ts +612 -0
- package/src/env/log-trace/span-storage.test.ts +145 -0
- package/src/env/log-trace/span-storage.ts +230 -0
- package/src/env/log-trace/trace-context.test.ts +187 -0
- package/src/env/log-trace/trace-context.ts +162 -0
- package/src/env/log-trace/types.test.ts +63 -0
- package/src/env/log-trace/types.ts +172 -0
- package/src/env/mcp/README.md +244 -0
- package/src/env/mcp/__integration__/mcp-component.integration.test.ts +373 -0
- package/src/env/mcp/config.test.ts +74 -0
- package/src/env/mcp/config.ts +116 -0
- package/src/env/mcp/index.ts +41 -0
- package/src/env/mcp/loader.test.ts +161 -0
- package/src/env/mcp/loader.ts +209 -0
- package/src/env/mcp/mcp-component.test.ts +111 -0
- package/src/env/mcp/mcp-component.ts +358 -0
- package/src/env/mcp/mcp-config-registration.test.ts +304 -0
- package/src/env/mcp/mcp-config-registration.ts +50 -0
- package/src/env/mcp/scanner.test.ts +170 -0
- package/src/env/mcp/scanner.ts +246 -0
- package/src/env/mcp/tool/adapter.test.ts +520 -0
- package/src/env/mcp/tool/adapter.ts +521 -0
- package/src/env/mcp/tool/index.ts +5 -0
- package/src/env/mcp/types.test.ts +171 -0
- package/src/env/mcp/types.ts +79 -0
- package/src/env/memory/README.md +177 -0
- package/src/env/memory/built-in/index.ts +59 -0
- package/src/env/memory/built-in/recall-memory.ts +103 -0
- package/src/env/memory/built-in/record-memory.ts +148 -0
- package/src/env/memory/index.ts +20 -0
- package/src/env/memory/memory-component.test.ts +239 -0
- package/src/env/memory/memory-component.ts +503 -0
- package/src/env/memory/memory-config-registration.test.ts +67 -0
- package/src/env/memory/memory-config-registration.ts +48 -0
- package/src/env/memory/memory-config.ts +45 -0
- package/src/env/memory/memory-file.test.ts +268 -0
- package/src/env/memory/plugin/index.ts +48 -0
- package/src/env/memory/plugin/memory-agent.test.ts +249 -0
- package/src/env/memory/plugin/memory-agent.ts +365 -0
- package/src/env/memory/plugin/memory-manager.ts +198 -0
- package/src/env/memory/plugin/memory-plugin-agent.test.ts +145 -0
- package/src/env/memory/plugin/memory-plugin.ts +210 -0
- package/src/env/memory/plugin/plugin-simplified.test.ts +51 -0
- package/src/env/memory/plugin/recall-memory.test.ts +106 -0
- package/src/env/memory/plugin/recall-memory.ts +53 -0
- package/src/env/memory/plugin/types.ts +101 -0
- package/src/env/memory/tools/memory-agent-tools.ts +228 -0
- package/src/env/memory/types.ts +85 -0
- package/src/env/paths.ts +118 -0
- package/src/env/prompt/index.ts +18 -0
- package/src/env/prompt/memory-prompts.test.ts +91 -0
- package/src/env/prompt/prompt-component.test.ts +491 -0
- package/src/env/prompt/prompt-component.ts +619 -0
- package/src/env/prompt/prompt-config-registration.test.ts +213 -0
- package/src/env/prompt/prompt-config-registration.ts +39 -0
- package/src/env/prompt/prompts-index.ts +504 -0
- package/src/env/prompt/renderer.ts +67 -0
- package/src/env/prompt/types.ts +136 -0
- package/src/env/session/hooks.ts +18 -0
- package/src/env/session/index.ts +37 -0
- package/src/env/session/search-query-parser.test.ts +425 -0
- package/src/env/session/search-query-parser.ts +171 -0
- package/src/env/session/session-checkpoint.test.ts +523 -0
- package/src/env/session/session-component.extract-recent-messages.test.ts +209 -0
- package/src/env/session/session-component.test.ts +132 -0
- package/src/env/session/session-component.ts +1249 -0
- package/src/env/session/session-config-registration.test.ts +138 -0
- package/src/env/session/session-config-registration.ts +52 -0
- package/src/env/session/session-message-converter.test.ts +763 -0
- package/src/env/session/session-message-converter.ts +415 -0
- package/src/env/session/session-message-e2e.test.ts +448 -0
- package/src/env/session/session-search.test.ts +391 -0
- package/src/env/session/session-store.test.ts +362 -0
- package/src/env/session/session-store.ts +141 -0
- package/src/env/session/storage/index.ts +6 -0
- package/src/env/session/storage/memory.ts +502 -0
- package/src/env/session/storage/sqlite.ts +794 -0
- package/src/env/session/types.ts +742 -0
- package/src/env/skill/config.ts +39 -0
- package/src/env/skill/index.ts +6 -0
- package/src/env/skill/parser.test.ts +116 -0
- package/src/env/skill/parser.ts +77 -0
- package/src/env/skill/scanner.test.ts +211 -0
- package/src/env/skill/scanner.ts +119 -0
- package/src/env/skill/skill-component.test.ts +234 -0
- package/src/env/skill/skill-component.ts +352 -0
- package/src/env/skill/skill-config-registration.test.ts +60 -0
- package/src/env/skill/skill-config-registration.ts +43 -0
- package/src/env/skill/tool/index.ts +1 -0
- package/src/env/skill/tool/skill-tool.test.ts +100 -0
- package/src/env/skill/tool/skill-tool.ts +72 -0
- package/src/env/skill/types.ts +64 -0
- package/src/env/task/delegate/delegate-tool.test.ts +498 -0
- package/src/env/task/delegate/delegate-tool.ts +1014 -0
- package/src/env/task/delegate/index.ts +18 -0
- package/src/env/task/delegate/stop-tool.test.ts +140 -0
- package/src/env/task/delegate/stop-tool.ts +119 -0
- package/src/env/task/delegate/task-events.test.ts +178 -0
- package/src/env/task/delegate/task-events.ts +143 -0
- package/src/env/task/hooks/contexts.test.ts +92 -0
- package/src/env/task/hooks/contexts.ts +192 -0
- package/src/env/task/hooks/index.ts +23 -0
- package/src/env/task/hooks/task-hook-points.test.ts +32 -0
- package/src/env/task/hooks/task-hook-points.ts +54 -0
- package/src/env/task/index.ts +7 -0
- package/src/env/task/plugins/index.ts +13 -0
- package/src/env/task/plugins/task-plugin.test.ts +74 -0
- package/src/env/task/plugins/task-plugin.ts +89 -0
- package/src/env/task/plugins/task-tag-plugin.test.ts +377 -0
- package/src/env/task/plugins/task-tag-plugin.ts +319 -0
- package/src/env/task/plugins/task-workflow-extractor.integration.test.ts +226 -0
- package/src/env/task/plugins/workflow-extractor-agent.test.ts +107 -0
- package/src/env/task/plugins/workflow-extractor-agent.ts +225 -0
- package/src/env/task/storage/index.ts +6 -0
- package/src/env/task/storage/sqlite-task-store.test.ts +283 -0
- package/src/env/task/storage/sqlite-task-store.ts +903 -0
- package/src/env/task/storage/task-search.test.ts +291 -0
- package/src/env/task/tag-service.test.ts +198 -0
- package/src/env/task/tag-service.ts +264 -0
- package/src/env/task/task-component.test.ts +193 -0
- package/src/env/task/task-component.ts +658 -0
- package/src/env/task/task-config-registration.test.ts +57 -0
- package/src/env/task/task-config-registration.ts +37 -0
- package/src/env/task/task-types.test.ts +137 -0
- package/src/env/task/tools/complete-tool.ts +44 -0
- package/src/env/task/tools/create-tool.ts +49 -0
- package/src/env/task/tools/delete-tool.ts +43 -0
- package/src/env/task/tools/get-tool.ts +59 -0
- package/src/env/task/tools/index.ts +10 -0
- package/src/env/task/tools/list-tool.ts +40 -0
- package/src/env/task/tools/operation/create-tool.ts +48 -0
- package/src/env/task/tools/operation/delete-tool.ts +43 -0
- package/src/env/task/tools/operation/get-tool.ts +43 -0
- package/src/env/task/tools/operation/index.ts +9 -0
- package/src/env/task/tools/operation/list-tool.ts +40 -0
- package/src/env/task/tools/operation/operation-tools.test.ts +274 -0
- package/src/env/task/tools/operation/operation-types.ts +75 -0
- package/src/env/task/tools/operation/update-tool.ts +47 -0
- package/src/env/task/tools/task-tools.test.ts +203 -0
- package/src/env/task/tools/task-types.test.ts +75 -0
- package/src/env/task/tools/task-types.ts +68 -0
- package/src/env/task/tools/update-tool.ts +70 -0
- package/src/env/task/types.ts +160 -0
- package/src/env/tool/built-in/bash.ts +201 -0
- package/src/env/tool/built-in/echo.ts +29 -0
- package/src/env/tool/built-in/edit-file.test.ts +136 -0
- package/src/env/tool/built-in/edit-file.ts +92 -0
- package/src/env/tool/built-in/glob.test.ts +94 -0
- package/src/env/tool/built-in/glob.ts +65 -0
- package/src/env/tool/built-in/grep.test.ts +122 -0
- package/src/env/tool/built-in/grep.ts +108 -0
- package/src/env/tool/built-in/index.ts +44 -0
- package/src/env/tool/built-in/read-file.test.ts +84 -0
- package/src/env/tool/built-in/read-file.ts +75 -0
- package/src/env/tool/built-in/write-file.test.ts +119 -0
- package/src/env/tool/built-in/write-file.ts +68 -0
- package/src/env/tool/index.ts +24 -0
- package/src/env/tool/registry.test.ts +257 -0
- package/src/env/tool/registry.ts +167 -0
- package/src/env/tool/tool-component.test.ts +559 -0
- package/src/env/tool/tool-component.ts +563 -0
- package/src/env/tool/tool-config-registration.test.ts +249 -0
- package/src/env/tool/tool-config-registration.ts +46 -0
- package/src/env/tool/types.ts +267 -0
- package/src/env/tool/validator.test.ts +143 -0
- package/src/env/tool/validator.ts +44 -0
- package/src/env/types.ts +180 -0
- package/src/env/workflow/ask-user-tool-registration.test.ts +216 -0
- package/src/env/workflow/complex-workflow.integration.test.ts +1900 -0
- package/src/env/workflow/decorators/decorator-node.ts +229 -0
- package/src/env/workflow/decorators/decorator.test.ts +196 -0
- package/src/env/workflow/decorators/edge.ts +82 -0
- package/src/env/workflow/decorators/index.ts +31 -0
- package/src/env/workflow/decorators/node-as.ts +98 -0
- package/src/env/workflow/decorators/workflow.ts +54 -0
- package/src/env/workflow/engine/dag-manager.test.ts +570 -0
- package/src/env/workflow/engine/dag-manager.ts +594 -0
- package/src/env/workflow/engine/engine.ts +1422 -0
- package/src/env/workflow/engine/event-bus.test.ts +359 -0
- package/src/env/workflow/engine/event-bus.ts +156 -0
- package/src/env/workflow/engine/executor-agent-session.test.ts +84 -0
- package/src/env/workflow/engine/executor.test.ts +619 -0
- package/src/env/workflow/engine/executor.ts +593 -0
- package/src/env/workflow/engine/index.ts +24 -0
- package/src/env/workflow/engine/node-registry.test.ts +560 -0
- package/src/env/workflow/engine/node-registry.ts +289 -0
- package/src/env/workflow/engine/resume-removed.test.ts +22 -0
- package/src/env/workflow/engine/scheduler.test.ts +715 -0
- package/src/env/workflow/engine/scheduler.ts +318 -0
- package/src/env/workflow/engine/workflow-engine.test.ts +815 -0
- package/src/env/workflow/extractor/workflow-converter.ts +306 -0
- package/src/env/workflow/fixtures.ts +380 -0
- package/src/env/workflow/index.ts +38 -0
- package/src/env/workflow/integration/run-resume-unified.test.ts +186 -0
- package/src/env/workflow/integration/service-integration.test.ts +267 -0
- package/src/env/workflow/metadata/keys.ts +12 -0
- package/src/env/workflow/nodes/agent-component-adapter.test.ts +318 -0
- package/src/env/workflow/nodes/agent-component-adapter.ts +448 -0
- package/src/env/workflow/nodes/agent-node.test.ts +371 -0
- package/src/env/workflow/nodes/agent-node.ts +598 -0
- package/src/env/workflow/nodes/ask-user-node.ts +113 -0
- package/src/env/workflow/nodes/condition-node.ts +200 -0
- package/src/env/workflow/nodes/index.ts +9 -0
- package/src/env/workflow/nodes/merge-node.ts +141 -0
- package/src/env/workflow/nodes/skill-node.test.ts +253 -0
- package/src/env/workflow/nodes/skill-node.ts +393 -0
- package/src/env/workflow/nodes/tool-node.test.ts +251 -0
- package/src/env/workflow/nodes/tool-node.ts +493 -0
- package/src/env/workflow/nodes/workflow-llm-history.test.ts +455 -0
- package/src/env/workflow/nodes/workflow-node.test.ts +315 -0
- package/src/env/workflow/nodes/workflow-node.ts +311 -0
- package/src/env/workflow/service/index.ts +27 -0
- package/src/env/workflow/service/registry.test.ts +133 -0
- package/src/env/workflow/service/registry.ts +71 -0
- package/src/env/workflow/service/workflow-service.test.ts +310 -0
- package/src/env/workflow/service/workflow-service.ts +393 -0
- package/src/env/workflow/storage/index.ts +28 -0
- package/src/env/workflow/storage/mock-repositories.ts +385 -0
- package/src/env/workflow/storage/sqlite.test.ts +179 -0
- package/src/env/workflow/storage/sqlite.ts +163 -0
- package/src/env/workflow/storage/workflow-repo.test.ts +780 -0
- package/src/env/workflow/storage/workflow-repo.ts +342 -0
- package/src/env/workflow/tools/ask-user-tool.ts +82 -0
- package/src/env/workflow/tools/index.ts +26 -0
- package/src/env/workflow/tools/run-workflow.test.ts +352 -0
- package/src/env/workflow/tools/run-workflow.ts +214 -0
- package/src/env/workflow/types/context.ts +18 -0
- package/src/env/workflow/types/decorators-types.ts +198 -0
- package/src/env/workflow/types/event.test.ts +515 -0
- package/src/env/workflow/types/event.ts +193 -0
- package/src/env/workflow/types/index.ts +49 -0
- package/src/env/workflow/types/run.test.ts +437 -0
- package/src/env/workflow/types/run.ts +173 -0
- package/src/env/workflow/types/workflow-hil.ts +114 -0
- package/src/env/workflow/types/workflow-message.test.ts +138 -0
- package/src/env/workflow/types/workflow-message.ts +196 -0
- package/src/env/workflow/types/workflow-session.test.ts +95 -0
- package/src/env/workflow/types/workflow-session.ts +59 -0
- package/src/env/workflow/types/workflow.test.ts +495 -0
- package/src/env/workflow/types/workflow.ts +195 -0
- package/src/env/workflow/types_compat.ts +51 -0
- package/src/env/workflow/utils/create-workflow.ts +47 -0
- package/src/env/workflow/utils/execution-state.ts +245 -0
- package/src/env/workflow/utils/index.ts +18 -0
- package/src/env/workflow/utils/node-registry-helper.ts +58 -0
- package/src/env/workflow/utils/recovery-validator.test.ts +460 -0
- package/src/env/workflow/utils/recovery-validator.ts +377 -0
- package/src/env/workflow/utils/session-parser.test.ts +111 -0
- package/src/env/workflow/utils/session-parser.ts +94 -0
- package/src/env/workflow/utils/session-recovery.test.ts +334 -0
- package/src/env/workflow/utils/session-recovery.ts +188 -0
- package/src/env/workflow/utils/template-resolver.test.ts +258 -0
- package/src/env/workflow/utils/template-resolver.ts +436 -0
- package/src/env/workflow/utils/validation-rules.ts +149 -0
- package/src/env/workflow/workflow-component.ts +544 -0
- package/src/index.ts +422 -0
- package/src/utils/id.ts +21 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import { ProtocolResolver } from "./protocol-resolver";
|
|
6
|
+
|
|
7
|
+
describe("parseJSONC", () => {
|
|
8
|
+
test("should parse valid JSON", () => {
|
|
9
|
+
// Dynamic import to ensure module is loaded
|
|
10
|
+
const { parseJSONC } = require("./config-parser");
|
|
11
|
+
const content = '{"name": "test", "value": 123}';
|
|
12
|
+
const result = parseJSONC(content);
|
|
13
|
+
expect(result).toEqual({ name: "test", value: 123 });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("should handle single-line comments", () => {
|
|
17
|
+
const { parseJSONC } = require("./config-parser");
|
|
18
|
+
const content = `{
|
|
19
|
+
// This is a comment
|
|
20
|
+
"name": "test"
|
|
21
|
+
}`;
|
|
22
|
+
const result = parseJSONC(content);
|
|
23
|
+
expect(result).toEqual({ name: "test" });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("should handle multi-line comments", () => {
|
|
27
|
+
const { parseJSONC } = require("./config-parser");
|
|
28
|
+
const content = `{
|
|
29
|
+
/* This is
|
|
30
|
+
a multi-line
|
|
31
|
+
comment */
|
|
32
|
+
"name": "test"
|
|
33
|
+
}`;
|
|
34
|
+
const result = parseJSONC(content);
|
|
35
|
+
expect(result).toEqual({ name: "test" });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("should handle trailing comments", () => {
|
|
39
|
+
const { parseJSONC } = require("./config-parser");
|
|
40
|
+
const content = `{
|
|
41
|
+
"name": "test" // trailing comment
|
|
42
|
+
}`;
|
|
43
|
+
const result = parseJSONC(content);
|
|
44
|
+
expect(result).toEqual({ name: "test" });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("should handle mixed comments", () => {
|
|
48
|
+
const { parseJSONC } = require("./config-parser");
|
|
49
|
+
const content = `{
|
|
50
|
+
// Header comment
|
|
51
|
+
"name": "test", // inline comment
|
|
52
|
+
/* Block comment */
|
|
53
|
+
"value": 123
|
|
54
|
+
}`;
|
|
55
|
+
const result = parseJSONC(content);
|
|
56
|
+
expect(result).toEqual({ name: "test", value: 123 });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("should handle empty JSONC file", () => {
|
|
60
|
+
const { parseJSONC } = require("./config-parser");
|
|
61
|
+
const result = parseJSONC("// just a comment\n\n// another comment");
|
|
62
|
+
expect(result).toEqual({});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("substituteEnvVars", () => {
|
|
67
|
+
test("should substitute simple env var", () => {
|
|
68
|
+
const { substituteEnvVars } = require("./config-parser");
|
|
69
|
+
process.env.TEST_VAR = "hello";
|
|
70
|
+
const result = substituteEnvVars("${TEST_VAR}");
|
|
71
|
+
expect(result).toBe("hello");
|
|
72
|
+
delete process.env.TEST_VAR;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("should handle missing env var with empty string", () => {
|
|
76
|
+
const { substituteEnvVars } = require("./config-parser");
|
|
77
|
+
delete process.env.NONEXISTENT_VAR;
|
|
78
|
+
const result = substituteEnvVars("${NONEXISTENT_VAR}");
|
|
79
|
+
expect(result).toBe("");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("should handle default value syntax", () => {
|
|
83
|
+
const { substituteEnvVars } = require("./config-parser");
|
|
84
|
+
delete process.env.NONEXISTENT_VAR;
|
|
85
|
+
const result = substituteEnvVars("${NONEXISTENT_VAR:-fallback}");
|
|
86
|
+
expect(result).toBe("fallback");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("should prefer env var over default", () => {
|
|
90
|
+
const { substituteEnvVars } = require("./config-parser");
|
|
91
|
+
process.env.EXISTING_VAR = "actual";
|
|
92
|
+
const result = substituteEnvVars("${EXISTING_VAR:-default}");
|
|
93
|
+
expect(result).toBe("actual");
|
|
94
|
+
delete process.env.EXISTING_VAR;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("should substitute in object values", () => {
|
|
98
|
+
const { substituteEnvVars } = require("./config-parser");
|
|
99
|
+
process.env.API_KEY = "secret123";
|
|
100
|
+
const result = substituteEnvVars({ apiKey: "${API_KEY}" });
|
|
101
|
+
expect(result).toEqual({ apiKey: "secret123" });
|
|
102
|
+
delete process.env.API_KEY;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("should handle nested objects", () => {
|
|
106
|
+
const { substituteEnvVars } = require("./config-parser");
|
|
107
|
+
process.env.HOST = "localhost";
|
|
108
|
+
process.env.PORT = "3000";
|
|
109
|
+
const result = substituteEnvVars({
|
|
110
|
+
server: { host: "${HOST}", port: "${PORT}" }
|
|
111
|
+
});
|
|
112
|
+
expect(result).toEqual({ server: { host: "localhost", port: "3000" } });
|
|
113
|
+
delete process.env.HOST;
|
|
114
|
+
delete process.env.PORT;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("should preserve non-string values", () => {
|
|
118
|
+
const { substituteEnvVars } = require("./config-parser");
|
|
119
|
+
process.env.NUM = "42";
|
|
120
|
+
const result = substituteEnvVars({ count: "${NUM}", enabled: true, rate: 0.5 });
|
|
121
|
+
expect(result).toEqual({ count: "42", enabled: true, rate: 0.5 });
|
|
122
|
+
delete process.env.NUM;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("should handle arrays", () => {
|
|
126
|
+
const { substituteEnvVars } = require("./config-parser");
|
|
127
|
+
process.env.ITEM1 = "a";
|
|
128
|
+
process.env.ITEM2 = "b";
|
|
129
|
+
const result = substituteEnvVars(["${ITEM1}", "${ITEM2}"]);
|
|
130
|
+
expect(result).toEqual(["a", "b"]);
|
|
131
|
+
delete process.env.ITEM1;
|
|
132
|
+
delete process.env.ITEM2;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("should handle mixed arrays with strings and values", () => {
|
|
136
|
+
const { substituteEnvVars } = require("./config-parser");
|
|
137
|
+
process.env.VALUE = "test";
|
|
138
|
+
const result = substituteEnvVars(["${VALUE}", 123, true, null]);
|
|
139
|
+
expect(result).toEqual(["test", 123, true, null]);
|
|
140
|
+
delete process.env.VALUE;
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe("parseJSONCWithEnv", () => {
|
|
145
|
+
test("should parse JSONC and substitute env vars", () => {
|
|
146
|
+
const { parseJSONCWithEnv } = require("./config-parser");
|
|
147
|
+
process.env.API_KEY = "my-secret";
|
|
148
|
+
const content = '{\n // API Configuration\n "apiKey": "${API_KEY}",\n "model": "gpt-4o"\n }';
|
|
149
|
+
const result = parseJSONCWithEnv(content);
|
|
150
|
+
expect(result).toEqual({ apiKey: "my-secret", model: "gpt-4o" });
|
|
151
|
+
delete process.env.API_KEY;
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe("substituteProtocolRefs", () => {
|
|
156
|
+
const testDir = path.join(__dirname, ".test-protocol-subst");
|
|
157
|
+
let resolver: ProtocolResolver;
|
|
158
|
+
|
|
159
|
+
beforeEach(async () => {
|
|
160
|
+
// 创建测试目录
|
|
161
|
+
await fs.promises.mkdir(testDir, { recursive: true });
|
|
162
|
+
resolver = new ProtocolResolver({ xdgDataHome: testDir });
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
afterEach(async () => {
|
|
166
|
+
// 清理测试目录
|
|
167
|
+
try {
|
|
168
|
+
await fs.promises.rm(testDir, { recursive: true, force: true });
|
|
169
|
+
} catch {
|
|
170
|
+
// ignore
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("should substitute file:// protocol reference", async () => {
|
|
175
|
+
const { substituteProtocolRefs } = require("./config-parser");
|
|
176
|
+
|
|
177
|
+
// 创建测试文件
|
|
178
|
+
const configPath = path.join(testDir, "api.json");
|
|
179
|
+
await fs.promises.writeFile(configPath, JSON.stringify({ key: "file-key-123" }), "utf-8");
|
|
180
|
+
|
|
181
|
+
const result = substituteProtocolRefs("${file://api.json#key}", resolver);
|
|
182
|
+
expect(result).toBe("file-key-123");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("should substitute file:// protocol reference from external file", async () => {
|
|
186
|
+
const { substituteProtocolRefs } = require("./config-parser");
|
|
187
|
+
|
|
188
|
+
const refPath = path.join(testDir, "ref.json");
|
|
189
|
+
await fs.promises.writeFile(refPath, JSON.stringify({ token: "file-token-456" }), "utf-8");
|
|
190
|
+
|
|
191
|
+
const fileResolver = new ProtocolResolver({
|
|
192
|
+
xdgDataHome: testDir,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const result = substituteProtocolRefs("${file://ref.json#token}", fileResolver);
|
|
196
|
+
expect(result).toBe("file-token-456");
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("should substitute env:// protocol reference", () => {
|
|
200
|
+
const { substituteProtocolRefs } = require("./config-parser");
|
|
201
|
+
process.env.PROTOCOL_TEST_VAR = "env-value-789";
|
|
202
|
+
|
|
203
|
+
const result = substituteProtocolRefs("${env://PROTOCOL_TEST_VAR}", resolver);
|
|
204
|
+
expect(result).toBe("env-value-789");
|
|
205
|
+
|
|
206
|
+
delete process.env.PROTOCOL_TEST_VAR;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("should preserve original value when protocol reference is invalid", () => {
|
|
210
|
+
const { substituteProtocolRefs } = require("./config-parser");
|
|
211
|
+
|
|
212
|
+
const result = substituteProtocolRefs("plain text", resolver);
|
|
213
|
+
expect(result).toBe("plain text");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("should preserve original value when file does not exist", () => {
|
|
217
|
+
const { substituteProtocolRefs } = require("./config-parser");
|
|
218
|
+
|
|
219
|
+
const result = substituteProtocolRefs("${file://nonexistent.json#key}", resolver);
|
|
220
|
+
expect(result).toBe("${file://nonexistent.json#key}");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("should substitute in object values", async () => {
|
|
224
|
+
const { substituteProtocolRefs } = require("./config-parser");
|
|
225
|
+
|
|
226
|
+
const configPath = path.join(testDir, "server.json");
|
|
227
|
+
await fs.promises.writeFile(configPath, JSON.stringify({ host: "localhost", port: 8080 }), "utf-8");
|
|
228
|
+
|
|
229
|
+
const result = substituteProtocolRefs(
|
|
230
|
+
{ api: "${file://server.json#host}", port: "${file://server.json#port}" },
|
|
231
|
+
resolver
|
|
232
|
+
);
|
|
233
|
+
expect(result).toEqual({ api: "localhost", port: 8080 });
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("should handle nested objects", async () => {
|
|
237
|
+
const { substituteProtocolRefs } = require("./config-parser");
|
|
238
|
+
|
|
239
|
+
const configPath = path.join(testDir, "nested.json");
|
|
240
|
+
await fs.promises.writeFile(configPath, JSON.stringify({
|
|
241
|
+
db: { host: "db.example.com", credentials: { user: "admin" } }
|
|
242
|
+
}), "utf-8");
|
|
243
|
+
|
|
244
|
+
const result = substituteProtocolRefs(
|
|
245
|
+
{ connection: "${file://nested.json#db.host}", user: "${file://nested.json#db.credentials.user}" },
|
|
246
|
+
resolver
|
|
247
|
+
);
|
|
248
|
+
expect(result).toEqual({ connection: "db.example.com", user: "admin" });
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("should preserve non-string values", () => {
|
|
252
|
+
const { substituteProtocolRefs } = require("./config-parser");
|
|
253
|
+
|
|
254
|
+
const result = substituteProtocolRefs(
|
|
255
|
+
{ count: 123, enabled: true, data: null },
|
|
256
|
+
resolver
|
|
257
|
+
);
|
|
258
|
+
expect(result).toEqual({ count: 123, enabled: true, data: null });
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test("should handle arrays with protocol references", async () => {
|
|
262
|
+
const { substituteProtocolRefs } = require("./config-parser");
|
|
263
|
+
|
|
264
|
+
const configPath = path.join(testDir, "items.json");
|
|
265
|
+
await fs.promises.writeFile(configPath, JSON.stringify({ item1: "a", item2: "b" }), "utf-8");
|
|
266
|
+
|
|
267
|
+
const result = substituteProtocolRefs(
|
|
268
|
+
["${file://items.json#item1}", "${file://items.json#item2}"],
|
|
269
|
+
resolver
|
|
270
|
+
);
|
|
271
|
+
expect(result).toEqual(["a", "b"]);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("should handle mixed arrays with protocol references and regular values", async () => {
|
|
275
|
+
const { substituteProtocolRefs } = require("./config-parser");
|
|
276
|
+
|
|
277
|
+
const configPath = path.join(testDir, "mixed.json");
|
|
278
|
+
await fs.promises.writeFile(configPath, JSON.stringify({ value: "from-file" }), "utf-8");
|
|
279
|
+
|
|
280
|
+
const result = substituteProtocolRefs(
|
|
281
|
+
["${file://mixed.json#value}", 123, true, "plain"],
|
|
282
|
+
resolver
|
|
283
|
+
);
|
|
284
|
+
expect(result).toEqual(["from-file", 123, true, "plain"]);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe("parseJSONCWithProtocols", () => {
|
|
289
|
+
const testDir = path.join(__dirname, ".test-protocol-parse");
|
|
290
|
+
let resolver: ProtocolResolver;
|
|
291
|
+
|
|
292
|
+
beforeEach(async () => {
|
|
293
|
+
await fs.promises.mkdir(testDir, { recursive: true });
|
|
294
|
+
resolver = new ProtocolResolver({ xdgDataHome: testDir });
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
afterEach(async () => {
|
|
298
|
+
try {
|
|
299
|
+
await fs.promises.rm(testDir, { recursive: true, force: true });
|
|
300
|
+
} catch {
|
|
301
|
+
// ignore
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
test("should parse JSONC and substitute protocol refs", async () => {
|
|
306
|
+
// 注意:JSONC 的单行注释解析器会错误处理字符串内部的 "//" 语法
|
|
307
|
+
// 所以这里使用 substituteProtocolRefs 直接测试,绕过 JSONC 解析
|
|
308
|
+
const { substituteProtocolRefs } = require("./config-parser");
|
|
309
|
+
|
|
310
|
+
const configPath = path.join(testDir, "api.json");
|
|
311
|
+
await fs.promises.writeFile(configPath, JSON.stringify({ key: "secret-key" }), "utf-8");
|
|
312
|
+
|
|
313
|
+
// 先创建已解析的 JSON 对象(模拟 parseJSONC 的结果)
|
|
314
|
+
const parsed = { apiKey: "${file://api.json#key}", model: "gpt-4o" };
|
|
315
|
+
|
|
316
|
+
const result = substituteProtocolRefs(parsed, resolver);
|
|
317
|
+
expect(result).toEqual({ apiKey: "secret-key", model: "gpt-4o" });
|
|
318
|
+
});
|
|
319
|
+
});
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Parser
|
|
3
|
+
*
|
|
4
|
+
* Provides JSONC parsing and environment/protocol variable substitution
|
|
5
|
+
*/
|
|
6
|
+
import { parse as parseJSONCInternal, ParseErrorCode } from "jsonc-parser";
|
|
7
|
+
import { ProtocolResolver } from "./protocol-resolver";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Parse JSONC (JSON with Comments) content
|
|
11
|
+
* Supports single-line comments (//) and multi-line comments
|
|
12
|
+
*
|
|
13
|
+
* @param content - JSONC content string
|
|
14
|
+
* @returns Parsed JSON object
|
|
15
|
+
*/
|
|
16
|
+
export function parseJSONC(content: string): Record<string, unknown> {
|
|
17
|
+
const errors: any[] = [];
|
|
18
|
+
const result = parseJSONCInternal(content, errors, {
|
|
19
|
+
allowTrailingComma: true,
|
|
20
|
+
disallowComments: false,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (errors.length > 0) {
|
|
24
|
+
// 过滤掉"没有值"的警告(文件只有注释时会出现)
|
|
25
|
+
// 只有当 result 是 undefined 且只有一个 ValueExpected 错误时才忽略
|
|
26
|
+
const hasOnlyValueExpected =
|
|
27
|
+
result === undefined &&
|
|
28
|
+
errors.length === 1 &&
|
|
29
|
+
errors[0].error === ParseErrorCode.ValueExpected;
|
|
30
|
+
|
|
31
|
+
if (!hasOnlyValueExpected) {
|
|
32
|
+
const error = errors[0];
|
|
33
|
+
const offset = error.offset;
|
|
34
|
+
const lines = content.substring(0, offset).split("\n");
|
|
35
|
+
const line = lines.length;
|
|
36
|
+
const col = lines[lines.length - 1].length + 1;
|
|
37
|
+
|
|
38
|
+
const errorMessages: Record<number, string> = {
|
|
39
|
+
[ParseErrorCode.UnexpectedEndOfString]: "Unexpected end of string",
|
|
40
|
+
[ParseErrorCode.InvalidCharacter]: "Invalid character",
|
|
41
|
+
[ParseErrorCode.PropertyNameExpected]: "Property name expected",
|
|
42
|
+
[ParseErrorCode.ValueExpected]: "Value expected",
|
|
43
|
+
[ParseErrorCode.ColonExpected]: "Colon expected",
|
|
44
|
+
[ParseErrorCode.CommaExpected]: "Comma expected",
|
|
45
|
+
[ParseErrorCode.CloseBraceExpected]: "Close brace expected",
|
|
46
|
+
[ParseErrorCode.CloseBracketExpected]: "Close bracket expected",
|
|
47
|
+
[ParseErrorCode.EndOfFileExpected]: "Unexpected end of input",
|
|
48
|
+
[ParseErrorCode.InvalidSymbol]: "Invalid symbol",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
throw new Error(
|
|
52
|
+
`JSONC parse error at line ${line}, col ${col}: ${errorMessages[error.error] || "Unknown error"}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 处理空内容(只有注释的情况)
|
|
58
|
+
if (result === undefined || result === null) {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return result as Record<string, unknown>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Substitute environment variables in a value
|
|
67
|
+
* Supports ${VAR} and ${VAR:-default} syntax
|
|
68
|
+
*
|
|
69
|
+
* @param value - Value to substitute (string, object, array, or primitive)
|
|
70
|
+
* @returns Value with environment variables substituted
|
|
71
|
+
*/
|
|
72
|
+
export function substituteEnvVars(value: unknown): unknown {
|
|
73
|
+
if (typeof value === "string") {
|
|
74
|
+
return value.replace(/\$\{([^}]+)\}/g, (match, varExpr) => {
|
|
75
|
+
const [varName, defaultValue] = varExpr.split(":-");
|
|
76
|
+
const envValue = process.env[varName.trim()];
|
|
77
|
+
if (envValue !== undefined) {
|
|
78
|
+
return envValue;
|
|
79
|
+
}
|
|
80
|
+
if (defaultValue !== undefined) {
|
|
81
|
+
return defaultValue;
|
|
82
|
+
}
|
|
83
|
+
return "";
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (Array.isArray(value)) {
|
|
88
|
+
return value.map(item => substituteEnvVars(item));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (value !== null && typeof value === "object") {
|
|
92
|
+
const result: Record<string, unknown> = {};
|
|
93
|
+
for (const [key, val] of Object.entries(value)) {
|
|
94
|
+
result[key] = substituteEnvVars(val);
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parse JSONC content and substitute environment variables
|
|
104
|
+
*
|
|
105
|
+
* @param content - JSONC content string
|
|
106
|
+
* @returns Parsed JSON object with env vars substituted
|
|
107
|
+
*/
|
|
108
|
+
export function parseJSONCWithEnv(content: string): Record<string, unknown> {
|
|
109
|
+
const parsed = parseJSONC(content);
|
|
110
|
+
return substituteEnvVars(parsed) as Record<string, unknown>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Protocol reference pattern - 支持带默认值的协议引用
|
|
115
|
+
* 格式: ${protocol://path#key:-default} 或 ${protocol://path:-default}
|
|
116
|
+
*/
|
|
117
|
+
const PROTOCOL_REF_PATTERN = /^\$\{([^:]+):\/\/[^}]+\}$/;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Protocol reference with default value pattern
|
|
121
|
+
* 格式: ${protocol://path#key:-default}
|
|
122
|
+
*/
|
|
123
|
+
const PROTOCOL_REF_WITH_DEFAULT_PATTERN = /^\$\{([^:]+):\/\/([^:#]+)(?:#([^:-]+))?(?::-(.*))?\}$/;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Check if a string is a protocol reference
|
|
127
|
+
*/
|
|
128
|
+
function isProtocolRefString(value: string): boolean {
|
|
129
|
+
return PROTOCOL_REF_PATTERN.test(value);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Extract default value from protocol reference string
|
|
134
|
+
*/
|
|
135
|
+
function extractDefaultValue(ref: string): string | undefined {
|
|
136
|
+
const match = ref.match(PROTOCOL_REF_WITH_DEFAULT_PATTERN);
|
|
137
|
+
return match ? match[4] : undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Substitute protocol references in a value using the provided resolver
|
|
142
|
+
*
|
|
143
|
+
* Supports:
|
|
144
|
+
* - ${file://path#key} - Read from file
|
|
145
|
+
* - ${env://varName} - Read from environment variable
|
|
146
|
+
* - All above with default values: ${protocol://...:-default}
|
|
147
|
+
*
|
|
148
|
+
* @param value - Value to substitute (string, object, array, or primitive)
|
|
149
|
+
* @param resolver - ProtocolResolver instance
|
|
150
|
+
* @returns Value with protocol references substituted
|
|
151
|
+
*/
|
|
152
|
+
export function substituteProtocolRefs(
|
|
153
|
+
value: unknown,
|
|
154
|
+
resolver: ProtocolResolver
|
|
155
|
+
): unknown {
|
|
156
|
+
if (typeof value === "string") {
|
|
157
|
+
// 如果整个字符串就是协议引用,尝试解析
|
|
158
|
+
if (isProtocolRefString(value)) {
|
|
159
|
+
const result = resolver.resolve(value);
|
|
160
|
+
if (result !== null) {
|
|
161
|
+
return result.value;
|
|
162
|
+
}
|
|
163
|
+
// 解析失败,检查是否有默认值
|
|
164
|
+
const defaultValue = extractDefaultValue(value);
|
|
165
|
+
if (defaultValue !== undefined) {
|
|
166
|
+
return defaultValue;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return value;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (Array.isArray(value)) {
|
|
173
|
+
return value.map(item => substituteProtocolRefs(item, resolver));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (value !== null && typeof value === "object") {
|
|
177
|
+
const result: Record<string, unknown> = {};
|
|
178
|
+
for (const [key, val] of Object.entries(value)) {
|
|
179
|
+
result[key] = substituteProtocolRefs(val, resolver);
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Parse JSONC content and substitute protocol references
|
|
189
|
+
*
|
|
190
|
+
* @param content - JSONC content string
|
|
191
|
+
* @param resolver - ProtocolResolver instance
|
|
192
|
+
* @returns Parsed JSON object with protocol refs substituted
|
|
193
|
+
*/
|
|
194
|
+
export function parseJSONCWithProtocols(
|
|
195
|
+
content: string,
|
|
196
|
+
resolver: ProtocolResolver
|
|
197
|
+
): Record<string, unknown> {
|
|
198
|
+
const parsed = parseJSONC(content);
|
|
199
|
+
// 先解析协议引用(保护协议引用不被环境变量替换破坏)
|
|
200
|
+
const withProtocols = substituteProtocolRefs(parsed, resolver) as Record<string, unknown>;
|
|
201
|
+
// 再解析环境变量(只处理非协议引用的字符串)
|
|
202
|
+
return substituteEnvVars(withProtocols) as Record<string, unknown>;
|
|
203
|
+
}
|