Claude-code-plugins-plus flexport-local-dev-loop
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/flexport-pack/skills/flexport-local-dev-loop" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-flexport-local-dev-loop && rm -rf "$T"
manifest:
plugins/saas-packs/flexport-pack/skills/flexport-local-dev-loop/SKILL.mdsource content
Flexport Local Dev Loop
Overview
Set up a fast, reproducible local development workflow for Flexport integrations. Since Flexport has no official SDK, the dev loop centers on a typed HTTP client wrapper with mock responses for testing.
Instructions
Step 1: Project Structure
flexport-integration/ ├── src/ │ ├── flexport/ │ │ ├── client.ts # Typed Flexport API client │ │ ├── types.ts # API response types │ │ └── mock-data.ts # Test fixtures │ └── index.ts ├── tests/ │ ├── flexport.test.ts # Unit tests with mocks │ └── integration.test.ts # Live API tests (CI only) ├── .env.local # Local secrets (git-ignored) ├── .env.example └── package.json
Step 2: Typed Client Wrapper
// src/flexport/types.ts interface FlexportShipment { id: string; status: 'booked' | 'in_transit' | 'arrived' | 'delivered'; freight_type: 'ocean' | 'air' | 'trucking'; origin_port: { code: string; name: string }; destination_port: { code: string; name: string }; cargo_ready_date: string; estimated_arrival_date: string; } interface FlexportResponse<T> { data: { records: T[]; total_count: number }; } // src/flexport/client.ts export class FlexportClient { private base = 'https://api.flexport.com'; private headers: Record<string, string>; constructor(apiKey: string) { this.headers = { 'Authorization': `Bearer ${apiKey}`, 'Flexport-Version': '2', 'Content-Type': 'application/json', }; } async listShipments(page = 1, per = 25): Promise<FlexportResponse<FlexportShipment>> { const res = await fetch(`${this.base}/shipments?page=${page}&per=${per}`, { headers: this.headers, }); if (!res.ok) throw new Error(`Flexport ${res.status}: ${await res.text()}`); return res.json(); } }
Step 3: Mock Data for Testing
// src/flexport/mock-data.ts export const mockShipment: FlexportShipment = { id: 'shp_test_001', status: 'in_transit', freight_type: 'ocean', origin_port: { code: 'CNSHA', name: 'Shanghai' }, destination_port: { code: 'USLAX', name: 'Los Angeles' }, cargo_ready_date: '2025-04-01', estimated_arrival_date: '2025-05-15', }; export function mockFlexportFetch(path: string) { if (path.includes('/shipments')) { return { data: { records: [mockShipment], total_count: 1 } }; } throw new Error(`No mock for ${path}`); }
Step 4: Vitest Unit Tests
// tests/flexport.test.ts import { describe, it, expect, vi, beforeEach } from 'vitest'; import { FlexportClient } from '../src/flexport/client'; import { mockShipment } from '../src/flexport/mock-data'; describe('FlexportClient', () => { beforeEach(() => { vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve({ data: { records: [mockShipment], total_count: 1 } }), })); }); it('lists shipments', async () => { const client = new FlexportClient('test-key'); const result = await client.listShipments(); expect(result.data.records).toHaveLength(1); expect(result.data.records[0].freight_type).toBe('ocean'); }); it('sends correct auth header', async () => { const client = new FlexportClient('test-key'); await client.listShipments(); expect(fetch).toHaveBeenCalledWith( expect.stringContaining('/shipments'), expect.objectContaining({ headers: expect.objectContaining({ 'Flexport-Version': '2' }) }), ); }); });
Step 5: Dev Scripts
{ "scripts": { "dev": "tsx watch src/index.ts", "test": "vitest", "test:watch": "vitest --watch", "test:integration": "FLEXPORT_LIVE=1 vitest tests/integration.test.ts" } }
Resources
Next Steps
See
flexport-sdk-patterns for production-ready code patterns.