diff --git a/.changeset/viewer-alpha.md b/.changeset/viewer-alpha.md new file mode 100644 index 00000000..06d83b7f --- /dev/null +++ b/.changeset/viewer-alpha.md @@ -0,0 +1,11 @@ +--- +"@transloadit/viewer": patch +--- + +Publish the first alpha of `@transloadit/viewer`: native responsive images from Transloadit Storage +or an origin-pinned HTTP/S3 Smart CDN Template, with a Next.js Server Component and a resolved-model +renderer. Image bytes go directly from Smart CDN to the browser. + +This is an early, images-only API that may change between releases. Install the `alpha` tag and pin +the exact version. Storage and browser CLI login require the matching backend and Console rollout; +an existing compatible HTTP/S3 Template does not require a Storage migration. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a95e917d..09096cba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,7 +30,7 @@ jobs: - uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d with: version: corepack yarn changeset:version:release - publish: corepack yarn changeset publish + publish: corepack yarn release:publish commitMode: github-api env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b254407a..f4ba3199 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -116,12 +116,20 @@ Changelog guidance: Manual fallback (maintainers only): -- `corepack yarn changeset publish` +- On the generated version PR's merged commit: `corepack yarn release:publish`. +- This publishes Viewer with its explicit `alpha` tag, then uses `changeset publish` for the + remaining packages and `changeset tag` for release discovery. A failed registry lookup stops the + release; retries do not republish an existing version. Notes: - CI publishing requires npm trusted publishing (OIDC) configured for this repo. -- Scoped packages now publish to the default `latest` dist-tag unless a workflow explicitly overrides it. +- Scoped packages publish to `latest`, except `@transloadit/viewer`, which is an alpha and uses + `alpha`. Its `publishConfig.tag` alone is insufficient because Changesets passes `--tag latest`. +- Mark the Viewer GitHub release as a prerelease and verify the npm tags after publishing. On first + publication, npm may also assign `latest`; remove that Viewer tag if present. +- Viewer prepack uses incremental TypeScript builds, like Utils. Do not recursively clean project + references there: Changesets can pack dependent packages concurrently. - If this was a pre-release, remember to reset the [npm `latest` tag](https://www.npmjs.com/package/transloadit?activeTab=versions) to the previous version (replace `x.y.z` with previous version): - `npm dist-tag add transloadit@X.Y.Z latest` diff --git a/docs/img-dogfood.md b/docs/img-dogfood.md index f263be79..05dd3cc6 100644 --- a/docs/img-dogfood.md +++ b/docs/img-dogfood.md @@ -3,14 +3,15 @@ These are maintainer-only packing, SDK seed and devdock notes, not consumer prerequisites. Use the [package README](../packages/img/README.md) for the application integration. -## September 17 consumer release gates +## Consumer release gates -Publish the minor Node/legacy release only after API2 #9057 and Content #5973 are deployed. -Device login, public delivery and safe CLI-key revocation need that coordinated rollout. +Kevin authorized the first Viewer alpha and the normal SDK release train on September 21. +Package publication does not deploy API2 or enable Storage. Device login, public Storage delivery +and safe CLI-key revocation still need the coordinated API2 #9057 and Console rollout. Apply the CLI approval-key-identity migration before deploying API2; logout now signs explicit `action: 'revoke_auth_key'` intent. Native move responses and `builtin/storage-serve@0.0.3` must -be available before publishing the new consumer helpers. Viewer remains private dogfood. -At publication, replace preview-branch documentation links with their merged main locations. +be available before using those consumer helpers. Existing compatible HTTP/S3 image Templates +can use Viewer without migrating to Storage. The first Viewer release is an images-only alpha. Production Bunny checks, independent backup/restore and app-owned access checks remain gates; passing the local private-media fixture is not a production deployment claim. @@ -27,8 +28,8 @@ Storage uses relative catalog paths; custom HTTP/S3 Templates accept relative in intrinsic dimensions. Their fixed origins and import credentials belong in the Template, not JSX. See the package reference's custom Template recipe; the historical canaries below cover Storage. -This workspace remains private at version `0.0.0` while the API and production dogfood soak. Do not -depend on it from npm yet. +Use the package README's `alpha` install for released consumers. The local tarball procedure below +is for verifying unreleased changes and does not establish registry or production availability. ## Seed your first image @@ -51,8 +52,7 @@ Keep the app's stock Next.js TypeScript configuration and Node/React type depend ### Install the local packages Use a clean checkout of [transloadit/node-sdk](https://github.com/transloadit/node-sdk), at the -reviewed revision of [PR #500](https://github.com/transloadit/node-sdk/pull/500). For example, -`gh pr checkout 500` selects that PR; record `git rev-parse HEAD` before packing. From that SDK +reviewed revision being tested; record `git rev-parse HEAD` before packing. From that SDK checkout, install its locked dependencies and pack into your own temporary directory: ```bash diff --git a/docs/prompts/2026-09-21-viewer-alpha-release.md b/docs/prompts/2026-09-21-viewer-alpha-release.md new file mode 100644 index 00000000..d715b867 --- /dev/null +++ b/docs/prompts/2026-09-21-viewer-alpha-release.md @@ -0,0 +1,85 @@ +# Viewer alpha: Changesets release and Content adoption + +## Requested outcome + +Kevin authorized publishing the first Viewer alpha and completing the existing Version Packages +PR #502, including its Node/CLI, Utils, Types, Zod and MCP releases. Use the release procedure in +`CONTRIBUTING.md`: changeset in a reviewed PR, generated version/changelog PR, then trusted +publishing. Do not hand-edit versions or generated changelogs, deploy API2, or merge Uppy. + +The first Viewer patch changeset should produce **0.0.1**. This is an alpha product, not a stable +API promise: label its description and README accordingly, provide the `alpha` npm tag, and mark +its GitHub release as a prerelease. Record the actual registry version and tags before declaring +it available. The existing Changesets publisher passes `--tag latest`, overriding a package's +`publishConfig.tag`; that field alone is not proof of the final registry state. Do not put the +whole SDK into Changesets prerelease mode merely to release one experimental package. + +Viewer needs Utils 4.9's new exports. Let the existing Utils changesets and normal dependency +versioning update its registry dependency, and verify that the generated Viewer manifest requires +at least 4.9.0 before publishing. The old private workspace protocol must not escape into npm. + +## Gates + +- [x] Read contributing/release instructions and confirm the existing release PR's scope. +- [x] Reproduce private-package/registry-dependency metadata failure with a regression test. +- [x] Verify package checks and the packed Next browser fixture (Chromium/WebKit, both cache modes, + and development diagnostics). Run the versioned release dry run in PR CI. +- [ ] Reconcile council review; open and finish the alpha preparation PR. +- [ ] Inspect generated #502 versions, dependency floors, changelogs and exact-head CI. +- [ ] Merge #502, watch trusted publication and verify every expected registry version. +- [ ] Verify Viewer alpha tag and GitHub prerelease; prevent an accidental stable `latest` claim. +- [ ] Prove a registry-installed consumer, not just a workspace-linked or vendored package. + +For a new npm package, trusted-publisher bootstrap may need a maintainer action. Follow the +documented `release:publish` Changesets fallback only if the actual CI failure requires it, on the exact +generated release commit, after identifying which packages remain unpublished. Never republish a +version, expose credentials or treat a successful pack as a successful release. + +## Review and verification + +There were no comments or reviews on #502 at inspection. The first council found two valid release +issues, both reproduced red-first and fixed: Changesets overriding the alpha npm tag, and Viewer's +recursive TypeScript clean deleting dependent Utils artifacts during concurrent packaging. +The publisher now explicitly publishes only Viewer to alpha, then lets Changesets publish the +remaining packages and generate tags. Registry lookup failures fail closed and published versions +are skipped on retry. Viewer uses the same incremental build approach as Utils. A follow-up council +reported no further findings. An explicit TypeScript check caught an overloaded `execa` type +extraction error; the options now use its public `Options` type. Claude's review legs could not run because their monthly +spend limit was reached; do not count that leg as a successful independent review. + +Local `verify:full`, `check`, the 30 image fixture contract tests and the seven publisher tests pass. +The packed fixture passed both production modes (58 browser cases each), the secretless public-only +builds, and ten development cases. Evidence: `/tmp/viewer-alpha-20260921.gU9s3H/`. + +## Before API2 is deployed + +Storage Built-ins, native catalog APIs and device login need the matching backend rollout. The +README must not imply that merging or publishing the SDK has deployed those endpoints. Existing +origin-pinned HTTP/S3 Templates are independently usable. + +Content already has a signed website-image Template and a static candidate policy. Its regular +Viewer `Image` direct-delivery factory intentionally calls `connection()` and rotates short-lived +signatures. Replacing the current static site wrapper with that factory would change prerendering +and cache behavior. Start with the published package and its resolved-model `TransloaditPicture` +renderer, preserving the existing URLs, origin limits, expiry, layout, responsive widths, no-script +fallback and WebKit hydration safeguards. Media-gated slots need their existing inert fallback +until the shared renderer supports the same contract. Do not silently make pages dynamic or add +per-image redirect requests for this public-site adoption. + +The later production sequence remains backend deploy by Kevin/deployer → Console browser and +`gog` account/workspace proof → published CLI → Storage dogfood in Content and the Convex wedding +golden path. Keep application authorization/metadata in Convex and do not merge Uppy. + +## Build monitoring + +Monitoring began September 21, 2026 at 18:15 UTC, with a three-hour deadline (21:15 UTC), using +Content's `_scripts/alphalib/bin/gh-run-watch.ts`. SDK main `a2c5dd7` is green. API2 main `f664f5e` +was superseded/cancelled by `3abb5f0b81`; follow run `35637074003`, which includes the Storage merge. +Its predecessor `35627027644` still occupied the main concurrency group at inspection. +At 18:43 UTC, another main merge superseded that pending run: follow `35640243219` at `3555e451c2` +without extending the original 21:15 UTC monitoring deadline. + +Content main run `35636664743` failed before checkout on Spot-interrupted runners, including +`sb93ep` with deadline 18:13:19 UTC. Preserve the logs and do not call that run green. Evidence and +watcher logs: `/tmp/viewer-alpha-20260921.gU9s3H/`. Report the actual final API2 buildtar URL when +verified; deployment belongs to Kevin and the deployer agent. diff --git a/package.json b/package.json index 12497670..86e0f0d1 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "verify:full": "yarn verify && yarn knip && yarn test:types", "changeset:version:release": "yarn changeset version && YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install", "release:pack:dry-run": "node scripts/release-dry-run.ts", + "release:publish": "node scripts/publish-release.ts", "lint:js": "biome check .", "lint:ts": "yarn tsc:types && yarn tsc:node && yarn tsc:zod && yarn tsc:img && yarn workspace @transloadit/notify-url-relay lint:ts", "lint:transloadit-sync": "node scripts/check-transloadit-sync.ts", @@ -27,7 +28,7 @@ "pack": "node scripts/pack-transloadit.ts", "parity:transloadit": "node scripts/prepare-transloadit.ts && node scripts/fingerprint-pack.ts packages/transloadit --ignore-scripts --quiet --out /tmp/transloadit-after.json && node scripts/verify-fingerprint.ts --current /tmp/transloadit-after.json --diff", "test:img:fixture": "node scripts/test-img-next-fixture.ts", - "test:unit": "vitest run ./scripts/withProcess.test.ts ./scripts/img-next-fixture.test.ts ./scripts/knip.test.ts && yarn workspace @transloadit/utils test:unit && yarn workspace @transloadit/viewer test:unit && yarn workspace @transloadit/node test:unit && yarn workspace @transloadit/mcp-server test:unit && yarn workspace @transloadit/types test:unit && yarn workspace @transloadit/zod test:unit && yarn workspace @transloadit/notify-url-relay test:unit", + "test:unit": "vitest run ./scripts/withProcess.test.ts ./scripts/img-next-fixture.test.ts ./scripts/knip.test.ts ./scripts/publish-release.test.ts && yarn workspace @transloadit/utils test:unit && yarn workspace @transloadit/viewer test:unit && yarn workspace @transloadit/node test:unit && yarn workspace @transloadit/mcp-server test:unit && yarn workspace @transloadit/types test:unit && yarn workspace @transloadit/zod test:unit && yarn workspace @transloadit/notify-url-relay test:unit", "test:types": "yarn workspace @transloadit/zod test:types", "test:e2e": "yarn workspace @transloadit/node test:e2e", "test": "yarn workspace @transloadit/node test", diff --git a/packages/img/README.md b/packages/img/README.md index 1fb209fc..5a5cac22 100644 --- a/packages/img/README.md +++ b/packages/img/README.md @@ -1,16 +1,17 @@ # `@transloadit/viewer` -Responsive images from Transloadit Storage or your existing HTTP/S3 assets, for Next.js. -Native `picture/srcset`; bytes go straight from -Smart CDN (`.tlcdn.com`) to the browser, never through Next's image optimizer. +**Alpha — API may change between releases. Pin the exact version in production. Images only for now.** + +Responsive images from Storage or existing HTTP/S3 assets, for Next.js. Native `picture/srcset`; +bytes go straight from Smart CDN (`.tlcdn.com`) to the browser, never through Next's optimizer. ## Quickstart Next.js 16.3.3+ App Router, React 19, Node.js runtime. -**Unpublished dogfood:** ask a maintainer for matching viewer/utils/node/types tarballs. -After release: `npm install @transloadit/viewer && npm install --save-dev @transloadit/node`. -pnpm: `pnpm add @transloadit/viewer && pnpm add -D @transloadit/node`; -Yarn: `yarn add @transloadit/viewer && yarn add -D @transloadit/node`. +`npm install @transloadit/viewer@alpha && npm install --save-dev @transloadit/node`. +pnpm: `pnpm add @transloadit/viewer@alpha && pnpm add -D @transloadit/node`; +Yarn: `yarn add @transloadit/viewer@alpha && yarn add -D @transloadit/node`. +Storage needs a matching API2 deployment; [existing HTTP/S3 assets](#existing-http-or-s3-assets) do not. Run beside `package.json`. Start with `auth login` even without an account: choose Sign up in the browser it opens, create a free workspace, and approve the CLI. For `./hero.jpg`, use any JPEG you have. @@ -105,5 +106,4 @@ Lost metadata? Restore the committed catalog or [recover it from Storage](./docs ## Reference [Store an image from your application server](https://github.com/transloadit/node-sdk/blob/main/packages/node/README.md#store-an-image). - -[Full reference](./docs/reference.md) · [Version-pinned delivery](./docs/reference.md#cache-and-markup-cost) · [Maintainer dogfood setup](https://github.com/transloadit/node-sdk/blob/img-onboard/docs/img-dogfood.md). +[Full reference](./docs/reference.md) · [Version-pinned delivery](./docs/reference.md#cache-and-markup-cost) · [Maintainer dogfood setup](https://github.com/transloadit/node-sdk/blob/main/docs/img-dogfood.md). diff --git a/packages/img/package.json b/packages/img/package.json index 9a074f36..6cc50351 100644 --- a/packages/img/package.json +++ b/packages/img/package.json @@ -1,10 +1,13 @@ { "name": "@transloadit/viewer", "version": "0.0.0", - "description": "Native responsive images from Transloadit Storage and Smart CDN Templates", - "private": true, + "description": "Alpha: native responsive images from Transloadit Storage and Smart CDN Templates", "type": "module", "license": "MIT", + "publishConfig": { + "access": "public", + "tag": "alpha" + }, "repository": { "type": "git", "url": "https://github.com/transloadit/node-sdk", @@ -54,7 +57,7 @@ "./package.json": "./package.json" }, "scripts": { - "build": "../../node_modules/.bin/tsc --build --clean tsconfig.build.json && ../../node_modules/.bin/tsc --build tsconfig.build.json", + "build": "../../node_modules/.bin/tsc --build tsconfig.build.json", "check": "yarn lint:ts && yarn test:unit", "lint:ts": "../../node_modules/.bin/tsc --build tsconfig.build.json && ../../node_modules/.bin/tsc --noEmit --project tsconfig.json", "prepack": "yarn build", @@ -62,7 +65,7 @@ }, "dependencies": { "@noble/ciphers": "^1.3.0", - "@transloadit/utils": "workspace:^", + "@transloadit/utils": "^4.8.1", "server-only": "^0.0.1", "thumbhash": "0.1.1" }, diff --git a/scripts/fixtures/img-next/package-lock.json b/scripts/fixtures/img-next/package-lock.json index 507a6d8b..79005e4a 100644 --- a/scripts/fixtures/img-next/package-lock.json +++ b/scripts/fixtures/img-next/package-lock.json @@ -7,6 +7,7 @@ "name": "transloadit-img-next-fixture", "dependencies": { "@noble/ciphers": "1.3.0", + "@transloadit/utils": "4.8.1", "next": "16.3.4", "react": "19.2.8", "react-dom": "19.2.8", @@ -853,10 +854,9 @@ "license": "MIT" }, "node_modules/@transloadit/utils": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@transloadit/utils/-/utils-4.8.0.tgz", - "integrity": "sha512-lNe9zKj51CeN1WpzSsGatCIPaQItudlbc6SgWzTc0jPnWUqf4hHJChAL2MfgOZ75zWz2st/S/jnKCtcZDImG6g==", - "dev": true, + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@transloadit/utils/-/utils-4.8.1.tgz", + "integrity": "sha512-Ne1XWAWyLSCRXrMtf5iLGokI30ZsNc0oWpLB4OOBMhTSZvXgEd1uRfSLf0QyNpp+I7tpXPOP0hSHOkWGVf4hBA==", "license": "MIT" }, "node_modules/@types/http-cache-semantics": { diff --git a/scripts/fixtures/img-next/package.json b/scripts/fixtures/img-next/package.json index acfa74db..ee3843d8 100644 --- a/scripts/fixtures/img-next/package.json +++ b/scripts/fixtures/img-next/package.json @@ -7,6 +7,7 @@ }, "dependencies": { "@noble/ciphers": "1.3.0", + "@transloadit/utils": "4.8.1", "next": "16.3.4", "react": "19.2.8", "react-dom": "19.2.8", diff --git a/scripts/img-next-fixture.test.ts b/scripts/img-next-fixture.test.ts index 8bcff89b..e40b5d50 100644 --- a/scripts/img-next-fixture.test.ts +++ b/scripts/img-next-fixture.test.ts @@ -43,12 +43,29 @@ test('scaffold helpers load in a cold checkout without built workspace packages' }) interface PackageManifest { + description?: string engines?: { node?: string } files?: string[] dependencies?: Record devDependencies?: Record + private?: boolean + publishConfig?: { access?: string; tag?: string } } +test('Viewer alpha is publishable with explicit release metadata and registry dependencies', async () => { + const manifest = await readManifest(resolve(import.meta.dirname, '../packages/img/package.json')) + expect(manifest.private).not.toBe(true) + expect(manifest.description).toMatch(/\balpha\b/i) + expect(manifest.publishConfig).toEqual({ access: 'public', tag: 'alpha' }) + expect(manifest.dependencies?.['@transloadit/utils']).toMatch(/^\^\d+\.\d+\.\d+$/) + const readme = await readFile(resolve(import.meta.dirname, '../packages/img/README.md'), 'utf8') + expect(readme).toContain('Alpha') + expect(readme).toContain('API may change') + expect(readme).toContain('@transloadit/viewer@alpha') + expect(readme).toContain('matching API2 deployment') + expect(readme).not.toContain('Unpublished dogfood') +}) + function scaffoldUploadCommand(page: string): string { const commands: string[] = [] function visit(node: Node): void { @@ -399,9 +416,7 @@ test('gets to the first image before teaching the security model and keeps the p expect(readme).toContain('One factory owns both modes') expect(readme).not.toMatch(/\bcreatePrivateStorageImages\b|\bcreateTransloaditImage\b|--next/) expect(readme).toContain('TRANSLOADIT_ENDPOINT') - expect(readme).toContain( - 'https://github.com/transloadit/node-sdk/blob/img-onboard/docs/img-dogfood.md', - ) + expect(readme).toContain('https://github.com/transloadit/node-sdk/blob/main/docs/img-dogfood.md') expect(readme).not.toContain('](../../docs/') expect(readme).toContain('The default route is `/api/storage-images`') expect(readme).toContain('capability has no independent expiry') @@ -414,8 +429,8 @@ test('answers stranger signup and delivery setup questions without a private-doc 'utf8', ) const quickstart = readme.slice(readme.indexOf('## Quickstart'), readme.indexOf('## Responsive')) - expect(quickstart).toContain('maintainer') - expect(quickstart).toContain('tarballs') + expect(quickstart).toContain('@transloadit/viewer@alpha') + expect(quickstart).toContain('matching API2 deployment') expect(quickstart).toContain('pnpm add') expect(quickstart).toContain('yarn add') expect(quickstart).toMatch(/choose Sign up in the\s+browser it opens/) diff --git a/scripts/publish-release.test.ts b/scripts/publish-release.test.ts new file mode 100644 index 00000000..75d62673 --- /dev/null +++ b/scripts/publish-release.test.ts @@ -0,0 +1,63 @@ +import { readFile } from 'node:fs/promises' + +import { afterEach, expect, test, vi } from 'vitest' + +import viewer from '../packages/img/package.json' with { type: 'json' } + +const { run } = vi.hoisted(() => ({ run: vi.fn() })) +vi.mock('execa', () => ({ execa: run })) + +afterEach(() => { + vi.resetModules() + vi.resetAllMocks() +}) + +test('the release publishes Viewer to alpha and leaves stable packages to Changesets', async () => { + run.mockResolvedValue({ exitCode: 0 }) + run.mockResolvedValueOnce({ exitCode: 1, stderr: 'npm error code E404' }) + await import('./publish-release.ts') + expect(run.mock.calls.map(([command, args]) => [command, args])).toEqual([ + ['npm', ['view', `${viewer.name}@${viewer.version}`, 'version', '--json']], + ['npm', ['publish', './packages/img', '--access', 'public', '--tag', 'alpha']], + ['corepack', ['yarn', 'changeset', 'publish']], + ['corepack', ['yarn', 'changeset', 'tag']], + ]) +}) + +test('an already published Viewer is not republished on a release retry', async () => { + run.mockResolvedValue({ exitCode: 0 }) + run.mockResolvedValueOnce({ exitCode: 0, stdout: JSON.stringify(viewer.version) }) + await import('./publish-release.ts') + expect(run.mock.calls.map(([, args]) => args[0])).toEqual(['view', 'yarn', 'yarn']) +}) + +test('a failed registry lookup is not mistaken for an unpublished package', async () => { + run.mockResolvedValueOnce({ exitCode: 1, stderr: 'npm error code E503' }) + await expect(import('./publish-release.ts')).rejects.toThrow('lookup') + expect(run).toHaveBeenCalledTimes(1) +}) + +test('an unexpected registry response stops publication', async () => { + run.mockResolvedValueOnce({ exitCode: 0, stdout: '"another-version"' }) + await expect(import('./publish-release.ts')).rejects.toThrow('lookup') + expect(run).toHaveBeenCalledTimes(1) +}) + +test('a failed Viewer publish stops before the stable release and tags', async () => { + run.mockResolvedValueOnce({ exitCode: 1, stderr: 'npm error code E404' }) + run.mockRejectedValueOnce(new Error('publish refused')) + await expect(import('./publish-release.ts')).rejects.toThrow('publish refused') + expect(run).toHaveBeenCalledTimes(2) +}) + +test('Viewer prepack never recursively deletes artifacts used by another publisher', () => { + expect(viewer.scripts.build).not.toContain('--clean') +}) + +test('the release workflow uses the package-aware Changesets publisher', async () => { + const workflow = await readFile( + new URL('../.github/workflows/release.yml', import.meta.url), + 'utf8', + ) + expect(workflow).toContain('publish: corepack yarn release:publish') +}) diff --git a/scripts/publish-release.ts b/scripts/publish-release.ts new file mode 100644 index 00000000..1e1be399 --- /dev/null +++ b/scripts/publish-release.ts @@ -0,0 +1,37 @@ +import type { Options } from 'execa' + +import { execa } from 'execa' + +import viewer from '../packages/img/package.json' with { type: 'json' } + +async function main(): Promise { + const cwd = new URL('..', import.meta.url) + const options = { cwd, stdio: 'inherit' } satisfies Options + const published = await execa( + 'npm', + ['view', `${viewer.name}@${viewer.version}`, 'version', '--json'], + { cwd, reject: false }, + ) + + if (published.exitCode === 0) { + if (JSON.parse(published.stdout) !== viewer.version) { + throw new Error('Unexpected Viewer registry lookup response; refusing to publish') + } + } else if (/^npm (error|ERR!) code E404$/m.test(published.stderr)) { + // Changesets hardcodes --tag latest, overriding publishConfig.tag. Publish this one alpha + // first; Changesets then discovers it as already published and handles the stable packages. + await execa( + 'npm', + ['publish', './packages/img', '--access', 'public', '--tag', 'alpha'], + options, + ) + } else { + throw new Error('Viewer registry lookup failed; refusing to treat a network/auth error as E404') + } + + await execa('corepack', ['yarn', 'changeset', 'publish'], options) + // Include the alpha in Changesets' normal tag output so its action creates the release too. + await execa('corepack', ['yarn', 'changeset', 'tag'], options) +} + +await main() diff --git a/scripts/release-dry-run.ts b/scripts/release-dry-run.ts index 840cd7b0..a36d06d2 100644 --- a/scripts/release-dry-run.ts +++ b/scripts/release-dry-run.ts @@ -28,6 +28,9 @@ async function packPackage(packDir: string, target: PackTarget): Promise { const main = async (): Promise => { const packDir = await mkdtemp(resolve(tmpdir(), 'transloadit-release-dry-run-')) + await packPackage(packDir, { packagePath: './packages/utils' }) + await packPackage(packDir, { packagePath: './packages/img' }) + await Promise.all([ packPackage(packDir, { packagePath: './packages/node', diff --git a/yarn.lock b/yarn.lock index b1cce35c..4d409ad5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1512,7 +1512,7 @@ __metadata: languageName: unknown linkType: soft -"@transloadit/utils@npm:^4.3.0, @transloadit/utils@npm:^4.8.1, @transloadit/utils@workspace:^, @transloadit/utils@workspace:packages/utils": +"@transloadit/utils@npm:^4.3.0, @transloadit/utils@npm:^4.8.1, @transloadit/utils@workspace:packages/utils": version: 0.0.0-use.local resolution: "@transloadit/utils@workspace:packages/utils" dependencies: @@ -1525,7 +1525,7 @@ __metadata: resolution: "@transloadit/viewer@workspace:packages/img" dependencies: "@noble/ciphers": "npm:^1.3.0" - "@transloadit/utils": "workspace:^" + "@transloadit/utils": "npm:^4.8.1" "@types/react": "npm:^19.2.14" "@types/react-dom": "npm:^19.2.3" happy-dom: "npm:^20.9.0"