janela 0.5.0 → 0.6.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 CHANGED
@@ -138,24 +138,27 @@ framework templates' default.
138
138
 
139
139
  ```ts
140
140
  // src-host/main.ts
141
- import { defineCommands, defineEvents, emit, on, onAsync, type JanelaApp } from "janela/host";
141
+ import { defineCommands, defineEvents, type JanelaApp } from "janela/host";
142
142
 
143
- export const commands = defineCommands<{
143
+ export type AppCommands = {
144
144
  add: { args: { a: number; b: number }; result: number };
145
145
  greet: { args: { name: string }; result: string };
146
146
  wait: { args: { ms: number }; result: string };
147
- }>();
147
+ };
148
+ export type AppEvents = { added: number };
148
149
 
149
- export const events = defineEvents<{ added: number }>();
150
+ export const commands = defineCommands<AppCommands>();
151
+ export const events = defineEvents<AppEvents>();
150
152
 
151
153
  export type App = { commands: typeof commands; events: typeof events };
152
154
 
153
- export function setup(app: JanelaApp): void {
154
- on(app, commands, "add", (args) => { // args inferred: { a: number; b: number }
155
- emit(app, events, "added", args.a + args.b);
155
+ // Typing the app with the contract is what makes the methods below checked.
156
+ export function setup(app: JanelaApp<AppCommands, AppEvents>): void {
157
+ app.command("add", (args) => { // args inferred: { a: number; b: number }
158
+ app.emit("added", args.a + args.b); // event name and payload checked
156
159
  return args.a + args.b; // return type checked against the contract
157
160
  });
158
- onAsync(app, commands, "wait", (args, resolve) => {
161
+ app.commandAsync("wait", (args, resolve) => {
159
162
  app.sleep(args.ms, () => resolve("waited " + args.ms + "ms"));
160
163
  });
161
164
  }
@@ -309,6 +312,43 @@ nested modal loop would otherwise re-enter the host loop underneath a live TS
309
312
  frame; [docs/native-shell.md](../../docs/native-shell.md) has the details, the
310
313
  per-platform table, and the Windows GUI-subsystem note.
311
314
 
315
+ ## Migrating from 0.5.x
316
+
317
+ The contract now rides on the app itself, so the standalone registrars are no
318
+ longer needed. Type the app with your contract and call its methods:
319
+
320
+ ```ts
321
+ // before (0.5.x)
322
+ export function setup(app: JanelaApp): void {
323
+ on(app, commands, "add", (args) => args.a + args.b);
324
+ onAsync(app, commands, "wait", (args, resolve) => { … });
325
+ emit(app, events, "added", 42);
326
+ }
327
+
328
+ // after (0.6.x)
329
+ export function setup(app: JanelaApp<AppCommands, AppEvents>): void {
330
+ app.command("add", (args) => args.a + args.b);
331
+ app.commandAsync("wait", (args, resolve) => { … });
332
+ app.emit("added", 42);
333
+ }
334
+ ```
335
+
336
+ Declare each contract as a named type so the same one feeds `defineCommands`
337
+ and the `setup` signature:
338
+
339
+ ```ts
340
+ export type AppCommands = { add: { args: { a: number; b: number }; result: number } };
341
+ export type AppEvents = { added: number };
342
+ export const commands = defineCommands<AppCommands>();
343
+ export const events = defineEvents<AppEvents>();
344
+ ```
345
+
346
+ `on`, `onAsync` and `emit` still work — they are `@deprecated` one-line
347
+ wrappers now — so 0.5.x code keeps compiling. The page side is unchanged:
348
+ `createClient<App>()` and `client.invoke(...)` are exactly as before. An app
349
+ with no contract needs no change at all: `setup(app: JanelaApp)` still gets an
350
+ untyped `app.command(name, handler)`.
351
+
312
352
  ## Migrating from 0.4.x
313
353
 
314
354
  Nothing breaks: `app.command`, `app.emit`, and the untyped `invoke` / `listen`
@@ -338,7 +378,7 @@ export const commands = defineCommands<{
338
378
  }>();
339
379
  export type App = { commands: typeof commands; events: typeof events };
340
380
 
341
- on(app, commands, "add", (args) => args.a + args.b); // args inferred, no cast
381
+ app.command("add", (args) => args.a + args.b); // args inferred, no cast
342
382
  ```
343
383
 
344
384
  then on the page, replace `invoke<number>("add", …)` with
package/bin/janela.mjs CHANGED
@@ -516,10 +516,18 @@ 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, JanelaApp } from "./janela";\n` +
519
520
  `import { WINDOW } from "./config";\n` +
520
521
  `import { INDEX_HTML } from "./frontend";\n` +
521
522
  `import { setup } from "./main";\n\n` +
522
- `const app = createApp(WINDOW);\n` +
523
+ `// The app's type parameters are read back off setup()'s own signature,\n` +
524
+ `// so a contract-typed setup(app: JanelaApp<App>) and a plain\n` +
525
+ `// setup(app: JanelaApp) each get an app instantiated to match. scriptc\n` +
526
+ `// monomorphises generic classes, so the right instantiation must be\n` +
527
+ `// CONSTRUCTED here - no cast can bridge JanelaApp<A> and JanelaApp<B>.\n` +
528
+ `type CmdsOf<F> = F extends (app: JanelaApp<infer C, infer _E>) => void ? C : CommandShapes;\n` +
529
+ `type EvtsOf<F> = F extends (app: JanelaApp<infer _C, infer E>) => void ? E : Record<string, unknown>;\n\n` +
530
+ `const app = createApp<CmdsOf<typeof setup>, EvtsOf<typeof setup>>(WINDOW);\n` +
523
531
  `setup(app);\n` +
524
532
  `const rc = app.run(INDEX_HTML) + 0;\n` +
525
533
  `console.log("[janela] run returned", rc);\n`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "janela",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Desktop apps in pure TypeScript, compiled to native. No Rust, no Node, no Electron.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,7 +13,7 @@
13
13
  "default": "./api/index.js"
14
14
  },
15
15
  "./host": {
16
- "types": "./runtime/types.ts"
16
+ "types": "./runtime/janela.ts"
17
17
  },
18
18
  "./global": {
19
19
  "types": "./api/global.d.ts"