Skip to content

Commit 8a94a6d

Browse files
committed
feat(sdk-ts): implement unified typescript sdk wrapper with examples and default export
1 parent e2dd9d0 commit 8a94a6d

File tree

108 files changed

+16726
-148
lines changed

Some content is hidden

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

108 files changed

+16726
-148
lines changed

core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"server": "tsx watch src/server/index.ts",
2626
"server:prod": "node dist/server/index.js",
2727
"generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=0.4.4,library=urllib3",
28-
"generate:sdk:all": "npm run generate:sdk:python"
28+
"generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript --additional-properties=npmName=pmxtjs,npmVersion=0.0.1,supportsES6=true,typescriptThreePlus=true",
29+
"generate:sdk:all": "npm run generate:sdk:python && npm run generate:sdk:typescript"
2930
},
3031
"keywords": [],
3132
"author": "",

package-lock.json

Lines changed: 8306 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdks/SDK_DEVELOPMENT.md

Lines changed: 87 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -27,158 +27,131 @@ PMXT uses a **"Sidecar" architecture** for multi-language support:
2727

2828
### Why This Approach?
2929

30-
1. **Single Source of Truth**: The Node.js implementation is the canonical version
30+
1. **Single Source of Truth**: The implementation in `core/` is the canonical version
3131
2. **Consistency**: All languages get identical behavior
3232
3. **Rapid Iteration**: Update the server, all SDKs update automatically
3333
4. **Quality**: We can write the core logic once, in TypeScript, with full testing
3434

35-
## Directory Structure
35+
## General Directory Structure
36+
37+
All SDKs follow this pattern:
3638

3739
```
3840
sdks/
39-
└── python/
41+
└── {language}/
4042
├── pmxt/ # Human-written wrapper (EDIT THIS)
41-
│ ├── __init__.py
42-
│ ├── client.py # Main Exchange classes
43-
│ └── models.py # Clean dataclasses
44-
├── generated/ # Auto-generated (DO NOT EDIT)
45-
│ └── pmxt_internal/ # Raw OpenAPI client
43+
│ └── ... # Clean, idiomatic code
44+
├── {generated}/ # Auto-generated client (DO NOT EDIT)
45+
│ └── ... # Raw OpenAPI client
4646
├── examples/
47-
│ └── basic_usage.py
48-
├── pyproject.toml
49-
└── README.md
47+
└── {package-manager-files}
5048
```
5149

5250
### The Golden Rule
5351

54-
**NEVER manually edit files in `generated/`**. They will be overwritten.
52+
**NEVER manually edit files in the generated directory**. They will be overwritten.
5553

56-
All human code goes in `pmxt/` (the wrapper).
54+
All human logic goes in the `pmxt/` directory (the wrapper).
5755

58-
## Generating SDKs
56+
## Supported Languages
5957

60-
### Python
58+
### 1. Python (`sdks/python`)
6159

62-
```bash
63-
# Generate the raw OpenAPI client
64-
npm run generate:sdk:python
60+
- **Reference**: [sdks/python/README.md](./python/README.md)
61+
- **Generator**: `python`
62+
- **Generated Dir**: `sdks/python/generated/`
63+
- **Wrapper Dir**: `sdks/python/pmxt/`
6564

66-
# Or manually:
67-
npx @openapitools/openapi-generator-cli generate \
68-
-i src/server/openapi.yaml \
69-
-g python \
70-
-o sdks/python/generated \
71-
--package-name pmxt_internal \
72-
--additional-properties=projectName=pmxt-internal,packageVersion=0.4.4,library=urllib3
73-
```
65+
### 2. TypeScript (`sdks/typescript`)
7466

75-
This creates the "ugly" auto-generated client in `sdks/python/generated/pmxt_internal/`.
67+
- **Reference**: [sdks/typescript/README.md](./typescript/README.md)
68+
- **Generator**: `typescript-fetch`
69+
- **Generated Dir**: `sdks/typescript/src/`
70+
- **Wrapper Dir**: `sdks/typescript/pmxt/`
7671

77-
The human wrapper in `sdks/python/pmxt/` imports this and provides a clean API.
72+
## Generating SDKs
7873

79-
### Future Languages
74+
To regenerate all SDKs after updating the server specification:
8075

81-
When adding Go, Java, etc.:
76+
```bash
77+
# In the root or core directory
78+
npm run generate:sdk:all
79+
```
80+
81+
To generate a specific language:
8282

8383
```bash
84-
# Go
85-
npm run generate:sdk:go
84+
# Python
85+
npm run generate:sdk:python
8686

87-
# Java
88-
npm run generate:sdk:java
87+
# TypeScript
88+
npm run generate:sdk:typescript
8989
```
9090

91-
Each will follow the same pattern:
92-
- `sdks/{language}/generated/` - Auto-generated client
93-
- `sdks/{language}/pmxt/` - Human wrapper
94-
9591
## The Human Wrapper Pattern
9692

97-
The wrapper does three things:
93+
The "wrapper" is a handwritten layer that sits on top of the auto-generated code. Its job is to make the API feel native and clean for that language.
9894

99-
### 1. Clean Imports
95+
### Responsibilities
10096

101-
```python
102-
# User writes:
103-
import pmxt
104-
poly = pmxt.Polymarket()
105-
106-
# Not:
107-
from pmxt_internal.api.default_api import DefaultApi
108-
from pmxt_internal.configuration import Configuration
109-
# ... etc
110-
```
97+
1. **Hide the Ugly**: Generated code is often verbose. The wrapper hides this.
98+
2. **Provide Idiomatic API**: Use language-specific features (e.g., Python properties, TypeScript interfaces).
99+
3. **Manage Server Lifecycle**: Include the `ServerManager` to auto-start the sidecar.
100+
4. **Simplify Models**: Convert complex generated schemas into simple data classes/interfaces.
111101

112-
### 2. Pythonic API
102+
### Example: Python vs TypeScript
113103

114-
```python
115-
# User writes:
116-
markets = poly.search_markets("Trump", MarketFilterParams(limit=10))
104+
**Python Wrapper (`sdks/python/pmxt/client.py`)**:
117105

118-
# Not:
119-
request = SearchMarketsRequest(args=["Trump", {"limit": 10}])
120-
response = api.search_markets(exchange="polymarket", search_markets_request=request)
121-
data = response.to_dict()["data"]
106+
```python
107+
class Exchange(ABC):
108+
def search_markets(self, query):
109+
# Calls self._api.search_markets()
110+
# Converts response to UnifiedMarket dataclass
111+
pass
122112
```
123113

124-
### 3. Clean Data Models
114+
**TypeScript Wrapper (`sdks/typescript/pmxt/client.ts`)**:
125115

126-
```python
127-
# User gets:
128-
@dataclass
129-
class UnifiedMarket:
130-
id: str
131-
title: str
132-
outcomes: List[MarketOutcome]
133-
# ...
134-
135-
# Not:
136-
class UnifiedMarket(BaseModel):
137-
def __init__(self, id=None, title=None, outcomes=None, ...):
138-
# 100 lines of generated code
116+
```typescript
117+
export abstract class Exchange {
118+
async searchMarkets(query: string): Promise<UnifiedMarket[]> {
119+
// Calls this.api.searchMarkets()
120+
// Converts response to UnifiedMarket interface
121+
return markets;
122+
}
123+
}
139124
```
140125

141-
## Maintaining the Wrapper
126+
## Maintaining the SDKs
142127

143128
When you add a new endpoint to the OpenAPI spec:
144129

145-
1. **Update `src/server/openapi.yaml`** with the new endpoint
146-
2. **Regenerate**: `npm run generate:sdk:python`
147-
3. **Update the wrapper**:
148-
- Add the method to `pmxt/client.py`
149-
- Add any new models to `pmxt/models.py`
150-
- Add a converter function if needed
130+
1. **Update `src/server/openapi.yaml`** with the new endpoint.
131+
2. **Regenerate** SDKs: `npm run generate:sdk:all`.
132+
3. **Update the wrappers**:
133+
* **Python**: Update `sdks/python/pmxt/client.py` & `models.py`.
134+
* **TypeScript**: Update `sdks/typescript/pmxt/client.ts` & `models.ts`.
151135

152-
Example:
136+
## Testing
153137

154-
```python
155-
# In pmxt/client.py
156-
def fetch_new_thing(self, thing_id: str) -> NewThing:
157-
"""Fetch a new thing."""
158-
try:
159-
request_body = {"args": [thing_id]}
160-
161-
response = self._api.fetch_new_thing(
162-
exchange=self.exchange_name,
163-
fetch_new_thing_request=request_body,
164-
)
165-
166-
data = self._handle_response(response.to_dict())
167-
return _convert_new_thing(data)
168-
except ApiException as e:
169-
raise Exception(f"Failed to fetch new thing: {e}")
170-
```
138+
Always test your changes in **both** languages.
171139

172-
## Testing
140+
### Python
173141

174142
```bash
175143
cd sdks/python
176-
177-
# Install in development mode
178144
pip install -e ".[dev]"
145+
python examples/*
146+
```
147+
148+
### TypeScript
179149

180-
# Run the example (requires server running)
181-
python examples/basic_usage.py
150+
```bash
151+
cd sdks/typescript
152+
npm install
153+
npm run build
154+
npx tsx examples/*
182155
```
183156

184157
## Publishing
@@ -187,61 +160,31 @@ python examples/basic_usage.py
187160

188161
```bash
189162
cd sdks/python
190-
191-
# Build
192163
python -m build
193-
194-
# Upload to PyPI
195164
python -m twine upload dist/*
196165
```
197166

198-
### Version Bumping
199-
200-
When releasing a new version:
201-
202-
1. Update `package.json` version
203-
2. Update `sdks/python/pyproject.toml` version
204-
3. Regenerate SDKs: `npm run generate:sdk:all`
205-
4. Commit and tag: `git tag v0.4.5`
206-
207-
## Common Issues
208-
209-
### "Module not found: pmxt_internal"
167+
### TypeScript (NPM)
210168

211-
The generated client isn't in the Python path. The wrapper adds it dynamically:
212-
213-
```python
214-
# In pmxt/client.py
215-
_GENERATED_PATH = os.path.join(os.path.dirname(__file__), "..", "generated")
216-
sys.path.insert(0, _GENERATED_PATH)
169+
```bash
170+
cd sdks/typescript
171+
npm publish --access public
217172
```
218173

219-
### "OpenAPI validation error"
220-
221-
Your `openapi.yaml` has a schema issue. Common problems:
222-
223-
- Empty arrays without `items: {}`
224-
- Missing required fields
225-
- Invalid enum values
226-
227-
Run the generator with `--skip-validate-spec` to see the raw error.
174+
## Version Bumping
228175

229-
### "Server not running"
230-
231-
The Python SDK requires the sidecar server:
232-
233-
```bash
234-
# Terminal 1: Start server
235-
npm run server
176+
When releasing a new version:
236177

237-
# Terminal 2: Run Python code
238-
python examples/basic_usage.py
239-
```
178+
1. Update `core/package.json` version.
179+
2. Update `sdks/python/pyproject.toml` version.
180+
3. Update `sdks/typescript/package.json` version.
181+
4. Regenerate SDKs: `npm run generate:sdk:all`.
182+
5. Commit and tag.
240183

241184
## Future: Native Bindings (v2.0.0)
242185

243186
Eventually, we'll move to native bindings (Rust + FFI) to eliminate the sidecar dependency. But for v1.0.0, the sidecar approach lets us move fast and support many languages with minimal effort.
244187

245188
## Questions?
246189

247-
See the main [ROADMAP.md](../../ROADMAP.md) for the overall project vision.
190+
See the main [ROADMAP.md](../ROADMAP.md) for the overall project vision.

sdks/typescript/.gitignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Dependencies
2+
node_modules/
3+
package-lock.json
4+
5+
# Build output
6+
dist/
7+
*.tsbuildinfo
8+
9+
# IDE
10+
.vscode/
11+
.idea/
12+
*.swp
13+
*.swo
14+
*~
15+
16+
# OS
17+
.DS_Store
18+
Thumbs.db
19+
20+
# Logs
21+
*.log
22+
npm-debug.log*
23+
24+
# Testing
25+
coverage/
26+
.nyc_output/
27+
28+
# Environment
29+
.env
30+
.env.local

sdks/typescript/.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
README.md
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# OpenAPI Generator Ignore
2+
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
3+
4+
# Use this file to prevent files from being overwritten by the generator.
5+
# The patterns follow closely to .gitignore or .dockerignore.
6+
7+
# As an example, the C# client generator defines ApiClient.cs.
8+
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
9+
#ApiClient.cs
10+
11+
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
12+
#foo/*/qux
13+
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
14+
15+
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
16+
#foo/**/qux
17+
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
18+
19+
# You can also negate patterns with an exclamation (!).
20+
# For example, you can ignore all files in a docs folder with the file extension .md:
21+
#docs/*.md
22+
# Then explicitly reverse the ignore rule for a single file:
23+
#!docs/README.md

0 commit comments

Comments
 (0)