Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
12 changes: 12 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [],
"privatePackages": { "version": true, "tag": false }
}
5 changes: 5 additions & 0 deletions .changeset/revive-bubble.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"transloadit-bubble-plugin": major
---

Replace deprecated Robodog with current Uppy, add a signed uploader element with Bubble states and events, and prepare tested exports for manual Bubble publication. Existing VideoWrapper and open action IDs are retained.
18 changes: 18 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
cooldown:
default-days: 7
groups:
uppy:
patterns: ['@uppy/*']
dev-dependencies:
dependency-type: development
update-types: [minor, patch]
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
38 changes: 38 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CI
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
strategy:
matrix:
node: ['22.23.4', '26.12.1']
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
- run: npm install --global corepack@0.36.0 && corepack enable
- run: corepack yarn install --immutable
- run: corepack yarn check
- run: corepack yarn playwright install chromium
- run: corepack yarn test:browser
e2e:
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '26.12.1'
- run: npm install --global corepack@0.36.0 && corepack enable
- run: corepack yarn install --immutable
- run: corepack yarn playwright install chromium
- run: corepack yarn test:e2e
env:
TRANSLOADIT_KEY: ${{ secrets.BUBBLE_E2E_TRANSLOADIT_KEY }}
TRANSLOADIT_SECRET: ${{ secrets.BUBBLE_E2E_TRANSLOADIT_SECRET }}
54 changes: 54 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Release candidate
on:
push:
branches: [main]
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
version:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
outputs:
hasChangesets: ${{ steps.changesets.outputs.hasChangesets }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
# Changesets uses its own API token; installs do not need a write credential.
persist-credentials: false
- uses: actions/setup-node@v6
with:
node-version: '26.12.1'
- run: npm install --global corepack@0.36.0 && corepack enable
- run: corepack yarn install --immutable
- id: changesets
uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d
with:
version: corepack yarn changeset:version:release
commitMode: github-api
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
candidate:
needs: version
if: needs.version.outputs.hasChangesets == 'false'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: actions/setup-node@v6
with:
node-version: '26.12.1'
- run: npm install --global corepack@0.36.0 && corepack enable
- run: corepack yarn install --immutable
# Changesets writes ~/.netrc. This separate runner keeps its token away from candidate checks.
- run: corepack yarn release:prepare
- uses: actions/upload-artifact@v7
with:
name: bubble-candidate-${{ github.sha }}
path: build/bubble-plugin-*.zip*
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
node_modules/
.yarn/
.env
.env.*
coverage/
build/
test-results/
playwright-report/
*.log
2 changes: 2 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
nodeLinker: node-modules
npmMinimalAgeGate: 2880
102 changes: 102 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Contributing

## Layout and checks

Bubble remains the primary plugin store. Keep `actions/`, `elements/`, `api/`,
`html_headers.html`, `meta_data.json` and `shared_tech_params.json` in their export layout;
never rename existing opaque IDs. New element `AAu` coexists with legacy `AAp`.

Handwritten code lives in TypeScript: `src/`, `scripts/`, `test/` and the root test configs.
Node runs scripts with native type stripping; `tsc --noEmit` checks them all. A separate browser
config keeps Node globals out of `src/` and the browser harness. Bubble callbacks
remain generated JavaScript for editor sync; the legacy `AAp` export stays unchanged.

Edit TypeScript in `src/`, then run `corepack yarn build` and commit the generated callbacks and
header. The header loads the pinned official Uppy 6.0.5 ESM/CSS CDN bundle, whose core 6.3.0,
Dashboard 6.0.2 and Transloadit 6.0.1 match the typed npm dependencies. Upgrade these together;
Dependabot cannot choose the corresponding CDN release for you. Only our glue is minified into
the header; no vendored Uppy bundle needs reviewing. Apps must allow `releases.transloadit.com`
and Transloadit's API/upload/result hosts through their CSP/network policy.

`corepack yarn check` runs Biome, TypeScript, Vitest with enforced coverage, and deterministic
build verification. `corepack yarn fix` formats source. `corepack yarn test:browser` exercises the
actual generated header and callbacks in Chromium, through Bubble-shaped canvas/state/event APIs.
Install Chromium once with `corepack yarn playwright install chromium`. Unit tests cover lifecycle,
signature byte preservation, progress/results, failures, reset, multiple updates and legacy uploads.

## Real API tests

Use the opentest key from node-sdk as process environment only, without editing or copying `.env`:

```sh
corepack yarn build
BUBBLE_REAL_API=1 node --env-file="$HOME/code/node-sdk/.env" node_modules/@playwright/test/cli.js test
```

This mounts the built element, selects a generated non-customer PNG, performs a signed tus upload,
waits for a real `/image/resize` result, and downloads it. It creates a billable test Assembly but
no persistent Template. The local signer keeps the Auth Secret in Node; only signed params reach
the browser. Traces and request payload recording are disabled to keep credentials out of artifacts.

CI requires `BUBBLE_E2E_TRANSLOADIT_KEY` and `BUBBLE_E2E_TRANSLOADIT_SECRET` as Actions secrets
(and Dependabot secrets for dependency PRs). The test receives them as `TRANSLOADIT_KEY` and
`TRANSLOADIT_SECRET`. Internal PRs fail clearly when missing; fork PRs run unit/browser checks only.

## Versioning and release candidates

Add a user-facing Changeset with `corepack yarn changeset`. On main, Changesets opens a version PR
that updates `package.json` and `CHANGELOG.md`. Before merging, close and reopen it as a maintainer
if CI is absent: GitHub does not trigger PR workflows for `GITHUB_TOKEN` writes. Wait for its checks
to pass. Merge the version PR, then download that commit's
`bubble-candidate-<sha>` artifact from **Release candidate** Actions. The archive and SHA-256 file
contain only Bubble's export. Locally, `corepack yarn release:prepare` produces the same handoff.
Candidates are built on main only when no Changesets remain pending; use the version PR's commit for publication.
Changesets versions are repository candidates; record Bubble's separately assigned published
version in the release notes. There is deliberately no automatic Bubble deploy or user migration.

## Bubble handoff (Kevin)

1. In a clean checkout of the reviewed/merged commit, run `corepack yarn install --immutable`,
`corepack yarn check`, then `corepack yarn release:prepare`. Verify the archive SHA-256 before
handing it off. Keep the prior published version available for rollback.
2. Log in to [the existing plugin editor](https://bubble.io/plugin_editor?id=1550344595465x140038650298630140).
Under **Version → GitHub**, synchronize the existing repository after the PR is merged. Bubble
pulls the export; resolve any editor/GitHub conflicts before continuing. Do not create a new plugin.
If synchronization is unavailable, the archive maps directly to tabs: Shared HTML header and
shared keys, General metadata, Elements (`AAp` and `AAu`) and Actions (`AAF`). Paste complete
function expressions from the matching generated `.js` files; declare fields/states/events
exactly as in their `params.json`. Do not replace the old element or action IDs.
3. Check **Shared → HTML header** uses the current `html_headers.html`, and confirm the new
**Transloadit Uploader** fields/states/events appear. Keep jQuery disabled; the uploader supports
both DOM and Bubble's jQuery-style canvas wrapper without depending on jQuery.
4. In the plugin editor's test-app setting, choose a disposable Bubble app, then **Go to test app**
to install/refresh this development plugin. Add the new element and an authenticated backend
signer; bind its returned params/signature strings. Never expose the Auth Secret to the page.
5. In editor and run mode, test Template ID and Instructions; inline Dashboard and button; valid
upload to completed result URLs; expired signature; type/size restriction; cancel/reset; a second
upload; two instances; and page navigation away during upload. Verify existing VideoWrapper and
open-action workflows. Repeat at desktop and 390px; check browser errors/network and CORS on any
legacy notification URL. Confirm both result URLs and Bubble workflow events, not screenshots alone.
6. Only after that pass, deploy from **Version**, selecting the appropriate change type and copying
the Changeset release notes. Verify the marketplace listing opens while signed out, install the
published version in the test app, and repeat an upload. Update README with its verified URL and
record the published Bubble version, repository commit and test evidence on #1. Then close #1.
If anything fails, keep the current published version and fix the candidate; if already deployed,
restore its saved export and publish a corrective version from the editor.

[Bubble's GitHub integration](https://manual.bubble.io/account-and-marketplace/building-plugins/github-integration)
explains synchronization; [publishing](https://manual.bubble.io/account-and-marketplace/building-plugins/publishing-and-versioning)
is a separate editor action. These login-dependent steps remain unexecuted by CI.

## Maintenance decisions

Tooling follows transloadit/zapier's commits from 2026-10-14 through 2026-10-21: Yarn 4, Biome,
TypeScript, Vitest, tests of the actual built artifact, Changesets, PR CI and weekly grouped updates.
We preserve legacy IDs like Zapier preserves saved Zap contracts. Bubble has no Zapier-style CLI
upload/promote/migrate, so releases produce an export and explicit human handoff. The official CDN
keeps the export small; a future custom bundle can reduce unused Uppy plugins if download size matters.
Uppy's `errorReporting: false` only disables request-level reports in the pinned version. Its
plugin error handler still sends messages, browser user agent and client identification to
`transloaditstatus.com/client_error`, including validation failures; this is not a telemetry opt-out.
Uploads retain the original one-file limit. Multi-file batches need separate result aggregation and
partial-retry handling; exposing a larger limit before those exist can silently lose processed results.
72 changes: 70 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,71 @@
This is a repository based on the [Bubble](https://bubble.is) plugin 'uppy-transloadit'. This lets you use Github's functionality in terms of version control, forks and pull requests. Note that the the plugin code is stored on Bubble's servers, and you need to synchronize your repository in the Bubble Plugin Editor.
# Transloadit for Bubble

This plugin can be used in [Bubble applications](https://bubble.is).
Resumable file uploads and media processing using [Uppy](https://uppy.io) and
[Transloadit](https://transloadit.com). Add an inline Dashboard or an upload button,
then connect processed file URLs to Bubble workflows.

## Use in a Bubble app

The modernization is a release candidate: installation in a real Bubble app and marketplace
publication still need the maintainer's Bubble login. See [the handoff](CONTRIBUTING.md#bubble-handoff-kevin).
The existing plugin is `1550344595465x140038650298630140`; its marketplace URL must be
verified after publication before linking builders to it.

1. Add **Transloadit Uploader** to a page (at least 350px high for an inline Dashboard).
2. Have your backend return **params** (a JSON string) and **signature** (SHA-384).
Bind those strings to **Signed Assembly params** and **Signature**. Refresh them before each
upload; signatures expire. Keep the exact params string, including spaces and key order.
3. Set **Inline Dashboard** to yes, or no for an upload button. Set allowed types and maximum bytes
per file (default 5 MiB). Each upload accepts one local file, matching the original plugin.
4. On **Encoding completed**, read **Result URLs** (a list of texts), **Results JSON**, or
**Assembly JSON**. Other states: **Assembly ID**, **Is uploading**, **Upload progress** (0–100,
transfer progress; processing may continue), and **Error message**. **Upload started** and
**Upload error** events allow loading and failure workflows. Use Done or Bubble's reset before
the next batch; one batch owns the result states at a time.

The `params` string supports a Template ID:

```json
{"auth":{"key":"PUBLIC_AUTH_KEY","expires":"2026-10-30T12:00:00Z"},"template_id":"TEMPLATE_ID"}
```

Or replace `template_id` with `steps` containing your Assembly Instructions. Generate it and
its signature on a trusted server, for example with Node:

```js
import { createHmac } from 'node:crypto'
const params = JSON.stringify({
auth: { key: process.env.TRANSLOADIT_KEY, expires: new Date(Date.now() + 300_000).toISOString() },
template_id: 'YOUR_TEMPLATE_ID',
})
const signature = `sha384:${createHmac('sha384', process.env.TRANSLOADIT_SECRET).update(params).digest('hex')}`
// Return { params, signature } through your authenticated backend endpoint.
```

Never put an Auth Secret in Bubble client properties, shared client-safe keys, HTML, or browser code.
Your backend must authorize the request and choose its allowed Template/Instructions rather than
sign arbitrary user-submitted params. See [signature authentication](https://transloadit.com/docs/api/signature-authentication/).

## Existing apps

`VideoWrapper` and its `src` field keep their IDs and behavior. The global **open** action now opens
current Uppy; its existing Auth Key, Template ID, maximum file size, allowed types and notification
URL settings still work. Its optional params/signature inputs enable signed uploads. Legacy
unsigned uploads need a key that permits them; migrate to signed uploads. Notifications still POST
an array of completed Assemblies from the browser (the receiver needs CORS); server notifications
can instead use `notify_url` inside signed params. Old remote-source settings are retained but
unused; this version offers local file selection without Companion/OAuth configuration.

## Develop

Use Node 22.23.4+ (CI also tests 26.12.1) and Yarn 4.12 via Corepack:

```sh
corepack yarn install
corepack yarn build
corepack yarn check
corepack yarn test:browser
```

Read [CONTRIBUTING.md](CONTRIBUTING.md) for the source/build layout, real-API tests,
maintenance decisions and release process. Git commits and CI artifacts do not publish to Bubble.
37 changes: 4 additions & 33 deletions actions/AAF/client.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,5 @@
function(properties, context) {
//Load any data

let restrictions = {
maxNumberOfFiles: 1,
maxFileSize: parseInt(context.keys['MAX FILE SIZE']) || 5242880
}
if(context.keys['ALLOWED FILE TYPES']){
restrictions.allowedFileTypes = context.keys['ALLOWED FILE TYPES'].split(" ")
}

Robodog.pick({
target: 'body',
restrictions: restrictions,
params: {
auth: { key: context.keys['AUTH KEY'] },
template_id: context.keys['TEMPLATE ID']
},
waitForEncoding: true,
providers: context.keys['ALLOWED SOURCES'] ? context.keys['ALLOWED SOURCES'].split(" ") : ['url', 'webcam', 'dropbox', 'google-drive', 'instagram']
})
.then((bundle) => {
fetch(context.keys['NOTIFY URL'], {
method: 'post',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify(bundle.transloadit)
}).then(res => res.json())
.then(res => console.log(res));

})
}
// Generated by yarn build.
window.TransloaditBubbleReady.then(function(api) { api.open(properties, context); })
.catch(function(error) { context.reportDebugger(error.message); });
}
26 changes: 22 additions & 4 deletions actions/AAF/params.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
{
"category": "plugins",
"display": "open",
"type": "client_side"
}
"category": "plugins",
"display": "open",
"type": "client_side",
"fields": {
"ABM": {
"name": "params",
"caption": "Signed Assembly params (JSON)",
"editor": "DynamicValue",
"value": "text",
"optional": true,
"rank": 0
},
"ABN": {
"name": "signature",
"caption": "Signature (server-generated)",
"editor": "DynamicValue",
"value": "text",
"optional": true,
"rank": 1
}
}
}
Loading
Loading