zilmate 1.8.0 → 1.8.5
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/.agents/skills/copywriting/SKILL.md +252 -0
- package/.agents/skills/copywriting/evals/evals.json +111 -0
- package/.agents/skills/copywriting/references/copy-frameworks.md +344 -0
- package/.agents/skills/copywriting/references/natural-transitions.md +272 -0
- package/.agents/skills/firecrawl-deep-research/SKILL.md +108 -0
- package/.agents/skills/shadcn/SKILL.md +267 -0
- package/.agents/skills/shadcn/agents/openai.yml +5 -0
- package/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
- package/.agents/skills/shadcn/assets/shadcn.png +0 -0
- package/.agents/skills/shadcn/cli.md +290 -0
- package/.agents/skills/shadcn/customization.md +209 -0
- package/.agents/skills/shadcn/evals/evals.json +47 -0
- package/.agents/skills/shadcn/mcp.md +105 -0
- package/.agents/skills/shadcn/registry.md +277 -0
- package/.agents/skills/shadcn/rules/base-vs-radix.md +306 -0
- package/.agents/skills/shadcn/rules/composition.md +195 -0
- package/.agents/skills/shadcn/rules/forms.md +192 -0
- package/.agents/skills/shadcn/rules/icons.md +101 -0
- package/.agents/skills/shadcn/rules/styling.md +162 -0
- package/.agents/skills/tavily-research/SKILL.md +100 -0
- package/.agents/skills/upstash/SKILL.md +48 -0
- package/.agents/skills/upstash/upstash-box-js/overview.md +243 -0
- package/.agents/skills/upstash/upstash-box-py/overview.md +275 -0
- package/.agents/skills/upstash/upstash-cli/overview.md +138 -0
- package/.agents/skills/upstash/upstash-qstash-js/advanced/callbacks.md +147 -0
- package/.agents/skills/upstash/upstash-qstash-js/advanced/deduplication.md +86 -0
- package/.agents/skills/upstash/upstash-qstash-js/advanced/dlq.md +164 -0
- package/.agents/skills/upstash/upstash-qstash-js/advanced/multi-region/summary.md +388 -0
- package/.agents/skills/upstash/upstash-qstash-js/advanced/multi-region/verify-multi-region-setup.ts +388 -0
- package/.agents/skills/upstash/upstash-qstash-js/fundamentals/local-development.md +106 -0
- package/.agents/skills/upstash/upstash-qstash-js/fundamentals/publishing-messages.md +144 -0
- package/.agents/skills/upstash/upstash-qstash-js/fundamentals/queues-and-flow-control.md +148 -0
- package/.agents/skills/upstash/upstash-qstash-js/fundamentals/schedules.md +177 -0
- package/.agents/skills/upstash/upstash-qstash-js/fundamentals/url-groups.md +127 -0
- package/.agents/skills/upstash/upstash-qstash-js/overview.md +81 -0
- package/.agents/skills/upstash/upstash-qstash-js/verification/platform-specific/nextjs.md +171 -0
- package/.agents/skills/upstash/upstash-qstash-js/verification/receiver.md +213 -0
- package/.agents/skills/upstash/upstash-ratelimit-js/algorithms.md +84 -0
- package/.agents/skills/upstash/upstash-ratelimit-js/features.md +117 -0
- package/.agents/skills/upstash/upstash-ratelimit-js/methods-getting-started.md +77 -0
- package/.agents/skills/upstash/upstash-ratelimit-js/overview.md +26 -0
- package/.agents/skills/upstash/upstash-ratelimit-js/pricing-cost.md +146 -0
- package/.agents/skills/upstash/upstash-ratelimit-js/traffic-protection.md +77 -0
- package/.agents/skills/upstash/upstash-redis-js/advanced-features/auto-pipeline.md +49 -0
- package/.agents/skills/upstash/upstash-redis-js/advanced-features/pipeline-and-transactions.md +42 -0
- package/.agents/skills/upstash/upstash-redis-js/advanced-features/scripting.md +124 -0
- package/.agents/skills/upstash/upstash-redis-js/data-structures/hashes.md +71 -0
- package/.agents/skills/upstash/upstash-redis-js/data-structures/json.md +133 -0
- package/.agents/skills/upstash/upstash-redis-js/data-structures/lists.md +72 -0
- package/.agents/skills/upstash/upstash-redis-js/data-structures/sets.md +90 -0
- package/.agents/skills/upstash/upstash-redis-js/data-structures/sorted-sets.md +126 -0
- package/.agents/skills/upstash/upstash-redis-js/data-structures/streams.md +196 -0
- package/.agents/skills/upstash/upstash-redis-js/data-structures/strings.md +73 -0
- package/.agents/skills/upstash/upstash-redis-js/migrations/from-ioredis.md +163 -0
- package/.agents/skills/upstash/upstash-redis-js/migrations/from-redis-node.md +205 -0
- package/.agents/skills/upstash/upstash-redis-js/overview.md +183 -0
- package/.agents/skills/upstash/upstash-redis-js/patterns/caching.md +58 -0
- package/.agents/skills/upstash/upstash-redis-js/patterns/distributed-locks.md +130 -0
- package/.agents/skills/upstash/upstash-redis-js/patterns/leaderboard.md +134 -0
- package/.agents/skills/upstash/upstash-redis-js/patterns/rate-limiting.md +187 -0
- package/.agents/skills/upstash/upstash-redis-js/patterns/session-management.md +217 -0
- package/.agents/skills/upstash/upstash-redis-js/performance/batching-operations.md +76 -0
- package/.agents/skills/upstash/upstash-redis-js/performance/data-serialization.md +134 -0
- package/.agents/skills/upstash/upstash-redis-js/performance/error-handling.md +118 -0
- package/.agents/skills/upstash/upstash-redis-js/performance/pipeline-optimization.md +105 -0
- package/.agents/skills/upstash/upstash-redis-js/performance/redis-replicas.md +14 -0
- package/.agents/skills/upstash/upstash-redis-js/performance/ttl-expiration.md +32 -0
- package/.agents/skills/upstash/upstash-redis-js/search/adapters.md +99 -0
- package/.agents/skills/upstash/upstash-redis-js/search/commands/aggregating.md +234 -0
- package/.agents/skills/upstash/upstash-redis-js/search/commands/aliases.md +76 -0
- package/.agents/skills/upstash/upstash-redis-js/search/commands/index-management.md +124 -0
- package/.agents/skills/upstash/upstash-redis-js/search/commands/querying.md +315 -0
- package/.agents/skills/upstash/upstash-redis-js/search/overview.md +155 -0
- package/.agents/skills/upstash/upstash-redis-start/overview.md +71 -0
- package/.agents/skills/upstash/upstash-search-js/overview.md +48 -0
- package/.agents/skills/upstash/upstash-search-js/quick-start.md +158 -0
- package/.agents/skills/upstash/upstash-search-js/sdk-overview.md +158 -0
- package/.agents/skills/upstash/upstash-vector-js/features/filtering-and-metadata.md +82 -0
- package/.agents/skills/upstash/upstash-vector-js/features/index-structure.md +193 -0
- package/.agents/skills/upstash/upstash-vector-js/features/namespaces.md +32 -0
- package/.agents/skills/upstash/upstash-vector-js/overview.md +44 -0
- package/.agents/skills/upstash/upstash-vector-js/sdk-methods.md +210 -0
- package/.agents/skills/upstash/upstash-workflow-js/agents.md +248 -0
- package/.agents/skills/upstash/upstash-workflow-js/basics/client.md +180 -0
- package/.agents/skills/upstash/upstash-workflow-js/basics/context.md +230 -0
- package/.agents/skills/upstash/upstash-workflow-js/basics/serve.md +70 -0
- package/.agents/skills/upstash/upstash-workflow-js/features/flow-control.md +145 -0
- package/.agents/skills/upstash/upstash-workflow-js/features/invoke.md +97 -0
- package/.agents/skills/upstash/upstash-workflow-js/features/retries-failures-reliability.md +239 -0
- package/.agents/skills/upstash/upstash-workflow-js/features/wait-for-event.md +97 -0
- package/.agents/skills/upstash/upstash-workflow-js/features/webhooks.md +79 -0
- package/.agents/skills/upstash/upstash-workflow-js/how-to/local-dev.md +177 -0
- package/.agents/skills/upstash/upstash-workflow-js/how-to/middleware.md +166 -0
- package/.agents/skills/upstash/upstash-workflow-js/how-to/migrations.md +201 -0
- package/.agents/skills/upstash/upstash-workflow-js/how-to/realtime.md +186 -0
- package/.agents/skills/upstash/upstash-workflow-js/overview.md +55 -0
- package/.agents/skills/upstash/upstash-workflow-js/rest-api.md +156 -0
- package/.agents/skills/upstash/upstash-workflow-js/troubleshooting.md +201 -0
- package/.agents/skills/upstash-box-js/SKILL.md +248 -0
- package/.agents/skills/upstash-box-py/SKILL.md +280 -0
- package/.agents/skills/upstash-cli/SKILL.md +143 -0
- package/.agents/skills/upstash-qstash-js/SKILL.md +86 -0
- package/.agents/skills/upstash-qstash-js/advanced/callbacks.md +147 -0
- package/.agents/skills/upstash-qstash-js/advanced/deduplication.md +86 -0
- package/.agents/skills/upstash-qstash-js/advanced/dlq.md +164 -0
- package/.agents/skills/upstash-qstash-js/advanced/multi-region/summary.md +388 -0
- package/.agents/skills/upstash-qstash-js/advanced/multi-region/verify-multi-region-setup.ts +388 -0
- package/.agents/skills/upstash-qstash-js/fundamentals/local-development.md +106 -0
- package/.agents/skills/upstash-qstash-js/fundamentals/publishing-messages.md +144 -0
- package/.agents/skills/upstash-qstash-js/fundamentals/queues-and-flow-control.md +148 -0
- package/.agents/skills/upstash-qstash-js/fundamentals/schedules.md +177 -0
- package/.agents/skills/upstash-qstash-js/fundamentals/url-groups.md +127 -0
- package/.agents/skills/upstash-qstash-js/verification/platform-specific/nextjs.md +171 -0
- package/.agents/skills/upstash-qstash-js/verification/receiver.md +213 -0
- package/.agents/skills/upstash-ratelimit-js/SKILL.md +31 -0
- package/.agents/skills/upstash-ratelimit-js/algorithms.md +84 -0
- package/.agents/skills/upstash-ratelimit-js/features.md +117 -0
- package/.agents/skills/upstash-ratelimit-js/methods-getting-started.md +77 -0
- package/.agents/skills/upstash-ratelimit-js/pricing-cost.md +146 -0
- package/.agents/skills/upstash-ratelimit-js/traffic-protection.md +77 -0
- package/.agents/skills/upstash-redis-js/SKILL.md +188 -0
- package/.agents/skills/upstash-redis-js/advanced-features/auto-pipeline.md +49 -0
- package/.agents/skills/upstash-redis-js/advanced-features/pipeline-and-transactions.md +42 -0
- package/.agents/skills/upstash-redis-js/advanced-features/scripting.md +124 -0
- package/.agents/skills/upstash-redis-js/data-structures/hashes.md +71 -0
- package/.agents/skills/upstash-redis-js/data-structures/json.md +133 -0
- package/.agents/skills/upstash-redis-js/data-structures/lists.md +72 -0
- package/.agents/skills/upstash-redis-js/data-structures/sets.md +90 -0
- package/.agents/skills/upstash-redis-js/data-structures/sorted-sets.md +126 -0
- package/.agents/skills/upstash-redis-js/data-structures/streams.md +196 -0
- package/.agents/skills/upstash-redis-js/data-structures/strings.md +73 -0
- package/.agents/skills/upstash-redis-js/migrations/from-ioredis.md +163 -0
- package/.agents/skills/upstash-redis-js/migrations/from-redis-node.md +205 -0
- package/.agents/skills/upstash-redis-js/patterns/caching.md +58 -0
- package/.agents/skills/upstash-redis-js/patterns/distributed-locks.md +130 -0
- package/.agents/skills/upstash-redis-js/patterns/leaderboard.md +134 -0
- package/.agents/skills/upstash-redis-js/patterns/rate-limiting.md +187 -0
- package/.agents/skills/upstash-redis-js/patterns/session-management.md +217 -0
- package/.agents/skills/upstash-redis-js/performance/batching-operations.md +76 -0
- package/.agents/skills/upstash-redis-js/performance/data-serialization.md +134 -0
- package/.agents/skills/upstash-redis-js/performance/error-handling.md +118 -0
- package/.agents/skills/upstash-redis-js/performance/pipeline-optimization.md +105 -0
- package/.agents/skills/upstash-redis-js/performance/redis-replicas.md +14 -0
- package/.agents/skills/upstash-redis-js/performance/ttl-expiration.md +32 -0
- package/.agents/skills/upstash-redis-js/search/adapters.md +99 -0
- package/.agents/skills/upstash-redis-js/search/commands/aggregating.md +234 -0
- package/.agents/skills/upstash-redis-js/search/commands/aliases.md +76 -0
- package/.agents/skills/upstash-redis-js/search/commands/index-management.md +124 -0
- package/.agents/skills/upstash-redis-js/search/commands/querying.md +315 -0
- package/.agents/skills/upstash-redis-js/search/overview.md +155 -0
- package/.agents/skills/upstash-redis-start/SKILL.md +76 -0
- package/.agents/skills/upstash-search-js/SKILL.md +53 -0
- package/.agents/skills/upstash-search-js/quick-start.md +158 -0
- package/.agents/skills/upstash-search-js/sdk-overview.md +158 -0
- package/.agents/skills/upstash-vector-js/SKILL.md +49 -0
- package/.agents/skills/upstash-vector-js/features/filtering-and-metadata.md +82 -0
- package/.agents/skills/upstash-vector-js/features/index-structure.md +193 -0
- package/.agents/skills/upstash-vector-js/features/namespaces.md +32 -0
- package/.agents/skills/upstash-vector-js/sdk-methods.md +210 -0
- package/.agents/skills/upstash-workflow-js/SKILL.md +60 -0
- package/.agents/skills/upstash-workflow-js/agents.md +248 -0
- package/.agents/skills/upstash-workflow-js/basics/client.md +180 -0
- package/.agents/skills/upstash-workflow-js/basics/context.md +230 -0
- package/.agents/skills/upstash-workflow-js/basics/serve.md +70 -0
- package/.agents/skills/upstash-workflow-js/features/flow-control.md +145 -0
- package/.agents/skills/upstash-workflow-js/features/invoke.md +97 -0
- package/.agents/skills/upstash-workflow-js/features/retries-failures-reliability.md +239 -0
- package/.agents/skills/upstash-workflow-js/features/wait-for-event.md +97 -0
- package/.agents/skills/upstash-workflow-js/features/webhooks.md +79 -0
- package/.agents/skills/upstash-workflow-js/how-to/local-dev.md +177 -0
- package/.agents/skills/upstash-workflow-js/how-to/middleware.md +166 -0
- package/.agents/skills/upstash-workflow-js/how-to/migrations.md +201 -0
- package/.agents/skills/upstash-workflow-js/how-to/realtime.md +186 -0
- package/.agents/skills/upstash-workflow-js/rest-api.md +156 -0
- package/.agents/skills/upstash-workflow-js/troubleshooting.md +201 -0
- package/dist/agents/coding.agent.d.ts +185 -42
- package/dist/agents/coding.agent.d.ts.map +1 -1
- package/dist/agents/coding.agent.js +42 -5
- package/dist/agents/coding.agent.js.map +1 -1
- package/dist/agents/manager.d.ts +216 -41
- package/dist/agents/manager.d.ts.map +1 -1
- package/dist/agents/manager.js +103 -140
- package/dist/agents/manager.js.map +1 -1
- package/dist/agents/security.agent.d.ts +3 -3
- package/dist/agents/swarm/development/lead.d.ts +3 -0
- package/dist/agents/swarm/development/lead.d.ts.map +1 -0
- package/dist/agents/swarm/development/lead.js +24 -0
- package/dist/agents/swarm/development/lead.js.map +1 -0
- package/dist/agents/swarm/main.d.ts +69 -0
- package/dist/agents/swarm/main.d.ts.map +1 -1
- package/dist/agents/swarm/main.js +65 -11
- package/dist/agents/swarm/main.js.map +1 -1
- package/dist/agents/swarm/registry.d.ts +2 -1
- package/dist/agents/swarm/registry.d.ts.map +1 -1
- package/dist/agents/swarm/registry.js +225 -95
- package/dist/agents/swarm/registry.js.map +1 -1
- package/dist/cli/composer.d.ts.map +1 -1
- package/dist/cli/composer.js +85 -30
- package/dist/cli/composer.js.map +1 -1
- package/dist/cli/confirm.d.ts +2 -2
- package/dist/cli/confirm.d.ts.map +1 -1
- package/dist/cli/confirm.js +373 -4
- package/dist/cli/confirm.js.map +1 -1
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +47 -2
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/format.d.ts +4 -0
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +352 -65
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +80 -2
- package/dist/cli/interactive.js.map +1 -1
- package/dist/cli/menu.d.ts.map +1 -1
- package/dist/cli/menu.js +93 -83
- package/dist/cli/menu.js.map +1 -1
- package/dist/cli/render.d.ts.map +1 -1
- package/dist/cli/render.js +2 -1
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +262 -27
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/swarm.d.ts.map +1 -1
- package/dist/cli/swarm.js +13 -10
- package/dist/cli/swarm.js.map +1 -1
- package/dist/cli/theme.d.ts +7 -1
- package/dist/cli/theme.d.ts.map +1 -1
- package/dist/cli/theme.js +4 -16
- package/dist/cli/theme.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +27 -4
- package/dist/cli/update.js.map +1 -1
- package/dist/config/env.d.ts +3 -1
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +22 -2
- package/dist/config/env.js.map +1 -1
- package/dist/config/models.d.ts +1 -0
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +17 -1
- package/dist/config/models.js.map +1 -1
- package/dist/documents/pdf.d.ts.map +1 -1
- package/dist/documents/pdf.js +282 -22
- package/dist/documents/pdf.js.map +1 -1
- package/dist/documents/slides.d.ts.map +1 -1
- package/dist/documents/slides.js +160 -30
- package/dist/documents/slides.js.map +1 -1
- package/dist/index.js +73 -18
- package/dist/index.js.map +1 -1
- package/dist/memory/history.js +1 -1
- package/dist/observability/doctor.d.ts.map +1 -1
- package/dist/observability/doctor.js +3 -0
- package/dist/observability/doctor.js.map +1 -1
- package/dist/runtime/progress.d.ts +5 -1
- package/dist/runtime/progress.d.ts.map +1 -1
- package/dist/runtime/progress.js.map +1 -1
- package/dist/runtime/swarm.d.ts +1 -1
- package/dist/runtime/swarm.d.ts.map +1 -1
- package/dist/runtime/swarm.js +19 -3
- package/dist/runtime/swarm.js.map +1 -1
- package/dist/runtime/tool-utils.d.ts +2 -0
- package/dist/runtime/tool-utils.d.ts.map +1 -1
- package/dist/runtime/tool-utils.js +91 -0
- package/dist/runtime/tool-utils.js.map +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +16 -0
- package/dist/skills/loader.js.map +1 -1
- package/dist/test-call.d.ts +2 -0
- package/dist/test-call.d.ts.map +1 -0
- package/dist/test-call.js +29 -0
- package/dist/test-call.js.map +1 -0
- package/dist/test-manager-call.d.ts +2 -0
- package/dist/test-manager-call.d.ts.map +1 -0
- package/dist/test-manager-call.js +138 -0
- package/dist/test-manager-call.js.map +1 -0
- package/dist/test-manager-filtered.d.ts +2 -0
- package/dist/test-manager-filtered.d.ts.map +1 -0
- package/dist/test-manager-filtered.js +130 -0
- package/dist/test-manager-filtered.js.map +1 -0
- package/dist/test-mcp-call.d.ts +2 -0
- package/dist/test-mcp-call.d.ts.map +1 -0
- package/dist/test-mcp-call.js +103 -0
- package/dist/test-mcp-call.js.map +1 -0
- package/dist/tools/composio.tool.d.ts +1 -1
- package/dist/tools/composio.tool.d.ts.map +1 -1
- package/dist/tools/composio.tool.js +7 -16
- package/dist/tools/composio.tool.js.map +1 -1
- package/dist/tools/daytona-browser.tool.js +1 -1
- package/dist/tools/daytona-browser.tool.js.map +1 -1
- package/dist/tools/executive.tool.d.ts +40 -0
- package/dist/tools/executive.tool.d.ts.map +1 -0
- package/dist/tools/executive.tool.js +74 -0
- package/dist/tools/executive.tool.js.map +1 -0
- package/dist/tools/filesystem.tool.d.ts +183 -40
- package/dist/tools/filesystem.tool.d.ts.map +1 -1
- package/dist/tools/filesystem.tool.js +564 -111
- package/dist/tools/filesystem.tool.js.map +1 -1
- package/dist/tools/mcp.tool.d.ts +56 -0
- package/dist/tools/mcp.tool.d.ts.map +1 -0
- package/dist/tools/mcp.tool.js +285 -0
- package/dist/tools/mcp.tool.js.map +1 -0
- package/dist/tools/osint.tool.d.ts +4 -4
- package/dist/tools/pentest.tool.d.ts +2 -2
- package/dist/tools/setup-assistant.tool.d.ts +1 -1
- package/dist/tools/setup-assistant.tool.js +1 -1
- package/dist/tools/setup-assistant.tool.js.map +1 -1
- package/dist/workspace/paths.d.ts +1 -0
- package/dist/workspace/paths.d.ts.map +1 -1
- package/dist/workspace/paths.js +3 -4
- package/dist/workspace/paths.js.map +1 -1
- package/package.json +8 -4
- package/scripts/postinstall.mjs +19 -4
- package/scripts/release-github.mjs +11 -11
package/.agents/skills/upstash/upstash-qstash-js/advanced/multi-region/verify-multi-region-setup.ts
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-magic-numbers */
|
|
3
|
+
/* eslint-disable no-console */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Script to verify QStash environment variable setup
|
|
7
|
+
*
|
|
8
|
+
* This script checks:
|
|
9
|
+
* - Whether the setup is single-region or multi-region
|
|
10
|
+
* - Which region will be used for outgoing messages
|
|
11
|
+
* - Whether all required environment variables are present
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* npx tsx skills/advanced/scripts/verify-multi-region-setup.ts
|
|
15
|
+
* # or with node
|
|
16
|
+
* node skills/advanced/scripts/verify-multi-region-setup.js
|
|
17
|
+
*
|
|
18
|
+
* To load from .env file, install dotenv first:
|
|
19
|
+
* npm install dotenv
|
|
20
|
+
* npx tsx -r dotenv/config skills/advanced/scripts/verify-multi-region-setup.ts
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
// // Try to load dotenv if available
|
|
24
|
+
try {
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-require-imports, unicorn/prefer-module
|
|
26
|
+
require("dotenv").config();
|
|
27
|
+
} catch {
|
|
28
|
+
// dotenv not available, will use process.env directly
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type QStashRegion = "EU_CENTRAL_1" | "US_EAST_1";
|
|
32
|
+
|
|
33
|
+
const VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"] as const;
|
|
34
|
+
const DEFAULT_QSTASH_URL = "https://qstash.upstash.io";
|
|
35
|
+
|
|
36
|
+
type CheckResult = {
|
|
37
|
+
status: "success" | "warning" | "error";
|
|
38
|
+
message: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type VerificationResult = {
|
|
42
|
+
mode: "single-region" | "multi-region";
|
|
43
|
+
primaryRegion?: QStashRegion;
|
|
44
|
+
checks: CheckResult[];
|
|
45
|
+
outgoingConfig: {
|
|
46
|
+
url?: string;
|
|
47
|
+
token?: string;
|
|
48
|
+
source: string;
|
|
49
|
+
};
|
|
50
|
+
incomingConfig: {
|
|
51
|
+
currentKey?: string;
|
|
52
|
+
nextKey?: string;
|
|
53
|
+
source: string;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Colors for terminal output
|
|
58
|
+
const colors = {
|
|
59
|
+
reset: "\u001B[0m",
|
|
60
|
+
bright: "\u001B[1m",
|
|
61
|
+
red: "\u001B[31m",
|
|
62
|
+
green: "\u001B[32m",
|
|
63
|
+
yellow: "\u001B[33m",
|
|
64
|
+
blue: "\u001B[34m",
|
|
65
|
+
cyan: "\u001B[36m",
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function colorize(color: keyof typeof colors, text: string): string {
|
|
69
|
+
return `${colors[color]}${text}${colors.reset}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function printHeader(text: string): void {
|
|
73
|
+
console.log("\n" + colorize("bright", colorize("cyan", text)));
|
|
74
|
+
console.log(colorize("cyan", "=".repeat(text.length)));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function printCheck(result: CheckResult): void {
|
|
78
|
+
const icon = result.status === "success" ? "✓" : result.status === "warning" ? "⚠" : "✗";
|
|
79
|
+
const color =
|
|
80
|
+
result.status === "success" ? "green" : result.status === "warning" ? "yellow" : "red";
|
|
81
|
+
console.log(`${colorize(color, icon)} ${result.message}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeRegion(region: string | undefined): QStashRegion | undefined {
|
|
85
|
+
if (!region) return undefined;
|
|
86
|
+
|
|
87
|
+
const normalized = region.replaceAll("-", "_").toUpperCase();
|
|
88
|
+
if (VALID_REGIONS.includes(normalized as QStashRegion)) {
|
|
89
|
+
return normalized as QStashRegion;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function checkEnvironmentVariable(name: string, required = false): CheckResult {
|
|
96
|
+
const value = process.env[name];
|
|
97
|
+
|
|
98
|
+
if (!value) {
|
|
99
|
+
return {
|
|
100
|
+
status: required ? "error" : "warning",
|
|
101
|
+
message: `${name} is ${required ? "required but " : ""}not set`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const maskedValue = value.length > 10 ? `${value.slice(0, 4)}...${value.slice(-4)}` : "***";
|
|
106
|
+
return {
|
|
107
|
+
status: "success",
|
|
108
|
+
message: `${name} = ${maskedValue}`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function verifySetup(): VerificationResult {
|
|
113
|
+
const checks: CheckResult[] = [];
|
|
114
|
+
const qstashRegion = process.env.QSTASH_REGION;
|
|
115
|
+
const normalizedRegion = normalizeRegion(qstashRegion);
|
|
116
|
+
|
|
117
|
+
// Determine mode
|
|
118
|
+
const mode = normalizedRegion ? "multi-region" : "single-region";
|
|
119
|
+
const primaryRegion = normalizedRegion;
|
|
120
|
+
|
|
121
|
+
printHeader("QStash Environment Verification");
|
|
122
|
+
|
|
123
|
+
// Check mode
|
|
124
|
+
if (mode === "multi-region" && primaryRegion) {
|
|
125
|
+
console.log(colorize("bright", `\nMode: `) + colorize("green", "Multi-Region"));
|
|
126
|
+
console.log(colorize("bright", `Primary Region: `) + colorize("green", primaryRegion));
|
|
127
|
+
} else {
|
|
128
|
+
console.log(colorize("bright", `\nMode: `) + colorize("blue", "Single-Region (Default)"));
|
|
129
|
+
if (qstashRegion && !normalizedRegion) {
|
|
130
|
+
checks.push({
|
|
131
|
+
status: "error",
|
|
132
|
+
message: `Invalid QSTASH_REGION="${qstashRegion}". Valid values: ${VALID_REGIONS.join(", ")}`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let outgoingUrl: string | undefined;
|
|
138
|
+
let outgoingToken: string | undefined;
|
|
139
|
+
let outgoingSource: string;
|
|
140
|
+
|
|
141
|
+
if (mode === "multi-region" && primaryRegion) {
|
|
142
|
+
// Multi-region mode
|
|
143
|
+
const regionUrl = process.env[`${primaryRegion}_QSTASH_URL`];
|
|
144
|
+
const regionToken = process.env[`${primaryRegion}_QSTASH_TOKEN`];
|
|
145
|
+
|
|
146
|
+
if (regionUrl && regionToken) {
|
|
147
|
+
outgoingUrl = regionUrl;
|
|
148
|
+
outgoingToken = regionToken;
|
|
149
|
+
outgoingSource = `${primaryRegion}_QSTASH_*`;
|
|
150
|
+
|
|
151
|
+
checks.push(
|
|
152
|
+
{
|
|
153
|
+
status: "success",
|
|
154
|
+
message: `Using ${primaryRegion} credentials`,
|
|
155
|
+
},
|
|
156
|
+
checkEnvironmentVariable(`${primaryRegion}_QSTASH_URL`, true),
|
|
157
|
+
checkEnvironmentVariable(`${primaryRegion}_QSTASH_TOKEN`, true)
|
|
158
|
+
);
|
|
159
|
+
} else {
|
|
160
|
+
// In multi-region mode, missing region-specific credentials is an error
|
|
161
|
+
checks.push({
|
|
162
|
+
status: "error",
|
|
163
|
+
message: `${primaryRegion} credentials required in multi-region mode`,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (!regionUrl) {
|
|
167
|
+
checks.push(checkEnvironmentVariable(`${primaryRegion}_QSTASH_URL`, true));
|
|
168
|
+
}
|
|
169
|
+
if (!regionToken) {
|
|
170
|
+
checks.push(checkEnvironmentVariable(`${primaryRegion}_QSTASH_TOKEN`, true));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Still set fallback values for summary display
|
|
174
|
+
outgoingUrl = process.env.QSTASH_URL ?? DEFAULT_QSTASH_URL;
|
|
175
|
+
outgoingToken = process.env.QSTASH_TOKEN;
|
|
176
|
+
outgoingSource = "QSTASH_* (fallback - ERROR)";
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
// Single-region mode
|
|
180
|
+
outgoingUrl = process.env.QSTASH_URL ?? DEFAULT_QSTASH_URL;
|
|
181
|
+
outgoingToken = process.env.QSTASH_TOKEN;
|
|
182
|
+
outgoingSource = "QSTASH_*";
|
|
183
|
+
|
|
184
|
+
checks.push(
|
|
185
|
+
checkEnvironmentVariable("QSTASH_URL", false),
|
|
186
|
+
checkEnvironmentVariable("QSTASH_TOKEN", true)
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let currentKey: string | undefined;
|
|
191
|
+
let nextKey: string | undefined;
|
|
192
|
+
let incomingSource: string;
|
|
193
|
+
|
|
194
|
+
if (mode === "multi-region" && primaryRegion) {
|
|
195
|
+
// In multi-region mode, check for region-specific keys
|
|
196
|
+
const usCurrentKey = process.env.US_EAST_1_QSTASH_CURRENT_SIGNING_KEY;
|
|
197
|
+
const usNextKey = process.env.US_EAST_1_QSTASH_NEXT_SIGNING_KEY;
|
|
198
|
+
const euCurrentKey = process.env.EU_CENTRAL_1_QSTASH_CURRENT_SIGNING_KEY;
|
|
199
|
+
const euNextKey = process.env.EU_CENTRAL_1_QSTASH_NEXT_SIGNING_KEY;
|
|
200
|
+
|
|
201
|
+
const hasUsKeys = usCurrentKey && usNextKey;
|
|
202
|
+
const hasEuKeys = euCurrentKey && euNextKey;
|
|
203
|
+
|
|
204
|
+
if (hasUsKeys && hasEuKeys) {
|
|
205
|
+
checks.push(
|
|
206
|
+
{
|
|
207
|
+
status: "success",
|
|
208
|
+
message: "Region-specific signing keys configured for both US and EU",
|
|
209
|
+
},
|
|
210
|
+
checkEnvironmentVariable("US_EAST_1_QSTASH_CURRENT_SIGNING_KEY", false),
|
|
211
|
+
checkEnvironmentVariable("US_EAST_1_QSTASH_NEXT_SIGNING_KEY", false),
|
|
212
|
+
checkEnvironmentVariable("EU_CENTRAL_1_QSTASH_CURRENT_SIGNING_KEY", false),
|
|
213
|
+
checkEnvironmentVariable("EU_CENTRAL_1_QSTASH_NEXT_SIGNING_KEY", false)
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
currentKey = primaryRegion === "US_EAST_1" ? usCurrentKey : euCurrentKey;
|
|
217
|
+
nextKey = primaryRegion === "US_EAST_1" ? usNextKey : euNextKey;
|
|
218
|
+
incomingSource = "Region-specific keys";
|
|
219
|
+
} else if (hasUsKeys) {
|
|
220
|
+
checks.push(
|
|
221
|
+
{
|
|
222
|
+
status: "warning",
|
|
223
|
+
message: "Only US region signing keys configured",
|
|
224
|
+
},
|
|
225
|
+
checkEnvironmentVariable("US_EAST_1_QSTASH_CURRENT_SIGNING_KEY", false),
|
|
226
|
+
checkEnvironmentVariable("US_EAST_1_QSTASH_NEXT_SIGNING_KEY", false),
|
|
227
|
+
{
|
|
228
|
+
status: "warning",
|
|
229
|
+
message: "EU signing keys not configured - EU messages will attempt to use keys",
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
currentKey = usCurrentKey;
|
|
234
|
+
nextKey = usNextKey;
|
|
235
|
+
incomingSource = "US-specific keys";
|
|
236
|
+
} else if (hasEuKeys) {
|
|
237
|
+
checks.push(
|
|
238
|
+
{
|
|
239
|
+
status: "warning",
|
|
240
|
+
message: "Only EU region signing keys configured",
|
|
241
|
+
},
|
|
242
|
+
checkEnvironmentVariable("EU_CENTRAL_1_QSTASH_CURRENT_SIGNING_KEY", false),
|
|
243
|
+
checkEnvironmentVariable("EU_CENTRAL_1_QSTASH_NEXT_SIGNING_KEY", false),
|
|
244
|
+
{
|
|
245
|
+
status: "warning",
|
|
246
|
+
message: "US signing keys not configured - US messages will attempt to use default keys",
|
|
247
|
+
}
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
currentKey = euCurrentKey;
|
|
251
|
+
nextKey = euNextKey;
|
|
252
|
+
incomingSource = "EU-specific keys";
|
|
253
|
+
} else {
|
|
254
|
+
checks.push({
|
|
255
|
+
status: "warning",
|
|
256
|
+
message: "No region-specific signing keys found, using default keys",
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
currentKey = process.env.QSTASH_CURRENT_SIGNING_KEY;
|
|
260
|
+
nextKey = process.env.QSTASH_NEXT_SIGNING_KEY;
|
|
261
|
+
incomingSource = "QSTASH_* (default)";
|
|
262
|
+
|
|
263
|
+
checks.push(
|
|
264
|
+
checkEnvironmentVariable("QSTASH_CURRENT_SIGNING_KEY", false),
|
|
265
|
+
checkEnvironmentVariable("QSTASH_NEXT_SIGNING_KEY", false)
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
// Single-region mode
|
|
270
|
+
currentKey = process.env.QSTASH_CURRENT_SIGNING_KEY;
|
|
271
|
+
nextKey = process.env.QSTASH_NEXT_SIGNING_KEY;
|
|
272
|
+
incomingSource = "QSTASH_*";
|
|
273
|
+
|
|
274
|
+
checks.push(
|
|
275
|
+
checkEnvironmentVariable("QSTASH_CURRENT_SIGNING_KEY", false),
|
|
276
|
+
checkEnvironmentVariable("QSTASH_NEXT_SIGNING_KEY", false)
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
if (!currentKey && !nextKey) {
|
|
280
|
+
checks.push({
|
|
281
|
+
status: "warning",
|
|
282
|
+
message:
|
|
283
|
+
"Signing keys not set - incoming message verification will fail if verifier is used",
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
mode,
|
|
290
|
+
primaryRegion,
|
|
291
|
+
checks,
|
|
292
|
+
outgoingConfig: {
|
|
293
|
+
url: outgoingUrl,
|
|
294
|
+
token: outgoingToken,
|
|
295
|
+
source: outgoingSource,
|
|
296
|
+
},
|
|
297
|
+
incomingConfig: {
|
|
298
|
+
currentKey,
|
|
299
|
+
nextKey,
|
|
300
|
+
source: incomingSource,
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function printSummary(result: VerificationResult): void {
|
|
306
|
+
console.log(colorize("bright", "\nOutgoing Messages:"));
|
|
307
|
+
console.log(` URL: ${result.outgoingConfig.url ?? "not set"}`);
|
|
308
|
+
console.log(` Token: ${result.outgoingConfig.token ? "✓ configured" : "✗ not set"}`);
|
|
309
|
+
console.log(` Source: ${result.outgoingConfig.source}`);
|
|
310
|
+
|
|
311
|
+
console.log(colorize("bright", "\nIncoming Messages:"));
|
|
312
|
+
console.log(` Current Key: ${result.incomingConfig.currentKey ? "✓ configured" : "✗ not set"}`);
|
|
313
|
+
console.log(` Next Key: ${result.incomingConfig.nextKey ? "✓ configured" : "✗ not set"}`);
|
|
314
|
+
console.log(` Source: ${result.incomingConfig.source}`);
|
|
315
|
+
|
|
316
|
+
printHeader("Verification Results");
|
|
317
|
+
|
|
318
|
+
const errors = result.checks.filter((c) => c.status === "error");
|
|
319
|
+
const warnings = result.checks.filter((c) => c.status === "warning");
|
|
320
|
+
const successes = result.checks.filter((c) => c.status === "success");
|
|
321
|
+
|
|
322
|
+
for (const element of result.checks) {
|
|
323
|
+
printCheck(element);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
console.log(
|
|
327
|
+
`\n${colorize("bright", "Summary:")} ${colorize("green", `${successes.length} passed`)}, ${colorize("yellow", `${warnings.length} warnings`)}, ${colorize("red", `${errors.length} errors`)}`
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
if (errors.length > 0) {
|
|
331
|
+
console.log(
|
|
332
|
+
colorize("red", "\n✗ Configuration has errors. Please fix them before using QStash.")
|
|
333
|
+
);
|
|
334
|
+
process.exit(1);
|
|
335
|
+
} else if (warnings.length > 0) {
|
|
336
|
+
console.log(
|
|
337
|
+
colorize("yellow", "\n⚠ Configuration has warnings. Review them to ensure proper setup.")
|
|
338
|
+
);
|
|
339
|
+
} else {
|
|
340
|
+
console.log(colorize("green", "\n✓ Configuration looks good!"));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Provide recommendations
|
|
344
|
+
if (result.mode === "multi-region") {
|
|
345
|
+
printHeader("Multi-Region Recommendations");
|
|
346
|
+
|
|
347
|
+
console.log("For optimal multi-region operation:");
|
|
348
|
+
console.log("1. Configure region-specific signing keys for both US and EU");
|
|
349
|
+
console.log("2. Always pass the 'upstashRegion' parameter in receiver.verify()");
|
|
350
|
+
console.log("3. Monitor SDK warnings in your application logs");
|
|
351
|
+
console.log("4. Test message delivery from both regions");
|
|
352
|
+
|
|
353
|
+
console.log(colorize("cyan", "\nExample verification code:"));
|
|
354
|
+
console.log(`
|
|
355
|
+
await receiver.verify({
|
|
356
|
+
signature: request.headers.get("upstash-signature")!,
|
|
357
|
+
body: await request.text(),
|
|
358
|
+
upstashRegion: request.headers.get("upstash-region") ?? undefined,
|
|
359
|
+
});
|
|
360
|
+
`);
|
|
361
|
+
} else {
|
|
362
|
+
printHeader("Recommendations");
|
|
363
|
+
|
|
364
|
+
if (!result.incomingConfig.currentKey || !result.incomingConfig.nextKey) {
|
|
365
|
+
console.log("Consider adding signing keys for incoming message verification:");
|
|
366
|
+
console.log(" QSTASH_CURRENT_SIGNING_KEY=your_current_key");
|
|
367
|
+
console.log(" QSTASH_NEXT_SIGNING_KEY=your_next_key");
|
|
368
|
+
console.log("\nGet keys from: https://console.upstash.com/qstash");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (!process.env.QSTASH_URL) {
|
|
372
|
+
console.log("\nUsing default EU region. To specify a custom URL:");
|
|
373
|
+
console.log(" QSTASH_URL=https://qstash.upstash.io");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
console.log(" Multi-Region: skills/advanced/multi-region.md");
|
|
377
|
+
console.log(" Verification: skills/verification/receiver.md");
|
|
378
|
+
console.log("");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Main execution
|
|
382
|
+
try {
|
|
383
|
+
const result = verifySetup();
|
|
384
|
+
printSummary(result);
|
|
385
|
+
} catch (error) {
|
|
386
|
+
console.error(colorize("red", "\nError running verification:"), error);
|
|
387
|
+
process.exit(1);
|
|
388
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Local Development
|
|
2
|
+
|
|
3
|
+
Use the QStash dev server to test publishing and signature verification end-to-end on your machine — no public tunnel, no real QStash account, no real signing keys.
|
|
4
|
+
|
|
5
|
+
## Automatic dev server (recommended)
|
|
6
|
+
|
|
7
|
+
Pass `devMode: true` to the `Client` (and the matching verifier). The SDK will download the QStash CLI binary, start it, and wire credentials for you.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { Client } from "@upstash/qstash";
|
|
11
|
+
|
|
12
|
+
const client = new Client({ devMode: true });
|
|
13
|
+
|
|
14
|
+
await client.publishJSON({
|
|
15
|
+
url: "https://example.com/webhook",
|
|
16
|
+
body: { hello: "world" },
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
When `devMode` is on:
|
|
21
|
+
|
|
22
|
+
- The SDK downloads the latest `qstash` binary on first use, caches it under the OS cache directory, and reuses it across projects.
|
|
23
|
+
- Spawns the dev server on port `8080` (override via `QSTASH_DEV_PORT`).
|
|
24
|
+
- Reuses an already-running server on the configured port instead of spawning a duplicate.
|
|
25
|
+
- Injects deterministic dev `baseUrl`, `token`, and signing keys. **Any explicit `token` / `baseUrl` / signing keys you pass are ignored** (a warning is printed). To use real credentials, set `devMode: false`.
|
|
26
|
+
- Forwards dev server logs to stdout/stderr with a dim `[QStash CLI]` prefix.
|
|
27
|
+
- No-ops in production (`NODE_ENV=production`), during `next build`, and in browser/edge runtimes.
|
|
28
|
+
|
|
29
|
+
### Environment variable
|
|
30
|
+
|
|
31
|
+
Instead of hard-coding `devMode: true`, leave the option unset and toggle via env:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
QSTASH_DEV=true # or "1"
|
|
35
|
+
QSTASH_DEV_PORT=8080 # optional, default 8080
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The resolution order is: explicit `devMode` boolean > `QSTASH_DEV` env var > off. Use this so production code never accidentally turns the dev server on.
|
|
39
|
+
|
|
40
|
+
### Verifying signatures in dev
|
|
41
|
+
|
|
42
|
+
The receiving side also needs `devMode: true` so it verifies against the dev server's deterministic signing keys instead of your real ones.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { Receiver } from "@upstash/qstash";
|
|
46
|
+
|
|
47
|
+
const receiver = new Receiver({ devMode: true });
|
|
48
|
+
|
|
49
|
+
await receiver.verify({
|
|
50
|
+
signature: request.headers.get("upstash-signature")!,
|
|
51
|
+
body: await request.text(),
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For Next.js, the wrappers accept the same flag:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// app/api/webhook/route.ts
|
|
59
|
+
import { verifySignatureAppRouter } from "@upstash/qstash/nextjs";
|
|
60
|
+
|
|
61
|
+
export const POST = verifySignatureAppRouter(
|
|
62
|
+
async (request) => new Response("ok"),
|
|
63
|
+
{ devMode: true }
|
|
64
|
+
);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Next.js: starting the server before edge routes
|
|
68
|
+
|
|
69
|
+
Edge runtimes cannot spawn child processes. If a route in the Edge Runtime is the first thing to use QStash, the SDK can only check whether the server is reachable — it cannot start it. Call `registerQStashDev()` from `instrumentation.ts` to spawn the server during Next.js startup, before any request hits:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// instrumentation.ts
|
|
73
|
+
import { registerQStashDev } from "@upstash/qstash/nextjs";
|
|
74
|
+
|
|
75
|
+
export function register() {
|
|
76
|
+
registerQStashDev();
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`registerQStashDev` is itself a no-op in production and during `next build`.
|
|
81
|
+
|
|
82
|
+
### Common pitfalls
|
|
83
|
+
|
|
84
|
+
- **Passing `token` alongside `devMode: true`** — the explicit token is ignored and you get a console warning. Drop the `token` field, or set `devMode: false` to use it.
|
|
85
|
+
- **Hitting build-time errors in `next build`** — if a route module is evaluated at build time and instantiates the `Client`, mark the route `export const dynamic = "force-dynamic"` so it isn't pre-rendered.
|
|
86
|
+
- **Port 8080 already in use** — the SDK errors with a clear message; either stop the other process or set `QSTASH_DEV_PORT` to a free port (matched on both client and receiver sides).
|
|
87
|
+
- **Calling QStash from an edge route without `instrumentation.ts`** — the edge runtime can't spawn the binary. Either move the call to a Node-runtime route or wire `registerQStashDev()` as shown above.
|
|
88
|
+
|
|
89
|
+
## Manual dev server
|
|
90
|
+
|
|
91
|
+
If you cannot use the automatic flow (non-Node runtime, CI without internet, custom binary), run the CLI yourself:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npx @upstash/qstash-cli dev
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Then construct the client with the printed dev credentials explicitly and **leave `devMode` unset/false**:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const client = new Client({
|
|
101
|
+
baseUrl: "http://127.0.0.1:8080",
|
|
102
|
+
token: "eyJVc2VySUQiOiJkZWZhdWx0VXNlciIsIlBhc3N3b3JkIjoiZGVmYXVsdFBhc3N3b3JkIn0=",
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
See the [Local Development docs](https://upstash.com/docs/qstash/howto/local-development) for the full list of test users and CLI options.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Publishing Messages
|
|
2
|
+
|
|
3
|
+
Publish HTTP messages to destinations using the QStash SDK. Messages are delivered asynchronously with built-in retries and monitoring.
|
|
4
|
+
|
|
5
|
+
## Basic Publishing
|
|
6
|
+
|
|
7
|
+
### publishJSON()
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { Client } from "@upstash/qstash";
|
|
11
|
+
|
|
12
|
+
const client = new Client({ token: process.env.QSTASH_TOKEN! });
|
|
13
|
+
|
|
14
|
+
const result = await client.publishJSON({
|
|
15
|
+
url: "https://api.example.com/webhook",
|
|
16
|
+
body: { userId: "123", event: "order.completed" },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
console.log(result.messageId); // "msg_123..."
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Destination Options
|
|
23
|
+
|
|
24
|
+
Specify where to send the message using one of these mutually exclusive options:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
await client.publishJSON({
|
|
28
|
+
// Send to a single HTTP endpoint:
|
|
29
|
+
url: "https://api.example.com/webhook",
|
|
30
|
+
// Send to all endpoints in a URL group. Creates one message per endpoint:
|
|
31
|
+
// Learn more in [URL Groups](url-groups.md).
|
|
32
|
+
urlGroup: "my-api-group",
|
|
33
|
+
// ...
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Message Options
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// JSON object
|
|
41
|
+
await client.publishJSON({
|
|
42
|
+
url: "https://api.example.com/webhook",
|
|
43
|
+
// The message payload
|
|
44
|
+
body: { order_id: "123", items: [1, 2, 3] },
|
|
45
|
+
// Send to a FIFO queue for ordered processing:
|
|
46
|
+
// Learn more in [Queues and Flow Control](queues-and-flow-control.md).
|
|
47
|
+
queueName: "my-fifo-queue",
|
|
48
|
+
// Send with a flow control key to limit rate/parallelism:
|
|
49
|
+
flowControl: {
|
|
50
|
+
key: "user-123",
|
|
51
|
+
parallelism: 2,
|
|
52
|
+
rate: 10,
|
|
53
|
+
period: 60,
|
|
54
|
+
},
|
|
55
|
+
// request headers
|
|
56
|
+
headers: {
|
|
57
|
+
"Content-Type": "application/json",
|
|
58
|
+
"X-Custom-Header": "value",
|
|
59
|
+
Authorization: "Bearer token", // auth token for the destination
|
|
60
|
+
},
|
|
61
|
+
// request method
|
|
62
|
+
method: "PUT",
|
|
63
|
+
// Delay message delivery by a duration in seconds:
|
|
64
|
+
delay: 60,
|
|
65
|
+
// alternative of delay, deliver at a specific Unix timestamp in seconds:
|
|
66
|
+
notBefore: 1700000000,
|
|
67
|
+
// retries
|
|
68
|
+
retries: 10,
|
|
69
|
+
// Customize the delay between retries using a mathematical expression.
|
|
70
|
+
// Default is exponential backoff.
|
|
71
|
+
// Supported functions: `pow`, `sqrt`, `abs`, `exp`, `floor`, `ceil`, `round`, `min`, `max`.
|
|
72
|
+
retryDelay: "5000",
|
|
73
|
+
// Maximum duration for the HTTP request in seconds:
|
|
74
|
+
timeout: 15,
|
|
75
|
+
// URL called if the message is successfully delivered:
|
|
76
|
+
callback: "https://api.example.com/qstash-callback",
|
|
77
|
+
// URL called only when all retries are exhausted:
|
|
78
|
+
failureCallback: "https://api.example.com/failure-handler",
|
|
79
|
+
// id for deduplicating messages
|
|
80
|
+
deduplicationId: "custom-id-123",
|
|
81
|
+
// enable content-based deduplication
|
|
82
|
+
contentBasedDeduplication: true,
|
|
83
|
+
// label for filtering logs, dlq, cancellation
|
|
84
|
+
label: "order-webhook",
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Batch Publishing
|
|
89
|
+
|
|
90
|
+
Publish multiple messages in a single request:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const results = await client.batchJSON([
|
|
94
|
+
{
|
|
95
|
+
url: "https://api.example.com/webhook-1",
|
|
96
|
+
body: { event: "first" },
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
url: "https://api.example.com/webhook-2",
|
|
100
|
+
body: { event: "second" },
|
|
101
|
+
delay: 60,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
urlGroup: "my-group",
|
|
105
|
+
body: { event: "third" },
|
|
106
|
+
},
|
|
107
|
+
]);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Each message in the batch can have different options.
|
|
111
|
+
|
|
112
|
+
## Response Types
|
|
113
|
+
|
|
114
|
+
### Single URL
|
|
115
|
+
|
|
116
|
+
When publishing to a `url`, response contains:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
{
|
|
120
|
+
messageId: string; // Unique message identifier
|
|
121
|
+
url: string; // Destination URL
|
|
122
|
+
deduplicated?: boolean; // true if message was deduplicated
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### URL Group
|
|
127
|
+
|
|
128
|
+
When publishing to a `urlGroup`, response contains an array:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
[
|
|
132
|
+
{
|
|
133
|
+
messageId: string;
|
|
134
|
+
url: string; // First endpoint URL
|
|
135
|
+
deduplicated?: boolean;
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
messageId: string;
|
|
139
|
+
url: string; // Second endpoint URL
|
|
140
|
+
deduplicated?: boolean;
|
|
141
|
+
},
|
|
142
|
+
// ... one per endpoint
|
|
143
|
+
]
|
|
144
|
+
```
|