@@ -82,6 +82,14 @@ npm run pack # extension zip + xpi
|
|||||||
npm run build:dist:package # all-platform host zips (needs bare-build; best on CI)
|
npm run build:dist:package # all-platform host zips (needs bare-build; best on CI)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Try the examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Opens **http://127.0.0.1:4173/** (do not use `file://` — browsers treat each local file as a unique origin). Pick a demo and open it in two tabs.
|
||||||
|
|
||||||
### Your First P2P App
|
### Your First P2P App
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@@ -313,6 +321,7 @@ Run `npm run build:hrpc` to generate the HRPC spec.
|
|||||||
### Build Commands
|
### Build Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
npm run examples # Serve demos at http://127.0.0.1:4173/ (not file://)
|
||||||
npm run build:protomux # Rebuild Protomux bundle
|
npm run build:protomux # Rebuild Protomux bundle
|
||||||
npm run build:hrpc # Rebuild HRPC spec (+ mirror into native-host/spec)
|
npm run build:hrpc # Rebuild HRPC spec (+ mirror into native-host/spec)
|
||||||
npm run build # host launcher + protomux + hrpc
|
npm run build # host launcher + protomux + hrpc
|
||||||
|
|||||||
+43
-28
@@ -2,53 +2,68 @@
|
|||||||
|
|
||||||
Open these in your browser **after** installing the BridgeSwarm extension and native host.
|
Open these in your browser **after** installing the BridgeSwarm extension and native host.
|
||||||
|
|
||||||
Each example lives in its own directory with separated concerns: **index.html** (structure), **style.css** (styles), **app.js** (logic). Open the directory’s **index.html** (e.g. `chat/index.html` or run a local server and go to `http://localhost:3000/chat/`). Each subdirectory has its own **README.md** with detailed usage, APIs, and file roles.
|
**Do not open examples via `file://`.** Modern Chrome/Firefox treat each local file as a unique opaque origin, which breaks scripts, styles, and extension injection (you may see: *Unsafe attempt to load URL file://… 'file:' URLs are treated as unique security origins*).
|
||||||
|
|
||||||
|
## Run the examples server
|
||||||
|
|
||||||
|
From the BridgeSwarm repo root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run examples
|
||||||
|
```
|
||||||
|
|
||||||
|
This serves `examples/` at **http://127.0.0.1:4173/** and opens the landing page. Use two browser tabs for P2P demos.
|
||||||
|
|
||||||
|
| Demo | URL |
|
||||||
|
|------|-----|
|
||||||
|
| Landing | http://127.0.0.1:4173/ |
|
||||||
|
| Chat | http://127.0.0.1:4173/chat/ |
|
||||||
|
| Advanced Chat | http://127.0.0.1:4173/chat-advanced/ |
|
||||||
|
| SDK Demo | http://127.0.0.1:4173/sdk-demo/ |
|
||||||
|
| HRPC Demo | http://127.0.0.1:4173/hrpc-demo/ |
|
||||||
|
| Whiteboard | http://127.0.0.1:4173/whiteboard/ |
|
||||||
|
| Screenshare | http://127.0.0.1:4173/screenshare/ |
|
||||||
|
| Data API | http://127.0.0.1:4173/data-demo/ |
|
||||||
|
|
||||||
|
Each subdirectory has its own **README.md** with APIs and file roles. Layout per demo: **index.html**, **style.css**, **app.js**.
|
||||||
|
|
||||||
## Chat (`chat/`)
|
## Chat (`chat/`)
|
||||||
|
|
||||||
A minimal P2P chat: join a topic, see peers, send and receive messages.
|
Minimal P2P chat: join a topic, see peers, send and receive messages.
|
||||||
|
|
||||||
1. Open `chat/index.html` in Chrome (or Edge/Firefox) — e.g. drag the file into the browser or use **File → Open**.
|
1. Open http://127.0.0.1:4173/chat/
|
||||||
2. Click **Join** (default topic is `bridge-swarm-demo`).
|
2. Click **Join** (default topic `bridge-swarm-demo`).
|
||||||
3. Open the same file in another tab (or another window/device) and join the same topic.
|
3. Open the same URL in another tab and join.
|
||||||
4. Type a message and click **Send**; it appears in the other tab.
|
4. Send a message; it appears in the other tab.
|
||||||
|
|
||||||
|
## Advanced Chat (`chat-advanced/`)
|
||||||
|
|
||||||
|
Rooms, presence, markdown, emoji, and file sharing. Open http://127.0.0.1:4173/chat-advanced/ in two tabs.
|
||||||
|
|
||||||
## SDK Demo (`sdk-demo/`)
|
## SDK Demo (`sdk-demo/`)
|
||||||
|
|
||||||
A complete example of all BridgeSwarm SDK features:
|
Swarm lifecycle, raw messages, and Protomux. Open http://127.0.0.1:4173/sdk-demo/ in two tabs.
|
||||||
|
|
||||||
1. **Swarm** — Join/leave topic, destroy swarm.
|
|
||||||
2. **Connections** — Connection events, `peerInfo` (publicKey, topics).
|
|
||||||
3. **Raw messages** — `conn.write(data)` and `conn.on('data')` for raw bytes.
|
|
||||||
4. **Protomux** — `swarm.createProtomux(conn)`, create a channel with protocol `sdk-demo/v1`, add string and binary messages via `compact-encoding` (`c.string`, `c.binary`), `channel.open()`, and `msg.send()`.
|
|
||||||
|
|
||||||
Open `sdk-demo/index.html` in two tabs (or two devices), join the same topic, then try sending raw messages and Protomux messages. The log shows which path each message used.
|
|
||||||
|
|
||||||
## HRPC Demo (`hrpc-demo/`)
|
## HRPC Demo (`hrpc-demo/`)
|
||||||
|
|
||||||
Demonstrates **HRPC** on a connection: HRPC is **auto-enabled** on each connection. Open the page in two tabs, join the same topic; when a peer connects, the host enables HRPC and either tab can use “Ping peer” to get a pong. No manual enable step. See [../docs/HRPC.md](../docs/HRPC.md).
|
HRPC ping/streams over connections. Open http://127.0.0.1:4173/hrpc-demo/ in two tabs. See [../docs/HRPC.md](../docs/HRPC.md).
|
||||||
|
|
||||||
## Whiteboard (`whiteboard/`)
|
## Whiteboard (`whiteboard/`)
|
||||||
|
|
||||||
Collaborative P2P whiteboard: join a topic, draw on the canvas, and strokes sync to all peers in real time. Open `whiteboard/index.html` in two tabs (or two devices), join the same topic, then draw; strokes and “Clear board” are broadcast to everyone. Uses raw `conn.write()` / `conn.on('data')` with JSON stroke and clear messages.
|
Collaborative drawing. Open http://127.0.0.1:4173/whiteboard/ in two tabs.
|
||||||
|
|
||||||
## Screenshare (`screenshare/`)
|
## Screenshare (`screenshare/`)
|
||||||
|
|
||||||
P2P screen sharing using **WebRTC** for the media stream and **BridgeSwarm for signaling** (no separate signaling server). SDP offers/answers and ICE candidates are exchanged over the BridgeSwarm connection.
|
WebRTC media + BridgeSwarm signaling. Open http://127.0.0.1:4173/screenshare/ in two tabs.
|
||||||
|
|
||||||
1. Open `screenshare/index.html` in two tabs (or two windows).
|
|
||||||
2. **Tab 1 (sharer):** Click **Join**, then **Share my screen** and pick a screen or window to share.
|
|
||||||
3. **Tab 2 (viewer):** Join the same topic; the shared screen appears in the video area.
|
|
||||||
|
|
||||||
Signaling uses BridgeSwarm; the actual video flows via the browser's WebRTC (RTCPeerConnection). No public STUN or TURN is used — fully enclosed P2P; ICE uses only host and local-network candidates. The sharer can start sharing before any viewer joins; viewers see the stream when they join the topic.
|
|
||||||
|
|
||||||
## Data API Demo (`data-demo/`)
|
## Data API Demo (`data-demo/`)
|
||||||
|
|
||||||
Uses the native host’s **Hypercore**, **Hyperbee**, **Hyperdrive**, and **Autobase** via `BridgeSwarm.request(type, payload)`.
|
Hypercore / Hyperbee / Hyperdrive / Autobase / Hyperdb via `BridgeSwarm.request`. Open http://127.0.0.1:4173/data-demo/. See [../docs/DATA-API.md](../docs/DATA-API.md).
|
||||||
|
|
||||||
1. Open `data-demo/index.html` in the browser (with the extension installed).
|
## Troubleshooting
|
||||||
2. Use the buttons to get core info, append to the core, put/get/del in Hyperbee, put/get/list/delete files in Hyperdrive, and append/read the Autobase linearized view.
|
|
||||||
|
|
||||||
See [../docs/DATA-API.md](../docs/DATA-API.md) for the full command reference.
|
If the page says the extension is not detected:
|
||||||
|
|
||||||
**Note:** If the page says the extension is not detected: (1) The page waits a few seconds for the extension to inject—try again after a moment. (2) For `file://` URLs, open the extension’s **Options** and ensure “Do not inject on file:// URLs” is *off*, then in `chrome://extensions` (or the browser’s extension page) find BridgeSwarm and enable **Allow access to file URLs**. (3) Or run a local server (e.g. `npx serve .` in the `examples` folder) and open `http://localhost:3000/chat/`.
|
1. Wait a moment for content-script injection, then retry.
|
||||||
|
2. Confirm the BridgeSwarm extension and native host are installed.
|
||||||
|
3. Confirm you are on `http://127.0.0.1:4173/…`, not `file://`.
|
||||||
|
|||||||
@@ -42,10 +42,11 @@ A feature-rich P2P chat application built on BridgeSwarm with real-time messagin
|
|||||||
|
|
||||||
## How to Run
|
## How to Run
|
||||||
|
|
||||||
1. Load the BridgeSwarm extension in your browser
|
1. Load the BridgeSwarm extension and native host.
|
||||||
2. Open `index.html` in your browser (File → Open or serve locally)
|
2. From the repo root: `npm run examples`
|
||||||
3. Enter your nickname and click "Join"
|
3. Open **http://127.0.0.1:4173/chat-advanced/** in two tabs.
|
||||||
4. Open the same page in another tab or browser to start chatting
|
|
||||||
|
Do **not** open via `file://` — browsers treat each local file as a unique security origin.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -58,18 +59,3 @@ A feature-rich P2P chat application built on BridgeSwarm with real-time messagin
|
|||||||
- Type in the message box
|
- Type in the message box
|
||||||
- Press Enter or click Send
|
- Press Enter or click Send
|
||||||
- Use **bold**, *italic*, and `code` formatting
|
- Use **bold**, *italic*, and `code` formatting
|
||||||
|
|
||||||
### Rooms
|
|
||||||
- Click "+" next to Rooms to create a new room
|
|
||||||
- Click a room to switch to it
|
|
||||||
|
|
||||||
### Files
|
|
||||||
- Click the 📎 button to share a file
|
|
||||||
|
|
||||||
## Technical Details
|
|
||||||
|
|
||||||
- Uses BridgeSwarm API for P2P networking
|
|
||||||
- Each peer maintains connections to other peers
|
|
||||||
- Messages are broadcast to all connected peers
|
|
||||||
- Presence is shared via periodic broadcasts
|
|
||||||
- Unique public key identifies each user/tab
|
|
||||||
|
|||||||
@@ -6,6 +6,19 @@
|
|||||||
<title>BridgeSwarm – Advanced Chat</title>
|
<title>BridgeSwarm – Advanced Chat</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/styles/atom-one-dark.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/styles/atom-one-dark.min.css">
|
||||||
|
<script>
|
||||||
|
if (location.protocol === 'file:') {
|
||||||
|
document.documentElement.innerHTML =
|
||||||
|
'<body style="margin:0;font:15px/1.5 Segoe UI,system-ui,sans-serif;background:#0f1419;color:#e7ecf3">' +
|
||||||
|
'<div style="max-width:36rem;margin:10vh auto;padding:24px;background:#1a2332;border:1px solid #2a3548;border-radius:14px">' +
|
||||||
|
'<h1 style="margin:0 0 10px;font-size:1.25rem">Open this demo over HTTP</h1>' +
|
||||||
|
'<p style="color:#8b9bb4">Browsers treat each <code>file://</code> URL as a unique security origin, so local demos break.</p>' +
|
||||||
|
'<p>From the BridgeSwarm repo root run:</p>' +
|
||||||
|
'<pre style="padding:12px;background:#0f1419;border-radius:10px;color:#2dd4bf">npm run examples</pre>' +
|
||||||
|
'<p style="color:#8b9bb4">Then open <code>http://127.0.0.1:4173/</code></p>' +
|
||||||
|
'</div></body>';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
|
|||||||
+14
-12
@@ -6,11 +6,19 @@ Minimal P2P chat: join a topic, see peer count, and send and receive text messag
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Install the [BridgeSwarm extension and native host](../../README.md#install-one-command) before opening this example.
|
Install the [BridgeSwarm extension and native host](../../README.md#easy-install-recommended) before opening this example.
|
||||||
|
|
||||||
## How to run
|
## How to run
|
||||||
|
|
||||||
Open `index.html` in your browser (File → Open or drag the file in). For `file://` URLs, ensure the extension has **Allow access to file URLs** enabled, or run a local server (e.g. `npx serve .` from the `examples` folder) and open `http://localhost:3000/chat/`.
|
From the repo root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open **http://127.0.0.1:4173/chat/** (use two tabs for P2P).
|
||||||
|
|
||||||
|
Do **not** open `index.html` via `file://` — browsers treat each local file as a unique origin and the demo will fail.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -28,13 +36,7 @@ Open `index.html` in your browser (File → Open or drag the file in). For `file
|
|||||||
## APIs and concepts
|
## APIs and concepts
|
||||||
|
|
||||||
- `BridgeSwarm.ready()` — Wait for the injected API.
|
- `BridgeSwarm.ready()` — Wait for the injected API.
|
||||||
- `new BridgeSwarm({ appName })` — Create a swarm (app name `bridge-swarm-chat`).
|
- `new BridgeSwarm({ appName })` — Create a swarm.
|
||||||
- `swarm.join(topic)` — Join the topic.
|
- `swarm.join(topic)` / `swarm.leave(topic)` — Topic discovery.
|
||||||
- `swarm.on('connection', (conn, peerInfo))` — Handle new peers.
|
- `swarm.on('connection', …)` — Peer connections.
|
||||||
- `conn.on('data', fn)` — Receive messages; decode with `TextDecoder`.
|
- `conn.write(data)` / `conn.on('data', …)` — Raw messages.
|
||||||
- `conn.write(data)` — Send bytes (e.g. `TextEncoder().encode(text)`).
|
|
||||||
- `swarm.leave(topic)`, `swarm.destroy()` — Leave and tear down.
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [API reference](../../docs/API-REFERENCE.md) — Page API and host request types.
|
|
||||||
|
|||||||
@@ -5,6 +5,19 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>BridgeSwarm – Chat</title>
|
<title>BridgeSwarm – Chat</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script>
|
||||||
|
if (location.protocol === 'file:') {
|
||||||
|
document.documentElement.innerHTML =
|
||||||
|
'<body style="margin:0;font:15px/1.5 Segoe UI,system-ui,sans-serif;background:#0f1419;color:#e7ecf3">' +
|
||||||
|
'<div style="max-width:36rem;margin:10vh auto;padding:24px;background:#1a2332;border:1px solid #2a3548;border-radius:14px">' +
|
||||||
|
'<h1 style="margin:0 0 10px;font-size:1.25rem">Open this demo over HTTP</h1>' +
|
||||||
|
'<p style="color:#8b9bb4">Browsers treat each <code>file://</code> URL as a unique security origin, so local demos break.</p>' +
|
||||||
|
'<p>From the BridgeSwarm repo root run:</p>' +
|
||||||
|
'<pre style="padding:12px;background:#0f1419;border-radius:10px;color:#2dd4bf">npm run examples</pre>' +
|
||||||
|
'<p style="color:#8b9bb4">Then open <code>http://127.0.0.1:4173/</code></p>' +
|
||||||
|
'</div></body>';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>BridgeSwarm – Chat</h1>
|
<h1>BridgeSwarm – Chat</h1>
|
||||||
|
|||||||
@@ -6,11 +6,15 @@ Uses the native host Data API via `BridgeSwarm.request(type, payload)`: Hypercor
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Install the [BridgeSwarm extension and native host](../../README.md#install-one-command) before opening this example.
|
Install the [BridgeSwarm extension and native host](../../README.md#easy-install-recommended) before opening this example.
|
||||||
|
|
||||||
## How to run
|
## How to run
|
||||||
|
|
||||||
Open `index.html` in your browser (File → Open or drag the file in). For `file://` URLs, ensure the extension has **Allow access to file URLs** enabled, or run a local server (e.g. `npx serve .` from the `examples` folder) and open `http://localhost:3000/data-demo/`.
|
```bash
|
||||||
|
npm run examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Open **http://127.0.0.1:4173/data-demo/**. Do not use `file://`.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,19 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>BridgeSwarm – Data API Demo</title>
|
<title>BridgeSwarm – Data API Demo</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script>
|
||||||
|
if (location.protocol === 'file:') {
|
||||||
|
document.documentElement.innerHTML =
|
||||||
|
'<body style="margin:0;font:15px/1.5 Segoe UI,system-ui,sans-serif;background:#0f1419;color:#e7ecf3">' +
|
||||||
|
'<div style="max-width:36rem;margin:10vh auto;padding:24px;background:#1a2332;border:1px solid #2a3548;border-radius:14px">' +
|
||||||
|
'<h1 style="margin:0 0 10px;font-size:1.25rem">Open this demo over HTTP</h1>' +
|
||||||
|
'<p style="color:#8b9bb4">Browsers treat each <code>file://</code> URL as a unique security origin, so local demos break.</p>' +
|
||||||
|
'<p>From the BridgeSwarm repo root run:</p>' +
|
||||||
|
'<pre style="padding:12px;background:#0f1419;border-radius:10px;color:#2dd4bf">npm run examples</pre>' +
|
||||||
|
'<p style="color:#8b9bb4">Then open <code>http://127.0.0.1:4173/</code></p>' +
|
||||||
|
'</div></body>';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>BridgeSwarm – Data API Demo</h1>
|
<h1>BridgeSwarm – Data API Demo</h1>
|
||||||
|
|||||||
@@ -6,11 +6,15 @@ HRPC on a connection: HRPC is auto-enabled when a peer connects. Either tab can
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Install the [BridgeSwarm extension and native host](../../README.md#install-one-command) before opening this example.
|
Install the [BridgeSwarm extension and native host](../../README.md#easy-install-recommended) before opening this example.
|
||||||
|
|
||||||
## How to run
|
## How to run
|
||||||
|
|
||||||
Open `index.html` in your browser (File → Open or drag the file in). For file:// URLs, ensure the extension has **Allow access to file URLs** enabled, or run a local server (e.g. `npx serve .` from the `examples` folder) and open `http://localhost:3000/hrpc-demo/`.
|
```bash
|
||||||
|
npm run examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Open **http://127.0.0.1:4173/hrpc-demo/** (two tabs for P2P). Do not use `file://`.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,19 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>BridgeSwarm – HRPC Demo</title>
|
<title>BridgeSwarm – HRPC Demo</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script>
|
||||||
|
if (location.protocol === 'file:') {
|
||||||
|
document.documentElement.innerHTML =
|
||||||
|
'<body style="margin:0;font:15px/1.5 Segoe UI,system-ui,sans-serif;background:#0f1419;color:#e7ecf3">' +
|
||||||
|
'<div style="max-width:36rem;margin:10vh auto;padding:24px;background:#1a2332;border:1px solid #2a3548;border-radius:14px">' +
|
||||||
|
'<h1 style="margin:0 0 10px;font-size:1.25rem">Open this demo over HTTP</h1>' +
|
||||||
|
'<p style="color:#8b9bb4">Browsers treat each <code>file://</code> URL as a unique security origin, so local demos break.</p>' +
|
||||||
|
'<p>From the BridgeSwarm repo root run:</p>' +
|
||||||
|
'<pre style="padding:12px;background:#0f1419;border-radius:10px;color:#2dd4bf">npm run examples</pre>' +
|
||||||
|
'<p style="color:#8b9bb4">Then open <code>http://127.0.0.1:4173/</code></p>' +
|
||||||
|
'</div></body>';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>BridgeSwarm – HRPC Demo</h1>
|
<h1>BridgeSwarm – HRPC Demo</h1>
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>BridgeSwarm Examples</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #0f1419;
|
||||||
|
--panel: #1a2332;
|
||||||
|
--text: #e7ecf3;
|
||||||
|
--muted: #8b9bb4;
|
||||||
|
--accent: #3d8bfd;
|
||||||
|
--accent-2: #2dd4bf;
|
||||||
|
--border: #2a3548;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
font-family: "Segoe UI", system-ui, sans-serif;
|
||||||
|
background:
|
||||||
|
radial-gradient(1200px 600px at 10% -10%, rgba(61, 139, 253, 0.18), transparent 55%),
|
||||||
|
radial-gradient(900px 500px at 90% 0%, rgba(45, 212, 191, 0.12), transparent 50%),
|
||||||
|
var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
max-width: 880px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 48px 24px 80px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 2rem;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
}
|
||||||
|
.lede {
|
||||||
|
color: var(--muted);
|
||||||
|
margin: 0 0 28px;
|
||||||
|
max-width: 54ch;
|
||||||
|
}
|
||||||
|
.warn {
|
||||||
|
border: 1px solid rgba(251, 191, 36, 0.35);
|
||||||
|
background: rgba(251, 191, 36, 0.08);
|
||||||
|
color: #fde68a;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
.warn code { color: #fff7c2; }
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
a.card {
|
||||||
|
display: block;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
background: var(--panel);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 18px 18px 16px;
|
||||||
|
transition: border-color 0.15s ease, transform 0.15s ease;
|
||||||
|
}
|
||||||
|
a.card:hover {
|
||||||
|
border-color: rgba(61, 139, 253, 0.7);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
a.card h2 {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
}
|
||||||
|
a.card p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
.path {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--accent-2);
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
margin-top: 36px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
footer code {
|
||||||
|
color: #cbd5e1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<h1>BridgeSwarm Examples</h1>
|
||||||
|
<p class="lede">
|
||||||
|
Interactive demos for Hyperswarm in the browser. Install the BridgeSwarm extension
|
||||||
|
and native host first, then open any demo below in two tabs to try P2P.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="warn" id="fileWarn" hidden>
|
||||||
|
You opened this page via <code>file://</code>. Modern browsers treat each local file as a
|
||||||
|
unique security origin, so demos break. Run <code>npm run examples</code> from the repo
|
||||||
|
and use <code>http://127.0.0.1:4173/</code> instead.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
<a class="card" href="./chat/">
|
||||||
|
<h2>Chat</h2>
|
||||||
|
<p>Minimal P2P chat over a topic — join, see peers, send messages.</p>
|
||||||
|
<span class="path">/chat/</span>
|
||||||
|
</a>
|
||||||
|
<a class="card" href="./chat-advanced/">
|
||||||
|
<h2>Advanced Chat</h2>
|
||||||
|
<p>Rooms, presence, markdown, emoji, and file sharing.</p>
|
||||||
|
<span class="path">/chat-advanced/</span>
|
||||||
|
</a>
|
||||||
|
<a class="card" href="./sdk-demo/">
|
||||||
|
<h2>SDK Demo</h2>
|
||||||
|
<p>Swarm lifecycle, raw messages, and Protomux channels.</p>
|
||||||
|
<span class="path">/sdk-demo/</span>
|
||||||
|
</a>
|
||||||
|
<a class="card" href="./hrpc-demo/">
|
||||||
|
<h2>HRPC Demo</h2>
|
||||||
|
<p>Typed RPC (ping / streams) over Hyperswarm connections.</p>
|
||||||
|
<span class="path">/hrpc-demo/</span>
|
||||||
|
</a>
|
||||||
|
<a class="card" href="./whiteboard/">
|
||||||
|
<h2>Whiteboard</h2>
|
||||||
|
<p>Collaborative drawing synced peer-to-peer in real time.</p>
|
||||||
|
<span class="path">/whiteboard/</span>
|
||||||
|
</a>
|
||||||
|
<a class="card" href="./screenshare/">
|
||||||
|
<h2>Screenshare</h2>
|
||||||
|
<p>WebRTC media with BridgeSwarm used only for signaling.</p>
|
||||||
|
<span class="path">/screenshare/</span>
|
||||||
|
</a>
|
||||||
|
<a class="card" href="./data-demo/">
|
||||||
|
<h2>Data API Demo</h2>
|
||||||
|
<p>Hypercore, Hyperbee, Hyperdrive, Autobase, and Hyperdb from the page.</p>
|
||||||
|
<span class="path">/data-demo/</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
Start the server with <code>npm run examples</code> from the BridgeSwarm repo root.
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
<script>
|
||||||
|
if (location.protocol === 'file:') {
|
||||||
|
document.getElementById('fileWarn').hidden = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -6,11 +6,15 @@ P2P screen sharing: BridgeSwarm is used only for **signaling** (SDP offer/answer
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Install the [BridgeSwarm extension and native host](../../README.md#install-one-command) before opening this example.
|
Install the [BridgeSwarm extension and native host](../../README.md#easy-install-recommended) before opening this example.
|
||||||
|
|
||||||
## How to run
|
## How to run
|
||||||
|
|
||||||
Open `index.html` in your browser (File → Open or drag the file in). For `file://` URLs, ensure the extension has **Allow access to file URLs** enabled, or run a local server (e.g. `npx serve .` from the `examples` folder) and open `http://localhost:3000/screenshare/`.
|
```bash
|
||||||
|
npm run examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Open **http://127.0.0.1:4173/screenshare/** (two tabs for P2P). Do not use `file://`.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,19 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>BridgeSwarm – Screenshare</title>
|
<title>BridgeSwarm – Screenshare</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script>
|
||||||
|
if (location.protocol === 'file:') {
|
||||||
|
document.documentElement.innerHTML =
|
||||||
|
'<body style="margin:0;font:15px/1.5 Segoe UI,system-ui,sans-serif;background:#0f1419;color:#e7ecf3">' +
|
||||||
|
'<div style="max-width:36rem;margin:10vh auto;padding:24px;background:#1a2332;border:1px solid #2a3548;border-radius:14px">' +
|
||||||
|
'<h1 style="margin:0 0 10px;font-size:1.25rem">Open this demo over HTTP</h1>' +
|
||||||
|
'<p style="color:#8b9bb4">Browsers treat each <code>file://</code> URL as a unique security origin, so local demos break.</p>' +
|
||||||
|
'<p>From the BridgeSwarm repo root run:</p>' +
|
||||||
|
'<pre style="padding:12px;background:#0f1419;border-radius:10px;color:#2dd4bf">npm run examples</pre>' +
|
||||||
|
'<p style="color:#8b9bb4">Then open <code>http://127.0.0.1:4173/</code></p>' +
|
||||||
|
'</div></body>';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>BridgeSwarm – Screenshare</h1>
|
<h1>BridgeSwarm – Screenshare</h1>
|
||||||
|
|||||||
@@ -6,11 +6,15 @@ Demonstrates all core BridgeSwarm SDK features in one page: swarm lifecycle, con
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Install the [BridgeSwarm extension and native host](../../README.md#install-one-command) before opening this example.
|
Install the [BridgeSwarm extension and native host](../../README.md#easy-install-recommended) before opening this example.
|
||||||
|
|
||||||
## How to run
|
## How to run
|
||||||
|
|
||||||
Open `index.html` in your browser (File → Open or drag the file in). For file:// URLs, ensure the extension has **Allow access to file URLs** enabled, or run a local server (e.g. `npx serve .` from the `examples` folder) and open `http://localhost:3000/sdk-demo/`.
|
```bash
|
||||||
|
npm run examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Open **http://127.0.0.1:4173/sdk-demo/** (two tabs for P2P). Do not use `file://`.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,19 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>BridgeSwarm – SDK Demo</title>
|
<title>BridgeSwarm – SDK Demo</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script>
|
||||||
|
if (location.protocol === 'file:') {
|
||||||
|
document.documentElement.innerHTML =
|
||||||
|
'<body style="margin:0;font:15px/1.5 Segoe UI,system-ui,sans-serif;background:#0f1419;color:#e7ecf3">' +
|
||||||
|
'<div style="max-width:36rem;margin:10vh auto;padding:24px;background:#1a2332;border:1px solid #2a3548;border-radius:14px">' +
|
||||||
|
'<h1 style="margin:0 0 10px;font-size:1.25rem">Open this demo over HTTP</h1>' +
|
||||||
|
'<p style="color:#8b9bb4">Browsers treat each <code>file://</code> URL as a unique security origin, so local demos break.</p>' +
|
||||||
|
'<p>From the BridgeSwarm repo root run:</p>' +
|
||||||
|
'<pre style="padding:12px;background:#0f1419;border-radius:10px;color:#2dd4bf">npm run examples</pre>' +
|
||||||
|
'<p style="color:#8b9bb4">Then open <code>http://127.0.0.1:4173/</code></p>' +
|
||||||
|
'</div></body>';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>BridgeSwarm – SDK Demo</h1>
|
<h1>BridgeSwarm – SDK Demo</h1>
|
||||||
|
|||||||
@@ -6,11 +6,15 @@ Collaborative P2P whiteboard: join a topic, draw on a canvas, and strokes and
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Install the [BridgeSwarm extension and native host](../../README.md#install-one-command) before opening this example.
|
Install the [BridgeSwarm extension and native host](../../README.md#easy-install-recommended) before opening this example.
|
||||||
|
|
||||||
## How to run
|
## How to run
|
||||||
|
|
||||||
Open `index.html` in your browser (File → Open or drag the file in). For `file://` URLs, ensure the extension has **Allow access to file URLs** enabled, or run a local server (e.g. `npx serve .` from the `examples` folder) and open `http://localhost:3000/whiteboard/`.
|
```bash
|
||||||
|
npm run examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Open **http://127.0.0.1:4173/whiteboard/** (two tabs for P2P). Do not use `file://`.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,19 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>BridgeSwarm – Whiteboard</title>
|
<title>BridgeSwarm – Whiteboard</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script>
|
||||||
|
if (location.protocol === 'file:') {
|
||||||
|
document.documentElement.innerHTML =
|
||||||
|
'<body style="margin:0;font:15px/1.5 Segoe UI,system-ui,sans-serif;background:#0f1419;color:#e7ecf3">' +
|
||||||
|
'<div style="max-width:36rem;margin:10vh auto;padding:24px;background:#1a2332;border:1px solid #2a3548;border-radius:14px">' +
|
||||||
|
'<h1 style="margin:0 0 10px;font-size:1.25rem">Open this demo over HTTP</h1>' +
|
||||||
|
'<p style="color:#8b9bb4">Browsers treat each <code>file://</code> URL as a unique security origin, so local demos break.</p>' +
|
||||||
|
'<p>From the BridgeSwarm repo root run:</p>' +
|
||||||
|
'<pre style="padding:12px;background:#0f1419;border-radius:10px;color:#2dd4bf">npm run examples</pre>' +
|
||||||
|
'<p style="color:#8b9bb4">Then open <code>http://127.0.0.1:4173/</code></p>' +
|
||||||
|
'</div></body>';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>BridgeSwarm – Whiteboard</h1>
|
<h1>BridgeSwarm – Whiteboard</h1>
|
||||||
|
|||||||
+35
-12
@@ -5,13 +5,16 @@
|
|||||||
* available as a global before any other module runs (required for bare-pack
|
* available as a global before any other module runs (required for bare-pack
|
||||||
* standalone binaries).
|
* standalone binaries).
|
||||||
*
|
*
|
||||||
|
* set-tmpdir must run before loading native addons (macOS codesign).
|
||||||
|
*
|
||||||
* All local imports are static so bare-pack can pre-resolve the full module graph.
|
* All local imports are static so bare-pack can pre-resolve the full module graph.
|
||||||
*
|
*
|
||||||
* macOS install: when run with --extract-addons, loads native addons so the
|
* IMPORTANT: Never write non-framed data to stdout — Chrome native messaging
|
||||||
* Bare runtime extracts them; the installer then ad-hoc signs those files.
|
* owns stdout. All logs go to stderr (and optional log file).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'bare-process/global';
|
import 'bare-process/global';
|
||||||
|
import './set-tmpdir.mjs';
|
||||||
import _messenger from './messenger.js';
|
import _messenger from './messenger.js';
|
||||||
import _host from './host.js';
|
import _host from './host.js';
|
||||||
|
|
||||||
@@ -21,6 +24,19 @@ function logErr(msg) {
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep stdout clean for the native messaging framing protocol.
|
||||||
|
const _stderrWrite = (...args) => {
|
||||||
|
try {
|
||||||
|
const line = args.map((a) => (typeof a === 'string' ? a : String(a))).join(' ') + '\n';
|
||||||
|
process.stderr.write(line);
|
||||||
|
} catch (_) {}
|
||||||
|
};
|
||||||
|
console.log = _stderrWrite;
|
||||||
|
console.info = _stderrWrite;
|
||||||
|
console.warn = _stderrWrite;
|
||||||
|
console.error = _stderrWrite;
|
||||||
|
console.debug = _stderrWrite;
|
||||||
|
|
||||||
if (process.argv.includes('--extract-addons')) {
|
if (process.argv.includes('--extract-addons')) {
|
||||||
(async () => {
|
(async () => {
|
||||||
const addonPackages = [
|
const addonPackages = [
|
||||||
@@ -49,13 +65,8 @@ if (process.argv.includes('--extract-addons')) {
|
|||||||
const input = process.stdin;
|
const input = process.stdin;
|
||||||
const output = process.stdout;
|
const output = process.stdout;
|
||||||
|
|
||||||
// On Windows, native messaging requires binary I/O (no CRLF translation).
|
// Do NOT setEncoding('binary') — that makes 'data' emit strings and breaks
|
||||||
if (input.setRawMode) {
|
// length-prefixed Buffer framing with Chrome's native messaging pipes.
|
||||||
input.setEncoding?.('binary');
|
|
||||||
}
|
|
||||||
if (output.setDefaultEncoding) {
|
|
||||||
output.setDefaultEncoding?.('binary');
|
|
||||||
}
|
|
||||||
|
|
||||||
const messenger = createMessenger({
|
const messenger = createMessenger({
|
||||||
input,
|
input,
|
||||||
@@ -75,15 +86,27 @@ if (process.argv.includes('--extract-addons')) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function shutdown() {
|
function shutdown() {
|
||||||
cleanup();
|
try {
|
||||||
messenger.destroy();
|
cleanup();
|
||||||
|
} catch (_) {}
|
||||||
|
try {
|
||||||
|
messenger.destroy();
|
||||||
|
} catch (_) {}
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on('exit', () => cleanup());
|
process.on('exit', () => {
|
||||||
|
try {
|
||||||
|
cleanup();
|
||||||
|
} catch (_) {}
|
||||||
|
});
|
||||||
process.on('SIGTERM', shutdown);
|
process.on('SIGTERM', shutdown);
|
||||||
process.on('SIGINT', shutdown);
|
process.on('SIGINT', shutdown);
|
||||||
|
|
||||||
|
// Chrome closes stdin when the port disconnects — exit cleanly.
|
||||||
|
input.on('end', shutdown);
|
||||||
|
input.on('close', shutdown);
|
||||||
|
|
||||||
logErr('ready');
|
logErr('ready');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logErr(`startup error: ${err.message}`);
|
logErr(`startup error: ${err.message}`);
|
||||||
|
|||||||
@@ -22,12 +22,25 @@ function createMessenger({ input, output, onMessage, onError }) {
|
|||||||
|
|
||||||
function fail(err) {
|
function fail(err) {
|
||||||
if (onError) onError(err);
|
if (onError) onError(err);
|
||||||
else console.error(err);
|
else {
|
||||||
|
try {
|
||||||
|
process.stderr.write(String(err && err.stack ? err.stack : err) + '\n');
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toBuffer(chunk) {
|
||||||
|
if (!chunk) return null;
|
||||||
|
if (Buffer.isBuffer(chunk)) return chunk;
|
||||||
|
if (chunk instanceof Uint8Array) return Buffer.from(chunk);
|
||||||
|
if (typeof chunk === 'string') return Buffer.from(chunk, 'latin1');
|
||||||
|
return Buffer.from(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
function processChunk(chunk) {
|
function processChunk(chunk) {
|
||||||
if (!chunk || chunk.length === 0) return;
|
const buf = toBuffer(chunk);
|
||||||
buffer = Buffer.concat([buffer, chunk]);
|
if (!buf || buf.length === 0) return;
|
||||||
|
buffer = Buffer.concat([buffer, buf]);
|
||||||
|
|
||||||
while (buffer.length >= needed) {
|
while (buffer.length >= needed) {
|
||||||
const slice = buffer.subarray(0, needed);
|
const slice = buffer.subarray(0, needed);
|
||||||
@@ -61,7 +74,7 @@ function createMessenger({ input, output, onMessage, onError }) {
|
|||||||
|
|
||||||
input.on('data', processChunk);
|
input.on('data', processChunk);
|
||||||
input.on('error', fail);
|
input.on('error', fail);
|
||||||
input.resume?.();
|
if (typeof input.resume === 'function') input.resume();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
@@ -71,11 +84,10 @@ function createMessenger({ input, output, onMessage, onError }) {
|
|||||||
send(msg) {
|
send(msg) {
|
||||||
try {
|
try {
|
||||||
const json = JSON.stringify(msg);
|
const json = JSON.stringify(msg);
|
||||||
const buf = Buffer.from(json, 'utf8');
|
const body = Buffer.from(json, 'utf8');
|
||||||
const len = Buffer.allocUnsafe(4);
|
const len = Buffer.allocUnsafe(4);
|
||||||
len.writeUInt32LE(buf.length, 0);
|
len.writeUInt32LE(body.length, 0);
|
||||||
output.write(len);
|
output.write(Buffer.concat([len, body]));
|
||||||
output.write(buf);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
fail(e);
|
fail(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Pin macOS TMPDIR (signed addons) and default Corestore path next to the
|
||||||
|
* executable. Chrome often launches native hosts with cwd=/ and no env.
|
||||||
|
* Must run before any module that loads native addons / opens storage.
|
||||||
|
*/
|
||||||
|
import path from 'bare-path';
|
||||||
|
|
||||||
|
if (typeof process.execPath === 'string') {
|
||||||
|
try {
|
||||||
|
const dir = path.dirname(process.execPath);
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
process.env.TMPDIR = path.join(dir, 'tmp');
|
||||||
|
}
|
||||||
|
if (!process.env.BRIDGE_SWARM_STORAGE) {
|
||||||
|
process.env.BRIDGE_SWARM_STORAGE = path.join(dir, 'bridge-swarm-storage');
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
"build:hrpc": "node scripts/build-hrpc.js",
|
"build:hrpc": "node scripts/build-hrpc.js",
|
||||||
"build": "npm run build:host && npm run build:protomux && npm run build:hrpc",
|
"build": "npm run build:host && npm run build:protomux && npm run build:hrpc",
|
||||||
"pack": "node scripts/pack-extension.js",
|
"pack": "node scripts/pack-extension.js",
|
||||||
|
"examples": "node scripts/serve-examples.js",
|
||||||
"build:dist": "node scripts/build-distributable.js",
|
"build:dist": "node scripts/build-distributable.js",
|
||||||
"build:dist:all": "node scripts/build-distributable.js --all",
|
"build:dist:all": "node scripts/build-distributable.js --all",
|
||||||
"build:dist:mac": "node scripts/build-distributable.js --host darwin-arm64 --host darwin-x64",
|
"build:dist:mac": "node scripts/build-distributable.js --host darwin-arm64 --host darwin-x64",
|
||||||
|
|||||||
+9
-4
@@ -111,12 +111,14 @@ if [[ "$PLATFORM" == "darwin" ]]; then
|
|||||||
printf '%s\n' '<?xml version="1.0" encoding="UTF-8"?>' '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' '<plist version="1.0"><dict><key>com.apple.security.cs.disable-library-validation</key><true/></dict></plist>' > "$ENTITLEMENTS_PLIST"
|
printf '%s\n' '<?xml version="1.0" encoding="UTF-8"?>' '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' '<plist version="1.0"><dict><key>com.apple.security.cs.disable-library-validation</key><true/></dict></plist>' > "$ENTITLEMENTS_PLIST"
|
||||||
codesign --force --sign - --entitlements "$ENTITLEMENTS_PLIST" "$HOST_BIN" 2>/dev/null || true
|
codesign --force --sign - --entitlements "$ENTITLEMENTS_PLIST" "$HOST_BIN" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Optional helper launcher (manual runs). Native messaging uses the Mach-O
|
||||||
|
# binary directly — set-tmpdir.mjs inside the binary pins TMPDIR for signed addons.
|
||||||
LAUNCHER="${HOST_DIR}/run-bridge-swarm-host.sh"
|
LAUNCHER="${HOST_DIR}/run-bridge-swarm-host.sh"
|
||||||
printf '%s\n' '#!/bin/bash' 'DIR="$(cd "$(dirname "$0")" && pwd)"' 'export TMPDIR="${DIR}/tmp"' 'export BRIDGE_SWARM_STORAGE="${BRIDGE_SWARM_STORAGE:-${DIR}/bridge-swarm-storage}"' 'exec "${DIR}/bridge-swarm-host" "$@"' > "$LAUNCHER"
|
printf '%s\n' '#!/bin/bash' 'DIR="$(cd "$(dirname "$0")" && pwd)"' 'export TMPDIR="${DIR}/tmp"' 'export BRIDGE_SWARM_STORAGE="${BRIDGE_SWARM_STORAGE:-${DIR}/bridge-swarm-storage}"' 'exec "${DIR}/bridge-swarm-host" "$@"' > "$LAUNCHER"
|
||||||
chmod +x "$LAUNCHER"
|
chmod +x "$LAUNCHER"
|
||||||
|
|
||||||
echo " Extracting native addons (--extract-addons)..."
|
echo " Extracting native addons (--extract-addons)..."
|
||||||
"$LAUNCHER" --extract-addons 2>/dev/null || true
|
TMPDIR="$ADDON_TMPDIR" "$HOST_BIN" --extract-addons 2>/dev/null || true
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
SIGNED=0
|
SIGNED=0
|
||||||
@@ -127,16 +129,19 @@ if [[ "$PLATFORM" == "darwin" ]]; then
|
|||||||
done < <(find "$ADDON_TMPDIR" \( -name "*.bare" -o -name "*.dylib" \) -print0 2>/dev/null)
|
done < <(find "$ADDON_TMPDIR" \( -name "*.bare" -o -name "*.dylib" \) -print0 2>/dev/null)
|
||||||
fi
|
fi
|
||||||
echo " Signed ${SIGNED} native addons; main binary has library-validation disabled"
|
echo " Signed ${SIGNED} native addons; main binary has library-validation disabled"
|
||||||
HOST_BIN="$LAUNCHER"
|
# Keep HOST_BIN as the Mach-O binary for Chrome native messaging
|
||||||
else
|
else
|
||||||
# Linux launcher sets storage path next to the binary
|
# Linux: binary path is fine; set-tmpdir is macOS-only
|
||||||
HOST_DIR="$(dirname "$HOST_BIN")"
|
HOST_DIR="$(dirname "$HOST_BIN")"
|
||||||
LAUNCHER="${HOST_DIR}/run-bridge-swarm-host.sh"
|
LAUNCHER="${HOST_DIR}/run-bridge-swarm-host.sh"
|
||||||
printf '%s\n' '#!/bin/bash' 'DIR="$(cd "$(dirname "$0")" && pwd)"' 'export BRIDGE_SWARM_STORAGE="${BRIDGE_SWARM_STORAGE:-${DIR}/bridge-swarm-storage}"' 'exec "${DIR}/bridge-swarm-host" "$@"' > "$LAUNCHER"
|
printf '%s\n' '#!/bin/bash' 'DIR="$(cd "$(dirname "$0")" && pwd)"' 'export BRIDGE_SWARM_STORAGE="${BRIDGE_SWARM_STORAGE:-${DIR}/bridge-swarm-storage}"' 'exec "${DIR}/bridge-swarm-host" "$@"' > "$LAUNCHER"
|
||||||
chmod +x "$LAUNCHER"
|
chmod +x "$LAUNCHER"
|
||||||
HOST_BIN="$LAUNCHER"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Default storage next to install when launched by the browser
|
||||||
|
# (bare host also honors BRIDGE_SWARM_STORAGE if set by a wrapper)
|
||||||
|
export BRIDGE_SWARM_STORAGE="${BRIDGE_SWARM_STORAGE:-${INSTALL_DIR}/bridge-swarm-storage}"
|
||||||
|
|
||||||
echo " Binary: $HOST_BIN"
|
echo " Binary: $HOST_BIN"
|
||||||
|
|
||||||
# ── Extension downloads ────────────────────────────────────────────────────────
|
# ── Extension downloads ────────────────────────────────────────────────────────
|
||||||
|
|||||||
Executable
+122
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Serve BridgeSwarm examples over http://localhost so they share one origin.
|
||||||
|
* Modern Chrome/Firefox treat each file:// URL as a unique opaque origin, which
|
||||||
|
* breaks local HTML demos (scripts, styles, extension injection).
|
||||||
|
*
|
||||||
|
* Usage: npm run examples
|
||||||
|
* node scripts/serve-examples.js [port]
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const http = require('http');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
|
||||||
|
const ROOT = path.resolve(__dirname, '..', 'examples');
|
||||||
|
const PORT = Number(process.argv[2]) || Number(process.env.PORT) || 4173;
|
||||||
|
const HOST = process.env.HOST || '127.0.0.1';
|
||||||
|
|
||||||
|
const MIME = {
|
||||||
|
'.html': 'text/html; charset=utf-8',
|
||||||
|
'.js': 'text/javascript; charset=utf-8',
|
||||||
|
'.mjs': 'text/javascript; charset=utf-8',
|
||||||
|
'.css': 'text/css; charset=utf-8',
|
||||||
|
'.json': 'application/json; charset=utf-8',
|
||||||
|
'.svg': 'image/svg+xml',
|
||||||
|
'.png': 'image/png',
|
||||||
|
'.jpg': 'image/jpeg',
|
||||||
|
'.jpeg': 'image/jpeg',
|
||||||
|
'.gif': 'image/gif',
|
||||||
|
'.webp': 'image/webp',
|
||||||
|
'.ico': 'image/x-icon',
|
||||||
|
'.woff': 'font/woff',
|
||||||
|
'.woff2': 'font/woff2',
|
||||||
|
'.map': 'application/json',
|
||||||
|
'.txt': 'text/plain; charset=utf-8',
|
||||||
|
'.md': 'text/markdown; charset=utf-8',
|
||||||
|
};
|
||||||
|
|
||||||
|
function safeJoin(root, urlPath) {
|
||||||
|
const decoded = decodeURIComponent((urlPath || '/').split('?')[0]);
|
||||||
|
const cleaned = path.normalize(decoded).replace(/^(\.\.[/\\])+/, '');
|
||||||
|
const full = path.join(root, cleaned);
|
||||||
|
if (!full.startsWith(root)) return null;
|
||||||
|
return full;
|
||||||
|
}
|
||||||
|
|
||||||
|
function send(res, status, body, headers = {}) {
|
||||||
|
res.writeHead(status, {
|
||||||
|
'Cache-Control': 'no-store',
|
||||||
|
...headers,
|
||||||
|
});
|
||||||
|
res.end(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
function contentType(filePath) {
|
||||||
|
return MIME[path.extname(filePath).toLowerCase()] || 'application/octet-stream';
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = http.createServer((req, res) => {
|
||||||
|
let urlPath = req.url || '/';
|
||||||
|
if (urlPath === '/') urlPath = '/index.html';
|
||||||
|
|
||||||
|
const filePath = safeJoin(ROOT, urlPath);
|
||||||
|
if (!filePath) {
|
||||||
|
send(res, 403, 'Forbidden');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.stat(filePath, (err, st) => {
|
||||||
|
if (!err && st.isDirectory()) {
|
||||||
|
const indexPath = path.join(filePath, 'index.html');
|
||||||
|
fs.readFile(indexPath, (err2, data) => {
|
||||||
|
if (err2) {
|
||||||
|
send(res, 404, 'Not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send(res, 200, data, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.readFile(filePath, (err2, data) => {
|
||||||
|
if (err2) {
|
||||||
|
send(res, 404, 'Not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send(res, 200, data, { 'Content-Type': contentType(filePath) });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(PORT, HOST, () => {
|
||||||
|
const base = `http://${HOST}:${PORT}`;
|
||||||
|
console.log('');
|
||||||
|
console.log('BridgeSwarm examples');
|
||||||
|
console.log('====================');
|
||||||
|
console.log(`Serving ${ROOT}`);
|
||||||
|
console.log(`Open: ${base}/`);
|
||||||
|
console.log('');
|
||||||
|
console.log('Demos:');
|
||||||
|
console.log(` ${base}/chat/`);
|
||||||
|
console.log(` ${base}/chat-advanced/`);
|
||||||
|
console.log(` ${base}/sdk-demo/`);
|
||||||
|
console.log(` ${base}/hrpc-demo/`);
|
||||||
|
console.log(` ${base}/whiteboard/`);
|
||||||
|
console.log(` ${base}/screenshare/`);
|
||||||
|
console.log(` ${base}/data-demo/`);
|
||||||
|
console.log('');
|
||||||
|
console.log('Do not open examples via file:// — Chrome treats each file as a unique origin.');
|
||||||
|
console.log('Press Ctrl+C to stop.');
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
const openUrl = `${base}/`;
|
||||||
|
if (process.env.BRIDGESWARM_NO_OPEN !== '1') {
|
||||||
|
const platform = process.platform;
|
||||||
|
const cmd =
|
||||||
|
platform === 'darwin' ? `open "${openUrl}"` : platform === 'win32' ? `start "" "${openUrl}"` : `xdg-open "${openUrl}"`;
|
||||||
|
exec(cmd, () => {});
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user