janela 0.7.0 → 0.8.0
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/README.md +44 -7
- package/api/index.d.ts +10 -6
- package/bin/janela.mjs +9 -7
- package/package.json +1 -1
- package/runtime/janela.ts +36 -6
- package/runtime/types.ts +66 -3
- package/templates/react/files/src-host/main.ts +7 -8
- package/templates/solid/files/src-host/main.ts +7 -8
- package/templates/svelte/files/src-host/main.ts +7 -8
- package/templates/vue/files/src-host/main.ts +7 -8
package/README.md
CHANGED
|
@@ -141,9 +141,10 @@ framework templates' default.
|
|
|
141
141
|
import type { JanelaApp } from "janela/host";
|
|
142
142
|
|
|
143
143
|
export type AppCommands = {
|
|
144
|
-
add:
|
|
145
|
-
greet:
|
|
146
|
-
wait:
|
|
144
|
+
add: (args: { a: number; b: number }) => number;
|
|
145
|
+
greet: (args: { name: string }) => string;
|
|
146
|
+
wait: (args: { ms: number }) => string;
|
|
147
|
+
quit: () => void; // a command that takes nothing
|
|
147
148
|
};
|
|
148
149
|
export type AppEvents = { added: number };
|
|
149
150
|
|
|
@@ -195,11 +196,23 @@ Two things worth knowing:
|
|
|
195
196
|
own declarations rather than from an assertion you write by hand, which is
|
|
196
197
|
only possible because both sides are TypeScript.
|
|
197
198
|
|
|
198
|
-
|
|
199
|
-
|
|
199
|
+
A command that takes nothing is declared `() => void`, and its handler
|
|
200
|
+
returns `null` — every command answers the page's promise with a value, so
|
|
201
|
+
`void` is normalised to `null`. The page calls it as
|
|
202
|
+
`client.invoke("quit", null)`.
|
|
200
203
|
|
|
201
|
-
|
|
202
|
-
|
|
204
|
+
An **event payload is a single value** of any JSON-shaped type. For an event
|
|
205
|
+
carrying several things, prefer an object (`{ done: number; total: number }`):
|
|
206
|
+
adding a field later does not break existing listeners, and the names read
|
|
207
|
+
better at the call site. A tuple works too, but note that only the payload
|
|
208
|
+
*value* may be a tuple — the varargs spelling `app.emit("progress", 3, 10)`
|
|
209
|
+
does not compile (`SC2011: values of type '[done: number, total: number]'
|
|
210
|
+
have no static representation`, which would require `--dynamic` and ~620 KB
|
|
211
|
+
of embedded engine).
|
|
212
|
+
|
|
213
|
+
The `{ args; result }` record form of 0.5.x–0.7.x is still accepted, and the
|
|
214
|
+
untyped `invoke` / `listen` still work unchanged; the contract is additive,
|
|
215
|
+
and the `vanilla` template still uses the global.
|
|
203
216
|
|
|
204
217
|
## Async commands
|
|
205
218
|
|
|
@@ -310,6 +323,30 @@ nested modal loop would otherwise re-enter the host loop underneath a live TS
|
|
|
310
323
|
frame; [docs/native-shell.md](../../docs/native-shell.md) has the details, the
|
|
311
324
|
per-platform table, and the Windows GUI-subsystem note.
|
|
312
325
|
|
|
326
|
+
## Migrating from 0.7.x
|
|
327
|
+
|
|
328
|
+
Commands are declared as the functions they are. The old `{ args; result }`
|
|
329
|
+
record form still compiles, so this is optional — but the function form is
|
|
330
|
+
shorter, and a command that takes nothing is finally natural to write.
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
// 0.7.x
|
|
334
|
+
export type AppCommands = {
|
|
335
|
+
add: { args: { a: number; b: number }; result: number };
|
|
336
|
+
quit: { args: null; result: null };
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// 0.8.0
|
|
340
|
+
export type AppCommands = {
|
|
341
|
+
add: (args: { a: number; b: number }) => number;
|
|
342
|
+
quit: () => void;
|
|
343
|
+
};
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Nothing else changes: `export type App = JanelaApp<AppCommands, AppEvents>`,
|
|
347
|
+
`setup(app: App)`, `app.command(...)` and the page's `createClient<App>()` are
|
|
348
|
+
all as they were. A handler for a `() => void` command returns `null`.
|
|
349
|
+
|
|
313
350
|
## Migrating from 0.6.x
|
|
314
351
|
|
|
315
352
|
The contract lives entirely in the types now, so the runtime tokens are gone.
|
package/api/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* parse.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type {
|
|
9
|
+
import type { JanelaAppImpl } from "../runtime/janela";
|
|
10
10
|
import type { CommandShapes, Commands, Events } from "../runtime/types";
|
|
11
11
|
|
|
12
12
|
/** Removes a subscription created by `listen` / `client.on`. */
|
|
@@ -65,18 +65,22 @@ export interface Contract {
|
|
|
65
65
|
/**
|
|
66
66
|
* The command table declared by a contract.
|
|
67
67
|
*
|
|
68
|
-
* Reads the contract off
|
|
69
|
-
*
|
|
70
|
-
*
|
|
68
|
+
* Reads the contract off the app type — what a host's `App` is from 0.7.0 —
|
|
69
|
+
* and falls back to the 0.5.x/0.6.x `{ commands, events }` wrapper, so a
|
|
70
|
+
* project written against either shape keeps checking.
|
|
71
|
+
*
|
|
72
|
+
* The inference targets the class rather than the `JanelaApp` alias: the
|
|
73
|
+
* alias applies `Norm<C>`, which cannot be inferred backwards, and the
|
|
74
|
+
* normalised table is what indexing wants in any case.
|
|
71
75
|
*/
|
|
72
|
-
export type CommandsOf<A> = A extends
|
|
76
|
+
export type CommandsOf<A> = A extends JanelaAppImpl<infer M, infer _E>
|
|
73
77
|
? M
|
|
74
78
|
: A extends { commands: Commands<infer M> }
|
|
75
79
|
? M
|
|
76
80
|
: never;
|
|
77
81
|
|
|
78
82
|
/** The event table declared by a contract; see CommandsOf for the two shapes. */
|
|
79
|
-
export type EventsOf<A> = A extends
|
|
83
|
+
export type EventsOf<A> = A extends JanelaAppImpl<infer _M, infer E>
|
|
80
84
|
? E
|
|
81
85
|
: A extends { events: Events<infer E> }
|
|
82
86
|
? E
|
package/bin/janela.mjs
CHANGED
|
@@ -516,17 +516,19 @@ function build(root, { devUrl = null, gui = true } = {}) {
|
|
|
516
516
|
join(buildDir, "entry.ts"),
|
|
517
517
|
`// Generated by janela — do not edit.\n` +
|
|
518
518
|
`import { createApp } from "./janela";\n` +
|
|
519
|
-
`import type { CommandShapes,
|
|
519
|
+
`import type { CommandShapes, JanelaAppImpl } from "./janela";\n` +
|
|
520
520
|
`import { WINDOW } from "./config";\n` +
|
|
521
521
|
`import { INDEX_HTML } from "./frontend";\n` +
|
|
522
522
|
`import { setup } from "./main";\n\n` +
|
|
523
523
|
`// The app's type parameters are read back off setup()'s own signature,\n` +
|
|
524
|
-
`// so a contract-typed setup(app:
|
|
525
|
-
`//
|
|
526
|
-
`//
|
|
527
|
-
`//
|
|
528
|
-
|
|
529
|
-
|
|
524
|
+
`// so a contract-typed setup(app: App) and a plain setup(app: JanelaApp)\n` +
|
|
525
|
+
`// each get an app instantiated to match. scriptc monomorphises generic\n` +
|
|
526
|
+
`// classes, so the right instantiation must be CONSTRUCTED here - no cast\n` +
|
|
527
|
+
`// can bridge two of them. The inference reads the CLASS, not the\n` +
|
|
528
|
+
`// JanelaApp alias: the alias applies Norm<C>, which cannot be reversed,\n` +
|
|
529
|
+
`// and what is wanted here is the normalised table anyway.\n` +
|
|
530
|
+
`type CmdsOf<F> = F extends (app: JanelaAppImpl<infer C, infer _E>) => void ? C : CommandShapes;\n` +
|
|
531
|
+
`type EvtsOf<F> = F extends (app: JanelaAppImpl<infer _C, infer E>) => void ? E : Record<string, unknown>;\n\n` +
|
|
530
532
|
`const app = createApp<CmdsOf<typeof setup>, EvtsOf<typeof setup>>(WINDOW);\n` +
|
|
531
533
|
`setup(app);\n` +
|
|
532
534
|
`const rc = app.run(INDEX_HTML) + 0;\n` +
|
package/package.json
CHANGED
package/runtime/janela.ts
CHANGED
|
@@ -83,11 +83,16 @@ const BOOTSTRAP =
|
|
|
83
83
|
// user's editor can see them). Re-exported here because the compiled build
|
|
84
84
|
// resolves them through this module — see the specifier rewrite in the CLI.
|
|
85
85
|
export type {
|
|
86
|
+
ArgsOf,
|
|
86
87
|
AsyncCommandHandler,
|
|
87
88
|
CommandHandler,
|
|
88
89
|
CommandShape,
|
|
89
90
|
CommandShapes,
|
|
91
|
+
CommandSpec,
|
|
92
|
+
CommandSpecs,
|
|
90
93
|
Commands,
|
|
94
|
+
Norm,
|
|
95
|
+
ResultOf,
|
|
91
96
|
DialogFilter,
|
|
92
97
|
Events,
|
|
93
98
|
FsCallback,
|
|
@@ -105,7 +110,9 @@ import type {
|
|
|
105
110
|
AsyncCommandHandler,
|
|
106
111
|
CommandHandler,
|
|
107
112
|
CommandShapes,
|
|
113
|
+
CommandSpecs,
|
|
108
114
|
Commands,
|
|
115
|
+
Norm,
|
|
109
116
|
DialogFilter,
|
|
110
117
|
Events,
|
|
111
118
|
FsCallback,
|
|
@@ -156,7 +163,7 @@ const TICK_DRAIN_MS = 4;
|
|
|
156
163
|
* interface (being signature-only) never is. A class receiver works even as a
|
|
157
164
|
* plain function parameter, which is what `setup(app)` is.
|
|
158
165
|
*/
|
|
159
|
-
export class
|
|
166
|
+
export class JanelaAppImpl<
|
|
160
167
|
C extends CommandShapes = CommandShapes,
|
|
161
168
|
E = Record<string, unknown>,
|
|
162
169
|
> {
|
|
@@ -571,11 +578,34 @@ export class JanelaApp<
|
|
|
571
578
|
}
|
|
572
579
|
}
|
|
573
580
|
|
|
581
|
+
/**
|
|
582
|
+
* A running janela app, typed by the contract it serves.
|
|
583
|
+
*
|
|
584
|
+
* This is an alias rather than the class itself so that a contract may be
|
|
585
|
+
* written as plain function types: `Norm` converts it to the record form the
|
|
586
|
+
* class indexes, at a point where the table is still concrete. Writing the
|
|
587
|
+
* record form directly keeps working — `Norm` is idempotent.
|
|
588
|
+
*
|
|
589
|
+
* ```ts
|
|
590
|
+
* export type AppCommands = { add: (args: { a: number; b: number }) => number };
|
|
591
|
+
* export type AppEvents = { added: number };
|
|
592
|
+
* export type App = JanelaApp<AppCommands, AppEvents>;
|
|
593
|
+
*
|
|
594
|
+
* export function setup(app: App): void {
|
|
595
|
+
* app.command("add", (args) => args.a + args.b); // args inferred, result checked
|
|
596
|
+
* }
|
|
597
|
+
* ```
|
|
598
|
+
*/
|
|
599
|
+
export type JanelaApp<
|
|
600
|
+
C extends CommandSpecs = CommandShapes,
|
|
601
|
+
E = Record<string, unknown>,
|
|
602
|
+
> = JanelaAppImpl<Norm<C>, E>;
|
|
603
|
+
|
|
574
604
|
export function createApp<
|
|
575
605
|
C extends CommandShapes = CommandShapes,
|
|
576
606
|
E = Record<string, unknown>,
|
|
577
|
-
>(cfg: WindowConfig):
|
|
578
|
-
return new
|
|
607
|
+
>(cfg: WindowConfig): JanelaAppImpl<C, E> {
|
|
608
|
+
return new JanelaAppImpl<C, E>(cfg);
|
|
579
609
|
}
|
|
580
610
|
|
|
581
611
|
// ---------------------------------------------------------------------------
|
|
@@ -586,7 +616,7 @@ export function createApp<
|
|
|
586
616
|
|
|
587
617
|
/** @deprecated Use `app.command(name, handler)` on a contract-typed app. */
|
|
588
618
|
export function on<M extends CommandShapes, K extends keyof M & string>(
|
|
589
|
-
app:
|
|
619
|
+
app: JanelaAppImpl,
|
|
590
620
|
_commands: Commands<M>,
|
|
591
621
|
name: K,
|
|
592
622
|
handler: (args: M[K]["args"]) => M[K]["result"],
|
|
@@ -596,7 +626,7 @@ export function on<M extends CommandShapes, K extends keyof M & string>(
|
|
|
596
626
|
|
|
597
627
|
/** @deprecated Use `app.commandAsync(name, handler)` on a contract-typed app. */
|
|
598
628
|
export function onAsync<M extends CommandShapes, K extends keyof M & string>(
|
|
599
|
-
app:
|
|
629
|
+
app: JanelaAppImpl,
|
|
600
630
|
_commands: Commands<M>,
|
|
601
631
|
name: K,
|
|
602
632
|
handler: (
|
|
@@ -615,7 +645,7 @@ export function onAsync<M extends CommandShapes, K extends keyof M & string>(
|
|
|
615
645
|
|
|
616
646
|
/** @deprecated Use `app.emit(event, payload)` on a contract-typed app. */
|
|
617
647
|
export function emit<E, K extends keyof E & string>(
|
|
618
|
-
app:
|
|
648
|
+
app: JanelaAppImpl,
|
|
619
649
|
_events: Events<E>,
|
|
620
650
|
name: K,
|
|
621
651
|
payload: E[K],
|
package/runtime/types.ts
CHANGED
|
@@ -90,15 +90,78 @@ export interface WindowConfig {
|
|
|
90
90
|
// tokens and their `define*` constructors are the 0.5.x/0.6.x shape, kept so
|
|
91
91
|
// projects written against it still compile.
|
|
92
92
|
|
|
93
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* One command's argument and result types, in normalised form.
|
|
95
|
+
*
|
|
96
|
+
* This is what the app class works with internally. A contract is *written*
|
|
97
|
+
* as plain function types — see CommandSpec — and normalised to this by
|
|
98
|
+
* `Norm` before it reaches the class.
|
|
99
|
+
*/
|
|
94
100
|
export interface CommandShape {
|
|
95
101
|
args: unknown;
|
|
96
102
|
result: unknown;
|
|
97
103
|
}
|
|
98
104
|
|
|
99
|
-
/** A
|
|
105
|
+
/** A normalised command table: name → shape. */
|
|
100
106
|
export type CommandShapes = Record<string, CommandShape>;
|
|
101
107
|
|
|
108
|
+
/**
|
|
109
|
+
* How a command may be declared in a contract: as a plain function type
|
|
110
|
+
* (preferred), or as the `{ args; result }` record of 0.5.x–0.7.x.
|
|
111
|
+
*
|
|
112
|
+
* ```ts
|
|
113
|
+
* type AppCommands = {
|
|
114
|
+
* add: (args: { a: number; b: number }) => number;
|
|
115
|
+
* quit: () => void; // no arguments
|
|
116
|
+
* legacy: { args: { name: string }; result: string }; // still accepted
|
|
117
|
+
* };
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export type CommandSpec = ((...args: never[]) => unknown) | CommandShape;
|
|
121
|
+
|
|
122
|
+
/** A contract's command table as written: name → spec. */
|
|
123
|
+
export type CommandSpecs = Record<string, CommandSpec>;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The argument type of a declared command. A function's single parameter, or
|
|
127
|
+
* a record's `args`. A command declared with no parameters takes `null` — the
|
|
128
|
+
* page's `invoke(name)` sends null, and nothing is lost.
|
|
129
|
+
*/
|
|
130
|
+
export type ArgsOf<F> = F extends (...a: infer P) => unknown
|
|
131
|
+
? P extends [infer A]
|
|
132
|
+
? A
|
|
133
|
+
: null
|
|
134
|
+
: F extends { args: infer A }
|
|
135
|
+
? A
|
|
136
|
+
: null;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The result type of a declared command. `void` is normalised to `null`:
|
|
140
|
+
* every command answers the page's promise with a value, and scriptc has no
|
|
141
|
+
* conversion from a void value to the `unknown` the handler table holds.
|
|
142
|
+
*/
|
|
143
|
+
export type ResultOf<F> = F extends (...a: never[]) => infer R
|
|
144
|
+
? [R] extends [void]
|
|
145
|
+
? null
|
|
146
|
+
: R
|
|
147
|
+
: F extends { result: infer R }
|
|
148
|
+
? R
|
|
149
|
+
: never;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Normalise a written contract to the record form the app class indexes.
|
|
153
|
+
*
|
|
154
|
+
* This runs where `C` is still concrete — in the `JanelaApp<C, E>` alias, one
|
|
155
|
+
* step before the class — on purpose. scriptc cannot compile a *value* whose
|
|
156
|
+
* type is an unresolved conditional or a mapped type indexed by a type
|
|
157
|
+
* parameter (`SC2001: values of type 'ArgsOf<C[K]>' cannot be compiled yet`),
|
|
158
|
+
* so the class body only ever sees plain indexed access on a record.
|
|
159
|
+
* Idempotent: normalising a record-form table returns it unchanged.
|
|
160
|
+
*/
|
|
161
|
+
export type Norm<C> = {
|
|
162
|
+
[K in keyof C]: { args: ArgsOf<C[K]>; result: ResultOf<C[K]> };
|
|
163
|
+
};
|
|
164
|
+
|
|
102
165
|
/**
|
|
103
166
|
* A declared command contract. Carries `M` at the type level only — the value
|
|
104
167
|
* is empty, and exists so that inference has something to read at a call site.
|
|
@@ -119,7 +182,7 @@ export interface Events<E> {
|
|
|
119
182
|
* types and name the app itself:
|
|
120
183
|
*
|
|
121
184
|
* ```ts
|
|
122
|
-
* export type AppCommands = { add:
|
|
185
|
+
* export type AppCommands = { add: (args: { a: number; b: number }) => number };
|
|
123
186
|
* export type AppEvents = { added: number };
|
|
124
187
|
* export type App = JanelaApp<AppCommands, AppEvents>;
|
|
125
188
|
* export function setup(app: App): void { … }
|
|
@@ -9,19 +9,18 @@
|
|
|
9
9
|
// Two gotchas inherited from scriptc:
|
|
10
10
|
// - never use a bare FFI-backed call as a complete variable initializer;
|
|
11
11
|
// wrap it in any expression (`+ 0`);
|
|
12
|
-
// -
|
|
13
|
-
//
|
|
14
|
-
// to compile.
|
|
12
|
+
// - a command that returns nothing is declared `() => void` and its handler
|
|
13
|
+
// returns `null`; every command answers the page's promise with a value.
|
|
15
14
|
|
|
16
15
|
import type { JanelaApp } from "janela/host";
|
|
17
16
|
|
|
18
17
|
/** Every command this app answers. Declared once; the page checks against it. */
|
|
19
18
|
export type AppCommands = {
|
|
20
|
-
add:
|
|
21
|
-
greet:
|
|
22
|
-
log:
|
|
23
|
-
wait:
|
|
24
|
-
quit:
|
|
19
|
+
add: (args: { a: number; b: number }) => number;
|
|
20
|
+
greet: (args: { name: string }) => string;
|
|
21
|
+
log: (args: string) => void;
|
|
22
|
+
wait: (args: { ms: number }) => string;
|
|
23
|
+
quit: () => void;
|
|
25
24
|
};
|
|
26
25
|
|
|
27
26
|
/** Every event this app emits, and what each one carries. */
|
|
@@ -9,19 +9,18 @@
|
|
|
9
9
|
// Two gotchas inherited from scriptc:
|
|
10
10
|
// - never use a bare FFI-backed call as a complete variable initializer;
|
|
11
11
|
// wrap it in any expression (`+ 0`);
|
|
12
|
-
// -
|
|
13
|
-
//
|
|
14
|
-
// to compile.
|
|
12
|
+
// - a command that returns nothing is declared `() => void` and its handler
|
|
13
|
+
// returns `null`; every command answers the page's promise with a value.
|
|
15
14
|
|
|
16
15
|
import type { JanelaApp } from "janela/host";
|
|
17
16
|
|
|
18
17
|
/** Every command this app answers. Declared once; the page checks against it. */
|
|
19
18
|
export type AppCommands = {
|
|
20
|
-
add:
|
|
21
|
-
greet:
|
|
22
|
-
log:
|
|
23
|
-
wait:
|
|
24
|
-
quit:
|
|
19
|
+
add: (args: { a: number; b: number }) => number;
|
|
20
|
+
greet: (args: { name: string }) => string;
|
|
21
|
+
log: (args: string) => void;
|
|
22
|
+
wait: (args: { ms: number }) => string;
|
|
23
|
+
quit: () => void;
|
|
25
24
|
};
|
|
26
25
|
|
|
27
26
|
/** Every event this app emits, and what each one carries. */
|
|
@@ -9,19 +9,18 @@
|
|
|
9
9
|
// Two gotchas inherited from scriptc:
|
|
10
10
|
// - never use a bare FFI-backed call as a complete variable initializer;
|
|
11
11
|
// wrap it in any expression (`+ 0`);
|
|
12
|
-
// -
|
|
13
|
-
//
|
|
14
|
-
// to compile.
|
|
12
|
+
// - a command that returns nothing is declared `() => void` and its handler
|
|
13
|
+
// returns `null`; every command answers the page's promise with a value.
|
|
15
14
|
|
|
16
15
|
import type { JanelaApp } from "janela/host";
|
|
17
16
|
|
|
18
17
|
/** Every command this app answers. Declared once; the page checks against it. */
|
|
19
18
|
export type AppCommands = {
|
|
20
|
-
add:
|
|
21
|
-
greet:
|
|
22
|
-
log:
|
|
23
|
-
wait:
|
|
24
|
-
quit:
|
|
19
|
+
add: (args: { a: number; b: number }) => number;
|
|
20
|
+
greet: (args: { name: string }) => string;
|
|
21
|
+
log: (args: string) => void;
|
|
22
|
+
wait: (args: { ms: number }) => string;
|
|
23
|
+
quit: () => void;
|
|
25
24
|
};
|
|
26
25
|
|
|
27
26
|
/** Every event this app emits, and what each one carries. */
|
|
@@ -9,19 +9,18 @@
|
|
|
9
9
|
// Two gotchas inherited from scriptc:
|
|
10
10
|
// - never use a bare FFI-backed call as a complete variable initializer;
|
|
11
11
|
// wrap it in any expression (`+ 0`);
|
|
12
|
-
// -
|
|
13
|
-
//
|
|
14
|
-
// to compile.
|
|
12
|
+
// - a command that returns nothing is declared `() => void` and its handler
|
|
13
|
+
// returns `null`; every command answers the page's promise with a value.
|
|
15
14
|
|
|
16
15
|
import type { JanelaApp } from "janela/host";
|
|
17
16
|
|
|
18
17
|
/** Every command this app answers. Declared once; the page checks against it. */
|
|
19
18
|
export type AppCommands = {
|
|
20
|
-
add:
|
|
21
|
-
greet:
|
|
22
|
-
log:
|
|
23
|
-
wait:
|
|
24
|
-
quit:
|
|
19
|
+
add: (args: { a: number; b: number }) => number;
|
|
20
|
+
greet: (args: { name: string }) => string;
|
|
21
|
+
log: (args: string) => void;
|
|
22
|
+
wait: (args: { ms: number }) => string;
|
|
23
|
+
quit: () => void;
|
|
25
24
|
};
|
|
26
25
|
|
|
27
26
|
/** Every event this app emits, and what each one carries. */
|