@jaypie/mcp 0.7.16 → 0.7.18

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.
@@ -9,7 +9,7 @@ import { gt } from 'semver';
9
9
  /**
10
10
  * Docs Suite - Documentation services (skill, version, release_notes)
11
11
  */
12
- const BUILD_VERSION_STRING = "@jaypie/mcp@0.7.16#d8b8c444"
12
+ const BUILD_VERSION_STRING = "@jaypie/mcp@0.7.18#db5c9ff1"
13
13
  ;
14
14
  const __filename$1 = fileURLToPath(import.meta.url);
15
15
  const __dirname$1 = path.dirname(__filename$1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/mcp",
3
- "version": "0.7.16",
3
+ "version": "0.7.18",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,15 @@
1
+ ---
2
+ version: 1.2.30
3
+ date: 2026-02-13
4
+ summary: Default security headers for JaypieDistribution
5
+ ---
6
+
7
+ # @jaypie/constructs 1.2.30
8
+
9
+ ## Features
10
+
11
+ - **JaypieDistribution**: Ships with default security response headers via a `ResponseHeadersPolicy`, analogous to `helmet` for Express. Headers include HSTS, X-Content-Type-Options, X-Frame-Options (DENY), Referrer-Policy, Content-Security-Policy, Permissions-Policy, Cross-Origin-Opener-Policy, Cross-Origin-Resource-Policy, and Server removal.
12
+ - **JaypieDistribution**: New `securityHeaders` prop — `true` (default) applies sensible defaults, `false` disables, or pass a `SecurityHeadersOverrides` object to selectively override individual headers.
13
+ - **JaypieDistribution**: New `responseHeadersPolicy` prop for full override with a custom `IResponseHeadersPolicy`.
14
+ - **SecurityHeadersOverrides**: New exported interface for selective security header configuration.
15
+ - **CDK.SECURITY_HEADERS**: New constants for default CSP, HSTS max-age, and Permissions-Policy values.
@@ -0,0 +1,10 @@
1
+ ---
2
+ version: 0.7.18
3
+ date: 2026-02-14
4
+ summary: Fix dynamodb skill documentation errors
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - Fix non-existent `JaypieTable` reference to `JaypieDynamoDb` in dynamodb skill
10
+ - Add minimal-index guidance: start with zero GSIs, add only when access patterns demand
package/skills/cdk.md CHANGED
@@ -169,6 +169,40 @@ new JaypieNextJs(this, "App", {
169
169
 
170
170
  **Streaming Note:** When `streaming: true`, also create `open-next.config.ts` in your Next.js app with `wrapper: "aws-lambda-streaming"`. See `skill("streaming")` for details.
171
171
 
172
+ ## Security Headers
173
+
174
+ `JaypieDistribution` ships with default security response headers via a `ResponseHeadersPolicy` (analogous to `helmet` for Express):
175
+
176
+ - HSTS (2-year max-age, includeSubDomains, preload)
177
+ - X-Content-Type-Options (nosniff)
178
+ - X-Frame-Options (DENY)
179
+ - Referrer-Policy (strict-origin-when-cross-origin)
180
+ - Content-Security-Policy (conservative defaults)
181
+ - Permissions-Policy (camera, microphone, geolocation, payment disabled)
182
+ - Cross-Origin-Opener-Policy (same-origin)
183
+ - Cross-Origin-Resource-Policy (same-origin)
184
+ - Server header removed
185
+
186
+ ```typescript
187
+ // Disable security headers
188
+ new JaypieDistribution(this, "Dist", { handler, securityHeaders: false });
189
+
190
+ // Override specific headers
191
+ new JaypieDistribution(this, "Dist", {
192
+ handler,
193
+ securityHeaders: {
194
+ contentSecurityPolicy: "default-src 'self';",
195
+ frameOption: HeadersFrameOption.SAMEORIGIN,
196
+ },
197
+ });
198
+
199
+ // Full custom policy override
200
+ new JaypieDistribution(this, "Dist", {
201
+ handler,
202
+ responseHeadersPolicy: myCustomPolicy,
203
+ });
204
+ ```
205
+
172
206
  ## See Also
173
207
 
174
208
  - **`skill("streaming")`** - JaypieDistribution and JaypieNextJs streaming configuration
@@ -65,7 +65,9 @@ const result = await client.send(new QueryCommand({
65
65
 
66
66
  ## GSI Patterns
67
67
 
68
- ### By-Status Index
68
+ Start with zero GSIs. Add indexes only when a real access pattern requires them — GSIs cost money and can always be added later.
69
+
70
+ ### By-Status Index (example)
69
71
 
70
72
  ```typescript
71
73
  // GSI for querying by status across all users
@@ -92,18 +94,18 @@ const result = await client.send(new QueryCommand({
92
94
 
93
95
  ## CDK Table Definition
94
96
 
97
+ Start with a basic table and add indexes only when access patterns demand them.
98
+
95
99
  ```typescript
96
- import { JaypieTable } from "@jaypie/constructs";
97
-
98
- const table = new JaypieTable(this, "DataTable", {
99
- partitionKey: { name: "pk", type: AttributeType.STRING },
100
- sortKey: { name: "sk", type: AttributeType.STRING },
101
- globalSecondaryIndexes: [
102
- {
103
- indexName: "gsi1",
104
- partitionKey: { name: "gsi1pk", type: AttributeType.STRING },
105
- sortKey: { name: "gsi1sk", type: AttributeType.STRING },
106
- },
100
+ import { JaypieDynamoDb } from "@jaypie/constructs";
101
+
102
+ // Recommended: start with no indexes
103
+ const table = new JaypieDynamoDb(this, "myApp");
104
+
105
+ // Add indexes later when driven by real access patterns
106
+ const table = new JaypieDynamoDb(this, "myApp", {
107
+ indexes: [
108
+ { pk: ["scope", "model"], sk: ["sequence"] },
107
109
  ],
108
110
  });
109
111
  ```
@@ -0,0 +1,200 @@
1
+ ---
2
+ description: Seeded deterministic test data generation with @jaypie/fabricator
3
+ related: tests, mocks, models
4
+ ---
5
+
6
+ # Fabricator
7
+
8
+ `@jaypie/fabricator` provides seeded, deterministic data generation built on `@faker-js/faker`.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install @jaypie/fabricator
14
+ ```
15
+
16
+ ## Core: Fabricator Class
17
+
18
+ Wraps faker.js with deterministic seeding. Same seed = same data.
19
+
20
+ ```typescript
21
+ import { Fabricator } from "@jaypie/fabricator";
22
+
23
+ const fab = new Fabricator("my-seed");
24
+ fab.internet.email(); // Always the same for "my-seed"
25
+ fab.person.firstName(); // Proxies to faker.js
26
+ fab.id; // UUID derived from seed
27
+ fab.name; // Auto-generated word pair
28
+ ```
29
+
30
+ ### Factory Function
31
+
32
+ ```typescript
33
+ import { fabricator } from "@jaypie/fabricator";
34
+
35
+ const fab = fabricator("my-seed");
36
+ const fab = fabricator({ seed: "my-seed", name: "Custom Name" });
37
+ ```
38
+
39
+ ### Environment Seed
40
+
41
+ ```typescript
42
+ process.env.PROJECT_SEED = "test-seed";
43
+ const fab = new Fabricator(); // Falls back to PROJECT_SEED
44
+ ```
45
+
46
+ ## Random Number Generation
47
+
48
+ ```typescript
49
+ const fab = new Fabricator("seed");
50
+
51
+ fab.random(); // 0-1 float
52
+ fab.random({ min: 1, max: 10, integer: true }); // 1-10 integer
53
+ fab.random({ mean: 50, stddev: 10 }); // Normal distribution
54
+ fab.random({ min: 10, max: 100, currency: true }); // 2 decimal places
55
+ fab.random({ min: 0, max: 1, precision: 4 }); // 4 decimal places
56
+ ```
57
+
58
+ Standalone:
59
+
60
+ ```typescript
61
+ import { random } from "@jaypie/fabricator";
62
+
63
+ const rng = random("my-seed");
64
+ rng({ min: 1, max: 100, integer: true });
65
+ ```
66
+
67
+ ## Built-in Generators
68
+
69
+ ### Words
70
+
71
+ ```typescript
72
+ fab.generate.words(); // "adjective noun", "adjective verb", or "noun verb"
73
+ ```
74
+
75
+ ### Person
76
+
77
+ Generates realistic person objects with probabilistic variations using Jaypie golden numbers:
78
+
79
+ ```typescript
80
+ fab.generate.person();
81
+ // { id, firstName, middleName, lastName, fullName }
82
+ ```
83
+
84
+ - **UNCOMMON (14.6%)**: middleName missing, fullName includes middle
85
+ - **RARE (2.1%)**: firstName is a surname, lastName is hyphenated
86
+ - **EPIC (0.307%)**: double middle names
87
+
88
+ ## CHANCE Constants
89
+
90
+ ```typescript
91
+ import { CHANCE } from "@jaypie/fabricator/constants";
92
+
93
+ CHANCE.UNCOMMON; // 0.146 (14.6%)
94
+ CHANCE.RARE; // 0.021 (2.1%)
95
+ CHANCE.EPIC; // 0.00307 (0.307%)
96
+ CHANCE.LEGENDARY; // 0.000441 (0.0441%)
97
+ ```
98
+
99
+ ## Nested Fabricators
100
+
101
+ Create hierarchical fabricators with `Fabricator.new`:
102
+
103
+ ```typescript
104
+ const world = Fabricator.new({
105
+ seed: "earth",
106
+ name: ({ fabricator }) => fabricator.generate.words(),
107
+ fabricators: {
108
+ cities: { name: ({ fabricator }) => fabricator.location.city() },
109
+ exports: { name: ({ fabricator }) => fabricator.commerce.product() },
110
+ },
111
+ });
112
+
113
+ world.cities(5); // Array of 5 city fabricators
114
+ for (const c of world.cities()) break; // Infinite generator
115
+ ```
116
+
117
+ ## EventFabricator
118
+
119
+ Abstract base for generating temporally-distributed events across a year.
120
+
121
+ ```typescript
122
+ import { EventFabricator } from "@jaypie/fabricator";
123
+
124
+ class OrderFabricator extends EventFabricator<Order> {
125
+ protected createEvent({ timestamp, seed, index }) {
126
+ return { id: seed, timestamp, amount: 100 };
127
+ }
128
+ }
129
+
130
+ const fab = new OrderFabricator({
131
+ seed: "orders-2025",
132
+ annualCount: 10000,
133
+ template: ["HOURS_BUSINESS", "DAYS_WEEKDAYS_ONLY", "BOOST_HOLIDAY_SEASON"],
134
+ timezone: "America/New_York",
135
+ });
136
+
137
+ const orders = fab.events({ year: 2025 });
138
+ ```
139
+
140
+ ### Temporal Templates
141
+
142
+ Combine templates for realistic distribution patterns:
143
+
144
+ | Category | Templates |
145
+ |----------|-----------|
146
+ | Hours | `HOURS_BUSINESS`, `HOURS_RETAIL`, `HOURS_EVENING`, `HOURS_OVERNIGHT`, `HOURS_24_7`, etc. |
147
+ | Days | `DAYS_WEEKDAYS_ONLY`, `DAYS_NO_SUNDAY`, `DAYS_NO_MONDAY`, `DAYS_NO_SUNDAY_MONDAY` |
148
+ | Dates | `DATES_15_AND_25` (billing cycles) |
149
+ | Seasonal | `SEASON_SUMMER_ONLY`, `SEASON_WINTER_ONLY`, `SEASON_NO_SUMMER`, `SEASON_NO_WINTER` |
150
+ | Curves | `CURVE_EVENING_PEAK`, `CURVE_ECOMMERCE`, `CURVE_MIDDAY_PEAK` |
151
+ | Spikes | `SPIKE_MORNING`, `SPIKE_LUNCH`, `SPIKE_EVENING` |
152
+ | Boosts | `BOOST_SUMMER`, `BOOST_WINTER`, `BOOST_WEEKENDS`, `BOOST_HOLIDAY_SEASON` |
153
+ | Lulls | `LULL_SUMMER`, `LULL_WINTER`, `LULL_WEEKENDS`, `LULL_WEEKDAYS` |
154
+
155
+ ### Derived Events
156
+
157
+ Generate cascading follow-up events from parent events:
158
+
159
+ ```typescript
160
+ const fab = new TransactionFabricator({
161
+ seed: "txn",
162
+ annualCount: 500,
163
+ derived: {
164
+ maxDepth: 3,
165
+ rules: [
166
+ {
167
+ name: "refund",
168
+ probability: CHANCE.UNCOMMON,
169
+ timing: { mode: "range", delayMin: 1, delayMax: 30, unit: "days" },
170
+ createDerived: ({ parent, seed, timestamp }) => ({
171
+ id: seed,
172
+ type: "refund",
173
+ amount: -parent.amount,
174
+ timestamp,
175
+ parentId: parent.id,
176
+ }),
177
+ },
178
+ ],
179
+ },
180
+ });
181
+ ```
182
+
183
+ #### Timing Modes
184
+
185
+ | Mode | Description |
186
+ |------|-------------|
187
+ | `fixed` | Exact delay from parent |
188
+ | `range` | Random delay within min/max |
189
+ | `recurring` | Repeated at interval with `maxRecurrences` or `until` |
190
+ | `same-day` | Same day as parent event |
191
+
192
+ ## Utilities
193
+
194
+ ```typescript
195
+ import { isUuid, numericSeed, uuidFrom } from "@jaypie/fabricator";
196
+
197
+ isUuid("550e8400-e29b-41d4-a716-446655440000"); // true
198
+ numericSeed("my-string"); // Deterministic number
199
+ uuidFrom("any-value"); // UUID v5 from string
200
+ ```
package/skills/secrets.md CHANGED
@@ -92,7 +92,7 @@ The construct auto-detects consumer mode for personal/ephemeral environments (`P
92
92
 
93
93
  ## Generated Secrets
94
94
 
95
- For secrets without a source value (e.g., database passwords):
95
+ For secrets that don't come from an external source, use `generateSecretString`. CloudFormation generates the value on the **first deploy only** and preserves it across subsequent deploys. The value is never visible in templates, logs, or CI/CD output — it exists only inside AWS Secrets Manager and is retrievable at runtime.
96
96
 
97
97
  ```typescript
98
98
  new JaypieEnvSecret(this, "DB_PASSWORD", {
@@ -103,6 +103,93 @@ new JaypieEnvSecret(this, "DB_PASSWORD", {
103
103
  });
104
104
  ```
105
105
 
106
+ This is the preferred pattern for any secret that can be randomly generated. No GitHub secret or workflow variable is needed — the CDK deploy creates and manages the value automatically.
107
+
108
+ ### Base62 Generated Secrets
109
+
110
+ For secrets that must be base62-safe (`0-9A-Za-z` only), such as seeds and signing keys:
111
+
112
+ ```typescript
113
+ new JaypieEnvSecret(this, "AdminSeed", {
114
+ envKey: "PROJECT_ADMIN_SEED",
115
+ generateSecretString: {
116
+ excludePunctuation: true,
117
+ includeSpace: false,
118
+ passwordLength: 64,
119
+ },
120
+ });
121
+ ```
122
+
123
+ With `excludePunctuation: true` and `includeSpace: false`, the generated value contains only base62 characters.
124
+
125
+ ## Seeds and API Keys in Workflows
126
+
127
+ Seeds are secrets used to derive deterministic values (like API keys) at runtime. The generated-secret pattern is ideal for seeds because:
128
+
129
+ 1. **Set once**: CloudFormation generates the value on the first deploy. Subsequent deploys do not regenerate it.
130
+ 2. **Never known**: No human ever sees the seed value. It exists only in Secrets Manager.
131
+ 3. **Retrievable at runtime**: `loadEnvSecrets("PROJECT_ADMIN_SEED")` populates `process.env` for the handler to derive keys from.
132
+ 4. **Environment-isolated**: Each environment (sandbox, development, production) generates its own independent seed.
133
+
134
+ ### CDK Pattern
135
+
136
+ Create the secret with `generateSecretString` and pass it to the Lambda:
137
+
138
+ ```typescript
139
+ import { JaypieEnvSecret, JaypieExpressLambda } from "@jaypie/constructs";
140
+
141
+ const adminSeed = new JaypieEnvSecret(this, "ProjectAdminSeed", {
142
+ envKey: "PROJECT_ADMIN_SEED",
143
+ generateSecretString: {
144
+ excludePunctuation: true,
145
+ includeSpace: false,
146
+ passwordLength: 64,
147
+ },
148
+ });
149
+
150
+ new JaypieExpressLambda(this, "ApiLambda", {
151
+ code: "dist",
152
+ handler: "index.handler",
153
+ secrets: [adminSeed],
154
+ });
155
+ ```
156
+
157
+ ### Runtime Usage
158
+
159
+ The handler loads the seed at startup and derives keys:
160
+
161
+ ```typescript
162
+ import { expressHandler } from "jaypie";
163
+
164
+ export default expressHandler(handler, {
165
+ secrets: ["PROJECT_ADMIN_SEED"],
166
+ setup: () => initClient(),
167
+ });
168
+ ```
169
+
170
+ At runtime, `process.env.PROJECT_ADMIN_SEED` contains the generated seed value. Application code uses it to derive API keys via HMAC, validate presented keys, and auto-provision on first use.
171
+
172
+ ### Workflow Integration
173
+
174
+ No special workflow steps are needed for generated secrets. The CDK deploy step handles everything:
175
+
176
+ ```yaml
177
+ - name: Deploy CDK Stacks
178
+ uses: ./.github/actions/cdk-deploy
179
+ with:
180
+ stack-name: JaypieGardenApi
181
+ ```
182
+
183
+ On the first deploy, CloudFormation generates the seed. On subsequent deploys, the seed is preserved. The workflow never needs to generate, store, or pass the seed value.
184
+
185
+ ### Why Not GitHub Secrets?
186
+
187
+ For externally-provided credentials (API keys from vendors, database URIs), GitHub environment secrets and env vars are the right approach. But for **internally-generated** secrets like seeds:
188
+
189
+ - `generateSecretString` is simpler — no manual setup per environment
190
+ - The value is never exposed to CI/CD logs or GitHub settings
191
+ - CloudFormation guarantees idempotent creation — first deploy sets, updates preserve
192
+
106
193
  ## Tagging
107
194
 
108
195
  Apply standard tags for organization: