@jgamaraalv/ts-dev-kit 1.0.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/.claude-plugin/marketplace.json +24 -0
- package/.claude-plugin/plugin.json +24 -0
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +128 -0
- package/agents/accessibility-pro.md +139 -0
- package/agents/api-builder.md +110 -0
- package/agents/code-reviewer.md +190 -0
- package/agents/database-expert.md +138 -0
- package/agents/debugger.md +241 -0
- package/agents/docker-expert.md +51 -0
- package/agents/multi-agent-coordinator.md +378 -0
- package/agents/nextjs-expert.md +136 -0
- package/agents/performance-engineer.md +138 -0
- package/agents/playwright-expert.md +126 -0
- package/agents/react-specialist.md +97 -0
- package/agents/security-scanner.md +105 -0
- package/agents/test-generator.md +221 -0
- package/agents/typescript-pro.md +253 -0
- package/agents/ux-optimizer.md +93 -0
- package/docs/rules/orchestration.md.template +126 -0
- package/package.json +28 -0
- package/skills/bullmq/SKILL.md +225 -0
- package/skills/bullmq/references/flows-and-schedulers.md +186 -0
- package/skills/bullmq/references/job-types-and-options.md +163 -0
- package/skills/bullmq/references/patterns.md +273 -0
- package/skills/bullmq/references/production.md +308 -0
- package/skills/composition-patterns/SKILL.md +58 -0
- package/skills/composition-patterns/references/architecture-avoid-boolean-props.md +87 -0
- package/skills/composition-patterns/references/architecture-compound-components.md +107 -0
- package/skills/composition-patterns/references/patterns-children-over-render-props.md +77 -0
- package/skills/composition-patterns/references/patterns-explicit-variants.md +87 -0
- package/skills/composition-patterns/references/react19-no-forwardref.md +37 -0
- package/skills/composition-patterns/references/state-context-interface.md +194 -0
- package/skills/composition-patterns/references/state-decouple-implementation.md +96 -0
- package/skills/composition-patterns/references/state-lift-state.md +126 -0
- package/skills/conventional-commits/SKILL.md +148 -0
- package/skills/docker/SKILL.md +55 -0
- package/skills/docker/references/compose-configs.md +95 -0
- package/skills/docker/references/monorepo-dockerfile.md +111 -0
- package/skills/drizzle-pg/SKILL.md +202 -0
- package/skills/drizzle-pg/references/advanced.md +299 -0
- package/skills/drizzle-pg/references/migrations.md +214 -0
- package/skills/drizzle-pg/references/queries.md +321 -0
- package/skills/drizzle-pg/references/relations.md +272 -0
- package/skills/drizzle-pg/references/schema-pg.md +256 -0
- package/skills/drizzle-pg/references/sql-operator.md +215 -0
- package/skills/fastify-best-practices/SKILL.md +143 -0
- package/skills/fastify-best-practices/references/hooks-and-lifecycle.md +122 -0
- package/skills/fastify-best-practices/references/plugins-and-encapsulation.md +137 -0
- package/skills/fastify-best-practices/references/request-reply-errors.md +189 -0
- package/skills/fastify-best-practices/references/routes-and-handlers.md +134 -0
- package/skills/fastify-best-practices/references/server-and-options.md +127 -0
- package/skills/fastify-best-practices/references/typescript-and-logging.md +223 -0
- package/skills/fastify-best-practices/references/validation-and-serialization.md +190 -0
- package/skills/ioredis/SKILL.md +51 -0
- package/skills/ioredis/references/advanced-patterns.md +312 -0
- package/skills/ioredis/references/cluster-sentinel.md +280 -0
- package/skills/ioredis/references/connection-options.md +187 -0
- package/skills/ioredis/references/core-api.md +179 -0
- package/skills/nextjs-best-practices/SKILL.md +194 -0
- package/skills/nextjs-best-practices/references/async-patterns.md +84 -0
- package/skills/nextjs-best-practices/references/bundling.md +192 -0
- package/skills/nextjs-best-practices/references/data-patterns.md +310 -0
- package/skills/nextjs-best-practices/references/debug-tricks.md +127 -0
- package/skills/nextjs-best-practices/references/directives.md +74 -0
- package/skills/nextjs-best-practices/references/error-handling.md +237 -0
- package/skills/nextjs-best-practices/references/file-conventions.md +152 -0
- package/skills/nextjs-best-practices/references/font.md +175 -0
- package/skills/nextjs-best-practices/references/functions.md +116 -0
- package/skills/nextjs-best-practices/references/hydration-error.md +86 -0
- package/skills/nextjs-best-practices/references/image.md +184 -0
- package/skills/nextjs-best-practices/references/metadata.md +305 -0
- package/skills/nextjs-best-practices/references/parallel-routes.md +299 -0
- package/skills/nextjs-best-practices/references/route-handlers.md +154 -0
- package/skills/nextjs-best-practices/references/rsc-boundaries.md +168 -0
- package/skills/nextjs-best-practices/references/runtime-selection.md +40 -0
- package/skills/nextjs-best-practices/references/scripts.md +148 -0
- package/skills/nextjs-best-practices/references/self-hosting.md +210 -0
- package/skills/nextjs-best-practices/references/suspense-boundaries.md +67 -0
- package/skills/owasp-security-review/SKILL.md +98 -0
- package/skills/owasp-security-review/references/a01-broken-access-control.md +78 -0
- package/skills/owasp-security-review/references/a02-security-misconfiguration.md +81 -0
- package/skills/owasp-security-review/references/a03-supply-chain-failures.md +65 -0
- package/skills/owasp-security-review/references/a04-cryptographic-failures.md +82 -0
- package/skills/owasp-security-review/references/a05-injection.md +106 -0
- package/skills/owasp-security-review/references/a06-insecure-design.md +76 -0
- package/skills/owasp-security-review/references/a07-authentication-failures.md +83 -0
- package/skills/owasp-security-review/references/a08-integrity-failures.md +72 -0
- package/skills/owasp-security-review/references/a09-logging-alerting-failures.md +76 -0
- package/skills/owasp-security-review/references/a10-exceptional-conditions.md +131 -0
- package/skills/postgresql/SKILL.md +50 -0
- package/skills/postgresql/references/ddl-schema.md +300 -0
- package/skills/postgresql/references/indexes.md +257 -0
- package/skills/postgresql/references/jsonb.md +261 -0
- package/skills/postgresql/references/performance.md +291 -0
- package/skills/postgresql/references/psql-cli.md +153 -0
- package/skills/postgresql/references/queries.md +287 -0
- package/skills/postgresql/references/transactions.md +280 -0
- package/skills/react-best-practices/SKILL.md +110 -0
- package/skills/react-best-practices/references/advanced-patterns.md +91 -0
- package/skills/react-best-practices/references/async-patterns.md +233 -0
- package/skills/react-best-practices/references/bundle-optimization.md +201 -0
- package/skills/react-best-practices/references/client-patterns.md +178 -0
- package/skills/react-best-practices/references/js-performance.md +210 -0
- package/skills/react-best-practices/references/rendering-performance.md +209 -0
- package/skills/react-best-practices/references/rerender-optimization.md +316 -0
- package/skills/react-best-practices/references/server-performance.md +274 -0
- package/skills/service-worker/SKILL.md +195 -0
- package/skills/service-worker/references/api-reference.md +114 -0
- package/skills/service-worker/references/caching-strategies.md +202 -0
- package/skills/service-worker/references/push-and-sync.md +261 -0
- package/skills/typescript-conventions/SKILL.md +51 -0
- package/skills/ui-ux-guidelines/SKILL.md +105 -0
- package/skills/ui-ux-guidelines/references/accessibility-and-interaction.md +74 -0
- package/skills/ui-ux-guidelines/references/forms-content-checklist.md +126 -0
- package/skills/ui-ux-guidelines/references/layout-typography-animation.md +95 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# ioredis Connection Options Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [RedisOptions type alias](#redisoptions-type-alias)
|
|
6
|
+
- [CommonRedisOptions](#commonredisoptions)
|
|
7
|
+
- [StandaloneConnectionOptions](#standaloneconnectionoptions)
|
|
8
|
+
- [SentinelConnectionOptions](#sentinelconnectionoptions-summary)
|
|
9
|
+
- [retryStrategy patterns](#retrystrategy-patterns)
|
|
10
|
+
- [reconnectOnError patterns](#reconnectonerror-patterns)
|
|
11
|
+
- [TLS configuration](#tls-configuration)
|
|
12
|
+
- [Blocking command timeout](#blocking-command-timeout)
|
|
13
|
+
- [Connection lifecycle](#connection-lifecycle)
|
|
14
|
+
|
|
15
|
+
## RedisOptions type alias
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
type RedisOptions = CommonRedisOptions & SentinelConnectionOptions & StandaloneConnectionOptions;
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
All three interfaces are merged. Standalone options include `host`, `port`, `path`, `tls`, and `disconnectTimeout`.
|
|
22
|
+
|
|
23
|
+
## CommonRedisOptions
|
|
24
|
+
|
|
25
|
+
| Property | Type | Default | Description |
|
|
26
|
+
| ------------------------------- | --------------------------------------------------- | ------------------- | ----------------------------------------------------------------------- |
|
|
27
|
+
| `retryStrategy` | `(times: number) => number \| void` | exponential backoff | Return ms to wait before reconnect. Return `undefined` to stop |
|
|
28
|
+
| `commandTimeout` | `number` | `undefined` | Ms before "Command timed out" error |
|
|
29
|
+
| `blockingTimeout` | `number` | `undefined` | Client-side timeout for blocking commands (opt-in, disabled by default) |
|
|
30
|
+
| `blockingTimeoutGrace` | `number` | `100` | Grace period ms added to blocking timeouts |
|
|
31
|
+
| `socketTimeout` | `number` | `undefined` | Ms of socket inactivity before destroying |
|
|
32
|
+
| `keepAlive` | `number` | `0` | TCP keepalive initial delay in ms. `0` = disabled |
|
|
33
|
+
| `noDelay` | `boolean` | `true` | Disable Nagle's algorithm (TCP_NODELAY) |
|
|
34
|
+
| `connectionName` | `string` | `undefined` | CLIENT SETNAME value |
|
|
35
|
+
| `disableClientInfo` | `boolean` | `false` | Skip CLIENT SETINFO |
|
|
36
|
+
| `clientInfoTag` | `string` | `undefined` | Tag appended to library name in CLIENT SETINFO |
|
|
37
|
+
| `username` | `string` | `undefined` | AUTH username (Redis >= 6 ACL) |
|
|
38
|
+
| `password` | `string` | `undefined` | AUTH password |
|
|
39
|
+
| `db` | `number` | `0` | Database index |
|
|
40
|
+
| `autoResubscribe` | `boolean` | `true` | Re-subscribe channels on reconnect |
|
|
41
|
+
| `autoResendUnfulfilledCommands` | `boolean` | `true` | Resend pending blocking commands on reconnect |
|
|
42
|
+
| `reconnectOnError` | `(err: Error) => boolean \| 0 \| 1 \| 2` | `null` | See reconnectOnError patterns below |
|
|
43
|
+
| `readOnly` | `boolean` | `false` | Send READONLY after connect |
|
|
44
|
+
| `stringNumbers` | `boolean` | `false` | Return numbers as strings (safe for > 2^53) |
|
|
45
|
+
| `connectTimeout` | `number` | `10000` | Ms before killing socket during initial connection |
|
|
46
|
+
| `monitor` | `boolean` | `false` | Enter MONITOR mode on connect (internal use) |
|
|
47
|
+
| `maxRetriesPerRequest` | `number \| null` | `20` | Flush queue after N reconnects. `null` = wait forever |
|
|
48
|
+
| `maxLoadingRetryTime` | `number` | `10000` | Max ms to wait for server to finish loading |
|
|
49
|
+
| `enableAutoPipelining` | `boolean` | `false` | Auto-batch commands per event loop tick |
|
|
50
|
+
| `autoPipeliningIgnoredCommands` | `string[]` | `[]` | Commands excluded from auto-pipelining |
|
|
51
|
+
| `enableOfflineQueue` | `boolean` | `true` | Queue commands before "ready". `false` = error immediately |
|
|
52
|
+
| `enableReadyCheck` | `boolean` | `true` | Wait for INFO loading:0 before emitting "ready" |
|
|
53
|
+
| `lazyConnect` | `boolean` | `false` | Delay connection until first command or explicit `connect()` |
|
|
54
|
+
| `scripts` | `Record<string, { lua, numberOfKeys?, readOnly? }>` | `undefined` | Pre-define Lua commands |
|
|
55
|
+
| `keyPrefix` | `string` | `undefined` | Auto-prefix all keys |
|
|
56
|
+
| `showFriendlyErrorStack` | `boolean` | `false` | Better stacks in dev (perf cost — never in production) |
|
|
57
|
+
| `Connector` | `ConnectorConstructor` | `undefined` | Custom connector class |
|
|
58
|
+
|
|
59
|
+
## StandaloneConnectionOptions
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
type StandaloneConnectionOptions = Partial<TcpOptions & IpcOptions> & {
|
|
63
|
+
disconnectTimeout?: number;
|
|
64
|
+
tls?: ConnectionOptions;
|
|
65
|
+
};
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Includes Node.js `net.connect()` options:
|
|
69
|
+
|
|
70
|
+
- `host`: string (default `"127.0.0.1"`)
|
|
71
|
+
- `port`: number (default `6379`)
|
|
72
|
+
- `path`: string (Unix socket path, overrides host/port)
|
|
73
|
+
- `tls`: Node.js `tls.connect()` options
|
|
74
|
+
- `disconnectTimeout`: ms to wait for graceful disconnect
|
|
75
|
+
|
|
76
|
+
## SentinelConnectionOptions (summary)
|
|
77
|
+
|
|
78
|
+
See [cluster-sentinel.md](cluster-sentinel.md) for full Sentinel reference.
|
|
79
|
+
|
|
80
|
+
Key properties: `sentinels`, `name`, `role`, `sentinelPassword`, `sentinelUsername`, `sentinelTLS`, `enableTLSForSentinelMode`, `failoverDetector`, `sentinelRetryStrategy`, `sentinelReconnectStrategy`, `updateSentinels`, `natMap`, `preferredSlaves`, `sentinelMaxConnections`, `sentinelCommandTimeout`.
|
|
81
|
+
|
|
82
|
+
## retryStrategy patterns
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// Default: exponential backoff capped at 2s
|
|
86
|
+
retryStrategy(times) {
|
|
87
|
+
return Math.min(times * 50, 2000);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Stop after 10 attempts:
|
|
91
|
+
retryStrategy(times) {
|
|
92
|
+
if (times > 10) return undefined; // stop reconnecting
|
|
93
|
+
return Math.min(times * 100, 3000);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Immediate reconnect for first 3 attempts, then backoff:
|
|
97
|
+
retryStrategy(times) {
|
|
98
|
+
if (times <= 3) return 0;
|
|
99
|
+
return Math.min(times * 100, 5000);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Never reconnect (disable auto-reconnect):
|
|
103
|
+
retryStrategy() {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`times` is the number of reconnection attempts so far (starts at 1). When the function returns `undefined` or a non-number, ioredis stops reconnecting and emits the `end` event.
|
|
109
|
+
|
|
110
|
+
## reconnectOnError patterns
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
// Reconnect on READONLY (AWS ElastiCache failover):
|
|
114
|
+
reconnectOnError(err) {
|
|
115
|
+
if (err.message.includes("READONLY")) return true;
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Return values:
|
|
121
|
+
|
|
122
|
+
- `false` / `0` — do not reconnect
|
|
123
|
+
- `true` / `1` — reconnect
|
|
124
|
+
- `2` — reconnect AND resend the failed command after reconnection
|
|
125
|
+
|
|
126
|
+
Default is `null` (never reconnect on Redis errors).
|
|
127
|
+
|
|
128
|
+
## TLS configuration
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// Minimal (system CAs):
|
|
132
|
+
new Redis({ host: "redis.example.com", tls: {} });
|
|
133
|
+
|
|
134
|
+
// With CA certificate:
|
|
135
|
+
new Redis({
|
|
136
|
+
host: "redis.example.com",
|
|
137
|
+
tls: { ca: fs.readFileSync("ca.pem") },
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Via URL (rediss:// = TLS):
|
|
141
|
+
new Redis("rediss://redis.example.com:6380");
|
|
142
|
+
|
|
143
|
+
// TLS profiles (deprecated, will be removed in v6):
|
|
144
|
+
new Redis({ host: "localhost", tls: "RedisCloudFixed" });
|
|
145
|
+
new Redis({ host: "localhost", tls: "RedisCloudFlexible" });
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Blocking command timeout
|
|
149
|
+
|
|
150
|
+
Opt-in client-side protection for blocking commands (`blpop`, `brpop`, `xread`, etc.):
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const redis = new Redis({
|
|
154
|
+
blockingTimeout: 30000, // Enable — 30s safety net for "block forever" commands
|
|
155
|
+
blockingTimeoutGrace: 100, // Grace period added to finite timeouts (default: 100ms)
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- Disabled by default (`blockingTimeout` undefined, 0, or negative)
|
|
160
|
+
- For finite-timeout commands (e.g., `blpop("key", 5)`): deadline = command timeout + grace
|
|
161
|
+
- For infinite-timeout commands (`timeout = 0`): deadline = `blockingTimeout` value
|
|
162
|
+
- On timeout: resolves with `null` (same as Redis native timeout behavior)
|
|
163
|
+
|
|
164
|
+
## Connection lifecycle
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
[new Redis()] → connecting → connect → ready → (commands execute)
|
|
168
|
+
↑ ↓
|
|
169
|
+
└── reconnecting ← close → end (if retryStrategy returns undefined)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
- `connect`: TCP established
|
|
173
|
+
- `ready`: Server loaded and accepting commands
|
|
174
|
+
- `close` → `reconnecting`: Auto-reconnect cycle
|
|
175
|
+
- `end`: Final — no more reconnection attempts
|
|
176
|
+
|
|
177
|
+
Graceful shutdown:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
await redis.quit(); // Waits for pending replies, sends QUIT, closes
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Force shutdown:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
redis.disconnect(); // Immediate close, pending commands rejected
|
|
187
|
+
```
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# ioredis Core API Reference
|
|
2
|
+
|
|
3
|
+
## Quick Start
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
const redis = new Redis(); // localhost:6379
|
|
7
|
+
|
|
8
|
+
// All Redis commands are methods: redis.hset(), redis.zadd(), redis.xadd(), etc.
|
|
9
|
+
// Format: redis[commandInLowerCase](...args) — returns Promise
|
|
10
|
+
// Buffer variants: redis.getBuffer("key") returns Buffer instead of string
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Connection
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
new Redis(); // 127.0.0.1:6379
|
|
17
|
+
new Redis(6380); // 127.0.0.1:6380
|
|
18
|
+
new Redis(6379, "192.168.1.1"); // 192.168.1.1:6379
|
|
19
|
+
new Redis("/tmp/redis.sock"); // Unix socket
|
|
20
|
+
new Redis("redis://:password@host:6379/0"); // URL (redis:// or rediss://)
|
|
21
|
+
new Redis("redis://user:pass@host:6379/4"); // With username (Redis >= 6)
|
|
22
|
+
|
|
23
|
+
new Redis({
|
|
24
|
+
host: "127.0.0.1",
|
|
25
|
+
port: 6379,
|
|
26
|
+
username: "default", // Redis >= 6 ACL
|
|
27
|
+
password: "secret",
|
|
28
|
+
db: 0, // Database index (default: 0)
|
|
29
|
+
keyPrefix: "app:", // Auto-prefix all keys
|
|
30
|
+
lazyConnect: false, // true = don't connect until first command
|
|
31
|
+
enableReadyCheck: true, // Wait for server to finish loading
|
|
32
|
+
maxRetriesPerRequest: 20, // null = wait forever
|
|
33
|
+
connectTimeout: 10000, // ms
|
|
34
|
+
commandTimeout: undefined, // ms, throws "Command timed out"
|
|
35
|
+
keepAlive: 0, // ms, 0 = disabled
|
|
36
|
+
noDelay: true, // Nagle's algorithm
|
|
37
|
+
connectionName: "my-app", // CLIENT SETNAME
|
|
38
|
+
enableAutoPipelining: false,
|
|
39
|
+
retryStrategy(times) {
|
|
40
|
+
return Math.min(times * 50, 2000); // ms delay, or undefined to stop
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Full options reference**: See [connection-options.md](connection-options.md)
|
|
46
|
+
|
|
47
|
+
### TLS
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
new Redis({ host: "redis.example.com", tls: {} });
|
|
51
|
+
// or
|
|
52
|
+
new Redis("rediss://redis.example.com");
|
|
53
|
+
// or with CA:
|
|
54
|
+
new Redis({ host: "redis.example.com", tls: { ca: fs.readFileSync("cert.pem") } });
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Pipeline
|
|
58
|
+
|
|
59
|
+
Batch commands for 50-300% throughput improvement:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const pipeline = redis.pipeline();
|
|
63
|
+
pipeline.set("foo", "bar");
|
|
64
|
+
pipeline.get("foo");
|
|
65
|
+
const results = await pipeline.run();
|
|
66
|
+
// results === [[null, "OK"], [null, "bar"]]
|
|
67
|
+
// Each entry: [error, result]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Note:** The actual method to run a pipeline is `.exec()` — shown as `.run()` here for brevity. In ioredis, both pipelines and transactions use the `.exec()` method to send queued commands to Redis.
|
|
71
|
+
|
|
72
|
+
## Transaction (multi)
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const tx = redis.multi();
|
|
76
|
+
tx.set("foo", "bar");
|
|
77
|
+
tx.get("foo");
|
|
78
|
+
const results = await tx.exec();
|
|
79
|
+
// results === [[null, "OK"], [null, "bar"]]
|
|
80
|
+
|
|
81
|
+
// Without pipeline (sends immediately):
|
|
82
|
+
redis.multi({ pipeline: false });
|
|
83
|
+
redis.set("foo", "bar");
|
|
84
|
+
redis.get("foo");
|
|
85
|
+
await redis.exec();
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Pub/Sub
|
|
89
|
+
|
|
90
|
+
A subscribed connection cannot run other commands. Use separate instances:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const sub = new Redis();
|
|
94
|
+
const pub = new Redis();
|
|
95
|
+
|
|
96
|
+
await sub.subscribe("channel-1", "channel-2");
|
|
97
|
+
sub.on("message", (channel, message) => {
|
|
98
|
+
/* ... */
|
|
99
|
+
});
|
|
100
|
+
// Also: "messageBuffer" for binary data
|
|
101
|
+
|
|
102
|
+
pub.publish("channel-1", JSON.stringify({ data: 123 }));
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Lua Scripting
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
// Define at runtime:
|
|
109
|
+
redis.defineCommand("mycommand", {
|
|
110
|
+
numberOfKeys: 2,
|
|
111
|
+
lua: "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
|
|
112
|
+
});
|
|
113
|
+
const result = await redis.mycommand("k1", "k2", "a1", "a2");
|
|
114
|
+
|
|
115
|
+
// Define via constructor:
|
|
116
|
+
const redis = new Redis({
|
|
117
|
+
scripts: {
|
|
118
|
+
mycommand: {
|
|
119
|
+
numberOfKeys: 2,
|
|
120
|
+
lua: "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Dynamic key count — pass numberOfKeys as first arg:
|
|
126
|
+
redis.defineCommand("dynamicCmd", { lua: "return KEYS[1]" });
|
|
127
|
+
await redis.dynamicCmd(1, "mykey");
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Scan (Streaming)
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const stream = redis.scanStream({ match: "user:*", count: 100 });
|
|
134
|
+
stream.on("data", (keys) => {
|
|
135
|
+
// keys: string[], may contain duplicates
|
|
136
|
+
stream.pause();
|
|
137
|
+
processKeys(keys).then(() => stream.resume());
|
|
138
|
+
});
|
|
139
|
+
stream.on("end", () => console.log("done"));
|
|
140
|
+
|
|
141
|
+
// Hash/Set/ZSet variants:
|
|
142
|
+
redis.hscanStream("myhash", { match: "field:*" });
|
|
143
|
+
redis.sscanStream("myset", { match: "*" });
|
|
144
|
+
redis.zscanStream("myzset", { match: "*" });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Connection Events
|
|
148
|
+
|
|
149
|
+
| Event | Description |
|
|
150
|
+
| -------------- | --------------------------------------------------------------- |
|
|
151
|
+
| `connect` | TCP connection established |
|
|
152
|
+
| `ready` | Server ready for commands (after loading if `enableReadyCheck`) |
|
|
153
|
+
| `error` | Connection error (emitted silently — only if listener exists) |
|
|
154
|
+
| `close` | Connection closed |
|
|
155
|
+
| `reconnecting` | Reconnection scheduled, arg = delay in ms |
|
|
156
|
+
| `end` | No more reconnections will be made |
|
|
157
|
+
| `wait` | `lazyConnect` is set, waiting for first command |
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
redis.on("error", (err) => console.error("Redis error:", err));
|
|
161
|
+
redis.on("ready", () => console.log("Redis ready"));
|
|
162
|
+
|
|
163
|
+
// Check status programmatically:
|
|
164
|
+
redis.status; // "wait" | "reconnecting" | "connecting" | "connect" | "ready" | "close" | "end"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Key Methods
|
|
168
|
+
|
|
169
|
+
| Method | Description |
|
|
170
|
+
| ----------------------- | ------------------------------------------------------------ |
|
|
171
|
+
| `redis.connect()` | Connect manually (when `lazyConnect: true`). Returns Promise |
|
|
172
|
+
| `redis.disconnect()` | Force close. No reconnect. Pending commands rejected |
|
|
173
|
+
| `redis.quit()` | Graceful close. Waits for pending replies, then sends QUIT |
|
|
174
|
+
| `redis.duplicate()` | Create new instance with same options |
|
|
175
|
+
| `redis.pipeline()` | Create pipeline |
|
|
176
|
+
| `redis.multi()` | Create transaction |
|
|
177
|
+
| `redis.defineCommand()` | Register Lua script as command |
|
|
178
|
+
| `redis.monitor()` | Enter MONITOR mode (returns monitor instance) |
|
|
179
|
+
| `redis.scanStream()` | Create readable stream for SCAN |
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nextjs-best-practices
|
|
3
|
+
description: "Next.js App Router best practices — file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling. Use when writing, reviewing, or debugging Next.js App Router code."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Next.js Best Practices
|
|
7
|
+
|
|
8
|
+
Apply these rules when writing or reviewing Next.js code.
|
|
9
|
+
|
|
10
|
+
> **Note:** Next.js 16 renamed `middleware.ts` to `proxy.ts`. Verify `proxy.ts` support in your version; `middleware.ts` remains the stable API.
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
- [File Conventions](#file-conventions)
|
|
15
|
+
- [RSC Boundaries](#rsc-boundaries)
|
|
16
|
+
- [Async Patterns](#async-patterns)
|
|
17
|
+
- [Runtime Selection](#runtime-selection)
|
|
18
|
+
- [Directives](#directives)
|
|
19
|
+
- [Functions](#functions)
|
|
20
|
+
- [Error Handling](#error-handling)
|
|
21
|
+
- [Data Patterns](#data-patterns)
|
|
22
|
+
- [Route Handlers](#route-handlers)
|
|
23
|
+
- [Metadata & OG Images](#metadata--og-images)
|
|
24
|
+
- [Image Optimization](#image-optimization)
|
|
25
|
+
- [Font Optimization](#font-optimization)
|
|
26
|
+
- [Bundling](#bundling)
|
|
27
|
+
- [Scripts](#scripts)
|
|
28
|
+
- [Hydration Errors](#hydration-errors)
|
|
29
|
+
- [Suspense Boundaries](#suspense-boundaries)
|
|
30
|
+
- [Parallel & Intercepting Routes](#parallel--intercepting-routes)
|
|
31
|
+
- [Self-Hosting](#self-hosting)
|
|
32
|
+
- [Debug Tricks](#debug-tricks)
|
|
33
|
+
|
|
34
|
+
## File Conventions
|
|
35
|
+
|
|
36
|
+
See [file-conventions.md](references/file-conventions.md) for:
|
|
37
|
+
|
|
38
|
+
- Project structure and special files
|
|
39
|
+
- Route segments (dynamic, catch-all, groups)
|
|
40
|
+
- Parallel and intercepting routes
|
|
41
|
+
- Middleware rename in v16 (middleware → proxy)
|
|
42
|
+
|
|
43
|
+
## RSC Boundaries
|
|
44
|
+
|
|
45
|
+
Detect invalid React Server Component patterns.
|
|
46
|
+
|
|
47
|
+
See [rsc-boundaries.md](references/rsc-boundaries.md) for:
|
|
48
|
+
|
|
49
|
+
- Async client component detection (invalid)
|
|
50
|
+
- Non-serializable props detection
|
|
51
|
+
- Server Action exceptions
|
|
52
|
+
|
|
53
|
+
## Async Patterns
|
|
54
|
+
|
|
55
|
+
Next.js 16.1.6+ async API changes.
|
|
56
|
+
|
|
57
|
+
See [async-patterns.md](references/async-patterns.md) for:
|
|
58
|
+
|
|
59
|
+
- Async `params` and `searchParams`
|
|
60
|
+
- Async `cookies()` and `headers()`
|
|
61
|
+
- Migration codemod
|
|
62
|
+
|
|
63
|
+
## Runtime Selection
|
|
64
|
+
|
|
65
|
+
See [runtime-selection.md](references/runtime-selection.md) for:
|
|
66
|
+
|
|
67
|
+
- Default to Node.js runtime
|
|
68
|
+
- When Edge runtime is appropriate
|
|
69
|
+
|
|
70
|
+
## Directives
|
|
71
|
+
|
|
72
|
+
See [directives.md](references/directives.md) for:
|
|
73
|
+
|
|
74
|
+
- `'use client'`, `'use server'` (React)
|
|
75
|
+
- `'use cache'` (Next.js)
|
|
76
|
+
|
|
77
|
+
## Functions
|
|
78
|
+
|
|
79
|
+
See [functions.md](references/functions.md) for:
|
|
80
|
+
|
|
81
|
+
- Navigation hooks: `useRouter`, `usePathname`, `useSearchParams`, `useParams`
|
|
82
|
+
- Server functions: `cookies`, `headers`, `draftMode`, `after`
|
|
83
|
+
- Generate functions: `generateStaticParams`, `generateMetadata`
|
|
84
|
+
|
|
85
|
+
## Error Handling
|
|
86
|
+
|
|
87
|
+
See [error-handling.md](references/error-handling.md) for:
|
|
88
|
+
|
|
89
|
+
- `error.tsx`, `global-error.tsx`, `not-found.tsx`
|
|
90
|
+
- `redirect`, `permanentRedirect`, `notFound`
|
|
91
|
+
- `forbidden`, `unauthorized` (auth errors)
|
|
92
|
+
- `unstable_rethrow` for catch blocks
|
|
93
|
+
|
|
94
|
+
## Data Patterns
|
|
95
|
+
|
|
96
|
+
See [data-patterns.md](references/data-patterns.md) for:
|
|
97
|
+
|
|
98
|
+
- Server Components vs Server Actions vs Route Handlers
|
|
99
|
+
- Avoiding data waterfalls (`Promise.all`, Suspense, preload)
|
|
100
|
+
- Client component data fetching
|
|
101
|
+
|
|
102
|
+
## Route Handlers
|
|
103
|
+
|
|
104
|
+
See [route-handlers.md](references/route-handlers.md) for:
|
|
105
|
+
|
|
106
|
+
- `route.ts` basics
|
|
107
|
+
- GET handler conflicts with `page.tsx`
|
|
108
|
+
- Environment behavior (no React DOM)
|
|
109
|
+
- When to use vs Server Actions
|
|
110
|
+
|
|
111
|
+
## Metadata & OG Images
|
|
112
|
+
|
|
113
|
+
See [metadata.md](references/metadata.md) for:
|
|
114
|
+
|
|
115
|
+
- Static and dynamic metadata
|
|
116
|
+
- `generateMetadata` function
|
|
117
|
+
- OG image generation with `next/og`
|
|
118
|
+
- File-based metadata conventions
|
|
119
|
+
|
|
120
|
+
## Image Optimization
|
|
121
|
+
|
|
122
|
+
See [image.md](references/image.md) for:
|
|
123
|
+
|
|
124
|
+
- Always use `next/image` over `<img>`
|
|
125
|
+
- Remote images configuration
|
|
126
|
+
- Responsive `sizes` attribute
|
|
127
|
+
- Blur placeholders
|
|
128
|
+
- Priority loading for LCP
|
|
129
|
+
|
|
130
|
+
## Font Optimization
|
|
131
|
+
|
|
132
|
+
See [font.md](references/font.md) for:
|
|
133
|
+
|
|
134
|
+
- `next/font` setup
|
|
135
|
+
- Google Fonts, local fonts
|
|
136
|
+
- Tailwind CSS integration
|
|
137
|
+
- Preloading subsets
|
|
138
|
+
|
|
139
|
+
## Bundling
|
|
140
|
+
|
|
141
|
+
See [bundling.md](references/bundling.md) for:
|
|
142
|
+
|
|
143
|
+
- Server-incompatible packages
|
|
144
|
+
- CSS imports (not link tags)
|
|
145
|
+
- Polyfills (already included)
|
|
146
|
+
- ESM/CommonJS issues
|
|
147
|
+
- Bundle analysis
|
|
148
|
+
|
|
149
|
+
## Scripts
|
|
150
|
+
|
|
151
|
+
See [scripts.md](references/scripts.md) for:
|
|
152
|
+
|
|
153
|
+
- `next/script` vs native script tags
|
|
154
|
+
- Inline scripts need `id`
|
|
155
|
+
- Loading strategies
|
|
156
|
+
- Google Analytics with `@next/third-parties`
|
|
157
|
+
|
|
158
|
+
## Hydration Errors
|
|
159
|
+
|
|
160
|
+
See [hydration-error.md](references/hydration-error.md) for:
|
|
161
|
+
|
|
162
|
+
- Common causes (browser APIs, dates, invalid HTML)
|
|
163
|
+
- Debugging with error overlay
|
|
164
|
+
- Fixes for each cause
|
|
165
|
+
|
|
166
|
+
## Suspense Boundaries
|
|
167
|
+
|
|
168
|
+
See [suspense-boundaries.md](references/suspense-boundaries.md) for:
|
|
169
|
+
|
|
170
|
+
- CSR bailout with `useSearchParams` and `usePathname`
|
|
171
|
+
- Which hooks require Suspense boundaries
|
|
172
|
+
|
|
173
|
+
## Parallel & Intercepting Routes
|
|
174
|
+
|
|
175
|
+
See [parallel-routes.md](references/parallel-routes.md) for:
|
|
176
|
+
|
|
177
|
+
- Modal patterns with `@slot` and `(.)` interceptors
|
|
178
|
+
- `default.tsx` for fallbacks
|
|
179
|
+
- Closing modals correctly with `router.back()`
|
|
180
|
+
|
|
181
|
+
## Self-Hosting
|
|
182
|
+
|
|
183
|
+
See [self-hosting.md](references/self-hosting.md) for:
|
|
184
|
+
|
|
185
|
+
- `output: 'standalone'` for Docker
|
|
186
|
+
- Cache handlers for multi-instance ISR
|
|
187
|
+
- What works vs needs extra setup
|
|
188
|
+
|
|
189
|
+
## Debug Tricks
|
|
190
|
+
|
|
191
|
+
See [debug-tricks.md](references/debug-tricks.md) for:
|
|
192
|
+
|
|
193
|
+
- MCP endpoint for AI-assisted debugging
|
|
194
|
+
- Rebuild specific routes with `--debug-build-paths`
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Async Patterns
|
|
2
|
+
|
|
3
|
+
In Next.js 16.1.6+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous.
|
|
4
|
+
|
|
5
|
+
## Async Params and SearchParams
|
|
6
|
+
|
|
7
|
+
Always type them as `Promise<...>` and await them.
|
|
8
|
+
|
|
9
|
+
### Pages and Layouts
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
type Props = { params: Promise<{ slug: string }> };
|
|
13
|
+
|
|
14
|
+
export default async function Page({ params }: Props) {
|
|
15
|
+
const { slug } = await params;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Route Handlers
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
export async function GET(request: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
23
|
+
const { id } = await params;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### SearchParams
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
type Props = {
|
|
31
|
+
params: Promise<{ slug: string }>;
|
|
32
|
+
searchParams: Promise<{ query?: string }>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default async function Page({ params, searchParams }: Props) {
|
|
36
|
+
const { slug } = await params;
|
|
37
|
+
const { query } = await searchParams;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Synchronous Components
|
|
42
|
+
|
|
43
|
+
Use `React.use()` for non-async components:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { use } from "react";
|
|
47
|
+
|
|
48
|
+
type Props = { params: Promise<{ slug: string }> };
|
|
49
|
+
|
|
50
|
+
export default function Page({ params }: Props) {
|
|
51
|
+
const { slug } = use(params);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### generateMetadata
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
type Props = { params: Promise<{ slug: string }> };
|
|
59
|
+
|
|
60
|
+
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|
61
|
+
const { slug } = await params;
|
|
62
|
+
return { title: slug };
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Async Cookies and Headers
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import { cookies, headers } from "next/headers";
|
|
70
|
+
|
|
71
|
+
export default async function Page() {
|
|
72
|
+
const cookieStore = await cookies();
|
|
73
|
+
const headersList = await headers();
|
|
74
|
+
|
|
75
|
+
const theme = cookieStore.get("theme");
|
|
76
|
+
const userAgent = headersList.get("user-agent");
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Migration Codemod
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx @next/codemod@latest next-async-request-api .
|
|
84
|
+
```
|