@ai-setting/roy-agent-core 1.3.10 → 1.3.14
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/config/index.js +1647 -0
- package/dist/index.js +12579 -89691
- package/package.json +19 -56
- package/src/config/config-component.test.ts +0 -627
- package/src/config/config-component.ts +0 -906
- package/src/config/config-parser.test.ts +0 -319
- package/src/config/config-parser.ts +0 -203
- package/src/config/decentralized-config.test.ts +0 -740
- package/src/config/env-key.ts +0 -210
- package/src/config/env-source.test.ts +0 -252
- package/src/config/env-source.ts +0 -301
- package/src/config/file-source.test.ts +0 -357
- package/src/config/file-source.ts +0 -421
- package/src/config/index.ts +0 -24
- package/src/config/protocol-resolver.test.ts +0 -217
- package/src/config/protocol-resolver.ts +0 -228
- package/src/env/agent/agent-component.abort.test.ts +0 -511
- package/src/env/agent/agent-component.record-session.test.ts +0 -349
- package/src/env/agent/agent-component.test.ts +0 -1389
- package/src/env/agent/agent-component.tool-error.test.ts +0 -327
- package/src/env/agent/agent-component.ts +0 -1711
- package/src/env/agent/agent-config-registration.test.ts +0 -226
- package/src/env/agent/agent-config-registration.ts +0 -46
- package/src/env/agent/agent-reminder-plugin.integration.test.ts +0 -243
- package/src/env/agent/index.ts +0 -10
- package/src/env/agent/summary-agent.parse-hint.test.ts +0 -360
- package/src/env/agent/summary-agent.ts +0 -508
- package/src/env/agent/types.ts +0 -536
- package/src/env/commands/commands-component.test.ts +0 -364
- package/src/env/commands/commands-component.ts +0 -604
- package/src/env/commands/commands-config-registration.test.ts +0 -198
- package/src/env/commands/commands-config-registration.ts +0 -38
- package/src/env/commands/index.ts +0 -21
- package/src/env/commands/parser.test.ts +0 -203
- package/src/env/commands/parser.ts +0 -115
- package/src/env/commands/types.ts +0 -184
- package/src/env/commands-prompt-integration.test.ts +0 -243
- package/src/env/component-env.test.ts +0 -119
- package/src/env/component.ts +0 -335
- package/src/env/constants.test.ts +0 -72
- package/src/env/constants.ts +0 -123
- package/src/env/debug/debug-component.test.ts +0 -114
- package/src/env/debug/debug-component.ts +0 -547
- package/src/env/debug/formatters/index.ts +0 -9
- package/src/env/debug/formatters/repl-formatter.test.ts +0 -139
- package/src/env/debug/formatters/repl-formatter.ts +0 -358
- package/src/env/debug/formatters/trace-formatter.test.ts +0 -119
- package/src/env/debug/formatters/trace-formatter.ts +0 -191
- package/src/env/debug/formatters/tree-formatter.test.ts +0 -107
- package/src/env/debug/formatters/tree-formatter.ts +0 -325
- package/src/env/debug/index.ts +0 -38
- package/src/env/debug/parser/regex-parser.test.ts +0 -201
- package/src/env/debug/parser/regex-parser.ts +0 -196
- package/src/env/debug/parser/span-builder.test.ts +0 -241
- package/src/env/debug/parser/span-builder.ts +0 -386
- package/src/env/debug/reader/log-reader.test.ts +0 -170
- package/src/env/debug/reader/log-reader.ts +0 -186
- package/src/env/debug/reader/span-db-reader.test.ts +0 -118
- package/src/env/debug/reader/span-db-reader.ts +0 -201
- package/src/env/debug/types.test.ts +0 -187
- package/src/env/debug/types.ts +0 -171
- package/src/env/environment-init.test.ts +0 -183
- package/src/env/environment-lifecycle.test.ts +0 -516
- package/src/env/environment-service.test.ts +0 -332
- package/src/env/environment.handle-query.test.ts +0 -96
- package/src/env/environment.test.ts +0 -232
- package/src/env/environment.ts +0 -708
- package/src/env/errors.test.ts +0 -165
- package/src/env/errors.ts +0 -157
- package/src/env/event-source/event-source-agent-handler.test.ts +0 -193
- package/src/env/event-source/event-source-agent-handler.ts +0 -111
- package/src/env/event-source/event-source-component.process-cleanup.test.ts +0 -236
- package/src/env/event-source/event-source-component.stop.test.ts +0 -346
- package/src/env/event-source/event-source-component.test.ts +0 -1207
- package/src/env/event-source/event-source-component.ts +0 -1379
- package/src/env/event-source/event-source-config-registration.test.ts +0 -242
- package/src/env/event-source/event-source-config-registration.ts +0 -37
- package/src/env/event-source/event-source-integration.test.ts +0 -320
- package/src/env/event-source/event-source-platform.test.ts +0 -630
- package/src/env/event-source/types.ts +0 -298
- package/src/env/hook/global-hook-manager.ts +0 -162
- package/src/env/hook/hook-manager.test.ts +0 -374
- package/src/env/hook/hook-manager.ts +0 -309
- package/src/env/hook/index.ts +0 -38
- package/src/env/hook/types.ts +0 -138
- package/src/env/index.ts +0 -144
- package/src/env/interface.ts +0 -203
- package/src/env/llm/hooks.test.ts +0 -293
- package/src/env/llm/hooks.ts +0 -316
- package/src/env/llm/index.ts +0 -61
- package/src/env/llm/invoke-threshold-check.test.ts +0 -88
- package/src/env/llm/invoke-timeout.test.ts +0 -54
- package/src/env/llm/invoke.test.ts +0 -71
- package/src/env/llm/invoke.ts +0 -1039
- package/src/env/llm/llm-config.test.ts +0 -523
- package/src/env/llm/llm.test.ts +0 -233
- package/src/env/llm/llm.ts +0 -568
- package/src/env/llm/provider.test.ts +0 -182
- package/src/env/llm/provider.ts +0 -108
- package/src/env/llm/transform.test.ts +0 -251
- package/src/env/llm/transform.ts +0 -286
- package/src/env/llm/types.test.ts +0 -580
- package/src/env/llm/types.ts +0 -424
- package/src/env/log-trace/decorator-otel.test.ts +0 -182
- package/src/env/log-trace/decorator.ts +0 -230
- package/src/env/log-trace/index.ts +0 -79
- package/src/env/log-trace/log-trace-component.test.ts +0 -242
- package/src/env/log-trace/log-trace-component.ts +0 -497
- package/src/env/log-trace/log-trace-config-registration.test.ts +0 -348
- package/src/env/log-trace/log-trace-config-registration.ts +0 -45
- package/src/env/log-trace/logger.test.ts +0 -149
- package/src/env/log-trace/logger.ts +0 -522
- package/src/env/log-trace/opentelemetry/cli-propagation.test.ts +0 -147
- package/src/env/log-trace/opentelemetry/cli-propagation.ts +0 -194
- package/src/env/log-trace/opentelemetry/integration.test.ts +0 -668
- package/src/env/log-trace/opentelemetry/mod.ts +0 -25
- package/src/env/log-trace/opentelemetry/propagation-env.test.ts +0 -181
- package/src/env/log-trace/opentelemetry/propagation-env.ts +0 -136
- package/src/env/log-trace/opentelemetry/propagation.test.ts +0 -259
- package/src/env/log-trace/opentelemetry/propagation.ts +0 -215
- package/src/env/log-trace/opentelemetry/tracer-provider-context.test.ts +0 -166
- package/src/env/log-trace/opentelemetry/tracer-provider.test.ts +0 -379
- package/src/env/log-trace/opentelemetry/tracer-provider.ts +0 -612
- package/src/env/log-trace/span-storage.test.ts +0 -145
- package/src/env/log-trace/span-storage.ts +0 -230
- package/src/env/log-trace/trace-context.test.ts +0 -187
- package/src/env/log-trace/trace-context.ts +0 -162
- package/src/env/log-trace/types.test.ts +0 -63
- package/src/env/log-trace/types.ts +0 -172
- package/src/env/mcp/README.md +0 -244
- package/src/env/mcp/__integration__/mcp-component.integration.test.ts +0 -373
- package/src/env/mcp/config.test.ts +0 -74
- package/src/env/mcp/config.ts +0 -116
- package/src/env/mcp/index.ts +0 -41
- package/src/env/mcp/loader.test.ts +0 -161
- package/src/env/mcp/loader.ts +0 -209
- package/src/env/mcp/mcp-component.test.ts +0 -111
- package/src/env/mcp/mcp-component.ts +0 -358
- package/src/env/mcp/mcp-config-registration.test.ts +0 -304
- package/src/env/mcp/mcp-config-registration.ts +0 -50
- package/src/env/mcp/scanner.test.ts +0 -170
- package/src/env/mcp/scanner.ts +0 -246
- package/src/env/mcp/tool/adapter.test.ts +0 -520
- package/src/env/mcp/tool/adapter.ts +0 -521
- package/src/env/mcp/tool/index.ts +0 -5
- package/src/env/mcp/types.test.ts +0 -171
- package/src/env/mcp/types.ts +0 -79
- package/src/env/memory/README.md +0 -177
- package/src/env/memory/built-in/index.ts +0 -59
- package/src/env/memory/built-in/recall-memory.ts +0 -103
- package/src/env/memory/built-in/record-memory.ts +0 -148
- package/src/env/memory/index.ts +0 -20
- package/src/env/memory/memory-component.test.ts +0 -239
- package/src/env/memory/memory-component.ts +0 -503
- package/src/env/memory/memory-config-registration.test.ts +0 -67
- package/src/env/memory/memory-config-registration.ts +0 -48
- package/src/env/memory/memory-config.ts +0 -45
- package/src/env/memory/memory-file.test.ts +0 -268
- package/src/env/memory/plugin/index.ts +0 -48
- package/src/env/memory/plugin/memory-agent.test.ts +0 -249
- package/src/env/memory/plugin/memory-agent.ts +0 -365
- package/src/env/memory/plugin/memory-manager.ts +0 -198
- package/src/env/memory/plugin/memory-plugin-agent.test.ts +0 -145
- package/src/env/memory/plugin/memory-plugin.ts +0 -210
- package/src/env/memory/plugin/plugin-simplified.test.ts +0 -51
- package/src/env/memory/plugin/recall-memory.test.ts +0 -106
- package/src/env/memory/plugin/recall-memory.ts +0 -53
- package/src/env/memory/plugin/types.ts +0 -101
- package/src/env/memory/tools/memory-agent-tools.ts +0 -228
- package/src/env/memory/types.ts +0 -85
- package/src/env/paths.ts +0 -118
- package/src/env/prompt/index.ts +0 -18
- package/src/env/prompt/memory-prompts.test.ts +0 -91
- package/src/env/prompt/prompt-component.test.ts +0 -491
- package/src/env/prompt/prompt-component.ts +0 -619
- package/src/env/prompt/prompt-config-registration.test.ts +0 -213
- package/src/env/prompt/prompt-config-registration.ts +0 -39
- package/src/env/prompt/prompts-index.ts +0 -504
- package/src/env/prompt/renderer.ts +0 -67
- package/src/env/prompt/types.ts +0 -136
- package/src/env/session/hooks.ts +0 -18
- package/src/env/session/index.ts +0 -37
- package/src/env/session/search-query-parser.test.ts +0 -425
- package/src/env/session/search-query-parser.ts +0 -171
- package/src/env/session/session-checkpoint.test.ts +0 -523
- package/src/env/session/session-component.extract-recent-messages.test.ts +0 -209
- package/src/env/session/session-component.test.ts +0 -132
- package/src/env/session/session-component.ts +0 -1249
- package/src/env/session/session-config-registration.test.ts +0 -138
- package/src/env/session/session-config-registration.ts +0 -52
- package/src/env/session/session-message-converter.test.ts +0 -763
- package/src/env/session/session-message-converter.ts +0 -415
- package/src/env/session/session-message-e2e.test.ts +0 -448
- package/src/env/session/session-search.test.ts +0 -391
- package/src/env/session/session-store.test.ts +0 -362
- package/src/env/session/session-store.ts +0 -141
- package/src/env/session/storage/index.ts +0 -6
- package/src/env/session/storage/memory.ts +0 -502
- package/src/env/session/storage/sqlite.ts +0 -794
- package/src/env/session/types.ts +0 -742
- package/src/env/skill/config.ts +0 -39
- package/src/env/skill/index.ts +0 -6
- package/src/env/skill/parser.test.ts +0 -116
- package/src/env/skill/parser.ts +0 -77
- package/src/env/skill/scanner.test.ts +0 -211
- package/src/env/skill/scanner.ts +0 -119
- package/src/env/skill/skill-component.test.ts +0 -234
- package/src/env/skill/skill-component.ts +0 -352
- package/src/env/skill/skill-config-registration.test.ts +0 -60
- package/src/env/skill/skill-config-registration.ts +0 -43
- package/src/env/skill/tool/index.ts +0 -1
- package/src/env/skill/tool/skill-tool.test.ts +0 -100
- package/src/env/skill/tool/skill-tool.ts +0 -72
- package/src/env/skill/types.ts +0 -64
- package/src/env/task/delegate/delegate-tool.test.ts +0 -498
- package/src/env/task/delegate/delegate-tool.ts +0 -1014
- package/src/env/task/delegate/index.ts +0 -18
- package/src/env/task/delegate/stop-tool.test.ts +0 -140
- package/src/env/task/delegate/stop-tool.ts +0 -119
- package/src/env/task/delegate/task-events.test.ts +0 -178
- package/src/env/task/delegate/task-events.ts +0 -143
- package/src/env/task/hooks/contexts.test.ts +0 -92
- package/src/env/task/hooks/contexts.ts +0 -192
- package/src/env/task/hooks/index.ts +0 -23
- package/src/env/task/hooks/task-hook-points.test.ts +0 -32
- package/src/env/task/hooks/task-hook-points.ts +0 -54
- package/src/env/task/index.ts +0 -7
- package/src/env/task/plugins/index.ts +0 -13
- package/src/env/task/plugins/task-plugin.test.ts +0 -74
- package/src/env/task/plugins/task-plugin.ts +0 -89
- package/src/env/task/plugins/task-tag-plugin.test.ts +0 -377
- package/src/env/task/plugins/task-tag-plugin.ts +0 -319
- package/src/env/task/plugins/task-workflow-extractor.integration.test.ts +0 -226
- package/src/env/task/plugins/workflow-extractor-agent.test.ts +0 -107
- package/src/env/task/plugins/workflow-extractor-agent.ts +0 -225
- package/src/env/task/storage/index.ts +0 -6
- package/src/env/task/storage/sqlite-task-store.test.ts +0 -283
- package/src/env/task/storage/sqlite-task-store.ts +0 -903
- package/src/env/task/storage/task-search.test.ts +0 -291
- package/src/env/task/tag-service.test.ts +0 -198
- package/src/env/task/tag-service.ts +0 -264
- package/src/env/task/task-component.test.ts +0 -193
- package/src/env/task/task-component.ts +0 -658
- package/src/env/task/task-config-registration.test.ts +0 -57
- package/src/env/task/task-config-registration.ts +0 -37
- package/src/env/task/task-types.test.ts +0 -137
- package/src/env/task/tools/complete-tool.ts +0 -44
- package/src/env/task/tools/create-tool.ts +0 -49
- package/src/env/task/tools/delete-tool.ts +0 -43
- package/src/env/task/tools/get-tool.ts +0 -59
- package/src/env/task/tools/index.ts +0 -10
- package/src/env/task/tools/list-tool.ts +0 -40
- package/src/env/task/tools/operation/create-tool.ts +0 -48
- package/src/env/task/tools/operation/delete-tool.ts +0 -43
- package/src/env/task/tools/operation/get-tool.ts +0 -43
- package/src/env/task/tools/operation/index.ts +0 -9
- package/src/env/task/tools/operation/list-tool.ts +0 -40
- package/src/env/task/tools/operation/operation-tools.test.ts +0 -274
- package/src/env/task/tools/operation/operation-types.ts +0 -75
- package/src/env/task/tools/operation/update-tool.ts +0 -47
- package/src/env/task/tools/task-tools.test.ts +0 -203
- package/src/env/task/tools/task-types.test.ts +0 -75
- package/src/env/task/tools/task-types.ts +0 -68
- package/src/env/task/tools/update-tool.ts +0 -70
- package/src/env/task/types.ts +0 -160
- package/src/env/tool/built-in/bash.ts +0 -201
- package/src/env/tool/built-in/echo.ts +0 -29
- package/src/env/tool/built-in/edit-file.test.ts +0 -136
- package/src/env/tool/built-in/edit-file.ts +0 -92
- package/src/env/tool/built-in/glob.test.ts +0 -94
- package/src/env/tool/built-in/glob.ts +0 -65
- package/src/env/tool/built-in/grep.test.ts +0 -122
- package/src/env/tool/built-in/grep.ts +0 -108
- package/src/env/tool/built-in/index.ts +0 -44
- package/src/env/tool/built-in/read-file.test.ts +0 -84
- package/src/env/tool/built-in/read-file.ts +0 -75
- package/src/env/tool/built-in/write-file.test.ts +0 -119
- package/src/env/tool/built-in/write-file.ts +0 -68
- package/src/env/tool/index.ts +0 -24
- package/src/env/tool/registry.test.ts +0 -257
- package/src/env/tool/registry.ts +0 -167
- package/src/env/tool/tool-component.test.ts +0 -559
- package/src/env/tool/tool-component.ts +0 -563
- package/src/env/tool/tool-config-registration.test.ts +0 -249
- package/src/env/tool/tool-config-registration.ts +0 -46
- package/src/env/tool/types.ts +0 -267
- package/src/env/tool/validator.test.ts +0 -143
- package/src/env/tool/validator.ts +0 -44
- package/src/env/types.ts +0 -180
- package/src/env/workflow/ask-user-tool-registration.test.ts +0 -216
- package/src/env/workflow/complex-workflow.integration.test.ts +0 -1900
- package/src/env/workflow/decorators/decorator-node.ts +0 -229
- package/src/env/workflow/decorators/decorator.test.ts +0 -196
- package/src/env/workflow/decorators/edge.ts +0 -82
- package/src/env/workflow/decorators/index.ts +0 -31
- package/src/env/workflow/decorators/node-as.ts +0 -98
- package/src/env/workflow/decorators/workflow.ts +0 -54
- package/src/env/workflow/engine/dag-manager.test.ts +0 -570
- package/src/env/workflow/engine/dag-manager.ts +0 -594
- package/src/env/workflow/engine/engine.ts +0 -1422
- package/src/env/workflow/engine/event-bus.test.ts +0 -359
- package/src/env/workflow/engine/event-bus.ts +0 -156
- package/src/env/workflow/engine/executor-agent-session.test.ts +0 -84
- package/src/env/workflow/engine/executor.test.ts +0 -619
- package/src/env/workflow/engine/executor.ts +0 -593
- package/src/env/workflow/engine/index.ts +0 -24
- package/src/env/workflow/engine/node-registry.test.ts +0 -560
- package/src/env/workflow/engine/node-registry.ts +0 -289
- package/src/env/workflow/engine/resume-removed.test.ts +0 -22
- package/src/env/workflow/engine/scheduler.test.ts +0 -715
- package/src/env/workflow/engine/scheduler.ts +0 -318
- package/src/env/workflow/engine/workflow-engine.test.ts +0 -815
- package/src/env/workflow/extractor/workflow-converter.ts +0 -306
- package/src/env/workflow/fixtures.ts +0 -380
- package/src/env/workflow/index.ts +0 -38
- package/src/env/workflow/integration/run-resume-unified.test.ts +0 -186
- package/src/env/workflow/integration/service-integration.test.ts +0 -267
- package/src/env/workflow/metadata/keys.ts +0 -12
- package/src/env/workflow/nodes/agent-component-adapter.test.ts +0 -318
- package/src/env/workflow/nodes/agent-component-adapter.ts +0 -448
- package/src/env/workflow/nodes/agent-node.test.ts +0 -371
- package/src/env/workflow/nodes/agent-node.ts +0 -598
- package/src/env/workflow/nodes/ask-user-node.ts +0 -113
- package/src/env/workflow/nodes/condition-node.ts +0 -200
- package/src/env/workflow/nodes/index.ts +0 -9
- package/src/env/workflow/nodes/merge-node.ts +0 -141
- package/src/env/workflow/nodes/skill-node.test.ts +0 -253
- package/src/env/workflow/nodes/skill-node.ts +0 -393
- package/src/env/workflow/nodes/tool-node.test.ts +0 -251
- package/src/env/workflow/nodes/tool-node.ts +0 -493
- package/src/env/workflow/nodes/workflow-llm-history.test.ts +0 -455
- package/src/env/workflow/nodes/workflow-node.test.ts +0 -315
- package/src/env/workflow/nodes/workflow-node.ts +0 -311
- package/src/env/workflow/service/index.ts +0 -27
- package/src/env/workflow/service/registry.test.ts +0 -133
- package/src/env/workflow/service/registry.ts +0 -71
- package/src/env/workflow/service/workflow-service.test.ts +0 -310
- package/src/env/workflow/service/workflow-service.ts +0 -393
- package/src/env/workflow/storage/index.ts +0 -28
- package/src/env/workflow/storage/mock-repositories.ts +0 -385
- package/src/env/workflow/storage/sqlite.test.ts +0 -179
- package/src/env/workflow/storage/sqlite.ts +0 -163
- package/src/env/workflow/storage/workflow-repo.test.ts +0 -780
- package/src/env/workflow/storage/workflow-repo.ts +0 -342
- package/src/env/workflow/tools/ask-user-tool.ts +0 -82
- package/src/env/workflow/tools/index.ts +0 -26
- package/src/env/workflow/tools/run-workflow.test.ts +0 -352
- package/src/env/workflow/tools/run-workflow.ts +0 -214
- package/src/env/workflow/types/context.ts +0 -18
- package/src/env/workflow/types/decorators-types.ts +0 -198
- package/src/env/workflow/types/event.test.ts +0 -515
- package/src/env/workflow/types/event.ts +0 -193
- package/src/env/workflow/types/index.ts +0 -49
- package/src/env/workflow/types/run.test.ts +0 -437
- package/src/env/workflow/types/run.ts +0 -173
- package/src/env/workflow/types/workflow-hil.ts +0 -114
- package/src/env/workflow/types/workflow-message.test.ts +0 -138
- package/src/env/workflow/types/workflow-message.ts +0 -196
- package/src/env/workflow/types/workflow-session.test.ts +0 -95
- package/src/env/workflow/types/workflow-session.ts +0 -59
- package/src/env/workflow/types/workflow.test.ts +0 -495
- package/src/env/workflow/types/workflow.ts +0 -195
- package/src/env/workflow/types_compat.ts +0 -51
- package/src/env/workflow/utils/create-workflow.ts +0 -47
- package/src/env/workflow/utils/execution-state.ts +0 -245
- package/src/env/workflow/utils/index.ts +0 -18
- package/src/env/workflow/utils/node-registry-helper.ts +0 -58
- package/src/env/workflow/utils/recovery-validator.test.ts +0 -460
- package/src/env/workflow/utils/recovery-validator.ts +0 -377
- package/src/env/workflow/utils/session-parser.test.ts +0 -111
- package/src/env/workflow/utils/session-parser.ts +0 -94
- package/src/env/workflow/utils/session-recovery.test.ts +0 -334
- package/src/env/workflow/utils/session-recovery.ts +0 -188
- package/src/env/workflow/utils/template-resolver.test.ts +0 -258
- package/src/env/workflow/utils/template-resolver.ts +0 -436
- package/src/env/workflow/utils/validation-rules.ts +0 -149
- package/src/env/workflow/workflow-component.ts +0 -544
- package/src/index.ts +0 -422
- package/src/utils/id.ts +0 -21
package/src/config/env-source.ts
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ConfigSource,
|
|
3
|
-
ConfigSourceType,
|
|
4
|
-
ConfigChangeEvent,
|
|
5
|
-
} from "./config-component";
|
|
6
|
-
import { toEnvKey, fromEnvKey } from "./env-key";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* EnvSource 配置
|
|
10
|
-
*/
|
|
11
|
-
export interface EnvSourceOptions {
|
|
12
|
-
/** 环境变量前缀 */
|
|
13
|
-
prefix?: string;
|
|
14
|
-
/** 值转换函数 */
|
|
15
|
-
transform?: (value: string, key: string) => unknown;
|
|
16
|
-
/** 轮询间隔(ms),用于检测环境变量变化 */
|
|
17
|
-
pollInterval?: number;
|
|
18
|
-
/** 优先级 */
|
|
19
|
-
priority?: number;
|
|
20
|
-
/** 是否启用监听(轮询),默认 true */
|
|
21
|
-
watch?: boolean;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* 环境变量配置源
|
|
26
|
-
*
|
|
27
|
-
* 从 process.env 读取配置,支持:
|
|
28
|
-
* - 前缀过滤
|
|
29
|
-
* - 值转换
|
|
30
|
-
* - 环境变量监听(轮询)
|
|
31
|
-
*/
|
|
32
|
-
export class EnvSource implements ConfigSource {
|
|
33
|
-
readonly name: ConfigSourceType = "env";
|
|
34
|
-
readonly priority = 20;
|
|
35
|
-
|
|
36
|
-
private prefix: string;
|
|
37
|
-
private transform?: (value: string, key: string) => unknown;
|
|
38
|
-
private pollInterval?: number;
|
|
39
|
-
private watchers = new Set<(event: ConfigChangeEvent) => void>();
|
|
40
|
-
private pollTimer?: NodeJS.Timeout;
|
|
41
|
-
private lastValues = new Map<string, string>();
|
|
42
|
-
private watchEnabled: boolean = true;
|
|
43
|
-
|
|
44
|
-
constructor(options: EnvSourceOptions = {}) {
|
|
45
|
-
this.prefix = options.prefix ?? "";
|
|
46
|
-
this.transform = options.transform;
|
|
47
|
-
this.pollInterval = options.pollInterval;
|
|
48
|
-
this.watchEnabled = options.watch ?? true;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* 获取环境变量名(带前缀)
|
|
53
|
-
* 将点号路径转换为下划线分隔的大写形式
|
|
54
|
-
* 例如: "llm.provider" + prefix "LLM" -> "LLM_LLM_PROVIDER"
|
|
55
|
-
* 例如: "DEBUG" + prefix "MY_APP_" -> "MY_APP_DEBUG"
|
|
56
|
-
* 例如: "llm.defaultModel" + prefix "LLM" -> "LLM_LLM_DEFAULT_MODEL"
|
|
57
|
-
*/
|
|
58
|
-
private getEnvKey(key: string): string {
|
|
59
|
-
return toEnvKey(key, this.prefix);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* 移除前缀获取内部 key
|
|
64
|
-
* 注意:保持原始格式(大小写),仅移除前缀
|
|
65
|
-
*/
|
|
66
|
-
private getInternalKey(envKey: string): string {
|
|
67
|
-
return fromEnvKey(envKey, this.prefix);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 读取环境变量
|
|
72
|
-
*/
|
|
73
|
-
read(key: string): unknown {
|
|
74
|
-
const envKey = this.getEnvKey(key);
|
|
75
|
-
const value = process.env[envKey];
|
|
76
|
-
|
|
77
|
-
if (value === undefined) {
|
|
78
|
-
return undefined;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (this.transform) {
|
|
82
|
-
return this.transform(value, key);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return value;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* 写入环境变量
|
|
90
|
-
*/
|
|
91
|
-
write(key: string, value: unknown): boolean {
|
|
92
|
-
const envKey = this.getEnvKey(key);
|
|
93
|
-
const oldValue = process.env[envKey];
|
|
94
|
-
const stringValue = String(value);
|
|
95
|
-
|
|
96
|
-
// 设置环境变量
|
|
97
|
-
process.env[envKey] = stringValue;
|
|
98
|
-
|
|
99
|
-
// 通知监听器
|
|
100
|
-
const event: ConfigChangeEvent = {
|
|
101
|
-
type: oldValue === undefined ? "add" : "change",
|
|
102
|
-
key,
|
|
103
|
-
oldValue: oldValue !== undefined ? this.transformValue(oldValue, key) : undefined,
|
|
104
|
-
newValue: this.transformValue(stringValue, key),
|
|
105
|
-
source: this.name,
|
|
106
|
-
timestamp: Date.now(),
|
|
107
|
-
};
|
|
108
|
-
this.notifyWatchers(event);
|
|
109
|
-
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* 删除环境变量
|
|
115
|
-
*/
|
|
116
|
-
delete(key: string): boolean {
|
|
117
|
-
const envKey = this.getEnvKey(key);
|
|
118
|
-
const oldValue = process.env[envKey];
|
|
119
|
-
|
|
120
|
-
if (oldValue === undefined) {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
delete process.env[envKey];
|
|
125
|
-
|
|
126
|
-
// 通知监听器
|
|
127
|
-
const event: ConfigChangeEvent = {
|
|
128
|
-
type: "delete",
|
|
129
|
-
key,
|
|
130
|
-
oldValue: this.transformValue(oldValue, key),
|
|
131
|
-
newValue: undefined,
|
|
132
|
-
source: this.name,
|
|
133
|
-
timestamp: Date.now(),
|
|
134
|
-
};
|
|
135
|
-
this.notifyWatchers(event);
|
|
136
|
-
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* 列出所有环境变量
|
|
142
|
-
*/
|
|
143
|
-
list(): Array<{ key: string; value: unknown }> {
|
|
144
|
-
const result: Array<{ key: string; value: unknown }> = [];
|
|
145
|
-
const prefix = this.prefix.toUpperCase();
|
|
146
|
-
|
|
147
|
-
for (const envKey of Object.keys(process.env)) {
|
|
148
|
-
// 检查前缀匹配
|
|
149
|
-
if (prefix && !envKey.startsWith(prefix)) {
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const key = this.getInternalKey(envKey);
|
|
154
|
-
const value = process.env[envKey];
|
|
155
|
-
|
|
156
|
-
if (value !== undefined) {
|
|
157
|
-
result.push({
|
|
158
|
-
key,
|
|
159
|
-
value: this.transformValue(value, key),
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return result;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* 监听环境变量变化
|
|
169
|
-
*/
|
|
170
|
-
watch(callback: (event: ConfigChangeEvent) => void): () => void {
|
|
171
|
-
this.watchers.add(callback);
|
|
172
|
-
|
|
173
|
-
// 如果 watch 被禁用,直接返回
|
|
174
|
-
if (!this.watchEnabled) {
|
|
175
|
-
return () => {
|
|
176
|
-
this.watchers.delete(callback);
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// 启动轮询
|
|
181
|
-
this.ensurePolling();
|
|
182
|
-
|
|
183
|
-
// 记录初始值
|
|
184
|
-
this.recordCurrentValues();
|
|
185
|
-
|
|
186
|
-
return () => {
|
|
187
|
-
this.watchers.delete(callback);
|
|
188
|
-
|
|
189
|
-
// 停止轮询如果没有监听器
|
|
190
|
-
if (this.watchers.size === 0) {
|
|
191
|
-
this.stopPolling();
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
private ensurePolling(): void {
|
|
197
|
-
if (this.pollTimer !== undefined) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const interval = this.pollInterval ?? 1000;
|
|
202
|
-
this.pollTimer = setInterval(() => {
|
|
203
|
-
this.checkForChanges();
|
|
204
|
-
}, interval);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
private stopPolling(): void {
|
|
208
|
-
if (this.pollTimer) {
|
|
209
|
-
clearInterval(this.pollTimer);
|
|
210
|
-
this.pollTimer = undefined;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
private recordCurrentValues(): void {
|
|
215
|
-
this.lastValues.clear();
|
|
216
|
-
const entries = this.list();
|
|
217
|
-
for (const entry of entries) {
|
|
218
|
-
const envKey = this.getEnvKey(entry.key);
|
|
219
|
-
const value = process.env[envKey];
|
|
220
|
-
if (value !== undefined) {
|
|
221
|
-
this.lastValues.set(envKey, value);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
private checkForChanges(): void {
|
|
227
|
-
const currentEntries = this.list();
|
|
228
|
-
const currentValues = new Map<string, string>();
|
|
229
|
-
|
|
230
|
-
for (const entry of currentEntries) {
|
|
231
|
-
const envKey = this.getEnvKey(entry.key);
|
|
232
|
-
const value = process.env[envKey];
|
|
233
|
-
if (value !== undefined) {
|
|
234
|
-
currentValues.set(envKey, value);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// 检测删除
|
|
239
|
-
for (const [envKey, oldValue] of this.lastValues.entries()) {
|
|
240
|
-
if (!currentValues.has(envKey)) {
|
|
241
|
-
const key = this.getInternalKey(envKey);
|
|
242
|
-
this.notifyWatchers({
|
|
243
|
-
type: "delete",
|
|
244
|
-
key,
|
|
245
|
-
oldValue: this.transformValue(oldValue, key),
|
|
246
|
-
newValue: undefined,
|
|
247
|
-
source: this.name,
|
|
248
|
-
timestamp: Date.now(),
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// 检测添加和变更
|
|
254
|
-
for (const [envKey, newValue] of currentValues.entries()) {
|
|
255
|
-
const key = this.getInternalKey(envKey);
|
|
256
|
-
const oldValue = this.lastValues.get(envKey);
|
|
257
|
-
|
|
258
|
-
if (oldValue === undefined) {
|
|
259
|
-
this.notifyWatchers({
|
|
260
|
-
type: "add",
|
|
261
|
-
key,
|
|
262
|
-
oldValue: undefined,
|
|
263
|
-
newValue: this.transformValue(newValue, key),
|
|
264
|
-
source: this.name,
|
|
265
|
-
timestamp: Date.now(),
|
|
266
|
-
});
|
|
267
|
-
} else if (oldValue !== newValue) {
|
|
268
|
-
this.notifyWatchers({
|
|
269
|
-
type: "change",
|
|
270
|
-
key,
|
|
271
|
-
oldValue: this.transformValue(oldValue, key),
|
|
272
|
-
newValue: this.transformValue(newValue, key),
|
|
273
|
-
source: this.name,
|
|
274
|
-
timestamp: Date.now(),
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// 更新记录
|
|
280
|
-
this.lastValues = currentValues;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
private transformValue(value: string, key: string): unknown {
|
|
284
|
-
if (this.transform) {
|
|
285
|
-
return this.transform(value, key);
|
|
286
|
-
}
|
|
287
|
-
return value;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
private notifyWatchers(event: ConfigChangeEvent): void {
|
|
291
|
-
this.watchers.forEach((cb) => cb(event));
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* 关闭并清理资源
|
|
296
|
-
*/
|
|
297
|
-
close(): void {
|
|
298
|
-
this.stopPolling();
|
|
299
|
-
this.watchers.clear();
|
|
300
|
-
}
|
|
301
|
-
}
|
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
-
import { promises as fs } from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { FileSource } from "./file-source";
|
|
5
|
-
|
|
6
|
-
describe("FileSource", () => {
|
|
7
|
-
const testDir = path.join(__dirname, ".test-file-source");
|
|
8
|
-
const testFile = path.join(testDir, "config.json");
|
|
9
|
-
|
|
10
|
-
beforeEach(async () => {
|
|
11
|
-
// 创建测试目录
|
|
12
|
-
await fs.mkdir(testDir, { recursive: true });
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
afterEach(async () => {
|
|
16
|
-
// 清理测试目录
|
|
17
|
-
try {
|
|
18
|
-
await fs.rm(testDir, { recursive: true, force: true });
|
|
19
|
-
} catch {
|
|
20
|
-
// ignore
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test("read returns undefined for non-existent key", async () => {
|
|
25
|
-
const source = new FileSource(testFile);
|
|
26
|
-
const value = source.read("nonExistent");
|
|
27
|
-
expect(value).toBeUndefined();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test("read loads value from JSON file", async () => {
|
|
31
|
-
// 初始化文件
|
|
32
|
-
await fs.writeFile(testFile, JSON.stringify({ name: "roy", age: 30 }), "utf-8");
|
|
33
|
-
|
|
34
|
-
const source = new FileSource(testFile);
|
|
35
|
-
expect(source.read("name")).toBe("roy");
|
|
36
|
-
expect(source.read("age")).toBe(30);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("read returns nested value using dot notation", async () => {
|
|
40
|
-
await fs.writeFile(
|
|
41
|
-
testFile,
|
|
42
|
-
JSON.stringify({ user: { profile: { name: "alice" } } }),
|
|
43
|
-
"utf-8"
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const source = new FileSource(testFile);
|
|
47
|
-
expect(source.read("user.profile.name")).toBe("alice");
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("write saves value to JSON file", async () => {
|
|
51
|
-
const source = new FileSource(testFile);
|
|
52
|
-
const result = source.write("name", "roy");
|
|
53
|
-
expect(result).toBe(true);
|
|
54
|
-
|
|
55
|
-
// 等待防抖延迟完成
|
|
56
|
-
await new Promise((r) => setTimeout(r, 150));
|
|
57
|
-
|
|
58
|
-
const content = await fs.readFile(testFile, "utf-8");
|
|
59
|
-
const data = JSON.parse(content);
|
|
60
|
-
expect(data.name).toBe("roy");
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("write updates existing value", async () => {
|
|
64
|
-
await fs.writeFile(testFile, JSON.stringify({ name: "old" }), "utf-8");
|
|
65
|
-
|
|
66
|
-
const source = new FileSource(testFile);
|
|
67
|
-
source.write("name", "new");
|
|
68
|
-
|
|
69
|
-
// 等待防抖延迟完成
|
|
70
|
-
await new Promise((r) => setTimeout(r, 150));
|
|
71
|
-
|
|
72
|
-
const content = await fs.readFile(testFile, "utf-8");
|
|
73
|
-
const data = JSON.parse(content);
|
|
74
|
-
expect(data.name).toBe("new");
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test("write creates nested structure for dot notation keys", async () => {
|
|
78
|
-
const source = new FileSource(testFile);
|
|
79
|
-
source.write("user.profile.name", "bob");
|
|
80
|
-
|
|
81
|
-
// 等待防抖延迟完成
|
|
82
|
-
await new Promise((r) => setTimeout(r, 150));
|
|
83
|
-
|
|
84
|
-
const content = await fs.readFile(testFile, "utf-8");
|
|
85
|
-
const data = JSON.parse(content);
|
|
86
|
-
expect(data.user.profile.name).toBe("bob");
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test("list returns all key-value pairs", async () => {
|
|
90
|
-
await fs.writeFile(testFile, JSON.stringify({ name: "roy", age: 30, active: true }), "utf-8");
|
|
91
|
-
|
|
92
|
-
const source = new FileSource(testFile);
|
|
93
|
-
const entries = source.list();
|
|
94
|
-
|
|
95
|
-
expect(entries).toContainEqual({ key: "name", value: "roy" });
|
|
96
|
-
expect(entries).toContainEqual({ key: "age", value: 30 });
|
|
97
|
-
expect(entries).toContainEqual({ key: "active", value: true });
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("list returns flattened nested structure", async () => {
|
|
101
|
-
await fs.writeFile(
|
|
102
|
-
testFile,
|
|
103
|
-
JSON.stringify({ user: { name: "roy", age: 30 } }),
|
|
104
|
-
"utf-8"
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
const source = new FileSource(testFile);
|
|
108
|
-
const entries = source.list();
|
|
109
|
-
|
|
110
|
-
expect(entries).toContainEqual({ key: "user.name", value: "roy" });
|
|
111
|
-
expect(entries).toContainEqual({ key: "user.age", value: 30 });
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test("watch monitors file changes", async () => {
|
|
115
|
-
await fs.writeFile(testFile, JSON.stringify({ name: "roy" }), "utf-8");
|
|
116
|
-
|
|
117
|
-
const source = new FileSource(testFile);
|
|
118
|
-
const events: any[] = [];
|
|
119
|
-
|
|
120
|
-
const unwatch = source.watch((event) => {
|
|
121
|
-
events.push(event);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// 等待文件系统稳定
|
|
125
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
126
|
-
|
|
127
|
-
// 修改文件
|
|
128
|
-
await fs.writeFile(testFile, JSON.stringify({ name: "alice" }), "utf-8");
|
|
129
|
-
|
|
130
|
-
// 等待事件触发
|
|
131
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
132
|
-
|
|
133
|
-
unwatch();
|
|
134
|
-
|
|
135
|
-
expect(events.length).toBeGreaterThan(0);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
test("watch returns unsubscribe function", async () => {
|
|
139
|
-
const source = new FileSource(testFile);
|
|
140
|
-
const callback = vi.fn();
|
|
141
|
-
|
|
142
|
-
const unwatch = source.watch(callback);
|
|
143
|
-
unwatch();
|
|
144
|
-
|
|
145
|
-
await fs.writeFile(testFile, JSON.stringify({ name: "changed" }), "utf-8");
|
|
146
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
147
|
-
|
|
148
|
-
expect(callback).not.toHaveBeenCalled();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test("has correct name and priority", () => {
|
|
152
|
-
const source = new FileSource(testFile);
|
|
153
|
-
expect(source.name).toBe("file");
|
|
154
|
-
expect(source.priority).toBe(10);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test("delete removes key from file", async () => {
|
|
158
|
-
await fs.writeFile(testFile, JSON.stringify({ name: "roy", age: 30 }), "utf-8");
|
|
159
|
-
|
|
160
|
-
const source = new FileSource(testFile);
|
|
161
|
-
const result = source.delete("name");
|
|
162
|
-
expect(result).toBe(true);
|
|
163
|
-
|
|
164
|
-
// 等待防抖延迟完成
|
|
165
|
-
await new Promise((r) => setTimeout(r, 150));
|
|
166
|
-
|
|
167
|
-
const content = await fs.readFile(testFile, "utf-8");
|
|
168
|
-
const data = JSON.parse(content);
|
|
169
|
-
expect(data.name).toBeUndefined();
|
|
170
|
-
expect(data.age).toBe(30);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test("delete returns false for non-existent key", async () => {
|
|
174
|
-
await fs.writeFile(testFile, JSON.stringify({ name: "roy" }), "utf-8");
|
|
175
|
-
|
|
176
|
-
const source = new FileSource(testFile);
|
|
177
|
-
const result = source.delete("nonExistent");
|
|
178
|
-
expect(result).toBe(false);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
test("should parse JSONC file with single-line comments", async () => {
|
|
182
|
-
const jsoncFile = path.join(testDir, "config.jsonc");
|
|
183
|
-
const content = `{
|
|
184
|
-
// API Configuration
|
|
185
|
-
"apiKey": "test-key",
|
|
186
|
-
"model": "gpt-4o"
|
|
187
|
-
}`;
|
|
188
|
-
await fs.writeFile(jsoncFile, content, "utf-8");
|
|
189
|
-
|
|
190
|
-
const source = new FileSource(jsoncFile);
|
|
191
|
-
expect(source.read("apiKey")).toBe("test-key");
|
|
192
|
-
expect(source.read("model")).toBe("gpt-4o");
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test("should parse JSONC file with multi-line comments", async () => {
|
|
196
|
-
const jsoncFile = path.join(testDir, "config.jsonc");
|
|
197
|
-
const content = `{
|
|
198
|
-
/* This is a
|
|
199
|
-
multi-line comment */
|
|
200
|
-
"name": "test"
|
|
201
|
-
}`;
|
|
202
|
-
await fs.writeFile(jsoncFile, content, "utf-8");
|
|
203
|
-
|
|
204
|
-
const source = new FileSource(jsoncFile);
|
|
205
|
-
expect(source.read("name")).toBe("test");
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
test("should substitute env vars in config values", async () => {
|
|
209
|
-
process.env.TEST_API_KEY = "my-secret-key";
|
|
210
|
-
const envFile = path.join(testDir, "env-config.json");
|
|
211
|
-
await fs.writeFile(envFile, JSON.stringify({ apiKey: "${TEST_API_KEY}" }), "utf-8");
|
|
212
|
-
|
|
213
|
-
const source = new FileSource(envFile);
|
|
214
|
-
expect(source.read("apiKey")).toBe("my-secret-key");
|
|
215
|
-
|
|
216
|
-
delete process.env.TEST_API_KEY;
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
test("should substitute env vars with defaults", async () => {
|
|
220
|
-
delete process.env.NONEXISTENT_KEY;
|
|
221
|
-
const envFile = path.join(testDir, "env-defaults.json");
|
|
222
|
-
await fs.writeFile(envFile, JSON.stringify({ key: "${NONEXISTENT_KEY:-default-value}" }), "utf-8");
|
|
223
|
-
|
|
224
|
-
const source = new FileSource(envFile);
|
|
225
|
-
expect(source.read("key")).toBe("default-value");
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
test("should handle combined JSONC and env vars", async () => {
|
|
229
|
-
process.env.CFG_HOST = "localhost";
|
|
230
|
-
process.env.CFG_PORT = "8080";
|
|
231
|
-
const combinedFile = path.join(testDir, "combined.jsonc");
|
|
232
|
-
const content = `{
|
|
233
|
-
// Server configuration
|
|
234
|
-
"host": "\${CFG_HOST}", // from env
|
|
235
|
-
"port": "\${CFG_PORT}",
|
|
236
|
-
"name": "app"
|
|
237
|
-
}`;
|
|
238
|
-
await fs.writeFile(combinedFile, content, "utf-8");
|
|
239
|
-
|
|
240
|
-
const source = new FileSource(combinedFile);
|
|
241
|
-
expect(source.read("host")).toBe("localhost");
|
|
242
|
-
expect(source.read("port")).toBe("8080");
|
|
243
|
-
expect(source.read("name")).toBe("app");
|
|
244
|
-
|
|
245
|
-
delete process.env.CFG_HOST;
|
|
246
|
-
delete process.env.CFG_PORT;
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test("should preserve non-string values during env substitution", async () => {
|
|
250
|
-
const mixedFile = path.join(testDir, "mixed.json");
|
|
251
|
-
await fs.writeFile(mixedFile, JSON.stringify({
|
|
252
|
-
count: "${UNDEFINED_VAR:-42}",
|
|
253
|
-
enabled: true,
|
|
254
|
-
rate: 0.95
|
|
255
|
-
}), "utf-8");
|
|
256
|
-
|
|
257
|
-
const source = new FileSource(mixedFile);
|
|
258
|
-
expect(source.read("count")).toBe("42"); // env var resolved to string
|
|
259
|
-
expect(source.read("enabled")).toBe(true);
|
|
260
|
-
expect(source.read("rate")).toBe(0.95);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
test("should resolve protocol references when resolver is provided", async () => {
|
|
264
|
-
// 创建被引用的文件
|
|
265
|
-
const authFile = path.join(testDir, "auth.json");
|
|
266
|
-
await fs.writeFile(authFile, JSON.stringify({
|
|
267
|
-
api: { key: "secret-key-from-file", url: "https://api.example.com" }
|
|
268
|
-
}), "utf-8");
|
|
269
|
-
|
|
270
|
-
// 创建主配置文件
|
|
271
|
-
const mainFile = path.join(testDir, "main.json");
|
|
272
|
-
await fs.writeFile(mainFile, JSON.stringify({
|
|
273
|
-
apiKey: "${file://" + authFile + "#api.key}",
|
|
274
|
-
apiUrl: "${file://" + authFile + "#api.url}"
|
|
275
|
-
}), "utf-8");
|
|
276
|
-
|
|
277
|
-
// 使用绝对路径的 file:// 协议
|
|
278
|
-
const { ProtocolResolver } = require("./protocol-resolver");
|
|
279
|
-
const resolver = new ProtocolResolver();
|
|
280
|
-
const source = new FileSource(mainFile, resolver);
|
|
281
|
-
|
|
282
|
-
expect(source.read("apiKey")).toBe("secret-key-from-file");
|
|
283
|
-
expect(source.read("apiUrl")).toBe("https://api.example.com");
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
test("should resolve file:// protocol references", async () => {
|
|
287
|
-
// 创建引用文件
|
|
288
|
-
const refFile = path.join(testDir, "ref.json");
|
|
289
|
-
await fs.writeFile(refFile, JSON.stringify({
|
|
290
|
-
token: "file-token-xxx",
|
|
291
|
-
secret: "file-secret"
|
|
292
|
-
}), "utf-8");
|
|
293
|
-
|
|
294
|
-
// 创建主配置文件
|
|
295
|
-
const mainFile = path.join(testDir, "app.json");
|
|
296
|
-
await fs.writeFile(mainFile, JSON.stringify({
|
|
297
|
-
token: "${file://ref.json#token}",
|
|
298
|
-
secret: "${file://ref.json#secret}"
|
|
299
|
-
}), "utf-8");
|
|
300
|
-
|
|
301
|
-
// 使用 file:// 协议
|
|
302
|
-
const { ProtocolResolver } = require("./protocol-resolver");
|
|
303
|
-
const resolver = new ProtocolResolver({
|
|
304
|
-
xdgDataHome: testDir,
|
|
305
|
-
});
|
|
306
|
-
const source = new FileSource(mainFile, resolver);
|
|
307
|
-
|
|
308
|
-
expect(source.read("token")).toBe("file-token-xxx");
|
|
309
|
-
expect(source.read("secret")).toBe("file-secret");
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
test("should resolve env:// protocol references", async () => {
|
|
313
|
-
process.env.PROTOCOL_TEST_VAR = "protocol-env-value";
|
|
314
|
-
|
|
315
|
-
const envFile = path.join(testDir, "env.json");
|
|
316
|
-
await fs.writeFile(envFile, JSON.stringify({
|
|
317
|
-
value: "${env://PROTOCOL_TEST_VAR}",
|
|
318
|
-
other: "static"
|
|
319
|
-
}), "utf-8");
|
|
320
|
-
|
|
321
|
-
const { ProtocolResolver } = require("./protocol-resolver");
|
|
322
|
-
const resolver = new ProtocolResolver();
|
|
323
|
-
const source = new FileSource(envFile, resolver);
|
|
324
|
-
|
|
325
|
-
expect(source.read("value")).toBe("protocol-env-value");
|
|
326
|
-
expect(source.read("other")).toBe("static");
|
|
327
|
-
|
|
328
|
-
delete process.env.PROTOCOL_TEST_VAR;
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
test("should handle combined env and protocol references", async () => {
|
|
332
|
-
process.env.DEFAULT_DB_HOST = "db.example.com";
|
|
333
|
-
|
|
334
|
-
// 创建引用文件
|
|
335
|
-
const refFile = path.join(testDir, "ref.json");
|
|
336
|
-
await fs.writeFile(refFile, JSON.stringify({
|
|
337
|
-
apiKey: "secret-api-key"
|
|
338
|
-
}), "utf-8");
|
|
339
|
-
|
|
340
|
-
const combinedFile = path.join(testDir, "combined.json");
|
|
341
|
-
await fs.writeFile(combinedFile, JSON.stringify({
|
|
342
|
-
host: "${env://DEFAULT_DB_HOST:-localhost}",
|
|
343
|
-
apiKey: "${file://ref.json#apiKey:-no-key}"
|
|
344
|
-
}), "utf-8");
|
|
345
|
-
|
|
346
|
-
const { ProtocolResolver } = require("./protocol-resolver");
|
|
347
|
-
const resolver = new ProtocolResolver({
|
|
348
|
-
xdgDataHome: testDir,
|
|
349
|
-
});
|
|
350
|
-
const source = new FileSource(combinedFile, resolver);
|
|
351
|
-
|
|
352
|
-
expect(source.read("host")).toBe("db.example.com");
|
|
353
|
-
expect(source.read("apiKey")).toBe("secret-api-key");
|
|
354
|
-
|
|
355
|
-
delete process.env.DEFAULT_DB_HOST;
|
|
356
|
-
});
|
|
357
|
-
});
|