@jaypie/mcp 0.8.53 → 0.8.55

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.
@@ -1 +1 @@
1
- {"version":3,"file":"createMcpServer.js","sources":["../src/createMcpServer.ts"],"sourcesContent":["import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport { createMcpServerFromSuite } from \"@jaypie/fabric/mcp\";\n\nimport { suite } from \"./suite.js\";\n\nexport interface CreateMcpServerOptions {\n version?: string;\n verbose?: boolean;\n}\n\n/**\n * Creates and configures an MCP server instance with Jaypie tools\n *\n * Uses ServiceSuite to register all services as MCP tools automatically.\n * Services are defined in suite.ts using fabricService and registered\n * by category. The createMcpServerFromSuite bridge converts them to\n * MCP tools with proper Zod schema validation.\n *\n * @param options - Configuration options (or legacy version string)\n * @returns Configured MCP server instance\n */\nexport function createMcpServer(\n options: CreateMcpServerOptions | string = {},\n): McpServer {\n // Support legacy signature: createMcpServer(version: string)\n const config: CreateMcpServerOptions =\n typeof options === \"string\" ? { version: options } : options;\n\n const { version = \"0.0.0\", verbose = false } = config;\n\n if (verbose) {\n console.error(\"[jaypie-mcp] Creating MCP server instance from suite\");\n }\n\n const server = createMcpServerFromSuite(suite, {\n name: suite.name,\n version,\n });\n\n if (verbose) {\n console.error(\n `[jaypie-mcp] Registered ${suite.services.length} tools from suite`,\n );\n console.error(`[jaypie-mcp] Categories: ${suite.categories.join(\", \")}`);\n }\n\n return server;\n}\n"],"names":[],"mappings":";;;AAWA;;;;;;;;;;AAUG;AACG,SAAU,eAAe,CAC7B,OAAA,GAA2C,EAAE,EAAA;;AAG7C,IAAA,MAAM,MAAM,GACV,OAAO,OAAO,KAAK,QAAQ,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO;IAE9D,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,MAAM;IAErD,IAAI,OAAO,EAAE;AACX,QAAA,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC;IACvE;AAEA,IAAA,MAAM,MAAM,GAAG,wBAAwB,CAAC,KAAK,EAAE;QAC7C,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO;AACR,KAAA,CAAC;IAEF,IAAI,OAAO,EAAE;QACX,OAAO,CAAC,KAAK,CACX,CAAA,wBAAA,EAA2B,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAA,iBAAA,CAAmB,CACpE;AACD,QAAA,OAAO,CAAC,KAAK,CAAC,CAAA,yBAAA,EAA4B,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAE,CAAC;IAC1E;AAEA,IAAA,OAAO,MAAM;AACf;;;;"}
1
+ {"version":3,"file":"createMcpServer.js","sources":["../src/createMcpServer.ts"],"sourcesContent":["/* eslint-disable no-console -- MCP stdio: stderr is the only valid log channel; stdout is reserved for JSON-RPC */\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nimport { createMcpServerFromSuite } from \"@jaypie/fabric/mcp\";\n\nimport { suite } from \"./suite.js\";\n\nexport interface CreateMcpServerOptions {\n version?: string;\n verbose?: boolean;\n}\n\n/**\n * Creates and configures an MCP server instance with Jaypie tools\n *\n * Uses ServiceSuite to register all services as MCP tools automatically.\n * Services are defined in suite.ts using fabricService and registered\n * by category. The createMcpServerFromSuite bridge converts them to\n * MCP tools with proper Zod schema validation.\n *\n * @param options - Configuration options (or legacy version string)\n * @returns Configured MCP server instance\n */\nexport function createMcpServer(\n options: CreateMcpServerOptions | string = {},\n): McpServer {\n // Support legacy signature: createMcpServer(version: string)\n const config: CreateMcpServerOptions =\n typeof options === \"string\" ? { version: options } : options;\n\n const { version = \"0.0.0\", verbose = false } = config;\n\n if (verbose) {\n console.error(\"[jaypie-mcp] Creating MCP server instance from suite\");\n }\n\n const server = createMcpServerFromSuite(suite, {\n name: suite.name,\n version,\n });\n\n if (verbose) {\n console.error(\n `[jaypie-mcp] Registered ${suite.services.length} tools from suite`,\n );\n console.error(`[jaypie-mcp] Categories: ${suite.categories.join(\", \")}`);\n }\n\n return server;\n}\n"],"names":[],"mappings":";;;AAYA;;;;;;;;;;AAUG;AACG,SAAU,eAAe,CAC7B,OAAA,GAA2C,EAAE,EAAA;;AAG7C,IAAA,MAAM,MAAM,GACV,OAAO,OAAO,KAAK,QAAQ,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO;IAE9D,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,MAAM;IAErD,IAAI,OAAO,EAAE;AACX,QAAA,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC;IACvE;AAEA,IAAA,MAAM,MAAM,GAAG,wBAAwB,CAAC,KAAK,EAAE;QAC7C,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO;AACR,KAAA,CAAC;IAEF,IAAI,OAAO,EAAE;QACX,OAAO,CAAC,KAAK,CACX,CAAA,wBAAA,EAA2B,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAA,iBAAA,CAAmB,CACpE;AACD,QAAA,OAAO,CAAC,KAAK,CAAC,CAAA,yBAAA,EAA4B,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAE,CAAC;IAC1E;AAEA,IAAA,OAAO,MAAM;AACf;;;;"}
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
6
6
  import { createMcpServer } from './createMcpServer.js';
7
7
  export { mcpExpressHandler } from './mcpExpressHandler.js';
8
8
 
9
+ /* eslint-disable no-console -- MCP stdio: stderr is the only valid log channel; stdout is reserved for JSON-RPC */
9
10
  // Version will be injected during build
10
11
  const version = "0.0.0";
11
12
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFileSync, realpathSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { createMcpServer } from \"./createMcpServer.js\";\n\n// Version will be injected during build\nconst version = \"0.0.0\";\n\n/**\n * Load environment variables from .env file in current working directory\n * Simple implementation that doesn't require external dependencies\n */\nfunction loadEnvFile() {\n try {\n const envPath = join(process.cwd(), \".env\");\n const content = readFileSync(envPath, \"utf-8\");\n\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n // Skip comments and empty lines\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex === -1) continue;\n\n const key = trimmed.slice(0, eqIndex).trim();\n let value = trimmed.slice(eqIndex + 1).trim();\n\n // Remove surrounding quotes if present\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n // Only set if not already defined (environment takes precedence)\n if (!process.env[key]) {\n process.env[key] = value;\n }\n }\n } catch {\n // .env file not found or not readable - that's fine\n }\n}\n\n// Load .env file before anything else\nloadEnvFile();\n\n// Parse command-line arguments\nconst args = process.argv.slice(2);\nconst verbose = args.includes(\"--verbose\") || args.includes(\"-v\");\n\n// Logger for verbose mode (uses stderr to not interfere with JSON-RPC on stdout)\nconst log = {\n info: (message: string, ...args: unknown[]) => {\n if (verbose) {\n console.error(`[jaypie-mcp] ${message}`, ...args);\n }\n },\n error: (message: string, ...args: unknown[]) => {\n console.error(`[jaypie-mcp ERROR] ${message}`, ...args);\n },\n};\n\nlet server: McpServer | null = null;\nlet isShuttingDown = false;\n\nasync function gracefulShutdown(exitCode = 0) {\n if (isShuttingDown) {\n return;\n }\n isShuttingDown = true;\n\n log.info(\"Shutting down gracefully...\");\n\n try {\n if (server) {\n await server.close();\n log.info(\"Server closed successfully\");\n }\n } catch (error) {\n log.error(\"Error during shutdown:\", error);\n } finally {\n process.exit(exitCode);\n }\n}\n\nasync function main() {\n log.info(\"Starting Jaypie MCP server...\");\n log.info(`Version: ${version}`);\n log.info(`Node version: ${process.version}`);\n log.info(`Verbose mode: ${verbose ? \"enabled\" : \"disabled\"}`);\n\n server = createMcpServer({ version, verbose });\n\n log.info(\"MCP server created successfully\");\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n log.info(\"Connected to stdio transport\");\n log.info(\"Server is ready to accept requests\");\n\n // Handle process termination signals\n process.on(\"SIGINT\", () => {\n log.info(\"Received SIGINT signal\");\n gracefulShutdown(0);\n });\n process.on(\"SIGTERM\", () => {\n log.info(\"Received SIGTERM signal\");\n gracefulShutdown(0);\n });\n\n // Handle stdio stream errors (but let transport handle normal stdin end/close)\n process.stdin.on(\"error\", (error) => {\n if (error.message?.includes(\"EPIPE\") || error.message?.includes(\"EOF\")) {\n log.info(\"stdin closed\");\n gracefulShutdown(0);\n }\n });\n\n process.stdout.on(\"error\", (error) => {\n if (error.message?.includes(\"EPIPE\")) {\n log.info(\"stdout pipe broken\");\n gracefulShutdown(0);\n }\n });\n\n // Server is running on stdio\n}\n\n// Only run main() if this module is executed directly (not imported)\n// This handles npx and symlinks in node_modules/.bin correctly\nif (process.argv[1]) {\n const realPath = realpathSync(process.argv[1]);\n const realPathAsUrl = pathToFileURL(realPath).href;\n if (import.meta.url === realPathAsUrl) {\n main().catch((error) => {\n log.error(\"Fatal error:\", error);\n process.exit(1);\n });\n }\n}\n\nexport { createMcpServer } from \"./createMcpServer.js\";\nexport type { CreateMcpServerOptions } from \"./createMcpServer.js\";\nexport {\n mcpExpressHandler,\n type McpExpressHandlerOptions,\n} from \"./mcpExpressHandler.js\";\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n/* eslint-disable no-console -- MCP stdio: stderr is the only valid log channel; stdout is reserved for JSON-RPC */\nimport { readFileSync, realpathSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { createMcpServer } from \"./createMcpServer.js\";\n\n// Version will be injected during build\nconst version = \"0.0.0\";\n\n/**\n * Load environment variables from .env file in current working directory\n * Simple implementation that doesn't require external dependencies\n */\nfunction loadEnvFile() {\n try {\n const envPath = join(process.cwd(), \".env\");\n const content = readFileSync(envPath, \"utf-8\");\n\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n // Skip comments and empty lines\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex === -1) continue;\n\n const key = trimmed.slice(0, eqIndex).trim();\n let value = trimmed.slice(eqIndex + 1).trim();\n\n // Remove surrounding quotes if present\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n // Only set if not already defined (environment takes precedence)\n if (!process.env[key]) {\n process.env[key] = value;\n }\n }\n } catch {\n // .env file not found or not readable - that's fine\n }\n}\n\n// Load .env file before anything else\nloadEnvFile();\n\n// Parse command-line arguments\nconst args = process.argv.slice(2);\nconst verbose = args.includes(\"--verbose\") || args.includes(\"-v\");\n\n// Logger for verbose mode (uses stderr to not interfere with JSON-RPC on stdout)\nconst log = {\n info: (message: string, ...args: unknown[]) => {\n if (verbose) {\n console.error(`[jaypie-mcp] ${message}`, ...args);\n }\n },\n error: (message: string, ...args: unknown[]) => {\n console.error(`[jaypie-mcp ERROR] ${message}`, ...args);\n },\n};\n\nlet server: McpServer | null = null;\nlet isShuttingDown = false;\n\nasync function gracefulShutdown(exitCode = 0) {\n if (isShuttingDown) {\n return;\n }\n isShuttingDown = true;\n\n log.info(\"Shutting down gracefully...\");\n\n try {\n if (server) {\n await server.close();\n log.info(\"Server closed successfully\");\n }\n } catch (error) {\n log.error(\"Error during shutdown:\", error);\n } finally {\n process.exit(exitCode);\n }\n}\n\nasync function main() {\n log.info(\"Starting Jaypie MCP server...\");\n log.info(`Version: ${version}`);\n log.info(`Node version: ${process.version}`);\n log.info(`Verbose mode: ${verbose ? \"enabled\" : \"disabled\"}`);\n\n server = createMcpServer({ version, verbose });\n\n log.info(\"MCP server created successfully\");\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n log.info(\"Connected to stdio transport\");\n log.info(\"Server is ready to accept requests\");\n\n // Handle process termination signals\n process.on(\"SIGINT\", () => {\n log.info(\"Received SIGINT signal\");\n gracefulShutdown(0);\n });\n process.on(\"SIGTERM\", () => {\n log.info(\"Received SIGTERM signal\");\n gracefulShutdown(0);\n });\n\n // Handle stdio stream errors (but let transport handle normal stdin end/close)\n process.stdin.on(\"error\", (error) => {\n if (error.message?.includes(\"EPIPE\") || error.message?.includes(\"EOF\")) {\n log.info(\"stdin closed\");\n gracefulShutdown(0);\n }\n });\n\n process.stdout.on(\"error\", (error) => {\n if (error.message?.includes(\"EPIPE\")) {\n log.info(\"stdout pipe broken\");\n gracefulShutdown(0);\n }\n });\n\n // Server is running on stdio\n}\n\n// Only run main() if this module is executed directly (not imported)\n// This handles npx and symlinks in node_modules/.bin correctly\nif (process.argv[1]) {\n const realPath = realpathSync(process.argv[1]);\n const realPathAsUrl = pathToFileURL(realPath).href;\n if (import.meta.url === realPathAsUrl) {\n main().catch((error) => {\n log.error(\"Fatal error:\", error);\n process.exit(1);\n });\n }\n}\n\nexport { createMcpServer } from \"./createMcpServer.js\";\nexport type { CreateMcpServerOptions } from \"./createMcpServer.js\";\nexport {\n mcpExpressHandler,\n type McpExpressHandlerOptions,\n} from \"./mcpExpressHandler.js\";\n"],"names":[],"mappings":"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"}
@@ -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.8.53#ce234537"
12
+ const BUILD_VERSION_STRING = "@jaypie/mcp@0.8.55#735a0f6f"
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.8.53",
3
+ "version": "0.8.55",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,25 @@
1
+ ---
2
+ version: 1.2.52
3
+ date: 2026-05-06
4
+ summary: JaypieWebDeploymentBucket adopts JaypieDistribution defaults — security headers, WAF, and CloudFront access logging
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - `JaypieWebDeploymentBucket` now applies the same default security `ResponseHeadersPolicy` as `JaypieDistribution` (HSTS, X-Frame-Options, CSP, Referrer-Policy, Permissions-Policy, Cross-Origin-* headers, Server header removal). Attached to the default behavior and the production `/*` behavior.
10
+ - Adds `securityHeaders` (`boolean | SecurityHeadersOverrides`) and `responseHeadersPolicy` (full override) props matching `JaypieDistribution`'s shape.
11
+ - Adds CloudFront access logging by default — creates a log bucket with Datadog forwarding. New `logBucket` and `destination` props mirror `JaypieDistribution`.
12
+ - Adds WAFv2 WebACL by default with `AWSManagedRulesCommonRuleSet`, `AWSManagedRulesKnownBadInputsRuleSet`, and IP rate limiting (2000/5min). New `waf` prop accepts `boolean | JaypieWebDeploymentBucketWafConfig`.
13
+ - WAF defaults to namespacing the WebACL and WAF log bucket with the construct id, so multiple `JaypieWebDeploymentBucket` (or paired `JaypieDistribution`) instances coexist in one stack without name collisions.
14
+ - `host` widened to `string | HostConfig` on `JaypieWebDeploymentBucket` and `JaypieStaticWebBucket`, matching `JaypieDistribution` / `JaypieApiGateway` / `JaypieWebSocket`. HostConfig values are resolved via `envHostname()`.
15
+ - Exposes `logBucket`, `responseHeadersPolicy`, `wafLogBucket`, and `webAcl` as public readonly properties.
16
+
17
+ ## Motivation
18
+
19
+ `JaypieDistribution` ships with sensible defaults for security headers, WAF, and access logging. Static-site deployments fronted by `JaypieWebDeploymentBucket` deserve the same baseline rather than having to compose two constructs by hand.
20
+
21
+ ## Migration
22
+
23
+ - Existing `JaypieWebDeploymentBucket` users pick up new AWS resources on next deploy: a CloudFront access-log bucket, a WAFv2 WebACL, and a WAF log bucket. There is ongoing AWS cost (WAF + S3 storage). Opt out per feature with `securityHeaders: false`, `waf: false`, `destination: false`, or `logBucket: <existing>`.
24
+ - Custom security headers: pass `securityHeaders: { contentSecurityPolicy, frameOption, ... }` or a full `responseHeadersPolicy`.
25
+ - Custom WAF: `waf: { name: "static", rateLimitPerIp: 500 }` or `waf: { webAclArn: "arn:..." }` to attach an existing WebACL.
@@ -0,0 +1,22 @@
1
+ ---
2
+ version: 1.2.4
3
+ date: 2026-05-06
4
+ summary: Fix TypeScript resolver — switch from legacy import-x/resolver to resolver-next so eslint-import-resolver-typescript v4 (interfaceVersion 3) loads cleanly
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - `jaypie:typescriptRecommended` now sets `import-x/resolver-next: [createTypeScriptImportResolver()]` and clears the legacy `import-x/resolver` entry inherited from `importxFlatConfigs.typescript`.
10
+
11
+ ## Motivation
12
+
13
+ `eslint-plugin-import-x@4.16.2` validates legacy resolvers via `interfaceVersion === 2`, but `eslint-import-resolver-typescript@4.4.4` exposes `interfaceVersion: 3`. The legacy form (`'import-x/resolver': { typescript: true }`) inherited from the plugin's bundled flat config therefore failed validation, and every `.ts`/`.tsx` file produced `Resolve error: typescript with invalid interface loaded as resolver` plus cascading `import-x/no-unresolved` errors.
14
+
15
+ The v3-compatible path is `import-x/resolver-next`, which accepts modern resolvers via the resolver factory.
16
+
17
+ ## Migration
18
+
19
+ - No consumer changes required. Re-export `@jaypie/eslint` as before.
20
+ - Downstream workarounds that overrode `import-x/resolver-next` can be removed.
21
+
22
+ Fixes #333.
@@ -0,0 +1,11 @@
1
+ ---
2
+ version: 0.8.54
3
+ date: 2026-05-06
4
+ summary: Update web skill for JaypieWebDeploymentBucket defaults; ship release notes for @jaypie/constructs 1.2.52 and @jaypie/eslint 1.2.4
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - Updated `skill("web")` props table and added Security Headers / WAF / Access Logging sections covering `JaypieWebDeploymentBucket`'s new defaults and override mechanisms (`securityHeaders`, `responseHeadersPolicy`, `waf`, `logBucket`, `destination`).
10
+ - Mirrors the `JaypieDistribution` patterns documented in `skill("cdk")` so static-site and dynamic-origin guidance stay aligned.
11
+ - Added release notes for `@jaypie/constructs` 1.2.52 and `@jaypie/eslint` 1.2.4 (#333).
@@ -0,0 +1,11 @@
1
+ ---
2
+ version: 0.8.55
3
+ date: 2026-05-07
4
+ summary: Document JaypieStack in the cdk skill
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - Added a "Stacks" section to `skill("cdk")` introducing `JaypieStack` as the recommended base class for Jaypie CDK stacks, covering automatic stack naming, account/region resolution, and the standard tag set.
10
+ - Mentioned `JaypieAppStack` and `JaypieInfrastructureStack` as thin subclasses that pre-fill the `app` and `infra` keys.
11
+ - Updated the "Environment Configuration" example to reflect that `JaypieStack` resolves `CDK_DEFAULT_ACCOUNT` / `CDK_DEFAULT_REGION` automatically, so manual env wiring is only needed for cross-account/region deploys.
package/skills/cdk.md CHANGED
@@ -96,22 +96,65 @@ workspaces/
96
96
  │ └── package.json
97
97
  ```
98
98
 
99
+ ## Stacks
100
+
101
+ ### JaypieStack
102
+
103
+ `JaypieStack` extends CDK's `Stack` and is the recommended base class for every stack in a Jaypie project. Extending it gives you three things automatically:
104
+
105
+ 1. **Stack naming** from environment variables, in the form
106
+ `cdk-{PROJECT_SPONSOR}-{PROJECT_KEY}-{PROJECT_ENV}-{PROJECT_NONCE}[-{key}]`,
107
+ so naming stays consistent across sandbox, personal, and production deploys.
108
+ 2. **Account & region** resolved from `CDK_DEFAULT_ACCOUNT` / `CDK_DEFAULT_REGION` (overridable via `env`).
109
+ 3. **Standard tags** applied to the stack and propagated to every taggable child resource: `env`, `project`, `sponsor`, `nonce`, `commit`, `buildHex`, `buildDate`, `buildTime`, `version`, `service`, `creation`, `role`, `stack`.
110
+
111
+ ```typescript
112
+ import { Construct } from "constructs";
113
+ import { JaypieStack, JaypieStackProps } from "@jaypie/constructs";
114
+
115
+ export class DataStack extends JaypieStack {
116
+ constructor(scope: Construct, id: string, props: JaypieStackProps = {}) {
117
+ super(scope, id, { key: "data", ...props });
118
+ // ...your resources...
119
+ }
120
+ }
121
+ ```
122
+
123
+ Props extend `StackProps` with one addition:
124
+
125
+ | Prop | Description |
126
+ |------|-------------|
127
+ | `key` | Suffix appended to the generated stack name (e.g., `"data"`, `"events"`). Optional. |
128
+
129
+ If you see `undefined` or `unknown` in a generated stack name or tag, an env var is missing — fix the environment rather than overriding `stackName`.
130
+
131
+ #### `JaypieAppStack` / `JaypieInfrastructureStack`
132
+
133
+ Thin convenience subclasses that pre-fill `key`:
134
+
135
+ - `JaypieAppStack` → `key: "app"` (application workloads — Lambdas, APIs)
136
+ - `JaypieInfrastructureStack` → `key: "infra"` (shared infrastructure — buckets, hosted zones, shared secrets); also tags the stack with `stackSha` from `CDK_ENV_INFRASTRUCTURE_STACK_SHA` when set
137
+
138
+ Use them when their key fits; extend `JaypieStack` directly for any other category.
139
+
99
140
  ## Environment Configuration
100
141
 
101
- Use environment-specific configuration:
142
+ `JaypieStack` resolves the AWS account and region from `CDK_DEFAULT_ACCOUNT` / `CDK_DEFAULT_REGION` automatically, so most stacks need no env wiring at the call site:
102
143
 
103
144
  ```typescript
104
- const env = process.env.PROJECT_ENV || "sandbox";
105
- const nonce = process.env.PROJECT_NONCE || "dev";
145
+ new ApiStack(app, "ApiStack");
146
+ ```
106
147
 
107
- const stack = new ApiStack(app, `api-${env}-${nonce}`, {
108
- env: {
109
- account: process.env.CDK_DEFAULT_ACCOUNT,
110
- region: process.env.CDK_DEFAULT_REGION,
111
- },
148
+ Override per-stack only when you need to deploy outside the default context:
149
+
150
+ ```typescript
151
+ new ApiStack(app, "ApiStack", {
152
+ env: { account: "123456789012", region: "us-west-2" },
112
153
  });
113
154
  ```
114
155
 
156
+ See `skill("variables")` for the full set of `PROJECT_*` and `CDK_*` environment variables that drive naming and tagging.
157
+
115
158
  ## Resource Naming
116
159
 
117
160
  CDK logical IDs (the construct `id` parameter) are stack-scoped and unique per stack. However, **physical resource names** set via props like `repositoryName`, `logGroupName`, `clusterName`, `family`, and `ruleName` are account-global. Always include `PROJECT_ENV` and `PROJECT_NONCE` to avoid collisions when multiple stacks deploy to the same account.
package/skills/web.md CHANGED
@@ -35,11 +35,16 @@ new JaypieWebDeploymentBucket(this, "Web", {
35
35
 
36
36
  | Prop | Type | Default |
37
37
  |------|------|---------|
38
- | `host` | `string` | `mergeDomain(CDK_ENV_WEB_SUBDOMAIN, CDK_ENV_WEB_HOSTED_ZONE \|\| CDK_ENV_HOSTED_ZONE)` |
38
+ | `host` | `string \| HostConfig` | `mergeDomain(CDK_ENV_WEB_SUBDOMAIN, CDK_ENV_WEB_HOSTED_ZONE \|\| CDK_ENV_HOSTED_ZONE)` — `HostConfig` is resolved via `envHostname()` |
39
39
  | `zone` | `string \| IHostedZone \| JaypieHostedZone` | `CDK_ENV_WEB_HOSTED_ZONE \|\| CDK_ENV_HOSTED_ZONE` |
40
40
  | `certificate` | `boolean \| ICertificate` | `true` (creates via `resolveCertificate`) |
41
+ | `destination` | `LambdaDestination \| boolean` | `true` (Datadog forwarder for access-log bucket notifications) |
42
+ | `logBucket` | `IBucket \| string \| { exportName } \| true` | undefined — creates a new bucket if `destination !== false` |
41
43
  | `name` | `string` | `constructEnvName("web")` |
44
+ | `responseHeadersPolicy` | `IResponseHeadersPolicy` | undefined — full override; bypasses default security headers |
42
45
  | `roleTag` | `string` | `CDK.ROLE.HOSTING` |
46
+ | `securityHeaders` | `boolean \| SecurityHeadersOverrides` | `true` |
47
+ | `waf` | `boolean \| JaypieWebDeploymentBucketWafConfig` | `true` (WAF name defaults to construct id) |
43
48
 
44
49
  Also accepts all `s3.BucketProps` — the bucket defaults to `autoDeleteObjects: true`, `publicReadAccess: true`, `websiteIndexDocument: "index.html"`, `websiteErrorDocument: "index.html"` (SPA-friendly).
45
50
 
@@ -47,6 +52,52 @@ Also accepts all `s3.BucketProps` — the bucket defaults to `autoDeleteObjects:
47
52
 
48
53
  - Default behavior: S3 static website origin, `REDIRECT_TO_HTTPS`, `CACHING_DISABLED`.
49
54
  - In production (`isProductionEnv()`), a second behavior on `/*` enables `CACHING_OPTIMIZED` — `index.html` stays uncached so SPA deploys are visible immediately; hashed assets get edge cache.
55
+ - Access logs land in a CloudFront log bucket with Datadog forwarding by default. Set `destination: false` to skip notifications, or pass `logBucket: <existing>` to reuse a bucket.
56
+
57
+ ### Security Headers
58
+
59
+ Same defaults as `JaypieDistribution`: HSTS, X-Frame-Options, CSP, Referrer-Policy, Permissions-Policy, Cross-Origin-* headers, and Server header removal (applied via a `ResponseHeadersPolicy` on the default behavior and the production `/*` behavior). Override the same way:
60
+
61
+ ```typescript
62
+ // Disable
63
+ new JaypieWebDeploymentBucket(this, "Web", { host, zone, securityHeaders: false });
64
+
65
+ // Override specific headers
66
+ new JaypieWebDeploymentBucket(this, "Web", {
67
+ host, zone,
68
+ securityHeaders: {
69
+ contentSecurityPolicy: "default-src 'self';",
70
+ frameOption: HeadersFrameOption.SAMEORIGIN,
71
+ },
72
+ });
73
+
74
+ // Full custom policy
75
+ new JaypieWebDeploymentBucket(this, "Web", { host, zone, responseHeadersPolicy: myPolicy });
76
+ ```
77
+
78
+ ### WAF
79
+
80
+ Same defaults as `JaypieDistribution` (AWSManagedRulesCommonRuleSet, AWSManagedRulesKnownBadInputsRuleSet, IP rate limit 2000/5min, WAF logging to S3 with Datadog forwarding). The WebACL and WAF log bucket are namespaced with the construct id by default so multiple instances coexist without collision:
81
+
82
+ ```typescript
83
+ // Default — WAF named after construct id ("MyWeb-WebAcl")
84
+ new JaypieWebDeploymentBucket(this, "MyWeb", { host, zone });
85
+
86
+ // Disable
87
+ new JaypieWebDeploymentBucket(this, "Web", { host, zone, waf: false });
88
+
89
+ // Custom name + rate limit
90
+ new JaypieWebDeploymentBucket(this, "Web", {
91
+ host, zone,
92
+ waf: { name: "static", rateLimitPerIp: 500 },
93
+ });
94
+
95
+ // Existing WebACL
96
+ new JaypieWebDeploymentBucket(this, "Web", {
97
+ host, zone,
98
+ waf: { webAclArn: "arn:aws:wafv2:..." },
99
+ });
100
+ ```
50
101
 
51
102
  ### DNS
52
103