{"id":48110187,"url":"https://github.com/1amageek/swift-bun","last_synced_at":"2026-04-04T16:03:13.390Z","repository":{"id":348168456,"uuid":"1196792357","full_name":"1amageek/swift-bun","owner":"1amageek","description":"Bun-compatible JavaScript runtime for iOS/macOS via JavaScriptCore. Run Bun-built JS bundles natively on Apple platforms.","archived":false,"fork":false,"pushed_at":"2026-03-31T05:51:49.000Z","size":88,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-31T05:57:08.103Z","etag":null,"topics":["bun","ios","javascript","javascriptcore","macos","nodejs","runtime","swift","swift-package-manager"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1amageek.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-31T03:33:48.000Z","updated_at":"2026-03-31T05:51:53.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/1amageek/swift-bun","commit_stats":null,"previous_names":["1amageek/swift-bun"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/1amageek/swift-bun","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1amageek%2Fswift-bun","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1amageek%2Fswift-bun/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1amageek%2Fswift-bun/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1amageek%2Fswift-bun/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1amageek","download_url":"https://codeload.github.com/1amageek/swift-bun/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1amageek%2Fswift-bun/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31405227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bun","ios","javascript","javascriptcore","macos","nodejs","runtime","swift","swift-package-manager"],"created_at":"2026-04-04T16:03:11.862Z","updated_at":"2026-04-04T16:03:13.323Z","avatar_url":"https://github.com/1amageek.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# swift-bun\n\nA Swift package that runs Bun-built JavaScript bundles natively on iOS and macOS via JavaScriptCore.\n\nStatus: `0.1.0` is intended as an experimental compatibility release. The runtime is already useful for a real subset of Bun/Node workloads, but it does not claim full Bun or Node parity.\n\n## Overview\n\nswift-bun provides a Node.js/Bun compatibility layer on top of JavaScriptCore, enabling JavaScript code bundled with Bun (or esbuild) to execute on Apple platforms without embedding a full Node.js runtime.\n\n**What it does:**\n- Loads and executes ESM and CJS bundles built by Bun or esbuild\n- Resolves installed CommonJS packages from plain `node_modules` without rebundling\n- Polyfills Node.js built-in modules (`fs`, `path`, `crypto`, `http`, `stream`, etc.)\n- Bridges `fetch()` to `URLSession` for real HTTP networking\n- Provides `Bun.*` API shims (`Bun.file()`, `Bun.env`, `Bun.write()`, etc.)\n- Runs long-lived JS applications with a NIO EventLoop (timers, fetch, stdin)\n- Separates `process.stdout.write` (application data) from `console.log` (diagnostics)\n\n**What it doesn't do:**\n- Bundle or transpile JavaScript (use Bun or esbuild for that)\n- Provide `bun install`, `bun test`, or other CLI features\n- Fully emulate Node/Bun package resolution features such as `exports`, `imports`, `.mjs`, `.cjs`, or native addons\n\n## Platform Notes\n\nThe core runtime is mostly shared across iOS and macOS. `BunProcess`, the CommonJS loader, `fetch`, `fs`, `path`, `crypto`, `http`, `net`, and the client `WebSocket` bridge all use the same implementation model on both platforms.\n\nImportant caveat: the implementation does not generally fork into separate \"iOS version\" and \"macOS version\" of each API. In most cases the code path is the same, and the observable difference comes from host capabilities.\n\nWhat is actually different today:\n\n- `node:child_process` never exposes general subprocess execution on either platform.\n- Intercepted `child_process` builtins such as keychain-style `security` commands and the `rg --files` bridge use the same `BuiltinCommandBridge` implementation on both iOS and macOS. README should not imply a macOS-only subprocess path here.\n- TTY APIs are exposed through the same Darwin-backed implementation on both platforms, but whether `isatty`, window sizing, and raw mode are meaningful depends on the host file descriptors actually being attached to a terminal. That is common in macOS CLI-style environments and uncommon in normal iOS app hosts.\n- `process.platform` and `node:os` report the Darwin family on both platforms; there is not a separate iOS-specific Node platform surface.\n\nWhen integrating `swift-bun` into an app, treat Web APIs and most pure JS/Node polyfills as portable across iOS and macOS, and treat terminal/process-adjacent features as host-dependent rather than platform-forked.\n\n## Requirements\n\n- iOS 26.0+ / macOS 26.0+\n- Swift 6.2+\n- Xcode 26.0+\n\n## Installation\n\nAdd to your `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/1amageek/swift-bun.git\", from: \"0.1.0\"),\n],\ntargets: [\n    .target(\n        name: \"YourApp\",\n        dependencies: [\n            .product(name: \"BunRuntime\", package: \"swift-bun\"),\n        ]\n    ),\n]\n```\n\n## Usage\n\n### Process mode: run a long-lived application\n\n```swift\nimport BunRuntime\n\nlet process = BunProcess(\n    bundle: cliBundle,\n    arguments: [\"-p\", \"--input-format\", \"stream-json\"],\n    cwd: \"/path/to/project\",\n    environment: [\"API_KEY\": \"sk-ant-...\"]\n)\n\n// Read application protocol data from stdout\nTask {\n    for await data in process.stdout {\n        let event = parseNDJSON(data)\n    }\n}\n\n// Read diagnostic console output\nTask {\n    for await line in process.output {\n        print(line) // \"[log] hello\", \"[error] bad\"\n    }\n}\n\n// Run until process.exit() or all pending work completes\nlet exitCode = try await process.run()\n```\n\n`process.stdout.write()` in JS writes to `stdout`. `console.log/error` writes to `output`. They are separate channels — stdout carries protocol data, output carries diagnostics.\n\n### Sending stdin input\n\n```swift\nlet process = BunProcess(bundle: interactiveApp)\nlet task = Task { try await process.run() }\n\nprocess.sendInput(\"user input\\n\".data(using: .utf8)!)\nprocess.sendInput(nil) // EOF\n\nlet exitCode = try await task.value\n```\n\n### Library mode: load a bundle and call functions\n\n```swift\nlet runtime = BunProcess(\n    bundle: Bundle.main.url(forResource: \"app.bundle\", withExtension: \"js\")!\n)\ntry await runtime.load()\n\nlet result = try await runtime.evaluate(js: \"1 + 2\")\nprint(result.int32Value) // 3\n\nlet greeting = try await runtime.call(\"greet\", arguments: [\"World\"])\nprint(greeting.stringValue) // \"Hello, World!\"\n```\n\n### Bare context (no bundle)\n\n```swift\nlet runtime = BunProcess()\ntry await runtime.load()\ntry await runtime.evaluate(js: \"var path = require('node:path')\")\nlet result = try await runtime.evaluate(js: \"path.join('/usr', 'local')\")\n```\n\n### Installed CommonJS packages from `node_modules`\n\n`run()` and `require()` support plain CommonJS package loading from a normal `node_modules` tree.\n\nSupported today:\n- bare specifiers such as `require(\"semver\")`\n- package subpaths such as `require(\"semver/functions/valid\")`\n- `package.json.main`\n- `index.js` / `index.json`\n- `.js` / `.json`\n- `module.createRequire(...)`\n\nNot supported yet:\n- `package.json.exports`\n- `package.json.imports`\n- `.mjs` / `.cjs` specific behavior\n- native `.node` addons\n- package-manager-specific install logic such as `bun install`\n\n## API\n\n```swift\npublic final class BunProcess: Sendable {\n    // Configuration at init\n    init(bundle: URL? = nil, arguments: [String] = [], cwd: String? = nil, environment: [String: String] = [:])\n\n    // Streams (available immediately after init)\n    let stdout: AsyncStream\u003cString\u003e   // process.stdout.write() data\n    let output: AsyncStream\u003cString\u003e   // console.log/error diagnostics\n\n    // Library mode\n    func load() async throws\n    func evaluate(js: String) async throws -\u003e JSResult\n    func call(_ function: String, arguments: [Any]) async throws -\u003e JSResult\n\n    // Process mode\n    func run() async throws -\u003e Int32\n    func sendInput(_ data: Data?)\n    func terminate(exitCode: Int32)\n}\n```\n\n`load()` and `run()` are mutually exclusive on a single instance.\n\n`process.argv` is automatically set to `[\"node\", bundlePath, ...arguments]`.\n\n## Polyfill Coverage\n\nJSCore's `evaluateScript()` provides only ECMAScript language features. All platform APIs are polyfilled in three layers:\n\n- **Layer 0**: `polyfills.bundle.js` + runtime scripts — Web APIs (JS-owned semantics)\n- **Layer 1**: ModuleBootstrap — Node.js globals + modules (Swift strings)\n- **Layer 2**: host bridges — EventLoop-backed overrides (Swift closures)\n\n`ModuleBootstrap` is split internally into:\n- `ModuleGlobalBootstrap`\n- `BuiltinModuleBootstrap`\n- `RequireBootstrap`\n\n### Web APIs (Layer 0)\n\n| API | Status | Notes |\n|-----|--------|-------|\n| ReadableStream / WritableStream / TransformStream | ✅ Full | web-streams-polyfill (npm) |\n| Event / EventTarget / CustomEvent | ✅ Full | |\n| Blob / File | ✅ Basic | text, arrayBuffer, stream |\n| FormData | ✅ Full | |\n| MessageChannel / MessagePort | ✅ Basic | |\n| fetch / Headers / Request / Response | ✅ Streaming | `Response.body` is a `ReadableStream` |\n| TextDecoderStream / TextEncoderStream | ✅ Full | UTF-8 streaming codecs |\n| AbortController / AbortSignal | ✅ Full | Includes `AbortSignal.any()` |\n| crypto.getRandomValues / randomUUID | ✅ Basic | `getRandomValues` is not cryptographically secure |\n| structuredClone | ✅ Basic | JSON roundtrip |\n| Symbol.dispose / asyncDispose | ✅ Full | |\n| WebSocket | ✅ Basic | Runtime-installed client backed by `URLSessionWebSocketTask`; `run()`-mode E2E covered |\n| Worker | ⚠️ Stub | Throws |\n| crypto.subtle | ⚠️ Partial | `digest`, `importKey`, `exportKey`, `generateKey`, `sign`, `verify`, `encrypt`, `decrypt`, `deriveBits`, `deriveKey`, `wrapKey`, `unwrapKey` for HMAC, AES-GCM, PBKDF2, HKDF, and imported RSA/ECDSA signing keys |\n\n### Node.js Modules (Layer 1)\n\n| Module | Status | Notes |\n|--------|--------|-------|\n| `node:path` | ✅ | Full POSIX path API |\n| `node:buffer` | ✅ | Uint8Array-based Buffer |\n| `node:url` | ✅ | URL/URLSearchParams |\n| `node:util` | ✅ | format, promisify, debuglog, types, `isDeepStrictEqual` |\n| `node:os` | ✅ | ProcessInfo-backed, includes `version()` |\n| `node:fs` | ✅ | FileManager-backed (sync + promises, realpath, access, chmod) |\n| `node:crypto` | ✅ | Hash/HMAC/random APIs plus `createPrivateKey` |\n| `node:http/https` | ✅ | URLSession-backed client APIs plus minimal `createServer` |\n| `node:stream` | ✅ | Readable, Writable, Transform, EventEmitter |\n| `node:events` | ✅ | EventEmitter (supports extends) |\n| `node:timers` | ✅ | NIO EventLoop-backed |\n| `node:async_hooks` | ⚠️ Partial | AsyncLocalStorage plus minimal `AsyncResource` APIs |\n| `node:child_process` | ⚠️ Limited | No general subprocess support. Native bridges may emulate specific commands. |\n| `node:net` | ✅ Basic | Plain TCP `createServer`, `connect`, `createConnection` |\n| `node:tls` | ⚠️ Stub | TLS not implemented |\n| `node:zlib` | ⚠️ Partial | gzip/deflate/inflate/raw/unzip/brotli sync + callback + promise + transform APIs |\n| `node:dns` | ⚠️ Basic | `lookup` |\n| `node:v8` | ⚠️ Basic | `getHeapSpaceStatistics` shape |\n\n### Bun APIs\n\n| API | Status |\n|-----|--------|\n| `Bun.file(path)` | ✅ (text, json, exists) |\n| `Bun.write(path, data)` | ✅ |\n| `Bun.env` | ✅ (alias for process.env) |\n| `Bun.version` | ✅ |\n| `Bun.nanoseconds()` | ✅ |\n| `Bun.hash(data)` | ✅ (djb2) |\n| `Bun.escapeHTML(str)` | ✅ |\n| `Bun.spawn()` | ⚠️ (throws by default) |\n| `Bun.serve()` | ❌ Not supported |\n\n### Global APIs\n\n`fetch`, `Request`, `Response`, `Headers`, `URL`, `URLSearchParams`, `TextEncoder`, `TextDecoder`, `TextEncoderStream`, `TextDecoderStream`, `AbortController`, `AbortSignal`, `Buffer`, `console`, `process`, `setTimeout`, `setInterval`, `setImmediate`, `queueMicrotask`, `atob`, `btoa`, `ReadableStream`, `WritableStream`, `TransformStream`, `Event`, `EventTarget`, `Blob`, `File`, `FormData`, `crypto`, `navigator`, `structuredClone`\n\n## Current Limitations\n\n- `crypto.getRandomValues` still uses a non-cryptographic fallback. Use `require('node:crypto')` or `crypto.subtle` for security-sensitive work.\n- `crypto.subtle` now covers `digest`, `importKey`, `exportKey`, `generateKey`, `sign`, `verify`, `encrypt`, `decrypt`, `deriveBits`, `deriveKey`, `wrapKey`, and `unwrapKey` for HMAC, AES-GCM, PBKDF2, HKDF, and imported RSA/ECDSA signing keys. It still does not cover the full Web Crypto surface.\n- `globalThis.WebSocket` is client-only. Text/binary messaging, headers, subprotocol negotiation, close events, ping/pong, and process-mode keep-alive are supported, but `proxy` and custom `tls` options are currently accepted and ignored.\n- server-side WebSocket APIs, `node:tls`, `node:http2`, `Worker`, and native addons remain unsupported.\n- `node:child_process` does not provide general subprocess execution. Use native bridges for specific host capabilities instead.\n- `node:zlib` currently covers gzip/deflate/inflate/raw/unzip/brotli sync APIs, callback APIs, promise APIs, and transform constructors. It is still a compatibility subset rather than full Node zlib parity.\n- `node:dns` currently exposes `lookup` only.\n- `http.createServer` and `node:net` are intentionally minimal and focused on local server/client use cases.\n\n## Building a JS bundle\n\n```bash\n# With Bun (ESM — transformed automatically by es-module-lexer)\nbun build src/index.ts --target=node --format=esm --outfile=app.bundle.js\n\n# With esbuild (CJS — no transformation needed)\nnpx esbuild src/index.ts --bundle --platform=node --format=cjs \\\n  --external:node:* --outfile=app.bundle.js\n```\n\nBoth ESM and CJS bundles are supported. ESM bundles are automatically transformed to CJS before evaluation using es-module-lexer (WASM).\n\nWhen a bundle is not required, `swift-bun` can also execute installed CommonJS packages directly from `node_modules` through its built-in loader.\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────┐\n│               Your Swift App                 │\n│                                              │\n│   BunProcess(bundle:arguments:cwd:env:)      │\n│     .run()  → Int32      (process mode)      │\n│     .load() → evaluate() (library mode)      │\n│     .stdout → AsyncStream (protocol data)    │\n│     .output → AsyncStream (console logs)     │\n│     .sendInput(data)                         │\n│        ↓                                     │\n│   ┌──────────────────────────────────────┐   │\n│   │     NIO EventLoop (dedicated thread)  │  │\n│   │  ┌────────────────────────────────┐  │   │\n│   │  │  JavaScriptCore.framework      │  │   │\n│   │  │  ┌──────────────────────────┐  │  │   │\n│   │  │  │  ModuleBootstrap         │  │  │   │\n│   │  │  │  • Node.js modules       │  │  │   │\n│   │  │  │  • Bun API shims         │  │  │   │\n│   │  │  └──────────────────────────┘  │  │   │\n│   │  │  ┌──────────────────────────┐  │  │   │\n│   │  │  │  NIO-backed bridges      │  │  │   │\n│   │  │  │  • setTimeout → sched    │  │  │   │\n│   │  │  │  • fetch → URLSession    │  │  │   │\n│   │  │  │  • stdin → sendInput     │  │  │   │\n│   │  │  │  • stdout.write → stdout │  │  │   │\n│   │  │  │  • console → output      │  │  │   │\n│   │  │  └──────────────────────────┘  │  │   │\n│   │  └────────────────────────────────┘  │   │\n│   └──────────────────────────────────────┘   │\n└──────────────────────────────────────────────┘\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1amageek%2Fswift-bun","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1amageek%2Fswift-bun","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1amageek%2Fswift-bun/lists"}