@nativewindow/webview 1.0.1 → 1.0.2

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.
Files changed (2) hide show
  1. package/dist/index.js +386 -400
  2. package/package.json +18 -12
package/dist/index.js CHANGED
@@ -1,406 +1,392 @@
1
- import { NativeWindow as NativeWindow$1, init, pumpEvents } from "../native-window.js";
2
- import { checkRuntime, ensureRuntime, loadHtmlOrigin } from "../native-window.js";
3
- let _pump = null;
4
- let _windowCount = 0;
1
+ import { NativeWindow as NativeWindow$1, checkRuntime, ensureRuntime, init, loadHtmlOrigin, pumpEvents } from "../native-window.js";
2
+ //#region index.ts
3
+ var _pump = null;
4
+ var _windowCount = 0;
5
5
  function ensureInit() {
6
- if (_pump) return;
7
- init();
8
- _pump = setInterval(() => {
9
- try {
10
- pumpEvents();
11
- } catch (e) {
12
- console.error("[native-window] pumpEvents() error:", e);
13
- }
14
- }, 16);
6
+ if (_pump) return;
7
+ init();
8
+ _pump = setInterval(() => {
9
+ try {
10
+ pumpEvents();
11
+ } catch (e) {
12
+ console.error("[native-window] pumpEvents() error:", e);
13
+ }
14
+ }, 16);
15
15
  }
16
16
  function stopPump() {
17
- if (_pump) {
18
- clearInterval(_pump);
19
- _pump = null;
20
- }
21
- }
22
- class NativeWindow {
23
- /** @internal */
24
- _native;
25
- /** @internal */
26
- _closed = false;
27
- /** @internal */
28
- _unsafe;
29
- /** @internal */
30
- _devtools;
31
- constructor(options) {
32
- ensureInit();
33
- _windowCount++;
34
- this._native = new NativeWindow$1(options);
35
- this._native.onClose(() => this._handleClose());
36
- }
37
- /** @internal */
38
- _handleClose() {
39
- if (this._closed) return;
40
- this._closed = true;
41
- _windowCount--;
42
- this._userCloseCallback?.();
43
- if (_windowCount <= 0) {
44
- _windowCount = 0;
45
- setTimeout(() => stopPump(), 200);
46
- }
47
- }
48
- /**
49
- * Throws if the window has been closed.
50
- * @internal
51
- */
52
- _ensureOpen() {
53
- if (this._closed) {
54
- throw new Error("Window is closed");
55
- }
56
- }
57
- // ---- onClose with user callback support ----
58
- _userCloseCallback;
59
- /**
60
- * Register a handler for the window close event.
61
- * The pump is automatically stopped when all windows are closed.
62
- *
63
- * Calling this multiple times replaces the previous handler.
64
- */
65
- onClose(callback) {
66
- if (this._userCloseCallback) {
67
- console.warn(
68
- "NativeWindow: onClose() called multiple times. The previous handler will be replaced."
69
- );
70
- }
71
- this._userCloseCallback = callback;
72
- }
73
- // ---- Getters ----
74
- /** Unique window ID */
75
- get id() {
76
- return this._native.id;
77
- }
78
- // ---- Content loading ----
79
- loadUrl(url) {
80
- this._ensureOpen();
81
- this._native.loadUrl(url);
82
- }
83
- /**
84
- * Load raw HTML content into the webview.
85
- *
86
- * @security **Injection risk.** Never interpolate unsanitized user input
87
- * into HTML strings. Use a dedicated sanitization library such as
88
- * [DOMPurify](https://github.com/cure53/DOMPurify) or
89
- * [sanitize-html](https://github.com/apostrophecms/sanitize-html) to
90
- * sanitize untrusted content before embedding it.
91
- */
92
- loadHtml(html) {
93
- this._ensureOpen();
94
- this._native.loadHtml(html);
95
- }
96
- postMessage(message) {
97
- this._ensureOpen();
98
- this._native.postMessage(message);
99
- }
100
- // ---- Unsafe operations ----
101
- /**
102
- * Namespace for operations that require extra care to avoid injection risks.
103
- * Methods under `unsafe` execute arbitrary code in the webview context.
104
- *
105
- * @security Never pass unsanitized user input to these methods.
106
- * Use {@link sanitizeForJs} to escape strings before embedding them in
107
- * script code.
108
- */
109
- get unsafe() {
110
- this._ensureOpen();
111
- if (!this._unsafe) {
112
- this._unsafe = {
113
- evaluateJs: (script) => {
114
- this._ensureOpen();
115
- this._native.evaluateJs(script);
116
- }
117
- };
118
- }
119
- return this._unsafe;
120
- }
121
- // ---- Devtools ----
122
- /**
123
- * Namespace for controlling the browser devtools panel.
124
- * Requires `devtools: true` in {@link WindowOptions} at window creation.
125
- *
126
- * @example
127
- * ```ts
128
- * const win = new NativeWindow({ devtools: true });
129
- * win.devtools.open();
130
- * console.log(win.devtools.isOpen()); // true
131
- * win.devtools.close();
132
- * ```
133
- */
134
- get devtools() {
135
- this._ensureOpen();
136
- if (!this._devtools) {
137
- this._devtools = {
138
- open: () => {
139
- this._ensureOpen();
140
- this._native.openDevtools();
141
- },
142
- close: () => {
143
- this._ensureOpen();
144
- this._native.closeDevtools();
145
- },
146
- isOpen: () => {
147
- this._ensureOpen();
148
- return this._native.isDevtoolsOpen();
149
- }
150
- };
151
- }
152
- return this._devtools;
153
- }
154
- // ---- Window control ----
155
- setTitle(title) {
156
- this._ensureOpen();
157
- this._native.setTitle(title);
158
- }
159
- setSize(width, height) {
160
- this._ensureOpen();
161
- this._native.setSize(width, height);
162
- }
163
- setMinSize(width, height) {
164
- this._ensureOpen();
165
- this._native.setMinSize(width, height);
166
- }
167
- setMaxSize(width, height) {
168
- this._ensureOpen();
169
- this._native.setMaxSize(width, height);
170
- }
171
- setPosition(x, y) {
172
- this._ensureOpen();
173
- this._native.setPosition(x, y);
174
- }
175
- setResizable(resizable) {
176
- this._ensureOpen();
177
- this._native.setResizable(resizable);
178
- }
179
- setDecorations(decorations) {
180
- this._ensureOpen();
181
- this._native.setDecorations(decorations);
182
- }
183
- setAlwaysOnTop(alwaysOnTop) {
184
- this._ensureOpen();
185
- this._native.setAlwaysOnTop(alwaysOnTop);
186
- }
187
- /**
188
- * Set the window icon from a PNG or ICO file path.
189
- * On macOS this is silently ignored (macOS doesn't support per-window icons).
190
- * Relative paths resolve from the working directory.
191
- */
192
- setIcon(path) {
193
- this._ensureOpen();
194
- this._native.setIcon(path);
195
- }
196
- // ---- Window state ----
197
- show() {
198
- this._ensureOpen();
199
- this._native.show();
200
- }
201
- hide() {
202
- this._ensureOpen();
203
- this._native.hide();
204
- }
205
- close() {
206
- this._ensureOpen();
207
- this._native.close();
208
- }
209
- focus() {
210
- this._ensureOpen();
211
- this._native.focus();
212
- }
213
- maximize() {
214
- this._ensureOpen();
215
- this._native.maximize();
216
- }
217
- minimize() {
218
- this._ensureOpen();
219
- this._native.minimize();
220
- }
221
- unmaximize() {
222
- this._ensureOpen();
223
- this._native.unmaximize();
224
- }
225
- reload() {
226
- this._ensureOpen();
227
- this._native.reload();
228
- }
229
- // ---- Event handlers ----
230
- /**
231
- * Register a handler for messages from the webview.
232
- *
233
- * @security **No origin filtering.** The raw `onMessage` API does not
234
- * enforce origin restrictions. If your webview navigates to untrusted
235
- * URLs, validate the `sourceUrl` parameter before processing messages.
236
- * For automatic origin filtering, use `createChannel()` with the
237
- * `trustedOrigins` option from `native-window-ipc`.
238
- *
239
- * @security **No rate limiting.** Messages from the webview are delivered
240
- * without throttling. A malicious page can flood the host with messages.
241
- * Consider implementing application-level rate limiting if loading
242
- * untrusted content.
243
- */
244
- onMessage(callback) {
245
- this._ensureOpen();
246
- this._native.onMessage(callback);
247
- }
248
- onResize(callback) {
249
- this._ensureOpen();
250
- this._native.onResize(callback);
251
- }
252
- onMove(callback) {
253
- this._ensureOpen();
254
- this._native.onMove(callback);
255
- }
256
- onFocus(callback) {
257
- this._ensureOpen();
258
- this._native.onFocus(callback);
259
- }
260
- onBlur(callback) {
261
- this._ensureOpen();
262
- this._native.onBlur(callback);
263
- }
264
- onPageLoad(callback) {
265
- this._ensureOpen();
266
- this._native.onPageLoad(callback);
267
- }
268
- onTitleChanged(callback) {
269
- this._ensureOpen();
270
- this._native.onTitleChanged(callback);
271
- }
272
- onReload(callback) {
273
- this._ensureOpen();
274
- this._native.onReload(callback);
275
- }
276
- /**
277
- * Register a handler for blocked navigation events.
278
- * Fired when a navigation is blocked by the {@link WindowOptions.allowedHosts}
279
- * restriction. Receives the URL that was blocked.
280
- *
281
- * @example
282
- * ```ts
283
- * win.onNavigationBlocked((url) => {
284
- * console.log("Blocked navigation to:", url);
285
- * });
286
- * ```
287
- */
288
- onNavigationBlocked(callback) {
289
- this._ensureOpen();
290
- this._native.onNavigationBlocked(callback);
291
- }
292
- // ---- Cookie access ----
293
- /**
294
- * Validate and parse a raw cookies JSON array from the native layer.
295
- * Returns a cleaned {@link CookieInfo} array or `null` if the payload
296
- * is malformed.
297
- *
298
- * @internal
299
- */
300
- _validateCookies(raw) {
301
- let parsed;
302
- try {
303
- parsed = JSON.parse(raw);
304
- } catch {
305
- return null;
306
- }
307
- if (!Array.isArray(parsed)) return null;
308
- const cookies = [];
309
- for (const item of parsed) {
310
- if (typeof item !== "object" || item === null) continue;
311
- const obj = item;
312
- if (typeof obj.name !== "string" || typeof obj.value !== "string") {
313
- continue;
314
- }
315
- if (typeof obj.domain !== "string" || typeof obj.path !== "string") {
316
- continue;
317
- }
318
- if (typeof obj.httpOnly !== "boolean" || typeof obj.secure !== "boolean") {
319
- continue;
320
- }
321
- if (typeof obj.sameSite !== "string" || typeof obj.expires !== "number") {
322
- continue;
323
- }
324
- cookies.push({
325
- name: obj.name,
326
- value: obj.value,
327
- domain: obj.domain,
328
- path: obj.path,
329
- httpOnly: obj.httpOnly,
330
- secure: obj.secure,
331
- sameSite: obj.sameSite,
332
- expires: obj.expires
333
- });
334
- }
335
- return cookies;
336
- }
337
- /**
338
- * Query cookies from the native cookie store.
339
- *
340
- * Returns a Promise that resolves with validated {@link CookieInfo} objects,
341
- * including `HttpOnly` cookies that are invisible to `document.cookie`.
342
- *
343
- * - **macOS**: Uses `WKHTTPCookieStore.getAllCookies` with client-side
344
- * URL filtering (domain + path match).
345
- * - **Windows**: Uses `ICoreWebView2CookieManager.GetCookies` which
346
- * filters by URI natively.
347
- *
348
- * @param url If provided, only cookies matching this URL are returned.
349
- * If omitted, all cookies in the webview's cookie store are returned.
350
- *
351
- * @example
352
- * ```ts
353
- * const cookies = await win.getCookies("https://example.com");
354
- * const session = cookies.find((c) => c.name === "session_id");
355
- * if (session) console.log("Session:", session.value, "HttpOnly:", session.httpOnly);
356
- * ```
357
- */
358
- getCookies(url) {
359
- this._ensureOpen();
360
- return new Promise((resolve, reject) => {
361
- const timeout = setTimeout(() => {
362
- reject(new Error("getCookies() timed out after 10 seconds"));
363
- }, 1e4);
364
- this._native.onCookies((raw) => {
365
- clearTimeout(timeout);
366
- const validated = this._validateCookies(raw);
367
- if (validated) {
368
- resolve(validated);
369
- } else {
370
- reject(new Error("Failed to parse cookie response"));
371
- }
372
- });
373
- this._native.getCookies(url);
374
- });
375
- }
376
- /**
377
- * Clear cookies from the native cookie store.
378
- *
379
- * - If `host` is provided, only cookies whose domain matches that host
380
- * are deleted (e.g. `"example.com"` deletes `.example.com` cookies).
381
- * - If omitted, all cookies in the webview's cookie store are cleared.
382
- *
383
- * @example
384
- * ```ts
385
- * // Clear all cookies
386
- * win.clearCookies();
387
- *
388
- * // Clear cookies for a specific host
389
- * win.clearCookies("example.com");
390
- * ```
391
- */
392
- clearCookies(host) {
393
- this._ensureOpen();
394
- this._native.clearCookies(host);
395
- }
17
+ if (_pump) {
18
+ clearInterval(_pump);
19
+ _pump = null;
20
+ }
396
21
  }
22
+ /**
23
+ * A native OS window with an embedded webview.
24
+ *
25
+ * Automatically initializes the native subsystem and starts pumping
26
+ * events on first construction. Stops the pump when all windows close.
27
+ */
28
+ var NativeWindow = class {
29
+ /** @internal */
30
+ _native;
31
+ /** @internal */
32
+ _closed = false;
33
+ /** @internal */
34
+ _unsafe;
35
+ /** @internal */
36
+ _devtools;
37
+ constructor(options) {
38
+ ensureInit();
39
+ _windowCount++;
40
+ this._native = new NativeWindow$1(options);
41
+ this._native.onClose(() => this._handleClose());
42
+ }
43
+ /** @internal */
44
+ _handleClose() {
45
+ if (this._closed) return;
46
+ this._closed = true;
47
+ _windowCount--;
48
+ this._userCloseCallback?.();
49
+ if (_windowCount <= 0) {
50
+ _windowCount = 0;
51
+ setTimeout(() => stopPump(), 200);
52
+ }
53
+ }
54
+ /**
55
+ * Throws if the window has been closed.
56
+ * @internal
57
+ */
58
+ _ensureOpen() {
59
+ if (this._closed) throw new Error("Window is closed");
60
+ }
61
+ _userCloseCallback;
62
+ /**
63
+ * Register a handler for the window close event.
64
+ * The pump is automatically stopped when all windows are closed.
65
+ *
66
+ * Calling this multiple times replaces the previous handler.
67
+ */
68
+ onClose(callback) {
69
+ if (this._userCloseCallback) console.warn("NativeWindow: onClose() called multiple times. The previous handler will be replaced.");
70
+ this._userCloseCallback = callback;
71
+ }
72
+ /** Unique window ID */
73
+ get id() {
74
+ return this._native.id;
75
+ }
76
+ loadUrl(url) {
77
+ this._ensureOpen();
78
+ this._native.loadUrl(url);
79
+ }
80
+ /**
81
+ * Load raw HTML content into the webview.
82
+ *
83
+ * @security **Injection risk.** Never interpolate unsanitized user input
84
+ * into HTML strings. Use a dedicated sanitization library such as
85
+ * [DOMPurify](https://github.com/cure53/DOMPurify) or
86
+ * [sanitize-html](https://github.com/apostrophecms/sanitize-html) to
87
+ * sanitize untrusted content before embedding it.
88
+ */
89
+ loadHtml(html) {
90
+ this._ensureOpen();
91
+ this._native.loadHtml(html);
92
+ }
93
+ postMessage(message) {
94
+ this._ensureOpen();
95
+ this._native.postMessage(message);
96
+ }
97
+ /**
98
+ * Namespace for operations that require extra care to avoid injection risks.
99
+ * Methods under `unsafe` execute arbitrary code in the webview context.
100
+ *
101
+ * @security Never pass unsanitized user input to these methods.
102
+ * Use {@link sanitizeForJs} to escape strings before embedding them in
103
+ * script code.
104
+ */
105
+ get unsafe() {
106
+ this._ensureOpen();
107
+ if (!this._unsafe) this._unsafe = { evaluateJs: (script) => {
108
+ this._ensureOpen();
109
+ this._native.evaluateJs(script);
110
+ } };
111
+ return this._unsafe;
112
+ }
113
+ /**
114
+ * Namespace for controlling the browser devtools panel.
115
+ * Requires `devtools: true` in {@link WindowOptions} at window creation.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * const win = new NativeWindow({ devtools: true });
120
+ * win.devtools.open();
121
+ * console.log(win.devtools.isOpen()); // true
122
+ * win.devtools.close();
123
+ * ```
124
+ */
125
+ get devtools() {
126
+ this._ensureOpen();
127
+ if (!this._devtools) this._devtools = {
128
+ open: () => {
129
+ this._ensureOpen();
130
+ this._native.openDevtools();
131
+ },
132
+ close: () => {
133
+ this._ensureOpen();
134
+ this._native.closeDevtools();
135
+ },
136
+ isOpen: () => {
137
+ this._ensureOpen();
138
+ return this._native.isDevtoolsOpen();
139
+ }
140
+ };
141
+ return this._devtools;
142
+ }
143
+ setTitle(title) {
144
+ this._ensureOpen();
145
+ this._native.setTitle(title);
146
+ }
147
+ setSize(width, height) {
148
+ this._ensureOpen();
149
+ this._native.setSize(width, height);
150
+ }
151
+ setMinSize(width, height) {
152
+ this._ensureOpen();
153
+ this._native.setMinSize(width, height);
154
+ }
155
+ setMaxSize(width, height) {
156
+ this._ensureOpen();
157
+ this._native.setMaxSize(width, height);
158
+ }
159
+ setPosition(x, y) {
160
+ this._ensureOpen();
161
+ this._native.setPosition(x, y);
162
+ }
163
+ setResizable(resizable) {
164
+ this._ensureOpen();
165
+ this._native.setResizable(resizable);
166
+ }
167
+ setDecorations(decorations) {
168
+ this._ensureOpen();
169
+ this._native.setDecorations(decorations);
170
+ }
171
+ setAlwaysOnTop(alwaysOnTop) {
172
+ this._ensureOpen();
173
+ this._native.setAlwaysOnTop(alwaysOnTop);
174
+ }
175
+ /**
176
+ * Set the window icon from a PNG or ICO file path.
177
+ * On macOS this is silently ignored (macOS doesn't support per-window icons).
178
+ * Relative paths resolve from the working directory.
179
+ */
180
+ setIcon(path) {
181
+ this._ensureOpen();
182
+ this._native.setIcon(path);
183
+ }
184
+ show() {
185
+ this._ensureOpen();
186
+ this._native.show();
187
+ }
188
+ hide() {
189
+ this._ensureOpen();
190
+ this._native.hide();
191
+ }
192
+ close() {
193
+ this._ensureOpen();
194
+ this._native.close();
195
+ }
196
+ focus() {
197
+ this._ensureOpen();
198
+ this._native.focus();
199
+ }
200
+ maximize() {
201
+ this._ensureOpen();
202
+ this._native.maximize();
203
+ }
204
+ minimize() {
205
+ this._ensureOpen();
206
+ this._native.minimize();
207
+ }
208
+ unmaximize() {
209
+ this._ensureOpen();
210
+ this._native.unmaximize();
211
+ }
212
+ reload() {
213
+ this._ensureOpen();
214
+ this._native.reload();
215
+ }
216
+ /**
217
+ * Register a handler for messages from the webview.
218
+ *
219
+ * @security **No origin filtering.** The raw `onMessage` API does not
220
+ * enforce origin restrictions. If your webview navigates to untrusted
221
+ * URLs, validate the `sourceUrl` parameter before processing messages.
222
+ * For automatic origin filtering, use `createChannel()` with the
223
+ * `trustedOrigins` option from `native-window-ipc`.
224
+ *
225
+ * @security **No rate limiting.** Messages from the webview are delivered
226
+ * without throttling. A malicious page can flood the host with messages.
227
+ * Consider implementing application-level rate limiting if loading
228
+ * untrusted content.
229
+ */
230
+ onMessage(callback) {
231
+ this._ensureOpen();
232
+ this._native.onMessage(callback);
233
+ }
234
+ onResize(callback) {
235
+ this._ensureOpen();
236
+ this._native.onResize(callback);
237
+ }
238
+ onMove(callback) {
239
+ this._ensureOpen();
240
+ this._native.onMove(callback);
241
+ }
242
+ onFocus(callback) {
243
+ this._ensureOpen();
244
+ this._native.onFocus(callback);
245
+ }
246
+ onBlur(callback) {
247
+ this._ensureOpen();
248
+ this._native.onBlur(callback);
249
+ }
250
+ onPageLoad(callback) {
251
+ this._ensureOpen();
252
+ this._native.onPageLoad(callback);
253
+ }
254
+ onTitleChanged(callback) {
255
+ this._ensureOpen();
256
+ this._native.onTitleChanged(callback);
257
+ }
258
+ onReload(callback) {
259
+ this._ensureOpen();
260
+ this._native.onReload(callback);
261
+ }
262
+ /**
263
+ * Register a handler for blocked navigation events.
264
+ * Fired when a navigation is blocked by the {@link WindowOptions.allowedHosts}
265
+ * restriction. Receives the URL that was blocked.
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * win.onNavigationBlocked((url) => {
270
+ * console.log("Blocked navigation to:", url);
271
+ * });
272
+ * ```
273
+ */
274
+ onNavigationBlocked(callback) {
275
+ this._ensureOpen();
276
+ this._native.onNavigationBlocked(callback);
277
+ }
278
+ /**
279
+ * Validate and parse a raw cookies JSON array from the native layer.
280
+ * Returns a cleaned {@link CookieInfo} array or `null` if the payload
281
+ * is malformed.
282
+ *
283
+ * @internal
284
+ */
285
+ _validateCookies(raw) {
286
+ let parsed;
287
+ try {
288
+ parsed = JSON.parse(raw);
289
+ } catch {
290
+ return null;
291
+ }
292
+ if (!Array.isArray(parsed)) return null;
293
+ const cookies = [];
294
+ for (const item of parsed) {
295
+ if (typeof item !== "object" || item === null) continue;
296
+ const obj = item;
297
+ if (typeof obj.name !== "string" || typeof obj.value !== "string") continue;
298
+ if (typeof obj.domain !== "string" || typeof obj.path !== "string") continue;
299
+ if (typeof obj.httpOnly !== "boolean" || typeof obj.secure !== "boolean") continue;
300
+ if (typeof obj.sameSite !== "string" || typeof obj.expires !== "number") continue;
301
+ cookies.push({
302
+ name: obj.name,
303
+ value: obj.value,
304
+ domain: obj.domain,
305
+ path: obj.path,
306
+ httpOnly: obj.httpOnly,
307
+ secure: obj.secure,
308
+ sameSite: obj.sameSite,
309
+ expires: obj.expires
310
+ });
311
+ }
312
+ return cookies;
313
+ }
314
+ /**
315
+ * Query cookies from the native cookie store.
316
+ *
317
+ * Returns a Promise that resolves with validated {@link CookieInfo} objects,
318
+ * including `HttpOnly` cookies that are invisible to `document.cookie`.
319
+ *
320
+ * - **macOS**: Uses `WKHTTPCookieStore.getAllCookies` with client-side
321
+ * URL filtering (domain + path match).
322
+ * - **Windows**: Uses `ICoreWebView2CookieManager.GetCookies` which
323
+ * filters by URI natively.
324
+ *
325
+ * @param url If provided, only cookies matching this URL are returned.
326
+ * If omitted, all cookies in the webview's cookie store are returned.
327
+ *
328
+ * @example
329
+ * ```ts
330
+ * const cookies = await win.getCookies("https://example.com");
331
+ * const session = cookies.find((c) => c.name === "session_id");
332
+ * if (session) console.log("Session:", session.value, "HttpOnly:", session.httpOnly);
333
+ * ```
334
+ */
335
+ getCookies(url) {
336
+ this._ensureOpen();
337
+ return new Promise((resolve, reject) => {
338
+ const timeout = setTimeout(() => {
339
+ reject(/* @__PURE__ */ new Error("getCookies() timed out after 10 seconds"));
340
+ }, 1e4);
341
+ this._native.onCookies((raw) => {
342
+ clearTimeout(timeout);
343
+ const validated = this._validateCookies(raw);
344
+ if (validated) resolve(validated);
345
+ else reject(/* @__PURE__ */ new Error("Failed to parse cookie response"));
346
+ });
347
+ this._native.getCookies(url);
348
+ });
349
+ }
350
+ /**
351
+ * Clear cookies from the native cookie store.
352
+ *
353
+ * - If `host` is provided, only cookies whose domain matches that host
354
+ * are deleted (e.g. `"example.com"` deletes `.example.com` cookies).
355
+ * - If omitted, all cookies in the webview's cookie store are cleared.
356
+ *
357
+ * @example
358
+ * ```ts
359
+ * // Clear all cookies
360
+ * win.clearCookies();
361
+ *
362
+ * // Clear cookies for a specific host
363
+ * win.clearCookies("example.com");
364
+ * ```
365
+ */
366
+ clearCookies(host) {
367
+ this._ensureOpen();
368
+ this._native.clearCookies(host);
369
+ }
370
+ };
371
+ /**
372
+ * Escape a string for safe embedding inside a JavaScript string literal.
373
+ * Handles backslashes, double quotes, newlines, carriage returns, null
374
+ * bytes, closing `<\/script>` tags, Unicode line/paragraph separators
375
+ * (U+2028, U+2029), backticks, and `${` template expressions.
376
+ *
377
+ * Safe for use in double-quoted, single-quoted, and template literal
378
+ * contexts.
379
+ *
380
+ * @example
381
+ * ```ts
382
+ * import { NativeWindow, sanitizeForJs } from "native-window";
383
+ *
384
+ * const userInput = 'He said "hello"\n<script>alert(1)<\/script>';
385
+ * win.unsafe.evaluateJs(`display("${sanitizeForJs(userInput)}")`);
386
+ * ```
387
+ */
397
388
  function sanitizeForJs(input) {
398
- return JSON.stringify(input).slice(1, -1).replace(/<\/script>/gi, "<\\/script>").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
389
+ return JSON.stringify(input).slice(1, -1).replace(/<\/script>/gi, "<\\/script>").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
399
390
  }
400
- export {
401
- NativeWindow,
402
- checkRuntime,
403
- ensureRuntime,
404
- loadHtmlOrigin,
405
- sanitizeForJs
406
- };
391
+ //#endregion
392
+ export { NativeWindow, checkRuntime, ensureRuntime, loadHtmlOrigin, sanitizeForJs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nativewindow/webview",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Native OS webview windows for Bun & Node.js (beta)",
5
5
  "homepage": "https://nativewindow.fcannizzaro.com",
6
6
  "bugs": {
@@ -34,34 +34,40 @@
34
34
  "exports": {
35
35
  ".": {
36
36
  "types": "./dist/index.d.ts",
37
- "import": "./dist/index.js"
37
+ "import": "./dist/index.js",
38
+ "default": "./dist/index.js"
38
39
  }
39
40
  },
40
41
  "scripts": {
41
42
  "build": "napi build --release --platform && vite build",
42
43
  "build:debug": "napi build --platform && vite build",
43
44
  "build:ts": "vite build",
44
- "version": "napi version"
45
+ "version": "napi version",
46
+ "fuzz": "cargo +nightly fuzz list --fuzz-dir fuzz",
47
+ "fuzz:json-escape": "cargo +nightly fuzz run fuzz_json_escape --fuzz-dir fuzz",
48
+ "fuzz:extract-origin": "cargo +nightly fuzz run fuzz_extract_origin --fuzz-dir fuzz",
49
+ "fuzz:host-matching": "cargo +nightly fuzz run fuzz_host_matching --fuzz-dir fuzz",
50
+ "fuzz:url-scheme": "cargo +nightly fuzz run fuzz_url_scheme --fuzz-dir fuzz"
45
51
  },
46
52
  "dependencies": {
47
53
  "zod": "^4.3.6"
48
54
  },
49
55
  "devDependencies": {
50
- "@napi-rs/cli": "^3.0.0",
56
+ "@napi-rs/cli": "^3.6.0",
51
57
  "@types/bun": "^1.3.9",
52
- "vite": "^7.3.1",
58
+ "vite": "^8.0.3",
53
59
  "vite-plugin-dts": "^4.5.4"
54
60
  },
55
61
  "peerDependencies": {
56
- "typescript": "^5"
62
+ "typescript": "^6.0.2"
57
63
  },
58
64
  "optionalDependencies": {
59
- "@nativewindow/webview-darwin-arm64": "1.0.1",
60
- "@nativewindow/webview-darwin-x64": "1.0.1",
61
- "@nativewindow/webview-linux-arm64-gnu": "1.0.1",
62
- "@nativewindow/webview-linux-x64-gnu": "1.0.1",
63
- "@nativewindow/webview-win32-arm64-msvc": "1.0.1",
64
- "@nativewindow/webview-win32-x64-msvc": "1.0.1"
65
+ "@nativewindow/webview-darwin-arm64": "1.0.2",
66
+ "@nativewindow/webview-darwin-x64": "1.0.2",
67
+ "@nativewindow/webview-linux-arm64-gnu": "1.0.2",
68
+ "@nativewindow/webview-linux-x64-gnu": "1.0.2",
69
+ "@nativewindow/webview-win32-arm64-msvc": "1.0.2",
70
+ "@nativewindow/webview-win32-x64-msvc": "1.0.2"
65
71
  },
66
72
  "napi": {
67
73
  "binaryName": "native-window",