sparkbun 0.2.4 → 0.2.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sparkbun",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Build fast, lightweight, cross-platform desktop apps with TypeScript and Bun.",
5
5
  "license": "MIT",
6
6
  "author": "SparkBun Contributors",
@@ -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?: T;
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 = 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 = 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 = 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();
@@ -97,7 +97,7 @@ sparkBunEventEmitter.on("close", (event: { data: { id: number } }) => {
97
97
 
98
98
  });
99
99
 
100
- export class BrowserWindow<T extends RPCWithTransport = RPCWithTransport> {
100
+ export class BrowserWindow<T extends RPCWithTransport = AnyRPC> {
101
101
  id = 0;
102
102
  title: string = "SparkBun";
103
103
  state: "creating" | "created" = "creating";
@@ -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(compressed);
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;
@@ -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 = hasFFI ? core_.symbols.getPrimaryDisplay() : null;
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 = hasFFI ? core_.symbols.getAllDisplays() : null;
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 = hasFFI ? core_.symbols.getCursorScreenPoint() : null;
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 hasFFI ? core_.symbols.getMouseButtons() : BigInt(0);
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 = Buffer.from(readFileSync(exePath));
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, 0); // filename
4413
- Buffer.from(now.padEnd(12, " ")).copy(header, 16); // timestamp
4414
- Buffer.from("0".padEnd(6, " ")).copy(header, 28); // owner
4415
- Buffer.from("0".padEnd(6, " ")).copy(header, 34); // group
4416
- Buffer.from(mode.toString(8).padEnd(8, " ")).copy(header, 40); // mode
4417
- Buffer.from(size.toString().padEnd(10, " ")).copy(header, 48); // size
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, 0, 0, Math.min(tarPath.length, 100));
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, 136);
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, 108);
4462
- Buffer.from("0000000\0").copy(header, 116);
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, 257);
4466
- Buffer.from("00").copy(header, 263);
4467
- Buffer.from("root\0").copy(header, 265);
4468
- Buffer.from("root\0").copy(header, 297);
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, 100);
4472
- Buffer.from("00000000000\0").copy(header, 124);
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, 100);
4477
- Buffer.from((content!.length).toString(8).padStart(11, "0") + "\0").copy(header, 124);
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, 148);
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, 148);
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
- projectRoot: string,
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;
@@ -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", "utf-16le")));
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"),
@@ -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], newData, newStart);
51
- const hiMatch = matchlen(oldData, suffixArray[hi], newData, newStart);
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], controlBuf, i * 8);
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);
@@ -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
- "exclude": ["node_modules", "dist", "vendors", "build", "src/tests"]
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
  }