Skip to content

Commit 39f9c40

Browse files
committed
feat(sdk): add TypeScript client for Node.js
Adds sdk/javascript — a zero-dependency TypeScript SDK mirroring the Python client's API: create, destroy, list, status, and withCopy. Uses the global fetch available in Node 18+ with an injectable implementation for testing. Ships a CI job and a tag-triggered npm publish workflow with provenance. Also modernises the Python pyproject.toml (correct build backend, SPDX licence string, project URLs) and adds sdk/python/README.md.
1 parent 137a1de commit 39f9c40

19 files changed

+788
-2
lines changed

.github/workflows/ci.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,37 @@ jobs:
3030
globs: |
3131
**/*.md
3232
33+
javascript-sdk:
34+
name: JavaScript SDK
35+
runs-on: ubuntu-latest
36+
defaults:
37+
run:
38+
working-directory: sdk/javascript
39+
steps:
40+
- uses: actions/checkout@v6
41+
- uses: actions/setup-node@v6
42+
with:
43+
node-version: "22"
44+
cache: npm
45+
cache-dependency-path: sdk/javascript/package-lock.json
46+
- run: npm ci
47+
- run: npm test
48+
- run: npm run build
49+
50+
python-sdk:
51+
name: Python SDK
52+
runs-on: ubuntu-latest
53+
defaults:
54+
run:
55+
working-directory: sdk/python
56+
steps:
57+
- uses: actions/checkout@v6
58+
- uses: actions/setup-python@v6
59+
with:
60+
python-version: "3.11"
61+
- run: python -m pip install --upgrade build
62+
- run: python -m build
63+
3364
test:
3465
name: Test
3566
runs-on: ubuntu-latest
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Release JavaScript SDK
2+
3+
on:
4+
push:
5+
tags:
6+
- "sdk-js-v*.*.*"
7+
8+
permissions:
9+
contents: read
10+
id-token: write
11+
12+
jobs:
13+
publish:
14+
name: Publish to npm
15+
runs-on: ubuntu-latest
16+
defaults:
17+
run:
18+
working-directory: sdk/javascript
19+
steps:
20+
- uses: actions/checkout@v6
21+
- uses: actions/setup-node@v6
22+
with:
23+
node-version: "22"
24+
cache: npm
25+
cache-dependency-path: sdk/javascript/package-lock.json
26+
- run: npm install -g npm@11
27+
- name: Verify tag matches package version
28+
run: |
29+
TAG_VERSION="${GITHUB_REF_NAME#sdk-js-v}"
30+
PACKAGE_VERSION="$(node -p "require('./package.json').version")"
31+
if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then
32+
echo "tag version $TAG_VERSION does not match package.json version $PACKAGE_VERSION" >&2
33+
exit 1
34+
fi
35+
- run: npm ci
36+
- run: npm test
37+
- run: npm run build
38+
- run: npm publish --provenance
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Release Python SDK
2+
3+
on:
4+
push:
5+
tags:
6+
- "sdk-python-v*.*.*"
7+
8+
permissions:
9+
contents: read
10+
id-token: write
11+
12+
jobs:
13+
publish:
14+
name: Publish to PyPI
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v6
18+
- uses: actions/setup-python@v6
19+
with:
20+
python-version: "3.11"
21+
- name: Verify tag matches package version
22+
run: |
23+
python - <<'PY'
24+
import os
25+
from pathlib import Path
26+
import tomllib
27+
28+
tag_version = os.environ["GITHUB_REF_NAME"].removeprefix("sdk-python-v")
29+
pyproject = tomllib.loads(Path("sdk/python/pyproject.toml").read_text())
30+
package_version = pyproject["project"]["version"]
31+
32+
if tag_version != package_version:
33+
raise SystemExit(
34+
f"tag version {tag_version} does not match pyproject version {package_version}"
35+
)
36+
PY
37+
- name: Build distributions
38+
working-directory: sdk/python
39+
run: |
40+
python -m pip install --upgrade build
41+
python -m build
42+
- uses: pypa/gh-action-pypi-publish@release/v1
43+
with:
44+
packages-dir: sdk/python/dist

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ __pycache__/
3333
dist/
3434
.venv/
3535

36+
# TypeScript / JavaScript
37+
node_modules/
38+
*.tsbuildinfo
39+
sdk/javascript/.tmp/
40+
3641
# OS
3742
.DS_Store
3843
Thumbs.db

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ aligned before you invest implementation time.
117117
| `internal/server/` | HTTP API server for remote copy operations |
118118
| `internal/store/` | SQLite metadata for copy state and lifecycle events |
119119
| `pkg/ditto/` | Go SDK for tests and programmatic copy lifecycle |
120+
| `sdk/javascript/` | TypeScript SDK for Node.js clients |
120121
| `sdk/python/` | Python SDK and pytest fixture |
121122
| `actions/` | Composite GitHub Actions bundled in the repository |
122123
| `docs/` | Diataxis documentation set |

docs/how-to/use-ditto-in-ci.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,31 @@ with client.with_copy() as dsn:
171171

172172
The Python SDK reads `DITTO_SERVER_URL`, `DITTO_TOKEN`, and `DITTO_TTL` from the environment when
173173
they are present.
174+
175+
## Use the JavaScript / TypeScript SDK
176+
177+
Install from the package directory during local development, or from npm once you have published the
178+
package for your environment:
179+
180+
```bash
181+
npm install ./sdk/javascript
182+
```
183+
184+
Example:
185+
186+
```ts
187+
import { DittoClient } from "@attaradev/ditto-sdk";
188+
189+
const client = new DittoClient({
190+
serverUrl: "http://ditto.internal:8080",
191+
token: process.env.DITTO_TOKEN,
192+
ttlSeconds: 600,
193+
});
194+
195+
await client.withCopy(async (dsn) => {
196+
await runMigrations(dsn);
197+
});
198+
```
199+
200+
The JavaScript SDK reads `DITTO_SERVER_URL`, `DITTO_TOKEN`, and `DITTO_TTL` from the environment
201+
when they are present.

sdk/javascript/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# ditto JavaScript SDK
2+
3+
TypeScript client for provisioning ephemeral database copies from a running ditto server.
4+
5+
## Install
6+
7+
```bash
8+
npm install @attaradev/ditto-sdk
9+
```
10+
11+
## Usage
12+
13+
```ts
14+
import { DittoClient } from "@attaradev/ditto-sdk";
15+
16+
const client = new DittoClient({
17+
serverUrl: "http://ditto.internal:8080",
18+
token: process.env.DITTO_TOKEN
19+
});
20+
21+
const copy = await client.create({ ttlSeconds: 600 });
22+
console.log(copy.connection_string);
23+
await client.destroy(copy.id);
24+
```
25+
26+
Use `withCopy` when you want automatic cleanup:
27+
28+
```ts
29+
import { DittoClient } from "@attaradev/ditto-sdk";
30+
31+
const client = new DittoClient({ serverUrl: "http://ditto.internal:8080" });
32+
33+
await client.withCopy(async (dsn) => {
34+
console.log(dsn);
35+
});
36+
```
37+
38+
## Environment variables
39+
40+
`DittoClient` reads these variables by default:
41+
42+
- `DITTO_SERVER_URL`
43+
- `DITTO_TOKEN`
44+
- `DITTO_TTL`

sdk/javascript/package-lock.json

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

sdk/javascript/package.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"name": "@attaradev/ditto-sdk",
3+
"version": "0.1.0",
4+
"description": "TypeScript client for the ditto ephemeral database copy service",
5+
"type": "module",
6+
"main": "./dist/index.js",
7+
"types": "./dist/index.d.ts",
8+
"exports": {
9+
".": {
10+
"types": "./dist/index.d.ts",
11+
"import": "./dist/index.js"
12+
}
13+
},
14+
"files": [
15+
"dist",
16+
"README.md"
17+
],
18+
"scripts": {
19+
"clean": "rm -rf dist .tmp",
20+
"build": "npm run clean && tsc -p tsconfig.build.json",
21+
"build:test": "rm -rf .tmp && tsc -p tsconfig.test.json",
22+
"test": "npm run build:test && node --test .tmp/test/client.test.js",
23+
"prepublishOnly": "npm run build"
24+
},
25+
"keywords": [
26+
"ditto",
27+
"database",
28+
"testing",
29+
"typescript",
30+
"sdk"
31+
],
32+
"license": "MIT",
33+
"engines": {
34+
"node": ">=18"
35+
},
36+
"publishConfig": {
37+
"access": "public",
38+
"registry": "https://registry.npmjs.org/"
39+
},
40+
"repository": {
41+
"type": "git",
42+
"url": "git+https://github.com/attaradev/ditto.git",
43+
"directory": "sdk/javascript"
44+
},
45+
"bugs": {
46+
"url": "https://github.com/attaradev/ditto/issues"
47+
},
48+
"homepage": "https://github.com/attaradev/ditto/tree/main/sdk/javascript",
49+
"devDependencies": {
50+
"@types/node": "^24.0.0",
51+
"typescript": "^5.0.0"
52+
}
53+
}

0 commit comments

Comments
 (0)