Skip to content

Commit 254e33b

Browse files
kvzclaude
andauthored
Incorporate CLI that supports all transloadify commands (#268)
* first stab at merging in transloadify * use cli subdir. more re-use of code * fix e2e * apply stricter biome & tsconfig rules * abort assemblies * Improve types, make replay clearer, roll test for it * stronger types * wip * fix version * format * run e2e for own PRs * dotenv * cli docs * add missing docs * merge CI * add node 24 * e2e only on 24 * test: use unique template names to avoid CI conflicts Use a unique testId (timestamp + random string) for all template names in e2e tests to prevent conflicts when tests run in parallel or when previous runs didn't clean up properly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: honor abort signal during awaitAssemblyCompletion polling Previously, the abort signal passed to createAssembly was only honored during the initial HTTP POST and TUS uploads. Now it's also honored during the polling loop in awaitAssemblyCompletion, allowing users to cancel long-running assembly operations at any point. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * add browser * fix bug * fix: address code review feedback - JobsPromise now throws if error handler not set before adding promises - Added clarifying comment explaining orphaned promise pattern in createAssembly - WatchJobEmitter now properly cleans up file watchers on SIGINT/SIGTERM and errors - Expanded --verbose/--quiet documentation with output level table 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: replace --verbose/--quiet with --log-level (-l) Use syslog-style severity levels inspired by @transloadit/sev-logger: - err (3): Error conditions - warn (4): Warning conditions - notice (5): Normal but significant (default) - info (6): Informational messages - debug (7): Debug-level messages Example: `npx transloadit assemblies list -l debug` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: allow numeric values for --log-level Now accepts both level names and numeric values: -l warn OR -l 4 -l debug OR -l 7 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: add trace log level (8) Most verbose level for detailed tracing, matching sev-logger. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * add onPoll * fix * format * fix: CLI exits with code 1 when jobs fail, fix AbortSignal listener leak - Add hasFailures tracking to JobsPromise so CLI can detect failures - CLI assemblies create now returns exit code 1 when any job fails - Fix memory leak in awaitAssemblyCompletion: remove abort listener when sleep timeout resolves normally - Update CHANGELOG with better onPoll description 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(cli): add --endpoint option and --single-assembly flag - Add --endpoint option to all commands for custom API endpoint (also reads from TRANSLOADIT_ENDPOINT env var) - Add --single-assembly flag to assemblies create command Passes all input files to a single assembly instead of creating one assembly per file. Cannot be used with --watch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: document --endpoint and --single-assembly CLI options 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: handle templates with no steps in sync download When downloading a template that has no steps defined, `result.content.steps` is undefined. JSON.stringify strips undefined values, so after writing and reading the file back, the `steps` property would be missing. Use nullish coalescing to default to empty object: `steps ?? {}` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: use tryCatch utility for cleaner error handling Replace try-catch blocks with tryCatch() in assemblies-create.ts for simple "stat file, use default on error" patterns. This reduces boilerplate and improves readability. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: remove non-functional notifications list, add download abort signal - Remove assembly-notifications list command (Transloadit API doesn't have a list notifications endpoint, only replay) - Remove the stub implementation and test - Update README to remove the non-existent list command - Add AbortSignal to https.get downloads so they can be cancelled when errors occur or the process is aborted 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: use got.stream + pipeline for downloads Replace http/https.get with got.stream() and Node's pipeline(). This is cleaner, more consistent with the rest of the SDK (which uses got), and reduces callback nesting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: add e2e test verifying download integrity via md5 hash - Add test that verifies downloaded file md5 matches the md5hash from the assembly result - Return assembly status from job promises so tests can access result metadata like md5hash 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: prevent file descriptor exhaustion with concurrency limiting Add --concurrency option and fix file handle leaks: 1. Default mode: Queue job metadata and process up to N at a time (default: 5). Creates fresh streams only when job slots available. 2. Single-assembly mode: Close streams immediately after collecting paths, create fresh streams only when uploading. Both fixes prevent EMFILE errors when processing many files. Tests verify: - "PROCESSING JOB" messages emitted with concurrency limiting - "STREAM CLOSED" messages emitted in single-assembly mode - Max concurrent jobs respects the concurrency limit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * perf: split assemblies e2e tests for parallel execution Split assemblies.test.ts into three files to leverage vitest's file-level parallelism: - assemblies.test.ts: get, delete, replay tests - assemblies-create.test.ts: create tests - assemblies-list.test.ts: list test (slowest at ~30s) This reduces e2e test wall time from ~73s to ~40s (~45% faster). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * upgrade yarn, add p-queue * upgrade yarn * use p-queue instead of JobPromise * natives * no esnureDir * unify cli files * format --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5307302 commit 254e33b

Some content is hidden

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

47 files changed

+5016
-579
lines changed

.gemini/settings.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"mcpServers": {
3+
"playwright": {
4+
"command": "npx",
5+
"args": ["-y", "@playwright/mcp@latest"]
6+
}
7+
}
8+
}

.github/workflows/ci.yml

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
name: CI
22

33
on:
4+
workflow_dispatch:
45
pull_request:
56
push:
67
branches:
78
- main
89
tags:
910
- '*'
11+
schedule:
12+
- cron: '0 8 * * *'
1013

1114
jobs:
1215
pack:
16+
name: Build package
1317
runs-on: ubuntu-latest
1418
steps:
1519
- uses: actions/checkout@v4
@@ -24,6 +28,7 @@ jobs:
2428
path: '*.tgz'
2529

2630
biome:
31+
name: Lint (Biome)
2732
runs-on: ubuntu-latest
2833
steps:
2934
- uses: actions/checkout@v4
@@ -34,6 +39,7 @@ jobs:
3439
- run: corepack yarn lint:js
3540

3641
typescript:
42+
name: Lint (TypeScript)
3743
runs-on: ubuntu-latest
3844
steps:
3945
- uses: actions/checkout@v4
@@ -43,13 +49,15 @@ jobs:
4349
- run: corepack yarn
4450
- run: corepack yarn lint:ts
4551

46-
vitest:
52+
unit:
53+
name: Unit tests (Node ${{ matrix.node }})
4754
runs-on: ubuntu-latest
4855
strategy:
4956
matrix:
5057
node:
5158
- 20
5259
- 22
60+
- 24
5361
steps:
5462
- uses: actions/checkout@v4
5563
- uses: actions/setup-node@v4
@@ -58,14 +66,75 @@ jobs:
5866
- run: corepack yarn
5967
- run: corepack yarn test:unit
6068
- name: Upload coverage reports artifact
61-
if: matrix.node == 22 # Only upload coverage from the latest Node.js version
69+
if: matrix.node == 24
6270
uses: actions/upload-artifact@v4
6371
with:
6472
name: coverage-reports
6573
path: coverage/
6674

75+
e2e:
76+
name: E2E tests
77+
# Run on push/schedule/dispatch, or on PRs only if from same repo (not forks)
78+
# This protects secrets from being exposed to fork PRs
79+
if: >
80+
github.event_name != 'pull_request' ||
81+
github.event.pull_request.head.repo.full_name == github.repository
82+
runs-on: ubuntu-latest
83+
steps:
84+
- uses: actions/checkout@v4
85+
- uses: actions/setup-node@v4
86+
with:
87+
node-version: 24
88+
- run: corepack yarn
89+
- name: Download cloudflared
90+
run: |
91+
curl -fsSLo cloudflared-linux-amd64 https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64
92+
chmod +x cloudflared-linux-amd64
93+
94+
# can be used for debugging:
95+
# - name: Setup tmate session
96+
# uses: mxschmitt/action-tmate@v3
97+
98+
- run: corepack yarn test
99+
env:
100+
TRANSLOADIT_KEY: ${{ secrets.TRANSLOADIT_KEY }}
101+
TRANSLOADIT_SECRET: ${{ secrets.TRANSLOADIT_SECRET }}
102+
NODE_OPTIONS: --trace-deprecation --trace-warnings
103+
CLOUDFLARED_PATH: ./cloudflared-linux-amd64
104+
DEBUG: 'transloadit:*'
105+
106+
- name: Generate the badge from the json-summary
107+
run: node --experimental-strip-types test/generate-coverage-badge.ts coverage/coverage-summary.json
108+
- name: Move HTML report and badge to the correct location
109+
run: |
110+
mv coverage/lcov-report static-build
111+
mv coverage-badge.svg static-build/
112+
# *** BEGIN PUBLISH STATIC SITE STEPS ***
113+
# Use the standard checkout action to check out the destination repo to a separate directory
114+
# See https://github.com/mifi/github-action-push-static
115+
- uses: actions/checkout@v4
116+
with:
117+
ssh-key: ${{ secrets.COVERAGE_REPO_SSH_PRIVATE_KEY }}
118+
repository: transloadit/node-sdk-coverage
119+
path: static-files-destination
120+
121+
# Push coverage data
122+
- run: |
123+
git config --global user.name github-actions
124+
git config --global user.email github-actions@github.com
125+
# Remove existing files:
126+
rm -rf static-files-destination/*
127+
# Replace with new files:
128+
cp -a static-build/* static-files-destination/
129+
cd static-files-destination
130+
git add .
131+
# git diff-index: to avoid doing the git commit failing if there are no changes to be commit
132+
git diff-index --quiet HEAD || git commit --message 'Static file updates'
133+
git push
134+
67135
coverage:
68-
needs: vitest
136+
name: Upload coverage
137+
needs: unit
69138
runs-on: ubuntu-latest
70139
if: github.ref == 'refs/heads/main'
71140
steps:
@@ -83,13 +152,26 @@ jobs:
83152
name: node-sdk
84153
fail_ci_if_error: true
85154

155+
slack-on-failure:
156+
name: Slack notification
157+
needs: [e2e]
158+
if: ${{ failure() && github.ref == 'refs/heads/main' }}
159+
runs-on: ubuntu-latest
160+
steps:
161+
- uses: 8398a7/action-slack@v3
162+
with:
163+
status: failure
164+
env:
165+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
166+
86167
release:
168+
name: Publish to npm
87169
runs-on: ubuntu-latest
88170
needs:
89171
- pack
90172
- biome
91173
- typescript
92-
- vitest
174+
- unit
93175
if: startsWith(github.ref, 'refs/tags/')
94176
permissions:
95177
id-token: write

.github/workflows/integration.yml

Lines changed: 0 additions & 88 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22

33
You may also want to refer to [GitHub releases](https://github.com/transloadit/node-sdk/releases).
44

5+
## Unreleased
6+
7+
- Add `signal` option to `createAssembly()` for cancelling in-flight HTTP requests and TUS uploads via `AbortController`
8+
- Add `signal` and `onPoll` options to `awaitAssemblyCompletion()` for cancellation and early termination (useful for custom progress reporting or superseding assemblies in watch mode)
9+
- Integrate transloadify CLI into the SDK, providing `assemblies`, `templates`, `bills`, and `assembly-notifications` commands
10+
- Add `--log-level (-l)` CLI option using syslog severity levels (err=3, warn=4, notice=5, info=6, debug=7, trace=8)
11+
- Add `--endpoint` CLI option for custom API endpoint (also reads `TRANSLOADIT_ENDPOINT` env var)
12+
- Add `--single-assembly` flag to `assemblies create` for passing multiple input files to a single assembly
13+
- Add `--concurrency` option to `assemblies create` to limit parallel processing (default: 5)
14+
- Fix file descriptor exhaustion by closing streams immediately and creating fresh ones on demand
15+
- Apply stricter biome lint rules (noExplicitAny, useAwait, noForEach, noNonNullAssertion)
16+
517
## v4.0.7
618

719
Released: 2025-11-26.

CONTRIBUTING.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ yarn test:unit
5050

5151
This will also generate a coverage report in the `coverage` directory.
5252

53-
### Integration tests
53+
### e2e tests
5454

55-
Integration tests are in the [`test/integration`](test/integration) folder. They require some extra setup.
55+
e2e tests are in the [`test/e2e`](test/e2e) folder. They require some extra setup.
5656

5757
Firstly, these tests require the Cloudflare executable. You can download this with:
5858

@@ -63,10 +63,10 @@ chmod +x cloudflared-linux-amd64
6363

6464
They also require a Transloadit key and secret, which you can get from https://transloadit.com/c/credentials.
6565

66-
You can run the integration tests with:
66+
You can run the e2e tests with:
6767

6868
```sh
69-
TRANSLOADIT_KEY='YOUR_TRANSLOADIT_KEY' TRANSLOADIT_SECRET='YOUR_TRANSLOADIT_SECRET' CLOUDFLARED_PATH='./cloudflared-linux-amd64' yarn test:integration
69+
TRANSLOADIT_KEY='YOUR_TRANSLOADIT_KEY' TRANSLOADIT_SECRET='YOUR_TRANSLOADIT_SECRET' CLOUDFLARED_PATH='./cloudflared-linux-amd64' yarn test:e2e
7070
```
7171

7272
### Code Coverage

MIGRATION.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ Version 4 focuses on type-safety, clearer errors, and modern Node support. Most
1919
```js
2020
// CommonJS import example
2121
async function getClient() {
22-
const { Transloadit } = await import("transloadit");
22+
const { Transloadit } = await import('transloadit')
2323
return new Transloadit({
24-
authKey: process.env.TRANSLOADIT_KEY ?? "",
25-
authSecret: process.env.TRANSLOADIT_SECRET ?? "",
26-
});
24+
authKey: process.env.TRANSLOADIT_KEY ?? '',
25+
authSecret: process.env.TRANSLOADIT_SECRET ?? '',
26+
})
2727
}
2828
```
2929
@@ -48,16 +48,16 @@ The package also exports `AssemblyInstructionsInput`, `AssemblyIndexItem`, `Asse
4848
const params: AssemblyInstructionsInput = {
4949
steps: {
5050
resize: {
51-
use: ":original",
52-
robot: "/image/resize",
51+
use: ':original',
52+
robot: '/image/resize',
5353
width: 320,
5454
height: 240,
5555
result: true,
5656
},
5757
},
58-
};
58+
}
5959

60-
await transloadit.createAssembly({ params, waitForCompletion: true });
60+
await transloadit.createAssembly({ params, waitForCompletion: true })
6161
```
6262
6363
## 3. Adjust API result handling
@@ -82,15 +82,15 @@ const createdAssembly = await transloadit.createAssembly(...);
8282
8383
```ts
8484
try {
85-
await transloadit.createAssembly({ params });
85+
await transloadit.createAssembly({ params })
8686
} catch (error) {
8787
if (error instanceof ApiError && error.response.assembly_id) {
8888
console.error(
89-
"Troubleshoot at https://transloadit.com/c/assemblies/" +
89+
'Troubleshoot at https://transloadit.com/c/assemblies/' +
9090
error.response.assembly_id
91-
);
91+
)
9292
}
93-
throw error;
93+
throw error
9494
}
9595
```
9696
@@ -103,18 +103,18 @@ try {
103103
authKey,
104104
authSecret,
105105
validateResponses: true,
106-
});
106+
})
107107
```
108108
109109
- `getSignedSmartCDNUrl` generates Smart CDN URLs with signatures that match the server-side implementation:
110110
111111
```ts
112112
const signedUrl = transloadit.getSignedSmartCDNUrl({
113-
workspace: "my-team",
114-
template: "hero-image",
115-
input: "landing.jpg",
116-
urlParams: { format: "webp" },
117-
});
113+
workspace: 'my-team',
114+
template: 'hero-image',
115+
input: 'landing.jpg',
116+
urlParams: { format: 'webp' },
117+
})
118118
```
119119
120120
## 6. Removed `createAssembly` callback support
@@ -135,7 +135,7 @@ As a consequence of upgrading `got` to v14, the `gotRetry` option no longer acce
135135
136136
## Testing & troubleshooting
137137
138-
- Run your existing integration tests on Node 20+. If you relied on CommonJS `require`, convert those modules or wrap calls in `import()` shims as shown above.
138+
- Run your existing e2e tests on Node 20+. If you relied on CommonJS `require`, convert those modules or wrap calls in `import()` shims as shown above.
139139
- If TypeScript raises errors about unfamiliar properties, import the respective types from `transloadit` instead of redefining them.
140140
- Schemas intentionally mirror the current public API. Some properties remain permissive while we tighten validation in the API itself; report gaps if the SDK raises or misses invalid data.
141141

0 commit comments

Comments
 (0)