Skip to content

Commit d993bc1

Browse files
chore: productionalize SDK structure (#24)
* docs: add productionalize design spec Covers tooling (ESLint, Prettier), dependency upgrades, Express migration with security headers, env parser, Makefile, CI/CD, and documentation updates. * docs: address spec review findings - Renumber steps sequentially (no more 3.5) - Reorder env parser before Express migration - Pin packageManager to pnpm@10.33.0 (actual latest) - Add engines field, .git-blame-ignore-revs, .gitignore updates - Split dep upgrades into devDeps-first then runtime - Clarify channel-open/close, parseCommaSeparatedList, COMMITMENT_PUBKEY - Note ESLint override strategy and helmet behavior change * docs: add implementation plan for productionalization 12 tasks covering ESLint, Prettier, dep upgrades, env parser, Express migration, Makefile, .env.example files, CI/CD, README/CLAUDE.md updates, and review gates. * docs: fix trustProxy type to return string for Express compatibility * chore: add ESLint 9, Prettier, tsconfig tweaks, packageManager * style: format codebase with Prettier * chore: add .git-blame-ignore-revs for Prettier formatting commit * chore: upgrade all dependencies to latest - typescript 5.8.0 → 5.9.3, vitest 3.1.0 → 4.1.2 - mppx 0.4.7 → 0.4.11, zod pinned → ^4.3.6 - Add express, helmet, express-rate-limit, cors for upcoming tasks - Fix test mocks for vitest 4.x constructor validation * feat: add Stellar-aware env parsing primitives * feat: add per-example Env config classes using env primitives * refactor: migrate charge server example to Express with security headers * refactor: migrate channel server example to Express with security headers * refactor: use Env classes and env primitives in all example scripts * chore: add self-documenting Makefile for dev workflow * docs: add .env.example files for all demo configurations * ci: add GitHub Actions pipeline with quality gates * docs: update README and CLAUDE.md for productionalization changes * style: format README.md * fix: use strict empty checks in env parsers, remove stack trace from error response - parseRequired/parsePort/parseNumber now use `=== undefined || === ''` instead of falsy check, so values like "0" are not treated as missing - Remove stack trace from /demo/pay error response to avoid leaking internal paths to clients * docs: add code quality & restructure design spec Covers directory restructure (charge/ folder), shared utilities extraction, store key standardization, configurable parameters, robustness (polling backoff, simulation timeouts, error classification), pino logging, and version bump to 0.2.0. * docs: address spec review findings for restructure design - Add test file migration plan (6+ test files) - Define new root index.ts exports explicitly - Clarify resolveKeypair scope (no hex seed, stays Keypair | S...) - Cover standalone close() function extraction - Add test requirements for shared utilities - Correct polling loop count to 6 - Rename shared/constants.ts to shared/defaults.ts - Add shared/errors.ts for error base class consolidation - Note store key migration is acceptable (pre-production) - Add simulate timeout mechanism (Promise.race + AbortController) - Show full channel server parameter signature - Add concrete acceptance criteria for review gates - Note Steps 3 and 5 touch overlapping files * docs: fix minor inconsistencies in restructure spec * docs: add implementation plan for code quality restructure 11 tasks: shared foundation, keypairs, validation, simulate, poll, fee-bump, directory restructure, charge refactor, channel refactor, pino examples, docs/version bump, review gates. * feat: add shared foundation modules (defaults, logger, errors, units) * feat: add shared keypairs and validation utilities * feat: add wrapFeeBump utility with configurable max fee * feat: add simulateCall with timeout and error classification * feat: add pollTransaction with exponential backoff and jitter * refactor: move charge code into charge/ folder, restructure shared utilities - Move sdk/src/Methods.ts → sdk/src/charge/Methods.ts - Move sdk/src/client/ → sdk/src/charge/client/ - Move sdk/src/server/ → sdk/src/charge/server/ - Move sdk/src/scval.ts → sdk/src/shared/scval.ts - Remove sdk/src/signers.ts (replaced by shared/keypairs.ts) - Create sdk/src/charge/index.ts - Update package.json exports: ./client → ./charge/client, ./server → ./charge/server - Bump version to 0.2.0 - Update all import paths in charge, channel, and example files * refactor: charge server+client use shared utils, configurable params, new store keys * refactor: channel server+client use shared utils, finalized→closed, configurable params * feat: add pino logging to example servers * docs: update README and CLAUDE.md for 0.2.0 restructure * fix: wire simulateCall and validateAmount into production code, fix timer leak * feat: add /e2e-check slash command for full end-to-end verification Covers: make check, example script validation, charge E2E demo, channel E2E demo. Channel settlement E2E left as TODO (requires WASM). * feat: add /e2e-check skill with proper frontmatter Structured as a Claude Code project skill with YAML frontmatter for discovery. Covers: make check, example script validation, charge E2E, channel E2E, and channel settlement E2E (TODO). * fix: move e2e-check from commands/ to skills/ directory * refactor: move e2e-check skill to subdirectory with SKILL.md convention Moves .claude/skills/e2e-check.md to .claude/skills/e2e-check/SKILL.md to follow the standard skill directory structure. * fix: replace generic Error throws with typed StellarMppError subclasses Use StellarMppError for client/shared errors, PaymentVerificationError for charge server errors, and ChannelVerificationError for channel server errors. Error messages are unchanged — this is a backward- compatible change since all classes extend Error. * refactor: extract magic numbers to named constants Replace hardcoded fee '100', timeout 180, and timeout 30 with DEFAULT_FEE, DEFAULT_TIMEOUT, and DEFAULT_SIM_TIMEOUT_SECS constants. Values are numerically identical — this is a clarity improvement only. * fix: replace remaining hardcoded fee '100' in close() function Missed one location in the close() standalone export function. * refactor: rename charge examples for naming consistency Rename examples/server.ts → examples/charge-server.ts and examples/client.ts → examples/charge-client.ts to match the channel example naming convention (channel-server.ts, channel-client.ts). Update all references in package.json scripts, demo scripts, demo HTML, README.md, CLAUDE.md, and SKILL.md. * test: add dedicated tests for scValToBigInt covering all 6 ScVal types Tests all supported conversions (u32, i32, u64, i64, u128, i128), a u128 zero edge case, and the error path for unsupported types. * fix: add null safety to State.ts ledger data parsing Add defensive null guards to readCloseEffectiveAtLedger for the contractData/instance chain, providing clearer error attribution when ledger entry structure is unexpected. * ci: fix redundant Node setup and add test coverage reporting Remove duplicate setup-node call. Add --coverage flag to test step using @vitest/coverage-v8. * style: fix prettier formatting after error standardization and constant extraction * fix README docs to match codebase * remove completed superpowers plans * fix: escape dynamic HTML in demo page to prevent DOM XSS * drop CORS middleware from example servers
1 parent a540183 commit d993bc1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+4611
-2065
lines changed

.claude/skills/e2e-check/SKILL.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
---
2+
name: e2e-check
3+
description: Use when verifying the full solution works end-to-end — after refactoring, before PRs, or after dependency upgrades. Runs quality pipeline, validates example scripts, and executes live Stellar testnet demos for both charge and channel payment modes.
4+
---
5+
6+
# E2E Check
7+
8+
Full end-to-end verification: quality pipeline, example script validation, and live Stellar testnet demos.
9+
10+
**Prerequisites:** `.env` file at project root with testnet keys (see `examples/.env.*.example`), funded Stellar testnet accounts, `pnpm` installed.
11+
12+
Run all checks in order. Stop on first failure and report.
13+
14+
## Check 1: Full Quality Pipeline
15+
16+
```bash
17+
make check
18+
```
19+
20+
Runs: install -> format-check -> lint -> typecheck -> test -> build.
21+
22+
**Pass:** 0 lint errors (warnings OK), all tests pass, build succeeds.
23+
24+
## Check 2: Example Script Validation
25+
26+
Verify all 6 example scripts start correctly (imports resolve, env parsing works):
27+
28+
```bash
29+
for f in examples/charge-server.ts examples/charge-client.ts examples/channel-server.ts examples/channel-client.ts examples/channel-open.ts examples/channel-close.ts; do
30+
echo "--- $f ---"
31+
timeout 3 npx tsx "$f" 2>&1 | head -3
32+
echo ""
33+
done
34+
```
35+
36+
**Pass criteria per script:**
37+
38+
| Script | Expected |
39+
| ---------------------------- | --------------------------------------------------------------- |
40+
| `examples/charge-server.ts` | Starts Express on port 3000 (pino JSON log) |
41+
| `examples/charge-client.ts` | Loads keypair, starts client |
42+
| `examples/channel-server.ts` | Starts Express on port 3001 (pino JSON log) |
43+
| `examples/channel-client.ts` | Loads commitment key, starts client |
44+
| `examples/channel-open.ts` | Env validation error: `OPEN_TX_XDR is required` (expected) |
45+
| `examples/channel-close.ts` | Env validation error: `CHANNEL_CONTRACT is required` (expected) |
46+
47+
**Fail:** Any import error, syntax error, or module-not-found error.
48+
49+
## Check 3: Charge E2E Demo
50+
51+
```bash
52+
source .env
53+
STELLAR_RECIPIENT="$STELLAR_RECIPIENT" STELLAR_SECRET="$STELLAR_SECRET" timeout 120 ./demo/run.sh
54+
```
55+
56+
**Expected flow:**
57+
58+
1. Server starts on port 3000 with pino JSON logging
59+
2. Client receives 402 Payment Required challenge
60+
3. Client signs SAC transfer transaction
61+
4. Server verifies and broadcasts on Stellar testnet
62+
5. **200 OK** with "Payment verified" message
63+
64+
**Pass:** Client prints `--- Response (200) ---` with paid content JSON.
65+
66+
## Check 4: Channel E2E Demo
67+
68+
```bash
69+
source .env
70+
CHANNEL_CONTRACT="$CHANNEL_CONTRACT" COMMITMENT_PUBKEY="$COMMITMENT_PUBKEY" COMMITMENT_SECRET="$COMMITMENT_SECRET" SOURCE_ACCOUNT="${SOURCE_ACCOUNT:-}" timeout 120 ./demo/run-channel.sh
71+
```
72+
73+
**Expected flow:**
74+
75+
1. Channel server starts on port 3001 with pino JSON logging
76+
2. Client makes 2 requests, signing cumulative commitments off-chain
77+
3. Request 1: cumulative 1,000,000 stroops -> **200 OK**
78+
4. Request 2: cumulative 2,000,000 stroops -> **200 OK**
79+
5. "No on-chain transaction was needed for this payment!"
80+
81+
**Pass:** Both requests return 200. Cumulative amount grows between requests.
82+
83+
## Check 5: Channel E2E with On-Chain Settlement
84+
85+
Requires the compiled one-way-channel WASM from https://github.com/stellar-experimental/one-way-channel.
86+
87+
```bash
88+
WASM_PATH=/Users/marcelosantos/Workspace/one-way-channel/target/wasm32v1-none/release/channel.wasm \
89+
./demo/run-channel-e2e.sh
90+
```
91+
92+
Full lifecycle: deploy contract -> 2 off-chain payments -> on-chain close -> balance verified at 0.
93+
94+
**Expected flow:**
95+
96+
1. Deploys one-way-channel contract on Stellar testnet
97+
2. Funder opens channel with initial deposit
98+
3. 2 off-chain payment commitments via MPP 402 flow
99+
4. Recipient closes channel on-chain with latest commitment
100+
5. Final balance verified at 0 (all funds claimed)
101+
102+
**Pass:** Script completes with `Channel balance after close: 0` and exit code 0.
103+
104+
## Reporting
105+
106+
After running all checks, report:
107+
108+
| Check | Status | Notes |
109+
| -------------------------------------------------- | --------- | --------------------------------- |
110+
| `make check` (full pipeline) | PASS/FAIL | test count, any errors |
111+
| Example script validation (6 scripts) | PASS/FAIL | which scripts failed |
112+
| Charge E2E (`demo/run.sh`) | PASS/FAIL | final HTTP status |
113+
| Channel E2E (`demo/run-channel.sh`) | PASS/FAIL | request count, cumulative amounts |
114+
| Channel E2E settlement (`demo/run-channel-e2e.sh`) | PASS/FAIL | balance after close |

.git-blame-ignore-revs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Prettier initial formatting
2+
d00c1833f95abad7bd32435c3a6b3a9a244085ae

.github/workflows/ci.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
concurrency:
9+
group: ${{ github.head_ref || github.run_id }}
10+
cancel-in-progress: true
11+
12+
permissions:
13+
contents: read
14+
15+
jobs:
16+
check-test-build:
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
20+
21+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
22+
with:
23+
node-version: '22'
24+
25+
- name: Setup pnpm via corepack
26+
run: |
27+
corepack enable
28+
corepack install
29+
30+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
31+
with:
32+
node-version: '22'
33+
cache: 'pnpm'
34+
35+
- name: Install dependencies
36+
run: pnpm install --frozen-lockfile
37+
38+
- name: Check formatting
39+
run: pnpm format:check
40+
41+
- name: Lint
42+
run: pnpm lint
43+
44+
- name: Typecheck
45+
run: pnpm check:types
46+
47+
- name: Test
48+
run: pnpm test -- --run --coverage --coverage.reporter=text
49+
50+
- name: Build
51+
run: pnpm build
52+
53+
complete:
54+
if: always()
55+
needs: [check-test-build]
56+
runs-on: ubuntu-latest
57+
steps:
58+
- name: Check status
59+
run: |
60+
if [ "${{ needs.check-test-build.result }}" != "success" ]; then
61+
echo "CI failed or was cancelled"
62+
exit 1
63+
fi

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,8 @@ dist/
33
*.tsbuildinfo
44
.DS_Store
55
.claude/
6+
!.claude/skills/
67
package-lock.json
78
.env
9+
examples/.env.*
10+
!examples/.env.*.example

.prettierignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dist/
2+
node_modules/
3+
pnpm-lock.yaml

.prettierrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"semi": false,
3+
"singleQuote": true,
4+
"trailingComma": "all",
5+
"printWidth": 100,
6+
"tabWidth": 2
7+
}

CLAUDE.md

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ pnpm run build # Compile TypeScript → dist/
1919
pnpm run check:types # Type-check only (tsc --noEmit)
2020
pnpm test # Run vitest (watch mode)
2121
pnpm test -- --run # Run tests once without watch
22-
pnpm test -- sdk/src/client/Charge.test.ts # Run a single test file
22+
pnpm test -- sdk/src/charge/client/Charge.test.ts # Run a single test file
23+
make help # Show all Makefile targets
24+
make check # Run full quality pipeline (mirrors CI)
25+
pnpm run lint # Run ESLint
26+
pnpm run format:check # Check Prettier formatting
2327
```
2428

2529
## Verification Checklist
@@ -41,7 +45,7 @@ Each example must load and execute without import/type errors. Expected behavior
4145
```bash
4246
# Charge server — should start and return 402 on requests
4347
PORT=3099 STELLAR_RECIPIENT=GBHEGW3KWOY2OFH767EDALFGCUTBOEVBDQMCKUVJ3LKEWI4ZNVPP5EFC \
44-
npx tsx examples/server.ts
48+
npx tsx examples/charge-server.ts
4549
# → "Stellar MPP server running on http://localhost:3099" — Ctrl+C to stop
4650

4751
# Channel server — should start and return 402 on requests
@@ -55,7 +59,7 @@ CHANNEL_CONTRACT=CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC \
5559
# Client — should load, create keypair, fail on network (no server running)
5660
STELLAR_SECRET=$(npx tsx -e "import{Keypair}from'@stellar/stellar-sdk';console.log(Keypair.random().secret())" 2>/dev/null) \
5761
SERVER_URL=http://localhost:9999 \
58-
npx tsx examples/client.ts
62+
npx tsx examples/charge-client.ts
5963
# → "Using Stellar account: G..." then ECONNREFUSED (expected)
6064

6165
# Channel client — should load, create commitment key, fail on network
@@ -103,38 +107,75 @@ Methods.ts (Zod schema) → client/ (create credentials) + server/ (verify crede
103107

104108
### Module Map
105109

106-
| Path | Role |
107-
|------|------|
108-
| `sdk/src/Methods.ts` | Charge method schema (Zod discriminated union: `transaction` vs `hash` credentials) |
109-
| `sdk/src/constants.ts` | SAC addresses (USDC/XLM), RPC URLs, network passphrases, defaults |
110-
| `sdk/src/scval.ts` | Soroban ScVal ↔ BigInt conversion |
111-
| `sdk/src/client/Charge.ts` | Creates SAC `transfer` invocations; handles pull (send XDR) and push (broadcast + send hash) flows |
112-
| `sdk/src/server/Charge.ts` | Verifies and broadcasts SAC transfers; supports fee sponsorship via FeeBumpTransaction |
113-
| `sdk/src/channel/Methods.ts` | Channel method schema (discriminated union: `open` / `voucher` / `close` actions) |
110+
| Path | Role |
111+
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
112+
| `sdk/src/charge/Methods.ts` | Charge method schema (Zod discriminated union: `transaction` vs `hash` credentials) |
113+
| `sdk/src/charge/client/Charge.ts` | Creates SAC `transfer` invocations; handles pull (send XDR) and push (broadcast + send hash) flows |
114+
| `sdk/src/charge/server/Charge.ts` | Verifies and broadcasts SAC transfers; supports fee sponsorship via FeeBumpTransaction |
115+
| `sdk/src/channel/Methods.ts` | Channel method schema (discriminated union: `open` / `voucher` / `close` actions) |
114116
| `sdk/src/channel/client/Channel.ts` | Signs cumulative commitment amounts off-chain via ed25519; handles `open` action (sends signed deploy tx XDR + initial commitment) |
115-
| `sdk/src/channel/server/Channel.ts` | Verifies commitment signatures via contract simulation; `open` action broadcasts the deploy tx and initialises cumulative store |
116-
| `sdk/src/channel/server/State.ts` | Queries on-chain channel state (balance, close status, refund period) |
117-
| `sdk/src/channel/server/Watcher.ts` | Polls for contract events (close, refund, top_up) |
117+
| `sdk/src/channel/server/Channel.ts` | Verifies commitment signatures via contract simulation; `open` action broadcasts the deploy tx and initialises cumulative store |
118+
| `sdk/src/channel/server/State.ts` | Queries on-chain channel state (balance, close status, refund period) |
119+
| `sdk/src/channel/server/Watcher.ts` | Polls for contract events (close, refund, top_up) |
120+
| `sdk/src/shared/defaults.ts` | Internal default constants (poll intervals, fee limits, timeouts) |
121+
| `sdk/src/shared/errors.ts` | StellarMppError, PaymentVerificationError, ChannelVerificationError |
122+
| `sdk/src/shared/fee-bump.ts` | Fee bump wrapping |
123+
| `sdk/src/shared/keypairs.ts` | Keypair resolution (Keypair or S... string) |
124+
| `sdk/src/shared/logger.ts` | Logger interface (pino-compatible) and noopLogger |
125+
| `sdk/src/shared/poll.ts` | Transaction polling with backoff and jitter |
126+
| `sdk/src/shared/scval.ts` | Soroban ScVal ↔ BigInt conversion |
127+
| `sdk/src/shared/simulate.ts` | Simulation with timeout and error classification |
128+
| `sdk/src/shared/units.ts` | toBaseUnits / fromBaseUnits |
129+
| `sdk/src/shared/validation.ts` | Hex signature and amount validation |
130+
| `sdk/src/constants.ts` | SAC addresses (USDC/XLM), RPC URLs, network passphrases, defaults |
131+
| `sdk/src/env.ts` | Stellar-aware env parsing primitives (parsePort, parseStellarPublicKey, etc.) |
132+
| `examples/config/*.ts` | Per-example Env classes using env primitives |
118133

119134
### Subpath Exports
120135

121136
Package.json exports allow selective imports to avoid bundling unused code:
122-
- `stellar-mpp-sdk` — root (schemas + constants)
123-
- `stellar-mpp-sdk/client` — charge client only
124-
- `stellar-mpp-sdk/server` — charge server only
137+
138+
- `stellar-mpp-sdk` — root (schemas + constants + `resolveKeypair` + `Logger` type + unit conversion)
139+
- `stellar-mpp-sdk/charge` — charge method schema
140+
- `stellar-mpp-sdk/charge/client` — charge client only
141+
- `stellar-mpp-sdk/charge/server` — charge server only
125142
- `stellar-mpp-sdk/channel` — channel schema
126143
- `stellar-mpp-sdk/channel/client` — channel client
127144
- `stellar-mpp-sdk/channel/server` — channel server
145+
- `stellar-mpp-sdk/env` — env parsing primitives
146+
147+
### Shared Utilities Convention
148+
149+
The `shared/` directory contains internal utility modules. These are **not** exported as a subpath from the package. Exceptions that are re-exported from the root `index.ts`:
150+
151+
- `resolveKeypair` (from `shared/keypairs.ts`)
152+
- `Logger` type (from `shared/logger.ts`)
153+
- `toBaseUnits` / `fromBaseUnits` (from `shared/units.ts`)
154+
155+
All other `shared/` modules are strictly internal and consumed only by `charge/` and `channel/` code.
128156

129157
### Key Patterns
130158

131159
- **mppx integration**: Methods defined via `Method.from()`, adapted with `.toClient()` / `.toServer()`. Namespaced as `stellar.charge()` and `stellar.channel()`.
132160
- **Serialization locks**: Both Charge and Channel servers use Promise-based locks (`let verifyLock: Promise<unknown> = Promise.resolve()`) to serialize verification and prevent race conditions on store get/put.
133161
- **Contract simulation**: Uses Soroban RPC `simulateTransaction` for read-only verification — SAC transfer validation, `prepare_commitment` for commitment bytes, and channel state queries.
134162
- **Zod validation**: All method schemas use Zod v4 with discriminated unions for credential/action types.
163+
- **Shared utility extraction**: Common logic (polling, fee bumps, simulation, keypair resolution, validation, error types, logging) lives in `shared/` and is imported by both `charge/` and `channel/`.
164+
- **Configurable defaults**: Server and client functions accept optional parameters (`pollMaxAttempts`, `pollDelayMs`, `pollTimeoutMs`, `simulationTimeoutMs`, `maxFeeBumpStroops`, `logger`) with defaults from `shared/defaults.ts`, applied via parameter destructuring.
165+
- **Logger interface**: Matches pino's API (`debug`, `info`, `warn`, `error` methods). A `noopLogger` is used when no logger is provided.
166+
- **Store key naming**: Keys follow the convention `stellar:{intent}:{type}:{id}` (e.g., `stellar:charge:nonce:abc123`).
167+
- **Express + security headers**: Example servers use Express with helmet and rate limiting middleware. Env vars configure rate limits and trust proxy.
168+
- **Env parsing**: Published as `stellar-mpp-sdk/env`. Core primitives read from `process.env` with validation. Per-example `Env` classes compose these into static getters.
135169

136170
### Test Setup
137171

138172
- **Vitest** with test files colocated alongside source (`*.test.ts` next to `*.ts`)
139173
- Tests mock `@stellar/stellar-sdk` and `mppx` internals
140174
- Integration test at `sdk/src/channel/integration.test.ts`
175+
176+
### Tooling
177+
178+
- **ESLint 9** flat config (`eslint.config.mjs`) with typescript-eslint recommended rules
179+
- **Prettier** for formatting (`.prettierrc`), separate from ESLint
180+
- **GitHub Actions** CI runs: format-check → lint → typecheck → test → build
181+
- **Makefile** for dev workflow (`make help` for all targets, `make check` mirrors CI)

Makefile

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
.PHONY: install build clean typecheck lint lint-fix format format-check test test-watch check \
2+
demo-server demo-client demo-channel-server demo-channel-client help
3+
4+
help: ## Show this help message
5+
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n\nTargets:\n"} /^[a-zA-Z_-]+:.*##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
6+
7+
install: ## Install dependencies
8+
pnpm install
9+
10+
build: ## Compile TypeScript → dist/
11+
pnpm run build
12+
13+
clean: ## Remove dist/ and node_modules/
14+
rm -rf dist node_modules
15+
16+
typecheck: ## Type-check without emitting (tsc --noEmit)
17+
pnpm run check:types
18+
19+
lint: ## Run ESLint
20+
pnpm run lint
21+
22+
lint-fix: ## Run ESLint with auto-fix
23+
pnpm run lint:fix
24+
25+
format: ## Format code with Prettier
26+
pnpm run format
27+
28+
format-check: ## Check formatting (CI-friendly)
29+
pnpm run format:check
30+
31+
test: ## Run tests once (vitest --run)
32+
pnpm test -- --run
33+
34+
test-watch: ## Run tests in watch mode
35+
pnpm test
36+
37+
check: install format-check lint typecheck test build ## Run full quality pipeline (mirrors CI)
38+
39+
demo-server: ## Run charge server example
40+
pnpm run demo:server
41+
42+
demo-client: ## Run charge client example
43+
pnpm run demo:client
44+
45+
demo-channel-server: ## Run channel server example
46+
pnpm run demo:channel-server
47+
48+
demo-channel-client: ## Run channel client example
49+
pnpm run demo:channel-client
50+
51+
.DEFAULT_GOAL := help

0 commit comments

Comments
 (0)