@jaypie/mcp 0.2.10 → 0.3.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 +62 -0
- package/dist/aws.d.ts +197 -0
- package/dist/index.js +1179 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/prompts/Jaypie_CDK_Constructs_and_Patterns.md +23 -28
- package/prompts/Jaypie_CICD_with_GitHub_Actions.md +141 -14
- package/prompts/Jaypie_DynamoDB_Package.md +55 -55
- package/prompts/Jaypie_Express_Package.md +0 -44
- package/prompts/{Jaypie_Vocabulary_Commander.md → Jaypie_Fabric_Commander.md} +38 -38
- package/prompts/{Jaypie_Vocabulary_LLM.md → Jaypie_Fabric_LLM.md} +45 -45
- package/prompts/{Jaypie_Vocabulary_Lambda.md → Jaypie_Fabric_Lambda.md} +40 -42
- package/prompts/{Jaypie_Vocabulary_MCP.md → Jaypie_Fabric_MCP.md} +39 -39
- package/prompts/{Jaypie_Vocabulary_Package.md → Jaypie_Fabric_Package.md} +151 -142
- package/prompts/Jaypie_Init_CICD_with_GitHub_Actions.md +151 -104
- package/prompts/Jaypie_MCP_Package.md +249 -0
- package/prompts/Jaypie_Streaming.md +408 -0
package/package.json
CHANGED
|
@@ -102,33 +102,27 @@ Preconfigured with API-optimized timeouts and role tags.
|
|
|
102
102
|
|
|
103
103
|
### Streaming Lambda Functions
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
For streaming responses (SSE, real-time updates), use `createLambdaStreamHandler` from `@jaypie/express` with `JaypieDistribution`:
|
|
106
106
|
|
|
107
107
|
```typescript
|
|
108
|
-
import {
|
|
108
|
+
import { JaypieExpressLambda, JaypieDistribution } from "@jaypie/constructs";
|
|
109
|
+
import * as lambda from "aws-cdk-lib/aws-lambda";
|
|
109
110
|
|
|
110
|
-
// Create
|
|
111
|
-
const
|
|
111
|
+
// Create Lambda (handler uses createLambdaStreamHandler internally)
|
|
112
|
+
const streamingApi = new JaypieExpressLambda(this, "StreamingApi", {
|
|
112
113
|
code: "dist/api",
|
|
113
|
-
handler: "
|
|
114
|
-
streaming: true, // Enables RESPONSE_STREAM invoke mode
|
|
115
|
-
port: 8000, // Port your app listens on (default: 8000)
|
|
114
|
+
handler: "index.handler",
|
|
116
115
|
});
|
|
117
116
|
|
|
118
|
-
// CloudFront
|
|
117
|
+
// Use with CloudFront and RESPONSE_STREAM invoke mode
|
|
119
118
|
new JaypieDistribution(this, "Distribution", {
|
|
120
|
-
handler:
|
|
119
|
+
handler: streamingApi,
|
|
120
|
+
invokeMode: lambda.InvokeMode.RESPONSE_STREAM,
|
|
121
121
|
host: "api.example.com",
|
|
122
122
|
zone: "example.com",
|
|
123
123
|
});
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
Features:
|
|
127
|
-
- Automatically adds AWS Lambda Web Adapter layer (supports ARM64 and x86_64)
|
|
128
|
-
- Sets `AWS_LAMBDA_EXEC_WRAPPER` and `AWS_LWA_INVOKE_MODE` environment variables
|
|
129
|
-
- Exposes `invokeMode` property for auto-detection by `JaypieDistribution`
|
|
130
|
-
- Use `streaming: false` for buffered mode (traditional Lambda behavior)
|
|
131
|
-
|
|
132
126
|
For direct Function URL access (bypass CloudFront):
|
|
133
127
|
```typescript
|
|
134
128
|
import { FunctionUrlAuthType, InvokeMode } from "aws-cdk-lib/aws-lambda";
|
|
@@ -147,7 +141,7 @@ const functionUrl = streamingLambda.addFunctionUrl({
|
|
|
147
141
|
new cdk.CfnOutput(this, "StreamingUrl", { value: functionUrl.url });
|
|
148
142
|
```
|
|
149
143
|
|
|
150
|
-
Note:
|
|
144
|
+
Note: Streaming requires Lambda Function URLs (not API Gateway). `JaypieDistribution` uses Function URLs by default.
|
|
151
145
|
|
|
152
146
|
### Stack Types
|
|
153
147
|
|
|
@@ -323,21 +317,22 @@ Common usage:
|
|
|
323
317
|
|
|
324
318
|
### DynamoDB Tables
|
|
325
319
|
|
|
326
|
-
Use `JaypieDynamoDb` for single-table design with Jaypie
|
|
320
|
+
Use `JaypieDynamoDb` for single-table design with Jaypie patterns:
|
|
327
321
|
```typescript
|
|
328
322
|
// Shorthand: tableName becomes "myApp", construct id is "JaypieDynamoDb-myApp"
|
|
329
323
|
const table = new JaypieDynamoDb(this, "myApp");
|
|
330
324
|
|
|
331
|
-
//
|
|
332
|
-
const
|
|
333
|
-
|
|
325
|
+
// With standard Jaypie GSIs (indexScope, indexAlias, indexClass, indexType, indexXid)
|
|
326
|
+
const tableWithIndexes = new JaypieDynamoDb(this, "myApp", {
|
|
327
|
+
indexes: JaypieDynamoDb.DEFAULT_INDEXES,
|
|
334
328
|
});
|
|
335
329
|
|
|
336
|
-
//
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
330
|
+
// With custom indexes using IndexDefinition from @jaypie/fabric
|
|
331
|
+
const customTable = new JaypieDynamoDb(this, "myApp", {
|
|
332
|
+
indexes: [
|
|
333
|
+
{ pk: ["scope", "model"], sk: ["sequence"] }, // indexScopeModel
|
|
334
|
+
{ pk: ["scope", "model", "type"], sparse: true }, // indexScopeModelType
|
|
335
|
+
{ name: "byAlias", pk: ["alias"], sk: ["createdAt"] }, // custom name
|
|
341
336
|
],
|
|
342
337
|
});
|
|
343
338
|
|
|
@@ -352,9 +347,9 @@ Features:
|
|
|
352
347
|
- Default partition key: `model` (string), sort key: `id` (string)
|
|
353
348
|
- PAY_PER_REQUEST billing mode by default
|
|
354
349
|
- RETAIN removal policy in production, DESTROY otherwise
|
|
355
|
-
-
|
|
356
|
-
-
|
|
357
|
-
-
|
|
350
|
+
- No GSIs by default - use `indexes` prop to add them
|
|
351
|
+
- `JaypieDynamoDb.DEFAULT_INDEXES` provides standard Jaypie GSIs from `@jaypie/fabric`
|
|
352
|
+
- Uses `IndexDefinition` from `@jaypie/fabric` for GSI configuration
|
|
358
353
|
- Implements `ITableV2` interface
|
|
359
354
|
|
|
360
355
|
For single-table design patterns, key builders, and query utilities, see `Jaypie_DynamoDB_Package.md`.
|
|
@@ -6,6 +6,14 @@ description: conventions around deployment and environment variables, especially
|
|
|
6
6
|
|
|
7
7
|
Reference for Jaypie CI/CD conventions. For setup instructions, see `Jaypie_Init_CICD_with_GitHub_Actions.md`.
|
|
8
8
|
|
|
9
|
+
## Workspace Naming Conventions
|
|
10
|
+
|
|
11
|
+
| Directory | Purpose |
|
|
12
|
+
|-----------|---------|
|
|
13
|
+
| `packages/` | Default workspace for npm packages (preferred when only one namespace needed) |
|
|
14
|
+
| `stacks/` | CDK-deployed infrastructure and sites (as opposed to npm-published) |
|
|
15
|
+
| `workspaces/` | Generic workspace for other work |
|
|
16
|
+
|
|
9
17
|
## Jaypie Environments
|
|
10
18
|
|
|
11
19
|
Jaypie assumes multiple deployed environments:
|
|
@@ -19,11 +27,21 @@ Jaypie assumes multiple deployed environments:
|
|
|
19
27
|
|
|
20
28
|
## Naming Conventions
|
|
21
29
|
|
|
22
|
-
Workflow
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- `
|
|
30
|
+
### Workflow File Naming
|
|
31
|
+
|
|
32
|
+
| Pattern | Purpose | Example |
|
|
33
|
+
|---------|---------|---------|
|
|
34
|
+
| `deploy-env-$ENV.yml` | Deploy all stacks to an environment | `deploy-env-sandbox.yml` |
|
|
35
|
+
| `deploy-$ENV.yml` | Legacy alias for environment deploy | `deploy-sandbox.yml` |
|
|
36
|
+
| `deploy-stack-$STACK.yml` | Deploy specific stack content | `deploy-stack-documentation.yml` |
|
|
37
|
+
|
|
38
|
+
### Tag Naming
|
|
39
|
+
|
|
40
|
+
| Pattern | Purpose | Example |
|
|
41
|
+
|---------|---------|---------|
|
|
42
|
+
| `v*` | Production version release | `v1.2.3` |
|
|
43
|
+
| `$ENV-*` | Deploy to specific environment | `sandbox-hotfix` |
|
|
44
|
+
| `stack-$STACK-*` | Deploy specific stack | `stack-documentation-v1.0.0` |
|
|
27
45
|
|
|
28
46
|
Always prefer full words for maximum clarity.
|
|
29
47
|
|
|
@@ -54,11 +72,17 @@ jobs: # alphabetical
|
|
|
54
72
|
|
|
55
73
|
| Environment | Trigger |
|
|
56
74
|
|-------------|---------|
|
|
57
|
-
| production | tags: `v
|
|
58
|
-
| development | branches: `[main]` |
|
|
59
|
-
| sandbox | branches: `[
|
|
75
|
+
| production | tags: `v*`, `production-*` |
|
|
76
|
+
| development | branches: `[main, development/*]`, tags: `development-*` |
|
|
77
|
+
| sandbox | branches: `[feat/*, sandbox/*]`, tags: `sandbox-*` |
|
|
60
78
|
| personal | branches-ignore: `[main, develop, nobuild-*, nobuild/*, sandbox, sandbox-*, sandbox/*]` |
|
|
61
79
|
|
|
80
|
+
### Stack Triggers
|
|
81
|
+
|
|
82
|
+
| Stack | Trigger |
|
|
83
|
+
|-------|---------|
|
|
84
|
+
| documentation | Push to `main` with path filter, tags: `stack-documentation-*` |
|
|
85
|
+
|
|
62
86
|
### Dependencies
|
|
63
87
|
|
|
64
88
|
| Environment | Lint/Test Dependency |
|
|
@@ -74,14 +98,20 @@ Use `npm run lint` and `npm run test`. Verify the test script uses `vitest run`
|
|
|
74
98
|
|
|
75
99
|
## Concurrency
|
|
76
100
|
|
|
77
|
-
|
|
101
|
+
Use concurrency groups without `cancel-in-progress` to avoid stuck deployments. Environments run in parallel between each other but concurrently within an environment.
|
|
102
|
+
|
|
103
|
+
### Environment Builds
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
concurrency:
|
|
107
|
+
group: deploy-env-production
|
|
108
|
+
```
|
|
78
109
|
|
|
79
|
-
###
|
|
110
|
+
### Stack Builds
|
|
80
111
|
|
|
81
112
|
```yaml
|
|
82
113
|
concurrency:
|
|
83
|
-
group: deploy-
|
|
84
|
-
cancel-in-progress: true
|
|
114
|
+
group: deploy-stack-documentation-${{ github.event.inputs.environment || 'development' }}
|
|
85
115
|
```
|
|
86
116
|
|
|
87
117
|
### Personal Builds
|
|
@@ -89,7 +119,6 @@ concurrency:
|
|
|
89
119
|
```yaml
|
|
90
120
|
concurrency:
|
|
91
121
|
group: deploy-personal-build-${{ github.actor }}
|
|
92
|
-
cancel-in-progress: true
|
|
93
122
|
```
|
|
94
123
|
|
|
95
124
|
## Personal Builds
|
|
@@ -122,6 +151,48 @@ Create GitHub environments matching deployment targets:
|
|
|
122
151
|
|
|
123
152
|
Each environment contains variables and secrets specific to that deployment target.
|
|
124
153
|
|
|
154
|
+
## GitHub Variable Scoping
|
|
155
|
+
|
|
156
|
+
Variables are configured at different levels in GitHub Settings:
|
|
157
|
+
|
|
158
|
+
### Organization Level
|
|
159
|
+
|
|
160
|
+
| Variable | Description | Default |
|
|
161
|
+
|----------|-------------|---------|
|
|
162
|
+
| `AWS_REGION` | AWS region | `us-east-1` |
|
|
163
|
+
| `LOG_LEVEL` | Application log level | `debug` |
|
|
164
|
+
| `MODULE_LOG_LEVEL` | Module log level | `warn` |
|
|
165
|
+
| `PROJECT_SPONSOR` | Organization name | `finlaysonstudio` |
|
|
166
|
+
|
|
167
|
+
### Repository Level
|
|
168
|
+
|
|
169
|
+
| Variable | Description | Default |
|
|
170
|
+
|----------|-------------|---------|
|
|
171
|
+
| `AWS_HOSTED_ZONE` | Route53 hosted zone | `example.com` |
|
|
172
|
+
| `PROJECT_KEY` | Project identifier | `myapp` |
|
|
173
|
+
| `PROJECT_SERVICE` | Service name | `stacks` |
|
|
174
|
+
|
|
175
|
+
### Environment Level
|
|
176
|
+
|
|
177
|
+
| Variable | Description | Required |
|
|
178
|
+
|----------|-------------|----------|
|
|
179
|
+
| `AWS_ROLE_ARN` | IAM role ARN for OIDC | Yes |
|
|
180
|
+
| `DATADOG_API_KEY_ARN` | Datadog API key ARN in Secrets Manager | No |
|
|
181
|
+
| `PROJECT_ENV` | Environment identifier | Yes |
|
|
182
|
+
| `PROJECT_NONCE` | Unique resource identifier | No |
|
|
183
|
+
|
|
184
|
+
### Auto-Generated Variables
|
|
185
|
+
|
|
186
|
+
These variables are set automatically from GitHub context:
|
|
187
|
+
|
|
188
|
+
| Variable | Source | Description |
|
|
189
|
+
|----------|--------|-------------|
|
|
190
|
+
| `CDK_DEFAULT_ACCOUNT` | `${{ github.repository_owner }}` | Repository owner |
|
|
191
|
+
| `CDK_DEFAULT_REGION` | `AWS_REGION` | Same as AWS region |
|
|
192
|
+
| `CDK_ENV_REPO` | `${{ github.repository }}` | Repository name (owner/repo) |
|
|
193
|
+
| `PROJECT_COMMIT` | `${{ github.sha }}` | Current commit SHA |
|
|
194
|
+
| `PROJECT_VERSION` | `package.json` | Version from package.json |
|
|
195
|
+
|
|
125
196
|
## Environment Variables Reference
|
|
126
197
|
|
|
127
198
|
### Application Variables
|
|
@@ -169,6 +240,8 @@ Follow naming from vendor documentation. If documentation does not reference env
|
|
|
169
240
|
|
|
170
241
|
## Secrets
|
|
171
242
|
|
|
243
|
+
By default, no secrets are required. Dependencies add secrets as needed.
|
|
244
|
+
|
|
172
245
|
### Secret Naming
|
|
173
246
|
|
|
174
247
|
Secrets follow the same conventions as variables. Use `SECRET_` prefix for secrets that will be resolved at runtime:
|
|
@@ -177,6 +250,8 @@ Secrets follow the same conventions as variables. Use `SECRET_` prefix for secre
|
|
|
177
250
|
|
|
178
251
|
### Passing Secrets
|
|
179
252
|
|
|
253
|
+
Secrets are passed to CDK via `JaypieEnvSecret` construct and made available at runtime.
|
|
254
|
+
|
|
180
255
|
Pass secrets only to steps that need them:
|
|
181
256
|
|
|
182
257
|
```yaml
|
|
@@ -190,6 +265,30 @@ Pass secrets only to steps that need them:
|
|
|
190
265
|
|
|
191
266
|
Never expose secrets in logs or workflow outputs.
|
|
192
267
|
|
|
268
|
+
### Adding Dependency Secrets
|
|
269
|
+
|
|
270
|
+
When adding dependencies that require secrets, update the workflow accordingly.
|
|
271
|
+
|
|
272
|
+
#### Example: Auth0 Integration
|
|
273
|
+
|
|
274
|
+
**Environment Variables:**
|
|
275
|
+
- `AUTH0_AUDIENCE` - API identifier
|
|
276
|
+
- `AUTH0_CLIENT_ID` - Application client ID
|
|
277
|
+
- `AUTH0_DOMAIN` - Auth0 tenant domain
|
|
278
|
+
|
|
279
|
+
**Environment Secrets:**
|
|
280
|
+
- `AUTH0_CLIENT_SECRET` - Application client secret
|
|
281
|
+
|
|
282
|
+
Add to workflow:
|
|
283
|
+
```yaml
|
|
284
|
+
- name: Deploy CDK Stack
|
|
285
|
+
uses: ./.github/actions/cdk-deploy
|
|
286
|
+
with:
|
|
287
|
+
stack-name: AppStack
|
|
288
|
+
env:
|
|
289
|
+
AUTH0_CLIENT_SECRET: ${{ secrets.AUTH0_CLIENT_SECRET }}
|
|
290
|
+
```
|
|
291
|
+
|
|
193
292
|
## CDK Stack Names
|
|
194
293
|
|
|
195
294
|
The `stack-name` parameter in deploy workflows must match the stack ID defined in `packages/cdk/bin/cdk.ts`:
|
|
@@ -200,7 +299,7 @@ new AppStack(app, "AppStack"); // "AppStack" is the stack ID
|
|
|
200
299
|
```
|
|
201
300
|
|
|
202
301
|
```yaml
|
|
203
|
-
# deploy-*.yml
|
|
302
|
+
# deploy-env-*.yml
|
|
204
303
|
- name: Deploy CDK Stack
|
|
205
304
|
uses: ./.github/actions/cdk-deploy
|
|
206
305
|
with:
|
|
@@ -209,6 +308,34 @@ new AppStack(app, "AppStack"); // "AppStack" is the stack ID
|
|
|
209
308
|
|
|
210
309
|
If you rename the stack ID (e.g., to "MyProjectAppStack" for clarity in AWS), update all deploy workflows to match. Mismatched names cause "No stacks match the name(s)" errors.
|
|
211
310
|
|
|
311
|
+
## Environment-Aware Hostnames
|
|
312
|
+
|
|
313
|
+
Jaypie CDK constructs use `envHostname()` from `@jaypie/constructs` to determine deployment hostnames based on `PROJECT_ENV`:
|
|
314
|
+
|
|
315
|
+
| `PROJECT_ENV` | Example Hostname |
|
|
316
|
+
|---------------|------------------|
|
|
317
|
+
| `production` | `example.com` (apex domain) |
|
|
318
|
+
| `sandbox` | `sandbox.example.com` |
|
|
319
|
+
| `development` | `development.example.com` |
|
|
320
|
+
| `personal` | `personal.example.com` |
|
|
321
|
+
|
|
322
|
+
**CRITICAL**: If `PROJECT_ENV` is not set or is set incorrectly in the GitHub environment, deployments may target the wrong hostname (e.g., deploying sandbox changes to production apex).
|
|
323
|
+
|
|
324
|
+
### How It Works
|
|
325
|
+
|
|
326
|
+
1. The `setup-environment` action reads `${{ vars.PROJECT_ENV }}` from the GitHub environment
|
|
327
|
+
2. This is exported as `PROJECT_ENV` to `$GITHUB_ENV`
|
|
328
|
+
3. CDK constructs read `process.env.PROJECT_ENV` via `envHostname()`
|
|
329
|
+
4. For production, the environment prefix is omitted (apex domain)
|
|
330
|
+
5. For all other environments, the prefix is prepended
|
|
331
|
+
|
|
332
|
+
### Troubleshooting Wrong Hostname
|
|
333
|
+
|
|
334
|
+
If deployment targets the wrong hostname:
|
|
335
|
+
1. Go to GitHub Settings → Environments
|
|
336
|
+
2. Select the environment (sandbox, development, production)
|
|
337
|
+
3. Verify `PROJECT_ENV` variable matches the environment name exactly
|
|
338
|
+
|
|
212
339
|
## Integrations
|
|
213
340
|
|
|
214
341
|
### AWS
|
|
@@ -21,21 +21,21 @@ All entities share a single DynamoDB table with:
|
|
|
21
21
|
- **Primary Key**: `model` (partition) + `id` (sort)
|
|
22
22
|
- **Five GSIs**: All use `sequence` as sort key for chronological ordering
|
|
23
23
|
|
|
24
|
-
###
|
|
24
|
+
### Scope
|
|
25
25
|
|
|
26
|
-
The `
|
|
27
|
-
- Root entities: `
|
|
28
|
-
- Child entities: `
|
|
26
|
+
The `scope` field creates hierarchical relationships:
|
|
27
|
+
- Root entities: `scope = "@"` (APEX constant)
|
|
28
|
+
- Child entities: `scope = "{parent.model}#{parent.id}"`
|
|
29
29
|
|
|
30
30
|
### GSI Pattern
|
|
31
31
|
|
|
32
32
|
| GSI Name | Partition Key | Use Case |
|
|
33
33
|
|----------|---------------|----------|
|
|
34
|
-
| `
|
|
35
|
-
| `indexAlias` | `{
|
|
36
|
-
| `indexClass` | `{
|
|
37
|
-
| `indexType` | `{
|
|
38
|
-
| `indexXid` | `{
|
|
34
|
+
| `indexScope` | `{scope}#{model}` | List all entities under a parent |
|
|
35
|
+
| `indexAlias` | `{scope}#{model}#{alias}` | Human-friendly slug lookup |
|
|
36
|
+
| `indexClass` | `{scope}#{model}#{class}` | Category filtering |
|
|
37
|
+
| `indexType` | `{scope}#{model}#{type}` | Type filtering |
|
|
38
|
+
| `indexXid` | `{scope}#{model}#{xid}` | External system ID lookup |
|
|
39
39
|
|
|
40
40
|
## Client Initialization
|
|
41
41
|
|
|
@@ -75,7 +75,7 @@ interface MyRecord extends StorableEntity {
|
|
|
75
75
|
|
|
76
76
|
// Required fields
|
|
77
77
|
name: string;
|
|
78
|
-
|
|
78
|
+
scope: string; // APEX or hierarchical
|
|
79
79
|
sequence: number; // Date.now()
|
|
80
80
|
|
|
81
81
|
// Timestamps (ISO 8601)
|
|
@@ -108,7 +108,7 @@ const record = await putEntity({
|
|
|
108
108
|
model: "record",
|
|
109
109
|
id: crypto.randomUUID(),
|
|
110
110
|
name: "Daily Log",
|
|
111
|
-
|
|
111
|
+
scope: APEX,
|
|
112
112
|
sequence: Date.now(),
|
|
113
113
|
alias: "2026-01-07", // Optional
|
|
114
114
|
class: "memory", // Optional
|
|
@@ -118,7 +118,7 @@ const record = await putEntity({
|
|
|
118
118
|
});
|
|
119
119
|
|
|
120
120
|
// Result includes auto-populated indexes:
|
|
121
|
-
//
|
|
121
|
+
// indexScope: "@#record"
|
|
122
122
|
// indexAlias: "@#record#2026-01-07"
|
|
123
123
|
// indexClass: "@#record#memory"
|
|
124
124
|
```
|
|
@@ -187,16 +187,16 @@ await destroyEntity({ id: "123", model: "record" });
|
|
|
187
187
|
|
|
188
188
|
### Hierarchical Entities
|
|
189
189
|
|
|
190
|
-
Use `
|
|
190
|
+
Use `calculateScope` to derive scope from parent:
|
|
191
191
|
|
|
192
192
|
```typescript
|
|
193
|
-
import {
|
|
193
|
+
import { calculateScope, putEntity, queryByScope } from "@jaypie/dynamodb";
|
|
194
194
|
|
|
195
195
|
// Parent reference
|
|
196
196
|
const chat = { model: "chat", id: "abc-123" };
|
|
197
197
|
|
|
198
|
-
// Calculate child
|
|
199
|
-
const
|
|
198
|
+
// Calculate child scope
|
|
199
|
+
const messageScope = calculateScope(chat); // "chat#abc-123"
|
|
200
200
|
|
|
201
201
|
// Create child entity
|
|
202
202
|
const message = await putEntity({
|
|
@@ -204,39 +204,39 @@ const message = await putEntity({
|
|
|
204
204
|
model: "message",
|
|
205
205
|
id: crypto.randomUUID(),
|
|
206
206
|
name: "First message",
|
|
207
|
-
|
|
207
|
+
scope: messageScope,
|
|
208
208
|
sequence: Date.now(),
|
|
209
209
|
createdAt: now,
|
|
210
210
|
updatedAt: now,
|
|
211
211
|
},
|
|
212
212
|
});
|
|
213
|
-
//
|
|
213
|
+
// indexScope: "chat#abc-123#message"
|
|
214
214
|
|
|
215
215
|
// Query all messages in chat
|
|
216
|
-
const { items } = await
|
|
216
|
+
const { items } = await queryByScope({ model: "message", scope: messageScope });
|
|
217
217
|
```
|
|
218
218
|
|
|
219
219
|
## Query Functions
|
|
220
220
|
|
|
221
221
|
All queries use object parameters and filter soft-deleted and archived records by default.
|
|
222
222
|
|
|
223
|
-
###
|
|
223
|
+
### queryByScope - List by Parent
|
|
224
224
|
|
|
225
225
|
List all entities of a model under a parent:
|
|
226
226
|
|
|
227
227
|
```typescript
|
|
228
|
-
import { APEX,
|
|
228
|
+
import { APEX, queryByScope } from "@jaypie/dynamodb";
|
|
229
229
|
|
|
230
230
|
// Root-level records
|
|
231
|
-
const { items, lastEvaluatedKey } = await
|
|
231
|
+
const { items, lastEvaluatedKey } = await queryByScope({
|
|
232
232
|
model: "record",
|
|
233
|
-
|
|
233
|
+
scope: APEX,
|
|
234
234
|
});
|
|
235
235
|
|
|
236
236
|
// Messages under a chat
|
|
237
|
-
const { items: messages } = await
|
|
237
|
+
const { items: messages } = await queryByScope({
|
|
238
238
|
model: "message",
|
|
239
|
-
|
|
239
|
+
scope: "chat#abc-123",
|
|
240
240
|
});
|
|
241
241
|
```
|
|
242
242
|
|
|
@@ -250,7 +250,7 @@ import { APEX, queryByAlias } from "@jaypie/dynamodb";
|
|
|
250
250
|
const record = await queryByAlias({
|
|
251
251
|
alias: "2026-01-07",
|
|
252
252
|
model: "record",
|
|
253
|
-
|
|
253
|
+
scope: APEX,
|
|
254
254
|
});
|
|
255
255
|
// Returns entity or null
|
|
256
256
|
```
|
|
@@ -263,14 +263,14 @@ import { APEX, queryByClass, queryByType } from "@jaypie/dynamodb";
|
|
|
263
263
|
// All memory records
|
|
264
264
|
const { items } = await queryByClass({
|
|
265
265
|
model: "record",
|
|
266
|
-
|
|
266
|
+
scope: APEX,
|
|
267
267
|
recordClass: "memory",
|
|
268
268
|
});
|
|
269
269
|
|
|
270
270
|
// All note-type records
|
|
271
271
|
const { items: notes } = await queryByType({
|
|
272
272
|
model: "record",
|
|
273
|
-
|
|
273
|
+
scope: APEX,
|
|
274
274
|
type: "note",
|
|
275
275
|
});
|
|
276
276
|
```
|
|
@@ -284,7 +284,7 @@ import { APEX, queryByXid } from "@jaypie/dynamodb";
|
|
|
284
284
|
|
|
285
285
|
const record = await queryByXid({
|
|
286
286
|
model: "record",
|
|
287
|
-
|
|
287
|
+
scope: APEX,
|
|
288
288
|
xid: "ext-12345",
|
|
289
289
|
});
|
|
290
290
|
// Returns entity or null
|
|
@@ -295,9 +295,9 @@ const record = await queryByXid({
|
|
|
295
295
|
```typescript
|
|
296
296
|
import type { BaseQueryOptions } from "@jaypie/dynamodb";
|
|
297
297
|
|
|
298
|
-
const result = await
|
|
298
|
+
const result = await queryByScope({
|
|
299
299
|
model: "record",
|
|
300
|
-
|
|
300
|
+
scope: APEX,
|
|
301
301
|
// BaseQueryOptions:
|
|
302
302
|
limit: 25, // Max items to return
|
|
303
303
|
ascending: true, // Oldest first (default: false = newest first)
|
|
@@ -310,15 +310,15 @@ const result = await queryByOu({
|
|
|
310
310
|
### Pagination
|
|
311
311
|
|
|
312
312
|
```typescript
|
|
313
|
-
import { APEX,
|
|
313
|
+
import { APEX, queryByScope } from "@jaypie/dynamodb";
|
|
314
314
|
|
|
315
315
|
let startKey: Record<string, unknown> | undefined;
|
|
316
316
|
const allItems: StorableEntity[] = [];
|
|
317
317
|
|
|
318
318
|
do {
|
|
319
|
-
const { items, lastEvaluatedKey } = await
|
|
319
|
+
const { items, lastEvaluatedKey } = await queryByScope({
|
|
320
320
|
model: "record",
|
|
321
|
-
|
|
321
|
+
scope: APEX,
|
|
322
322
|
limit: 100,
|
|
323
323
|
startKey,
|
|
324
324
|
});
|
|
@@ -337,11 +337,11 @@ import { indexEntity } from "@jaypie/dynamodb";
|
|
|
337
337
|
const indexed = indexEntity({
|
|
338
338
|
model: "record",
|
|
339
339
|
id: "123",
|
|
340
|
-
|
|
340
|
+
scope: "@",
|
|
341
341
|
alias: "my-alias",
|
|
342
342
|
// ...
|
|
343
343
|
});
|
|
344
|
-
//
|
|
344
|
+
// indexScope: "@#record"
|
|
345
345
|
// indexAlias: "@#record#my-alias"
|
|
346
346
|
```
|
|
347
347
|
|
|
@@ -349,14 +349,14 @@ Use individual key builders for manual key construction:
|
|
|
349
349
|
|
|
350
350
|
```typescript
|
|
351
351
|
import {
|
|
352
|
-
|
|
352
|
+
buildIndexScope,
|
|
353
353
|
buildIndexAlias,
|
|
354
354
|
buildIndexClass,
|
|
355
355
|
buildIndexType,
|
|
356
356
|
buildIndexXid,
|
|
357
357
|
} from "@jaypie/dynamodb";
|
|
358
358
|
|
|
359
|
-
|
|
359
|
+
buildIndexScope("@", "record"); // "@#record"
|
|
360
360
|
buildIndexAlias("@", "record", "my-alias"); // "@#record#my-alias"
|
|
361
361
|
buildIndexClass("@", "record", "memory"); // "@#record#memory"
|
|
362
362
|
buildIndexType("@", "record", "note"); // "@#record#note"
|
|
@@ -369,7 +369,7 @@ buildIndexXid("@", "record", "ext-123"); // "@#record#ext-123"
|
|
|
369
369
|
import {
|
|
370
370
|
APEX, // "@" - Root-level marker
|
|
371
371
|
SEPARATOR, // "#" - Composite key separator
|
|
372
|
-
|
|
372
|
+
INDEX_SCOPE, // "indexScope"
|
|
373
373
|
INDEX_ALIAS, // "indexAlias"
|
|
374
374
|
INDEX_CLASS, // "indexClass"
|
|
375
375
|
INDEX_TYPE, // "indexType"
|
|
@@ -385,7 +385,7 @@ AttributeDefinitions:
|
|
|
385
385
|
AttributeType: S
|
|
386
386
|
- AttributeName: id
|
|
387
387
|
AttributeType: S
|
|
388
|
-
- AttributeName:
|
|
388
|
+
- AttributeName: indexScope
|
|
389
389
|
AttributeType: S
|
|
390
390
|
- AttributeName: indexAlias
|
|
391
391
|
AttributeType: S
|
|
@@ -405,9 +405,9 @@ KeySchema:
|
|
|
405
405
|
KeyType: RANGE
|
|
406
406
|
|
|
407
407
|
GlobalSecondaryIndexes:
|
|
408
|
-
- IndexName:
|
|
408
|
+
- IndexName: indexScope
|
|
409
409
|
KeySchema:
|
|
410
|
-
- AttributeName:
|
|
410
|
+
- AttributeName: indexScope
|
|
411
411
|
KeyType: HASH
|
|
412
412
|
- AttributeName: sequence
|
|
413
413
|
KeyType: RANGE
|
|
@@ -445,7 +445,7 @@ const created = await seedEntityIfNotExists({
|
|
|
445
445
|
alias: "config-main",
|
|
446
446
|
model: "config",
|
|
447
447
|
name: "Main Configuration",
|
|
448
|
-
|
|
448
|
+
scope: APEX,
|
|
449
449
|
});
|
|
450
450
|
// Returns true if created, false if already exists
|
|
451
451
|
```
|
|
@@ -458,8 +458,8 @@ Seed multiple entities with idempotency:
|
|
|
458
458
|
import { APEX, seedEntities } from "@jaypie/dynamodb";
|
|
459
459
|
|
|
460
460
|
const result = await seedEntities([
|
|
461
|
-
{ alias: "vocab-en", model: "vocabulary", name: "English",
|
|
462
|
-
{ alias: "vocab-es", model: "vocabulary", name: "Spanish",
|
|
461
|
+
{ alias: "vocab-en", model: "vocabulary", name: "English", scope: APEX },
|
|
462
|
+
{ alias: "vocab-es", model: "vocabulary", name: "Spanish", scope: APEX },
|
|
463
463
|
]);
|
|
464
464
|
// result.created: aliases of created entities
|
|
465
465
|
// result.skipped: aliases of entities that already existed
|
|
@@ -476,7 +476,7 @@ Auto-generates `id` (UUID), `createdAt`, `updatedAt`, and `sequence` if missing.
|
|
|
476
476
|
|
|
477
477
|
### exportEntities
|
|
478
478
|
|
|
479
|
-
Export entities by model and
|
|
479
|
+
Export entities by model and scope:
|
|
480
480
|
|
|
481
481
|
```typescript
|
|
482
482
|
import { APEX, exportEntities } from "@jaypie/dynamodb";
|
|
@@ -546,11 +546,11 @@ import {
|
|
|
546
546
|
exportEntities,
|
|
547
547
|
getEntity,
|
|
548
548
|
putEntity,
|
|
549
|
-
|
|
549
|
+
queryByScope,
|
|
550
550
|
seedEntities,
|
|
551
551
|
} from "@jaypie/testkit/mock";
|
|
552
552
|
|
|
553
|
-
|
|
553
|
+
queryByScope.mockResolvedValue({
|
|
554
554
|
items: [{ id: "123", name: "Test" }],
|
|
555
555
|
lastEvaluatedKey: undefined,
|
|
556
556
|
});
|
|
@@ -644,12 +644,12 @@ await deleteEntity({ id: "123", model: "record" });
|
|
|
644
644
|
await archiveEntity({ id: "123", model: "record" });
|
|
645
645
|
|
|
646
646
|
// Queries exclude both by default
|
|
647
|
-
const { items } = await
|
|
647
|
+
const { items } = await queryByScope({ model: "record", scope: APEX });
|
|
648
648
|
|
|
649
649
|
// Include if needed
|
|
650
|
-
const { items: all } = await
|
|
650
|
+
const { items: all } = await queryByScope({
|
|
651
651
|
model: "record",
|
|
652
|
-
|
|
652
|
+
scope: APEX,
|
|
653
653
|
includeDeleted: true,
|
|
654
654
|
includeArchived: true,
|
|
655
655
|
});
|
|
@@ -704,7 +704,7 @@ const { tools } = registerDynamoDbTools({ server });
|
|
|
704
704
|
#### Query Operations
|
|
705
705
|
| Tool | Description |
|
|
706
706
|
|------|-------------|
|
|
707
|
-
| `
|
|
707
|
+
| `dynamodb_query_scope` | Query by scope |
|
|
708
708
|
| `dynamodb_query_alias` | Query by human-friendly alias |
|
|
709
709
|
| `dynamodb_query_class` | Query by category classification |
|
|
710
710
|
| `dynamodb_query_type` | Query by type classification |
|
|
@@ -753,15 +753,15 @@ Generate docker-compose and create table:
|
|
|
753
753
|
"id": "abc-123",
|
|
754
754
|
"model": "record",
|
|
755
755
|
"name": "My Record",
|
|
756
|
-
"
|
|
756
|
+
"scope": "@",
|
|
757
757
|
"alias": "my-record",
|
|
758
758
|
"class": "memory"
|
|
759
759
|
}
|
|
760
760
|
|
|
761
|
-
//
|
|
761
|
+
// dynamodb_query_scope
|
|
762
762
|
{
|
|
763
763
|
"model": "record",
|
|
764
|
-
"
|
|
764
|
+
"scope": "@",
|
|
765
765
|
"limit": 10
|
|
766
766
|
}
|
|
767
767
|
|