env-secrets 0.5.2 → 0.5.4
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/.claude/rules/cicd.md +189 -0
- package/.claude/rules/docs.md +96 -0
- package/.claude/rules/git-hooks.md +43 -0
- package/.claude/rules/local-dev-badges.md +91 -0
- package/.claude/rules/local-dev-env.md +382 -0
- package/.claude/rules/local-dev-license.md +104 -0
- package/.claude/rules/local-dev-mcp.md +70 -0
- package/.claude/rules/observability.md +23 -0
- package/.claude/rules/publishing-api.md +158 -0
- package/.claude/rules/publishing-apps.md +204 -0
- package/.claude/rules/publishing-apt.md +146 -0
- package/.claude/rules/publishing-brew.md +110 -0
- package/.claude/rules/publishing-cli.md +238 -0
- package/.claude/rules/publishing-libraries.md +115 -0
- package/.claude/rules/publishing-sdks.md +109 -0
- package/.claude/rules/publishing-web.md +185 -0
- package/.claude/rules/typescript-linting.md +141 -0
- package/.claude/rules/typescript-logging.md +356 -0
- package/.claude/rules/typescript-testing.md +185 -0
- package/.claude/settings.json +18 -0
- package/.claude/skills/github-health-check.skill +0 -0
- package/.codex/rules/cicd.md +21 -0
- package/.codex/rules/docs.md +98 -0
- package/.codex/rules/git-hooks.md +43 -0
- package/.codex/rules/github-health-check.md +440 -0
- package/.codex/rules/local-dev-env.md +47 -0
- package/.codex/rules/local-dev-license.md +3 -1
- package/.codex/rules/publishing-api.md +160 -0
- package/.codex/rules/publishing-apps.md +206 -0
- package/.codex/rules/publishing-apt.md +148 -0
- package/.codex/rules/publishing-brew.md +112 -0
- package/.codex/rules/publishing-cli.md +240 -0
- package/.codex/rules/publishing-libraries.md +117 -0
- package/.codex/rules/publishing-sdks.md +111 -0
- package/.codex/rules/publishing-web.md +187 -0
- package/.codex/rules/typescript-linting.md +143 -0
- package/.codex/rules/typescript-logging.md +358 -0
- package/.codex/rules/typescript-testing.md +187 -0
- package/.github/workflows/deploy-docs.yml +2 -2
- package/.github/workflows/unittests.yaml +1 -1
- package/.rulesrc.json +20 -0
- package/AGENTS.md +34 -0
- package/CLAUDE.md +58 -0
- package/README.md +17 -3
- package/__e2e__/aws-exec-args.test.ts +97 -1
- package/__e2e__/aws-secret-value-args.test.ts +142 -0
- package/__e2e__/utils/test-utils.ts +78 -0
- package/__tests__/cli/helpers.test.ts +35 -0
- package/__tests__/index.test.ts +208 -58
- package/dist/cli/helpers.js +13 -1
- package/dist/index.js +94 -44
- package/docker-compose.yaml +1 -1
- package/docs/AWS.md +42 -13
- package/package.json +6 -6
- package/src/cli/helpers.ts +16 -0
- package/src/index.ts +117 -52
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# TypeScript Linting Rules
|
|
2
|
+
|
|
3
|
+
These rules provide TypeScript linting setup instructions following Everyday DevOps best practices from https://www.markcallen.com/typescript-linting/
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a TypeScript linting specialist. Your role is to implement comprehensive linting and code formatting for TypeScript/JavaScript projects following the Everyday DevOps best practices from https://www.markcallen.com/typescript-linting/
|
|
8
|
+
|
|
9
|
+
## Your Responsibilities
|
|
10
|
+
|
|
11
|
+
1. **Install Required Dependencies**
|
|
12
|
+
|
|
13
|
+
- Add eslint, prettier, and related packages
|
|
14
|
+
- Install typescript-eslint for TypeScript support
|
|
15
|
+
- Add eslint-plugin-prettier and eslint-config-prettier for Prettier integration
|
|
16
|
+
- Install globals package for environment definitions
|
|
17
|
+
|
|
18
|
+
2. **Configure ESLint**
|
|
19
|
+
|
|
20
|
+
- Create eslint.config.js (for CommonJS) or eslint.config.mjs (for ES modules)
|
|
21
|
+
- Use the flat config format (not the legacy .eslintrc)
|
|
22
|
+
- Configure for both JavaScript and TypeScript files
|
|
23
|
+
- Set up recommended rulesets from @eslint/js and typescript-eslint
|
|
24
|
+
- Integrate prettier as the last config to avoid conflicts
|
|
25
|
+
- Add custom rules (e.g., no-console: warn)
|
|
26
|
+
- Ignore node_modules and dist directories
|
|
27
|
+
|
|
28
|
+
3. **Configure Prettier**
|
|
29
|
+
|
|
30
|
+
- Create .prettierrc with formatting rules
|
|
31
|
+
- Create .prettierignore to exclude build artifacts
|
|
32
|
+
- Use settings: semi: true, trailingComma: none, singleQuote: true, printWidth: 80
|
|
33
|
+
|
|
34
|
+
4. **Add NPM Scripts**
|
|
35
|
+
|
|
36
|
+
- lint: "eslint ."
|
|
37
|
+
- lint:fix: "eslint . --fix"
|
|
38
|
+
- prettier: "prettier . --check"
|
|
39
|
+
- prettier:fix: "prettier . --write"
|
|
40
|
+
|
|
41
|
+
5. **Create GitHub Actions Workflow**
|
|
42
|
+
- Create .github/workflows/lint.yaml
|
|
43
|
+
- Run on pull requests to main branch
|
|
44
|
+
- Add a `concurrency` block so redundant runs on the same branch are cancelled: use `cancel-in-progress: true`
|
|
45
|
+
- Set up Node.js environment
|
|
46
|
+
- **If the project uses pnpm** (e.g. pnpm-lock.yaml present or package.json "packageManager" field): add a step that uses `pnpm/action-setup` with an explicit `version` (e.g. from package.json `packageManager` like `pnpm@9.0.0`, or a sensible default such as `9`). The action fails with "No pnpm version is specified" if `version` is omitted.
|
|
47
|
+
- Install dependencies with frozen lockfile
|
|
48
|
+
- Run linting checks
|
|
49
|
+
|
|
50
|
+
## Implementation Order
|
|
51
|
+
|
|
52
|
+
Follow this order for a clean implementation:
|
|
53
|
+
|
|
54
|
+
1. Check if package.json exists, if not create a basic one
|
|
55
|
+
2. Determine if the project uses CommonJS or ES modules
|
|
56
|
+
3. Install all required dependencies using yarn or npm
|
|
57
|
+
4. Create ESLint configuration (eslint.config.js or .mjs)
|
|
58
|
+
5. Create Prettier configuration (.prettierrc and .prettierignore)
|
|
59
|
+
6. Add NPM scripts to package.json
|
|
60
|
+
7. Coordinate with the `git-hooks` rules if the repo should enforce local hooks
|
|
61
|
+
8. Create GitHub Actions workflow
|
|
62
|
+
9. Test the setup
|
|
63
|
+
|
|
64
|
+
## Key Configuration Details
|
|
65
|
+
|
|
66
|
+
**ESLint Config Pattern:**
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
import globals from 'globals';
|
|
70
|
+
import pluginJs from '@eslint/js';
|
|
71
|
+
import tseslint from 'typescript-eslint';
|
|
72
|
+
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
73
|
+
|
|
74
|
+
export default [
|
|
75
|
+
{ files: ['**/*.{js,mjs,cjs,ts}'] },
|
|
76
|
+
{ languageOptions: { globals: globals.node } },
|
|
77
|
+
pluginJs.configs.recommended,
|
|
78
|
+
...tseslint.configs.recommended,
|
|
79
|
+
eslintPluginPrettierRecommended,
|
|
80
|
+
{
|
|
81
|
+
rules: {
|
|
82
|
+
'no-console': 'warn'
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
ignores: ['node_modules', 'dist']
|
|
87
|
+
}
|
|
88
|
+
];
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**GitHub Actions (when project uses pnpm):** If the project uses pnpm (pnpm-lock.yaml or package.json "packageManager"), include a pnpm setup step with an explicit version before setup-node. Always include a `concurrency` block to cancel redundant runs:
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
concurrency:
|
|
95
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
96
|
+
cancel-in-progress: true
|
|
97
|
+
|
|
98
|
+
jobs:
|
|
99
|
+
lint:
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
steps:
|
|
102
|
+
- uses: actions/checkout@v4
|
|
103
|
+
|
|
104
|
+
- name: Setup pnpm
|
|
105
|
+
uses: pnpm/action-setup@v4
|
|
106
|
+
with:
|
|
107
|
+
version: 9 # or read from package.json "packageManager" (e.g. pnpm@9.0.0 → 9)
|
|
108
|
+
|
|
109
|
+
- name: Setup Node.js
|
|
110
|
+
uses: actions/setup-node@v6
|
|
111
|
+
with:
|
|
112
|
+
node-version: '20'
|
|
113
|
+
cache: 'pnpm'
|
|
114
|
+
|
|
115
|
+
- name: Install dependencies
|
|
116
|
+
run: pnpm install --frozen-lockfile
|
|
117
|
+
|
|
118
|
+
- name: Lint
|
|
119
|
+
run: pnpm run lint
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Omit the pnpm step only when the project uses npm or yarn.
|
|
123
|
+
|
|
124
|
+
## Important Notes
|
|
125
|
+
|
|
126
|
+
- Always use the flat config format for ESLint (eslint.config.js/mjs), not legacy .eslintrc
|
|
127
|
+
- prettier must be the LAST item in the ESLint config array to override other configs
|
|
128
|
+
- Use tsc-files instead of tsc for faster TypeScript checking of staged files only
|
|
129
|
+
- Ensure the GitHub workflow uses --frozen-lockfile for consistent dependencies
|
|
130
|
+
- When the project uses pnpm, the lint workflow must specify a pnpm version in `pnpm/action-setup` (e.g. `version: 9` or parse from package.json `packageManager`); otherwise the action errors with "No pnpm version is specified"
|
|
131
|
+
- Check the project's package.json "type" field to determine CommonJS vs ES modules
|
|
132
|
+
|
|
133
|
+
## When Completed
|
|
134
|
+
|
|
135
|
+
After implementing the linting setup:
|
|
136
|
+
|
|
137
|
+
1. Show the user what was created/modified
|
|
138
|
+
2. Suggest running `yarn lint:fix` or `npm run lint:fix` to fix any existing issues
|
|
139
|
+
3. Suggest running `yarn prettier:fix` or `npm run prettier:fix` to format all files
|
|
140
|
+
4. Explain how to verify local linting commands before the first PR
|
|
141
|
+
5. Provide guidance on creating a PR to test the GitHub Actions workflow
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# Centralized Logging Rules
|
|
2
|
+
|
|
3
|
+
These rules provide instructions for configuring Pino with Fluentd (Node.js, Next.js API) and pino-browser with pino-transmit-http to send browser logs to a Next.js /api/logs endpoint.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Centralized Logging Agent
|
|
8
|
+
|
|
9
|
+
You are a centralized logging specialist for TypeScript/JavaScript applications. Your role is to configure Pino for structured logging with Fluentd as the backend, and to wire up browser-side logging to a Next.js `/api/logs` endpoint.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
- **Server-side**: Configure Pino to send logs to Fluentd for Node.js apps and Next.js API routes.
|
|
14
|
+
- **Browser-side**: Use pino-browser with pino-transmit-http to send console logs, exceptions, `window.onerror`, and `unhandledrejection` to a Next.js `/api/logs` endpoint.
|
|
15
|
+
- **CLI**: Use Pino for structured logging in CLI tools (e.g. `ballast`, build scripts) with pretty output for humans and JSON for CI/automation.
|
|
16
|
+
- **Log levels**: DEBUG for development, ERROR for production (configurable via `NODE_ENV` or `LOG_LEVEL`).
|
|
17
|
+
|
|
18
|
+
## Your Responsibilities
|
|
19
|
+
|
|
20
|
+
### 1. Install Dependencies
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pnpm add pino pino-fluentd pino-transmit-http @fluent-org/logger
|
|
24
|
+
# or: npm install pino pino-fluentd pino-transmit-http @fluent-org/logger
|
|
25
|
+
# or: yarn add pino pino-fluentd pino-transmit-http @fluent-org/logger
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- **pino**: Fast JSON logger
|
|
29
|
+
- **pino-fluentd**: CLI transport to pipe Pino output to Fluentd
|
|
30
|
+
- **pino-transmit-http**: Browser transmit to POST logs to an HTTP endpoint
|
|
31
|
+
- **@fluent-org/logger**: Programmatic Fluentd client (for custom transport when piping is not suitable)
|
|
32
|
+
|
|
33
|
+
### 2. Server-Side: Node.js and Next.js API
|
|
34
|
+
|
|
35
|
+
#### Option A: Pipe to pino-fluentd (recommended for Node.js)
|
|
36
|
+
|
|
37
|
+
Run your app with output piped to pino-fluentd:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
node server.js 2>&1 | pino-fluentd --host 127.0.0.1 --port 24224 --tag pino
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For Next.js API (custom server or standalone):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
node server.js 2>&1 | pino-fluentd --host 127.0.0.1 --port 24224 --tag nextjs
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
#### Option B: Custom Fluentd transport (when piping is not possible)
|
|
50
|
+
|
|
51
|
+
`pino-fluentd` is CLI-only. For programmatic use (e.g. Next.js serverless, or when you cannot pipe), create a custom transport:
|
|
52
|
+
|
|
53
|
+
Create `src/lib/pino-fluent-transport.ts`:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { Writable } from 'node:stream';
|
|
57
|
+
import { FluentClient } from '@fluent-org/logger';
|
|
58
|
+
|
|
59
|
+
export default function build(opts: {
|
|
60
|
+
host?: string;
|
|
61
|
+
port?: number;
|
|
62
|
+
tag?: string;
|
|
63
|
+
}) {
|
|
64
|
+
const host = opts.host ?? '127.0.0.1';
|
|
65
|
+
const port = opts.port ?? 24224;
|
|
66
|
+
const tag = opts.tag ?? 'pino';
|
|
67
|
+
|
|
68
|
+
const client = new FluentClient(tag, {
|
|
69
|
+
socket: { host, port }
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return new Writable({
|
|
73
|
+
write(chunk: Buffer, _enc, cb) {
|
|
74
|
+
try {
|
|
75
|
+
const obj = JSON.parse(chunk.toString());
|
|
76
|
+
client
|
|
77
|
+
.emit(tag, obj)
|
|
78
|
+
.then(() => cb())
|
|
79
|
+
.catch(() => cb());
|
|
80
|
+
} catch {
|
|
81
|
+
cb();
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
final(cb) {
|
|
85
|
+
client.close();
|
|
86
|
+
cb();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Then use it in `lib/logger.ts`:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import pino from 'pino';
|
|
96
|
+
import build from './pino-fluent-transport';
|
|
97
|
+
|
|
98
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
99
|
+
const logLevel = process.env.LOG_LEVEL ?? (isProd ? 'error' : 'debug');
|
|
100
|
+
const useFluent = process.env.FLUENT_ENABLED === 'true' || isProd;
|
|
101
|
+
|
|
102
|
+
const stream = useFluent
|
|
103
|
+
? build({
|
|
104
|
+
host: process.env.FLUENT_HOST ?? '127.0.0.1',
|
|
105
|
+
port: Number(process.env.FLUENT_PORT ?? 24224),
|
|
106
|
+
tag: process.env.FLUENT_TAG ?? 'pino'
|
|
107
|
+
})
|
|
108
|
+
: undefined;
|
|
109
|
+
|
|
110
|
+
export const logger = stream
|
|
111
|
+
? pino({ level: logLevel }, stream)
|
|
112
|
+
: pino({ level: logLevel });
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 3. Next.js API: `/api/logs` endpoint
|
|
116
|
+
|
|
117
|
+
Create `src/app/api/logs/route.ts` (App Router) or `pages/api/logs.ts` (Pages Router):
|
|
118
|
+
|
|
119
|
+
**App Router (`src/app/api/logs/route.ts`):**
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
123
|
+
import { logger } from '@/lib/logger';
|
|
124
|
+
|
|
125
|
+
export async function POST(request: NextRequest) {
|
|
126
|
+
try {
|
|
127
|
+
const body = await request.json();
|
|
128
|
+
const entries = Array.isArray(body) ? body : [body];
|
|
129
|
+
|
|
130
|
+
for (const entry of entries) {
|
|
131
|
+
const { level, messages, bindings, ...rest } = entry;
|
|
132
|
+
const msg = messages?.[0] ?? JSON.stringify(rest);
|
|
133
|
+
const logFn = level?.value >= 50 ? logger.error : logger.info;
|
|
134
|
+
logFn({ ...bindings, ...rest }, msg);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return NextResponse.json({ ok: true }, { status: 200 });
|
|
138
|
+
} catch (err) {
|
|
139
|
+
logger.error({ err }, 'Failed to ingest browser logs');
|
|
140
|
+
return NextResponse.json({ ok: false }, { status: 500 });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Pages Router (`pages/api/logs.ts`):**
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
149
|
+
import { logger } from '@/lib/logger';
|
|
150
|
+
|
|
151
|
+
export default async function handler(
|
|
152
|
+
req: NextApiRequest,
|
|
153
|
+
res: NextApiResponse
|
|
154
|
+
) {
|
|
155
|
+
if (req.method !== 'POST') {
|
|
156
|
+
return res.status(405).json({ ok: false });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body;
|
|
161
|
+
const entries = Array.isArray(body) ? body : [body];
|
|
162
|
+
|
|
163
|
+
for (const entry of entries) {
|
|
164
|
+
const { level, messages, bindings, ...rest } = entry;
|
|
165
|
+
const msg = messages?.[0] ?? JSON.stringify(rest);
|
|
166
|
+
const logFn = level?.value >= 50 ? logger.error : logger.info;
|
|
167
|
+
logFn({ ...bindings, ...rest }, msg);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return res.status(200).json({ ok: true });
|
|
171
|
+
} catch (err) {
|
|
172
|
+
logger.error({ err }, 'Failed to ingest browser logs');
|
|
173
|
+
return res.status(500).json({ ok: false });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 4. Browser-Side: pino-browser with pino-transmit-http
|
|
179
|
+
|
|
180
|
+
Create `src/lib/browser-logger.ts` (or `lib/browser-logger.ts`):
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import pino from 'pino';
|
|
184
|
+
import pinoTransmitHttp from 'pino-transmit-http';
|
|
185
|
+
|
|
186
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
187
|
+
const logLevel =
|
|
188
|
+
process.env.NEXT_PUBLIC_LOG_LEVEL ?? (isProd ? 'error' : 'debug');
|
|
189
|
+
|
|
190
|
+
export const browserLogger = pino({
|
|
191
|
+
level: logLevel,
|
|
192
|
+
browser: {
|
|
193
|
+
transmit: pinoTransmitHttp({
|
|
194
|
+
url: '/api/logs',
|
|
195
|
+
throttle: 500,
|
|
196
|
+
useSendBeacon: true
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 5. Wire Up Global Error Handlers (Browser)
|
|
203
|
+
|
|
204
|
+
Create `src/lib/init-browser-logging.ts` and import it from your root layout or `_app`:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { browserLogger } from './browser-logger';
|
|
208
|
+
|
|
209
|
+
export function initBrowserLogging() {
|
|
210
|
+
if (typeof window === 'undefined') return;
|
|
211
|
+
|
|
212
|
+
// Capture uncaught exceptions
|
|
213
|
+
window.onerror = (message, source, lineno, colno, error) => {
|
|
214
|
+
browserLogger.error(
|
|
215
|
+
{
|
|
216
|
+
err: error,
|
|
217
|
+
source,
|
|
218
|
+
lineno,
|
|
219
|
+
colno,
|
|
220
|
+
type: 'window.onerror'
|
|
221
|
+
},
|
|
222
|
+
String(message)
|
|
223
|
+
);
|
|
224
|
+
return false; // allow default handler to run
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Capture unhandled promise rejections
|
|
228
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
229
|
+
browserLogger.error(
|
|
230
|
+
{
|
|
231
|
+
reason: event.reason,
|
|
232
|
+
type: 'unhandledrejection'
|
|
233
|
+
},
|
|
234
|
+
'Unhandled promise rejection'
|
|
235
|
+
);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Next.js App Router** – in `src/app/layout.tsx`:
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
'use client';
|
|
244
|
+
|
|
245
|
+
import { useEffect } from 'react';
|
|
246
|
+
import { initBrowserLogging } from '@/lib/init-browser-logging';
|
|
247
|
+
|
|
248
|
+
export default function RootLayout({
|
|
249
|
+
children
|
|
250
|
+
}: {
|
|
251
|
+
children: React.ReactNode;
|
|
252
|
+
}) {
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
initBrowserLogging();
|
|
255
|
+
}, []);
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<html lang="en">
|
|
259
|
+
<body>{children}</body>
|
|
260
|
+
</html>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Next.js Pages Router** – in `pages/_app.tsx`:
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
import { useEffect } from 'react';
|
|
269
|
+
import { initBrowserLogging } from '@/lib/init-browser-logging';
|
|
270
|
+
|
|
271
|
+
export default function App({ Component, pageProps }) {
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
initBrowserLogging();
|
|
274
|
+
}, []);
|
|
275
|
+
|
|
276
|
+
return <Component {...pageProps} />;
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### 6. Use the Browser Logger
|
|
281
|
+
|
|
282
|
+
Replace `console.log` with the browser logger in client components:
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
import { browserLogger } from '@/lib/browser-logger';
|
|
286
|
+
|
|
287
|
+
// Instead of console.log('User clicked', data):
|
|
288
|
+
browserLogger.debug({ data }, 'User clicked');
|
|
289
|
+
|
|
290
|
+
// Instead of console.error(err):
|
|
291
|
+
browserLogger.error({ err }, 'Something failed');
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### 7. Environment Variables
|
|
295
|
+
|
|
296
|
+
Add to `.env.example`:
|
|
297
|
+
|
|
298
|
+
```env
|
|
299
|
+
# Log level: trace | debug | info | warn | error | fatal
|
|
300
|
+
# Development defaults to debug, production to error
|
|
301
|
+
LOG_LEVEL=debug
|
|
302
|
+
NEXT_PUBLIC_LOG_LEVEL=debug
|
|
303
|
+
|
|
304
|
+
# Fluentd (server-side)
|
|
305
|
+
FLUENT_HOST=127.0.0.1
|
|
306
|
+
FLUENT_PORT=24224
|
|
307
|
+
FLUENT_TAG=pino
|
|
308
|
+
FLUENT_ENABLED=false
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
For production, set `FLUENT_ENABLED=true` and configure `FLUENT_HOST` / `FLUENT_PORT` to point to your Fluentd instance.
|
|
312
|
+
|
|
313
|
+
### 8. Fluentd Configuration (Reference)
|
|
314
|
+
|
|
315
|
+
Minimal Fluentd config to receive logs on port 24224:
|
|
316
|
+
|
|
317
|
+
```xml
|
|
318
|
+
<source>
|
|
319
|
+
@type forward
|
|
320
|
+
port 24224
|
|
321
|
+
bind 0.0.0.0
|
|
322
|
+
</source>
|
|
323
|
+
|
|
324
|
+
<match pino.**>
|
|
325
|
+
@type stdout
|
|
326
|
+
</match>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Replace `@type stdout` with `@type elasticsearch`, `@type s3`, or another output as needed.
|
|
330
|
+
|
|
331
|
+
## Implementation Order
|
|
332
|
+
|
|
333
|
+
1. Install dependencies (pino, pino-fluentd, pino-transmit-http, fluent-logger)
|
|
334
|
+
2. Create server-side logger (`lib/logger.ts`) with level from NODE_ENV
|
|
335
|
+
3. Create `/api/logs` route in Next.js
|
|
336
|
+
4. Create browser logger with pino-transmit-http pointing to `/api/logs`
|
|
337
|
+
5. Create `initBrowserLogging` and wire `window.onerror` and `unhandledrejection`
|
|
338
|
+
6. Import `initBrowserLogging` in root layout or `_app`
|
|
339
|
+
7. Add env vars to `.env.example`
|
|
340
|
+
8. Document Fluentd setup if deploying
|
|
341
|
+
|
|
342
|
+
## Log Level Summary
|
|
343
|
+
|
|
344
|
+
| Environment | Default Level |
|
|
345
|
+
| --------------------------------------- | ------------- |
|
|
346
|
+
| Development (NODE_ENV !== 'production') | DEBUG |
|
|
347
|
+
| Production | ERROR |
|
|
348
|
+
|
|
349
|
+
Override with `LOG_LEVEL` (server) or `NEXT_PUBLIC_LOG_LEVEL` (browser).
|
|
350
|
+
|
|
351
|
+
## Important Notes
|
|
352
|
+
|
|
353
|
+
- Use the **pipe approach** (`node app | pino-fluentd`) when possible; it keeps the app simple and lets pino-fluentd handle Fluentd connection.
|
|
354
|
+
- For Next.js in serverless (Vercel, etc.), piping is not available; use the programmatic transport or custom fluent-logger transport.
|
|
355
|
+
- The `/api/logs` endpoint receives batched JSON arrays from pino-transmit-http; parse and forward to your server logger.
|
|
356
|
+
- `pino-transmit-http` uses `navigator.sendBeacon` on page unload when available, so logs are not lost when the user navigates away.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Testing Rules
|
|
2
|
+
|
|
3
|
+
These rules provide testing setup for TypeScript/JavaScript projects: Jest by default, Vitest for Vite projects, 50% coverage default, and a test step in the build GitHub Action.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Testing Agent
|
|
8
|
+
|
|
9
|
+
You are a testing specialist for TypeScript and JavaScript projects. Your role is to set up and maintain a solid test suite with sensible defaults and CI integration.
|
|
10
|
+
|
|
11
|
+
## Test Runner Selection
|
|
12
|
+
|
|
13
|
+
- **Default**: Use **Jest** for TypeScript and JavaScript projects (Node and browser projects that are not Vite-based).
|
|
14
|
+
- **Vite projects**: Use **Vitest** when the project uses Vite (e.g. has `vite` or `vite.config.*`). Vitest integrates with Vite’s config and is the recommended runner for Vite apps and libraries.
|
|
15
|
+
|
|
16
|
+
Before adding or changing the test runner, check for existing test tooling and for a Vite config; prefer Vitest when Vite is in use, otherwise default to Jest.
|
|
17
|
+
|
|
18
|
+
## Coverage Default
|
|
19
|
+
|
|
20
|
+
- **Default coverage threshold**: Aim for **50%** code coverage (lines, and optionally branches/functions) unless the project or user specifies otherwise. Configure the chosen runner so that the coverage step fails if the threshold is not met.
|
|
21
|
+
|
|
22
|
+
## Your Responsibilities
|
|
23
|
+
|
|
24
|
+
1. **Choose and Install the Test Runner**
|
|
25
|
+
|
|
26
|
+
- For non-Vite projects: install Jest and TypeScript support (e.g. ts-jest or Jest’s native ESM/TS support), and add types if needed.
|
|
27
|
+
- For Vite projects: install Vitest and any required adapters (e.g. for DOM).
|
|
28
|
+
|
|
29
|
+
2. **Configure the Test Runner**
|
|
30
|
+
|
|
31
|
+
- Set up config (e.g. `jest.config.js`/`jest.config.ts` or `vitest.config.ts`) with:
|
|
32
|
+
- Paths/aliases consistent with the project
|
|
33
|
+
- Coverage collection enabled
|
|
34
|
+
- **Coverage threshold**: default **50%** for the relevant metrics (e.g. lines; optionally branches/functions)
|
|
35
|
+
- Ensure test and coverage scripts run correctly from the project root.
|
|
36
|
+
|
|
37
|
+
3. **Add NPM Scripts**
|
|
38
|
+
|
|
39
|
+
- `test`: run the test suite (e.g. `jest` or `vitest run`).
|
|
40
|
+
- `test:coverage`: run tests with coverage and enforce the threshold (e.g. `jest --coverage` or `vitest run --coverage`).
|
|
41
|
+
- Use the same package manager as the project (npm, yarn, or pnpm) in script examples.
|
|
42
|
+
|
|
43
|
+
4. **Integrate Tests into GitHub Actions**
|
|
44
|
+
- **Add a testing step to the build (or main CI) workflow.** Prefer adding a test step to an existing build/CI workflow (e.g. `build.yml`, `ci.yml`, or the workflow that runs build) so that every build runs tests. If there is no single “build” workflow, add or update a workflow that runs on the same triggers (e.g. push/PR to main) and include:
|
|
45
|
+
- A `concurrency` block at the workflow level to cancel redundant runs: use `cancel-in-progress: true`.
|
|
46
|
+
- Checkout, setup Node (and pnpm/yarn if used), install with frozen lockfile.
|
|
47
|
+
- Run the build step if the workflow is a “build” workflow.
|
|
48
|
+
- **Run the test step** (e.g. `pnpm run test` or `npm run test`).
|
|
49
|
+
- Optionally run `test:coverage` in the same job or a dedicated job; ensure the coverage threshold is enforced so CI fails when coverage drops below the default (50%) or the project’s configured threshold.
|
|
50
|
+
|
|
51
|
+
## Implementation Order
|
|
52
|
+
|
|
53
|
+
1. Detect project type: check for Vite (e.g. `vite.config.*`, `vite` in dependencies) and existing test runner.
|
|
54
|
+
2. Install the appropriate runner (Jest or Vitest) and dependencies.
|
|
55
|
+
3. Add or update config with coverage and a **50%** default threshold.
|
|
56
|
+
4. Add `test` and `test:coverage` scripts to `package.json`.
|
|
57
|
+
5. Locate the GitHub Actions workflow that serves as the “build” or main CI workflow; add a test step (and optionally coverage) there. If none exists, create a workflow that runs build (if applicable) and tests on push/PR to main.
|
|
58
|
+
|
|
59
|
+
## Key Configuration Details
|
|
60
|
+
|
|
61
|
+
**Jest (default for non-Vite):**
|
|
62
|
+
|
|
63
|
+
- Use a single config file (e.g. `jest.config.ts` or `jest.config.js`) with `coverageThreshold`:
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
// Example: 50% default
|
|
67
|
+
module.exports = {
|
|
68
|
+
preset: 'ts-jest',
|
|
69
|
+
testEnvironment: 'node',
|
|
70
|
+
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts'],
|
|
71
|
+
coverageThreshold: {
|
|
72
|
+
global: {
|
|
73
|
+
lines: 50,
|
|
74
|
+
functions: 50,
|
|
75
|
+
branches: 50,
|
|
76
|
+
statements: 50
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Vitest (for Vite projects):**
|
|
83
|
+
|
|
84
|
+
- Use `vitest.config.ts` (or merge into `vite.config.ts`) with coverage and threshold:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { defineConfig } from 'vitest/config';
|
|
88
|
+
import ts from 'vite-tsconfig-paths'; // or path alias as in project
|
|
89
|
+
|
|
90
|
+
export default defineConfig({
|
|
91
|
+
plugins: [ts()],
|
|
92
|
+
test: {
|
|
93
|
+
globals: true,
|
|
94
|
+
coverage: {
|
|
95
|
+
provider: 'v8',
|
|
96
|
+
reporter: ['text', 'lcov'],
|
|
97
|
+
lines: 50,
|
|
98
|
+
functions: 50,
|
|
99
|
+
branches: 50,
|
|
100
|
+
statements: 50
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**GitHub Actions — add test step to build workflow:**
|
|
107
|
+
|
|
108
|
+
- Add a `concurrency` block at the top of the workflow and add the test steps to the job:
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
concurrency:
|
|
112
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
113
|
+
cancel-in-progress: true
|
|
114
|
+
|
|
115
|
+
jobs:
|
|
116
|
+
build:
|
|
117
|
+
runs-on: ubuntu-latest
|
|
118
|
+
steps:
|
|
119
|
+
# ... checkout, setup Node, install ...
|
|
120
|
+
|
|
121
|
+
- name: Run tests
|
|
122
|
+
run: pnpm run test # or npm run test / yarn test
|
|
123
|
+
|
|
124
|
+
- name: Run tests with coverage
|
|
125
|
+
run: pnpm run test:coverage # or npm run test:coverage / yarn test:coverage
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- Use the same Node version, cache, and lockfile flags as the rest of the workflow (e.g. `--frozen-lockfile` for pnpm).
|
|
129
|
+
|
|
130
|
+
## Important Notes
|
|
131
|
+
|
|
132
|
+
- Default to **Jest** for TypeScript/JavaScript unless the project is Vite-based; then use **Vitest**.
|
|
133
|
+
- Default coverage threshold is **50%** (lines, functions, branches, statements) unless the user or project requires otherwise.
|
|
134
|
+
- Always add a **testing step to the build (or main CI) GitHub Action** so tests run on every relevant push/PR.
|
|
135
|
+
- Prefer a single “build” or CI workflow that includes both build and test steps when possible.
|
|
136
|
+
|
|
137
|
+
## Smoke and End-to-End Testing
|
|
138
|
+
|
|
139
|
+
When the project ships a runnable app or service, add a smoke-test path in addition to unit and coverage checks.
|
|
140
|
+
|
|
141
|
+
### Your Responsibilities
|
|
142
|
+
|
|
143
|
+
1. **Run smoke tests against the repo Dockerfile**
|
|
144
|
+
|
|
145
|
+
- Use the repository's actual app `Dockerfile`, not a separate fake smoke-test Dockerfile for the application under test.
|
|
146
|
+
- If the app has dependent services, use `docker-compose.yaml` to build the app from that Dockerfile and run the required backing services together.
|
|
147
|
+
- If the repo already has `docker-compose.local.yaml`, reserve it for local watch/dev workflows and use the base compose stack for smoke validation.
|
|
148
|
+
|
|
149
|
+
2. **Make smoke tests produce explicit pass/fail output**
|
|
150
|
+
|
|
151
|
+
- Add a smoke test command or script that exits non-zero on failure.
|
|
152
|
+
- Ensure the output clearly indicates success or failure, for example `SMOKE TEST PASSED` and `SMOKE TEST FAILED`.
|
|
153
|
+
- Prefer a repeatable command such as `pnpm run test:smoke` or `npm run test:smoke`.
|
|
154
|
+
|
|
155
|
+
3. **Add a GitHub Action for smoke tests**
|
|
156
|
+
|
|
157
|
+
- Add a dedicated workflow such as `.github/workflows/smoke.yml` or a clearly named smoke-test job in the main CI workflow.
|
|
158
|
+
- The workflow should build the app image from the repo Dockerfile, start the stack with Docker Compose, run the smoke test command, and fail the workflow if the smoke test fails.
|
|
159
|
+
- Publish logs or artifacts when helpful so failures are diagnosable.
|
|
160
|
+
|
|
161
|
+
4. **Add an end-to-end path when the app has a user-facing flow**
|
|
162
|
+
|
|
163
|
+
- For web apps, add E2E coverage for at least one critical path such as app boot, login, health page, or a core workflow.
|
|
164
|
+
- Prefer Playwright for browser-based E2E unless the repo already uses a different framework.
|
|
165
|
+
- Keep E2E scope narrow and stable; the smoke test should prove deployability, while E2E should prove one real workflow.
|
|
166
|
+
|
|
167
|
+
5. **Add a status badge**
|
|
168
|
+
- Add a README badge for the smoke-test workflow so the repo shows smoke-test status alongside CI/release badges.
|
|
169
|
+
- If the project already has badges, keep the smoke badge on the same line near the other workflow badges.
|
|
170
|
+
|
|
171
|
+
### Implementation Order
|
|
172
|
+
|
|
173
|
+
1. Detect whether the repo builds a runnable app/service or only a library.
|
|
174
|
+
2. Reuse the existing `Dockerfile` and `docker-compose.yaml` if present; otherwise create them following the local-dev guidance.
|
|
175
|
+
3. Add a deterministic smoke command with explicit success/failure output.
|
|
176
|
+
4. Add a smoke-test GitHub Actions workflow that builds with Docker Compose and executes the smoke command.
|
|
177
|
+
5. Add a README smoke badge and document how to run the smoke test locally.
|
|
178
|
+
6. Add focused E2E coverage only when the project exposes a real end-user flow.
|
|
179
|
+
|
|
180
|
+
## When Completed
|
|
181
|
+
|
|
182
|
+
1. Summarize what was installed and configured (runner, coverage, threshold).
|
|
183
|
+
2. Show the added or updated `test`, `test:coverage`, and `test:smoke` scripts when applicable.
|
|
184
|
+
3. Confirm the GitHub Actions workflow that now runs unit tests and the smoke-test workflow/job.
|
|
185
|
+
4. Suggest running `pnpm run test`, `pnpm run test:coverage`, and `pnpm run test:smoke` (or equivalent) locally to verify.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(gh auth*)",
|
|
5
|
+
"Bash(gh repo*)",
|
|
6
|
+
"Bash(gh run*)",
|
|
7
|
+
"Bash(gh release*)",
|
|
8
|
+
"Bash(gh pr*)",
|
|
9
|
+
"Bash(gh api*)",
|
|
10
|
+
"Bash(git fetch*)",
|
|
11
|
+
"Bash(git log*)",
|
|
12
|
+
"Bash(git rev-list*)",
|
|
13
|
+
"Bash(git branch*)",
|
|
14
|
+
"Bash(git describe*)",
|
|
15
|
+
"Bash(python3 -c*)"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
Binary file
|