database-client is the transport-facing client for the canonical DatabaseWire protocol.
Database semantics remain owned by the database runtime.
| Product | Runtime | Responsibility |
|---|---|---|
DatabaseClient |
Swift 6.4+ application and Embedded environments | Typed operation calls, request correlation, bounded DatabaseWire encode/decode |
DatabaseClientJavaScript |
JavaScript-hosted WASI runtime | Promise and Uint8Array transport |
DatabaseClientHTTP |
Apple and Linux | Authenticated URLSession transport |
DatabaseClientWebSocket |
Apple and Linux | Persistent WebSocket transport with request-ID correlation |
DatabaseClientFramedStream |
Native, WASI, and Embedded | Ordered UInt32 length-prefixed transport over an injected byte-stream connection |
The dependency boundary is intentionally one-way:
DatabaseClient
│
├── DatabaseClientJavaScript
├── DatabaseClientHTTP
├── DatabaseClientWebSocket
└── DatabaseClientFramedStream
DatabaseClient is the Foundation-free core of the Embedded dependency graph. It
depends directly on DatabaseWire and the Foundation-free primitives in
DatabaseTypes. Query and operation payload contracts remain owned by
database-kit.
DatabaseClientJavaScript adds only the JavaScript promise boundary needed by
hosted Embedded WebAssembly. Foundation and URLSession remain isolated to the
network adapter products.
ByteString is the owned byte type across the client and wire layers. Request and response
values are passed by ownership sharing, payload decoding returns constant-time slices,
and exact-size encoders transfer their final array storage through copy-on-write.
Copies exist only where a foreign runtime requires a different owner:
| Boundary | Request | Response | Reason |
|---|---|---|---|
DatabaseTransport |
0 | 0 | Both sides exchange ByteString owners |
| JavaScriptKit | 1 | 1 | JavaScript Uint8Array and Swift memory have independent lifetimes |
| URLSession HTTP | 1 | 0 or 1 | URLRequest requires Data; one response fragment is retained, while multiple fragments are assembled once |
| URLSession WebSocket | 1 | 0 | The received Foundation Data owner is retained by ByteString |
| Framed stream | 0 | 0 | Prefix and payload are separate owned writes; the injected connection returns an owned response range |
Adapters retain a compatible immutable foreign owner when possible. When a copy
is required, they copy directly into the final owner and do not create an
intermediate [UInt8] payload.
Every operation statically associates its request and response types.
let transport = JavaScriptDatabaseTransport()
let client = DatabaseClient(transport: transport)
let capabilities = try await client.execute(
DatabaseOperationCatalog.capabilitiesDescribe,
request: EmptyOperationPayload(),
metadata: OperationRequestMetadata(traceID: traceID)
)The default client is target-free because the default runtime has one database
execution root. DatabaseClient.execute sends that operation directly; it does
not construct a synthetic .database target.
The non-default MultipleBases trait adds the target-bound
DatabaseSessionClient surface. In that graph, use client.database for the
control domain, client.base(baseID) for one Base, and
client.composition(compositionID) for a read-only Composition. Only that
trait-enabled graph exposes DatabaseOperationTarget.
let company = client.base(try Base.ID("company-a"))
let rows = try await company.execute(
DatabaseOperationCatalog.queryExecute,
request: request
)DatabaseClient allocates a UInt64 request ID, builds the canonical envelope,
sends it through DatabaseTransport, validates response correlation, and
decodes the typed response. A remote failure is returned as
RemoteOperationError through DatabaseCallError.remote.
For split-phase runtimes, DatabaseCall<Request, Response> retains the concrete
operation value and exposes encoding and response decoding without owning a
transport.
The same state-isolation contract applies to Native, WASM, and Embedded builds. Enabling target selection never removes synchronization.
| Logical state | Storage | Read and mutation entry point | External work |
|---|---|---|---|
| Core request identifiers | Atomic<UInt64> |
Compare-and-exchange reservation | Encoding and transport calls occur after reservation |
| JavaScript promise completion | Mutex<State> |
withLock completion transition |
Promise handlers, timers, tasks, and continuation resumption occur outside the lock |
| HTTP request lifecycle | Mutex<State> |
withLock lifecycle transition |
URLSession calls, task cancellation, and continuation resumption occur outside the lock |
| WebSocket connection and pending requests | actor isolation |
Actor methods | Frame I/O suspends without a mutex-held critical section |
| Framed-stream request gate and shutdown | actor isolation |
Actor methods | Exactly one request owns the ordered stream while connection I/O remains outside non-suspending critical sections |
Identifier spaces never wrap and reuse a live generation. Exhaustion is reported as a typed failure.
JavaScriptDatabaseTransport calls a global async request entrypoint named
__databaseExecute by default. The entrypoint accepts and returns Uint8Array and must
preserve bytes exactly.
The returned value may be an offset view. The transport copies exactly the view's
byteOffset..<byteOffset + byteLength range once into Swift-owned storage; it never widens
the response to the complete backing ArrayBuffer. The bridge reads intrinsic
TypedArray metadata rather than overridable JavaScript properties and rejects
invalid, detached, oversized, or changing views as typed transport failures.
Synchronous exceptions from the entrypoint are returned as
database_entrypoint_threw; Promise rejection is returned as
database_entrypoint_rejected. Timeout and task cancellation detach both
Promise handlers before completing the Swift continuation, so a late settlement
cannot decode bytes or retain the pending request.
globalThis.__databaseExecute = async (request) => {
const id = env.DATABASE.idFromName("main")
const stub = env.DATABASE.get(id)
return stub.execute(request)
}The JavaScript boundary does not parse queries, schemas, indexes, or transactions.
HTTPDatabaseTransport posts application/octet-stream and applies the configured
authorization and database scope headers. It rejects oversized Foundation responses before
retaining a single response fragment or assembling fragmented response bytes once.
let configuration = try HTTPDatabaseConfiguration(
endpoint: endpoint,
accessToken: accessToken,
databaseID: "calendar"
)
let client = DatabaseClient(
transport: HTTPDatabaseTransport(configuration: configuration)
)WebSocketDatabaseTransport keeps one WebSocket connection, correlates responses by
the envelope request ID, and supports concurrent callers. Call shutdown() when the owning
application stops. Shutdown rejects new sends, cancels the receive loop and all
per-request send/timeout tasks, closes the connection, and does not return until
those child tasks have completed. Concurrent shutdown callers join the same
completion boundary.
FramedStreamDatabaseTransport adapts an injected
DatabaseFramedStreamConnection to DatabaseTransport. Each direction uses a
four-byte unsigned big-endian payload length followed by exactly one
DatabaseWire envelope. Zero-length, oversized, truncated, malformed, or
request-ID-mismatched responses close the connection and remain typed failures.
The transport serializes requests because one byte stream has one response
order. It does not own a process, file descriptor, socket, or storage backend.
The embedding application owns that concrete connection and must implement
shutdown() so every pending read and write is unblocked before it returns.
Frame limits use fixed-width comparisons and remain valid on 32-bit WASI.
Pin the compiler and SDK to the same Swift snapshot. For the currently validated snapshot:
/Users/1amageek/Library/Developer/Toolchains/swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-07-23-a.xctoolchain/usr/bin/swift \
build \
--swift-sdk swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-07-23-a_wasm-embedded \
--product DatabaseClientThe same command with --product DatabaseClientJavaScript verifies the
JavaScript transport inside a true Embedded WebAssembly build.
The release gate uses the same source revision for all checks:
| Target | Required result |
|---|---|
| Native macOS, standard | 48 passed; zero failures, skips, expected failures, or runtime warnings |
Native macOS, MultipleBases |
49 passed with DATABASE_CLIENT_EXPECTED_TEST_COUNT=49; zero failures, skips, expected failures, or runtime warnings |
| JavaScript/WASI on Node | 27 passed; zero failures or skips; normal Swift Testing process termination |
Embedded WASM DatabaseClient |
Product compiles and links with the pinned Embedded SDK |
Package.swift resolves database-kit, database-types, and JavaScriptKit from
their published release tags. A checkout of another database repository next
to this package is not part of the build contract.
Licensed under the MIT License.