@lingxia/types 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -171,6 +171,14 @@ export interface PageInstance<TData extends object = Record<string, unknown>> {
171
171
  setDataPath(path: string, value: JsonValue | undefined): void;
172
172
  /** Drain pending state writes; an attached View acknowledges application, not paint. Rejects on unload. */
173
173
  flush(): Promise<void>;
174
+ /**
175
+ * Aborted as the page unloads, before `onUnload` runs, however the page
176
+ * leaves. Pass it to work the page starts — `fetch(url, { signal:
177
+ * this.signal })` — so that work stops with the page, rejecting with an
178
+ * `AbortError`, instead of resolving into a page that is gone. When the
179
+ * whole lxapp shuts down, its Logic ends with it and the signal does not fire.
180
+ */
181
+ readonly signal: AbortSignal;
174
182
  }
175
183
 
176
184
  /**
@@ -1138,8 +1146,11 @@ export type HostAppApi = globalThis.HostAppApi;
1138
1146
  * Build-time deployment environment of the host app (`dev` | `prod`).
1139
1147
  * Surfaced via {@link HostAppApi.env}. Taken from the `env` field in
1140
1148
  * the generated `app.json`. Missing `env` is treated as `'prod'`.
1141
- * This is the host build axis: which server, package-id suffix, publish
1142
- * token, and self-update endpoint the host uses. It is **not** the
1149
+ * This is the immutable host build axis: package-id suffix, publish
1150
+ * token, signed App Link entitlements, and self-update channel.
1151
+ * In-app App Link hosts follow {@link HostAppApi.getServiceEnv} on
1152
+ * the next launch. The mutable service environment is
1153
+ * {@link HostAppApi.toggleServiceEnv}. It is **not** the
1143
1154
  * lxapp publish channel (`LxAppEnvVersion` / `LxAppReleaseType`:
1144
1155
  * `'release' | 'draft'`). Default channel is derived
1145
1156
  * from env (`dev` → `draft`, `prod` → `release`) and can be
@@ -2757,7 +2768,7 @@ export type WindowsTerminalInlineImageStatus = {
2757
2768
  export interface AppBaseInfo {
2758
2769
  /**
2759
2770
  * Platform family: `"iOS"` / `"macOS"` / `"Android"` / `"Windows"` /
2760
- * `"Harmony"`. Matches the View-side `usePlatform().os` value.
2771
+ * `"Harmony"`. Matches the View-side `useLxHost().os` value.
2761
2772
  */
2762
2773
  os: HostOs;
2763
2774
  productName: string;
@@ -2784,6 +2795,23 @@ export interface FileStats {
2784
2795
  createTime?: number;
2785
2796
  }
2786
2797
 
2798
+ export interface HostServiceEnvState {
2799
+ buildEnv: HostAppEnv;
2800
+ /** Environment this process is running; unchanged until the app restarts. */
2801
+ serviceEnv: HostAppEnv;
2802
+ /** Environment that will be used after closing and reopening the app. */
2803
+ nextLaunchEnv: HostAppEnv;
2804
+ available: HostAppEnv[];
2805
+ canToggle: boolean;
2806
+ }
2807
+
2808
+ export interface HostServiceEnvSwitchResult {
2809
+ state: HostServiceEnvState;
2810
+ exitRequested: boolean;
2811
+ /** If present, the environment was saved; close and reopen the app manually. */
2812
+ exitError?: string;
2813
+ }
2814
+
2787
2815
  export interface ImageInfo {
2788
2816
  width: number;
2789
2817
  height: number;
@@ -2963,6 +2991,15 @@ declare global {
2963
2991
  * restricted to the Control app, like the other host-level APIs on `lx.host`.
2964
2992
  */
2965
2993
  screenshot(options?: AppScreenshotOptions): Promise<AppScreenshotResult>;
2994
+ /** Query the running and next-launch service environments. Control app only. */
2995
+ getServiceEnv(): HostServiceEnvState;
2996
+ /**
2997
+ * Save the other service environment for next launch and request exit.
2998
+ * A dev build cannot switch. Control app only. Save failures throw before exit.
2999
+ * If exitRequested is false, ask the user to close and reopen the app manually.
3000
+ * Retrying before restart keeps the same target environment.
3001
+ */
3002
+ toggleServiceEnv(): HostServiceEnvSwitchResult;
2966
3003
  /**
2967
3004
  * Query whether the host app has an update.
2968
3005
  * This host-level capability is restricted to the Control app. A successful
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  export * from './generated/logic.js';
8
8
  export type { Automation } from './automation/index.js';
9
+ export type { MockAnswer, MockContext, MockHandler, MockRequest, Mocks } from './mocks.js';
9
10
  export * from './error.js';
10
11
  export * from './generated/error.js';
11
12
  export * from './generated/i18n.js';
@@ -46,11 +47,16 @@ declare global {
46
47
 
47
48
  interface Lx {
48
49
  /**
49
- * In-process UI/runtime automation.
50
+ * In-process UI/runtime automation, as app Logic may use it.
50
51
  *
51
- * Select the current app with `.lxapp()` or a specific running app with
52
- * `.lxapp(appid)`. Host-only surfaces enforce the `host` privilege when
53
- * selected; `lingxia dev` and the Runner grant it implicitly.
52
+ * Select the calling app with `.lxapp()` (requires the `automation`
53
+ * privilege) or another running app with `.lxapp(appid)`; the host tiers
54
+ * (`lxapps`, `browser`, `shell`, `device`, `desktop`, `terminal`) require
55
+ * `host`, checked when the property is read. A privilege holds only when
56
+ * it is allowed for the app and the native host sealed a session grant
57
+ * for it; `lingxia dev` and the Runner do not widen that. Test-run-only
58
+ * members (`network`, nav `waitUntil: 'ready'`) belong to
59
+ * `HostRunAutomation`, the root of an `lxdev test` program.
54
60
  */
55
61
  automation(): Automation;
56
62
  }
package/src/mocks.ts ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Types for an lxapp's `mocks/index.ts`: the complete set of mock
3
+ * handlers, one per call the app makes, answered in development when the
4
+ * mock selection says so (`mocks/config.json`, `lingxia dev --mock`,
5
+ * `lxdev mock`). Import them type-only; mocks never ship:
6
+ *
7
+ * ```ts
8
+ * import type { Mocks } from '@lingxia/types/mocks';
9
+ *
10
+ * let signedIn = true; // module state: fresh on save, reset, restart and each spec
11
+ *
12
+ * export default {
13
+ * 'GET **\/devices': () => (signedIn ? { json: DEVICES } : { status: 401 }),
14
+ * 'DELETE **\/sessions/current': () => { signedIn = false; return { status: 204 }; },
15
+ * 'GET **\/legal': { json: [] },
16
+ * } satisfies Mocks;
17
+ * ```
18
+ */
19
+ import type { NetworkRouteAnswer } from './automation/index.js';
20
+
21
+ /**
22
+ * What a handler answers: exactly a route answer — `{ status?, headers?,
23
+ * json | body, contentType?, delay? }`, `{ abort: 'failed' }`,
24
+ * `{ hang: true }`, `{ sse: [...] }`, or `{ continue: true, patchJson? }`
25
+ * (the real backend answers). One answer per call: `sequence`, `times`,
26
+ * `match` and `bodyBase64` are scenario fields.
27
+ */
28
+ export type MockAnswer = NetworkRouteAnswer;
29
+
30
+ /** The call a handler answers. */
31
+ export type MockRequest = {
32
+ /** Fetch cancellation when supplied; null otherwise (including SSE). */
33
+ signal: AbortSignal | null;
34
+ /** Upper-case. */
35
+ method: string;
36
+ url: URL;
37
+ /** What the app sent. */
38
+ headers: Headers;
39
+ /** The body as text; rejects for a stream, `Blob` or `FormData` body. */
40
+ text(): Promise<string>;
41
+ json<T = unknown>(): Promise<T>;
42
+ };
43
+
44
+ /** What a handler can use besides the request. */
45
+ export type MockContext = {
46
+ /** The original `fetch`, not intercepted: proxy without recursing. */
47
+ fetch: typeof fetch;
48
+ };
49
+
50
+ /**
51
+ * Answers one call. Throwing, returning `undefined` or an invalid answer
52
+ * fails the request (`TypeError: fetch failed`); it never falls through to
53
+ * the real backend.
54
+ */
55
+ export type MockHandler = (req: MockRequest, ctx: MockContext) => MockAnswer | Promise<MockAnswer>;
56
+
57
+ /**
58
+ * The default export of `mocks/index.ts`: keys are HTTP targets
59
+ * (`'GET **\/devices/*'`, `'* **\/x'`, `'POST /regex/'`), tried in order;
60
+ * values are answers or handlers.
61
+ */
62
+ export type Mocks = Record<string, MockAnswer | MockHandler>;
package/src/page.ts ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `T` where every value survives the JSON transfer `setData` makes to the
3
+ * View; a function, Date, Map, Set, promise or class instance becomes
4
+ * `never`, so a contract holding one does not compile. `undefined` members
5
+ * are allowed: the transfer drops them, as an optional member reads.
6
+ */
7
+ export type JsonData<T> =
8
+ 0 extends 1 & T ? any
9
+ : unknown extends T ? unknown
10
+ : T extends string | number | boolean | null | undefined ? T
11
+ : T extends (...args: never[]) => unknown ? never
12
+ : T extends Date | RegExp | Map<unknown, unknown> | Set<unknown> | PromiseLike<unknown> | symbol | bigint ? never
13
+ : T extends readonly unknown[] ? { [K in keyof T]: JsonData<T[K]> }
14
+ : T extends object ? { [K in keyof T]: JsonData<T[K]> }
15
+ : never;
16
+
17
+ /**
18
+ * A shared, type-only page contract: the page's `data` and public actions,
19
+ * written once and used by Logic, View and tests. Importing it installs no
20
+ * Logic globals. `Data` must be JSON (see `JsonData`); otherwise the
21
+ * contract has no `data`, and every use of it names the problem.
22
+ */
23
+ export type PageContract<Data = unknown, Actions = unknown> =
24
+ [Data] extends [JsonData<Data>]
25
+ ? { readonly data: Data; readonly actions: Actions }
26
+ : { readonly __pageDataMustBeJson: "PageContract data must be JSON: no functions, Dates, Maps, Sets or class instances" };
@@ -2,6 +2,8 @@ import type {
2
2
  Automation,
3
3
  BrowserCookies,
4
4
  BrowserDriver,
5
+ ClockDriver,
6
+ DialogDriver,
5
7
  DesktopApp,
6
8
  DesktopAx,
7
9
  DesktopClipboard,
@@ -14,8 +16,13 @@ import type {
14
16
  DeviceDriver,
15
17
  LxAppDriver,
16
18
  LxAppManager,
19
+ MockDriver,
17
20
  NavDriver,
21
+ NetworkDriver,
22
+ NetworkRoute,
23
+ Scenario,
18
24
  PageDriver,
25
+ ProfileDriver,
19
26
  PageKey,
20
27
  PagePointer,
21
28
  ShellDriver,
@@ -144,9 +151,11 @@ const HOST_APP_API = [
144
151
  'env',
145
152
  'exit',
146
153
  'getBaseInfo',
154
+ 'getServiceEnv',
147
155
  'notification',
148
156
  'screenshot',
149
157
  'setBadge',
158
+ 'toggleServiceEnv',
150
159
  ] as const;
151
160
  // `autostart`, `banner`, `control`, `cache`, and `notification` are injected
152
161
  // only where they apply, so a runtime walk of `lx.host` must not require them.
@@ -239,7 +248,24 @@ const AUTOMATION_API = [
239
248
  ] as const;
240
249
  const SHELL_DRIVER_API = ['pins', 'setPin', 'reorderPins'] as const;
241
250
  const TERMINAL_DRIVER_API = ['input', 'newTab', 'setMaximized', 'snapshot', 'split'] as const;
242
- const LXAPP_DRIVER_API = ['eval', 'info', 'nav', 'page', 'pages', 'surfaceLayout'] as const;
251
+ const LXAPP_DRIVER_API = ['clock', 'dialogs', 'eval', 'info', 'mock', 'nav', 'network', 'page', 'pages', 'profile', 'surfaceLayout'] as const;
252
+ const MOCK_DRIVER_API = ['reset', 'use'] as const;
253
+ const CLOCK_DRIVER_API = ['install', 'runAll', 'setSystemTime', 'tick', 'uninstall'] as const;
254
+ const DIALOG_DRIVER_API = [
255
+ 'actionSheets',
256
+ 'answerNextActionSheet',
257
+ 'answerNextModal',
258
+ 'modals',
259
+ 'setAnswerMode',
260
+ 'toasts',
261
+ 'unanswered',
262
+ 'unwatch',
263
+ 'watch',
264
+ ] as const;
265
+ const PROFILE_DRIVER_API = ['checkpoint', 'drop', 'restore'] as const;
266
+ const NETWORK_DRIVER_API = ['captureResponses', 'requests', 'responses', 'route', 'unrouteAll'] as const;
267
+ const NETWORK_ROUTE_API = ['id', 'pattern', 'requests', 'requestsAfter', 'unroute'] as const;
268
+ const SCENARIO_API = ['calls', 'callsAfter', 'name', 'rules', 'unroute', 'variant'] as const;
243
269
  const LXAPP_MANAGER_API = [
244
270
  'applink',
245
271
  'close',
@@ -252,6 +278,7 @@ const LXAPP_MANAGER_API = [
252
278
  'windows',
253
279
  ] as const;
254
280
  const PAGE_DRIVER_API = [
281
+ 'action',
255
282
  'click',
256
283
  'eval',
257
284
  'fill',
@@ -542,8 +569,9 @@ export const LX_RUNTIME_SURFACES = [
542
569
  layer: 'automation',
543
570
  expression: 'lx.automation().lxapp()',
544
571
  members: LXAPP_DRIVER_API,
545
- properties: ['nav', 'page'],
572
+ properties: ['clock', 'dialogs', 'mock', 'nav', 'network', 'page', 'profile'],
546
573
  },
574
+ { name: 'MockDriver', layer: 'automation', expression: 'lx.automation().lxapp().mock', members: MOCK_DRIVER_API },
547
575
  {
548
576
  name: 'PageDriver',
549
577
  layer: 'automation',
@@ -554,6 +582,44 @@ export const LX_RUNTIME_SURFACES = [
554
582
  { name: 'PagePointer', layer: 'automation', expression: 'lx.automation().lxapp().page.pointer', members: PAGE_POINTER_API },
555
583
  { name: 'PageKey', layer: 'automation', expression: 'lx.automation().lxapp().page.key', members: PAGE_KEY_API },
556
584
  { name: 'NavDriver', layer: 'automation', expression: 'lx.automation().lxapp().nav', members: NAV_DRIVER_API },
585
+ {
586
+ name: 'NetworkDriver',
587
+ layer: 'automation',
588
+ expression: 'lx.automation().lxapp().network',
589
+ members: NETWORK_DRIVER_API,
590
+ },
591
+ {
592
+ name: 'ProfileDriver',
593
+ layer: 'automation',
594
+ expression: 'lx.automation().lxapp().profile',
595
+ members: PROFILE_DRIVER_API,
596
+ },
597
+ {
598
+ name: 'ClockDriver',
599
+ layer: 'automation',
600
+ expression: 'lx.automation().lxapp().clock',
601
+ members: CLOCK_DRIVER_API,
602
+ },
603
+ {
604
+ name: 'DialogDriver',
605
+ layer: 'automation',
606
+ expression: 'lx.automation().lxapp().dialogs',
607
+ members: DIALOG_DRIVER_API,
608
+ },
609
+ {
610
+ name: 'NetworkRoute',
611
+ layer: 'automation',
612
+ expression: 'lx.automation().lxapp().network.route()',
613
+ members: NETWORK_ROUTE_API,
614
+ properties: ['id', 'pattern'],
615
+ },
616
+ {
617
+ name: 'Scenario',
618
+ layer: 'automation',
619
+ expression: 'lx.automation().lxapp().mock.use()',
620
+ members: SCENARIO_API,
621
+ properties: ['name', 'rules', 'variant'],
622
+ },
557
623
  { name: 'LxAppManager', layer: 'automation', expression: 'lx.automation().lxapps', members: LXAPP_MANAGER_API },
558
624
  { name: 'DeviceDriver', layer: 'automation', expression: 'lx.automation().device', members: DEVICE_DRIVER_API },
559
625
  {
@@ -795,6 +861,13 @@ export type LxApiManifestGate = [
795
861
  AssertTrue<Exact<PagePointer, typeof PAGE_POINTER_API>>,
796
862
  AssertTrue<Exact<PageKey, typeof PAGE_KEY_API>>,
797
863
  AssertTrue<Exact<NavDriver, typeof NAV_DRIVER_API>>,
864
+ AssertTrue<Exact<NetworkDriver, typeof NETWORK_DRIVER_API>>,
865
+ AssertTrue<Exact<NetworkRoute, typeof NETWORK_ROUTE_API>>,
866
+ AssertTrue<Exact<Scenario, typeof SCENARIO_API>>,
867
+ AssertTrue<Exact<MockDriver, typeof MOCK_DRIVER_API>>,
868
+ AssertTrue<Exact<ProfileDriver, typeof PROFILE_DRIVER_API>>,
869
+ AssertTrue<Exact<ClockDriver, typeof CLOCK_DRIVER_API>>,
870
+ AssertTrue<Exact<DialogDriver, typeof DIALOG_DRIVER_API>>,
798
871
  AssertTrue<Exact<DeviceDriver, typeof DEVICE_DRIVER_API>>,
799
872
  AssertTrue<Exact<BrowserDriver, typeof BROWSER_DRIVER_API>>,
800
873
  AssertTrue<Exact<BrowserCookies, typeof BROWSER_COOKIES_API>>,
@@ -1,14 +0,0 @@
1
- /// <reference path="./logic-globals.d.ts" />
2
-
3
- import type { Automation } from './automation/index.js';
4
-
5
- /** Globals exposed by the `lxdev test` JavaScript runtime. */
6
- interface AutomationTestLx {
7
- automation(): Automation;
8
- }
9
-
10
- declare global {
11
- const lx: AutomationTestLx;
12
- }
13
-
14
- export {};