@monimesl/dot-service 0.1.0-alpha.2 → 0.1.0-alpha.21

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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as Page, c as PageHost, d as PageOptions, f as State, h as definePage, i as Mutation, l as PageId, m as StateUpdate, n as ActionContext, o as PageContext, p as StateChanges, r as DeepReadonly, s as PageDefinition, t as Action, u as PageNavigation } from "./page-BfYJ-G-D.js";
2
- import { AppState, ColorScheme, DotEvent, DotEvent as DotEvent$1, DotHooks, DotHooks as DotHooks$1, EventEmitter, HostLayerMessages, LogicLayerMessages, MessageChannel, Navigation, PageHooks, PageLaunchOptions, PageLaunchOptions as PageLaunchOptions$1, PageState, PageState as PageState$1, SystemContext, SystemContext as SystemContext$1, Theme, Theme as Theme$1, Transport, Transport as Transport$1, UILayerMessages, createEvent, hostTransport } from "@monimesl/dot-core";
1
+ import { a as Page, c as PageHost, d as PageOptions, f as State, h as definePage, i as Mutation, l as PageId, m as StateUpdate, n as ActionContext, o as PageContext, p as StateChanges, r as DeepReadonly, s as PageDefinition, t as Action, u as PageNavigation } from "./page-CysQwLtO.js";
2
+ import { AppState, CheckedEvent, ColorScheme, DataEvent, DotEvent, DotEvent as DotEvent$1, DotHooks, DotHooks as DotHooks$1, EventEmitter, HostCaller, HostLayerMessages, IndexEvent, LogicLayerMessages, MessageChannel, Navigation, NavigationBarOptions, PageConfigs, PageConfigs as PageConfigs$1, PageHooks, PageLaunchOptions, PageLaunchOptions as PageLaunchOptions$1, PageState, PageState as PageState$1, SystemContext, SystemContext as SystemContext$1, TabBarOptions, Theme, Theme as Theme$1, Transport, Transport as Transport$1, UILayerMessages, ValueEvent, createEvent, hostTransport } from "@monimesl/dot-core";
3
3
  //#region src/service.d.ts
4
4
  type ServiceChannel = MessageChannel<LogicLayerMessages, UILayerMessages & HostLayerMessages>;
5
5
  interface ServiceHooks {
@@ -26,6 +26,7 @@ interface DotDefinition {
26
26
  readonly theme?: Theme$1;
27
27
  readonly plugins?: readonly ServicePlugin[];
28
28
  readonly pages: readonly [string, ...string[]];
29
+ readonly configs?: PageConfigs$1;
29
30
  readonly hooks?: Partial<DotHooks$1> & ThisType<DotContext>;
30
31
  }
31
32
  export declare function defineDot(dot: DotDefinition): DotDefinition;
@@ -46,15 +47,14 @@ export declare class Service implements DotContext {
46
47
  get systemContext(): SystemContext$1;
47
48
  setTheme(theme: Theme$1): void;
48
49
  getPage(instanceId: number): Page;
50
+ readonly callHost: HostCaller;
49
51
  loadPage(instanceId: number, id: string, options?: PageLaunchOptions$1): Page;
52
+ replacePages(definitions: Record<string, PageDefinition>): void;
50
53
  unloadPage(instanceId: number): void;
51
54
  reportError(work: Promise<void>, instanceId?: number): void;
52
55
  }
53
56
  //#endregion
54
57
  //#region src/plugins/logger.d.ts
55
- /**
56
- * Logs page lifecycle, events, state changes, and errors.
57
- */
58
58
  export declare function logger(options?: {
59
59
  log: (...args: unknown[]) => void;
60
60
  logError: (...args: unknown[]) => void;
@@ -62,6 +62,7 @@ export declare function logger(options?: {
62
62
  //#endregion
63
63
  //#region src/index.d.ts
64
64
  export declare function createService(options: ServiceOptions): Service;
65
+ export declare function runService(options: Omit<ServiceOptions, 'transport'>): Service;
65
66
  //#endregion
66
- export { type Action, type ActionContext, type AppState, type ColorScheme, type DeepReadonly, type DotContext, type DotDefinition, type DotEvent, type DotHooks, type Mutation, Page, type PageContext, type PageDefinition, type PageHooks, type PageHost, type PageId, type PageLaunchOptions, type PageNavigation, type PageOptions, type PageState, type ServiceChannel, type ServiceHooks, type ServiceOptions, type ServicePlugin, type State, type StateChanges, type StateUpdate, type SystemContext, type Theme, type Transport, createEvent, definePage, hostTransport };
67
+ export { type Action, type ActionContext, type AppState, type CheckedEvent, type ColorScheme, type DataEvent, type DeepReadonly, type DotContext, type DotDefinition, type DotEvent, type DotHooks, type IndexEvent, type Mutation, type NavigationBarOptions, Page, type PageConfigs, type PageContext, type PageDefinition, type PageHooks, type PageHost, type PageId, type PageLaunchOptions, type PageNavigation, type PageOptions, type PageState, type ServiceChannel, type ServiceHooks, type ServiceOptions, type ServicePlugin, type State, type StateChanges, type StateUpdate, type SystemContext, type TabBarOptions, type Theme, type Transport, type ValueEvent, createEvent, definePage, hostTransport };
67
68
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/service.ts","../src/plugins/logger.ts","../src/index.ts"],"mappings":";;;KA0BY,iBAAiB,eAC3B,oBACA,kBAAkB;UAGH;EACf;EAEA,SAAS,MAAM;EAEf,WAAW,MAAM;EAEjB,SAAS,MAAM;EAEf,SAAS,MAAM;EAGf,SAAS,MAAM,MAAM,YAAY;EAGjC,MAAM,MAAM,MAAM,iBAAiB,OAAO;EAG1C,SAAS,MAAM,MAAM,OAAO;EAG5B,MAAM,gBAAgB;;UAGP;WACN;EAET,MAAM,SAAS;;UAGA;WACN,OAAO;WACP,eAAe;EAExB,SAAS,OAAO;;UAGD;WACN,QAAQ;WACR,mBAAmB;WACnB;WACA,QAAQ,QAAQ,cAAY,SAAS;;wBAGhC,UAAU,KAAK,gBAAgB;UAI9B;EACf,WAAW;EACX,MAAM,QAAQ;EACd,OAAO,eAAe;EACtB;;qBAIW,mBAAmB;;WACrB,SAAS;WACT,OAAK,aAAA;EAUF,cAAE,WAAW,OAAO,KAAU,UAAkB;EAsC5D,IAAI,QAAQ;EAKZ;MAcI,SAAS;MAIT,iBAAiB;EAIrB,SAAS,OAAO;EAKhB,QAAQ,qBAAqB;EAQ7B,SAAS,oBAAoB,YAAY,UAAS,sBAAyB;EA4B3E,WAAW;EAmBX,YAAY,MAAM,eAAe;;;;;;;wBC3NnB,OACd;EACE,SAAS;EACT,cAAc;IAKf;;;wBC8Ba,cAAc,SAAS,iBAAiB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/service.ts","../src/plugins/logger.ts","../src/index.ts"],"mappings":";;;KA6BY,iBAAiB,eAC3B,oBACA,kBAAkB;UAGH;EACf;EAEA,SAAS,MAAM;EAEf,WAAW,MAAM;EAEjB,SAAS,MAAM;EAEf,SAAS,MAAM;EAGf,SAAS,MAAM,MAAM,YAAY;EAGjC,MAAM,MAAM,MAAM,iBAAiB,OAAO;EAG1C,SAAS,MAAM,MAAM,OAAO;EAG5B,MAAM,gBAAgB;;UAGP;WACN;EAET,MAAM,SAAS;;UAGA;WACN,OAAO;WACP,eAAe;EAExB,SAAS,OAAO;;UAGD;WACN,QAAQ;WACR,mBAAmB;WACnB;WAEA,UAAU;WACV,QAAQ,QAAQ,cAAY,SAAS;;wBAGhC,UAAU,KAAK,gBAAgB;UAI9B;EACf,WAAW;EACX,MAAM,QAAQ;EACd,OAAO,eAAe;EACtB;;qBAKW,mBAAmB;;WACrB,SAAS;WACT,OAAK,aAAA;EAcF,cAAE,WAAW,OAAO,KAAU,UAAkB;EAgD5D,IAAI,QAAQ;EAKZ;MAaI,SAAS;MAIT,iBAAiB;EAIrB,SAAS,OAAO;EAKhB,QAAQ,qBAAqB;WASpB,UAAU;EAGnB,SAAS,oBAAoB,YAAY,UAAS,sBAAyB;EAe3E,aAAa,aAAa,eAAe;EAwGzC,WAAW;EAoBX,YAAY,MAAM,eAAe;;;;wBChVnB,OACd;EACE,SAAS;EACT,cAAc;IAKf;;;wBCyCa,cAAc,SAAS,iBAAiB;wBAUxC,WAAW,SAAS,KAAK,+BAA+B"}
package/dist/index.js CHANGED
@@ -1,9 +1,6 @@
1
- import { i as definePage, n as defineDot, r as Page, t as Service } from "./service-dAFmZ_Mh.js";
2
- import { createEvent, hostTransport } from "@monimesl/dot-core";
1
+ import { i as definePage, n as defineDot, r as Page, t as Service } from "./service-Dd4nvlhf.js";
2
+ import { connectHost, createEvent, hostTransport, hostTransport as hostTransport$1 } from "@monimesl/dot-core";
3
3
  //#region src/plugins/logger.ts
4
- /**
5
- * Logs page lifecycle, events, state changes, and errors.
6
- */
7
4
  function logger(options = {
8
5
  log: console.debug,
9
6
  logError: console.error
@@ -28,7 +25,23 @@ function createService(options) {
28
25
  service.start();
29
26
  return service;
30
27
  }
28
+ const RUNNING_SERVICE = Symbol.for("dot.service");
29
+ function runService(options) {
30
+ const scope = globalThis;
31
+ const running = scope[RUNNING_SERVICE];
32
+ if (running) {
33
+ connectHost(running.callHost);
34
+ running.replacePages(options.pages);
35
+ return running;
36
+ }
37
+ const service = createService({
38
+ ...options,
39
+ transport: hostTransport$1()
40
+ });
41
+ scope[RUNNING_SERVICE] = service;
42
+ return service;
43
+ }
31
44
  //#endregion
32
- export { Page, Service, createEvent, createService, defineDot, definePage, hostTransport, logger };
45
+ export { Page, Service, createEvent, createService, defineDot, definePage, hostTransport, logger, runService };
33
46
 
34
47
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/plugins/logger.ts","../src/index.ts"],"sourcesContent":["import type { ServicePlugin } from '../service';\n\n/**\n * Logs page lifecycle, events, state changes, and errors.\n */\nexport function logger(\n options: {\n log: (...args: unknown[]) => void;\n logError: (...args: unknown[]) => void;\n } = {\n log: console.debug,\n logError: console.error,\n },\n): ServicePlugin {\n const { log, logError } = options;\n return {\n name: 'logger',\n setup({ hooks }) {\n hooks.on('pageLoad', (page) => log(`[dot] load ${page.id}#${page.instanceId}`, page.state));\n hooks.on('pageUnload', (page) => log(`[dot] unload ${page.id}#${page.instanceId}`));\n hooks.on('event', (page, handler, event) => log(`[dot] ${page.id} ${handler}`, event));\n hooks.on('navigate', (page, navigation) => log(`[dot] ${page.id} navigate`, navigation));\n hooks.on('setState', (page, patch) => log(`[dot] ${page.id} setState`, patch));\n hooks.on('error', (error) => logError('[dot] error', error));\n },\n };\n}\n","import type { ServiceOptions } from './service';\nimport { Service } from './service';\n\nexport { Page, definePage } from './page';\nexport type {\n Action,\n ActionContext,\n DeepReadonly,\n Mutation,\n PageContext,\n PageDefinition,\n PageHost,\n PageId,\n PageNavigation,\n PageOptions,\n State,\n StateChanges,\n StateUpdate,\n} from './page';\nexport { Service, defineDot } from './service';\nexport type {\n DotContext,\n DotDefinition,\n ServiceHooks,\n ServiceOptions,\n ServicePlugin,\n ServiceChannel,\n} from './service';\nexport { logger } from './plugins/logger';\nexport { createEvent, hostTransport } from '@monimesl/dot-core';\nexport type {\n ColorScheme,\n DotEvent,\n DotHooks,\n PageState,\n PageHooks,\n PageLaunchOptions,\n AppState,\n SystemContext,\n Theme,\n Transport,\n} from '@monimesl/dot-core';\n\nexport function createService(options: ServiceOptions): Service {\n const service = new Service(options);\n service.start();\n return service;\n}\n"],"mappings":";;;;;;AAKA,SAAgB,OACd,UAGI;CACF,KAAK,QAAQ;CACb,UAAU,QAAQ;AACpB,GACe;CACf,MAAM,EAAE,KAAK,aAAa;CAC1B,OAAO;EACL,MAAM;EACN,MAAM,EAAE,SAAS;GACf,MAAM,GAAG,aAAa,SAAS,IAAI,cAAc,KAAK,GAAG,GAAG,KAAK,cAAc,KAAK,KAAK,CAAC;GAC1F,MAAM,GAAG,eAAe,SAAS,IAAI,gBAAgB,KAAK,GAAG,GAAG,KAAK,YAAY,CAAC;GAClF,MAAM,GAAG,UAAU,MAAM,SAAS,UAAU,IAAI,SAAS,KAAK,GAAG,GAAG,WAAW,KAAK,CAAC;GACrF,MAAM,GAAG,aAAa,MAAM,eAAe,IAAI,SAAS,KAAK,GAAG,YAAY,UAAU,CAAC;GACvF,MAAM,GAAG,aAAa,MAAM,UAAU,IAAI,SAAS,KAAK,GAAG,YAAY,KAAK,CAAC;GAC7E,MAAM,GAAG,UAAU,UAAU,SAAS,eAAe,KAAK,CAAC;EAC7D;CACF;AACF;;;ACiBA,SAAgB,cAAc,SAAkC;CAC9D,MAAM,UAAU,IAAI,QAAQ,OAAO;CACnC,QAAQ,MAAM;CACd,OAAO;AACT"}
1
+ {"version":3,"file":"index.js","names":["hostTransport"],"sources":["../src/plugins/logger.ts","../src/index.ts"],"sourcesContent":["import type { ServicePlugin } from '../service';\n\n// Logs page lifecycle, events, state changes, and errors.\nexport function logger(\n options: {\n log: (...args: unknown[]) => void;\n logError: (...args: unknown[]) => void;\n } = {\n log: console.debug,\n logError: console.error,\n },\n): ServicePlugin {\n const { log, logError } = options;\n return {\n name: 'logger',\n setup({ hooks }) {\n hooks.on('pageLoad', (page) => log(`[dot] load ${page.id}#${page.instanceId}`, page.state));\n hooks.on('pageUnload', (page) => log(`[dot] unload ${page.id}#${page.instanceId}`));\n hooks.on('event', (page, handler, event) => log(`[dot] ${page.id} ${handler}`, event));\n hooks.on('navigate', (page, navigation) => log(`[dot] ${page.id} navigate`, navigation));\n hooks.on('setState', (page, patch) => log(`[dot] ${page.id} setState`, patch));\n hooks.on('error', (error) => logError('[dot] error', error));\n },\n };\n}\n","import { connectHost, hostTransport } from '@monimesl/dot-core';\n\nimport type { ServiceOptions } from './service';\nimport { Service } from './service';\n\nexport { Page, definePage } from './page';\nexport type {\n Action,\n ActionContext,\n DeepReadonly,\n Mutation,\n PageContext,\n PageDefinition,\n PageHost,\n PageId,\n PageNavigation,\n PageOptions,\n State,\n StateChanges,\n StateUpdate,\n} from './page';\nexport { Service, defineDot } from './service';\nexport type {\n DotContext,\n DotDefinition,\n ServiceHooks,\n ServiceOptions,\n ServicePlugin,\n ServiceChannel,\n} from './service';\nexport { logger } from './plugins/logger';\nexport { createEvent, hostTransport } from '@monimesl/dot-core';\nexport type {\n CheckedEvent,\n ColorScheme,\n DataEvent,\n DotEvent,\n IndexEvent,\n ValueEvent,\n DotHooks,\n NavigationBarOptions,\n PageConfigs,\n PageState,\n PageHooks,\n PageLaunchOptions,\n AppState,\n SystemContext,\n TabBarOptions,\n Theme,\n Transport,\n} from '@monimesl/dot-core';\n\nexport function createService(options: ServiceOptions): Service {\n const service = new Service(options);\n service.start();\n return service;\n}\n\n// Lets the dot's next build, loaded into the same engine, find the running service.\nconst RUNNING_SERVICE = Symbol.for('dot.service');\n\n// Starts the service. On a hot reload, hands the new build's pages to the one already running.\nexport function runService(options: Omit<ServiceOptions, 'transport'>): Service {\n const scope = globalThis as unknown as Record<symbol, Service | undefined>;\n const running = scope[RUNNING_SERVICE];\n if (running) {\n // The new build has its own copy of the host connection, and its `dash` calls go through it.\n connectHost(running.callHost);\n running.replacePages(options.pages);\n return running;\n }\n const service = createService({ ...options, transport: hostTransport() });\n scope[RUNNING_SERVICE] = service;\n return service;\n}\n"],"mappings":";;;AAGA,SAAgB,OACd,UAGI;CACF,KAAK,QAAQ;CACb,UAAU,QAAQ;AACpB,GACe;CACf,MAAM,EAAE,KAAK,aAAa;CAC1B,OAAO;EACL,MAAM;EACN,MAAM,EAAE,SAAS;GACf,MAAM,GAAG,aAAa,SAAS,IAAI,cAAc,KAAK,GAAG,GAAG,KAAK,cAAc,KAAK,KAAK,CAAC;GAC1F,MAAM,GAAG,eAAe,SAAS,IAAI,gBAAgB,KAAK,GAAG,GAAG,KAAK,YAAY,CAAC;GAClF,MAAM,GAAG,UAAU,MAAM,SAAS,UAAU,IAAI,SAAS,KAAK,GAAG,GAAG,WAAW,KAAK,CAAC;GACrF,MAAM,GAAG,aAAa,MAAM,eAAe,IAAI,SAAS,KAAK,GAAG,YAAY,UAAU,CAAC;GACvF,MAAM,GAAG,aAAa,MAAM,UAAU,IAAI,SAAS,KAAK,GAAG,YAAY,KAAK,CAAC;GAC7E,MAAM,GAAG,UAAU,UAAU,SAAS,eAAe,KAAK,CAAC;EAC7D;CACF;AACF;;;AC4BA,SAAgB,cAAc,SAAkC;CAC9D,MAAM,UAAU,IAAI,QAAQ,OAAO;CACnC,QAAQ,MAAM;CACd,OAAO;AACT;AAGA,MAAM,kBAAkB,OAAO,IAAI,aAAa;AAGhD,SAAgB,WAAW,SAAqD;CAC9E,MAAM,QAAQ;CACd,MAAM,UAAU,MAAM;CACtB,IAAI,SAAS;EAEX,YAAY,QAAQ,QAAQ;EAC5B,QAAQ,aAAa,QAAQ,KAAK;EAClC,OAAO;CACT;CACA,MAAM,UAAU,cAAc;EAAE,GAAG;EAAS,WAAWA,gBAAc;CAAE,CAAC;CACxE,MAAM,mBAAmB;CACzB,OAAO;AACT"}
@@ -1,14 +1,22 @@
1
- import { DotEvent, Navigation, PageHooks, PageLaunchOptions, PageState } from "@monimesl/dot-core";
1
+ import { DotEvent, Navigation, NavigationBarOptions, PageBars, PageConfigs, PageHooks, PageLaunchOptions, PageState, TabBarOptions } from "@monimesl/dot-core";
2
+ import { Dash } from "@monimesl/dot-dash";
2
3
  //#region src/page.d.ts
3
4
  declare global {
4
5
  interface DotPageIds {}
5
6
  }
6
7
  type PageId = [keyof DotPageIds] extends [never] ? string : Extract<keyof DotPageIds, string>;
8
+ interface PushNavigation {
9
+ readonly action: 'push';
10
+ readonly page: string;
11
+ readonly options: PageLaunchOptions;
12
+ }
7
13
  interface PageNavigation {
8
14
  push(page: PageId, options?: PageLaunchOptions): void;
9
15
  replace(page: PageId, options?: PageLaunchOptions): void;
10
16
  reset(page: PageId, options?: PageLaunchOptions): void;
11
17
  back(delta?: number): void;
18
+ pushForResult<Result = unknown>(page: PageId, options?: PageLaunchOptions): Promise<Result | undefined>;
19
+ backWithResult(result: unknown): void;
12
20
  }
13
21
  type DeepReadonly<T> = T extends ((...args: never[]) => unknown) ? T : T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]>; } : T;
14
22
  type State<S> = DeepReadonly<S>;
@@ -25,7 +33,10 @@ interface PageContext<S extends PageState, Mu, Ac> {
25
33
  readonly id: string;
26
34
  readonly instanceId: number;
27
35
  readonly state: State<S>;
36
+ readonly dash: Dash;
28
37
  setState(update: StateUpdate<S>): void;
38
+ setNavigationBar(options: NavigationBarOptions): void;
39
+ setTabBar(options: TabBarOptions): void;
29
40
  readonly mutations: { readonly [K in keyof Mu]: (...event: EventArgs<Mu[K]>) => void; };
30
41
  readonly actions: { readonly [K in keyof Ac]: (...event: EventArgs<Ac[K]>) => Promise<void>; };
31
42
  }
@@ -35,6 +46,7 @@ interface ActionContext<S extends PageState, Mu, Ac> extends PageContext<S, Mu,
35
46
  interface PageOptions<S extends PageState, Mu, Ac> {
36
47
  id?: string;
37
48
  initialState: S;
49
+ configs?: PageConfigs;
38
50
  hooks?: Partial<PageHooks> & ThisType<PageContext<S, Mu, Ac>>;
39
51
  mutations?: Mu & ThisType<void>;
40
52
  actions?: Ac & ThisType<ActionContext<S, Mu, Ac>>;
@@ -42,6 +54,7 @@ interface PageOptions<S extends PageState, Mu, Ac> {
42
54
  interface PageDefinition<S extends PageState = PageState> {
43
55
  readonly id?: string;
44
56
  readonly initialState: S;
57
+ readonly configs?: PageConfigs;
45
58
  readonly hooks?: Partial<PageHooks>;
46
59
  readonly mutations?: Readonly<Record<string, Mutation<S>>>;
47
60
  readonly actions?: Readonly<Record<string, Action<S>>>;
@@ -49,9 +62,17 @@ interface PageDefinition<S extends PageState = PageState> {
49
62
  declare function definePage<S extends PageState, Mu extends Record<string, Mutation<S>>, Ac extends Record<string, Action<S>>>(options: PageOptions<S, Mu, Ac>): PageDefinition<S>;
50
63
  interface PageHost {
51
64
  commit(page: Page, patch: PageState): void;
65
+ showBars(page: Page): void;
66
+ readonly dotConfigs?: PageConfigs | undefined;
52
67
  navigate(page: Page, navigation: Navigation): void;
68
+ pushForResult(page: Page, navigation: PushNavigation): Promise<unknown>;
69
+ backWithResult(page: Page, result: unknown): void;
53
70
  readonly strict?: boolean;
54
71
  }
72
+ interface RestoredPage<S> {
73
+ readonly state: State<S>;
74
+ readonly barChanges: PageBars;
75
+ }
55
76
  type Mutations<S> = Record<string, Mutation<S>>;
56
77
  type Actions<S> = Record<string, Action<S>>;
57
78
  declare class Page<S extends PageState = PageState> {
@@ -60,9 +81,14 @@ declare class Page<S extends PageState = PageState> {
60
81
  readonly instanceId: number;
61
82
  readonly context: PageContext<S, Mutations<S>, Actions<S>>;
62
83
  readonly actionContext: ActionContext<S, Mutations<S>, Actions<S>>;
63
- constructor(instanceId: number, id: string, definition: PageDefinition<S>, host: PageHost);
84
+ readonly dash: Dash;
85
+ constructor(instanceId: number, id: string, definition: PageDefinition<S>, host: PageHost, restored?: RestoredPage<S>);
64
86
  get state(): State<S>;
65
- handlers(): string[];
87
+ get refreshable(): boolean;
88
+ get bars(): PageBars;
89
+ get barChanges(): PageBars;
90
+ setNavigationBar(options: NavigationBarOptions): void;
91
+ setTabBar(options: TabBarOptions): void;
66
92
  setState(update: StateUpdate<S>): void;
67
93
  mutate(name: string, event?: DotEvent): void;
68
94
  act(name: string, event?: DotEvent): Promise<void>;
@@ -72,4 +98,4 @@ declare class Page<S extends PageState = PageState> {
72
98
  }
73
99
  //#endregion
74
100
  export { Page as a, PageHost as c, PageOptions as d, State as f, definePage as h, Mutation as i, PageId as l, StateUpdate as m, ActionContext as n, PageContext as o, StateChanges as p, DeepReadonly as r, PageDefinition as s, Action as t, PageNavigation as u };
75
- //# sourceMappingURL=page-BfYJ-G-D.d.ts.map
101
+ //# sourceMappingURL=page-CysQwLtO.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-CysQwLtO.d.ts","names":[],"sources":["../src/page.ts"],"mappings":";;;QAqBQ;YAEI;;KAKA,gBAAgB,uCAExB,cAAc;UAGD;WACN;WACA;WACA,SAAS;;UAGH;EACf,KAAK,MAAM,QAAQ,UAAU;EAE7B,QAAQ,MAAM,QAAQ,UAAU;EAEhC,MAAM,MAAM,QAAQ,UAAU;EAE9B,KAAK;EAIL,cAAc,kBACZ,MAAM,QACN,UAAU,oBACT,QAAQ;EAGX,eAAe;;KAGL,aAAa,KAAK,eAAc,6BACxC,IACA,+BACc,WAAW,IAAI,aAAa,EAAE,SAC1C;KAEM,MAAM,KAAK,aAAa;KAExB,aAAa,KAAK,QAAQ,MAAM;KAEhC,YAAY,KAAK,aAAa,OAAO,OAAO,MAAM,OAAO,aAAa;KAItE,SAAS;EACnB,SAAS,OAAO,MAAM,IAAI,OAAO,WAAW,aAAa;;KAG/C,OAAO;EACjB,OAAO,OAAO,MAAM,IAAI,OAAO,kBAAkB;;KAI9C,UAAU,KAAK,YAAW,iBAAiB,YAAY,iBAAgB;UAE3D,YAAY,UAAU,WAAW,IAAI;WAC3C;WACA;WACA,OAAO,MAAM;WAEb,MAAM;EAEf,SAAS,QAAQ,YAAY;EAG7B,iBAAiB,SAAS;EAG1B,UAAU,SAAS;WAEV,uBAAuB,WAAW,SAAS,OAAO,UAAU,GAAG;WAC/D,qBAAqB,WAAW,SAAS,OAAO,UAAU,GAAG,QAAQ;;UAG/D,cAAc,UAAU,WAAW,IAAI,YAAY,YAAY,GAAG,IAAI;WAC5E,YAAY;;UAGN,YAAY,UAAU,WAAW,IAAI;EACpD;EACA,cAAc;EAGd,UAAU;EACV,QAAQ,QAAQ,aAAa,SAAS,YAAY,GAAG,IAAI;EACzD,YAAY,KAAK;EACjB,UAAU,KAAK,SAAS,cAAc,GAAG,IAAI;;UAG9B,eAAe,UAAU,YAAY;WAC3C;WACA,cAAc;WACd,UAAU;WACV,QAAQ,QAAQ;WAChB,YAAY,SAAS,eAAe,SAAS;WAC7C,UAAU,SAAS,eAAe,OAAO;;iBAGpC,WACd,UAAU,WACV,WAAW,eAAe,SAAS,KACnC,WAAW,eAAe,OAAO,KACjC,SAAS,YAAY,GAAG,IAAI,MAAM,eAAe;UAIlC;EACf,OAAO,MAAM,MAAM,OAAO;EAG1B,SAAS,MAAM;WAGN,aAAa;EAEtB,SAAS,MAAM,MAAM,YAAY;EAGjC,cAAc,MAAM,MAAM,YAAY,iBAAiB;EAEvD,eAAe,MAAM,MAAM;WAElB;;UAIM,aAAa;WACnB,OAAO,MAAM;WACb,YAAY;;KAGlB,UAAU,KAAK,eAAe,SAAS;KACvC,QAAQ,KAAK,eAAe,OAAO;cAI3B,KAAK,UAAU,YAAY;;WAC7B;WACA;WACA,SAAS,YAAY,GAAG,UAAU,IAAI,QAAQ;WAC9C,eAAe,cAAc,GAAG,UAAU,IAAI,QAAQ;WACtD,MAAM;EAWb,YAAA,oBACA,YACA,YAAY,eAAe,IAC3B,MAAM,UACN,WAAW,aAAa;MAsCtB,SAAS,MAAM;MAKf;MAIA,QAAQ;MAIR,cAAc;EAIlB,iBAAiB,SAAS;EAM1B,UAAU,SAAS;EAcnB,SAAS,QAAQ,YAAY;EAqB7B,OAAO,cAAc,QAAQ;EAQvB,IAAI,cAAc,QAAQ,WAAW;EAS3C,SAAS,cAAc,OAAO,WAAW;EAanC,UAAU,gBAAgB,WAC9B,UAAU,MACP,MAAM,WAAW,UAAU,MAC7B;EASH"}
@@ -1,4 +1,10 @@
1
- import { DEFAULT_SYSTEM_CONTEXT, EventEmitter, HostError, MAX_STACK_DEPTH, MessageChannel, applyChanges, connectHost, createHostRequest } from "@monimesl/dot-core";
1
+ import { DEFAULT_SYSTEM_CONTEXT, EventEmitter, HostError, MAX_STACK_DEPTH, MessageChannel, applyChanges, checkConfigs, checkNavigationBar, checkTabBar, connectHost, createHostRequest, mergeConfigs } from "@monimesl/dot-core";
2
+ import { dash, tracked } from "@monimesl/dot-dash";
3
+ //#region src/utils.ts
4
+ function cancelledError(method) {
5
+ return new HostError("cancelled", `"${method}" was cancelled`);
6
+ }
7
+ //#endregion
2
8
  //#region src/host-calls.ts
3
9
  var HostCalls = class {
4
10
  #channel;
@@ -31,7 +37,7 @@ var HostCalls = class {
31
37
  if (!call) return;
32
38
  this.#waiting.delete(id);
33
39
  this.#channel.send("hostCancel", { id });
34
- call.reject(new HostError("cancelled", `"${call.method}" was cancelled`));
40
+ call.reject(cancelledError(call.method));
35
41
  }
36
42
  #settle(result) {
37
43
  const call = this.#waiting.get(result.id);
@@ -51,20 +57,34 @@ var Page = class {
51
57
  instanceId;
52
58
  context;
53
59
  actionContext;
60
+ dash = tracked(dash, (request) => this.#track(request));
54
61
  #definition;
55
62
  #host;
56
63
  #state;
57
64
  #closed = false;
65
+ #dashRequests = /* @__PURE__ */ new Set();
58
66
  #previousHook = Promise.resolve();
59
- constructor(instanceId, id, definition, host) {
67
+ #barChanges;
68
+ constructor(instanceId, id, definition, host, restored) {
60
69
  this.id = id;
61
70
  this.instanceId = instanceId;
62
71
  this.#definition = definition;
63
72
  this.#host = host;
64
- this.#state = this.#freeze(JSON.parse(JSON.stringify(definition.initialState)));
73
+ if (restored) {
74
+ this.#state = this.#freeze(restored.state);
75
+ this.#barChanges = restored.barChanges;
76
+ } else {
77
+ const copy = JSON.parse(JSON.stringify(definition.initialState));
78
+ this.#state = this.#freeze(copy);
79
+ this.#barChanges = {
80
+ navigationBar: {},
81
+ tabBar: {}
82
+ };
83
+ }
84
+ checkConfigs(definition.configs ?? {});
65
85
  const { mutations = {}, actions = {} } = definition;
66
86
  const clash = Object.keys(mutations).find((name) => name in actions);
67
- if (clash) throw new Error(`Page "${id}": "${clash}" is both a mutation and an action`);
87
+ if (clash) throw new Error(`Page "${id}" uses "${clash}" as both a mutation and an action. Rename one of them`);
68
88
  const mutationCalls = mapNames(mutations, (name) => (event) => this.mutate(name, event));
69
89
  const actionCalls = mapNames(actions, (name) => (event) => this.act(name, event));
70
90
  this.context = new HookContext(this, mutationCalls, actionCalls);
@@ -73,9 +93,41 @@ var Page = class {
73
93
  get state() {
74
94
  return this.#state;
75
95
  }
76
- handlers() {
77
- const { mutations = {}, actions = {} } = this.#definition;
78
- return [...Object.keys(mutations), ...Object.keys(actions)];
96
+ get refreshable() {
97
+ return this.#definition.hooks?.onRefresh !== void 0;
98
+ }
99
+ get bars() {
100
+ return mergeConfigs(this.#host.dotConfigs, this.#definition.configs, this.#barChanges);
101
+ }
102
+ get barChanges() {
103
+ return this.#barChanges;
104
+ }
105
+ setNavigationBar(options) {
106
+ checkNavigationBar(options);
107
+ const navigationBar = {
108
+ ...this.#barChanges.navigationBar,
109
+ ...options
110
+ };
111
+ this.#changeBars({
112
+ ...this.#barChanges,
113
+ navigationBar
114
+ });
115
+ }
116
+ setTabBar(options) {
117
+ checkTabBar(options);
118
+ const tabBar = {
119
+ ...this.#barChanges.tabBar,
120
+ ...options
121
+ };
122
+ this.#changeBars({
123
+ ...this.#barChanges,
124
+ tabBar
125
+ });
126
+ }
127
+ #changeBars(barChanges) {
128
+ if (this.#closed) return;
129
+ this.#barChanges = barChanges;
130
+ this.#host.showBars(this);
79
131
  }
80
132
  setState(update) {
81
133
  if (this.#closed) return;
@@ -113,6 +165,14 @@ var Page = class {
113
165
  }
114
166
  close() {
115
167
  this.#closed = true;
168
+ for (const request of this.#dashRequests) request.cancel();
169
+ this.#dashRequests.clear();
170
+ }
171
+ #track(request) {
172
+ if (this.#closed) return;
173
+ this.#dashRequests.add(request);
174
+ const forget = () => this.#dashRequests.delete(request);
175
+ request.then(forget, forget);
116
176
  }
117
177
  #runMutation(mutation, event) {
118
178
  this.setState((state) => mutation(state, event));
@@ -140,7 +200,18 @@ var Page = class {
140
200
  back: (delta = 1) => this.#navigate({
141
201
  action: "back",
142
202
  delta
143
- })
203
+ }),
204
+ pushForResult: (page, options = {}) => {
205
+ if (this.#closed) return Promise.resolve(void 0);
206
+ return this.#host.pushForResult(this, {
207
+ action: "push",
208
+ page,
209
+ options
210
+ });
211
+ },
212
+ backWithResult: (result) => {
213
+ if (!this.#closed) this.#host.backWithResult(this, result);
214
+ }
144
215
  };
145
216
  }
146
217
  #navigate(navigation) {
@@ -155,10 +226,12 @@ var HookContext = class {
155
226
  instanceId;
156
227
  mutations;
157
228
  actions;
229
+ dash;
158
230
  #page;
159
231
  constructor(page, mutations, actions) {
160
232
  this.id = page.id;
161
233
  this.instanceId = page.instanceId;
234
+ this.dash = page.dash;
162
235
  this.mutations = mutations;
163
236
  this.actions = actions;
164
237
  this.#page = page;
@@ -169,6 +242,12 @@ var HookContext = class {
169
242
  setState = (update) => {
170
243
  this.#page.setState(update);
171
244
  };
245
+ setNavigationBar = (options) => {
246
+ this.#page.setNavigationBar(options);
247
+ };
248
+ setTabBar = (options) => {
249
+ this.#page.setTabBar(options);
250
+ };
172
251
  };
173
252
  var HandlerContext = class extends HookContext {
174
253
  navigation;
@@ -183,7 +262,7 @@ function mapNames(record, create) {
183
262
  function deepFreeze(value) {
184
263
  if (typeof value === "object" && value !== null && !Object.isFrozen(value)) {
185
264
  Object.freeze(value);
186
- Object.values(value).forEach(deepFreeze);
265
+ for (const child of Object.values(value)) deepFreeze(child);
187
266
  }
188
267
  return value;
189
268
  }
@@ -205,6 +284,8 @@ var Service = class {
205
284
  pages: [],
206
285
  limit: MAX_STACK_DEPTH
207
286
  };
287
+ #awaitingResults = /* @__PURE__ */ new Map();
288
+ #results = /* @__PURE__ */ new Map();
208
289
  #hostCalls;
209
290
  constructor({ transport, pages, dot = {}, strict = false }) {
210
291
  this.channel = new MessageChannel(transport);
@@ -213,6 +294,7 @@ var Service = class {
213
294
  this.#dot = dot;
214
295
  this.#strict = strict;
215
296
  this.#theme = dot.theme ?? {};
297
+ checkConfigs(dot.configs ?? {});
216
298
  this.channel.handle("connect", () => this.channel.send("setTheme", { theme: this.#theme }));
217
299
  this.channel.handle("syncStack", (stack) => {
218
300
  this.#stack = stack;
@@ -224,6 +306,7 @@ var Service = class {
224
306
  this.channel.handle("unload", ({ instanceId }) => this.unloadPage(instanceId));
225
307
  this.channel.handle("show", ({ instanceId }) => {
226
308
  const page = this.getPage(instanceId);
309
+ this.#settleResult(instanceId);
227
310
  this.hooks.emit("pageShow", page);
228
311
  this.reportError(page.lifecycle("onShow"), instanceId);
229
312
  });
@@ -232,6 +315,12 @@ var Service = class {
232
315
  this.hooks.emit("pageHide", page);
233
316
  this.reportError(page.lifecycle("onHide"), instanceId);
234
317
  });
318
+ this.channel.handle("refresh", ({ instanceId }) => {
319
+ const refresh = this.getPage(instanceId).lifecycle("onRefresh").finally(() => {
320
+ this.channel.send("refreshed", { instanceId });
321
+ });
322
+ this.reportError(refresh, instanceId);
323
+ });
235
324
  this.channel.handle("event", ({ instanceId, handler, event }) => {
236
325
  const page = this.getPage(instanceId);
237
326
  this.hooks.emit("event", page, handler, event);
@@ -247,7 +336,7 @@ var Service = class {
247
336
  const stopListening = this.channel.listen((error, message) => {
248
337
  this.#reportError(error, instanceIdOf(message));
249
338
  });
250
- connectHost((method, params, options) => this.#hostCalls.call(method, params, options));
339
+ connectHost(this.callHost);
251
340
  this.hooks.emit("launch");
252
341
  this.reportError(this.#runLaunchHook());
253
342
  return () => {
@@ -270,7 +359,44 @@ var Service = class {
270
359
  if (!page) throw new Error(`Page instance ${instanceId} is not loaded`);
271
360
  return page;
272
361
  }
362
+ callHost = (method, params, options) => this.#hostCalls.call(method, params, options);
273
363
  loadPage(instanceId, id, options = {}) {
364
+ const page = this.#createPage(instanceId, id);
365
+ this.channel.send("initPageState", {
366
+ instanceId,
367
+ state: page.state,
368
+ refreshable: page.refreshable
369
+ });
370
+ this.#sendBars(page);
371
+ this.hooks.emit("pageLoad", page);
372
+ this.reportError(page.lifecycle("onLoad", options), instanceId);
373
+ return page;
374
+ }
375
+ replacePages(definitions) {
376
+ const previous = this.#definitions;
377
+ this.#definitions = definitions;
378
+ for (const [instanceId, page] of this.#pages) {
379
+ const definition = definitions[page.id];
380
+ if (!definition) continue;
381
+ page.close();
382
+ if (JSON.stringify(previous[page.id]?.initialState) !== JSON.stringify(definition.initialState)) {
383
+ const options = this.#stack.pages.find((entry) => entry.instanceId === instanceId)?.options;
384
+ this.loadPage(instanceId, page.id, options);
385
+ } else {
386
+ const kept = this.#createPage(instanceId, page.id, {
387
+ state: page.state,
388
+ barChanges: page.barChanges
389
+ });
390
+ this.#sendBars(kept);
391
+ if (kept.refreshable !== page.refreshable) this.channel.send("initPageState", {
392
+ instanceId,
393
+ state: kept.state,
394
+ refreshable: kept.refreshable
395
+ });
396
+ }
397
+ }
398
+ }
399
+ #createPage(instanceId, id, restored) {
274
400
  const page = new Page(instanceId, id, this.#definition(id), {
275
401
  commit: (page, patch) => {
276
402
  this.hooks.emit("setState", page, patch);
@@ -279,32 +405,64 @@ var Service = class {
279
405
  patchState: patch
280
406
  });
281
407
  },
282
- navigate: (page, navigation) => {
283
- if (navigation.action !== "back") this.#definition(navigation.page);
284
- const { pages, limit } = this.#stack;
285
- if (navigation.action === "push" && pages.length >= limit) throw new Error(`Cannot open "${navigation.page}": ${limit} pages are already open. Use replace or back.`);
286
- this.hooks.emit("navigate", page, navigation);
287
- this.channel.send("navigate", {
288
- ...navigation,
289
- from: page.instanceId
290
- });
291
- },
408
+ showBars: (page) => this.#sendBars(page),
409
+ dotConfigs: this.#dot.configs,
410
+ navigate: (page, navigation) => this.#navigate(page, navigation),
411
+ pushForResult: (page, navigation) => this.#pushForResult(page, navigation),
412
+ backWithResult: (page, result) => this.#backWithResult(page, result),
292
413
  strict: this.#strict
293
- });
414
+ }, restored);
294
415
  this.#pages.set(instanceId, page);
295
- this.channel.send("initPageState", {
296
- instanceId,
297
- state: page.state
298
- });
299
- this.hooks.emit("pageLoad", page);
300
- this.reportError(page.lifecycle("onLoad", options), instanceId);
301
416
  return page;
302
417
  }
418
+ #navigate(page, navigation) {
419
+ if (navigation.action !== "back") this.#definition(navigation.page);
420
+ const { pages, limit } = this.#stack;
421
+ if (navigation.action === "push" && pages.length >= limit) throw new Error(`Can't open "${navigation.page}": ${limit} pages are already open. Use replace or back.`);
422
+ this.hooks.emit("navigate", page, navigation);
423
+ this.channel.send("navigate", {
424
+ ...navigation,
425
+ from: page.instanceId
426
+ });
427
+ }
428
+ #pushForResult(page, navigation) {
429
+ this.#settleResult(page.instanceId);
430
+ return new Promise((resolve) => {
431
+ this.#navigate(page, navigation);
432
+ this.#awaitingResults.set(page.instanceId, resolve);
433
+ });
434
+ }
435
+ #backWithResult(page, result) {
436
+ const { pages } = this.#stack;
437
+ const index = pages.findIndex((entry) => entry.instanceId === page.instanceId);
438
+ if (index > 0) {
439
+ const below = pages[index - 1];
440
+ this.#results.set(below.instanceId, result);
441
+ }
442
+ this.#navigate(page, {
443
+ action: "back",
444
+ delta: 1
445
+ });
446
+ }
447
+ #settleResult(instanceId) {
448
+ const result = this.#results.get(instanceId);
449
+ this.#results.delete(instanceId);
450
+ const resolve = this.#awaitingResults.get(instanceId);
451
+ this.#awaitingResults.delete(instanceId);
452
+ resolve?.(result);
453
+ }
454
+ #sendBars(page) {
455
+ this.channel.send("pageBars", {
456
+ instanceId: page.instanceId,
457
+ ...page.bars
458
+ });
459
+ }
303
460
  unloadPage(instanceId) {
304
461
  const page = this.#pages.get(instanceId);
305
462
  if (!page) return;
306
463
  this.#pages.delete(instanceId);
307
464
  page.close();
465
+ this.#settleResult(instanceId);
308
466
  this.reportError(page.lifecycle("onUnload"), instanceId);
309
467
  this.hooks.emit("pageUnload", page);
310
468
  }
@@ -335,6 +493,6 @@ function instanceIdOf(message) {
335
493
  if (typeof payload?.instanceId === "number") return payload.instanceId;
336
494
  }
337
495
  //#endregion
338
- export { definePage as i, defineDot as n, Page as r, Service as t };
496
+ export { cancelledError as a, definePage as i, defineDot as n, Page as r, Service as t };
339
497
 
340
- //# sourceMappingURL=service-dAFmZ_Mh.js.map
498
+ //# sourceMappingURL=service-Dd4nvlhf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-Dd4nvlhf.js","names":[],"sources":["../src/utils.ts","../src/host-calls.ts","../src/page.ts","../src/service.ts"],"sourcesContent":["import { HostError } from '@monimesl/dot-core';\n\nexport function cancelledError(method: string): HostError {\n return new HostError('cancelled', `\"${method}\" was cancelled`);\n}\n","import type {\n HostCallOptions,\n HostRequest,\n HostResult,\n LogicLayerMessages,\n} from '@monimesl/dot-core';\nimport { HostError, createHostRequest } from '@monimesl/dot-core';\n\nimport type { ServiceChannel } from './service';\nimport { cancelledError } from './utils';\n\ninterface WaitingCall {\n readonly method: string;\n resolve(value: unknown): void;\n reject(error: HostError): void;\n}\n\n// Calls to the host that are waiting for their `hostResult`.\nexport class HostCalls {\n readonly #channel: ServiceChannel;\n readonly #waiting = new Map<number, WaitingCall>();\n #nextId = 1;\n\n constructor(channel: ServiceChannel) {\n this.#channel = channel;\n channel.handle('hostResult', (result) => this.#settle(result));\n }\n\n call(method: string, params: unknown, options: HostCallOptions): HostRequest<unknown> {\n const id = this.#nextId++;\n const promise = new Promise<unknown>((resolve, reject) => {\n this.#waiting.set(id, { method, resolve, reject });\n });\n const message: LogicLayerMessages['hostCall'] = { id, method, params };\n if (options.timeout !== undefined) {\n message.timeout = options.timeout;\n }\n this.#channel.send('hostCall', message);\n return createHostRequest(promise, () => this.#cancel(id));\n }\n\n #cancel(id: number): void {\n const call = this.#waiting.get(id);\n if (!call) {\n return;\n }\n this.#waiting.delete(id);\n this.#channel.send('hostCancel', { id });\n call.reject(cancelledError(call.method));\n }\n\n #settle(result: HostResult): void {\n const call = this.#waiting.get(result.id);\n // A cancelled call may still get a late answer from the host. Ignore it.\n if (!call) {\n return;\n }\n this.#waiting.delete(result.id);\n if ('error' in result) {\n call.reject(new HostError(result.error.code, result.error.message));\n } else {\n call.resolve(result.value);\n }\n }\n}\n","import type {\n DotEvent,\n Navigation,\n NavigationBarOptions,\n PageBars,\n PageConfigs,\n PageHooks,\n PageLaunchOptions,\n PageState,\n TabBarOptions,\n} from '@monimesl/dot-core';\nimport {\n applyChanges,\n checkConfigs,\n checkNavigationBar,\n checkTabBar,\n mergeConfigs,\n} from '@monimesl/dot-core';\nimport type { Dash, DashRequest } from '@monimesl/dot-dash';\nimport { dash, tracked } from '@monimesl/dot-dash';\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface DotPageIds {}\n}\n\n// Any string until `.dot/pages.d.ts` lists the dot's pages. The brackets make TypeScript test the\n// ids as a whole, not one by one.\nexport type PageId = [keyof DotPageIds] extends [never]\n ? string\n : Extract<keyof DotPageIds, string>;\n\n// The navigation pushForResult asks for: a page on top.\nexport interface PushNavigation {\n readonly action: 'push';\n readonly page: string;\n readonly options: PageLaunchOptions;\n}\n\nexport interface PageNavigation {\n push(page: PageId, options?: PageLaunchOptions): void;\n\n replace(page: PageId, options?: PageLaunchOptions): void;\n\n reset(page: PageId, options?: PageLaunchOptions): void;\n\n back(delta?: number): void;\n\n // Opens `page` on top, and resolves with what it gives back with backWithResult, or undefined if\n // it closes without one, such as when the person goes back.\n pushForResult<Result = unknown>(\n page: PageId,\n options?: PageLaunchOptions,\n ): Promise<Result | undefined>;\n\n // Closes this page, and gives `result` to the page below if it opened this one with pushForResult.\n backWithResult(result: unknown): void;\n}\n\nexport type DeepReadonly<T> = T extends (...args: never[]) => unknown\n ? T\n : T extends object\n ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n : T;\n\nexport type State<S> = DeepReadonly<S>;\n\nexport type StateChanges<S> = Partial<State<S>>;\n\nexport type StateUpdate<S> = StateChanges<S> | ((state: State<S>) => StateChanges<S> | undefined);\n\n// Declared as methods because TypeScript checks method parameters loosely. That lets a handler\n// narrow its event, such as `(state, event: DotEvent<{ size: number }>) => ...`.\nexport type Mutation<S> = {\n mutation(state: State<S>, event: DotEvent): StateChanges<S> | undefined;\n}['mutation'];\n\nexport type Action<S> = {\n action(state: State<S>, event: DotEvent): void | Promise<void>;\n}['action'];\n\n// A handler's parameters after `state`, which another handler passes when it calls it.\ntype EventArgs<F> = F extends (state: never, ...rest: infer R) => unknown ? R : never;\n\nexport interface PageContext<S extends PageState, Mu, Ac> {\n readonly id: string;\n readonly instanceId: number;\n readonly state: State<S>;\n // Dash's features. Calls still running when the page closes are cancelled.\n readonly dash: Dash;\n\n setState(update: StateUpdate<S>): void;\n\n // Changes Dash's navigation bar on this page, on top of what definePage set.\n setNavigationBar(options: NavigationBarOptions): void;\n\n // Hides or shows Dash's tab bar while this page shows.\n setTabBar(options: TabBarOptions): void;\n\n readonly mutations: { readonly [K in keyof Mu]: (...event: EventArgs<Mu[K]>) => void };\n readonly actions: { readonly [K in keyof Ac]: (...event: EventArgs<Ac[K]>) => Promise<void> };\n}\n\nexport interface ActionContext<S extends PageState, Mu, Ac> extends PageContext<S, Mu, Ac> {\n readonly navigation: PageNavigation;\n}\n\nexport interface PageOptions<S extends PageState, Mu, Ac> {\n id?: string;\n initialState: S;\n // What Dash's bars show on this page, on top of defineDot's configs. `setNavigationBar` and\n // `setTabBar` change them while the page runs.\n configs?: PageConfigs;\n hooks?: Partial<PageHooks> & ThisType<PageContext<S, Mu, Ac>>;\n mutations?: Mu & ThisType<void>;\n actions?: Ac & ThisType<ActionContext<S, Mu, Ac>>;\n}\n\nexport interface PageDefinition<S extends PageState = PageState> {\n readonly id?: string;\n readonly initialState: S;\n readonly configs?: PageConfigs;\n readonly hooks?: Partial<PageHooks>;\n readonly mutations?: Readonly<Record<string, Mutation<S>>>;\n readonly actions?: Readonly<Record<string, Action<S>>>;\n}\n\nexport function definePage<\n S extends PageState,\n Mu extends Record<string, Mutation<S>>,\n Ac extends Record<string, Action<S>>,\n>(options: PageOptions<S, Mu, Ac>): PageDefinition<S> {\n return options;\n}\n\nexport interface PageHost {\n commit(page: Page, patch: PageState): void;\n\n // The page's bars changed.\n showBars(page: Page): void;\n\n // defineDot's configs. The page's own configs override them.\n readonly dotConfigs?: PageConfigs | undefined;\n\n navigate(page: Page, navigation: Navigation): void;\n\n // Opens a page on top of `page`, which waits for what that page gives back.\n pushForResult(page: Page, navigation: PushNavigation): Promise<unknown>;\n\n backWithResult(page: Page, result: unknown): void;\n\n readonly strict?: boolean;\n}\n\n// What a page keeps from the copy it replaces, such as after a hot reload.\nexport interface RestoredPage<S> {\n readonly state: State<S>;\n readonly barChanges: PageBars;\n}\n\ntype Mutations<S> = Record<string, Mutation<S>>;\ntype Actions<S> = Record<string, Action<S>>;\ntype MutationCalls<S extends PageState> = PageContext<S, Mutations<S>, Actions<S>>['mutations'];\ntype ActionCalls<S extends PageState> = PageContext<S, Mutations<S>, Actions<S>>['actions'];\n\nexport class Page<S extends PageState = PageState> {\n readonly id: string;\n readonly instanceId: number;\n readonly context: PageContext<S, Mutations<S>, Actions<S>>;\n readonly actionContext: ActionContext<S, Mutations<S>, Actions<S>>;\n readonly dash: Dash = tracked(dash, (request) => this.#track(request));\n readonly #definition: PageDefinition<S>;\n readonly #host: PageHost;\n #state: State<S>;\n #closed = false;\n readonly #dashRequests = new Set<DashRequest<unknown>>();\n #previousHook: Promise<unknown> = Promise.resolve();\n // Bar changes the page made while running, on top of its configs.\n #barChanges: PageBars;\n\n constructor(\n instanceId: number,\n id: string,\n definition: PageDefinition<S>,\n host: PageHost,\n restored?: RestoredPage<S>,\n ) {\n this.id = id;\n this.instanceId = instanceId;\n this.#definition = definition;\n this.#host = host;\n if (restored) {\n this.#state = this.#freeze(restored.state);\n this.#barChanges = restored.barChanges;\n } else {\n const copy = JSON.parse(JSON.stringify(definition.initialState)) as State<S>;\n this.#state = this.#freeze(copy);\n this.#barChanges = { navigationBar: {}, tabBar: {} };\n }\n checkConfigs(definition.configs ?? {});\n\n const { mutations = {}, actions = {} } = definition;\n const clash = Object.keys(mutations).find((name) => name in actions);\n if (clash) {\n throw new Error(\n `Page \"${id}\" uses \"${clash}\" as both a mutation and an action. Rename one of them`,\n );\n }\n\n const mutationCalls = mapNames(\n mutations,\n (name) => (event?: DotEvent) => this.mutate(name, event),\n );\n const actionCalls = mapNames(actions, (name) => (event?: DotEvent) => this.act(name, event));\n this.context = new HookContext(this, mutationCalls, actionCalls);\n this.actionContext = new HandlerContext(\n this,\n mutationCalls,\n actionCalls,\n this.#createNavigation(),\n );\n }\n\n get state(): State<S> {\n return this.#state;\n }\n\n // Whether the person can pull the page down to refresh it.\n get refreshable(): boolean {\n return this.#definition.hooks?.onRefresh !== undefined;\n }\n\n get bars(): PageBars {\n return mergeConfigs(this.#host.dotConfigs, this.#definition.configs, this.#barChanges);\n }\n\n get barChanges(): PageBars {\n return this.#barChanges;\n }\n\n setNavigationBar(options: NavigationBarOptions): void {\n checkNavigationBar(options);\n const navigationBar = { ...this.#barChanges.navigationBar, ...options };\n this.#changeBars({ ...this.#barChanges, navigationBar });\n }\n\n setTabBar(options: TabBarOptions): void {\n checkTabBar(options);\n const tabBar = { ...this.#barChanges.tabBar, ...options };\n this.#changeBars({ ...this.#barChanges, tabBar });\n }\n\n #changeBars(barChanges: PageBars): void {\n if (this.#closed) {\n return;\n }\n this.#barChanges = barChanges;\n this.#host.showBars(this as Page);\n }\n\n setState(update: StateUpdate<S>): void {\n if (this.#closed) {\n return;\n }\n const changes = typeof update === 'function' ? update(this.#state) : update;\n if (changes === undefined) {\n return;\n }\n const next = applyChanges(this.#state, changes);\n const patch: PageState = {};\n for (const key of Object.keys(next) as (keyof State<S> & string)[]) {\n if (next[key] !== this.#state[key]) {\n patch[key] = next[key];\n }\n }\n this.#state = this.#freeze(next);\n if (Object.keys(patch).length > 0) {\n this.#host.commit(this as Page, patch);\n }\n }\n\n mutate(name: string, event?: DotEvent): void {\n const mutation = this.#definition.mutations?.[name];\n if (!mutation) {\n throw new Error(`Page \"${this.id}\" has no mutation \"${name}\"`);\n }\n this.#runMutation(mutation, event);\n }\n\n async act(name: string, event?: DotEvent): Promise<void> {\n const action = this.#definition.actions?.[name];\n if (!action) {\n throw new Error(`Page \"${this.id}\" has no action \"${name}\"`);\n }\n await this.#runAction(action, event);\n }\n\n // Runs the mutation or action named by a view event. A mutation is done when this returns.\n dispatch(name: string, event: DotEvent): Promise<void> {\n const action = this.#definition.actions?.[name];\n if (action) {\n return this.#runAction(action, event);\n }\n const mutation = this.#definition.mutations?.[name];\n if (!mutation) {\n throw new Error(`Page \"${this.id}\" has no mutation or action \"${name}\"`);\n }\n this.#runMutation(mutation, event);\n return Promise.resolve();\n }\n\n async lifecycle<K extends keyof PageHooks>(\n hookName: K,\n ...args: Parameters<PageHooks[K]>\n ): Promise<void> {\n const hook = this.#definition.hooks?.[hookName] as\n ((...hookArgs: Parameters<PageHooks[K]>) => unknown) | undefined;\n // Hooks run one at a time, in call order. A failed hook doesn't block the next one.\n const run = this.#previousHook.then(() => hook?.call(this.context, ...args));\n this.#previousHook = run.catch(() => {});\n await run;\n }\n\n close(): void {\n this.#closed = true;\n for (const request of this.#dashRequests) {\n request.cancel();\n }\n this.#dashRequests.clear();\n }\n\n #track(request: DashRequest<unknown>): void {\n if (this.#closed) {\n return;\n }\n this.#dashRequests.add(request);\n const forget = () => this.#dashRequests.delete(request);\n void request.then(forget, forget);\n }\n\n // Handlers called from other handlers may leave out the event, so it can be undefined here.\n #runMutation(mutation: Mutation<S>, event: DotEvent | undefined): void {\n this.setState((state) => mutation(state, event as DotEvent));\n }\n\n async #runAction(action: Action<S>, event: DotEvent | undefined): Promise<void> {\n await action.call(this.actionContext, this.#state, event as DotEvent);\n }\n\n #createNavigation(): PageNavigation {\n return {\n push: (page, options = {}) => this.#navigate({ action: 'push', page, options }),\n replace: (page, options = {}) => this.#navigate({ action: 'replace', page, options }),\n reset: (page, options = {}) => this.#navigate({ action: 'reset', page, options }),\n back: (delta = 1) => this.#navigate({ action: 'back', delta }),\n pushForResult: <Result>(page: PageId, options: PageLaunchOptions = {}) => {\n if (this.#closed) {\n return Promise.resolve(undefined);\n }\n const result = this.#host.pushForResult(this as Page, { action: 'push', page, options });\n return result as Promise<Result | undefined>;\n },\n backWithResult: (result) => {\n if (!this.#closed) {\n this.#host.backWithResult(this as Page, result);\n }\n },\n };\n }\n\n #navigate(navigation: Navigation): void {\n if (!this.#closed) {\n this.#host.navigate(this as Page, navigation);\n }\n }\n\n #freeze(state: State<S>): State<S> {\n return this.#host.strict ? deepFreeze(state) : state;\n }\n}\n\n// What hooks get as `this`. `setState` is bound, so a hook may destructure it.\nclass HookContext<S extends PageState> implements PageContext<S, Mutations<S>, Actions<S>> {\n readonly id: string;\n readonly instanceId: number;\n readonly mutations: MutationCalls<S>;\n readonly actions: ActionCalls<S>;\n readonly dash: Dash;\n readonly #page: Page<S>;\n\n constructor(page: Page<S>, mutations: MutationCalls<S>, actions: ActionCalls<S>) {\n this.id = page.id;\n this.instanceId = page.instanceId;\n this.dash = page.dash;\n this.mutations = mutations;\n this.actions = actions;\n this.#page = page;\n }\n\n get state(): State<S> {\n return this.#page.state;\n }\n\n readonly setState = (update: StateUpdate<S>): void => {\n this.#page.setState(update);\n };\n\n readonly setNavigationBar = (options: NavigationBarOptions): void => {\n this.#page.setNavigationBar(options);\n };\n\n readonly setTabBar = (options: TabBarOptions): void => {\n this.#page.setTabBar(options);\n };\n}\n\n// What actions get as `this`: the hook context plus navigation.\nclass HandlerContext<S extends PageState>\n extends HookContext<S>\n implements ActionContext<S, Mutations<S>, Actions<S>>\n{\n readonly navigation: PageNavigation;\n\n constructor(\n page: Page<S>,\n mutations: MutationCalls<S>,\n actions: ActionCalls<S>,\n navigation: PageNavigation,\n ) {\n super(page, mutations, actions);\n this.navigation = navigation;\n }\n}\n\nfunction mapNames<T>(\n record: Readonly<Record<string, unknown>>,\n create: (name: string) => T,\n): Record<string, T> {\n return Object.fromEntries(Object.keys(record).map((name) => [name, create(name)]));\n}\n\nfunction deepFreeze<T>(value: T): T {\n if (typeof value === 'object' && value !== null && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const child of Object.values(value)) {\n deepFreeze(child);\n }\n }\n return value;\n}\n","import type {\n DotHooks,\n DotEvent,\n Envelope,\n HostCaller,\n HostLayerMessages,\n LogicLayerMessages,\n Navigation,\n PageConfigs,\n PageLaunchOptions,\n PageState,\n SystemContext,\n Theme,\n Transport,\n UILayerMessages,\n} from '@monimesl/dot-core';\nimport {\n DEFAULT_SYSTEM_CONTEXT,\n checkConfigs,\n connectHost,\n EventEmitter,\n MAX_STACK_DEPTH,\n MessageChannel,\n} from '@monimesl/dot-core';\n\nimport { HostCalls } from './host-calls';\nimport type { PageDefinition, PushNavigation, RestoredPage } from './page';\nimport { Page } from './page';\n\nexport type ServiceChannel = MessageChannel<\n LogicLayerMessages,\n UILayerMessages & HostLayerMessages\n>;\n\nexport interface ServiceHooks {\n launch(): void;\n\n pageLoad(page: Page): void;\n\n pageUnload(page: Page): void;\n\n pageShow(page: Page): void;\n\n pageHide(page: Page): void;\n\n // A page asked to navigate. The request is on its way to the view layer.\n navigate(page: Page, navigation: Navigation): void;\n\n // Before a handler runs\n event(page: Page, handler: string, event: DotEvent): void;\n\n // After the state changes, with the changed keys, before they go to the view layer\n setState(page: Page, patch: PageState): void;\n\n // `instanceId` is set when the error came from a page.\n error(error: unknown, instanceId: number | undefined): void;\n}\n\nexport interface ServicePlugin {\n readonly name: string;\n\n setup(service: Service): void;\n}\n\nexport interface DotContext {\n readonly theme: Theme;\n readonly systemContext: SystemContext;\n\n setTheme(theme: Theme): void;\n}\n\nexport interface DotDefinition {\n readonly theme?: Theme;\n readonly plugins?: readonly ServicePlugin[];\n readonly pages: readonly [string, ...string[]];\n // What Dash's bars show on every page. A page's own configs override them.\n readonly configs?: PageConfigs;\n readonly hooks?: Partial<DotHooks> & ThisType<DotContext>;\n}\n\nexport function defineDot(dot: DotDefinition): DotDefinition {\n return dot;\n}\n\nexport interface ServiceOptions {\n transport: Transport;\n dot?: Partial<DotDefinition>;\n pages: Record<string, PageDefinition>;\n strict?: boolean;\n}\n\n// The logic layer. It owns every page's state, runs handlers, and sends state changes to the\n// view layer.\nexport class Service implements DotContext {\n readonly channel: ServiceChannel;\n readonly hooks = new EventEmitter<ServiceHooks>();\n #definitions: Record<string, PageDefinition>;\n readonly #pages = new Map<number, Page>();\n readonly #dot: Partial<DotDefinition>;\n readonly #strict: boolean;\n #theme: Theme;\n #systemContext: SystemContext = DEFAULT_SYSTEM_CONTEXT;\n #stack: HostLayerMessages['syncStack'] = { pages: [], limit: MAX_STACK_DEPTH };\n // How each page waiting for a result from a page it opened with pushForResult gets it.\n readonly #awaitingResults = new Map<number, (result: unknown) => void>();\n // Results given with backWithResult, kept for the page below until it shows again.\n readonly #results = new Map<number, unknown>();\n readonly #hostCalls: HostCalls;\n\n constructor({ transport, pages, dot = {}, strict = false }: ServiceOptions) {\n this.channel = new MessageChannel(transport);\n this.#hostCalls = new HostCalls(this.channel);\n this.#definitions = pages;\n this.#dot = dot;\n this.#strict = strict;\n this.#theme = dot.theme ?? {};\n checkConfigs(dot.configs ?? {});\n\n this.channel.handle('connect', () => this.channel.send('setTheme', { theme: this.#theme }));\n this.channel.handle('syncStack', (stack) => {\n this.#stack = stack;\n });\n this.channel.handle('syncSystemContext', (context) => {\n this.#systemContext = context;\n });\n this.channel.handle('load', ({ instanceId, page, options }) =>\n this.loadPage(instanceId, page, options),\n );\n this.channel.handle('unload', ({ instanceId }) => this.unloadPage(instanceId));\n this.channel.handle('show', ({ instanceId }) => {\n const page = this.getPage(instanceId);\n // The page it opened has closed, with or without a result.\n this.#settleResult(instanceId);\n this.hooks.emit('pageShow', page);\n this.reportError(page.lifecycle('onShow'), instanceId);\n });\n this.channel.handle('hide', ({ instanceId }) => {\n const page = this.getPage(instanceId);\n this.hooks.emit('pageHide', page);\n this.reportError(page.lifecycle('onHide'), instanceId);\n });\n this.channel.handle('refresh', ({ instanceId }) => {\n const page = this.getPage(instanceId);\n const refresh = page.lifecycle('onRefresh').finally(() => {\n this.channel.send('refreshed', { instanceId });\n });\n this.reportError(refresh, instanceId);\n });\n this.channel.handle('event', ({ instanceId, handler, event }) => {\n const page = this.getPage(instanceId);\n this.hooks.emit('event', page, handler, event);\n this.reportError(page.dispatch(handler, event), instanceId);\n });\n\n dot.plugins?.forEach((plugin) => this.use(plugin));\n }\n\n use(plugin: ServicePlugin): this {\n plugin.setup(this);\n return this;\n }\n\n start(): () => void {\n const stopListening = this.channel.listen((error, message) => {\n this.#reportError(error, instanceIdOf(message));\n });\n connectHost(this.callHost);\n this.hooks.emit('launch');\n this.reportError(this.#runLaunchHook());\n return () => {\n stopListening();\n connectHost(null);\n };\n }\n\n get theme(): Theme {\n return this.#theme;\n }\n\n get systemContext(): SystemContext {\n return this.#systemContext;\n }\n\n setTheme(theme: Theme): void {\n this.#theme = theme;\n this.channel.send('setTheme', { theme });\n }\n\n getPage(instanceId: number): Page {\n const page = this.#pages.get(instanceId);\n if (!page) {\n throw new Error(`Page instance ${instanceId} is not loaded`);\n }\n return page;\n }\n\n // Runs a host call, such as `dash.ui.alert`, through this service's channel.\n readonly callHost: HostCaller = (method, params, options) =>\n this.#hostCalls.call(method, params, options);\n\n loadPage(instanceId: number, id: string, options: PageLaunchOptions = {}): Page {\n const page = this.#createPage(instanceId, id);\n this.channel.send('initPageState', {\n instanceId,\n state: page.state,\n refreshable: page.refreshable,\n });\n this.#sendBars(page);\n this.hooks.emit('pageLoad', page);\n this.reportError(page.lifecycle('onLoad', options), instanceId);\n return page;\n }\n\n // Swaps in a newer build's pages on a hot reload. A page restarts only if its initialState\n // changed.\n replacePages(definitions: Record<string, PageDefinition>): void {\n const previous = this.#definitions;\n this.#definitions = definitions;\n for (const [instanceId, page] of this.#pages) {\n const definition = definitions[page.id];\n // A page the new build no longer has keeps its old code until it closes.\n if (!definition) {\n continue;\n }\n page.close();\n const initialStateChanged =\n JSON.stringify(previous[page.id]?.initialState) !== JSON.stringify(definition.initialState);\n if (initialStateChanged) {\n // The options the page was opened with, so onLoad runs again with them.\n const options = this.#stack.pages.find((entry) => entry.instanceId === instanceId)?.options;\n this.loadPage(instanceId, page.id, options);\n } else {\n const kept = this.#createPage(instanceId, page.id, {\n state: page.state,\n barChanges: page.barChanges,\n });\n // The new definePage may set other configs, or add or drop onRefresh.\n this.#sendBars(kept);\n if (kept.refreshable !== page.refreshable) {\n this.channel.send('initPageState', {\n instanceId,\n state: kept.state,\n refreshable: kept.refreshable,\n });\n }\n }\n }\n }\n\n #createPage(instanceId: number, id: string, restored?: RestoredPage<PageState>): Page {\n const page = new Page(\n instanceId,\n id,\n this.#definition(id),\n {\n commit: (page, patch) => {\n this.hooks.emit('setState', page, patch);\n this.channel.send('setPageState', { instanceId: page.instanceId, patchState: patch });\n },\n showBars: (page) => this.#sendBars(page),\n dotConfigs: this.#dot.configs,\n navigate: (page, navigation) => this.#navigate(page, navigation),\n pushForResult: (page, navigation) => this.#pushForResult(page, navigation),\n backWithResult: (page, result) => this.#backWithResult(page, result),\n strict: this.#strict,\n },\n restored,\n );\n this.#pages.set(instanceId, page);\n return page;\n }\n\n #navigate(page: Page, navigation: Navigation): void {\n if (navigation.action !== 'back') {\n this.#definition(navigation.page);\n }\n const { pages, limit } = this.#stack;\n if (navigation.action === 'push' && pages.length >= limit) {\n throw new Error(\n `Can't open \"${navigation.page}\": ${limit} pages are already open. Use replace or back.`,\n );\n }\n this.hooks.emit('navigate', page, navigation);\n this.channel.send('navigate', { ...navigation, from: page.instanceId });\n }\n\n #pushForResult(page: Page, navigation: PushNavigation): Promise<unknown> {\n // A result it was still waiting for can't come now.\n this.#settleResult(page.instanceId);\n // When the page can't open, the promise rejects with the reason.\n return new Promise((resolve) => {\n this.#navigate(page, navigation);\n this.#awaitingResults.set(page.instanceId, resolve);\n });\n }\n\n #backWithResult(page: Page, result: unknown): void {\n const { pages } = this.#stack;\n const index = pages.findIndex((entry) => entry.instanceId === page.instanceId);\n if (index > 0) {\n const below = pages[index - 1]!;\n this.#results.set(below.instanceId, result);\n }\n this.#navigate(page, { action: 'back', delta: 1 });\n }\n\n // Ends the page's wait for a result: with the one kept for it, or undefined without one.\n #settleResult(instanceId: number): void {\n const result = this.#results.get(instanceId);\n this.#results.delete(instanceId);\n const resolve = this.#awaitingResults.get(instanceId);\n this.#awaitingResults.delete(instanceId);\n resolve?.(result);\n }\n\n #sendBars(page: Page): void {\n this.channel.send('pageBars', { instanceId: page.instanceId, ...page.bars });\n }\n\n unloadPage(instanceId: number): void {\n const page = this.#pages.get(instanceId);\n if (!page) {\n return;\n }\n this.#pages.delete(instanceId);\n page.close();\n this.#settleResult(instanceId);\n this.reportError(page.lifecycle('onUnload'), instanceId);\n this.hooks.emit('pageUnload', page);\n }\n\n #definition(id: string): PageDefinition {\n const definition = this.#definitions[id];\n if (!definition) {\n throw new Error(`Unknown page \"${id}\"`);\n }\n return definition;\n }\n\n reportError(work: Promise<void>, instanceId?: number): void {\n work.catch((error: unknown) => {\n this.#reportError(error, instanceId);\n });\n }\n\n async #runLaunchHook(): Promise<void> {\n await this.#dot.hooks?.onLaunch?.call(this);\n }\n\n #reportError(error: unknown, instanceId: number | undefined): void {\n this.hooks.emit('error', error, instanceId);\n const reason = error instanceof Error ? error : new Error(String(error));\n this.#dot.hooks?.onError?.call(this, reason);\n const report: LogicLayerMessages['error'] = { message: reason.message };\n if (instanceId !== undefined) {\n report.instanceId = instanceId;\n }\n this.channel.send('error', report);\n }\n}\n\nfunction instanceIdOf(message: Envelope): number | undefined {\n const payload = message.payload as { instanceId?: unknown } | undefined;\n if (typeof payload?.instanceId === 'number') {\n return payload.instanceId;\n }\n return undefined;\n}\n"],"mappings":";;;AAEA,SAAgB,eAAe,QAA2B;CACxD,OAAO,IAAI,UAAU,aAAa,IAAI,OAAO,gBAAgB;AAC/D;;;ACcA,IAAa,YAAb,MAAuB;CACrB;CACA,2BAAoB,IAAI,IAAyB;CACjD,UAAU;CAEV,YAAY,SAAyB;EACnC,KAAK,WAAW;EAChB,QAAQ,OAAO,eAAe,WAAW,KAAK,QAAQ,MAAM,CAAC;CAC/D;CAEA,KAAK,QAAgB,QAAiB,SAAgD;EACpF,MAAM,KAAK,KAAK;EAChB,MAAM,UAAU,IAAI,SAAkB,SAAS,WAAW;GACxD,KAAK,SAAS,IAAI,IAAI;IAAE;IAAQ;IAAS;GAAO,CAAC;EACnD,CAAC;EACD,MAAM,UAA0C;GAAE;GAAI;GAAQ;EAAO;EACrE,IAAI,QAAQ,YAAY,KAAA,GACtB,QAAQ,UAAU,QAAQ;EAE5B,KAAK,SAAS,KAAK,YAAY,OAAO;EACtC,OAAO,kBAAkB,eAAe,KAAK,QAAQ,EAAE,CAAC;CAC1D;CAEA,QAAQ,IAAkB;EACxB,MAAM,OAAO,KAAK,SAAS,IAAI,EAAE;EACjC,IAAI,CAAC,MACH;EAEF,KAAK,SAAS,OAAO,EAAE;EACvB,KAAK,SAAS,KAAK,cAAc,EAAE,GAAG,CAAC;EACvC,KAAK,OAAO,eAAe,KAAK,MAAM,CAAC;CACzC;CAEA,QAAQ,QAA0B;EAChC,MAAM,OAAO,KAAK,SAAS,IAAI,OAAO,EAAE;EAExC,IAAI,CAAC,MACH;EAEF,KAAK,SAAS,OAAO,OAAO,EAAE;EAC9B,IAAI,WAAW,QACb,KAAK,OAAO,IAAI,UAAU,OAAO,MAAM,MAAM,OAAO,MAAM,OAAO,CAAC;OAElE,KAAK,QAAQ,OAAO,KAAK;CAE7B;AACF;;;AC+DA,SAAgB,WAId,SAAoD;CACpD,OAAO;AACT;AAgCA,IAAa,OAAb,MAAmD;CACjD;CACA;CACA;CACA;CACA,OAAsB,QAAQ,OAAO,YAAY,KAAK,OAAO,OAAO,CAAC;CACrE;CACA;CACA;CACA,UAAU;CACV,gCAAyB,IAAI,IAA0B;CACvD,gBAAkC,QAAQ,QAAQ;CAElD;CAEA,YACE,YACA,IACA,YACA,MACA,UACA;EACA,KAAK,KAAK;EACV,KAAK,aAAa;EAClB,KAAK,cAAc;EACnB,KAAK,QAAQ;EACb,IAAI,UAAU;GACZ,KAAK,SAAS,KAAK,QAAQ,SAAS,KAAK;GACzC,KAAK,cAAc,SAAS;EAC9B,OAAO;GACL,MAAM,OAAO,KAAK,MAAM,KAAK,UAAU,WAAW,YAAY,CAAC;GAC/D,KAAK,SAAS,KAAK,QAAQ,IAAI;GAC/B,KAAK,cAAc;IAAE,eAAe,CAAC;IAAG,QAAQ,CAAC;GAAE;EACrD;EACA,aAAa,WAAW,WAAW,CAAC,CAAC;EAErC,MAAM,EAAE,YAAY,CAAC,GAAG,UAAU,CAAC,MAAM;EACzC,MAAM,QAAQ,OAAO,KAAK,SAAS,CAAC,CAAC,MAAM,SAAS,QAAQ,OAAO;EACnE,IAAI,OACF,MAAM,IAAI,MACR,SAAS,GAAG,UAAU,MAAM,uDAC9B;EAGF,MAAM,gBAAgB,SACpB,YACC,UAAU,UAAqB,KAAK,OAAO,MAAM,KAAK,CACzD;EACA,MAAM,cAAc,SAAS,UAAU,UAAU,UAAqB,KAAK,IAAI,MAAM,KAAK,CAAC;EAC3F,KAAK,UAAU,IAAI,YAAY,MAAM,eAAe,WAAW;EAC/D,KAAK,gBAAgB,IAAI,eACvB,MACA,eACA,aACA,KAAK,kBAAkB,CACzB;CACF;CAEA,IAAI,QAAkB;EACpB,OAAO,KAAK;CACd;CAGA,IAAI,cAAuB;EACzB,OAAO,KAAK,YAAY,OAAO,cAAc,KAAA;CAC/C;CAEA,IAAI,OAAiB;EACnB,OAAO,aAAa,KAAK,MAAM,YAAY,KAAK,YAAY,SAAS,KAAK,WAAW;CACvF;CAEA,IAAI,aAAuB;EACzB,OAAO,KAAK;CACd;CAEA,iBAAiB,SAAqC;EACpD,mBAAmB,OAAO;EAC1B,MAAM,gBAAgB;GAAE,GAAG,KAAK,YAAY;GAAe,GAAG;EAAQ;EACtE,KAAK,YAAY;GAAE,GAAG,KAAK;GAAa;EAAc,CAAC;CACzD;CAEA,UAAU,SAA8B;EACtC,YAAY,OAAO;EACnB,MAAM,SAAS;GAAE,GAAG,KAAK,YAAY;GAAQ,GAAG;EAAQ;EACxD,KAAK,YAAY;GAAE,GAAG,KAAK;GAAa;EAAO,CAAC;CAClD;CAEA,YAAY,YAA4B;EACtC,IAAI,KAAK,SACP;EAEF,KAAK,cAAc;EACnB,KAAK,MAAM,SAAS,IAAY;CAClC;CAEA,SAAS,QAA8B;EACrC,IAAI,KAAK,SACP;EAEF,MAAM,UAAU,OAAO,WAAW,aAAa,OAAO,KAAK,MAAM,IAAI;EACrE,IAAI,YAAY,KAAA,GACd;EAEF,MAAM,OAAO,aAAa,KAAK,QAAQ,OAAO;EAC9C,MAAM,QAAmB,CAAC;EAC1B,KAAK,MAAM,OAAO,OAAO,KAAK,IAAI,GAChC,IAAI,KAAK,SAAS,KAAK,OAAO,MAC5B,MAAM,OAAO,KAAK;EAGtB,KAAK,SAAS,KAAK,QAAQ,IAAI;EAC/B,IAAI,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,GAC9B,KAAK,MAAM,OAAO,MAAc,KAAK;CAEzC;CAEA,OAAO,MAAc,OAAwB;EAC3C,MAAM,WAAW,KAAK,YAAY,YAAY;EAC9C,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,SAAS,KAAK,GAAG,qBAAqB,KAAK,EAAE;EAE/D,KAAK,aAAa,UAAU,KAAK;CACnC;CAEA,MAAM,IAAI,MAAc,OAAiC;EACvD,MAAM,SAAS,KAAK,YAAY,UAAU;EAC1C,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,SAAS,KAAK,GAAG,mBAAmB,KAAK,EAAE;EAE7D,MAAM,KAAK,WAAW,QAAQ,KAAK;CACrC;CAGA,SAAS,MAAc,OAAgC;EACrD,MAAM,SAAS,KAAK,YAAY,UAAU;EAC1C,IAAI,QACF,OAAO,KAAK,WAAW,QAAQ,KAAK;EAEtC,MAAM,WAAW,KAAK,YAAY,YAAY;EAC9C,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,SAAS,KAAK,GAAG,+BAA+B,KAAK,EAAE;EAEzE,KAAK,aAAa,UAAU,KAAK;EACjC,OAAO,QAAQ,QAAQ;CACzB;CAEA,MAAM,UACJ,UACA,GAAG,MACY;EACf,MAAM,OAAO,KAAK,YAAY,QAAQ;EAGtC,MAAM,MAAM,KAAK,cAAc,WAAW,MAAM,KAAK,KAAK,SAAS,GAAG,IAAI,CAAC;EAC3E,KAAK,gBAAgB,IAAI,YAAY,CAAC,CAAC;EACvC,MAAM;CACR;CAEA,QAAc;EACZ,KAAK,UAAU;EACf,KAAK,MAAM,WAAW,KAAK,eACzB,QAAQ,OAAO;EAEjB,KAAK,cAAc,MAAM;CAC3B;CAEA,OAAO,SAAqC;EAC1C,IAAI,KAAK,SACP;EAEF,KAAK,cAAc,IAAI,OAAO;EAC9B,MAAM,eAAe,KAAK,cAAc,OAAO,OAAO;EACtD,QAAa,KAAK,QAAQ,MAAM;CAClC;CAGA,aAAa,UAAuB,OAAmC;EACrE,KAAK,UAAU,UAAU,SAAS,OAAO,KAAiB,CAAC;CAC7D;CAEA,MAAM,WAAW,QAAmB,OAA4C;EAC9E,MAAM,OAAO,KAAK,KAAK,eAAe,KAAK,QAAQ,KAAiB;CACtE;CAEA,oBAAoC;EAClC,OAAO;GACL,OAAO,MAAM,UAAU,CAAC,MAAM,KAAK,UAAU;IAAE,QAAQ;IAAQ;IAAM;GAAQ,CAAC;GAC9E,UAAU,MAAM,UAAU,CAAC,MAAM,KAAK,UAAU;IAAE,QAAQ;IAAW;IAAM;GAAQ,CAAC;GACpF,QAAQ,MAAM,UAAU,CAAC,MAAM,KAAK,UAAU;IAAE,QAAQ;IAAS;IAAM;GAAQ,CAAC;GAChF,OAAO,QAAQ,MAAM,KAAK,UAAU;IAAE,QAAQ;IAAQ;GAAM,CAAC;GAC7D,gBAAwB,MAAc,UAA6B,CAAC,MAAM;IACxE,IAAI,KAAK,SACP,OAAO,QAAQ,QAAQ,KAAA,CAAS;IAGlC,OADe,KAAK,MAAM,cAAc,MAAc;KAAE,QAAQ;KAAQ;KAAM;IAAQ,CAC1E;GACd;GACA,iBAAiB,WAAW;IAC1B,IAAI,CAAC,KAAK,SACR,KAAK,MAAM,eAAe,MAAc,MAAM;GAElD;EACF;CACF;CAEA,UAAU,YAA8B;EACtC,IAAI,CAAC,KAAK,SACR,KAAK,MAAM,SAAS,MAAc,UAAU;CAEhD;CAEA,QAAQ,OAA2B;EACjC,OAAO,KAAK,MAAM,SAAS,WAAW,KAAK,IAAI;CACjD;AACF;AAGA,IAAM,cAAN,MAA2F;CACzF;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,MAAe,WAA6B,SAAyB;EAC/E,KAAK,KAAK,KAAK;EACf,KAAK,aAAa,KAAK;EACvB,KAAK,OAAO,KAAK;EACjB,KAAK,YAAY;EACjB,KAAK,UAAU;EACf,KAAK,QAAQ;CACf;CAEA,IAAI,QAAkB;EACpB,OAAO,KAAK,MAAM;CACpB;CAEA,YAAqB,WAAiC;EACpD,KAAK,MAAM,SAAS,MAAM;CAC5B;CAEA,oBAA6B,YAAwC;EACnE,KAAK,MAAM,iBAAiB,OAAO;CACrC;CAEA,aAAsB,YAAiC;EACrD,KAAK,MAAM,UAAU,OAAO;CAC9B;AACF;AAGA,IAAM,iBAAN,cACU,YAEV;CACE;CAEA,YACE,MACA,WACA,SACA,YACA;EACA,MAAM,MAAM,WAAW,OAAO;EAC9B,KAAK,aAAa;CACpB;AACF;AAEA,SAAS,SACP,QACA,QACmB;CACnB,OAAO,OAAO,YAAY,OAAO,KAAK,MAAM,CAAC,CAAC,KAAK,SAAS,CAAC,MAAM,OAAO,IAAI,CAAC,CAAC,CAAC;AACnF;AAEA,SAAS,WAAc,OAAa;CAClC,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,OAAO,SAAS,KAAK,GAAG;EAC1E,OAAO,OAAO,KAAK;EACnB,KAAK,MAAM,SAAS,OAAO,OAAO,KAAK,GACrC,WAAW,KAAK;CAEpB;CACA,OAAO;AACT;;;ACjXA,SAAgB,UAAU,KAAmC;CAC3D,OAAO;AACT;AAWA,IAAa,UAAb,MAA2C;CACzC;CACA,QAAiB,IAAI,aAA2B;CAChD;CACA,yBAAkB,IAAI,IAAkB;CACxC;CACA;CACA;CACA,iBAAgC;CAChC,SAAyC;EAAE,OAAO,CAAC;EAAG,OAAO;CAAgB;CAE7E,mCAA4B,IAAI,IAAuC;CAEvE,2BAAoB,IAAI,IAAqB;CAC7C;CAEA,YAAY,EAAE,WAAW,OAAO,MAAM,CAAC,GAAG,SAAS,SAAyB;EAC1E,KAAK,UAAU,IAAI,eAAe,SAAS;EAC3C,KAAK,aAAa,IAAI,UAAU,KAAK,OAAO;EAC5C,KAAK,eAAe;EACpB,KAAK,OAAO;EACZ,KAAK,UAAU;EACf,KAAK,SAAS,IAAI,SAAS,CAAC;EAC5B,aAAa,IAAI,WAAW,CAAC,CAAC;EAE9B,KAAK,QAAQ,OAAO,iBAAiB,KAAK,QAAQ,KAAK,YAAY,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC;EAC1F,KAAK,QAAQ,OAAO,cAAc,UAAU;GAC1C,KAAK,SAAS;EAChB,CAAC;EACD,KAAK,QAAQ,OAAO,sBAAsB,YAAY;GACpD,KAAK,iBAAiB;EACxB,CAAC;EACD,KAAK,QAAQ,OAAO,SAAS,EAAE,YAAY,MAAM,cAC/C,KAAK,SAAS,YAAY,MAAM,OAAO,CACzC;EACA,KAAK,QAAQ,OAAO,WAAW,EAAE,iBAAiB,KAAK,WAAW,UAAU,CAAC;EAC7E,KAAK,QAAQ,OAAO,SAAS,EAAE,iBAAiB;GAC9C,MAAM,OAAO,KAAK,QAAQ,UAAU;GAEpC,KAAK,cAAc,UAAU;GAC7B,KAAK,MAAM,KAAK,YAAY,IAAI;GAChC,KAAK,YAAY,KAAK,UAAU,QAAQ,GAAG,UAAU;EACvD,CAAC;EACD,KAAK,QAAQ,OAAO,SAAS,EAAE,iBAAiB;GAC9C,MAAM,OAAO,KAAK,QAAQ,UAAU;GACpC,KAAK,MAAM,KAAK,YAAY,IAAI;GAChC,KAAK,YAAY,KAAK,UAAU,QAAQ,GAAG,UAAU;EACvD,CAAC;EACD,KAAK,QAAQ,OAAO,YAAY,EAAE,iBAAiB;GAEjD,MAAM,UADO,KAAK,QAAQ,UACP,CAAC,CAAC,UAAU,WAAW,CAAC,CAAC,cAAc;IACxD,KAAK,QAAQ,KAAK,aAAa,EAAE,WAAW,CAAC;GAC/C,CAAC;GACD,KAAK,YAAY,SAAS,UAAU;EACtC,CAAC;EACD,KAAK,QAAQ,OAAO,UAAU,EAAE,YAAY,SAAS,YAAY;GAC/D,MAAM,OAAO,KAAK,QAAQ,UAAU;GACpC,KAAK,MAAM,KAAK,SAAS,MAAM,SAAS,KAAK;GAC7C,KAAK,YAAY,KAAK,SAAS,SAAS,KAAK,GAAG,UAAU;EAC5D,CAAC;EAED,IAAI,SAAS,SAAS,WAAW,KAAK,IAAI,MAAM,CAAC;CACnD;CAEA,IAAI,QAA6B;EAC/B,OAAO,MAAM,IAAI;EACjB,OAAO;CACT;CAEA,QAAoB;EAClB,MAAM,gBAAgB,KAAK,QAAQ,QAAQ,OAAO,YAAY;GAC5D,KAAK,aAAa,OAAO,aAAa,OAAO,CAAC;EAChD,CAAC;EACD,YAAY,KAAK,QAAQ;EACzB,KAAK,MAAM,KAAK,QAAQ;EACxB,KAAK,YAAY,KAAK,eAAe,CAAC;EACtC,aAAa;GACX,cAAc;GACd,YAAY,IAAI;EAClB;CACF;CAEA,IAAI,QAAe;EACjB,OAAO,KAAK;CACd;CAEA,IAAI,gBAA+B;EACjC,OAAO,KAAK;CACd;CAEA,SAAS,OAAoB;EAC3B,KAAK,SAAS;EACd,KAAK,QAAQ,KAAK,YAAY,EAAE,MAAM,CAAC;CACzC;CAEA,QAAQ,YAA0B;EAChC,MAAM,OAAO,KAAK,OAAO,IAAI,UAAU;EACvC,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,iBAAiB,WAAW,eAAe;EAE7D,OAAO;CACT;CAGA,YAAiC,QAAQ,QAAQ,YAC/C,KAAK,WAAW,KAAK,QAAQ,QAAQ,OAAO;CAE9C,SAAS,YAAoB,IAAY,UAA6B,CAAC,GAAS;EAC9E,MAAM,OAAO,KAAK,YAAY,YAAY,EAAE;EAC5C,KAAK,QAAQ,KAAK,iBAAiB;GACjC;GACA,OAAO,KAAK;GACZ,aAAa,KAAK;EACpB,CAAC;EACD,KAAK,UAAU,IAAI;EACnB,KAAK,MAAM,KAAK,YAAY,IAAI;EAChC,KAAK,YAAY,KAAK,UAAU,UAAU,OAAO,GAAG,UAAU;EAC9D,OAAO;CACT;CAIA,aAAa,aAAmD;EAC9D,MAAM,WAAW,KAAK;EACtB,KAAK,eAAe;EACpB,KAAK,MAAM,CAAC,YAAY,SAAS,KAAK,QAAQ;GAC5C,MAAM,aAAa,YAAY,KAAK;GAEpC,IAAI,CAAC,YACH;GAEF,KAAK,MAAM;GAGX,IADE,KAAK,UAAU,SAAS,KAAK,GAAG,EAAE,YAAY,MAAM,KAAK,UAAU,WAAW,YAAY,GACnE;IAEvB,MAAM,UAAU,KAAK,OAAO,MAAM,MAAM,UAAU,MAAM,eAAe,UAAU,CAAC,EAAE;IACpF,KAAK,SAAS,YAAY,KAAK,IAAI,OAAO;GAC5C,OAAO;IACL,MAAM,OAAO,KAAK,YAAY,YAAY,KAAK,IAAI;KACjD,OAAO,KAAK;KACZ,YAAY,KAAK;IACnB,CAAC;IAED,KAAK,UAAU,IAAI;IACnB,IAAI,KAAK,gBAAgB,KAAK,aAC5B,KAAK,QAAQ,KAAK,iBAAiB;KACjC;KACA,OAAO,KAAK;KACZ,aAAa,KAAK;IACpB,CAAC;GAEL;EACF;CACF;CAEA,YAAY,YAAoB,IAAY,UAA0C;EACpF,MAAM,OAAO,IAAI,KACf,YACA,IACA,KAAK,YAAY,EAAE,GACnB;GACE,SAAS,MAAM,UAAU;IACvB,KAAK,MAAM,KAAK,YAAY,MAAM,KAAK;IACvC,KAAK,QAAQ,KAAK,gBAAgB;KAAE,YAAY,KAAK;KAAY,YAAY;IAAM,CAAC;GACtF;GACA,WAAW,SAAS,KAAK,UAAU,IAAI;GACvC,YAAY,KAAK,KAAK;GACtB,WAAW,MAAM,eAAe,KAAK,UAAU,MAAM,UAAU;GAC/D,gBAAgB,MAAM,eAAe,KAAK,eAAe,MAAM,UAAU;GACzE,iBAAiB,MAAM,WAAW,KAAK,gBAAgB,MAAM,MAAM;GACnE,QAAQ,KAAK;EACf,GACA,QACF;EACA,KAAK,OAAO,IAAI,YAAY,IAAI;EAChC,OAAO;CACT;CAEA,UAAU,MAAY,YAA8B;EAClD,IAAI,WAAW,WAAW,QACxB,KAAK,YAAY,WAAW,IAAI;EAElC,MAAM,EAAE,OAAO,UAAU,KAAK;EAC9B,IAAI,WAAW,WAAW,UAAU,MAAM,UAAU,OAClD,MAAM,IAAI,MACR,eAAe,WAAW,KAAK,KAAK,MAAM,8CAC5C;EAEF,KAAK,MAAM,KAAK,YAAY,MAAM,UAAU;EAC5C,KAAK,QAAQ,KAAK,YAAY;GAAE,GAAG;GAAY,MAAM,KAAK;EAAW,CAAC;CACxE;CAEA,eAAe,MAAY,YAA8C;EAEvE,KAAK,cAAc,KAAK,UAAU;EAElC,OAAO,IAAI,SAAS,YAAY;GAC9B,KAAK,UAAU,MAAM,UAAU;GAC/B,KAAK,iBAAiB,IAAI,KAAK,YAAY,OAAO;EACpD,CAAC;CACH;CAEA,gBAAgB,MAAY,QAAuB;EACjD,MAAM,EAAE,UAAU,KAAK;EACvB,MAAM,QAAQ,MAAM,WAAW,UAAU,MAAM,eAAe,KAAK,UAAU;EAC7E,IAAI,QAAQ,GAAG;GACb,MAAM,QAAQ,MAAM,QAAQ;GAC5B,KAAK,SAAS,IAAI,MAAM,YAAY,MAAM;EAC5C;EACA,KAAK,UAAU,MAAM;GAAE,QAAQ;GAAQ,OAAO;EAAE,CAAC;CACnD;CAGA,cAAc,YAA0B;EACtC,MAAM,SAAS,KAAK,SAAS,IAAI,UAAU;EAC3C,KAAK,SAAS,OAAO,UAAU;EAC/B,MAAM,UAAU,KAAK,iBAAiB,IAAI,UAAU;EACpD,KAAK,iBAAiB,OAAO,UAAU;EACvC,UAAU,MAAM;CAClB;CAEA,UAAU,MAAkB;EAC1B,KAAK,QAAQ,KAAK,YAAY;GAAE,YAAY,KAAK;GAAY,GAAG,KAAK;EAAK,CAAC;CAC7E;CAEA,WAAW,YAA0B;EACnC,MAAM,OAAO,KAAK,OAAO,IAAI,UAAU;EACvC,IAAI,CAAC,MACH;EAEF,KAAK,OAAO,OAAO,UAAU;EAC7B,KAAK,MAAM;EACX,KAAK,cAAc,UAAU;EAC7B,KAAK,YAAY,KAAK,UAAU,UAAU,GAAG,UAAU;EACvD,KAAK,MAAM,KAAK,cAAc,IAAI;CACpC;CAEA,YAAY,IAA4B;EACtC,MAAM,aAAa,KAAK,aAAa;EACrC,IAAI,CAAC,YACH,MAAM,IAAI,MAAM,iBAAiB,GAAG,EAAE;EAExC,OAAO;CACT;CAEA,YAAY,MAAqB,YAA2B;EAC1D,KAAK,OAAO,UAAmB;GAC7B,KAAK,aAAa,OAAO,UAAU;EACrC,CAAC;CACH;CAEA,MAAM,iBAAgC;EACpC,MAAM,KAAK,KAAK,OAAO,UAAU,KAAK,IAAI;CAC5C;CAEA,aAAa,OAAgB,YAAsC;EACjE,KAAK,MAAM,KAAK,SAAS,OAAO,UAAU;EAC1C,MAAM,SAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;EACvE,KAAK,KAAK,OAAO,SAAS,KAAK,MAAM,MAAM;EAC3C,MAAM,SAAsC,EAAE,SAAS,OAAO,QAAQ;EACtE,IAAI,eAAe,KAAA,GACjB,OAAO,aAAa;EAEtB,KAAK,QAAQ,KAAK,SAAS,MAAM;CACnC;AACF;AAEA,SAAS,aAAa,SAAuC;CAC3D,MAAM,UAAU,QAAQ;CACxB,IAAI,OAAO,SAAS,eAAe,UACjC,OAAO,QAAQ;AAGnB"}
package/dist/testing.d.ts CHANGED
@@ -1,11 +1,12 @@
1
- import { f as State, s as PageDefinition } from "./page-BfYJ-G-D.js";
2
- import { DotEvent, Navigation, PageLaunchOptions, PageState } from "@monimesl/dot-core";
1
+ import { f as State, s as PageDefinition } from "./page-CysQwLtO.js";
2
+ import { DotEvent, Navigation, PageBars, PageLaunchOptions, PageState } from "@monimesl/dot-core";
3
3
  //#region src/testing.d.ts
4
4
  export type TestHost = Readonly<Record<string, (params: never) => unknown>>;
5
5
  export interface TestPageOptions {
6
6
  id?: string;
7
7
  options?: PageLaunchOptions;
8
8
  host?: TestHost;
9
+ results?: Readonly<Record<string, unknown>>;
9
10
  }
10
11
  export interface HostCallRecord {
11
12
  readonly method: string;
@@ -16,12 +17,15 @@ export interface PageTest<S extends PageState> {
16
17
  readonly patches: readonly PageState[];
17
18
  readonly navigations: readonly Navigation[];
18
19
  readonly hostCalls: readonly HostCallRecord[];
20
+ readonly bars: PageBars;
21
+ readonly result: unknown;
19
22
  tap(name: string, detail?: unknown): Promise<void>;
20
23
  dispatch(name: string, event?: DotEvent): Promise<void>;
21
24
  show(): Promise<void>;
22
25
  hide(): Promise<void>;
23
26
  unload(): Promise<void>;
27
+ refresh(): Promise<void>;
24
28
  }
25
- export declare function testPage<S extends PageState>(definition: PageDefinition<S>, { id, options, host }?: TestPageOptions): Promise<PageTest<S>>;
29
+ export declare function testPage<S extends PageState>(definition: PageDefinition<S>, { id, options, host, results }?: TestPageOptions): Promise<PageTest<S>>;
26
30
  //#endregion
27
31
  //# sourceMappingURL=testing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","names":[],"sources":["../src/testing.ts"],"mappings":";;;YAYY,WAAW,SAAS,gBAAgB;iBAE/B;EACf;EACA,UAAU;EAEV,OAAO;;iBAGQ;WACN;WACA;;iBAGM,SAAS,UAAU;WACzB,OAAO,MAAM;WACb,kBAAkB;WAClB,sBAAsB;WACtB,oBAAoB;EAE7B,IAAI,cAAc,mBAAmB;EAErC,SAAS,cAAc,QAAQ,WAAW;EAE1C,QAAQ;EAER,QAAQ;EAER,UAAU;;wBAGU,SAAS,UAAU,WACvC,YAAY,eAAe,MACzB,IAAa,SAAc,SAAa,kBACzC,QAAQ,SAAS"}
1
+ {"version":3,"file":"testing.d.ts","names":[],"sources":["../src/testing.ts"],"mappings":";;;YAuBY,WAAW,SAAS,gBAAgB;iBAE/B;EACf;EACA,UAAU;EAEV,OAAO;EAGP,UAAU,SAAS;;iBAGJ;WACN;WACA;;iBAGM,SAAS,UAAU;WACzB,OAAO,MAAM;WACb,kBAAkB;WAClB,sBAAsB;WACtB,oBAAoB;WAEpB,MAAM;WAEN;EAET,IAAI,cAAc,mBAAmB;EAErC,SAAS,cAAc,QAAQ,WAAW;EAE1C,QAAQ;EAER,QAAQ;EAER,UAAU;EAGV,WAAW;;wBAGS,SAAS,UAAU,WACvC,YAAY,eAAe,MACzB,IAAa,SAAc,MAAW,YAAgB,kBACvD,QAAQ,SAAS"}
package/dist/testing.js CHANGED
@@ -1,30 +1,31 @@
1
- import { i as definePage, n as defineDot, r as Page } from "./service-dAFmZ_Mh.js";
1
+ import { a as cancelledError, i as definePage, n as defineDot, r as Page } from "./service-Dd4nvlhf.js";
2
2
  import { HostError, connectHost, createEvent, createHostRequest } from "@monimesl/dot-core";
3
3
  //#region src/testing.ts
4
- Object.assign(globalThis, {
5
- definePage,
6
- defineDot,
7
- createEvent
8
- });
9
- async function testPage(definition, { id = "test", options = {}, host = {} } = {}) {
4
+ const scope = globalThis;
5
+ scope.definePage = definePage;
6
+ scope.defineDot = defineDot;
7
+ scope.createEvent = createEvent;
8
+ async function testPage(definition, { id = "test", options = {}, host = {}, results = {} } = {}) {
10
9
  const patches = [];
11
10
  const navigations = [];
12
11
  const hostCalls = [];
13
- connectHost((method, params) => {
14
- hostCalls.push({
15
- method,
16
- params
17
- });
18
- const handler = host[method];
19
- if (!handler) {
20
- const error = new HostError("unsupported", `The test gives no host method "${method}"`);
21
- return createHostRequest(Promise.reject(error));
22
- }
23
- return createHostRequest(Promise.resolve().then(() => handler(params)));
24
- });
12
+ let result;
13
+ connectHost(fakeHostCaller(host, hostCalls));
25
14
  const page = new Page(1, id, definition, {
26
15
  commit: (_page, patch) => patches.push(patch),
16
+ showBars: () => {},
27
17
  navigate: (_page, navigation) => navigations.push(navigation),
18
+ pushForResult: (_page, navigation) => {
19
+ navigations.push(navigation);
20
+ return Promise.resolve(results[navigation.page]);
21
+ },
22
+ backWithResult: (_page, given) => {
23
+ result = given;
24
+ navigations.push({
25
+ action: "back",
26
+ delta: 1
27
+ });
28
+ },
28
29
  strict: true
29
30
  });
30
31
  const dispatch = async (name, event = createEvent("tap")) => {
@@ -36,6 +37,12 @@ async function testPage(definition, { id = "test", options = {}, host = {} } = {
36
37
  get state() {
37
38
  return page.state;
38
39
  },
40
+ get bars() {
41
+ return page.bars;
42
+ },
43
+ get result() {
44
+ return result;
45
+ },
39
46
  patches,
40
47
  navigations,
41
48
  hostCalls,
@@ -46,7 +53,27 @@ async function testPage(definition, { id = "test", options = {}, host = {} } = {
46
53
  unload: async () => {
47
54
  page.close();
48
55
  await page.lifecycle("onUnload");
56
+ },
57
+ refresh: () => page.lifecycle("onRefresh")
58
+ };
59
+ }
60
+ function fakeHostCaller(host, hostCalls) {
61
+ return (method, params) => {
62
+ hostCalls.push({
63
+ method,
64
+ params
65
+ });
66
+ const handler = host[method];
67
+ if (!handler) {
68
+ const error = new HostError("unsupported", `The test has no host method "${method}". Add it to testPage's \`host\` option`);
69
+ return createHostRequest(Promise.reject(error));
49
70
  }
71
+ let cancel = () => {};
72
+ const answer = new Promise((resolve, reject) => {
73
+ cancel = () => reject(cancelledError(method));
74
+ Promise.resolve().then(() => handler(params)).then(resolve, reject);
75
+ });
76
+ return createHostRequest(answer, () => cancel());
50
77
  };
51
78
  }
52
79
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","names":[],"sources":["../src/testing.ts"],"sourcesContent":["import type { DotEvent, Navigation, PageLaunchOptions, PageState } from '@monimesl/dot-core';\nimport { HostError, connectHost, createEvent, createHostRequest } from '@monimesl/dot-core';\n\nimport type { PageDefinition, State } from './page';\nimport { definePage, Page } from './page';\nimport { defineDot } from './service';\n\n// A dot's code calls these without importing them; the compiler adds the imports when it bundles.\n// Tests import that code directly, so this module provides them, and must be imported first.\nObject.assign(globalThis, { definePage, defineDot, createEvent });\n\n// Fake host methods by name, such as `'dash.ui.alert': () => ({ action: 'ok' })`.\nexport type TestHost = Readonly<Record<string, (params: never) => unknown>>;\n\nexport interface TestPageOptions {\n id?: string;\n options?: PageLaunchOptions;\n // Methods the page may call on the host; any other rejects with `unsupported`.\n host?: TestHost;\n}\n\nexport interface HostCallRecord {\n readonly method: string;\n readonly params: unknown;\n}\n\nexport interface PageTest<S extends PageState> {\n readonly state: State<S>;\n readonly patches: readonly PageState[];\n readonly navigations: readonly Navigation[];\n readonly hostCalls: readonly HostCallRecord[];\n\n tap(name: string, detail?: unknown): Promise<void>;\n\n dispatch(name: string, event?: DotEvent): Promise<void>;\n\n show(): Promise<void>;\n\n hide(): Promise<void>;\n\n unload(): Promise<void>;\n}\n\nexport async function testPage<S extends PageState>(\n definition: PageDefinition<S>,\n { id = 'test', options = {}, host = {} }: TestPageOptions = {},\n): Promise<PageTest<S>> {\n const patches: PageState[] = [];\n const navigations: Navigation[] = [];\n const hostCalls: HostCallRecord[] = [];\n connectHost((method, params) => {\n hostCalls.push({ method, params });\n const handler = host[method];\n if (!handler) {\n const error = new HostError('unsupported', `The test gives no host method \"${method}\"`);\n return createHostRequest(Promise.reject(error));\n }\n return createHostRequest(Promise.resolve().then(() => handler(params as never)));\n });\n const page = new Page<S>(1, id, definition, {\n commit: (_page, patch) => patches.push(patch),\n navigate: (_page, navigation) => navigations.push(navigation),\n strict: true,\n });\n const dispatch = async (name: string, event: DotEvent = createEvent('tap')) => {\n await page.dispatch(name, event);\n };\n\n await page.lifecycle('onLoad', options);\n await page.lifecycle('onShow');\n\n return {\n get state() {\n return page.state;\n },\n patches,\n navigations,\n hostCalls,\n tap: (name, detail = {}) => dispatch(name, createEvent('tap', detail)),\n dispatch,\n show: () => page.lifecycle('onShow'),\n hide: () => page.lifecycle('onHide'),\n unload: async () => {\n page.close();\n await page.lifecycle('onUnload');\n },\n };\n}\n"],"mappings":";;;AASA,OAAO,OAAO,YAAY;CAAE;CAAY;CAAW;AAAY,CAAC;AAkChE,eAAsB,SACpB,YACA,EAAE,KAAK,QAAQ,UAAU,CAAC,GAAG,OAAO,CAAC,MAAuB,CAAC,GACvC;CACtB,MAAM,UAAuB,CAAC;CAC9B,MAAM,cAA4B,CAAC;CACnC,MAAM,YAA8B,CAAC;CACrC,aAAa,QAAQ,WAAW;EAC9B,UAAU,KAAK;GAAE;GAAQ;EAAO,CAAC;EACjC,MAAM,UAAU,KAAK;EACrB,IAAI,CAAC,SAAS;GACZ,MAAM,QAAQ,IAAI,UAAU,eAAe,kCAAkC,OAAO,EAAE;GACtF,OAAO,kBAAkB,QAAQ,OAAO,KAAK,CAAC;EAChD;EACA,OAAO,kBAAkB,QAAQ,QAAQ,CAAC,CAAC,WAAW,QAAQ,MAAe,CAAC,CAAC;CACjF,CAAC;CACD,MAAM,OAAO,IAAI,KAAQ,GAAG,IAAI,YAAY;EAC1C,SAAS,OAAO,UAAU,QAAQ,KAAK,KAAK;EAC5C,WAAW,OAAO,eAAe,YAAY,KAAK,UAAU;EAC5D,QAAQ;CACV,CAAC;CACD,MAAM,WAAW,OAAO,MAAc,QAAkB,YAAY,KAAK,MAAM;EAC7E,MAAM,KAAK,SAAS,MAAM,KAAK;CACjC;CAEA,MAAM,KAAK,UAAU,UAAU,OAAO;CACtC,MAAM,KAAK,UAAU,QAAQ;CAE7B,OAAO;EACL,IAAI,QAAQ;GACV,OAAO,KAAK;EACd;EACA;EACA;EACA;EACA,MAAM,MAAM,SAAS,CAAC,MAAM,SAAS,MAAM,YAAY,OAAO,MAAM,CAAC;EACrE;EACA,YAAY,KAAK,UAAU,QAAQ;EACnC,YAAY,KAAK,UAAU,QAAQ;EACnC,QAAQ,YAAY;GAClB,KAAK,MAAM;GACX,MAAM,KAAK,UAAU,UAAU;EACjC;CACF;AACF"}
1
+ {"version":3,"file":"testing.js","names":[],"sources":["../src/testing.ts"],"sourcesContent":["import type {\n DotEvent,\n HostCaller,\n Navigation,\n PageBars,\n PageLaunchOptions,\n PageState,\n} from '@monimesl/dot-core';\nimport { HostError, connectHost, createEvent, createHostRequest } from '@monimesl/dot-core';\n\nimport type { PageDefinition, State } from './page';\nimport { definePage, Page } from './page';\nimport { defineDot } from './service';\nimport { cancelledError } from './utils';\n\n// A dot's code uses these without imports, and the compiler adds the imports when it bundles.\n// Tests load that code directly, so this module sets them up. Import it first.\nconst scope = globalThis as unknown as Record<string, unknown>;\nscope.definePage = definePage;\nscope.defineDot = defineDot;\nscope.createEvent = createEvent;\n\n// Fake host methods by name, such as `'dash.ui.alert': () => ({ action: 'ok' })`.\nexport type TestHost = Readonly<Record<string, (params: never) => unknown>>;\n\nexport interface TestPageOptions {\n id?: string;\n options?: PageLaunchOptions;\n // Host methods the page may call. Any other call rejects with `unsupported`.\n host?: TestHost;\n // What each page the test page opens with pushForResult gives back, by its id. A page not listed\n // gives undefined, as if the person went back.\n results?: Readonly<Record<string, unknown>>;\n}\n\nexport interface HostCallRecord {\n readonly method: string;\n readonly params: unknown;\n}\n\nexport interface PageTest<S extends PageState> {\n readonly state: State<S>;\n readonly patches: readonly PageState[];\n readonly navigations: readonly Navigation[];\n readonly hostCalls: readonly HostCallRecord[];\n // What Dash's bars show over the page now.\n readonly bars: PageBars;\n // What the page gave back with backWithResult, or undefined.\n readonly result: unknown;\n\n tap(name: string, detail?: unknown): Promise<void>;\n\n dispatch(name: string, event?: DotEvent): Promise<void>;\n\n show(): Promise<void>;\n\n hide(): Promise<void>;\n\n unload(): Promise<void>;\n\n // Runs the page's onRefresh, as when the person pulls it down.\n refresh(): Promise<void>;\n}\n\nexport async function testPage<S extends PageState>(\n definition: PageDefinition<S>,\n { id = 'test', options = {}, host = {}, results = {} }: TestPageOptions = {},\n): Promise<PageTest<S>> {\n const patches: PageState[] = [];\n const navigations: Navigation[] = [];\n const hostCalls: HostCallRecord[] = [];\n let result: unknown;\n connectHost(fakeHostCaller(host, hostCalls));\n const page = new Page<S>(1, id, definition, {\n commit: (_page, patch) => patches.push(patch),\n showBars: () => {},\n navigate: (_page, navigation) => navigations.push(navigation),\n pushForResult: (_page, navigation) => {\n navigations.push(navigation);\n return Promise.resolve(results[navigation.page]);\n },\n backWithResult: (_page, given) => {\n result = given;\n navigations.push({ action: 'back', delta: 1 });\n },\n strict: true,\n });\n const dispatch = async (name: string, event: DotEvent = createEvent('tap')) => {\n await page.dispatch(name, event);\n };\n\n await page.lifecycle('onLoad', options);\n await page.lifecycle('onShow');\n\n return {\n get state() {\n return page.state;\n },\n get bars() {\n return page.bars;\n },\n get result() {\n return result;\n },\n patches,\n navigations,\n hostCalls,\n tap: (name, detail = {}) => dispatch(name, createEvent('tap', detail)),\n dispatch,\n show: () => page.lifecycle('onShow'),\n hide: () => page.lifecycle('onHide'),\n unload: async () => {\n page.close();\n await page.lifecycle('onUnload');\n },\n refresh: () => page.lifecycle('onRefresh'),\n };\n}\n\n// Answers each call with the test's fake for that method.\nfunction fakeHostCaller(host: TestHost, hostCalls: HostCallRecord[]): HostCaller {\n return (method, params) => {\n hostCalls.push({ method, params });\n const handler = host[method];\n if (!handler) {\n const error = new HostError(\n 'unsupported',\n `The test has no host method \"${method}\". Add it to testPage's \\`host\\` option`,\n );\n return createHostRequest(Promise.reject(error));\n }\n // Cancelling rejects the call, as it does in a running dot.\n let cancel = () => {};\n const answer = new Promise<unknown>((resolve, reject) => {\n cancel = () => reject(cancelledError(method));\n Promise.resolve()\n .then(() => handler(params as never))\n .then(resolve, reject);\n });\n return createHostRequest(answer, () => cancel());\n };\n}\n"],"mappings":";;;AAiBA,MAAM,QAAQ;AACd,MAAM,aAAa;AACnB,MAAM,YAAY;AAClB,MAAM,cAAc;AA4CpB,eAAsB,SACpB,YACA,EAAE,KAAK,QAAQ,UAAU,CAAC,GAAG,OAAO,CAAC,GAAG,UAAU,CAAC,MAAuB,CAAC,GACrD;CACtB,MAAM,UAAuB,CAAC;CAC9B,MAAM,cAA4B,CAAC;CACnC,MAAM,YAA8B,CAAC;CACrC,IAAI;CACJ,YAAY,eAAe,MAAM,SAAS,CAAC;CAC3C,MAAM,OAAO,IAAI,KAAQ,GAAG,IAAI,YAAY;EAC1C,SAAS,OAAO,UAAU,QAAQ,KAAK,KAAK;EAC5C,gBAAgB,CAAC;EACjB,WAAW,OAAO,eAAe,YAAY,KAAK,UAAU;EAC5D,gBAAgB,OAAO,eAAe;GACpC,YAAY,KAAK,UAAU;GAC3B,OAAO,QAAQ,QAAQ,QAAQ,WAAW,KAAK;EACjD;EACA,iBAAiB,OAAO,UAAU;GAChC,SAAS;GACT,YAAY,KAAK;IAAE,QAAQ;IAAQ,OAAO;GAAE,CAAC;EAC/C;EACA,QAAQ;CACV,CAAC;CACD,MAAM,WAAW,OAAO,MAAc,QAAkB,YAAY,KAAK,MAAM;EAC7E,MAAM,KAAK,SAAS,MAAM,KAAK;CACjC;CAEA,MAAM,KAAK,UAAU,UAAU,OAAO;CACtC,MAAM,KAAK,UAAU,QAAQ;CAE7B,OAAO;EACL,IAAI,QAAQ;GACV,OAAO,KAAK;EACd;EACA,IAAI,OAAO;GACT,OAAO,KAAK;EACd;EACA,IAAI,SAAS;GACX,OAAO;EACT;EACA;EACA;EACA;EACA,MAAM,MAAM,SAAS,CAAC,MAAM,SAAS,MAAM,YAAY,OAAO,MAAM,CAAC;EACrE;EACA,YAAY,KAAK,UAAU,QAAQ;EACnC,YAAY,KAAK,UAAU,QAAQ;EACnC,QAAQ,YAAY;GAClB,KAAK,MAAM;GACX,MAAM,KAAK,UAAU,UAAU;EACjC;EACA,eAAe,KAAK,UAAU,WAAW;CAC3C;AACF;AAGA,SAAS,eAAe,MAAgB,WAAyC;CAC/E,QAAQ,QAAQ,WAAW;EACzB,UAAU,KAAK;GAAE;GAAQ;EAAO,CAAC;EACjC,MAAM,UAAU,KAAK;EACrB,IAAI,CAAC,SAAS;GACZ,MAAM,QAAQ,IAAI,UAChB,eACA,gCAAgC,OAAO,wCACzC;GACA,OAAO,kBAAkB,QAAQ,OAAO,KAAK,CAAC;EAChD;EAEA,IAAI,eAAe,CAAC;EACpB,MAAM,SAAS,IAAI,SAAkB,SAAS,WAAW;GACvD,eAAe,OAAO,eAAe,MAAM,CAAC;GAC5C,QAAQ,QAAQ,CAAC,CACd,WAAW,QAAQ,MAAe,CAAC,CAAC,CACpC,KAAK,SAAS,MAAM;EACzB,CAAC;EACD,OAAO,kBAAkB,cAAc,OAAO,CAAC;CACjD;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monimesl/dot-service",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.21",
4
4
  "description": "Dot logic layer: pages, state and plugins",
5
5
  "type": "module",
6
6
  "repository": {
@@ -30,7 +30,8 @@
30
30
  "main": "./dist/index.js",
31
31
  "types": "./dist/index.d.ts",
32
32
  "dependencies": {
33
- "@monimesl/dot-core": "0.1.0-alpha.2"
33
+ "@monimesl/dot-core": "0.1.0-alpha.21",
34
+ "@monimesl/dot-dash": "0.1.0-alpha.21"
34
35
  },
35
36
  "devDependencies": {
36
37
  "publint": "^0.3.24",
@@ -1 +0,0 @@
1
- {"version":3,"file":"page-BfYJ-G-D.d.ts","names":[],"sources":["../src/page.ts"],"mappings":";;QASQ;YAEI;;KAKA,gBAAgB,uCAExB,cAAc;UAED;EACf,KAAK,MAAM,QAAQ,UAAU;EAE7B,QAAQ,MAAM,QAAQ,UAAU;EAEhC,MAAM,MAAM,QAAQ,UAAU;EAE9B,KAAK;;KAGK,aAAa,KAAK,eAAc,6BACxC,IACA,+BACc,WAAW,IAAI,aAAa,EAAE,SAC1C;KAEM,MAAM,KAAK,aAAa;KAExB,aAAa,KAAK,QAAQ,MAAM;KAEhC,YAAY,KAAK,aAAa,OAAO,OAAO,MAAM,OAAO,aAAa;KAItE,SAAS;EACnB,SAAS,OAAO,MAAM,IAAI,OAAO,WAAW,aAAa;;KAG/C,OAAO;EACjB,OAAO,OAAO,MAAM,IAAI,OAAO,kBAAkB;;KAI9C,UAAU,KAAK,YAAW,iBAAiB,YAAY,iBAAgB;UAE3D,YAAY,UAAU,WAAW,IAAI;WAC3C;WACA;WACA,OAAO,MAAM;EAEtB,SAAS,QAAQ,YAAY;WAEpB,uBAAuB,WAAW,SAAS,OAAO,UAAU,GAAG;WAC/D,qBAAqB,WAAW,SAAS,OAAO,UAAU,GAAG,QAAQ;;UAG/D,cAAc,UAAU,WAAW,IAAI,YAAY,YAAY,GAAG,IAAI;WAC5E,YAAY;;UAGN,YAAY,UAAU,WAAW,IAAI;EACpD;EACA,cAAc;EACd,QAAQ,QAAQ,aAAa,SAAS,YAAY,GAAG,IAAI;EACzD,YAAY,KAAK;EACjB,UAAU,KAAK,SAAS,cAAc,GAAG,IAAI;;UAG9B,eAAe,UAAU,YAAY;WAC3C;WACA,cAAc;WACd,QAAQ,QAAQ;WAChB,YAAY,SAAS,eAAe,SAAS;WAC7C,UAAU,SAAS,eAAe,OAAO;;iBAGpC,WACd,UAAU,WACV,WAAW,eAAe,SAAS,KACnC,WAAW,eAAe,OAAO,KACjC,SAAS,YAAY,GAAG,IAAI,MAAM,eAAe;UAIlC;EACf,OAAO,MAAM,MAAM,OAAO;EAE1B,SAAS,MAAM,MAAM,YAAY;WAExB;;KAGN,UAAU,KAAK,eAAe,SAAS;KACvC,QAAQ,KAAK,eAAe,OAAO;cAI3B,KAAK,UAAU,YAAY;;WAC7B;WACA;WACA,SAAS,YAAY,GAAG,UAAU,IAAI,QAAQ;WAC9C,eAAe,cAAc,GAAG,UAAU,IAAI,QAAQ;EAOnD,YAAA,oBAAoB,YAAY,YAAY,eAAe,IAAI,MAAM;MA2B7E,SAAS,MAAM;EAInB;EAKA,SAAS,QAAQ,YAAY;EAqB7B,OAAO,cAAc,QAAQ;EAQvB,IAAI,cAAc,QAAQ,WAAW;EAS3C,SAAS,cAAc,OAAO,WAAW;EAanC,UAAU,gBAAgB,WAC9B,UAAU,MACP,MAAM,WAAW,UAAU,MAC7B;EASH"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"service-dAFmZ_Mh.js","names":[],"sources":["../src/host-calls.ts","../src/page.ts","../src/service.ts"],"sourcesContent":["import type {\n HostCallOptions,\n HostRequest,\n HostResult,\n LogicLayerMessages,\n} from '@monimesl/dot-core';\nimport { HostError, createHostRequest } from '@monimesl/dot-core';\n\nimport type { ServiceChannel } from './service';\n\ninterface WaitingCall {\n readonly method: string;\n resolve(value: unknown): void;\n reject(error: HostError): void;\n}\n\n// Calls to the host that are waiting for their `hostResult`.\nexport class HostCalls {\n readonly #channel: ServiceChannel;\n readonly #waiting = new Map<number, WaitingCall>();\n #nextId = 1;\n\n constructor(channel: ServiceChannel) {\n this.#channel = channel;\n channel.handle('hostResult', (result) => this.#settle(result));\n }\n\n call(method: string, params: unknown, options: HostCallOptions): HostRequest<unknown> {\n const id = this.#nextId++;\n const promise = new Promise<unknown>((resolve, reject) => {\n this.#waiting.set(id, { method, resolve, reject });\n });\n const message: LogicLayerMessages['hostCall'] = { id, method, params };\n if (options.timeout !== undefined) {\n message.timeout = options.timeout;\n }\n this.#channel.send('hostCall', message);\n return createHostRequest(promise, () => this.#cancel(id));\n }\n\n #cancel(id: number): void {\n const call = this.#waiting.get(id);\n if (!call) {\n return;\n }\n this.#waiting.delete(id);\n this.#channel.send('hostCancel', { id });\n call.reject(new HostError('cancelled', `\"${call.method}\" was cancelled`));\n }\n\n #settle(result: HostResult): void {\n const call = this.#waiting.get(result.id);\n // A call cancelled here may still get the host's late answer, which no one wants any more.\n if (!call) {\n return;\n }\n this.#waiting.delete(result.id);\n if ('error' in result) {\n call.reject(new HostError(result.error.code, result.error.message));\n } else {\n call.resolve(result.value);\n }\n }\n}\n","import type {\n DotEvent,\n Navigation,\n PageHooks,\n PageLaunchOptions,\n PageState,\n} from '@monimesl/dot-core';\nimport { applyChanges } from '@monimesl/dot-core';\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface DotPageIds {}\n}\n\n// Any string until `.dot/pages.d.ts` lists the dot's pages; the brackets stop TypeScript from\n// applying the condition to each id separately.\nexport type PageId = [keyof DotPageIds] extends [never]\n ? string\n : Extract<keyof DotPageIds, string>;\n\nexport interface PageNavigation {\n push(page: PageId, options?: PageLaunchOptions): void;\n\n replace(page: PageId, options?: PageLaunchOptions): void;\n\n reset(page: PageId, options?: PageLaunchOptions): void;\n\n back(delta?: number): void;\n}\n\nexport type DeepReadonly<T> = T extends (...args: never[]) => unknown\n ? T\n : T extends object\n ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n : T;\n\nexport type State<S> = DeepReadonly<S>;\n\nexport type StateChanges<S> = Partial<State<S>>;\n\nexport type StateUpdate<S> = StateChanges<S> | ((state: State<S>) => StateChanges<S> | undefined);\n\n// Declared as methods, whose parameters TypeScript checks loosely, so a handler may narrow its\n// event, e.g. `(state, event: DotEvent<{ size: number }>) => ...`.\nexport type Mutation<S> = {\n mutation(state: State<S>, event: DotEvent): StateChanges<S> | undefined;\n}['mutation'];\n\nexport type Action<S> = {\n action(state: State<S>, event: DotEvent): void | Promise<void>;\n}['action'];\n\n// A handler's parameters after `state`: what calling it from another handler takes.\ntype EventArgs<F> = F extends (state: never, ...rest: infer R) => unknown ? R : never;\n\nexport interface PageContext<S extends PageState, Mu, Ac> {\n readonly id: string;\n readonly instanceId: number;\n readonly state: State<S>;\n\n setState(update: StateUpdate<S>): void;\n\n readonly mutations: { readonly [K in keyof Mu]: (...event: EventArgs<Mu[K]>) => void };\n readonly actions: { readonly [K in keyof Ac]: (...event: EventArgs<Ac[K]>) => Promise<void> };\n}\n\nexport interface ActionContext<S extends PageState, Mu, Ac> extends PageContext<S, Mu, Ac> {\n readonly navigation: PageNavigation;\n}\n\nexport interface PageOptions<S extends PageState, Mu, Ac> {\n id?: string;\n initialState: S;\n hooks?: Partial<PageHooks> & ThisType<PageContext<S, Mu, Ac>>;\n mutations?: Mu & ThisType<void>;\n actions?: Ac & ThisType<ActionContext<S, Mu, Ac>>;\n}\n\nexport interface PageDefinition<S extends PageState = PageState> {\n readonly id?: string;\n readonly initialState: S;\n readonly hooks?: Partial<PageHooks>;\n readonly mutations?: Readonly<Record<string, Mutation<S>>>;\n readonly actions?: Readonly<Record<string, Action<S>>>;\n}\n\nexport function definePage<\n S extends PageState,\n Mu extends Record<string, Mutation<S>>,\n Ac extends Record<string, Action<S>>,\n>(options: PageOptions<S, Mu, Ac>): PageDefinition<S> {\n return options;\n}\n\nexport interface PageHost {\n commit(page: Page, patch: PageState): void;\n\n navigate(page: Page, navigation: Navigation): void;\n\n readonly strict?: boolean;\n}\n\ntype Mutations<S> = Record<string, Mutation<S>>;\ntype Actions<S> = Record<string, Action<S>>;\ntype MutationCalls<S extends PageState> = PageContext<S, Mutations<S>, Actions<S>>['mutations'];\ntype ActionCalls<S extends PageState> = PageContext<S, Mutations<S>, Actions<S>>['actions'];\n\nexport class Page<S extends PageState = PageState> {\n readonly id: string;\n readonly instanceId: number;\n readonly context: PageContext<S, Mutations<S>, Actions<S>>;\n readonly actionContext: ActionContext<S, Mutations<S>, Actions<S>>;\n readonly #definition: PageDefinition<S>;\n readonly #host: PageHost;\n #state: State<S>;\n #closed = false;\n #previousHook: Promise<unknown> = Promise.resolve();\n\n constructor(instanceId: number, id: string, definition: PageDefinition<S>, host: PageHost) {\n this.id = id;\n this.instanceId = instanceId;\n this.#definition = definition;\n this.#host = host;\n this.#state = this.#freeze(JSON.parse(JSON.stringify(definition.initialState)) as State<S>);\n\n const { mutations = {}, actions = {} } = definition;\n const clash = Object.keys(mutations).find((name) => name in actions);\n if (clash) {\n throw new Error(`Page \"${id}\": \"${clash}\" is both a mutation and an action`);\n }\n\n const mutationCalls = mapNames(\n mutations,\n (name) => (event?: DotEvent) => this.mutate(name, event),\n );\n const actionCalls = mapNames(actions, (name) => (event?: DotEvent) => this.act(name, event));\n this.context = new HookContext(this, mutationCalls, actionCalls);\n this.actionContext = new HandlerContext(\n this,\n mutationCalls,\n actionCalls,\n this.#createNavigation(),\n );\n }\n\n get state(): State<S> {\n return this.#state;\n }\n\n handlers(): string[] {\n const { mutations = {}, actions = {} } = this.#definition;\n return [...Object.keys(mutations), ...Object.keys(actions)];\n }\n\n setState(update: StateUpdate<S>): void {\n if (this.#closed) {\n return;\n }\n const changes = typeof update === 'function' ? update(this.#state) : update;\n if (changes === undefined) {\n return;\n }\n const next = applyChanges(this.#state, changes);\n const patch: PageState = {};\n for (const key of Object.keys(next) as (keyof State<S> & string)[]) {\n if (next[key] !== this.#state[key]) {\n patch[key] = next[key];\n }\n }\n this.#state = this.#freeze(next);\n if (Object.keys(patch).length > 0) {\n this.#host.commit(this as Page, patch);\n }\n }\n\n mutate(name: string, event?: DotEvent): void {\n const mutation = this.#definition.mutations?.[name];\n if (!mutation) {\n throw new Error(`Page \"${this.id}\" has no mutation \"${name}\"`);\n }\n this.#runMutation(mutation, event);\n }\n\n async act(name: string, event?: DotEvent): Promise<void> {\n const action = this.#definition.actions?.[name];\n if (!action) {\n throw new Error(`Page \"${this.id}\" has no action \"${name}\"`);\n }\n await this.#runAction(action, event);\n }\n\n // Runs the mutation or action named by a view event. A mutation is done when this returns.\n dispatch(name: string, event: DotEvent): Promise<void> {\n const action = this.#definition.actions?.[name];\n if (action) {\n return this.#runAction(action, event);\n }\n const mutation = this.#definition.mutations?.[name];\n if (!mutation) {\n throw new Error(`Page \"${this.id}\" has no mutation or action \"${name}\"`);\n }\n this.#runMutation(mutation, event);\n return Promise.resolve();\n }\n\n async lifecycle<K extends keyof PageHooks>(\n hookName: K,\n ...args: Parameters<PageHooks[K]>\n ): Promise<void> {\n const hook = this.#definition.hooks?.[hookName] as\n ((...hookArgs: Parameters<PageHooks[K]>) => unknown) | undefined;\n // Hooks run one at a time, in call order; a failed hook does not block the next one.\n const run = this.#previousHook.then(() => hook?.call(this.context, ...args));\n this.#previousHook = run.catch(() => {});\n await run;\n }\n\n close(): void {\n this.#closed = true;\n }\n\n // Handlers called from other handlers may leave out the event, so it can be undefined here.\n #runMutation(mutation: Mutation<S>, event: DotEvent | undefined): void {\n this.setState((state) => mutation(state, event as DotEvent));\n }\n\n async #runAction(action: Action<S>, event: DotEvent | undefined): Promise<void> {\n await action.call(this.actionContext, this.#state, event as DotEvent);\n }\n\n #createNavigation(): PageNavigation {\n return {\n push: (page, options = {}) => this.#navigate({ action: 'push', page, options }),\n replace: (page, options = {}) => this.#navigate({ action: 'replace', page, options }),\n reset: (page, options = {}) => this.#navigate({ action: 'reset', page, options }),\n back: (delta = 1) => this.#navigate({ action: 'back', delta }),\n };\n }\n\n #navigate(navigation: Navigation): void {\n if (!this.#closed) {\n this.#host.navigate(this as Page, navigation);\n }\n }\n\n #freeze(state: State<S>): State<S> {\n return this.#host.strict ? deepFreeze(state) : state;\n }\n}\n\n// What hooks get as `this`. `setState` is bound, so a hook may destructure it.\nclass HookContext<S extends PageState> implements PageContext<S, Mutations<S>, Actions<S>> {\n readonly id: string;\n readonly instanceId: number;\n readonly mutations: MutationCalls<S>;\n readonly actions: ActionCalls<S>;\n readonly #page: Page<S>;\n\n constructor(page: Page<S>, mutations: MutationCalls<S>, actions: ActionCalls<S>) {\n this.id = page.id;\n this.instanceId = page.instanceId;\n this.mutations = mutations;\n this.actions = actions;\n this.#page = page;\n }\n\n get state(): State<S> {\n return this.#page.state;\n }\n\n readonly setState = (update: StateUpdate<S>): void => {\n this.#page.setState(update);\n };\n}\n\n// What actions get as `this`: the hook context plus navigation.\nclass HandlerContext<S extends PageState>\n extends HookContext<S>\n implements ActionContext<S, Mutations<S>, Actions<S>>\n{\n readonly navigation: PageNavigation;\n\n constructor(\n page: Page<S>,\n mutations: MutationCalls<S>,\n actions: ActionCalls<S>,\n navigation: PageNavigation,\n ) {\n super(page, mutations, actions);\n this.navigation = navigation;\n }\n}\n\nfunction mapNames<T>(\n record: Readonly<Record<string, unknown>>,\n create: (name: string) => T,\n): Record<string, T> {\n return Object.fromEntries(Object.keys(record).map((name) => [name, create(name)]));\n}\n\nfunction deepFreeze<T>(value: T): T {\n if (typeof value === 'object' && value !== null && !Object.isFrozen(value)) {\n Object.freeze(value);\n Object.values(value).forEach(deepFreeze);\n }\n return value;\n}\n","import type {\n DotHooks,\n DotEvent,\n Envelope,\n HostLayerMessages,\n LogicLayerMessages,\n Navigation,\n PageLaunchOptions,\n PageState,\n SystemContext,\n Theme,\n Transport,\n UILayerMessages,\n} from '@monimesl/dot-core';\nimport {\n DEFAULT_SYSTEM_CONTEXT,\n connectHost,\n EventEmitter,\n MAX_STACK_DEPTH,\n MessageChannel,\n} from '@monimesl/dot-core';\n\nimport { HostCalls } from './host-calls';\nimport type { PageDefinition } from './page';\nimport { Page } from './page';\n\nexport type ServiceChannel = MessageChannel<\n LogicLayerMessages,\n UILayerMessages & HostLayerMessages\n>;\n\nexport interface ServiceHooks {\n launch(): void;\n\n pageLoad(page: Page): void;\n\n pageUnload(page: Page): void;\n\n pageShow(page: Page): void;\n\n pageHide(page: Page): void;\n\n // A page asked to navigate; the request is on its way to the view layer.\n navigate(page: Page, navigation: Navigation): void;\n\n // Before a handler runs\n event(page: Page, handler: string, event: DotEvent): void;\n\n // After the state changes, with the changed keys, before they are sent to the view layer\n setState(page: Page, patch: PageState): void;\n\n // The instance is known when the error came from one page.\n error(error: unknown, instanceId: number | undefined): void;\n}\n\nexport interface ServicePlugin {\n readonly name: string;\n\n setup(service: Service): void;\n}\n\nexport interface DotContext {\n readonly theme: Theme;\n readonly systemContext: SystemContext;\n\n setTheme(theme: Theme): void;\n}\n\nexport interface DotDefinition {\n readonly theme?: Theme;\n readonly plugins?: readonly ServicePlugin[];\n readonly pages: readonly [string, ...string[]];\n readonly hooks?: Partial<DotHooks> & ThisType<DotContext>;\n}\n\nexport function defineDot(dot: DotDefinition): DotDefinition {\n return dot;\n}\n\nexport interface ServiceOptions {\n transport: Transport;\n dot?: Partial<DotDefinition>;\n pages: Record<string, PageDefinition>;\n strict?: boolean;\n}\n\n// The logic layer: owns every page's state, runs handlers, and sends state changes to the view layer.\nexport class Service implements DotContext {\n readonly channel: ServiceChannel;\n readonly hooks = new EventEmitter<ServiceHooks>();\n readonly #definitions: Record<string, PageDefinition>;\n readonly #pages = new Map<number, Page>();\n readonly #dot: Partial<DotDefinition>;\n readonly #strict: boolean;\n #theme: Theme;\n #systemContext: SystemContext = DEFAULT_SYSTEM_CONTEXT;\n #stack: HostLayerMessages['syncStack'] = { pages: [], limit: MAX_STACK_DEPTH };\n readonly #hostCalls: HostCalls;\n\n constructor({ transport, pages, dot = {}, strict = false }: ServiceOptions) {\n this.channel = new MessageChannel(transport);\n this.#hostCalls = new HostCalls(this.channel);\n this.#definitions = pages;\n this.#dot = dot;\n this.#strict = strict;\n this.#theme = dot.theme ?? {};\n\n this.channel.handle('connect', () => this.channel.send('setTheme', { theme: this.#theme }));\n this.channel.handle('syncStack', (stack) => {\n this.#stack = stack;\n });\n this.channel.handle('syncSystemContext', (context) => {\n this.#systemContext = context;\n });\n this.channel.handle('load', ({ instanceId, page, options }) =>\n this.loadPage(instanceId, page, options),\n );\n this.channel.handle('unload', ({ instanceId }) => this.unloadPage(instanceId));\n this.channel.handle('show', ({ instanceId }) => {\n const page = this.getPage(instanceId);\n this.hooks.emit('pageShow', page);\n this.reportError(page.lifecycle('onShow'), instanceId);\n });\n this.channel.handle('hide', ({ instanceId }) => {\n const page = this.getPage(instanceId);\n this.hooks.emit('pageHide', page);\n this.reportError(page.lifecycle('onHide'), instanceId);\n });\n this.channel.handle('event', ({ instanceId, handler, event }) => {\n const page = this.getPage(instanceId);\n this.hooks.emit('event', page, handler, event);\n this.reportError(page.dispatch(handler, event), instanceId);\n });\n\n dot.plugins?.forEach((plugin) => this.use(plugin));\n }\n\n use(plugin: ServicePlugin): this {\n plugin.setup(this);\n return this;\n }\n\n start(): () => void {\n const stopListening = this.channel.listen((error, message) => {\n this.#reportError(error, instanceIdOf(message));\n });\n // Host calls, such as `dash.ui.alert`, now go through this service's channel.\n connectHost((method, params, options) => this.#hostCalls.call(method, params, options));\n this.hooks.emit('launch');\n this.reportError(this.#runLaunchHook());\n return () => {\n stopListening();\n connectHost(null);\n };\n }\n\n get theme(): Theme {\n return this.#theme;\n }\n\n get systemContext(): SystemContext {\n return this.#systemContext;\n }\n\n setTheme(theme: Theme): void {\n this.#theme = theme;\n this.channel.send('setTheme', { theme });\n }\n\n getPage(instanceId: number): Page {\n const page = this.#pages.get(instanceId);\n if (!page) {\n throw new Error(`Page instance ${instanceId} is not loaded`);\n }\n return page;\n }\n\n loadPage(instanceId: number, id: string, options: PageLaunchOptions = {}): Page {\n const page = new Page(instanceId, id, this.#definition(id), {\n commit: (page, patch) => {\n this.hooks.emit('setState', page, patch);\n this.channel.send('setPageState', { instanceId: page.instanceId, patchState: patch });\n },\n navigate: (page, navigation) => {\n if (navigation.action !== 'back') {\n this.#definition(navigation.page);\n }\n const { pages, limit } = this.#stack;\n if (navigation.action === 'push' && pages.length >= limit) {\n throw new Error(\n `Cannot open \"${navigation.page}\": ${limit} pages are already open. Use replace or back.`,\n );\n }\n this.hooks.emit('navigate', page, navigation);\n this.channel.send('navigate', { ...navigation, from: page.instanceId });\n },\n strict: this.#strict,\n });\n this.#pages.set(instanceId, page);\n this.channel.send('initPageState', { instanceId, state: page.state });\n this.hooks.emit('pageLoad', page);\n this.reportError(page.lifecycle('onLoad', options), instanceId);\n return page;\n }\n\n unloadPage(instanceId: number): void {\n const page = this.#pages.get(instanceId);\n if (!page) {\n return;\n }\n this.#pages.delete(instanceId);\n page.close();\n this.reportError(page.lifecycle('onUnload'), instanceId);\n this.hooks.emit('pageUnload', page);\n }\n\n #definition(id: string): PageDefinition {\n const definition = this.#definitions[id];\n if (!definition) {\n throw new Error(`Unknown page \"${id}\"`);\n }\n return definition;\n }\n\n reportError(work: Promise<void>, instanceId?: number): void {\n work.catch((error: unknown) => {\n this.#reportError(error, instanceId);\n });\n }\n\n async #runLaunchHook(): Promise<void> {\n await this.#dot.hooks?.onLaunch?.call(this);\n }\n\n #reportError(error: unknown, instanceId: number | undefined): void {\n this.hooks.emit('error', error, instanceId);\n const reason = error instanceof Error ? error : new Error(String(error));\n this.#dot.hooks?.onError?.call(this, reason);\n const report: LogicLayerMessages['error'] = { message: reason.message };\n if (instanceId !== undefined) {\n report.instanceId = instanceId;\n }\n this.channel.send('error', report);\n }\n}\n\n// The page instance a channel message was for, if it names one.\nfunction instanceIdOf(message: Envelope): number | undefined {\n const payload = message.payload as { instanceId?: unknown } | undefined;\n if (typeof payload?.instanceId === 'number') {\n return payload.instanceId;\n }\n return undefined;\n}\n"],"mappings":"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"}