@hivehub/rulebook 2.1.0 → 3.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/README.md +779 -661
- package/dist/cli/commands.d.ts +22 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +291 -8
- package/dist/cli/commands.js.map +1 -1
- package/dist/core/claude-mcp.d.ts +32 -0
- package/dist/core/claude-mcp.d.ts.map +1 -0
- package/dist/core/claude-mcp.js +92 -0
- package/dist/core/claude-mcp.js.map +1 -0
- package/dist/core/config-manager.d.ts.map +1 -1
- package/dist/core/config-manager.js +27 -6
- package/dist/core/config-manager.js.map +1 -1
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/generator.js +98 -49
- package/dist/core/generator.js.map +1 -1
- package/dist/core/migrator.d.ts +13 -0
- package/dist/core/migrator.d.ts.map +1 -1
- package/dist/core/migrator.js +76 -9
- package/dist/core/migrator.js.map +1 -1
- package/dist/core/openspec-migrator.d.ts +1 -1
- package/dist/core/openspec-migrator.d.ts.map +1 -1
- package/dist/core/openspec-migrator.js +14 -7
- package/dist/core/openspec-migrator.js.map +1 -1
- package/dist/core/workflow-generator.js +297 -261
- package/dist/core/workflow-generator.js.map +1 -1
- package/dist/index.js +40 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/rulebook-server.d.ts.map +1 -1
- package/dist/mcp/rulebook-server.js +255 -74
- package/dist/mcp/rulebook-server.js.map +1 -1
- package/dist/memory/hnsw-index.d.ts +63 -0
- package/dist/memory/hnsw-index.d.ts.map +1 -0
- package/dist/memory/hnsw-index.js +421 -0
- package/dist/memory/hnsw-index.js.map +1 -0
- package/dist/memory/memory-cache.d.ts +33 -0
- package/dist/memory/memory-cache.d.ts.map +1 -0
- package/dist/memory/memory-cache.js +85 -0
- package/dist/memory/memory-cache.js.map +1 -0
- package/dist/memory/memory-hooks.d.ts +42 -0
- package/dist/memory/memory-hooks.d.ts.map +1 -0
- package/dist/memory/memory-hooks.js +193 -0
- package/dist/memory/memory-hooks.js.map +1 -0
- package/dist/memory/memory-manager.d.ts +55 -0
- package/dist/memory/memory-manager.d.ts.map +1 -0
- package/dist/memory/memory-manager.js +209 -0
- package/dist/memory/memory-manager.js.map +1 -0
- package/dist/memory/memory-search.d.ts +42 -0
- package/dist/memory/memory-search.d.ts.map +1 -0
- package/dist/memory/memory-search.js +166 -0
- package/dist/memory/memory-search.js.map +1 -0
- package/dist/memory/memory-store.d.ts +59 -0
- package/dist/memory/memory-store.d.ts.map +1 -0
- package/dist/memory/memory-store.js +394 -0
- package/dist/memory/memory-store.js.map +1 -0
- package/dist/memory/memory-types.d.ts +69 -0
- package/dist/memory/memory-types.d.ts.map +1 -0
- package/dist/memory/memory-types.js +7 -0
- package/dist/memory/memory-types.js.map +1 -0
- package/dist/memory/memory-vectorizer.d.ts +29 -0
- package/dist/memory/memory-vectorizer.d.ts.map +1 -0
- package/dist/memory/memory-vectorizer.js +104 -0
- package/dist/memory/memory-vectorizer.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +107 -106
- package/templates/commands/rulebook-memory-save.md +48 -0
- package/templates/commands/rulebook-memory-search.md +47 -0
- package/templates/commands/rulebook-task-apply.md +2 -2
- package/templates/commands/rulebook-task-archive.md +2 -2
- package/templates/commands/rulebook-task-create.md +2 -2
- package/templates/commands/rulebook-task-list.md +2 -2
- package/templates/commands/rulebook-task-show.md +2 -2
- package/templates/commands/rulebook-task-validate.md +2 -2
- package/templates/git/CI_CD_PATTERNS.md +4 -4
- package/templates/git/GITHUB_ACTIONS.md +3 -3
- package/templates/git/GITLAB_CI.md +4 -4
- package/templates/git/SECRETS_MANAGEMENT.md +4 -4
- package/templates/hooks/COMMIT_MSG.md +4 -4
- package/templates/hooks/POST_CHECKOUT.md +3 -3
- package/templates/hooks/PREPARE_COMMIT_MSG.md +3 -3
- package/templates/hooks/PRE_COMMIT.md +4 -4
- package/templates/hooks/PRE_PUSH.md +4 -4
- package/templates/modules/MEMORY.md +63 -0
- package/templates/skills/cli/aider/SKILL.md +59 -0
- package/templates/skills/cli/amazon-q/SKILL.md +35 -0
- package/templates/skills/cli/auggie/SKILL.md +42 -0
- package/templates/skills/cli/claude/SKILL.md +42 -0
- package/templates/skills/cli/claude-code/SKILL.md +146 -0
- package/templates/skills/cli/cline/SKILL.md +42 -0
- package/templates/skills/cli/codebuddy/SKILL.md +30 -0
- package/templates/skills/cli/codeium/SKILL.md +30 -0
- package/templates/skills/cli/codex/SKILL.md +31 -0
- package/templates/skills/cli/continue/SKILL.md +44 -0
- package/templates/skills/cli/cursor-cli/SKILL.md +38 -0
- package/templates/skills/cli/factory/SKILL.md +28 -0
- package/templates/skills/cli/gemini/SKILL.md +45 -0
- package/templates/skills/cli/kilocode/SKILL.md +28 -0
- package/templates/skills/cli/opencode/SKILL.md +28 -0
- package/templates/skills/core/agent-automation/SKILL.md +194 -0
- package/templates/skills/core/dag/SKILL.md +314 -0
- package/templates/skills/core/documentation-rules/SKILL.md +47 -0
- package/templates/skills/core/quality-enforcement/SKILL.md +78 -0
- package/templates/skills/frameworks/angular/SKILL.md +46 -0
- package/templates/skills/frameworks/django/SKILL.md +93 -0
- package/templates/skills/frameworks/electron/SKILL.md +157 -0
- package/templates/skills/frameworks/flask/SKILL.md +48 -0
- package/templates/skills/frameworks/flutter/SKILL.md +65 -0
- package/templates/skills/frameworks/jquery/SKILL.md +42 -0
- package/templates/skills/frameworks/laravel/SKILL.md +48 -0
- package/templates/skills/frameworks/nestjs/SKILL.md +53 -0
- package/templates/skills/frameworks/nextjs/SKILL.md +137 -0
- package/templates/skills/frameworks/nuxt/SKILL.md +50 -0
- package/templates/skills/frameworks/rails/SKILL.md +76 -0
- package/templates/skills/frameworks/react/SKILL.md +48 -0
- package/templates/skills/frameworks/react-native/SKILL.md +57 -0
- package/templates/skills/frameworks/spring/SKILL.md +49 -0
- package/templates/skills/frameworks/symfony/SKILL.md +46 -0
- package/templates/skills/frameworks/vue/SKILL.md +46 -0
- package/templates/skills/frameworks/zend/SKILL.md +45 -0
- package/templates/skills/ides/copilot/SKILL.md +47 -0
- package/templates/skills/ides/cursor/SKILL.md +53 -0
- package/templates/skills/ides/jetbrains-ai/SKILL.md +45 -0
- package/templates/skills/ides/replit/SKILL.md +46 -0
- package/templates/skills/ides/tabnine/SKILL.md +39 -0
- package/templates/skills/ides/vscode/SKILL.md +50 -0
- package/templates/skills/ides/windsurf/SKILL.md +46 -0
- package/templates/skills/ides/zed/SKILL.md +42 -0
- package/templates/skills/languages/ada/SKILL.md +68 -0
- package/templates/skills/languages/c/SKILL.md +343 -0
- package/templates/skills/languages/cpp/SKILL.md +753 -0
- package/templates/skills/languages/csharp/SKILL.md +427 -0
- package/templates/skills/languages/dart/SKILL.md +342 -0
- package/templates/skills/languages/elixir/SKILL.md +464 -0
- package/templates/skills/languages/erlang/SKILL.md +371 -0
- package/templates/skills/languages/go/SKILL.md +655 -0
- package/templates/skills/languages/haskell/SKILL.md +187 -0
- package/templates/skills/languages/java/SKILL.md +617 -0
- package/templates/skills/languages/javascript/SKILL.md +641 -0
- package/templates/skills/languages/julia/SKILL.md +107 -0
- package/templates/skills/languages/kotlin/SKILL.md +521 -0
- package/templates/skills/languages/lisp/SKILL.md +110 -0
- package/templates/skills/languages/lua/SKILL.md +84 -0
- package/templates/skills/languages/objectivec/SKILL.md +100 -0
- package/templates/skills/languages/php/SKILL.md +426 -0
- package/templates/skills/languages/python/SKILL.md +692 -0
- package/templates/skills/languages/r/SKILL.md +360 -0
- package/templates/skills/languages/ruby/SKILL.md +431 -0
- package/templates/skills/languages/rust/SKILL.md +487 -0
- package/templates/skills/languages/sas/SKILL.md +83 -0
- package/templates/skills/languages/scala/SKILL.md +358 -0
- package/templates/skills/languages/solidity/SKILL.md +590 -0
- package/templates/skills/languages/sql/SKILL.md +147 -0
- package/templates/skills/languages/swift/SKILL.md +476 -0
- package/templates/skills/languages/zig/SKILL.md +275 -0
- package/templates/skills/modules/atlassian/SKILL.md +265 -0
- package/templates/skills/modules/context7/SKILL.md +64 -0
- package/templates/skills/modules/figma/SKILL.md +277 -0
- package/templates/skills/modules/github-mcp/SKILL.md +74 -0
- package/templates/skills/modules/grafana/SKILL.md +338 -0
- package/templates/skills/modules/memory/SKILL.md +73 -0
- package/templates/skills/modules/notion/SKILL.md +257 -0
- package/templates/skills/modules/playwright/SKILL.md +100 -0
- package/templates/skills/modules/rulebook-mcp/SKILL.md +166 -0
- package/templates/skills/modules/serena/SKILL.md +347 -0
- package/templates/skills/modules/supabase/SKILL.md +233 -0
- package/templates/skills/modules/synap/SKILL.md +79 -0
- package/templates/skills/modules/vectorizer/SKILL.md +73 -0
- package/templates/skills/services/azure-blob/SKILL.md +194 -0
- package/templates/skills/services/cassandra/SKILL.md +249 -0
- package/templates/skills/services/dynamodb/SKILL.md +318 -0
- package/templates/skills/services/elasticsearch/SKILL.md +357 -0
- package/templates/skills/services/gcs/SKILL.md +188 -0
- package/templates/skills/services/influxdb/SKILL.md +275 -0
- package/templates/skills/services/kafka/SKILL.md +351 -0
- package/templates/skills/services/mariadb/SKILL.md +193 -0
- package/templates/skills/services/memcached/SKILL.md +252 -0
- package/templates/skills/services/minio/SKILL.md +211 -0
- package/templates/skills/services/mongodb/SKILL.md +278 -0
- package/templates/skills/services/mysql/SKILL.md +368 -0
- package/templates/skills/services/neo4j/SKILL.md +257 -0
- package/templates/skills/services/oracle/SKILL.md +300 -0
- package/templates/skills/services/postgresql/SKILL.md +336 -0
- package/templates/skills/services/rabbitmq/SKILL.md +296 -0
- package/templates/skills/services/redis/SKILL.md +302 -0
- package/templates/skills/services/s3/SKILL.md +308 -0
- package/templates/skills/services/sqlite/SKILL.md +304 -0
- package/templates/skills/services/sqlserver/SKILL.md +304 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "DynamoDB"
|
|
3
|
+
description: "Use DynamoDB for serverless NoSQL database with automatic scaling, high performance, and pay-per-use pricing."
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
category: "services"
|
|
6
|
+
author: "Rulebook"
|
|
7
|
+
tags: ["services", "database"]
|
|
8
|
+
dependencies: []
|
|
9
|
+
conflicts: []
|
|
10
|
+
---
|
|
11
|
+
<!-- DYNAMODB:START -->
|
|
12
|
+
# AWS DynamoDB Instructions
|
|
13
|
+
|
|
14
|
+
**CRITICAL**: Use DynamoDB for serverless NoSQL database with automatic scaling, high performance, and pay-per-use pricing.
|
|
15
|
+
|
|
16
|
+
## Core Features
|
|
17
|
+
|
|
18
|
+
### Connection
|
|
19
|
+
```typescript
|
|
20
|
+
// Using @aws-sdk/client-dynamodb
|
|
21
|
+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
|
|
22
|
+
import { DynamoDBDocumentClient, PutCommand, GetCommand, QueryCommand, ScanCommand } from '@aws-sdk/lib-dynamodb'
|
|
23
|
+
|
|
24
|
+
const client = new DynamoDBClient({
|
|
25
|
+
region: process.env.AWS_REGION || 'us-east-1',
|
|
26
|
+
credentials: {
|
|
27
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID || '',
|
|
28
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '',
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const docClient = DynamoDBDocumentClient.from(client)
|
|
33
|
+
|
|
34
|
+
// Using AWS SDK v2
|
|
35
|
+
import AWS from 'aws-sdk'
|
|
36
|
+
|
|
37
|
+
const dynamodb = new AWS.DynamoDB.DocumentClient({
|
|
38
|
+
region: process.env.AWS_REGION || 'us-east-1',
|
|
39
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
40
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Basic Operations
|
|
45
|
+
```typescript
|
|
46
|
+
// Put item
|
|
47
|
+
await docClient.send(new PutCommand({
|
|
48
|
+
TableName: 'Users',
|
|
49
|
+
Item: {
|
|
50
|
+
id: 'user-1',
|
|
51
|
+
email: 'john@example.com',
|
|
52
|
+
name: 'John Doe',
|
|
53
|
+
createdAt: new Date().toISOString(),
|
|
54
|
+
},
|
|
55
|
+
}))
|
|
56
|
+
|
|
57
|
+
// Get item
|
|
58
|
+
const result = await docClient.send(new GetCommand({
|
|
59
|
+
TableName: 'Users',
|
|
60
|
+
Key: {
|
|
61
|
+
id: 'user-1',
|
|
62
|
+
},
|
|
63
|
+
}))
|
|
64
|
+
const user = result.Item
|
|
65
|
+
|
|
66
|
+
// Update item
|
|
67
|
+
await docClient.send(new UpdateCommand({
|
|
68
|
+
TableName: 'Users',
|
|
69
|
+
Key: { id: 'user-1' },
|
|
70
|
+
UpdateExpression: 'SET #name = :name, updatedAt = :updatedAt',
|
|
71
|
+
ExpressionAttributeNames: {
|
|
72
|
+
'#name': 'name',
|
|
73
|
+
},
|
|
74
|
+
ExpressionAttributeValues: {
|
|
75
|
+
':name': 'Jane Doe',
|
|
76
|
+
':updatedAt': new Date().toISOString(),
|
|
77
|
+
},
|
|
78
|
+
}))
|
|
79
|
+
|
|
80
|
+
// Delete item
|
|
81
|
+
await docClient.send(new DeleteCommand({
|
|
82
|
+
TableName: 'Users',
|
|
83
|
+
Key: { id: 'user-1' },
|
|
84
|
+
}))
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Query Operations
|
|
88
|
+
```typescript
|
|
89
|
+
// Query by partition key
|
|
90
|
+
const result = await docClient.send(new QueryCommand({
|
|
91
|
+
TableName: 'Posts',
|
|
92
|
+
KeyConditionExpression: 'userId = :userId',
|
|
93
|
+
ExpressionAttributeValues: {
|
|
94
|
+
':userId': 'user-1',
|
|
95
|
+
},
|
|
96
|
+
ScanIndexForward: false, // Sort descending
|
|
97
|
+
Limit: 10,
|
|
98
|
+
}))
|
|
99
|
+
|
|
100
|
+
// Query with GSI (Global Secondary Index)
|
|
101
|
+
const result = await docClient.send(new QueryCommand({
|
|
102
|
+
TableName: 'Users',
|
|
103
|
+
IndexName: 'EmailIndex',
|
|
104
|
+
KeyConditionExpression: 'email = :email',
|
|
105
|
+
ExpressionAttributeValues: {
|
|
106
|
+
':email': 'john@example.com',
|
|
107
|
+
},
|
|
108
|
+
}))
|
|
109
|
+
|
|
110
|
+
// Scan (use sparingly)
|
|
111
|
+
const result = await docClient.send(new ScanCommand({
|
|
112
|
+
TableName: 'Users',
|
|
113
|
+
FilterExpression: '#status = :status',
|
|
114
|
+
ExpressionAttributeNames: {
|
|
115
|
+
'#status': 'status',
|
|
116
|
+
},
|
|
117
|
+
ExpressionAttributeValues: {
|
|
118
|
+
':status': 'active',
|
|
119
|
+
},
|
|
120
|
+
Limit: 100,
|
|
121
|
+
}))
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Advanced Features
|
|
125
|
+
```typescript
|
|
126
|
+
// Batch operations
|
|
127
|
+
import { BatchWriteCommand } from '@aws-sdk/lib-dynamodb'
|
|
128
|
+
|
|
129
|
+
await docClient.send(new BatchWriteCommand({
|
|
130
|
+
RequestItems: {
|
|
131
|
+
Users: [
|
|
132
|
+
{
|
|
133
|
+
PutRequest: {
|
|
134
|
+
Item: { id: 'user-1', name: 'User 1' },
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
PutRequest: {
|
|
139
|
+
Item: { id: 'user-2', name: 'User 2' },
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
}))
|
|
145
|
+
|
|
146
|
+
// Transactions
|
|
147
|
+
import { TransactWriteCommand } from '@aws-sdk/lib-dynamodb'
|
|
148
|
+
|
|
149
|
+
await docClient.send(new TransactWriteCommand({
|
|
150
|
+
TransactItems: [
|
|
151
|
+
{
|
|
152
|
+
Put: {
|
|
153
|
+
TableName: 'Accounts',
|
|
154
|
+
Item: { userId: 'user-1', balance: 1000 },
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
Put: {
|
|
159
|
+
TableName: 'Transactions',
|
|
160
|
+
Item: { id: 'tx-1', userId: 'user-1', amount: 1000 },
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
}))
|
|
165
|
+
|
|
166
|
+
// Conditional writes
|
|
167
|
+
await docClient.send(new PutCommand({
|
|
168
|
+
TableName: 'Users',
|
|
169
|
+
Item: { id: 'user-1', email: 'john@example.com' },
|
|
170
|
+
ConditionExpression: 'attribute_not_exists(id)',
|
|
171
|
+
}))
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Common Patterns
|
|
175
|
+
|
|
176
|
+
### Single Table Design
|
|
177
|
+
```typescript
|
|
178
|
+
// Store different entity types in one table
|
|
179
|
+
await docClient.send(new PutCommand({
|
|
180
|
+
TableName: 'AppData',
|
|
181
|
+
Item: {
|
|
182
|
+
PK: 'USER#user-1',
|
|
183
|
+
SK: 'METADATA',
|
|
184
|
+
EntityType: 'User',
|
|
185
|
+
email: 'john@example.com',
|
|
186
|
+
name: 'John Doe',
|
|
187
|
+
},
|
|
188
|
+
}))
|
|
189
|
+
|
|
190
|
+
await docClient.send(new PutCommand({
|
|
191
|
+
TableName: 'AppData',
|
|
192
|
+
Item: {
|
|
193
|
+
PK: 'USER#user-1',
|
|
194
|
+
SK: 'POST#post-1',
|
|
195
|
+
EntityType: 'Post',
|
|
196
|
+
title: 'My Post',
|
|
197
|
+
content: 'Content here',
|
|
198
|
+
},
|
|
199
|
+
}))
|
|
200
|
+
|
|
201
|
+
// Query all posts for a user
|
|
202
|
+
const result = await docClient.send(new QueryCommand({
|
|
203
|
+
TableName: 'AppData',
|
|
204
|
+
KeyConditionExpression: 'PK = :pk AND begins_with(SK, :sk)',
|
|
205
|
+
ExpressionAttributeValues: {
|
|
206
|
+
':pk': 'USER#user-1',
|
|
207
|
+
':sk': 'POST#',
|
|
208
|
+
},
|
|
209
|
+
}))
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Time-to-Live (TTL)
|
|
213
|
+
```typescript
|
|
214
|
+
// Items automatically deleted after TTL
|
|
215
|
+
await docClient.send(new PutCommand({
|
|
216
|
+
TableName: 'Sessions',
|
|
217
|
+
Item: {
|
|
218
|
+
sessionId: 'session-1',
|
|
219
|
+
userId: 'user-1',
|
|
220
|
+
ttl: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now
|
|
221
|
+
},
|
|
222
|
+
}))
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Best Practices
|
|
226
|
+
|
|
227
|
+
✅ **DO:**
|
|
228
|
+
- Design for access patterns
|
|
229
|
+
- Use appropriate partition keys
|
|
230
|
+
- Create GSIs for alternative access patterns
|
|
231
|
+
- Use batch operations when possible
|
|
232
|
+
- Implement exponential backoff for throttling
|
|
233
|
+
- Use TTL for temporary data
|
|
234
|
+
- Monitor provisioned capacity
|
|
235
|
+
- Use transactions for atomic operations
|
|
236
|
+
- Implement proper error handling
|
|
237
|
+
- Use single table design when appropriate
|
|
238
|
+
|
|
239
|
+
❌ **DON'T:**
|
|
240
|
+
- Use Scan for frequent queries
|
|
241
|
+
- Create too many GSIs (affects write performance)
|
|
242
|
+
- Store large items (> 400KB)
|
|
243
|
+
- Ignore throttling errors
|
|
244
|
+
- Hardcode table names
|
|
245
|
+
- Skip error handling
|
|
246
|
+
- Use eventually consistent reads when strong consistency needed
|
|
247
|
+
- Ignore capacity planning
|
|
248
|
+
- Store sensitive data without encryption
|
|
249
|
+
- Skip monitoring
|
|
250
|
+
|
|
251
|
+
## Configuration
|
|
252
|
+
|
|
253
|
+
### Environment Variables
|
|
254
|
+
```bash
|
|
255
|
+
AWS_REGION=us-east-1
|
|
256
|
+
AWS_ACCESS_KEY_ID=your-access-key
|
|
257
|
+
AWS_SECRET_ACCESS_KEY=your-secret-key
|
|
258
|
+
DYNAMODB_TABLE_PREFIX=myapp_
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### IAM Policy
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"Version": "2012-10-17",
|
|
265
|
+
"Statement": [
|
|
266
|
+
{
|
|
267
|
+
"Effect": "Allow",
|
|
268
|
+
"Action": [
|
|
269
|
+
"dynamodb:PutItem",
|
|
270
|
+
"dynamodb:GetItem",
|
|
271
|
+
"dynamodb:UpdateItem",
|
|
272
|
+
"dynamodb:DeleteItem",
|
|
273
|
+
"dynamodb:Query",
|
|
274
|
+
"dynamodb:Scan"
|
|
275
|
+
],
|
|
276
|
+
"Resource": "arn:aws:dynamodb:*:*:table/myapp-*"
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Integration with Development
|
|
283
|
+
|
|
284
|
+
### Testing
|
|
285
|
+
```typescript
|
|
286
|
+
// Use DynamoDB Local
|
|
287
|
+
const localClient = new DynamoDBClient({
|
|
288
|
+
endpoint: 'http://localhost:8000',
|
|
289
|
+
region: 'local',
|
|
290
|
+
credentials: {
|
|
291
|
+
accessKeyId: 'local',
|
|
292
|
+
secretAccessKey: 'local',
|
|
293
|
+
},
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
// Clean up after tests
|
|
297
|
+
afterEach(async () => {
|
|
298
|
+
// Delete test items or use separate test table
|
|
299
|
+
})
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Health Checks
|
|
303
|
+
```typescript
|
|
304
|
+
async function checkDynamoDBHealth(): Promise<boolean> {
|
|
305
|
+
try {
|
|
306
|
+
await docClient.send(new GetCommand({
|
|
307
|
+
TableName: 'HealthCheck',
|
|
308
|
+
Key: { id: 'health' },
|
|
309
|
+
}))
|
|
310
|
+
return true
|
|
311
|
+
} catch {
|
|
312
|
+
return false
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
<!-- DYNAMODB:END -->
|
|
318
|
+
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Elasticsearch"
|
|
3
|
+
description: "Use Elasticsearch for full-text search, log aggregation, and real-time analytics with distributed architecture."
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
category: "services"
|
|
6
|
+
author: "Rulebook"
|
|
7
|
+
tags: ["services", "database"]
|
|
8
|
+
dependencies: []
|
|
9
|
+
conflicts: []
|
|
10
|
+
---
|
|
11
|
+
<!-- ELASTICSEARCH:START -->
|
|
12
|
+
# Elasticsearch Search Instructions
|
|
13
|
+
|
|
14
|
+
**CRITICAL**: Use Elasticsearch for full-text search, log aggregation, and real-time analytics with distributed architecture.
|
|
15
|
+
|
|
16
|
+
## Core Features
|
|
17
|
+
|
|
18
|
+
### Connection
|
|
19
|
+
```typescript
|
|
20
|
+
// Using @elastic/elasticsearch
|
|
21
|
+
import { Client } from '@elastic/elasticsearch'
|
|
22
|
+
|
|
23
|
+
const client = new Client({
|
|
24
|
+
node: process.env.ELASTICSEARCH_URL || 'http://localhost:9200',
|
|
25
|
+
auth: {
|
|
26
|
+
username: process.env.ELASTICSEARCH_USER || 'elastic',
|
|
27
|
+
password: process.env.ELASTICSEARCH_PASSWORD || '',
|
|
28
|
+
},
|
|
29
|
+
maxRetries: 5,
|
|
30
|
+
requestTimeout: 60000,
|
|
31
|
+
sniffOnStart: true,
|
|
32
|
+
sniffInterval: 30000,
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Index Operations
|
|
37
|
+
```typescript
|
|
38
|
+
// Create index
|
|
39
|
+
await client.indices.create({
|
|
40
|
+
index: 'users',
|
|
41
|
+
body: {
|
|
42
|
+
mappings: {
|
|
43
|
+
properties: {
|
|
44
|
+
name: { type: 'text' },
|
|
45
|
+
email: { type: 'keyword' },
|
|
46
|
+
age: { type: 'integer' },
|
|
47
|
+
createdAt: { type: 'date' },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
settings: {
|
|
51
|
+
number_of_shards: 1,
|
|
52
|
+
number_of_replicas: 1,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// Delete index
|
|
58
|
+
await client.indices.delete({ index: 'users' })
|
|
59
|
+
|
|
60
|
+
// Check if index exists
|
|
61
|
+
const exists = await client.indices.exists({ index: 'users' })
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Document Operations
|
|
65
|
+
```typescript
|
|
66
|
+
// Index document
|
|
67
|
+
await client.index({
|
|
68
|
+
index: 'users',
|
|
69
|
+
id: '1',
|
|
70
|
+
body: {
|
|
71
|
+
name: 'John Doe',
|
|
72
|
+
email: 'john@example.com',
|
|
73
|
+
age: 30,
|
|
74
|
+
createdAt: new Date(),
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// Get document
|
|
79
|
+
const result = await client.get({
|
|
80
|
+
index: 'users',
|
|
81
|
+
id: '1',
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// Update document
|
|
85
|
+
await client.update({
|
|
86
|
+
index: 'users',
|
|
87
|
+
id: '1',
|
|
88
|
+
body: {
|
|
89
|
+
doc: {
|
|
90
|
+
age: 31,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// Delete document
|
|
96
|
+
await client.delete({
|
|
97
|
+
index: 'users',
|
|
98
|
+
id: '1',
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// Bulk operations
|
|
102
|
+
await client.bulk({
|
|
103
|
+
body: [
|
|
104
|
+
{ index: { _index: 'users', _id: '1' } },
|
|
105
|
+
{ name: 'User 1', email: 'user1@example.com' },
|
|
106
|
+
{ index: { _index: 'users', _id: '2' } },
|
|
107
|
+
{ name: 'User 2', email: 'user2@example.com' },
|
|
108
|
+
],
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Search Operations
|
|
113
|
+
```typescript
|
|
114
|
+
// Simple search
|
|
115
|
+
const result = await client.search({
|
|
116
|
+
index: 'users',
|
|
117
|
+
body: {
|
|
118
|
+
query: {
|
|
119
|
+
match: {
|
|
120
|
+
name: 'John',
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// Multi-match search
|
|
127
|
+
const result = await client.search({
|
|
128
|
+
index: 'users',
|
|
129
|
+
body: {
|
|
130
|
+
query: {
|
|
131
|
+
multi_match: {
|
|
132
|
+
query: 'search term',
|
|
133
|
+
fields: ['name^2', 'email'], // name has 2x boost
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
// Bool query (AND/OR/NOT)
|
|
140
|
+
const result = await client.search({
|
|
141
|
+
index: 'users',
|
|
142
|
+
body: {
|
|
143
|
+
query: {
|
|
144
|
+
bool: {
|
|
145
|
+
must: [
|
|
146
|
+
{ match: { name: 'John' } },
|
|
147
|
+
{ range: { age: { gte: 18, lte: 65 } } },
|
|
148
|
+
],
|
|
149
|
+
must_not: [
|
|
150
|
+
{ term: { status: 'inactive' } },
|
|
151
|
+
],
|
|
152
|
+
should: [
|
|
153
|
+
{ match: { tags: 'premium' } },
|
|
154
|
+
],
|
|
155
|
+
minimum_should_match: 1,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// Aggregations
|
|
162
|
+
const result = await client.search({
|
|
163
|
+
index: 'users',
|
|
164
|
+
body: {
|
|
165
|
+
aggs: {
|
|
166
|
+
age_groups: {
|
|
167
|
+
range: {
|
|
168
|
+
field: 'age',
|
|
169
|
+
ranges: [
|
|
170
|
+
{ to: 25 },
|
|
171
|
+
{ from: 25, to: 50 },
|
|
172
|
+
{ from: 50 },
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
avg_age: {
|
|
177
|
+
avg: { field: 'age' },
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
})
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Common Patterns
|
|
185
|
+
|
|
186
|
+
### Full-Text Search
|
|
187
|
+
```typescript
|
|
188
|
+
async function searchUsers(query: string, filters?: any) {
|
|
189
|
+
const must: any[] = [
|
|
190
|
+
{
|
|
191
|
+
multi_match: {
|
|
192
|
+
query,
|
|
193
|
+
fields: ['name^2', 'email'],
|
|
194
|
+
fuzziness: 'AUTO',
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
if (filters) {
|
|
200
|
+
if (filters.age) {
|
|
201
|
+
must.push({ range: { age: filters.age } })
|
|
202
|
+
}
|
|
203
|
+
if (filters.status) {
|
|
204
|
+
must.push({ term: { status: filters.status } })
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const result = await client.search({
|
|
209
|
+
index: 'users',
|
|
210
|
+
body: {
|
|
211
|
+
query: {
|
|
212
|
+
bool: {
|
|
213
|
+
must,
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
highlight: {
|
|
217
|
+
fields: {
|
|
218
|
+
name: {},
|
|
219
|
+
email: {},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
return result.body.hits.hits.map(hit => ({
|
|
226
|
+
...hit._source,
|
|
227
|
+
score: hit._score,
|
|
228
|
+
highlights: hit.highlight,
|
|
229
|
+
}))
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Auto-complete
|
|
234
|
+
```typescript
|
|
235
|
+
// Create index with completion suggester
|
|
236
|
+
await client.indices.create({
|
|
237
|
+
index: 'suggestions',
|
|
238
|
+
body: {
|
|
239
|
+
mappings: {
|
|
240
|
+
properties: {
|
|
241
|
+
name: {
|
|
242
|
+
type: 'text',
|
|
243
|
+
fields: {
|
|
244
|
+
suggest: {
|
|
245
|
+
type: 'completion',
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// Search with suggestions
|
|
255
|
+
const result = await client.search({
|
|
256
|
+
index: 'suggestions',
|
|
257
|
+
body: {
|
|
258
|
+
suggest: {
|
|
259
|
+
name_suggest: {
|
|
260
|
+
prefix: 'joh',
|
|
261
|
+
completion: {
|
|
262
|
+
field: 'name.suggest',
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
})
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Best Practices
|
|
271
|
+
|
|
272
|
+
✅ **DO:**
|
|
273
|
+
- Use appropriate field types (text vs keyword)
|
|
274
|
+
- Create indexes with proper mappings
|
|
275
|
+
- Use bulk API for multiple operations
|
|
276
|
+
- Implement proper error handling
|
|
277
|
+
- Use aliases for zero-downtime reindexing
|
|
278
|
+
- Monitor cluster health
|
|
279
|
+
- Use appropriate shard and replica counts
|
|
280
|
+
- Implement search result pagination
|
|
281
|
+
- Use filters for exact matches (faster than queries)
|
|
282
|
+
- Enable slow query logging
|
|
283
|
+
|
|
284
|
+
❌ **DON'T:**
|
|
285
|
+
- Store large binary data (use external storage)
|
|
286
|
+
- Use too many shards (1 per 10-50GB)
|
|
287
|
+
- Skip mapping definitions
|
|
288
|
+
- Ignore cluster health
|
|
289
|
+
- Hardcode connection strings
|
|
290
|
+
- Use wildcard queries in production
|
|
291
|
+
- Skip error handling
|
|
292
|
+
- Ignore query performance
|
|
293
|
+
- Store sensitive data without encryption
|
|
294
|
+
- Use default refresh interval for real-time needs
|
|
295
|
+
|
|
296
|
+
## Configuration
|
|
297
|
+
|
|
298
|
+
### Environment Variables
|
|
299
|
+
```bash
|
|
300
|
+
ELASTICSEARCH_URL=http://localhost:9200
|
|
301
|
+
ELASTICSEARCH_USER=elastic
|
|
302
|
+
ELASTICSEARCH_PASSWORD=securepassword
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Docker Compose
|
|
306
|
+
```yaml
|
|
307
|
+
services:
|
|
308
|
+
elasticsearch:
|
|
309
|
+
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
|
|
310
|
+
environment:
|
|
311
|
+
- discovery.type=single-node
|
|
312
|
+
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
|
313
|
+
- xpack.security.enabled=false
|
|
314
|
+
ports:
|
|
315
|
+
- "9200:9200"
|
|
316
|
+
volumes:
|
|
317
|
+
- elasticsearch_data:/usr/share/elasticsearch/data
|
|
318
|
+
healthcheck:
|
|
319
|
+
test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"]
|
|
320
|
+
interval: 30s
|
|
321
|
+
timeout: 10s
|
|
322
|
+
retries: 5
|
|
323
|
+
|
|
324
|
+
volumes:
|
|
325
|
+
elasticsearch_data:
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Integration with Development
|
|
329
|
+
|
|
330
|
+
### Testing
|
|
331
|
+
```typescript
|
|
332
|
+
// Use test index
|
|
333
|
+
const testIndex = 'test_users'
|
|
334
|
+
|
|
335
|
+
beforeEach(async () => {
|
|
336
|
+
await client.indices.create({ index: testIndex })
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
afterEach(async () => {
|
|
340
|
+
await client.indices.delete({ index: testIndex })
|
|
341
|
+
})
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Health Checks
|
|
345
|
+
```typescript
|
|
346
|
+
async function checkElasticsearchHealth(): Promise<boolean> {
|
|
347
|
+
try {
|
|
348
|
+
const response = await client.cluster.health()
|
|
349
|
+
return response.body.status !== 'red'
|
|
350
|
+
} catch {
|
|
351
|
+
return false
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
<!-- ELASTICSEARCH:END -->
|
|
357
|
+
|