dotdo 0.0.1 → 0.0.2
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/LICENSE +1 -1
- package/README.md +446 -315
- package/dist/ai/index.js +19 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/template-literals.js +852 -0
- package/dist/ai/template-literals.js.map +1 -0
- package/dist/api/analytics/router.js +601 -0
- package/dist/api/analytics/router.js.map +1 -0
- package/dist/api/index.js +158 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/middleware/auth-federation.js +573 -0
- package/dist/api/middleware/auth-federation.js.map +1 -0
- package/dist/api/middleware/auth.js +544 -0
- package/dist/api/middleware/auth.js.map +1 -0
- package/dist/api/middleware/error-handling.js +176 -0
- package/dist/api/middleware/error-handling.js.map +1 -0
- package/dist/api/middleware/request-id.js +21 -0
- package/dist/api/middleware/request-id.js.map +1 -0
- package/dist/api/pages.js +1180 -0
- package/dist/api/pages.js.map +1 -0
- package/dist/api/routes/api.js +612 -0
- package/dist/api/routes/api.js.map +1 -0
- package/dist/api/routes/browsers.js +471 -0
- package/dist/api/routes/browsers.js.map +1 -0
- package/dist/api/routes/do.js +188 -0
- package/dist/api/routes/do.js.map +1 -0
- package/dist/api/routes/mcp.js +459 -0
- package/dist/api/routes/mcp.js.map +1 -0
- package/dist/api/routes/obs.js +445 -0
- package/dist/api/routes/obs.js.map +1 -0
- package/dist/api/routes/openapi.js +794 -0
- package/dist/api/routes/openapi.js.map +1 -0
- package/dist/api/routes/rpc.js +1103 -0
- package/dist/api/routes/rpc.js.map +1 -0
- package/dist/api/routes/sandboxes.js +389 -0
- package/dist/api/routes/sandboxes.js.map +1 -0
- package/dist/api/test-do.js +38 -0
- package/dist/api/test-do.js.map +1 -0
- package/dist/api/types.js +11 -0
- package/dist/api/types.js.map +1 -0
- package/dist/cli/bin.js +2 -0
- package/dist/cli/main.js +52342 -0
- package/dist/db/actions.js +212 -0
- package/dist/db/actions.js.map +1 -0
- package/dist/db/auth.js +506 -0
- package/dist/db/auth.js.map +1 -0
- package/dist/db/branches.js +65 -0
- package/dist/db/branches.js.map +1 -0
- package/dist/db/clickhouse.js +1074 -0
- package/dist/db/clickhouse.js.map +1 -0
- package/dist/db/dlq.js +39 -0
- package/dist/db/dlq.js.map +1 -0
- package/dist/db/events.js +28 -0
- package/dist/db/events.js.map +1 -0
- package/dist/db/exec.js +64 -0
- package/dist/db/exec.js.map +1 -0
- package/dist/db/files.js +85 -0
- package/dist/db/files.js.map +1 -0
- package/dist/db/flags.js +24 -0
- package/dist/db/flags.js.map +1 -0
- package/dist/db/git.js +116 -0
- package/dist/db/git.js.map +1 -0
- package/dist/db/iceberg/inverted-index.js +862 -0
- package/dist/db/iceberg/inverted-index.js.map +1 -0
- package/dist/db/iceberg/puffin.js +878 -0
- package/dist/db/iceberg/puffin.js.map +1 -0
- package/dist/db/iceberg/search-manifest.js +422 -0
- package/dist/db/iceberg/search-manifest.js.map +1 -0
- package/dist/db/iceberg/types.js +8 -0
- package/dist/db/iceberg/types.js.map +1 -0
- package/dist/db/index.js +121 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/integrations.js +368 -0
- package/dist/db/integrations.js.map +1 -0
- package/dist/db/json-indexes.js +332 -0
- package/dist/db/json-indexes.js.map +1 -0
- package/dist/db/linked-accounts.js +287 -0
- package/dist/db/linked-accounts.js.map +1 -0
- package/dist/db/nouns.js +183 -0
- package/dist/db/nouns.js.map +1 -0
- package/dist/db/objects.js +170 -0
- package/dist/db/objects.js.map +1 -0
- package/dist/db/primitives/dag-scheduler/index.js +869 -0
- package/dist/db/primitives/dag-scheduler/index.js.map +1 -0
- package/dist/db/primitives/exactly-once-context.js +237 -0
- package/dist/db/primitives/exactly-once-context.js.map +1 -0
- package/dist/db/primitives/index.js +62 -0
- package/dist/db/primitives/index.js.map +1 -0
- package/dist/db/primitives/keyed-router.js +145 -0
- package/dist/db/primitives/keyed-router.js.map +1 -0
- package/dist/db/primitives/observability.js +162 -0
- package/dist/db/primitives/observability.js.map +1 -0
- package/dist/db/primitives/schema-evolution.js +643 -0
- package/dist/db/primitives/schema-evolution.js.map +1 -0
- package/dist/db/primitives/stateful-operator/index.js +770 -0
- package/dist/db/primitives/stateful-operator/index.js.map +1 -0
- package/dist/db/primitives/temporal-store.js +306 -0
- package/dist/db/primitives/temporal-store.js.map +1 -0
- package/dist/db/primitives/typed-column-store.js +1229 -0
- package/dist/db/primitives/typed-column-store.js.map +1 -0
- package/dist/db/primitives/utils/duration.js +162 -0
- package/dist/db/primitives/utils/duration.js.map +1 -0
- package/dist/db/primitives/utils/murmur3.js +118 -0
- package/dist/db/primitives/utils/murmur3.js.map +1 -0
- package/dist/db/primitives/watermark-service.js +136 -0
- package/dist/db/primitives/watermark-service.js.map +1 -0
- package/dist/db/primitives/window-manager.js +764 -0
- package/dist/db/primitives/window-manager.js.map +1 -0
- package/dist/db/relationships.js +66 -0
- package/dist/db/relationships.js.map +1 -0
- package/dist/db/schema-minimal.js +61 -0
- package/dist/db/schema-minimal.js.map +1 -0
- package/dist/db/search.js +28 -0
- package/dist/db/search.js.map +1 -0
- package/dist/db/stores.js +1665 -0
- package/dist/db/stores.js.map +1 -0
- package/dist/db/things.js +297 -0
- package/dist/db/things.js.map +1 -0
- package/dist/db/vault.js +171 -0
- package/dist/db/vault.js.map +1 -0
- package/dist/db/verbs.js +102 -0
- package/dist/db/verbs.js.map +1 -0
- package/dist/do/base.js +48 -0
- package/dist/do/base.js.map +1 -0
- package/dist/do/bash.js +35 -0
- package/dist/do/bash.js.map +1 -0
- package/dist/do/fs.js +25 -0
- package/dist/do/fs.js.map +1 -0
- package/dist/do/full.js +61 -0
- package/dist/do/full.js.map +1 -0
- package/dist/do/git.js +28 -0
- package/dist/do/git.js.map +1 -0
- package/dist/do/index.js +52 -0
- package/dist/do/index.js.map +1 -0
- package/dist/do/tiny.js +31 -0
- package/dist/do/tiny.js.map +1 -0
- package/dist/lib/DOAuth.js +261 -0
- package/dist/lib/DOAuth.js.map +1 -0
- package/dist/lib/DODispatcher.js +72 -0
- package/dist/lib/DODispatcher.js.map +1 -0
- package/dist/lib/Modifier.js +189 -0
- package/dist/lib/Modifier.js.map +1 -0
- package/dist/lib/StateStorage.js +403 -0
- package/dist/lib/StateStorage.js.map +1 -0
- package/dist/lib/TypeRegistry.js +122 -0
- package/dist/lib/TypeRegistry.js.map +1 -0
- package/dist/lib/agent/tools/bash.js +336 -0
- package/dist/lib/agent/tools/bash.js.map +1 -0
- package/dist/lib/agent/tools/edit.js +157 -0
- package/dist/lib/agent/tools/edit.js.map +1 -0
- package/dist/lib/agent/tools/glob.js +137 -0
- package/dist/lib/agent/tools/glob.js.map +1 -0
- package/dist/lib/agent/tools/grep.js +315 -0
- package/dist/lib/agent/tools/grep.js.map +1 -0
- package/dist/lib/agent/tools/index.js +71 -0
- package/dist/lib/agent/tools/index.js.map +1 -0
- package/dist/lib/agent/tools/read.js +212 -0
- package/dist/lib/agent/tools/read.js.map +1 -0
- package/dist/lib/agent/tools/types.js +197 -0
- package/dist/lib/agent/tools/types.js.map +1 -0
- package/dist/lib/agent/tools/write.js +159 -0
- package/dist/lib/agent/tools/write.js.map +1 -0
- package/dist/lib/ai/gateway.js +247 -0
- package/dist/lib/ai/gateway.js.map +1 -0
- package/dist/lib/ai/tool-loop-agent.js +591 -0
- package/dist/lib/ai/tool-loop-agent.js.map +1 -0
- package/dist/lib/auto-wiring.js +439 -0
- package/dist/lib/auto-wiring.js.map +1 -0
- package/dist/lib/browse/browserbase.js +163 -0
- package/dist/lib/browse/browserbase.js.map +1 -0
- package/dist/lib/browse/cloudflare.js +144 -0
- package/dist/lib/browse/cloudflare.js.map +1 -0
- package/dist/lib/browse/index.js +62 -0
- package/dist/lib/browse/index.js.map +1 -0
- package/dist/lib/browse/types.js +13 -0
- package/dist/lib/browse/types.js.map +1 -0
- package/dist/lib/cache/index.js +37 -0
- package/dist/lib/cache/index.js.map +1 -0
- package/dist/lib/cache/visibility.js +638 -0
- package/dist/lib/cache/visibility.js.map +1 -0
- package/dist/lib/capabilities.js +268 -0
- package/dist/lib/capabilities.js.map +1 -0
- package/dist/lib/channels/base.js +106 -0
- package/dist/lib/channels/base.js.map +1 -0
- package/dist/lib/channels/discord.js +94 -0
- package/dist/lib/channels/discord.js.map +1 -0
- package/dist/lib/channels/email.js +204 -0
- package/dist/lib/channels/email.js.map +1 -0
- package/dist/lib/channels/index.js +90 -0
- package/dist/lib/channels/index.js.map +1 -0
- package/dist/lib/channels/mdxui-chat.js +95 -0
- package/dist/lib/channels/mdxui-chat.js.map +1 -0
- package/dist/lib/channels/slack-blockkit.js +121 -0
- package/dist/lib/channels/slack-blockkit.js.map +1 -0
- package/dist/lib/channels/types.js +7 -0
- package/dist/lib/channels/types.js.map +1 -0
- package/dist/lib/cloudflare/ai.js +654 -0
- package/dist/lib/cloudflare/ai.js.map +1 -0
- package/dist/lib/cloudflare/index.js +88 -0
- package/dist/lib/cloudflare/index.js.map +1 -0
- package/dist/lib/cloudflare/kv.js +342 -0
- package/dist/lib/cloudflare/kv.js.map +1 -0
- package/dist/lib/cloudflare/queues.js +434 -0
- package/dist/lib/cloudflare/queues.js.map +1 -0
- package/dist/lib/cloudflare/r2.js +604 -0
- package/dist/lib/cloudflare/r2.js.map +1 -0
- package/dist/lib/cloudflare/vectorize.js +494 -0
- package/dist/lib/cloudflare/vectorize.js.map +1 -0
- package/dist/lib/cloudflare/workflows.js +569 -0
- package/dist/lib/cloudflare/workflows.js.map +1 -0
- package/dist/lib/colo/caching.js +196 -0
- package/dist/lib/colo/caching.js.map +1 -0
- package/dist/lib/colo/detection.js +194 -0
- package/dist/lib/colo/detection.js.map +1 -0
- package/dist/lib/colo/external-data.js +219 -0
- package/dist/lib/colo/external-data.js.map +1 -0
- package/dist/lib/colo/globe-data.js +179 -0
- package/dist/lib/colo/globe-data.js.map +1 -0
- package/dist/lib/colo/index.js +16 -0
- package/dist/lib/colo/index.js.map +1 -0
- package/dist/lib/decorators.js +37 -0
- package/dist/lib/decorators.js.map +1 -0
- package/dist/lib/discovery.js +81 -0
- package/dist/lib/discovery.js.map +1 -0
- package/dist/lib/executors/AgenticFunctionExecutor.js +619 -0
- package/dist/lib/executors/AgenticFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/BaseFunctionExecutor.js +328 -0
- package/dist/lib/executors/BaseFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/CascadeExecutor.js +418 -0
- package/dist/lib/executors/CascadeExecutor.js.map +1 -0
- package/dist/lib/executors/CodeFunctionExecutor.js +904 -0
- package/dist/lib/executors/CodeFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/GenerativeFunctionExecutor.js +904 -0
- package/dist/lib/executors/GenerativeFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/HumanFunctionExecutor.js +884 -0
- package/dist/lib/executors/HumanFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/ParallelStepExecutor.js +308 -0
- package/dist/lib/executors/ParallelStepExecutor.js.map +1 -0
- package/dist/lib/executors/types.js +12 -0
- package/dist/lib/executors/types.js.map +1 -0
- package/dist/lib/experiments.js +89 -0
- package/dist/lib/experiments.js.map +1 -0
- package/dist/lib/flags/store.js +262 -0
- package/dist/lib/flags/store.js.map +1 -0
- package/dist/lib/functions/FunctionComposition.js +467 -0
- package/dist/lib/functions/FunctionComposition.js.map +1 -0
- package/dist/lib/functions/FunctionMiddleware.js +457 -0
- package/dist/lib/functions/FunctionMiddleware.js.map +1 -0
- package/dist/lib/functions/FunctionRegistry.js +426 -0
- package/dist/lib/functions/FunctionRegistry.js.map +1 -0
- package/dist/lib/functions/createFunction.js +1048 -0
- package/dist/lib/functions/createFunction.js.map +1 -0
- package/dist/lib/humans/index.js +68 -0
- package/dist/lib/humans/index.js.map +1 -0
- package/dist/lib/humans/templates.js +117 -0
- package/dist/lib/humans/templates.js.map +1 -0
- package/dist/lib/identity.js +98 -0
- package/dist/lib/identity.js.map +1 -0
- package/dist/lib/index.js +9 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/logging/error-logger.js +163 -0
- package/dist/lib/logging/error-logger.js.map +1 -0
- package/dist/lib/logging/index.js +160 -0
- package/dist/lib/logging/index.js.map +1 -0
- package/dist/lib/mixins/bash.js +825 -0
- package/dist/lib/mixins/bash.js.map +1 -0
- package/dist/lib/mixins/fs.js +648 -0
- package/dist/lib/mixins/fs.js.map +1 -0
- package/dist/lib/mixins/git.js +1011 -0
- package/dist/lib/mixins/git.js.map +1 -0
- package/dist/lib/mixins/index.js +29 -0
- package/dist/lib/mixins/index.js.map +1 -0
- package/dist/lib/mixins/npm.js +662 -0
- package/dist/lib/mixins/npm.js.map +1 -0
- package/dist/lib/noun-id.js +278 -0
- package/dist/lib/noun-id.js.map +1 -0
- package/dist/lib/rate-limit/sliding-window.js +148 -0
- package/dist/lib/rate-limit/sliding-window.js.map +1 -0
- package/dist/lib/rate-limit.js +110 -0
- package/dist/lib/rate-limit.js.map +1 -0
- package/dist/lib/rpc/bindings.js +548 -0
- package/dist/lib/rpc/bindings.js.map +1 -0
- package/dist/lib/rpc/index.js +64 -0
- package/dist/lib/rpc/index.js.map +1 -0
- package/dist/lib/safe-stringify.js +223 -0
- package/dist/lib/safe-stringify.js.map +1 -0
- package/dist/lib/sandbox/miniflare-sandbox.js +1007 -0
- package/dist/lib/sandbox/miniflare-sandbox.js.map +1 -0
- package/dist/lib/sqids.js +110 -0
- package/dist/lib/sqids.js.map +1 -0
- package/dist/lib/sql/adapters/index.js +10 -0
- package/dist/lib/sql/adapters/index.js.map +1 -0
- package/dist/lib/sql/adapters/node-sql-parser.js +552 -0
- package/dist/lib/sql/adapters/node-sql-parser.js.map +1 -0
- package/dist/lib/sql/adapters/pgsql-parser.js +1189 -0
- package/dist/lib/sql/adapters/pgsql-parser.js.map +1 -0
- package/dist/lib/sql/index.js +277 -0
- package/dist/lib/sql/index.js.map +1 -0
- package/dist/lib/sql/types.js +56 -0
- package/dist/lib/sql/types.js.map +1 -0
- package/dist/lib/type-classifier.js +126 -0
- package/dist/lib/type-classifier.js.map +1 -0
- package/dist/lib/utils/html.js +47 -0
- package/dist/lib/utils/html.js.map +1 -0
- package/dist/lib/validation.js +48 -0
- package/dist/lib/validation.js.map +1 -0
- package/dist/lib/vault/store.js +411 -0
- package/dist/lib/vault/store.js.map +1 -0
- package/dist/metrics/hunch.js +739 -0
- package/dist/metrics/hunch.js.map +1 -0
- package/dist/objects/API.js +302 -0
- package/dist/objects/API.js.map +1 -0
- package/dist/objects/Agent.js +179 -0
- package/dist/objects/Agent.js.map +1 -0
- package/dist/objects/AgenticFunctionExecutor.js +8 -0
- package/dist/objects/AgenticFunctionExecutor.js.map +1 -0
- package/dist/objects/App.js +83 -0
- package/dist/objects/App.js.map +1 -0
- package/dist/objects/Browser.js +884 -0
- package/dist/objects/Browser.js.map +1 -0
- package/dist/objects/Business.js +107 -0
- package/dist/objects/Business.js.map +1 -0
- package/dist/objects/CLI.js +221 -0
- package/dist/objects/CLI.js.map +1 -0
- package/dist/objects/CodeFunctionExecutor.js +8 -0
- package/dist/objects/CodeFunctionExecutor.js.map +1 -0
- package/dist/objects/Collection.js +161 -0
- package/dist/objects/Collection.js.map +1 -0
- package/dist/objects/DO.js +41 -0
- package/dist/objects/DO.js.map +1 -0
- package/dist/objects/DOBase.js +2309 -0
- package/dist/objects/DOBase.js.map +1 -0
- package/dist/objects/DOFull.js +1676 -0
- package/dist/objects/DOFull.js.map +1 -0
- package/dist/objects/DOTiny.js +207 -0
- package/dist/objects/DOTiny.js.map +1 -0
- package/dist/objects/Directory.js +199 -0
- package/dist/objects/Directory.js.map +1 -0
- package/dist/objects/Entity.js +413 -0
- package/dist/objects/Entity.js.map +1 -0
- package/dist/objects/Function.js +116 -0
- package/dist/objects/Function.js.map +1 -0
- package/dist/objects/Human.js +231 -0
- package/dist/objects/Human.js.map +1 -0
- package/dist/objects/HumanFunctionExecutor.js +8 -0
- package/dist/objects/HumanFunctionExecutor.js.map +1 -0
- package/dist/objects/IcebergMetadataDO.js +938 -0
- package/dist/objects/IcebergMetadataDO.js.map +1 -0
- package/dist/objects/IntegrationsDO.js +1174 -0
- package/dist/objects/IntegrationsDO.js.map +1 -0
- package/dist/objects/ObservabilityBroadcaster.js +149 -0
- package/dist/objects/ObservabilityBroadcaster.js.map +1 -0
- package/dist/objects/Package.js +154 -0
- package/dist/objects/Package.js.map +1 -0
- package/dist/objects/Product.js +193 -0
- package/dist/objects/Product.js.map +1 -0
- package/dist/objects/SDK.js +152 -0
- package/dist/objects/SDK.js.map +1 -0
- package/dist/objects/SaaS.js +235 -0
- package/dist/objects/SaaS.js.map +1 -0
- package/dist/objects/SandboxDO.js +759 -0
- package/dist/objects/SandboxDO.js.map +1 -0
- package/dist/objects/Service.js +337 -0
- package/dist/objects/Service.js.map +1 -0
- package/dist/objects/Site.js +80 -0
- package/dist/objects/Site.js.map +1 -0
- package/dist/objects/Startup.js +479 -0
- package/dist/objects/Startup.js.map +1 -0
- package/dist/objects/ThingsDO.js +170 -0
- package/dist/objects/ThingsDO.js.map +1 -0
- package/dist/objects/VectorShardDO.js +648 -0
- package/dist/objects/VectorShardDO.js.map +1 -0
- package/dist/objects/Worker.js +144 -0
- package/dist/objects/Worker.js.map +1 -0
- package/dist/objects/Workflow.js +196 -0
- package/dist/objects/Workflow.js.map +1 -0
- package/dist/objects/WorkflowFactory.js +313 -0
- package/dist/objects/WorkflowFactory.js.map +1 -0
- package/dist/objects/WorkflowRuntime.js +863 -0
- package/dist/objects/WorkflowRuntime.js.map +1 -0
- package/dist/objects/circuit-breaker-bulkhead.js +178 -0
- package/dist/objects/circuit-breaker-bulkhead.js.map +1 -0
- package/dist/objects/createFunction.js +934 -0
- package/dist/objects/createFunction.js.map +1 -0
- package/dist/objects/index.js +80 -0
- package/dist/objects/index.js.map +1 -0
- package/dist/objects/lifecycle/Branch.js +275 -0
- package/dist/objects/lifecycle/Branch.js.map +1 -0
- package/dist/objects/lifecycle/Clone.js +1499 -0
- package/dist/objects/lifecycle/Clone.js.map +1 -0
- package/dist/objects/lifecycle/Compact.js +237 -0
- package/dist/objects/lifecycle/Compact.js.map +1 -0
- package/dist/objects/lifecycle/Promote.js +476 -0
- package/dist/objects/lifecycle/Promote.js.map +1 -0
- package/dist/objects/lifecycle/Shard.js +560 -0
- package/dist/objects/lifecycle/Shard.js.map +1 -0
- package/dist/objects/lifecycle/index.js +15 -0
- package/dist/objects/lifecycle/index.js.map +1 -0
- package/dist/objects/lifecycle/types.js +33 -0
- package/dist/objects/lifecycle/types.js.map +1 -0
- package/dist/objects/mixins/infrastructure.js +171 -0
- package/dist/objects/mixins/infrastructure.js.map +1 -0
- package/dist/objects/modules/StoresModule.js +153 -0
- package/dist/objects/modules/StoresModule.js.map +1 -0
- package/dist/objects/persistence/checkpoint-manager.js +606 -0
- package/dist/objects/persistence/checkpoint-manager.js.map +1 -0
- package/dist/objects/persistence/index.js +72 -0
- package/dist/objects/persistence/index.js.map +1 -0
- package/dist/objects/persistence/migration-runner.js +562 -0
- package/dist/objects/persistence/migration-runner.js.map +1 -0
- package/dist/objects/persistence/replication-manager.js +501 -0
- package/dist/objects/persistence/replication-manager.js.map +1 -0
- package/dist/objects/persistence/tiered-storage-manager.js +595 -0
- package/dist/objects/persistence/tiered-storage-manager.js.map +1 -0
- package/dist/objects/persistence/types.js +14 -0
- package/dist/objects/persistence/types.js.map +1 -0
- package/dist/objects/persistence/wal-manager.js +653 -0
- package/dist/objects/persistence/wal-manager.js.map +1 -0
- package/dist/objects/presets/index.js +20 -0
- package/dist/objects/presets/index.js.map +1 -0
- package/dist/objects/presets/primitives.js +188 -0
- package/dist/objects/presets/primitives.js.map +1 -0
- package/dist/objects/primitives/alarm-adapter.js +141 -0
- package/dist/objects/primitives/alarm-adapter.js.map +1 -0
- package/dist/objects/primitives/index.js +337 -0
- package/dist/objects/primitives/index.js.map +1 -0
- package/dist/objects/primitives/storage-adapter.js +182 -0
- package/dist/objects/primitives/storage-adapter.js.map +1 -0
- package/dist/objects/primitives/with-primitives.js +102 -0
- package/dist/objects/primitives/with-primitives.js.map +1 -0
- package/dist/objects/services/StoreManager.js +227 -0
- package/dist/objects/services/StoreManager.js.map +1 -0
- package/dist/objects/services/index.js +13 -0
- package/dist/objects/services/index.js.map +1 -0
- package/dist/objects/transport/auth-layer.js +1451 -0
- package/dist/objects/transport/auth-layer.js.map +1 -0
- package/dist/objects/transport/capnweb-target.js +355 -0
- package/dist/objects/transport/capnweb-target.js.map +1 -0
- package/dist/objects/transport/chain.js +441 -0
- package/dist/objects/transport/chain.js.map +1 -0
- package/dist/objects/transport/handler.js +58 -0
- package/dist/objects/transport/handler.js.map +1 -0
- package/dist/objects/transport/index.js +53 -0
- package/dist/objects/transport/index.js.map +1 -0
- package/dist/objects/transport/mcp-server.js +690 -0
- package/dist/objects/transport/mcp-server.js.map +1 -0
- package/dist/objects/transport/rest-autowire.js +1507 -0
- package/dist/objects/transport/rest-autowire.js.map +1 -0
- package/dist/objects/transport/rest-router.js +440 -0
- package/dist/objects/transport/rest-router.js.map +1 -0
- package/dist/objects/transport/rpc-server.js +1536 -0
- package/dist/objects/transport/rpc-server.js.map +1 -0
- package/dist/objects/transport/shared.js +575 -0
- package/dist/objects/transport/shared.js.map +1 -0
- package/dist/objects/transport/sync-engine.js +291 -0
- package/dist/objects/transport/sync-engine.js.map +1 -0
- package/dist/objects/transport/types.js +8 -0
- package/dist/objects/transport/types.js.map +1 -0
- package/dist/primitives/bashx/src/ast/analyze.js +1472 -0
- package/dist/primitives/bashx/src/ast/analyze.js.map +1 -0
- package/dist/primitives/bashx/src/ast/parser.js +1488 -0
- package/dist/primitives/bashx/src/ast/parser.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/crypto.js +1954 -0
- package/dist/primitives/bashx/src/do/commands/crypto.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/data-processing.js +1812 -0
- package/dist/primitives/bashx/src/do/commands/data-processing.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/extended-utils.js +804 -0
- package/dist/primitives/bashx/src/do/commands/extended-utils.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/math-control.js +1122 -0
- package/dist/primitives/bashx/src/do/commands/math-control.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/posix-utils.js +1015 -0
- package/dist/primitives/bashx/src/do/commands/posix-utils.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/system-utils.js +687 -0
- package/dist/primitives/bashx/src/do/commands/system-utils.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/test-command.js +523 -0
- package/dist/primitives/bashx/src/do/commands/test-command.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/text-processing.js +1550 -0
- package/dist/primitives/bashx/src/do/commands/text-processing.js.map +1 -0
- package/dist/primitives/bashx/src/do/container-executor.js +429 -0
- package/dist/primitives/bashx/src/do/container-executor.js.map +1 -0
- package/dist/primitives/bashx/src/do/index.js +668 -0
- package/dist/primitives/bashx/src/do/index.js.map +1 -0
- package/dist/primitives/bashx/src/do/tiered-executor.js +2647 -0
- package/dist/primitives/bashx/src/do/tiered-executor.js.map +1 -0
- package/dist/primitives/bashx/src/do/worker.js +352 -0
- package/dist/primitives/bashx/src/do/worker.js.map +1 -0
- package/dist/primitives/bashx/src/types.js +10 -0
- package/dist/primitives/bashx/src/types.js.map +1 -0
- package/dist/primitives/fsx/core/backend.js +480 -0
- package/dist/primitives/fsx/core/backend.js.map +1 -0
- package/dist/primitives/fsx/core/constants.js +140 -0
- package/dist/primitives/fsx/core/constants.js.map +1 -0
- package/dist/primitives/fsx/core/fsx.js +1184 -0
- package/dist/primitives/fsx/core/fsx.js.map +1 -0
- package/dist/primitives/fsx/core/glob/glob.js +438 -0
- package/dist/primitives/fsx/core/glob/glob.js.map +1 -0
- package/dist/primitives/fsx/core/glob/index.js +8 -0
- package/dist/primitives/fsx/core/glob/index.js.map +1 -0
- package/dist/primitives/fsx/core/glob/match.js +392 -0
- package/dist/primitives/fsx/core/glob/match.js.map +1 -0
- package/dist/primitives/fsx/core/types.js +307 -0
- package/dist/primitives/fsx/core/types.js.map +1 -0
- package/dist/sandbox/index.js +258 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sdk/capnweb-compat.js +42 -0
- package/dist/sdk/capnweb-compat.js.map +1 -0
- package/dist/sdk/client.js +20 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/sdk/index.js +17 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/snippets/artifacts-config.js +241 -0
- package/dist/snippets/artifacts-config.js.map +1 -0
- package/dist/snippets/artifacts-ingest.js +832 -0
- package/dist/snippets/artifacts-ingest.js.map +1 -0
- package/dist/snippets/artifacts-serve.js +1035 -0
- package/dist/snippets/artifacts-serve.js.map +1 -0
- package/dist/snippets/artifacts-types.js +161 -0
- package/dist/snippets/artifacts-types.js.map +1 -0
- package/dist/snippets/cache-probe.js +376 -0
- package/dist/snippets/cache-probe.js.map +1 -0
- package/dist/snippets/cache.js +10 -0
- package/dist/snippets/cache.js.map +1 -0
- package/dist/snippets/events.js +469 -0
- package/dist/snippets/events.js.map +1 -0
- package/dist/snippets/index.js +7 -0
- package/dist/snippets/index.js.map +1 -0
- package/dist/snippets/proxy.js +495 -0
- package/dist/snippets/proxy.js.map +1 -0
- package/dist/snippets/search.js +1759 -0
- package/dist/snippets/search.js.map +1 -0
- package/dist/streams/index.js +30 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams/observability.js +68 -0
- package/dist/streams/observability.js.map +1 -0
- package/dist/types/AI.js +92 -0
- package/dist/types/AI.js.map +1 -0
- package/dist/types/AIFunction.js +171 -0
- package/dist/types/AIFunction.js.map +1 -0
- package/dist/types/BrowseVerb.js +89 -0
- package/dist/types/BrowseVerb.js.map +1 -0
- package/dist/types/Browser.js +31 -0
- package/dist/types/Browser.js.map +1 -0
- package/dist/types/Chaos.js +15 -0
- package/dist/types/Chaos.js.map +1 -0
- package/dist/types/CloudflareBindings.js +109 -0
- package/dist/types/CloudflareBindings.js.map +1 -0
- package/dist/types/Collection.js +50 -0
- package/dist/types/Collection.js.map +1 -0
- package/dist/types/DO.js +2 -0
- package/dist/types/DO.js.map +1 -0
- package/dist/types/DOLocation.js +63 -0
- package/dist/types/DOLocation.js.map +1 -0
- package/dist/types/EventHandler.js +57 -0
- package/dist/types/EventHandler.js.map +1 -0
- package/dist/types/Experiment.js +33 -0
- package/dist/types/Experiment.js.map +1 -0
- package/dist/types/Flag.js +57 -0
- package/dist/types/Flag.js.map +1 -0
- package/dist/types/Lifecycle.js +13 -0
- package/dist/types/Lifecycle.js.map +1 -0
- package/dist/types/Location.js +169 -0
- package/dist/types/Location.js.map +1 -0
- package/dist/types/Noun.js +66 -0
- package/dist/types/Noun.js.map +1 -0
- package/dist/types/SessionEvent.js +194 -0
- package/dist/types/SessionEvent.js.map +1 -0
- package/dist/types/Thing.js +55 -0
- package/dist/types/Thing.js.map +1 -0
- package/dist/types/ThingDO.js +153 -0
- package/dist/types/ThingDO.js.map +1 -0
- package/dist/types/Things.js +2 -0
- package/dist/types/Things.js.map +1 -0
- package/dist/types/Verb.js +119 -0
- package/dist/types/Verb.js.map +1 -0
- package/dist/types/WorkflowContext.js +70 -0
- package/dist/types/WorkflowContext.js.map +1 -0
- package/dist/types/analytics-api.js +13 -0
- package/dist/types/analytics-api.js.map +1 -0
- package/dist/types/capabilities.js +135 -0
- package/dist/types/capabilities.js.map +1 -0
- package/dist/types/drizzle.js +12 -0
- package/dist/types/drizzle.js.map +1 -0
- package/dist/types/event.js +201 -0
- package/dist/types/event.js.map +1 -0
- package/dist/types/fn.js +12 -0
- package/dist/types/fn.js.map +1 -0
- package/dist/types/iceberg.js +48 -0
- package/dist/types/iceberg.js.map +1 -0
- package/dist/types/ids.js +170 -0
- package/dist/types/ids.js.map +1 -0
- package/dist/types/index.js +41 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/introspect.js +54 -0
- package/dist/types/introspect.js.map +1 -0
- package/dist/types/observability.js +124 -0
- package/dist/types/observability.js.map +1 -0
- package/dist/types/sync-protocol.js +175 -0
- package/dist/types/sync-protocol.js.map +1 -0
- package/dist/types/vector.js +13 -0
- package/dist/types/vector.js.map +1 -0
- package/dist/workflows/ScheduleManager.js +473 -0
- package/dist/workflows/ScheduleManager.js.map +1 -0
- package/dist/workflows/StepDOBridge.js +149 -0
- package/dist/workflows/StepDOBridge.js.map +1 -0
- package/dist/workflows/StepResultStorage.js +232 -0
- package/dist/workflows/StepResultStorage.js.map +1 -0
- package/dist/workflows/WaitForEventManager.js +461 -0
- package/dist/workflows/WaitForEventManager.js.map +1 -0
- package/dist/workflows/analyzer.js +332 -0
- package/dist/workflows/analyzer.js.map +1 -0
- package/dist/workflows/compat/activity-router.js +484 -0
- package/dist/workflows/compat/activity-router.js.map +1 -0
- package/dist/workflows/compat/backends/cloudflare-workflows.js +431 -0
- package/dist/workflows/compat/backends/cloudflare-workflows.js.map +1 -0
- package/dist/workflows/compat/backends/index.js +14 -0
- package/dist/workflows/compat/backends/index.js.map +1 -0
- package/dist/workflows/compat/errors/index.js +375 -0
- package/dist/workflows/compat/errors/index.js.map +1 -0
- package/dist/workflows/compat/index.js +79 -0
- package/dist/workflows/compat/index.js.map +1 -0
- package/dist/workflows/compat/inngest/index.js +989 -0
- package/dist/workflows/compat/inngest/index.js.map +1 -0
- package/dist/workflows/compat/qstash/index.js +1263 -0
- package/dist/workflows/compat/qstash/index.js.map +1 -0
- package/dist/workflows/compat/temporal/activities.js +739 -0
- package/dist/workflows/compat/temporal/activities.js.map +1 -0
- package/dist/workflows/compat/temporal/child-workflows.js +154 -0
- package/dist/workflows/compat/temporal/child-workflows.js.map +1 -0
- package/dist/workflows/compat/temporal/client.js +381 -0
- package/dist/workflows/compat/temporal/client.js.map +1 -0
- package/dist/workflows/compat/temporal/context.js +309 -0
- package/dist/workflows/compat/temporal/context.js.map +1 -0
- package/dist/workflows/compat/temporal/determinism.js +216 -0
- package/dist/workflows/compat/temporal/determinism.js.map +1 -0
- package/dist/workflows/compat/temporal/errors.js +128 -0
- package/dist/workflows/compat/temporal/errors.js.map +1 -0
- package/dist/workflows/compat/temporal/index.js +2464 -0
- package/dist/workflows/compat/temporal/index.js.map +1 -0
- package/dist/workflows/compat/temporal/saga.js +504 -0
- package/dist/workflows/compat/temporal/saga.js.map +1 -0
- package/dist/workflows/compat/temporal/signals.js +364 -0
- package/dist/workflows/compat/temporal/signals.js.map +1 -0
- package/dist/workflows/compat/temporal/storage.js +271 -0
- package/dist/workflows/compat/temporal/storage.js.map +1 -0
- package/dist/workflows/compat/temporal/timers.js +347 -0
- package/dist/workflows/compat/temporal/timers.js.map +1 -0
- package/dist/workflows/compat/temporal/types.js +7 -0
- package/dist/workflows/compat/temporal/types.js.map +1 -0
- package/dist/workflows/compat/temporal/unified-primitives.js +339 -0
- package/dist/workflows/compat/temporal/unified-primitives.js.map +1 -0
- package/dist/workflows/compat/trigger/index.js +468 -0
- package/dist/workflows/compat/trigger/index.js.map +1 -0
- package/dist/workflows/compat/utils/index.js +69 -0
- package/dist/workflows/compat/utils/index.js.map +1 -0
- package/dist/workflows/context/correlation-capability.js +266 -0
- package/dist/workflows/context/correlation-capability.js.map +1 -0
- package/dist/workflows/context/correlation.js +484 -0
- package/dist/workflows/context/correlation.js.map +1 -0
- package/dist/workflows/context/experiment.js +289 -0
- package/dist/workflows/context/experiment.js.map +1 -0
- package/dist/workflows/context/flag.js +244 -0
- package/dist/workflows/context/flag.js.map +1 -0
- package/dist/workflows/context/foundation.js +648 -0
- package/dist/workflows/context/foundation.js.map +1 -0
- package/dist/workflows/context/human-base.js +106 -0
- package/dist/workflows/context/human-base.js.map +1 -0
- package/dist/workflows/context/human.js +368 -0
- package/dist/workflows/context/human.js.map +1 -0
- package/dist/workflows/context/measure.js +354 -0
- package/dist/workflows/context/measure.js.map +1 -0
- package/dist/workflows/context/rate-limit.js +358 -0
- package/dist/workflows/context/rate-limit.js.map +1 -0
- package/dist/workflows/context/user.js +117 -0
- package/dist/workflows/context/user.js.map +1 -0
- package/dist/workflows/context/vault.js +360 -0
- package/dist/workflows/context/vault.js.map +1 -0
- package/dist/workflows/data/entity-events/entity-events.js +489 -0
- package/dist/workflows/data/entity-events/entity-events.js.map +1 -0
- package/dist/workflows/data/experiment/index.js +599 -0
- package/dist/workflows/data/experiment/index.js.map +1 -0
- package/dist/workflows/data/goal/context.js +558 -0
- package/dist/workflows/data/goal/context.js.map +1 -0
- package/dist/workflows/data/goal/index.js +32 -0
- package/dist/workflows/data/goal/index.js.map +1 -0
- package/dist/workflows/data/measure/index.js +840 -0
- package/dist/workflows/data/measure/index.js.map +1 -0
- package/dist/workflows/data/stream/index.js +1215 -0
- package/dist/workflows/data/stream/index.js.map +1 -0
- package/dist/workflows/data/track/context.js +883 -0
- package/dist/workflows/data/track/context.js.map +1 -0
- package/dist/workflows/data/track/index.js +15 -0
- package/dist/workflows/data/track/index.js.map +1 -0
- package/dist/workflows/data/view/context.js +864 -0
- package/dist/workflows/data/view/context.js.map +1 -0
- package/dist/workflows/domain.js +93 -0
- package/dist/workflows/domain.js.map +1 -0
- package/dist/workflows/flag.js +176 -0
- package/dist/workflows/flag.js.map +1 -0
- package/dist/workflows/flags.js +217 -0
- package/dist/workflows/flags.js.map +1 -0
- package/dist/workflows/hash.js +209 -0
- package/dist/workflows/hash.js.map +1 -0
- package/dist/workflows/index.js +50 -0
- package/dist/workflows/index.js.map +1 -0
- package/dist/workflows/on.js +378 -0
- package/dist/workflows/on.js.map +1 -0
- package/dist/workflows/pipeline-promise.js +481 -0
- package/dist/workflows/pipeline-promise.js.map +1 -0
- package/dist/workflows/pipeline-types.js +20 -0
- package/dist/workflows/pipeline-types.js.map +1 -0
- package/dist/workflows/proxy.js +76 -0
- package/dist/workflows/proxy.js.map +1 -0
- package/dist/workflows/runtime.js +310 -0
- package/dist/workflows/runtime.js.map +1 -0
- package/dist/workflows/schedule-builder.js +327 -0
- package/dist/workflows/schedule-builder.js.map +1 -0
- package/dist/workflows/visibility/index.js +148 -0
- package/dist/workflows/visibility/index.js.map +1 -0
- package/dist/workflows/visibility/query-parser.js +150 -0
- package/dist/workflows/visibility/query-parser.js.map +1 -0
- package/dist/workflows/visibility/store.js +223 -0
- package/dist/workflows/visibility/store.js.map +1 -0
- package/dist/workflows/visibility/types.js +30 -0
- package/dist/workflows/visibility/types.js.map +1 -0
- package/dist/workflows/workflow.js +53 -0
- package/dist/workflows/workflow.js.map +1 -0
- package/package.json +279 -46
|
@@ -0,0 +1,1812 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Processing Commands
|
|
3
|
+
*
|
|
4
|
+
* Native implementations of data processing commands:
|
|
5
|
+
* - jq: JSON processor
|
|
6
|
+
* - yq: YAML processor
|
|
7
|
+
* - base64: Encoding/decoding
|
|
8
|
+
* - envsubst: Environment variable substitution
|
|
9
|
+
*
|
|
10
|
+
* These run as Tier 1 native commands in the Worker.
|
|
11
|
+
*
|
|
12
|
+
* @module bashx/do/commands/data-processing
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Sentinel value indicating expression was not a builtin function
|
|
16
|
+
*/
|
|
17
|
+
const NOT_A_BUILTIN = Symbol('NOT_A_BUILTIN');
|
|
18
|
+
/**
|
|
19
|
+
* Custom error for jq parsing/execution errors
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* throw new JqError('parse error: Invalid JSON input', 5)
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export class JqError extends Error {
|
|
27
|
+
exitCode;
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new JqError
|
|
30
|
+
*
|
|
31
|
+
* @param message - Error message describing the problem
|
|
32
|
+
* @param exitCode - Exit code to return (default: 1, parse errors: 5)
|
|
33
|
+
*/
|
|
34
|
+
constructor(message, exitCode = 1) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.exitCode = exitCode;
|
|
37
|
+
this.name = 'JqError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* JqEngine - Reusable JSON query processor
|
|
42
|
+
*
|
|
43
|
+
* Provides a jq-compatible query language for filtering and transforming JSON data.
|
|
44
|
+
* Supports:
|
|
45
|
+
* - Key access (.key, .key.nested)
|
|
46
|
+
* - Array operations (.[n], .[], map, select, sort_by)
|
|
47
|
+
* - Object construction ({key, newKey: .expr})
|
|
48
|
+
* - Built-in functions (length, keys, values, type, etc.)
|
|
49
|
+
* - Conditionals (if-then-else, alternative operator //)
|
|
50
|
+
* - Variables (--arg, --argjson)
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const engine = new JqEngine()
|
|
55
|
+
* const result = engine.execute('.name', '{"name": "test"}')
|
|
56
|
+
* // result = '"test"\n'
|
|
57
|
+
*
|
|
58
|
+
* const filtered = engine.execute(
|
|
59
|
+
* '.[] | select(.age > 18)',
|
|
60
|
+
* '[{"name": "alice", "age": 25}]'
|
|
61
|
+
* )
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export class JqEngine {
|
|
65
|
+
/** Cached parsed JSON to avoid re-parsing */
|
|
66
|
+
cachedInput = null;
|
|
67
|
+
cachedData = null;
|
|
68
|
+
/**
|
|
69
|
+
* Execute a jq query on JSON input
|
|
70
|
+
*
|
|
71
|
+
* @param query - The jq query expression (e.g., '.name', '.[] | select(.age > 18)')
|
|
72
|
+
* @param input - JSON input string
|
|
73
|
+
* @param options - Execution options (raw output, compact, slurp, variables)
|
|
74
|
+
* @returns Query result as formatted string
|
|
75
|
+
* @throws {JqError} On invalid JSON or query syntax errors
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const engine = new JqEngine()
|
|
80
|
+
*
|
|
81
|
+
* // Simple key access
|
|
82
|
+
* engine.execute('.name', '{"name": "test"}')
|
|
83
|
+
* // => '"test"\n'
|
|
84
|
+
*
|
|
85
|
+
* // With raw output (no quotes)
|
|
86
|
+
* engine.execute('.name', '{"name": "test"}', { raw: true })
|
|
87
|
+
* // => 'test\n'
|
|
88
|
+
*
|
|
89
|
+
* // With variables
|
|
90
|
+
* engine.execute(
|
|
91
|
+
* '.[] | select(.age > $minAge)',
|
|
92
|
+
* '[{"age": 25}, {"age": 17}]',
|
|
93
|
+
* { argjson: { minAge: 18 } }
|
|
94
|
+
* )
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
execute(query, input, options = {}) {
|
|
98
|
+
const data = this.parseInput(input, options);
|
|
99
|
+
const context = this.createContext(options);
|
|
100
|
+
const result = this.evaluateWithMeta(query, data, context);
|
|
101
|
+
return this.formatOutput(result.value, options, result.isIterator);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Evaluate a jq expression and return the raw value (not formatted)
|
|
105
|
+
*
|
|
106
|
+
* @param query - The jq query expression
|
|
107
|
+
* @param data - Parsed JSON data
|
|
108
|
+
* @param context - Execution context with variables
|
|
109
|
+
* @returns Raw evaluated value
|
|
110
|
+
*/
|
|
111
|
+
evaluate(query, data, context) {
|
|
112
|
+
const trimmedQuery = query.trim();
|
|
113
|
+
if (trimmedQuery === '.' || trimmedQuery === '') {
|
|
114
|
+
return data;
|
|
115
|
+
}
|
|
116
|
+
const tokens = this.tokenize(trimmedQuery);
|
|
117
|
+
return this.executeTokens(tokens, data, context).value;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Parse JSON input, handling slurp mode for multiple documents
|
|
121
|
+
*
|
|
122
|
+
* @param input - JSON input string
|
|
123
|
+
* @param options - Options containing slurp mode setting
|
|
124
|
+
* @returns Parsed JSON data
|
|
125
|
+
* @throws {JqError} On invalid JSON
|
|
126
|
+
*/
|
|
127
|
+
parseInput(input, options) {
|
|
128
|
+
// Check cache for non-slurp mode
|
|
129
|
+
if (!options.slurp && input === this.cachedInput) {
|
|
130
|
+
return this.cachedData;
|
|
131
|
+
}
|
|
132
|
+
let data;
|
|
133
|
+
if (options.slurp) {
|
|
134
|
+
data = this.parseSlurpMode(input);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
try {
|
|
138
|
+
data = JSON.parse(input);
|
|
139
|
+
// Cache the parsed result
|
|
140
|
+
this.cachedInput = input;
|
|
141
|
+
this.cachedData = data;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
throw new JqError('parse error: Invalid JSON input', 5);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return data;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Parse multiple JSON documents in slurp mode
|
|
151
|
+
*
|
|
152
|
+
* @param input - Input containing multiple JSON documents (newline-separated)
|
|
153
|
+
* @returns Array of parsed documents
|
|
154
|
+
*/
|
|
155
|
+
parseSlurpMode(input) {
|
|
156
|
+
const docs = [];
|
|
157
|
+
const lines = input.trim().split('\n');
|
|
158
|
+
let currentDoc = '';
|
|
159
|
+
let braceCount = 0;
|
|
160
|
+
let bracketCount = 0;
|
|
161
|
+
for (const line of lines) {
|
|
162
|
+
currentDoc += line;
|
|
163
|
+
for (const char of line) {
|
|
164
|
+
if (char === '{')
|
|
165
|
+
braceCount++;
|
|
166
|
+
else if (char === '}')
|
|
167
|
+
braceCount--;
|
|
168
|
+
else if (char === '[')
|
|
169
|
+
bracketCount++;
|
|
170
|
+
else if (char === ']')
|
|
171
|
+
bracketCount--;
|
|
172
|
+
}
|
|
173
|
+
if (braceCount === 0 && bracketCount === 0 && currentDoc.trim()) {
|
|
174
|
+
try {
|
|
175
|
+
docs.push(JSON.parse(currentDoc.trim()));
|
|
176
|
+
currentDoc = '';
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Continue accumulating
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Try to parse any remaining content
|
|
184
|
+
if (currentDoc.trim()) {
|
|
185
|
+
try {
|
|
186
|
+
docs.push(JSON.parse(currentDoc.trim()));
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// Ignore
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return docs;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Create execution context from options
|
|
196
|
+
*
|
|
197
|
+
* @param options - Options containing variable bindings
|
|
198
|
+
* @returns JqContext with variables
|
|
199
|
+
*/
|
|
200
|
+
createContext(options) {
|
|
201
|
+
return {
|
|
202
|
+
vars: { ...options.args },
|
|
203
|
+
argjson: { ...options.argjson },
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Evaluate a jq expression with iterator metadata
|
|
208
|
+
*
|
|
209
|
+
* @param query - The jq query expression
|
|
210
|
+
* @param data - Parsed JSON data
|
|
211
|
+
* @param context - Execution context
|
|
212
|
+
* @returns Result with value and iterator flag
|
|
213
|
+
*/
|
|
214
|
+
evaluateWithMeta(query, data, context) {
|
|
215
|
+
const trimmedQuery = query.trim();
|
|
216
|
+
if (trimmedQuery === '.' || trimmedQuery === '') {
|
|
217
|
+
return { value: data, isIterator: false };
|
|
218
|
+
}
|
|
219
|
+
const tokens = this.tokenize(trimmedQuery);
|
|
220
|
+
return this.executeTokens(tokens, data, context);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Tokenize a jq query into pipe-separated components
|
|
224
|
+
*
|
|
225
|
+
* Handles nested structures (parentheses, brackets, braces) and strings
|
|
226
|
+
* to correctly split on pipe operators.
|
|
227
|
+
*
|
|
228
|
+
* @param query - The jq query to tokenize
|
|
229
|
+
* @returns Array of token strings
|
|
230
|
+
*/
|
|
231
|
+
tokenize(query) {
|
|
232
|
+
const tokens = [];
|
|
233
|
+
let current = '';
|
|
234
|
+
let parenDepth = 0;
|
|
235
|
+
let bracketDepth = 0;
|
|
236
|
+
let braceDepth = 0;
|
|
237
|
+
let inString = false;
|
|
238
|
+
let stringChar = '';
|
|
239
|
+
for (let i = 0; i < query.length; i++) {
|
|
240
|
+
const char = query[i];
|
|
241
|
+
const prevChar = i > 0 ? query[i - 1] : '';
|
|
242
|
+
// Handle strings
|
|
243
|
+
if ((char === '"' || char === "'") && prevChar !== '\\') {
|
|
244
|
+
if (!inString) {
|
|
245
|
+
inString = true;
|
|
246
|
+
stringChar = char;
|
|
247
|
+
}
|
|
248
|
+
else if (char === stringChar) {
|
|
249
|
+
inString = false;
|
|
250
|
+
}
|
|
251
|
+
current += char;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (inString) {
|
|
255
|
+
current += char;
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
// Track nesting
|
|
259
|
+
if (char === '(')
|
|
260
|
+
parenDepth++;
|
|
261
|
+
else if (char === ')')
|
|
262
|
+
parenDepth--;
|
|
263
|
+
else if (char === '[')
|
|
264
|
+
bracketDepth++;
|
|
265
|
+
else if (char === ']')
|
|
266
|
+
bracketDepth--;
|
|
267
|
+
else if (char === '{')
|
|
268
|
+
braceDepth++;
|
|
269
|
+
else if (char === '}')
|
|
270
|
+
braceDepth--;
|
|
271
|
+
// Pipe separator at top level
|
|
272
|
+
if (char === '|' && parenDepth === 0 && bracketDepth === 0 && braceDepth === 0) {
|
|
273
|
+
if (current.trim()) {
|
|
274
|
+
tokens.push(current.trim());
|
|
275
|
+
}
|
|
276
|
+
current = '';
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
current += char;
|
|
280
|
+
}
|
|
281
|
+
if (current.trim()) {
|
|
282
|
+
tokens.push(current.trim());
|
|
283
|
+
}
|
|
284
|
+
return tokens;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Execute tokenized jq query
|
|
288
|
+
*
|
|
289
|
+
* Handles iterator semantics: when a filter produces multiple outputs,
|
|
290
|
+
* subsequent filters are applied to each output independently.
|
|
291
|
+
*
|
|
292
|
+
* @param tokens - Array of query tokens (pipe-separated components)
|
|
293
|
+
* @param data - Input data
|
|
294
|
+
* @param context - Execution context
|
|
295
|
+
* @returns Result with value and iterator flag
|
|
296
|
+
*/
|
|
297
|
+
executeTokens(tokens, data, context) {
|
|
298
|
+
let result = data;
|
|
299
|
+
let isIterator = false;
|
|
300
|
+
for (const token of tokens) {
|
|
301
|
+
if (isIterator && Array.isArray(result)) {
|
|
302
|
+
// Apply filter to each element
|
|
303
|
+
const filtered = [];
|
|
304
|
+
for (const item of result) {
|
|
305
|
+
const itemResult = this.executeExpression(token, item, context);
|
|
306
|
+
if (itemResult !== undefined) {
|
|
307
|
+
filtered.push(itemResult);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
result = filtered;
|
|
311
|
+
isIterator = true;
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
result = this.executeExpression(token, result, context);
|
|
315
|
+
isIterator = token === '.[]' || /^\.[a-zA-Z_]\w*\[\]$/.test(token);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return { value: result, isIterator };
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Execute a single jq expression
|
|
322
|
+
*
|
|
323
|
+
* @param expr - Expression to execute
|
|
324
|
+
* @param data - Current data
|
|
325
|
+
* @param context - Execution context
|
|
326
|
+
* @returns Expression result
|
|
327
|
+
* @throws {JqError} On invalid expression
|
|
328
|
+
*/
|
|
329
|
+
executeExpression(expr, data, context) {
|
|
330
|
+
const trimmed = expr.trim();
|
|
331
|
+
// Identity
|
|
332
|
+
if (trimmed === '.') {
|
|
333
|
+
return data;
|
|
334
|
+
}
|
|
335
|
+
// Iterator: .[]
|
|
336
|
+
if (trimmed === '.[]') {
|
|
337
|
+
return this.handleIterator(data);
|
|
338
|
+
}
|
|
339
|
+
// Key access with iterator: .key[]
|
|
340
|
+
const keyIterMatch = trimmed.match(/^\.([a-zA-Z_][a-zA-Z0-9_]*)\[\]$/);
|
|
341
|
+
if (keyIterMatch) {
|
|
342
|
+
return this.handleKeyIterator(data, keyIterMatch[1]);
|
|
343
|
+
}
|
|
344
|
+
// Path with iterator: .items[].name
|
|
345
|
+
const pathIterMatch = trimmed.match(/^\.([\w.]+)\[\]\.(\w+)$/);
|
|
346
|
+
if (pathIterMatch) {
|
|
347
|
+
return this.handlePathIterator(data, pathIterMatch[1], pathIterMatch[2]);
|
|
348
|
+
}
|
|
349
|
+
// Simple key access: .key or .key.nested
|
|
350
|
+
if (trimmed.startsWith('.') && /^\.[\w.]+$/.test(trimmed)) {
|
|
351
|
+
return this.getPath(data, trimmed.slice(1));
|
|
352
|
+
}
|
|
353
|
+
// Array index: .[n] or .[n:m]
|
|
354
|
+
const indexMatch = trimmed.match(/^\.\[(-?\d+)(?::(-?\d+))?\]$/);
|
|
355
|
+
if (indexMatch) {
|
|
356
|
+
return this.handleArrayIndex(data, indexMatch);
|
|
357
|
+
}
|
|
358
|
+
// Key with array index: .key[n] or .key[n:m]
|
|
359
|
+
const keyIndexMatch = trimmed.match(/^\.(\w+)\[(-?\d+)(?::(-?\d+))?\]$/);
|
|
360
|
+
if (keyIndexMatch) {
|
|
361
|
+
return this.handleKeyIndex(data, keyIndexMatch);
|
|
362
|
+
}
|
|
363
|
+
// Nested path with array index: .items[0].name
|
|
364
|
+
const nestedIndexMatch = trimmed.match(/^\.(\w+)\[(-?\d+)\]\.(\w+)$/);
|
|
365
|
+
if (nestedIndexMatch) {
|
|
366
|
+
return this.handleNestedIndex(data, nestedIndexMatch);
|
|
367
|
+
}
|
|
368
|
+
// Variable access: $var
|
|
369
|
+
if (trimmed.startsWith('$')) {
|
|
370
|
+
return this.handleVariable(trimmed, context);
|
|
371
|
+
}
|
|
372
|
+
// Dynamic key access with variable: .[$key]
|
|
373
|
+
const dynKeyMatch = trimmed.match(/^\.\[\$(\w+)\]$/);
|
|
374
|
+
if (dynKeyMatch) {
|
|
375
|
+
return this.handleDynamicKey(data, dynKeyMatch[1], context);
|
|
376
|
+
}
|
|
377
|
+
// Built-in functions
|
|
378
|
+
const builtinResult = this.handleBuiltinFunction(trimmed, data, context);
|
|
379
|
+
if (builtinResult !== NOT_A_BUILTIN) {
|
|
380
|
+
return builtinResult;
|
|
381
|
+
}
|
|
382
|
+
// Object construction
|
|
383
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
384
|
+
return this.constructObject(trimmed, data, context);
|
|
385
|
+
}
|
|
386
|
+
// Object addition
|
|
387
|
+
const addMatch = trimmed.match(/^\.\s*\+\s*(\{.+\})$/);
|
|
388
|
+
if (addMatch) {
|
|
389
|
+
const newObj = this.constructObject(addMatch[1], data, context);
|
|
390
|
+
return { ...data, ...newObj };
|
|
391
|
+
}
|
|
392
|
+
// Alternative operator: expr // default
|
|
393
|
+
if (trimmed.includes(' // ')) {
|
|
394
|
+
return this.handleAlternative(trimmed, data, context);
|
|
395
|
+
}
|
|
396
|
+
// if-then-else
|
|
397
|
+
const ifMatch = trimmed.match(/^if\s+(.+)\s+then\s+(.+)\s+else\s+(.+)\s+end$/);
|
|
398
|
+
if (ifMatch) {
|
|
399
|
+
return this.handleConditional(ifMatch, data, context);
|
|
400
|
+
}
|
|
401
|
+
// try-catch
|
|
402
|
+
const tryMatch = trimmed.match(/^try\s+(.+)\s+catch\s+(.+)$/);
|
|
403
|
+
if (tryMatch) {
|
|
404
|
+
return this.handleTryCatch(tryMatch, data, context);
|
|
405
|
+
}
|
|
406
|
+
// Literals
|
|
407
|
+
const literalResult = this.handleLiteral(trimmed);
|
|
408
|
+
if (literalResult !== NOT_A_BUILTIN) {
|
|
409
|
+
return literalResult;
|
|
410
|
+
}
|
|
411
|
+
throw new JqError(`Unknown expression: ${trimmed}`);
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Handle iterator expression (.[] )
|
|
415
|
+
*/
|
|
416
|
+
handleIterator(data) {
|
|
417
|
+
if (Array.isArray(data)) {
|
|
418
|
+
return data;
|
|
419
|
+
}
|
|
420
|
+
if (data && typeof data === 'object') {
|
|
421
|
+
return Object.values(data);
|
|
422
|
+
}
|
|
423
|
+
throw new JqError(`Cannot iterate over ${typeof data}`);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Handle key iterator (.key[])
|
|
427
|
+
*/
|
|
428
|
+
handleKeyIterator(data, key) {
|
|
429
|
+
const obj = data;
|
|
430
|
+
const value = obj?.[key];
|
|
431
|
+
if (Array.isArray(value)) {
|
|
432
|
+
return value;
|
|
433
|
+
}
|
|
434
|
+
if (value && typeof value === 'object') {
|
|
435
|
+
return Object.values(value);
|
|
436
|
+
}
|
|
437
|
+
throw new JqError(`Cannot iterate over ${typeof value}`);
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Handle path iterator (.items[].name)
|
|
441
|
+
*/
|
|
442
|
+
handlePathIterator(data, basePath, finalKey) {
|
|
443
|
+
let current = data;
|
|
444
|
+
for (const key of basePath.split('.')) {
|
|
445
|
+
if (current && typeof current === 'object') {
|
|
446
|
+
current = current[key];
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
return [null];
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (Array.isArray(current)) {
|
|
453
|
+
return current.map((item) => item?.[finalKey]);
|
|
454
|
+
}
|
|
455
|
+
throw new JqError(`Cannot iterate: not an array`);
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Handle array index (.[n] or .[n:m])
|
|
459
|
+
*/
|
|
460
|
+
handleArrayIndex(data, match) {
|
|
461
|
+
if (!Array.isArray(data)) {
|
|
462
|
+
throw new JqError('Cannot index non-array');
|
|
463
|
+
}
|
|
464
|
+
const start = parseInt(match[1], 10);
|
|
465
|
+
if (match[2] !== undefined) {
|
|
466
|
+
const end = parseInt(match[2], 10);
|
|
467
|
+
return data.slice(start < 0 ? data.length + start : start, end < 0 ? data.length + end : end);
|
|
468
|
+
}
|
|
469
|
+
const idx = start < 0 ? data.length + start : start;
|
|
470
|
+
return data[idx];
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Handle key with array index (.key[n])
|
|
474
|
+
*/
|
|
475
|
+
handleKeyIndex(data, match) {
|
|
476
|
+
const key = match[1];
|
|
477
|
+
const arr = data?.[key];
|
|
478
|
+
if (!Array.isArray(arr)) {
|
|
479
|
+
throw new JqError(`Cannot index: .${key} is not an array`);
|
|
480
|
+
}
|
|
481
|
+
const start = parseInt(match[2], 10);
|
|
482
|
+
if (match[3] !== undefined) {
|
|
483
|
+
const end = parseInt(match[3], 10);
|
|
484
|
+
return arr.slice(start < 0 ? arr.length + start : start, end < 0 ? arr.length + end : end);
|
|
485
|
+
}
|
|
486
|
+
const idx = start < 0 ? arr.length + start : start;
|
|
487
|
+
return arr[idx];
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Handle nested path with array index (.items[0].name)
|
|
491
|
+
*/
|
|
492
|
+
handleNestedIndex(data, match) {
|
|
493
|
+
const key = match[1];
|
|
494
|
+
const idx = parseInt(match[2], 10);
|
|
495
|
+
const finalKey = match[3];
|
|
496
|
+
const arr = data?.[key];
|
|
497
|
+
if (!Array.isArray(arr)) {
|
|
498
|
+
throw new JqError(`Cannot index: .${key} is not an array`);
|
|
499
|
+
}
|
|
500
|
+
const realIdx = idx < 0 ? arr.length + idx : idx;
|
|
501
|
+
const item = arr[realIdx];
|
|
502
|
+
if (item === undefined || item === null) {
|
|
503
|
+
throw new JqError(`Cannot get .${finalKey} of null`);
|
|
504
|
+
}
|
|
505
|
+
return item?.[finalKey];
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Handle variable access ($var)
|
|
509
|
+
*/
|
|
510
|
+
handleVariable(varExpr, context) {
|
|
511
|
+
const varName = varExpr.slice(1);
|
|
512
|
+
if (varName in context.argjson) {
|
|
513
|
+
return context.argjson[varName];
|
|
514
|
+
}
|
|
515
|
+
if (varName in context.vars) {
|
|
516
|
+
return context.vars[varName];
|
|
517
|
+
}
|
|
518
|
+
throw new JqError(`Variable ${varExpr} is not defined`);
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Handle dynamic key access (.[$key])
|
|
522
|
+
*/
|
|
523
|
+
handleDynamicKey(data, varName, context) {
|
|
524
|
+
let key;
|
|
525
|
+
if (varName in context.argjson) {
|
|
526
|
+
key = String(context.argjson[varName]);
|
|
527
|
+
}
|
|
528
|
+
else if (varName in context.vars) {
|
|
529
|
+
key = context.vars[varName];
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
throw new JqError(`Variable $${varName} is not defined`);
|
|
533
|
+
}
|
|
534
|
+
return data?.[key];
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Handle built-in functions (length, keys, sort, etc.)
|
|
538
|
+
*
|
|
539
|
+
* @returns Result value, undefined for select filter-out, or NOT_A_BUILTIN if not a builtin
|
|
540
|
+
*/
|
|
541
|
+
handleBuiltinFunction(expr, data, context) {
|
|
542
|
+
// Simple builtins
|
|
543
|
+
switch (expr) {
|
|
544
|
+
case 'length':
|
|
545
|
+
if (Array.isArray(data))
|
|
546
|
+
return data.length;
|
|
547
|
+
if (typeof data === 'string')
|
|
548
|
+
return data.length;
|
|
549
|
+
if (data && typeof data === 'object')
|
|
550
|
+
return Object.keys(data).length;
|
|
551
|
+
return 0;
|
|
552
|
+
case 'keys':
|
|
553
|
+
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
554
|
+
return Object.keys(data).sort();
|
|
555
|
+
}
|
|
556
|
+
if (Array.isArray(data)) {
|
|
557
|
+
return data.map((_, i) => i);
|
|
558
|
+
}
|
|
559
|
+
throw new JqError('keys requires an object or array');
|
|
560
|
+
case 'values':
|
|
561
|
+
if (data === null || data === undefined) {
|
|
562
|
+
return undefined;
|
|
563
|
+
}
|
|
564
|
+
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
565
|
+
return Object.values(data);
|
|
566
|
+
}
|
|
567
|
+
return data;
|
|
568
|
+
case 'type':
|
|
569
|
+
if (data === null)
|
|
570
|
+
return 'null';
|
|
571
|
+
if (Array.isArray(data))
|
|
572
|
+
return 'array';
|
|
573
|
+
return typeof data;
|
|
574
|
+
case 'tonumber': {
|
|
575
|
+
const num = Number(data);
|
|
576
|
+
if (isNaN(num))
|
|
577
|
+
throw new JqError('Cannot convert to number');
|
|
578
|
+
return num;
|
|
579
|
+
}
|
|
580
|
+
case 'tostring':
|
|
581
|
+
if (typeof data === 'string')
|
|
582
|
+
return data;
|
|
583
|
+
return JSON.stringify(data);
|
|
584
|
+
case 'sort':
|
|
585
|
+
if (!Array.isArray(data))
|
|
586
|
+
throw new JqError('sort requires an array');
|
|
587
|
+
return [...data].sort((a, b) => {
|
|
588
|
+
if (typeof a === 'string' && typeof b === 'string')
|
|
589
|
+
return a.localeCompare(b);
|
|
590
|
+
if (typeof a === 'number' && typeof b === 'number')
|
|
591
|
+
return a - b;
|
|
592
|
+
return String(a).localeCompare(String(b));
|
|
593
|
+
});
|
|
594
|
+
case 'reverse':
|
|
595
|
+
if (!Array.isArray(data))
|
|
596
|
+
throw new JqError('reverse requires an array');
|
|
597
|
+
return [...data].reverse();
|
|
598
|
+
case 'unique':
|
|
599
|
+
if (!Array.isArray(data))
|
|
600
|
+
throw new JqError('unique requires an array');
|
|
601
|
+
return this.uniqueArray(data);
|
|
602
|
+
case 'flatten':
|
|
603
|
+
if (!Array.isArray(data))
|
|
604
|
+
throw new JqError('flatten requires an array');
|
|
605
|
+
return data.flat(Infinity);
|
|
606
|
+
case 'add':
|
|
607
|
+
if (!Array.isArray(data))
|
|
608
|
+
throw new JqError('add requires an array');
|
|
609
|
+
return this.addArray(data);
|
|
610
|
+
case 'ascii_upcase':
|
|
611
|
+
if (typeof data !== 'string')
|
|
612
|
+
throw new JqError('ascii_upcase requires a string');
|
|
613
|
+
return data.toUpperCase();
|
|
614
|
+
case 'ascii_downcase':
|
|
615
|
+
if (typeof data !== 'string')
|
|
616
|
+
throw new JqError('ascii_downcase requires a string');
|
|
617
|
+
return data.toLowerCase();
|
|
618
|
+
}
|
|
619
|
+
// Parameterized builtins
|
|
620
|
+
return this.handleParameterizedBuiltin(expr, data, context);
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Handle parameterized built-in functions (sort_by, map, select, etc.)
|
|
624
|
+
*/
|
|
625
|
+
handleParameterizedBuiltin(expr, data, context) {
|
|
626
|
+
// sort_by(.key)
|
|
627
|
+
const sortByMatch = expr.match(/^sort_by\(\.(\w+)\)$/);
|
|
628
|
+
if (sortByMatch) {
|
|
629
|
+
if (!Array.isArray(data))
|
|
630
|
+
throw new JqError('sort_by requires an array');
|
|
631
|
+
const key = sortByMatch[1];
|
|
632
|
+
return [...data].sort((a, b) => {
|
|
633
|
+
const aVal = a?.[key];
|
|
634
|
+
const bVal = b?.[key];
|
|
635
|
+
if (typeof aVal === 'number' && typeof bVal === 'number')
|
|
636
|
+
return aVal - bVal;
|
|
637
|
+
return String(aVal ?? '').localeCompare(String(bVal ?? ''));
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
// map(expr)
|
|
641
|
+
const mapMatch = expr.match(/^map\((.+)\)$/);
|
|
642
|
+
if (mapMatch) {
|
|
643
|
+
if (!Array.isArray(data))
|
|
644
|
+
throw new JqError('map requires an array');
|
|
645
|
+
const innerExpr = mapMatch[1];
|
|
646
|
+
// Handle select inside map - filter out undefined results
|
|
647
|
+
const results = [];
|
|
648
|
+
for (const item of data) {
|
|
649
|
+
const result = this.evaluate(innerExpr, item, context);
|
|
650
|
+
if (result !== undefined) {
|
|
651
|
+
results.push(result);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return results;
|
|
655
|
+
}
|
|
656
|
+
// select(condition)
|
|
657
|
+
const selectMatch = expr.match(/^select\((.+)\)$/);
|
|
658
|
+
if (selectMatch) {
|
|
659
|
+
if (this.evaluateCondition(selectMatch[1], data, context)) {
|
|
660
|
+
return data;
|
|
661
|
+
}
|
|
662
|
+
return undefined;
|
|
663
|
+
}
|
|
664
|
+
// has("key")
|
|
665
|
+
const hasMatch = expr.match(/^has\("([^"]+)"\)$/) || expr.match(/^has\(\\"([^"]+)\\"\)$/);
|
|
666
|
+
if (hasMatch) {
|
|
667
|
+
if (data && typeof data === 'object') {
|
|
668
|
+
return hasMatch[1] in data;
|
|
669
|
+
}
|
|
670
|
+
return false;
|
|
671
|
+
}
|
|
672
|
+
// split("delimiter")
|
|
673
|
+
const splitMatch = expr.match(/^split\("([^"]*)"\)$/) || expr.match(/^split\(\\"([^"]*)\\"\)$/);
|
|
674
|
+
if (splitMatch) {
|
|
675
|
+
if (typeof data !== 'string')
|
|
676
|
+
throw new JqError('split requires a string');
|
|
677
|
+
return data.split(splitMatch[1]);
|
|
678
|
+
}
|
|
679
|
+
// join("delimiter")
|
|
680
|
+
const joinMatch = expr.match(/^join\("([^"]*)"\)$/) || expr.match(/^join\(\\"([^"]*)\\"\)$/);
|
|
681
|
+
if (joinMatch) {
|
|
682
|
+
if (!Array.isArray(data))
|
|
683
|
+
throw new JqError('join requires an array');
|
|
684
|
+
return data.join(joinMatch[1]);
|
|
685
|
+
}
|
|
686
|
+
// test("pattern")
|
|
687
|
+
const testMatch = expr.match(/^test\("([^"]+)"\)$/) || expr.match(/^test\(\\"([^"]+)\\"\)$/);
|
|
688
|
+
if (testMatch) {
|
|
689
|
+
if (typeof data !== 'string')
|
|
690
|
+
throw new JqError('test requires a string');
|
|
691
|
+
return new RegExp(testMatch[1]).test(data);
|
|
692
|
+
}
|
|
693
|
+
return NOT_A_BUILTIN;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Get unique values from array
|
|
697
|
+
*/
|
|
698
|
+
uniqueArray(data) {
|
|
699
|
+
const seen = new Set();
|
|
700
|
+
return data.filter((item) => {
|
|
701
|
+
const key = JSON.stringify(item);
|
|
702
|
+
if (seen.has(key))
|
|
703
|
+
return false;
|
|
704
|
+
seen.add(key);
|
|
705
|
+
return true;
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Add array elements (numbers, strings, arrays, or objects)
|
|
710
|
+
*/
|
|
711
|
+
addArray(data) {
|
|
712
|
+
if (data.length === 0)
|
|
713
|
+
return null;
|
|
714
|
+
if (typeof data[0] === 'number') {
|
|
715
|
+
return data.reduce((a, b) => a + b, 0);
|
|
716
|
+
}
|
|
717
|
+
if (typeof data[0] === 'string') {
|
|
718
|
+
return data.join('');
|
|
719
|
+
}
|
|
720
|
+
if (Array.isArray(data[0])) {
|
|
721
|
+
return data.flat(1);
|
|
722
|
+
}
|
|
723
|
+
return data.reduce((a, b) => ({ ...a, ...b }), {});
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Handle alternative operator (expr // default)
|
|
727
|
+
*/
|
|
728
|
+
handleAlternative(expr, data, context) {
|
|
729
|
+
const [primary, fallback] = expr.split(' // ');
|
|
730
|
+
try {
|
|
731
|
+
const result = this.evaluate(primary.trim(), data, context);
|
|
732
|
+
if (result === null || result === undefined) {
|
|
733
|
+
return this.evaluate(fallback.trim(), data, context);
|
|
734
|
+
}
|
|
735
|
+
return result;
|
|
736
|
+
}
|
|
737
|
+
catch {
|
|
738
|
+
return this.evaluate(fallback.trim(), data, context);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Handle if-then-else conditional
|
|
743
|
+
*/
|
|
744
|
+
handleConditional(match, data, context) {
|
|
745
|
+
const condition = match[1];
|
|
746
|
+
const thenExpr = match[2];
|
|
747
|
+
const elseExpr = match[3];
|
|
748
|
+
if (this.evaluateCondition(condition, data, context)) {
|
|
749
|
+
return this.evaluate(thenExpr, data, context);
|
|
750
|
+
}
|
|
751
|
+
return this.evaluate(elseExpr, data, context);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Handle try-catch expression
|
|
755
|
+
*/
|
|
756
|
+
handleTryCatch(match, data, context) {
|
|
757
|
+
try {
|
|
758
|
+
return this.evaluate(match[1], data, context);
|
|
759
|
+
}
|
|
760
|
+
catch {
|
|
761
|
+
const catchExpr = match[2].trim();
|
|
762
|
+
if (catchExpr.startsWith('"') && catchExpr.endsWith('"')) {
|
|
763
|
+
return catchExpr.slice(1, -1);
|
|
764
|
+
}
|
|
765
|
+
if (catchExpr.startsWith('\\"') && catchExpr.endsWith('\\"')) {
|
|
766
|
+
return catchExpr.slice(2, -2);
|
|
767
|
+
}
|
|
768
|
+
return this.evaluate(catchExpr, data, context);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Handle literal values (strings, numbers, booleans, null)
|
|
773
|
+
*/
|
|
774
|
+
handleLiteral(expr) {
|
|
775
|
+
// String literal
|
|
776
|
+
if ((expr.startsWith('"') && expr.endsWith('"')) || (expr.startsWith('\\"') && expr.endsWith('\\"'))) {
|
|
777
|
+
return expr.startsWith('\\"') ? expr.slice(2, -2) : expr.slice(1, -1);
|
|
778
|
+
}
|
|
779
|
+
// Number literal
|
|
780
|
+
if (/^-?\d+(\.\d+)?$/.test(expr)) {
|
|
781
|
+
return parseFloat(expr);
|
|
782
|
+
}
|
|
783
|
+
// Boolean and null literals
|
|
784
|
+
if (expr === 'true')
|
|
785
|
+
return true;
|
|
786
|
+
if (expr === 'false')
|
|
787
|
+
return false;
|
|
788
|
+
if (expr === 'null')
|
|
789
|
+
return null;
|
|
790
|
+
return NOT_A_BUILTIN;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Evaluate a condition expression for select/if-then-else
|
|
794
|
+
*/
|
|
795
|
+
evaluateCondition(condition, data, context) {
|
|
796
|
+
const trimmed = condition.trim();
|
|
797
|
+
// Handle compound conditions (and/or)
|
|
798
|
+
if (trimmed.includes(' and ')) {
|
|
799
|
+
const parts = trimmed.split(' and ');
|
|
800
|
+
return parts.every((part) => this.evaluateCondition(part.trim(), data, context));
|
|
801
|
+
}
|
|
802
|
+
if (trimmed.includes(' or ')) {
|
|
803
|
+
const parts = trimmed.split(' or ');
|
|
804
|
+
return parts.some((part) => this.evaluateCondition(part.trim(), data, context));
|
|
805
|
+
}
|
|
806
|
+
// Comparison operators
|
|
807
|
+
const compMatch = trimmed.match(/^(.+?)\s*(>=|<=|>|<|==|!=)\s*(.+)$/);
|
|
808
|
+
if (compMatch) {
|
|
809
|
+
return this.evaluateComparison(compMatch, data, context);
|
|
810
|
+
}
|
|
811
|
+
// Truthy check for path expressions
|
|
812
|
+
if (trimmed.startsWith('.')) {
|
|
813
|
+
const value = this.evaluate(trimmed, data, context);
|
|
814
|
+
return Boolean(value);
|
|
815
|
+
}
|
|
816
|
+
// has("key") condition
|
|
817
|
+
const hasCondMatch = trimmed.match(/^has\("([^"]+)"\)$/) || trimmed.match(/^has\(\\"([^"]+)\\"\)$/);
|
|
818
|
+
if (hasCondMatch) {
|
|
819
|
+
const hasKey = data && typeof data === 'object' && hasCondMatch[1] in data;
|
|
820
|
+
return hasKey;
|
|
821
|
+
}
|
|
822
|
+
return false;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Evaluate comparison expression
|
|
826
|
+
*/
|
|
827
|
+
evaluateComparison(match, data, context) {
|
|
828
|
+
const left = this.evaluate(match[1].trim(), data, context);
|
|
829
|
+
const op = match[2];
|
|
830
|
+
let right = match[3].trim();
|
|
831
|
+
// Parse right side
|
|
832
|
+
if (/^-?\d+(\.\d+)?$/.test(right)) {
|
|
833
|
+
right = parseFloat(right);
|
|
834
|
+
}
|
|
835
|
+
else if (right.startsWith('$')) {
|
|
836
|
+
const varName = right.slice(1);
|
|
837
|
+
right = context.argjson[varName] ?? context.vars[varName];
|
|
838
|
+
}
|
|
839
|
+
else if (right.startsWith('.')) {
|
|
840
|
+
right = this.evaluate(right, data, context);
|
|
841
|
+
}
|
|
842
|
+
else if (right.startsWith('"') && right.endsWith('"')) {
|
|
843
|
+
right = right.slice(1, -1);
|
|
844
|
+
}
|
|
845
|
+
switch (op) {
|
|
846
|
+
case '>':
|
|
847
|
+
return left > right;
|
|
848
|
+
case '<':
|
|
849
|
+
return left < right;
|
|
850
|
+
case '>=':
|
|
851
|
+
return left >= right;
|
|
852
|
+
case '<=':
|
|
853
|
+
return left <= right;
|
|
854
|
+
case '==':
|
|
855
|
+
return left === right;
|
|
856
|
+
case '!=':
|
|
857
|
+
return left !== right;
|
|
858
|
+
default:
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Construct an object from jq object syntax ({key, newKey: .expr})
|
|
864
|
+
*/
|
|
865
|
+
constructObject(expr, data, context) {
|
|
866
|
+
const inner = expr.slice(1, -1).trim();
|
|
867
|
+
const result = {};
|
|
868
|
+
const pairs = this.parseObjectPairs(inner);
|
|
869
|
+
for (const pair of pairs) {
|
|
870
|
+
// Shorthand: key (same as key: .key)
|
|
871
|
+
if (/^\w+$/.test(pair)) {
|
|
872
|
+
result[pair] = data?.[pair];
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
// Full syntax: newKey: .expr or newKey: "value"
|
|
876
|
+
const colonIdx = pair.indexOf(':');
|
|
877
|
+
if (colonIdx > 0) {
|
|
878
|
+
const key = pair.slice(0, colonIdx).trim();
|
|
879
|
+
const valueExpr = pair.slice(colonIdx + 1).trim();
|
|
880
|
+
if (valueExpr.startsWith('\\"') && valueExpr.endsWith('\\"')) {
|
|
881
|
+
result[key] = valueExpr.slice(2, -2);
|
|
882
|
+
}
|
|
883
|
+
else if (valueExpr.startsWith('"') && valueExpr.endsWith('"')) {
|
|
884
|
+
result[key] = valueExpr.slice(1, -1);
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
result[key] = this.evaluate(valueExpr, data, context);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return result;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Parse object key-value pairs, handling nested structures
|
|
895
|
+
*/
|
|
896
|
+
parseObjectPairs(inner) {
|
|
897
|
+
const pairs = [];
|
|
898
|
+
let current = '';
|
|
899
|
+
let depth = 0;
|
|
900
|
+
let inString = false;
|
|
901
|
+
for (let i = 0; i < inner.length; i++) {
|
|
902
|
+
const char = inner[i];
|
|
903
|
+
if (char === '"' && inner[i - 1] !== '\\') {
|
|
904
|
+
inString = !inString;
|
|
905
|
+
}
|
|
906
|
+
if (!inString) {
|
|
907
|
+
if (char === '{' || char === '[')
|
|
908
|
+
depth++;
|
|
909
|
+
else if (char === '}' || char === ']')
|
|
910
|
+
depth--;
|
|
911
|
+
else if (char === ',' && depth === 0) {
|
|
912
|
+
pairs.push(current.trim());
|
|
913
|
+
current = '';
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
current += char;
|
|
918
|
+
}
|
|
919
|
+
if (current.trim()) {
|
|
920
|
+
pairs.push(current.trim());
|
|
921
|
+
}
|
|
922
|
+
return pairs;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Get value at a dot-separated path
|
|
926
|
+
*/
|
|
927
|
+
getPath(data, path) {
|
|
928
|
+
const parts = path.split('.');
|
|
929
|
+
let current = data;
|
|
930
|
+
for (const part of parts) {
|
|
931
|
+
if (current === null || current === undefined) {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
if (typeof current !== 'object') {
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
current = current[part];
|
|
938
|
+
}
|
|
939
|
+
return current === undefined ? null : current;
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Format jq output based on options
|
|
943
|
+
*/
|
|
944
|
+
formatOutput(result, options, isIteratorResult = false) {
|
|
945
|
+
// Filter out undefined values
|
|
946
|
+
if (Array.isArray(result) && result.some((r) => r === undefined)) {
|
|
947
|
+
result = result.filter((r) => r !== undefined);
|
|
948
|
+
}
|
|
949
|
+
// Handle undefined
|
|
950
|
+
if (result === undefined) {
|
|
951
|
+
return '';
|
|
952
|
+
}
|
|
953
|
+
// Handle empty iterator result
|
|
954
|
+
if (isIteratorResult && Array.isArray(result) && result.length === 0) {
|
|
955
|
+
return '';
|
|
956
|
+
}
|
|
957
|
+
// Iterator output - each result on its own line
|
|
958
|
+
if (isIteratorResult && Array.isArray(result)) {
|
|
959
|
+
if (options.tab) {
|
|
960
|
+
return result
|
|
961
|
+
.map((item) => {
|
|
962
|
+
if (typeof item === 'string')
|
|
963
|
+
return options.raw ? item : JSON.stringify(item);
|
|
964
|
+
return JSON.stringify(item);
|
|
965
|
+
})
|
|
966
|
+
.join('\n') + '\n';
|
|
967
|
+
}
|
|
968
|
+
return result
|
|
969
|
+
.map((item) => {
|
|
970
|
+
if (options.raw && typeof item === 'string')
|
|
971
|
+
return item;
|
|
972
|
+
return options.compact ? JSON.stringify(item) : JSON.stringify(item, null, 2);
|
|
973
|
+
})
|
|
974
|
+
.join('\n') + '\n';
|
|
975
|
+
}
|
|
976
|
+
// Raw output for strings
|
|
977
|
+
if (options.raw && typeof result === 'string') {
|
|
978
|
+
return result + '\n';
|
|
979
|
+
}
|
|
980
|
+
// Normal JSON output
|
|
981
|
+
const formatted = options.compact ? JSON.stringify(result) : JSON.stringify(result, null, 2);
|
|
982
|
+
return formatted + '\n';
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* Clear the parsed JSON cache
|
|
986
|
+
*/
|
|
987
|
+
clearCache() {
|
|
988
|
+
this.cachedInput = null;
|
|
989
|
+
this.cachedData = null;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
// Global JqEngine instance for simple usage
|
|
993
|
+
const defaultJqEngine = new JqEngine();
|
|
994
|
+
/**
|
|
995
|
+
* Execute a jq query on JSON input
|
|
996
|
+
*
|
|
997
|
+
* This is the main entry point for jq functionality. For repeated queries
|
|
998
|
+
* on the same input, consider using JqEngine directly for better caching.
|
|
999
|
+
*
|
|
1000
|
+
* @param query - The jq query expression
|
|
1001
|
+
* @param input - JSON input string
|
|
1002
|
+
* @param options - Execution options
|
|
1003
|
+
* @returns Query result as string
|
|
1004
|
+
* @throws {JqError} On invalid JSON or query syntax errors
|
|
1005
|
+
*
|
|
1006
|
+
* @example
|
|
1007
|
+
* ```typescript
|
|
1008
|
+
* // Simple key extraction
|
|
1009
|
+
* executeJq('.name', '{"name": "test"}')
|
|
1010
|
+
* // => '"test"\n'
|
|
1011
|
+
*
|
|
1012
|
+
* // Filter array
|
|
1013
|
+
* executeJq('.[] | select(.age > 18)', '[{"age": 25}, {"age": 17}]')
|
|
1014
|
+
* ```
|
|
1015
|
+
*/
|
|
1016
|
+
export function executeJq(query, input, options = {}) {
|
|
1017
|
+
return defaultJqEngine.execute(query, input, options);
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Parse a YAML string into a JavaScript value
|
|
1021
|
+
*
|
|
1022
|
+
* Supports a subset of YAML including:
|
|
1023
|
+
* - Key-value pairs
|
|
1024
|
+
* - Nested objects
|
|
1025
|
+
* - Arrays (both block and inline)
|
|
1026
|
+
* - Anchors and aliases
|
|
1027
|
+
* - Multi-document files
|
|
1028
|
+
* - Basic scalar types (strings, numbers, booleans, null)
|
|
1029
|
+
*
|
|
1030
|
+
* @param input - YAML input string
|
|
1031
|
+
* @returns Parsed JavaScript value
|
|
1032
|
+
* @throws {Error} On invalid YAML syntax
|
|
1033
|
+
*
|
|
1034
|
+
* @example
|
|
1035
|
+
* ```typescript
|
|
1036
|
+
* parseYaml('name: bashx\nversion: 1.0.0')
|
|
1037
|
+
* // => { name: 'bashx', version: '1.0.0' }
|
|
1038
|
+
* ```
|
|
1039
|
+
*/
|
|
1040
|
+
export function parseYaml(input) {
|
|
1041
|
+
const lines = input.split('\n');
|
|
1042
|
+
const docs = [];
|
|
1043
|
+
let currentDocLines = [];
|
|
1044
|
+
for (const line of lines) {
|
|
1045
|
+
if (line === '---') {
|
|
1046
|
+
if (currentDocLines.length > 0) {
|
|
1047
|
+
docs.push(parseYamlDocument(currentDocLines));
|
|
1048
|
+
currentDocLines = [];
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
else {
|
|
1052
|
+
currentDocLines.push(line);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (currentDocLines.length > 0 || docs.length === 0) {
|
|
1056
|
+
docs.push(parseYamlDocument(currentDocLines));
|
|
1057
|
+
}
|
|
1058
|
+
return docs.length === 1 ? docs[0] : docs;
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Parse a single YAML document
|
|
1062
|
+
*/
|
|
1063
|
+
function parseYamlDocument(lines) {
|
|
1064
|
+
const anchors = {};
|
|
1065
|
+
return parseYamlLines(lines, 0, anchors).value;
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Parse YAML lines at a given indentation level
|
|
1069
|
+
*/
|
|
1070
|
+
function parseYamlLines(lines, startIndent, anchors) {
|
|
1071
|
+
const result = {};
|
|
1072
|
+
let i = 0;
|
|
1073
|
+
while (i < lines.length) {
|
|
1074
|
+
const line = lines[i];
|
|
1075
|
+
// Skip empty lines and comments
|
|
1076
|
+
if (!line.trim() || line.trim().startsWith('#')) {
|
|
1077
|
+
i++;
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
const indent = line.search(/\S/);
|
|
1081
|
+
if (indent < startIndent) {
|
|
1082
|
+
break;
|
|
1083
|
+
}
|
|
1084
|
+
const content = line.trim();
|
|
1085
|
+
// Handle list item
|
|
1086
|
+
if (content.startsWith('- ')) {
|
|
1087
|
+
const arr = [];
|
|
1088
|
+
while (i < lines.length) {
|
|
1089
|
+
const listLine = lines[i];
|
|
1090
|
+
if (!listLine.trim() || listLine.trim().startsWith('#')) {
|
|
1091
|
+
i++;
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
const listIndent = listLine.search(/\S/);
|
|
1095
|
+
if (listIndent < startIndent || !listLine.trim().startsWith('-')) {
|
|
1096
|
+
break;
|
|
1097
|
+
}
|
|
1098
|
+
const itemContent = listLine.trim().slice(2).trim();
|
|
1099
|
+
arr.push(parseYamlValue(itemContent, anchors));
|
|
1100
|
+
i++;
|
|
1101
|
+
}
|
|
1102
|
+
return { value: arr, consumed: i };
|
|
1103
|
+
}
|
|
1104
|
+
// Handle key: value
|
|
1105
|
+
const colonIdx = content.indexOf(':');
|
|
1106
|
+
if (colonIdx > 0) {
|
|
1107
|
+
const key = content.slice(0, colonIdx).trim();
|
|
1108
|
+
let valueStr = content.slice(colonIdx + 1).trim();
|
|
1109
|
+
// Handle anchor definition: &anchorName
|
|
1110
|
+
let anchorName = null;
|
|
1111
|
+
const anchorMatch = valueStr.match(/^&(\w+)\s*/);
|
|
1112
|
+
if (anchorMatch) {
|
|
1113
|
+
anchorName = anchorMatch[1];
|
|
1114
|
+
valueStr = valueStr.slice(anchorMatch[0].length);
|
|
1115
|
+
}
|
|
1116
|
+
// Handle merge: <<: *anchorName
|
|
1117
|
+
if (key === '<<') {
|
|
1118
|
+
const mergeAliasMatch = valueStr.match(/^\*(\w+)$/);
|
|
1119
|
+
if (mergeAliasMatch) {
|
|
1120
|
+
const merged = anchors[mergeAliasMatch[1]];
|
|
1121
|
+
if (merged && typeof merged === 'object') {
|
|
1122
|
+
Object.assign(result, merged);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
i++;
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
// Handle alias: *anchorName
|
|
1129
|
+
const aliasMatch = valueStr.match(/^\*(\w+)$/);
|
|
1130
|
+
if (aliasMatch) {
|
|
1131
|
+
result[key] = anchors[aliasMatch[1]];
|
|
1132
|
+
i++;
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
if (valueStr) {
|
|
1136
|
+
// Inline value
|
|
1137
|
+
const value = parseYamlValue(valueStr, anchors);
|
|
1138
|
+
if (anchorName) {
|
|
1139
|
+
anchors[anchorName] = value;
|
|
1140
|
+
}
|
|
1141
|
+
result[key] = value;
|
|
1142
|
+
i++;
|
|
1143
|
+
}
|
|
1144
|
+
else {
|
|
1145
|
+
// Nested structure
|
|
1146
|
+
i++;
|
|
1147
|
+
const nextIndent = i < lines.length ? lines[i].search(/\S/) : 0;
|
|
1148
|
+
if (nextIndent > indent) {
|
|
1149
|
+
const nested = parseYamlLines(lines.slice(i), nextIndent, anchors);
|
|
1150
|
+
if (anchorName) {
|
|
1151
|
+
anchors[anchorName] = nested.value;
|
|
1152
|
+
}
|
|
1153
|
+
result[key] = nested.value;
|
|
1154
|
+
i += nested.consumed;
|
|
1155
|
+
}
|
|
1156
|
+
else {
|
|
1157
|
+
result[key] = null;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
else {
|
|
1162
|
+
i++;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
return { value: result, consumed: i };
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Parse a single YAML value
|
|
1169
|
+
*/
|
|
1170
|
+
function parseYamlValue(str, anchors) {
|
|
1171
|
+
const trimmed = str.trim();
|
|
1172
|
+
// Handle anchor reference
|
|
1173
|
+
if (trimmed.startsWith('*')) {
|
|
1174
|
+
return anchors[trimmed.slice(1)];
|
|
1175
|
+
}
|
|
1176
|
+
// Handle quoted strings
|
|
1177
|
+
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
|
1178
|
+
return trimmed.slice(1, -1);
|
|
1179
|
+
}
|
|
1180
|
+
// Detect unclosed quotes - invalid YAML
|
|
1181
|
+
if ((trimmed.startsWith('"') && !trimmed.endsWith('"')) ||
|
|
1182
|
+
(trimmed.startsWith("'") && !trimmed.endsWith("'"))) {
|
|
1183
|
+
throw new Error(`Unclosed quote in YAML value: ${trimmed}`);
|
|
1184
|
+
}
|
|
1185
|
+
// Handle inline array
|
|
1186
|
+
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
|
1187
|
+
const inner = trimmed.slice(1, -1);
|
|
1188
|
+
if (!inner.trim())
|
|
1189
|
+
return [];
|
|
1190
|
+
return inner.split(',').map((s) => parseYamlValue(s.trim(), anchors));
|
|
1191
|
+
}
|
|
1192
|
+
// Detect unclosed bracket - invalid YAML
|
|
1193
|
+
if (trimmed.startsWith('[') && !trimmed.endsWith(']')) {
|
|
1194
|
+
throw new Error(`Unclosed bracket in YAML value: ${trimmed}`);
|
|
1195
|
+
}
|
|
1196
|
+
// Handle inline object
|
|
1197
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
1198
|
+
const inner = trimmed.slice(1, -1);
|
|
1199
|
+
if (!inner.trim())
|
|
1200
|
+
return {};
|
|
1201
|
+
const obj = {};
|
|
1202
|
+
const pairs = inner.split(',');
|
|
1203
|
+
for (const pair of pairs) {
|
|
1204
|
+
const [k, v] = pair.split(':').map((s) => s.trim());
|
|
1205
|
+
if (k && v !== undefined) {
|
|
1206
|
+
obj[k] = parseYamlValue(v, anchors);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
return obj;
|
|
1210
|
+
}
|
|
1211
|
+
// Detect unclosed brace - invalid YAML
|
|
1212
|
+
if (trimmed.startsWith('{') && !trimmed.endsWith('}')) {
|
|
1213
|
+
throw new Error(`Unclosed brace in YAML value: ${trimmed}`);
|
|
1214
|
+
}
|
|
1215
|
+
// Handle numbers
|
|
1216
|
+
if (/^-?\d+$/.test(trimmed)) {
|
|
1217
|
+
return parseInt(trimmed, 10);
|
|
1218
|
+
}
|
|
1219
|
+
if (/^-?\d+\.\d+$/.test(trimmed)) {
|
|
1220
|
+
return parseFloat(trimmed);
|
|
1221
|
+
}
|
|
1222
|
+
// Handle booleans
|
|
1223
|
+
if (trimmed === 'true' || trimmed === 'yes' || trimmed === 'on') {
|
|
1224
|
+
return true;
|
|
1225
|
+
}
|
|
1226
|
+
if (trimmed === 'false' || trimmed === 'no' || trimmed === 'off') {
|
|
1227
|
+
return false;
|
|
1228
|
+
}
|
|
1229
|
+
// Handle null
|
|
1230
|
+
if (trimmed === 'null' || trimmed === '~' || trimmed === '') {
|
|
1231
|
+
return null;
|
|
1232
|
+
}
|
|
1233
|
+
// Plain string
|
|
1234
|
+
return trimmed;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Convert a JavaScript value to YAML format
|
|
1238
|
+
*
|
|
1239
|
+
* @param data - Value to stringify
|
|
1240
|
+
* @param indent - Current indentation level
|
|
1241
|
+
* @returns YAML string representation
|
|
1242
|
+
*
|
|
1243
|
+
* @example
|
|
1244
|
+
* ```typescript
|
|
1245
|
+
* stringifyYaml({ name: 'bashx', version: '1.0.0' })
|
|
1246
|
+
* // => 'name: bashx\nversion: 1.0.0'
|
|
1247
|
+
* ```
|
|
1248
|
+
*/
|
|
1249
|
+
export function stringifyYaml(data, indent = 0) {
|
|
1250
|
+
const prefix = ' '.repeat(indent);
|
|
1251
|
+
if (data === null || data === undefined) {
|
|
1252
|
+
return 'null';
|
|
1253
|
+
}
|
|
1254
|
+
if (typeof data === 'string') {
|
|
1255
|
+
// Quote if contains special characters
|
|
1256
|
+
if (/[:\[\]{}"'#|>&*!?]/.test(data) || /^\s|\s$/.test(data)) {
|
|
1257
|
+
return JSON.stringify(data);
|
|
1258
|
+
}
|
|
1259
|
+
return data;
|
|
1260
|
+
}
|
|
1261
|
+
if (typeof data === 'number' || typeof data === 'boolean') {
|
|
1262
|
+
return String(data);
|
|
1263
|
+
}
|
|
1264
|
+
if (Array.isArray(data)) {
|
|
1265
|
+
if (data.length === 0)
|
|
1266
|
+
return '[]';
|
|
1267
|
+
return data.map((item) => `${prefix}- ${stringifyYaml(item, indent + 1).trimStart()}`).join('\n');
|
|
1268
|
+
}
|
|
1269
|
+
if (typeof data === 'object') {
|
|
1270
|
+
const entries = Object.entries(data);
|
|
1271
|
+
if (entries.length === 0)
|
|
1272
|
+
return '{}';
|
|
1273
|
+
return entries
|
|
1274
|
+
.map(([key, value]) => {
|
|
1275
|
+
if (value && typeof value === 'object') {
|
|
1276
|
+
return `${prefix}${key}:\n${stringifyYaml(value, indent + 1)}`;
|
|
1277
|
+
}
|
|
1278
|
+
return `${prefix}${key}: ${stringifyYaml(value, indent)}`;
|
|
1279
|
+
})
|
|
1280
|
+
.join('\n');
|
|
1281
|
+
}
|
|
1282
|
+
return String(data);
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Execute yq command on YAML input
|
|
1286
|
+
*
|
|
1287
|
+
* @param query - The yq query expression
|
|
1288
|
+
* @param input - YAML input string
|
|
1289
|
+
* @param options - Execution options
|
|
1290
|
+
* @returns Query result as formatted string
|
|
1291
|
+
* @throws {Error} On invalid YAML or query errors
|
|
1292
|
+
*
|
|
1293
|
+
* @example
|
|
1294
|
+
* ```typescript
|
|
1295
|
+
* executeYq('.name', 'name: bashx\nversion: 1.0.0')
|
|
1296
|
+
* // => 'bashx\n'
|
|
1297
|
+
*
|
|
1298
|
+
* executeYq('.', 'name: bashx', { output: 'json' })
|
|
1299
|
+
* // => '{"name":"bashx"}\n'
|
|
1300
|
+
* ```
|
|
1301
|
+
*/
|
|
1302
|
+
export function executeYq(query, input, options = {}) {
|
|
1303
|
+
// Parse YAML
|
|
1304
|
+
let data;
|
|
1305
|
+
try {
|
|
1306
|
+
data = parseYaml(input);
|
|
1307
|
+
}
|
|
1308
|
+
catch (e) {
|
|
1309
|
+
throw new Error(`YAML parse error: ${e instanceof Error ? e.message : String(e)}`);
|
|
1310
|
+
}
|
|
1311
|
+
// Handle multi-document queries
|
|
1312
|
+
const docs = Array.isArray(data) && input.includes('---') ? data : [data];
|
|
1313
|
+
// Process query
|
|
1314
|
+
let result;
|
|
1315
|
+
// Handle eval-all
|
|
1316
|
+
if (query.startsWith('eval-all ')) {
|
|
1317
|
+
query = query.slice(9).trim();
|
|
1318
|
+
}
|
|
1319
|
+
// Handle document_index selection
|
|
1320
|
+
const docIndexMatch = query.match(/select\(document_index\s*==\s*(\d+)\)/);
|
|
1321
|
+
if (docIndexMatch) {
|
|
1322
|
+
result = docs[parseInt(docIndexMatch[1], 10)];
|
|
1323
|
+
}
|
|
1324
|
+
else if (query === '.') {
|
|
1325
|
+
result = docs.length === 1 ? docs[0] : docs;
|
|
1326
|
+
}
|
|
1327
|
+
else if (query.startsWith('.') && query.includes(' = ')) {
|
|
1328
|
+
// Assignment
|
|
1329
|
+
const [path, valueExpr] = query.split(' = ');
|
|
1330
|
+
const pathParts = path.slice(1).split('.');
|
|
1331
|
+
const value = parseYamlValue(valueExpr.replace(/^\\?"/, '').replace(/\\?"$/, ''), {});
|
|
1332
|
+
result = setPath(docs[0], pathParts, value);
|
|
1333
|
+
}
|
|
1334
|
+
else if (query.startsWith('del(')) {
|
|
1335
|
+
// Deletion
|
|
1336
|
+
const pathMatch = query.match(/del\(\.(\w+)\)/);
|
|
1337
|
+
if (pathMatch) {
|
|
1338
|
+
const obj = { ...docs[0] };
|
|
1339
|
+
delete obj[pathMatch[1]];
|
|
1340
|
+
result = obj;
|
|
1341
|
+
}
|
|
1342
|
+
else {
|
|
1343
|
+
result = docs[0];
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
else if (query.startsWith('explode(')) {
|
|
1347
|
+
// Explode anchors (already expanded during parse)
|
|
1348
|
+
result = docs[0];
|
|
1349
|
+
}
|
|
1350
|
+
else if (query.includes(' += ')) {
|
|
1351
|
+
// Append to array
|
|
1352
|
+
const [path, valueExpr] = query.split(' += ');
|
|
1353
|
+
const pathParts = path.slice(1).split('.');
|
|
1354
|
+
const arr = getPath(docs[0], path.slice(1));
|
|
1355
|
+
const newItems = JSON.parse(valueExpr.replace(/\\"/g, '"'));
|
|
1356
|
+
result = setPath(docs[0], pathParts, [...arr, ...newItems]);
|
|
1357
|
+
}
|
|
1358
|
+
else {
|
|
1359
|
+
// Use jq-style evaluation
|
|
1360
|
+
result = defaultJqEngine.evaluate(query, docs[0], { vars: {}, argjson: {} });
|
|
1361
|
+
}
|
|
1362
|
+
// Format output
|
|
1363
|
+
if (options.output === 'json') {
|
|
1364
|
+
return options.compact ? JSON.stringify(result) + '\n' : JSON.stringify(result, null, 2) + '\n';
|
|
1365
|
+
}
|
|
1366
|
+
if (options.output === 'props') {
|
|
1367
|
+
return formatAsProps(result);
|
|
1368
|
+
}
|
|
1369
|
+
if (options.output === 'csv') {
|
|
1370
|
+
return formatAsCsv(result);
|
|
1371
|
+
}
|
|
1372
|
+
// Default YAML output
|
|
1373
|
+
if (typeof result === 'string' || typeof result === 'number' || typeof result === 'boolean') {
|
|
1374
|
+
return String(result) + '\n';
|
|
1375
|
+
}
|
|
1376
|
+
return stringifyYaml(result) + '\n';
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Get value at a dot-separated path (for yq)
|
|
1380
|
+
*/
|
|
1381
|
+
function getPath(data, path) {
|
|
1382
|
+
const parts = path.split('.');
|
|
1383
|
+
let current = data;
|
|
1384
|
+
for (const part of parts) {
|
|
1385
|
+
if (current === null || current === undefined) {
|
|
1386
|
+
return null;
|
|
1387
|
+
}
|
|
1388
|
+
if (typeof current !== 'object') {
|
|
1389
|
+
return null;
|
|
1390
|
+
}
|
|
1391
|
+
current = current[part];
|
|
1392
|
+
}
|
|
1393
|
+
return current === undefined ? null : current;
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Set value at path in object (for yq mutations)
|
|
1397
|
+
*/
|
|
1398
|
+
function setPath(obj, path, value) {
|
|
1399
|
+
if (path.length === 0)
|
|
1400
|
+
return value;
|
|
1401
|
+
const result = { ...obj };
|
|
1402
|
+
const [head, ...rest] = path;
|
|
1403
|
+
if (rest.length === 0) {
|
|
1404
|
+
result[head] = value;
|
|
1405
|
+
}
|
|
1406
|
+
else {
|
|
1407
|
+
result[head] = setPath(result[head], rest, value);
|
|
1408
|
+
}
|
|
1409
|
+
return result;
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Format data as properties file format
|
|
1413
|
+
*/
|
|
1414
|
+
function formatAsProps(data, prefix = '') {
|
|
1415
|
+
const lines = [];
|
|
1416
|
+
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
1417
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1418
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
1419
|
+
if (value && typeof value === 'object') {
|
|
1420
|
+
lines.push(formatAsProps(value, path));
|
|
1421
|
+
}
|
|
1422
|
+
else {
|
|
1423
|
+
lines.push(`${path} = ${value}`);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
return lines.join('\n') + '\n';
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Format data as CSV
|
|
1431
|
+
*/
|
|
1432
|
+
function formatAsCsv(data) {
|
|
1433
|
+
if (Array.isArray(data)) {
|
|
1434
|
+
return data.map((item) => (Array.isArray(item) ? item.join(',') : String(item))).join('\n') + '\n';
|
|
1435
|
+
}
|
|
1436
|
+
if (data && typeof data === 'object') {
|
|
1437
|
+
const obj = data;
|
|
1438
|
+
const keys = Object.keys(obj);
|
|
1439
|
+
const values = Object.values(obj);
|
|
1440
|
+
return keys.join(',') + '\n' + values.join(',') + '\n';
|
|
1441
|
+
}
|
|
1442
|
+
return String(data) + '\n';
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Custom error for base64 encoding/decoding errors
|
|
1446
|
+
*/
|
|
1447
|
+
export class Base64Error extends Error {
|
|
1448
|
+
constructor(message) {
|
|
1449
|
+
super(message);
|
|
1450
|
+
this.name = 'Base64Error';
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Execute base64 encoding or decoding
|
|
1455
|
+
*
|
|
1456
|
+
* @param input - Input string to encode/decode
|
|
1457
|
+
* @param options - Encoding/decoding options
|
|
1458
|
+
* @returns Encoded or decoded string
|
|
1459
|
+
* @throws {Base64Error} On invalid base64 input during decoding
|
|
1460
|
+
*
|
|
1461
|
+
* @example
|
|
1462
|
+
* ```typescript
|
|
1463
|
+
* // Encode
|
|
1464
|
+
* executeBase64('Hello, World!')
|
|
1465
|
+
* // => 'SGVsbG8sIFdvcmxkIQ==\n'
|
|
1466
|
+
*
|
|
1467
|
+
* // Decode
|
|
1468
|
+
* executeBase64('SGVsbG8sIFdvcmxkIQ==', { decode: true })
|
|
1469
|
+
* // => 'Hello, World!'
|
|
1470
|
+
* ```
|
|
1471
|
+
*/
|
|
1472
|
+
export function executeBase64(input, options = {}) {
|
|
1473
|
+
if (options.decode) {
|
|
1474
|
+
return decodeBase64(input, options);
|
|
1475
|
+
}
|
|
1476
|
+
return encodeBase64(input, options);
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Encode string to base64
|
|
1480
|
+
*/
|
|
1481
|
+
function encodeBase64(input, options) {
|
|
1482
|
+
let encoded;
|
|
1483
|
+
if (options.urlSafe) {
|
|
1484
|
+
// URL-safe base64
|
|
1485
|
+
encoded = btoa(input).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
1486
|
+
}
|
|
1487
|
+
else {
|
|
1488
|
+
encoded = btoa(input);
|
|
1489
|
+
}
|
|
1490
|
+
// Apply line wrapping
|
|
1491
|
+
const wrap = options.wrap ?? 76;
|
|
1492
|
+
if (wrap > 0 && encoded.length > wrap) {
|
|
1493
|
+
const lines = [];
|
|
1494
|
+
for (let i = 0; i < encoded.length; i += wrap) {
|
|
1495
|
+
lines.push(encoded.slice(i, i + wrap));
|
|
1496
|
+
}
|
|
1497
|
+
return lines.join('\n') + '\n';
|
|
1498
|
+
}
|
|
1499
|
+
return encoded + '\n';
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Decode base64 string
|
|
1503
|
+
*/
|
|
1504
|
+
function decodeBase64(input, options) {
|
|
1505
|
+
let cleaned = input.replace(/\s/g, '');
|
|
1506
|
+
if (options.ignoreGarbage) {
|
|
1507
|
+
if (options.urlSafe) {
|
|
1508
|
+
cleaned = cleaned.replace(/[^A-Za-z0-9\-_=]/g, '');
|
|
1509
|
+
}
|
|
1510
|
+
else {
|
|
1511
|
+
cleaned = cleaned.replace(/[^A-Za-z0-9+/=]/g, '');
|
|
1512
|
+
}
|
|
1513
|
+
// For standard base64, truncate at padding
|
|
1514
|
+
if (!options.urlSafe) {
|
|
1515
|
+
const paddingMatch = cleaned.match(/^[A-Za-z0-9+/]*(={0,2})/);
|
|
1516
|
+
if (paddingMatch) {
|
|
1517
|
+
cleaned = paddingMatch[0];
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
if (options.urlSafe) {
|
|
1522
|
+
cleaned = cleaned.replace(/-/g, '+').replace(/_/g, '/');
|
|
1523
|
+
while (cleaned.length % 4 !== 0) {
|
|
1524
|
+
cleaned += '=';
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
// Validate base64
|
|
1528
|
+
if (!options.ignoreGarbage && !options.urlSafe && !/^[A-Za-z0-9+/]*={0,2}$/.test(cleaned)) {
|
|
1529
|
+
throw new Base64Error('invalid base64 input');
|
|
1530
|
+
}
|
|
1531
|
+
try {
|
|
1532
|
+
return atob(cleaned);
|
|
1533
|
+
}
|
|
1534
|
+
catch {
|
|
1535
|
+
if (options.urlSafe) {
|
|
1536
|
+
return '';
|
|
1537
|
+
}
|
|
1538
|
+
throw new Base64Error('invalid base64 input');
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Custom error for envsubst errors (e.g., required variable missing)
|
|
1543
|
+
*/
|
|
1544
|
+
export class EnvsubstError extends Error {
|
|
1545
|
+
constructor(message) {
|
|
1546
|
+
super(message);
|
|
1547
|
+
this.name = 'EnvsubstError';
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Execute environment variable substitution
|
|
1552
|
+
*
|
|
1553
|
+
* Supports:
|
|
1554
|
+
* - $VAR and ${VAR} syntax
|
|
1555
|
+
* - ${VAR:-default} - use default if unset/empty
|
|
1556
|
+
* - ${VAR:+alternate} - use alternate if set and non-empty
|
|
1557
|
+
* - ${VAR:?error} - error if unset/empty
|
|
1558
|
+
* - ${VAR:=default} - use default if unset/empty (assignment)
|
|
1559
|
+
* - $$ escape sequence for literal $
|
|
1560
|
+
*
|
|
1561
|
+
* @param template - Template string with variable references
|
|
1562
|
+
* @param options - Substitution options including env vars
|
|
1563
|
+
* @returns Substituted string
|
|
1564
|
+
* @throws {EnvsubstError} On ${VAR:?error} with missing variable
|
|
1565
|
+
*
|
|
1566
|
+
* @example
|
|
1567
|
+
* ```typescript
|
|
1568
|
+
* executeEnvsubst('Hello, $NAME!', { env: { NAME: 'World' } })
|
|
1569
|
+
* // => 'Hello, World!'
|
|
1570
|
+
*
|
|
1571
|
+
* executeEnvsubst('${VAR:-default}', { env: {} })
|
|
1572
|
+
* // => 'default'
|
|
1573
|
+
* ```
|
|
1574
|
+
*/
|
|
1575
|
+
export function executeEnvsubst(template, options) {
|
|
1576
|
+
const { env, variables, listVariables } = options;
|
|
1577
|
+
if (listVariables) {
|
|
1578
|
+
const vars = extractVariables(template);
|
|
1579
|
+
return vars.join('\n') + '\n';
|
|
1580
|
+
}
|
|
1581
|
+
return substituteVariables(template, env, variables);
|
|
1582
|
+
}
|
|
1583
|
+
/**
|
|
1584
|
+
* Extract variable names from template
|
|
1585
|
+
*/
|
|
1586
|
+
function extractVariables(template) {
|
|
1587
|
+
const vars = new Set();
|
|
1588
|
+
const bracedPattern = /\$\{([A-Z_][A-Z0-9_]*)(:[^}]+)?\}/gi;
|
|
1589
|
+
const simplePattern = /\$([A-Z_][A-Z0-9_]*)/gi;
|
|
1590
|
+
let match;
|
|
1591
|
+
while ((match = bracedPattern.exec(template)) !== null) {
|
|
1592
|
+
vars.add(match[1]);
|
|
1593
|
+
}
|
|
1594
|
+
while ((match = simplePattern.exec(template)) !== null) {
|
|
1595
|
+
vars.add(match[1]);
|
|
1596
|
+
}
|
|
1597
|
+
return Array.from(vars);
|
|
1598
|
+
}
|
|
1599
|
+
/**
|
|
1600
|
+
* Substitute variables in template
|
|
1601
|
+
*/
|
|
1602
|
+
function substituteVariables(template, env, onlyVars) {
|
|
1603
|
+
let result = template;
|
|
1604
|
+
// Handle escaped dollar signs: $$ -> $
|
|
1605
|
+
result = result.replace(/\$\$/g, '\x00ESCAPED_DOLLAR\x00');
|
|
1606
|
+
// Handle ${VAR:modifier} patterns
|
|
1607
|
+
result = result.replace(/\$\{([A-Z_][A-Z0-9_]*)(:[^}]+)?\}/gi, (match, varName, modifier) => {
|
|
1608
|
+
if (onlyVars && !onlyVars.includes(varName)) {
|
|
1609
|
+
return match;
|
|
1610
|
+
}
|
|
1611
|
+
const value = env[varName];
|
|
1612
|
+
const isEmpty = value === undefined || value === '';
|
|
1613
|
+
if (modifier) {
|
|
1614
|
+
const modType = modifier.slice(1, 2);
|
|
1615
|
+
const modValue = modifier.slice(2);
|
|
1616
|
+
switch (modType) {
|
|
1617
|
+
case '-':
|
|
1618
|
+
return isEmpty ? modValue : value;
|
|
1619
|
+
case '+':
|
|
1620
|
+
return isEmpty ? '' : modValue;
|
|
1621
|
+
case '?':
|
|
1622
|
+
if (isEmpty) {
|
|
1623
|
+
throw new EnvsubstError(`${varName}: ${modValue}`);
|
|
1624
|
+
}
|
|
1625
|
+
return value;
|
|
1626
|
+
case '=':
|
|
1627
|
+
return isEmpty ? modValue : value;
|
|
1628
|
+
default:
|
|
1629
|
+
return value ?? '';
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
return value ?? '';
|
|
1633
|
+
});
|
|
1634
|
+
// Handle simple $VAR patterns
|
|
1635
|
+
result = result.replace(/\$([A-Z_][A-Z0-9_]*)/gi, (match, varName) => {
|
|
1636
|
+
if (onlyVars && !onlyVars.includes(varName)) {
|
|
1637
|
+
return match;
|
|
1638
|
+
}
|
|
1639
|
+
return env[varName] ?? '';
|
|
1640
|
+
});
|
|
1641
|
+
// Restore escaped dollar signs
|
|
1642
|
+
result = result.replace(/\x00ESCAPED_DOLLAR\x00/g, '$');
|
|
1643
|
+
return result;
|
|
1644
|
+
}
|
|
1645
|
+
// ============================================================================
|
|
1646
|
+
// COMMAND PARSING HELPERS
|
|
1647
|
+
// ============================================================================
|
|
1648
|
+
/**
|
|
1649
|
+
* Parse jq command line arguments
|
|
1650
|
+
*
|
|
1651
|
+
* @param args - Array of command line arguments
|
|
1652
|
+
* @returns Parsed query, file path, and options
|
|
1653
|
+
*
|
|
1654
|
+
* @example
|
|
1655
|
+
* ```typescript
|
|
1656
|
+
* parseJqArgs(['-r', '.name', 'file.json'])
|
|
1657
|
+
* // => { query: '.name', file: 'file.json', options: { raw: true } }
|
|
1658
|
+
* ```
|
|
1659
|
+
*/
|
|
1660
|
+
export function parseJqArgs(args) {
|
|
1661
|
+
const options = {
|
|
1662
|
+
args: {},
|
|
1663
|
+
argjson: {},
|
|
1664
|
+
};
|
|
1665
|
+
let query = '';
|
|
1666
|
+
let file;
|
|
1667
|
+
let i = 0;
|
|
1668
|
+
while (i < args.length) {
|
|
1669
|
+
const arg = args[i];
|
|
1670
|
+
if (arg === '-r' || arg === '--raw-output') {
|
|
1671
|
+
options.raw = true;
|
|
1672
|
+
}
|
|
1673
|
+
else if (arg === '-c' || arg === '--compact-output') {
|
|
1674
|
+
options.compact = true;
|
|
1675
|
+
}
|
|
1676
|
+
else if (arg === '-s' || arg === '--slurp') {
|
|
1677
|
+
options.slurp = true;
|
|
1678
|
+
}
|
|
1679
|
+
else if (arg === '-t' || arg === '--tab') {
|
|
1680
|
+
options.tab = true;
|
|
1681
|
+
}
|
|
1682
|
+
else if (arg === '--arg' && i + 2 < args.length) {
|
|
1683
|
+
const name = args[++i];
|
|
1684
|
+
const value = args[++i];
|
|
1685
|
+
options.args[name] = value;
|
|
1686
|
+
}
|
|
1687
|
+
else if (arg === '--argjson' && i + 2 < args.length) {
|
|
1688
|
+
const name = args[++i];
|
|
1689
|
+
const value = args[++i];
|
|
1690
|
+
options.argjson[name] = JSON.parse(value);
|
|
1691
|
+
}
|
|
1692
|
+
else if (!arg.startsWith('-') && !query) {
|
|
1693
|
+
query = arg;
|
|
1694
|
+
}
|
|
1695
|
+
else if (!arg.startsWith('-')) {
|
|
1696
|
+
file = arg;
|
|
1697
|
+
}
|
|
1698
|
+
i++;
|
|
1699
|
+
}
|
|
1700
|
+
return { query, file, options };
|
|
1701
|
+
}
|
|
1702
|
+
/**
|
|
1703
|
+
* Parse yq command line arguments
|
|
1704
|
+
*
|
|
1705
|
+
* @param args - Array of command line arguments
|
|
1706
|
+
* @returns Parsed query, file path, and options
|
|
1707
|
+
*/
|
|
1708
|
+
export function parseYqArgs(args) {
|
|
1709
|
+
const options = {};
|
|
1710
|
+
let query = '';
|
|
1711
|
+
let file;
|
|
1712
|
+
let i = 0;
|
|
1713
|
+
while (i < args.length) {
|
|
1714
|
+
const arg = args[i];
|
|
1715
|
+
if (arg === '-o' && i + 1 < args.length) {
|
|
1716
|
+
const format = args[++i];
|
|
1717
|
+
if (format === 'json' || format === 'yaml' || format === 'props' || format === 'csv') {
|
|
1718
|
+
options.output = format;
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
else if (arg === '-c' || arg === '--compact-output') {
|
|
1722
|
+
options.compact = true;
|
|
1723
|
+
}
|
|
1724
|
+
else if (arg === '-i' || arg === '--inplace') {
|
|
1725
|
+
options.inPlace = true;
|
|
1726
|
+
}
|
|
1727
|
+
else if (arg === 'eval-all') {
|
|
1728
|
+
query = 'eval-all ' + (args[i + 1] || '.');
|
|
1729
|
+
i++;
|
|
1730
|
+
}
|
|
1731
|
+
else if (!arg.startsWith('-') && !query) {
|
|
1732
|
+
query = arg;
|
|
1733
|
+
}
|
|
1734
|
+
else if (!arg.startsWith('-')) {
|
|
1735
|
+
file = arg;
|
|
1736
|
+
}
|
|
1737
|
+
i++;
|
|
1738
|
+
}
|
|
1739
|
+
return { query: query || '.', file, options };
|
|
1740
|
+
}
|
|
1741
|
+
/**
|
|
1742
|
+
* Parse base64 command line arguments
|
|
1743
|
+
*
|
|
1744
|
+
* @param args - Array of command line arguments
|
|
1745
|
+
* @returns Parsed file path and options
|
|
1746
|
+
*/
|
|
1747
|
+
export function parseBase64Args(args) {
|
|
1748
|
+
const options = {};
|
|
1749
|
+
let file;
|
|
1750
|
+
let i = 0;
|
|
1751
|
+
while (i < args.length) {
|
|
1752
|
+
const arg = args[i];
|
|
1753
|
+
if (arg === '-d' || arg === '--decode' || arg === '-D') {
|
|
1754
|
+
options.decode = true;
|
|
1755
|
+
}
|
|
1756
|
+
else if (arg === '-w' && i + 1 < args.length) {
|
|
1757
|
+
options.wrap = parseInt(args[++i], 10);
|
|
1758
|
+
}
|
|
1759
|
+
else if (arg.startsWith('-w')) {
|
|
1760
|
+
options.wrap = parseInt(arg.slice(2), 10);
|
|
1761
|
+
}
|
|
1762
|
+
else if (arg === '-i' || arg === '--ignore-garbage') {
|
|
1763
|
+
options.ignoreGarbage = true;
|
|
1764
|
+
}
|
|
1765
|
+
else if (arg === '--url') {
|
|
1766
|
+
options.urlSafe = true;
|
|
1767
|
+
}
|
|
1768
|
+
else if (!arg.startsWith('-')) {
|
|
1769
|
+
file = arg;
|
|
1770
|
+
}
|
|
1771
|
+
i++;
|
|
1772
|
+
}
|
|
1773
|
+
return { file, options };
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Parse envsubst command line arguments
|
|
1777
|
+
*
|
|
1778
|
+
* @param args - Array of command line arguments
|
|
1779
|
+
* @param env - Environment variables
|
|
1780
|
+
* @returns Parsed options and input redirect path
|
|
1781
|
+
*/
|
|
1782
|
+
export function parseEnvsubstArgs(args, env) {
|
|
1783
|
+
const options = { env };
|
|
1784
|
+
let inputRedirect;
|
|
1785
|
+
let i = 0;
|
|
1786
|
+
while (i < args.length) {
|
|
1787
|
+
const arg = args[i];
|
|
1788
|
+
if (arg === '--variables' || arg === '-v') {
|
|
1789
|
+
options.listVariables = true;
|
|
1790
|
+
}
|
|
1791
|
+
else if (arg === '<' && i + 1 < args.length) {
|
|
1792
|
+
inputRedirect = args[++i];
|
|
1793
|
+
}
|
|
1794
|
+
else if (arg.startsWith('$')) {
|
|
1795
|
+
options.variables = arg
|
|
1796
|
+
.split(/\s+/)
|
|
1797
|
+
.filter((v) => v.startsWith('$'))
|
|
1798
|
+
.map((v) => v.slice(1));
|
|
1799
|
+
}
|
|
1800
|
+
else if (!arg.startsWith('-')) {
|
|
1801
|
+
if (arg.includes('$')) {
|
|
1802
|
+
options.variables = arg
|
|
1803
|
+
.split(/\s+/)
|
|
1804
|
+
.filter((v) => v.startsWith('$'))
|
|
1805
|
+
.map((v) => v.slice(1));
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
i++;
|
|
1809
|
+
}
|
|
1810
|
+
return { options, inputRedirect };
|
|
1811
|
+
}
|
|
1812
|
+
//# sourceMappingURL=data-processing.js.map
|