Skip to content

feat: Image Upload, Design Systems, and Test Hardening#294

Open
davideast wants to merge 10 commits intomainfrom
feat/upload-image
Open

feat: Image Upload, Design Systems, and Test Hardening#294
davideast wants to merge 10 commits intomainfrom
feat/upload-image

Conversation

@davideast
Copy link
Copy Markdown
Collaborator

This PR introduces several handwritten extensions to the Stitch SDK to support advanced workflows, along with test suite hardening:

  1. Direct Image Upload: Support for uploading images directly to the Stitch API via Project.uploadImage (using private REST endpoints not exposed in MCP).
  2. Design System Utilities: Handwritten helpers on Project to infer themes from HTML, sync themes to the API, and inject them into prompts.
  3. Asset Downloader: A robust Project.downloadAssets method that localizes screen assets and prevents ENAMETOOLONG errors.
  4. Test Suite Hardening: Resolves a critical data leak in test mocks and removes all as any assertions in key test files.
  5. Workflow Cleanup: Removes deprecated Fleet workflows, leaving only the core CI pipeline.

Usage

Image Upload & Theme Extraction Flow

import { stitch } from "@google/stitch-sdk";
import * as fs from "fs/promises";

const project = stitch.project("4044680601076201931");

// 1. Upload an image
const [screen] = await project.uploadImage("path/to/mockup.png", {
  title: "Source Mockup"
});

// 2. Infer theme from the generated HTML
const theme = await project.inferTheme(screen.id);

// 3. Sync inferred theme to the project's design system
await project.syncTheme(theme);

// 4. Guide future generations with this theme
const prompted = project.themePrompt("Create a login page", theme);

Asset Downloader

// Download all screens and assets to a local folder
await project.downloadAssets("./dist/preview");

Changes

Image Upload

  • Project.uploadImage — Handwritten method for direct REST upload of image assets (PNG, JPEG, WebP), returning domain Screen objects.
  • upload-handler.ts — Internal handler for managing multipart uploads and resolving correct endpoints.

Design System Utilities (Handwritten)

  • Project.inferTheme(screenId) — Parses HTML of a screen to extract theme tokens (customColor, headlineFont, roundness) using a rule-based extraction system.
  • Project.syncTheme(theme) — Orchestrates the API dance to either create or update the project's design system with the inferred theme.
  • Project.themePrompt(prompt, theme) — Injects design system tokens into a text prompt to guide future generations.

Asset Downloader

  • Project.downloadAssets(outputDir) — Downloads all screens and their referenced assets, rewriting URLs to be self-contained.
  • Path Length Safety — Truncates base encoded filenames to 100 chars to prevent ENAMETOOLONG errors on deep paths.

Test Suite & Hardening

  • Data Leak Fix — Fixed a mock leak in theme-extensions.test.ts where using .buffer exposed the Node.js shared pool. Now uses clean manual ArrayBuffer construction.
  • Type Safety — Removed manual as any casts in tests and resolved all type errors in theme-extensions.test.ts.
  • Workflows — Removed fleet-analyze.yml, fleet-dispatch.yml, fleet-label.yml, fleet-merge.yml, and jules-merge-conflicts.yml. Only ci.yml remains.

…sHandler

- DownloadAssetsInputSchema gets three optional fields (all backward-compatible):
  - fileMode (default 0o600): Unix permission bits for written files
  - tempDir (default outputDir): override atomic-write temp directory
  - assetsSubdir (default 'assets'): override assets subdirectory name
- atomicRename() helper handles EXDEV cross-device fallback (copyFile+unlink)
- assetsSubdir is guarded via path.basename() to strip traversal attempts
- project-ext.ts: downloadAssets() now accepts an optional opts object
- 3 new tests cover each option path; all 11 unit tests pass
const tempFilename = `.tmp-${crypto.randomBytes(8).toString('hex')}-${filename}`;
const tempFullPath = path.join(resolvedTempDir, tempFilename);

await fs.writeFile(tempFullPath, Buffer.from(buffer), { flag: 'wx', mode: fileMode });
- domhandler: switch from value import to 'import type { AnyNode }' so tsc
  can resolve the type without domhandler in package.json (it's a transitive
  dep of cheerio, but not guaranteed to be hoisted in CI)
- CodeQL: temp filenames now contain only crypto.randomBytes — network-derived
  filenames are never embedded in the temp path. The sanitized filename only
  appears in the final atomicRename destination, which tsc/CodeQL can verify
  is safe (sanitizeFilename strips everything non-alphanumeric)
- test: update writeFile assertion to check '.tmp-' instead of 'badname'
  since temp paths are now fully random; rename-dest assertion unchanged
tsc resolves 'import type' the same as value imports — if the package
isn't in package.json, the typecheck fails even in CI environments that
don't hoist transitive deps. domhandler is pinned to 5.0.3 to match the
version used by cheerio@1.0.0-rc.12.
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