@nativewindow/webview 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +341 -0
- package/dist/index.d.ts +268 -0
- package/dist/index.js +406 -0
- package/native-window.d.ts +264 -0
- package/native-window.js +62 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Francesco Saverio Cannizzaro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/public/native-window.webp" alt="native-window" width="200" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# @nativewindow/webview
|
|
6
|
+
|
|
7
|
+
[](https://github.com/nativewindow/webview/actions/workflows/ci.yml)
|
|
8
|
+
[](https://www.npmjs.com/package/@nativewindow/webview)
|
|
9
|
+
[](https://www.npmjs.com/package/@nativewindow/ipc)
|
|
10
|
+
[](https://www.npmjs.com/package/@nativewindow/react)
|
|
11
|
+
[](https://www.npmjs.com/package/@nativewindow/tsdb)
|
|
12
|
+
|
|
13
|
+
> [!WARNING]
|
|
14
|
+
> This project is in **alpha**. APIs may change without notice and some features may be incomplete or unstable.
|
|
15
|
+
|
|
16
|
+
Native OS webviews for Bun, Deno & Node.js. Create real desktop windows with embedded web content using [wry](https://github.com/tauri-apps/wry) + [tao](https://github.com/tauri-apps/tao) — providing WebKit on macOS and Linux, and WebView2 on Windows.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Native webviews** — powered by [wry](https://github.com/tauri-apps/wry) + [tao](https://github.com/tauri-apps/tao) (WebKit on macOS/Linux, WebView2 on Windows), no Electron or Chromium bundled
|
|
21
|
+
- **Multi-window** — create and manage multiple independent windows
|
|
22
|
+
- **HTML & URL loading** — load inline HTML strings or navigate to URLs
|
|
23
|
+
- **Bidirectional IPC** — send messages between Bun/Deno/Node and the webview
|
|
24
|
+
- **Typesafe IPC channels** — typed message layer with schema-based validation and compile-time checked event maps
|
|
25
|
+
- **Full window control** — title, size, position, min/max size, decorations, transparency, always-on-top
|
|
26
|
+
- **Window events** — close, resize, move, focus, blur, page load, title change
|
|
27
|
+
- **Rust + napi-rs + wry + tao** — high-performance native addon, no runtime overhead
|
|
28
|
+
- **Runtime detection** — check for WebView2 availability and auto-install on Windows
|
|
29
|
+
|
|
30
|
+
## Packages
|
|
31
|
+
|
|
32
|
+
| Package | Description |
|
|
33
|
+
| ----------------------------------------------------------- | --------------------------------------------------------- |
|
|
34
|
+
| [`@nativewindow/webview`](./packages/webview) | Rust napi-rs addon providing native window + webview APIs |
|
|
35
|
+
| [`@nativewindow/ipc`](./packages/ipc) | Pure TypeScript typesafe IPC channel layer |
|
|
36
|
+
| [`@nativewindow/react`](./packages/react) | React bindings for the typed IPC layer |
|
|
37
|
+
| [`@nativewindow/tsdb`](./packages/tsdb) | TanStack DB collection adapter for native-window IPC |
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { init, pumpEvents, NativeWindow } from "native-window";
|
|
43
|
+
|
|
44
|
+
init();
|
|
45
|
+
const pump = setInterval(() => pumpEvents(), 16);
|
|
46
|
+
|
|
47
|
+
const win = new NativeWindow({
|
|
48
|
+
title: "My App",
|
|
49
|
+
width: 800,
|
|
50
|
+
height: 600,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
win.loadHtml(`
|
|
54
|
+
<h1>Hello from native webview!</h1>
|
|
55
|
+
<button onclick="window.ipc.postMessage('clicked')">Click me</button>
|
|
56
|
+
`);
|
|
57
|
+
|
|
58
|
+
win.onMessage((msg) => {
|
|
59
|
+
console.log("From webview:", msg);
|
|
60
|
+
win.postMessage(`Echo: ${msg}`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
win.onClose(() => {
|
|
64
|
+
clearInterval(pump);
|
|
65
|
+
process.exit(0);
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Typed IPC
|
|
70
|
+
|
|
71
|
+
Use `native-window-ipc` for compile-time checked messaging between Bun/Deno/Node and the webview. Schemas provide both types and runtime validation.
|
|
72
|
+
|
|
73
|
+
### Host side (Bun/Deno/Node)
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { z } from "zod";
|
|
77
|
+
import { createWindow } from "native-window-ipc";
|
|
78
|
+
|
|
79
|
+
const ch = createWindow(
|
|
80
|
+
{ title: "Typed IPC" },
|
|
81
|
+
{
|
|
82
|
+
schemas: {
|
|
83
|
+
"user-click": z.object({ x: z.number(), y: z.number() }),
|
|
84
|
+
"update-title": z.string(),
|
|
85
|
+
counter: z.number(),
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
ch.on("user-click", (pos) => {
|
|
91
|
+
// pos: { x: number; y: number }
|
|
92
|
+
console.log(`Click at ${pos.x}, ${pos.y}`);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
ch.on("counter", (n) => {
|
|
96
|
+
// n: number
|
|
97
|
+
ch.send("update-title", `Count: ${n}`);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// ch.send("counter", "wrong"); // Type error!
|
|
101
|
+
// ch.send("typo", 123); // Type error!
|
|
102
|
+
|
|
103
|
+
ch.window.loadHtml(`<html>...</html>`);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Webview side (inline HTML)
|
|
107
|
+
|
|
108
|
+
The `__channel__` object is auto-injected by `createWindow` / `createChannel`:
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<script>
|
|
112
|
+
__channel__.send("user-click", { x: 10, y: 20 });
|
|
113
|
+
__channel__.on("update-title", (title) => {
|
|
114
|
+
document.title = title;
|
|
115
|
+
});
|
|
116
|
+
</script>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Webview side (bundled app)
|
|
120
|
+
|
|
121
|
+
For webview apps bundled with their own build step, import the client directly:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { z } from "zod";
|
|
125
|
+
import { createChannelClient } from "native-window-ipc/client";
|
|
126
|
+
|
|
127
|
+
const ch = createChannelClient({
|
|
128
|
+
schemas: {
|
|
129
|
+
counter: z.number(),
|
|
130
|
+
"update-title": z.string(),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
ch.send("counter", 42); // Typed!
|
|
134
|
+
ch.on("update-title", (t) => {
|
|
135
|
+
// t: string
|
|
136
|
+
document.title = t;
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Runtime Detection
|
|
141
|
+
|
|
142
|
+
On Windows 10, the WebView2 runtime may not be installed. Use `checkRuntime()` to detect it and `ensureRuntime()` to auto-install if missing.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { checkRuntime, ensureRuntime } from "native-window";
|
|
146
|
+
|
|
147
|
+
const info = checkRuntime();
|
|
148
|
+
console.log(info);
|
|
149
|
+
// { available: true, version: "128.0.2739.42", platform: "windows" }
|
|
150
|
+
// { available: false, version: undefined, platform: "windows" }
|
|
151
|
+
// { available: true, version: undefined, platform: "macos" }
|
|
152
|
+
// { available: true, version: undefined, platform: "linux" }
|
|
153
|
+
|
|
154
|
+
if (!info.available) {
|
|
155
|
+
console.log("WebView2 not found, installing...");
|
|
156
|
+
const result = ensureRuntime(); // downloads ~2MB bootstrapper, runs silently
|
|
157
|
+
console.log("Installed:", result.version);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
On macOS, both functions return `{ available: true }` immediately — WKWebView is a system framework. On Linux, both functions also return `{ available: true }` — WebKitGTK is assumed to be installed. On Windows 11, WebView2 is pre-installed.
|
|
162
|
+
|
|
163
|
+
## API Reference
|
|
164
|
+
|
|
165
|
+
### `native-window`
|
|
166
|
+
|
|
167
|
+
#### `init()`
|
|
168
|
+
|
|
169
|
+
Initialize the native window system. Must be called once before creating any windows.
|
|
170
|
+
|
|
171
|
+
#### `pumpEvents()`
|
|
172
|
+
|
|
173
|
+
Process pending native UI events. Call periodically (~16ms via `setInterval`) to keep windows responsive.
|
|
174
|
+
|
|
175
|
+
#### `checkRuntime(): RuntimeInfo`
|
|
176
|
+
|
|
177
|
+
Check if the native webview runtime is available. Returns `{ available: boolean, version?: string, platform: "macos" | "windows" | "linux" | "unsupported" }`.
|
|
178
|
+
|
|
179
|
+
#### `ensureRuntime(): RuntimeInfo`
|
|
180
|
+
|
|
181
|
+
Check for the runtime and install it if missing (Windows only). Downloads the WebView2 Evergreen Bootstrapper (~2MB) from Microsoft and runs it silently. Throws on failure.
|
|
182
|
+
|
|
183
|
+
#### `new NativeWindow(options?)`
|
|
184
|
+
|
|
185
|
+
Create a native window with an embedded webview.
|
|
186
|
+
|
|
187
|
+
**WindowOptions:**
|
|
188
|
+
|
|
189
|
+
| Option | Type | Default | Description |
|
|
190
|
+
| ------------------------ | --------- | ------- | ----------------------------- |
|
|
191
|
+
| `title` | `string` | `""` | Window title |
|
|
192
|
+
| `width` | `number` | `800` | Inner width (logical pixels) |
|
|
193
|
+
| `height` | `number` | `600` | Inner height (logical pixels) |
|
|
194
|
+
| `x` | `number` | — | X position |
|
|
195
|
+
| `y` | `number` | — | Y position |
|
|
196
|
+
| `minWidth` / `minHeight` | `number` | — | Minimum size |
|
|
197
|
+
| `maxWidth` / `maxHeight` | `number` | — | Maximum size |
|
|
198
|
+
| `resizable` | `boolean` | `true` | Allow resizing |
|
|
199
|
+
| `decorations` | `boolean` | `true` | Show title bar and borders |
|
|
200
|
+
| `transparent` | `boolean` | `false` | Transparent background |
|
|
201
|
+
| `alwaysOnTop` | `boolean` | `false` | Float above other windows |
|
|
202
|
+
| `visible` | `boolean` | `true` | Initially visible |
|
|
203
|
+
| `devtools` | `boolean` | `false` | Enable devtools |
|
|
204
|
+
|
|
205
|
+
**Content methods:**
|
|
206
|
+
|
|
207
|
+
| Method | Description |
|
|
208
|
+
| --------------------------- | ------------------------------------------------------------ |
|
|
209
|
+
| `loadUrl(url)` | Navigate to a URL |
|
|
210
|
+
| `loadHtml(html)` | Load an HTML string |
|
|
211
|
+
| `unsafe.evaluateJs(script)` | Execute JS in the webview (fire-and-forget) |
|
|
212
|
+
| `postMessage(msg)` | Send a string to the webview via `window.__native_message__` |
|
|
213
|
+
|
|
214
|
+
**Window control:**
|
|
215
|
+
|
|
216
|
+
| Method | Description |
|
|
217
|
+
| -------------------------------------------- | ---------------------------- |
|
|
218
|
+
| `setTitle(title)` | Set the window title |
|
|
219
|
+
| `setSize(w, h)` | Set the window size |
|
|
220
|
+
| `setMinSize(w, h)` / `setMaxSize(w, h)` | Set size constraints |
|
|
221
|
+
| `setPosition(x, y)` | Set window position |
|
|
222
|
+
| `setResizable(bool)` | Toggle resizability |
|
|
223
|
+
| `setDecorations(bool)` | Toggle decorations |
|
|
224
|
+
| `setAlwaysOnTop(bool)` | Toggle always-on-top |
|
|
225
|
+
| `show()` / `hide()` | Show or hide the window |
|
|
226
|
+
| `close()` | Close and destroy the window |
|
|
227
|
+
| `focus()` | Bring the window to focus |
|
|
228
|
+
| `maximize()` / `minimize()` / `unmaximize()` | Window state |
|
|
229
|
+
|
|
230
|
+
**Events:**
|
|
231
|
+
|
|
232
|
+
| Method | Callback signature |
|
|
233
|
+
| ---------------------------- | ------------------------------------------------------- |
|
|
234
|
+
| `onMessage(cb)` | `(message: string) => void` |
|
|
235
|
+
| `onClose(cb)` | `() => void` |
|
|
236
|
+
| `onResize(cb)` | `(width: number, height: number) => void` |
|
|
237
|
+
| `onMove(cb)` | `(x: number, y: number) => void` |
|
|
238
|
+
| `onFocus(cb)` / `onBlur(cb)` | `() => void` |
|
|
239
|
+
| `onPageLoad(cb)` | `(event: "started" \| "finished", url: string) => void` |
|
|
240
|
+
| `onTitleChanged(cb)` | `(title: string) => void` |
|
|
241
|
+
|
|
242
|
+
### `native-window-ipc`
|
|
243
|
+
|
|
244
|
+
#### `createChannel<S>(win, options): NativeWindowChannel<InferSchemaMap<S>>`
|
|
245
|
+
|
|
246
|
+
Wrap an existing `NativeWindow` with a typed message channel. Schemas are required. Auto-injects the webview client script (disable with `{ injectClient: false }`).
|
|
247
|
+
|
|
248
|
+
#### `createWindow<S>(windowOptions, channelOptions): NativeWindowChannel<InferSchemaMap<S>>`
|
|
249
|
+
|
|
250
|
+
Convenience: creates a `NativeWindow` and wraps it with `createChannel`.
|
|
251
|
+
|
|
252
|
+
#### `getClientScript(): string`
|
|
253
|
+
|
|
254
|
+
Returns the webview-side client as a self-contained JS string for manual injection.
|
|
255
|
+
|
|
256
|
+
#### `createChannelClient<S>(options): TypedChannel<InferSchemaMap<S>>` (from `native-window-ipc/client`)
|
|
257
|
+
|
|
258
|
+
Create a typed channel client inside the webview. Schemas are required. For use in bundled webview apps.
|
|
259
|
+
|
|
260
|
+
#### `TypedChannel<T>`
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
interface TypedChannel<T extends EventMap> {
|
|
264
|
+
send<K extends keyof T & string>(type: K, payload: T[K]): void;
|
|
265
|
+
on<K extends keyof T & string>(type: K, handler: (payload: T[K]) => void): void;
|
|
266
|
+
off<K extends keyof T & string>(type: K, handler: (payload: T[K]) => void): void;
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Security
|
|
271
|
+
|
|
272
|
+
All security hardening is compiled in by default on all supported platforms — no build-time feature flags required.
|
|
273
|
+
|
|
274
|
+
- **URL scheme blocking** — `javascript:`, `file:`, `data:`, and `blob:` navigations are blocked at the native layer
|
|
275
|
+
- **Content Security Policy** — inject a CSP via the `csp` option in `WindowOptions`
|
|
276
|
+
- **Trusted origin filtering** — restrict IPC messages and client injection to specific origins at the native and IPC layers
|
|
277
|
+
- **Webview surface hardening** — context menus, status bar, and built-in error page are disabled on Windows
|
|
278
|
+
- **IPC bridge hardening** — `window.ipc` and `window.__channel__` are frozen, non-writable objects
|
|
279
|
+
- **Message size limits** — 10 MB hard limit at the native layer, configurable 1 MB default at the IPC layer
|
|
280
|
+
- **Schema-based validation** — all incoming IPC payloads are validated at runtime against user-defined schemas
|
|
281
|
+
|
|
282
|
+
See the [Security documentation](https://native-window.dev/docs/security) for the full threat model and best practices.
|
|
283
|
+
|
|
284
|
+
## Building
|
|
285
|
+
|
|
286
|
+
### Prerequisites
|
|
287
|
+
|
|
288
|
+
- [Bun](https://bun.sh) (v1.3+), [Deno](https://deno.com) (v2+), or [Node.js](https://nodejs.org) (v18+)
|
|
289
|
+
- [Rust](https://rustup.rs) (stable)
|
|
290
|
+
- macOS, Windows, or Linux (for native compilation)
|
|
291
|
+
- On Linux: WebKitGTK development headers (e.g. `libwebkit2gtk-4.1-dev` on Ubuntu/Debian)
|
|
292
|
+
|
|
293
|
+
### Install dependencies
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
bun install
|
|
297
|
+
# or
|
|
298
|
+
deno install
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Build the native addon
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
cd packages/webview
|
|
305
|
+
bun run build # release build
|
|
306
|
+
bun run build:debug # debug build
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
The build targets the current platform. Cross-compilation targets are configured in `packages/webview/package.json` under `napi.triples`.
|
|
310
|
+
|
|
311
|
+
## Samples
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
# Raw IPC example
|
|
315
|
+
bun samples/basic.ts
|
|
316
|
+
|
|
317
|
+
# Typed IPC example
|
|
318
|
+
bun samples/typed-ipc.ts
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Testing
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
# Run the IPC channel tests
|
|
325
|
+
cd packages/ipc
|
|
326
|
+
bun test
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Known Limitations
|
|
330
|
+
|
|
331
|
+
- **~16ms event latency** from the `pumpEvents()` polling interval
|
|
332
|
+
- **HTML null origin** — content loaded via `loadHtml()` has a null CORS origin; use a custom protocol or `loadUrl()` for fetch/XHR
|
|
333
|
+
- **Windows 10** may require the [WebView2 Runtime](https://developer.microsoft.com/en-us/microsoft-edge/webview2/) — use `ensureRuntime()` to auto-install (included by default on Windows 11)
|
|
334
|
+
- **Linux** requires [WebKitGTK](https://webkitgtk.org/) to be installed (e.g. `libwebkit2gtk-4.1-dev` on Ubuntu/Debian)
|
|
335
|
+
- **No return values from `unsafe.evaluateJs()`** — use `postMessage`/`onMessage` to send results back
|
|
336
|
+
- **2 MB HTML limit on Windows** when using `loadHtml()`
|
|
337
|
+
- **Use `bun --watch`** instead of `bun --hot` for development (native addon reloading requires a process restart)
|
|
338
|
+
|
|
339
|
+
## License
|
|
340
|
+
|
|
341
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { checkRuntime, ensureRuntime, loadHtmlOrigin } from '../native-window.js';
|
|
2
|
+
export { checkRuntime, ensureRuntime, loadHtmlOrigin };
|
|
3
|
+
export type { WindowOptions, RuntimeInfo } from '../native-window.js';
|
|
4
|
+
/**
|
|
5
|
+
* Operations that execute arbitrary code in the webview context.
|
|
6
|
+
* Grouped under {@link NativeWindow.unsafe} to signal injection risk.
|
|
7
|
+
*
|
|
8
|
+
* @security Never pass unsanitized user input to these methods.
|
|
9
|
+
* Use {@link sanitizeForJs} to escape strings before embedding them in
|
|
10
|
+
* script code.
|
|
11
|
+
*/
|
|
12
|
+
export interface UnsafeNamespace {
|
|
13
|
+
/**
|
|
14
|
+
* Evaluate arbitrary JavaScript in the webview context.
|
|
15
|
+
* Fire-and-forget — there is no return value.
|
|
16
|
+
* Use `postMessage`/`onMessage` to send results back.
|
|
17
|
+
*
|
|
18
|
+
* @security **Injection risk.** Never pass unsanitized user input directly.
|
|
19
|
+
* Use {@link sanitizeForJs} to escape strings before embedding them in
|
|
20
|
+
* script code.
|
|
21
|
+
*/
|
|
22
|
+
evaluateJs(script: string): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Control the browser devtools panel for this window's webview.
|
|
26
|
+
* Grouped under {@link NativeWindow.devtools} for discoverability.
|
|
27
|
+
*
|
|
28
|
+
* Requires `devtools: true` in {@link WindowOptions} at window creation.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const win = new NativeWindow({ devtools: true });
|
|
33
|
+
* win.devtools.open();
|
|
34
|
+
* console.log(win.devtools.isOpen()); // true
|
|
35
|
+
* win.devtools.close();
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export interface DevtoolsNamespace {
|
|
39
|
+
/** Open the browser devtools panel. */
|
|
40
|
+
open(): void;
|
|
41
|
+
/** Close the browser devtools panel. */
|
|
42
|
+
close(): void;
|
|
43
|
+
/** Check whether the devtools panel is currently open. */
|
|
44
|
+
isOpen(): boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Information about a cookie from the native cookie store.
|
|
48
|
+
* Includes `HttpOnly` cookies that are invisible to `document.cookie`.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* win.onCookies((cookies) => {
|
|
53
|
+
* for (const c of cookies) {
|
|
54
|
+
* console.log(c.name, c.value, c.httpOnly);
|
|
55
|
+
* }
|
|
56
|
+
* });
|
|
57
|
+
* win.getCookies("https://example.com");
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export interface CookieInfo {
|
|
61
|
+
/** Cookie name. */
|
|
62
|
+
name: string;
|
|
63
|
+
/** Cookie value. */
|
|
64
|
+
value: string;
|
|
65
|
+
/** Domain the cookie belongs to. */
|
|
66
|
+
domain: string;
|
|
67
|
+
/** Path the cookie is restricted to. */
|
|
68
|
+
path: string;
|
|
69
|
+
/** Whether the cookie is HttpOnly (inaccessible to JS). */
|
|
70
|
+
httpOnly: boolean;
|
|
71
|
+
/** Whether the cookie requires HTTPS. */
|
|
72
|
+
secure: boolean;
|
|
73
|
+
/** SameSite policy: "none", "lax", or "strict". */
|
|
74
|
+
sameSite: string;
|
|
75
|
+
/** Expiry as Unix timestamp (seconds). -1 for session cookies. */
|
|
76
|
+
expires: number;
|
|
77
|
+
}
|
|
78
|
+
type WindowOptions = import('../native-window.js').WindowOptions;
|
|
79
|
+
/**
|
|
80
|
+
* A native OS window with an embedded webview.
|
|
81
|
+
*
|
|
82
|
+
* Automatically initializes the native subsystem and starts pumping
|
|
83
|
+
* events on first construction. Stops the pump when all windows close.
|
|
84
|
+
*/
|
|
85
|
+
export declare class NativeWindow {
|
|
86
|
+
/** @internal */
|
|
87
|
+
private _native;
|
|
88
|
+
/** @internal */
|
|
89
|
+
private _closed;
|
|
90
|
+
/** @internal */
|
|
91
|
+
private _unsafe?;
|
|
92
|
+
/** @internal */
|
|
93
|
+
private _devtools?;
|
|
94
|
+
constructor(options?: WindowOptions);
|
|
95
|
+
/** @internal */
|
|
96
|
+
private _handleClose;
|
|
97
|
+
/**
|
|
98
|
+
* Throws if the window has been closed.
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
private _ensureOpen;
|
|
102
|
+
private _userCloseCallback?;
|
|
103
|
+
/**
|
|
104
|
+
* Register a handler for the window close event.
|
|
105
|
+
* The pump is automatically stopped when all windows are closed.
|
|
106
|
+
*
|
|
107
|
+
* Calling this multiple times replaces the previous handler.
|
|
108
|
+
*/
|
|
109
|
+
onClose(callback: () => void): void;
|
|
110
|
+
/** Unique window ID */
|
|
111
|
+
get id(): number;
|
|
112
|
+
loadUrl(url: string): void;
|
|
113
|
+
/**
|
|
114
|
+
* Load raw HTML content into the webview.
|
|
115
|
+
*
|
|
116
|
+
* @security **Injection risk.** Never interpolate unsanitized user input
|
|
117
|
+
* into HTML strings. Use a dedicated sanitization library such as
|
|
118
|
+
* [DOMPurify](https://github.com/cure53/DOMPurify) or
|
|
119
|
+
* [sanitize-html](https://github.com/apostrophecms/sanitize-html) to
|
|
120
|
+
* sanitize untrusted content before embedding it.
|
|
121
|
+
*/
|
|
122
|
+
loadHtml(html: string): void;
|
|
123
|
+
postMessage(message: string): void;
|
|
124
|
+
/**
|
|
125
|
+
* Namespace for operations that require extra care to avoid injection risks.
|
|
126
|
+
* Methods under `unsafe` execute arbitrary code in the webview context.
|
|
127
|
+
*
|
|
128
|
+
* @security Never pass unsanitized user input to these methods.
|
|
129
|
+
* Use {@link sanitizeForJs} to escape strings before embedding them in
|
|
130
|
+
* script code.
|
|
131
|
+
*/
|
|
132
|
+
get unsafe(): UnsafeNamespace;
|
|
133
|
+
/**
|
|
134
|
+
* Namespace for controlling the browser devtools panel.
|
|
135
|
+
* Requires `devtools: true` in {@link WindowOptions} at window creation.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* const win = new NativeWindow({ devtools: true });
|
|
140
|
+
* win.devtools.open();
|
|
141
|
+
* console.log(win.devtools.isOpen()); // true
|
|
142
|
+
* win.devtools.close();
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
get devtools(): DevtoolsNamespace;
|
|
146
|
+
setTitle(title: string): void;
|
|
147
|
+
setSize(width: number, height: number): void;
|
|
148
|
+
setMinSize(width: number, height: number): void;
|
|
149
|
+
setMaxSize(width: number, height: number): void;
|
|
150
|
+
setPosition(x: number, y: number): void;
|
|
151
|
+
setResizable(resizable: boolean): void;
|
|
152
|
+
setDecorations(decorations: boolean): void;
|
|
153
|
+
setAlwaysOnTop(alwaysOnTop: boolean): void;
|
|
154
|
+
/**
|
|
155
|
+
* Set the window icon from a PNG or ICO file path.
|
|
156
|
+
* On macOS this is silently ignored (macOS doesn't support per-window icons).
|
|
157
|
+
* Relative paths resolve from the working directory.
|
|
158
|
+
*/
|
|
159
|
+
setIcon(path: string): void;
|
|
160
|
+
show(): void;
|
|
161
|
+
hide(): void;
|
|
162
|
+
close(): void;
|
|
163
|
+
focus(): void;
|
|
164
|
+
maximize(): void;
|
|
165
|
+
minimize(): void;
|
|
166
|
+
unmaximize(): void;
|
|
167
|
+
reload(): void;
|
|
168
|
+
/**
|
|
169
|
+
* Register a handler for messages from the webview.
|
|
170
|
+
*
|
|
171
|
+
* @security **No origin filtering.** The raw `onMessage` API does not
|
|
172
|
+
* enforce origin restrictions. If your webview navigates to untrusted
|
|
173
|
+
* URLs, validate the `sourceUrl` parameter before processing messages.
|
|
174
|
+
* For automatic origin filtering, use `createChannel()` with the
|
|
175
|
+
* `trustedOrigins` option from `native-window-ipc`.
|
|
176
|
+
*
|
|
177
|
+
* @security **No rate limiting.** Messages from the webview are delivered
|
|
178
|
+
* without throttling. A malicious page can flood the host with messages.
|
|
179
|
+
* Consider implementing application-level rate limiting if loading
|
|
180
|
+
* untrusted content.
|
|
181
|
+
*/
|
|
182
|
+
onMessage(callback: (message: string, sourceUrl: string) => void): void;
|
|
183
|
+
onResize(callback: (width: number, height: number) => void): void;
|
|
184
|
+
onMove(callback: (x: number, y: number) => void): void;
|
|
185
|
+
onFocus(callback: () => void): void;
|
|
186
|
+
onBlur(callback: () => void): void;
|
|
187
|
+
onPageLoad(callback: (event: "started" | "finished", url: string) => void): void;
|
|
188
|
+
onTitleChanged(callback: (title: string) => void): void;
|
|
189
|
+
onReload(callback: () => void): void;
|
|
190
|
+
/**
|
|
191
|
+
* Register a handler for blocked navigation events.
|
|
192
|
+
* Fired when a navigation is blocked by the {@link WindowOptions.allowedHosts}
|
|
193
|
+
* restriction. Receives the URL that was blocked.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* win.onNavigationBlocked((url) => {
|
|
198
|
+
* console.log("Blocked navigation to:", url);
|
|
199
|
+
* });
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
onNavigationBlocked(callback: (url: string) => void): void;
|
|
203
|
+
/**
|
|
204
|
+
* Validate and parse a raw cookies JSON array from the native layer.
|
|
205
|
+
* Returns a cleaned {@link CookieInfo} array or `null` if the payload
|
|
206
|
+
* is malformed.
|
|
207
|
+
*
|
|
208
|
+
* @internal
|
|
209
|
+
*/
|
|
210
|
+
private _validateCookies;
|
|
211
|
+
/**
|
|
212
|
+
* Query cookies from the native cookie store.
|
|
213
|
+
*
|
|
214
|
+
* Returns a Promise that resolves with validated {@link CookieInfo} objects,
|
|
215
|
+
* including `HttpOnly` cookies that are invisible to `document.cookie`.
|
|
216
|
+
*
|
|
217
|
+
* - **macOS**: Uses `WKHTTPCookieStore.getAllCookies` with client-side
|
|
218
|
+
* URL filtering (domain + path match).
|
|
219
|
+
* - **Windows**: Uses `ICoreWebView2CookieManager.GetCookies` which
|
|
220
|
+
* filters by URI natively.
|
|
221
|
+
*
|
|
222
|
+
* @param url If provided, only cookies matching this URL are returned.
|
|
223
|
+
* If omitted, all cookies in the webview's cookie store are returned.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```ts
|
|
227
|
+
* const cookies = await win.getCookies("https://example.com");
|
|
228
|
+
* const session = cookies.find((c) => c.name === "session_id");
|
|
229
|
+
* if (session) console.log("Session:", session.value, "HttpOnly:", session.httpOnly);
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
getCookies(url?: string): Promise<CookieInfo[]>;
|
|
233
|
+
/**
|
|
234
|
+
* Clear cookies from the native cookie store.
|
|
235
|
+
*
|
|
236
|
+
* - If `host` is provided, only cookies whose domain matches that host
|
|
237
|
+
* are deleted (e.g. `"example.com"` deletes `.example.com` cookies).
|
|
238
|
+
* - If omitted, all cookies in the webview's cookie store are cleared.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```ts
|
|
242
|
+
* // Clear all cookies
|
|
243
|
+
* win.clearCookies();
|
|
244
|
+
*
|
|
245
|
+
* // Clear cookies for a specific host
|
|
246
|
+
* win.clearCookies("example.com");
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
clearCookies(host?: string): void;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Escape a string for safe embedding inside a JavaScript string literal.
|
|
253
|
+
* Handles backslashes, double quotes, newlines, carriage returns, null
|
|
254
|
+
* bytes, closing `</script>` tags, Unicode line/paragraph separators
|
|
255
|
+
* (U+2028, U+2029), backticks, and `${` template expressions.
|
|
256
|
+
*
|
|
257
|
+
* Safe for use in double-quoted, single-quoted, and template literal
|
|
258
|
+
* contexts.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```ts
|
|
262
|
+
* import { NativeWindow, sanitizeForJs } from "native-window";
|
|
263
|
+
*
|
|
264
|
+
* const userInput = 'He said "hello"\n<script>alert(1)</script>';
|
|
265
|
+
* win.unsafe.evaluateJs(`display("${sanitizeForJs(userInput)}")`);
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
export declare function sanitizeForJs(input: string): string;
|