@absolutejs/absolute 0.20.0-beta.113 → 0.20.0-beta.115

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +6 -0
  3. package/changelog.json +44 -0
  4. package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
  5. package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
  6. package/dist/build.js +54 -2
  7. package/dist/build.js.map +3 -3
  8. package/dist/cli/{compile-1d5arbqj.js → compile-ya4kd2sg.js} +3 -3
  9. package/dist/cli/{config-vq2qcc4p.js → config-rjeexvza.js} +1 -1
  10. package/dist/cli/{dev-f0safhsr.js → dev-5cqmf7za.js} +2 -2
  11. package/dist/cli/{expoProject-zkprmjjz.js → expoProject-f5j20xms.js} +1 -1
  12. package/dist/cli/{index-gps0zk0m.js → index-5fmgn0nx.js} +2 -2
  13. package/dist/cli/{index-dfqzrznk.js → index-6mbm4n3g.js} +2 -2
  14. package/dist/cli/{index-0zcebhm4.js → index-dr21p6ae.js} +566 -83
  15. package/dist/cli/{index-df2mjbdy.js → index-exdd5k1g.js} +55 -0
  16. package/dist/cli/index.js +6 -6
  17. package/dist/cli/{mobile-v11nwcj9.js → mobile-fg2226vv.js} +153 -8
  18. package/dist/cli/{start-8v6g2y31.js → start-jt61f40d.js} +4 -4
  19. package/dist/index.js +54 -2
  20. package/dist/index.js.map +3 -3
  21. package/dist/mobile/index.js +1478 -895
  22. package/dist/mobile/index.js.map +9 -7
  23. package/dist/mobile/remoteMacAgentEntry.js +243 -242
  24. package/dist/react/hooks/index.js +6 -2
  25. package/dist/react/hooks/index.js.map +3 -3
  26. package/dist/src/mobile/androidServiceReadiness.d.ts +14 -0
  27. package/dist/src/mobile/branding.d.ts +92 -0
  28. package/dist/src/mobile/config.d.ts +20 -0
  29. package/dist/src/mobile/index.d.ts +1 -0
  30. package/dist/src/react/hooks/useMediaQuery.d.ts +3 -1
  31. package/dist/src/utils/userAgentFunctions.d.ts +1 -1
  32. package/dist/types/build.d.ts +36 -0
  33. package/package.json +2 -1
@@ -11,6 +11,7 @@ var UPDATE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
11
11
  var RELEASE_TARGET_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/u;
12
12
  var UPDATE_PUBLIC_KEY_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/u;
13
13
  var ENVIRONMENT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/u;
14
+ var COLOR_PATTERN = /^#[0-9A-F]{6}$/u;
14
15
  var DEFAULT_UPDATE_BOOT_TIMEOUT_MS = 20000;
15
16
  var DEFAULT_UPDATE_HEALTH_FAILURE_RATE = 0.2;
16
17
  var DEFAULT_UPDATE_HEALTH_MINIMUM_REPORTS = 20;
@@ -43,6 +44,58 @@ var resolveProjectPath = (projectRoot, value, field) => {
43
44
  }
44
45
  return path;
45
46
  };
47
+ var normalizeBrandingColor = (value, fallback, field) => {
48
+ const normalized = (value ?? fallback).trim().toUpperCase();
49
+ if (!COLOR_PATTERN.test(normalized))
50
+ throw new TypeError(`${field} must use #RRGGBB hexadecimal notation.`);
51
+ return normalized;
52
+ };
53
+ var normalizeBrandingPath = (projectRoot, value, field) => {
54
+ const path = resolveProjectPath(projectRoot, requireText(value, field), field);
55
+ if (!path.toLowerCase().endsWith(".png"))
56
+ throw new TypeError(`${field} must reference a PNG file.`);
57
+ return path;
58
+ };
59
+ var normalizeBranding = (config, projectRoot) => {
60
+ const { branding } = config;
61
+ if (!branding)
62
+ return;
63
+ const darkIcon = branding.ios?.darkIcon ? normalizeBrandingPath(projectRoot, branding.ios.darkIcon, "mobile.branding.ios.darkIcon") : undefined;
64
+ const tintedIcon = branding.ios?.tintedIcon ? normalizeBrandingPath(projectRoot, branding.ios.tintedIcon, "mobile.branding.ios.tintedIcon") : undefined;
65
+ const logoScale = branding.splash?.logoScale ?? 0.2;
66
+ if (!Number.isFinite(logoScale) || logoScale < 0.1 || logoScale > 0.4)
67
+ throw new TypeError("mobile.branding.splash.logoScale must be between 0.1 and 0.4.");
68
+ return {
69
+ android: {
70
+ backgroundColor: normalizeBrandingColor(branding.android?.backgroundColor, "#FFFFFF", "mobile.branding.android.backgroundColor"),
71
+ ...branding.android?.backgroundImage ? {
72
+ backgroundImage: normalizeBrandingPath(projectRoot, branding.android.backgroundImage, "mobile.branding.android.backgroundImage")
73
+ } : {},
74
+ ...branding.android?.foreground ? {
75
+ foreground: normalizeBrandingPath(projectRoot, branding.android.foreground, "mobile.branding.android.foreground")
76
+ } : {},
77
+ ...branding.android?.monochrome ? {
78
+ monochrome: normalizeBrandingPath(projectRoot, branding.android.monochrome, "mobile.branding.android.monochrome")
79
+ } : {}
80
+ },
81
+ icon: normalizeBrandingPath(projectRoot, branding.icon, "mobile.branding.icon"),
82
+ ios: {
83
+ ...darkIcon ? { darkIcon } : {},
84
+ ...tintedIcon ? { tintedIcon } : {}
85
+ },
86
+ splash: {
87
+ backgroundColor: normalizeBrandingColor(branding.splash?.backgroundColor, "#FFFFFF", "mobile.branding.splash.backgroundColor"),
88
+ darkBackgroundColor: normalizeBrandingColor(branding.splash?.darkBackgroundColor, "#111111", "mobile.branding.splash.darkBackgroundColor"),
89
+ ...branding.splash?.darkImage ? {
90
+ darkImage: normalizeBrandingPath(projectRoot, branding.splash.darkImage, "mobile.branding.splash.darkImage")
91
+ } : {},
92
+ ...branding.splash?.image ? {
93
+ image: normalizeBrandingPath(projectRoot, branding.splash.image, "mobile.branding.splash.image")
94
+ } : {},
95
+ logoScale
96
+ }
97
+ };
98
+ };
46
99
  var requireText = (value, field) => {
47
100
  const normalized = value.trim();
48
101
  if (!normalized)
@@ -445,11 +498,13 @@ var normalizeAbsoluteMobileConfig = (config, projectRoot) => {
445
498
  const updates = normalizeUpdates(config, productionOrigin, projectRoot);
446
499
  const updateServer = normalizeUpdateServer(config, productionOrigin, projectRoot, updates);
447
500
  const observability = normalizeObservability(config, productionOrigin);
501
+ const branding = normalizeBranding(config, projectRoot);
448
502
  return {
449
503
  androidCertificateFingerprints: normalizeCertificateFingerprints(config.deepLinks?.android?.sha256CertificateFingerprints),
450
504
  appId,
451
505
  appleAppIdPrefix: normalizeAppleAppIdPrefix(config.deepLinks?.apple?.appIdPrefix),
452
506
  appName: requireText(config.appName, "mobile.appName"),
507
+ ...branding ? { branding } : {},
453
508
  bundleDirectory: resolveProjectPath(projectRoot, config.bundleDirectory ?? ".absolutejs/mobile/web", "mobile.bundleDirectory"),
454
509
  deepLinkHosts: normalizeHosts(config.deepLinks?.hosts, productionOrigin),
455
510
  deepLinkScheme,
package/dist/cli/index.js CHANGED
@@ -58,7 +58,7 @@ if (command === "dev") {
58
58
  }
59
59
  const positionalArgs = stripNamedArgs("--config", "--android-device", "--ios-device").filter((arg) => arg !== "--no-mobile" && arg !== "--eager");
60
60
  const serverEntry = positionalArgs[0] ?? DEFAULT_SERVER_ENTRY;
61
- const { dev } = await import("./dev-f0safhsr.js");
61
+ const { dev } = await import("./dev-5cqmf7za.js");
62
62
  await dev(serverEntry, configPath, {
63
63
  androidDevice,
64
64
  eager: args.includes("--eager"),
@@ -71,7 +71,7 @@ if (command === "dev") {
71
71
  const configPath = parseNamedArg("--config");
72
72
  const positionalArgs = stripNamedArgs("--outdir", "--config").filter((arg) => arg !== "--prebuilt");
73
73
  const serverEntry = positionalArgs[0] ?? DEFAULT_SERVER_ENTRY;
74
- const { start } = await import("./start-8v6g2y31.js");
74
+ const { start } = await import("./start-jt61f40d.js");
75
75
  await start(serverEntry, outdir, configPath, {
76
76
  prebuilt: args.includes("--prebuilt")
77
77
  });
@@ -81,7 +81,7 @@ if (command === "dev") {
81
81
  const configPath = parseNamedArg("--config");
82
82
  const positionalArgs = stripNamedArgs("--outdir", "--config");
83
83
  const serverEntry = positionalArgs[0] ?? DEFAULT_SERVER_ENTRY;
84
- const { start } = await import("./start-8v6g2y31.js");
84
+ const { start } = await import("./start-jt61f40d.js");
85
85
  await start(serverEntry, outdir, configPath, { prepareOnly: true });
86
86
  } else if (command === "build") {
87
87
  sendTelemetryEvent("cli:command", { command });
@@ -197,13 +197,13 @@ if (command === "dev") {
197
197
  const configPath = parseNamedArg("--config");
198
198
  const positionalArgs = stripNamedArgs("--outdir", "--outfile", "--config");
199
199
  const serverEntry = positionalArgs[0] ?? DEFAULT_SERVER_ENTRY;
200
- const { compile } = await import("./compile-1d5arbqj.js");
200
+ const { compile } = await import("./compile-ya4kd2sg.js");
201
201
  await compile(serverEntry, outdir, outfile, configPath);
202
202
  } else if (command === "mobile") {
203
203
  sendTelemetryEvent("cli:command", {
204
204
  command: `mobile:${workspaceCommand ?? "unknown"}`
205
205
  });
206
- const { runMobile } = await import("./mobile-v11nwcj9.js");
206
+ const { runMobile } = await import("./mobile-fg2226vv.js");
207
207
  try {
208
208
  await runMobile(args);
209
209
  } catch (error) {
@@ -238,7 +238,7 @@ if (command === "dev") {
238
238
  console.error(" prepare [entry] [--outdir dir] Build production assets and server without launching");
239
239
  console.error(" start [entry] [--outdir dir] [--prebuilt] Start production server");
240
240
  console.error(" compile [entry] [--outdir dir] [--outfile path] Compile standalone executable");
241
- console.error(" mobile <init|sync|inspect|ci|pair|remotes|doctor|test> Manage Capacitor apps and the experimental Expo hybrid shell");
241
+ console.error(" mobile <init|sync|assets|inspect|ci|pair|remotes|doctor|test> Manage Capacitor apps and the experimental Expo hybrid shell");
242
242
  console.error(" config [--port n] Open the unified config UI (ESLint, tsconfig, Prettier)");
243
243
  console.error(" db <backup|restore|seed> Backup/restore any Postgres DB (ORM-agnostic, upsert by PK) or run the seed script");
244
244
  console.error(" doctor [--fix] [--json] Diagnose the project (bun, type graph, config, framework dirs, env, port)");
@@ -7,12 +7,12 @@ import {
7
7
  } from "./index-zh6hhrwy.js";
8
8
  import {
9
9
  start
10
- } from "./index-gps0zk0m.js";
10
+ } from "./index-5fmgn0nx.js";
11
11
  import {
12
12
  ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
13
13
  ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
14
14
  resolveAbsoluteMobileRoute
15
- } from "./index-dfqzrznk.js";
15
+ } from "./index-6mbm4n3g.js";
16
16
  import"./index-bzs19w3r.js";
17
17
  import"./index-s2tazax7.js";
18
18
  import {
@@ -56,21 +56,25 @@ import {
56
56
  import"./index-w5vswatm.js";
57
57
  import {
58
58
  normalizeAbsoluteMobileConfig
59
- } from "./index-df2mjbdy.js";
59
+ } from "./index-exdd5k1g.js";
60
60
  import {
61
+ ABSOLUTE_CAPACITOR_ASSETS_SPEC,
61
62
  ABSOLUTE_MOBILE_UPDATE_FORMAT,
62
63
  absoluteDeviceNativeRequirements,
63
64
  absoluteMobileUpdateSigningPayload,
64
65
  assertAbsoluteDeviceCapabilityPackages,
65
66
  canonicalizeAbsoluteMobileUpdate,
66
67
  discoverAbsoluteSyncSchema,
68
+ generateAbsoluteCapacitorBranding,
69
+ inspectAbsoluteMobileBranding,
67
70
  parseAbsoluteMobileUpdateManifest,
71
+ renderAbsoluteMobileBrandingPreview,
68
72
  resolveAbsoluteDeviceCapabilityPlan,
69
73
  resolveAbsoluteMobileUpdateRuntime,
70
74
  syncAbsoluteExpoWebAssets,
71
75
  unsignedAbsoluteMobileUpdate,
72
76
  writeAbsoluteExpoProject
73
- } from "./index-0zcebhm4.js";
77
+ } from "./index-dr21p6ae.js";
74
78
  import {
75
79
  buildAbsoluteExpoAndroidWslBundle
76
80
  } from "./index-28nxv57v.js";
@@ -19779,6 +19783,14 @@ var mobileUpdateServerCheck = async (config, projectRoot) => {
19779
19783
  return fail("updates.trusted-server", error instanceof Error ? error.message : "The trusted mobile update server is not release-ready.", config.updateServer?.registryModule ?? "mobile.update.ts", "Run `absolute mobile update provision --storage s3 --force`, provision its environment variables on the trusted server, and rerun the release doctor.");
19780
19784
  }
19781
19785
  };
19786
+ var mobileBrandingCheck = async (config, projectRoot) => {
19787
+ const inspection = await inspectAbsoluteMobileBranding(config, projectRoot);
19788
+ if (inspection.status === "absent")
19789
+ return warn("mobile.branding", inspection.detail, undefined, "Configure mobile.branding and run `absolute mobile assets` before store submission.");
19790
+ if (!inspection.ready)
19791
+ return fail("mobile.branding", inspection.detail, inspection.manifestPath, "Run `absolute mobile assets`, review the generated preview, and commit the native resource changes.");
19792
+ return pass("mobile.branding", inspection.detail, inspection.manifestPath);
19793
+ };
19782
19794
  var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
19783
19795
  var readJsonObject = async (path) => {
19784
19796
  const value = JSON.parse(await readFile9(path, "utf8"));
@@ -20538,6 +20550,7 @@ var inspectAbsoluteMobileRelease = async (config, projectRoot) => {
20538
20550
  Promise.resolve(associationIdentityCheck(config, projectRoot)),
20539
20551
  dependencyLockCheck(projectRoot),
20540
20552
  config.engine === "expo" ? expoVersionCheck(config) : capacitorVersionCheck(config, projectRoot),
20553
+ mobileBrandingCheck(config, projectRoot),
20541
20554
  mobileUpdateServerCheck(config, projectRoot)
20542
20555
  ]);
20543
20556
  const checks = globalChecks.filter((check) => check !== undefined).map((check) => ({
@@ -20822,6 +20835,40 @@ var buildAbsoluteAndroidRelease = async (options) => {
20822
20835
 
20823
20836
  // src/mobile/androidReleaseAcceptance.ts
20824
20837
  import { createHash as createHash3 } from "crypto";
20838
+
20839
+ // src/mobile/androidServiceReadiness.ts
20840
+ var SERVICE_TIMEOUT_MS = 180000;
20841
+ var SERVICE_POLL_MS = 500;
20842
+ var waitForAbsoluteAndroidNetworkServices = async (options) => {
20843
+ const startedAt = performance.now();
20844
+ const waitForServices = async () => {
20845
+ const states = await Promise.all(["wifi", "phone"].map(async (service) => {
20846
+ const result = await options.run([
20847
+ options.adb,
20848
+ "-s",
20849
+ options.serial,
20850
+ "shell",
20851
+ "service",
20852
+ "check",
20853
+ service
20854
+ ]);
20855
+ return {
20856
+ ready: result.exitCode === 0 && result.stdout.trim() === `Service ${service}: found`,
20857
+ service
20858
+ };
20859
+ }));
20860
+ const missing = states.filter(({ ready }) => !ready).map(({ service }) => service);
20861
+ if (missing.length === 0)
20862
+ return;
20863
+ if (performance.now() - startedAt >= (options.timeoutMs ?? SERVICE_TIMEOUT_MS))
20864
+ throw new Error(`Android network services did not become ready: ${missing.join(", ")}. Finish booting or restart the emulator before installed offline acceptance; no network settings were changed.`);
20865
+ await Bun.sleep(options.pollMs ?? SERVICE_POLL_MS);
20866
+ await waitForServices();
20867
+ };
20868
+ await waitForServices();
20869
+ };
20870
+
20871
+ // src/mobile/androidReleaseAcceptance.ts
20825
20872
  import {
20826
20873
  access as access6,
20827
20874
  mkdir as mkdir8,
@@ -21113,6 +21160,11 @@ var runAbsoluteAndroidReleaseAcceptance = async (options) => {
21113
21160
  if (options.release.metadata.versionCode !== undefined && installed.versionCode !== options.release.metadata.versionCode)
21114
21161
  throw new Error(`Installed Android versionCode ${installed.versionCode ?? "unknown"} does not match release ${options.release.metadata.versionCode}.`);
21115
21162
  await requireSuccess(run, adb(options.adb, options.serial, "logcat", "-c"), "Android release log isolation");
21163
+ await waitForAbsoluteAndroidNetworkServices({
21164
+ adb: options.adb,
21165
+ run,
21166
+ serial: options.serial
21167
+ });
21116
21168
  const [wifi, mobile] = await Promise.all([
21117
21169
  requireSuccess(run, adb(options.adb, options.serial, "shell", "settings", "get", "global", "wifi_on"), "Android Wi-Fi state inspection"),
21118
21170
  requireSuccess(run, adb(options.adb, options.serial, "shell", "settings", "get", "global", "mobile_data"), "Android mobile-data state inspection")
@@ -24983,13 +25035,13 @@ var resolvedPackageVersion = async (projectRoot, packageName) => {
24983
25035
  }
24984
25036
  };
24985
25037
  var exactVersionFromSpec = (spec) => spec.slice(spec.lastIndexOf("@") + 1);
24986
- var installApprovedPackages = async (projectRoot, args, message, specs) => {
25038
+ var installApprovedPackages = async (projectRoot, args, message, specs, dev = false) => {
24987
25039
  if (specs.length === 0)
24988
25040
  return;
24989
25041
  const approved = args.includes("--yes") || await confirmInstall(message);
24990
25042
  if (!approved)
24991
- throw new TypeError(`Mobile initialization requires: bun add ${specs.join(" ")}`);
24992
- if (!installPackages(projectRoot, specs))
25043
+ throw new TypeError(`Mobile tooling requires: bun add${dev ? " --dev" : ""} ${specs.join(" ")}`);
25044
+ if (!installPackages(projectRoot, specs, dev))
24993
25045
  throw new TypeError("Failed to install the AbsoluteJS mobile toolchain.");
24994
25046
  };
24995
25047
  var packagesNeedingExactInstall = async (projectRoot, specs, installed, exactPackages) => (await Promise.all(specs.map(async (spec) => {
@@ -25010,6 +25062,11 @@ var ensureCapacitorPackages = async (projectRoot, args, mobile) => {
25010
25062
  const directCapabilityPackages = await directProjectPackages(projectRoot);
25011
25063
  const capabilityPackages = await packagesNeedingExactInstall(projectRoot, capabilityPlan.requiredPackages, directCapabilityPackages, new Set(capabilityPlan.requiredPackages.map(packageNameFromSpec)));
25012
25064
  await installApprovedPackages(projectRoot, args, `AbsoluteJS detected native device capabilities (${capabilityPlan.capabilities.join(", ")}). Install only their required Capacitor plugins now?`, capabilityPackages);
25065
+ if (mobile?.branding) {
25066
+ const installedBrandingPackages = await directProjectPackages(projectRoot);
25067
+ const brandingPackages = await packagesNeedingExactInstall(projectRoot, [ABSOLUTE_CAPACITOR_ASSETS_SPEC], installedBrandingPackages, new Set(["@capacitor/assets"]));
25068
+ await installApprovedPackages(projectRoot, args, "AbsoluteJS mobile branding uses the pinned official Capacitor asset generator. Install it as a development dependency now?", brandingPackages, true);
25069
+ }
25013
25070
  };
25014
25071
  var valueAfter = (args, flag) => {
25015
25072
  const index = args.indexOf(flag);
@@ -25234,6 +25291,8 @@ var initialize = async (args) => {
25234
25291
  if (args.includes("--no-native"))
25235
25292
  return;
25236
25293
  await runCapacitorForPlatforms(projectRoot, "add", mobile.platforms);
25294
+ if (mobile.branding)
25295
+ await generateAbsoluteCapacitorBranding(mobile, projectRoot);
25237
25296
  await applyAbsoluteNativeDeepLinks(mobile);
25238
25297
  await applyAbsoluteNativeDeviceCapabilities(projectRoot, mobile);
25239
25298
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile);
@@ -25270,6 +25329,10 @@ var sync = async (args) => {
25270
25329
  if (platforms.includes("ios"))
25271
25330
  await repairAbsoluteIosDevSession(projectRoot);
25272
25331
  await runCapacitorForPlatforms(projectRoot, "sync", platforms);
25332
+ if (mobile.branding)
25333
+ await generateAbsoluteCapacitorBranding(mobile, projectRoot, {
25334
+ platforms
25335
+ });
25273
25336
  await applyAbsoluteNativeDeepLinks(mobile, platforms);
25274
25337
  await applyAbsoluteNativeDeviceCapabilities(projectRoot, mobile, platforms);
25275
25338
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, platforms);
@@ -25277,6 +25340,84 @@ var sync = async (args) => {
25277
25340
  await applyAbsoluteNativeObservability(mobile, platforms);
25278
25341
  await applyAbsoluteNativeReleaseReadiness(mobile, platforms);
25279
25342
  };
25343
+ var printMobileBrandingInspection = (inspection, json) => {
25344
+ if (json)
25345
+ console.log(JSON.stringify(inspection, null, 2));
25346
+ else
25347
+ console.log(inspection.detail);
25348
+ };
25349
+ var checkMobileBranding = async (mobile, projectRoot, json) => {
25350
+ const inspection = await inspectAbsoluteMobileBranding(mobile, projectRoot);
25351
+ printMobileBrandingInspection(inspection, json);
25352
+ if (!inspection.ready)
25353
+ throw new TypeError("Mobile branding is not current. Run `absolute mobile assets`.");
25354
+ return inspection;
25355
+ };
25356
+ var generateMobileBranding = async (args, mobile, platforms, projectRoot) => {
25357
+ if (!mobile.branding)
25358
+ throw new TypeError("absolute.config.ts must define mobile.branding before generating mobile assets.");
25359
+ if (mobile.engine !== "expo") {
25360
+ await ensureCapacitorPackages(projectRoot, args, mobile);
25361
+ await generateAbsoluteCapacitorBranding(mobile, projectRoot, {
25362
+ platforms
25363
+ });
25364
+ return;
25365
+ }
25366
+ await writeAbsoluteExpoProject(mobile, {
25367
+ force: args.includes("--force"),
25368
+ projectRoot
25369
+ });
25370
+ await ensureExpoPackages(mobile.nativeProjectDirectory, args);
25371
+ await runExpo(mobile.nativeProjectDirectory, [
25372
+ "prebuild",
25373
+ "--no-install",
25374
+ "--platform",
25375
+ platforms.length === 2 ? "all" : platforms[0] ?? "all"
25376
+ ]);
25377
+ };
25378
+ var renderMobileBrandingResult = (inspection, json, preview, projectRoot) => {
25379
+ const result = {
25380
+ ...inspection,
25381
+ ...preview ? { preview: relative17(projectRoot, preview).replaceAll("\\", "/") } : {}
25382
+ };
25383
+ if (json)
25384
+ console.log(JSON.stringify(result, null, 2));
25385
+ else
25386
+ console.log(`\u2713 ${inspection.detail}`);
25387
+ if (!json && result.preview)
25388
+ console.log(`Preview: ${result.preview}`);
25389
+ return result;
25390
+ };
25391
+ var assets = async (args) => {
25392
+ const startedAt = performance.now();
25393
+ const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
25394
+ const requested = args.find((value) => value === "android" || value === "ios");
25395
+ if (requested && !mobile.platforms.includes(requested))
25396
+ throw new TypeError(`Mobile platform ${requested} is not enabled in mobile.platforms.`);
25397
+ const platforms = requested ? [requested] : [...mobile.platforms];
25398
+ try {
25399
+ if (args.includes("--check"))
25400
+ return await checkMobileBranding(mobile, projectRoot, args.includes("--json"));
25401
+ await generateMobileBranding(args, mobile, platforms, projectRoot);
25402
+ const preview = args.includes("--preview") ? await renderAbsoluteMobileBrandingPreview(mobile, projectRoot) : undefined;
25403
+ const inspection = await inspectAbsoluteMobileBranding(mobile, projectRoot);
25404
+ sendTelemetryEvent("mobile:assets", {
25405
+ durationMs: Math.round(performance.now() - startedAt),
25406
+ engine: mobile.engine,
25407
+ platforms: platforms.join(","),
25408
+ success: true
25409
+ });
25410
+ return renderMobileBrandingResult(inspection, args.includes("--json"), preview, projectRoot);
25411
+ } catch (error) {
25412
+ sendTelemetryEvent("mobile:assets", {
25413
+ durationMs: Math.round(performance.now() - startedAt),
25414
+ engine: mobile.engine,
25415
+ platforms: platforms.join(","),
25416
+ success: false
25417
+ });
25418
+ throw error;
25419
+ }
25420
+ };
25280
25421
  var associations = async (args) => {
25281
25422
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
25282
25423
  const outputDirectory = resolve18(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
@@ -28269,6 +28410,10 @@ var runMobile = async (args) => {
28269
28410
  await sync(args.slice(1));
28270
28411
  return;
28271
28412
  }
28413
+ if (command === "assets") {
28414
+ await assets(args.slice(1));
28415
+ return;
28416
+ }
28272
28417
  if (command === "associations") {
28273
28418
  await associations(args.slice(1));
28274
28419
  return;
@@ -28364,7 +28509,7 @@ var runMobile = async (args) => {
28364
28509
  await publishIos(args.slice(2));
28365
28510
  return;
28366
28511
  }
28367
- throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | certify <release-dir> [--evidence report-dir]... [--require installed|simulator|device|store] [--outdir dir] [--json] | certify <release-dir> --verify certification-dir [--require installed|simulator|device|store] [--json] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | ci promote <android|ios> --run-id id --certification path [--channel name] [--play-track track|--testflight-group group] [--ref branch] [--repo owner/name] [--watch] [--audit] [--outdir path] [--json] | ci promote --resume dispatch-id [--watch] [--audit] [--outdir path] [--json] | ci promotions [--json] | ci status --run-id id [--watch] [--repo owner/name] [--json] | ci audit --run-id id [--source-run-id id] [--repo owner/name] [--outdir path] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--registered-device-artifact] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--registry module] [--json] | update advance [--rollout fraction] [--registry module] [--json] | update pause|resume|cancel|reconcile [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--release release-dir] [--certification certification-dir] [--certification-attestation verification-json] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--release release-dir] [--certification certification-dir] [--certification-attestation verification-json] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--release release-dir [--yes] | --route path [--wait-for-hmr] [--port n]] [--report [dir]] [--serial id] [--artifacts dir] [--json] [--config path] | test ios [--release release-dir [--remote name] [--device id [--testflight] --yes] | --wait-for-hmr] [--report [dir]] [--udid id] [--artifacts dir] [--json] [--config path]");
28512
+ throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | assets [ios|android] [--check] [--preview] [--json] [--yes] | inspect [--json] [--require-bundle] | certify <release-dir> [--evidence report-dir]... [--require installed|simulator|device|store] [--outdir dir] [--json] | certify <release-dir> --verify certification-dir [--require installed|simulator|device|store] [--json] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | ci promote <android|ios> --run-id id --certification path [--channel name] [--play-track track|--testflight-group group] [--ref branch] [--repo owner/name] [--watch] [--audit] [--outdir path] [--json] | ci promote --resume dispatch-id [--watch] [--audit] [--outdir path] [--json] | ci promotions [--json] | ci status --run-id id [--watch] [--repo owner/name] [--json] | ci audit --run-id id [--source-run-id id] [--repo owner/name] [--outdir path] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--registered-device-artifact] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--registry module] [--json] | update advance [--rollout fraction] [--registry module] [--json] | update pause|resume|cancel|reconcile [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--release release-dir] [--certification certification-dir] [--certification-attestation verification-json] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--release release-dir] [--certification certification-dir] [--certification-attestation verification-json] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--release release-dir [--yes] | --route path [--wait-for-hmr] [--port n]] [--report [dir]] [--serial id] [--artifacts dir] [--json] [--config path] | test ios [--release release-dir [--remote name] [--device id [--testflight] --yes] | --wait-for-hmr] [--report [dir]] [--udid id] [--artifacts dir] [--json] [--config path]");
28368
28513
  };
28369
28514
  export {
28370
28515
  runMobile
@@ -1,8 +1,8 @@
1
1
  // @bun
2
2
  import {
3
3
  start
4
- } from "./index-gps0zk0m.js";
5
- import"./index-dfqzrznk.js";
4
+ } from "./index-5fmgn0nx.js";
5
+ import"./index-6mbm4n3g.js";
6
6
  import"./index-bzs19w3r.js";
7
7
  import"./index-s2tazax7.js";
8
8
  import"./index-fc9vxw56.js";
@@ -10,8 +10,8 @@ import"./index-r8x3839e.js";
10
10
  import"./index-06cmrcdq.js";
11
11
  import"./index-9kwmb3c0.js";
12
12
  import"./index-w5vswatm.js";
13
- import"./index-df2mjbdy.js";
14
- import"./index-0zcebhm4.js";
13
+ import"./index-exdd5k1g.js";
14
+ import"./index-dr21p6ae.js";
15
15
  import"./index-ar4gz15x.js";
16
16
  import"./index-t84wxk1p.js";
17
17
  import"./index-wtfnv3n2.js";
package/dist/index.js CHANGED
@@ -10644,13 +10644,62 @@ __export(exports_config, {
10644
10644
  import { readFileSync as readFileSync11 } from "fs";
10645
10645
  import { resolve as resolve12 } from "path";
10646
10646
  import { createHash as createHash4, createPublicKey, X509Certificate } from "crypto";
10647
- var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FINGERPRINT_PATTERN, UPDATE_NAME_PATTERN, RELEASE_TARGET_PATTERN, UPDATE_PUBLIC_KEY_PATTERN, ENVIRONMENT_NAME_PATTERN, DEFAULT_UPDATE_BOOT_TIMEOUT_MS = 20000, DEFAULT_UPDATE_HEALTH_FAILURE_RATE = 0.2, DEFAULT_UPDATE_HEALTH_MINIMUM_REPORTS = 20, DEFAULT_UPDATE_ROLLOUT_FAILURE_RATE = 0.05, DEFAULT_UPDATE_ROLLOUT_OBSERVATION_MINUTES = 60, MINUTE_MS, DEFAULT_UPDATE_ROLLOUT_STAGES, MINIMUM_UPDATE_BOOT_TIMEOUT_MS = 5000, MAXIMUM_UPDATE_BOOT_TIMEOUT_MS = 120000, HOSTNAME_PATTERN, EXPO_RESERVED_ROUTE_PREFIXES, resolveProjectPath = (projectRoot, value, field) => {
10647
+ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FINGERPRINT_PATTERN, UPDATE_NAME_PATTERN, RELEASE_TARGET_PATTERN, UPDATE_PUBLIC_KEY_PATTERN, ENVIRONMENT_NAME_PATTERN, COLOR_PATTERN, DEFAULT_UPDATE_BOOT_TIMEOUT_MS = 20000, DEFAULT_UPDATE_HEALTH_FAILURE_RATE = 0.2, DEFAULT_UPDATE_HEALTH_MINIMUM_REPORTS = 20, DEFAULT_UPDATE_ROLLOUT_FAILURE_RATE = 0.05, DEFAULT_UPDATE_ROLLOUT_OBSERVATION_MINUTES = 60, MINUTE_MS, DEFAULT_UPDATE_ROLLOUT_STAGES, MINIMUM_UPDATE_BOOT_TIMEOUT_MS = 5000, MAXIMUM_UPDATE_BOOT_TIMEOUT_MS = 120000, HOSTNAME_PATTERN, EXPO_RESERVED_ROUTE_PREFIXES, resolveProjectPath = (projectRoot, value, field) => {
10648
10648
  const root = resolve12(projectRoot);
10649
10649
  const path = resolve12(root, value);
10650
10650
  if (path !== root && !path.startsWith(`${root}/`)) {
10651
10651
  throw new TypeError(`${field} must remain inside the project root.`);
10652
10652
  }
10653
10653
  return path;
10654
+ }, normalizeBrandingColor = (value, fallback, field) => {
10655
+ const normalized = (value ?? fallback).trim().toUpperCase();
10656
+ if (!COLOR_PATTERN.test(normalized))
10657
+ throw new TypeError(`${field} must use #RRGGBB hexadecimal notation.`);
10658
+ return normalized;
10659
+ }, normalizeBrandingPath = (projectRoot, value, field) => {
10660
+ const path = resolveProjectPath(projectRoot, requireText(value, field), field);
10661
+ if (!path.toLowerCase().endsWith(".png"))
10662
+ throw new TypeError(`${field} must reference a PNG file.`);
10663
+ return path;
10664
+ }, normalizeBranding = (config, projectRoot) => {
10665
+ const { branding } = config;
10666
+ if (!branding)
10667
+ return;
10668
+ const darkIcon = branding.ios?.darkIcon ? normalizeBrandingPath(projectRoot, branding.ios.darkIcon, "mobile.branding.ios.darkIcon") : undefined;
10669
+ const tintedIcon = branding.ios?.tintedIcon ? normalizeBrandingPath(projectRoot, branding.ios.tintedIcon, "mobile.branding.ios.tintedIcon") : undefined;
10670
+ const logoScale = branding.splash?.logoScale ?? 0.2;
10671
+ if (!Number.isFinite(logoScale) || logoScale < 0.1 || logoScale > 0.4)
10672
+ throw new TypeError("mobile.branding.splash.logoScale must be between 0.1 and 0.4.");
10673
+ return {
10674
+ android: {
10675
+ backgroundColor: normalizeBrandingColor(branding.android?.backgroundColor, "#FFFFFF", "mobile.branding.android.backgroundColor"),
10676
+ ...branding.android?.backgroundImage ? {
10677
+ backgroundImage: normalizeBrandingPath(projectRoot, branding.android.backgroundImage, "mobile.branding.android.backgroundImage")
10678
+ } : {},
10679
+ ...branding.android?.foreground ? {
10680
+ foreground: normalizeBrandingPath(projectRoot, branding.android.foreground, "mobile.branding.android.foreground")
10681
+ } : {},
10682
+ ...branding.android?.monochrome ? {
10683
+ monochrome: normalizeBrandingPath(projectRoot, branding.android.monochrome, "mobile.branding.android.monochrome")
10684
+ } : {}
10685
+ },
10686
+ icon: normalizeBrandingPath(projectRoot, branding.icon, "mobile.branding.icon"),
10687
+ ios: {
10688
+ ...darkIcon ? { darkIcon } : {},
10689
+ ...tintedIcon ? { tintedIcon } : {}
10690
+ },
10691
+ splash: {
10692
+ backgroundColor: normalizeBrandingColor(branding.splash?.backgroundColor, "#FFFFFF", "mobile.branding.splash.backgroundColor"),
10693
+ darkBackgroundColor: normalizeBrandingColor(branding.splash?.darkBackgroundColor, "#111111", "mobile.branding.splash.darkBackgroundColor"),
10694
+ ...branding.splash?.darkImage ? {
10695
+ darkImage: normalizeBrandingPath(projectRoot, branding.splash.darkImage, "mobile.branding.splash.darkImage")
10696
+ } : {},
10697
+ ...branding.splash?.image ? {
10698
+ image: normalizeBrandingPath(projectRoot, branding.splash.image, "mobile.branding.splash.image")
10699
+ } : {},
10700
+ logoScale
10701
+ }
10702
+ };
10654
10703
  }, requireText = (value, field) => {
10655
10704
  const normalized = value.trim();
10656
10705
  if (!normalized)
@@ -11036,11 +11085,13 @@ var APP_ID_PATTERN, SCHEME_PATTERN, APPLE_APP_ID_PREFIX_PATTERN, CERTIFICATE_FIN
11036
11085
  const updates = normalizeUpdates(config, productionOrigin, projectRoot);
11037
11086
  const updateServer = normalizeUpdateServer(config, productionOrigin, projectRoot, updates);
11038
11087
  const observability = normalizeObservability(config, productionOrigin);
11088
+ const branding = normalizeBranding(config, projectRoot);
11039
11089
  return {
11040
11090
  androidCertificateFingerprints: normalizeCertificateFingerprints(config.deepLinks?.android?.sha256CertificateFingerprints),
11041
11091
  appId,
11042
11092
  appleAppIdPrefix: normalizeAppleAppIdPrefix(config.deepLinks?.apple?.appIdPrefix),
11043
11093
  appName: requireText(config.appName, "mobile.appName"),
11094
+ ...branding ? { branding } : {},
11044
11095
  bundleDirectory: resolveProjectPath(projectRoot, config.bundleDirectory ?? ".absolutejs/mobile/web", "mobile.bundleDirectory"),
11045
11096
  deepLinkHosts: normalizeHosts(config.deepLinks?.hosts, productionOrigin),
11046
11097
  deepLinkScheme,
@@ -11068,6 +11119,7 @@ var init_config = __esm(() => {
11068
11119
  RELEASE_TARGET_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/u;
11069
11120
  UPDATE_PUBLIC_KEY_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/u;
11070
11121
  ENVIRONMENT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/u;
11122
+ COLOR_PATTERN = /^#[0-9A-F]{6}$/u;
11071
11123
  MINUTE_MS = 60 * 1000;
11072
11124
  DEFAULT_UPDATE_ROLLOUT_STAGES = [
11073
11125
  { minimumReports: 20, observationMinutes: 60, rollout: 0.05 },
@@ -45649,5 +45701,5 @@ export {
45649
45701
  wrapPageHandlerWithStreamingSlots
45650
45702
  };
45651
45703
 
45652
- //# debugId=35BD5CEBA947237E64756E2164756E21
45704
+ //# debugId=69E962978A5CFCEE64756E2164756E21
45653
45705
  //# sourceMappingURL=index.js.map