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,1149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream Processing API ($.stream)
|
|
3
|
+
*
|
|
4
|
+
* Provides real-time stream processing capabilities:
|
|
5
|
+
* - Stream sources from domain events, tracked events, and measurements
|
|
6
|
+
* - Transformations: filter(), map(), enrich(), flatMap()
|
|
7
|
+
* - Keyed streams via keyBy()
|
|
8
|
+
* - Windowing: tumbling, sliding, session windows
|
|
9
|
+
* - Aggregations within windows
|
|
10
|
+
* - Stream joins with temporal constraints
|
|
11
|
+
* - Sinks to track, measure, and view
|
|
12
|
+
*
|
|
13
|
+
* @module workflows/data/stream
|
|
14
|
+
*/
|
|
15
|
+
import { toMillis } from '../../../db/primitives/utils/duration';
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Implementation
|
|
18
|
+
// ============================================================================
|
|
19
|
+
class StreamImpl {
|
|
20
|
+
type = 'stream';
|
|
21
|
+
source;
|
|
22
|
+
sink;
|
|
23
|
+
bufferCapacity;
|
|
24
|
+
bufferStrategy;
|
|
25
|
+
subscribers = [];
|
|
26
|
+
errorHandlers = [];
|
|
27
|
+
deadLetterHandlers = [];
|
|
28
|
+
forEachHandler;
|
|
29
|
+
running = false; // Streams don't run until explicitly started
|
|
30
|
+
streamName;
|
|
31
|
+
maxRetries = 0;
|
|
32
|
+
_bufferCapacity = Infinity;
|
|
33
|
+
_bufferStrategy = 'dropOldest';
|
|
34
|
+
_buffer = []; // Renamed to avoid conflict with buffer() method
|
|
35
|
+
throttleMs = 0;
|
|
36
|
+
lastEmitTime = 0;
|
|
37
|
+
throttleQueue = [];
|
|
38
|
+
throttleTimer = null;
|
|
39
|
+
ctx;
|
|
40
|
+
pipeline = [];
|
|
41
|
+
keyedSubscribers = [];
|
|
42
|
+
started = false; // Track if start() was called
|
|
43
|
+
isRoot = true; // Whether this is a root stream (receives from emit directly)
|
|
44
|
+
parentStream; // Parent stream for child streams
|
|
45
|
+
pendingOperations = []; // Track pending async operations
|
|
46
|
+
constructor(source, ctx, parent) {
|
|
47
|
+
this.source = Object.freeze({ ...source });
|
|
48
|
+
this.ctx = ctx;
|
|
49
|
+
if (parent) {
|
|
50
|
+
this.isRoot = false;
|
|
51
|
+
this.parentStream = parent;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
isRootStream() {
|
|
55
|
+
return this.isRoot;
|
|
56
|
+
}
|
|
57
|
+
filter(predicate) {
|
|
58
|
+
const newStream = new StreamImpl(this.source, this.ctx, this);
|
|
59
|
+
newStream.pipeline.push(async (item) => {
|
|
60
|
+
const result = await predicate(item);
|
|
61
|
+
if (!result)
|
|
62
|
+
return Symbol.for('skip');
|
|
63
|
+
return item;
|
|
64
|
+
});
|
|
65
|
+
// Subscribe to parent to receive items, tracking async operations
|
|
66
|
+
this.subscribe((item) => {
|
|
67
|
+
const op = newStream.processItem(item);
|
|
68
|
+
newStream.pendingOperations.push(op);
|
|
69
|
+
});
|
|
70
|
+
this.ctx.registerStream(newStream);
|
|
71
|
+
return newStream;
|
|
72
|
+
}
|
|
73
|
+
map(fn) {
|
|
74
|
+
const newStream = new StreamImpl(this.source, this.ctx, this);
|
|
75
|
+
newStream.pipeline.push(async (item) => {
|
|
76
|
+
return await fn(item);
|
|
77
|
+
});
|
|
78
|
+
// Subscribe to parent to receive items, tracking async operations
|
|
79
|
+
this.subscribe((item) => {
|
|
80
|
+
const op = newStream.processItem(item);
|
|
81
|
+
newStream.pendingOperations.push(op);
|
|
82
|
+
});
|
|
83
|
+
this.ctx.registerStream(newStream);
|
|
84
|
+
return newStream;
|
|
85
|
+
}
|
|
86
|
+
flatMap(fn) {
|
|
87
|
+
const newStream = new StreamImpl(this.source, this.ctx, this);
|
|
88
|
+
newStream.pipeline.push(async (item) => {
|
|
89
|
+
const result = await fn(item);
|
|
90
|
+
return { __flatMap: result };
|
|
91
|
+
});
|
|
92
|
+
// Subscribe to parent to receive items, tracking async operations
|
|
93
|
+
this.subscribe((item) => {
|
|
94
|
+
const op = newStream.processItem(item);
|
|
95
|
+
newStream.pendingOperations.push(op);
|
|
96
|
+
});
|
|
97
|
+
this.ctx.registerStream(newStream);
|
|
98
|
+
return newStream;
|
|
99
|
+
}
|
|
100
|
+
enrich(fn) {
|
|
101
|
+
const newStream = new StreamImpl(this.source, this.ctx, this);
|
|
102
|
+
newStream.pipeline.push(async (item) => {
|
|
103
|
+
return await fn(item, this.ctx);
|
|
104
|
+
});
|
|
105
|
+
// Subscribe to parent to receive items, tracking async operations
|
|
106
|
+
this.subscribe((item) => {
|
|
107
|
+
const op = newStream.processItem(item);
|
|
108
|
+
newStream.pendingOperations.push(op);
|
|
109
|
+
});
|
|
110
|
+
this.ctx.registerStream(newStream);
|
|
111
|
+
return newStream;
|
|
112
|
+
}
|
|
113
|
+
keyBy(keyOrFn) {
|
|
114
|
+
return new KeyedStreamImpl(this, keyOrFn, this.ctx);
|
|
115
|
+
}
|
|
116
|
+
// These methods exist to provide helpful error messages
|
|
117
|
+
join() {
|
|
118
|
+
throw new Error('join() requires a keyed stream. Call keyBy() first.');
|
|
119
|
+
}
|
|
120
|
+
leftJoin() {
|
|
121
|
+
throw new Error('leftJoin() requires a keyed stream. Call keyBy() first.');
|
|
122
|
+
}
|
|
123
|
+
coGroup() {
|
|
124
|
+
throw new Error('coGroup() requires a keyed stream. Call keyBy() first.');
|
|
125
|
+
}
|
|
126
|
+
get window() {
|
|
127
|
+
// For non-keyed streams, use global key
|
|
128
|
+
const globalKeyedStream = new KeyedStreamImpl(this, () => '__global__', this.ctx);
|
|
129
|
+
return {
|
|
130
|
+
tumbling: (size) => {
|
|
131
|
+
const ws = globalKeyedStream.window.tumbling(size);
|
|
132
|
+
ws.key = '__global__';
|
|
133
|
+
return ws;
|
|
134
|
+
},
|
|
135
|
+
sliding: (size, slide) => globalKeyedStream.window.sliding(size, slide),
|
|
136
|
+
session: (gap) => globalKeyedStream.window.session(gap),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
subscribe(fn) {
|
|
140
|
+
this.subscribers.push(fn);
|
|
141
|
+
// Auto-start when subscribed (implicit running) unless explicitly stopped
|
|
142
|
+
if (!this.started) {
|
|
143
|
+
this.running = true;
|
|
144
|
+
}
|
|
145
|
+
return () => {
|
|
146
|
+
const idx = this.subscribers.indexOf(fn);
|
|
147
|
+
if (idx !== -1)
|
|
148
|
+
this.subscribers.splice(idx, 1);
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
forEach(fn) {
|
|
152
|
+
this.forEachHandler = fn;
|
|
153
|
+
// Auto-start when forEach is called
|
|
154
|
+
if (!this.started) {
|
|
155
|
+
this.running = true;
|
|
156
|
+
}
|
|
157
|
+
return this;
|
|
158
|
+
}
|
|
159
|
+
async flush() {
|
|
160
|
+
// First, flush parent stream if we have one (to ensure upstream items flow through)
|
|
161
|
+
if (this.parentStream) {
|
|
162
|
+
await this.parentStream.flush();
|
|
163
|
+
}
|
|
164
|
+
// Wait for all pending async operations on THIS stream
|
|
165
|
+
// Keep flushing until no more pending operations (items may chain through)
|
|
166
|
+
while (this.pendingOperations.length > 0) {
|
|
167
|
+
const ops = this.pendingOperations;
|
|
168
|
+
this.pendingOperations = [];
|
|
169
|
+
await Promise.all(ops);
|
|
170
|
+
}
|
|
171
|
+
// Advance time to trigger window closes
|
|
172
|
+
this.ctx.advanceTime(Date.now());
|
|
173
|
+
// Process any throttled items
|
|
174
|
+
if (this.throttleTimer) {
|
|
175
|
+
clearTimeout(this.throttleTimer);
|
|
176
|
+
this.throttleTimer = null;
|
|
177
|
+
}
|
|
178
|
+
for (const item of this.throttleQueue) {
|
|
179
|
+
await this.deliverItem(item);
|
|
180
|
+
}
|
|
181
|
+
this.throttleQueue = [];
|
|
182
|
+
}
|
|
183
|
+
async start() {
|
|
184
|
+
this.started = true;
|
|
185
|
+
this.running = true;
|
|
186
|
+
}
|
|
187
|
+
async stop() {
|
|
188
|
+
this.running = false;
|
|
189
|
+
}
|
|
190
|
+
isRunning() {
|
|
191
|
+
return this.started && this.running;
|
|
192
|
+
}
|
|
193
|
+
name(n) {
|
|
194
|
+
this.streamName = n;
|
|
195
|
+
this.ctx.namedStreams.set(n, this);
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
getName() {
|
|
199
|
+
return this.streamName;
|
|
200
|
+
}
|
|
201
|
+
onError(handler) {
|
|
202
|
+
this.errorHandlers.push(handler);
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
deadLetter(handler) {
|
|
206
|
+
this.deadLetterHandlers.push(handler);
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
retry(maxRetries) {
|
|
210
|
+
this.maxRetries = maxRetries;
|
|
211
|
+
return this;
|
|
212
|
+
}
|
|
213
|
+
buffer(capacity, options) {
|
|
214
|
+
this._bufferCapacity = capacity;
|
|
215
|
+
this.bufferCapacity = capacity;
|
|
216
|
+
this._bufferStrategy = options?.strategy ?? 'dropOldest';
|
|
217
|
+
this.bufferStrategy = this._bufferStrategy;
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
throttle(interval) {
|
|
221
|
+
this.throttleMs = toMillis(interval);
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
get to() {
|
|
225
|
+
const self = this;
|
|
226
|
+
return {
|
|
227
|
+
track: new Proxy({}, {
|
|
228
|
+
get(_, event) {
|
|
229
|
+
self.sink = { type: 'track', event };
|
|
230
|
+
self.ctx.registerSinkStream(self, 'track', event);
|
|
231
|
+
return self;
|
|
232
|
+
},
|
|
233
|
+
}),
|
|
234
|
+
measure: new Proxy({}, {
|
|
235
|
+
get(_, metric) {
|
|
236
|
+
self.sink = { type: 'measure', metric };
|
|
237
|
+
self.ctx.registerSinkStream(self, 'measure', metric);
|
|
238
|
+
return self;
|
|
239
|
+
},
|
|
240
|
+
}),
|
|
241
|
+
view: new Proxy({}, {
|
|
242
|
+
get(_, view) {
|
|
243
|
+
self.sink = { type: 'view', view };
|
|
244
|
+
self.ctx.registerSinkStream(self, 'view', view);
|
|
245
|
+
return self;
|
|
246
|
+
},
|
|
247
|
+
}),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
async processItem(item) {
|
|
251
|
+
// If explicitly stopped (started = true but running = false), don't process
|
|
252
|
+
if (this.started && !this.running) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
// Otherwise, process if:
|
|
256
|
+
// - root stream (always processes unless explicitly stopped)
|
|
257
|
+
// - has subscribers
|
|
258
|
+
// - has forEachHandler
|
|
259
|
+
// - has pipeline (predicates/transforms to run for side effects like spying)
|
|
260
|
+
if (!this.isRoot && this.subscribers.length === 0 && !this.forEachHandler && this.pipeline.length === 0) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
let current = item;
|
|
264
|
+
let attempts = 0;
|
|
265
|
+
const maxAttempts = this.maxRetries + 1;
|
|
266
|
+
while (attempts < maxAttempts) {
|
|
267
|
+
try {
|
|
268
|
+
// Process through pipeline
|
|
269
|
+
for (const step of this.pipeline) {
|
|
270
|
+
current = await step(current);
|
|
271
|
+
if (current === Symbol.for('skip'))
|
|
272
|
+
return;
|
|
273
|
+
if (current && typeof current === 'object' && '__flatMap' in current) {
|
|
274
|
+
const items = current.__flatMap;
|
|
275
|
+
for (const flatItem of items) {
|
|
276
|
+
await this.deliverItem(flatItem);
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
await this.deliverItem(current);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
attempts++;
|
|
286
|
+
if (attempts >= maxAttempts) {
|
|
287
|
+
for (const handler of this.errorHandlers) {
|
|
288
|
+
handler(error);
|
|
289
|
+
}
|
|
290
|
+
for (const handler of this.deadLetterHandlers) {
|
|
291
|
+
handler(item, error);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
async deliverItem(item) {
|
|
298
|
+
// Handle throttling
|
|
299
|
+
if (this.throttleMs > 0) {
|
|
300
|
+
const now = Date.now();
|
|
301
|
+
if (now - this.lastEmitTime < this.throttleMs) {
|
|
302
|
+
this.throttleQueue.push(item);
|
|
303
|
+
if (!this.throttleTimer) {
|
|
304
|
+
this.throttleTimer = setTimeout(() => {
|
|
305
|
+
const next = this.throttleQueue.shift();
|
|
306
|
+
if (next)
|
|
307
|
+
this.deliverItem(next);
|
|
308
|
+
this.throttleTimer = null;
|
|
309
|
+
}, this.throttleMs - (now - this.lastEmitTime));
|
|
310
|
+
}
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
this.lastEmitTime = now;
|
|
314
|
+
}
|
|
315
|
+
// Handle buffering
|
|
316
|
+
if (this._bufferCapacity < Infinity) {
|
|
317
|
+
this._buffer.push(item);
|
|
318
|
+
if (this._buffer.length > this._bufferCapacity) {
|
|
319
|
+
if (this._bufferStrategy === 'dropOldest') {
|
|
320
|
+
this._buffer.shift();
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
this._buffer.pop();
|
|
324
|
+
return; // Drop newest means don't deliver this one
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
for (const subscriber of this.subscribers) {
|
|
329
|
+
subscriber(item);
|
|
330
|
+
}
|
|
331
|
+
if (this.forEachHandler) {
|
|
332
|
+
await this.forEachHandler(item);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
class KeyedStreamImpl {
|
|
337
|
+
type = 'keyed-stream';
|
|
338
|
+
keySelector;
|
|
339
|
+
parentStream;
|
|
340
|
+
ctx;
|
|
341
|
+
subscribers = [];
|
|
342
|
+
timestampSubscribers = [];
|
|
343
|
+
constructor(parent, keySelector, ctx) {
|
|
344
|
+
this.parentStream = parent;
|
|
345
|
+
this.keySelector = keySelector;
|
|
346
|
+
this.ctx = ctx;
|
|
347
|
+
// Subscribe to parent stream to get transformed items
|
|
348
|
+
this.parentStream.subscribe((item) => {
|
|
349
|
+
// Get timestamp from item if available, otherwise use current time
|
|
350
|
+
const timestamp = item?.timestamp ?? Date.now();
|
|
351
|
+
this.processItem(item, timestamp);
|
|
352
|
+
});
|
|
353
|
+
this.ctx.registerKeyedStream(this);
|
|
354
|
+
}
|
|
355
|
+
getKey(item) {
|
|
356
|
+
if (typeof this.keySelector === 'string') {
|
|
357
|
+
return item[this.keySelector];
|
|
358
|
+
}
|
|
359
|
+
return this.keySelector(item);
|
|
360
|
+
}
|
|
361
|
+
get window() {
|
|
362
|
+
return {
|
|
363
|
+
tumbling: (size) => {
|
|
364
|
+
const sizeMs = toMillis(size);
|
|
365
|
+
return new WindowedStreamImpl(this, { type: 'tumbling', size: sizeMs }, this.ctx);
|
|
366
|
+
},
|
|
367
|
+
sliding: (size, slide) => {
|
|
368
|
+
const sizeMs = toMillis(size);
|
|
369
|
+
const slideMs = toMillis(slide);
|
|
370
|
+
if (slideMs > sizeMs) {
|
|
371
|
+
throw new Error('Slide cannot be larger than size');
|
|
372
|
+
}
|
|
373
|
+
return new WindowedStreamImpl(this, { type: 'sliding', size: sizeMs, slide: slideMs }, this.ctx);
|
|
374
|
+
},
|
|
375
|
+
session: (gap) => {
|
|
376
|
+
const gapMs = toMillis(gap);
|
|
377
|
+
return new WindowedStreamImpl(this, { type: 'session', gap: gapMs }, this.ctx);
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
join(other) {
|
|
382
|
+
return new JoinBuilderImpl(this, other, 'inner', this.ctx);
|
|
383
|
+
}
|
|
384
|
+
leftJoin(other) {
|
|
385
|
+
return new JoinBuilderImpl(this, other, 'left', this.ctx);
|
|
386
|
+
}
|
|
387
|
+
coGroup(other) {
|
|
388
|
+
return new CoGroupBuilderImpl(this, other, this.ctx);
|
|
389
|
+
}
|
|
390
|
+
subscribe(fn) {
|
|
391
|
+
this.subscribers.push(fn);
|
|
392
|
+
return () => {
|
|
393
|
+
const idx = this.subscribers.indexOf(fn);
|
|
394
|
+
if (idx !== -1)
|
|
395
|
+
this.subscribers.splice(idx, 1);
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
subscribeWithTimestamp(fn) {
|
|
399
|
+
this.timestampSubscribers.push(fn);
|
|
400
|
+
return () => {
|
|
401
|
+
const idx = this.timestampSubscribers.indexOf(fn);
|
|
402
|
+
if (idx !== -1)
|
|
403
|
+
this.timestampSubscribers.splice(idx, 1);
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
async flush() {
|
|
407
|
+
await this.parentStream.flush();
|
|
408
|
+
}
|
|
409
|
+
processItem(item, timestamp) {
|
|
410
|
+
const key = this.getKey(item);
|
|
411
|
+
const ts = timestamp ?? item?.timestamp ?? Date.now();
|
|
412
|
+
for (const subscriber of this.subscribers) {
|
|
413
|
+
subscriber(item, key);
|
|
414
|
+
}
|
|
415
|
+
for (const subscriber of this.timestampSubscribers) {
|
|
416
|
+
subscriber(item, key, ts);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
class WindowedStreamImpl {
|
|
421
|
+
type = 'windowed-stream';
|
|
422
|
+
window;
|
|
423
|
+
key;
|
|
424
|
+
keyedStream;
|
|
425
|
+
ctx;
|
|
426
|
+
windowCloseHandlers = [];
|
|
427
|
+
windowState = new Map();
|
|
428
|
+
subscribers = [];
|
|
429
|
+
aggregateSpec;
|
|
430
|
+
reduceSpec;
|
|
431
|
+
constructor(keyedStream, windowSpec, ctx) {
|
|
432
|
+
this.keyedStream = keyedStream;
|
|
433
|
+
this.window = windowSpec;
|
|
434
|
+
this.ctx = ctx;
|
|
435
|
+
// Subscribe to keyed stream to receive items
|
|
436
|
+
this.keyedStream.subscribeWithTimestamp((item, key, timestamp) => {
|
|
437
|
+
this.processItem(item, timestamp);
|
|
438
|
+
});
|
|
439
|
+
this.ctx.registerWindowedStream(this);
|
|
440
|
+
}
|
|
441
|
+
aggregate(spec) {
|
|
442
|
+
this.aggregateSpec = spec;
|
|
443
|
+
return new AggregatedStreamImpl(this, this.ctx);
|
|
444
|
+
}
|
|
445
|
+
reduce(reducer, initial) {
|
|
446
|
+
this.reduceSpec = { reducer: reducer, initial };
|
|
447
|
+
return new AggregatedStreamImpl(this, this.ctx);
|
|
448
|
+
}
|
|
449
|
+
onWindowClose(handler) {
|
|
450
|
+
this.windowCloseHandlers.push(handler);
|
|
451
|
+
}
|
|
452
|
+
subscribe(fn) {
|
|
453
|
+
this.subscribers.push(fn);
|
|
454
|
+
return () => {
|
|
455
|
+
const idx = this.subscribers.indexOf(fn);
|
|
456
|
+
if (idx !== -1)
|
|
457
|
+
this.subscribers.splice(idx, 1);
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
async flush() {
|
|
461
|
+
// Trigger window close for all pending windows
|
|
462
|
+
for (const [windowKey, state] of this.windowState.entries()) {
|
|
463
|
+
const [key] = windowKey.split('|');
|
|
464
|
+
const windowInfo = this.computeWindowInfo(state.lastTimestamp);
|
|
465
|
+
for (const handler of this.windowCloseHandlers) {
|
|
466
|
+
handler(key, state.elements, windowInfo);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
this.windowState.clear();
|
|
470
|
+
}
|
|
471
|
+
computeWindowInfo(timestamp) {
|
|
472
|
+
if (this.window.type === 'tumbling') {
|
|
473
|
+
const size = this.window.size;
|
|
474
|
+
const start = Math.floor(timestamp / size) * size;
|
|
475
|
+
return { start, end: start + size };
|
|
476
|
+
}
|
|
477
|
+
else if (this.window.type === 'sliding') {
|
|
478
|
+
const size = this.window.size;
|
|
479
|
+
const slide = this.window.slide;
|
|
480
|
+
const start = Math.floor(timestamp / slide) * slide;
|
|
481
|
+
return { start, end: start + size };
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
// Session - approximate based on elements
|
|
485
|
+
return { start: timestamp, end: timestamp + (this.window.gap || 0) };
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
processItem(item, timestamp) {
|
|
489
|
+
const key = this.keyedStream.getKey(item);
|
|
490
|
+
const windowKeys = this.getWindowKeys(key, timestamp);
|
|
491
|
+
for (const windowKey of windowKeys) {
|
|
492
|
+
let state = this.windowState.get(windowKey);
|
|
493
|
+
if (!state) {
|
|
494
|
+
state = { elements: [], lastTimestamp: timestamp };
|
|
495
|
+
this.windowState.set(windowKey, state);
|
|
496
|
+
}
|
|
497
|
+
state.elements.push(item);
|
|
498
|
+
state.lastTimestamp = Math.max(state.lastTimestamp, timestamp);
|
|
499
|
+
}
|
|
500
|
+
for (const subscriber of this.subscribers) {
|
|
501
|
+
subscriber(item, key);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
getWindowKeys(key, timestamp) {
|
|
505
|
+
if (this.window.type === 'tumbling') {
|
|
506
|
+
const size = this.window.size;
|
|
507
|
+
const windowStart = Math.floor(timestamp / size) * size;
|
|
508
|
+
return [`${key}|${windowStart}`];
|
|
509
|
+
}
|
|
510
|
+
else if (this.window.type === 'session') {
|
|
511
|
+
// For session windows, check if we should start a new session
|
|
512
|
+
const gap = this.window.gap;
|
|
513
|
+
// Find the current session for this key (if any)
|
|
514
|
+
let currentSession = null;
|
|
515
|
+
let currentLastTimestamp = -Infinity;
|
|
516
|
+
for (const [windowKey, state] of this.windowState.entries()) {
|
|
517
|
+
if (windowKey.startsWith(`${key}|session_`)) {
|
|
518
|
+
if (state.lastTimestamp > currentLastTimestamp) {
|
|
519
|
+
currentSession = windowKey;
|
|
520
|
+
currentLastTimestamp = state.lastTimestamp;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
// If there's no current session, or the gap is too large, start a new session
|
|
525
|
+
if (currentSession === null || timestamp - currentLastTimestamp > gap) {
|
|
526
|
+
return [`${key}|session_${timestamp}`];
|
|
527
|
+
}
|
|
528
|
+
// Otherwise, add to the current session
|
|
529
|
+
return [currentSession];
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
// Sliding window: item belongs to all windows where windowStart <= timestamp < windowStart + size
|
|
533
|
+
const size = this.window.size;
|
|
534
|
+
const slide = this.window.slide;
|
|
535
|
+
const keys = [];
|
|
536
|
+
// Find the earliest window that could contain this timestamp
|
|
537
|
+
// Window starts at multiples of slide
|
|
538
|
+
const earliestWindowStart = Math.floor((timestamp - size + 1) / slide) * slide;
|
|
539
|
+
const latestWindowStart = Math.floor(timestamp / slide) * slide;
|
|
540
|
+
for (let windowStart = Math.max(0, earliestWindowStart); windowStart <= latestWindowStart; windowStart += slide) {
|
|
541
|
+
if (timestamp >= windowStart && timestamp < windowStart + size) {
|
|
542
|
+
keys.push(`${key}|${windowStart}`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return keys;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
triggerWindowClose(currentTime) {
|
|
549
|
+
const windowsToClose = [];
|
|
550
|
+
for (const [windowKey, state] of this.windowState.entries()) {
|
|
551
|
+
const [key, windowId] = windowKey.split('|');
|
|
552
|
+
let shouldClose = false;
|
|
553
|
+
let windowInfo;
|
|
554
|
+
if (this.window.type === 'tumbling') {
|
|
555
|
+
const size = this.window.size;
|
|
556
|
+
const windowStart = parseInt(windowId);
|
|
557
|
+
const windowEnd = windowStart + size;
|
|
558
|
+
windowInfo = { start: windowStart, end: windowEnd };
|
|
559
|
+
shouldClose = currentTime >= windowEnd;
|
|
560
|
+
}
|
|
561
|
+
else if (this.window.type === 'sliding') {
|
|
562
|
+
const size = this.window.size;
|
|
563
|
+
const slide = this.window.slide;
|
|
564
|
+
const windowStart = parseInt(windowId);
|
|
565
|
+
const windowEnd = windowStart + size;
|
|
566
|
+
windowInfo = { start: windowStart, end: windowEnd };
|
|
567
|
+
shouldClose = currentTime >= windowEnd;
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
// Session
|
|
571
|
+
const gap = this.window.gap;
|
|
572
|
+
windowInfo = { start: state.lastTimestamp - gap, end: state.lastTimestamp + gap };
|
|
573
|
+
shouldClose = currentTime >= state.lastTimestamp + gap;
|
|
574
|
+
}
|
|
575
|
+
if (shouldClose) {
|
|
576
|
+
windowsToClose.push({ key: key, windowKey, state });
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
for (const { key, windowKey, state } of windowsToClose) {
|
|
580
|
+
const windowInfo = this.computeWindowInfo(state.lastTimestamp);
|
|
581
|
+
for (const handler of this.windowCloseHandlers) {
|
|
582
|
+
handler(key, state.elements, windowInfo);
|
|
583
|
+
}
|
|
584
|
+
this.windowState.delete(windowKey);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
getAggregateSpec() {
|
|
588
|
+
return this.aggregateSpec;
|
|
589
|
+
}
|
|
590
|
+
getReduceSpec() {
|
|
591
|
+
return this.reduceSpec;
|
|
592
|
+
}
|
|
593
|
+
getWindowState() {
|
|
594
|
+
return this.windowState;
|
|
595
|
+
}
|
|
596
|
+
getKeyedStream() {
|
|
597
|
+
return this.keyedStream;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
class AggregatedStreamImpl {
|
|
601
|
+
type = 'stream';
|
|
602
|
+
sink;
|
|
603
|
+
windowedStream;
|
|
604
|
+
ctx;
|
|
605
|
+
subscribers = [];
|
|
606
|
+
mapFn;
|
|
607
|
+
constructor(windowedStream, ctx) {
|
|
608
|
+
this.windowedStream = windowedStream;
|
|
609
|
+
this.ctx = ctx;
|
|
610
|
+
this.ctx.registerAggregatedStream(this);
|
|
611
|
+
}
|
|
612
|
+
map(fn) {
|
|
613
|
+
const newStream = new AggregatedStreamImpl(this.windowedStream, this.ctx);
|
|
614
|
+
const prevMapFn = this.mapFn;
|
|
615
|
+
newStream.mapFn = (item) => {
|
|
616
|
+
const prev = prevMapFn ? prevMapFn(item) : item;
|
|
617
|
+
return fn(prev);
|
|
618
|
+
};
|
|
619
|
+
return newStream;
|
|
620
|
+
}
|
|
621
|
+
subscribe(fn) {
|
|
622
|
+
this.subscribers.push(fn);
|
|
623
|
+
return () => {
|
|
624
|
+
const idx = this.subscribers.indexOf(fn);
|
|
625
|
+
if (idx !== -1)
|
|
626
|
+
this.subscribers.splice(idx, 1);
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
async flush() {
|
|
630
|
+
// Advance time to trigger window closes
|
|
631
|
+
this.ctx.advanceTime(Date.now());
|
|
632
|
+
await this.windowedStream.flush();
|
|
633
|
+
}
|
|
634
|
+
get to() {
|
|
635
|
+
const self = this;
|
|
636
|
+
return {
|
|
637
|
+
track: new Proxy({}, {
|
|
638
|
+
get(_, event) {
|
|
639
|
+
self.sink = { type: 'track', event };
|
|
640
|
+
return self;
|
|
641
|
+
},
|
|
642
|
+
}),
|
|
643
|
+
measure: new Proxy({}, {
|
|
644
|
+
get(_, metric) {
|
|
645
|
+
self.sink = { type: 'measure', metric };
|
|
646
|
+
self.ctx.registerAggregatedSink(self, 'measure', metric);
|
|
647
|
+
return self;
|
|
648
|
+
},
|
|
649
|
+
}),
|
|
650
|
+
view: new Proxy({}, {
|
|
651
|
+
get(_, view) {
|
|
652
|
+
self.sink = { type: 'view', view };
|
|
653
|
+
self.ctx.registerAggregatedSink(self, 'view', view);
|
|
654
|
+
return self;
|
|
655
|
+
},
|
|
656
|
+
}),
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
emitResult(result, key, info) {
|
|
660
|
+
let finalResult = result;
|
|
661
|
+
if (this.mapFn) {
|
|
662
|
+
finalResult = this.mapFn(result);
|
|
663
|
+
}
|
|
664
|
+
for (const subscriber of this.subscribers) {
|
|
665
|
+
subscriber(finalResult, key, info);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
getWindowedStream() {
|
|
669
|
+
return this.windowedStream;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
class JoinBuilderImpl {
|
|
673
|
+
type = 'joined-stream';
|
|
674
|
+
window;
|
|
675
|
+
leftStream;
|
|
676
|
+
rightStream;
|
|
677
|
+
joinType;
|
|
678
|
+
ctx;
|
|
679
|
+
constructor(left, right, joinType, ctx) {
|
|
680
|
+
this.leftStream = left;
|
|
681
|
+
this.rightStream = right;
|
|
682
|
+
this.joinType = joinType;
|
|
683
|
+
this.ctx = ctx;
|
|
684
|
+
}
|
|
685
|
+
within(duration) {
|
|
686
|
+
this.window = toMillis(duration);
|
|
687
|
+
return this;
|
|
688
|
+
}
|
|
689
|
+
on(fn) {
|
|
690
|
+
const newStream = new StreamImpl({ type: 'domain' }, this.ctx);
|
|
691
|
+
this.ctx.registerJoin(this.leftStream, this.rightStream, this.window ?? Infinity, this.joinType, fn, newStream);
|
|
692
|
+
return newStream;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
class CoGroupBuilderImpl {
|
|
696
|
+
type = 'joined-stream';
|
|
697
|
+
window;
|
|
698
|
+
leftStream;
|
|
699
|
+
rightStream;
|
|
700
|
+
ctx;
|
|
701
|
+
constructor(left, right, ctx) {
|
|
702
|
+
this.leftStream = left;
|
|
703
|
+
this.rightStream = right;
|
|
704
|
+
this.ctx = ctx;
|
|
705
|
+
}
|
|
706
|
+
within(duration) {
|
|
707
|
+
this.window = toMillis(duration);
|
|
708
|
+
return this;
|
|
709
|
+
}
|
|
710
|
+
apply(fn) {
|
|
711
|
+
const newStream = new StreamImpl({ type: 'domain' }, this.ctx);
|
|
712
|
+
this.ctx.registerCoGroup(this.leftStream, this.rightStream, this.window ?? Infinity, fn, newStream);
|
|
713
|
+
return newStream;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
// ============================================================================
|
|
717
|
+
// Stream Context Implementation
|
|
718
|
+
// ============================================================================
|
|
719
|
+
class StreamContextImpl {
|
|
720
|
+
_storage = {
|
|
721
|
+
customers: new Map(),
|
|
722
|
+
};
|
|
723
|
+
trackHandlers = new Map();
|
|
724
|
+
measureHandlers = new Map();
|
|
725
|
+
entityActionHandlers = new Map();
|
|
726
|
+
streams = [];
|
|
727
|
+
keyedStreams = [];
|
|
728
|
+
windowedStreams = [];
|
|
729
|
+
aggregatedStreams = [];
|
|
730
|
+
namedStreams = new Map();
|
|
731
|
+
eventBuffer = new Map();
|
|
732
|
+
trackEventBuffer = new Map();
|
|
733
|
+
// Sink streams
|
|
734
|
+
sinkStreams = new Map();
|
|
735
|
+
aggregatedSinks = new Map();
|
|
736
|
+
// Joins
|
|
737
|
+
joins = [];
|
|
738
|
+
coGroups = [];
|
|
739
|
+
// Views storage
|
|
740
|
+
viewData = new Map();
|
|
741
|
+
_hooks = {
|
|
742
|
+
onTrack: (event, handler) => {
|
|
743
|
+
if (!this.trackHandlers.has(event))
|
|
744
|
+
this.trackHandlers.set(event, []);
|
|
745
|
+
this.trackHandlers.get(event).push(handler);
|
|
746
|
+
},
|
|
747
|
+
onMeasure: (metric, handler) => {
|
|
748
|
+
if (!this.measureHandlers.has(metric))
|
|
749
|
+
this.measureHandlers.set(metric, []);
|
|
750
|
+
this.measureHandlers.get(metric).push(handler);
|
|
751
|
+
},
|
|
752
|
+
onEntityAction: (entity, action, handler) => {
|
|
753
|
+
const key = `${entity}.${action}`;
|
|
754
|
+
if (!this.entityActionHandlers.has(key))
|
|
755
|
+
this.entityActionHandlers.set(key, []);
|
|
756
|
+
this.entityActionHandlers.get(key).push(handler);
|
|
757
|
+
},
|
|
758
|
+
};
|
|
759
|
+
stream = {
|
|
760
|
+
from: new Proxy({}, {
|
|
761
|
+
get: (_, entity) => {
|
|
762
|
+
if (entity === 'track') {
|
|
763
|
+
return new Proxy({}, {
|
|
764
|
+
get: (_, event) => {
|
|
765
|
+
const stream = new StreamImpl({ type: 'track', event }, this);
|
|
766
|
+
this.registerStream(stream);
|
|
767
|
+
return stream;
|
|
768
|
+
},
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
if (entity === 'measure') {
|
|
772
|
+
return new Proxy({}, {
|
|
773
|
+
get: (_, metric) => {
|
|
774
|
+
const stream = new StreamImpl({ type: 'measure', metric }, this);
|
|
775
|
+
this.registerStream(stream);
|
|
776
|
+
return stream;
|
|
777
|
+
},
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
return new Proxy({}, {
|
|
781
|
+
get: (_, event) => {
|
|
782
|
+
const stream = new StreamImpl({ type: 'domain', entity, event }, this);
|
|
783
|
+
this.registerStream(stream);
|
|
784
|
+
return stream;
|
|
785
|
+
},
|
|
786
|
+
});
|
|
787
|
+
},
|
|
788
|
+
}),
|
|
789
|
+
emit: async (entity, event, data) => {
|
|
790
|
+
const timestamp = data?.timestamp ?? Date.now();
|
|
791
|
+
if (entity === 'track') {
|
|
792
|
+
const eventKey = event;
|
|
793
|
+
if (!this.trackEventBuffer.has(eventKey)) {
|
|
794
|
+
this.trackEventBuffer.set(eventKey, []);
|
|
795
|
+
}
|
|
796
|
+
this.trackEventBuffer.get(eventKey).push({ data, timestamp });
|
|
797
|
+
// Process through streams
|
|
798
|
+
for (const stream of this.streams) {
|
|
799
|
+
if (stream.source.type === 'track' && stream.source.event === event) {
|
|
800
|
+
await stream.processItem(data);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
// Don't return - continue to process joins and coGroups below
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
// Domain events
|
|
807
|
+
const eventKey = `${entity}.${event}`;
|
|
808
|
+
if (!this.eventBuffer.has(eventKey)) {
|
|
809
|
+
this.eventBuffer.set(eventKey, []);
|
|
810
|
+
}
|
|
811
|
+
this.eventBuffer.get(eventKey).push({ data, timestamp });
|
|
812
|
+
// Process through ROOT streams only (child streams get data via parent subscription)
|
|
813
|
+
for (const stream of this.streams) {
|
|
814
|
+
// Check if this stream matches the emit
|
|
815
|
+
const isMatchingSource =
|
|
816
|
+
// Domain events: $.stream.from.Order.created
|
|
817
|
+
((stream.source.entity === entity || stream.source.entity === '*') &&
|
|
818
|
+
(stream.source.event === event || stream.source.event === '*'));
|
|
819
|
+
if (stream.isRootStream() && isMatchingSource) {
|
|
820
|
+
await stream.processItem(data);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
// NOTE: keyed streams and windowed streams receive data through parent subscriptions
|
|
825
|
+
// They don't need direct processing here anymore
|
|
826
|
+
// Process joins
|
|
827
|
+
for (const join of this.joins) {
|
|
828
|
+
const leftKey = join.left.getKey(data);
|
|
829
|
+
const rightKey = join.right.getKey?.(data);
|
|
830
|
+
// Check if this event matches left stream
|
|
831
|
+
const leftSource = join.left.parentStream?.source;
|
|
832
|
+
const leftSourceMatches = (leftSource?.entity === entity && leftSource?.event === event) ||
|
|
833
|
+
(leftSource?.type === 'track' && entity === 'track' && leftSource?.event === event);
|
|
834
|
+
if (leftSourceMatches) {
|
|
835
|
+
join.leftBuffer.set(leftKey, join.leftBuffer.get(leftKey) ?? []);
|
|
836
|
+
join.leftBuffer.get(leftKey).push({ data, timestamp });
|
|
837
|
+
// Try to match with right buffer
|
|
838
|
+
const rightMatches = join.rightBuffer.get(leftKey) ?? [];
|
|
839
|
+
for (const rightItem of rightMatches) {
|
|
840
|
+
if (Math.abs(timestamp - rightItem.timestamp) <= join.window) {
|
|
841
|
+
const result = join.mapper(data, rightItem.data);
|
|
842
|
+
await join.output.processItem(result);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
// Check if this event matches right stream
|
|
847
|
+
const rightSource = join.right.parentStream?.source;
|
|
848
|
+
const rightSourceMatches = (rightSource?.entity === entity && rightSource?.event === event) ||
|
|
849
|
+
(rightSource?.type === 'track' && entity === 'track' && rightSource?.event === event);
|
|
850
|
+
if (rightSourceMatches) {
|
|
851
|
+
if (rightKey) {
|
|
852
|
+
join.rightBuffer.set(rightKey, join.rightBuffer.get(rightKey) ?? []);
|
|
853
|
+
join.rightBuffer.get(rightKey).push({ data, timestamp });
|
|
854
|
+
// Try to match with left buffer
|
|
855
|
+
const leftMatches = join.leftBuffer.get(rightKey) ?? [];
|
|
856
|
+
for (const leftItem of leftMatches) {
|
|
857
|
+
if (Math.abs(timestamp - leftItem.timestamp) <= join.window) {
|
|
858
|
+
const result = join.mapper(leftItem.data, data);
|
|
859
|
+
await join.output.processItem(result);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
// Process coGroups
|
|
866
|
+
for (const coGroup of this.coGroups) {
|
|
867
|
+
const leftKey = coGroup.left.getKey(data);
|
|
868
|
+
const rightKey = coGroup.right.getKey?.(data);
|
|
869
|
+
const leftSource = coGroup.left.parentStream?.source;
|
|
870
|
+
const leftSourceMatches = (leftSource?.entity === entity && leftSource?.event === event) ||
|
|
871
|
+
(leftSource?.type === 'track' && entity === 'track' && leftSource?.event === event);
|
|
872
|
+
if (leftSourceMatches) {
|
|
873
|
+
coGroup.leftBuffer.set(leftKey, coGroup.leftBuffer.get(leftKey) ?? []);
|
|
874
|
+
coGroup.leftBuffer.get(leftKey).push({ data, timestamp });
|
|
875
|
+
}
|
|
876
|
+
const rightSource = coGroup.right.parentStream?.source;
|
|
877
|
+
const rightSourceMatches = (rightSource?.entity === entity && rightSource?.event === event) ||
|
|
878
|
+
(rightSource?.type === 'track' && entity === 'track' && rightSource?.event === event);
|
|
879
|
+
if (rightSourceMatches) {
|
|
880
|
+
if (rightKey) {
|
|
881
|
+
coGroup.rightBuffer.set(rightKey, coGroup.rightBuffer.get(rightKey) ?? []);
|
|
882
|
+
coGroup.rightBuffer.get(rightKey).push({ data, timestamp });
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
},
|
|
887
|
+
get: (name) => this.namedStreams.get(name),
|
|
888
|
+
};
|
|
889
|
+
registerStream(stream) {
|
|
890
|
+
this.streams.push(stream);
|
|
891
|
+
}
|
|
892
|
+
registerKeyedStream(stream) {
|
|
893
|
+
this.keyedStreams.push(stream);
|
|
894
|
+
}
|
|
895
|
+
registerWindowedStream(stream) {
|
|
896
|
+
this.windowedStreams.push(stream);
|
|
897
|
+
}
|
|
898
|
+
registerAggregatedStream(stream) {
|
|
899
|
+
this.aggregatedStreams.push(stream);
|
|
900
|
+
}
|
|
901
|
+
registerSinkStream(stream, type, name) {
|
|
902
|
+
const key = `${type}.${name}`;
|
|
903
|
+
if (!this.sinkStreams.has(key))
|
|
904
|
+
this.sinkStreams.set(key, []);
|
|
905
|
+
this.sinkStreams.get(key).push(stream);
|
|
906
|
+
}
|
|
907
|
+
registerAggregatedSink(stream, type, name) {
|
|
908
|
+
const key = `${type}.${name}`;
|
|
909
|
+
if (!this.aggregatedSinks.has(key))
|
|
910
|
+
this.aggregatedSinks.set(key, []);
|
|
911
|
+
this.aggregatedSinks.get(key).push(stream);
|
|
912
|
+
}
|
|
913
|
+
registerJoin(left, right, window, joinType, mapper, output) {
|
|
914
|
+
this.joins.push({
|
|
915
|
+
left,
|
|
916
|
+
right,
|
|
917
|
+
window,
|
|
918
|
+
joinType,
|
|
919
|
+
mapper,
|
|
920
|
+
output,
|
|
921
|
+
leftBuffer: new Map(),
|
|
922
|
+
rightBuffer: new Map(),
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
registerCoGroup(left, right, window, mapper, output) {
|
|
926
|
+
this.coGroups.push({
|
|
927
|
+
left,
|
|
928
|
+
right,
|
|
929
|
+
window,
|
|
930
|
+
mapper,
|
|
931
|
+
output,
|
|
932
|
+
leftBuffer: new Map(),
|
|
933
|
+
rightBuffer: new Map(),
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
// Aggregation helpers
|
|
937
|
+
count() {
|
|
938
|
+
return { type: 'count' };
|
|
939
|
+
}
|
|
940
|
+
sum(field) {
|
|
941
|
+
return { type: 'sum', field };
|
|
942
|
+
}
|
|
943
|
+
avg(field) {
|
|
944
|
+
return { type: 'avg', field };
|
|
945
|
+
}
|
|
946
|
+
min(field) {
|
|
947
|
+
return { type: 'min', field };
|
|
948
|
+
}
|
|
949
|
+
max(field) {
|
|
950
|
+
return { type: 'max', field };
|
|
951
|
+
}
|
|
952
|
+
first() {
|
|
953
|
+
return { type: 'first' };
|
|
954
|
+
}
|
|
955
|
+
last() {
|
|
956
|
+
return { type: 'last' };
|
|
957
|
+
}
|
|
958
|
+
collect() {
|
|
959
|
+
return { type: 'collect' };
|
|
960
|
+
}
|
|
961
|
+
distinct(field) {
|
|
962
|
+
return { type: 'distinct', field };
|
|
963
|
+
}
|
|
964
|
+
// Entity proxies
|
|
965
|
+
Customer = this.createEntityProxy('Customer');
|
|
966
|
+
Order = this.createEntityProxy('Order');
|
|
967
|
+
Payment = this.createEntityProxy('Payment');
|
|
968
|
+
createEntityProxy(entityName) {
|
|
969
|
+
return (id) => ({
|
|
970
|
+
get: async () => {
|
|
971
|
+
const storage = this._storage[entityName.toLowerCase() + 's'] ?? this._storage.customers;
|
|
972
|
+
return storage.get(id);
|
|
973
|
+
},
|
|
974
|
+
notify: async (payload) => {
|
|
975
|
+
const handlers = this.entityActionHandlers.get(`${entityName}.notify`) ?? [];
|
|
976
|
+
for (const handler of handlers) {
|
|
977
|
+
handler(id, payload);
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
// View namespace
|
|
983
|
+
view = new Proxy({}, {
|
|
984
|
+
get: (_, viewName) => {
|
|
985
|
+
if (!this.viewData.has(viewName)) {
|
|
986
|
+
this.viewData.set(viewName, new Map());
|
|
987
|
+
}
|
|
988
|
+
const viewStore = this.viewData.get(viewName);
|
|
989
|
+
return {
|
|
990
|
+
get: async (key) => viewStore.get(key),
|
|
991
|
+
set: async (key, value) => { viewStore.set(key, value); },
|
|
992
|
+
};
|
|
993
|
+
},
|
|
994
|
+
});
|
|
995
|
+
// Trigger window processing (called when time advances)
|
|
996
|
+
advanceTime(currentTime) {
|
|
997
|
+
for (const windowedStream of this.windowedStreams) {
|
|
998
|
+
// Process aggregations BEFORE triggering window close (which deletes the state)
|
|
999
|
+
const aggregateSpec = windowedStream.getAggregateSpec();
|
|
1000
|
+
const reduceSpec = windowedStream.getReduceSpec();
|
|
1001
|
+
const windowState = windowedStream.getWindowState();
|
|
1002
|
+
if (aggregateSpec || reduceSpec) {
|
|
1003
|
+
for (const [windowKey, state] of windowState.entries()) {
|
|
1004
|
+
const [key, windowId] = windowKey.split('|');
|
|
1005
|
+
let shouldClose = false;
|
|
1006
|
+
let windowInfo;
|
|
1007
|
+
if (windowedStream.window.type === 'tumbling') {
|
|
1008
|
+
const size = windowedStream.window.size;
|
|
1009
|
+
const windowStart = parseInt(windowId);
|
|
1010
|
+
const windowEnd = windowStart + size;
|
|
1011
|
+
windowInfo = { start: windowStart, end: windowEnd };
|
|
1012
|
+
shouldClose = currentTime >= windowEnd;
|
|
1013
|
+
}
|
|
1014
|
+
else if (windowedStream.window.type === 'sliding') {
|
|
1015
|
+
const size = windowedStream.window.size;
|
|
1016
|
+
const slide = windowedStream.window.slide;
|
|
1017
|
+
const windowStart = parseInt(windowId);
|
|
1018
|
+
const windowEnd = windowStart + size;
|
|
1019
|
+
windowInfo = { start: windowStart, end: windowEnd };
|
|
1020
|
+
shouldClose = currentTime >= windowEnd;
|
|
1021
|
+
}
|
|
1022
|
+
else {
|
|
1023
|
+
const gap = windowedStream.window.gap;
|
|
1024
|
+
windowInfo = { start: state.lastTimestamp - gap, end: state.lastTimestamp + gap };
|
|
1025
|
+
shouldClose = currentTime >= state.lastTimestamp + gap;
|
|
1026
|
+
}
|
|
1027
|
+
if (shouldClose) {
|
|
1028
|
+
let result;
|
|
1029
|
+
if (aggregateSpec) {
|
|
1030
|
+
result = this.computeAggregation(aggregateSpec, state.elements, key);
|
|
1031
|
+
}
|
|
1032
|
+
else if (reduceSpec) {
|
|
1033
|
+
result = state.elements.reduce((acc, item) => reduceSpec.reducer(acc, item), reduceSpec.initial);
|
|
1034
|
+
}
|
|
1035
|
+
// Emit to aggregated streams
|
|
1036
|
+
for (const aggStream of this.aggregatedStreams) {
|
|
1037
|
+
if (aggStream.getWindowedStream() === windowedStream) {
|
|
1038
|
+
aggStream.emitResult(result, key, windowInfo);
|
|
1039
|
+
// Handle sinks
|
|
1040
|
+
if (aggStream.sink?.type === 'measure') {
|
|
1041
|
+
const handlers = this.measureHandlers.get(aggStream.sink.metric) ?? [];
|
|
1042
|
+
const value = typeof result === 'object' ? result.revenue ?? result.count ?? 0 : 0;
|
|
1043
|
+
for (const handler of handlers) {
|
|
1044
|
+
handler(value, { region: key });
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
if (aggStream.sink?.type === 'view') {
|
|
1048
|
+
const viewName = aggStream.sink.view;
|
|
1049
|
+
if (!this.viewData.has(viewName)) {
|
|
1050
|
+
this.viewData.set(viewName, new Map());
|
|
1051
|
+
}
|
|
1052
|
+
this.viewData.get(viewName).set(key, result);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
// Trigger window close AFTER processing aggregations (this clears the state)
|
|
1060
|
+
windowedStream.triggerWindowClose(currentTime);
|
|
1061
|
+
}
|
|
1062
|
+
// Process left joins (emit unmatched left elements after window expires)
|
|
1063
|
+
for (const join of this.joins) {
|
|
1064
|
+
if (join.joinType === 'left') {
|
|
1065
|
+
for (const [key, leftItems] of join.leftBuffer.entries()) {
|
|
1066
|
+
const rightItems = join.rightBuffer.get(key) ?? [];
|
|
1067
|
+
for (const leftItem of leftItems) {
|
|
1068
|
+
if (currentTime - leftItem.timestamp > join.window) {
|
|
1069
|
+
// Check if there was a match
|
|
1070
|
+
const hasMatch = rightItems.some((r) => Math.abs(leftItem.timestamp - r.timestamp) <= join.window);
|
|
1071
|
+
if (!hasMatch) {
|
|
1072
|
+
const result = join.mapper(leftItem.data, null);
|
|
1073
|
+
join.output.processItem(result);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
// Process coGroups
|
|
1081
|
+
for (const coGroup of this.coGroups) {
|
|
1082
|
+
const allKeys = new Set([
|
|
1083
|
+
...coGroup.leftBuffer.keys(),
|
|
1084
|
+
...coGroup.rightBuffer.keys(),
|
|
1085
|
+
]);
|
|
1086
|
+
for (const key of allKeys) {
|
|
1087
|
+
const leftItems = coGroup.leftBuffer.get(key) ?? [];
|
|
1088
|
+
const rightItems = coGroup.rightBuffer.get(key) ?? [];
|
|
1089
|
+
// Check if any items are past the window
|
|
1090
|
+
const expiredLeft = leftItems.filter((i) => currentTime - i.timestamp > coGroup.window);
|
|
1091
|
+
const expiredRight = rightItems.filter((i) => currentTime - i.timestamp > coGroup.window);
|
|
1092
|
+
if (expiredLeft.length > 0 || expiredRight.length > 0) {
|
|
1093
|
+
const result = coGroup.mapper(leftItems.map((i) => i.data), rightItems.map((i) => i.data));
|
|
1094
|
+
coGroup.output.processItem(result);
|
|
1095
|
+
// Clear processed items
|
|
1096
|
+
coGroup.leftBuffer.delete(key);
|
|
1097
|
+
coGroup.rightBuffer.delete(key);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
computeAggregation(spec, elements, key) {
|
|
1103
|
+
const result = { key };
|
|
1104
|
+
for (const [name, aggSpec] of Object.entries(spec)) {
|
|
1105
|
+
switch (aggSpec.type) {
|
|
1106
|
+
case 'count':
|
|
1107
|
+
result[name] = elements.length;
|
|
1108
|
+
break;
|
|
1109
|
+
case 'sum':
|
|
1110
|
+
result[name] = elements.reduce((sum, el) => sum + (el[aggSpec.field] ?? 0), 0);
|
|
1111
|
+
break;
|
|
1112
|
+
case 'avg':
|
|
1113
|
+
result[name] =
|
|
1114
|
+
elements.length > 0
|
|
1115
|
+
? elements.reduce((sum, el) => sum + (el[aggSpec.field] ?? 0), 0) /
|
|
1116
|
+
elements.length
|
|
1117
|
+
: 0;
|
|
1118
|
+
break;
|
|
1119
|
+
case 'min':
|
|
1120
|
+
result[name] = Math.min(...elements.map((el) => el[aggSpec.field] ?? Infinity));
|
|
1121
|
+
break;
|
|
1122
|
+
case 'max':
|
|
1123
|
+
result[name] = Math.max(...elements.map((el) => el[aggSpec.field] ?? -Infinity));
|
|
1124
|
+
break;
|
|
1125
|
+
case 'first':
|
|
1126
|
+
result[name] = elements[0];
|
|
1127
|
+
break;
|
|
1128
|
+
case 'last':
|
|
1129
|
+
result[name] = elements[elements.length - 1];
|
|
1130
|
+
break;
|
|
1131
|
+
case 'collect':
|
|
1132
|
+
result[name] = [...elements];
|
|
1133
|
+
break;
|
|
1134
|
+
case 'distinct':
|
|
1135
|
+
result[name] = new Set(elements.map((el) => el[aggSpec.field])).size;
|
|
1136
|
+
break;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return result;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
// ============================================================================
|
|
1143
|
+
// Factory
|
|
1144
|
+
// ============================================================================
|
|
1145
|
+
export function createStreamContext() {
|
|
1146
|
+
const ctx = new StreamContextImpl();
|
|
1147
|
+
return ctx;
|
|
1148
|
+
}
|
|
1149
|
+
//# sourceMappingURL=index.js.map
|