mirinjs 0.1.0-alpha.15 → 0.1.0-alpha.17

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": "mirinjs",
3
- "version": "0.1.0-alpha.15",
3
+ "version": "0.1.0-alpha.17",
4
4
  "description": "Build desktop apps with Bun, TypeScript, and Chromium (CEF). The Bun-native Electron alternative.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -311,6 +311,8 @@ export interface ReleaseConfig {
311
311
  baseUrl: string;
312
312
  /** Update channel; baked into artifact names + support dir. Default "stable". */
313
313
  channel?: string;
314
+ /** Optional markdown release notes embedded in the update manifest. */
315
+ notes?: string;
314
316
  }
315
317
 
316
318
  export interface CefConfig {
package/src/index.ts CHANGED
@@ -50,10 +50,12 @@ export type { LogLevel } from "./logger.ts";
50
50
  export { getLogLevel, Logger, logger, setLogLevel } from "./logger.ts";
51
51
  export type { MenuItemTemplate, MenuRole } from "./menu.ts";
52
52
  export { menu } from "./menu.ts";
53
+ export type { NotificationOptions } from "./notification.ts";
54
+ export { notification } from "./notification.ts";
53
55
  export { NotAttachedError, resolveWorker } from "./runtime.ts";
54
56
  export { globalShortcut } from "./shortcut.ts";
55
57
  export type { SidecarOptions, SidecarProcess } from "./sidecar.ts";
56
- export { sidecar } from "./sidecar.ts";
58
+ export { resolveSidecar, sidecar } from "./sidecar.ts";
57
59
  export type { TrayOptions } from "./tray.ts";
58
60
  export { Tray } from "./tray.ts";
59
61
  export type { UpdateInfo, UpdateProgress, UpdaterEvents, UpdaterStatus } from "./updater/index.ts";
package/src/native.ts CHANGED
@@ -42,6 +42,7 @@ const symbols = {
42
42
  mirin_clipboard_read_text: { args: [], returns: FFIType.ptr },
43
43
  mirin_clipboard_write_text: { args: [FFIType.ptr], returns: FFIType.void },
44
44
  mirin_dialog_show: { args: [FFIType.ptr], returns: FFIType.void },
45
+ mirin_notification_show: { args: [FFIType.ptr], returns: FFIType.i32 },
45
46
  } as const;
46
47
 
47
48
  export type EventListener = (event: string) => void;
@@ -174,4 +175,9 @@ export class Core {
174
175
  const buf = nullTerminated(specJson);
175
176
  this.#lib.symbols.mirin_dialog_show(ptr(buf));
176
177
  }
178
+
179
+ notificationShow(specJson: string): boolean {
180
+ const buf = nullTerminated(specJson);
181
+ return this.#lib.symbols.mirin_notification_show(ptr(buf)) === 1;
182
+ }
177
183
  }
@@ -0,0 +1,19 @@
1
+ import { runtime } from "./runtime.ts";
2
+
3
+ export interface NotificationOptions {
4
+ title: string;
5
+ body?: string;
6
+ }
7
+
8
+ export const notification = {
9
+ /** Ask the operating system to display a desktop notification. */
10
+ show(options: NotificationOptions): boolean {
11
+ const title = options.title.trim();
12
+ if (!title || title.length > 128) return false;
13
+ if (options.body && options.body.length > 512) return false;
14
+
15
+ return runtime().core.notificationShow(
16
+ JSON.stringify({ ...options, title, appName: runtime().id }),
17
+ );
18
+ },
19
+ };
package/src/sidecar.ts CHANGED
@@ -36,6 +36,17 @@ export interface SidecarProcess {
36
36
  kill(signal?: number | NodeJS.Signals): void;
37
37
  }
38
38
 
39
+ /** Resolve a declared sidecar's staged/bundled path without starting it. */
40
+ export function resolveSidecar(name: string): string {
41
+ const dir = runtime().sidecarDir;
42
+ if (!dir) {
43
+ throw new Error(
44
+ `resolveSidecar("${name}"): no sidecar dir (is this running under the mirin host?)`,
45
+ );
46
+ }
47
+ return join(dir, name);
48
+ }
49
+
39
50
  const children = new Set<Bun.Subprocess>();
40
51
  let hooked = false;
41
52
 
@@ -68,15 +79,9 @@ function ensureHooks(): void {
68
79
  * isn't attached or no `sidecarDir` is known (e.g. run outside the host).
69
80
  */
70
81
  export function sidecar(name: string, opts: SidecarOptions = {}): SidecarProcess {
71
- const dir = runtime().sidecarDir;
72
- if (!dir) {
73
- throw new Error(
74
- `app.sidecar("${name}"): no sidecar dir (is this running under the mirin host?)`,
75
- );
76
- }
77
82
  ensureHooks();
78
83
 
79
- const bin = join(dir, name);
84
+ const bin = resolveSidecar(name);
80
85
  const child = Bun.spawn([bin, ...(opts.args ?? [])], {
81
86
  cwd: opts.cwd,
82
87
  env: opts.env ? { ...process.env, ...opts.env } : process.env,
@@ -8,11 +8,13 @@ export function parseManifest(
8
8
  expected: { channel: string; platform: string; arch: string },
9
9
  ): Manifest {
10
10
  const manifest = record(value, "manifest");
11
+ const body = optionalStringField(manifest, "body", 64 * 1024);
11
12
  const parsed: Manifest = {
12
13
  version: versionField(manifest, "version"),
13
14
  channel: stringField(manifest, "channel"),
14
15
  platform: stringField(manifest, "platform"),
15
16
  arch: stringField(manifest, "arch"),
17
+ ...(body === undefined ? {} : { body }),
16
18
  tarHash: sha256Field(manifest, "tarHash"),
17
19
  bundle: artifactField(manifest.bundle, "bundle"),
18
20
  patches: patchesField(manifest.patches),
@@ -44,6 +46,19 @@ function stringField(source: Record<string, unknown>, key: string): string {
44
46
  return value;
45
47
  }
46
48
 
49
+ function optionalStringField(
50
+ source: Record<string, unknown>,
51
+ key: string,
52
+ maxLength: number,
53
+ ): string | undefined {
54
+ const value = source[key];
55
+ if (value === undefined) return undefined;
56
+ if (typeof value !== "string" || value.length > maxLength) {
57
+ throw new Error(`invalid update manifest field: ${key}`);
58
+ }
59
+ return value;
60
+ }
61
+
47
62
  function versionField(source: Record<string, unknown>, key: string): string {
48
63
  const value = stringField(source, key);
49
64
  if (
@@ -32,6 +32,8 @@ export interface Manifest {
32
32
  channel: string;
33
33
  platform: string;
34
34
  arch: string;
35
+ /** Optional markdown release notes shown by app UIs. */
36
+ body?: string;
35
37
  /** SHA-256 of the uncompressed bundle tar (update identity + integrity). */
36
38
  tarHash: string;
37
39
  bundle: UpdateArtifact;
@@ -43,6 +45,8 @@ export interface UpdateInfo {
43
45
  version: string;
44
46
  currentVersion: string;
45
47
  channel: string;
48
+ /** Optional markdown release notes from the update manifest. */
49
+ body?: string;
46
50
  }
47
51
 
48
52
  export interface UpdateProgress {
@@ -95,6 +95,7 @@ export class Updater {
95
95
  version: m.version,
96
96
  currentVersion: v.version,
97
97
  channel: v.channel,
98
+ body: m.body,
98
99
  };
99
100
  this.#setStatus("update-available");
100
101
  this.#emit("update-available", info);