Skip to content

feat: update publish command to use named File for ZIP and icon uploads#12

Merged
smoratino-apogea merged 2 commits into
mainfrom
feature/named-file-uploads
Apr 28, 2026
Merged

feat: update publish command to use named File for ZIP and icon uploads#12
smoratino-apogea merged 2 commits into
mainfrom
feature/named-file-uploads

Conversation

@smoratino-apogea

Copy link
Copy Markdown
Contributor

Send named File for bundle and icon uploads

Summary

Replace new Blob([buffer]) with new File([buffer], filename, { type }) in the publish command. The CLI was sending unnamed, untyped multipart entries — which the platform now rejects (bundle), or has been silently rejecting since the icon endpoint shipped (icon).

What changed

In src/cli/commands/publish.ts:

  • Bundle ZIP: new Blob([zipBuffer])new File([zipBuffer], 'bundle.zip', { type: 'application/zip' }). Renamed the local zipBlob variable and the publishApp / publishComponent parameter to zipFile: File.
  • Icon: new Blob([iconBuffer])new File([iconBuffer], basename(resolvedPath), { type: ICON_MIME_TYPES[ext] }). Added an ICON_MIME_TYPES map and derived ALLOWED_ICON_EXTENSIONS from its keys (single source of truth).

No changes to public API, client surface, or tests.

Why it didn't fail before

Two independent backend validations are involved, both added recently:

  1. Bundle — allowedExtensionFilter (platform commit 135f5d2 feat(uploads): add extension whitelist and fix S3 ContentType). This is part of the feat/free-tier-limits PR currently in flight on the platform repo. Before this filter, multer accepted any multipart entry regardless of filename or extension. Node's FormData defaults the filename of an unnamed Blob to "blob" (no extension), so the new filter rejects every CLI publish with a 400.

  2. Icon — ALLOWED_ICON_MIMETYPES (platform commit 8439550 feat: add optional icon support for items, March 1 2026). The icon endpoint validates file.mimetype ∈ {image/png, image/webp, image/x-icon, image/vnd.microsoft.icon}. A Blob constructed without type serializes as application/octet-stream over multipart, which is not in the allowlist. The icon CLI feature was added shortly after (b7949c0 feat: add icon support for items (#6)) but never sent a valid mimetype — so it has been broken since it shipped. The bug went unnoticed because the feature is new and likely unused in production.

Risk and side effects

The backend uses originalname and mimetype in three places. None are regressions:

  • Extension extraction (item.service.ts:118): now derives "zip" instead of "". This is the path the new filter validates.
  • Item name fallback (item.service.ts:119): only triggers when no name is supplied. The CLI always passes projectName, so this is unreachable from publish.
  • DB metadata (s3-multer.ts, iconMimeType field): cosmetic improvement — "bundle.zip" over "blob", "image/png" over "application/octet-stream".

64 published versions of thatopen-services (0.0.1 → 0.14.0) all carry the old Blob code. None of them work against a backend with the extension filter. After this fix, all future versions will.

Test plan

  • yarn build — passes (verified locally).
  • thatopen publish against a backend with allowedExtensionFilter enabled — bundle uploads succeed.
  • thatopen publish --icon ./icon.png — icon uploads succeed and the item's iconMimeType is image/png.
  • Same with .webp and .ico extensions.
  • Republish (existing appId / componentId in .thatopen) — createVersion / updateComponent succeed.

@smoratino-apogea smoratino-apogea merged commit 3a0b129 into main Apr 28, 2026
1 check passed
@agviegas agviegas deleted the feature/named-file-uploads branch June 4, 2026 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants