@jaypie/mcp 0.7.4 → 0.7.7
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/dist/suites/docs/index.js +1 -1
- package/package.json +1 -1
- package/release-notes/constructs/1.2.27.md +20 -0
- package/release-notes/constructs/1.2.28.md +12 -0
- package/release-notes/express/1.2.8.md +34 -0
- package/release-notes/mcp/0.7.5.md +13 -0
- package/release-notes/mcp/0.7.6.md +13 -0
- package/skills/agents.md +16 -3
- package/skills/cicd-actions.md +337 -0
- package/skills/cicd-deploy.md +332 -0
- package/skills/cicd-environments.md +184 -0
- package/skills/cicd.md +9 -1
- package/skills/development.md +3 -1
- package/skills/infrastructure.md +5 -2
- package/skills/monorepo.md +166 -0
- package/skills/secrets.md +108 -110
- package/skills/skills.md +2 -2
- package/skills/subpackage.md +219 -0
- package/skills/tools-llm.md +98 -0
- package/skills/tools.md +11 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Initialize a Jaypie monorepo project
|
|
3
|
+
related: subpackage, cicd, style, tests
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Jaypie Monorepo Setup
|
|
7
|
+
|
|
8
|
+
Initialize a new monorepo using Jaypie conventions and utilities.
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
- ESLint 9+ flat config with @jaypie/eslint
|
|
13
|
+
- NPM with Workspaces ("monorepo")
|
|
14
|
+
- TypeScript with ESM modules
|
|
15
|
+
- Vite for building, Vitest for testing
|
|
16
|
+
- Node.js 22, 24, 25 support
|
|
17
|
+
|
|
18
|
+
## Process
|
|
19
|
+
|
|
20
|
+
1. Create root configuration files
|
|
21
|
+
2. Install dev dependencies
|
|
22
|
+
3. Configure workspaces
|
|
23
|
+
|
|
24
|
+
## Root Files
|
|
25
|
+
|
|
26
|
+
### package.json
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"name": "@project-org/monorepo",
|
|
31
|
+
"version": "0.0.1",
|
|
32
|
+
"private": true,
|
|
33
|
+
"type": "module",
|
|
34
|
+
"workspaces": [
|
|
35
|
+
"packages/*"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "npm run build --workspaces --if-present",
|
|
39
|
+
"clean": "rimraf ./packages/*/dist",
|
|
40
|
+
"format": "npm run format:package && npm run format:lint",
|
|
41
|
+
"format:lint": "eslint --fix .",
|
|
42
|
+
"format:package": "sort-package-json ./package.json ./packages/*/package.json",
|
|
43
|
+
"lint": "eslint --quiet .",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"typecheck": "npm run typecheck --workspaces --if-present"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### eslint.config.mjs
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
export { default } from "@jaypie/eslint";
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For projects needing custom rules:
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
import jaypie from "@jaypie/eslint";
|
|
60
|
+
|
|
61
|
+
export default [
|
|
62
|
+
...jaypie,
|
|
63
|
+
{
|
|
64
|
+
ignores: ["LOCAL/**"],
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### tsconfig.json (root)
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"compilerOptions": {
|
|
74
|
+
"target": "ES2020",
|
|
75
|
+
"module": "ESNext",
|
|
76
|
+
"moduleResolution": "bundler",
|
|
77
|
+
"declaration": true,
|
|
78
|
+
"strict": true,
|
|
79
|
+
"esModuleInterop": true,
|
|
80
|
+
"skipLibCheck": true,
|
|
81
|
+
"forceConsistentCasingInFileNames": true
|
|
82
|
+
},
|
|
83
|
+
"exclude": ["node_modules", "dist"]
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### vitest.workspace.ts
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
export default ["packages/*/vitest.config.{ts,js}"];
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### .gitignore
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
.DS_Store
|
|
97
|
+
node_modules
|
|
98
|
+
dist
|
|
99
|
+
|
|
100
|
+
# Local env files
|
|
101
|
+
.env
|
|
102
|
+
.env.local
|
|
103
|
+
.env.*.local
|
|
104
|
+
|
|
105
|
+
# Log files
|
|
106
|
+
npm-debug.log*
|
|
107
|
+
|
|
108
|
+
# Editor directories
|
|
109
|
+
.idea
|
|
110
|
+
*.sw?
|
|
111
|
+
|
|
112
|
+
# Build artifacts
|
|
113
|
+
*.tsbuildinfo
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### .vscode/settings.json
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"editor.formatOnSave": true,
|
|
121
|
+
"editor.codeActionsOnSave": {
|
|
122
|
+
"source.fixAll.eslint": "explicit"
|
|
123
|
+
},
|
|
124
|
+
"typescript.preferences.importModuleSpecifier": "relative"
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Installation
|
|
129
|
+
|
|
130
|
+
Install root dev dependencies:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm install --save-dev @jaypie/eslint @jaypie/testkit eslint rimraf sort-package-json tsx vite vite-plugin-dts vitest
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Workspace Conventions
|
|
137
|
+
|
|
138
|
+
| Directory | Purpose |
|
|
139
|
+
|-----------|---------|
|
|
140
|
+
| `packages/` | npm packages (default workspace) |
|
|
141
|
+
| `stacks/` | CDK-deployed infrastructure and sites |
|
|
142
|
+
|
|
143
|
+
## Scripts Reference
|
|
144
|
+
|
|
145
|
+
| Script | Top-level | Package-level |
|
|
146
|
+
|--------|-----------|---------------|
|
|
147
|
+
| `build` | `npm run build --workspaces` | `vite build` |
|
|
148
|
+
| `clean` | `rimraf ./packages/*/dist` | `rimraf dist` |
|
|
149
|
+
| `format` | `eslint --fix .` | `eslint --fix` |
|
|
150
|
+
| `format:package` | `sort-package-json ./package.json ./packages/*/package.json` | `sort-package-json` |
|
|
151
|
+
| `lint` | `eslint --quiet .` | `eslint` |
|
|
152
|
+
| `test` | `vitest run` | `vitest run` |
|
|
153
|
+
| `typecheck` | `npm run typecheck --workspaces` | `tsc --noEmit` |
|
|
154
|
+
|
|
155
|
+
## Guidelines
|
|
156
|
+
|
|
157
|
+
- Run `npm install` to generate package-lock.json (do not hard-code versions)
|
|
158
|
+
- Use `"version": "0.0.1"`, `"type": "module"`, and `"private": true` for new packages
|
|
159
|
+
- Do not include authors, keywords, or external links in package.json
|
|
160
|
+
- If this is the first commit, commit directly to main; otherwise create a branch
|
|
161
|
+
|
|
162
|
+
## Next Steps
|
|
163
|
+
|
|
164
|
+
- `skill("subpackage")` - Create packages within the monorepo
|
|
165
|
+
- `skill("cicd")` - Add GitHub Actions workflows
|
|
166
|
+
- `skill("tests")` - Testing patterns with Vitest
|
package/skills/secrets.md
CHANGED
|
@@ -5,190 +5,188 @@ related: aws, cdk, variables
|
|
|
5
5
|
|
|
6
6
|
# Secret Management
|
|
7
7
|
|
|
8
|
-
Jaypie uses AWS Secrets Manager for secure credential storage
|
|
8
|
+
Jaypie uses AWS Secrets Manager for secure credential storage. The `JaypieEnvSecret` construct creates secrets at deploy time from environment variables, and `getEnvSecret` retrieves them at runtime.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## The Pattern
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
1. **Deploy time**: Set environment variables in CI/CD (e.g., `MONGODB_URI=mongodb+srv://...`)
|
|
13
|
+
2. **CDK**: `JaypieEnvSecret` reads the env var and creates/updates an AWS secret
|
|
14
|
+
3. **Runtime**: `getEnvSecret("MONGODB_URI")` fetches from Secrets Manager
|
|
15
|
+
|
|
16
|
+
This keeps secrets out of code and config files while enabling environment-specific values.
|
|
17
|
+
|
|
18
|
+
## CDK: Creating Secrets with JaypieEnvSecret
|
|
19
|
+
|
|
20
|
+
The simplest pattern uses the environment variable name as the construct ID:
|
|
13
21
|
|
|
14
22
|
```typescript
|
|
15
|
-
import {
|
|
23
|
+
import { JaypieEnvSecret, JaypieLambda } from "@jaypie/constructs";
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
const
|
|
25
|
+
// Creates secret from process.env.MONGODB_URI at deploy time
|
|
26
|
+
const mongoSecret = new JaypieEnvSecret(this, "MONGODB_URI");
|
|
27
|
+
const anthropicSecret = new JaypieEnvSecret(this, "ANTHROPIC_API_KEY");
|
|
28
|
+
|
|
29
|
+
// Lambda with secrets array (auto-creates JaypieEnvSecret instances)
|
|
30
|
+
new JaypieLambda(this, "Handler", {
|
|
31
|
+
code: "dist/lambda",
|
|
32
|
+
handler: "index.handler",
|
|
33
|
+
secrets: ["MONGODB_URI", "ANTHROPIC_API_KEY"],
|
|
34
|
+
});
|
|
19
35
|
```
|
|
20
36
|
|
|
21
|
-
|
|
37
|
+
When the construct ID matches an environment variable name, `JaypieEnvSecret` automatically:
|
|
38
|
+
- Uses that env var's value as the secret content
|
|
39
|
+
- Sets `envKey` to the ID for later reference
|
|
22
40
|
|
|
23
|
-
|
|
41
|
+
### CI/CD Setup
|
|
24
42
|
|
|
25
|
-
|
|
26
|
-
2. `{name}_SECRET` - If found, fetches from AWS Secrets Manager
|
|
27
|
-
3. `{name}` - Returns direct value without AWS call
|
|
43
|
+
Set secrets as environment variables in your deployment pipeline:
|
|
28
44
|
|
|
29
|
-
|
|
45
|
+
```yaml
|
|
46
|
+
# GitHub Actions example
|
|
47
|
+
jobs:
|
|
48
|
+
deploy:
|
|
49
|
+
env:
|
|
50
|
+
MONGODB_URI: ${{ secrets.MONGODB_URI }}
|
|
51
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
52
|
+
steps:
|
|
53
|
+
- run: npx cdk deploy
|
|
54
|
+
```
|
|
30
55
|
|
|
31
|
-
##
|
|
56
|
+
## Runtime: Retrieving Secrets
|
|
32
57
|
|
|
33
|
-
Use `
|
|
58
|
+
Use `getEnvSecret` to fetch secrets in Lambda:
|
|
34
59
|
|
|
35
60
|
```typescript
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
-
// Load secrets and set in process.env
|
|
39
|
-
await loadEnvSecrets("ANTHROPIC_API_KEY", "OPENAI_API_KEY", "MONGODB_URI");
|
|
61
|
+
import { getEnvSecret } from "jaypie";
|
|
40
62
|
|
|
41
|
-
|
|
63
|
+
const mongoUri = await getEnvSecret("MONGODB_URI");
|
|
64
|
+
const apiKey = await getEnvSecret("ANTHROPIC_API_KEY");
|
|
42
65
|
```
|
|
43
66
|
|
|
44
|
-
|
|
67
|
+
### Loading Multiple Secrets
|
|
45
68
|
|
|
46
|
-
|
|
69
|
+
Use `loadEnvSecrets` during handler initialization to populate `process.env`:
|
|
47
70
|
|
|
48
71
|
```typescript
|
|
49
|
-
|
|
50
|
-
environment: {
|
|
51
|
-
// SECRET_ prefix triggers AWS Secrets Manager fetch
|
|
52
|
-
SECRET_MONGODB_URI: "my-project/mongodb-uri",
|
|
53
|
-
SECRET_API_KEY: "my-project/third-party-api-key",
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
```
|
|
72
|
+
import { loadEnvSecrets } from "jaypie";
|
|
57
73
|
|
|
58
|
-
|
|
74
|
+
await loadEnvSecrets("ANTHROPIC_API_KEY", "OPENAI_API_KEY", "MONGODB_URI");
|
|
59
75
|
|
|
60
|
-
|
|
61
|
-
// getEnvSecret sees SECRET_MONGODB_URI and fetches from Secrets Manager
|
|
62
|
-
const mongoUri = await getEnvSecret("MONGODB_URI");
|
|
76
|
+
// Now available as process.env.ANTHROPIC_API_KEY, etc.
|
|
63
77
|
```
|
|
64
78
|
|
|
65
|
-
##
|
|
79
|
+
## Provider/Consumer Pattern
|
|
66
80
|
|
|
67
|
-
|
|
81
|
+
For shared secrets across environments (e.g., sandbox providing to personal builds):
|
|
68
82
|
|
|
69
83
|
```typescript
|
|
70
|
-
|
|
84
|
+
// Sandbox stack (provider) - exports the secret name
|
|
85
|
+
new JaypieEnvSecret(this, "SHARED_API_KEY", { provider: true });
|
|
71
86
|
|
|
72
|
-
//
|
|
73
|
-
|
|
87
|
+
// Personal build (consumer) - imports from sandbox
|
|
88
|
+
new JaypieEnvSecret(this, "SHARED_API_KEY"); // consumer auto-detected
|
|
74
89
|
```
|
|
75
90
|
|
|
76
|
-
|
|
91
|
+
The construct auto-detects consumer mode for personal/ephemeral environments (`PROJECT_ENV=personal` or `CDK_ENV_PERSONAL=true`).
|
|
77
92
|
|
|
78
|
-
##
|
|
93
|
+
## Generated Secrets
|
|
79
94
|
|
|
80
|
-
|
|
95
|
+
For secrets without a source value (e.g., database passwords):
|
|
81
96
|
|
|
82
97
|
```typescript
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
new JaypieEnvSecret(this, "DB_PASSWORD", {
|
|
99
|
+
generateSecretString: {
|
|
100
|
+
excludePunctuation: true,
|
|
101
|
+
passwordLength: 32,
|
|
102
|
+
},
|
|
88
103
|
});
|
|
89
|
-
|
|
90
|
-
// Grant read access
|
|
91
|
-
secret.grantRead(lambdaFunction);
|
|
92
104
|
```
|
|
93
105
|
|
|
94
|
-
|
|
106
|
+
## Tagging
|
|
95
107
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
Apply standard tags for organization:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
new JaypieEnvSecret(this, "STRIPE_KEY", {
|
|
112
|
+
roleTag: CDK.ROLE.PAYMENT,
|
|
113
|
+
vendorTag: CDK.VENDOR.STRIPE,
|
|
114
|
+
});
|
|
100
115
|
```
|
|
101
116
|
|
|
102
|
-
##
|
|
117
|
+
## Local Development
|
|
103
118
|
|
|
104
|
-
|
|
119
|
+
For local development, set environment variables directly in `.env.local`:
|
|
105
120
|
|
|
121
|
+
```bash
|
|
122
|
+
# .env.local (not committed)
|
|
123
|
+
ANTHROPIC_API_KEY=sk-ant-test123
|
|
124
|
+
MONGODB_URI=mongodb://localhost:27017/dev
|
|
106
125
|
```
|
|
107
|
-
{project-key}/{secret-name}
|
|
108
126
|
|
|
109
|
-
|
|
110
|
-
- my-api/mongodb-uri
|
|
111
|
-
- my-api/stripe-key
|
|
112
|
-
- my-api/auth0-secret
|
|
113
|
-
```
|
|
127
|
+
`getEnvSecret` returns these values directly without AWS calls when no `SECRET_` prefix is present.
|
|
114
128
|
|
|
115
|
-
##
|
|
129
|
+
## Alternative Approaches
|
|
116
130
|
|
|
117
|
-
|
|
131
|
+
### Explicit Value
|
|
118
132
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
133
|
+
Pass a value directly instead of reading from environment:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
new JaypieEnvSecret(this, "ApiKey", {
|
|
137
|
+
value: "sk_live_abc123", // Not recommended - prefer env vars
|
|
138
|
+
});
|
|
123
139
|
```
|
|
124
140
|
|
|
125
|
-
|
|
141
|
+
### Manual SECRET_ Linking
|
|
142
|
+
|
|
143
|
+
For non-JaypieEnvSecret secrets, manually set the `SECRET_` prefix:
|
|
126
144
|
|
|
127
145
|
```typescript
|
|
128
|
-
|
|
129
|
-
|
|
146
|
+
new JaypieLambda(this, "Handler", {
|
|
147
|
+
environment: {
|
|
148
|
+
SECRET_MONGODB_URI: "my-project/mongodb-uri", // AWS secret name
|
|
149
|
+
},
|
|
150
|
+
});
|
|
130
151
|
```
|
|
131
152
|
|
|
132
|
-
|
|
153
|
+
At runtime, `getEnvSecret("MONGODB_URI")` sees `SECRET_MONGODB_URI` and fetches from that AWS secret name.
|
|
133
154
|
|
|
134
|
-
|
|
155
|
+
The `_SECRET` suffix also works:
|
|
135
156
|
|
|
136
157
|
```typescript
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// Second call: returns cached value
|
|
141
|
-
const key2 = await getEnvSecret("API_KEY");
|
|
158
|
+
environment: {
|
|
159
|
+
MONGODB_URI_SECRET: "my-project/mongodb-uri",
|
|
160
|
+
}
|
|
142
161
|
```
|
|
143
162
|
|
|
144
|
-
|
|
163
|
+
### Direct Secret Access
|
|
145
164
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
Configure automatic rotation for supported secrets:
|
|
165
|
+
Use `getSecret` when you need to fetch by exact AWS secret name:
|
|
149
166
|
|
|
150
167
|
```typescript
|
|
151
|
-
|
|
152
|
-
secretName: "my-project/db-password",
|
|
153
|
-
generateSecretString: {
|
|
154
|
-
excludePunctuation: true,
|
|
155
|
-
passwordLength: 32,
|
|
156
|
-
},
|
|
157
|
-
});
|
|
168
|
+
import { getSecret } from "jaypie";
|
|
158
169
|
|
|
159
|
-
secret
|
|
160
|
-
automaticallyAfter: Duration.days(30),
|
|
161
|
-
rotationLambda: rotationFunction,
|
|
162
|
-
});
|
|
170
|
+
const secret = await getSecret("my-project/production/api-key");
|
|
163
171
|
```
|
|
164
172
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
For local development, set environment variables directly:
|
|
173
|
+
Note: `getSecret` requires `AWS_SESSION_TOKEN` and always calls Secrets Manager.
|
|
168
174
|
|
|
169
|
-
|
|
170
|
-
# .env.local (not committed)
|
|
171
|
-
ANTHROPIC_API_KEY=sk-ant-test123
|
|
172
|
-
MONGODB_URI=mongodb://localhost:27017/dev
|
|
173
|
-
API_KEY=test_key_123
|
|
174
|
-
```
|
|
175
|
+
## Caching
|
|
175
176
|
|
|
176
|
-
|
|
177
|
+
Secrets are cached by default to reduce API calls. Cache is scoped to Lambda execution context (warm starts reuse cache).
|
|
177
178
|
|
|
178
179
|
## IAM Permissions
|
|
179
180
|
|
|
180
|
-
|
|
181
|
+
`JaypieEnvSecret` implements `ISecret`, so grant access directly:
|
|
181
182
|
|
|
182
183
|
```typescript
|
|
184
|
+
const secret = new JaypieEnvSecret(this, "API_KEY");
|
|
183
185
|
secret.grantRead(lambdaFunction);
|
|
184
|
-
|
|
185
|
-
// Or via policy
|
|
186
|
-
lambdaFunction.addToRolePolicy(new PolicyStatement({
|
|
187
|
-
actions: ["secretsmanager:GetSecretValue"],
|
|
188
|
-
resources: [secret.secretArn],
|
|
189
|
-
}));
|
|
190
186
|
```
|
|
191
187
|
|
|
188
|
+
Or use the `secrets` array on `JaypieLambda`, which handles permissions automatically.
|
|
189
|
+
|
|
192
190
|
## Testing
|
|
193
191
|
|
|
194
192
|
Mock secret functions in tests:
|
package/skills/skills.md
CHANGED
|
@@ -16,7 +16,7 @@ Look up skills by alias: `mcp__jaypie__skill(alias)`
|
|
|
16
16
|
| Category | Skills |
|
|
17
17
|
|----------|--------|
|
|
18
18
|
| contents | index, releasenotes |
|
|
19
|
-
| development | documentation, errors, logs, mocks, style, tests |
|
|
20
|
-
| infrastructure | aws, cdk, cicd, datadog, dns, dynamodb, express, lambda, secrets, variables |
|
|
19
|
+
| development | documentation, errors, llm, logs, mocks, monorepo, style, subpackages, tests |
|
|
20
|
+
| infrastructure | aws, cdk, cicd, datadog, dns, dynamodb, express, lambda, secrets, streaming, variables, websockets |
|
|
21
21
|
| patterns | fabric, handlers, models, services, vocabulary |
|
|
22
22
|
| meta | issues, jaypie, skills, tools |
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Create a subpackage within a monorepo
|
|
3
|
+
related: monorepo, tests, style
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Jaypie Subpackage Setup
|
|
7
|
+
|
|
8
|
+
Create a new subpackage within an existing Jaypie monorepo.
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
- TypeScript subpackage with Vite/Vitest
|
|
13
|
+
- Standard Jaypie project structure
|
|
14
|
+
- NPM workspace integration
|
|
15
|
+
- ESLint configuration inheritance
|
|
16
|
+
|
|
17
|
+
## Guidelines
|
|
18
|
+
|
|
19
|
+
- Subpackage names follow `@project-org/package-name` pattern
|
|
20
|
+
- Use `"version": "0.0.1"`, `"type": "module"`, and `"private": true`
|
|
21
|
+
- Place packages in `packages/<package-name>/` directory
|
|
22
|
+
- Use Vite for new TypeScript packages
|
|
23
|
+
- Never manually edit package.json for dependencies; use npm commands
|
|
24
|
+
|
|
25
|
+
## Process
|
|
26
|
+
|
|
27
|
+
1. Create package directory structure
|
|
28
|
+
2. Create configuration files from templates
|
|
29
|
+
3. Create basic src structure
|
|
30
|
+
4. Update workspace configuration
|
|
31
|
+
5. Install dependencies
|
|
32
|
+
|
|
33
|
+
## Directory Structure
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
packages/<package-name>/
|
|
37
|
+
├── src/
|
|
38
|
+
│ ├── index.ts
|
|
39
|
+
│ └── __tests__/
|
|
40
|
+
│ └── index.spec.ts
|
|
41
|
+
├── package.json
|
|
42
|
+
├── tsconfig.json
|
|
43
|
+
├── vite.config.ts
|
|
44
|
+
├── vitest.config.ts
|
|
45
|
+
└── vitest.setup.ts
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Template Files
|
|
49
|
+
|
|
50
|
+
### package.json
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"name": "@project-org/package-name",
|
|
55
|
+
"version": "0.0.1",
|
|
56
|
+
"type": "module",
|
|
57
|
+
"private": true,
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "vite build",
|
|
60
|
+
"clean": "rimraf dist",
|
|
61
|
+
"format": "eslint --fix",
|
|
62
|
+
"format:package": "sort-package-json",
|
|
63
|
+
"lint": "eslint",
|
|
64
|
+
"test": "vitest run",
|
|
65
|
+
"test:watch": "vitest watch",
|
|
66
|
+
"typecheck": "tsc --noEmit"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### tsconfig.json
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"compilerOptions": {
|
|
76
|
+
"target": "ES2020",
|
|
77
|
+
"module": "ESNext",
|
|
78
|
+
"moduleResolution": "bundler",
|
|
79
|
+
"declaration": true,
|
|
80
|
+
"outDir": "./dist",
|
|
81
|
+
"strict": true,
|
|
82
|
+
"esModuleInterop": true,
|
|
83
|
+
"skipLibCheck": true,
|
|
84
|
+
"forceConsistentCasingInFileNames": true
|
|
85
|
+
},
|
|
86
|
+
"exclude": ["node_modules", "dist"],
|
|
87
|
+
"include": ["src/**/*"]
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### vite.config.ts
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { defineConfig } from "vite";
|
|
95
|
+
import dts from "vite-plugin-dts";
|
|
96
|
+
|
|
97
|
+
export default defineConfig({
|
|
98
|
+
plugins: [
|
|
99
|
+
dts({
|
|
100
|
+
include: ["src"],
|
|
101
|
+
exclude: ["**/*.spec.ts"],
|
|
102
|
+
}),
|
|
103
|
+
],
|
|
104
|
+
build: {
|
|
105
|
+
lib: {
|
|
106
|
+
entry: "./src/index.ts",
|
|
107
|
+
name: "PackageName",
|
|
108
|
+
fileName: "index",
|
|
109
|
+
formats: ["es"],
|
|
110
|
+
},
|
|
111
|
+
rollupOptions: {
|
|
112
|
+
external: [
|
|
113
|
+
// Add external dependencies here
|
|
114
|
+
"jaypie",
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
target: "node22",
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### vitest.config.ts
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { defineConfig } from "vitest/config";
|
|
126
|
+
|
|
127
|
+
export default defineConfig({
|
|
128
|
+
test: {
|
|
129
|
+
globals: true,
|
|
130
|
+
environment: "node",
|
|
131
|
+
setupFiles: ["./vitest.setup.ts"],
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### vitest.setup.ts
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { matchers as jaypieMatchers } from "@jaypie/testkit";
|
|
140
|
+
import * as extendedMatchers from "jest-extended";
|
|
141
|
+
import { expect } from "vitest";
|
|
142
|
+
|
|
143
|
+
expect.extend(extendedMatchers);
|
|
144
|
+
expect.extend(jaypieMatchers);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### src/index.ts
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// Export public API here
|
|
151
|
+
export {};
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### src/__tests__/index.spec.ts
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { describe, expect, it } from "vitest";
|
|
158
|
+
|
|
159
|
+
describe("Package Name", () => {
|
|
160
|
+
describe("Base Cases", () => {
|
|
161
|
+
it("is a function", () => {
|
|
162
|
+
// Replace with actual export test
|
|
163
|
+
expect(true).toBe(true);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe("Happy Paths", () => {
|
|
168
|
+
it("works", () => {
|
|
169
|
+
// Add happy path tests
|
|
170
|
+
expect(true).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Installation Commands
|
|
177
|
+
|
|
178
|
+
Add dependencies to the subpackage:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Runtime dependencies
|
|
182
|
+
npm install <package-name> --workspace ./packages/<package-name>
|
|
183
|
+
|
|
184
|
+
# Dev dependencies
|
|
185
|
+
npm install <package-name> --workspace ./packages/<package-name> --save-dev
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Common dev dependencies for subpackages:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm install jest-extended --workspace ./packages/<package-name> --save-dev
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Workspace Configuration
|
|
195
|
+
|
|
196
|
+
The root `vitest.workspace.ts` uses a glob pattern that auto-discovers packages:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
export default ["packages/*/vitest.config.{ts,js}"];
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
New packages are automatically included when they have a `vitest.config.ts`.
|
|
203
|
+
|
|
204
|
+
## Checklist
|
|
205
|
+
|
|
206
|
+
After creating a subpackage:
|
|
207
|
+
|
|
208
|
+
1. ✅ Update package name in `package.json`
|
|
209
|
+
2. ✅ Update `name` in `vite.config.ts` build.lib
|
|
210
|
+
3. ✅ Add external dependencies to `rollupOptions.external`
|
|
211
|
+
4. ✅ Run `npm install` from root to link workspace
|
|
212
|
+
5. ✅ Verify with `npm run build -w packages/<package-name>`
|
|
213
|
+
6. ✅ Verify with `npm run test -w packages/<package-name>`
|
|
214
|
+
|
|
215
|
+
## Next Steps
|
|
216
|
+
|
|
217
|
+
- `skill("tests")` - Testing patterns with Vitest
|
|
218
|
+
- `skill("mocks")` - Mock patterns via @jaypie/testkit
|
|
219
|
+
- `skill("style")` - Code style conventions
|