Skip to content

feat!: add browser support with unified MBTiles.open() API#1

Merged
gmaclennan merged 22 commits intomainfrom
claude/add-browser-support-tiCwf
Mar 17, 2026
Merged

feat!: add browser support with unified MBTiles.open() API#1
gmaclennan merged 22 commits intomainfrom
claude/add-browser-support-tiCwf

Conversation

@gmaclennan
Copy link
Member

@gmaclennan gmaclennan commented Mar 13, 2026

Summary

  • Add browser support using @sqlite.org/sqlite-wasm via conditional "browser" export
  • Breaking: replace new MBTiles() constructor with async MBTiles.open() on both platforms, unifying the API across Node.js and browser
  • Both platforms accept ArrayBuffer and Uint8Array; Node.js also accepts file paths and better-sqlite3 Database instances; browser also accepts File and OPFS paths (in a Worker)
  • Extract shared validation logic into lib/validate.js
  • Add comprehensive shared test suite, browser tests (Playwright), OPFS worker tests, and validation unit tests
  • Add CI workflow for both Node.js and browser environments
  • Conditional export types fields for correct TypeScript resolution with moduleResolution: "bundler"

Breaking changes

  • new MBTiles(path)await MBTiles.open(path) — the constructor is now private on both platforms
  • better-sqlite3 moved from dependencies to optionalDependencies

Test plan

  • Node tests pass (npm run test:node) — 34 tests
  • Browser tests pass (npm run test:browser) — Playwright chromium/firefox/webkit
  • npx publint reports no issues
  • TypeScript types resolve correctly — verified by installing the packed tarball in a fresh project and type-checking against moduleResolution: "nodenext", "bundler", and "bundler" with customConditions: ["browser"]

🤖 Generated with Claude Code

- Add browser entry point (index.browser.js) with async MBTiles.open() factory
  that accepts File, ArrayBuffer, or Uint8Array sources
- Extract shared validation logic into lib/validate.js, accepting a query
  function adapter so both better-sqlite3 and sqlite-wasm can use it
- Simplify lib/schema.js to a thin wrapper over shared validate
- Add conditional exports in package.json ("browser" → index.browser.js)
- Migrate tests from node:test to vitest with both node and browser projects
- Add browser tests via @vitest/browser with Playwright (chromium)
- Add GitHub Actions CI workflow for node (v20, v22) and browser tests
- Remove Buffer dependency from shared code for browser compatibility

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
@socket-security
Copy link

socket-security bot commented Mar 13, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​vitest/​browser@​4.1.0961007999100
Addedvitest@​4.1.0961007999100
Updated@​types/​node@​20.17.6 ⏵ 20.19.37100 +110081 +196100
Addedvite@​7.3.1961008299100
Added@​vitest/​browser-playwright@​4.1.0991008699100
Added@​sqlite.org/​sqlite-wasm@​3.51.2-build79610010095100
Addedplaywright@​1.58.210010010099100

View full report

@socket-security

This comment has been minimized.

claude added 11 commits March 13, 2026 22:14
- Remove lib/schema.js, inline the one-line adapter into index.js
- Create test/shared.js with registerSharedTests() that both node and
  browser test files use, ensuring feature parity between environments
- Both environments now run identical tests for getTile, iterator,
  metadata, error handling, and close behavior
- Add platform-specific browser instances: webkit on macOS, firefox
  locally (skipped in CI due to flakiness)
- Update CI workflow following zip-writer pattern: Playwright browser
  caching, npm caching via setup-node, ubuntu + macOS matrix

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
Add comprehensive tests for validate.js covering:
- Missing tiles/metadata tables and wrong column types
- Missing name metadata, PBF without json metadata
- Invalid minzoom/maxzoom/bounds/center string values
- Explicit vs derived minzoom, maxzoom, bounds, center
- JSON metadata merging precedence
- Format detection, scheme override, center zoom calculation

Fix bug where explicit minzoom=0 was ignored because `!0 === true`.
Changed `!metadata['minzoom']` to `metadata['minzoom'] == null`.

Also add test for node constructor accepting a Database instance.

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
- actions/checkout@v4 → v6
- actions/setup-node@v4 → v6 (caching enabled by default)
- actions/cache@v4 → v5
- Add Node 24 to test matrix
- Remove branch filter from pull_request trigger
- Remove claude/** from push trigger

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
Use vite@7 instead of v8 to avoid socket security warnings.
Remove legacy test/test.js (node:test based) now replaced by
vitest-based test/test.test.js.

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
Vite must remain a devDependency (pinned to v7) so npm ci installs
it in CI. The previous commit incorrectly removed it entirely.

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
Vite v7 pre-bundles @sqlite.org/sqlite-wasm by default, which breaks
the wasm file's import.meta.url resolution. Add optimizeDeps.exclude
and COOP/COEP headers to match mbtiles-viewer's working config.

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
Cover both Node.js and browser entry points, document the bundler
configuration needed for @sqlite.org/sqlite-wasm (optimizeDeps.exclude),
and expand API documentation with metadata properties, coordinate
system notes, and platform differences.

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
- Return Uint8Array from Node entry point instead of Buffer for
  consistent API across platforms
- Add private symbol guard to browser MBTiles constructor to enforce
  use of MBTiles.open()
- Update README to reflect unified Uint8Array type

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
Create a Uint8Array view over the Buffer's underlying ArrayBuffer
rather than copying the bytes, for zero-copy performance.

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
Drop the Node-only readableStream() method. Consumers can use
the sync iterator directly or wrap with Readable.from() if needed.

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
Rename test.test.js → node.test.js and test.browser.test.js →
browser.test.js for clarity. Update vitest config globs to match.

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
@gmaclennan gmaclennan changed the title Add browser support for MBTiles with SQLite WASM feat!: Add browser support for MBTiles with SQLite WASM Mar 14, 2026
claude and others added 9 commits March 14, 2026 00:27
When running in a Web Worker with OPFS support, MBTiles.open() now
copies the file to the Origin Private File System and opens it with
OpfsDb, avoiding loading the entire database into wasm memory. Falls
back to sqlite3_deserialize when OPFS is unavailable.

close() is now async in the browser to handle OPFS file cleanup.

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
Add a browser test that spawns a Web Worker, opens an MBTiles file
via OPFS (OpfsDb), reads a tile, and verifies the data. This exercises
the OPFS code path that the main-thread tests cannot.

Move better-sqlite3 and @sqlite.org/sqlite-wasm to optionalDependencies
so browser-only consumers don't need a C++ compiler for better-sqlite3,
and Node-only consumers don't download the wasm file unnecessarily.

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
MBTiles.open() now accepts a string (OPFS file path) to open
directly with OpfsDb, leaving file management to the consumer.
Non-string sources (File, ArrayBuffer, Uint8Array) still use
sqlite3_deserialize in-memory.

close() is sync again since there's no OPFS cleanup to do.

The OPFS worker test is skipped when crossOriginIsolated is false
(requires COOP/COEP headers for SharedArrayBuffer).

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
…lution

The package was in optionalDependencies which caused vite to fail resolving
the import in CI. It's a required runtime dependency for the browser entry
point, so it belongs in dependencies. Only better-sqlite3 needs to be
optional (native compilation, not used in browsers).

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
… plugin

- Add configureServer plugin to set COOP/COEP headers for cross-origin isolation
- Add --enable-features=SharedArrayBuffer Chromium launch arg to ensure
  SharedArrayBuffer is available (vitest browser mode ignores server.headers)
- Remove skipIf condition from OPFS worker test so it always runs

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
- Add server.headers for COOP/COEP alongside configureServer middleware
  to ensure cross-origin isolation headers reach all browsers
- Clean up stale OPFS files before writing to avoid lock conflicts
- Add retry logic for createSyncAccessHandle() to handle transient
  WebKit OPFS errors

https://claude.ai/code/session_01MQtSRZzGmAprLcBNK22mio
@gmaclennan gmaclennan changed the title feat!: Add browser support for MBTiles with SQLite WASM feat!: add browser support with unified MBTiles.open() API Mar 17, 2026
@gmaclennan gmaclennan merged commit 38e26b8 into main Mar 17, 2026
8 checks passed
@gmaclennan gmaclennan deleted the claude/add-browser-support-tiCwf branch March 17, 2026 11:39
gmaclennan pushed a commit that referenced this pull request Mar 17, 2026
## Optic Release Automation

This **draft** PR is opened by Github action
[optic-release-automation-action](https://github.com/nearform-actions/optic-release-automation-action).

A new **draft** GitHub release
[v2.0.0](https://github.com/digidem/mbtiles-reader/releases/tag/untagged-b38177ed612fac3ea7fc)
has been created.

Release author: @gmaclennan

#### If you want to go ahead with the release, please merge this PR.
When you merge:

- The GitHub release will be published

- No npm package will be published as configured



- No major or minor tags will be updated as configured


#### If you close the PR

- The new draft release will be deleted and nothing will change

## What's Changed
* feat!: add browser support with unified MBTiles.open() API by
@gmaclennan in #1
* chore: add release workflow by @gmaclennan in
#2
* chore: update deps by @gmaclennan in
#3

## New Contributors
* @gmaclennan made their first contribution in
#1

**Full Changelog**:
v1.0.0...v2.0.0

<!--

<release-meta>{"id":297911516,"version":"v2.0.0","npmTag":"latest","opticUrl":"https://optic-zf3votdk5a-ew.a.run.app/api/generate/"}</release-meta>
-->

Co-authored-by: gmaclennan <actions@users.noreply.github.com>
gmaclennan pushed a commit that referenced this pull request Mar 17, 2026
## Optic Release Automation

This **draft** PR is opened by Github action
[optic-release-automation-action](https://github.com/nearform-actions/optic-release-automation-action).

A new **draft** GitHub release
[v2.0.1](https://github.com/digidem/mbtiles-reader/releases/tag/untagged-29d8d7326e05e2099730)
has been created.

Release author: @gmaclennan

#### If you want to go ahead with the release, please merge this PR.
When you merge:

- The GitHub release will be published

- No npm package will be published as configured



- No major or minor tags will be updated as configured


#### If you close the PR

- The new draft release will be deleted and nothing will change

## What's Changed
* feat!: add browser support with unified MBTiles.open() API by
@gmaclennan in #1
* chore: add release workflow by @gmaclennan in
#2
* chore: update deps by @gmaclennan in
#3
* [OPTIC-RELEASE-AUTOMATION] release/v2.0.0 by
@optic-release-automation[bot] in
#4
* chore: add repository to package.json by @gmaclennan in
#5

## New Contributors
* @gmaclennan made their first contribution in
#1
* @optic-release-automation[bot] made their first contribution in
#4

**Full Changelog**:
v1.0.0...v2.0.1

<!--

<release-meta>{"id":297980268,"version":"v2.0.1","npmTag":"latest","opticUrl":"https://optic-zf3votdk5a-ew.a.run.app/api/generate/"}</release-meta>
-->

Co-authored-by: gmaclennan <actions@users.noreply.github.com>
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