@absolutejs/absolute 0.20.0-beta.55 → 0.20.0-beta.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/build.js +11 -2
- package/dist/build.js.map +3 -3
- package/dist/cli/index.js +53 -3
- package/dist/index.js +11 -2
- package/dist/index.js.map +3 -3
- package/dist/mobile/index.js +1332 -64
- package/dist/mobile/index.js.map +8 -6
- package/dist/mobile/remoteMacAgentEntry.js +101 -92
- package/dist/mobile/shellBootstrap.js +258 -4
- package/dist/src/mobile/adaptiveShell.d.ts +27 -0
- package/dist/src/mobile/deviceCapabilities.d.ts +1 -0
- package/dist/src/mobile/index.d.ts +1 -0
- package/package.json +7 -7
package/dist/mobile/index.js
CHANGED
|
@@ -815,7 +815,7 @@ import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
|
815
815
|
import { extname as extname3, join as join13, relative as relative9, resolve as resolve11 } from "path";
|
|
816
816
|
import { fileURLToPath } from "url";
|
|
817
817
|
import ts from "typescript";
|
|
818
|
-
var DEVICES_PACKAGE = "@absolutejs/devices", ADAPTERS, SOURCE_GLOB, IGNORED_DIRECTORIES, IDENTIFIER_PATTERN, providerModulePattern = (provider) => new RegExp(`^@absolutejs/devices-${provider}/[a-z][a-z0-9-]*$`, "u"), providerPackagePattern = (provider) => provider === "capacitor" ? /^@capacitor\/[a-z][a-z0-9-]*@\d+\.\d+\.\d+$/u : /^(?:expo-[a-z][a-z0-9-]*|@react-native-[a-z0-9-]+\/[a-z][a-z0-9-]*)@\d+\.\d+\.\d+$/u, providerLabel = (provider) => provider === "capacitor" ? "Capacitor" : "Expo", ANDROID_PERMISSION_PATTERN, IOS_USAGE_DESCRIPTIONS, IOS_PRIVACY_ACCESSED_API_REASONS, IOS_PRIVACY_ACCESSED_APIS, object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson = (path) => {
|
|
818
|
+
var DEVICES_PACKAGE = "@absolutejs/devices", ABSOLUTE_MOBILE_SHELL_DEVICE_CAPABILITIES, ADAPTERS, SOURCE_GLOB, IGNORED_DIRECTORIES, IDENTIFIER_PATTERN, providerModulePattern = (provider) => new RegExp(`^@absolutejs/devices-${provider}/[a-z][a-z0-9-]*$`, "u"), providerPackagePattern = (provider) => provider === "capacitor" ? /^@capacitor\/[a-z][a-z0-9-]*@\d+\.\d+\.\d+$/u : /^(?:expo-[a-z][a-z0-9-]*|@react-native-[a-z0-9-]+\/[a-z][a-z0-9-]*)@\d+\.\d+\.\d+$/u, providerLabel = (provider) => provider === "capacitor" ? "Capacitor" : "Expo", ANDROID_PERMISSION_PATTERN, IOS_USAGE_DESCRIPTIONS, IOS_PRIVACY_ACCESSED_API_REASONS, IOS_PRIVACY_ACCESSED_APIS, object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson = (path) => {
|
|
819
819
|
const value = JSON.parse(readFileSync4(path, "utf8"));
|
|
820
820
|
if (!object2(value))
|
|
821
821
|
throw new TypeError(`${path} must contain an object.`);
|
|
@@ -859,12 +859,12 @@ var DEVICES_PACKAGE = "@absolutejs/devices", ADAPTERS, SOURCE_GLOB, IGNORED_DIRE
|
|
|
859
859
|
const {
|
|
860
860
|
privacyAccessedApis,
|
|
861
861
|
pushNotifications,
|
|
862
|
-
systemBars,
|
|
862
|
+
systemBars: systemBars2,
|
|
863
863
|
usageDescriptions
|
|
864
864
|
} = value;
|
|
865
865
|
if (pushNotifications !== undefined && pushNotifications !== true)
|
|
866
866
|
throw new TypeError(`${field2}.pushNotifications must be true.`);
|
|
867
|
-
if (
|
|
867
|
+
if (systemBars2 !== undefined && systemBars2 !== true)
|
|
868
868
|
throw new TypeError(`${field2}.systemBars must be true.`);
|
|
869
869
|
if (usageDescriptions !== undefined && (!Array.isArray(usageDescriptions) || !usageDescriptions.every((purpose) => typeof purpose === "string" && IOS_USAGE_DESCRIPTIONS.has(purpose))))
|
|
870
870
|
throw new TypeError(`${field2}.usageDescriptions contains an unsupported purpose.`);
|
|
@@ -872,7 +872,7 @@ var DEVICES_PACKAGE = "@absolutejs/devices", ADAPTERS, SOURCE_GLOB, IGNORED_DIRE
|
|
|
872
872
|
return {
|
|
873
873
|
...privacy === undefined ? {} : { privacyAccessedApis: privacy },
|
|
874
874
|
...pushNotifications === true ? { pushNotifications: true } : {},
|
|
875
|
-
...
|
|
875
|
+
...systemBars2 === true ? { systemBars: true } : {},
|
|
876
876
|
...usageDescriptions === undefined ? {} : { usageDescriptions: [...usageDescriptions] }
|
|
877
877
|
};
|
|
878
878
|
}, parseProvider = (name, value, providerName) => {
|
|
@@ -1046,6 +1046,11 @@ var DEVICES_PACKAGE = "@absolutejs/devices", ADAPTERS, SOURCE_GLOB, IGNORED_DIRE
|
|
|
1046
1046
|
}, resolveAbsoluteDeviceCapabilityPlan = (projectRoot, provider = "capacitor") => {
|
|
1047
1047
|
const allProviders = loadAbsoluteDeviceCapabilityProviders(projectRoot, provider);
|
|
1048
1048
|
const capabilities = discoverAbsoluteDeviceCapabilities(projectRoot, allProviders);
|
|
1049
|
+
for (const capability of ABSOLUTE_MOBILE_SHELL_DEVICE_CAPABILITIES) {
|
|
1050
|
+
if (allProviders[capability] && !capabilities.includes(capability))
|
|
1051
|
+
capabilities.push(capability);
|
|
1052
|
+
}
|
|
1053
|
+
capabilities.sort();
|
|
1049
1054
|
const providers = {};
|
|
1050
1055
|
for (const name of capabilities) {
|
|
1051
1056
|
const capabilityProvider = allProviders[name];
|
|
@@ -1061,6 +1066,10 @@ var DEVICES_PACKAGE = "@absolutejs/devices", ADAPTERS, SOURCE_GLOB, IGNORED_DIRE
|
|
|
1061
1066
|
};
|
|
1062
1067
|
};
|
|
1063
1068
|
var init_deviceCapabilities = __esm(() => {
|
|
1069
|
+
ABSOLUTE_MOBILE_SHELL_DEVICE_CAPABILITIES = [
|
|
1070
|
+
"keyboard",
|
|
1071
|
+
"systemBars"
|
|
1072
|
+
];
|
|
1064
1073
|
ADAPTERS = {
|
|
1065
1074
|
capacitor: "@absolutejs/devices-capacitor",
|
|
1066
1075
|
expo: "@absolutejs/devices-expo"
|
|
@@ -1116,7 +1125,7 @@ var init_telemetry = __esm(() => {
|
|
|
1116
1125
|
|
|
1117
1126
|
// src/cli/telemetryEvent.ts
|
|
1118
1127
|
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
1119
|
-
import { arch, platform } from "os";
|
|
1128
|
+
import { arch, platform as platform2 } from "os";
|
|
1120
1129
|
import { dirname as dirname12, join as join18, parse } from "path";
|
|
1121
1130
|
var checkCandidate = (candidate) => {
|
|
1122
1131
|
if (!existsSync6(candidate)) {
|
|
@@ -1157,7 +1166,7 @@ var checkCandidate = (candidate) => {
|
|
|
1157
1166
|
arch: arch(),
|
|
1158
1167
|
bunVersion: Bun.version,
|
|
1159
1168
|
event,
|
|
1160
|
-
os:
|
|
1169
|
+
os: platform2(),
|
|
1161
1170
|
payload,
|
|
1162
1171
|
timestamp: new Date().toISOString(),
|
|
1163
1172
|
version: getVersion()
|
|
@@ -1645,6 +1654,1254 @@ var verifyAbsoluteMobileCompatibilityProducer = async (release, maxProducerBytes
|
|
|
1645
1654
|
}
|
|
1646
1655
|
return release;
|
|
1647
1656
|
};
|
|
1657
|
+
// node_modules/@absolutejs/devices/dist/index.js
|
|
1658
|
+
class DeviceError extends Error {
|
|
1659
|
+
code;
|
|
1660
|
+
cause;
|
|
1661
|
+
constructor(code, message, options) {
|
|
1662
|
+
super(message);
|
|
1663
|
+
this.name = "DeviceError";
|
|
1664
|
+
this.code = code;
|
|
1665
|
+
this.cause = options?.cause;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
var DEFAULT_DEVICE_DOCUMENT_MAX_BYTES = 64 * 1024 * 1024;
|
|
1669
|
+
var SHARE_PROTOCOLS = new Set(["http:", "https:"]);
|
|
1670
|
+
var MAX_NOTIFICATION_ID = 2147483647;
|
|
1671
|
+
var MAX_NOTIFICATION_DATA_BYTES = 16 * 1024;
|
|
1672
|
+
var availableCapability = (fidelity, native) => ({
|
|
1673
|
+
available: true,
|
|
1674
|
+
fidelity,
|
|
1675
|
+
...native === undefined ? {} : { native }
|
|
1676
|
+
});
|
|
1677
|
+
var unavailableCapability = (reason, message, native) => ({
|
|
1678
|
+
available: false,
|
|
1679
|
+
reason,
|
|
1680
|
+
...message === undefined ? {} : { message },
|
|
1681
|
+
...native === undefined ? {} : { native }
|
|
1682
|
+
});
|
|
1683
|
+
var requireFiniteNonNegative = (value, field) => {
|
|
1684
|
+
if (!Number.isFinite(value) || value < 0)
|
|
1685
|
+
throw new TypeError(`${field} must be a finite non-negative number.`);
|
|
1686
|
+
};
|
|
1687
|
+
var requireFinitePositive = (value, field) => {
|
|
1688
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
1689
|
+
throw new TypeError(`${field} must be a finite positive number.`);
|
|
1690
|
+
};
|
|
1691
|
+
var validateDeviceLocationOptions = (options) => {
|
|
1692
|
+
const watchOptions = options;
|
|
1693
|
+
if (options?.maximumAgeMs !== undefined)
|
|
1694
|
+
requireFiniteNonNegative(options.maximumAgeMs, "Location maximumAgeMs");
|
|
1695
|
+
if (options?.timeoutMs !== undefined)
|
|
1696
|
+
requireFinitePositive(options.timeoutMs, "Location timeoutMs");
|
|
1697
|
+
if (watchOptions?.intervalMs !== undefined)
|
|
1698
|
+
requireFinitePositive(watchOptions.intervalMs, "Location intervalMs");
|
|
1699
|
+
if (watchOptions?.minimumUpdateIntervalMs !== undefined)
|
|
1700
|
+
requireFinitePositive(watchOptions.minimumUpdateIntervalMs, "Location minimumUpdateIntervalMs");
|
|
1701
|
+
return options;
|
|
1702
|
+
};
|
|
1703
|
+
var normalizeDeviceLocalNotification = (notification) => {
|
|
1704
|
+
if (!Number.isSafeInteger(notification.id) || notification.id < 1 || notification.id > MAX_NOTIFICATION_ID)
|
|
1705
|
+
throw new TypeError(`Local notification id must be an integer from 1 to ${MAX_NOTIFICATION_ID}.`);
|
|
1706
|
+
if (typeof notification.title !== "string" || notification.title.trim().length === 0 || notification.title.length > 200)
|
|
1707
|
+
throw new TypeError("Local notification title must contain 1 to 200 characters.");
|
|
1708
|
+
if (typeof notification.body !== "string" || notification.body.trim().length === 0 || notification.body.length > 4000)
|
|
1709
|
+
throw new TypeError("Local notification body must contain 1 to 4000 characters.");
|
|
1710
|
+
if (notification.scheduledAtMs !== undefined && (!Number.isFinite(notification.scheduledAtMs) || Number.isNaN(new Date(notification.scheduledAtMs).getTime())))
|
|
1711
|
+
throw new TypeError("Local notification scheduledAtMs must be a valid Unix timestamp.");
|
|
1712
|
+
let data;
|
|
1713
|
+
if (notification.data !== undefined) {
|
|
1714
|
+
const prototype = Object.getPrototypeOf(notification.data);
|
|
1715
|
+
if (typeof notification.data !== "object" || notification.data === null || prototype !== Object.prototype && prototype !== null)
|
|
1716
|
+
throw new TypeError("Local notification data must be a plain string record.");
|
|
1717
|
+
const entries = Object.entries(notification.data);
|
|
1718
|
+
if (entries.length > 32 || entries.some(([key, value]) => key.length === 0 || key.length > 128 || typeof value !== "string" || value.length > 4000))
|
|
1719
|
+
throw new TypeError("Local notification data supports up to 32 non-empty keys and string values up to 4000 characters.");
|
|
1720
|
+
data = Object.fromEntries(entries);
|
|
1721
|
+
if (new TextEncoder().encode(JSON.stringify(data)).byteLength > MAX_NOTIFICATION_DATA_BYTES)
|
|
1722
|
+
throw new TypeError("Local notification data cannot exceed 16 KiB.");
|
|
1723
|
+
}
|
|
1724
|
+
return {
|
|
1725
|
+
body: notification.body,
|
|
1726
|
+
...data === undefined ? {} : { data },
|
|
1727
|
+
id: notification.id,
|
|
1728
|
+
...notification.scheduledAtMs === undefined ? {} : { scheduledAtMs: notification.scheduledAtMs },
|
|
1729
|
+
title: notification.title
|
|
1730
|
+
};
|
|
1731
|
+
};
|
|
1732
|
+
var runtimeCapability = (runtime) => {
|
|
1733
|
+
if (runtime === "ssr")
|
|
1734
|
+
return unavailableCapability("unavailable", "Device capabilities are unavailable during server rendering.");
|
|
1735
|
+
if (runtime === "web")
|
|
1736
|
+
return availableCapability("web");
|
|
1737
|
+
if (runtime === "test")
|
|
1738
|
+
return availableCapability("emulated");
|
|
1739
|
+
return availableCapability("native");
|
|
1740
|
+
};
|
|
1741
|
+
var normalizeDeviceShareContent = (content) => {
|
|
1742
|
+
const normalized = Object.fromEntries(Object.entries(content).filter(([, value]) => typeof value === "string" && value.length > 0));
|
|
1743
|
+
if (Object.keys(normalized).length === 0)
|
|
1744
|
+
throw new TypeError("Device share content cannot be empty.");
|
|
1745
|
+
if (normalized.url) {
|
|
1746
|
+
const url = new URL(normalized.url);
|
|
1747
|
+
if (url.username || url.password || !SHARE_PROTOCOLS.has(url.protocol))
|
|
1748
|
+
throw new TypeError("Device share URLs must be credential-free HTTP(S) URLs.");
|
|
1749
|
+
normalized.url = url.href;
|
|
1750
|
+
}
|
|
1751
|
+
return normalized;
|
|
1752
|
+
};
|
|
1753
|
+
var noopSubscription = () => () => {
|
|
1754
|
+
return;
|
|
1755
|
+
};
|
|
1756
|
+
var unavailable = (capability) => new DeviceError("unavailable", `${capability} is unavailable during server rendering.`);
|
|
1757
|
+
var createSsrDeviceAdapter = () => ({
|
|
1758
|
+
runtime: "ssr",
|
|
1759
|
+
camera: {
|
|
1760
|
+
capability: async () => ({
|
|
1761
|
+
available: false,
|
|
1762
|
+
reason: "unavailable",
|
|
1763
|
+
message: "Camera is unavailable during server rendering."
|
|
1764
|
+
}),
|
|
1765
|
+
queryPermission: async () => ({ canRequest: false, state: "unavailable" }),
|
|
1766
|
+
requestPermission: async () => ({
|
|
1767
|
+
canRequest: false,
|
|
1768
|
+
state: "unavailable"
|
|
1769
|
+
}),
|
|
1770
|
+
takePhoto: async () => {
|
|
1771
|
+
throw unavailable("Camera");
|
|
1772
|
+
}
|
|
1773
|
+
},
|
|
1774
|
+
clipboard: {
|
|
1775
|
+
capability: async () => ({
|
|
1776
|
+
available: false,
|
|
1777
|
+
reason: "unavailable",
|
|
1778
|
+
message: "Clipboard is unavailable during server rendering."
|
|
1779
|
+
}),
|
|
1780
|
+
readText: async () => {
|
|
1781
|
+
throw unavailable("Clipboard");
|
|
1782
|
+
},
|
|
1783
|
+
writeText: async () => {
|
|
1784
|
+
throw unavailable("Clipboard");
|
|
1785
|
+
}
|
|
1786
|
+
},
|
|
1787
|
+
documents: {
|
|
1788
|
+
capability: async () => ({
|
|
1789
|
+
available: false,
|
|
1790
|
+
reason: "unavailable",
|
|
1791
|
+
message: "Documents are unavailable during server rendering."
|
|
1792
|
+
}),
|
|
1793
|
+
export: async () => {
|
|
1794
|
+
throw unavailable("Documents");
|
|
1795
|
+
},
|
|
1796
|
+
open: async () => {
|
|
1797
|
+
throw unavailable("Documents");
|
|
1798
|
+
},
|
|
1799
|
+
pick: async () => {
|
|
1800
|
+
throw unavailable("Documents");
|
|
1801
|
+
}
|
|
1802
|
+
},
|
|
1803
|
+
haptics: {
|
|
1804
|
+
capability: async () => ({
|
|
1805
|
+
available: false,
|
|
1806
|
+
reason: "unavailable",
|
|
1807
|
+
message: "Haptics are unavailable during server rendering."
|
|
1808
|
+
}),
|
|
1809
|
+
impact: async () => {
|
|
1810
|
+
return;
|
|
1811
|
+
},
|
|
1812
|
+
notification: async () => {
|
|
1813
|
+
return;
|
|
1814
|
+
},
|
|
1815
|
+
selectionChanged: async () => {
|
|
1816
|
+
return;
|
|
1817
|
+
},
|
|
1818
|
+
vibrate: async () => {
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
},
|
|
1822
|
+
keyboard: {
|
|
1823
|
+
capability: async () => ({
|
|
1824
|
+
available: false,
|
|
1825
|
+
reason: "unavailable",
|
|
1826
|
+
message: "Keyboard state is unavailable during server rendering."
|
|
1827
|
+
}),
|
|
1828
|
+
dismiss: async () => {
|
|
1829
|
+
throw unavailable("Keyboard");
|
|
1830
|
+
},
|
|
1831
|
+
getState: async () => ({ heightPx: 0, visible: false }),
|
|
1832
|
+
onChange: async () => noopSubscription()
|
|
1833
|
+
},
|
|
1834
|
+
localNotifications: {
|
|
1835
|
+
cancel: async () => {
|
|
1836
|
+
throw unavailable("Local notifications");
|
|
1837
|
+
},
|
|
1838
|
+
capability: async () => ({
|
|
1839
|
+
available: false,
|
|
1840
|
+
reason: "unavailable",
|
|
1841
|
+
message: "Local notifications are unavailable during server rendering."
|
|
1842
|
+
}),
|
|
1843
|
+
onAction: async () => noopSubscription(),
|
|
1844
|
+
onReceived: async () => noopSubscription(),
|
|
1845
|
+
pending: async () => [],
|
|
1846
|
+
queryPermission: async () => ({
|
|
1847
|
+
canRequest: false,
|
|
1848
|
+
state: "unavailable"
|
|
1849
|
+
}),
|
|
1850
|
+
requestPermission: async () => ({
|
|
1851
|
+
canRequest: false,
|
|
1852
|
+
state: "unavailable"
|
|
1853
|
+
}),
|
|
1854
|
+
schedule: async () => {
|
|
1855
|
+
throw unavailable("Local notifications");
|
|
1856
|
+
}
|
|
1857
|
+
},
|
|
1858
|
+
pushNotifications: {
|
|
1859
|
+
capability: async () => ({
|
|
1860
|
+
available: false,
|
|
1861
|
+
reason: "unavailable",
|
|
1862
|
+
message: "Push notifications are unavailable during server rendering."
|
|
1863
|
+
}),
|
|
1864
|
+
disable: async () => {
|
|
1865
|
+
return;
|
|
1866
|
+
},
|
|
1867
|
+
enable: async () => {
|
|
1868
|
+
throw unavailable("Push notifications");
|
|
1869
|
+
},
|
|
1870
|
+
onAction: async () => noopSubscription(),
|
|
1871
|
+
onReceived: async () => noopSubscription(),
|
|
1872
|
+
queryPermission: async () => ({
|
|
1873
|
+
canRequest: false,
|
|
1874
|
+
state: "unavailable"
|
|
1875
|
+
}),
|
|
1876
|
+
requestPermission: async () => ({
|
|
1877
|
+
canRequest: false,
|
|
1878
|
+
state: "unavailable"
|
|
1879
|
+
})
|
|
1880
|
+
},
|
|
1881
|
+
platform: {
|
|
1882
|
+
getInfo: async () => ({
|
|
1883
|
+
formFactor: "unknown",
|
|
1884
|
+
isNative: false,
|
|
1885
|
+
os: "unknown",
|
|
1886
|
+
prefersReducedMotion: false,
|
|
1887
|
+
runtime: "ssr",
|
|
1888
|
+
safeAreaInsets: { bottom: 0, left: 0, right: 0, top: 0 }
|
|
1889
|
+
})
|
|
1890
|
+
},
|
|
1891
|
+
lifecycle: {
|
|
1892
|
+
getState: async () => "inactive",
|
|
1893
|
+
onChange: async () => noopSubscription(),
|
|
1894
|
+
onRestoredOperation: async () => noopSubscription(),
|
|
1895
|
+
onResume: async () => noopSubscription()
|
|
1896
|
+
},
|
|
1897
|
+
links: {
|
|
1898
|
+
getLaunchUrl: async () => null,
|
|
1899
|
+
onOpen: async () => noopSubscription(),
|
|
1900
|
+
openExternal: async () => {
|
|
1901
|
+
throw unavailable("External links");
|
|
1902
|
+
}
|
|
1903
|
+
},
|
|
1904
|
+
location: {
|
|
1905
|
+
capability: async () => ({
|
|
1906
|
+
available: false,
|
|
1907
|
+
reason: "unavailable",
|
|
1908
|
+
message: "Location is unavailable during server rendering."
|
|
1909
|
+
}),
|
|
1910
|
+
current: async () => {
|
|
1911
|
+
throw unavailable("Location");
|
|
1912
|
+
},
|
|
1913
|
+
queryPermission: async () => ({
|
|
1914
|
+
canRequest: false,
|
|
1915
|
+
precision: "unknown",
|
|
1916
|
+
state: "unavailable"
|
|
1917
|
+
}),
|
|
1918
|
+
requestPermission: async () => ({
|
|
1919
|
+
canRequest: false,
|
|
1920
|
+
precision: "unknown",
|
|
1921
|
+
state: "unavailable"
|
|
1922
|
+
}),
|
|
1923
|
+
watch: async () => {
|
|
1924
|
+
throw unavailable("Location");
|
|
1925
|
+
}
|
|
1926
|
+
},
|
|
1927
|
+
network: {
|
|
1928
|
+
getStatus: async () => ({ connected: false, connectionType: "unknown" }),
|
|
1929
|
+
onChange: async () => noopSubscription()
|
|
1930
|
+
},
|
|
1931
|
+
photos: {
|
|
1932
|
+
capability: async () => ({
|
|
1933
|
+
available: false,
|
|
1934
|
+
reason: "unavailable",
|
|
1935
|
+
message: "Photo picker is unavailable during server rendering."
|
|
1936
|
+
}),
|
|
1937
|
+
pick: async () => {
|
|
1938
|
+
throw unavailable("Photo picker");
|
|
1939
|
+
}
|
|
1940
|
+
},
|
|
1941
|
+
share: {
|
|
1942
|
+
capability: async () => ({
|
|
1943
|
+
available: false,
|
|
1944
|
+
reason: "unavailable",
|
|
1945
|
+
message: "Sharing is unavailable during server rendering."
|
|
1946
|
+
}),
|
|
1947
|
+
share: async () => {
|
|
1948
|
+
throw unavailable("Sharing");
|
|
1949
|
+
}
|
|
1950
|
+
},
|
|
1951
|
+
storage: {
|
|
1952
|
+
clear: async () => {
|
|
1953
|
+
throw unavailable("Storage");
|
|
1954
|
+
},
|
|
1955
|
+
get: async () => null,
|
|
1956
|
+
keys: async () => [],
|
|
1957
|
+
remove: async () => {
|
|
1958
|
+
throw unavailable("Storage");
|
|
1959
|
+
},
|
|
1960
|
+
set: async () => {
|
|
1961
|
+
throw unavailable("Storage");
|
|
1962
|
+
}
|
|
1963
|
+
},
|
|
1964
|
+
systemBars: {
|
|
1965
|
+
capability: async () => ({
|
|
1966
|
+
available: false,
|
|
1967
|
+
reason: "unavailable",
|
|
1968
|
+
message: "System bars are unavailable during server rendering."
|
|
1969
|
+
}),
|
|
1970
|
+
setAppearance: async () => {
|
|
1971
|
+
throw unavailable("System bars");
|
|
1972
|
+
},
|
|
1973
|
+
setVisible: async () => {
|
|
1974
|
+
throw unavailable("System bars");
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
});
|
|
1978
|
+
var COARSE_TABLET_MIN_WIDTH = 768;
|
|
1979
|
+
var detectOs = () => {
|
|
1980
|
+
const platform = navigator.userAgent.toLowerCase();
|
|
1981
|
+
if (platform.includes("android"))
|
|
1982
|
+
return "android";
|
|
1983
|
+
if (/iphone|ipad|ipod/.test(platform))
|
|
1984
|
+
return "ios";
|
|
1985
|
+
if (platform.includes("win"))
|
|
1986
|
+
return "windows";
|
|
1987
|
+
if (platform.includes("mac"))
|
|
1988
|
+
return "macos";
|
|
1989
|
+
if (platform.includes("linux"))
|
|
1990
|
+
return "linux";
|
|
1991
|
+
return "unknown";
|
|
1992
|
+
};
|
|
1993
|
+
var networkStatus = () => {
|
|
1994
|
+
const connection = Reflect.get(navigator, "connection");
|
|
1995
|
+
const type = connection?.type;
|
|
1996
|
+
const connectionType = type === "wifi" || type === "cellular" || type === "ethernet" ? type : navigator.onLine ? "unknown" : "none";
|
|
1997
|
+
return { connected: navigator.onLine, connectionType };
|
|
1998
|
+
};
|
|
1999
|
+
var safeAreaInsets = () => {
|
|
2000
|
+
if (!document.body)
|
|
2001
|
+
return { bottom: 0, left: 0, right: 0, top: 0 };
|
|
2002
|
+
const probe = document.createElement("div");
|
|
2003
|
+
probe.style.cssText = [
|
|
2004
|
+
"position:fixed",
|
|
2005
|
+
"visibility:hidden",
|
|
2006
|
+
"pointer-events:none",
|
|
2007
|
+
"padding-top:env(safe-area-inset-top, 0px)",
|
|
2008
|
+
"padding-right:env(safe-area-inset-right, 0px)",
|
|
2009
|
+
"padding-bottom:env(safe-area-inset-bottom, 0px)",
|
|
2010
|
+
"padding-left:env(safe-area-inset-left, 0px)"
|
|
2011
|
+
].join(";");
|
|
2012
|
+
document.body.append(probe);
|
|
2013
|
+
const style = getComputedStyle(probe);
|
|
2014
|
+
const insets = {
|
|
2015
|
+
bottom: Number.parseFloat(style.paddingBottom) || 0,
|
|
2016
|
+
left: Number.parseFloat(style.paddingLeft) || 0,
|
|
2017
|
+
right: Number.parseFloat(style.paddingRight) || 0,
|
|
2018
|
+
top: Number.parseFloat(style.paddingTop) || 0
|
|
2019
|
+
};
|
|
2020
|
+
probe.remove();
|
|
2021
|
+
return insets;
|
|
2022
|
+
};
|
|
2023
|
+
var matches = (query) => typeof matchMedia === "function" && matchMedia(query).matches;
|
|
2024
|
+
var editableElement = (value) => value instanceof HTMLInputElement || value instanceof HTMLTextAreaElement || value instanceof HTMLElement && value.isContentEditable;
|
|
2025
|
+
var webKeyboardState = () => {
|
|
2026
|
+
const viewport = window.visualViewport;
|
|
2027
|
+
const heightPx = viewport ? Math.max(0, Math.round(window.innerHeight - viewport.height - viewport.offsetTop)) : 0;
|
|
2028
|
+
return {
|
|
2029
|
+
heightPx,
|
|
2030
|
+
visible: editableElement(document.activeElement) && heightPx > 50
|
|
2031
|
+
};
|
|
2032
|
+
};
|
|
2033
|
+
var requireStorage = () => {
|
|
2034
|
+
try {
|
|
2035
|
+
return window.localStorage;
|
|
2036
|
+
} catch (cause) {
|
|
2037
|
+
throw new DeviceError("unavailable", "Browser storage is unavailable.", {
|
|
2038
|
+
cause
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
};
|
|
2042
|
+
var webFailure = (error, message) => {
|
|
2043
|
+
const name = typeof error === "object" && error !== null ? Reflect.get(error, "name") : undefined;
|
|
2044
|
+
if (name === "AbortError")
|
|
2045
|
+
return new DeviceError("cancelled", "The device action was cancelled.", {
|
|
2046
|
+
cause: error
|
|
2047
|
+
});
|
|
2048
|
+
if (name === "NotAllowedError")
|
|
2049
|
+
return new DeviceError("permission-denied", message, { cause: error });
|
|
2050
|
+
return new DeviceError("failed", message, { cause: error });
|
|
2051
|
+
};
|
|
2052
|
+
var webOperation = async (message, operation) => {
|
|
2053
|
+
try {
|
|
2054
|
+
return await operation();
|
|
2055
|
+
} catch (error) {
|
|
2056
|
+
throw webFailure(error, message);
|
|
2057
|
+
}
|
|
2058
|
+
};
|
|
2059
|
+
var requireMaximumBytes = (value) => {
|
|
2060
|
+
const maximumBytes = value ?? DEFAULT_DEVICE_DOCUMENT_MAX_BYTES;
|
|
2061
|
+
if (!Number.isSafeInteger(maximumBytes) || maximumBytes < 1)
|
|
2062
|
+
throw new TypeError("Document maximumBytes must be a positive safe integer.");
|
|
2063
|
+
return maximumBytes;
|
|
2064
|
+
};
|
|
2065
|
+
var requireDocumentName = (name) => {
|
|
2066
|
+
if (name.length === 0 || name === "." || name === ".." || /[/\\\u0000-\u001f\u007f]/.test(name))
|
|
2067
|
+
throw new TypeError("Document names must be safe leaf filenames.");
|
|
2068
|
+
return name;
|
|
2069
|
+
};
|
|
2070
|
+
var requireDocumentAccept = (accept) => {
|
|
2071
|
+
if (accept === undefined)
|
|
2072
|
+
return;
|
|
2073
|
+
if (accept.length === 0 || accept.some((value) => !/^\.[a-z0-9]+$/i.test(value) && !/^[a-z0-9!#$&^_.+-]+\/(?:[a-z0-9!#$&^_.+*-]+)$/i.test(value)))
|
|
2074
|
+
throw new TypeError("Document accept entries must be MIME types or file extensions.");
|
|
2075
|
+
return accept.join(",");
|
|
2076
|
+
};
|
|
2077
|
+
var requireDocumentLimit = (limit) => {
|
|
2078
|
+
const value = limit ?? 1;
|
|
2079
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
2080
|
+
throw new TypeError("Document limit must be a positive safe integer.");
|
|
2081
|
+
return value;
|
|
2082
|
+
};
|
|
2083
|
+
var documentBlob = (options) => {
|
|
2084
|
+
requireDocumentName(options.name);
|
|
2085
|
+
const mimeType = options.mimeType ?? (options.content instanceof Blob && options.content.type ? options.content.type : typeof options.content === "string" ? "text/plain;charset=utf-8" : "application/octet-stream");
|
|
2086
|
+
const blob = options.content instanceof Blob ? options.content : new Blob([options.content], { type: mimeType });
|
|
2087
|
+
if (blob.size > requireMaximumBytes(options.maximumBytes))
|
|
2088
|
+
throw new DeviceError("failed", `Document ${options.name} exceeds the configured byte limit.`);
|
|
2089
|
+
return { blob, mimeType, name: options.name, sizeBytes: blob.size };
|
|
2090
|
+
};
|
|
2091
|
+
var releaseObjectUrl = (url) => setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
2092
|
+
var pickDocuments = async (options = {}) => {
|
|
2093
|
+
if (typeof document === "undefined" || !document.body)
|
|
2094
|
+
throw new DeviceError("unsupported", "Browser document selection is unavailable.");
|
|
2095
|
+
const limit = requireDocumentLimit(options.limit);
|
|
2096
|
+
const maximumBytes = requireMaximumBytes(options.maximumBytes);
|
|
2097
|
+
const input = document.createElement("input");
|
|
2098
|
+
input.type = "file";
|
|
2099
|
+
input.multiple = limit > 1;
|
|
2100
|
+
input.style.display = "none";
|
|
2101
|
+
const accept = requireDocumentAccept(options.accept);
|
|
2102
|
+
if (accept)
|
|
2103
|
+
input.accept = accept;
|
|
2104
|
+
document.body.append(input);
|
|
2105
|
+
try {
|
|
2106
|
+
const files = await new Promise((resolve, reject) => {
|
|
2107
|
+
const cleanup = () => {
|
|
2108
|
+
input.removeEventListener("change", changed);
|
|
2109
|
+
input.removeEventListener("cancel", cancelled);
|
|
2110
|
+
};
|
|
2111
|
+
const changed = () => {
|
|
2112
|
+
cleanup();
|
|
2113
|
+
resolve(Array.from(input.files ?? []));
|
|
2114
|
+
};
|
|
2115
|
+
const cancelled = () => {
|
|
2116
|
+
cleanup();
|
|
2117
|
+
resolve([]);
|
|
2118
|
+
};
|
|
2119
|
+
input.addEventListener("change", changed, { once: true });
|
|
2120
|
+
input.addEventListener("cancel", cancelled, { once: true });
|
|
2121
|
+
try {
|
|
2122
|
+
input.click();
|
|
2123
|
+
} catch (error) {
|
|
2124
|
+
cleanup();
|
|
2125
|
+
reject(error);
|
|
2126
|
+
}
|
|
2127
|
+
});
|
|
2128
|
+
if (files.length === 0)
|
|
2129
|
+
throw new DeviceError("cancelled", "Document selection was cancelled.");
|
|
2130
|
+
const selected = files.slice(0, limit);
|
|
2131
|
+
const oversized = selected.find((file) => file.size > maximumBytes);
|
|
2132
|
+
if (oversized)
|
|
2133
|
+
throw new DeviceError("failed", `Document ${oversized.name} exceeds the configured byte limit.`);
|
|
2134
|
+
return selected.map((file) => ({
|
|
2135
|
+
blob: file,
|
|
2136
|
+
...Number.isFinite(file.lastModified) ? { lastModifiedMs: file.lastModified } : {},
|
|
2137
|
+
mimeType: file.type || "application/octet-stream",
|
|
2138
|
+
name: file.name,
|
|
2139
|
+
sizeBytes: file.size
|
|
2140
|
+
}));
|
|
2141
|
+
} finally {
|
|
2142
|
+
input.remove();
|
|
2143
|
+
}
|
|
2144
|
+
};
|
|
2145
|
+
var locationFailure = (error) => {
|
|
2146
|
+
const code = typeof error === "object" && error !== null ? Reflect.get(error, "code") : undefined;
|
|
2147
|
+
if (code === 1)
|
|
2148
|
+
return new DeviceError("permission-denied", "Browser location permission was denied.", { cause: error });
|
|
2149
|
+
if (code === 2)
|
|
2150
|
+
return new DeviceError("temporarily-unavailable", "The browser could not determine the current location.", { cause: error });
|
|
2151
|
+
if (code === 3)
|
|
2152
|
+
return new DeviceError("temporarily-unavailable", "The browser location request timed out.", { cause: error });
|
|
2153
|
+
return webFailure(error, "Browser location failed.");
|
|
2154
|
+
};
|
|
2155
|
+
var locationPosition = (value) => {
|
|
2156
|
+
const { coords } = value;
|
|
2157
|
+
if (!Number.isFinite(coords.latitude) || !Number.isFinite(coords.longitude) || !Number.isFinite(coords.accuracy) || !Number.isFinite(value.timestamp))
|
|
2158
|
+
throw new DeviceError("failed", "The browser returned an invalid location position.");
|
|
2159
|
+
return {
|
|
2160
|
+
accuracyMeters: coords.accuracy,
|
|
2161
|
+
...coords.altitudeAccuracy === null ? {} : { altitudeAccuracyMeters: coords.altitudeAccuracy },
|
|
2162
|
+
...coords.altitude === null ? {} : { altitudeMeters: coords.altitude },
|
|
2163
|
+
...coords.heading === null ? {} : { headingDegrees: coords.heading },
|
|
2164
|
+
latitude: coords.latitude,
|
|
2165
|
+
longitude: coords.longitude,
|
|
2166
|
+
native: value,
|
|
2167
|
+
...coords.speed === null ? {} : { speedMetersPerSecond: coords.speed },
|
|
2168
|
+
timestampMs: value.timestamp
|
|
2169
|
+
};
|
|
2170
|
+
};
|
|
2171
|
+
var webLocationOptions = (options) => ({
|
|
2172
|
+
enableHighAccuracy: options?.accuracy === "high",
|
|
2173
|
+
...options?.maximumAgeMs === undefined ? {} : { maximumAge: options.maximumAgeMs },
|
|
2174
|
+
...options?.timeoutMs === undefined ? {} : { timeout: options.timeoutMs }
|
|
2175
|
+
});
|
|
2176
|
+
var clipboardStatus = (operation) => {
|
|
2177
|
+
const method = operation === "read" ? "readText" : "writeText";
|
|
2178
|
+
return typeof navigator.clipboard?.[method] === "function" ? availableCapability("web") : unavailableCapability("unsupported", `Clipboard ${operation} is not supported by this browser context.`);
|
|
2179
|
+
};
|
|
2180
|
+
var shareStatus = (content) => {
|
|
2181
|
+
if (typeof navigator.share !== "function")
|
|
2182
|
+
return unavailableCapability("unsupported", "The Web Share API is not supported by this browser.");
|
|
2183
|
+
if (content && typeof navigator.canShare === "function") {
|
|
2184
|
+
const normalized = normalizeDeviceShareContent(content);
|
|
2185
|
+
if (!navigator.canShare(normalized))
|
|
2186
|
+
return unavailableCapability("unsupported", "This browser cannot share the requested content.");
|
|
2187
|
+
}
|
|
2188
|
+
return availableCapability("web");
|
|
2189
|
+
};
|
|
2190
|
+
var vibrate = (durationMs) => {
|
|
2191
|
+
if (typeof navigator.vibrate === "function")
|
|
2192
|
+
navigator.vibrate(durationMs);
|
|
2193
|
+
};
|
|
2194
|
+
var requirePhotoOptions = (options) => {
|
|
2195
|
+
if (options?.limit !== undefined && (!Number.isInteger(options.limit) || options.limit < 1))
|
|
2196
|
+
throw new TypeError("Photo picker limit must be a positive integer.");
|
|
2197
|
+
const transform = options?.transform;
|
|
2198
|
+
if (transform && (!Number.isInteger(transform.width) || transform.width < 1 || !Number.isInteger(transform.height) || transform.height < 1 || transform.quality !== undefined && (!Number.isInteger(transform.quality) || transform.quality < 0 || transform.quality > 100)))
|
|
2199
|
+
throw new TypeError("Photo transforms require positive integer dimensions and quality from 0 to 100.");
|
|
2200
|
+
};
|
|
2201
|
+
var pickImages = async (options) => {
|
|
2202
|
+
if (typeof document === "undefined" || typeof URL.createObjectURL !== "function")
|
|
2203
|
+
throw new DeviceError("unsupported", "Browser photo selection is unavailable.");
|
|
2204
|
+
const input = document.createElement("input");
|
|
2205
|
+
input.type = "file";
|
|
2206
|
+
input.accept = "image/*";
|
|
2207
|
+
input.multiple = options.multiple ?? false;
|
|
2208
|
+
if (options.capture)
|
|
2209
|
+
input.setAttribute("capture", options.capture);
|
|
2210
|
+
input.style.display = "none";
|
|
2211
|
+
document.body?.append(input);
|
|
2212
|
+
try {
|
|
2213
|
+
const files = await new Promise((resolve, reject) => {
|
|
2214
|
+
const finish = (selected) => {
|
|
2215
|
+
input.removeEventListener("change", changed);
|
|
2216
|
+
input.removeEventListener("cancel", cancelled);
|
|
2217
|
+
resolve(selected);
|
|
2218
|
+
};
|
|
2219
|
+
const changed = () => finish(Array.from(input.files ?? []));
|
|
2220
|
+
const cancelled = () => finish([]);
|
|
2221
|
+
input.addEventListener("change", changed, { once: true });
|
|
2222
|
+
input.addEventListener("cancel", cancelled, { once: true });
|
|
2223
|
+
try {
|
|
2224
|
+
input.click();
|
|
2225
|
+
} catch (error) {
|
|
2226
|
+
reject(error);
|
|
2227
|
+
}
|
|
2228
|
+
});
|
|
2229
|
+
if (files.length === 0)
|
|
2230
|
+
throw new DeviceError("cancelled", "Photo selection was cancelled.");
|
|
2231
|
+
return files.slice(0, options.limit).map((file) => ({
|
|
2232
|
+
...file.type ? { format: file.type.replace(/^image\//, "") } : {},
|
|
2233
|
+
name: file.name,
|
|
2234
|
+
sizeBytes: file.size,
|
|
2235
|
+
webPath: URL.createObjectURL(file)
|
|
2236
|
+
}));
|
|
2237
|
+
} finally {
|
|
2238
|
+
input.remove();
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
2241
|
+
var createWebDeviceAdapter = () => {
|
|
2242
|
+
let cameraAuthorized = false;
|
|
2243
|
+
let locationAuthorized = false;
|
|
2244
|
+
const notificationActions = new Set;
|
|
2245
|
+
const notificationReceived = new Set;
|
|
2246
|
+
const pendingNotifications = new Map;
|
|
2247
|
+
const notificationPermission = () => {
|
|
2248
|
+
if (typeof Notification === "undefined")
|
|
2249
|
+
return { canRequest: false, state: "unavailable" };
|
|
2250
|
+
return Notification.permission === "granted" ? { canRequest: false, state: "granted" } : Notification.permission === "denied" ? { canRequest: false, state: "denied" } : { canRequest: true, state: "prompt" };
|
|
2251
|
+
};
|
|
2252
|
+
const showNotification = (notification) => {
|
|
2253
|
+
let displayed;
|
|
2254
|
+
try {
|
|
2255
|
+
displayed = new Notification(notification.title, {
|
|
2256
|
+
body: notification.body,
|
|
2257
|
+
data: notification.data,
|
|
2258
|
+
tag: `absolutejs:${notification.id}`
|
|
2259
|
+
});
|
|
2260
|
+
} catch (error) {
|
|
2261
|
+
throw webFailure(error, "Browser notification display failed.");
|
|
2262
|
+
}
|
|
2263
|
+
for (const listener of notificationReceived)
|
|
2264
|
+
listener(notification);
|
|
2265
|
+
displayed.onclick = () => {
|
|
2266
|
+
for (const listener of notificationActions)
|
|
2267
|
+
listener({ actionId: "tap", notification });
|
|
2268
|
+
window.focus();
|
|
2269
|
+
};
|
|
2270
|
+
};
|
|
2271
|
+
const queryLocationPermission = async () => {
|
|
2272
|
+
if (typeof navigator.geolocation === "undefined")
|
|
2273
|
+
return {
|
|
2274
|
+
canRequest: false,
|
|
2275
|
+
precision: "unknown",
|
|
2276
|
+
state: "unavailable"
|
|
2277
|
+
};
|
|
2278
|
+
if (locationAuthorized)
|
|
2279
|
+
return { canRequest: false, precision: "unknown", state: "granted" };
|
|
2280
|
+
if (typeof navigator.permissions?.query !== "function")
|
|
2281
|
+
return { canRequest: true, precision: "unknown", state: "prompt" };
|
|
2282
|
+
try {
|
|
2283
|
+
const status = await navigator.permissions.query({
|
|
2284
|
+
name: "geolocation"
|
|
2285
|
+
});
|
|
2286
|
+
return {
|
|
2287
|
+
canRequest: status.state === "prompt",
|
|
2288
|
+
precision: "unknown",
|
|
2289
|
+
state: status.state
|
|
2290
|
+
};
|
|
2291
|
+
} catch {
|
|
2292
|
+
return { canRequest: true, precision: "unknown", state: "prompt" };
|
|
2293
|
+
}
|
|
2294
|
+
};
|
|
2295
|
+
const currentLocation = async (options) => {
|
|
2296
|
+
validateDeviceLocationOptions(options);
|
|
2297
|
+
if (typeof navigator.geolocation === "undefined")
|
|
2298
|
+
throw new DeviceError("unsupported", "Browser geolocation is unavailable.");
|
|
2299
|
+
return new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition((position) => {
|
|
2300
|
+
try {
|
|
2301
|
+
resolve(locationPosition(position));
|
|
2302
|
+
} catch (error) {
|
|
2303
|
+
reject(error);
|
|
2304
|
+
}
|
|
2305
|
+
}, (error) => reject(locationFailure(error)), webLocationOptions(options)));
|
|
2306
|
+
};
|
|
2307
|
+
return {
|
|
2308
|
+
runtime: "web",
|
|
2309
|
+
camera: {
|
|
2310
|
+
capability: async () => typeof document !== "undefined" ? availableCapability("web") : unavailableCapability("unsupported", "Browser camera capture is unavailable."),
|
|
2311
|
+
queryPermission: async () => ({
|
|
2312
|
+
canRequest: !cameraAuthorized,
|
|
2313
|
+
state: cameraAuthorized ? "granted" : "prompt"
|
|
2314
|
+
}),
|
|
2315
|
+
requestPermission: async () => {
|
|
2316
|
+
cameraAuthorized = true;
|
|
2317
|
+
return { canRequest: false, state: "granted" };
|
|
2318
|
+
},
|
|
2319
|
+
takePhoto: async (options) => {
|
|
2320
|
+
requirePhotoOptions(options);
|
|
2321
|
+
return (await pickImages({
|
|
2322
|
+
capture: options?.direction === "front" ? "user" : "environment"
|
|
2323
|
+
}))[0];
|
|
2324
|
+
}
|
|
2325
|
+
},
|
|
2326
|
+
clipboard: {
|
|
2327
|
+
capability: async (operation = "write") => clipboardStatus(operation),
|
|
2328
|
+
readText: async () => {
|
|
2329
|
+
if (!clipboardStatus("read").available)
|
|
2330
|
+
throw new DeviceError("unsupported", "Clipboard read is unavailable.");
|
|
2331
|
+
return webOperation("Browser clipboard read was denied.", () => navigator.clipboard.readText());
|
|
2332
|
+
},
|
|
2333
|
+
writeText: async (value) => {
|
|
2334
|
+
if (!clipboardStatus("write").available)
|
|
2335
|
+
throw new DeviceError("unsupported", "Clipboard write is unavailable.");
|
|
2336
|
+
await webOperation("Browser clipboard write was denied.", () => navigator.clipboard.writeText(value));
|
|
2337
|
+
}
|
|
2338
|
+
},
|
|
2339
|
+
documents: {
|
|
2340
|
+
capability: async () => typeof document !== "undefined" && typeof URL.createObjectURL === "function" ? availableCapability("web") : unavailableCapability("unsupported", "Browser document handling is unavailable."),
|
|
2341
|
+
export: async (options) => {
|
|
2342
|
+
const result = documentBlob(options);
|
|
2343
|
+
const url = URL.createObjectURL(result.blob);
|
|
2344
|
+
try {
|
|
2345
|
+
const anchor = document.createElement("a");
|
|
2346
|
+
anchor.href = url;
|
|
2347
|
+
anchor.download = result.name;
|
|
2348
|
+
anchor.rel = "noopener";
|
|
2349
|
+
anchor.click();
|
|
2350
|
+
return {
|
|
2351
|
+
mimeType: result.mimeType,
|
|
2352
|
+
name: result.name,
|
|
2353
|
+
sizeBytes: result.sizeBytes
|
|
2354
|
+
};
|
|
2355
|
+
} finally {
|
|
2356
|
+
releaseObjectUrl(url);
|
|
2357
|
+
}
|
|
2358
|
+
},
|
|
2359
|
+
open: async (options) => {
|
|
2360
|
+
const result = documentBlob(options);
|
|
2361
|
+
const url = URL.createObjectURL(result.blob);
|
|
2362
|
+
const opened = window.open(url, "_blank", "noopener,noreferrer");
|
|
2363
|
+
if (opened === null) {
|
|
2364
|
+
URL.revokeObjectURL(url);
|
|
2365
|
+
throw new DeviceError("failed", "The browser blocked the document preview window.");
|
|
2366
|
+
}
|
|
2367
|
+
releaseObjectUrl(url);
|
|
2368
|
+
},
|
|
2369
|
+
pick: pickDocuments
|
|
2370
|
+
},
|
|
2371
|
+
haptics: {
|
|
2372
|
+
capability: async () => typeof navigator.vibrate === "function" ? availableCapability("web") : unavailableCapability("unsupported", "Vibration feedback is not supported by this browser."),
|
|
2373
|
+
impact: async (style = "medium") => vibrate(style === "light" ? 8 : style === "heavy" ? 24 : 14),
|
|
2374
|
+
notification: async (type = "success") => vibrate(type === "error" ? 40 : type === "warning" ? 28 : 18),
|
|
2375
|
+
selectionChanged: async () => vibrate(6),
|
|
2376
|
+
vibrate: async (durationMs = 300) => vibrate(durationMs)
|
|
2377
|
+
},
|
|
2378
|
+
keyboard: {
|
|
2379
|
+
capability: async () => typeof window.visualViewport === "undefined" ? unavailableCapability("unsupported", "This browser does not expose keyboard viewport changes.") : availableCapability("web"),
|
|
2380
|
+
dismiss: async () => {
|
|
2381
|
+
const active = document.activeElement;
|
|
2382
|
+
if (active instanceof HTMLElement)
|
|
2383
|
+
active.blur();
|
|
2384
|
+
},
|
|
2385
|
+
getState: async () => webKeyboardState(),
|
|
2386
|
+
onChange: async (listener) => {
|
|
2387
|
+
let previous = webKeyboardState();
|
|
2388
|
+
const emit = () => {
|
|
2389
|
+
const next = webKeyboardState();
|
|
2390
|
+
if (next.visible === previous.visible && next.heightPx === previous.heightPx)
|
|
2391
|
+
return;
|
|
2392
|
+
previous = next;
|
|
2393
|
+
listener(next);
|
|
2394
|
+
};
|
|
2395
|
+
window.visualViewport?.addEventListener("resize", emit);
|
|
2396
|
+
document.addEventListener("focusin", emit);
|
|
2397
|
+
document.addEventListener("focusout", emit);
|
|
2398
|
+
return () => {
|
|
2399
|
+
window.visualViewport?.removeEventListener("resize", emit);
|
|
2400
|
+
document.removeEventListener("focusin", emit);
|
|
2401
|
+
document.removeEventListener("focusout", emit);
|
|
2402
|
+
};
|
|
2403
|
+
}
|
|
2404
|
+
},
|
|
2405
|
+
localNotifications: {
|
|
2406
|
+
cancel: async (ids) => {
|
|
2407
|
+
for (const id of ids) {
|
|
2408
|
+
const pending = pendingNotifications.get(id);
|
|
2409
|
+
if (!pending)
|
|
2410
|
+
continue;
|
|
2411
|
+
clearTimeout(pending.timer);
|
|
2412
|
+
pendingNotifications.delete(id);
|
|
2413
|
+
}
|
|
2414
|
+
},
|
|
2415
|
+
capability: async () => typeof Notification === "undefined" ? unavailableCapability("unsupported", "Browser notifications are unavailable.") : availableCapability("emulated", {
|
|
2416
|
+
durableScheduling: false,
|
|
2417
|
+
maximumDelayMs: 2147483647
|
|
2418
|
+
}),
|
|
2419
|
+
onAction: async (listener) => {
|
|
2420
|
+
notificationActions.add(listener);
|
|
2421
|
+
return () => {
|
|
2422
|
+
notificationActions.delete(listener);
|
|
2423
|
+
};
|
|
2424
|
+
},
|
|
2425
|
+
onReceived: async (listener) => {
|
|
2426
|
+
notificationReceived.add(listener);
|
|
2427
|
+
return () => {
|
|
2428
|
+
notificationReceived.delete(listener);
|
|
2429
|
+
};
|
|
2430
|
+
},
|
|
2431
|
+
pending: async () => Array.from(pendingNotifications.values(), ({ notification }) => notification),
|
|
2432
|
+
queryPermission: async () => notificationPermission(),
|
|
2433
|
+
requestPermission: async () => {
|
|
2434
|
+
if (typeof Notification === "undefined")
|
|
2435
|
+
return { canRequest: false, state: "unavailable" };
|
|
2436
|
+
try {
|
|
2437
|
+
await Notification.requestPermission();
|
|
2438
|
+
return notificationPermission();
|
|
2439
|
+
} catch (error) {
|
|
2440
|
+
throw webFailure(error, "Browser notification permission failed.");
|
|
2441
|
+
}
|
|
2442
|
+
},
|
|
2443
|
+
schedule: async (input) => {
|
|
2444
|
+
const permission = notificationPermission();
|
|
2445
|
+
if (permission.state !== "granted")
|
|
2446
|
+
throw new DeviceError(permission.state === "denied" ? "permission-denied" : permission.state === "unavailable" ? "unavailable" : "permission-required", "Notification permission must be explicitly granted before scheduling.");
|
|
2447
|
+
const notification = normalizeDeviceLocalNotification(input);
|
|
2448
|
+
const delay = Math.max(0, (notification.scheduledAtMs ?? Date.now()) - Date.now());
|
|
2449
|
+
if (delay > 2147483647)
|
|
2450
|
+
throw new DeviceError("unsupported", "Browser notification scheduling cannot exceed 24 days and is not durable across reloads.");
|
|
2451
|
+
const existing = pendingNotifications.get(notification.id);
|
|
2452
|
+
if (existing)
|
|
2453
|
+
clearTimeout(existing.timer);
|
|
2454
|
+
if (delay === 0) {
|
|
2455
|
+
pendingNotifications.delete(notification.id);
|
|
2456
|
+
showNotification(notification);
|
|
2457
|
+
return notification;
|
|
2458
|
+
}
|
|
2459
|
+
const timer = setTimeout(() => {
|
|
2460
|
+
pendingNotifications.delete(notification.id);
|
|
2461
|
+
showNotification(notification);
|
|
2462
|
+
}, delay);
|
|
2463
|
+
pendingNotifications.set(notification.id, { notification, timer });
|
|
2464
|
+
return notification;
|
|
2465
|
+
}
|
|
2466
|
+
},
|
|
2467
|
+
platform: {
|
|
2468
|
+
getInfo: async () => ({
|
|
2469
|
+
formFactor: matches("(pointer: coarse)") ? innerWidth >= COARSE_TABLET_MIN_WIDTH ? "tablet" : "phone" : "desktop",
|
|
2470
|
+
isNative: false,
|
|
2471
|
+
language: navigator.language,
|
|
2472
|
+
locale: Intl.DateTimeFormat().resolvedOptions().locale,
|
|
2473
|
+
os: detectOs(),
|
|
2474
|
+
prefersReducedMotion: matches("(prefers-reduced-motion: reduce)"),
|
|
2475
|
+
runtime: "web",
|
|
2476
|
+
safeAreaInsets: safeAreaInsets()
|
|
2477
|
+
})
|
|
2478
|
+
},
|
|
2479
|
+
lifecycle: {
|
|
2480
|
+
getState: async () => document.visibilityState === "visible" ? "active" : "background",
|
|
2481
|
+
onChange: async (listener) => {
|
|
2482
|
+
const handler = () => listener(document.visibilityState === "visible" ? "active" : "background");
|
|
2483
|
+
document.addEventListener("visibilitychange", handler);
|
|
2484
|
+
return () => document.removeEventListener("visibilitychange", handler);
|
|
2485
|
+
},
|
|
2486
|
+
onRestoredOperation: async () => () => {
|
|
2487
|
+
return;
|
|
2488
|
+
},
|
|
2489
|
+
onResume: async (listener) => {
|
|
2490
|
+
const handler = () => {
|
|
2491
|
+
if (document.visibilityState === "visible")
|
|
2492
|
+
listener();
|
|
2493
|
+
};
|
|
2494
|
+
document.addEventListener("visibilitychange", handler);
|
|
2495
|
+
return () => document.removeEventListener("visibilitychange", handler);
|
|
2496
|
+
}
|
|
2497
|
+
},
|
|
2498
|
+
links: {
|
|
2499
|
+
getLaunchUrl: async () => location.href,
|
|
2500
|
+
onOpen: async (listener) => {
|
|
2501
|
+
let lastUrl = location.href;
|
|
2502
|
+
const handler = () => {
|
|
2503
|
+
if (location.href === lastUrl)
|
|
2504
|
+
return;
|
|
2505
|
+
lastUrl = location.href;
|
|
2506
|
+
listener(lastUrl);
|
|
2507
|
+
};
|
|
2508
|
+
addEventListener("popstate", handler);
|
|
2509
|
+
addEventListener("hashchange", handler);
|
|
2510
|
+
return () => {
|
|
2511
|
+
removeEventListener("popstate", handler);
|
|
2512
|
+
removeEventListener("hashchange", handler);
|
|
2513
|
+
};
|
|
2514
|
+
},
|
|
2515
|
+
openExternal: async (url) => {
|
|
2516
|
+
const parsed = new URL(url);
|
|
2517
|
+
if (!["http:", "https:", "mailto:", "tel:"].includes(parsed.protocol)) {
|
|
2518
|
+
throw new DeviceError("failed", `External URL protocol ${parsed.protocol} is not allowed.`);
|
|
2519
|
+
}
|
|
2520
|
+
window.open(parsed.href, "_blank", "noopener,noreferrer");
|
|
2521
|
+
}
|
|
2522
|
+
},
|
|
2523
|
+
location: {
|
|
2524
|
+
capability: async () => typeof navigator.geolocation === "undefined" ? unavailableCapability("unsupported", "Browser geolocation is unavailable.") : availableCapability("web"),
|
|
2525
|
+
current: currentLocation,
|
|
2526
|
+
queryPermission: queryLocationPermission,
|
|
2527
|
+
requestPermission: async () => {
|
|
2528
|
+
await currentLocation();
|
|
2529
|
+
locationAuthorized = true;
|
|
2530
|
+
const status = await queryLocationPermission();
|
|
2531
|
+
return { ...status, state: "granted" };
|
|
2532
|
+
},
|
|
2533
|
+
watch: async (listener, options) => {
|
|
2534
|
+
validateDeviceLocationOptions(options);
|
|
2535
|
+
if (typeof navigator.geolocation === "undefined")
|
|
2536
|
+
throw new DeviceError("unsupported", "Browser geolocation is unavailable.");
|
|
2537
|
+
let active = true;
|
|
2538
|
+
const id = navigator.geolocation.watchPosition((position) => {
|
|
2539
|
+
if (!active)
|
|
2540
|
+
return;
|
|
2541
|
+
try {
|
|
2542
|
+
listener({
|
|
2543
|
+
position: locationPosition(position),
|
|
2544
|
+
type: "position"
|
|
2545
|
+
});
|
|
2546
|
+
} catch (error) {
|
|
2547
|
+
listener({ error: locationFailure(error), type: "error" });
|
|
2548
|
+
}
|
|
2549
|
+
}, (error) => {
|
|
2550
|
+
if (active)
|
|
2551
|
+
listener({ error: locationFailure(error), type: "error" });
|
|
2552
|
+
}, webLocationOptions(options));
|
|
2553
|
+
return () => {
|
|
2554
|
+
if (!active)
|
|
2555
|
+
return;
|
|
2556
|
+
active = false;
|
|
2557
|
+
navigator.geolocation.clearWatch(id);
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
},
|
|
2561
|
+
network: {
|
|
2562
|
+
getStatus: async () => networkStatus(),
|
|
2563
|
+
onChange: async (listener) => {
|
|
2564
|
+
const handler = () => listener(networkStatus());
|
|
2565
|
+
addEventListener("online", handler);
|
|
2566
|
+
addEventListener("offline", handler);
|
|
2567
|
+
return () => {
|
|
2568
|
+
removeEventListener("online", handler);
|
|
2569
|
+
removeEventListener("offline", handler);
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
},
|
|
2573
|
+
photos: {
|
|
2574
|
+
capability: async () => typeof document !== "undefined" ? availableCapability("web") : unavailableCapability("unsupported", "Browser photo selection is unavailable."),
|
|
2575
|
+
pick: async (options) => {
|
|
2576
|
+
requirePhotoOptions(options);
|
|
2577
|
+
return pickImages({
|
|
2578
|
+
limit: options?.limit,
|
|
2579
|
+
multiple: options?.limit !== 1
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
},
|
|
2583
|
+
share: {
|
|
2584
|
+
capability: async (content) => shareStatus(content),
|
|
2585
|
+
share: async (content) => {
|
|
2586
|
+
const normalized = normalizeDeviceShareContent(content);
|
|
2587
|
+
if (!shareStatus(normalized).available)
|
|
2588
|
+
throw new DeviceError("unsupported", "Web sharing is unavailable.");
|
|
2589
|
+
await webOperation("Browser sharing failed.", () => navigator.share(normalized));
|
|
2590
|
+
return {};
|
|
2591
|
+
}
|
|
2592
|
+
},
|
|
2593
|
+
storage: {
|
|
2594
|
+
clear: async () => requireStorage().clear(),
|
|
2595
|
+
get: async (key) => requireStorage().getItem(key),
|
|
2596
|
+
keys: async () => {
|
|
2597
|
+
const storage = requireStorage();
|
|
2598
|
+
return Array.from({ length: storage.length }, (_, index) => storage.key(index)).filter((key) => key !== null);
|
|
2599
|
+
},
|
|
2600
|
+
remove: async (key) => requireStorage().removeItem(key),
|
|
2601
|
+
set: async (key, value) => requireStorage().setItem(key, value)
|
|
2602
|
+
},
|
|
2603
|
+
systemBars: {
|
|
2604
|
+
capability: async (operation = "appearance") => operation === "appearance" ? availableCapability("emulated", {
|
|
2605
|
+
targetedBars: false
|
|
2606
|
+
}) : unavailableCapability("unsupported", "Browser chrome visibility cannot be controlled reliably."),
|
|
2607
|
+
setAppearance: async (appearance) => {
|
|
2608
|
+
document.documentElement.style.colorScheme = appearance === "automatic" ? "normal" : appearance === "light" ? "dark" : "light";
|
|
2609
|
+
},
|
|
2610
|
+
setVisible: async () => {
|
|
2611
|
+
throw new DeviceError("unsupported", "Browser chrome visibility cannot be controlled reliably.");
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
};
|
|
2615
|
+
};
|
|
2616
|
+
var RUNTIME_REGISTRY = Symbol.for("@absolutejs/devices/runtime");
|
|
2617
|
+
var registryHost = globalThis;
|
|
2618
|
+
var isRuntimeRegistry = (value) => typeof value === "object" && value !== null && Array.isArray(Reflect.get(value, "installations"));
|
|
2619
|
+
var runtimeRegistry = (() => {
|
|
2620
|
+
const existing = registryHost[RUNTIME_REGISTRY];
|
|
2621
|
+
if (isRuntimeRegistry(existing))
|
|
2622
|
+
return existing;
|
|
2623
|
+
const created = { installations: [] };
|
|
2624
|
+
Object.defineProperty(registryHost, RUNTIME_REGISTRY, {
|
|
2625
|
+
configurable: false,
|
|
2626
|
+
enumerable: false,
|
|
2627
|
+
value: created,
|
|
2628
|
+
writable: false
|
|
2629
|
+
});
|
|
2630
|
+
return created;
|
|
2631
|
+
})();
|
|
2632
|
+
var defaultAdapter = () => typeof window === "undefined" || typeof navigator === "undefined" ? createSsrDeviceAdapter() : createWebDeviceAdapter();
|
|
2633
|
+
var getDeviceAdapter = () => runtimeRegistry.installations.at(-1)?.adapter ?? (runtimeRegistry.fallbackAdapter ??= defaultAdapter());
|
|
2634
|
+
var unavailableOptional = (capability) => unavailableCapability("unsupported", `${capability} is not installed for this runtime.`);
|
|
2635
|
+
var requireOptional = (value, capability) => {
|
|
2636
|
+
if (value !== undefined)
|
|
2637
|
+
return value;
|
|
2638
|
+
throw new DeviceError("unsupported", `${capability} is not installed for this runtime.`);
|
|
2639
|
+
};
|
|
2640
|
+
var keyboard = {
|
|
2641
|
+
capability: () => getDeviceAdapter().keyboard?.capability() ?? Promise.resolve(unavailableOptional("Keyboard")),
|
|
2642
|
+
dismiss: () => requireOptional(getDeviceAdapter().keyboard, "Keyboard").dismiss(),
|
|
2643
|
+
getState: () => requireOptional(getDeviceAdapter().keyboard, "Keyboard").getState(),
|
|
2644
|
+
onChange: (listener) => requireOptional(getDeviceAdapter().keyboard, "Keyboard").onChange(listener),
|
|
2645
|
+
state: () => requireOptional(getDeviceAdapter().keyboard, "Keyboard").getState()
|
|
2646
|
+
};
|
|
2647
|
+
var platform = {
|
|
2648
|
+
capability: () => Promise.resolve(runtimeCapability(getDeviceAdapter().runtime)),
|
|
2649
|
+
getInfo: () => getDeviceAdapter().platform.getInfo(),
|
|
2650
|
+
info: () => getDeviceAdapter().platform.getInfo()
|
|
2651
|
+
};
|
|
2652
|
+
var network = {
|
|
2653
|
+
capability: () => Promise.resolve(runtimeCapability(getDeviceAdapter().runtime)),
|
|
2654
|
+
getStatus: () => getDeviceAdapter().network.getStatus(),
|
|
2655
|
+
status: () => getDeviceAdapter().network.getStatus(),
|
|
2656
|
+
onChange: (...args) => getDeviceAdapter().network.onChange(...args)
|
|
2657
|
+
};
|
|
2658
|
+
var systemBars = {
|
|
2659
|
+
capability: (operation) => getDeviceAdapter().systemBars?.capability(operation) ?? Promise.resolve(unavailableOptional("System bars")),
|
|
2660
|
+
setAppearance: (appearance, bar) => requireOptional(getDeviceAdapter().systemBars, "System bars").setAppearance(appearance, bar),
|
|
2661
|
+
setVisible: (visible, bar) => requireOptional(getDeviceAdapter().systemBars, "System bars").setVisible(visible, bar)
|
|
2662
|
+
};
|
|
2663
|
+
|
|
2664
|
+
// src/mobile/adaptiveShell.ts
|
|
2665
|
+
var STYLE_ID = "absolute-mobile-adaptive-shell";
|
|
2666
|
+
var ANNOUNCER_ID = "absolute-mobile-announcer";
|
|
2667
|
+
var HOST_METRICS_EVENT = "absolute:native-host-metrics";
|
|
2668
|
+
var STATUS_STYLE = `
|
|
2669
|
+
[data-absolute-mobile-status] {
|
|
2670
|
+
display: grid;
|
|
2671
|
+
min-height: var(--absolute-available-height, 100dvh);
|
|
2672
|
+
place-items: center;
|
|
2673
|
+
padding: max(1.5rem, var(--absolute-safe-area-inset-top, 0px)) max(1.5rem, var(--absolute-safe-area-inset-right, 0px)) max(1.5rem, var(--absolute-safe-area-inset-bottom, 0px)) max(1.5rem, var(--absolute-safe-area-inset-left, 0px));
|
|
2674
|
+
box-sizing: border-box;
|
|
2675
|
+
color: CanvasText;
|
|
2676
|
+
background: Canvas;
|
|
2677
|
+
font: 500 1rem/1.5 system-ui, sans-serif;
|
|
2678
|
+
text-align: center;
|
|
2679
|
+
}
|
|
2680
|
+
[data-absolute-mobile-announcer] {
|
|
2681
|
+
position: fixed;
|
|
2682
|
+
width: 1px;
|
|
2683
|
+
height: 1px;
|
|
2684
|
+
padding: 0;
|
|
2685
|
+
margin: -1px;
|
|
2686
|
+
overflow: hidden;
|
|
2687
|
+
clip: rect(0, 0, 0, 0);
|
|
2688
|
+
white-space: nowrap;
|
|
2689
|
+
border: 0;
|
|
2690
|
+
}
|
|
2691
|
+
`;
|
|
2692
|
+
var DEFAULT_KEYBOARD = {
|
|
2693
|
+
heightPx: 0,
|
|
2694
|
+
visible: false
|
|
2695
|
+
};
|
|
2696
|
+
var DEFAULT_NETWORK = {
|
|
2697
|
+
connected: true,
|
|
2698
|
+
connectionType: "unknown"
|
|
2699
|
+
};
|
|
2700
|
+
var DEFAULT_PLATFORM = {
|
|
2701
|
+
formFactor: "unknown",
|
|
2702
|
+
isNative: true,
|
|
2703
|
+
os: "unknown",
|
|
2704
|
+
runtime: "capacitor"
|
|
2705
|
+
};
|
|
2706
|
+
var finitePixels = (value) => Number.isFinite(value) ? Math.max(0, Math.round(value ?? 0)) : 0;
|
|
2707
|
+
var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
2708
|
+
var viewportSize = () => ({
|
|
2709
|
+
height: finitePixels(globalThis.visualViewport?.height ?? innerHeight),
|
|
2710
|
+
width: finitePixels(globalThis.visualViewport?.width ?? innerWidth)
|
|
2711
|
+
});
|
|
2712
|
+
var setPixels = (name, value) => document.documentElement.style.setProperty(name, `${finitePixels(value)}px`);
|
|
2713
|
+
var ensureStyle = () => {
|
|
2714
|
+
let style = document.getElementById(STYLE_ID);
|
|
2715
|
+
if (!(style instanceof HTMLStyleElement)) {
|
|
2716
|
+
style = document.createElement("style");
|
|
2717
|
+
style.id = STYLE_ID;
|
|
2718
|
+
style.textContent = STATUS_STYLE;
|
|
2719
|
+
document.head.append(style);
|
|
2720
|
+
}
|
|
2721
|
+
};
|
|
2722
|
+
var ensureViewport = () => {
|
|
2723
|
+
let viewport = document.head.querySelector('meta[name="viewport"]');
|
|
2724
|
+
if (!viewport) {
|
|
2725
|
+
viewport = document.createElement("meta");
|
|
2726
|
+
viewport.name = "viewport";
|
|
2727
|
+
document.head.prepend(viewport);
|
|
2728
|
+
}
|
|
2729
|
+
const values = new Set(viewport.content.split(",").map((value) => value.trim()).filter(Boolean));
|
|
2730
|
+
values.add("width=device-width");
|
|
2731
|
+
values.add("initial-scale=1");
|
|
2732
|
+
values.add("viewport-fit=cover");
|
|
2733
|
+
viewport.content = [...values].join(",");
|
|
2734
|
+
};
|
|
2735
|
+
var ensureAnnouncer = () => {
|
|
2736
|
+
let announcer = document.getElementById(ANNOUNCER_ID);
|
|
2737
|
+
if (!(announcer instanceof HTMLElement)) {
|
|
2738
|
+
announcer = document.createElement("div");
|
|
2739
|
+
announcer.id = ANNOUNCER_ID;
|
|
2740
|
+
announcer.dataset.absoluteMobileAnnouncer = "";
|
|
2741
|
+
announcer.setAttribute("aria-atomic", "true");
|
|
2742
|
+
announcer.setAttribute("aria-live", "polite");
|
|
2743
|
+
announcer.setAttribute("role", "status");
|
|
2744
|
+
document.body.append(announcer);
|
|
2745
|
+
}
|
|
2746
|
+
return announcer;
|
|
2747
|
+
};
|
|
2748
|
+
var dispatchState = (state) => dispatchEvent(new CustomEvent("absolute:adaptive-shell-change", { detail: state }));
|
|
2749
|
+
var defaultDevices = {
|
|
2750
|
+
keyboard,
|
|
2751
|
+
network,
|
|
2752
|
+
platform,
|
|
2753
|
+
systemBars
|
|
2754
|
+
};
|
|
2755
|
+
var hostSafeAreaInsets = (value) => {
|
|
2756
|
+
if (typeof value !== "object" || value === null)
|
|
2757
|
+
return;
|
|
2758
|
+
const candidate = Reflect.get(value, "safeAreaInsets");
|
|
2759
|
+
if (typeof candidate !== "object" || candidate === null)
|
|
2760
|
+
return;
|
|
2761
|
+
const insets = {
|
|
2762
|
+
bottom: Reflect.get(candidate, "bottom"),
|
|
2763
|
+
left: Reflect.get(candidate, "left"),
|
|
2764
|
+
right: Reflect.get(candidate, "right"),
|
|
2765
|
+
top: Reflect.get(candidate, "top")
|
|
2766
|
+
};
|
|
2767
|
+
if (!isFiniteNumber(insets.bottom) || !isFiniteNumber(insets.left) || !isFiniteNumber(insets.right) || !isFiniteNumber(insets.top))
|
|
2768
|
+
return;
|
|
2769
|
+
return {
|
|
2770
|
+
bottom: finitePixels(insets.bottom),
|
|
2771
|
+
left: finitePixels(insets.left),
|
|
2772
|
+
right: finitePixels(insets.right),
|
|
2773
|
+
top: finitePixels(insets.top)
|
|
2774
|
+
};
|
|
2775
|
+
};
|
|
2776
|
+
var installAbsoluteMobileAdaptiveShell = async (devices = defaultDevices) => {
|
|
2777
|
+
let disposed = false;
|
|
2778
|
+
let keyboardState = { ...DEFAULT_KEYBOARD };
|
|
2779
|
+
let networkState = { ...DEFAULT_NETWORK };
|
|
2780
|
+
let platformInfo = { ...DEFAULT_PLATFORM };
|
|
2781
|
+
let viewport = viewportSize();
|
|
2782
|
+
const subscriptions = [];
|
|
2783
|
+
const state = () => ({
|
|
2784
|
+
availableHeight: globalThis.visualViewport ? viewport.height : Math.max(0, viewport.height - keyboardState.heightPx),
|
|
2785
|
+
keyboard: keyboardState,
|
|
2786
|
+
network: networkState,
|
|
2787
|
+
platform: platformInfo,
|
|
2788
|
+
viewportHeight: viewport.height,
|
|
2789
|
+
viewportWidth: viewport.width
|
|
2790
|
+
});
|
|
2791
|
+
const refreshDocument = () => {
|
|
2792
|
+
if (disposed || !document.head || !document.body)
|
|
2793
|
+
return;
|
|
2794
|
+
ensureViewport();
|
|
2795
|
+
ensureStyle();
|
|
2796
|
+
const root = document.documentElement;
|
|
2797
|
+
const current = state();
|
|
2798
|
+
root.dataset.absoluteMobile = "";
|
|
2799
|
+
root.dataset.absoluteRuntime = current.platform.runtime;
|
|
2800
|
+
root.dataset.absolutePlatform = current.platform.os;
|
|
2801
|
+
root.dataset.absoluteFormFactor = current.platform.formFactor;
|
|
2802
|
+
root.dataset.absoluteKeyboard = current.keyboard.visible ? "visible" : "hidden";
|
|
2803
|
+
root.dataset.absoluteNetwork = current.network.connected ? "online" : "offline";
|
|
2804
|
+
root.dataset.absoluteConnection = current.network.connectionType;
|
|
2805
|
+
root.dataset.absoluteReducedMotion = current.platform.prefersReducedMotion ? "reduce" : "no-preference";
|
|
2806
|
+
const insets = current.platform.safeAreaInsets ?? {
|
|
2807
|
+
bottom: 0,
|
|
2808
|
+
left: 0,
|
|
2809
|
+
right: 0,
|
|
2810
|
+
top: 0
|
|
2811
|
+
};
|
|
2812
|
+
setPixels("--absolute-safe-area-inset-top", insets.top);
|
|
2813
|
+
setPixels("--absolute-safe-area-inset-right", insets.right);
|
|
2814
|
+
setPixels("--absolute-safe-area-inset-bottom", insets.bottom);
|
|
2815
|
+
setPixels("--absolute-safe-area-inset-left", insets.left);
|
|
2816
|
+
setPixels("--absolute-keyboard-height", current.keyboard.heightPx);
|
|
2817
|
+
setPixels("--absolute-viewport-height", current.viewportHeight);
|
|
2818
|
+
setPixels("--absolute-viewport-width", current.viewportWidth);
|
|
2819
|
+
setPixels("--absolute-available-height", current.availableHeight);
|
|
2820
|
+
ensureAnnouncer();
|
|
2821
|
+
};
|
|
2822
|
+
const publish = () => {
|
|
2823
|
+
refreshDocument();
|
|
2824
|
+
dispatchState(state());
|
|
2825
|
+
};
|
|
2826
|
+
const updateViewport = () => {
|
|
2827
|
+
viewport = viewportSize();
|
|
2828
|
+
devices.platform.getInfo().then((info) => {
|
|
2829
|
+
platformInfo = info;
|
|
2830
|
+
publish();
|
|
2831
|
+
return;
|
|
2832
|
+
}).catch(() => publish());
|
|
2833
|
+
};
|
|
2834
|
+
const updateKeyboard = (next) => {
|
|
2835
|
+
keyboardState = {
|
|
2836
|
+
heightPx: finitePixels(next.heightPx),
|
|
2837
|
+
visible: next.visible
|
|
2838
|
+
};
|
|
2839
|
+
publish();
|
|
2840
|
+
};
|
|
2841
|
+
const updateNetwork = (next) => {
|
|
2842
|
+
const changed = networkState.connected !== next.connected;
|
|
2843
|
+
networkState = next;
|
|
2844
|
+
publish();
|
|
2845
|
+
if (changed || !next.connected)
|
|
2846
|
+
ensureAnnouncer().textContent = next.connected ? "Connection restored." : "You are offline.";
|
|
2847
|
+
};
|
|
2848
|
+
const updateHostMetrics = (event) => {
|
|
2849
|
+
const insets = hostSafeAreaInsets(Reflect.get(event, "detail"));
|
|
2850
|
+
if (!insets)
|
|
2851
|
+
return;
|
|
2852
|
+
platformInfo = { ...platformInfo, safeAreaInsets: insets };
|
|
2853
|
+
publish();
|
|
2854
|
+
};
|
|
2855
|
+
const installKeyboard = async () => {
|
|
2856
|
+
const capability = await devices.keyboard.capability();
|
|
2857
|
+
if (!capability.available)
|
|
2858
|
+
return;
|
|
2859
|
+
keyboardState = await devices.keyboard.getState();
|
|
2860
|
+
subscriptions.push(await devices.keyboard.onChange(updateKeyboard));
|
|
2861
|
+
};
|
|
2862
|
+
try {
|
|
2863
|
+
platformInfo = await devices.platform.getInfo();
|
|
2864
|
+
} catch {}
|
|
2865
|
+
const initialHostInsets = hostSafeAreaInsets(Reflect.get(globalThis, "__absoluteNativeHostMetrics"));
|
|
2866
|
+
if (initialHostInsets)
|
|
2867
|
+
platformInfo = {
|
|
2868
|
+
...platformInfo,
|
|
2869
|
+
safeAreaInsets: initialHostInsets
|
|
2870
|
+
};
|
|
2871
|
+
try {
|
|
2872
|
+
networkState = await devices.network.getStatus();
|
|
2873
|
+
subscriptions.push(await devices.network.onChange(updateNetwork));
|
|
2874
|
+
} catch {}
|
|
2875
|
+
try {
|
|
2876
|
+
await installKeyboard();
|
|
2877
|
+
} catch {}
|
|
2878
|
+
try {
|
|
2879
|
+
const capability = await devices.systemBars.capability("appearance");
|
|
2880
|
+
if (capability.available)
|
|
2881
|
+
await devices.systemBars.setAppearance("automatic");
|
|
2882
|
+
} catch {}
|
|
2883
|
+
addEventListener("resize", updateViewport);
|
|
2884
|
+
addEventListener(HOST_METRICS_EVENT, updateHostMetrics);
|
|
2885
|
+
globalThis.visualViewport?.addEventListener("resize", updateViewport);
|
|
2886
|
+
globalThis.visualViewport?.addEventListener("scroll", updateViewport);
|
|
2887
|
+
publish();
|
|
2888
|
+
if (!networkState.connected)
|
|
2889
|
+
ensureAnnouncer().textContent = "You are offline.";
|
|
2890
|
+
return {
|
|
2891
|
+
refreshDocument,
|
|
2892
|
+
state,
|
|
2893
|
+
dispose: async () => {
|
|
2894
|
+
if (disposed)
|
|
2895
|
+
return;
|
|
2896
|
+
disposed = true;
|
|
2897
|
+
removeEventListener("resize", updateViewport);
|
|
2898
|
+
removeEventListener(HOST_METRICS_EVENT, updateHostMetrics);
|
|
2899
|
+
globalThis.visualViewport?.removeEventListener("resize", updateViewport);
|
|
2900
|
+
globalThis.visualViewport?.removeEventListener("scroll", updateViewport);
|
|
2901
|
+
await Promise.all(subscriptions.map((unsubscribe) => unsubscribe()));
|
|
2902
|
+
}
|
|
2903
|
+
};
|
|
2904
|
+
};
|
|
1648
2905
|
// src/mobile/androidRelease.ts
|
|
1649
2906
|
import { createHash as createHash4 } from "crypto";
|
|
1650
2907
|
import {
|
|
@@ -1747,12 +3004,12 @@ var pathExists = async (path) => {
|
|
|
1747
3004
|
return false;
|
|
1748
3005
|
}
|
|
1749
3006
|
};
|
|
1750
|
-
var detectAbsoluteMobileHost = (
|
|
1751
|
-
if (
|
|
3007
|
+
var detectAbsoluteMobileHost = (platform2 = process.platform, wsl = isWSLEnvironment()) => {
|
|
3008
|
+
if (platform2 === "darwin")
|
|
1752
3009
|
return "macos";
|
|
1753
|
-
if (
|
|
3010
|
+
if (platform2 === "win32")
|
|
1754
3011
|
return "windows";
|
|
1755
|
-
if (
|
|
3012
|
+
if (platform2 === "linux" && wsl)
|
|
1756
3013
|
return "wsl";
|
|
1757
3014
|
return "linux";
|
|
1758
3015
|
};
|
|
@@ -1775,16 +3032,16 @@ var findExecutable = async (name, paths, options, host) => {
|
|
|
1775
3032
|
}
|
|
1776
3033
|
return;
|
|
1777
3034
|
};
|
|
1778
|
-
var toolCheck = (id, label,
|
|
3035
|
+
var toolCheck = (id, label, platform2, path, remediation) => path ? {
|
|
1779
3036
|
id,
|
|
1780
3037
|
label,
|
|
1781
3038
|
path,
|
|
1782
|
-
platform,
|
|
3039
|
+
platform: platform2,
|
|
1783
3040
|
status: "pass"
|
|
1784
3041
|
} : {
|
|
1785
3042
|
id,
|
|
1786
3043
|
label,
|
|
1787
|
-
platform,
|
|
3044
|
+
platform: platform2,
|
|
1788
3045
|
remediation,
|
|
1789
3046
|
status: "fail"
|
|
1790
3047
|
};
|
|
@@ -2639,7 +3896,7 @@ var findByExtension = async (root, extension) => {
|
|
|
2639
3896
|
if (!await pathExists4(root))
|
|
2640
3897
|
return;
|
|
2641
3898
|
const entries = await readdir3(root, { withFileTypes: true });
|
|
2642
|
-
const
|
|
3899
|
+
const matches2 = await Promise.all(entries.map(async (entry) => {
|
|
2643
3900
|
const path = join5(root, entry.name);
|
|
2644
3901
|
if (entry.isDirectory() && entry.name.endsWith(extension))
|
|
2645
3902
|
return path;
|
|
@@ -2647,13 +3904,13 @@ var findByExtension = async (root, extension) => {
|
|
|
2647
3904
|
return path;
|
|
2648
3905
|
return entry.isDirectory() ? findByExtension(path, extension) : undefined;
|
|
2649
3906
|
}));
|
|
2650
|
-
return
|
|
3907
|
+
return matches2.find((match) => match !== undefined);
|
|
2651
3908
|
};
|
|
2652
3909
|
var findAllByExtension = async (root, extension, options = {}) => {
|
|
2653
3910
|
if (!await pathExists4(root))
|
|
2654
3911
|
return [];
|
|
2655
3912
|
const entries = await readdir3(root, { withFileTypes: true });
|
|
2656
|
-
const
|
|
3913
|
+
const matches2 = await Promise.all(entries.map(async (entry) => {
|
|
2657
3914
|
const path = join5(root, entry.name);
|
|
2658
3915
|
if (entry.name.endsWith(extension))
|
|
2659
3916
|
return [path];
|
|
@@ -2661,7 +3918,7 @@ var findAllByExtension = async (root, extension, options = {}) => {
|
|
|
2661
3918
|
return [];
|
|
2662
3919
|
return findAllByExtension(path, extension, options);
|
|
2663
3920
|
}));
|
|
2664
|
-
return
|
|
3921
|
+
return matches2.flat().sort();
|
|
2665
3922
|
};
|
|
2666
3923
|
var resolveAbsoluteIosXcodeProject = async (nativeDirectory, options = {}) => {
|
|
2667
3924
|
const requestedWorkspace = options.workspacePath ? resolve4(nativeDirectory, options.workspacePath) : undefined;
|
|
@@ -5295,7 +6552,7 @@ var HTTP_OK = 200;
|
|
|
5295
6552
|
var VERIFY_TIMEOUT_MS = 1e4;
|
|
5296
6553
|
var ANDROID_ASSOCIATION_PATH = "/.well-known/assetlinks.json";
|
|
5297
6554
|
var APPLE_ASSOCIATION_PATH = "/.well-known/apple-app-site-association";
|
|
5298
|
-
var missingIdentity = (field2,
|
|
6555
|
+
var missingIdentity = (field2, platform2) => new TypeError(`${field2} is required to publish ${platform2} deep-link association files.`);
|
|
5299
6556
|
var createAppleDocument = (config, requireAll) => {
|
|
5300
6557
|
if (!config.platforms.includes("ios"))
|
|
5301
6558
|
return;
|
|
@@ -5419,9 +6676,9 @@ var publishGeneratedDirectory = async (temporary, root) => {
|
|
|
5419
6676
|
var materializeHost = async (root, host, files) => {
|
|
5420
6677
|
const directory = resolve7(root, host, ".well-known");
|
|
5421
6678
|
await mkdir7(directory, { recursive: true });
|
|
5422
|
-
return Promise.all(files.map(async ([name,
|
|
6679
|
+
return Promise.all(files.map(async ([name, document2]) => {
|
|
5423
6680
|
const path = resolve7(directory, name);
|
|
5424
|
-
await writeAtomic(path, `${JSON.stringify(
|
|
6681
|
+
await writeAtomic(path, `${JSON.stringify(document2, null, 2)}
|
|
5425
6682
|
`);
|
|
5426
6683
|
return path;
|
|
5427
6684
|
}));
|
|
@@ -5486,13 +6743,13 @@ var verifyAbsoluteMobileAssociationFiles = async (config, request = globalThis.f
|
|
|
5486
6743
|
if (!contentType.toLowerCase().startsWith("application/json")) {
|
|
5487
6744
|
throw new TypeError(`${endpoint.platform} association verification failed for ${url}: expected application/json.`);
|
|
5488
6745
|
}
|
|
5489
|
-
let
|
|
6746
|
+
let document2;
|
|
5490
6747
|
try {
|
|
5491
|
-
|
|
6748
|
+
document2 = await response.json();
|
|
5492
6749
|
} catch {
|
|
5493
6750
|
throw new TypeError(`${endpoint.platform} association verification failed for ${url}: invalid JSON.`);
|
|
5494
6751
|
}
|
|
5495
|
-
if (JSON.stringify(
|
|
6752
|
+
if (JSON.stringify(document2) !== JSON.stringify(endpoint.document)) {
|
|
5496
6753
|
throw new TypeError(`${endpoint.platform} association verification failed for ${url}: the hosted identity does not match mobile config.`);
|
|
5497
6754
|
}
|
|
5498
6755
|
return { platform: endpoint.platform, url };
|
|
@@ -6169,8 +7426,8 @@ var resolveAbsoluteMobileNavigation = (manifest, value, localOrigin) => {
|
|
|
6169
7426
|
const url = new URL(value, `${localOrigin}/`);
|
|
6170
7427
|
const local = new URL(localOrigin);
|
|
6171
7428
|
const production = new URL(manifest.productionOrigin);
|
|
6172
|
-
const
|
|
6173
|
-
if (!
|
|
7429
|
+
const matches2 = (candidate, allowed) => candidate.protocol === allowed.protocol && candidate.host === allowed.host;
|
|
7430
|
+
if (!matches2(url, local) && !matches2(url, production)) {
|
|
6174
7431
|
return;
|
|
6175
7432
|
}
|
|
6176
7433
|
return `${url.pathname}${url.search}${url.hash}`;
|
|
@@ -7263,6 +8520,7 @@ var webHostSource = (config, auth, sync) => {
|
|
|
7263
8520
|
import { router, usePathname } from 'expo-router';
|
|
7264
8521
|
import { useEffect, useRef, useState } from 'react';
|
|
7265
8522
|
import { ActivityIndicator, BackHandler, Platform, StyleSheet, View } from 'react-native';
|
|
8523
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
7266
8524
|
import { WebView, type WebViewMessageEvent } from 'react-native-webview';
|
|
7267
8525
|
import { materializeAbsoluteWebBundle } from './webAssets';
|
|
7268
8526
|
import { createExpoDevicesBridgeHost } from '@absolutejs/devices-expo/bridge';
|
|
@@ -7295,7 +8553,10 @@ const isNativeRoute = (pathname: string) => {
|
|
|
7295
8553
|
});
|
|
7296
8554
|
};
|
|
7297
8555
|
|
|
7298
|
-
const
|
|
8556
|
+
const hostMetricsScript = (safeAreaInsets: { bottom: number; left: number; right: number; top: number }) =>
|
|
8557
|
+
\`globalThis.__absoluteNativeHostMetrics = \${JSON.stringify({ safeAreaInsets })}; dispatchEvent(new CustomEvent('absolute:native-host-metrics', { detail: globalThis.__absoluteNativeHostMetrics })); true;\`;
|
|
8558
|
+
|
|
8559
|
+
const bridgeBootstrap = (path: string, safeAreaInsets: { bottom: number; left: number; right: number; top: number }) => {
|
|
7299
8560
|
const initialPath = DEV_ORIGIN
|
|
7300
8561
|
? 'location.pathname + location.search + location.hash'
|
|
7301
8562
|
: JSON.stringify(path);
|
|
@@ -7304,6 +8565,7 @@ const bridgeBootstrap = (path: string) => {
|
|
|
7304
8565
|
const listeners = new Map();
|
|
7305
8566
|
let sequence = 0;
|
|
7306
8567
|
let currentPath = \${initialPath};
|
|
8568
|
+
globalThis.__absoluteNativeHostMetrics = \${JSON.stringify({ safeAreaInsets })};
|
|
7307
8569
|
const send = value => {
|
|
7308
8570
|
const source = JSON.stringify(value);
|
|
7309
8571
|
if (new TextEncoder().encode(source).byteLength > 65536) throw new Error('Expo bridge message exceeds 64 KiB.');
|
|
@@ -7408,6 +8670,7 @@ const authStatus = async () => {
|
|
|
7408
8670
|
|
|
7409
8671
|
export function AbsoluteWebHost() {
|
|
7410
8672
|
const pathname = usePathname() || '/';
|
|
8673
|
+
const safeAreaInsets = useSafeAreaInsets();
|
|
7411
8674
|
const webView = useRef<WebView>(null);
|
|
7412
8675
|
const devicesBridge = useRef<{ close(): void | Promise<void>; request(method: string, params: Record<string, unknown>): Promise<unknown> } | undefined>(undefined);
|
|
7413
8676
|
const syncBridge = useRef<{ close(): void | Promise<void>; request(method: string, params: Record<string, unknown>): Promise<unknown> } | undefined>(undefined);
|
|
@@ -7450,6 +8713,9 @@ export function AbsoluteWebHost() {
|
|
|
7450
8713
|
}
|
|
7451
8714
|
void materializeAbsoluteWebBundle().then(uri => setIndexUri(uri + '?absolutePath=' + encodeURIComponent(pathname)));
|
|
7452
8715
|
}, [pathname]);
|
|
8716
|
+
useEffect(() => {
|
|
8717
|
+
webView.current?.injectJavaScript(hostMetricsScript(safeAreaInsets));
|
|
8718
|
+
}, [safeAreaInsets.bottom, safeAreaInsets.left, safeAreaInsets.right, safeAreaInsets.top]);
|
|
7453
8719
|
useEffect(() => {
|
|
7454
8720
|
const subscription = BackHandler.addEventListener('hardwareBackPress', () => {
|
|
7455
8721
|
if (!canGoBack) return false;
|
|
@@ -7533,7 +8799,7 @@ export function AbsoluteWebHost() {
|
|
|
7533
8799
|
allowFileAccessFromFileURLs
|
|
7534
8800
|
allowUniversalAccessFromFileURLs={false}
|
|
7535
8801
|
allowingReadAccessToURL={indexUri.slice(0, indexUri.lastIndexOf('/') + 1)}
|
|
7536
|
-
injectedJavaScriptBeforeContentLoaded={bridgeBootstrap(pathname)}
|
|
8802
|
+
injectedJavaScriptBeforeContentLoaded={bridgeBootstrap(pathname, safeAreaInsets)}
|
|
7537
8803
|
onMessage={onMessage}
|
|
7538
8804
|
onNavigationStateChange={state => setCanGoBack(state.canGoBack)}
|
|
7539
8805
|
onShouldStartLoadWithRequest={request => {
|
|
@@ -8495,18 +9761,18 @@ var planAbsoluteExpoDevSession = (config, options) => {
|
|
|
8495
9761
|
env,
|
|
8496
9762
|
role: "native-prepare"
|
|
8497
9763
|
};
|
|
8498
|
-
const native = options.platforms.map((
|
|
8499
|
-
const device =
|
|
9764
|
+
const native = options.platforms.map((platform2) => {
|
|
9765
|
+
const device = platform2 === "android" ? options.androidDevice : options.iosDevice;
|
|
8500
9766
|
const command = {
|
|
8501
9767
|
args: [
|
|
8502
|
-
`run:${
|
|
9768
|
+
`run:${platform2}`,
|
|
8503
9769
|
"--no-bundler",
|
|
8504
9770
|
"--port",
|
|
8505
9771
|
String(options.metroPort),
|
|
8506
9772
|
...device ? ["--device", device] : []
|
|
8507
9773
|
],
|
|
8508
9774
|
env,
|
|
8509
|
-
platform,
|
|
9775
|
+
platform: platform2,
|
|
8510
9776
|
role: "native-build"
|
|
8511
9777
|
};
|
|
8512
9778
|
return command;
|
|
@@ -8678,10 +9944,10 @@ var startAbsoluteExpoDevSession = async (options) => {
|
|
|
8678
9944
|
const runNativeCommand = async (command) => {
|
|
8679
9945
|
if (options.signal?.aborted)
|
|
8680
9946
|
throw abortError();
|
|
8681
|
-
const { platform } = command;
|
|
8682
|
-
if (!
|
|
9947
|
+
const { platform: platform2 } = command;
|
|
9948
|
+
if (!platform2)
|
|
8683
9949
|
throw new TypeError("Expo native build command is missing a platform.");
|
|
8684
|
-
const state =
|
|
9950
|
+
const state = platform2 === "android" ? "building-android" : "building-ios";
|
|
8685
9951
|
setState(state);
|
|
8686
9952
|
const started = performance.now();
|
|
8687
9953
|
const child = run(executable, command.args, {
|
|
@@ -8691,7 +9957,7 @@ var startAbsoluteExpoDevSession = async (options) => {
|
|
|
8691
9957
|
});
|
|
8692
9958
|
forwardLines(child, (line) => {
|
|
8693
9959
|
if (line)
|
|
8694
|
-
log(`[${
|
|
9960
|
+
log(`[${platform2}] ${line}`);
|
|
8695
9961
|
});
|
|
8696
9962
|
const abortChild = () => void stopProcess(child);
|
|
8697
9963
|
options.signal?.addEventListener("abort", abortChild, { once: true });
|
|
@@ -8700,9 +9966,9 @@ var startAbsoluteExpoDevSession = async (options) => {
|
|
|
8700
9966
|
if (options.signal?.aborted)
|
|
8701
9967
|
throw abortError();
|
|
8702
9968
|
if (exitCode !== 0) {
|
|
8703
|
-
throw new Error(`Expo ${
|
|
9969
|
+
throw new Error(`Expo ${platform2} development build exited with status ${exitCode}.`);
|
|
8704
9970
|
}
|
|
8705
|
-
if (
|
|
9971
|
+
if (platform2 === "ios" && options.certificateAuthorityPath && options.iosOrigin && new URL(options.iosOrigin).protocol === "https:" && !options.iosDevice) {
|
|
8706
9972
|
setState("enrolling-trust");
|
|
8707
9973
|
const trustStarted = performance.now();
|
|
8708
9974
|
const utilityOptions = {
|
|
@@ -8889,13 +10155,13 @@ var bundleAuditSteps = ` - name: Prepare production mobile bundle
|
|
|
8889
10155
|
- name: Enforce mobile bundle audit
|
|
8890
10156
|
if: steps.mobile-bundle-audit.outcome != 'success'
|
|
8891
10157
|
run: exit 1`;
|
|
8892
|
-
var releaseAuditSteps = (
|
|
10158
|
+
var releaseAuditSteps = (platform2) => ` - name: Run redacted mobile release audit
|
|
8893
10159
|
id: mobile-release-audit
|
|
8894
10160
|
continue-on-error: true
|
|
8895
10161
|
shell: bash
|
|
8896
10162
|
run: |
|
|
8897
10163
|
mkdir -p .absolutejs/mobile-ci
|
|
8898
|
-
args=(bunx absolute mobile doctor release ${
|
|
10164
|
+
args=(bunx absolute mobile doctor release ${platform2} --json)
|
|
8899
10165
|
${appendConfigArgument}
|
|
8900
10166
|
"\${args[@]}" > .absolutejs/mobile-ci/compliance.json
|
|
8901
10167
|
- name: Upload mobile compliance report
|
|
@@ -8962,7 +10228,7 @@ var publishingInputs = (platforms, includePublishing) => {
|
|
|
8962
10228
|
${fields.join(`
|
|
8963
10229
|
`)}`;
|
|
8964
10230
|
};
|
|
8965
|
-
var jobCondition = (
|
|
10231
|
+
var jobCondition = (platform2) => `github.event_name == 'workflow_dispatch' && (inputs.platform == 'all' || inputs.platform == '${platform2}')`;
|
|
8966
10232
|
var androidJob = (options) => {
|
|
8967
10233
|
const custom = customSecretEnvironment(options.customSecrets);
|
|
8968
10234
|
const publishEnvironment = options.includePublishing ? `
|
|
@@ -9272,7 +10538,7 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
|
9272
10538
|
changed: previous !== generated.workflow,
|
|
9273
10539
|
format: ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT,
|
|
9274
10540
|
path,
|
|
9275
|
-
platforms: options.config.engine === "expo" ? options.config.platforms.filter((
|
|
10541
|
+
platforms: options.config.engine === "expo" ? options.config.platforms.filter((platform2) => platform2 === "android") : [...options.config.platforms].sort(),
|
|
9276
10542
|
publishing: options.includePublishing === true,
|
|
9277
10543
|
requiredSecrets: generated.requiredSecrets
|
|
9278
10544
|
};
|
|
@@ -9479,13 +10745,13 @@ var createAbsoluteMobilePreviewPlugin = (mobile) => {
|
|
|
9479
10745
|
})).post("/__absolute/mobile-preview-telemetry", ({ body, status }) => {
|
|
9480
10746
|
const value = isRecord10(body) ? body : undefined;
|
|
9481
10747
|
const durationMs = value?.durationMs;
|
|
9482
|
-
const
|
|
9483
|
-
if (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0 || durationMs > 300000 ||
|
|
10748
|
+
const platform3 = value?.platform;
|
|
10749
|
+
if (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0 || durationMs > 300000 || platform3 !== "android" && platform3 !== "ios") {
|
|
9484
10750
|
return status(400, { error: "invalid-preview-telemetry" });
|
|
9485
10751
|
}
|
|
9486
10752
|
sendTelemetryEvent("mobile:preview-ready", {
|
|
9487
10753
|
durationMs: Math.round(durationMs),
|
|
9488
|
-
platform:
|
|
10754
|
+
platform: platform3,
|
|
9489
10755
|
provider: "capacitor",
|
|
9490
10756
|
target: "mobile-preview"
|
|
9491
10757
|
});
|
|
@@ -9649,12 +10915,12 @@ var configureIos = async (config) => {
|
|
|
9649
10915
|
return changed.some(Boolean);
|
|
9650
10916
|
};
|
|
9651
10917
|
var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms) => {
|
|
9652
|
-
const results = await Promise.all(platforms.map(async (
|
|
9653
|
-
const didChange =
|
|
9654
|
-
return { didChange, platform:
|
|
10918
|
+
const results = await Promise.all(platforms.map(async (platform3) => {
|
|
10919
|
+
const didChange = platform3 === "android" ? await configureAndroid(config) : await configureIos(config);
|
|
10920
|
+
return { didChange, platform: platform3 };
|
|
9655
10921
|
}));
|
|
9656
10922
|
return {
|
|
9657
|
-
changed: results.filter(({ didChange }) => didChange).map(({ platform:
|
|
10923
|
+
changed: results.filter(({ didChange }) => didChange).map(({ platform: platform3 }) => platform3)
|
|
9658
10924
|
};
|
|
9659
10925
|
};
|
|
9660
10926
|
// src/mobile/nativeDeviceCapabilities.ts
|
|
@@ -9997,12 +11263,12 @@ ${content}
|
|
|
9997
11263
|
return manifestChanged || firebaseChanged;
|
|
9998
11264
|
};
|
|
9999
11265
|
var applyAbsoluteNativeDeviceCapabilities = async (projectRoot, config, platforms = config.platforms, plan = resolveAbsoluteDeviceCapabilityPlan(projectRoot)) => {
|
|
10000
|
-
const results = await Promise.all(platforms.map(async (
|
|
10001
|
-
didChange:
|
|
10002
|
-
platform:
|
|
11266
|
+
const results = await Promise.all(platforms.map(async (platform3) => ({
|
|
11267
|
+
didChange: platform3 === "ios" ? await configureIos2(config, plan) : await configureAndroid2(config, plan),
|
|
11268
|
+
platform: platform3
|
|
10003
11269
|
})));
|
|
10004
11270
|
return {
|
|
10005
|
-
changed: results.filter(({ didChange }) => didChange).map(({ platform:
|
|
11271
|
+
changed: results.filter(({ didChange }) => didChange).map(({ platform: platform3 }) => platform3)
|
|
10006
11272
|
};
|
|
10007
11273
|
};
|
|
10008
11274
|
|
|
@@ -10705,15 +11971,15 @@ var createWebHttpTransport = (options = {}) => {
|
|
|
10705
11971
|
runtime: "web"
|
|
10706
11972
|
});
|
|
10707
11973
|
};
|
|
10708
|
-
var
|
|
10709
|
-
var
|
|
10710
|
-
var
|
|
10711
|
-
var
|
|
10712
|
-
const existing =
|
|
10713
|
-
if (
|
|
11974
|
+
var RUNTIME_REGISTRY2 = Symbol.for("@absolutejs/http/runtime");
|
|
11975
|
+
var registryHost2 = globalThis;
|
|
11976
|
+
var isRuntimeRegistry2 = (value) => typeof value === "object" && value !== null && Array.isArray(Reflect.get(value, "installations"));
|
|
11977
|
+
var runtimeRegistry2 = (() => {
|
|
11978
|
+
const existing = registryHost2[RUNTIME_REGISTRY2];
|
|
11979
|
+
if (isRuntimeRegistry2(existing))
|
|
10714
11980
|
return existing;
|
|
10715
11981
|
const created = { installations: [] };
|
|
10716
|
-
Object.defineProperty(
|
|
11982
|
+
Object.defineProperty(registryHost2, RUNTIME_REGISTRY2, {
|
|
10717
11983
|
configurable: false,
|
|
10718
11984
|
enumerable: false,
|
|
10719
11985
|
value: created,
|
|
@@ -10726,14 +11992,14 @@ var fallbackTransport = () => {
|
|
|
10726
11992
|
return createWebHttpTransport();
|
|
10727
11993
|
throw new AbsoluteHttpError("not-configured", "Absolute HTTP requires a request-scoped SSR transport outside a browser.");
|
|
10728
11994
|
};
|
|
10729
|
-
var getAbsoluteHttpTransport = () =>
|
|
11995
|
+
var getAbsoluteHttpTransport = () => runtimeRegistry2.installations.at(-1)?.transport ?? (runtimeRegistry2.fallback ??= fallbackTransport());
|
|
10730
11996
|
var installAbsoluteHttpTransport = (transport) => {
|
|
10731
11997
|
const installation = { transport };
|
|
10732
|
-
|
|
11998
|
+
runtimeRegistry2.installations.push(installation);
|
|
10733
11999
|
return () => {
|
|
10734
|
-
const index =
|
|
12000
|
+
const index = runtimeRegistry2.installations.indexOf(installation);
|
|
10735
12001
|
if (index >= 0)
|
|
10736
|
-
|
|
12002
|
+
runtimeRegistry2.installations.splice(index, 1);
|
|
10737
12003
|
};
|
|
10738
12004
|
};
|
|
10739
12005
|
var forbiddenCredentialHeaders = new Set([
|
|
@@ -10864,6 +12130,7 @@ export {
|
|
|
10864
12130
|
ABSOLUTE_MOBILE_PREVIEW_PATH,
|
|
10865
12131
|
ABSOLUTE_MOBILE_RETAINED_GENERATIONS,
|
|
10866
12132
|
ABSOLUTE_MOBILE_ROUTE_DETAIL,
|
|
12133
|
+
ABSOLUTE_MOBILE_SHELL_DEVICE_CAPABILITIES,
|
|
10867
12134
|
ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL,
|
|
10868
12135
|
ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV,
|
|
10869
12136
|
ABSOLUTE_NATIVE_AUTH_SCOPES,
|
|
@@ -10935,6 +12202,7 @@ export {
|
|
|
10935
12202
|
inspectAbsoluteRemoteMacLanHost,
|
|
10936
12203
|
inspectAbsoluteRemoteMacWorkspace,
|
|
10937
12204
|
installAbsoluteIosRelease,
|
|
12205
|
+
installAbsoluteMobileAdaptiveShell,
|
|
10938
12206
|
installAbsoluteMobileAuthEnvironment,
|
|
10939
12207
|
installAbsoluteMobileShellHttp,
|
|
10940
12208
|
installAbsoluteMobileSyncRemediation,
|
|
@@ -11011,5 +12279,5 @@ export {
|
|
|
11011
12279
|
writeAbsoluteMobileGithubWorkflow
|
|
11012
12280
|
};
|
|
11013
12281
|
|
|
11014
|
-
//# debugId=
|
|
12282
|
+
//# debugId=15034BCB4D45F19A64756E2164756E21
|
|
11015
12283
|
//# sourceMappingURL=index.js.map
|