Skip to content

WASI support: build SQLiteKit without SwiftNIO or AsyncKit - #4

Draft
scottmarchant wants to merge 3 commits into
base/vapor-mainfrom
feat/wasi-nio-free
Draft

WASI support: build SQLiteKit without SwiftNIO or AsyncKit#4
scottmarchant wants to merge 3 commits into
base/vapor-mainfrom
feat/wasi-nio-free

Conversation

@scottmarchant

Copy link
Copy Markdown
Collaborator

Builds SQLiteKit on wasm32-unknown-wasip1 without SwiftNIO or AsyncKit. 8 files, +50/−4, no Embedded Swift content. The Embedded work is stacked on top in a separate PR.

This should land last of the three — after the sqlite-nio and sql-kit WASI PRs — and then its version floors get bumped so the WASI path resolves without local wiring. Its native build and test suite work against the released sqlite-nio 1.9.0+ / sql-kit 3.29.3+ today, so ordering only matters for the WASI lane.

Why the base branch is base/vapor-main

base/vapor-main is an exact snapshot of vapor/sqlite-kit@main at 492c46e ("Add permissions to API docs workflow", 2026-06-18). It deliberately excludes this fork's one extra commit, 8374d3a "feat: Enable WebAssembly compilation (#3)" — that's the NIOAsyncRuntime approach, which depends on PassiveLogic/nio-async-runtime and gates on os(WASI). This PR supersedes it. That path is dead upstream since apple/swift-nio#3487 was closed unmerged, and a pre-1.0 dependency isn't acceptable to vapor anyway.

Rebasing this branch onto real vapor/main is a no-op.

What changed

feat: make the SwiftNIO/AsyncKit surface conditional#if canImport(...) around the parts that need those modules:

  • SQLiteConnectionSource is whole-file gated on canImport(AsyncKit), along with the AsyncKit re-export. There is no connection pool on WASI. That's a real functional loss and I want to be upfront about it — callers use sqlite-nio's concrete async SQLiteConnection directly. It's tolerable because WASI preview 1 is single-threaded, so a pool buys nothing there.
  • The SQLDatabase wrapper's eventLoop, its EventLoopFuture execute overload, and withSession(_:) gate on canImport(NIOCore), matching what SQLKit does to the corresponding protocol requirements.
  • SQLiteDataDecoder's JSON fallback reads a [UInt8] blob, matching SQLiteData's representation where SwiftNIO is absent.

build: elide SwiftNIO and AsyncKit on WASI — 13 lines, applied identically to both Package.swift (tools 5.8) and Package@swift-5.9.swift, so 5.9+ toolchains aren't silently downgraded to the 5.8 base. .wasi is available in PackageDescription from tools 5.6, so the 5.8 manifest is fine.

ci: build for WebAssembly in CIvapor/ci's reusable workflow already has the lane; sqlite-kit had no with: block at all.

On AsyncKit specifically

Upstream vapor/async-kit does not compile for WASI, so this elides it rather than building it. That means the async-kit wasm PR (vapor#111) is not a prerequisite for any of this — a genuine simplification, and useful given that PR is blocked on dependency policy. If async-kit ever gains WASI support the condition can simply be relaxed and the pool comes back.

The two things worth arguing about

nonWASIPlatforms is a whitelist, not an exclusion — same as the sibling PRs. SwiftPM has no "all except" form. Upstream apple/swift-nio uses the identical idiom (let historicalNIOPosixDependencyRequired: [Platform], from "Add support for WASILibc (#2671)"), as does apple/swift-crypto. Doc comment pinned to the tools-version sits above the list. An unconditional change isn't possible; neither dependency builds for WASI.

canImport(...) is build-wide, not target-scoped. Correct here because all three packages gate the same modules on the same condition. All these gates are in Sources/, never in the manifest.

Verification

result
native swift build + swift test (against released siblings) green, 9/9
native with CI's --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable green; the two Sendable warnings it emits are pre-existing on base/vapor-main
swift package diagnose-api-breaking-changes base/vapor-main No breaking changes detected in SQLiteKit
wasi --swift-sdk swift-6.3.1-RELEASE_wasm, same CI flags green, with the sibling WASI branches wired via swift package edit --path (the committed manifests keep upstream vapor/... URLs)
SwiftNIO / AsyncKit in the wasi build zeroNIOCore, NIOPosix, NIOEmbedded, NIOFoundationCompat, NIOConcurrencyHelpers, NIO, AsyncKit all produce 0 object files; nm over the SQLiteKit/SQLiteNIO/SQLKit wasm objects finds 0 NIO/EventLoop/ByteBuffer/AsyncKit symbols

The unit-tests / wasm check won't run while this is a draft (vapor/ci guards it with if: !draft). It will also fail until the sibling PRs land and the version floors are bumped, since the released sqlite-nio/sql-kit have no WASI support — that's the sequencing note at the top.

Constraints held

No SwiftPM traits, no versioned manifest added (the existing Package@swift-5.9.swift is preserved and gets the same hunk), no tools-version bump, no new dependency, no fork URLs. Manifest count unchanged at 2.

scottmarchant and others added 3 commits July 27, 2026 17:08
Gate the parts of SQLiteKit that require SwiftNIO or AsyncKit with
`#if canImport(...)` so the package still builds on platforms where they are
unavailable (the next commit elides them on WASI). Keying on `canImport`
rather than on a platform keeps these in step with the gates sqlite-nio and
sql-kit apply to the same dependencies.

When the modules are present the gates are unconditionally true and nothing
changes. When they are absent:

- `SQLiteConnectionSource` — a `ConnectionPoolSource` over AsyncKit and
  `NIOThreadPool` — drops out entirely, along with the `AsyncKit` re-export.
  Callers use sqlite-nio's concrete async `SQLiteConnection` directly; there
  is no connection pool on those platforms.
- The `SQLDatabase` wrapper's `eventLoop`, its
  `execute(sql:_:) -> EventLoopFuture<Void>` overload, and `withSession(_:)`
  (which rides sqlite-nio's protocol-level `withConnection`) drop out. SQLKit
  removes the first two as protocol requirements on the same platforms and
  supplies a default for the third.
- `SQLiteDataDecoder`'s JSON fallback reads a `[UInt8]` blob rather than a
  `ByteBuffer`, matching `SQLiteData`'s representation there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neither SwiftNIO nor AsyncKit builds for wasm32-unknown-wasip1, so
SQLiteKit currently cannot be configured for that platform at all. Gate
the NIOFoundationCompat and AsyncKit products on
`.when(platforms: nonWASIPlatforms)` in both manifests; because target
dependency conditions are evaluated per platform, the resolved dependency
set is unchanged everywhere else, and on WASI SQLiteKit compiles the
pool-free, async-only surface guarded in the previous commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`vapor/ci`'s reusable unit-test workflow already knows how to build a package
for `wasm32-unknown-wasip1`; sqlite-kit simply had not opted in. Turn the lane
on so the SwiftNIO-free path cannot regress unnoticed.

sql-kit already passes `with_wasm: true` to the same workflow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.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.

1 participant