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
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Rate Limiting
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Control request rates to prevent abuse and ensure fair resource usage. Use counters with TTL for simple rate limiting, or @upstash/ratelimit for production.
|
|
6
|
+
|
|
7
|
+
## Good For
|
|
8
|
+
|
|
9
|
+
- API rate limiting (requests per user/IP)
|
|
10
|
+
- Preventing brute force attacks
|
|
11
|
+
- Throttling expensive operations
|
|
12
|
+
- Fair resource allocation
|
|
13
|
+
|
|
14
|
+
## Limitations
|
|
15
|
+
|
|
16
|
+
- Simple counters can be imprecise at window boundaries
|
|
17
|
+
- Distributed rate limiting requires careful coordination
|
|
18
|
+
- Use @upstash/ratelimit for production (supports multiple algorithms)
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Redis } from "@upstash/redis";
|
|
24
|
+
|
|
25
|
+
const redis = Redis.fromEnv();
|
|
26
|
+
|
|
27
|
+
// Simple fixed-window rate limiter
|
|
28
|
+
async function simpleRateLimit(userId: string, limit: number = 10, window: number = 60) {
|
|
29
|
+
const key = `ratelimit:${userId}`;
|
|
30
|
+
|
|
31
|
+
const count = await redis.incr(key);
|
|
32
|
+
|
|
33
|
+
if (count === 1) {
|
|
34
|
+
await redis.expire(key, window);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
allowed: count <= limit,
|
|
39
|
+
remaining: Math.max(0, limit - count),
|
|
40
|
+
reset: window,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Usage
|
|
45
|
+
const result = await simpleRateLimit("user:123", 10, 60);
|
|
46
|
+
|
|
47
|
+
if (!result.allowed) {
|
|
48
|
+
throw new Error("Rate limit exceeded");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Sliding window rate limiter using sorted set
|
|
52
|
+
async function slidingWindowRateLimit(userId: string, limit: number = 10, window: number = 60) {
|
|
53
|
+
const key = `ratelimit:sliding:${userId}`;
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const windowStart = now - window * 1000;
|
|
56
|
+
|
|
57
|
+
// Remove old entries
|
|
58
|
+
await redis.zremrangebyscore(key, 0, windowStart);
|
|
59
|
+
|
|
60
|
+
// Count requests in window
|
|
61
|
+
const count = await redis.zcard(key);
|
|
62
|
+
|
|
63
|
+
if (count >= limit) {
|
|
64
|
+
return { allowed: false, remaining: 0 };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add current request
|
|
68
|
+
await redis.zadd(key, { score: now, member: `${now}:${Math.random()}` });
|
|
69
|
+
await redis.expire(key, window * 2); // Cleanup
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
allowed: true,
|
|
73
|
+
remaining: limit - count - 1,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Token bucket using Lua script
|
|
78
|
+
const tokenBucketScript = `
|
|
79
|
+
local key = KEYS[1]
|
|
80
|
+
local capacity = tonumber(ARGV[1])
|
|
81
|
+
local rate = tonumber(ARGV[2])
|
|
82
|
+
local now = tonumber(ARGV[3])
|
|
83
|
+
|
|
84
|
+
local bucket = redis.call('HMGET', key, 'tokens', 'last_refill')
|
|
85
|
+
local tokens = tonumber(bucket[1] or capacity)
|
|
86
|
+
local last_refill = tonumber(bucket[2] or now)
|
|
87
|
+
|
|
88
|
+
-- Refill tokens based on time elapsed
|
|
89
|
+
local elapsed = now - last_refill
|
|
90
|
+
local refill = math.floor(elapsed * rate)
|
|
91
|
+
tokens = math.min(capacity, tokens + refill)
|
|
92
|
+
|
|
93
|
+
if tokens >= 1 then
|
|
94
|
+
tokens = tokens - 1
|
|
95
|
+
redis.call('HMSET', key, 'tokens', tokens, 'last_refill', now)
|
|
96
|
+
redis.call('EXPIRE', key, 3600)
|
|
97
|
+
return 1
|
|
98
|
+
else
|
|
99
|
+
return 0
|
|
100
|
+
end
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
async function tokenBucketRateLimit(userId: string, capacity: number = 10, rate: number = 1) {
|
|
104
|
+
const key = `ratelimit:bucket:${userId}`;
|
|
105
|
+
const now = Date.now() / 1000;
|
|
106
|
+
|
|
107
|
+
const allowed = await redis.eval<number>(tokenBucketScript, [key], [capacity, rate, now]);
|
|
108
|
+
|
|
109
|
+
return { allowed: allowed === 1 };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Production: Use @upstash/ratelimit
|
|
113
|
+
// npm install @upstash/ratelimit
|
|
114
|
+
import { Ratelimit } from "@upstash/ratelimit";
|
|
115
|
+
|
|
116
|
+
// Fixed window
|
|
117
|
+
const fixedWindowLimiter = new Ratelimit({
|
|
118
|
+
redis,
|
|
119
|
+
limiter: Ratelimit.fixedWindow(10, "60 s"),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Sliding window
|
|
123
|
+
const slidingWindowLimiter = new Ratelimit({
|
|
124
|
+
redis,
|
|
125
|
+
limiter: Ratelimit.slidingWindow(10, "60 s"),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Token bucket
|
|
129
|
+
const tokenBucketLimiter = new Ratelimit({
|
|
130
|
+
redis,
|
|
131
|
+
limiter: Ratelimit.tokenBucket(10, "1 s", 10),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Usage
|
|
135
|
+
async function handleRequest(userId: string) {
|
|
136
|
+
const { success, limit, remaining, reset } = await fixedWindowLimiter.limit(userId);
|
|
137
|
+
|
|
138
|
+
if (!success) {
|
|
139
|
+
return {
|
|
140
|
+
error: "Rate limit exceeded",
|
|
141
|
+
limit,
|
|
142
|
+
remaining,
|
|
143
|
+
reset,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Process request
|
|
148
|
+
return { data: "Success", remaining };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Multi-tier rate limiting
|
|
152
|
+
async function multiTierRateLimit(userId: string, tier: "free" | "pro") {
|
|
153
|
+
const limits = {
|
|
154
|
+
free: { requests: 100, window: 3600 },
|
|
155
|
+
pro: { requests: 1000, window: 3600 },
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const config = limits[tier];
|
|
159
|
+
return await simpleRateLimit(userId, config.requests, config.window);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Per-endpoint rate limiting
|
|
163
|
+
async function endpointRateLimit(userId: string, endpoint: string) {
|
|
164
|
+
const key = `${userId}:${endpoint}`;
|
|
165
|
+
return await simpleRateLimit(key, 10, 60);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// IP-based rate limiting
|
|
169
|
+
async function ipRateLimit(ip: string) {
|
|
170
|
+
return await simpleRateLimit(`ip:${ip}`, 100, 60);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Combined rate limiting (both user and IP)
|
|
174
|
+
async function combinedRateLimit(userId: string, ip: string) {
|
|
175
|
+
const [userLimit, ipLimit] = await Promise.all([
|
|
176
|
+
simpleRateLimit(`user:${userId}`, 100, 60),
|
|
177
|
+
simpleRateLimit(`ip:${ip}`, 1000, 60),
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
allowed: userLimit.allowed && ipLimit.allowed,
|
|
182
|
+
limits: { user: userLimit, ip: ipLimit },
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Recommendation**: For production, use `@upstash/ratelimit` which provides battle-tested algorithms, analytics, and better accuracy.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Session Management
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Store user sessions in Redis with automatic serialization and TTL-based expiration. Sessions contain user state, authentication data, and preferences.
|
|
6
|
+
|
|
7
|
+
## Good For
|
|
8
|
+
|
|
9
|
+
- User authentication sessions
|
|
10
|
+
- Shopping carts
|
|
11
|
+
- Temporary user state
|
|
12
|
+
- Multi-page form data
|
|
13
|
+
- "Remember me" tokens
|
|
14
|
+
|
|
15
|
+
## Limitations
|
|
16
|
+
|
|
17
|
+
- Sessions expire after TTL (must be renewed)
|
|
18
|
+
- Large session objects consume memory
|
|
19
|
+
- Shared sessions require coordination across devices
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { Redis } from "@upstash/redis";
|
|
25
|
+
|
|
26
|
+
const redis = Redis.fromEnv();
|
|
27
|
+
|
|
28
|
+
// Create session with 1-hour expiration
|
|
29
|
+
async function createSession(userId: string, userData: any) {
|
|
30
|
+
const sessionId = crypto.randomUUID();
|
|
31
|
+
|
|
32
|
+
await redis.set(
|
|
33
|
+
`session:${sessionId}`,
|
|
34
|
+
{
|
|
35
|
+
userId,
|
|
36
|
+
...userData,
|
|
37
|
+
createdAt: Date.now(),
|
|
38
|
+
},
|
|
39
|
+
{ ex: 3600 } // 1 hour TTL
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return sessionId;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Get session
|
|
46
|
+
async function getSession(sessionId: string) {
|
|
47
|
+
const session = await redis.get<any>(`session:${sessionId}`);
|
|
48
|
+
return session;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Update session and refresh TTL
|
|
52
|
+
async function updateSession(sessionId: string, updates: any) {
|
|
53
|
+
const current = await redis.get<any>(`session:${sessionId}`);
|
|
54
|
+
|
|
55
|
+
if (!current) {
|
|
56
|
+
throw new Error("Session not found");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await redis.set(
|
|
60
|
+
`session:${sessionId}`,
|
|
61
|
+
{ ...current, ...updates, updatedAt: Date.now() },
|
|
62
|
+
{ ex: 3600 } // Reset TTL
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Extend session (refresh TTL without updating data)
|
|
67
|
+
async function extendSession(sessionId: string) {
|
|
68
|
+
await redis.expire(`session:${sessionId}`, 3600);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Delete session (logout)
|
|
72
|
+
async function deleteSession(sessionId: string) {
|
|
73
|
+
await redis.del(`session:${sessionId}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// User login flow
|
|
77
|
+
async function login(email: string, password: string) {
|
|
78
|
+
// Verify credentials (not shown)
|
|
79
|
+
const user = await verifyCredentials(email, password);
|
|
80
|
+
|
|
81
|
+
if (!user) {
|
|
82
|
+
throw new Error("Invalid credentials");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Create session
|
|
86
|
+
const sessionId = await createSession(user.id, {
|
|
87
|
+
email: user.email,
|
|
88
|
+
name: user.name,
|
|
89
|
+
role: user.role,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return sessionId;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// User logout
|
|
96
|
+
async function logout(sessionId: string) {
|
|
97
|
+
await deleteSession(sessionId);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Session with sliding expiration (extends on each access)
|
|
101
|
+
async function getSessionWithSliding(sessionId: string) {
|
|
102
|
+
const session = await redis.get<any>(`session:${sessionId}`);
|
|
103
|
+
|
|
104
|
+
if (session) {
|
|
105
|
+
// Extend session on access
|
|
106
|
+
await redis.expire(`session:${sessionId}`, 3600);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return session;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Shopping cart session
|
|
113
|
+
async function addToCartSession(sessionId: string, item: any) {
|
|
114
|
+
const key = `cart:${sessionId}`;
|
|
115
|
+
|
|
116
|
+
const cart = (await redis.get<any[]>(key)) || [];
|
|
117
|
+
cart.push(item);
|
|
118
|
+
|
|
119
|
+
await redis.set(key, cart, { ex: 86400 }); // 24 hour cart
|
|
120
|
+
|
|
121
|
+
return cart;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Multi-device sessions (track all user sessions)
|
|
125
|
+
async function createMultiDeviceSession(userId: string, deviceInfo: any) {
|
|
126
|
+
const sessionId = crypto.randomUUID();
|
|
127
|
+
|
|
128
|
+
// Store session
|
|
129
|
+
await redis.set(
|
|
130
|
+
`session:${sessionId}`,
|
|
131
|
+
{ userId, ...deviceInfo, createdAt: Date.now() },
|
|
132
|
+
{ ex: 3600 }
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Track in user's session list
|
|
136
|
+
await redis.sadd(`user:${userId}:sessions`, sessionId);
|
|
137
|
+
|
|
138
|
+
return sessionId;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Logout from all devices
|
|
142
|
+
async function logoutAllDevices(userId: string) {
|
|
143
|
+
const sessions = await redis.smembers(`user:${userId}:sessions`);
|
|
144
|
+
|
|
145
|
+
// Delete all sessions
|
|
146
|
+
const pipeline = redis.pipeline();
|
|
147
|
+
sessions.forEach((sessionId) => {
|
|
148
|
+
pipeline.del(`session:${sessionId}`);
|
|
149
|
+
});
|
|
150
|
+
pipeline.del(`user:${userId}:sessions`);
|
|
151
|
+
|
|
152
|
+
await pipeline.exec();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// "Remember me" token (long-lived)
|
|
156
|
+
async function createRememberMeToken(userId: string) {
|
|
157
|
+
const token = crypto.randomUUID();
|
|
158
|
+
|
|
159
|
+
await redis.set(
|
|
160
|
+
`remember:${token}`,
|
|
161
|
+
{ userId, createdAt: Date.now() },
|
|
162
|
+
{ ex: 2592000 } // 30 days
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
return token;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function loginWithRememberMe(token: string) {
|
|
169
|
+
const data = await redis.get<any>(`remember:${token}`);
|
|
170
|
+
|
|
171
|
+
if (!data) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Create new session
|
|
176
|
+
const sessionId = await createSession(data.userId, {});
|
|
177
|
+
|
|
178
|
+
return sessionId;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Session with user activity tracking
|
|
182
|
+
async function trackActivity(sessionId: string, action: string) {
|
|
183
|
+
const session = await redis.get<any>(`session:${sessionId}`);
|
|
184
|
+
|
|
185
|
+
if (!session) return;
|
|
186
|
+
|
|
187
|
+
session.lastActivity = {
|
|
188
|
+
action,
|
|
189
|
+
timestamp: Date.now(),
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
await redis.set(`session:${sessionId}`, session, { ex: 3600 });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Clean up expired sessions manually (if needed)
|
|
196
|
+
async function cleanupExpiredSessions(userId: string) {
|
|
197
|
+
const sessions = await redis.smembers(`user:${userId}:sessions`);
|
|
198
|
+
|
|
199
|
+
const pipeline = redis.pipeline();
|
|
200
|
+
sessions.forEach((sessionId) => {
|
|
201
|
+
pipeline.exists(`session:${sessionId}`);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const results = await pipeline.exec();
|
|
205
|
+
|
|
206
|
+
// Remove invalid sessions from set
|
|
207
|
+
const invalid = sessions.filter((_, i) => results[i] === 0);
|
|
208
|
+
if (invalid.length > 0) {
|
|
209
|
+
await redis.srem(`user:${userId}:sessions`, ...invalid);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Helper placeholder
|
|
214
|
+
async function verifyCredentials(email: string, password: string) {
|
|
215
|
+
return { id: "123", email, name: "User", role: "user" };
|
|
216
|
+
}
|
|
217
|
+
```
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Batching Operations
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Batch multiple Redis operations into single commands to reduce network round trips. Use MGET/MSET for strings, HMGET/HMSET for hashes, pipelines for mixed operations.
|
|
6
|
+
|
|
7
|
+
## Good For
|
|
8
|
+
|
|
9
|
+
- Fetching multiple keys at once
|
|
10
|
+
- Bulk data loading
|
|
11
|
+
- Reducing latency in high-latency networks
|
|
12
|
+
- Operations on related data
|
|
13
|
+
|
|
14
|
+
## Limitations
|
|
15
|
+
|
|
16
|
+
- Batch operations are atomic per command, not across commands
|
|
17
|
+
- Some commands don't have batch equivalents
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { Redis } from "@upstash/redis";
|
|
23
|
+
|
|
24
|
+
const redis = Redis.fromEnv();
|
|
25
|
+
|
|
26
|
+
// MGET - batch get multiple keys
|
|
27
|
+
const [user1, user2, user3] = await redis.mget<any[]>("user:1", "user:2", "user:3");
|
|
28
|
+
|
|
29
|
+
// MSET - batch set multiple keys
|
|
30
|
+
await redis.mset({
|
|
31
|
+
"user:1": { name: "Alice", age: 30 },
|
|
32
|
+
"user:2": { name: "Bob", age: 25 },
|
|
33
|
+
"user:3": { name: "Charlie", age: 35 },
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Compare: individual operations (3 round trips)
|
|
37
|
+
const u1 = await redis.get("user:1");
|
|
38
|
+
const u2 = await redis.get("user:2");
|
|
39
|
+
const u3 = await redis.get("user:3");
|
|
40
|
+
|
|
41
|
+
// vs batch operation (1 round trip)
|
|
42
|
+
const users = await redis.mget("user:1", "user:2", "user:3");
|
|
43
|
+
|
|
44
|
+
// HMGET - batch get hash fields
|
|
45
|
+
const [name, email] = await redis.hmget("user:123", "name", "email");
|
|
46
|
+
|
|
47
|
+
// HMSET - batch set hash fields
|
|
48
|
+
await redis.hset("user:123", {
|
|
49
|
+
name: "Alice",
|
|
50
|
+
email: "alice@example.com",
|
|
51
|
+
age: 30,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Batch with mixed operations - use pipeline
|
|
55
|
+
const pipeline = redis.pipeline();
|
|
56
|
+
userIds.forEach((id) => {
|
|
57
|
+
pipeline.get(`user:${id}`);
|
|
58
|
+
pipeline.hgetall(`user:${id}:profile`);
|
|
59
|
+
pipeline.zrank("leaderboard", id);
|
|
60
|
+
});
|
|
61
|
+
const results = await pipeline.exec();
|
|
62
|
+
|
|
63
|
+
// Batch delete
|
|
64
|
+
await redis.del("key1", "key2", "key3", "key4");
|
|
65
|
+
|
|
66
|
+
// Batch SADD
|
|
67
|
+
await redis.sadd("tags", "redis", "nodejs", "typescript", "upstash");
|
|
68
|
+
|
|
69
|
+
// Batch with Promise.all (for independent operations)
|
|
70
|
+
const [userCount, postCount, commentCount] = await Promise.all([
|
|
71
|
+
redis.get("count:users"),
|
|
72
|
+
redis.get("count:posts"),
|
|
73
|
+
redis.get("count:comments"),
|
|
74
|
+
]);
|
|
75
|
+
// Note: With auto-pipelining enabled, these batch automatically
|
|
76
|
+
```
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Data Serialization and Deserialization
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Automatic serialization preserves JavaScript types across Redis operations. Numbers stay numbers, objects stay objects, arrays stay arrays.
|
|
6
|
+
|
|
7
|
+
## Good For
|
|
8
|
+
|
|
9
|
+
- Storing any JavaScript value without JSON.stringify/parse
|
|
10
|
+
- Type preservation across GET/SET
|
|
11
|
+
- Cleaner code (no manual serialization)
|
|
12
|
+
- Storing numbers, booleans, objects, arrays, null
|
|
13
|
+
|
|
14
|
+
## Limitations
|
|
15
|
+
|
|
16
|
+
- undefined, functions, symbols cannot be serialized
|
|
17
|
+
- Date objects serialize as ISO strings
|
|
18
|
+
- Class instances lose their prototype
|
|
19
|
+
- Map and Set serialize to `{}` (their contents are lost) — convert to a plain object/array first
|
|
20
|
+
- Binary data requires special handling
|
|
21
|
+
|
|
22
|
+
## Examples
|
|
23
|
+
|
|
24
|
+
### Basic Type Preservation
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { Redis } from "@upstash/redis";
|
|
28
|
+
|
|
29
|
+
const redis = Redis.fromEnv();
|
|
30
|
+
|
|
31
|
+
// Numbers preserved
|
|
32
|
+
await redis.set("age", 42);
|
|
33
|
+
const age = await redis.get("age");
|
|
34
|
+
console.log(typeof age); // "number"
|
|
35
|
+
console.log(age === 42); // true
|
|
36
|
+
|
|
37
|
+
// Compare with other SDKs
|
|
38
|
+
// ioredis: returns "42" (string)
|
|
39
|
+
// @upstash/redis: returns 42 (number)
|
|
40
|
+
|
|
41
|
+
// Booleans preserved
|
|
42
|
+
await redis.set("active", true);
|
|
43
|
+
const active = await redis.get("active");
|
|
44
|
+
console.log(typeof active); // "boolean"
|
|
45
|
+
|
|
46
|
+
// Objects preserved
|
|
47
|
+
await redis.set("user", { name: "Alice", age: 30 });
|
|
48
|
+
const user = await redis.get("user");
|
|
49
|
+
console.log(user.name); // "Alice"
|
|
50
|
+
console.log(user.age); // 30 (number, not string)
|
|
51
|
+
|
|
52
|
+
// Arrays preserved
|
|
53
|
+
await redis.set("scores", [100, 200, 300]);
|
|
54
|
+
const scores = await redis.get<number[]>("scores");
|
|
55
|
+
console.log(scores[0]); // 100 (number)
|
|
56
|
+
|
|
57
|
+
// Nested structures preserved
|
|
58
|
+
await redis.set("data", {
|
|
59
|
+
user: { name: "Alice", age: 30 },
|
|
60
|
+
scores: [100, 200, 300],
|
|
61
|
+
active: true,
|
|
62
|
+
count: 42,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const data = await redis.get<any>("data");
|
|
66
|
+
console.log(typeof age); // "number"
|
|
67
|
+
console.log(Array.isArray(data.scores)); // true
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Complex Types
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { Redis } from "@upstash/redis";
|
|
74
|
+
|
|
75
|
+
const redis = Redis.fromEnv();
|
|
76
|
+
|
|
77
|
+
// Date objects become ISO strings
|
|
78
|
+
await redis.set("created", new Date());
|
|
79
|
+
const created = await redis.get<string>("created");
|
|
80
|
+
console.log(typeof created); // "string"
|
|
81
|
+
console.log(created); // "2024-01-01T00:00:00.000Z"
|
|
82
|
+
|
|
83
|
+
// Convert back to Date
|
|
84
|
+
const createdDate = new Date(created);
|
|
85
|
+
|
|
86
|
+
// Class instances lose prototype
|
|
87
|
+
class User {
|
|
88
|
+
constructor(public name: string) {}
|
|
89
|
+
greet() {
|
|
90
|
+
return `Hello, ${this.name}`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const alice = new User("Alice");
|
|
95
|
+
await redis.set("instance", alice);
|
|
96
|
+
|
|
97
|
+
const retrieved = await redis.get<any>("instance");
|
|
98
|
+
console.log(retrieved.name); // "Alice"
|
|
99
|
+
console.log(retrieved.greet); // undefined (method lost)
|
|
100
|
+
|
|
101
|
+
// Solution: serialize/deserialize manually
|
|
102
|
+
class SerializableUser {
|
|
103
|
+
constructor(public name: string) {}
|
|
104
|
+
|
|
105
|
+
static toRedis(user: SerializableUser) {
|
|
106
|
+
return { name: user.name };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static fromRedis(data: any) {
|
|
110
|
+
return new SerializableUser(data.name);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
await redis.set("ser_user", SerializableUser.toRedis(alice));
|
|
115
|
+
const serRetrieved = SerializableUser.fromRedis(await redis.get("ser_user"));
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Disabling Auto-Serialization
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { Redis } from "@upstash/redis";
|
|
122
|
+
|
|
123
|
+
// Disable auto-serialization if needed
|
|
124
|
+
const redis = new Redis({
|
|
125
|
+
url: process.env.UPSTASH_REDIS_REST_URL!,
|
|
126
|
+
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
|
|
127
|
+
automaticDeserialization: false,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
await redis.set("key", JSON.stringify({ value: 42 }));
|
|
131
|
+
const raw = await redis.get("key");
|
|
132
|
+
console.log(typeof raw); // "string"
|
|
133
|
+
const parsed = JSON.parse(raw as string);
|
|
134
|
+
```
|