The place for high quality transactions.
Find a file
exe-dev-bot 3feb6ee246
All checks were successful
Deploy to Cloudflare Workers / deploy (push) Successful in 1m37s
fix(images): fix iOS image upload and improve logging (#80)
## Problem

A user on iOS (iPhone, Safari 18.1.1) reported being unable to upload item images. The feature works on Linux and Android. Cloudflare logs showed minimal diagnostic information.

## Root Cause Analysis

### 1. Critical: Race condition unmounts form during upload
The `onSubmit` flow called `onSave(result.data, true)` **before** `uploadImages()` completed. This triggered `handleSave` in `ItemFormPage`, which set `success=true`, causing React to unmount `ItemForm` and render the success message instead. The unmount cleanup fired `abortControllerRef.current.abort()`, killing all in-flight image uploads. The `isUploading` parameter was accepted in the type signature but completely ignored by `handleSave`.

This race existed on all platforms but iOS Safari is more aggressive about unmount timing, making the window tighter.

### 2. High: HEIC/HEIF not accepted
iOS cameras default to HEIC format. The file input `accept` attribute and `ALLOWED_TYPES` only included JPEG/PNG/WebP. While iOS Safari auto-converts HEIC on selection, the conversion is inconsistent across iOS versions — sometimes reporting empty `file.type` or `image/heif`, which `validateImageFile` rejected.

### 3. Medium: Web Workers in image compression
`browser-image-compression` with `useWebWorker: true` uses `OffscreenCanvas` and `createImageBitmap` inside workers. iOS Safari has had longstanding issues with these APIs in worker contexts.

### 4. Medium: No FileReader error handler
If `FileReader.readAsDataURL` failed (e.g., corrupted compression output), `loadedCount` never reached `files.length`, leaving the UI stuck in "Processing images..." state forever.

## Changes

### `src/components/react/ItemForm.tsx`
- **Fix race condition**: Remove premature `onSave(result.data, true)` call. Now `onSave` is called only once, after uploads complete
- **HEIC/HEIF support**: Add to file input `accept` attribute
- **Disable web workers**: Set `useWebWorker: false` for iOS compatibility
- **FileReader error handler**: Prevent stuck processing state
- **Preserve compression output type**: Use `compressedBlob.type` instead of hardcoding `image/jpeg`
- **Diagnostic logging**: Log file metadata on selection, compression failures with details, and abort events

### `src/lib/storage.ts`
- **Magic byte validation**: Replace MIME-type-only validation with binary header inspection. Detects JPEG, PNG, WebP, and HEIC/HEIF from file headers. Files with empty/generic MIME types (common iOS quirk) are accepted only if magic bytes confirm they are real images. This prevents attackers from uploading non-image files (SVG with JS, HTML) by spoofing MIME types.
- **HEIC/HEIF in allowed types**: Add to `ALLOWED_TYPES` and MIME-to-extension map
- **Upload logging**: Log file path, type, and size at upload start

### `tests/unit/storage.test.ts`
- Rewrite mock file helpers to use real magic byte headers
- Add tests for HEIC/HEIF acceptance and extension mapping
- Add tests for empty MIME type + valid magic bytes (iOS scenario)
- Add tests for invalid magic bytes rejection
- Add RIFF-without-WEBP rejection test

## Testing

- [ ] Upload image on desktop browser (regression check)
- [ ] Upload image on iOS Safari (if available)
- [ ] Upload HEIC photo from iOS camera roll
- [ ] Verify success redirect waits for upload to complete
- [ ] Check browser console for new diagnostic logging
- [ ] `just test-unit` — all 331 tests pass

Reviewed-on: #80
Co-authored-by: exe-dev-bot <exe.dev@kwila.cloud>
Co-committed-by: exe-dev-bot <exe.dev@kwila.cloud>
2026-02-18 06:35:48 -05:00
.agents/skills/specture chore: update specture 2026-02-12 05:28:44 -05:00
.bots docs: add roadmap (#13) 2025-11-08 06:00:44 -05:00
.forgejo/workflows feat: complete spec-003 testing & cleanup (#79) 2026-02-14 04:12:46 -05:00
.opencode/command refactor: migrate to forgejo 2025-12-12 07:32:08 -05:00
.vscode feat: infrastructure setup for basic home page (#15) 2025-11-08 21:27:40 -05:00
docs fix: use Cloudflare runtime env for secrets (#73) 2026-02-07 17:09:32 -05:00
public feat: UI polish and improvements (#61) 2026-01-10 14:38:40 -05:00
scripts feat(newsletter): add newsletter generation utilities and pages (#69) 2026-02-06 04:40:09 -05:00
specs docs: update task list 2026-02-14 04:28:30 -05:00
src fix(images): fix iOS image upload and improve logging (#80) 2026-02-18 06:35:48 -05:00
supabase feat(newsletter): add delivery pipeline 2026-02-07 16:24:33 -05:00
tests fix(images): fix iOS image upload and improve logging (#80) 2026-02-18 06:35:48 -05:00
.env.example fix(email): derive SMTP secure mode from credentials (#76) 2026-02-07 18:54:38 -05:00
.envrc feat: basic connections system (#66) 2026-01-22 11:49:43 -05:00
.gitignore feat: complete spec-003 testing & cleanup (#79) 2026-02-14 04:12:46 -05:00
.pre-commit-config.yaml feat: implement user profile system (#40) 2025-12-24 13:37:55 -05:00
.prettierignore chore: tooling (#16) 2025-11-14 10:07:23 -05:00
.prettierrc chore: tooling (#16) 2025-11-14 10:07:23 -05:00
AGENTS.md chore: update specture 2026-02-12 05:28:44 -05:00
astro.config.mjs feat: complete spec-003 testing & cleanup (#79) 2026-02-14 04:12:46 -05:00
CONTRIBUTING.md feat: implement user profile system (#40) 2025-12-24 13:37:55 -05:00
eslint.config.js fix(images): fix iOS image upload and improve logging (#80) 2026-02-18 06:35:48 -05:00
flake.lock feat(newsletter): add delivery pipeline 2026-02-07 16:24:33 -05:00
flake.nix feat(newsletter): add delivery pipeline 2026-02-07 16:24:33 -05:00
justfile feat(newsletter): add delivery pipeline 2026-02-07 16:24:33 -05:00
LICENSE Initial commit 2025-11-03 20:23:28 -05:00
package-lock.json fix(ci): speed up CI/CD pipelines (#75) 2026-02-07 18:11:26 -05:00
package.json fix(ci): speed up CI/CD pipelines (#75) 2026-02-07 18:11:26 -05:00
playwright.config.ts feat: basic connections system (#66) 2026-01-22 11:49:43 -05:00
README.md feat: implement user profile system (#40) 2025-12-24 13:37:55 -05:00
tsconfig.json feat: infrastructure setup for basic home page (#15) 2025-11-08 21:27:40 -05:00
vitest.config.ts feat: complete spec-003 testing & cleanup (#79) 2026-02-14 04:12:46 -05:00
wrangler.jsonc fix(newsletter): use US timezones for Sunday delivery guard (#77) 2026-02-12 04:33:29 -05:00

Market

Find what you need through people you trust

Note

We are still determining a final name for the market. Let us know if you have ideas - market@kwila.cloud.

Problems to Solve

Used Market

If you are looking for a good deal on something on the used market, it takes way too much time and effort to research and find what you need.

What if you could simply post what you're looking for - "need a used stove, $200 budget" or "need a house cleaner" - and your community could help you find what you are looking for?

Small Businesses

If you are looking to have a digital presence for your small business without breaking the bank investing in a dedicated website, what do you do?

What if you could quickly and easily set up a vendor page to list your inventory and portfolio?

Our Solution

An online marketplace designed to foster authentic interactions and high-quality transactions within trusted communities. Built for local artisans, small businesses, and individuals who value relationships over profit margins.

This market exists to serve people and communities, not investors or growth metrics. We're building a platform for small-scale, high-trust commerce that keeps the focus on authentic human relationships.

How It Works

  1. Join through invitation: Receive an invite link from someone already in the network
  2. Browse or request: Search existing offerings or post what you're looking for
  3. Connect privately: Accept offers that interest you and start direct conversations
  4. Transact your way: Handle payments outside the platform (in person, Venmo, Cash, etc.)
  5. Build trust: Your network grows organically through real connections

Core Values

  • Humans are eternal, the things we buy and sell are not.
  • Relationships have lasting value, profit margins do not.
  • Quality is worth far more than quantity.

Core Features

Request-Driven Commerce

The heart of our market is the request system. Users can post what they're looking for, and others on the platform can attempt to fulfill those requests. Whether you need a used stove for $500 or a custom software solution, simply describe what you are looking for and leverage your social connections to reduce the time and effort it takes to find it.

Three Item Categories

  • New Items: Locally made goods from artisans and small businesses (custom crafts, 3D printed items, handmade clothing, etc.)
  • Resale Items: Quality used goods that deserve a second life instead of going to waste (equipment, furniture, sporting goods, etc.)
  • Services: Gig work and specialized skills (repairs, freelance work, custom shopping services, etc.)

Trust Through Connections

Our platform is invite-only during the beta phase. New accounts can only be created through invite links from existing members, automatically establishing a connection between inviter and invitee. This creates an organic network of people who know each other, ensuring authenticity from day one.

Privacy-First Design

Contact information (email and phone) has three visibility levels:

  • Hidden: Only the system can see it
  • Connections Only: Only visible to your direct connections
  • Public: Visible to all users and visitors

When someone responds to your request, you see their offer first—not their contact details. You choose whether to open a direct messaging channel or decline with no further contact.

Technical Stack

  • Frontend: Astro with React/Vue islands for interactive components
  • Backend: Supabase (database, authentication, image storage)
  • License: MIT

Project Status

The project is currently in early development. We're focused on validating the core marketplace mechanics with a trusted beta group before adding more complex features.

Contributing

This is an open-source project, and contributions are welcome. See our contributing guide to get started.

Documentation

For developers, see the developer documentation for architecture details and system documentation.