@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,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Span Builder
|
|
3
|
+
*
|
|
4
|
+
* Builds TraceEntry objects from parsed log lines and constructs call trees.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ParsedLogLine, TraceEntry, TraceTreeNode } from '../types';
|
|
8
|
+
import { RegexParser } from './regex-parser';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Paired enter/quit entries
|
|
12
|
+
*/
|
|
13
|
+
interface PairedSpan {
|
|
14
|
+
enter: TraceEntry;
|
|
15
|
+
quit: TraceEntry;
|
|
16
|
+
durationMs: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Span Builder for constructing trace entries and call trees
|
|
21
|
+
*/
|
|
22
|
+
export class SpanBuilder {
|
|
23
|
+
private parser: RegexParser;
|
|
24
|
+
|
|
25
|
+
constructor() {
|
|
26
|
+
this.parser = new RegexParser();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Build trace entries from parsed log lines
|
|
31
|
+
*/
|
|
32
|
+
buildTraceEntries(lines: ParsedLogLine[]): TraceEntry[] {
|
|
33
|
+
const entries: TraceEntry[] = [];
|
|
34
|
+
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
const entry = this.buildTraceEntry(line);
|
|
37
|
+
if (entry) {
|
|
38
|
+
entries.push(entry);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Sort by timestamp
|
|
43
|
+
entries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
44
|
+
|
|
45
|
+
return entries;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Build a single trace entry from a parsed log line
|
|
50
|
+
*/
|
|
51
|
+
private buildTraceEntry(line: ParsedLogLine): TraceEntry | null {
|
|
52
|
+
// Use requestId from parsed line if available, otherwise extract from raw line
|
|
53
|
+
const traceId = line.requestId || this.extractTraceId(line.raw);
|
|
54
|
+
|
|
55
|
+
const entry: TraceEntry = {
|
|
56
|
+
// Use traceId if available, otherwise generate a virtual one based on function and timestamp
|
|
57
|
+
traceId: traceId || this.generateVirtualTraceId(line.method, line.timestamp),
|
|
58
|
+
timestamp: line.timestamp,
|
|
59
|
+
function: line.method,
|
|
60
|
+
action: line.actionType,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Parse args based on action type
|
|
64
|
+
switch (line.actionType) {
|
|
65
|
+
case 'enter':
|
|
66
|
+
entry.params = this.parser.parseArgs(line.rawArgs) as unknown[] | undefined;
|
|
67
|
+
break;
|
|
68
|
+
case 'quit':
|
|
69
|
+
entry.result = this.parser.parseArgs(line.rawArgs);
|
|
70
|
+
break;
|
|
71
|
+
case 'error':
|
|
72
|
+
entry.error = line.rawArgs;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return entry;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Generate a virtual trace ID when no real traceId is present
|
|
81
|
+
* Uses function name and timestamp to create a pseudo-traceId
|
|
82
|
+
*/
|
|
83
|
+
generateVirtualTraceId(functionName: string, timestamp: string): string {
|
|
84
|
+
// Create a hash-like ID from function name and timestamp
|
|
85
|
+
const base = `${functionName}:${timestamp}`;
|
|
86
|
+
let hash = 0;
|
|
87
|
+
for (let i = 0; i < base.length; i++) {
|
|
88
|
+
const char = base.charCodeAt(i);
|
|
89
|
+
hash = ((hash << 5) - hash) + char;
|
|
90
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
91
|
+
}
|
|
92
|
+
return `vtrace_${Math.abs(hash).toString(36)}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Extract trace ID from log line
|
|
97
|
+
*/
|
|
98
|
+
private extractTraceId(line: string): string | null {
|
|
99
|
+
return this.parser.extractTraceId(line);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Calculate duration between enter and quit entries
|
|
104
|
+
*/
|
|
105
|
+
calculateDuration(enter: TraceEntry, quit: TraceEntry): number {
|
|
106
|
+
try {
|
|
107
|
+
const enterTime = new Date(enter.timestamp).getTime();
|
|
108
|
+
const quitTime = new Date(quit.timestamp).getTime();
|
|
109
|
+
|
|
110
|
+
if (isNaN(enterTime) || isNaN(quitTime)) {
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return quitTime - enterTime;
|
|
115
|
+
} catch {
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Pair enter and quit entries
|
|
122
|
+
*/
|
|
123
|
+
pairEnterQuit(entries: TraceEntry[]): PairedSpan[] {
|
|
124
|
+
const paired: PairedSpan[] = [];
|
|
125
|
+
const enterStack: TraceEntry[] = [];
|
|
126
|
+
|
|
127
|
+
for (const entry of entries) {
|
|
128
|
+
if (entry.action === 'enter') {
|
|
129
|
+
enterStack.push(entry);
|
|
130
|
+
} else if (entry.action === 'quit' && enterStack.length > 0) {
|
|
131
|
+
// Match with the most recent unmatched enter
|
|
132
|
+
const enter = enterStack.pop()!;
|
|
133
|
+
const duration = this.calculateDuration(enter, entry);
|
|
134
|
+
|
|
135
|
+
paired.push({
|
|
136
|
+
enter,
|
|
137
|
+
quit: entry,
|
|
138
|
+
durationMs: duration,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return paired;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Build a call tree from trace entries using call stack tracking
|
|
148
|
+
*
|
|
149
|
+
* Enhanced to support both:
|
|
150
|
+
* 1. Traditional enter/quit pair-based tree building (from log files)
|
|
151
|
+
* 2. Parent-child hierarchy-based tree building (from SQLite spans)
|
|
152
|
+
*/
|
|
153
|
+
buildCallTree(entries: TraceEntry[]): TraceTreeNode | null {
|
|
154
|
+
if (entries.length === 0) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Check if entries contain parent-child info (from SQLite spans)
|
|
159
|
+
// Entries from SQLite have spanId/parentSpanId as direct properties
|
|
160
|
+
const hasSpanHierarchy = entries.some(e =>
|
|
161
|
+
e.spanId !== undefined || e.parentSpanId !== undefined
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
if (hasSpanHierarchy) {
|
|
165
|
+
return this.buildCallTreeFromHierarchy(entries);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Fall back to traditional enter/quit pairing
|
|
169
|
+
return this.buildCallTreeFromPairs(entries);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Build call tree from parent-child span hierarchy (for SQLite spans)
|
|
174
|
+
*/
|
|
175
|
+
buildCallTreeFromHierarchy(entries: TraceEntry[]): TraceTreeNode | null {
|
|
176
|
+
// Build a map of spanId -> node
|
|
177
|
+
const nodeMap = new Map<string, TraceTreeNode>();
|
|
178
|
+
const rootNodes: TraceTreeNode[] = [];
|
|
179
|
+
const addedToRoots = new Set<string>();
|
|
180
|
+
|
|
181
|
+
// Create nodes from entries - only process 'enter' entries for the tree
|
|
182
|
+
const enterEntries = entries.filter(e => e.action === 'enter');
|
|
183
|
+
|
|
184
|
+
for (const entry of enterEntries) {
|
|
185
|
+
const spanId = entry.spanId;
|
|
186
|
+
const parentSpanId = entry.parentSpanId;
|
|
187
|
+
|
|
188
|
+
// Skip if already processed
|
|
189
|
+
if (spanId && nodeMap.has(spanId)) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Create node
|
|
194
|
+
const node: TraceTreeNode = {
|
|
195
|
+
traceId: entry.traceId,
|
|
196
|
+
function: entry.function,
|
|
197
|
+
startTime: entry.timestamp,
|
|
198
|
+
// Find corresponding quit entry for duration
|
|
199
|
+
endTime: undefined,
|
|
200
|
+
durationMs: entry.durationMs,
|
|
201
|
+
children: [],
|
|
202
|
+
entry,
|
|
203
|
+
spanId,
|
|
204
|
+
parentSpanId,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
if (spanId) {
|
|
208
|
+
nodeMap.set(spanId, node);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Track as root candidate if no parent or parent not in same trace
|
|
212
|
+
if (!parentSpanId || !nodeMap.has(parentSpanId)) {
|
|
213
|
+
rootNodes.push(node);
|
|
214
|
+
if (spanId) addedToRoots.add(spanId);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Find quit entries and update endTime/duration
|
|
219
|
+
const quitEntries = entries.filter(e => e.action === 'quit');
|
|
220
|
+
for (const quit of quitEntries) {
|
|
221
|
+
if (quit.spanId) {
|
|
222
|
+
const node = nodeMap.get(quit.spanId);
|
|
223
|
+
if (node) {
|
|
224
|
+
node.endTime = quit.timestamp;
|
|
225
|
+
node.durationMs = quit.durationMs || (node.endTime && node.startTime
|
|
226
|
+
? new Date(node.endTime).getTime() - new Date(node.startTime).getTime()
|
|
227
|
+
: undefined);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Build parent-child relationships
|
|
233
|
+
// Only connect to parent if it belongs to the same trace
|
|
234
|
+
for (const node of nodeMap.values()) {
|
|
235
|
+
if (node.parentSpanId && nodeMap.has(node.parentSpanId)) {
|
|
236
|
+
const parent = nodeMap.get(node.parentSpanId)!;
|
|
237
|
+
// Only add as child if parent is in the same trace
|
|
238
|
+
if (parent.traceId === node.traceId) {
|
|
239
|
+
parent.children.push(node);
|
|
240
|
+
// Remove from roots if it was added there
|
|
241
|
+
rootNodes.splice(rootNodes.indexOf(node), 1);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// If no explicit roots, try to find from entries without parentSpanId
|
|
247
|
+
if (rootNodes.length === 0) {
|
|
248
|
+
for (const entry of enterEntries) {
|
|
249
|
+
if (!entry.parentSpanId) {
|
|
250
|
+
const spanId = entry.spanId;
|
|
251
|
+
if (spanId && nodeMap.has(spanId) && !addedToRoots.has(spanId)) {
|
|
252
|
+
rootNodes.push(nodeMap.get(spanId)!);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Sort roots by start time
|
|
259
|
+
rootNodes.sort((a, b) => a.startTime.localeCompare(b.startTime));
|
|
260
|
+
|
|
261
|
+
// Merge multiple root nodes into a single tree if needed
|
|
262
|
+
if (rootNodes.length === 0) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (rootNodes.length === 1) {
|
|
267
|
+
return rootNodes[0];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Create a virtual root node if multiple roots exist
|
|
271
|
+
const allStartTimes = rootNodes.map(n => new Date(n.startTime).getTime());
|
|
272
|
+
const allEndTimes = rootNodes.map(n => n.endTime ? new Date(n.endTime).getTime() : 0).filter(t => t > 0);
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
traceId: entries[0]?.traceId || 'merged',
|
|
276
|
+
function: '(root)',
|
|
277
|
+
children: rootNodes,
|
|
278
|
+
startTime: new Date(Math.min(...allStartTimes)).toISOString(),
|
|
279
|
+
endTime: allEndTimes.length > 0
|
|
280
|
+
? new Date(Math.max(...allEndTimes)).toISOString()
|
|
281
|
+
: undefined,
|
|
282
|
+
durationMs: allEndTimes.length > 0 && allStartTimes.length > 0
|
|
283
|
+
? Math.max(...allEndTimes) - Math.min(...allStartTimes)
|
|
284
|
+
: undefined,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Build call tree from traditional enter/quit pairs (for log files)
|
|
290
|
+
*/
|
|
291
|
+
buildCallTreeFromPairs(entries: TraceEntry[]): TraceTreeNode | null {
|
|
292
|
+
// Sort entries by timestamp to ensure correct call stack order
|
|
293
|
+
const sortedEntries = [...entries].sort((a, b) =>
|
|
294
|
+
a.timestamp.localeCompare(b.timestamp)
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
// Pair enter/quit entries first
|
|
298
|
+
const paired = this.pairEnterQuit(sortedEntries);
|
|
299
|
+
|
|
300
|
+
if (paired.length === 0) {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Build tree using call stack simulation
|
|
305
|
+
const stack: { node: TraceTreeNode; paired: PairedSpan }[] = [];
|
|
306
|
+
let root: TraceTreeNode | null = null;
|
|
307
|
+
|
|
308
|
+
// Process sorted entries sequentially to track call depth
|
|
309
|
+
for (const entry of sortedEntries) {
|
|
310
|
+
if (entry.action === 'enter') {
|
|
311
|
+
// Find the paired quit for this enter
|
|
312
|
+
const span = paired.find(p => p.enter === entry);
|
|
313
|
+
if (!span) continue;
|
|
314
|
+
|
|
315
|
+
const node = this.buildNodeFromPaired(span);
|
|
316
|
+
|
|
317
|
+
if (stack.length === 0) {
|
|
318
|
+
// This is the root
|
|
319
|
+
root = node;
|
|
320
|
+
} else {
|
|
321
|
+
// Add to current parent's children
|
|
322
|
+
stack[stack.length - 1].node.children.push(node);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
stack.push({ node, paired: span });
|
|
326
|
+
} else if (entry.action === 'quit' && stack.length > 0) {
|
|
327
|
+
// Pop the stack - the function is returning
|
|
328
|
+
stack.pop();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return root;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Build a tree node from a paired span
|
|
337
|
+
*/
|
|
338
|
+
private buildNodeFromPaired(span: PairedSpan): TraceTreeNode {
|
|
339
|
+
return {
|
|
340
|
+
traceId: span.enter.traceId,
|
|
341
|
+
function: span.enter.function,
|
|
342
|
+
startTime: span.enter.timestamp,
|
|
343
|
+
endTime: span.quit.timestamp,
|
|
344
|
+
durationMs: span.durationMs,
|
|
345
|
+
children: [],
|
|
346
|
+
entry: span.enter,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Merge entries from multiple functions into a single call tree
|
|
352
|
+
*/
|
|
353
|
+
buildMergedTree(entriesByFunction: Map<string, TraceEntry[]>): TraceTreeNode | null {
|
|
354
|
+
// Get all entries across functions, sorted by timestamp
|
|
355
|
+
const allEntries: TraceEntry[] = [];
|
|
356
|
+
|
|
357
|
+
for (const entries of entriesByFunction.values()) {
|
|
358
|
+
allEntries.push(...entries);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
allEntries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
362
|
+
|
|
363
|
+
return this.buildCallTree(allEntries);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Get duration summary for entries
|
|
368
|
+
*/
|
|
369
|
+
getDurationSummary(entries: TraceEntry[]): Map<string, { count: number; totalMs: number; avgMs: number }> {
|
|
370
|
+
const summary = new Map<string, { count: number; totalMs: number; avgMs: number }>();
|
|
371
|
+
const paired = this.pairEnterQuit(entries);
|
|
372
|
+
|
|
373
|
+
for (const span of paired) {
|
|
374
|
+
const func = span.enter.function;
|
|
375
|
+
const existing = summary.get(func) || { count: 0, totalMs: 0, avgMs: 0 };
|
|
376
|
+
|
|
377
|
+
existing.count++;
|
|
378
|
+
existing.totalMs += span.durationMs;
|
|
379
|
+
existing.avgMs = Math.round(existing.totalMs / existing.count);
|
|
380
|
+
|
|
381
|
+
summary.set(func, existing);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return summary;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { describe, expect, test, beforeAll, afterAll } from 'bun:test';
|
|
2
|
+
import { writeFileSync, mkdirSync, rmSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { LogReader } from './log-reader';
|
|
5
|
+
|
|
6
|
+
describe('LogReader', () => {
|
|
7
|
+
const testDir = '/tmp/roy-debug-test';
|
|
8
|
+
let reader: LogReader;
|
|
9
|
+
|
|
10
|
+
beforeAll(() => {
|
|
11
|
+
mkdirSync(testDir, { recursive: true });
|
|
12
|
+
reader = new LogReader({ logDir: testDir });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterAll(() => {
|
|
16
|
+
rmSync(testDir, { recursive: true });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('getLogPath', () => {
|
|
20
|
+
test('should return default app.log path', () => {
|
|
21
|
+
const path = reader.getLogPath();
|
|
22
|
+
expect(path).toContain('app.log');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('should return custom filename path', () => {
|
|
26
|
+
const path = reader.getLogPath('custom.log');
|
|
27
|
+
expect(path).toContain('custom.log');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('should use absolute path directly', () => {
|
|
31
|
+
const absPath = '/absolute/path/server.log';
|
|
32
|
+
const path = reader.getLogPath(absPath);
|
|
33
|
+
expect(path).toBe(absPath);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('findLogFiles', () => {
|
|
38
|
+
test('should return array of files', async () => {
|
|
39
|
+
const files = await reader.findLogFiles();
|
|
40
|
+
|
|
41
|
+
expect(Array.isArray(files)).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('should handle custom pattern', async () => {
|
|
45
|
+
// Create test log file
|
|
46
|
+
writeFileSync(join(testDir, 'custom.log'), 'test log');
|
|
47
|
+
|
|
48
|
+
const files = await reader.findLogFiles('custom.log');
|
|
49
|
+
|
|
50
|
+
expect(Array.isArray(files)).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('should use forward slashes for glob pattern (Windows compatibility)', async () => {
|
|
54
|
+
// Create test log files
|
|
55
|
+
writeFileSync(join(testDir, 'app.log'), 'test');
|
|
56
|
+
writeFileSync(join(testDir, 'error.log'), 'test');
|
|
57
|
+
|
|
58
|
+
const files = await reader.findLogFiles('*.log');
|
|
59
|
+
|
|
60
|
+
// Verify files are found (proves glob pattern works)
|
|
61
|
+
expect(files.length).toBeGreaterThanOrEqual(2);
|
|
62
|
+
|
|
63
|
+
// Verify returned paths use forward slashes (not backslashes)
|
|
64
|
+
for (const file of files) {
|
|
65
|
+
expect(file).not.toContain('\\');
|
|
66
|
+
expect(file).toContain('/');
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('readLines', () => {
|
|
72
|
+
test('should read lines from file', async () => {
|
|
73
|
+
const testFile = join(testDir, 'read-test.log');
|
|
74
|
+
writeFileSync(testFile, 'line1\nline2\nline3\n');
|
|
75
|
+
|
|
76
|
+
const lines: string[] = [];
|
|
77
|
+
for await (const line of reader.readLines(testFile)) {
|
|
78
|
+
lines.push(line);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
expect(lines).toEqual(['line1', 'line2', 'line3']);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('should handle empty file', async () => {
|
|
85
|
+
const testFile = join(testDir, 'empty.log');
|
|
86
|
+
writeFileSync(testFile, '');
|
|
87
|
+
|
|
88
|
+
const lines: string[] = [];
|
|
89
|
+
for await (const line of reader.readLines(testFile)) {
|
|
90
|
+
lines.push(line);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
expect(lines).toEqual([]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('should handle file with trailing newline', async () => {
|
|
97
|
+
const testFile = join(testDir, 'trailing.log');
|
|
98
|
+
writeFileSync(testFile, 'line1\nline2\n');
|
|
99
|
+
|
|
100
|
+
const lines: string[] = [];
|
|
101
|
+
for await (const line of reader.readLines(testFile)) {
|
|
102
|
+
lines.push(line);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
expect(lines).toEqual(['line1', 'line2']);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('filterByTime', () => {
|
|
110
|
+
test('should filter by since time', async () => {
|
|
111
|
+
const testFile = join(testDir, 'time-filter.log');
|
|
112
|
+
writeFileSync(testFile,
|
|
113
|
+
'2026-04-08 10:00:00.000 line1\n' +
|
|
114
|
+
'2026-04-08 11:00:00.000 line2\n' +
|
|
115
|
+
'2026-04-08 12:00:00.000 line3\n'
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const lines: string[] = [];
|
|
119
|
+
for await (const line of reader.readLines(testFile, {
|
|
120
|
+
since: '2026-04-08T11:00:00'
|
|
121
|
+
})) {
|
|
122
|
+
lines.push(line);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
expect(lines.length).toBe(2);
|
|
126
|
+
expect(lines[0]).toContain('11:00:00');
|
|
127
|
+
expect(lines[1]).toContain('12:00:00');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('should filter by until time', async () => {
|
|
131
|
+
const testFile = join(testDir, 'until-filter.log');
|
|
132
|
+
writeFileSync(testFile,
|
|
133
|
+
'2026-04-08 10:00:00.000 line1\n' +
|
|
134
|
+
'2026-04-08 11:00:00.000 line2\n' +
|
|
135
|
+
'2026-04-08 12:00:00.000 line3\n'
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const lines: string[] = [];
|
|
139
|
+
for await (const line of reader.readLines(testFile, {
|
|
140
|
+
until: '2026-04-08T11:30:00'
|
|
141
|
+
})) {
|
|
142
|
+
lines.push(line);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
expect(lines.length).toBe(2);
|
|
146
|
+
expect(lines[0]).toContain('10:00:00');
|
|
147
|
+
expect(lines[1]).toContain('11:00:00');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('should filter by both since and until', async () => {
|
|
151
|
+
const testFile = join(testDir, 'range-filter.log');
|
|
152
|
+
writeFileSync(testFile,
|
|
153
|
+
'2026-04-08 10:00:00.000 line1\n' +
|
|
154
|
+
'2026-04-08 11:00:00.000 line2\n' +
|
|
155
|
+
'2026-04-08 12:00:00.000 line3\n'
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const lines: string[] = [];
|
|
159
|
+
for await (const line of reader.readLines(testFile, {
|
|
160
|
+
since: '2026-04-08T10:30:00',
|
|
161
|
+
until: '2026-04-08T11:30:00'
|
|
162
|
+
})) {
|
|
163
|
+
lines.push(line);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
expect(lines.length).toBe(1);
|
|
167
|
+
expect(lines[0]).toContain('11:00:00');
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|