@oniroproject/core 0.15.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -77,7 +77,7 @@ The injection points every frontend implements, plus their defaults.
77
77
 
78
78
  | Export | Description |
79
79
  | --- | --- |
80
- | `interface ConfigProvider` / `type ConfigKey` | `get(key, fallback)`. Keys: `sdkRootDir`, `cmdToolsPath`, `emulatorDir`, `hapPath`, `cmdToolsUrlLinux/Windows/Mac`, `emulatorUrl`, `tmpDir`, `sdkUrlBase`, `applicationCertPath`. |
80
+ | `interface ConfigProvider` / `type ConfigKey` | `get(key, fallback)`. Keys: `sdkRootDir`, `cmdToolsPath`, `emulatorDir`, `hapPath`, `cmdToolsUrlLinux/Windows/Mac`, `emulatorUrl`, `tmpDir`, `sdkUrlBase`, `applicationCertPath`, `cacheDir`. |
81
81
  | `staticConfig(values?)` | An in-memory `ConfigProvider` (expands `${userHome}`). For tests and as a base for frontend impls. |
82
82
  | `defaultPaths` | The fallback values core uses when a key is unset. |
83
83
  | `interface Logger` / `noopLogger` / `consoleLogger` | 4-level logger (`debug`/`info`/`warn`/`error`). Defaults to no-op. |
@@ -97,6 +97,7 @@ The injection points every frontend implements, plus their defaults.
97
97
  | `downloadAndInstallSdk` | `(InstallSdkOptions) => Promise<void>` | Download + install an SDK release into `<sdkRootDir>/<os>/<api>`, reporting progress; `tmpDir` overrides the scratch directory (default: next to the install target). |
98
98
  | `installSdkFromArchive` | `(InstallSdkFromArchiveOptions) => Promise<InstalledSdk>` | Install an SDK from a local `.tar.gz` (e.g. the full SDK from `OHOS_DAILY_BUILDS_URL`) into `<sdkRootDir>/<os>/<api>`, unpacking only the current OS's components and reading `api`/`version` from their `oh-uni-package.json`. Throws when that API is already installed unless `force`. |
99
99
  | `getSupportedSdksForUi` | `(config) => SdkInfo[]` | Known SDKs annotated with `installed`, newest first. |
100
+ | `getHarmonyOsSdkListing` | `(config) => HarmonyOsSdkListing \| null` | The HarmonyOS SDK a HarmonyOS build would use, which `sdk install` does not manage: `{ flavour: 'HarmonyOS', displayName, version, api, installed, path, source }`. |
100
101
  | `getInstalledSdks` | `(config) => string[]` | Versions with at least one OS-folder install. |
101
102
  | `removeSdk` | `(config, api) => boolean` | Remove an installed SDK by API level across OS folders. |
102
103
  | `detectProjectSdkVersion` | `(projectRoot, logger?) => number \| undefined` | Read `compileSdkVersion` from a project's `build-profile.json5`. |
@@ -124,8 +125,11 @@ are the pair the installers use; `resolveTmpRoot(config, override?, installRoot?
124
125
 
125
126
  | Export | Signature | Description |
126
127
  | --- | --- | --- |
127
- | `installCmdTools` | `(InstallCmdToolsOptions) => Promise<void>` | Install the tools by download or from a local zip (`localZipPath`); `tmpDir` overrides the scratch directory (default: next to the install target). |
128
- | `getCmdToolsStatus` | `(config) => CmdToolsStatus` | `{ installed, status }` (version when present). |
128
+ | `installCmdTools` | `(InstallCmdToolsOptions) => Promise<InstallCmdToolsResult>` | Install the tools by download or from a local zip (`localZipPath`); `tmpDir` overrides the scratch directory (default: next to the install target). The archive is staged inside the install directory and swapped in only once complete, so a failed install leaves the previous tools untouched (or rolls them back), and a successful one replaces the tools' own entries outright (other entries of the directory stay). If the rollback itself fails, the previous tools are kept in a directory the error names. Returns `{ path, installed, previous? }` (`CmdToolsInfo`s); when it replaces an install it logs `describeReplacement(previous, installed)` before deleting the old one. |
129
+ | `describeReplacement` | `(previous, next: CmdToolsInfo) => string[]` | `Replacing command-line tools <old> with <new>.`, plus a line when hvigor changes major version, when the new release is older, and how to go back. `Reinstalling …` for the same release. |
130
+ | `getCmdToolsStatus` | `(config) => CmdToolsStatus` | `{ installed, status, path, hdc }`, and when installed `flavour`, `version`, the bundled `sdk` (`CmdToolsSdk`: `path`, `displayName`, `platformVersion`, `apiVersion`, `build`) and `tools` (e.g. `{ hvigor, ohpm, codelinter, hstack }`). |
131
+ | `readCmdToolsInfo` | `(cmdToolsPath) => CmdToolsInfo` | `{ version, flavour, sdk, tools }` from `version.txt` and `sdk/default/sdk-pkg.json`; unknown fields left out. |
132
+ | `describeCmdTools` | `({ version?, sdk?, tools? }) => string` | One line, e.g. `6.1.1.280 (HarmonyOS 6.1.1, API 24, hvigor 6.24.2)`. |
129
133
  | `isCmdToolsInstalled` | `(config) => boolean` | — |
130
134
  | `removeCmdTools` | `(config) => void` | Delete the install. |
131
135
  | `getCmdToolsDownloadUrl` | `(config, platform?) => string` | Per-platform download URL (only Linux is public on the mirror). |
@@ -137,7 +141,11 @@ are the pair the installers use; `resolveTmpRoot(config, override?, installRoot?
137
141
 
138
142
  | Export | Signature | Description |
139
143
  | --- | --- | --- |
140
- | `generateSigningConfigs` | `(GenerateSigningConfigsOptions) => void` | Generate keys/certs/material and write the `signingConfigs` block into `build-profile.json5`. |
144
+ | `generateSigningConfigs` | `(GenerateSigningConfigsOptions) => GenerateSigningConfigsResult` | Generate keys/certs/material and write the `signingConfigs` block into `build-profile.json5`. Returns the `signaturesDir`, the signing `kind`, and a `SigningMaterialSummary` (profile type, apl, profile and certificate expiry). |
145
+ | `describeSigningMaterial` | `(SigningMaterialSummary) => string` | `release profile, apl normal; certificate valid until 2049-12-31 12:21 UTC`, as `sign` prints it. `signOpenHarmonyHap` returns the same summary as `material`. |
146
+ | `verifyHap` | `(VerifyHapOptions) => Promise<VerifyHapResult>` | Run `hap-sign-tool verify-app` on a HAP and report its `status` (`valid`/`invalid`/`unverified`), whether it is signed and the signature valid, the chain (leaf first, `CertificateInfo`) and root, the `signer` (`openharmony-dev`, `harmonyos-agc`, `other`), the profile (`HapProfileReport`: type, issuer, bundle, apl, app-feature, device count, validity, days left, whether its expiry is enforced and whether it names the signing certificate), and the `problems` that make it invalid. Never throws for a bad HAP. |
147
+ | `verifyHapExitCode` / `formatHapVerification` | `(VerifyHapResult) => number` / `=> string` | `0` valid, `1` invalid, `2` could not verify; the text `app verify` prints. |
148
+ | `findHapSignTool` | `(config, logger?) => string \| null` | The `hap-sign-tool.jar` of an installed OpenHarmony SDK, else the HarmonyOS SDK, else the command-line tools. |
141
149
  | `pickSigningKind` | `(apl) => SigningKind` | `'profile-release'` for `normal`, `'application-release'` for `system_basic`/`system_core`. |
142
150
  | `detectSigningConfigNames` | `(projectDir) => string[]` | The `products[*].signingConfig` names (falls back to `['default']`). |
143
151
  | `createMaterial` / `getKey` / `encryptPwd` / `decryptPwd` | — | Signing-material key derivation + password encryption used in the profile. |
@@ -150,8 +158,15 @@ are the pair the installers use; `resolveTmpRoot(config, override?, installRoot?
150
158
  | --- | --- | --- |
151
159
  | `installEmulator` | `(InstallEmulatorOptions) => Promise<void>` | Download + extract the QEMU emulator into `emulatorDir`; `tmpDir` overrides the scratch directory (default: next to the install target). |
152
160
  | `isEmulatorInstalled` / `removeEmulator` | `(config) => boolean` / `void` | — |
153
- | `startEmulator` | `(StartEmulatorOptions) => Promise<void>` | Launch detached via `run.sh`/`run.bat`; optional `headless`, `logFile`, `connect`, and `waitForHdcSeconds`. |
154
- | `stopEmulator` | `(logger?) => Promise<void>` | Kill running emulator processes. |
161
+ | `startEmulator` | `(StartEmulatorOptions) => Promise<void>` | Launch detached via `run.sh`/`run.bat`; optional `headless`, `logFile`, `connect`, `waitForHdcSeconds` and `waitReadySeconds` (waits for `bootevent.boot.completed` with `hdc tconn` only). Refuses while a pool instance runs. |
162
+ | `stopEmulator` | `(logger?) => Promise<void>` | Kill running emulator processes (every `qemu-system-x86_64`, pool instances included). |
163
+ | `getEmulatorStatus` | `(config, { address?, logger? }) => Promise<EmulatorStatus>` | `installed`, `running` (the default emulator; pool instances do not count), `connected`, and `instances`: the default emulator as instance 0, then every pool instance, each an `EmulatorInstanceStatus`. |
164
+ | `startPoolInstance` | `(StartPoolInstanceOptions) => Promise<EmulatorInstanceStatus>` | Emulator pool: launch instance `n` (1..99) with its own QEMU on qcow2 overlays of the images in `<emulatorDir>/instances/<n>/`, hdc on `127.0.0.1:55555+n`, VNC `5900+n`, serial `4444+n`. Options `smp` (2), `memory` (`2048M`), `resolution`, `headless` (true), `fresh`, `bootTimeoutSeconds` (300), `waitReadySeconds`, `qemuBin`, `qemuImg`, `accel`, `logFile`. Fails on a taken port, a running default emulator, or base images changed since the overlays were made. |
165
+ | `stopPoolInstance` | `({ config, instance, discard?, logger? }) => Promise<boolean>` | Signal only that instance's QEMU (by its recorded PID), `hdc tconn <addr> -remove`, and with `discard` delete its directory. |
166
+ | `getPoolInstanceStatus` / `listPoolInstanceStatuses` | `(config, n, opts?)` / `(config, opts?)` | Health of one or every pool instance: `stopped`, `booting`, `ready`, `hdc-lost`, `stuck` (`EmulatorHealth`), probing with `hdc tconn` and `param get bootevent.boot.completed`; records `readyAt` in `instance.json`. |
167
+ | `waitPoolInstanceReady` | `(WaitPoolInstanceReadyOptions) => Promise<EmulatorInstanceStatus>` | Poll until `ready`; throws on timeout or a dead QEMU. |
168
+ | `probeEmulatorBoot` / `classifyEmulatorHealth` | `(config, address, opts?)` / `(HealthInputs) => EmulatorHealth` | The probe and the pure health rule behind the above. |
169
+ | `poolInstancePorts` / `buildPoolQemuArgs` / `emulatorAccelArgs` | — | Pure helpers: an instance's ports, its QEMU argv, and the accelerator `run.sh` would pick. |
155
170
  | `attemptHdcConnection` | `(config, address?, logger?) => Promise<boolean>` | Try to connect `hdc` (default `127.0.0.1:55555`). |
156
171
 
157
172
  ### hdc — process execution
@@ -204,6 +219,8 @@ The injection-safe primitives every device operation builds on (`spawn` with `sh
204
219
  | `listRunningProcesses` / `findAppProcessId` | — | `track-jpid`-based process listing / project-PID resolution. |
205
220
  | `detectAaStartFailure` | `(output) => string \| null` | Detect a refused launch (`aa start` exits 0 even on failure). |
206
221
  | `getBundleName` / `getMainAbility` | `(projectDir[, module?, ability?]) => string` | Read the bundle / resolve the launch ability from project manifests. |
222
+ | `smokeCheck` | `(SmokeCheckOptions) => Promise<SmokeResult>` | Smoke-check a just-launched app: wait for its pid, wait `delayMs` (default 2000), screenshot and judge it against a flat frame (needs a `decodeGreyscale` decoder, since core decodes no images), check the same pid is still running, and ask an optional `crashProbe({ config, bundle, since, deviceSerial, logger })` for a crash since launch. Returns `{ passed, failures, pid, alive, blankScreen, blankScore, screenshot, crash, crashChecked, … }`; never throws for a failed check. |
223
+ | `smokeVerdict` | `({ pid, alive, blankScreen, crash }) => { passed, failures }` | The pure verdict behind `smokeCheck`. |
207
224
 
208
225
  ### hdc — capture, display & UI dump
209
226
 
@@ -212,24 +229,86 @@ The injection-safe primitives every device operation builds on (`spawn` with `sh
212
229
  | `takeScreenshot` | `(ScreenshotOptions) => Promise<Screenshot>` | `{ pixels: Buffer, width, height }` — raw JPEG, no decode. |
213
230
  | `captureBurst` | `(CaptureBurstOptions) => Promise<Buffer[]>` | N frames `intervalMs` apart, pulled back in order. |
214
231
  | `getDisplaySize` | `(config, opts?) => Promise<DisplaySize \| null>` | Render resolution from `hidumper` (no image work). |
215
- | `dumpLayout` | `(DumpLayoutOptions) => Promise<DumpLayoutResult>` | `uitest dumpLayout` → pruned, 0–1-normalized `LayoutNode` tree, with `hint`, `disabled`, and `covered` (painted over by a later filled layer). `detectKeyboard` adds `keyboard: KeyboardState`. |
232
+ | `dumpLayout` | `(DumpLayoutOptions) => Promise<DumpLayoutResult>` | `uitest dumpLayout` → pruned, 0–1-normalized `LayoutNode` tree, with `hint`, `disabled`, `hidden`, `covered` (painted over by a later filled layer), and `windowId` on each window's root. `detectKeyboard` adds `keyboard: KeyboardState`; `windowId` keeps one window (the screen is still dumped whole, so covering and the keyboard are judged on all of it). |
216
233
  | `parseBounds` / `pruneLayout` / `findCoveredNodes` | — | Pure helpers behind `dumpLayout`. |
217
- | `flattenLayout` / `findLayoutNodes` / `pickTarget` | — | Pure: flatten a tree (nodes tagged with their `window`), match a `LayoutSelector` (`text` / `id` / `type`), and choose one uncovered, preferably clickable node (throws `TargetNotFoundError` with `reason` `none` / `covered` / `ambiguous`). |
234
+ | `flattenLayout` / `findLayoutNodes` / `pickTarget` / `findVisibleMatch` | — | Pure: flatten a tree (nodes tagged with their `window` bundle and `windowId`), match an `ElementQuery` (a `LayoutSelector` — `text` / `id` / `type` — or a parsed `ElementSelector`), choose one uncovered, preferably clickable node (throws `TargetNotFoundError` with `reason` `none` / `covered` / `ambiguous`; a selector's `[n]` picks from all matches instead), and the first uncovered match (what `waitForTarget` polls for). |
235
+ | `listLayoutWindows` / `windowSubtree` / `rawWindowSubtree` / `windowNotFoundError` | — | Pure: the windows in a pruned tree (`LayoutWindow`: `windowId`, `bundle`, `b`), one window's part of a pruned or raw tree, and the error listing the on-screen windows. |
236
+ | `listWindows` / `parseWindowList` | `(ListWindowsOptions) => Promise<WindowInfo[]>` / `(text) => WindowInfo[]` | Every window from `hidumper -s WindowManagerService -a -a`: `id`, `name`, `pid`, `displayId`, `type`, `zOrder` (-1 = not shown), `rect`, `focused`. |
218
237
  | `getImeBundles` / `parseImeBundles` / `findKeyboardWindow` | — | Installed input methods (`hidumper -s 3703`) and the soft-keyboard window in a layout. |
219
238
 
239
+ ### hdc — element selectors
240
+
241
+ The `--selector` language of the CLI, evaluated over the pruned tree. All pure. The attribute DSL and the `xpath=` form follow openharmony-sig/deveco-cli (MIT); the XPath evaluator is a dependency-free subset (`/`, `//`, `*`, `.`, `..`, `[@a="v"]`, `[@a]`, `!=`, `contains` / `starts-with` / `ends-with` / `not` / `text()`, `and` / `or`, `[n]`, `[last()]`, `[position()<n]`, `(path)[n]`); anything else is a `SelectorParseError`.
242
+
243
+ | Export | Signature | Description |
244
+ | --- | --- | --- |
245
+ | `parseSelector` | `(text) => ElementSelector` | `xpath=<expr>` → `XPathSelector`; otherwise `key<op>value[,…][n]` → `DslSelector` (`clauses`, `index`: 0-based, negative from the end). Operators `=`, `*=`, `^=`, `$=`, `/=` (regex, `/flags`). Throws `SelectorParseError` (`position`, `input`) on bad syntax, an unknown key, or a repeated key. |
246
+ | `matchSelector` | `(tree, ElementSelector) => FlatLayoutNode[]` | Every match in paint order, window-tagged; the DSL index is left to the caller (`pickTarget`, `findVisibleMatch`). |
247
+ | `selectorFor` | `(tree, RecordedElement) => string` | The most specific stable selector for a recorded element (`Partial<FlatLayoutNode>`, e.g. from `wait --json`): `type` plus the first unique of `id`, `key`, `text`, `desc`, `hint`, then all of them, then all of them with `[n]`. Exact `=` clauses only; the result resolves back to that element. For record/replay. |
248
+ | `formatSelector` | `(clauses, index?) => string` | Render DSL clauses back to text `parseSelector` reads, quoting values as needed. |
249
+ | `clauseMatches` / `selectorAttribute` / `resolveSelectorKey` / `SELECTOR_KEYS` | — | One clause against a node; a key's value on a node (booleans as `"true"` / `"false"`; `key` falls back to `id`; `visible` = not `hidden` and not `covered`); key aliases (`description` → `desc`); the key list. |
250
+ | `parseXPath` / `evaluateXPath` | `(text) => XPathExpression` / `(tree, expr) => FlatLayoutNode[]` | The XPath subset on its own. Elements are named by component type (`node` when untyped). |
251
+ | `isElementSelector` / `ElementQuery` | — | Tell a parsed selector from a plain `LayoutSelector`; the union every target-taking function accepts. |
252
+ | `getImeBundles` / `parseImeBundles` / `findKeyboardWindow` | — | Installed input methods (`hidumper -s 3703`) and the soft-keyboard window in a layout. |
253
+ | `summarizeLayout` | `(DumpLayoutResult) => LayoutSummary` | Pure: per window (and in total) the element, content (text, description, placeholder or image), clickable and text-node counts. |
254
+ | `diffLayouts` | `(baseline, current) => LayoutDiff` | Pure: pair elements within a window by id, key, label and type, then type with overlapping boxes (greedy by IoU); report each pair's IoU and changed fields, the `missing` and `extra` elements, and a 0–1 `similarity`. |
255
+ | `parseLayoutBaseline` / `boxIoU` | — | Read a saved `dump layout` JSON (refuses a raw dump); IoU of two 0–1 boxes. |
256
+
257
+ ### Image comparison
258
+
259
+ Pure functions over decoded greyscale pixels (`GreyImage`: `{ width, height, data: Uint8Array }`); the caller decodes the JPEG (the CLI uses sharp).
260
+
261
+ | Export | Signature | Description |
262
+ | --- | --- | --- |
263
+ | `pHash` / `dHash` | `(GreyImage) => string` | 64-bit DCT perceptual hash / difference hash, as 16 hex digits. |
264
+ | `hammingDistance` | `(a, b) => number` | Differing bits between two hashes. |
265
+ | `compareImages` | `(baseline, current) => ImageComparison` | `phashDistance`, `dhashDistance`, `pixelChangeRatio` (share of pixels that moved more than 24 grey levels), `sizeMismatch`, and `same` (both hashes within 4 bits and under 1 % changed). |
266
+ | `analyzeBlank` | `(GreyImage) => BlankAnalysis` | Whether a screenshot shows a blank app, status and navigation bars cropped off: blank when its pHash is within 10 bits of a flat frame's with a standard deviation of at most 10, or when 99.8 % of it is one colour. `blankScore` is that share. |
267
+ | `resizeGrey` / `cropRows` / `pixelChangeRatio` / `flatFrameHash` | — | The helpers behind them. |
268
+
220
269
  ### hdc — input injection
221
270
 
222
271
  | Export | Signature | Description |
223
272
  | --- | --- | --- |
224
273
  | `sendInput` | `(SendInputOptions) => Promise<void>` | One `uitest uiInput` action in **pixel** space (`InputType`: click/doubleClick/longClick/swipe/drag/fling/keyEvent/inputText). `key` accepts names and combos (see `resolveKeyEvent`). |
225
- | `performInput` | `(PerformInputOptions) => Promise<PerformInputResult>` | `sendInput` plus a start point from an element selector (`target`), 0–1 fractions, or pixels; `clearText`, `inputText` with `clear`, and keyboard-aware `hideKeyboard`. |
226
- | `locateTarget` | `(LocateTargetOptions) => Promise<LocatedTarget>` | Resolve a selector against the live layout to one element and its pixel center, dismissing the keyboard if it hides the element. |
274
+ | `performInput` | `(PerformInputOptions) => Promise<PerformInputResult>` | `sendInput` plus a start point from an element selector (`target`: a `LayoutSelector` or parsed `ElementSelector`, optionally limited to `windowId`), 0–1 fractions, or pixels; `clearText`, `inputText` with `clear`, and keyboard-aware `hideKeyboard`. |
275
+ | `locateTarget` | `(LocateTargetOptions) => Promise<LocatedTarget>` | Resolve a selector against the live layout (optionally one `windowId`) to one element and its pixel center, dismissing the keyboard if it hides the element. |
276
+ | `selectableTree` | `(DumpLayoutResult, windowId?) => LayoutNode` | What selectors search: the tree minus the soft-keyboard window, unless a window was asked for. |
227
277
  | `hideKeyboard` | `(opts) => Promise<boolean>` | Press Back only when the soft keyboard is shown. |
228
- | `waitForTarget` | `(WaitForTargetOptions) => Promise<FlatLayoutNode \| null>` | Poll the layout until an uncovered element matches (or, with `gone`, until none does). |
278
+ | `waitForTarget` | `(WaitForTargetOptions) => Promise<FlatLayoutNode \| null>` | Poll the layout until an uncovered element matches (or, with `gone`, until none does); `windowId` limits it to one window, which counts as empty while it is not on screen. |
229
279
  | `sendGesture` | `(SendGestureOptions) => Promise<void>` | Multi-`Waypoint` path; routes through `uinput` when `holdStartMs`/`holdEndMs` need real press timing. |
230
280
  | `sendRawTouch` | `(SendRawTouchOptions) => Promise<void>` | Low-level `uinput -T` down/move/up events. |
231
281
  | `buildInputCommand` / `buildGestureCommands` / `buildRawTouchCommand` / `resolveKeyEvent` | — | Pure command builders (unit-testable). |
232
282
 
283
+ ### Flows — record and replay
284
+
285
+ The flow file format of `oniro-app record` / `replay` (see the CLI README, "Flow files"), its replay through `performInput` and `waitForTarget`, and the conversion of uitest's recorder output.
286
+
287
+ | Export | Signature | Description |
288
+ | --- | --- | --- |
289
+ | `Flow` / `FlowStep` / `FLOW_VERSION` / `FLOW_ACTIONS` | — | Version 1: `bundle`, `ability`, `device` (`api`, `display`), `steps` (`action`: click, longClick, doubleClick, swipe, inputText, keyEvent, wait; `selector`, `fallback` `{fx, fy}`, `text`, `clear`, `key`, `to`, `speed`, `gone`, `expect`, `waitMs`, `timeoutMs`, `note`). |
290
+ | `parseFlow` / `readFlowFile` / `formatFlow` | `(doc, file?) => Flow` / `(file) => Flow` / `(Flow) => string` | Validate a flow, listing every problem with its path (unknown fields, missing per-action fields, selectors that do not parse) in a `FlowFormatError` (`problems`). |
291
+ | `replayFlow` | `(ReplayFlowOptions) => Promise<ReplayReport>` | Per step: wait for its selector, act on it (or its fallback point, with `fallback`), pause `waitMs`, wait for `expect`; `from` / `to`, `stepTimeoutMs`, `screenshotsDir`, `launch`, `wake`, `onStep`. Stops at the first failure with `outcome: 'diverged'`, `failedStep`, `reason` (`not-found`, `expect-not-found`, `wrong-screen`, `ambiguous`, `covered`, `action-failed`), `message`, `selector`, `screenshot`, `layout` and `nearMatches`; `outcome: 'error'` when it could not run (no device, bad range). Never throws for those. |
292
+ | `nearMatches` / `summarizeDivergenceLayout` | — | Pure: elements failing exactly one clause of a DSL selector; what is on screen, focused on the flow's app (`appOnScreen`, `lockScreen`, `keyboard`, `elements`). |
293
+ | `recordFlow` | `(RecordFlowOptions) => Promise<RecordFlowResult>` | Run `uitest uiRecord record -l` in the background until `durationMs` or `abortSignal`, stop it with SIGINT, follow the multimodal input hilog domain for touch-down times, pull `record.csv` and the layouts, dump the screen before and after, and convert (`flowFromRecording`). `rawDir` keeps the raw recording. Returns `{ flow, warnings, events }`. |
294
+ | `flowFromRecording` | `(FlowFromRecordingInput) => FlowFromRecordingResult` | Pure conversion. uitest writes an event (and hit-tests its widget and dumps its layout) only once the next touch has started, so with `times` each touch is matched to the last layout written before it (`alignTouches`); the element under the touch (`elementAt`, uitest's widget when it is there) gets `selectorFor`'s selector, the point is the `fallback`, and `expectFor` names what appeared after it. Soft-keyboard taps and keystrokes become one `inputText` on the focused field (`fieldSelector`); other keys, Back and Home, `keyEvent`. |
295
+ | `parseUiRecordLines` / `alignTouches` / `elementAt` / `fieldSelector` / `expectFor` / `keystrokeChar` / `keystrokeName` | — | Pure helpers: `record.csv` (JSON Lines) → `UiRecordEvent[]`; pointer events → touch-down times; the topmost uncovered element under a point; a field selector that ignores its text; a selector for an element new in `after`; KeyCode ids → a typed character or a key name. |
296
+ | `parseDeviceTime` / `parseTouchDowns` / `parseRecorderForeground` / `UI_RECORD_CSV` | — | Pure: `date` / `stat %y` timestamps → epoch ms; `ServerMsgHandler: [P:D:…]` hilog lines → touch-down times; the recorder's `Current ForAbility` line; `/data/local/tmp/record.csv`. |
297
+
298
+ ### Performance — hitrace capture and summary
299
+
300
+ `oniro-app perf trace` / `perf summary`: a hitrace capture while the app cold-launches or a flow is replayed, and its summary for one bundle. Pure parsing and summary; `perfTrace` drives the device.
301
+
302
+ | Export | Signature | Description |
303
+ | --- | --- | --- |
304
+ | `perfTrace` | `(PerfTraceOptions) => Promise<PerfTraceReport>` | `hitrace --trace_begin -b <bufferKb> <categories>` (taking over a capture left running), then `launch` (force-stopped before tracing, `aa start` after), a `flow` through `replayFlow`, or a wait of `forMs`; `hitrace --trace_finish -o`, `file recv` to `out`, remove the device copy, and summarise with `summarizeTrace` (pid from `pidof`, refresh rate from `hidumper` as fallbacks). `hiperf` samples the app's process with `hiperf record -p` (stopped with SIGINT) and adds `hiperf report --sort dso,func`'s top functions. Returns `{ ok, bundle, durationMs, trace, refreshRateHz, frames, startup, flow, pid, mainThread, scenes, hiperf?, categories, notes, error? }`; never throws for a failed capture, and always stops tracing. |
305
+ | `evaluatePerfGate` | `(report, PerfThresholds) => PerfGateResult` | `maxJankRate` / `maxStartupMs` → `{ passed, failures }`; a threshold whose metric was not measured fails, and so does a flow that did not pass. |
306
+ | `DEFAULT_TRACE_CATEGORIES` / `DEFAULT_TRACE_BUFFER_KB` / `DEFAULT_PERF_FOR_MS` / `DEFAULT_PERF_FLOW_TAIL_MS` | — | `ace, app, graphic, ability, window`; 65536 KB; 5000 ms; 1000 ms. |
307
+ | `parseTraceText` / `parseTraceFile` / `TraceParser` | `(text, ParseTraceOptions?) => TraceModel` / `(file, opts?) => Promise<TraceModel>` | ftrace text (with or without the tgid column) → `TraceModel`: closed and open B/E slices per thread (`tid`, `pid`, `name`, `ts`, `dur`, `depth`; the level/tag suffix `\|I39` and the `H:` / HiTraceChain prefixes stripped), async S/F slices, counters, thread names, main-thread CPU time from `sched_switch`, RenderService surface owners, the buffer-overflow header. `keepSlice` (e.g. `perfSliceFilter`) keeps memory flat on long captures; the file is streamed. |
308
+ | `summarizeTrace` | `(TraceModel, SummarizeTraceOptions) => TraceSummary` | For `bundle`: the app's pid (thread name, `##<bundle>` launch markers, `pid`, else its RenderService surface), frames from the `ReceiveVsync` slices on its main thread timed from their vsync (`total`, `janky` over one refresh interval, `jankRate`, `p50Ms`, `p95Ms`, `maxMs`, `missedVsyncs`, `longest`), `refreshRateHz` from `VSyncReceiver:period`, `startup` with `launched` (`coldMs` from `aa`'s `StartAbility` to `RSJankStats::ReportEventFirstFrame`, `firstFrameMs`, `processMs`, `from`, `to`), `mainThread` (`busyMs`, `cpuMs`), RenderService `scenes`, and `notes`. |
309
+ | `perfSliceFilter` / `findAppPids` / `findAppPidsBySurface` / `commMatchesBundle` / `cleanMarkerName` / `quantile` | — | Pure helpers behind the above. |
310
+ | `parseHiperfReport` / `parseActiveRefreshRate` | `(text, limit?) => HiperfReport` / `(hidumper) => number \| null` | `hiperf report` rows → `{ event, samples, top: [{ percent, dso, func }] }`; `activeMode: …, refreshRate=59` → 59. |
311
+
233
312
  ### hdc — logging (hilog)
234
313
 
235
314
  | Export | Signature | Description |
@@ -242,18 +321,86 @@ The injection-safe primitives every device operation builds on (`spawn` with `sh
242
321
  | `setHilogLevel` | `(SetHilogLevelOptions) => Promise<void>` | Set the buffer level. |
243
322
  | `parseHilogLine` / `dedupEntries` | — | Pure helpers; `HilogEntry` / `HilogLevel` types. |
244
323
 
324
+ ### Faultlogs (crashes and freezes)
325
+
326
+ Adapted in part from deveco-cli's `faultlog/` module (MIT); see `src/faultlog/NOTICE.md`.
327
+
328
+ | Export | Signature | Description |
329
+ | --- | --- | --- |
330
+ | `latestCrash` | `({ config, bundle?, since?, deviceSerial?, waitMs?, pollMs?, saveDir? }) => Promise<CrashVerdict \| null>` | The newest fault of `bundle` (any process when unset) since `since` (a `Date`, or seconds before the device clock), classified and parsed; null when there is none. `waitMs` keeps polling, since hiview writes the faultlog seconds after the process dies. `saveDir` saves the whole log and sets `rawPath`. |
331
+ | `analyzeFaultLog` | `(text, { name?, path?, rawPath? }) => CrashVerdict` | Pure: classify a faultlog (or faultloggerd crash record) and read `crashed`, `kind` (`cpp_crash`, `js_crash`, `app_freeze`, `sample_stack`, `unknown`), `subtype`, `confidence`, `bundle`, `pid`, `tid`, `time`, `reason`, `topFrame`, `projectFrame`, `file`/`line`/`column` of the first project ArkTS frame, `jsError {name, message, stack}`, `stack`, `nativeFrames`, `jsFrames`, `binderChain`, `deadlocks`, `notes`. |
332
+ | `listFaultLogs` | `(ListFaultLogsOptions) => Promise<FaultLogEntry[]>` | Newest first: `hidumper -s 1201 -a '-p Faultlogger -LogSuffixWithMs'` (else `ls /data/log/faultlog/faultlogger`), plus the `cppcrash-<pid>-<ms>.json` records in `/data/log/faultlog/temp` that hiview did not turn into a faultlog. Filters by `bundle` and `since`. |
333
+ | `readFaultLog` | `({ config, entry, deviceSerial? }) => Promise<string>` | A listed entry's text: `hidumper … -f <name>` without its banner, else `cat`. |
334
+ | `classifyFaultLog` | `(text) => FaultLogClassification` | Pure: the kind, confidence, completeness (`full` faultlog or `fragment`), evidence and excluded kinds, by structure alone. |
335
+ | `parseCppCrash` / `parseJsCrash` / `parseFreeze` / `parseBinder` / `buildPropagation` | — | The per-kind parsers and the binder-chain analysis behind `analyzeFaultLog`. |
336
+ | `parseNativeFrame` / `parseJsFrame` / `parseThreadStacks` | — | Frame parsers: `#00 pc … /system/lib64/x.so(sym+8)(buildid)` and `at f entry (entry/src/main/ets/x.ets:8:13)`. |
337
+ | `parseFaultLogList` / `parseFaultLogName` / `parseCrashRecordHeads` / `stripHidumperBanner` / `renderCrashJson` | — | Pure helpers for the device listing and the crash-record format. |
338
+
245
339
  ### Build & dependencies
246
340
 
247
341
  | Export | Signature | Description |
248
342
  | --- | --- | --- |
249
- | `buildHap` | `(BuildHapOptions) => Promise<BuildHapResult>` | High-level orchestration: ensure deps → `hvigorw` → discover built HAPs. The call most consumers want. Throws `SdkNotInstalledError` (with the `version` to install) up front when an OpenHarmony project's SDK is missing; `diagnoseBuildFailure` adds a hint for system-API errors. |
343
+ | `buildHap` | `(BuildHapOptions) => Promise<BuildHapResult>` | High-level orchestration: ensure deps → `hvigorw` → discover built HAPs. Throws `SdkNotInstalledError` (with the `version` to install) up front when an OpenHarmony project's SDK is missing, and `BuildFailedError { exitCode, failedTask, diagnostics, summary, rawTail, hint, sdk }` when ohpm or hvigor fails; a failed hvigor run's message ends naming the SDK used (`Built against …`). The result carries the build's warnings as `diagnostics` and the SDK as `sdk`. |
344
+ | `resolveBuildSdk({ config, projectDir, runtimeOS })` / `describeBuildSdk(sdk)` | The `BuildSdk` (`runtimeOS`, `displayName`, `version`, `api`, `path`) a build compiles against, found as the build finds it; `HarmonyOS 5.1.0 (API 18) at <path>`. |
345
+ | `signingMismatch(projectDir, runtimeOS, product?)` | Why the product's signingConfig holds material for the other runtime (HarmonyOS product without `type: "HarmonyOS"`, or the reverse), else undefined. `buildHap` adds it to `warnings` and as a `signing` warning diagnostic when a signed HAP was built. |
346
+ | `dependencyApiDiagnostic(diagnostics, sdkLabel, remedy)` / `ohModulesPackage(file)` | A `warning` `sdk` diagnostic when ArkTS errors under `oh_modules/` are missing properties or names (`Property … does not exist`, `Cannot find name`): a dependency may need a newer API level. Message-level only. |
347
+ | `buildWithDiagnostics` | `(BuildHapOptions) => Promise<BuildReport>` | `buildHap` that returns `ok: false` with the diagnostics instead of throwing for a failed build or a missing SDK (`missingSdk`), with the SDK used as `sdk`. Still throws when the build cannot run at all. The shape `build --json` prints. |
348
+ | `parseBuildOutput` | `(output, { projectDir? }) => ParsedBuildOutput` | Pure parser of hvigor output: ArkTS, hvigor, native (clang, lld) and ohpm errors as `BuildDiagnostic { severity, source, code, rule, message, file, line, column, task }`, de-duplicated, plus `failedTask` and hvigor's `COMPILE RESULT` counts. |
349
+ | `sdkTooOldDiagnostic` / `missingSdkSymbols` | — | The `sdk` diagnostic naming the installed API level that declares the kit symbols a build could not find. `diagnoseBuildFailure` gives the hint for other causes (system APIs, an SDK path hvigor cannot find). |
250
350
  | `runHvigorw` | `(RunHvigorwOptions) => Promise<RunHvigorwResult>` | Raw single `hvigorw` spawn. Parallel by default; `parallel: false` adds `--no-parallel`. |
251
351
  | `buildHvigorwArgs` | `(opts) => string[]` | Pure argv builder (unit-testable). |
252
- | `discoverHaps` | `(DiscoverHapsOptions) => Promise<Record<string, ModuleHaps>>` | Group `*-signed`/`*-unsigned` HAPs by module, newest first. |
352
+ | `discoverHaps` | `(DiscoverHapsOptions) => Promise<Record<string, ModuleHaps>>` | Group `*-signed`/`*-unsigned` HAPs by module, newest first. Test HAPs (`*-ohosTest-*.hap`, `isTestHap`) are left out. |
253
353
  | `pickHap` | `(haps, module?) => PickedHap \| null` | The HAP to install: a signed one, else unsigned (`signed: false`). |
254
354
  | `runOhpm` / `ensureOhModules` | — | Run `ohpm`; install `--all` only when `oh_modules/` is missing. |
255
355
  | `runCodelinter` / `parseCodelinterReport` / `parseCodelinterFindings` | — | Run the codelinter → `CodelinterResult { code, findings, summary, notices, incomplete, raw }`; the parsers read its per-file table and `-Defects:` totals. |
256
356
 
357
+ ### ArkTS check
358
+
359
+ | Export | Signature | Description |
360
+ | --- | --- | --- |
361
+ | `checkArkts` | `(ArktsCheckOptions) => Promise<ArktsCheckReport>` | Run the SDK's ArkTS checker and the project rules on a project or a list of files, without a build (`oniro-app check arkts`). `completed: false` with `error` when the checker could not run or crashed; throws only when the SDK cannot be resolved (`SdkNotInstalledError` for an API that is not installed). `fix: true` applies the verified fixes. Diagnostics use `BuildDiagnostic` with `source: 'arkts-check'`. |
362
+ | `resolveArktsCheckSdk` | `(config, projectDir, logger?) => ArktsCheckSdk` | The SDK directory the check loads: `<sdk home>/<compileSdkVersion>` for OpenHarmony, `default/openharmony` plus `default/hms/ets` of the located HarmonyOS SDK for HarmonyOS. |
363
+ | `parseArktsCheckOutput` / `arktsCheckerScript` | — | Read the checker's JSON; the path of the vendored `arkts-check.cjs`. |
364
+
365
+ ### Screens
366
+
367
+ | Export | Signature | Description |
368
+ | --- | --- | --- |
369
+ | `inventoryScreens` | `(ScreensOptions) => ScreensInventory` | Static page and route inventory of every module (`oniro-app screens`): `pages` (`@Entry` pages from `main_pages.json` and `routeName`, `NavDestination`s from route maps and `.navDestination()` builders; `name`, `module`, `file`, `line`, `component`, `entry`, `kind`, `registered`, `contentNodes`, `routesOut`), `routes` (`router` and `NavPathStack` push/replace call sites and `loadContent`; `from`, `to`, `file`, `line`, `via`, `dynamic`), `placeholders` (`TODO`/`FIXME`, placeholder text, empty `build()`, pages without content), `warnings`. Synchronous, no SDK. Throws `OniroError` when the project cannot be read. |
370
+ | `checkScreensAgainstSpec` | `(inventory, { screens, routes, minContentNodes }) => ScreensSpecReport` | The G2 gate as a pure function of the inventory: every spec screen has a page with at least `minContentNodes` content nodes (per-screen override), every spec route (`{ from?, to }`) a push or replace call site. Returns `{ ok, screens, routes, failures }`, the matching page and call site for each. Names match a page's route name, its last path segment or its component, ignoring case. |
371
+ | `pagesMatching` | `(inventory, name) => ScreenPage[]` | The pages a spec name refers to, by that rule. |
372
+ | `scanEts` / `LAYOUT_COMPONENTS` | — | The ArkTS token scanner (comments, strings, templates, regex literals, matched brackets, declarations with decorators) the inventory uses; the components that do not count as content. |
373
+
374
+ ### Doctor
375
+
376
+ | Export | Signature | Description |
377
+ | --- | --- | --- |
378
+ | `runDoctor` | `(DoctorOptions) => Promise<DoctorReport>` | Every `oniro-app doctor` check: `{ ok, checks: [{ id, status, detail, fix, fixed }], project, versions }`. Never throws for a failed check. `fix: true` installs the project's missing OpenHarmony SDK. |
379
+ | `checkCmdTools` / `checkCmdToolsFlavour` / `checkSigning` / `checkDisk` / `checkNode` | — | The synchronous checks, for a caller that wants one. `CMD_TOOLS_REQUIRED` lists what a complete tools install holds. |
380
+ | `describeProject` / `apiLevelOf` | — | A project's `runtimeOS`, SDK versions and minimum device API; the API level of `20` or `"6.0.0(20)"`. |
381
+ | `resolveProjectSdkDir` | `(config, projectDir) => string \| undefined` | The SDK API directory an OpenHarmony project builds against, under the SDK home or `local.properties` `sdk.dir`. |
382
+
383
+ ### SDK declaration index
384
+
385
+ | Export | Signature | Description |
386
+ | --- | --- | --- |
387
+ | `loadApiIndex` | `(sdkDir, LoadApiIndexOptions?) => LoadedApiIndex` | The declaration index of one SDK API directory (`oniro-app api`): the cached one under `cacheDir` when it is current, else a new build that is then cached. The cache is keyed by the SDK directory and the mtimes of its `ets/` and `native/` `oh-uni-package.json`; `rebuild: true` forces a build. |
388
+ | `buildApiIndex` | `(sdkDir, BuildApiIndexOptions?) => ApiIndex` | Parse `ets/api/**`, `ets/arkts/**` and `ets/component/**` (`.d.ts`, `.d.ets`) with the SDK's bundled TypeScript (`ets/build-tools/ets-loader/node_modules/typescript`, else a resolvable `typescript`, or the one passed as `typescript`), map `ets/kits/@kit.*.d.ts` re-exports onto the symbols, and scan the Node-API headers (`native/sysroot/usr/include/{node_api*,js_native_api*}.h`, `napi/*.h`). Each `ApiEntry`: `name`, `qualifiedName`, `kind`, `language`, `module`, `kit`, `import`, `global`, `since`, `sinceText`, `deprecated`, `deprecatedSince`, `useInstead`, `syscap`, `systemApi`, `file`, `line`, `signature`, `doc`. |
389
+ | `searchApi` | `(index, query, ApiSearchOptions?) => ApiSearchResult` | Ranked token search: every word must match the name (exact > prefix > camelCase/snake_case part > substring), the owning class or namespace, or the doc line; a dotted word matches qualified names. Overloads fold into one hit (`overloads`). Filters: `kit`, `kinds`, `language`, `noDeprecated`, `noSystemApi`, `limit`. |
390
+ | `showApi` | `(index, symbol, opts?) => ApiShowResult` | Exact lookup by qualified name (`NavPathStack.pushPathByName`, `@Local`), else by dotted suffix; `found: false` when the SDK does not declare it. |
391
+ | `suggestApiSymbols` / `findApiInOtherSdks` | — | For a miss: the declared names closest to it, and the other installed SDKs that declare it (`[{ api, since }]`). |
392
+ | `resolveApiSdk` / `installedApiLevels` / `getApiIndexCacheDir` | — | The SDK to read (`sdkDir`, else `<sdk home>/<api>`, else the enclosing project's `compileSdkVersion`, else the newest installed); the installed API levels; the `cacheDir` config key (default `~/.oniro/cache`). |
393
+ | `serializeApiIndex` / `deserializeApiIndex` / `apiIndexCachePath` / `parseDocBlock` / `mergeDocBlocks` | — | The compact stored form, the cache file of an SDK, and the doc-comment tag reader. |
394
+
395
+ ### Tests
396
+
397
+ | Export | Signature | Description |
398
+ | --- | --- | --- |
399
+ | `runTests` | `(RunTestsOptions) => Promise<TestRunReport>` | Build a module's main and `ohosTest` HAPs, install both, run `aa test` (Hypium's OpenHarmonyTestRunner) and parse it. `outcome` is `passed`, `failed` or `runner-error`; it does not throw for a failed case, build or run. Wakes and unlocks the screen first unless `wakeScreen: false`. |
400
+ | `parseTestOutput` | `(output) => ParsedTestOutput` | Pure parser of `aa test` output: cases with `status` and the raw `statusCode` (0 pass, -2 assertion failed, -1 threw), message, stack and duration; the `OHOS_REPORT_RESULT` summary; `TestFinished-ResultCode`/`-ResultMsg`; a dry run's case list. |
401
+ | `aaTestCommand` / `testHapPath` / `readTestModuleName` / `decideOutcome` | — | The pieces of `runTests`, exported for callers that drive the runner themselves. |
402
+ | `wakeAndUnlock` / `holdScreenOn` | — | Turn the screen on and swipe away a lock screen without a credential; keep the screen on (`power-shell timeout -o`) and restore it. |
403
+
257
404
  ### Project scaffolding & inspection
258
405
 
259
406
  | Export | Signature | Description |
@@ -288,11 +435,14 @@ HarmonyOS support is additive: a project is HarmonyOS only when its product decl
288
435
  | Export | Description |
289
436
  | --- | --- |
290
437
  | `detectRuntimeOs(projectDir, productName?)` | `'HarmonyOS'` or `'OpenHarmony'`, from `build-profile.json5`. |
291
- | `findHarmonyOsSdk({ config, logger? })` / `requireHarmonyOsSdk(...)` | Locate a HarmonyOS SDK (`harmonyosSdkPath`, then DevEco Studio's default location, then `cmdToolsPath` when it is the HarmonyOS edition), with its release as `version`, e.g. `6.1.1(24)`; `null` or an actionable error when there is none. |
438
+ | `findHarmonyOsSdk({ config, logger? })` / `requireHarmonyOsSdk(...)` | Locate a HarmonyOS SDK (`harmonyosSdkPath`, then DevEco Studio's default location, then `cmdToolsPath` when it is the HarmonyOS edition), with its release as `version`, e.g. `6.1.1(24)`, its `apiVersion` and its `source` (`configured`, `deveco-studio` or `cmdtools`); `null`, or an error naming `oniro-app cmdtools install`, when there is none. |
439
+ | `readSdkPkg(sdkPath)` | `SdkPkgInfo` (`displayName`, `version`, `platformVersion`, `apiVersion`, `build`) from `<sdkPath>/default/sdk-pkg.json`. |
292
440
  | `harmonyOsSdkLabelForApi(api)`, `HARMONYOS_SDK_VERSIONS` | The `"<version>(<api>)"` label HarmonyOS products declare. |
293
- | `createHarmonyOsSession(opts)` | `login` / `logout` / `getUserInfo` / `listTeams` / `resolveAgcAuth` / `getDeveloperAgreement` for a Huawei developer account. The token is stored encrypted, with the account's region. |
441
+ | `createHarmonyOsSession(opts)` | `login` / `logout` / `getUserInfo` / `listTeams` / `resolveAgcAuth` / `getDeveloperAgreement` for a Huawei developer account. The token is stored encrypted, with the account's region. `onLoginUrl(url, { port })` reports the sign-in URL and loopback port. |
442
+ | `session.login(HarmonyOsLoginOptions)` | `timeoutMs`; `port` to listen on a fixed loopback port (for an `ssh -L` tunnel); `openBrowser: false` to leave opening the URL to the user; `pastedCallbacks`, an `AsyncIterable<string>` (e.g. a `readline` interface over stdin) of callback addresses pasted from a browser on another machine, checked like a request to the loopback port. |
294
443
  | `harmonyOsAutoSign(opts)` | Keystore + CSR → AGC debug certificate → device registration → debug profile → `build-profile.json5`, reusing still-valid material. |
295
- | `readProvisionProfile(path)` | Bundle name, team, expiry, devices and ACLs of a `.p7b`. |
444
+ | `readProvisionProfile(path)` / `parseProvisionProfile(buffer)` | Type, issuer, bundle name, team, apl, app-feature, validity, devices, ACLs and certificates of a `.p7b`. |
445
+ | `describeProfileValidity(profile, now?)` / `formatUtc(date)` | `debug, valid until 2026-10-01 11:50 UTC`, as `sign --harmonyos` prints it. `harmonyOsAutoSign` and `harmonyOsSignHap` return the profile's `profileType` and `notAfter`. |
296
446
  | `HUAWEI_SITES` | Each region's sign-in and AppGallery Connect hosts. |
297
447
 
298
448
  ```ts