Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .claude/rules/cross-sdk-parity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Cross-SDK Feature Parity

All SDKs must implement the same operations. When adding a feature to one SDK, implement it in all others (or open tracking issues).

## Required TurboSign Operations

| Operation | JS | Py | Go | PHP | Java |
|---|---|---|---|---|---|
| configure | `configure()` | `configure()` | `Configure()` | `configure()` | `configure()` |
| createSignatureReviewLink | `createSignatureReviewLink()` | `create_signature_review_link()` | `CreateSignatureReviewLink()` | `createSignatureReviewLink()` | `createSignatureReviewLink()` |
| sendSignature | `sendSignature()` | `send_signature()` | `SendSignature()` | `sendSignature()` | `sendSignature()` |
| getStatus | `getStatus()` | `get_status()` | `GetStatus()` | `getStatus()` | `getStatus()` |
| download | `download()` | `download()` | `Download()` | `download()` | `download()` |
| void | `void()` | `void_document()` | `VoidDocument()` | `voidDocument()` | `voidDocument()` |
| resend | `resend()` | `resend_email()` | `ResendEmail()` | `resend()` | `resendEmail()` |
| getAuditTrail | `getAuditTrail()` | `get_audit_trail()` | `GetAuditTrail()` | `getAuditTrail()` | `getAuditTrail()` |

## Required TurboPartner Operations

- Organization CRUD: create, list, getDetails, update, delete
- Organization entitlements: updateEntitlements
- Organization users: list, add, update role, remove, resend invitation
- Organization API keys: list, create, update, revoke
- Partner API keys: list, create, update, revoke
- Partner users: list, add, update permissions, remove, resend invitation
- Audit logs: list with filtering

## Naming Conventions by Language

| Language | Methods | Classes | Files | Constants |
|---|---|---|---|---|
| JS/TS | camelCase | PascalCase | kebab-case | UPPER_SNAKE |
| Python | snake_case | PascalCase | snake_case | UPPER_SNAKE |
| Go | PascalCase (exported) | PascalCase | snake_case | PascalCase |
| PHP | camelCase | PascalCase | PascalCase | UPPER_SNAKE |
| Java | camelCase | PascalCase | PascalCase | UPPER_SNAKE |
| Ruby | snake_case | PascalCase | snake_case | UPPER_SNAKE |

## New SDK Checklist

1. Create `packages/<lang>-sdk/` directory
2. Implement TurboSign with all operations above
3. Implement TurboPartner with all operations above
4. Implement error hierarchy (TurboDocxError + 5 subtypes)
5. Write tests matching parity of existing SDKs
6. Add CI job to `.github/workflows/ci.yml`
7. Add publish workflow `.github/workflows/publish-<lang>.yml`
8. Create README with install, configure, and usage examples
46 changes: 46 additions & 0 deletions .claude/rules/js-sdk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# JS/TS SDK Rules

## Zero Runtime Dependencies

The JS SDK has **no runtime dependencies** — only devDependencies (TypeScript, Jest, ts-jest, @types).
Use native `fetch` (Node 18+ global) for HTTP. Use native `fs` and `path` for file operations.
Never add runtime dependencies without explicit approval.

## Static Class Pattern

Both `TurboSign` and `TurboPartner` use static classes:
```typescript
TurboSign.configure({ apiKey, orgId, senderEmail });
const result = await TurboSign.sendSignature({ ... });
```
No `new TurboSign()` — all methods are static. The `HttpClient` instance is held as a private static field.

## TypeScript Configuration

- **Strict mode**: `strict: true` in tsconfig
- **Target**: ES2020
- **Module**: commonjs
- **Declaration files**: Generated in `dist/`
- All public APIs must have TypeScript types — no `any` in public interfaces

## senderEmail Validation

`senderEmail` is **required** for TurboSign (not for TurboPartner). The HttpClient throws `ValidationError` if missing unless `skipSenderValidation: true` (used internally by TurboPartner).

## File Type Detection

Use **magic bytes** to detect file types, not file extensions:
- PDF: bytes `0x25 0x50 0x44 0x46` (`%PDF`)
- DOCX: bytes `0x50 0x4B` (`PK`) + contains `word/`
- PPTX: bytes `0x50 0x4B` (`PK`) + contains `ppt/`

## Response Unwrapping

The `smartUnwrap` method strips the `{ data: ... }` wrapper when the response object has only a `data` key. This is automatic — module methods don't need to unwrap manually.

## Testing

- Framework: Jest + ts-jest
- Run: `npm test` (from `packages/js-sdk/`)
- Mock `global.fetch` — never make real HTTP calls in tests
- Test files live in `packages/js-sdk/tests/`
36 changes: 36 additions & 0 deletions .claude/rules/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Testing Rules

## TDD Workflow

1. Write tests first that describe expected behavior
2. Run tests to confirm they fail
3. Implement the feature to make tests pass
4. Refactor while keeping tests green

## Test Parity

All SDKs must have equivalent test coverage. When adding a test case in one SDK, add the same scenario in all others. Tests should cover:
- Successful operations (happy path)
- Error handling (auth errors, validation, not found, rate limit)
- Configuration (explicit config, env var fallback, missing required fields)
- File input variants (buffer, path, URL, deliverableId)
- Sender config behavior (senderEmail required, senderName optional)

## Per-SDK Test Commands

| SDK | Command | Framework |
|---|---|---|
| js-sdk | `npm test` | Jest + ts-jest |
| py-sdk | `pytest -v` | pytest |
| go-sdk | `go test -v ./...` | go test |
| php-sdk | `composer test` | PHPUnit |
| java-sdk | `mvn test -B` | JUnit (Maven Surefire) |
| ruby-sdk | `bundle exec rspec` | RSpec |

## Mock HTTP Responses, Not SDK Internals

- Mock the HTTP layer (fetch/requests/http.Client), not SDK module methods
- Return realistic response shapes matching the actual API
- Test error mapping: mock HTTP 401 → verify AuthenticationError is thrown
- Never make real HTTP calls in unit tests
- Keep mocks minimal — only mock what's needed for the specific test case
54 changes: 54 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# TurboDocx SDK Monorepo

Multi-language SDK monorepo for TurboDocx API (TurboSign digital signatures + TurboPartner portal management).

## Directory Structure

```
packages/
├── js-sdk/ # TypeScript/JavaScript (@turbodocx/sdk)
├── py-sdk/ # Python (turbodocx-sdk)
├── go-sdk/ # Go (github.com/TurboDocx/SDK/packages/go-sdk)
├── php-sdk/ # PHP (turbodocx/sdk)
├── java-sdk/ # Java (com.turbodocx:sdk)
└── ruby-sdk/ # Ruby (turbodocx-sdk)
.github/workflows/ # Per-SDK CI + publish workflows
```

## Build & Test Commands

| SDK | Install | Build | Test |
|-----|---------|-------|------|
| js-sdk | `npm ci` | `npm run build` | `npm test` |
| py-sdk | `pip install -e ".[dev]"` | — | `pytest -v` |
| go-sdk | `go mod tidy` | — | `go test -v ./...` |
| php-sdk | `composer install` | — | `composer test` |
| java-sdk | — | `mvn compile` | `mvn test -B` |
| ruby-sdk | `bundle install` | — | `bundle exec rspec` |

Root-level shortcuts (JS only): `npm run build:js`, `npm run test:js`

## Commit Format

```
feat|fix|docs|test|refactor: description
[js-sdk] Add batch signing # PR title format
```

## Cross-SDK Conventions

- All SDKs must maintain **feature parity** — see `.claude/rules/cross-sdk-parity.md`
- Two modules per SDK: **TurboSign** (signatures) and **TurboPartner** (partner portal)
- Follow each language's idiomatic naming (camelCase JS, snake_case Py, PascalCase Go)
- Shared error hierarchy: `TurboDocxError > Auth | Validation | NotFound | RateLimit | Network`
- Config pattern: explicit config → env var fallback → error

## Architecture

See @docs/ARCHITECTURE.md for HTTP client design, file input abstraction, error handling, and CI/CD details.

## Rules

- `.claude/rules/cross-sdk-parity.md` — feature parity requirements
- `.claude/rules/js-sdk.md` — JS/TS-specific conventions
- `.claude/rules/testing.md` — TDD workflow and test patterns
99 changes: 99 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# SDK Architecture

## Module Design

Each SDK exposes two top-level modules:

- **TurboSign** — digital signature operations (create review links, send signatures, void, resend, download, get status, audit trail)
- **TurboPartner** — partner portal management (organizations, users, API keys, entitlements, audit logs)

Modules use a **static class pattern**: configure once with `TurboSign.configure(config)`, then call static methods. No instantiation required.

## HTTP Client

Each SDK wraps a single `HttpClient` class responsible for:

1. **Authentication**: Bearer token via `apiKey` or `accessToken`, sent in `Authorization` header. Org ID sent as `x-rapiddocx-org-id` header.
2. **Base URL**: Configurable, defaults to `https://api.turbodocx.com`. Env var fallback: `TURBODOCX_BASE_URL`.
3. **Response unwrapping**: Backend wraps responses in `{ "data": ... }`. The client auto-unwraps when the response has only a `data` key (smart unwrap).
4. **Error mapping**: HTTP status codes map to typed errors (400→Validation, 401→Auth, 404→NotFound, 429→RateLimit, other→TurboDocxError).
5. **File upload**: Multipart form upload with magic-byte file type detection.

### Partner Client

TurboPartner uses a separate config (`PartnerClientConfig`) with `partnerApiKey` (TDXP- prefix) and `partnerId`. It skips sender email validation since it doesn't send signature emails.

## Configuration Pattern

All SDKs follow: **explicit config > env var fallback > error**.

| Config Field | Env Var | Required |
|---|---|---|
| `apiKey` | `TURBODOCX_API_KEY` | Yes (or accessToken) |
| `orgId` | `TURBODOCX_ORG_ID` | Yes for TurboSign |
| `senderEmail` | `TURBODOCX_SENDER_EMAIL` | Yes for TurboSign |
| `senderName` | `TURBODOCX_SENDER_NAME` | No (defaults to "API Service User") |
| `baseUrl` | `TURBODOCX_BASE_URL` | No (defaults to api.turbodocx.com) |

## Error Hierarchy

```
TurboDocxError (base)
├── AuthenticationError (401)
├── ValidationError (400)
├── NotFoundError (404)
├── RateLimitError (429)
└── NetworkError (no status — fetch/connection failure)
```

All errors carry `statusCode` and `code` properties. Each SDK implements this hierarchy using language-appropriate patterns (classes in JS/Py/Java/PHP, error types in Go, exceptions in Ruby).

## File Input Abstraction

TurboSign methods accept documents via multiple input types:

| Input | Description |
|---|---|
| `file` (Buffer/bytes) | Raw file content — type detected via magic bytes |
| `file` (path string) | Local file path — read and detect type |
| `file` (File object) | Browser File API object |
| `fileLink` (URL) | Remote file URL — server fetches it |
| `deliverableId` | TurboDocx-generated document ID |
| `templateId` | Pre-configured TurboSign template |

When a `file` is provided, the request uses multipart form upload. Otherwise, JSON body is used.

### Magic Byte Detection

File types are detected from content, not extensions:
- `%PDF` (0x25504446) → PDF
- `PK` (0x504B) + `word/` → DOCX
- `PK` (0x504B) + `ppt/` → PPTX

## Signature Field Positioning

Fields use **coordinate-based positioning**:
- `page` — page number (1-indexed)
- `x`, `y` — position from top-left in points
- `width`, `height` — field dimensions in points
- `recipientEmail` — binds field to a specific signer
- `type` — field type (`signature`, `initials`, `date`, `text`, etc.)

## Type/Model Organization

Each SDK organizes types by module:
- `types/sign` — TurboSign request/response types (recipients, fields, document status)
- `types/partner` — TurboPartner request/response types (organizations, users, API keys, audit logs)

## CI/CD

### Test Workflows (`.github/workflows/ci.yml`)
Runs on push to `main`/`develop` and all PRs. Per-SDK jobs with language-specific setup:
- JS: Node 22, `npm ci && npm run build && npm test`
- Python: 3.9, `pip install -e ".[dev]" && pytest -v`
- Go: 1.21, `go mod tidy && go test -v ./...`
- PHP: 8.1, `composer install && composer test && composer phpstan`
- Java: JDK 11 (Temurin), `mvn test -B`

### Publish Workflows
Separate workflow per SDK (`publish-{js,py,go,php,java}.yml`). Triggered on release or manual dispatch. Each publishes to the language's package registry.
Loading