@jaypie/mcp 0.7.15 → 0.7.17

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.15#2d2a7db9"
12
+ const BUILD_VERSION_STRING = "@jaypie/mcp@0.7.17#0cfeac13"
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.15",
3
+ "version": "0.7.17",
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,9 @@
1
+ ---
2
+ version: 0.7.16
3
+ date: 2025-02-07
4
+ summary: Update skill docs to reflect stacks/ to workspaces/ directory rename
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - Updated monorepo, cdk, jaypie, cicd-deploy, and cicd-actions skills to reference `workspaces/` instead of `stacks/`
package/skills/cdk.md CHANGED
@@ -80,10 +80,10 @@ const bucket = new JaypieBucket(this, "AssetsBucket", {
80
80
 
81
81
  ## Stack Structure
82
82
 
83
- Organize stacks in the `stacks/` directory:
83
+ Organize stacks in the `workspaces/` directory:
84
84
 
85
85
  ```
86
- stacks/
86
+ workspaces/
87
87
  ├── cdk/
88
88
  │ ├── src/
89
89
  │ │ ├── app.ts # CDK app entry
@@ -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
@@ -187,7 +187,7 @@ runs:
187
187
  path: |
188
188
  node_modules
189
189
  packages/*/node_modules
190
- stacks/*/node_modules
190
+ workspaces/*/node_modules
191
191
  key: ${{ runner.os }}-node-${{ inputs.node-version }}-${{ hashFiles('**/package-lock.json') }}
192
192
  restore-keys: |
193
193
  ${{ runner.os }}-node-${{ inputs.node-version }}-
@@ -198,8 +198,8 @@ runs:
198
198
  with:
199
199
  path: |
200
200
  packages/*/dist
201
- stacks/*/dist
202
- stacks/*/.open-next
201
+ workspaces/*/dist
202
+ workspaces/*/.open-next
203
203
  key: ${{ runner.os }}-build-${{ github.sha }}
204
204
  restore-keys: |
205
205
  ${{ runner.os }}-build-
@@ -255,7 +255,7 @@ inputs:
255
255
  working-directory:
256
256
  description: 'Working directory for CDK commands'
257
257
  required: false
258
- default: 'stacks/cdk'
258
+ default: 'workspaces/cdk'
259
259
  require-approval:
260
260
  description: 'CDK approval mode (never, any-change, broadening)'
261
261
  required: false
@@ -248,7 +248,7 @@ jobs:
248
248
  Use consistent stack naming with environment and nonce:
249
249
 
250
250
  ```typescript
251
- // stacks/cdk/src/app.ts
251
+ // workspaces/cdk/src/app.ts
252
252
  const env = process.env.PROJECT_ENV || "sandbox";
253
253
  const nonce = process.env.PROJECT_NONCE || "dev";
254
254
 
@@ -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/jaypie.md CHANGED
@@ -53,7 +53,7 @@ project/
53
53
  ├── packages/ # npm packages
54
54
  │ ├── api/ # Express API
55
55
  │ └── lib/ # Shared library
56
- ├── stacks/ # CDK infrastructure
56
+ ├── workspaces/ # CDK infrastructure
57
57
  │ └── cdk/ # CDK app
58
58
  └── package.json # Root workspace config
59
59
  ```
@@ -138,7 +138,7 @@ npm install --save-dev @jaypie/eslint @jaypie/testkit eslint rimraf sort-package
138
138
  | Directory | Purpose |
139
139
  |-----------|---------|
140
140
  | `packages/` | npm packages (default workspace) |
141
- | `stacks/` | CDK-deployed infrastructure and sites |
141
+ | `workspaces/` | CDK-deployed infrastructure and sites |
142
142
 
143
143
  ## Scripts Reference
144
144
 
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: