Skip to content

Commit 6b3affd

Browse files
committed
sea-napi-binding: scaffold native/sea/ crate with version() smoke test
Creates the napi-rs binding skeleton: Cargo.toml + lib.rs + module stubs for database/connection/statement/result/error/logger. Captures napi-rs tokio Handle via OnceCell in runtime.rs. Single working #[napi] fn version() proves the binding loads + executes end-to-end in Node. Depends on krn-async-public-api branch (path dep on kernel). Round 2 will add open/execute/fetch methods.
1 parent f05f8a9 commit 6b3affd

17 files changed

Lines changed: 928 additions & 0 deletions

lib/sea/SeaNativeLoader.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) 2026 Databricks, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/**
16+
* Loader for the SEA (Statement Execution API) native binding.
17+
*
18+
* Round 1b: minimal pass-through to the napi-rs auto-generated
19+
* `index.js` shim in `native/sea/`. The shim itself picks the right
20+
* per-platform `.node` artifact (linux-x64-gnu today; more triples in
21+
* the bundling feature).
22+
*
23+
* Round 2+ will extend this with: lazy require to defer the `.node`
24+
* load until the first SEA call, structured load-error diagnostics
25+
* (which platform/arch was attempted, whether the package was
26+
* installed at all), and a JS-side `DBSQLLogger` install path that
27+
* forwards to the binding's `installLogger()` once that surface lands.
28+
*/
29+
30+
// The path is relative to this file at runtime (`dist/sea/SeaNativeLoader.js`)
31+
// resolving to `dist/sea/../../native/sea/index.js` once `tsc` has emitted
32+
// to `dist/`. We use a require-time path resolution because the napi
33+
// shim is plain CommonJS and not part of the TS source tree.
34+
//
35+
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
36+
const native = require('../../native/sea/index.js');
37+
38+
export interface SeaNativeBinding {
39+
/** Returns the native crate version (smoke test for the binding's load path). */
40+
version(): string;
41+
}
42+
43+
/**
44+
* Returns the loaded native binding. Throws if the platform-specific
45+
* `.node` artifact cannot be found (napi-rs's auto-generated shim
46+
* surfaces a descriptive error in that case).
47+
*/
48+
export function getSeaNative(): SeaNativeBinding {
49+
return native as SeaNativeBinding;
50+
}
51+
52+
/**
53+
* Convenience accessor for the smoke-test path. Equivalent to
54+
* `getSeaNative().version()` but reads more naturally in tests and
55+
* REPLs.
56+
*/
57+
export function version(): string {
58+
return getSeaNative().version();
59+
}

native/sea/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Rust build artifacts
2+
target/
3+
Cargo.lock
4+
5+
# Platform-specific `.node` binaries are produced per-platform by the
6+
# bundling feature; not committed.
7+
*.node

native/sea/Cargo.toml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright (c) 2026 Databricks, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
[package]
16+
name = "databricks-sea-native"
17+
version = "0.1.0"
18+
edition = "2021"
19+
authors = ["Databricks"]
20+
license = "Apache-2.0"
21+
description = "Databricks SQL Node.js SEA native binding (napi-rs)"
22+
publish = false
23+
24+
[lib]
25+
crate-type = ["cdylib"]
26+
27+
[dependencies]
28+
# napi-rs v2 line; `napi6` enables N-API 6 surface, `async` enables the
29+
# `#[napi] async fn` glue that drives futures on napi-rs's tokio runtime.
30+
napi = { version = "2", default-features = false, features = ["napi6", "async"] }
31+
napi-derive = "2"
32+
33+
# Kernel — path dep on the async-public-api branch worktree. Once the
34+
# kernel is published this becomes a version dep.
35+
databricks-sql-kernel = { path = "../../../../databricks-sql-kernel-sea-WT/async-public-api" }
36+
37+
# Tokio is a transitive dep via the kernel and via napi's `async` feature;
38+
# declared explicitly so we can name `tokio::runtime::Handle` directly.
39+
tokio = { version = "1", default-features = false, features = ["rt"] }
40+
41+
# Lazy `OnceCell` for the captured tokio Handle.
42+
once_cell = "1"
43+
44+
# Tracing for kernel + binding diagnostics. The real subscriber is wired
45+
# in Round 3 via the ThreadsafeFunction logger bridge.
46+
tracing = "0.1"
47+
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
48+
49+
[build-dependencies]
50+
napi-build = "2"
51+
52+
[profile.release]
53+
lto = true
54+
strip = "symbols"

native/sea/build.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) 2026 Databricks, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
fn main() {
16+
napi_build::setup();
17+
}

native/sea/index.d.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* tslint:disable */
2+
/* eslint-disable */
3+
4+
/* auto-generated by NAPI-RS */
5+
6+
/**
7+
* JS-visible connection options. Empty in Round 1b; Round 2 may add
8+
* per-connection scope fields (catalog, schema, session config map).
9+
*/
10+
export interface ConnectionOptions {
11+
12+
}
13+
/**
14+
* JS-visible constructor options. Round 2 will populate this with
15+
* real fields (host, warehouseId, auth, …); for the scaffold it is
16+
* intentionally empty so the JS smoke test can call `new Database({})`
17+
* without TypeScript complaining about unknown properties.
18+
*/
19+
export interface DatabaseOptions {
20+
/**
21+
* Workspace host URL (e.g. `https://workspace.databricks.com`).
22+
* Optional in Round 1b; Round 2 makes it required.
23+
*/
24+
host?: string
25+
/** Warehouse id. Optional in Round 1b; Round 2 makes it required. */
26+
warehouseId?: string
27+
}
28+
/**
29+
* Returns the native binding's crate version (`CARGO_PKG_VERSION`).
30+
*
31+
* Acts as the round-1b smoke test: a JS `require()` of the `.node`
32+
* artifact that successfully calls `version()` proves the binding's
33+
* build + load + dispatch path is wired correctly.
34+
*/
35+
export declare function version(): string
36+
/** Opaque connection handle. Round 1b: marker only; no kernel state. */
37+
export declare class Connection {
38+
/**
39+
* Construct a new connection handle. Round 1b is a no-op shell;
40+
* Round 2 will wire it to `Database`'s `Session` (likely via an
41+
* async `Database::connect()` factory rather than a JS-side
42+
* `new Connection()`).
43+
*/
44+
constructor(options: ConnectionOptions)
45+
}
46+
/**
47+
* Opaque database handle on the JS side.
48+
*
49+
* Holds `Option<Session>` so `close()` (Round 2) can `.take()` the
50+
* session out and `.await` an async close, leaving `inner = None`.
51+
* The `Drop` impl checks `inner` to decide whether to schedule a
52+
* fire-and-forget close on the captured tokio runtime.
53+
*/
54+
export declare class Database {
55+
/**
56+
* Construct a new database handle. Round 1b: the options are
57+
* stashed for diagnostic purposes only — no network call.
58+
*/
59+
constructor(options: DatabaseOptions)
60+
}

0 commit comments

Comments
 (0)