diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..0e720b4d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,7 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/Plugins/PackageToJS/Templates/instantiate.js b/Plugins/PackageToJS/Templates/instantiate.js
index d786c31e..a239a79c 100644
--- a/Plugins/PackageToJS/Templates/instantiate.js
+++ b/Plugins/PackageToJS/Templates/instantiate.js
@@ -1,5 +1,4 @@
 // @ts-check
-// @ts-ignore
 import { SwiftRuntime } from "./runtime.js"
 
 export const MODULE_PATH = "@PACKAGE_TO_JS_MODULE_PATH@";
diff --git a/Plugins/PackageToJS/Templates/platforms/browser.js b/Plugins/PackageToJS/Templates/platforms/browser.js
index 672c274d..b1e469fb 100644
--- a/Plugins/PackageToJS/Templates/platforms/browser.js
+++ b/Plugins/PackageToJS/Templates/platforms/browser.js
@@ -2,8 +2,10 @@
 import { MODULE_PATH /* #if USE_SHARED_MEMORY */, MEMORY_TYPE /* #endif */} from "../instantiate.js"
 /* #if IS_WASI */
 /* #if USE_WASI_CDN */
+// @ts-ignore
 import { WASI, File, OpenFile, ConsoleStdout, PreopenDirectory } from 'https://cdn.jsdelivr.net/npm/@bjorn3/browser_wasi_shim@0.4.1/+esm';
 /* #else */
+// @ts-ignore
 import { WASI, File, OpenFile, ConsoleStdout, PreopenDirectory } from '@bjorn3/browser_wasi_shim';
 /* #endif */
 /* #endif */
diff --git a/Plugins/PackageToJS/Templates/runtime.d.ts b/Plugins/PackageToJS/Templates/runtime.d.ts
index aff6d1c8..98e1f1cc 100644
--- a/Plugins/PackageToJS/Templates/runtime.d.ts
+++ b/Plugins/PackageToJS/Templates/runtime.d.ts
@@ -18,58 +18,8 @@ declare class Memory {
     writeFloat64: (ptr: pointer, value: number) => void;
 }
 
-declare const enum Kind {
-    Boolean = 0,
-    String = 1,
-    Number = 2,
-    Object = 3,
-    Null = 4,
-    Undefined = 5,
-    Function = 6,
-    Symbol = 7,
-    BigInt = 8
-}
-
 type ref = number;
 type pointer = number;
-type bool = number;
-type JavaScriptValueKind = number;
-type JavaScriptValueKindAndFlags = number;
-interface ImportedFunctions {
-    swjs_set_prop(ref: number, name: number, kind: Kind, payload1: number, payload2: number): void;
-    swjs_get_prop(ref: number, name: number, payload1_ptr: pointer, payload2_ptr: pointer): JavaScriptValueKind;
-    swjs_set_subscript(ref: number, index: number, kind: Kind, payload1: number, payload2: number): void;
-    swjs_get_subscript(ref: number, index: number, payload1_ptr: pointer, payload2_ptr: pointer): JavaScriptValueKind;
-    swjs_encode_string(ref: number, bytes_ptr_result: pointer): number;
-    swjs_decode_string(bytes_ptr: pointer, length: number): number;
-    swjs_load_string(ref: number, buffer: pointer): void;
-    swjs_call_function(ref: number, argv: pointer, argc: number, payload1_ptr: pointer, payload2_ptr: pointer): JavaScriptValueKindAndFlags;
-    swjs_call_function_no_catch(ref: number, argv: pointer, argc: number, payload1_ptr: pointer, payload2_ptr: pointer): JavaScriptValueKindAndFlags;
-    swjs_call_function_with_this(obj_ref: ref, func_ref: ref, argv: pointer, argc: number, payload1_ptr: pointer, payload2_ptr: pointer): JavaScriptValueKindAndFlags;
-    swjs_call_function_with_this_no_catch(obj_ref: ref, func_ref: ref, argv: pointer, argc: number, payload1_ptr: pointer, payload2_ptr: pointer): JavaScriptValueKindAndFlags;
-    swjs_call_new(ref: number, argv: pointer, argc: number): number;
-    swjs_call_throwing_new(ref: number, argv: pointer, argc: number, exception_kind_ptr: pointer, exception_payload1_ptr: pointer, exception_payload2_ptr: pointer): number;
-    swjs_instanceof(obj_ref: ref, constructor_ref: ref): boolean;
-    swjs_value_equals(lhs_ref: ref, rhs_ref: ref): boolean;
-    swjs_create_function(host_func_id: number, line: number, file: ref): number;
-    swjs_create_typed_array(constructor_ref: ref, elementsPtr: pointer, length: number): number;
-    swjs_create_object(): number;
-    swjs_load_typed_array(ref: ref, buffer: pointer): void;
-    swjs_release(ref: number): void;
-    swjs_release_remote(tid: number, ref: number): void;
-    swjs_i64_to_bigint(value: bigint, signed: bool): ref;
-    swjs_bigint_to_i64(ref: ref, signed: bool): bigint;
-    swjs_i64_to_bigint_slow(lower: number, upper: number, signed: bool): ref;
-    swjs_unsafe_event_loop_yield: () => void;
-    swjs_send_job_to_main_thread: (unowned_job: number) => void;
-    swjs_listen_message_from_main_thread: () => void;
-    swjs_wake_up_worker_thread: (tid: number) => void;
-    swjs_listen_message_from_worker_thread: (tid: number) => void;
-    swjs_terminate_worker_thread: (tid: number) => void;
-    swjs_get_worker_thread_id: () => number;
-    swjs_request_sending_object: (sending_object: ref, transferring_objects: pointer, transferring_objects_count: number, object_source_tid: number, sending_context: pointer) => void;
-    swjs_request_sending_objects: (sending_objects: pointer, sending_objects_count: number, transferring_objects: pointer, transferring_objects_count: number, object_source_tid: number, sending_context: pointer) => void;
-}
 
 /**
  * A thread channel is a set of functions that are used to communicate between
@@ -254,11 +204,11 @@ declare class SwiftRuntime {
     private get closureDeallocator();
     private callHostFunction;
     /** @deprecated Use `wasmImports` instead */
-    importObjects: () => ImportedFunctions;
-    get wasmImports(): ImportedFunctions;
+    importObjects: () => WebAssembly.ModuleImports;
+    get wasmImports(): WebAssembly.ModuleImports;
     private postMessageToMainThread;
     private postMessageToWorkerThread;
 }
 
 export { SwiftRuntime };
-export type { SwiftRuntimeOptions };
+export type { SwiftRuntimeOptions, SwiftRuntimeThreadChannel };
diff --git a/Plugins/PackageToJS/Templates/tsconfig.json b/Plugins/PackageToJS/Templates/tsconfig.json
new file mode 100644
index 00000000..ac3a2b01
--- /dev/null
+++ b/Plugins/PackageToJS/Templates/tsconfig.json
@@ -0,0 +1,10 @@
+{
+    "compilerOptions": {
+        "module": "esnext",
+        "noEmit": true,
+        "allowJs": true,
+        "skipLibCheck": true,
+        "moduleResolution": "node"
+    },
+    "include": ["**/*.d.ts", "**/*.js"]
+}
diff --git a/Plugins/PackageToJS/Tests/TemplatesTests.swift b/Plugins/PackageToJS/Tests/TemplatesTests.swift
new file mode 100644
index 00000000..e885eb08
--- /dev/null
+++ b/Plugins/PackageToJS/Tests/TemplatesTests.swift
@@ -0,0 +1,20 @@
+import Testing
+import Foundation
+@testable import PackageToJS
+
+@Suite struct TemplatesTests {
+    static let templatesPath = URL(fileURLWithPath: #filePath)
+        .deletingLastPathComponent()
+        .deletingLastPathComponent()
+        .appendingPathComponent("Templates")
+
+    /// `npx tsc -p Templates/tsconfig.json`
+    @Test func tscCheck() throws {
+        let tsc = Process()
+        tsc.executableURL = try which("npx")
+        tsc.arguments = ["tsc", "-p", Self.templatesPath.appending(path: "tsconfig.json").path]
+        try tsc.run()
+        tsc.waitUntilExit()
+        #expect(tsc.terminationStatus == 0)
+    }
+}
diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts
index 83d588ad..ee12e5be 100644
--- a/Runtime/src/index.ts
+++ b/Runtime/src/index.ts
@@ -5,13 +5,13 @@ import {
     ref,
     pointer,
     TypedArray,
-    ImportedFunctions,
     MAIN_THREAD_TID,
 } from "./types.js";
 import * as JSValue from "./js-value.js";
 import { Memory } from "./memory.js";
 import { deserializeError, MainToWorkerMessage, MessageBroker, ResponseMessage, ITCInterface, serializeError, SwiftRuntimeThreadChannel, WorkerToMainMessage } from "./itc.js";
 import { decodeObjectRefs } from "./js-value.js";
+export { SwiftRuntimeThreadChannel };
 
 export type SwiftRuntimeOptions = {
     /**
@@ -181,7 +181,7 @@ export class SwiftRuntime {
     /** @deprecated Use `wasmImports` instead */
     importObjects = () => this.wasmImports;
 
-    get wasmImports(): ImportedFunctions {
+    get wasmImports(): WebAssembly.ModuleImports {
         let broker: MessageBroker | null = null;
         const getMessageBroker = (threadChannel: SwiftRuntimeThreadChannel) => {
             if (broker) return broker;
@@ -575,7 +575,7 @@ export class SwiftRuntime {
                     return BigInt.asIntN(64, object);
                 }
             },
-            swjs_i64_to_bigint_slow: (lower, upper, signed) => {
+            swjs_i64_to_bigint_slow: (lower: number, upper: number, signed: number) => {
                 const value =
                     BigInt.asUintN(32, BigInt(lower)) +
                     (BigInt.asUintN(32, BigInt(upper)) << BigInt(32));
@@ -586,7 +586,7 @@ export class SwiftRuntime {
             swjs_unsafe_event_loop_yield: () => {
                 throw new UnsafeEventLoopYield();
             },
-            swjs_send_job_to_main_thread: (unowned_job) => {
+            swjs_send_job_to_main_thread: (unowned_job: number) => {
                 this.postMessageToMainThread({ type: "job", data: unowned_job });
             },
             swjs_listen_message_from_main_thread: () => {
@@ -616,10 +616,10 @@ export class SwiftRuntime {
                     }
                 });
             },
-            swjs_wake_up_worker_thread: (tid) => {
+            swjs_wake_up_worker_thread: (tid: number) => {
                 this.postMessageToWorkerThread(tid, { type: "wake" });
             },
-            swjs_listen_message_from_worker_thread: (tid) => {
+            swjs_listen_message_from_worker_thread: (tid: number) => {
                 const threadChannel = this.options.threadChannel;
                 if (!(threadChannel && "listenMessageFromWorkerThread" in threadChannel)) {
                     throw new Error(
@@ -648,7 +648,7 @@ export class SwiftRuntime {
                     },
                 );
             },
-            swjs_terminate_worker_thread: (tid) => {
+            swjs_terminate_worker_thread: (tid: number) => {
                 const threadChannel = this.options.threadChannel;
                 if (threadChannel && "terminateWorkerThread" in threadChannel) {
                     threadChannel.terminateWorkerThread?.(tid);
diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts
index bb20bd95..a8872f80 100644
--- a/Runtime/src/types.ts
+++ b/Runtime/src/types.ts
@@ -1,5 +1,3 @@
-import * as JSValue from "./js-value.js";
-
 export type ref = number;
 export type pointer = number;
 export type bool = number;
@@ -26,114 +24,6 @@ export interface ExportedFunctions {
     swjs_receive_error(error: ref, context: number): void;
 }
 
-export interface ImportedFunctions {
-    swjs_set_prop(
-        ref: number,
-        name: number,
-        kind: JSValue.Kind,
-        payload1: number,
-        payload2: number
-    ): void;
-    swjs_get_prop(
-        ref: number,
-        name: number,
-        payload1_ptr: pointer,
-        payload2_ptr: pointer
-    ): JavaScriptValueKind;
-    swjs_set_subscript(
-        ref: number,
-        index: number,
-        kind: JSValue.Kind,
-        payload1: number,
-        payload2: number
-    ): void;
-    swjs_get_subscript(
-        ref: number,
-        index: number,
-        payload1_ptr: pointer,
-        payload2_ptr: pointer
-    ): JavaScriptValueKind;
-    swjs_encode_string(ref: number, bytes_ptr_result: pointer): number;
-    swjs_decode_string(bytes_ptr: pointer, length: number): number;
-    swjs_load_string(ref: number, buffer: pointer): void;
-    swjs_call_function(
-        ref: number,
-        argv: pointer,
-        argc: number,
-        payload1_ptr: pointer,
-        payload2_ptr: pointer
-    ): JavaScriptValueKindAndFlags;
-    swjs_call_function_no_catch(
-        ref: number,
-        argv: pointer,
-        argc: number,
-        payload1_ptr: pointer,
-        payload2_ptr: pointer
-    ): JavaScriptValueKindAndFlags;
-    swjs_call_function_with_this(
-        obj_ref: ref,
-        func_ref: ref,
-        argv: pointer,
-        argc: number,
-        payload1_ptr: pointer,
-        payload2_ptr: pointer
-    ): JavaScriptValueKindAndFlags;
-    swjs_call_function_with_this_no_catch(
-        obj_ref: ref,
-        func_ref: ref,
-        argv: pointer,
-        argc: number,
-        payload1_ptr: pointer,
-        payload2_ptr: pointer
-    ): JavaScriptValueKindAndFlags;
-    swjs_call_new(ref: number, argv: pointer, argc: number): number;
-    swjs_call_throwing_new(
-        ref: number,
-        argv: pointer,
-        argc: number,
-        exception_kind_ptr: pointer,
-        exception_payload1_ptr: pointer,
-        exception_payload2_ptr: pointer
-    ): number;
-    swjs_instanceof(obj_ref: ref, constructor_ref: ref): boolean;
-    swjs_value_equals(lhs_ref: ref, rhs_ref: ref): boolean;
-    swjs_create_function(host_func_id: number, line: number, file: ref): number;
-    swjs_create_typed_array(
-        constructor_ref: ref,
-        elementsPtr: pointer,
-        length: number
-    ): number;
-    swjs_create_object(): number;
-    swjs_load_typed_array(ref: ref, buffer: pointer): void;
-    swjs_release(ref: number): void;
-    swjs_release_remote(tid: number, ref: number): void;
-    swjs_i64_to_bigint(value: bigint, signed: bool): ref;
-    swjs_bigint_to_i64(ref: ref, signed: bool): bigint;
-    swjs_i64_to_bigint_slow(lower: number, upper: number, signed: bool): ref;
-    swjs_unsafe_event_loop_yield: () => void;
-    swjs_send_job_to_main_thread: (unowned_job: number) => void;
-    swjs_listen_message_from_main_thread: () => void;
-    swjs_wake_up_worker_thread: (tid: number) => void;
-    swjs_listen_message_from_worker_thread: (tid: number) => void;
-    swjs_terminate_worker_thread: (tid: number) => void;
-    swjs_get_worker_thread_id: () => number;
-    swjs_request_sending_object: (
-        sending_object: ref,
-        transferring_objects: pointer,
-        transferring_objects_count: number,
-        object_source_tid: number,
-        sending_context: pointer,
-    ) => void;
-    swjs_request_sending_objects: (
-        sending_objects: pointer,
-        sending_objects_count: number,
-        transferring_objects: pointer,
-        transferring_objects_count: number,
-        object_source_tid: number,
-        sending_context: pointer,
-    ) => void;
-}
-
 export const enum LibraryFeatures {
     WeakRefs = 1 << 0,
 }
diff --git a/package-lock.json b/package-lock.json
index ec5bd0a4..55981f7b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,9 @@
       "version": "0.0.0",
       "license": "MIT",
       "devDependencies": {
+        "@bjorn3/browser_wasi_shim": "^0.4.1",
         "@rollup/plugin-typescript": "^12.1.2",
+        "@types/node": "^22.13.14",
         "playwright": "^1.51.0",
         "prettier": "3.5.3",
         "rollup": "^4.37.0",
@@ -42,6 +44,12 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@bjorn3/browser_wasi_shim": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@bjorn3/browser_wasi_shim/-/browser_wasi_shim-0.4.1.tgz",
+      "integrity": "sha512-54kpBQX69TZ8I1zyDC8sziv/zPT1zoIadv3CmdIZNZ5WDF1houMjAzRZ3dwWvhXObiEBjOxXyS8Ja7vA0EfGEQ==",
+      "dev": true
+    },
     "node_modules/@jridgewell/sourcemap-codec": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
@@ -385,6 +393,15 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/node": {
+      "version": "22.13.14",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz",
+      "integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==",
+      "dev": true,
+      "dependencies": {
+        "undici-types": "~6.20.0"
+      }
+    },
     "node_modules/estree-walker": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
@@ -655,6 +672,12 @@
       "engines": {
         "node": ">=14.17"
       }
+    },
+    "node_modules/undici-types": {
+      "version": "6.20.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
+      "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+      "dev": true
     }
   }
 }
diff --git a/package.json b/package.json
index 0ff2d17a..867adb98 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,9 @@
   "author": "swiftwasm",
   "license": "MIT",
   "devDependencies": {
+    "@bjorn3/browser_wasi_shim": "^0.4.1",
     "@rollup/plugin-typescript": "^12.1.2",
+    "@types/node": "^22.13.14",
     "playwright": "^1.51.0",
     "prettier": "3.5.3",
     "rollup": "^4.37.0",