From 2359b874cb5002b50991fe06c5d51c44927e1c9f Mon Sep 17 00:00:00 2001 From: Alaeddin <15094821+BSalaeddin@users.noreply.github.com> Date: Sat, 12 Sep 2026 04:07:41 +0100 Subject: [PATCH] =?UTF-8?q?docs(landing):=20claim-integrity=20pass=20?= =?UTF-8?q?=E2=80=94=20truthful=20wording=20for=20parity,=20providers,=20N?= =?UTF-8?q?ode=20version,=20privacy=20and=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 22 +++-- .../docs/comparisons/upup-vs-filepond.mdx | 18 ++-- .../docs/comparisons/upup-vs-uploadthing.mdx | 20 ++-- .../content/docs/guides/reliability.mdx | 2 +- .../content/docs/guides/server-auth.mdx | 2 +- .../src/__tests__/claim-integrity.test.ts | 95 +++++++++++++++++++ apps/landing/src/app/privacy/page.tsx | 7 +- .../src/components/FeatureShowcase/index.tsx | 2 +- .../src/components/HomepageFeatures/index.tsx | 2 +- apps/landing/src/lib/faqs.ts | 2 +- 10 files changed, 135 insertions(+), 37 deletions(-) create mode 100644 apps/landing/src/__tests__/claim-integrity.test.ts diff --git a/README.md b/README.md index 41ce0e4d5..eec469e23 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,11 @@

One file uploader. Every framework.

- A headless upload engine with native, byte-identical UI packages for + A headless upload engine with native, DOM-identical UI packages for React, Vue, Svelte, Angular, Vanilla JS, and Preact — with optional server-mode uploads, cloud drives, camera, screen capture, and link imports.

-

CI License: MIT @@ -31,15 +30,15 @@ **upup** is a free, MIT-licensed file uploader built as one headless [`@useupup/core`](packages/core) engine with a matching native UI for every major framework. React is the visual canon; the Vue, Svelte, Angular, Vanilla, and -Preact ports render the **same DOM** with the **same props**, verified -byte-for-byte by a cross-framework parity harness. Upload straight from the -browser to any S3-compatible storage (Client Mode), or route through your own -backend with an HMAC-signed trust model (Server Mode via -[`@useupup/server`](packages/server)). +Preact ports render the **same DOM** with the **same props**, verified by a +cross-framework parity harness (structure, classes, accessibility attributes, +text). Upload straight from the browser to any S3-compatible storage (Client +Mode), or route through your own backend with an HMAC-signed trust model +(Server Mode via [`@useupup/server`](packages/server)). ## Install -Pick the package for your framework — the component API and rendered DOM are identical across all of them: +Pick the package for your framework — the component API and the rendered DOM contract are identical across all of them: | Package | Install | Get started | | ------------------ | ------------------------ | ------------------------------------------------------------------ | @@ -113,7 +112,7 @@ Express, Fastify, and Hono handlers ship as subpath exports ## Features - **Headless core.** `@useupup/core` is a zero-framework-dependency engine: file state, an upload pipeline (compression, HEIC→JPEG, EXIF stripping, checksums, thumbnails, optional Web Worker offload), cloud-drive plugins, i18n, and theming. Build your own UI on it, or use a native package. -- **Native UI for six frameworks.** React, Vue, Svelte, Angular, Vanilla JS, and Preact — same DOM, same Tailwind classes, enforced byte-for-byte by a parity harness. +- **Native UI for six frameworks.** React, Vue, Svelte, Angular, Vanilla JS, and Preact — same DOM structure, same `upup-` classes, enforced by a cross-framework parity harness. - **Client or Server mode.** Direct browser → storage presigned uploads, or a server-proxied [`@useupup/server`](packages/server) with an HMAC-signed trust model (signed length, key/uploadId binding, mandatory secrets). - **S3-compatible storage.** AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces, Backblaze B2, Wasabi — any S3-compatible endpoint. - **Cloud drives.** Import from Google Drive, OneDrive, Dropbox, and Box, in client or server mode. @@ -123,7 +122,10 @@ Express, Fastify, and Hono handlers ship as subpath exports - **i18n & theming.** ICU-based localization with 9 bundled locales and RTL support, plus a slot-level theming system that targets every rendered element. - **TypeScript-first.** Full type definitions out of the box. -## Battle-tested in production +## Used in production + +Both apps below run the upup v3.1 engine under its previous `@upupjs/*` scope — +the same codebase, published before the rename to `@useupup/*`. - **uNotes** — AI doc uploads for past exams → [unotes.net](https://unotes.net) - **Shorty** — media uploads for transcripts → [aishorty.com](https://aishorty.com) diff --git a/apps/landing/content/docs/comparisons/upup-vs-filepond.mdx b/apps/landing/content/docs/comparisons/upup-vs-filepond.mdx index ae0586434..94f486766 100644 --- a/apps/landing/content/docs/comparisons/upup-vs-filepond.mdx +++ b/apps/landing/content/docs/comparisons/upup-vs-filepond.mdx @@ -14,17 +14,17 @@ capture, and an S3-compatible server with an HMAC-signed trust model. ## At a glance -| Feature | FilePond | upup | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | -| Native first-party UI | Vanilla JS core + adapters: React, Vue, Angular, Svelte, jQuery | React, Vue, Svelte, Angular, Vanilla JS, Preact | +| Feature | FilePond | upup | +| ----------------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | +| Native first-party UI | Vanilla JS core + adapters: React, Vue, Angular, Svelte, jQuery | React, Vue, Svelte, Angular, Vanilla JS, Preact | | Headless core | No — renders its own UI component | Yes (`@useupup/core`) | -| License / pricing | MIT, free & open source (the Pintura image editor is a separate commercial product) | MIT, free & open source | +| License / pricing | MIT, free & open source (the Pintura image editor is a separate commercial product) | MIT, free & open source | | Self-host incl. S3-compatible | Yes — uploads to your own server endpoint (process / revert / restore), with chunk uploads; no built-in S3 signing | Yes — `@useupup/server` presigns and proxies to any S3-compatible storage | -| Cloud-drive sources | No (local files, directories, blobs, local/remote URLs, Data URIs, paste) | Google Drive, OneDrive, Dropbox, Box | -| Camera / screen capture | No | Yes (both) | -| Image editor | Via plugins (crop / resize / transform); a full editor is Pintura (separate/commercial) | Yes — React/Preact only | -| Resumable uploads | Chunked uploads (server-driven) | Yes — optional (tus or S3 multipart) | -| i18n | Configurable label strings (no locale bundles) | Yes (ICU locale bundles) | +| Cloud-drive sources | No (local files, directories, blobs, local/remote URLs, Data URIs, paste) | Google Drive, OneDrive, Dropbox, Box | +| Camera / screen capture | No | Yes (both) | +| Image editor | Via plugins (crop / resize / transform); a full editor is Pintura (separate/commercial) | Yes — React/Preact only | +| Resumable uploads | Chunked uploads (server-driven) | Yes — optional (tus or S3 multipart) | +| i18n | 48 locale files in `locale/`, plus configurable label strings | Yes (ICU locale bundles) | ## Choose FilePond if diff --git a/apps/landing/content/docs/comparisons/upup-vs-uploadthing.mdx b/apps/landing/content/docs/comparisons/upup-vs-uploadthing.mdx index cdcdab006..0f9077dde 100644 --- a/apps/landing/content/docs/comparisons/upup-vs-uploadthing.mdx +++ b/apps/landing/content/docs/comparisons/upup-vs-uploadthing.mdx @@ -15,18 +15,18 @@ screen capture, image editing, and native UI for six frameworks. ## At a glance -| Feature | UploadThing | upup | -| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | -| Model | Hosted SaaS (managed storage + CDN) | Self-hosted library (your storage) | -| Native first-party UI | React components (`UploadButton` / `UploadDropzone`) + adapters for Next.js and other full-stack frameworks (Solid, Svelte, Vue, Nuxt, Expo, and more) | React, Vue, Svelte, Angular, Vanilla JS, Preact | +| Feature | UploadThing | upup | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | +| Model | Hosted SaaS (managed storage + CDN) | Self-hosted library (your storage) | +| Native first-party UI | React components (`UploadButton` / `UploadDropzone`) + adapters for Next.js and other full-stack frameworks (Solid, Svelte, Vue, Nuxt, Expo, and more) | React, Vue, Svelte, Angular, Vanilla JS, Preact | | Headless core | React hooks (coupled to the service) | Yes (`@useupup/core`, framework-agnostic) | -| License / pricing | Open-source SDK (MIT); the service is a paid SaaS with a free tier and usage-based plans | MIT, free & open source (you pay only for your own storage) | +| License / pricing | Open-source SDK (MIT); the service is a paid SaaS with a free tier and usage-based plans | MIT, free & open source (you pay only for your own storage) | | Storage / self-host | Managed — files are stored on UploadThing's infrastructure | Any S3-compatible storage you own (AWS, MinIO, R2, Spaces, Wasabi, Backblaze) via `@useupup/server` | -| Cloud-drive sources | No | Google Drive, OneDrive, Dropbox, Box | -| Camera / screen capture | No | Yes (both) | -| Image editor | No | Yes — React/Preact only | -| Resumable uploads | Not a primary feature | Yes — optional (tus or S3 multipart) | -| i18n | No | Yes (ICU locale bundles) | +| Cloud-drive sources | Not built in | Google Drive, OneDrive, Dropbox, Box | +| Camera / screen capture | Not built in | Yes (both) | +| Image editor | Not built in | Yes — React/Preact only | +| Resumable uploads | Yes — `createUpload` (start, pause, resume), since v7 | Yes — optional (tus or S3 multipart) | +| i18n | No | Yes (ICU locale bundles) | ## Choose UploadThing if diff --git a/apps/landing/content/docs/guides/reliability.mdx b/apps/landing/content/docs/guides/reliability.mdx index 4513fc0e0..03f795746 100644 --- a/apps/landing/content/docs/guides/reliability.mdx +++ b/apps/landing/content/docs/guides/reliability.mdx @@ -298,7 +298,7 @@ all, tus remains the resumable option. ## A note on `fastAbortThreshold` `fastAbortThreshold` is accepted by the core options and forwarded internally, -but nothing reads it in v3.1.0 — setting it has no effect on behavior. It is +but nothing reads it as of v3.3 — setting it has no effect on behavior. It is documented here only so you do not spend an afternoon tuning a value that does nothing. Use `maxRetries` to control how long upup persists on a failing file. diff --git a/apps/landing/content/docs/guides/server-auth.mdx b/apps/landing/content/docs/guides/server-auth.mdx index 7af7e788c..fcb997b12 100644 --- a/apps/landing/content/docs/guides/server-auth.mdx +++ b/apps/landing/content/docs/guides/server-auth.mdx @@ -128,7 +128,7 @@ The token binds: from `exp`, since `init` is the only issuer and has always used the same TTL. It is signed with HMAC-SHA-256 over the payload using your -`uploadTokenSecret`, via Web Crypto (so it works on Node 18+, edge runtimes, +`uploadTokenSecret`, via Web Crypto (so it works on Node 20+, edge runtimes, and Cloudflare Workers). On every continuation request the handler verifies the signature **before** trusting any payload byte, compares it in constant time, and checks expiry — the one exception being `resume`, which applies the resume diff --git a/apps/landing/src/__tests__/claim-integrity.test.ts b/apps/landing/src/__tests__/claim-integrity.test.ts new file mode 100644 index 000000000..5b8417233 --- /dev/null +++ b/apps/landing/src/__tests__/claim-integrity.test.ts @@ -0,0 +1,95 @@ +import { readFileSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import { describe, expect, it } from 'vitest' + +// Ratchet for the 2026-09 claim-integrity pass. Marketing copy is read +// literally by the people evaluating upup, so the wording the audit retired +// must not creep back in. Two classes are gated here: +// +// 1. Claims the code cannot back. The cross-framework parity suite compares a +// NORMALIZED tree — structure, `upup-` class tokens, a11y attributes and +// text, with framework-injected attributes stripped and Angular host +// elements unwrapped (apps/e2e-test/cross-framework/parity-dom.ts) — so +// "byte-identical" / "byte-for-byte" overstates what it proves. The site +// also has no account system (src/app has no sign-up route), so copy about +// data collected "when you sign up" describes a flow that does not exist. +// 2. Social proof upup has never been able to substantiate: user counts, +// "trusted by" endorsements, and ranking superlatives. None appear today — +// this locks that in rather than fixing it after it ships. +// +// The sources are read as TEXT on purpose: the claims live in string literals +// and JSX prose, and a text read catches them wherever in the file they move. + +const COPY_SOURCES: Record = { + 'src/lib/faqs.ts': '../lib/faqs.ts', + 'src/components/FeatureShowcase/index.tsx': + '../components/FeatureShowcase/index.tsx', + 'src/components/HomepageFeatures/index.tsx': + '../components/HomepageFeatures/index.tsx', + 'src/app/privacy/page.tsx': '../app/privacy/page.tsx', +} + +function readCopySources(): [string, string][] { + return Object.entries(COPY_SOURCES).map(([label, relative]) => [ + label, + readFileSync(fileURLToPath(new URL(relative, import.meta.url)), 'utf8'), + ]) +} + +/** JSX prose is hard-wrapped, so a phrase can straddle a newline + indent. */ +function normalizeWhitespace(source: string): string { + return source.replace(/\s+/g, ' ') +} + +describe('landing copy claim integrity', () => { + it('reads every landing copy source this guard is supposed to cover', () => { + for (const [label, text] of readCopySources()) { + expect( + text.length, + `${label} is empty or unreadable`, + ).toBeGreaterThan(500) + } + }) + + it('states no byte-identical or byte-for-byte DOM claim, because the parity suite compares a normalized tree', () => { + for (const [label, text] of readCopySources()) { + const prose = normalizeWhitespace(text).toLowerCase() + expect( + prose, + `${label} claims byte-identical rendering the parity suite does not verify`, + ).not.toContain('byte-identical') + expect( + prose, + `${label} claims byte-for-byte rendering the parity suite does not verify`, + ).not.toContain('byte-for-byte') + } + }) + + it('describes no data collected at sign-up, because the site ships no sign-up route', () => { + for (const [label, text] of readCopySources()) { + const prose = normalizeWhitespace(text).toLowerCase() + expect( + prose, + `${label} references a sign-up flow that does not exist on useupup.com`, + ).not.toContain('when you sign up') + } + }) + + it('carries no unsubstantiated social proof such as trusted-by endorsements, user counts or ranking superlatives', () => { + const UNSUBSTANTIATED = [ + 'trusted by', + 'users worldwide', + '#1', + 'most popular', + ] + for (const [label, text] of readCopySources()) { + const prose = normalizeWhitespace(text).toLowerCase() + for (const phrase of UNSUBSTANTIATED) { + expect( + prose, + `${label} makes an unsubstantiated "${phrase}" claim — upup has no published adoption figures to back it`, + ).not.toContain(phrase) + } + } + }) +}) diff --git a/apps/landing/src/app/privacy/page.tsx b/apps/landing/src/app/privacy/page.tsx index bc8435e6c..1a7892104 100644 --- a/apps/landing/src/app/privacy/page.tsx +++ b/apps/landing/src/app/privacy/page.tsx @@ -16,7 +16,7 @@ export default function Privacy() {

Privacy{' '} @@ -36,8 +36,9 @@ export default function Privacy() {

1. Information We Collect

We may collect personal data such as your name and email - address when you sign up or contact us. Additionally, we - gather usage data via cookies and similar technologies. + address when you contact us, submit the support form, or + request a provider integration. Additionally, we gather + usage data via cookies and similar technologies.

2. How We Use Your Information

diff --git a/apps/landing/src/components/FeatureShowcase/index.tsx b/apps/landing/src/components/FeatureShowcase/index.tsx index 6649276b9..55531fdbc 100644 --- a/apps/landing/src/components/FeatureShowcase/index.tsx +++ b/apps/landing/src/components/FeatureShowcase/index.tsx @@ -46,7 +46,7 @@ const heroRows: HeroRow[] = [ icon: , title: 'Six Frameworks, One Uploader', description: - 'Native UI for React, Vue, Svelte, Angular, Vanilla JS, and Preact — one uploader that renders byte-identical DOM in every framework, enforced by a cross-framework parity suite.', + 'Native UI for React, Vue, Svelte, Angular, Vanilla JS, and Preact — one uploader that renders the same DOM structure, classes, and accessibility tree in every framework, enforced by a cross-framework parity suite.', Visual: FrameworksScene, live: true, }, diff --git a/apps/landing/src/components/HomepageFeatures/index.tsx b/apps/landing/src/components/HomepageFeatures/index.tsx index d74778cda..61ebe8742 100644 --- a/apps/landing/src/components/HomepageFeatures/index.tsx +++ b/apps/landing/src/components/HomepageFeatures/index.tsx @@ -927,7 +927,7 @@ export default function HomepageFeatures() {

You can connect to upup using any S3 provider. We - also support some non-S3 connections. + also support Azure Blob Storage in client mode.

diff --git a/apps/landing/src/lib/faqs.ts b/apps/landing/src/lib/faqs.ts index 54e741a50..e5dadff1d 100644 --- a/apps/landing/src/lib/faqs.ts +++ b/apps/landing/src/lib/faqs.ts @@ -9,7 +9,7 @@ export interface Faq { export const faqs: Faq[] = [ { question: 'Which frameworks does upup support?', - answer: 'upup ships one uploader with native UI packages for React, Vue, Svelte, Angular, Vanilla JS, and Preact. Every framework renders byte-identical DOM, verified by a cross-framework parity test suite, so the uploader looks and behaves the same everywhere.', + answer: 'upup ships one uploader with native UI packages for React, Vue, Svelte, Angular, Vanilla JS, and Preact. Every framework renders the same DOM structure, classes, and accessibility tree, verified by a cross-framework parity test suite, so the uploader looks and behaves the same everywhere.', }, { question: 'Is upup free and open source?',