sparkbun 0.2.4 → 0.2.6
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/dist-linux-arm64/libNativeWrapper.so +0 -0
- package/dist-linux-x64/libNativeWrapper.so +0 -0
- package/package.json +1 -1
- package/src/browser/index.ts +4 -3
- package/src/bun/core/BrowserView.ts +4 -3
- package/src/bun/core/BrowserWindow.ts +11 -2
- package/src/bun/core/Updater.ts +3 -1
- package/src/bun/preload/.generated/compiled.d.ts +5 -0
- package/src/bun/proc/native.ts +4 -4
- package/src/cli/index.ts +50 -44
- package/src/cli/templates/embedded.d.ts +12 -0
- package/src/launcher/main.ts +1 -1
- package/src/native/linux/nativeWrapper.cpp +83 -39
- package/src/shared/bsdiff.ts +12 -24
- package/src/shared/bspatch.ts +5 -5
- package/src/shared/rpc.ts +26 -0
- package/tsconfig.json +4 -1
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/src/browser/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type SparkBunRPCSchema,
|
|
5
5
|
type SparkBunRPCConfig,
|
|
6
6
|
type RPCWithTransport,
|
|
7
|
+
type AnyRPC,
|
|
7
8
|
createRPC,
|
|
8
9
|
defineSparkBunRPC,
|
|
9
10
|
} from "../shared/rpc.js";
|
|
@@ -18,11 +19,11 @@ const WEBVIEW_ID = window.__sparkbunWebviewId;
|
|
|
18
19
|
const HOST_SOCKET_PORT =
|
|
19
20
|
window.__sparkbunHostSocketPort ?? window.__sparkbunRpcSocketPort;
|
|
20
21
|
|
|
21
|
-
class Electroview<T extends RPCWithTransport> {
|
|
22
|
+
class Electroview<T extends RPCWithTransport = AnyRPC> {
|
|
22
23
|
hostSocket?: WebSocket;
|
|
23
24
|
hostSocketCanSend = false;
|
|
24
|
-
// user's custom rpc browser <-> bun
|
|
25
|
-
rpc
|
|
25
|
+
// user's custom rpc browser <-> bun — always provided via the constructor
|
|
26
|
+
rpc: T;
|
|
26
27
|
rpcHandler?: (msg: unknown) => void;
|
|
27
28
|
carrots = {
|
|
28
29
|
invoke: <R = unknown>(
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type SparkBunRPCSchema,
|
|
5
5
|
type SparkBunRPCConfig,
|
|
6
6
|
type RPCWithTransport,
|
|
7
|
+
type AnyRPC,
|
|
7
8
|
defineSparkBunRPC,
|
|
8
9
|
} from "../../shared/rpc.js";
|
|
9
10
|
import { BuildConfig } from "./BuildConfig";
|
|
@@ -62,7 +63,7 @@ const defaultOptions: Partial<BrowserViewOptions> = {
|
|
|
62
63
|
height: 600,
|
|
63
64
|
},
|
|
64
65
|
};
|
|
65
|
-
export class BrowserView<T extends RPCWithTransport =
|
|
66
|
+
export class BrowserView<T extends RPCWithTransport = AnyRPC> {
|
|
66
67
|
id = 0;
|
|
67
68
|
hostWebviewId?: number;
|
|
68
69
|
windowId!: number;
|
|
@@ -351,7 +352,7 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
|
|
|
351
352
|
|
|
352
353
|
// Core can create webviews before Bun has constructed a JS wrapper for them.
|
|
353
354
|
// Use this in native/runtime paths that need to ensure a wrapper exists.
|
|
354
|
-
static ensureWrapped<T extends RPCWithTransport =
|
|
355
|
+
static ensureWrapped<T extends RPCWithTransport = AnyRPC>(
|
|
355
356
|
id: number,
|
|
356
357
|
options: Partial<BrowserViewOptions<T>> = {},
|
|
357
358
|
) {
|
|
@@ -361,7 +362,7 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
|
|
|
361
362
|
);
|
|
362
363
|
}
|
|
363
364
|
|
|
364
|
-
static adoptExisting<T extends RPCWithTransport =
|
|
365
|
+
static adoptExisting<T extends RPCWithTransport = AnyRPC>(
|
|
365
366
|
id: number,
|
|
366
367
|
options: Partial<BrowserViewOptions<T>> = {},
|
|
367
368
|
) {
|
|
@@ -3,7 +3,7 @@ import sparkBunEventEmitter from "../events/eventEmitter";
|
|
|
3
3
|
import { BrowserView } from "./BrowserView";
|
|
4
4
|
import { type Pointer } from "bun:ffi";
|
|
5
5
|
import { BuildConfig } from "./BuildConfig";
|
|
6
|
-
import { type RPCWithTransport } from "../../shared/rpc.js";
|
|
6
|
+
import { type RPCWithTransport, type AnyRPC } from "../../shared/rpc.js";
|
|
7
7
|
import { WGPUView } from "./WGPUView";
|
|
8
8
|
|
|
9
9
|
const buildConfig = BuildConfig.getSync();
|
|
@@ -29,6 +29,12 @@ export type WindowOptionsType<T = undefined> = {
|
|
|
29
29
|
preload: string | null;
|
|
30
30
|
viewsRoot: string | null;
|
|
31
31
|
renderer: "native" | "cef";
|
|
32
|
+
// Storage partition for the webview's cookies/localStorage/cache/IndexedDB.
|
|
33
|
+
// A value starting with "persist:" is written to disk; any other non-empty
|
|
34
|
+
// value is ephemeral — in-memory only, gone when the process exits — which
|
|
35
|
+
// is what incognito-style/diskless sessions want. Omitted/null falls back to
|
|
36
|
+
// "persist:default" (on-disk).
|
|
37
|
+
partition?: string;
|
|
32
38
|
rpc?: T;
|
|
33
39
|
styleMask?: {};
|
|
34
40
|
// titleBarStyle options:
|
|
@@ -97,7 +103,7 @@ sparkBunEventEmitter.on("close", (event: { data: { id: number } }) => {
|
|
|
97
103
|
|
|
98
104
|
});
|
|
99
105
|
|
|
100
|
-
export class BrowserWindow<T extends RPCWithTransport =
|
|
106
|
+
export class BrowserWindow<T extends RPCWithTransport = AnyRPC> {
|
|
101
107
|
id = 0;
|
|
102
108
|
title: string = "SparkBun";
|
|
103
109
|
state: "creating" | "created" = "creating";
|
|
@@ -105,6 +111,7 @@ export class BrowserWindow<T extends RPCWithTransport = RPCWithTransport> {
|
|
|
105
111
|
html: string | null = null;
|
|
106
112
|
preload: string | null = null;
|
|
107
113
|
viewsRoot: string | null = null;
|
|
114
|
+
partition: string | null = null;
|
|
108
115
|
renderer: "native" | "cef" = "native";
|
|
109
116
|
transparent: boolean = false;
|
|
110
117
|
passthrough: boolean = false;
|
|
@@ -139,6 +146,7 @@ export class BrowserWindow<T extends RPCWithTransport = RPCWithTransport> {
|
|
|
139
146
|
this.html = options.html || null;
|
|
140
147
|
this.preload = options.preload || null;
|
|
141
148
|
this.viewsRoot = options.viewsRoot || null;
|
|
149
|
+
this.partition = options.partition ?? null;
|
|
142
150
|
this.renderer = options.renderer || defaultOptions.renderer;
|
|
143
151
|
this.transparent = options.transparent ?? false;
|
|
144
152
|
this.passthrough = options.passthrough ?? false;
|
|
@@ -223,6 +231,7 @@ export class BrowserWindow<T extends RPCWithTransport = RPCWithTransport> {
|
|
|
223
231
|
html: this.html,
|
|
224
232
|
preload: this.preload,
|
|
225
233
|
viewsRoot: this.viewsRoot,
|
|
234
|
+
partition: this.partition,
|
|
226
235
|
// frame: this.frame,
|
|
227
236
|
renderer: this.renderer,
|
|
228
237
|
frame: {
|
package/src/bun/core/Updater.ts
CHANGED
|
@@ -632,7 +632,9 @@ const Updater = {
|
|
|
632
632
|
emitStatus("decompressing", "Decompressing update bundle...");
|
|
633
633
|
try {
|
|
634
634
|
const compressed = readFileSync(prevVersionCompressedTarballPath);
|
|
635
|
-
const decompressed = Bun.gunzipSync(
|
|
635
|
+
const decompressed = Bun.gunzipSync(
|
|
636
|
+
compressed as unknown as Uint8Array<ArrayBuffer>,
|
|
637
|
+
);
|
|
636
638
|
writeFileSync(latestTarPath, decompressed);
|
|
637
639
|
emitStatus("decompressing", "Decompression complete");
|
|
638
640
|
} catch (err) {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Type declarations for the build-generated ./compiled.ts (produced by build.ts;
|
|
2
|
+
// gitignored). This stub lets the runtime typecheck from a clean tree before the
|
|
3
|
+
// file is generated; the generated .ts supersedes it at build time.
|
|
4
|
+
export const preloadScript: string;
|
|
5
|
+
export const preloadScriptSandboxed: string;
|
package/src/bun/proc/native.ts
CHANGED
|
@@ -2384,7 +2384,7 @@ export const Screen = {
|
|
|
2384
2384
|
* @returns Display object for the primary monitor
|
|
2385
2385
|
*/
|
|
2386
2386
|
getPrimaryDisplay: (): Display => {
|
|
2387
|
-
const jsonStr =
|
|
2387
|
+
const jsonStr = core_.symbols.getPrimaryDisplay();
|
|
2388
2388
|
if (!jsonStr) {
|
|
2389
2389
|
return {
|
|
2390
2390
|
id: 0,
|
|
@@ -2412,7 +2412,7 @@ export const Screen = {
|
|
|
2412
2412
|
* @returns Array of Display objects
|
|
2413
2413
|
*/
|
|
2414
2414
|
getAllDisplays: (): Display[] => {
|
|
2415
|
-
const jsonStr =
|
|
2415
|
+
const jsonStr = core_.symbols.getAllDisplays();
|
|
2416
2416
|
if (!jsonStr) {
|
|
2417
2417
|
return [];
|
|
2418
2418
|
}
|
|
@@ -2428,7 +2428,7 @@ export const Screen = {
|
|
|
2428
2428
|
* @returns Point with x and y coordinates
|
|
2429
2429
|
*/
|
|
2430
2430
|
getCursorScreenPoint: (): Point => {
|
|
2431
|
-
const jsonStr =
|
|
2431
|
+
const jsonStr = core_.symbols.getCursorScreenPoint();
|
|
2432
2432
|
if (!jsonStr) {
|
|
2433
2433
|
return { x: 0, y: 0 };
|
|
2434
2434
|
}
|
|
@@ -2444,7 +2444,7 @@ export const Screen = {
|
|
|
2444
2444
|
*/
|
|
2445
2445
|
getMouseButtons: (): bigint => {
|
|
2446
2446
|
try {
|
|
2447
|
-
return
|
|
2447
|
+
return core_.symbols.getMouseButtons();
|
|
2448
2448
|
} catch {
|
|
2449
2449
|
return 0n;
|
|
2450
2450
|
}
|
package/src/cli/index.ts
CHANGED
|
@@ -39,6 +39,14 @@ const _MAX_CHUNK_SIZE = 1024 * 2;
|
|
|
39
39
|
|
|
40
40
|
// const binExt = OS === 'win' ? '.exe' : '';
|
|
41
41
|
|
|
42
|
+
// @types/node's Buffer is backed by ArrayBufferLike, which doesn't structurally
|
|
43
|
+
// match the Uint8Array<ArrayBuffer> that Bun APIs, fs writes, and
|
|
44
|
+
// Buffer.prototype.copy expect under this lib config (ArrayBufferLike admits
|
|
45
|
+
// SharedArrayBuffer). These buffers are always ArrayBuffer-backed at runtime;
|
|
46
|
+
// asU8 narrows the type without touching the value.
|
|
47
|
+
const asU8 = (b: Buffer | Uint8Array): Uint8Array<ArrayBuffer> =>
|
|
48
|
+
b as unknown as Uint8Array<ArrayBuffer>;
|
|
49
|
+
|
|
42
50
|
// Create a tar file using system tar command (preserves file permissions unlike Bun.Archive)
|
|
43
51
|
function createTar(tarPath: string, cwd: string, entries: string[]) {
|
|
44
52
|
// Use a relative path for the tar output on Windows to avoid bsdtar
|
|
@@ -331,8 +339,6 @@ async function ensureCoreDependencies(
|
|
|
331
339
|
// Verify extraction completed successfully - check platform-specific binaries only
|
|
332
340
|
const requiredBinaries = [
|
|
333
341
|
platformPaths.BUN_BINARY,
|
|
334
|
-
platformPaths.BSDIFF,
|
|
335
|
-
platformPaths.BSPATCH,
|
|
336
342
|
];
|
|
337
343
|
if (platformOS === "macos") {
|
|
338
344
|
requiredBinaries.push(
|
|
@@ -426,6 +432,7 @@ function getEffectiveWGPUDir(
|
|
|
426
432
|
* Trims an ICU .dat file to only include the specified locales.
|
|
427
433
|
* Uses icupkg (from ICU tools) to list and remove unwanted locale data.
|
|
428
434
|
*/
|
|
435
|
+
// @ts-expect-error - reserved for future use (wiring for the build.locales option)
|
|
429
436
|
async function trimICUData(
|
|
430
437
|
source: string,
|
|
431
438
|
dest: string,
|
|
@@ -1194,6 +1201,8 @@ const defaultConfig = {
|
|
|
1194
1201
|
identifier: "com.example.myapp",
|
|
1195
1202
|
version: "0.1.0",
|
|
1196
1203
|
description: "" as string | undefined,
|
|
1204
|
+
publisher: undefined as string | undefined,
|
|
1205
|
+
copyright: undefined as string | undefined,
|
|
1197
1206
|
urlSchemes: undefined as string[] | undefined,
|
|
1198
1207
|
fileAssociations: undefined as FileAssociation[] | undefined,
|
|
1199
1208
|
},
|
|
@@ -1221,6 +1230,7 @@ const defaultConfig = {
|
|
|
1221
1230
|
icons: "icon.iconset",
|
|
1222
1231
|
defaultRenderer: undefined as "native" | "cef" | undefined,
|
|
1223
1232
|
chromiumFlags: undefined as Record<string, string | boolean> | undefined,
|
|
1233
|
+
requireAdmin: undefined as boolean | undefined,
|
|
1224
1234
|
},
|
|
1225
1235
|
win: {
|
|
1226
1236
|
bundleCEF: false,
|
|
@@ -1228,6 +1238,7 @@ const defaultConfig = {
|
|
|
1228
1238
|
icon: undefined as string | undefined,
|
|
1229
1239
|
defaultRenderer: undefined as "native" | "cef" | undefined,
|
|
1230
1240
|
chromiumFlags: undefined as Record<string, string | boolean> | undefined,
|
|
1241
|
+
requireAdmin: undefined as boolean | undefined,
|
|
1231
1242
|
},
|
|
1232
1243
|
linux: {
|
|
1233
1244
|
bundleCEF: false,
|
|
@@ -1235,6 +1246,8 @@ const defaultConfig = {
|
|
|
1235
1246
|
icon: undefined as string | undefined,
|
|
1236
1247
|
defaultRenderer: undefined as "native" | "cef" | undefined,
|
|
1237
1248
|
chromiumFlags: undefined as Record<string, string | boolean> | undefined,
|
|
1249
|
+
requireAdmin: undefined as boolean | undefined,
|
|
1250
|
+
createDeb: undefined as boolean | undefined,
|
|
1238
1251
|
},
|
|
1239
1252
|
bun: {
|
|
1240
1253
|
entrypoint: "src/bun/index.ts",
|
|
@@ -1301,13 +1314,13 @@ function escapeXml(str: string): string {
|
|
|
1301
1314
|
}
|
|
1302
1315
|
|
|
1303
1316
|
function patchPeSubsystem(exePath: string): void {
|
|
1304
|
-
const buf =
|
|
1317
|
+
const buf = readFileSync(exePath);
|
|
1305
1318
|
const peOffset = buf.readUInt32LE(0x3c);
|
|
1306
1319
|
const subsystemOffset = peOffset + 0x5c;
|
|
1307
1320
|
const current = buf.readUInt16LE(subsystemOffset);
|
|
1308
1321
|
if (current !== 2) {
|
|
1309
1322
|
buf.writeUInt16LE(2, subsystemOffset);
|
|
1310
|
-
writeFileSync(exePath, buf);
|
|
1323
|
+
writeFileSync(exePath, asU8(buf));
|
|
1311
1324
|
console.log(`Patched PE subsystem: ${current} -> 2 (WINDOWS)`);
|
|
1312
1325
|
}
|
|
1313
1326
|
}
|
|
@@ -2961,7 +2974,7 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
2961
2974
|
|
|
2962
2975
|
try {
|
|
2963
2976
|
const compressed = readFileSync(prevVersionCompressedTarballPath);
|
|
2964
|
-
const decompressed = Bun.gunzipSync(compressed);
|
|
2977
|
+
const decompressed = Bun.gunzipSync(asU8(compressed));
|
|
2965
2978
|
writeFileSync(prevTarballPath, decompressed);
|
|
2966
2979
|
} catch (err) {
|
|
2967
2980
|
console.log(
|
|
@@ -3018,7 +3031,7 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
3018
3031
|
{
|
|
3019
3032
|
console.log("compressing tarball with gzip...");
|
|
3020
3033
|
const tarBytes = readFileSync(tarPath);
|
|
3021
|
-
const gzipped = Bun.gzipSync(tarBytes);
|
|
3034
|
+
const gzipped = Bun.gzipSync(asU8(tarBytes));
|
|
3022
3035
|
writeFileSync(compressedTarPath, gzipped);
|
|
3023
3036
|
artifactsToUpload.push(compressedTarPath);
|
|
3024
3037
|
}
|
|
@@ -3385,8 +3398,6 @@ usageDescriptions : ""}${urlTypes ? "\n" + urlTypes : ""}${documentTypes ?
|
|
|
3385
3398
|
mkdirSync(buildFolder, { recursive: true });
|
|
3386
3399
|
|
|
3387
3400
|
const cliDir = dirname(decodeURIComponent(new URL(import.meta.url).pathname).replace(/^\/([A-Za-z]:)/, "$1"));
|
|
3388
|
-
const sparkbunRoot = join(cliDir, "..", "..");
|
|
3389
|
-
const bunTarget = `bun-${targetOS === "macos" ? "darwin" : targetOS === "win" ? "windows" : "linux"}-${targetARCH}` as const;
|
|
3390
3401
|
const targetOSName = targetOS === "macos" ? "darwin" : targetOS === "win" ? "windows" : "linux";
|
|
3391
3402
|
const isWindows = targetOS === "win";
|
|
3392
3403
|
|
|
@@ -4255,10 +4266,6 @@ ${archiveExports.join("\n")}
|
|
|
4255
4266
|
...defaultConfig.build.bun,
|
|
4256
4267
|
...(loadedConfig?.build?.bun || {}),
|
|
4257
4268
|
},
|
|
4258
|
-
zig: {
|
|
4259
|
-
...defaultConfig.build.zig,
|
|
4260
|
-
...(loadedConfig?.build?.zig || {}),
|
|
4261
|
-
},
|
|
4262
4269
|
},
|
|
4263
4270
|
runtime: {
|
|
4264
4271
|
...defaultConfig.runtime,
|
|
@@ -4409,12 +4416,12 @@ ${archiveExports.join("\n")}
|
|
|
4409
4416
|
function createArHeader(name: string, size: number, mode: number = 0o100644): Buffer {
|
|
4410
4417
|
const header = Buffer.alloc(60, 0x20); // fill with spaces
|
|
4411
4418
|
const now = Math.floor(Date.now() / 1000).toString();
|
|
4412
|
-
Buffer.from(name.padEnd(16, " ")).copy(header,
|
|
4413
|
-
Buffer.from(now.padEnd(12, " ")).copy(header,
|
|
4414
|
-
Buffer.from("0".padEnd(6, " ")).copy(header,
|
|
4415
|
-
Buffer.from("0".padEnd(6, " ")).copy(header,
|
|
4416
|
-
Buffer.from(mode.toString(8).padEnd(8, " ")).copy(header,
|
|
4417
|
-
Buffer.from(size.toString().padEnd(10, " ")).copy(header,
|
|
4419
|
+
Buffer.from(name.padEnd(16, " ")).copy(asU8(header),0); // filename
|
|
4420
|
+
Buffer.from(now.padEnd(12, " ")).copy(asU8(header),16); // timestamp
|
|
4421
|
+
Buffer.from("0".padEnd(6, " ")).copy(asU8(header),28); // owner
|
|
4422
|
+
Buffer.from("0".padEnd(6, " ")).copy(asU8(header),34); // group
|
|
4423
|
+
Buffer.from(mode.toString(8).padEnd(8, " ")).copy(asU8(header),40); // mode
|
|
4424
|
+
Buffer.from(size.toString().padEnd(10, " ")).copy(asU8(header),48); // size
|
|
4418
4425
|
header[58] = 0x60; // magic
|
|
4419
4426
|
header[59] = 0x0A;
|
|
4420
4427
|
return header;
|
|
@@ -4430,7 +4437,7 @@ ${archiveExports.join("\n")}
|
|
|
4430
4437
|
parts.push(Buffer.from("\n"));
|
|
4431
4438
|
}
|
|
4432
4439
|
}
|
|
4433
|
-
return Buffer.concat(parts);
|
|
4440
|
+
return Buffer.concat(parts.map(asU8));
|
|
4434
4441
|
}
|
|
4435
4442
|
|
|
4436
4443
|
function collectFiles(dir: string, prefix: string = ""): { path: string; fullPath: string; isDir: boolean }[] {
|
|
@@ -4451,38 +4458,38 @@ ${archiveExports.join("\n")}
|
|
|
4451
4458
|
|
|
4452
4459
|
function writeTarHeader(header: Buffer, file: { path: string; fullPath: string; isDir: boolean }, content?: Buffer): void {
|
|
4453
4460
|
const tarPath = file.path;
|
|
4454
|
-
Buffer.from(tarPath).copy(header,
|
|
4461
|
+
Buffer.from(tarPath).copy(asU8(header),0, 0, Math.min(tarPath.length, 100));
|
|
4455
4462
|
|
|
4456
4463
|
// mtime from actual file
|
|
4457
4464
|
const mtime = Math.floor(statSync(file.fullPath).mtimeMs / 1000);
|
|
4458
|
-
Buffer.from(mtime.toString(8).padStart(11, "0") + "\0").copy(header,
|
|
4465
|
+
Buffer.from(mtime.toString(8).padStart(11, "0") + "\0").copy(asU8(header),136);
|
|
4459
4466
|
|
|
4460
4467
|
// uid/gid = 0 (root)
|
|
4461
|
-
Buffer.from("0000000\0").copy(header,
|
|
4462
|
-
Buffer.from("0000000\0").copy(header,
|
|
4468
|
+
Buffer.from("0000000\0").copy(asU8(header),108);
|
|
4469
|
+
Buffer.from("0000000\0").copy(asU8(header),116);
|
|
4463
4470
|
|
|
4464
4471
|
// ustar magic
|
|
4465
|
-
Buffer.from("ustar\0").copy(header,
|
|
4466
|
-
Buffer.from("00").copy(header,
|
|
4467
|
-
Buffer.from("root\0").copy(header,
|
|
4468
|
-
Buffer.from("root\0").copy(header,
|
|
4472
|
+
Buffer.from("ustar\0").copy(asU8(header),257);
|
|
4473
|
+
Buffer.from("00").copy(asU8(header),263);
|
|
4474
|
+
Buffer.from("root\0").copy(asU8(header),265);
|
|
4475
|
+
Buffer.from("root\0").copy(asU8(header),297);
|
|
4469
4476
|
|
|
4470
4477
|
if (file.isDir) {
|
|
4471
|
-
Buffer.from("0040755\0").copy(header,
|
|
4472
|
-
Buffer.from("00000000000\0").copy(header,
|
|
4478
|
+
Buffer.from("0040755\0").copy(asU8(header),100);
|
|
4479
|
+
Buffer.from("00000000000\0").copy(asU8(header),124);
|
|
4473
4480
|
header[156] = 0x35; // '5'
|
|
4474
4481
|
} else {
|
|
4475
4482
|
const isExecutable = file.path.match(/\/bin\/|\/postinst$|\/preinst$|\/postrm$|\/prerm$/);
|
|
4476
|
-
Buffer.from(isExecutable ? "0100755\0" : "0100644\0").copy(header,
|
|
4477
|
-
Buffer.from((content!.length).toString(8).padStart(11, "0") + "\0").copy(header,
|
|
4483
|
+
Buffer.from(isExecutable ? "0100755\0" : "0100644\0").copy(asU8(header),100);
|
|
4484
|
+
Buffer.from((content!.length).toString(8).padStart(11, "0") + "\0").copy(asU8(header),124);
|
|
4478
4485
|
header[156] = 0x30; // '0'
|
|
4479
4486
|
}
|
|
4480
4487
|
|
|
4481
4488
|
// checksum (must be computed last)
|
|
4482
|
-
Buffer.from(" ").copy(header,
|
|
4489
|
+
Buffer.from(" ").copy(asU8(header),148);
|
|
4483
4490
|
let checksum = 0;
|
|
4484
|
-
for (let i = 0; i < 512; i++) checksum += header[i]
|
|
4485
|
-
Buffer.from(checksum.toString(8).padStart(6, "0") + "\0 ").copy(header,
|
|
4491
|
+
for (let i = 0; i < 512; i++) checksum += header[i]!;
|
|
4492
|
+
Buffer.from(checksum.toString(8).padStart(6, "0") + "\0 ").copy(asU8(header),148);
|
|
4486
4493
|
}
|
|
4487
4494
|
|
|
4488
4495
|
function buildTarGz(baseDir: string, prefix: string = "./"): Buffer {
|
|
@@ -4507,8 +4514,8 @@ ${archiveExports.join("\n")}
|
|
|
4507
4514
|
}
|
|
4508
4515
|
|
|
4509
4516
|
blocks.push(Buffer.alloc(1024, 0));
|
|
4510
|
-
const tarData = Buffer.concat(blocks);
|
|
4511
|
-
return Buffer.from(Bun.gzipSync(tarData));
|
|
4517
|
+
const tarData = Buffer.concat(blocks.map(asU8));
|
|
4518
|
+
return Buffer.from(Bun.gzipSync(asU8(tarData)));
|
|
4512
4519
|
}
|
|
4513
4520
|
|
|
4514
4521
|
async function createDebPackage(
|
|
@@ -4516,7 +4523,7 @@ ${archiveExports.join("\n")}
|
|
|
4516
4523
|
appBundleFolderPath: string,
|
|
4517
4524
|
config: any,
|
|
4518
4525
|
targetArch: string,
|
|
4519
|
-
|
|
4526
|
+
_projectRoot: string,
|
|
4520
4527
|
): Promise<string> {
|
|
4521
4528
|
const debArch = targetArch === "arm64" ? "arm64" : "amd64";
|
|
4522
4529
|
const appNameNoSpaces = config.app.name.replace(/ /g, "");
|
|
@@ -4567,19 +4574,19 @@ ${archiveExports.join("\n")}
|
|
|
4567
4574
|
const wmClass = config.app.name.slice(0, oldWmName.length);
|
|
4568
4575
|
if (existsSync(nativeLib)) {
|
|
4569
4576
|
const newName = Buffer.alloc(oldWmName.length, 0);
|
|
4570
|
-
Buffer.from(wmClass).copy(newName);
|
|
4577
|
+
Buffer.from(wmClass).copy(asU8(newName));
|
|
4571
4578
|
const data = readFileSync(nativeLib);
|
|
4572
4579
|
let patched = 0;
|
|
4573
4580
|
let offset = 0;
|
|
4574
4581
|
while (true) {
|
|
4575
|
-
const idx = data.indexOf(oldWmName, offset);
|
|
4582
|
+
const idx = data.indexOf(asU8(oldWmName), offset);
|
|
4576
4583
|
if (idx === -1) break;
|
|
4577
|
-
newName.copy(data, idx);
|
|
4584
|
+
newName.copy(asU8(data), idx);
|
|
4578
4585
|
offset = idx + oldWmName.length;
|
|
4579
4586
|
patched++;
|
|
4580
4587
|
}
|
|
4581
4588
|
if (patched > 0) {
|
|
4582
|
-
writeFileSync(nativeLib, data);
|
|
4589
|
+
writeFileSync(nativeLib, asU8(data));
|
|
4583
4590
|
console.log(` Patched WM class in libNativeWrapper.so (${patched} occurrences)`);
|
|
4584
4591
|
}
|
|
4585
4592
|
}
|
|
@@ -4590,7 +4597,7 @@ ${archiveExports.join("\n")}
|
|
|
4590
4597
|
if (existsSync(resourcesDir)) {
|
|
4591
4598
|
const pngs = readdirSync(resourcesDir).filter((f: string) => f.endsWith(".png"));
|
|
4592
4599
|
if (pngs.length > 0) {
|
|
4593
|
-
const iconSrc = join(resourcesDir, pngs[0]);
|
|
4600
|
+
const iconSrc = join(resourcesDir, pngs[0]!);
|
|
4594
4601
|
for (const size of ["256x256", "128x128"]) {
|
|
4595
4602
|
const iconDir = join(dataDir, "usr", "share", "icons", "hicolor", size, "apps");
|
|
4596
4603
|
mkdirSync(iconDir, { recursive: true });
|
|
@@ -4680,7 +4687,7 @@ update-desktop-database /usr/share/applications 2>/dev/null || true
|
|
|
4680
4687
|
|
|
4681
4688
|
const debFileName = `${pkgName}_${version}_${debArch}.deb`;
|
|
4682
4689
|
const debOutputPath = join(buildFolder, debFileName);
|
|
4683
|
-
writeFileSync(debOutputPath, debData);
|
|
4690
|
+
writeFileSync(debOutputPath, asU8(debData));
|
|
4684
4691
|
|
|
4685
4692
|
// Clean up
|
|
4686
4693
|
rmSync(stagingDir, { recursive: true, force: true });
|
|
@@ -4775,7 +4782,6 @@ update-desktop-database /usr/share/applications 2>/dev/null || true
|
|
|
4775
4782
|
}
|
|
4776
4783
|
|
|
4777
4784
|
// Sign CEF helper apps (they're in the main Frameworks directory, not inside CEF framework)
|
|
4778
|
-
const mainProcess = config.build.mainProcess ?? "bun";
|
|
4779
4785
|
const cefHelperApps = getCEFHelperNames().map(
|
|
4780
4786
|
(helperName) => `${helperName}.app`,
|
|
4781
4787
|
);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Type declarations for the build-generated ./embedded.ts (produced by build.ts
|
|
2
|
+
// from the templates/ directory; gitignored). This stub lets the CLI typecheck
|
|
3
|
+
// from a clean tree before the file is generated; the generated .ts supersedes
|
|
4
|
+
// it at build time.
|
|
5
|
+
export interface Template {
|
|
6
|
+
name: string;
|
|
7
|
+
files: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const templates: Record<string, Template>;
|
|
11
|
+
export function getTemplateNames(): string[];
|
|
12
|
+
export function getTemplate(name: string): Template | undefined;
|
package/src/launcher/main.ts
CHANGED
|
@@ -36,7 +36,7 @@ function elevateAndExit(): void {
|
|
|
36
36
|
returns: "ptr",
|
|
37
37
|
},
|
|
38
38
|
});
|
|
39
|
-
const encode = (s: string) => ptr(new Uint8Array(Buffer.from(s + "\0", "
|
|
39
|
+
const encode = (s: string) => ptr(new Uint8Array(Buffer.from(s + "\0", "utf16le")));
|
|
40
40
|
shell32.symbols.ShellExecuteW(
|
|
41
41
|
null,
|
|
42
42
|
encode("runas"),
|
|
@@ -11208,55 +11208,66 @@ static std::map<std::string, WebKitWebsiteDataManager*> g_partitionDataManagers;
|
|
|
11208
11208
|
static WebKitWebsiteDataManager* getDataManagerForPartition(const char* partitionIdentifier) {
|
|
11209
11209
|
std::string partition = partitionIdentifier ? partitionIdentifier : "";
|
|
11210
11210
|
|
|
11211
|
+
// Prefer the data manager owned by the live webview context for this
|
|
11212
|
+
// partition. getContextForPartition() builds each webview's context around
|
|
11213
|
+
// its own WebKitWebsiteDataManager; creating a separate manager here — even
|
|
11214
|
+
// one pointing at the same on-disk directory — clears a different instance
|
|
11215
|
+
// and leaves the running webview's storage untouched, which made
|
|
11216
|
+
// clearStorageData()/cookies.clear() appear to do nothing. Resolving through
|
|
11217
|
+
// the context keeps clears on the exact store the page reads and writes.
|
|
11218
|
+
if (partition.empty()) {
|
|
11219
|
+
WebKitWebContext* context = webkit_web_context_get_default();
|
|
11220
|
+
return webkit_web_context_get_website_data_manager(context);
|
|
11221
|
+
}
|
|
11222
|
+
|
|
11223
|
+
auto ctxIt = g_partitionContexts.find(partition);
|
|
11224
|
+
if (ctxIt != g_partitionContexts.end() && ctxIt->second) {
|
|
11225
|
+
return webkit_web_context_get_website_data_manager(ctxIt->second);
|
|
11226
|
+
}
|
|
11227
|
+
|
|
11228
|
+
// No live context for this partition yet — fall back to a standalone manager
|
|
11229
|
+
// (cached) so cookie/storage APIs still work before a webview is created.
|
|
11211
11230
|
auto it = g_partitionDataManagers.find(partition);
|
|
11212
11231
|
if (it != g_partitionDataManagers.end()) {
|
|
11213
11232
|
return it->second;
|
|
11214
11233
|
}
|
|
11215
11234
|
|
|
11216
11235
|
WebKitWebsiteDataManager* dataManager = nullptr;
|
|
11236
|
+
bool isPersistent = partition.substr(0, 8) == "persist:";
|
|
11217
11237
|
|
|
11218
|
-
if (
|
|
11219
|
-
|
|
11220
|
-
WebKitWebContext* context = webkit_web_context_get_default();
|
|
11221
|
-
dataManager = webkit_web_context_get_website_data_manager(context);
|
|
11222
|
-
} else {
|
|
11223
|
-
bool isPersistent = partition.substr(0, 8) == "persist:";
|
|
11238
|
+
if (isPersistent) {
|
|
11239
|
+
std::string partitionName = partition.substr(8);
|
|
11224
11240
|
|
|
11225
|
-
|
|
11226
|
-
|
|
11241
|
+
// Build paths with identifier/channel structure (consistent with CLI and updater)
|
|
11242
|
+
char* home = getenv("HOME");
|
|
11243
|
+
std::string homeStr = home ? std::string(home) : "/tmp";
|
|
11244
|
+
std::string dataPath = buildPartitionPath(homeStr + "/.local/share", g_sparkbunIdentifier, g_sparkbunChannel, "WebKit", partitionName);
|
|
11245
|
+
std::string cachePath = buildPartitionPath(homeStr + "/.cache", g_sparkbunIdentifier, g_sparkbunChannel, "WebKit", partitionName);
|
|
11227
11246
|
|
|
11228
|
-
|
|
11229
|
-
|
|
11230
|
-
std::string homeStr = home ? std::string(home) : "/tmp";
|
|
11231
|
-
std::string dataPath = buildPartitionPath(homeStr + "/.local/share", g_sparkbunIdentifier, g_sparkbunChannel, "WebKit", partitionName);
|
|
11232
|
-
std::string cachePath = buildPartitionPath(homeStr + "/.cache", g_sparkbunIdentifier, g_sparkbunChannel, "WebKit", partitionName);
|
|
11247
|
+
g_mkdir_with_parents(dataPath.c_str(), 0755);
|
|
11248
|
+
g_mkdir_with_parents(cachePath.c_str(), 0755);
|
|
11233
11249
|
|
|
11234
|
-
|
|
11235
|
-
|
|
11250
|
+
dataManager = webkit_website_data_manager_new(
|
|
11251
|
+
"base-data-directory", dataPath.c_str(),
|
|
11252
|
+
"base-cache-directory", cachePath.c_str(),
|
|
11253
|
+
NULL
|
|
11254
|
+
);
|
|
11236
11255
|
|
|
11237
|
-
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11256
|
+
// Enable persistent cookie storage (SQLite-backed)
|
|
11257
|
+
WebKitCookieManager* cookieManager = webkit_website_data_manager_get_cookie_manager(dataManager);
|
|
11258
|
+
if (cookieManager) {
|
|
11259
|
+
std::string cookiePath = dataPath + "/cookies.sqlite";
|
|
11260
|
+
webkit_cookie_manager_set_persistent_storage(
|
|
11261
|
+
cookieManager,
|
|
11262
|
+
cookiePath.c_str(),
|
|
11263
|
+
WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE
|
|
11241
11264
|
);
|
|
11242
|
-
|
|
11243
|
-
// Enable persistent cookie storage (SQLite-backed)
|
|
11244
|
-
WebKitCookieManager* cookieManager = webkit_website_data_manager_get_cookie_manager(dataManager);
|
|
11245
|
-
if (cookieManager) {
|
|
11246
|
-
std::string cookiePath = dataPath + "/cookies.sqlite";
|
|
11247
|
-
webkit_cookie_manager_set_persistent_storage(
|
|
11248
|
-
cookieManager,
|
|
11249
|
-
cookiePath.c_str(),
|
|
11250
|
-
WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE
|
|
11251
|
-
);
|
|
11252
|
-
}
|
|
11253
|
-
} else {
|
|
11254
|
-
dataManager = webkit_website_data_manager_new_ephemeral();
|
|
11255
11265
|
}
|
|
11256
|
-
|
|
11257
|
-
|
|
11266
|
+
} else {
|
|
11267
|
+
dataManager = webkit_website_data_manager_new_ephemeral();
|
|
11258
11268
|
}
|
|
11259
11269
|
|
|
11270
|
+
g_partitionDataManagers[partition] = dataManager;
|
|
11260
11271
|
return dataManager;
|
|
11261
11272
|
}
|
|
11262
11273
|
|
|
@@ -11651,11 +11662,44 @@ SPARKBUN_EXPORT bool sessionRemoveCookie(const char* partitionIdentifier, const
|
|
|
11651
11662
|
// Clear all cookies (WebKit2GTK)
|
|
11652
11663
|
// Clear all cookies (WebKit2GTK) - STUB implementation to prevent crashes
|
|
11653
11664
|
SPARKBUN_EXPORT void sessionClearCookies(const char* partitionIdentifier) {
|
|
11654
|
-
//
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
|
|
11658
|
-
|
|
11665
|
+
// Copy argument before dispatching to main thread
|
|
11666
|
+
std::string partitionStr = partitionIdentifier ? partitionIdentifier : "";
|
|
11667
|
+
|
|
11668
|
+
// Clear cookies via the website data manager rather than per-cookie
|
|
11669
|
+
// get/delete. This reuses the same dispatch + GMainLoop pattern as
|
|
11670
|
+
// sessionClearStorageData (which is stable) instead of the async cookie
|
|
11671
|
+
// enumeration that previously crashed and led to this being stubbed out.
|
|
11672
|
+
dispatch_sync_main_void([partitionStr]() {
|
|
11673
|
+
WebKitWebsiteDataManager* dataManager = getDataManagerForPartition(partitionStr.c_str());
|
|
11674
|
+
if (!dataManager) {
|
|
11675
|
+
return;
|
|
11676
|
+
}
|
|
11677
|
+
|
|
11678
|
+
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
|
|
11679
|
+
|
|
11680
|
+
webkit_website_data_manager_clear(dataManager, WEBKIT_WEBSITE_DATA_COOKIES, 0, nullptr,
|
|
11681
|
+
[](GObject* source, GAsyncResult* result, gpointer user_data) {
|
|
11682
|
+
GMainLoop* loop = static_cast<GMainLoop*>(user_data);
|
|
11683
|
+
GError* error = nullptr;
|
|
11684
|
+
webkit_website_data_manager_clear_finish(WEBKIT_WEBSITE_DATA_MANAGER(source), result, &error);
|
|
11685
|
+
if (error) {
|
|
11686
|
+
g_error_free(error);
|
|
11687
|
+
}
|
|
11688
|
+
g_main_loop_quit(loop);
|
|
11689
|
+
}, loop);
|
|
11690
|
+
|
|
11691
|
+
GSource* timeout = g_timeout_source_new(10000);
|
|
11692
|
+
g_source_set_callback(timeout, [](gpointer data) -> gboolean {
|
|
11693
|
+
g_main_loop_quit(static_cast<GMainLoop*>(data));
|
|
11694
|
+
return G_SOURCE_REMOVE;
|
|
11695
|
+
}, loop, nullptr);
|
|
11696
|
+
g_source_attach(timeout, g_main_loop_get_context(loop));
|
|
11697
|
+
|
|
11698
|
+
g_main_loop_run(loop);
|
|
11699
|
+
g_source_destroy(timeout);
|
|
11700
|
+
g_source_unref(timeout);
|
|
11701
|
+
g_main_loop_unref(loop);
|
|
11702
|
+
});
|
|
11659
11703
|
}
|
|
11660
11704
|
|
|
11661
11705
|
// Clear storage data (WebKit2GTK)
|
package/src/shared/bsdiff.ts
CHANGED
|
@@ -4,18 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
const MAGIC = "SBDIFF10";
|
|
6
6
|
|
|
7
|
-
function offtin(buf: Uint8Array, offset: number): bigint {
|
|
8
|
-
let y = 0n;
|
|
9
|
-
for (let i = 0; i < 7; i++) {
|
|
10
|
-
y |= BigInt(buf[offset + i]) << BigInt(i * 8);
|
|
11
|
-
}
|
|
12
|
-
y |= BigInt(buf[offset + 7] & 0x7f) << 56n;
|
|
13
|
-
if (buf[offset + 7] & 0x80) {
|
|
14
|
-
y = -y;
|
|
15
|
-
}
|
|
16
|
-
return y;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
7
|
function offtout(x: bigint, buf: Uint8Array, offset: number): void {
|
|
20
8
|
let y: bigint;
|
|
21
9
|
if (x < 0n) {
|
|
@@ -47,24 +35,24 @@ function search(
|
|
|
47
35
|
bestPos: { value: number },
|
|
48
36
|
): number {
|
|
49
37
|
if (hi - lo < 2) {
|
|
50
|
-
const loMatch = matchlen(oldData, suffixArray[lo]
|
|
51
|
-
const hiMatch = matchlen(oldData, suffixArray[hi]
|
|
38
|
+
const loMatch = matchlen(oldData, suffixArray[lo]!, newData, newStart);
|
|
39
|
+
const hiMatch = matchlen(oldData, suffixArray[hi]!, newData, newStart);
|
|
52
40
|
if (loMatch > hiMatch) {
|
|
53
|
-
bestPos.value = suffixArray[lo]
|
|
41
|
+
bestPos.value = suffixArray[lo]!;
|
|
54
42
|
return loMatch;
|
|
55
43
|
}
|
|
56
|
-
bestPos.value = suffixArray[hi]
|
|
44
|
+
bestPos.value = suffixArray[hi]!;
|
|
57
45
|
return hiMatch;
|
|
58
46
|
}
|
|
59
47
|
|
|
60
48
|
const mid = lo + ((hi - lo) >>> 1);
|
|
61
|
-
const midPos = suffixArray[mid]
|
|
49
|
+
const midPos = suffixArray[mid]!;
|
|
62
50
|
const compareLen = Math.min(oldData.length - midPos, newData.length - newStart);
|
|
63
51
|
|
|
64
52
|
let cmp = 0;
|
|
65
53
|
for (let i = 0; i < compareLen; i++) {
|
|
66
|
-
if (oldData[midPos + i] < newData[newStart + i]) { cmp = -1; break; }
|
|
67
|
-
if (oldData[midPos + i] > newData[newStart + i]) { cmp = 1; break; }
|
|
54
|
+
if (oldData[midPos + i]! < newData[newStart + i]!) { cmp = -1; break; }
|
|
55
|
+
if (oldData[midPos + i]! > newData[newStart + i]!) { cmp = 1; break; }
|
|
68
56
|
}
|
|
69
57
|
|
|
70
58
|
if (cmp < 0) {
|
|
@@ -82,7 +70,7 @@ function qsufsort(data: Uint8Array): Int32Array {
|
|
|
82
70
|
const lb = n - b;
|
|
83
71
|
const len = la < lb ? la : lb;
|
|
84
72
|
for (let i = 0; i < len; i++) {
|
|
85
|
-
if (data[a + i] !== data[b + i]) return data[a + i] - data[b + i]
|
|
73
|
+
if (data[a + i] !== data[b + i]) return data[a + i]! - data[b + i]!;
|
|
86
74
|
}
|
|
87
75
|
return la - lb;
|
|
88
76
|
});
|
|
@@ -175,12 +163,12 @@ export function createPatch(oldData: Uint8Array, newData: Uint8Array): Uint8Arra
|
|
|
175
163
|
}
|
|
176
164
|
|
|
177
165
|
for (let i = 0; i < lenf; i++) {
|
|
178
|
-
diffBytes.push((newData[lastScan + i] - oldData[lastPos + i] + 256) & 0xff);
|
|
166
|
+
diffBytes.push((newData[lastScan + i]! - oldData[lastPos + i]! + 256) & 0xff);
|
|
179
167
|
}
|
|
180
168
|
|
|
181
169
|
const extraLen = (scan - lenb) - (lastScan + lenf);
|
|
182
170
|
for (let i = 0; i < extraLen; i++) {
|
|
183
|
-
extraBytes.push(newData[lastScan + lenf + i]);
|
|
171
|
+
extraBytes.push(newData[lastScan + lenf + i]!);
|
|
184
172
|
}
|
|
185
173
|
|
|
186
174
|
controlEntries.push(BigInt(lenf), BigInt(extraLen), BigInt((pos - lenb) - (lastPos + lenf)));
|
|
@@ -193,7 +181,7 @@ export function createPatch(oldData: Uint8Array, newData: Uint8Array): Uint8Arra
|
|
|
193
181
|
|
|
194
182
|
const controlBuf = new Uint8Array(controlEntries.length * 8);
|
|
195
183
|
for (let i = 0; i < controlEntries.length; i++) {
|
|
196
|
-
offtout(controlEntries[i]
|
|
184
|
+
offtout(controlEntries[i]!, controlBuf, i * 8);
|
|
197
185
|
}
|
|
198
186
|
|
|
199
187
|
const controlCompressed = Bun.gzipSync(controlBuf);
|
|
@@ -225,7 +213,7 @@ if (import.meta.main) {
|
|
|
225
213
|
console.error("Usage: bsdiff <oldfile> <newfile> <patchfile>");
|
|
226
214
|
process.exit(1);
|
|
227
215
|
}
|
|
228
|
-
const [oldPath, newPath, patchPath] = args;
|
|
216
|
+
const [oldPath, newPath, patchPath] = args as [string, string, string];
|
|
229
217
|
const oldData = new Uint8Array(await Bun.file(oldPath).arrayBuffer());
|
|
230
218
|
const newData = new Uint8Array(await Bun.file(newPath).arrayBuffer());
|
|
231
219
|
const patch = createPatch(oldData, newData);
|
package/src/shared/bspatch.ts
CHANGED
|
@@ -7,10 +7,10 @@ const MAGIC = "SBDIFF10";
|
|
|
7
7
|
function offtin(buf: Uint8Array, offset: number): bigint {
|
|
8
8
|
let y = 0n;
|
|
9
9
|
for (let i = 0; i < 7; i++) {
|
|
10
|
-
y |= BigInt(buf[offset + i]) << BigInt(i * 8);
|
|
10
|
+
y |= BigInt(buf[offset + i]!) << BigInt(i * 8);
|
|
11
11
|
}
|
|
12
|
-
y |= BigInt(buf[offset + 7] & 0x7f) << 56n;
|
|
13
|
-
if (buf[offset + 7] & 0x80) {
|
|
12
|
+
y |= BigInt(buf[offset + 7]! & 0x7f) << 56n;
|
|
13
|
+
if (buf[offset + 7]! & 0x80) {
|
|
14
14
|
y = -y;
|
|
15
15
|
}
|
|
16
16
|
return y;
|
|
@@ -64,7 +64,7 @@ export function applyPatch(oldData: Uint8Array, patchData: Uint8Array): Uint8Arr
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
for (let i = 0; i < readDiffBy; i++) {
|
|
67
|
-
newData[newPos + i] = (oldData[oldPos + i] + diffBlock[diffPos + i]) & 0xff;
|
|
67
|
+
newData[newPos + i] = (oldData[oldPos + i]! + diffBlock[diffPos + i]!) & 0xff;
|
|
68
68
|
}
|
|
69
69
|
diffPos += readDiffBy;
|
|
70
70
|
newPos += readDiffBy;
|
|
@@ -88,7 +88,7 @@ if (import.meta.main) {
|
|
|
88
88
|
console.error("Usage: bspatch <oldfile> <newfile> <patchfile>");
|
|
89
89
|
process.exit(1);
|
|
90
90
|
}
|
|
91
|
-
const [oldPath, newPath, patchPath] = args;
|
|
91
|
+
const [oldPath, newPath, patchPath] = args as [string, string, string];
|
|
92
92
|
const oldData = new Uint8Array(await Bun.file(oldPath).arrayBuffer());
|
|
93
93
|
const patchData = new Uint8Array(await Bun.file(patchPath).arrayBuffer());
|
|
94
94
|
const newData = applyPatch(oldData, patchData);
|
package/src/shared/rpc.ts
CHANGED
|
@@ -461,6 +461,32 @@ export interface RPCWithTransport {
|
|
|
461
461
|
setTransport: (transport: RPCTransport) => void;
|
|
462
462
|
}
|
|
463
463
|
|
|
464
|
+
/**
|
|
465
|
+
* Permissive RPC surface used as the *default* type argument for BrowserView,
|
|
466
|
+
* BrowserWindow and Electroview when the caller does not parameterize them with
|
|
467
|
+
* a concrete schema (e.g. `let win: BrowserWindow`). It exposes the send/request
|
|
468
|
+
* proxies with loose typing so member access type-checks without casts, while a
|
|
469
|
+
* concrete `BrowserWindow<typeof myRpc>` still gets fully-typed methods. It
|
|
470
|
+
* extends RPCWithTransport, so real RPC objects (the return of defineRPC) remain
|
|
471
|
+
* assignable to the `T extends RPCWithTransport` constraint.
|
|
472
|
+
*/
|
|
473
|
+
export interface AnyRPC extends RPCWithTransport {
|
|
474
|
+
// Members are intentionally `any`: this is the permissive *default* type
|
|
475
|
+
// argument, so member access (rpc.send.foo(), rpc.request.bar()) type-checks
|
|
476
|
+
// without casts. Crucially, the concrete RPC object returned by defineRPC
|
|
477
|
+
// (with fully-typed send/request) stays assignable to AnyRPC — so a typed
|
|
478
|
+
// `new BrowserWindow({ rpc })` is assignable to a bare `BrowserWindow`
|
|
479
|
+
// annotation. Parameterize explicitly (BrowserWindow<typeof rpc>) for full
|
|
480
|
+
// per-method typing.
|
|
481
|
+
send: any;
|
|
482
|
+
request: any;
|
|
483
|
+
sendProxy: any;
|
|
484
|
+
requestProxy: any;
|
|
485
|
+
addMessageListener: any;
|
|
486
|
+
removeMessageListener: any;
|
|
487
|
+
proxy: any;
|
|
488
|
+
}
|
|
489
|
+
|
|
464
490
|
export type SparkBunRPCConfig<
|
|
465
491
|
Schema extends SparkBunRPCSchema,
|
|
466
492
|
Side extends "bun" | "webview",
|
package/tsconfig.json
CHANGED
|
@@ -27,5 +27,8 @@
|
|
|
27
27
|
"noUncheckedIndexedAccess": true
|
|
28
28
|
},
|
|
29
29
|
"include": ["src/**/*"],
|
|
30
|
-
|
|
30
|
+
// src/installer/* are standalone templates compiled by the CLI in a staging
|
|
31
|
+
// dir alongside generated siblings (app-archive.tar.gz, embedded-archives,
|
|
32
|
+
// metadata.json); they cannot resolve in the library's own compilation.
|
|
33
|
+
"exclude": ["node_modules", "dist", "vendors", "build", "src/tests", "src/installer"]
|
|
31
34
|
}
|