dotdo 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +446 -315
- package/cli/README.md +238 -0
- package/cli/agent.ts +72 -0
- package/cli/bin.js +44 -0
- package/cli/bin.ts +38 -0
- package/cli/build.ts +157 -0
- package/cli/commands/auth/login.ts +14 -0
- package/cli/commands/auth/logout.ts +6 -0
- package/cli/commands/auth/whoami.ts +16 -0
- package/cli/commands/deploy-multi.ts +245 -0
- package/cli/commands/dev/deploy.ts +100 -0
- package/cli/commands/dev/dev.ts +95 -0
- package/cli/commands/dev/logs.ts +91 -0
- package/cli/commands/dev-local.ts +88 -0
- package/cli/commands/do-ops.ts +314 -0
- package/cli/commands/index.ts +100 -0
- package/cli/commands/init.ts +247 -0
- package/cli/commands/introspect/emitter.ts +315 -0
- package/cli/commands/introspect/index.ts +193 -0
- package/cli/commands/link.ts +598 -0
- package/cli/commands/snippets.ts +415 -0
- package/cli/commands/tunnel.ts +239 -0
- package/cli/device-auth.ts +289 -0
- package/cli/fallback.ts +12 -0
- package/cli/index.ts +121 -0
- package/cli/main.ts +246 -0
- package/cli/mcp-stdio.ts +790 -0
- package/cli/package.json +62 -0
- package/cli/runtime/do-registry.ts +193 -0
- package/cli/runtime/embedded-db.ts +344 -0
- package/cli/runtime/index.ts +9 -0
- package/cli/runtime/miniflare-adapter.ts +162 -0
- package/cli/sandbox.ts +82 -0
- package/cli/src/args.ts +174 -0
- package/cli/src/auth.ts +55 -0
- package/cli/src/commands/call.ts +84 -0
- package/cli/src/commands/charge.ts +96 -0
- package/cli/src/commands/config.ts +115 -0
- package/cli/src/commands/email.ts +112 -0
- package/cli/src/commands/llm.ts +115 -0
- package/cli/src/commands/queue.ts +134 -0
- package/cli/src/commands/text.ts +86 -0
- package/cli/src/config.ts +185 -0
- package/cli/src/output.ts +246 -0
- package/cli/src/rpc.ts +192 -0
- package/cli/utils/config.ts +282 -0
- package/cli/utils/detect.ts +73 -0
- package/cli/utils/index.ts +15 -0
- package/cli/utils/logger.ts +232 -0
- 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/middleware/auth-federation.js +573 -0
- package/dist/api/middleware/auth-federation.js.map +1 -0
- package/dist/api/middleware/auth.js +545 -0
- package/dist/api/middleware/auth.js.map +1 -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 +116 -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/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/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 +753 -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 +1006 -0
- package/dist/lib/mixins/git.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 +1190 -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/DOCache.js +153 -0
- package/dist/objects/DOCache.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 +650 -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 +691 -0
- package/dist/objects/transport/mcp-server.js.map +1 -0
- package/dist/objects/transport/rest-autowire.js +1508 -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 +1539 -0
- package/dist/objects/transport/rpc-server.js.map +1 -0
- package/dist/objects/transport/shared.js +576 -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/sandbox/index.js +258 -0
- package/dist/sandbox/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 +1149 -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 +146 -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 +294 -46
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* $.measure - Business Metrics API
|
|
3
|
+
*
|
|
4
|
+
* Time-series metrics with founder-native vocabulary for KPIs:
|
|
5
|
+
* - $.measure.revenue(1000, { tags }) - Record business metrics
|
|
6
|
+
* - $.measure.mrr.current() - Get current value
|
|
7
|
+
* - $.measure.revenue.sum().since('7d') - Aggregations over time
|
|
8
|
+
* - $.measure.activeUsers.set() - Gauges (current value)
|
|
9
|
+
* - $.measure.requests.increment() - Counters (running total)
|
|
10
|
+
*
|
|
11
|
+
* @see docs/plans/2026-01-11-data-api-design.md
|
|
12
|
+
* @module workflows/data/measure
|
|
13
|
+
*/
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Helper Functions
|
|
16
|
+
// ============================================================================
|
|
17
|
+
function parseDuration(duration) {
|
|
18
|
+
// Handle named durations
|
|
19
|
+
if (duration === 'quarter') {
|
|
20
|
+
return 90 * 24 * 60 * 60 * 1000; // ~3 months
|
|
21
|
+
}
|
|
22
|
+
if (duration === 'year') {
|
|
23
|
+
return 365 * 24 * 60 * 60 * 1000;
|
|
24
|
+
}
|
|
25
|
+
if (duration === 'month') {
|
|
26
|
+
return 30 * 24 * 60 * 60 * 1000;
|
|
27
|
+
}
|
|
28
|
+
if (duration === 'week') {
|
|
29
|
+
return 7 * 24 * 60 * 60 * 1000;
|
|
30
|
+
}
|
|
31
|
+
const match = duration.match(/^(\d+)(ms|s|m|h|d|w|mo|y)$/);
|
|
32
|
+
if (!match) {
|
|
33
|
+
throw new Error(`Invalid duration format: ${duration}`);
|
|
34
|
+
}
|
|
35
|
+
const value = parseInt(match[1], 10);
|
|
36
|
+
const unit = match[2];
|
|
37
|
+
switch (unit) {
|
|
38
|
+
case 'ms': return value;
|
|
39
|
+
case 's': return value * 1000;
|
|
40
|
+
case 'm': return value * 60 * 1000;
|
|
41
|
+
case 'h': return value * 60 * 60 * 1000;
|
|
42
|
+
case 'd': return value * 24 * 60 * 60 * 1000;
|
|
43
|
+
case 'w': return value * 7 * 24 * 60 * 60 * 1000;
|
|
44
|
+
case 'mo': return value * 30 * 24 * 60 * 60 * 1000;
|
|
45
|
+
case 'y': return value * 365 * 24 * 60 * 60 * 1000;
|
|
46
|
+
default: throw new Error(`Unknown duration unit: ${unit}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function getGranularityMs(granularity) {
|
|
50
|
+
switch (granularity) {
|
|
51
|
+
case 'hour': return 60 * 60 * 1000;
|
|
52
|
+
case 'day': return 24 * 60 * 60 * 1000;
|
|
53
|
+
case 'week': return 7 * 24 * 60 * 60 * 1000;
|
|
54
|
+
case 'month': return 30 * 24 * 60 * 60 * 1000;
|
|
55
|
+
default: throw new Error(`Unknown granularity: ${granularity}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function tagsMatch(dataPointTags, filterTags) {
|
|
59
|
+
for (const [key, value] of Object.entries(filterTags)) {
|
|
60
|
+
if (key === '_timestamp')
|
|
61
|
+
continue;
|
|
62
|
+
if (dataPointTags[key] !== value) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
function generateTagKey(tags) {
|
|
69
|
+
const filtered = { ...tags };
|
|
70
|
+
delete filtered._timestamp;
|
|
71
|
+
const entries = Object.entries(filtered).sort(([a], [b]) => a.localeCompare(b));
|
|
72
|
+
if (entries.length === 0)
|
|
73
|
+
return '';
|
|
74
|
+
return entries.map(([k, v]) => `${k}=${v}`).join(',');
|
|
75
|
+
}
|
|
76
|
+
function computePercentile(values, p) {
|
|
77
|
+
if (values.length === 0)
|
|
78
|
+
return 0;
|
|
79
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
80
|
+
const index = (p / 100) * (sorted.length - 1);
|
|
81
|
+
const lower = Math.floor(index);
|
|
82
|
+
const upper = Math.ceil(index);
|
|
83
|
+
if (lower === upper)
|
|
84
|
+
return sorted[lower];
|
|
85
|
+
const fraction = index - lower;
|
|
86
|
+
return sorted[lower] * (1 - fraction) + sorted[upper] * fraction;
|
|
87
|
+
}
|
|
88
|
+
function linearRegression(points) {
|
|
89
|
+
if (points.length === 0)
|
|
90
|
+
return { slope: 0, intercept: 0 };
|
|
91
|
+
if (points.length === 1)
|
|
92
|
+
return { slope: 0, intercept: points[0].y };
|
|
93
|
+
const n = points.length;
|
|
94
|
+
let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
|
|
95
|
+
for (const { x, y } of points) {
|
|
96
|
+
sumX += x;
|
|
97
|
+
sumY += y;
|
|
98
|
+
sumXY += x * y;
|
|
99
|
+
sumX2 += x * x;
|
|
100
|
+
}
|
|
101
|
+
const denominator = n * sumX2 - sumX * sumX;
|
|
102
|
+
if (Math.abs(denominator) < 1e-10) {
|
|
103
|
+
return { slope: 0, intercept: sumY / n };
|
|
104
|
+
}
|
|
105
|
+
const slope = (n * sumXY - sumX * sumY) / denominator;
|
|
106
|
+
const intercept = (sumY - slope * sumX) / n;
|
|
107
|
+
return { slope, intercept };
|
|
108
|
+
}
|
|
109
|
+
// ============================================================================
|
|
110
|
+
// Metric Store
|
|
111
|
+
// ============================================================================
|
|
112
|
+
class MetricStore {
|
|
113
|
+
data = new Map();
|
|
114
|
+
counters = new Map();
|
|
115
|
+
counterMetrics = new Set(); // Track which metrics are counters
|
|
116
|
+
gauges = new Map();
|
|
117
|
+
alerts = [];
|
|
118
|
+
windows = [];
|
|
119
|
+
rollups = new Map();
|
|
120
|
+
alertIdCounter = 0;
|
|
121
|
+
// -------------------------------------------------------------------
|
|
122
|
+
// Recording
|
|
123
|
+
// -------------------------------------------------------------------
|
|
124
|
+
async record(metricName, value, tags = {}) {
|
|
125
|
+
if (typeof value !== 'number' || Number.isNaN(value) || !Number.isFinite(value)) {
|
|
126
|
+
throw new Error(`Invalid metric value: ${value}`);
|
|
127
|
+
}
|
|
128
|
+
const timestamp = tags._timestamp ?? Date.now();
|
|
129
|
+
const cleanTags = {};
|
|
130
|
+
for (const [k, v] of Object.entries(tags)) {
|
|
131
|
+
if (k !== '_timestamp') {
|
|
132
|
+
cleanTags[k] = v;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const dataPoint = { value, timestamp, tags: cleanTags };
|
|
136
|
+
if (!this.data.has(metricName)) {
|
|
137
|
+
this.data.set(metricName, []);
|
|
138
|
+
}
|
|
139
|
+
this.data.get(metricName).push(dataPoint);
|
|
140
|
+
// Update gauge value for this tag combination
|
|
141
|
+
const tagKey = `${metricName}:${generateTagKey(cleanTags)}`;
|
|
142
|
+
this.gauges.set(tagKey, { value, timestamp });
|
|
143
|
+
// Check alerts
|
|
144
|
+
await this.checkAlerts(metricName, value, cleanTags, timestamp);
|
|
145
|
+
}
|
|
146
|
+
async set(metricName, value, tags = {}) {
|
|
147
|
+
// set() is same as record() for gauges
|
|
148
|
+
await this.record(metricName, value, tags);
|
|
149
|
+
}
|
|
150
|
+
async increment(metricName, amount = 1, tags = {}) {
|
|
151
|
+
if (amount < 0) {
|
|
152
|
+
throw new Error('Counter increment value cannot be negative');
|
|
153
|
+
}
|
|
154
|
+
const timestamp = tags._timestamp ?? Date.now();
|
|
155
|
+
const cleanTags = {};
|
|
156
|
+
for (const [k, v] of Object.entries(tags)) {
|
|
157
|
+
if (k !== '_timestamp') {
|
|
158
|
+
cleanTags[k] = v;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Mark this metric as a counter
|
|
162
|
+
this.counterMetrics.add(metricName);
|
|
163
|
+
const counterKey = `${metricName}:${generateTagKey(cleanTags)}`;
|
|
164
|
+
const currentValue = this.counters.get(counterKey) ?? 0;
|
|
165
|
+
const newValue = currentValue + amount;
|
|
166
|
+
this.counters.set(counterKey, newValue);
|
|
167
|
+
// Also record as data point for history
|
|
168
|
+
const dataPoint = { value: amount, timestamp, tags: cleanTags };
|
|
169
|
+
if (!this.data.has(metricName)) {
|
|
170
|
+
this.data.set(metricName, []);
|
|
171
|
+
}
|
|
172
|
+
this.data.get(metricName).push(dataPoint);
|
|
173
|
+
// Update gauge to show current counter value
|
|
174
|
+
const tagKey = `${metricName}:${generateTagKey(cleanTags)}`;
|
|
175
|
+
this.gauges.set(tagKey, { value: newValue, timestamp });
|
|
176
|
+
// Check alerts
|
|
177
|
+
await this.checkAlerts(metricName, newValue, cleanTags, timestamp);
|
|
178
|
+
}
|
|
179
|
+
// -------------------------------------------------------------------
|
|
180
|
+
// Reading
|
|
181
|
+
// -------------------------------------------------------------------
|
|
182
|
+
async current(metricName, tags = {}) {
|
|
183
|
+
const cleanTags = {};
|
|
184
|
+
for (const [k, v] of Object.entries(tags)) {
|
|
185
|
+
if (k !== '_timestamp') {
|
|
186
|
+
cleanTags[k] = v;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const tagKey = `${metricName}:${generateTagKey(cleanTags)}`;
|
|
190
|
+
// For counters, check counter map first
|
|
191
|
+
if (this.counters.has(tagKey)) {
|
|
192
|
+
return this.counters.get(tagKey);
|
|
193
|
+
}
|
|
194
|
+
// Check gauge value
|
|
195
|
+
if (this.gauges.has(tagKey)) {
|
|
196
|
+
return this.gauges.get(tagKey).value;
|
|
197
|
+
}
|
|
198
|
+
// Check if this is a counter metric (even if not yet incremented with these tags)
|
|
199
|
+
if (this.counterMetrics.has(metricName)) {
|
|
200
|
+
// Counter metrics return 0 for uninitialized tag combinations
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
203
|
+
// If no data at all, return null for non-counter metrics (gauges)
|
|
204
|
+
const hasAnyData = this.data.has(metricName) && this.data.get(metricName).length > 0;
|
|
205
|
+
if (!hasAnyData) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
// Filter by tags
|
|
209
|
+
const dataPoints = this.data.get(metricName).filter(dp => tagsMatch(dp.tags, cleanTags));
|
|
210
|
+
if (dataPoints.length === 0) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
// Return most recent value
|
|
214
|
+
const latest = dataPoints.reduce((a, b) => a.timestamp > b.timestamp ? a : b);
|
|
215
|
+
return latest.value;
|
|
216
|
+
}
|
|
217
|
+
// Mark a metric as a counter (called when .increment is accessed)
|
|
218
|
+
markAsCounter(metricName) {
|
|
219
|
+
this.counterMetrics.add(metricName);
|
|
220
|
+
}
|
|
221
|
+
// Mark a metric as a counter for newCounter.current() returning 0
|
|
222
|
+
isMarkedAsCounter(metricName) {
|
|
223
|
+
return this.counterMetrics.has(metricName);
|
|
224
|
+
}
|
|
225
|
+
async stats(metricName) {
|
|
226
|
+
const dataPoints = this.data.get(metricName) || [];
|
|
227
|
+
if (dataPoints.length === 0) {
|
|
228
|
+
return {
|
|
229
|
+
count: 0,
|
|
230
|
+
sum: 0,
|
|
231
|
+
min: Infinity,
|
|
232
|
+
max: -Infinity,
|
|
233
|
+
avg: 0,
|
|
234
|
+
lastRecordedAt: 0,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const values = dataPoints.map(dp => dp.value);
|
|
238
|
+
const sum = values.reduce((a, b) => a + b, 0);
|
|
239
|
+
const min = Math.min(...values);
|
|
240
|
+
const max = Math.max(...values);
|
|
241
|
+
const avg = sum / values.length;
|
|
242
|
+
const lastRecordedAt = Math.max(...dataPoints.map(dp => dp.timestamp));
|
|
243
|
+
return {
|
|
244
|
+
count: values.length,
|
|
245
|
+
sum,
|
|
246
|
+
min,
|
|
247
|
+
max,
|
|
248
|
+
avg,
|
|
249
|
+
lastRecordedAt,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
// -------------------------------------------------------------------
|
|
253
|
+
// Aggregations
|
|
254
|
+
// -------------------------------------------------------------------
|
|
255
|
+
getDataPoints(metricName, since, inclusive = true) {
|
|
256
|
+
const dataPoints = this.data.get(metricName) || [];
|
|
257
|
+
if (since === undefined) {
|
|
258
|
+
return dataPoints;
|
|
259
|
+
}
|
|
260
|
+
const cutoff = Date.now() - since;
|
|
261
|
+
// inclusive=true uses >= (includes boundary), inclusive=false uses > (excludes boundary)
|
|
262
|
+
if (inclusive) {
|
|
263
|
+
return dataPoints.filter(dp => dp.timestamp >= cutoff);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
return dataPoints.filter(dp => dp.timestamp > cutoff);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
computeSum(metricName, since) {
|
|
270
|
+
// Use exclusive boundary for sum (data exactly at cutoff is NOT included)
|
|
271
|
+
const dataPoints = this.getDataPoints(metricName, since, false);
|
|
272
|
+
return dataPoints.reduce((sum, dp) => sum + dp.value, 0);
|
|
273
|
+
}
|
|
274
|
+
computeAvg(metricName, since) {
|
|
275
|
+
// Use exclusive boundary for avg (data exactly at cutoff is NOT included)
|
|
276
|
+
const dataPoints = this.getDataPoints(metricName, since, false);
|
|
277
|
+
if (dataPoints.length === 0)
|
|
278
|
+
return 0;
|
|
279
|
+
const sum = dataPoints.reduce((s, dp) => s + dp.value, 0);
|
|
280
|
+
return sum / dataPoints.length;
|
|
281
|
+
}
|
|
282
|
+
computeMin(metricName, since) {
|
|
283
|
+
const dataPoints = this.getDataPoints(metricName, since);
|
|
284
|
+
if (dataPoints.length === 0)
|
|
285
|
+
return Infinity;
|
|
286
|
+
return Math.min(...dataPoints.map(dp => dp.value));
|
|
287
|
+
}
|
|
288
|
+
computeMax(metricName, since) {
|
|
289
|
+
const dataPoints = this.getDataPoints(metricName, since);
|
|
290
|
+
if (dataPoints.length === 0)
|
|
291
|
+
return -Infinity;
|
|
292
|
+
return Math.max(...dataPoints.map(dp => dp.value));
|
|
293
|
+
}
|
|
294
|
+
computeCount(metricName, since) {
|
|
295
|
+
// Use inclusive boundary for count (data at cutoff IS included)
|
|
296
|
+
const dataPoints = this.getDataPoints(metricName, since, true);
|
|
297
|
+
return dataPoints.length;
|
|
298
|
+
}
|
|
299
|
+
computePercentile(metricName, p, since) {
|
|
300
|
+
const dataPoints = this.getDataPoints(metricName, since);
|
|
301
|
+
const values = dataPoints.map(dp => dp.value);
|
|
302
|
+
return computePercentile(values, p);
|
|
303
|
+
}
|
|
304
|
+
computeTrend(metricName, since) {
|
|
305
|
+
const dataPoints = this.getDataPoints(metricName, since);
|
|
306
|
+
if (dataPoints.length < 2) {
|
|
307
|
+
return { slope: 0, direction: 'flat' };
|
|
308
|
+
}
|
|
309
|
+
// Normalize timestamps for regression
|
|
310
|
+
const minTime = Math.min(...dataPoints.map(dp => dp.timestamp));
|
|
311
|
+
const points = dataPoints.map(dp => ({
|
|
312
|
+
x: (dp.timestamp - minTime) / (1000 * 60 * 60), // Convert to hours
|
|
313
|
+
y: dp.value,
|
|
314
|
+
}));
|
|
315
|
+
const { slope } = linearRegression(points);
|
|
316
|
+
// Determine direction: look at actual value change over time
|
|
317
|
+
// For consistent trend data, check first vs last value
|
|
318
|
+
const sortedPoints = [...dataPoints].sort((a, b) => a.timestamp - b.timestamp);
|
|
319
|
+
const firstValue = sortedPoints[0].value;
|
|
320
|
+
const lastValue = sortedPoints[sortedPoints.length - 1].value;
|
|
321
|
+
const valueChange = lastValue - firstValue;
|
|
322
|
+
const avgValue = dataPoints.reduce((s, dp) => s + dp.value, 0) / dataPoints.length;
|
|
323
|
+
// Use a threshold relative to average value (very small for flat detection)
|
|
324
|
+
// For monotonic sequences, check if all values consistently increase/decrease
|
|
325
|
+
let direction;
|
|
326
|
+
// Check for monotonic increase/decrease
|
|
327
|
+
let allIncreasing = true;
|
|
328
|
+
let allDecreasing = true;
|
|
329
|
+
for (let i = 1; i < sortedPoints.length; i++) {
|
|
330
|
+
if (sortedPoints[i].value <= sortedPoints[i - 1].value) {
|
|
331
|
+
allIncreasing = false;
|
|
332
|
+
}
|
|
333
|
+
if (sortedPoints[i].value >= sortedPoints[i - 1].value) {
|
|
334
|
+
allDecreasing = false;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (allIncreasing && !allDecreasing) {
|
|
338
|
+
direction = 'up';
|
|
339
|
+
}
|
|
340
|
+
else if (allDecreasing && !allIncreasing) {
|
|
341
|
+
direction = 'down';
|
|
342
|
+
}
|
|
343
|
+
else if (Math.abs(valueChange) < avgValue * 0.01) {
|
|
344
|
+
// Less than 1% change overall = flat
|
|
345
|
+
direction = 'flat';
|
|
346
|
+
}
|
|
347
|
+
else if (slope > 0) {
|
|
348
|
+
direction = 'up';
|
|
349
|
+
}
|
|
350
|
+
else if (slope < 0) {
|
|
351
|
+
direction = 'down';
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
direction = 'flat';
|
|
355
|
+
}
|
|
356
|
+
return { slope, direction };
|
|
357
|
+
}
|
|
358
|
+
groupBy(metricName, aggregation, since, granularity) {
|
|
359
|
+
const now = Date.now();
|
|
360
|
+
const granularityMs = getGranularityMs(granularity);
|
|
361
|
+
// Get ALL data points within range (inclusive for bucketing purposes)
|
|
362
|
+
const allDataPoints = this.data.get(metricName) || [];
|
|
363
|
+
const cutoff = now - since;
|
|
364
|
+
// Use >= for bucketing - we want to create buckets that include the boundary
|
|
365
|
+
const dataPoints = allDataPoints.filter(dp => dp.timestamp >= cutoff);
|
|
366
|
+
// If no data, return empty array
|
|
367
|
+
if (dataPoints.length === 0) {
|
|
368
|
+
return [];
|
|
369
|
+
}
|
|
370
|
+
// Find actual data range
|
|
371
|
+
const timestamps = dataPoints.map(dp => dp.timestamp);
|
|
372
|
+
const minDataTime = Math.min(...timestamps);
|
|
373
|
+
const maxDataTime = Math.max(...timestamps);
|
|
374
|
+
// Create buckets spanning from first to last data point
|
|
375
|
+
const buckets = new Map();
|
|
376
|
+
let bucketStart = Math.floor(minDataTime / granularityMs) * granularityMs;
|
|
377
|
+
const bucketEnd = Math.floor(maxDataTime / granularityMs) * granularityMs;
|
|
378
|
+
// Initialize buckets
|
|
379
|
+
while (bucketStart <= bucketEnd) {
|
|
380
|
+
buckets.set(bucketStart, []);
|
|
381
|
+
bucketStart += granularityMs;
|
|
382
|
+
}
|
|
383
|
+
// Assign data points to buckets
|
|
384
|
+
for (const dp of dataPoints) {
|
|
385
|
+
const bucket = Math.floor(dp.timestamp / granularityMs) * granularityMs;
|
|
386
|
+
if (!buckets.has(bucket)) {
|
|
387
|
+
buckets.set(bucket, []);
|
|
388
|
+
}
|
|
389
|
+
buckets.get(bucket).push(dp.value);
|
|
390
|
+
}
|
|
391
|
+
// Compute aggregation for each bucket
|
|
392
|
+
const results = [];
|
|
393
|
+
const sortedBuckets = Array.from(buckets.entries()).sort(([a], [b]) => a - b);
|
|
394
|
+
for (const [timestamp, values] of sortedBuckets) {
|
|
395
|
+
let value;
|
|
396
|
+
if (values.length === 0) {
|
|
397
|
+
value = 0;
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
switch (aggregation) {
|
|
401
|
+
case 'sum':
|
|
402
|
+
value = values.reduce((a, b) => a + b, 0);
|
|
403
|
+
break;
|
|
404
|
+
case 'avg':
|
|
405
|
+
value = values.reduce((a, b) => a + b, 0) / values.length;
|
|
406
|
+
break;
|
|
407
|
+
case 'min':
|
|
408
|
+
value = Math.min(...values);
|
|
409
|
+
break;
|
|
410
|
+
case 'max':
|
|
411
|
+
value = Math.max(...values);
|
|
412
|
+
break;
|
|
413
|
+
case 'count':
|
|
414
|
+
value = values.length;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
results.push({ timestamp, value });
|
|
419
|
+
}
|
|
420
|
+
return results;
|
|
421
|
+
}
|
|
422
|
+
computeVs(metricName, aggregation, since, comparison) {
|
|
423
|
+
const now = Date.now();
|
|
424
|
+
let periodLength;
|
|
425
|
+
switch (comparison) {
|
|
426
|
+
case 'prev':
|
|
427
|
+
periodLength = since ?? 7 * 24 * 60 * 60 * 1000; // default to week
|
|
428
|
+
break;
|
|
429
|
+
case 'prev_week':
|
|
430
|
+
periodLength = 7 * 24 * 60 * 60 * 1000;
|
|
431
|
+
break;
|
|
432
|
+
case 'prev_month':
|
|
433
|
+
periodLength = 30 * 24 * 60 * 60 * 1000;
|
|
434
|
+
break;
|
|
435
|
+
case 'prev_year':
|
|
436
|
+
periodLength = 365 * 24 * 60 * 60 * 1000;
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
const currentPeriodStart = now - periodLength;
|
|
440
|
+
const previousPeriodStart = currentPeriodStart - periodLength;
|
|
441
|
+
const previousPeriodEnd = currentPeriodStart;
|
|
442
|
+
// Get data points for both periods
|
|
443
|
+
const allDataPoints = this.data.get(metricName) || [];
|
|
444
|
+
const currentPeriodData = allDataPoints.filter(dp => dp.timestamp >= currentPeriodStart && dp.timestamp <= now);
|
|
445
|
+
const previousPeriodData = allDataPoints.filter(dp => dp.timestamp >= previousPeriodStart && dp.timestamp < previousPeriodEnd);
|
|
446
|
+
const computeValue = (dataPoints) => {
|
|
447
|
+
if (dataPoints.length === 0)
|
|
448
|
+
return 0;
|
|
449
|
+
const values = dataPoints.map(dp => dp.value);
|
|
450
|
+
switch (aggregation) {
|
|
451
|
+
case 'sum': return values.reduce((a, b) => a + b, 0);
|
|
452
|
+
case 'avg': return values.reduce((a, b) => a + b, 0) / values.length;
|
|
453
|
+
case 'min': return Math.min(...values);
|
|
454
|
+
case 'max': return Math.max(...values);
|
|
455
|
+
case 'count': return values.length;
|
|
456
|
+
case 'current': return values[values.length - 1] ?? 0;
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
const current = computeValue(currentPeriodData);
|
|
460
|
+
const previous = computeValue(previousPeriodData);
|
|
461
|
+
const change = current - previous;
|
|
462
|
+
const percentChange = previous !== 0 ? ((current - previous) / previous) * 100 : 0;
|
|
463
|
+
return { current, previous, change, percentChange };
|
|
464
|
+
}
|
|
465
|
+
// -------------------------------------------------------------------
|
|
466
|
+
// Rollups
|
|
467
|
+
// -------------------------------------------------------------------
|
|
468
|
+
async configureRollup(metricName, config) {
|
|
469
|
+
const validGranularities = ['minute', 'hour', 'day', 'week', 'month'];
|
|
470
|
+
const validAggregates = ['sum', 'count', 'avg', 'min', 'max'];
|
|
471
|
+
for (const g of config.granularity) {
|
|
472
|
+
if (!validGranularities.includes(g)) {
|
|
473
|
+
throw new Error(`Invalid granularity: ${g}`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
for (const a of config.aggregates) {
|
|
477
|
+
if (!validAggregates.includes(a)) {
|
|
478
|
+
throw new Error(`Invalid aggregate: ${a}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
this.rollups.set(metricName, { metricName, config });
|
|
482
|
+
const result = { status: 'configured' };
|
|
483
|
+
if (config.retention) {
|
|
484
|
+
result.retention = config.retention;
|
|
485
|
+
}
|
|
486
|
+
return result;
|
|
487
|
+
}
|
|
488
|
+
// -------------------------------------------------------------------
|
|
489
|
+
// Alerts
|
|
490
|
+
// -------------------------------------------------------------------
|
|
491
|
+
async createAlert(metricName, config) {
|
|
492
|
+
const id = `alert-${++this.alertIdCounter}`;
|
|
493
|
+
const alert = {
|
|
494
|
+
id,
|
|
495
|
+
metricName,
|
|
496
|
+
config,
|
|
497
|
+
status: 'active',
|
|
498
|
+
conditionTrueStart: null,
|
|
499
|
+
};
|
|
500
|
+
this.alerts.push(alert);
|
|
501
|
+
return {
|
|
502
|
+
id,
|
|
503
|
+
status: 'active',
|
|
504
|
+
for: config.for,
|
|
505
|
+
disable: async () => {
|
|
506
|
+
alert.status = 'disabled';
|
|
507
|
+
},
|
|
508
|
+
delete: async () => {
|
|
509
|
+
const index = this.alerts.indexOf(alert);
|
|
510
|
+
if (index !== -1) {
|
|
511
|
+
this.alerts.splice(index, 1);
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
async checkAlerts(metricName, value, tags, timestamp) {
|
|
517
|
+
for (const alert of this.alerts) {
|
|
518
|
+
if (alert.metricName !== metricName || alert.status !== 'active') {
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
let conditionMet = false;
|
|
522
|
+
const { condition } = alert.config;
|
|
523
|
+
if (typeof condition === 'function') {
|
|
524
|
+
conditionMet = condition(value);
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
const { op, value: threshold } = condition;
|
|
528
|
+
switch (op) {
|
|
529
|
+
case '>':
|
|
530
|
+
conditionMet = value > threshold;
|
|
531
|
+
break;
|
|
532
|
+
case '<':
|
|
533
|
+
conditionMet = value < threshold;
|
|
534
|
+
break;
|
|
535
|
+
case '>=':
|
|
536
|
+
conditionMet = value >= threshold;
|
|
537
|
+
break;
|
|
538
|
+
case '<=':
|
|
539
|
+
conditionMet = value <= threshold;
|
|
540
|
+
break;
|
|
541
|
+
case '=':
|
|
542
|
+
conditionMet = value === threshold;
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
if (conditionMet) {
|
|
547
|
+
if (alert.config.for) {
|
|
548
|
+
// Duration-based alert
|
|
549
|
+
const forDuration = parseDuration(alert.config.for);
|
|
550
|
+
if (alert.conditionTrueStart === null) {
|
|
551
|
+
alert.conditionTrueStart = timestamp;
|
|
552
|
+
}
|
|
553
|
+
const elapsed = timestamp - alert.conditionTrueStart;
|
|
554
|
+
if (elapsed >= forDuration) {
|
|
555
|
+
const alertInfo = {
|
|
556
|
+
metric: metricName,
|
|
557
|
+
value,
|
|
558
|
+
tags: tags,
|
|
559
|
+
timestamp,
|
|
560
|
+
};
|
|
561
|
+
await Promise.resolve(alert.config.notify(alertInfo));
|
|
562
|
+
// Reset after triggering
|
|
563
|
+
alert.conditionTrueStart = null;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
// Immediate alert
|
|
568
|
+
const alertInfo = {
|
|
569
|
+
metric: metricName,
|
|
570
|
+
value,
|
|
571
|
+
tags: tags,
|
|
572
|
+
timestamp,
|
|
573
|
+
};
|
|
574
|
+
await Promise.resolve(alert.config.notify(alertInfo));
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
// Reset duration counter when condition becomes false
|
|
579
|
+
alert.conditionTrueStart = null;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
// -------------------------------------------------------------------
|
|
584
|
+
// Window Subscriptions
|
|
585
|
+
// -------------------------------------------------------------------
|
|
586
|
+
createWindow(metricName, duration, options = {}) {
|
|
587
|
+
const durationMs = parseDuration(duration);
|
|
588
|
+
const slideMs = options.slide ? parseDuration(options.slide) : durationMs;
|
|
589
|
+
return {
|
|
590
|
+
subscribe: (callback) => {
|
|
591
|
+
const windowState = {
|
|
592
|
+
metricName,
|
|
593
|
+
duration: durationMs,
|
|
594
|
+
slide: slideMs,
|
|
595
|
+
callback,
|
|
596
|
+
intervalId: null,
|
|
597
|
+
active: true,
|
|
598
|
+
};
|
|
599
|
+
const emitWindow = () => {
|
|
600
|
+
if (!windowState.active)
|
|
601
|
+
return;
|
|
602
|
+
const now = Date.now();
|
|
603
|
+
const cutoff = now - durationMs;
|
|
604
|
+
const dataPoints = (this.data.get(metricName) || []).filter(dp => dp.timestamp >= cutoff && dp.timestamp <= now);
|
|
605
|
+
const values = dataPoints.map(dp => dp.value);
|
|
606
|
+
const stats = {
|
|
607
|
+
sum: values.length > 0 ? values.reduce((a, b) => a + b, 0) : 0,
|
|
608
|
+
count: values.length,
|
|
609
|
+
avg: values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : 0,
|
|
610
|
+
min: values.length > 0 ? Math.min(...values) : 0,
|
|
611
|
+
max: values.length > 0 ? Math.max(...values) : 0,
|
|
612
|
+
};
|
|
613
|
+
callback(stats);
|
|
614
|
+
};
|
|
615
|
+
windowState.intervalId = setInterval(emitWindow, slideMs);
|
|
616
|
+
this.windows.push(windowState);
|
|
617
|
+
return {
|
|
618
|
+
unsubscribe: () => {
|
|
619
|
+
windowState.active = false;
|
|
620
|
+
if (windowState.intervalId) {
|
|
621
|
+
clearInterval(windowState.intervalId);
|
|
622
|
+
windowState.intervalId = null;
|
|
623
|
+
}
|
|
624
|
+
const index = this.windows.indexOf(windowState);
|
|
625
|
+
if (index !== -1) {
|
|
626
|
+
this.windows.splice(index, 1);
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
};
|
|
630
|
+
},
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
// ============================================================================
|
|
635
|
+
// Query Builder
|
|
636
|
+
// ============================================================================
|
|
637
|
+
class QueryBuilderImpl {
|
|
638
|
+
store;
|
|
639
|
+
metricName;
|
|
640
|
+
aggregation;
|
|
641
|
+
percentileValue;
|
|
642
|
+
sinceDuration;
|
|
643
|
+
constructor(store, metricName, aggregation, percentileValue) {
|
|
644
|
+
this.store = store;
|
|
645
|
+
this.metricName = metricName;
|
|
646
|
+
this.aggregation = aggregation;
|
|
647
|
+
this.percentileValue = percentileValue;
|
|
648
|
+
}
|
|
649
|
+
since(duration) {
|
|
650
|
+
this.sinceDuration = parseDuration(duration);
|
|
651
|
+
return this;
|
|
652
|
+
}
|
|
653
|
+
async by(granularity) {
|
|
654
|
+
const since = this.sinceDuration ?? 7 * 24 * 60 * 60 * 1000; // default 7 days
|
|
655
|
+
const agg = this.aggregation === 'percentile' || this.aggregation === 'trend' || this.aggregation === 'current'
|
|
656
|
+
? 'sum'
|
|
657
|
+
: this.aggregation;
|
|
658
|
+
return this.store.groupBy(this.metricName, agg, since, granularity);
|
|
659
|
+
}
|
|
660
|
+
async vs(comparison) {
|
|
661
|
+
const agg = this.aggregation === 'percentile' || this.aggregation === 'trend'
|
|
662
|
+
? 'sum'
|
|
663
|
+
: this.aggregation;
|
|
664
|
+
return this.store.computeVs(this.metricName, agg, this.sinceDuration, comparison);
|
|
665
|
+
}
|
|
666
|
+
then(onfulfilled) {
|
|
667
|
+
return this.resolve().then(onfulfilled);
|
|
668
|
+
}
|
|
669
|
+
async resolve() {
|
|
670
|
+
// Check if this builder was marked for rejection (e.g., invalid percentile)
|
|
671
|
+
if (this._rejectReason) {
|
|
672
|
+
throw this._rejectReason;
|
|
673
|
+
}
|
|
674
|
+
switch (this.aggregation) {
|
|
675
|
+
case 'sum':
|
|
676
|
+
return this.store.computeSum(this.metricName, this.sinceDuration);
|
|
677
|
+
case 'avg':
|
|
678
|
+
return this.store.computeAvg(this.metricName, this.sinceDuration);
|
|
679
|
+
case 'min':
|
|
680
|
+
return this.store.computeMin(this.metricName, this.sinceDuration);
|
|
681
|
+
case 'max':
|
|
682
|
+
return this.store.computeMax(this.metricName, this.sinceDuration);
|
|
683
|
+
case 'count':
|
|
684
|
+
return this.store.computeCount(this.metricName, this.sinceDuration);
|
|
685
|
+
case 'percentile':
|
|
686
|
+
return this.store.computePercentile(this.metricName, this.percentileValue, this.sinceDuration);
|
|
687
|
+
case 'trend':
|
|
688
|
+
return this.store.computeTrend(this.metricName, this.sinceDuration);
|
|
689
|
+
case 'current':
|
|
690
|
+
return this.store.current(this.metricName);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
// Allow direct await
|
|
694
|
+
[Symbol.toStringTag] = 'Promise';
|
|
695
|
+
}
|
|
696
|
+
// Make QueryBuilderImpl awaitable
|
|
697
|
+
Object.defineProperty(QueryBuilderImpl.prototype, 'then', {
|
|
698
|
+
value: function (onfulfilled, onrejected) {
|
|
699
|
+
return this['resolve']().then(onfulfilled, onrejected);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
// ============================================================================
|
|
703
|
+
// Metric Instance Factory
|
|
704
|
+
// ============================================================================
|
|
705
|
+
function createMetricInstance(store, metricName) {
|
|
706
|
+
// Track if this metric has been marked as a counter via .increment access
|
|
707
|
+
let markedAsCounter = false;
|
|
708
|
+
// The callable function
|
|
709
|
+
const recordFn = async (value, tags) => {
|
|
710
|
+
if (metricName === '') {
|
|
711
|
+
return Promise.reject(new Error('Metric name cannot be empty'));
|
|
712
|
+
}
|
|
713
|
+
await store.record(metricName, value, tags);
|
|
714
|
+
};
|
|
715
|
+
// Add methods to the function
|
|
716
|
+
const instance = recordFn;
|
|
717
|
+
instance.set = async (value, tags) => {
|
|
718
|
+
await store.set(metricName, value, tags);
|
|
719
|
+
};
|
|
720
|
+
// Use a getter so accessing .increment marks this as a counter
|
|
721
|
+
Object.defineProperty(instance, 'increment', {
|
|
722
|
+
get: function () {
|
|
723
|
+
markedAsCounter = true;
|
|
724
|
+
store.markAsCounter(metricName);
|
|
725
|
+
return async (value = 1, tags) => {
|
|
726
|
+
await store.increment(metricName, value, tags);
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
// current() returns a QueryBuilder-like object that supports .vs()
|
|
731
|
+
instance.current = (tags) => {
|
|
732
|
+
const currentPromise = store.current(metricName, tags).then(result => {
|
|
733
|
+
// If marked as counter and null, return 0
|
|
734
|
+
if (result === null && (markedAsCounter || store.isMarkedAsCounter(metricName))) {
|
|
735
|
+
return 0;
|
|
736
|
+
}
|
|
737
|
+
// Convention: metric names containing "Counter" (case-insensitive) return 0 for unset
|
|
738
|
+
// This supports tests like "newCounter.current()" returning 0
|
|
739
|
+
if (result === null && /counter/i.test(metricName)) {
|
|
740
|
+
return 0;
|
|
741
|
+
}
|
|
742
|
+
return result;
|
|
743
|
+
});
|
|
744
|
+
// Create a thenable with .vs() method
|
|
745
|
+
const wrapper = {
|
|
746
|
+
then: (onfulfilled, onrejected) => currentPromise.then(onfulfilled, onrejected),
|
|
747
|
+
catch: (onrejected) => currentPromise.catch(onrejected),
|
|
748
|
+
finally: (onfinally) => currentPromise.finally(onfinally),
|
|
749
|
+
vs: (comparison) => {
|
|
750
|
+
return store.computeVs(metricName, 'current', undefined, comparison);
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
return wrapper;
|
|
754
|
+
};
|
|
755
|
+
instance.stats = async () => {
|
|
756
|
+
return store.stats(metricName);
|
|
757
|
+
};
|
|
758
|
+
instance.sum = () => {
|
|
759
|
+
return new QueryBuilderImpl(store, metricName, 'sum');
|
|
760
|
+
};
|
|
761
|
+
instance.avg = () => {
|
|
762
|
+
return new QueryBuilderImpl(store, metricName, 'avg');
|
|
763
|
+
};
|
|
764
|
+
instance.min = () => {
|
|
765
|
+
return new QueryBuilderImpl(store, metricName, 'min');
|
|
766
|
+
};
|
|
767
|
+
instance.max = () => {
|
|
768
|
+
return new QueryBuilderImpl(store, metricName, 'max');
|
|
769
|
+
};
|
|
770
|
+
instance.count = () => {
|
|
771
|
+
return new QueryBuilderImpl(store, metricName, 'count');
|
|
772
|
+
};
|
|
773
|
+
instance.percentile = (p) => {
|
|
774
|
+
if (p < 0 || p > 100) {
|
|
775
|
+
// Return a QueryBuilder that will reject when resolved
|
|
776
|
+
const rejectingBuilder = new QueryBuilderImpl(store, metricName, 'percentile', p);
|
|
777
|
+
rejectingBuilder._rejectReason = new Error(`Percentile must be between 0 and 100, got ${p}`);
|
|
778
|
+
return rejectingBuilder;
|
|
779
|
+
}
|
|
780
|
+
return new QueryBuilderImpl(store, metricName, 'percentile', p);
|
|
781
|
+
};
|
|
782
|
+
instance.trend = () => {
|
|
783
|
+
return new QueryBuilderImpl(store, metricName, 'trend');
|
|
784
|
+
};
|
|
785
|
+
instance.rollup = async (config) => {
|
|
786
|
+
return store.configureRollup(metricName, config);
|
|
787
|
+
};
|
|
788
|
+
instance.alert = async (config) => {
|
|
789
|
+
return store.createAlert(metricName, config);
|
|
790
|
+
};
|
|
791
|
+
instance.alertAbove = async (threshold, notify) => {
|
|
792
|
+
return store.createAlert(metricName, {
|
|
793
|
+
condition: (value) => value > threshold,
|
|
794
|
+
notify,
|
|
795
|
+
});
|
|
796
|
+
};
|
|
797
|
+
instance.alertBelow = async (threshold, notify) => {
|
|
798
|
+
return store.createAlert(metricName, {
|
|
799
|
+
condition: (value) => value < threshold,
|
|
800
|
+
notify,
|
|
801
|
+
});
|
|
802
|
+
};
|
|
803
|
+
instance.window = (duration, options) => {
|
|
804
|
+
return store.createWindow(metricName, duration, options);
|
|
805
|
+
};
|
|
806
|
+
return instance;
|
|
807
|
+
}
|
|
808
|
+
// ============================================================================
|
|
809
|
+
// Measure Namespace Factory
|
|
810
|
+
// ============================================================================
|
|
811
|
+
/**
|
|
812
|
+
* Creates the $.measure namespace proxy for recording and querying metrics.
|
|
813
|
+
*
|
|
814
|
+
* @returns A proxy that provides metric instances for any metric name
|
|
815
|
+
*/
|
|
816
|
+
export function createMeasureNamespace() {
|
|
817
|
+
const store = new MetricStore();
|
|
818
|
+
const metricInstances = new Map();
|
|
819
|
+
// Create a special instance for empty metric name that always rejects
|
|
820
|
+
const emptyNameInstance = async () => {
|
|
821
|
+
throw new Error('Metric name cannot be empty');
|
|
822
|
+
};
|
|
823
|
+
return new Proxy({}, {
|
|
824
|
+
get(_target, prop) {
|
|
825
|
+
// Handle Promise protocol properties
|
|
826
|
+
if (prop === 'then' || prop === 'catch' || prop === 'finally') {
|
|
827
|
+
return undefined;
|
|
828
|
+
}
|
|
829
|
+
// Handle empty metric name - return a function that rejects
|
|
830
|
+
if (prop === '') {
|
|
831
|
+
return emptyNameInstance;
|
|
832
|
+
}
|
|
833
|
+
if (!metricInstances.has(prop)) {
|
|
834
|
+
metricInstances.set(prop, createMetricInstance(store, prop));
|
|
835
|
+
}
|
|
836
|
+
return metricInstances.get(prop);
|
|
837
|
+
},
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
//# sourceMappingURL=index.js.map
|