@absolutejs/absolute 0.20.0-beta.97 → 0.20.0-beta.98

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/CHANGELOG.md CHANGED
@@ -6,6 +6,13 @@ This file is generated by `absolute-changelog` from the entries in
6
6
  `changelog/`. Edit an entry, not this file — and add new ones under
7
7
  `changelog/unreleased/`.
8
8
 
9
+ ## 0.20.0-beta.98 — 2026-09-15
10
+
11
+ ### Added
12
+
13
+ - **Expo Android development now has a performance and accessibility quality gate** (`DEFAULT_ABSOLUTE_EXPO_ANDROID_QUALITY_BUDGETS`, `parseAbsoluteAndroidAccessibilityHierarchy`, `parseAbsoluteExpoAndroidLaunchTiming`, `parseAbsoluteExpoAndroidMemory`)
14
+ An installed API 36 gate measures cold and warm launch, bridge and all-framework HMR p95, memory growth, native rebuild/reconnect, and native plus embedded-web accessibility while emitting only sanitized timing artifacts.
15
+
9
16
  ## 0.20.0-beta.97 — 2026-09-15
10
17
 
11
18
  ### Fixed
package/changelog.json CHANGED
@@ -2,6 +2,23 @@
2
2
  "contract": 1,
3
3
  "name": "@absolutejs/absolute",
4
4
  "releases": [
5
+ {
6
+ "changes": [
7
+ {
8
+ "detail": "An installed API 36 gate measures cold and warm launch, bridge and all-framework HMR p95, memory growth, native rebuild/reconnect, and native plus embedded-web accessibility while emitting only sanitized timing artifacts.",
9
+ "kind": "added",
10
+ "summary": "Expo Android development now has a performance and accessibility quality gate",
11
+ "symbols": [
12
+ "DEFAULT_ABSOLUTE_EXPO_ANDROID_QUALITY_BUDGETS",
13
+ "parseAbsoluteAndroidAccessibilityHierarchy",
14
+ "parseAbsoluteExpoAndroidLaunchTiming",
15
+ "parseAbsoluteExpoAndroidMemory"
16
+ ]
17
+ }
18
+ ],
19
+ "date": "2026-09-15",
20
+ "version": "0.20.0-beta.98"
21
+ },
5
22
  {
6
23
  "changes": [
7
24
  {
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
- // .angular-partial-tmp-VPql4f/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-QYKjNJ/src/core/streamingSlotRegistrar.ts
5
5
  var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
6
6
  var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
7
7
  var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
- // .angular-partial-tmp-VPql4f/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-QYKjNJ/src/core/streamingSlotRegistrar.ts
5
5
  var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
6
6
  var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
7
7
  var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
@@ -48,7 +48,7 @@ var warnMissingStreamingSlotCollector = (primitiveName) => {
48
48
  getWarningController()?.maybeWarn(primitiveName);
49
49
  };
50
50
 
51
- // .angular-partial-tmp-VPql4f/src/core/streamingSlotRegistry.ts
51
+ // .angular-partial-tmp-QYKjNJ/src/core/streamingSlotRegistry.ts
52
52
  var STREAMING_SLOT_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotAsyncLocalStorage");
53
53
  var isObjectRecord2 = (value) => Boolean(value) && typeof value === "object";
54
54
  var isAsyncLocalStorage = (value) => isObjectRecord2(value) && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
@@ -31336,6 +31336,97 @@ var startAbsoluteExpoDevSession = async (options) => {
31336
31336
  throw error;
31337
31337
  }
31338
31338
  };
31339
+ // src/mobile/expoAndroidQuality.ts
31340
+ var KIB_PER_MIB = 1024;
31341
+ var XML_BOUND_BOTTOM_INDEX = 4;
31342
+ var XML_BOUND_LEFT_INDEX = 1;
31343
+ var XML_BOUND_RIGHT_INDEX = 3;
31344
+ var XML_BOUND_TOP_INDEX = 2;
31345
+ var DEFAULT_ABSOLUTE_EXPO_ANDROID_QUALITY_BUDGETS = {
31346
+ bridgeP95Ms: 500,
31347
+ coldLaunchMs: 25000,
31348
+ hmrP95Ms: 1e4,
31349
+ maxMemoryGrowthMiB: 160,
31350
+ maxTotalPssMiB: 700,
31351
+ minTouchTargetDp: 44,
31352
+ warmLaunchMs: 5000
31353
+ };
31354
+ var requiredInteger = (source, name) => {
31355
+ const match = new RegExp(`^${name}:\\s*(\\d+)$`, "mu").exec(source);
31356
+ const value = Number(match?.[1]);
31357
+ if (!Number.isSafeInteger(value))
31358
+ throw new TypeError(`Android launch output is missing ${name}.`);
31359
+ return value;
31360
+ };
31361
+ var optionalInteger = (source, name) => {
31362
+ const match = new RegExp(`^${name}:\\s*(\\d+)$`, "mu").exec(source);
31363
+ if (!match)
31364
+ return;
31365
+ const value = Number(match[1]);
31366
+ if (!Number.isSafeInteger(value))
31367
+ throw new TypeError(`Android launch output has invalid ${name}.`);
31368
+ return value;
31369
+ };
31370
+ var parseAbsoluteExpoAndroidLaunchTiming = (source) => ({
31371
+ launchState: /^LaunchState:\s*(\S+)$/mu.exec(source)?.[1],
31372
+ thisTimeMs: optionalInteger(source, "ThisTime"),
31373
+ totalTimeMs: requiredInteger(source, "TotalTime"),
31374
+ waitTimeMs: requiredInteger(source, "WaitTime")
31375
+ });
31376
+ var parseAbsoluteExpoAndroidMemory = (source) => {
31377
+ const summary = /^\s*TOTAL PSS:\s*(\d+)/mu.exec(source)?.[1];
31378
+ const table = /^\s*TOTAL\s+(\d+)\s+/mu.exec(source)?.[1];
31379
+ const totalPssKiB = Number(summary ?? table);
31380
+ if (!Number.isSafeInteger(totalPssKiB) || totalPssKiB < 0)
31381
+ throw new TypeError("Android meminfo output is missing total PSS.");
31382
+ return { totalPssKiB };
31383
+ };
31384
+ var decodeXml = (value) => value.replaceAll("&quot;", '"').replaceAll("&apos;", "'").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
31385
+ var nodeAttributes = (source) => Object.fromEntries([...source.matchAll(/([\w:-]+)="([^"]*)"/gu)].map(([, name = "", value = ""]) => [name, decodeXml(value)]));
31386
+ var nodeBounds = (source) => {
31387
+ const match = /^\[(\d+),(\d+)\]\[(\d+),(\d+)\]$/u.exec(source);
31388
+ if (!match)
31389
+ return { bottom: 0, left: 0, right: 0, top: 0 };
31390
+ return {
31391
+ bottom: Number(match[XML_BOUND_BOTTOM_INDEX]),
31392
+ left: Number(match[XML_BOUND_LEFT_INDEX]),
31393
+ right: Number(match[XML_BOUND_RIGHT_INDEX]),
31394
+ top: Number(match[XML_BOUND_TOP_INDEX])
31395
+ };
31396
+ };
31397
+ var absoluteAndroidNodeLabel = (node) => node.contentDescription.trim() || node.text.trim();
31398
+ var absoluteAndroidTouchTargetDp = (node, density) => {
31399
+ if (!Number.isFinite(density) || density <= 0)
31400
+ throw new TypeError("Android display density must be positive.");
31401
+ return {
31402
+ height: (node.bounds.bottom - node.bounds.top) / density,
31403
+ width: (node.bounds.right - node.bounds.left) / density
31404
+ };
31405
+ };
31406
+ var absolutePercentile = (values, ratio) => {
31407
+ if (values.length === 0)
31408
+ throw new TypeError("A percentile requires at least one measurement.");
31409
+ if (!Number.isFinite(ratio) || ratio < 0 || ratio > 1)
31410
+ throw new TypeError("A percentile ratio must be between zero and one.");
31411
+ const ordered = [...values].sort((left, right) => left - right);
31412
+ const index = Math.ceil(ratio * ordered.length) - 1;
31413
+ return ordered[Math.max(0, index)] ?? 0;
31414
+ };
31415
+ var absolutePssMiB = (memory) => memory.totalPssKiB / KIB_PER_MIB;
31416
+ var parseAbsoluteAndroidAccessibilityHierarchy = (source) => [...source.matchAll(/<node\s+([^>]*?)\/?>(?:<\/node>)?/gu)].map(([, raw = ""]) => {
31417
+ const attributes = nodeAttributes(raw);
31418
+ return {
31419
+ bounds: nodeBounds(attributes.bounds ?? ""),
31420
+ className: attributes.class ?? "",
31421
+ clickable: attributes.clickable === "true",
31422
+ contentDescription: attributes["content-desc"] ?? "",
31423
+ enabled: attributes.enabled === "true",
31424
+ packageName: attributes.package ?? "",
31425
+ resourceId: attributes["resource-id"] ?? "",
31426
+ text: attributes.text ?? "",
31427
+ visible: attributes["visible-to-user"] !== "false"
31428
+ };
31429
+ });
31339
31430
  // src/mobile/expoNativeWatcher.ts
31340
31431
  import { watch as watch2 } from "fs";
31341
31432
  import { access as access12, readdir as readdir5, readFile as readFile16 } from "fs/promises";
@@ -40794,8 +40885,11 @@ export {
40794
40885
  ANDROID_ASSOCIATION_PATH,
40795
40886
  APPLE_ASSOCIATION_PATH,
40796
40887
  AbsoluteMobilePageProtocolError,
40888
+ DEFAULT_ABSOLUTE_EXPO_ANDROID_QUALITY_BUDGETS,
40797
40889
  DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE,
40798
40890
  MOBILE_PAGE_REQUEST_HEADERS,
40891
+ absoluteAndroidNodeLabel,
40892
+ absoluteAndroidTouchTargetDp,
40799
40893
  absoluteDeviceNativeRequirements,
40800
40894
  absoluteExpoExecutable,
40801
40895
  absoluteExpoNativeObservabilityFiles,
@@ -40803,6 +40897,8 @@ export {
40803
40897
  absoluteIosDeviceAcceptanceCommands,
40804
40898
  absoluteMobilePreviewDocument,
40805
40899
  absoluteMobileUpdateSigningPayload,
40900
+ absolutePercentile,
40901
+ absolutePssMiB,
40806
40902
  absoluteRemoteMacSshBase,
40807
40903
  absoluteRemoteProjectSyncCommands,
40808
40904
  absoluteRemoteReleaseInputSyncCommands,
@@ -40918,7 +41014,10 @@ export {
40918
41014
  normalizeAbsoluteMobileUpdatePath,
40919
41015
  openAbsoluteMobileSheet,
40920
41016
  pairAbsoluteRemoteMac,
41017
+ parseAbsoluteAndroidAccessibilityHierarchy,
40921
41018
  parseAbsoluteAndroidInstalledApp,
41019
+ parseAbsoluteExpoAndroidLaunchTiming,
41020
+ parseAbsoluteExpoAndroidMemory,
40922
41021
  parseAbsoluteExpoBridgeMessage,
40923
41022
  parseAbsoluteExpoUpdateDescriptor,
40924
41023
  parseAbsoluteIosHmrLog,
@@ -40996,5 +41095,5 @@ export {
40996
41095
  writeAbsoluteMobileUpdateRegistry
40997
41096
  };
40998
41097
 
40999
- //# debugId=CC8C4D11922DDEB264756E2164756E21
41098
+ //# debugId=35DF25B2FF31C85164756E2164756E21
41000
41099
  //# sourceMappingURL=index.js.map