## Summary Implemented a comprehensive API versioning system with resource-level version changes, strong typing using Zod schemas, and a composable getApi* pattern. ## Key Changes ### Versioning Infrastructure - **VersionChange base class**: Now strongly typed with Zod schemas (TNewSchema, TOldSchema, TDataSchema) - **Bidirectional transforms**: `transformRequest` (old→new) and `transformResponse` (new→old) - **Input + Data pattern**: `input` for version-specific data, `data` for additional context - **applyVersionChanges**: Fixed TypeScript errors and added biome-ignore comments for necessary `any` types ### Resource Organization - **Folder structure**: Each resource gets its own `changes/` folder - `shared/api/customers/changes/` - Customer-level changes - `shared/api/customers/cusProducts/changes/` - Product-level changes - `shared/api/customers/cusFeatures/changes/` - Feature-level changes ### Version Changes (Strongly Typed) - **V0_2_ProductItems**: Products gained items field (V0_2+ → V0_1) - **V1_2_FeaturesArrayToObject**: Features object ↔ array (V1_2 → V1_1) - **V1_1_MergedResponse**: Merged customer response ↔ split (V1_1+ → V1_0) - **V1_1_LegacyExpandInvoices**: Side-effect only (invoices auto-expand in V1_0) ### getApi* Pattern (server/src/internal/customers/cusUtils/apiCusUtils/) - **getApiCusProduct**: Builds product in latest format, applies V0_2_ProductItems transform - **getApiCusFeature**: Transforms balances (used→usage), applies V1_2_FeaturesArrayToObject - **getApiCustomer**: Orchestrates products/features, merges, applies V1_1_MergedResponse ### V2 Handler Demo - **handleGetCustomerV2**: Demonstrates zero version branching in handler - Calls `getApiCustomer` which handles all versioning internally - Side effects handled explicitly (expand invoices for V1_0) ## Type Safety - All version changes use Zod schemas for input/output types - Runtime validation with `parse()` ensures data integrity - Compile-time type checking catches transformation errors - Context data support via optional `data` parameter ## Benefits ✅ No scattered if-else version checks ✅ Composable resource-based architecture ✅ Strong typing with Zod schemas ✅ Self-documenting version changes ✅ Easy to add new versions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
53 lines
1.3 KiB
JavaScript
53 lines
1.3 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
import { getProcessOnPort, killProcess } from "./detect-ports.js";
|
||
|
||
const PORTS_TO_CHECK = [3000, 3001, 8080, 8081, 8082, 8083];
|
||
|
||
async function cleanupPorts() {
|
||
console.log("🧹 Cleaning up dev server ports...\n");
|
||
|
||
let killedCount = 0;
|
||
|
||
for (const port of PORTS_TO_CHECK) {
|
||
const processInfo = await getProcessOnPort(port);
|
||
|
||
if (processInfo) {
|
||
const { pid, processName } = processInfo;
|
||
|
||
// Check if it's a dev server process
|
||
const isDevProcess =
|
||
processName.includes("bun") ||
|
||
processName.includes("node") ||
|
||
processName.includes("vite");
|
||
|
||
if (isDevProcess) {
|
||
console.log(`⚠️ Port ${port}: ${processName} (PID: ${pid})`);
|
||
const killed = await killProcess(pid);
|
||
|
||
if (killed) {
|
||
console.log(` ✅ Killed process ${pid}\n`);
|
||
killedCount++;
|
||
} else {
|
||
console.log(` ❌ Failed to kill process ${pid}\n`);
|
||
}
|
||
} else {
|
||
console.log(
|
||
`ℹ️ Port ${port}: ${processName} (PID: ${pid}) - skipping (not a dev server)\n`,
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (killedCount === 0) {
|
||
console.log("✨ No dev server processes found on common ports");
|
||
} else {
|
||
console.log(`✅ Cleaned up ${killedCount} process(es)`);
|
||
}
|
||
}
|
||
|
||
cleanupPorts().catch((error) => {
|
||
console.error("Error cleaning up ports:", error);
|
||
process.exit(1);
|
||
});
|