@absolutejs/absolute 0.20.0-beta.6 → 0.20.0-beta.60

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 (179) hide show
  1. package/README.md +205 -0
  2. package/dist/angular/browser.js +392 -405
  3. package/dist/angular/browser.js.map +3 -3
  4. package/dist/angular/components/core/streamingSlotRegistrar.js +7 -7
  5. package/dist/angular/components/core/streamingSlotRegistry.js +4 -4
  6. package/dist/angular/index.js +379 -321
  7. package/dist/angular/index.js.map +8 -7
  8. package/dist/angular/server.js +133 -61
  9. package/dist/angular/server.js.map +8 -7
  10. package/dist/build.js +2336 -1347
  11. package/dist/build.js.map +22 -17
  12. package/dist/cli/config/client.js +12 -12
  13. package/dist/cli/config/server.js +1140 -1091
  14. package/dist/cli/index.js +9621 -2093
  15. package/dist/client/chunks/angularPatch-arh4sr68.js +157 -0
  16. package/dist/client/chunks/angularPatch-arh4sr68.js.map +10 -0
  17. package/dist/client/chunks/index-5py90ctf.js +87 -0
  18. package/dist/client/chunks/index-5py90ctf.js.map +10 -0
  19. package/dist/client/chunks/index-m35nbkdp.js +374 -0
  20. package/dist/client/chunks/index-m35nbkdp.js.map +12 -0
  21. package/dist/client/chunks/index-qxjc7kc5.js +12 -0
  22. package/dist/client/chunks/index-qxjc7kc5.js.map +9 -0
  23. package/dist/client/chunks/islands-xj2n8nwf.js +281 -0
  24. package/dist/client/chunks/islands-xj2n8nwf.js.map +14 -0
  25. package/dist/client/chunks/react-pdg4325x.js +65 -0
  26. package/dist/client/chunks/react-pdg4325x.js.map +10 -0
  27. package/dist/client/chunks/svelte-s0c964kb.js +19 -0
  28. package/dist/client/chunks/svelte-s0c964kb.js.map +10 -0
  29. package/dist/client/chunks/vue-rdskrnyg.js +24 -0
  30. package/dist/client/chunks/vue-rdskrnyg.js.map +10 -0
  31. package/dist/client/index.js +73 -841
  32. package/dist/client/index.js.map +7 -20
  33. package/dist/core/streamingSlotRegistrar.js +15 -13
  34. package/dist/core/streamingSlotRegistrar.js.map +2 -2
  35. package/dist/core/streamingSlotRegistry.js +11 -9
  36. package/dist/core/streamingSlotRegistry.js.map +2 -2
  37. package/dist/dev/client/hmrClient.ts +35 -3
  38. package/dist/dev/client/hmrTiming.ts +6 -0
  39. package/dist/dev/client/syncDevtools.ts +237 -0
  40. package/dist/dev/serverBootstrap.js +28 -0
  41. package/dist/image-client/imageClient.js +7 -7
  42. package/dist/index.js +3748 -2361
  43. package/dist/index.js.map +38 -30
  44. package/dist/islands/browser.js +11 -25
  45. package/dist/islands/browser.js.map +2 -2
  46. package/dist/islands/index.js +55 -52
  47. package/dist/islands/index.js.map +5 -4
  48. package/dist/mobile/browser.js +429 -20
  49. package/dist/mobile/browser.js.map +8 -5
  50. package/dist/mobile/index.js +10626 -3132
  51. package/dist/mobile/index.js.map +47 -18
  52. package/dist/mobile/remoteMacAgentEntry.js +1374 -14
  53. package/dist/mobile/shellAuth.js +36 -0
  54. package/dist/mobile/shellBootstrap.js +1800 -0
  55. package/dist/mobile/shellExpoAuth.js +163 -0
  56. package/dist/mobile/shellExpoDevices.js +77 -0
  57. package/dist/mobile/shellExpoSync.js +104 -0
  58. package/dist/mobile/shellSync.js +184 -0
  59. package/dist/mobile/shellUpdate.js +456 -0
  60. package/dist/mobile/uiPrimitives.js +264 -0
  61. package/dist/react/browser.js +17 -59
  62. package/dist/react/browser.js.map +2 -2
  63. package/dist/react/components/browser/index.js +107 -107
  64. package/dist/react/components/index.js +118 -116
  65. package/dist/react/components/index.js.map +2 -2
  66. package/dist/react/hooks/index.js +11 -25
  67. package/dist/react/hooks/index.js.map +2 -2
  68. package/dist/react/index.js +110 -67
  69. package/dist/react/index.js.map +8 -7
  70. package/dist/react/jsxDevRuntimeCompat.js +11 -9
  71. package/dist/react/jsxDevRuntimeCompat.js.map +2 -2
  72. package/dist/react/jsxRuntimeCompat.js +11 -9
  73. package/dist/react/jsxRuntimeCompat.js.map +2 -2
  74. package/dist/react/router/browser.js +9 -7
  75. package/dist/react/router/browser.js.map +2 -2
  76. package/dist/react/router/index.js +9 -7
  77. package/dist/react/router/index.js.map +2 -2
  78. package/dist/react/server.js +67 -11
  79. package/dist/react/server.js.map +7 -6
  80. package/dist/src/build/pwa.d.ts +16 -0
  81. package/dist/src/cli/config/server.d.ts +1 -1
  82. package/dist/src/cli/instanceStatus.d.ts +1 -0
  83. package/dist/src/cli/scripts/dev.d.ts +2 -0
  84. package/dist/src/client/browserTranslation.d.ts +19 -0
  85. package/dist/src/client/index.d.ts +1 -0
  86. package/dist/src/core/browserTranslation.d.ts +6 -0
  87. package/dist/src/core/devBuild.d.ts +1 -0
  88. package/dist/src/core/prepare.d.ts +376 -0
  89. package/dist/src/dev/clientManager.d.ts +1 -0
  90. package/dist/src/dev/devCert.d.ts +6 -4
  91. package/dist/src/dev/serverEntryCopies.d.ts +3 -0
  92. package/dist/src/ember/browser.d.ts +0 -16
  93. package/dist/src/mobile/adaptiveShell.d.ts +27 -0
  94. package/dist/src/mobile/androidEmulatorController.d.ts +8 -1
  95. package/dist/src/mobile/androidRelease.d.ts +9 -2
  96. package/dist/src/mobile/androidTestReport.d.ts +14 -0
  97. package/dist/src/mobile/androidUpgradeConformance.d.ts +47 -0
  98. package/dist/src/mobile/androidWebView.d.ts +1 -0
  99. package/dist/src/mobile/browser.d.ts +2 -0
  100. package/dist/src/mobile/capacitorBundle.d.ts +18 -0
  101. package/dist/src/mobile/ciWorkflow.d.ts +26 -0
  102. package/dist/src/mobile/config.d.ts +9 -1
  103. package/dist/src/mobile/devDeviceAdapter.d.ts +3 -0
  104. package/dist/src/mobile/deviceCapabilities.d.ts +49 -0
  105. package/dist/src/mobile/expoBridge.d.ts +91 -0
  106. package/dist/src/mobile/expoDevController.d.ts +56 -0
  107. package/dist/src/mobile/expoProject.d.ts +16 -0
  108. package/dist/src/mobile/index.d.ts +23 -0
  109. package/dist/src/mobile/iosDeviceAcceptance.d.ts +32 -0
  110. package/dist/src/mobile/iosPhysicalDeviceTransport.d.ts +16 -0
  111. package/dist/src/mobile/iosRelease.d.ts +63 -3
  112. package/dist/src/mobile/iosSimulatorController.d.ts +20 -2
  113. package/dist/src/mobile/iosTestReport.d.ts +24 -0
  114. package/dist/src/mobile/mobileBundleInspection.d.ts +63 -0
  115. package/dist/src/mobile/mobileInspect.d.ts +142 -0
  116. package/dist/src/mobile/mobilePreview.d.ts +174 -0
  117. package/dist/src/mobile/mobilePreviewClient.d.ts +1 -0
  118. package/dist/src/mobile/nativeAuth.d.ts +6 -0
  119. package/dist/src/mobile/nativeBackgroundSync.d.ts +4 -0
  120. package/dist/src/mobile/nativeDeviceCapabilities.d.ts +6 -0
  121. package/dist/src/mobile/nativeRoute.d.ts +13 -0
  122. package/dist/src/mobile/nativeTestReport.d.ts +91 -0
  123. package/dist/src/mobile/nativeUpdates.d.ts +4 -0
  124. package/dist/src/mobile/navigationLifecycle.d.ts +36 -0
  125. package/dist/src/mobile/navigationState.d.ts +53 -0
  126. package/dist/src/mobile/pageProtocol.d.ts +2 -0
  127. package/dist/src/mobile/releaseDoctor.d.ts +23 -1
  128. package/dist/src/mobile/remoteMacProtocol.d.ts +134 -1
  129. package/dist/src/mobile/remoteMacWire.d.ts +1 -1
  130. package/dist/src/mobile/shellAuth.d.ts +8 -1
  131. package/dist/src/mobile/shellBootstrap.d.ts +15 -2
  132. package/dist/src/mobile/shellExpoAuth.d.ts +12 -0
  133. package/dist/src/mobile/shellExpoDevices.d.ts +3 -0
  134. package/dist/src/mobile/shellExpoSync.d.ts +18 -0
  135. package/dist/src/mobile/shellHttp.d.ts +2 -0
  136. package/dist/src/mobile/shellPush.d.ts +11 -0
  137. package/dist/src/mobile/shellSync.d.ts +46 -1
  138. package/dist/src/mobile/shellUpdate.d.ts +2 -0
  139. package/dist/src/mobile/syncRemediation.d.ts +10 -0
  140. package/dist/src/mobile/syncSchema.d.ts +9 -0
  141. package/dist/src/mobile/transport.d.ts +14 -0
  142. package/dist/src/mobile/uiPrimitives.d.ts +37 -0
  143. package/dist/src/mobile/updateClient.d.ts +40 -0
  144. package/dist/src/mobile/updateProtocol.d.ts +51 -0
  145. package/dist/src/mobile/updatePublisher.d.ts +66 -0
  146. package/dist/src/mobile/updateRollout.d.ts +16 -0
  147. package/dist/src/mobile/updateRuntime.d.ts +27 -0
  148. package/dist/src/mobile/updateSigning.d.ts +20 -0
  149. package/dist/src/plugins/hmr.d.ts +3 -0
  150. package/dist/src/plugins/imageOptimizer.d.ts +1 -1
  151. package/dist/src/react/hooks/useMediaQuery.d.ts +1 -1
  152. package/dist/src/utils/imageProcessing.d.ts +3 -0
  153. package/dist/src/utils/loadConfig.d.ts +1 -0
  154. package/dist/src/utils/logger.d.ts +1 -0
  155. package/dist/src/utils/startupBanner.d.ts +1 -0
  156. package/dist/src/utils/userAgentFunctions.d.ts +1 -1
  157. package/dist/src/vue/browser.d.ts +1 -0
  158. package/dist/src/vue/browserTranslation.d.ts +5 -0
  159. package/dist/src/vue/index.d.ts +1 -0
  160. package/dist/svelte/browser.js +12 -26
  161. package/dist/svelte/browser.js.map +2 -2
  162. package/dist/svelte/index.js +104 -61
  163. package/dist/svelte/index.js.map +8 -7
  164. package/dist/svelte/server.js +84 -28
  165. package/dist/svelte/server.js.map +7 -6
  166. package/dist/types/build.d.ts +62 -3
  167. package/dist/types/cli.d.ts +1 -0
  168. package/dist/types/messages.d.ts +1 -1
  169. package/dist/vue/browser.js +116 -56
  170. package/dist/vue/browser.js.map +5 -3
  171. package/dist/vue/components/Image.js +9 -7
  172. package/dist/vue/components/Image.js.map +2 -2
  173. package/dist/vue/components/index.js +11 -9
  174. package/dist/vue/components/index.js.map +2 -2
  175. package/dist/vue/index.js +211 -94
  176. package/dist/vue/index.js.map +10 -7
  177. package/dist/vue/server.js +69 -13
  178. package/dist/vue/server.js.map +7 -6
  179. package/package.json +40 -14
@@ -0,0 +1,1800 @@
1
+ // src/mobile/uiPrimitives.ts
2
+ var STYLE_ID = "absolute-mobile-ui-primitives";
3
+ var NAVIGATION_EVENT = "absolute:navigation-change";
4
+ var SHEET_EVENT = "absolute:sheet-change";
5
+ var BACK_EVENT = "absolute:back-request";
6
+ var ACTIVE_SHEET_SELECTOR = "dialog[data-absolute-sheet][open]";
7
+ var TAB_LINK_SELECTOR = "[data-absolute-tab-bar] a[href]";
8
+ var UI_STYLE = `
9
+ [data-absolute-app-shell] {
10
+ display: grid;
11
+ grid-template-rows: auto minmax(0, 1fr) auto;
12
+ box-sizing: border-box;
13
+ min-height: var(--absolute-available-height, 100dvh);
14
+ padding-inline: var(--absolute-safe-area-inset-left, 0px) var(--absolute-safe-area-inset-right, 0px);
15
+ }
16
+ [data-absolute-app-header] {
17
+ padding-top: var(--absolute-safe-area-inset-top, 0px);
18
+ }
19
+ [data-absolute-app-main], [data-absolute-navigation-stack] {
20
+ min-width: 0;
21
+ min-height: 0;
22
+ }
23
+ [data-absolute-app-main] {
24
+ overflow: auto;
25
+ overscroll-behavior-y: contain;
26
+ }
27
+ [data-absolute-navigation-stack] {
28
+ view-transition-name: absolute-mobile-stack;
29
+ }
30
+ [data-absolute-tab-bar] {
31
+ display: flex;
32
+ align-items: stretch;
33
+ justify-content: space-around;
34
+ box-sizing: border-box;
35
+ padding-bottom: var(--absolute-safe-area-inset-bottom, 0px);
36
+ background: Canvas;
37
+ color: CanvasText;
38
+ }
39
+ [data-absolute-tab-bar] > a {
40
+ display: grid;
41
+ flex: 1 1 0;
42
+ min-width: 0;
43
+ min-height: 2.75rem;
44
+ place-items: center;
45
+ padding: 0.375rem 0.5rem;
46
+ color: inherit;
47
+ text-align: center;
48
+ text-decoration: none;
49
+ touch-action: manipulation;
50
+ }
51
+ [data-absolute-tab-bar] > a[aria-current="page"] {
52
+ font-weight: 700;
53
+ }
54
+ dialog[data-absolute-sheet] {
55
+ box-sizing: border-box;
56
+ width: min(100%, var(--absolute-sheet-max-width, 42rem));
57
+ max-height: min(90dvh, var(--absolute-available-height, 90dvh));
58
+ margin: auto auto 0;
59
+ padding: 1rem max(1rem, var(--absolute-safe-area-inset-right, 0px)) max(1rem, var(--absolute-safe-area-inset-bottom, 0px)) max(1rem, var(--absolute-safe-area-inset-left, 0px));
60
+ overflow: auto;
61
+ border: 1px solid color-mix(in srgb, CanvasText 18%, transparent);
62
+ border-radius: 1rem 1rem 0 0;
63
+ background: Canvas;
64
+ color: CanvasText;
65
+ box-shadow: 0 -0.5rem 2rem color-mix(in srgb, CanvasText 18%, transparent);
66
+ }
67
+ dialog[data-absolute-sheet]::backdrop {
68
+ background: color-mix(in srgb, CanvasText 38%, transparent);
69
+ }
70
+ @keyframes absolute-mobile-forward-old { to { opacity: 0; transform: translateX(-12%); } }
71
+ @keyframes absolute-mobile-forward-new { from { opacity: 0; transform: translateX(12%); } }
72
+ @keyframes absolute-mobile-back-old { to { opacity: 0; transform: translateX(12%); } }
73
+ @keyframes absolute-mobile-back-new { from { opacity: 0; transform: translateX(-12%); } }
74
+ html[data-absolute-navigation-direction="forward"]::view-transition-old(absolute-mobile-stack) { animation: 180ms ease both absolute-mobile-forward-old; }
75
+ html[data-absolute-navigation-direction="forward"]::view-transition-new(absolute-mobile-stack) { animation: 180ms ease both absolute-mobile-forward-new; }
76
+ html[data-absolute-navigation-direction="back"]::view-transition-old(absolute-mobile-stack) { animation: 180ms ease both absolute-mobile-back-old; }
77
+ html[data-absolute-navigation-direction="back"]::view-transition-new(absolute-mobile-stack) { animation: 180ms ease both absolute-mobile-back-new; }
78
+ html[data-absolute-reduced-motion="reduce"]::view-transition-old(absolute-mobile-stack),
79
+ html[data-absolute-reduced-motion="reduce"]::view-transition-new(absolute-mobile-stack) { animation: none; }
80
+ `;
81
+ var currentPath = () => `${location.pathname}${location.search}${location.hash}`;
82
+ var ensureStyle = () => {
83
+ let style = document.getElementById(STYLE_ID);
84
+ if (!(style instanceof HTMLStyleElement)) {
85
+ style = document.createElement("style");
86
+ style.id = STYLE_ID;
87
+ style.textContent = UI_STYLE;
88
+ document.head.append(style);
89
+ }
90
+ };
91
+ var sheetById = (id) => {
92
+ const target = document.getElementById(id);
93
+ return target instanceof HTMLDialogElement && target.dataset.absoluteSheet !== undefined ? target : undefined;
94
+ };
95
+ var dispatchSheet = (sheet, open) => dispatchEvent(new CustomEvent(SHEET_EVENT, {
96
+ detail: { id: sheet.id, open }
97
+ }));
98
+ var focusSheet = (sheet) => {
99
+ const target = sheet.querySelector('[autofocus], button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
100
+ (target ?? sheet).focus();
101
+ };
102
+ var sheetOpeners = new WeakMap;
103
+ var closeAbsoluteMobileSheet = (target) => {
104
+ const sheet = typeof target === "string" ? sheetById(target) : target;
105
+ if (!sheet || !sheet.open)
106
+ return false;
107
+ if (typeof sheet.close === "function")
108
+ sheet.close();
109
+ else
110
+ sheet.removeAttribute("open");
111
+ sheet.removeAttribute("aria-modal");
112
+ sheetOpeners.get(sheet)?.focus();
113
+ sheetOpeners.delete(sheet);
114
+ dispatchSheet(sheet, false);
115
+ return true;
116
+ };
117
+ var openAbsoluteMobileSheet = (target, opener) => {
118
+ const sheet = typeof target === "string" ? sheetById(target) : target;
119
+ if (!sheet || sheet.dataset.absoluteSheet === undefined)
120
+ return false;
121
+ const active = document.querySelector(ACTIVE_SHEET_SELECTOR);
122
+ if (active && active !== sheet)
123
+ closeAbsoluteMobileSheet(active);
124
+ if (opener)
125
+ sheetOpeners.set(sheet, opener);
126
+ if (!sheet.open) {
127
+ if (typeof sheet.showModal === "function")
128
+ sheet.showModal();
129
+ else
130
+ sheet.setAttribute("open", "");
131
+ }
132
+ sheet.setAttribute("aria-modal", "true");
133
+ focusSheet(sheet);
134
+ dispatchSheet(sheet, true);
135
+ return true;
136
+ };
137
+ var readAbsoluteMobileLinkIntent = (anchor) => {
138
+ const mode = anchor.dataset.absoluteLink;
139
+ if (mode === "back")
140
+ return { kind: "back" };
141
+ if (mode === "external")
142
+ return { kind: "external" };
143
+ return { kind: "navigate", replace: mode === "replace" };
144
+ };
145
+ var requestAbsoluteMobileBack = () => {
146
+ const event = new CustomEvent(BACK_EVENT, { cancelable: true });
147
+ return !dispatchEvent(event);
148
+ };
149
+ var normalizePathname = (value) => {
150
+ try {
151
+ return new URL(value, location.href).pathname.replace(/\/$/u, "") || "/";
152
+ } catch {
153
+ return;
154
+ }
155
+ };
156
+ var syncTabLinks = (path) => {
157
+ const activePath = normalizePathname(path);
158
+ if (!activePath)
159
+ return;
160
+ document.querySelectorAll(TAB_LINK_SELECTOR).forEach((anchor) => {
161
+ const candidate = normalizePathname(anchor.href);
162
+ const prefix = anchor.dataset.absoluteTabMatch === "prefix";
163
+ const active = candidate !== undefined && (prefix ? activePath === candidate || candidate !== "/" && activePath.startsWith(`${candidate}/`) : activePath === candidate);
164
+ if (active)
165
+ anchor.setAttribute("aria-current", "page");
166
+ else
167
+ anchor.removeAttribute("aria-current");
168
+ });
169
+ };
170
+ var installAbsoluteMobileUiPrimitives = () => {
171
+ let disposed = false;
172
+ let scheduled = false;
173
+ let path = currentPath();
174
+ const refreshDocument = (nextPath = path) => {
175
+ if (disposed || !document.head || !document.body)
176
+ return;
177
+ path = nextPath;
178
+ ensureStyle();
179
+ syncTabLinks(path);
180
+ };
181
+ const scheduleRefresh = () => {
182
+ if (scheduled || disposed)
183
+ return;
184
+ scheduled = true;
185
+ queueMicrotask(() => {
186
+ scheduled = false;
187
+ refreshDocument();
188
+ });
189
+ };
190
+ const handleClick = (event) => {
191
+ if (!(event.target instanceof Element))
192
+ return;
193
+ const opener = event.target.closest("[data-absolute-sheet-open]");
194
+ if (opener?.dataset.absoluteSheetOpen) {
195
+ if (openAbsoluteMobileSheet(opener.dataset.absoluteSheetOpen, opener))
196
+ event.preventDefault();
197
+ return;
198
+ }
199
+ const closer = event.target.closest("[data-absolute-sheet-close]");
200
+ const sheet = closer?.closest("dialog[data-absolute-sheet]");
201
+ if (sheet && closeAbsoluteMobileSheet(sheet)) {
202
+ event.preventDefault();
203
+ return;
204
+ }
205
+ if (event.target instanceof HTMLDialogElement && event.target.dataset.absoluteSheet !== undefined) {
206
+ const rect = event.target.getBoundingClientRect();
207
+ const outside = event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom;
208
+ if (outside && closeAbsoluteMobileSheet(event.target))
209
+ event.preventDefault();
210
+ }
211
+ };
212
+ const handleCancel = (event) => {
213
+ if (!(event.target instanceof HTMLDialogElement))
214
+ return;
215
+ if (event.target.dataset.absoluteSheet === undefined)
216
+ return;
217
+ event.preventDefault();
218
+ closeAbsoluteMobileSheet(event.target);
219
+ };
220
+ const handleBack = (event) => {
221
+ const active = document.querySelector(ACTIVE_SHEET_SELECTOR);
222
+ if (!active)
223
+ return;
224
+ event.preventDefault();
225
+ closeAbsoluteMobileSheet(active);
226
+ };
227
+ const observer = new MutationObserver(scheduleRefresh);
228
+ observer.observe(document.documentElement, {
229
+ childList: true,
230
+ subtree: true
231
+ });
232
+ addEventListener("click", handleClick);
233
+ addEventListener("cancel", handleCancel, true);
234
+ addEventListener(BACK_EVENT, handleBack);
235
+ refreshDocument();
236
+ return {
237
+ refreshDocument,
238
+ requestBack: requestAbsoluteMobileBack,
239
+ dispose: () => {
240
+ if (disposed)
241
+ return;
242
+ disposed = true;
243
+ observer.disconnect();
244
+ removeEventListener("click", handleClick);
245
+ removeEventListener("cancel", handleCancel, true);
246
+ removeEventListener(BACK_EVENT, handleBack);
247
+ },
248
+ navigate: (detail) => {
249
+ path = detail.to;
250
+ document.documentElement.dataset.absoluteNavigationDirection = detail.direction;
251
+ refreshDocument(path);
252
+ dispatchEvent(new CustomEvent(NAVIGATION_EVENT, {
253
+ detail
254
+ }));
255
+ }
256
+ };
257
+ };
258
+
259
+ // src/mobile/shellBootstrap.ts
260
+ import { App } from "@capacitor/app";
261
+ import { Browser } from "@capacitor/browser";
262
+
263
+ // src/mobile/producerContextState.ts
264
+ var ABSOLUTE_MOBILE_PRODUCER_STORAGE_KEY = Symbol.for("absolutejs.mobileProducerAsyncLocalStorage");
265
+ var frameworks = new Set([
266
+ "angular",
267
+ "ember",
268
+ "html",
269
+ "htmx",
270
+ "react",
271
+ "svelte",
272
+ "vue"
273
+ ]);
274
+
275
+ // src/mobile/pageProtocol.ts
276
+ var ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE = "application/vnd.absolute.page+json";
277
+ var ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION = 1;
278
+ var MOBILE_PAGE_REQUEST_HEADERS = {
279
+ appBuild: "x-absolute-mobile-app-build",
280
+ pageBundle: "x-absolute-mobile-page-bundle",
281
+ pageContracts: "x-absolute-mobile-page-contracts",
282
+ pageId: "x-absolute-mobile-page-id",
283
+ protocol: "x-absolute-mobile-protocol",
284
+ runtime: "x-absolute-mobile-runtime"
285
+ };
286
+ var headerVaryValues = [
287
+ "Accept",
288
+ MOBILE_PAGE_REQUEST_HEADERS.protocol,
289
+ MOBILE_PAGE_REQUEST_HEADERS.runtime,
290
+ MOBILE_PAGE_REQUEST_HEADERS.appBuild,
291
+ MOBILE_PAGE_REQUEST_HEADERS.pageBundle,
292
+ MOBILE_PAGE_REQUEST_HEADERS.pageContracts,
293
+ MOBILE_PAGE_REQUEST_HEADERS.pageId
294
+ ];
295
+
296
+ // src/mobile/client.ts
297
+ class AbsoluteMobilePageProtocolError extends Error {
298
+ constructor(code, message) {
299
+ super(message);
300
+ this.name = "AbsoluteMobilePageProtocolError";
301
+ this.code = code;
302
+ }
303
+ }
304
+ var disposeAbsoluteMobilePage = async (target = window) => {
305
+ const dispose = target.__ABSOLUTE_PAGE_DISPOSE__;
306
+ target.__ABSOLUTE_PAGE_DISPOSE__ = undefined;
307
+ target.__ABSOLUTE_PAGE_READY__ = undefined;
308
+ if (dispose)
309
+ await dispose();
310
+ };
311
+ var frameworks2 = new Set([
312
+ "angular",
313
+ "ember",
314
+ "html",
315
+ "htmx",
316
+ "react",
317
+ "svelte",
318
+ "vue"
319
+ ]);
320
+ var upgradeReasons = new Set([
321
+ "app-release",
322
+ "page-contract",
323
+ "protocol",
324
+ "runtime"
325
+ ]);
326
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
327
+ var isFramework = (value) => typeof value === "string" && frameworks2.has(value);
328
+ var isUpgradeReason = (value) => typeof value === "string" && upgradeReasons.has(value);
329
+ var parsePageResult = (value) => {
330
+ if (typeof value.contract !== "string" || !isFramework(value.framework) || typeof value.pageId !== "string" || typeof value.status !== "number") {
331
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The mobile page response is missing required page metadata.");
332
+ }
333
+ if (!isRecord(value.props)) {
334
+ throw new AbsoluteMobilePageProtocolError("invalid-props", "The mobile page response must contain an object props value.");
335
+ }
336
+ return {
337
+ contract: value.contract,
338
+ framework: value.framework,
339
+ kind: "page",
340
+ pageId: value.pageId,
341
+ props: value.props,
342
+ status: value.status
343
+ };
344
+ };
345
+ var parseUpgradeResult = (value) => {
346
+ if (typeof value.pageId !== "string" || !isUpgradeReason(value.reason)) {
347
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The update response is missing its page or reason.");
348
+ }
349
+ return {
350
+ kind: "upgrade-required",
351
+ pageId: value.pageId,
352
+ reason: value.reason,
353
+ supportedContracts: Array.isArray(value.supportedContracts) ? value.supportedContracts.filter((contract) => typeof contract === "string") : undefined,
354
+ supportedRuntimes: Array.isArray(value.supportedRuntimes) ? value.supportedRuntimes.filter((runtime) => typeof runtime === "string") : undefined
355
+ };
356
+ };
357
+ var activateAbsoluteMobilePage = async (value, options) => {
358
+ const envelope = parseAbsoluteMobilePageEnvelope(value);
359
+ if (envelope.response.kind === "upgrade-required") {
360
+ return envelope.response;
361
+ }
362
+ if (envelope.response.kind !== "page") {
363
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", "Expected a renderable mobile page response.");
364
+ }
365
+ const target = options.target ?? window;
366
+ await disposeAbsoluteMobilePage(target);
367
+ target.__INITIAL_PROPS__ = envelope.response.props;
368
+ target.__ABS_ANGULAR_REQUEST_CONTEXT__ = envelope.response.props;
369
+ target.__ABSOLUTE_PAGE_RENDER_MODE__ = "client";
370
+ await options.loadPage({
371
+ contract: envelope.response.contract,
372
+ pageId: envelope.response.pageId
373
+ });
374
+ if (target.__ABSOLUTE_PAGE_READY__) {
375
+ await target.__ABSOLUTE_PAGE_READY__;
376
+ }
377
+ return {
378
+ contract: envelope.response.contract,
379
+ kind: "rendered",
380
+ pageId: envelope.response.pageId
381
+ };
382
+ };
383
+ var parseAbsoluteMobilePageEnvelope = (value) => {
384
+ if (!isRecord(value) || !isRecord(value.response)) {
385
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The server did not return an AbsoluteJS mobile page envelope.");
386
+ }
387
+ if (value.protocol !== ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION) {
388
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", `Unsupported mobile page protocol ${String(value.protocol)}.`);
389
+ }
390
+ const { kind } = value.response;
391
+ if (kind === "page") {
392
+ return {
393
+ protocol: ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
394
+ response: parsePageResult(value.response)
395
+ };
396
+ }
397
+ if (kind === "upgrade-required") {
398
+ return {
399
+ protocol: ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
400
+ response: parseUpgradeResult(value.response)
401
+ };
402
+ }
403
+ if (kind === "invalid-request") {
404
+ throw new AbsoluteMobilePageProtocolError("server-rejected-request", typeof value.response.message === "string" ? value.response.message : "The server rejected the mobile page request.");
405
+ }
406
+ if (kind === "error") {
407
+ throw new AbsoluteMobilePageProtocolError("server-error", "The server could not create a compatible mobile page representation.");
408
+ }
409
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", `Unknown mobile page response kind ${String(kind)}.`);
410
+ };
411
+
412
+ // src/mobile/routeMatcher.ts
413
+ var REGEXP_SPECIAL_CHARACTERS = /[.*+?^${}()|[\]\\]/g;
414
+ var routeSegmentPattern = (segment) => {
415
+ if (segment === "*")
416
+ return ".*";
417
+ if (segment.startsWith(":") && segment.endsWith("?"))
418
+ return "[^/]*";
419
+ if (segment.startsWith(":"))
420
+ return "[^/]+";
421
+ return segment.replace(REGEXP_SPECIAL_CHARACTERS, "\\$&");
422
+ };
423
+ var matchesAbsoluteMobileRoutePattern = (pattern, pathname) => {
424
+ const expression = pattern.split("/").map(routeSegmentPattern).join("/");
425
+ return new RegExp(`^${expression}/?$`).test(pathname);
426
+ };
427
+ var resolveAbsoluteMobileRoute = (routes, pathname, method = "GET") => routes.find((route) => route.method === method && matchesAbsoluteMobileRoutePattern(route.pattern, pathname));
428
+
429
+ // src/mobile/transport.ts
430
+ var pageForPathname = (manifest, pathname) => {
431
+ const route = resolveAbsoluteMobileRoute(manifest.routes, pathname);
432
+ if (!route) {
433
+ throw new TypeError(`No embedded mobile page owns ${pathname}.`);
434
+ }
435
+ const page = manifest.pages.find((candidate) => candidate.pageId === route.pageId);
436
+ if (!page) {
437
+ throw new TypeError(`Mobile route ${route.pattern} references missing page ${route.pageId}.`);
438
+ }
439
+ return page;
440
+ };
441
+ var canonicalBackendUrl = (manifest, path) => {
442
+ const origin = new URL(manifest.productionOrigin);
443
+ const url = new URL(path, `${origin.origin}/`);
444
+ if (url.origin !== origin.origin) {
445
+ throw new TypeError("Mobile transport cannot leave productionOrigin.");
446
+ }
447
+ url.hash = "";
448
+ return url;
449
+ };
450
+ var createAbsoluteMobilePageRequest = (manifest, path, options = {}) => {
451
+ const url = canonicalBackendUrl(manifest, path);
452
+ const page = pageForPathname(manifest, url.pathname);
453
+ const headers = new Headers(options.headers);
454
+ headers.set("accept", ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE);
455
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.appBuild, manifest.appBuild);
456
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.pageBundle, page.bundleHash);
457
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.pageContracts, page.contract);
458
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.pageId, page.pageId);
459
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.protocol, String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION));
460
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.runtime, manifest.runtime);
461
+ return new Request(url, {
462
+ headers,
463
+ method: "GET",
464
+ ...options.signal ? { signal: options.signal } : {}
465
+ });
466
+ };
467
+ var fetchAbsoluteMobilePage = async (manifest, path, options = {}) => {
468
+ const request = createAbsoluteMobilePageRequest(manifest, path, options);
469
+ const response = await (options.fetch ?? globalThis.fetch)(request);
470
+ const value = await response.json();
471
+ return value;
472
+ };
473
+ var resolveAbsoluteMobileDeepLink = (manifest, value) => {
474
+ const url = new URL(value);
475
+ if (url.username || url.password) {
476
+ throw new TypeError("Mobile deep links cannot contain credentials.");
477
+ }
478
+ const isHttpsHost = url.protocol === "https:" && manifest.deepLinkHosts.includes(url.hostname.toLowerCase());
479
+ const isCustomScheme = manifest.deepLinkScheme !== undefined && url.protocol === `${manifest.deepLinkScheme}:`;
480
+ if (!isHttpsHost && !isCustomScheme) {
481
+ throw new TypeError("Mobile deep link is outside the configured app URLs.");
482
+ }
483
+ return `${url.pathname || "/"}${url.search}${url.hash}`;
484
+ };
485
+ var resolveAbsoluteMobileNavigation = (manifest, value, localOrigin) => {
486
+ const url = new URL(value, `${localOrigin}/`);
487
+ const local = new URL(localOrigin);
488
+ const production = new URL(manifest.productionOrigin);
489
+ const matches = (candidate, allowed) => candidate.protocol === allowed.protocol && candidate.host === allowed.host;
490
+ if (!matches(url, local) && !matches(url, production)) {
491
+ return;
492
+ }
493
+ return `${url.pathname}${url.search}${url.hash}`;
494
+ };
495
+
496
+ // src/mobile/staticDocument.ts
497
+ var HTMX_REQUEST_ATTRIBUTE_PATTERN = /(\s(?:action|formaction|hx-(?:delete|get|patch|post|put))\s*=\s*["'])(\/[^"']*)(["'])/giu;
498
+ var BLOCKED_FRAGMENT_ELEMENTS = 'script,base,iframe,object,embed,link[rel="modulepreload"],meta[http-equiv="refresh"]';
499
+ var HTMX_REQUEST_ATTRIBUTES = new Set([
500
+ "hx-delete",
501
+ "hx-get",
502
+ "hx-patch",
503
+ "hx-post",
504
+ "hx-put"
505
+ ]);
506
+ var URL_ATTRIBUTES = new Set(["action", "formaction", "href", "src"]);
507
+ var HEX_RADIX = 16;
508
+ var SCRIPT_PLACEHOLDER_ATTRIBUTE = "data-absolute-mobile-script";
509
+ var bytesToHex = (bytes) => [...bytes].map((byte) => byte.toString(HEX_RADIX).padStart(2, "0")).join("");
510
+ var hashAbsoluteMobileStaticDocument = async (source) => bytesToHex(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(source))));
511
+ var rewriteAbsoluteMobileHtmxRequests = (source, productionOrigin) => source.replace(HTMX_REQUEST_ATTRIBUTE_PATTERN, (_match, prefix, path, quote) => `${prefix}${new URL(path, productionOrigin).href}${quote}`);
512
+ var backendUrl = (value, productionOrigin) => {
513
+ try {
514
+ const expected = new URL(productionOrigin);
515
+ const candidate = new URL(value, expected);
516
+ return candidate.origin === expected.origin ? candidate.href : undefined;
517
+ } catch {
518
+ return;
519
+ }
520
+ };
521
+ var sanitizeFragmentAttribute = (element, attribute, productionOrigin) => {
522
+ const name = attribute.name.toLowerCase();
523
+ const isExecutable = name === "srcdoc" || name.startsWith("on") || name.startsWith("hx-on");
524
+ const isUnsafeUrl = (HTMX_REQUEST_ATTRIBUTES.has(name) || URL_ATTRIBUTES.has(name)) && !backendUrl(attribute.value, productionOrigin);
525
+ if (isExecutable || isUnsafeUrl)
526
+ element.removeAttribute(attribute.name);
527
+ else if (HTMX_REQUEST_ATTRIBUTES.has(name) || URL_ATTRIBUTES.has(name)) {
528
+ element.setAttribute(attribute.name, backendUrl(attribute.value, productionOrigin) ?? attribute.value);
529
+ }
530
+ };
531
+ var sanitizeAbsoluteMobileHtmxFragment = (source, productionOrigin, parse = (value) => new DOMParser().parseFromString(value, "text/html")) => {
532
+ const parsed = parse(source);
533
+ parsed.querySelectorAll(BLOCKED_FRAGMENT_ELEMENTS).forEach((element) => {
534
+ element.remove();
535
+ });
536
+ parsed.querySelectorAll("*").forEach((element) => {
537
+ for (const attribute of [...element.attributes]) {
538
+ sanitizeFragmentAttribute(element, attribute, productionOrigin);
539
+ }
540
+ });
541
+ return parsed.body.innerHTML;
542
+ };
543
+ var installHtmxFragmentBoundary = (productionOrigin) => {
544
+ const validateUrl = (event) => {
545
+ const detail = Reflect.get(event, "detail");
546
+ if (typeof detail !== "object" || detail === null) {
547
+ event.preventDefault();
548
+ return;
549
+ }
550
+ const url = Reflect.get(detail, "url");
551
+ if (!(url instanceof URL) || !backendUrl(url.href, productionOrigin)) {
552
+ event.preventDefault();
553
+ }
554
+ };
555
+ const configureRequest = (event) => {
556
+ const detail = Reflect.get(event, "detail");
557
+ if (typeof detail !== "object" || detail === null)
558
+ return;
559
+ const path = Reflect.get(detail, "path");
560
+ if (typeof path !== "string")
561
+ return;
562
+ const resolved = backendUrl(path, productionOrigin);
563
+ if (resolved)
564
+ Reflect.set(detail, "path", resolved);
565
+ };
566
+ const sanitize = (event) => {
567
+ const detail = Reflect.get(event, "detail");
568
+ if (typeof detail !== "object" || detail === null)
569
+ return;
570
+ const serverResponse = Reflect.get(detail, "serverResponse");
571
+ if (typeof serverResponse !== "string")
572
+ return;
573
+ Reflect.set(detail, "serverResponse", sanitizeAbsoluteMobileHtmxFragment(serverResponse, productionOrigin));
574
+ };
575
+ addEventListener("htmx:validateUrl", validateUrl);
576
+ addEventListener("htmx:configRequest", configureRequest);
577
+ addEventListener("htmx:beforeSwap", sanitize);
578
+ return () => {
579
+ removeEventListener("htmx:validateUrl", validateUrl);
580
+ removeEventListener("htmx:configRequest", configureRequest);
581
+ removeEventListener("htmx:beforeSwap", sanitize);
582
+ };
583
+ };
584
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
585
+ var replaceAttributes = (target, source) => {
586
+ for (const attribute of [...target.attributes]) {
587
+ target.removeAttribute(attribute.name);
588
+ }
589
+ for (const attribute of [...source.attributes]) {
590
+ target.setAttribute(attribute.name, attribute.value);
591
+ }
592
+ };
593
+ var applyScriptAttribute = (element, name, value, localUrl) => {
594
+ if (name === SCRIPT_PLACEHOLDER_ATTRIBUTE)
595
+ return;
596
+ if (name === "src") {
597
+ const source = new URL(value, localUrl);
598
+ const navigation = new URL(localUrl).searchParams.get("absoluteNavigation");
599
+ if (navigation)
600
+ source.searchParams.set("absoluteNavigation", navigation);
601
+ element.src = source.href;
602
+ return;
603
+ }
604
+ element.setAttribute(name, value);
605
+ };
606
+ var executableScript = (script, localUrl) => {
607
+ const element = document.createElement("script");
608
+ for (const [name, value] of script.attributes) {
609
+ applyScriptAttribute(element, name, value, localUrl);
610
+ }
611
+ element.textContent = script.content;
612
+ return element;
613
+ };
614
+ var executeTrustedDocumentScript = async (placeholder, script, localUrl) => {
615
+ const executable = executableScript(script, localUrl);
616
+ if (executable.type === "module" && executable.src !== "") {
617
+ placeholder.remove();
618
+ await import(executable.src);
619
+ return;
620
+ }
621
+ const waitsForLoad = executable.src !== "" || executable.type === "module";
622
+ const loaded = waitsForLoad ? new Promise((resolve, reject) => {
623
+ executable.addEventListener("load", () => resolve(), {
624
+ once: true
625
+ });
626
+ executable.addEventListener("error", () => reject(new TypeError(`Embedded document script failed: ${executable.src || "inline module"}.`)), { once: true });
627
+ }) : Promise.resolve();
628
+ placeholder.replaceWith(executable);
629
+ await loaded;
630
+ };
631
+ var configureHtmxDocument = (parsed) => {
632
+ let meta = parsed.head.querySelector('meta[name="htmx-config"]');
633
+ if (!meta) {
634
+ meta = parsed.createElement("meta");
635
+ meta.name = "htmx-config";
636
+ parsed.head.appendChild(meta);
637
+ }
638
+ let authorConfig = {};
639
+ try {
640
+ const parsedConfig = JSON.parse(meta.content || "{}");
641
+ if (isRecord2(parsedConfig))
642
+ authorConfig = parsedConfig;
643
+ } catch {}
644
+ meta.content = JSON.stringify({
645
+ ...authorConfig,
646
+ selfRequestsOnly: false
647
+ });
648
+ };
649
+ var executeTrustedDocumentScripts = async (scripts, localUrl, index = 0) => {
650
+ const script = scripts[index];
651
+ if (!script)
652
+ return;
653
+ const placeholder = document.querySelector(`script[${SCRIPT_PLACEHOLDER_ATTRIBUTE}="${index}"]`);
654
+ if (placeholder) {
655
+ await executeTrustedDocumentScript(placeholder, script, localUrl);
656
+ }
657
+ await executeTrustedDocumentScripts(scripts, localUrl, index + 1);
658
+ };
659
+ var installTrustedDocument = async (source, localUrl, options) => {
660
+ const parsed = new DOMParser().parseFromString(source, "text/html");
661
+ if (options.htmx)
662
+ configureHtmxDocument(parsed);
663
+ const scripts = [...parsed.querySelectorAll("script")].map((script, index) => {
664
+ const definition = {
665
+ attributes: [...script.attributes].map((attribute) => [
666
+ attribute.name,
667
+ attribute.value
668
+ ]),
669
+ content: script.textContent ?? ""
670
+ };
671
+ script.removeAttribute("src");
672
+ script.type = "application/x-absolute-mobile-script";
673
+ script.setAttribute(SCRIPT_PLACEHOLDER_ATTRIBUTE, String(index));
674
+ return definition;
675
+ });
676
+ replaceAttributes(document.documentElement, parsed.documentElement);
677
+ document.head.replaceChildren(...[...parsed.head.childNodes].map((node) => document.importNode(node, true)));
678
+ document.body.replaceWith(document.importNode(parsed.body, true));
679
+ await executeTrustedDocumentScripts(scripts, localUrl);
680
+ };
681
+ var installAbsoluteMobileStaticDocument = async (manifest, page, localUrl) => {
682
+ const response = await fetch(localUrl, { cache: "no-store" });
683
+ if (!response.ok) {
684
+ throw new TypeError(`Embedded ${page.framework} document failed with HTTP ${response.status}.`);
685
+ }
686
+ const source = await response.text();
687
+ const actualHash = await hashAbsoluteMobileStaticDocument(source);
688
+ if (actualHash !== page.bundleHash) {
689
+ throw new TypeError(`Embedded ${page.framework} document failed integrity.`);
690
+ }
691
+ const disposeHtmxBoundary = page.framework === "htmx" ? installHtmxFragmentBoundary(manifest.productionOrigin) : undefined;
692
+ const ready = installTrustedDocument(page.framework === "htmx" ? rewriteAbsoluteMobileHtmxRequests(source, manifest.productionOrigin) : source, localUrl, { htmx: page.framework === "htmx" });
693
+ Reflect.set(window, "__ABSOLUTE_PAGE_READY__", ready);
694
+ Reflect.set(window, "__ABSOLUTE_PAGE_DISPOSE__", () => disposeHtmxBoundary?.());
695
+ await ready;
696
+ };
697
+
698
+ // node_modules/@absolutejs/http/dist/index.js
699
+ class AbsoluteHttpError extends Error {
700
+ code;
701
+ cause;
702
+ status;
703
+ url;
704
+ constructor(code, message, options = {}) {
705
+ super(message);
706
+ this.name = "AbsoluteHttpError";
707
+ this.code = code;
708
+ this.cause = options.cause;
709
+ this.status = options.status;
710
+ this.url = options.url;
711
+ }
712
+ }
713
+ var loopbackHosts = new Set(["127.0.0.1", "[::1]", "localhost"]);
714
+ var normalizeAbsoluteHttpOrigin = (value) => {
715
+ let url;
716
+ try {
717
+ url = new URL(value);
718
+ } catch (cause) {
719
+ throw new AbsoluteHttpError("origin", "HTTP origin is invalid.", {
720
+ cause
721
+ });
722
+ }
723
+ const loopback = url.protocol === "http:" && loopbackHosts.has(url.hostname);
724
+ if (url.protocol !== "https:" && !loopback)
725
+ throw new AbsoluteHttpError("origin", "HTTP origin must use HTTPS outside loopback development.");
726
+ if (url.username || url.password || url.pathname !== "/" || url.search || url.hash)
727
+ throw new AbsoluteHttpError("origin", "HTTP origin cannot contain credentials, a path, query, or fragment.");
728
+ return url.origin;
729
+ };
730
+ var resolveAbsoluteHttpUrl = (target, origin) => {
731
+ let url;
732
+ try {
733
+ url = new URL(String(target), `${origin}/`);
734
+ } catch (cause) {
735
+ throw new AbsoluteHttpError("origin", "HTTP request URL is invalid.", {
736
+ cause
737
+ });
738
+ }
739
+ if (url.username || url.password)
740
+ throw new AbsoluteHttpError("origin", "HTTP request URLs cannot contain credentials.");
741
+ if (url.origin !== origin)
742
+ throw new AbsoluteHttpError("origin", "Absolute HTTP refused a request outside the configured application origin.", { url: url.href });
743
+ url.hash = "";
744
+ return url;
745
+ };
746
+ var createAbsoluteHttpTransport = ({
747
+ fetch: fetchImpl = globalThis.fetch,
748
+ origin,
749
+ runtime
750
+ }) => ({
751
+ fetch: fetchImpl,
752
+ origin: normalizeAbsoluteHttpOrigin(origin),
753
+ runtime
754
+ });
755
+ var createWebHttpTransport = (options = {}) => {
756
+ const origin = options.origin ?? globalThis.location?.origin;
757
+ if (!origin)
758
+ throw new TypeError("The web HTTP transport requires a browser origin or explicit origin.");
759
+ return createAbsoluteHttpTransport({
760
+ ...options.fetch ? { fetch: options.fetch } : {},
761
+ origin,
762
+ runtime: "web"
763
+ });
764
+ };
765
+ var RUNTIME_REGISTRY = Symbol.for("@absolutejs/http/runtime");
766
+ var registryHost = globalThis;
767
+ var isRuntimeRegistry = (value) => typeof value === "object" && value !== null && Array.isArray(Reflect.get(value, "installations"));
768
+ var runtimeRegistry = (() => {
769
+ const existing = registryHost[RUNTIME_REGISTRY];
770
+ if (isRuntimeRegistry(existing))
771
+ return existing;
772
+ const created = { installations: [] };
773
+ Object.defineProperty(registryHost, RUNTIME_REGISTRY, {
774
+ configurable: false,
775
+ enumerable: false,
776
+ value: created,
777
+ writable: false
778
+ });
779
+ return created;
780
+ })();
781
+ var fallbackTransport = () => {
782
+ if (typeof location !== "undefined" && location.origin)
783
+ return createWebHttpTransport();
784
+ throw new AbsoluteHttpError("not-configured", "Absolute HTTP requires a request-scoped SSR transport outside a browser.");
785
+ };
786
+ var getAbsoluteHttpTransport = () => runtimeRegistry.installations.at(-1)?.transport ?? (runtimeRegistry.fallback ??= fallbackTransport());
787
+ var installAbsoluteHttpTransport = (transport) => {
788
+ const installation = { transport };
789
+ runtimeRegistry.installations.push(installation);
790
+ return () => {
791
+ const index = runtimeRegistry.installations.indexOf(installation);
792
+ if (index >= 0)
793
+ runtimeRegistry.installations.splice(index, 1);
794
+ };
795
+ };
796
+ var forbiddenCredentialHeaders = new Set([
797
+ "authorization",
798
+ "cookie",
799
+ "proxy-authorization"
800
+ ]);
801
+ var transportFor = (configured) => configured ?? getAbsoluteHttpTransport();
802
+ var safeHeaders = (input) => {
803
+ const headers = new Headers(input);
804
+ for (const name of forbiddenCredentialHeaders) {
805
+ if (headers.has(name))
806
+ throw new AbsoluteHttpError("origin", `Absolute HTTP owns the ${name} credential header for trusted transports.`);
807
+ }
808
+ return headers;
809
+ };
810
+ var requestFor = (target, options, transport) => {
811
+ const source = target instanceof Request ? target : undefined;
812
+ const requestedUrl = source ? source.url : target;
813
+ const url = resolveAbsoluteHttpUrl(requestedUrl, transport.origin);
814
+ const headers = safeHeaders(options?.headers ?? source?.headers);
815
+ return new Request(source ?? url, {
816
+ ...options,
817
+ credentials: transport.runtime === "web" ? "same-origin" : "omit",
818
+ headers,
819
+ redirect: "error"
820
+ });
821
+ };
822
+ var normalizeFetchError = (caught, url) => {
823
+ if (caught instanceof AbsoluteHttpError)
824
+ return caught;
825
+ if (caught instanceof DOMException && (caught.name === "AbortError" || caught.name === "TimeoutError"))
826
+ return new AbsoluteHttpError("aborted", "HTTP request was aborted.", {
827
+ cause: caught,
828
+ url
829
+ });
830
+ return new AbsoluteHttpError("network", "HTTP request failed.", {
831
+ cause: caught,
832
+ url
833
+ });
834
+ };
835
+ var requireOk = (response) => {
836
+ if (!response.ok)
837
+ throw new AbsoluteHttpError("http", `HTTP request failed with status ${response.status}.`, { status: response.status, url: response.url });
838
+ return response;
839
+ };
840
+ var parseJson = async (response) => {
841
+ const text = await response.text();
842
+ if (text === "")
843
+ return;
844
+ try {
845
+ return JSON.parse(text);
846
+ } catch (cause) {
847
+ throw new AbsoluteHttpError("response", "HTTP response is not valid JSON.", {
848
+ cause,
849
+ status: response.status,
850
+ url: response.url
851
+ });
852
+ }
853
+ };
854
+ var jsonOptions = (method, body, options) => {
855
+ const headers = new Headers(options?.headers);
856
+ if (body !== undefined && !headers.has("content-type"))
857
+ headers.set("content-type", "application/json");
858
+ let encoded;
859
+ try {
860
+ encoded = body === undefined ? undefined : JSON.stringify(body);
861
+ } catch (cause) {
862
+ throw new AbsoluteHttpError("body", "HTTP body is not JSON serializable.", {
863
+ cause
864
+ });
865
+ }
866
+ return encoded === undefined ? { ...options, headers, method } : { ...options, body: encoded, headers, method };
867
+ };
868
+ var createAbsoluteHttpClient = (options = {}) => {
869
+ const fetchTrusted = async (input, init) => {
870
+ const transport = transportFor(options.transport);
871
+ const request2 = requestFor(input, init, transport);
872
+ try {
873
+ return await transport.fetch(request2);
874
+ } catch (caught) {
875
+ throw normalizeFetchError(caught, request2.url);
876
+ }
877
+ };
878
+ const request = (target, requestOptions) => fetchTrusted(target, requestOptions);
879
+ const json = async (target, requestOptions) => parseJson(requireOk(await request(target, requestOptions)));
880
+ const jsonMethod = (method, target, body, requestOptions) => json(target, jsonOptions(method, body, requestOptions));
881
+ return {
882
+ delete: (target, requestOptions) => {
883
+ const { body, ...optionsWithoutBody } = requestOptions ?? {};
884
+ return jsonMethod("DELETE", target, body, optionsWithoutBody);
885
+ },
886
+ fetch: fetchTrusted,
887
+ get: (target, requestOptions) => json(target, { ...requestOptions, method: "GET" }),
888
+ json,
889
+ origin: () => transportFor(options.transport).origin,
890
+ patch: (target, body, requestOptions) => jsonMethod("PATCH", target, body, requestOptions),
891
+ post: (target, body, requestOptions) => jsonMethod("POST", target, body, requestOptions),
892
+ put: (target, body, requestOptions) => jsonMethod("PUT", target, body, requestOptions),
893
+ request,
894
+ text: async (target, requestOptions) => requireOk(await request(target, requestOptions)).text()
895
+ };
896
+ };
897
+ var http = createAbsoluteHttpClient();
898
+
899
+ // src/mobile/shellHttp.ts
900
+ var installAbsoluteMobileShellHttp = (origin, fetch2 = globalThis.fetch) => installAbsoluteHttpTransport(createAbsoluteHttpTransport({ fetch: fetch2, origin, runtime: "native" }));
901
+
902
+ // src/mobile/adaptiveShell.ts
903
+ import {
904
+ keyboard,
905
+ network,
906
+ platform,
907
+ systemBars
908
+ } from "@absolutejs/devices";
909
+ var STYLE_ID2 = "absolute-mobile-adaptive-shell";
910
+ var ANNOUNCER_ID = "absolute-mobile-announcer";
911
+ var HOST_METRICS_EVENT = "absolute:native-host-metrics";
912
+ var STATUS_STYLE = `
913
+ [data-absolute-mobile-status] {
914
+ display: grid;
915
+ min-height: var(--absolute-available-height, 100dvh);
916
+ place-items: center;
917
+ 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));
918
+ box-sizing: border-box;
919
+ color: CanvasText;
920
+ background: Canvas;
921
+ font: 500 1rem/1.5 system-ui, sans-serif;
922
+ text-align: center;
923
+ }
924
+ [data-absolute-mobile-announcer] {
925
+ position: fixed;
926
+ width: 1px;
927
+ height: 1px;
928
+ padding: 0;
929
+ margin: -1px;
930
+ overflow: hidden;
931
+ clip: rect(0, 0, 0, 0);
932
+ white-space: nowrap;
933
+ border: 0;
934
+ }
935
+ `;
936
+ var DEFAULT_KEYBOARD = {
937
+ heightPx: 0,
938
+ visible: false
939
+ };
940
+ var DEFAULT_NETWORK = {
941
+ connected: true,
942
+ connectionType: "unknown"
943
+ };
944
+ var DEFAULT_PLATFORM = {
945
+ formFactor: "unknown",
946
+ isNative: true,
947
+ os: "unknown",
948
+ runtime: "capacitor"
949
+ };
950
+ var finitePixels = (value) => Number.isFinite(value) ? Math.max(0, Math.round(value ?? 0)) : 0;
951
+ var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
952
+ var viewportSize = () => ({
953
+ height: finitePixels(globalThis.visualViewport?.height ?? innerHeight),
954
+ width: finitePixels(globalThis.visualViewport?.width ?? innerWidth)
955
+ });
956
+ var setPixels = (name, value) => document.documentElement.style.setProperty(name, `${finitePixels(value)}px`);
957
+ var ensureStyle2 = () => {
958
+ let style = document.getElementById(STYLE_ID2);
959
+ if (!(style instanceof HTMLStyleElement)) {
960
+ style = document.createElement("style");
961
+ style.id = STYLE_ID2;
962
+ style.textContent = STATUS_STYLE;
963
+ document.head.append(style);
964
+ }
965
+ };
966
+ var ensureViewport = () => {
967
+ let viewport = document.head.querySelector('meta[name="viewport"]');
968
+ if (!viewport) {
969
+ viewport = document.createElement("meta");
970
+ viewport.name = "viewport";
971
+ document.head.prepend(viewport);
972
+ }
973
+ const values = new Set(viewport.content.split(",").map((value) => value.trim()).filter(Boolean));
974
+ values.add("width=device-width");
975
+ values.add("initial-scale=1");
976
+ values.add("viewport-fit=cover");
977
+ viewport.content = [...values].join(",");
978
+ };
979
+ var ensureAnnouncer = () => {
980
+ let announcer = document.getElementById(ANNOUNCER_ID);
981
+ if (!(announcer instanceof HTMLElement)) {
982
+ announcer = document.createElement("div");
983
+ announcer.id = ANNOUNCER_ID;
984
+ announcer.dataset.absoluteMobileAnnouncer = "";
985
+ announcer.setAttribute("aria-atomic", "true");
986
+ announcer.setAttribute("aria-live", "polite");
987
+ announcer.setAttribute("role", "status");
988
+ document.body.append(announcer);
989
+ }
990
+ return announcer;
991
+ };
992
+ var dispatchState = (state) => dispatchEvent(new CustomEvent("absolute:adaptive-shell-change", { detail: state }));
993
+ var defaultDevices = {
994
+ keyboard,
995
+ network,
996
+ platform,
997
+ systemBars
998
+ };
999
+ var hostSafeAreaInsets = (value) => {
1000
+ if (typeof value !== "object" || value === null)
1001
+ return;
1002
+ const candidate = Reflect.get(value, "safeAreaInsets");
1003
+ if (typeof candidate !== "object" || candidate === null)
1004
+ return;
1005
+ const insets = {
1006
+ bottom: Reflect.get(candidate, "bottom"),
1007
+ left: Reflect.get(candidate, "left"),
1008
+ right: Reflect.get(candidate, "right"),
1009
+ top: Reflect.get(candidate, "top")
1010
+ };
1011
+ if (!isFiniteNumber(insets.bottom) || !isFiniteNumber(insets.left) || !isFiniteNumber(insets.right) || !isFiniteNumber(insets.top))
1012
+ return;
1013
+ return {
1014
+ bottom: finitePixels(insets.bottom),
1015
+ left: finitePixels(insets.left),
1016
+ right: finitePixels(insets.right),
1017
+ top: finitePixels(insets.top)
1018
+ };
1019
+ };
1020
+ var installAbsoluteMobileAdaptiveShell = async (devices = defaultDevices) => {
1021
+ let disposed = false;
1022
+ let keyboardState = { ...DEFAULT_KEYBOARD };
1023
+ let networkState = { ...DEFAULT_NETWORK };
1024
+ let platformInfo = { ...DEFAULT_PLATFORM };
1025
+ let viewport = viewportSize();
1026
+ const subscriptions = [];
1027
+ const state = () => ({
1028
+ availableHeight: globalThis.visualViewport ? viewport.height : Math.max(0, viewport.height - keyboardState.heightPx),
1029
+ keyboard: keyboardState,
1030
+ network: networkState,
1031
+ platform: platformInfo,
1032
+ viewportHeight: viewport.height,
1033
+ viewportWidth: viewport.width
1034
+ });
1035
+ const refreshDocument = () => {
1036
+ if (disposed || !document.head || !document.body)
1037
+ return;
1038
+ ensureViewport();
1039
+ ensureStyle2();
1040
+ const root = document.documentElement;
1041
+ const current = state();
1042
+ root.dataset.absoluteMobile = "";
1043
+ root.dataset.absoluteRuntime = current.platform.runtime;
1044
+ root.dataset.absolutePlatform = current.platform.os;
1045
+ root.dataset.absoluteFormFactor = current.platform.formFactor;
1046
+ root.dataset.absoluteKeyboard = current.keyboard.visible ? "visible" : "hidden";
1047
+ root.dataset.absoluteNetwork = current.network.connected ? "online" : "offline";
1048
+ root.dataset.absoluteConnection = current.network.connectionType;
1049
+ root.dataset.absoluteReducedMotion = current.platform.prefersReducedMotion ? "reduce" : "no-preference";
1050
+ const insets = current.platform.safeAreaInsets ?? {
1051
+ bottom: 0,
1052
+ left: 0,
1053
+ right: 0,
1054
+ top: 0
1055
+ };
1056
+ setPixels("--absolute-safe-area-inset-top", insets.top);
1057
+ setPixels("--absolute-safe-area-inset-right", insets.right);
1058
+ setPixels("--absolute-safe-area-inset-bottom", insets.bottom);
1059
+ setPixels("--absolute-safe-area-inset-left", insets.left);
1060
+ setPixels("--absolute-keyboard-height", current.keyboard.heightPx);
1061
+ setPixels("--absolute-viewport-height", current.viewportHeight);
1062
+ setPixels("--absolute-viewport-width", current.viewportWidth);
1063
+ setPixels("--absolute-available-height", current.availableHeight);
1064
+ ensureAnnouncer();
1065
+ };
1066
+ const publish = () => {
1067
+ refreshDocument();
1068
+ dispatchState(state());
1069
+ };
1070
+ const updateViewport = () => {
1071
+ viewport = viewportSize();
1072
+ devices.platform.getInfo().then((info) => {
1073
+ platformInfo = info;
1074
+ publish();
1075
+ return;
1076
+ }).catch(() => publish());
1077
+ };
1078
+ const updateKeyboard = (next) => {
1079
+ keyboardState = {
1080
+ heightPx: finitePixels(next.heightPx),
1081
+ visible: next.visible
1082
+ };
1083
+ publish();
1084
+ };
1085
+ const updateNetwork = (next) => {
1086
+ const changed = networkState.connected !== next.connected;
1087
+ networkState = next;
1088
+ publish();
1089
+ if (changed || !next.connected)
1090
+ ensureAnnouncer().textContent = next.connected ? "Connection restored." : "You are offline.";
1091
+ };
1092
+ const updateHostMetrics = (event) => {
1093
+ const insets = hostSafeAreaInsets(Reflect.get(event, "detail"));
1094
+ if (!insets)
1095
+ return;
1096
+ platformInfo = { ...platformInfo, safeAreaInsets: insets };
1097
+ publish();
1098
+ };
1099
+ const installKeyboard = async () => {
1100
+ const capability = await devices.keyboard.capability();
1101
+ if (!capability.available)
1102
+ return;
1103
+ keyboardState = await devices.keyboard.getState();
1104
+ subscriptions.push(await devices.keyboard.onChange(updateKeyboard));
1105
+ };
1106
+ try {
1107
+ platformInfo = await devices.platform.getInfo();
1108
+ } catch {}
1109
+ const initialHostInsets = hostSafeAreaInsets(Reflect.get(globalThis, "__absoluteNativeHostMetrics"));
1110
+ if (initialHostInsets)
1111
+ platformInfo = {
1112
+ ...platformInfo,
1113
+ safeAreaInsets: initialHostInsets
1114
+ };
1115
+ try {
1116
+ networkState = await devices.network.getStatus();
1117
+ subscriptions.push(await devices.network.onChange(updateNetwork));
1118
+ } catch {}
1119
+ try {
1120
+ await installKeyboard();
1121
+ } catch {}
1122
+ try {
1123
+ const capability = await devices.systemBars.capability("appearance");
1124
+ if (capability.available)
1125
+ await devices.systemBars.setAppearance("automatic");
1126
+ } catch {}
1127
+ addEventListener("resize", updateViewport);
1128
+ addEventListener(HOST_METRICS_EVENT, updateHostMetrics);
1129
+ globalThis.visualViewport?.addEventListener("resize", updateViewport);
1130
+ globalThis.visualViewport?.addEventListener("scroll", updateViewport);
1131
+ publish();
1132
+ if (!networkState.connected)
1133
+ ensureAnnouncer().textContent = "You are offline.";
1134
+ return {
1135
+ refreshDocument,
1136
+ state,
1137
+ dispose: async () => {
1138
+ if (disposed)
1139
+ return;
1140
+ disposed = true;
1141
+ removeEventListener("resize", updateViewport);
1142
+ removeEventListener(HOST_METRICS_EVENT, updateHostMetrics);
1143
+ globalThis.visualViewport?.removeEventListener("resize", updateViewport);
1144
+ globalThis.visualViewport?.removeEventListener("scroll", updateViewport);
1145
+ await Promise.all(subscriptions.map((unsubscribe) => unsubscribe()));
1146
+ }
1147
+ };
1148
+ };
1149
+
1150
+ // src/mobile/navigationLifecycle.ts
1151
+ var isAbortError = (error) => error instanceof DOMException ? error.name === "AbortError" : error instanceof Error && error.name === "AbortError";
1152
+ var cancelledResult = { kind: "cancelled" };
1153
+ var committedResult = { kind: "committed" };
1154
+ var failedResult = (error, phase) => ({ error, kind: "failed", phase });
1155
+ var createAbsoluteMobileNavigationCoordinator = (options) => {
1156
+ let activeLoad;
1157
+ let commitQueue = Promise.resolve();
1158
+ let disposed = false;
1159
+ let generation = 0;
1160
+ let lifecyclePhase = "idle";
1161
+ const navigate = async (request) => {
1162
+ if (disposed)
1163
+ return cancelledResult;
1164
+ const ownGeneration = generation += 1;
1165
+ activeLoad?.abort();
1166
+ const controller = new AbortController;
1167
+ activeLoad = controller;
1168
+ lifecyclePhase = "loading";
1169
+ options.onStart?.(request);
1170
+ let payload;
1171
+ try {
1172
+ payload = await options.load(request, controller.signal);
1173
+ } catch (error) {
1174
+ if (controller.signal.aborted || isAbortError(error) || ownGeneration !== generation || disposed) {
1175
+ if (ownGeneration === generation)
1176
+ lifecyclePhase = "idle";
1177
+ return cancelledResult;
1178
+ }
1179
+ lifecyclePhase = "idle";
1180
+ options.onFailure?.(error, "load", request);
1181
+ return failedResult(error, "load");
1182
+ }
1183
+ if (ownGeneration !== generation || disposed)
1184
+ return cancelledResult;
1185
+ if (activeLoad === controller)
1186
+ activeLoad = undefined;
1187
+ lifecyclePhase = "queued";
1188
+ const execute = async () => {
1189
+ if (ownGeneration !== generation || disposed) {
1190
+ if (ownGeneration === generation)
1191
+ lifecyclePhase = "idle";
1192
+ return cancelledResult;
1193
+ }
1194
+ lifecyclePhase = "committing";
1195
+ try {
1196
+ await options.commit(payload, request);
1197
+ options.onCommit?.(request);
1198
+ } catch (error) {
1199
+ if (ownGeneration === generation) {
1200
+ lifecyclePhase = "idle";
1201
+ options.onFailure?.(error, "commit", request);
1202
+ }
1203
+ return failedResult(error, "commit");
1204
+ }
1205
+ if (ownGeneration !== generation || disposed) {
1206
+ return cancelledResult;
1207
+ }
1208
+ lifecyclePhase = "idle";
1209
+ options.onSuccess?.(request);
1210
+ return committedResult;
1211
+ };
1212
+ const result = commitQueue.then(execute, execute);
1213
+ commitQueue = result.then(() => {
1214
+ return;
1215
+ }, () => {
1216
+ return;
1217
+ });
1218
+ return result;
1219
+ };
1220
+ return {
1221
+ cancelPending: () => {
1222
+ if (disposed || lifecyclePhase !== "loading" && lifecyclePhase !== "queued")
1223
+ return false;
1224
+ generation += 1;
1225
+ activeLoad?.abort();
1226
+ activeLoad = undefined;
1227
+ lifecyclePhase = "idle";
1228
+ return true;
1229
+ },
1230
+ dispose: () => {
1231
+ if (disposed)
1232
+ return;
1233
+ disposed = true;
1234
+ generation += 1;
1235
+ activeLoad?.abort();
1236
+ activeLoad = undefined;
1237
+ lifecyclePhase = "idle";
1238
+ },
1239
+ navigate: (request) => navigate(request),
1240
+ phase: () => lifecyclePhase
1241
+ };
1242
+ };
1243
+
1244
+ // src/mobile/navigationState.ts
1245
+ var CONTROL_SELECTOR = 'input, textarea, select, details, [contenteditable="true"]';
1246
+ var FOCUS_SELECTOR = '[data-absolute-navigation-focus], button, a[href], input, textarea, select, [tabindex]:not([tabindex="-1"])';
1247
+ var SCROLL_SELECTOR = "[data-absolute-app-main], [data-absolute-scroll-restoration]";
1248
+ var SENSITIVE_AUTOCOMPLETE = new Set([
1249
+ "cc-csc",
1250
+ "cc-number",
1251
+ "current-password",
1252
+ "new-password",
1253
+ "one-time-code"
1254
+ ]);
1255
+ var locatorFor = (element, elements) => {
1256
+ if (element.id)
1257
+ return { kind: "id", value: element.id };
1258
+ const name = element.getAttribute("name");
1259
+ if (name) {
1260
+ const matching = elements.filter((candidate) => candidate.getAttribute("name") === name);
1261
+ return { index: matching.indexOf(element), kind: "name", value: name };
1262
+ }
1263
+ return { index: elements.indexOf(element), kind: "index" };
1264
+ };
1265
+ var resolveLocator = (locator, selector) => {
1266
+ if (locator.kind === "id") {
1267
+ return document.getElementById(locator.value) ?? undefined;
1268
+ }
1269
+ if (locator.kind === "name") {
1270
+ return [...document.querySelectorAll(selector)].filter((element) => element.getAttribute("name") === locator.value)[locator.index];
1271
+ }
1272
+ return document.querySelectorAll(selector)[locator.index];
1273
+ };
1274
+ var shouldCaptureControl = (element) => {
1275
+ if (element.closest('[data-absolute-navigation-preserve="off"]'))
1276
+ return false;
1277
+ if (!(element instanceof HTMLInputElement))
1278
+ return true;
1279
+ if (element.type === "file" || element.type === "hidden" || element.type === "password")
1280
+ return false;
1281
+ return !SENSITIVE_AUTOCOMPLETE.has(element.autocomplete.toLowerCase());
1282
+ };
1283
+ var captureControl = (element, locator) => {
1284
+ const snapshot = {
1285
+ locator,
1286
+ tag: element.tagName.toLowerCase()
1287
+ };
1288
+ if (element instanceof HTMLInputElement) {
1289
+ if (element.type === "checkbox" || element.type === "radio") {
1290
+ snapshot.checked = element.checked;
1291
+ } else {
1292
+ snapshot.value = element.value;
1293
+ if (element.selectionStart !== null)
1294
+ snapshot.selectionStart = element.selectionStart;
1295
+ if (element.selectionEnd !== null)
1296
+ snapshot.selectionEnd = element.selectionEnd;
1297
+ }
1298
+ } else if (element instanceof HTMLTextAreaElement) {
1299
+ snapshot.value = element.value;
1300
+ snapshot.selectionStart = element.selectionStart;
1301
+ snapshot.selectionEnd = element.selectionEnd;
1302
+ } else if (element instanceof HTMLSelectElement) {
1303
+ snapshot.values = [...element.selectedOptions].map(({ value }) => value);
1304
+ } else if (element instanceof HTMLDetailsElement) {
1305
+ snapshot.open = element.open;
1306
+ } else if (element.getAttribute("contenteditable") === "true") {
1307
+ snapshot.value = element.textContent ?? "";
1308
+ }
1309
+ return snapshot;
1310
+ };
1311
+ var restoreControl = (snapshot) => {
1312
+ const element = resolveLocator(snapshot.locator, CONTROL_SELECTOR);
1313
+ if (!element || element.tagName.toLowerCase() !== snapshot.tag)
1314
+ return;
1315
+ if (element instanceof HTMLInputElement) {
1316
+ if (snapshot.checked !== undefined)
1317
+ element.checked = snapshot.checked;
1318
+ else if (snapshot.value !== undefined)
1319
+ element.value = snapshot.value;
1320
+ if (snapshot.selectionStart !== undefined && snapshot.selectionEnd !== undefined) {
1321
+ try {
1322
+ element.setSelectionRange(snapshot.selectionStart, snapshot.selectionEnd);
1323
+ } catch {}
1324
+ }
1325
+ } else if (element instanceof HTMLTextAreaElement) {
1326
+ if (snapshot.value !== undefined)
1327
+ element.value = snapshot.value;
1328
+ if (snapshot.selectionStart !== undefined && snapshot.selectionEnd !== undefined)
1329
+ element.setSelectionRange(snapshot.selectionStart, snapshot.selectionEnd);
1330
+ } else if (element instanceof HTMLSelectElement && snapshot.values) {
1331
+ for (const option of element.options) {
1332
+ option.selected = snapshot.values.includes(option.value);
1333
+ }
1334
+ } else if (element instanceof HTMLDetailsElement && snapshot.open !== undefined) {
1335
+ element.open = snapshot.open;
1336
+ } else if (element.getAttribute("contenteditable") === "true" && snapshot.value !== undefined) {
1337
+ element.textContent = snapshot.value;
1338
+ }
1339
+ };
1340
+ var focusElement = (element) => {
1341
+ if (!(element instanceof HTMLElement))
1342
+ return false;
1343
+ if (!element.matches(FOCUS_SELECTOR))
1344
+ element.tabIndex = -1;
1345
+ element.focus({ preventScroll: true });
1346
+ return document.activeElement === element;
1347
+ };
1348
+ var createAbsoluteMobileHistoryEntry = (path, index, entryId = crypto.randomUUID()) => ({
1349
+ absoluteMobile: true,
1350
+ entryId,
1351
+ index,
1352
+ path
1353
+ });
1354
+ var readAbsoluteMobileHistoryEntry = (value) => {
1355
+ if (typeof value !== "object" || value === null)
1356
+ return;
1357
+ if (Reflect.get(value, "absoluteMobile") !== true || typeof Reflect.get(value, "entryId") !== "string" || !Number.isSafeInteger(Reflect.get(value, "index")) || typeof Reflect.get(value, "path") !== "string")
1358
+ return;
1359
+ return {
1360
+ absoluteMobile: true,
1361
+ entryId: Reflect.get(value, "entryId"),
1362
+ index: Reflect.get(value, "index"),
1363
+ path: Reflect.get(value, "path")
1364
+ };
1365
+ };
1366
+ var captureAbsoluteMobileDocumentState = () => {
1367
+ const controls = [
1368
+ ...document.querySelectorAll(CONTROL_SELECTOR)
1369
+ ].filter(shouldCaptureControl);
1370
+ const focusable = [...document.querySelectorAll(FOCUS_SELECTOR)];
1371
+ const scrollable = [...document.querySelectorAll(SCROLL_SELECTOR)];
1372
+ const active = document.activeElement;
1373
+ return {
1374
+ controls: controls.map((element) => captureControl(element, locatorFor(element, controls))),
1375
+ ...active instanceof Element && focusable.includes(active) ? { focus: locatorFor(active, focusable) } : {},
1376
+ scroll: scrollable.map((element) => ({
1377
+ left: element.scrollLeft,
1378
+ locator: locatorFor(element, scrollable),
1379
+ top: element.scrollTop
1380
+ })),
1381
+ window: { x: window.scrollX, y: window.scrollY }
1382
+ };
1383
+ };
1384
+ var restoreAbsoluteMobileDocumentState = (snapshot) => {
1385
+ for (const control of snapshot.controls)
1386
+ restoreControl(control);
1387
+ const focus = snapshot.focus ? resolveLocator(snapshot.focus, FOCUS_SELECTOR) : undefined;
1388
+ focusElement(focus);
1389
+ for (const scroll of snapshot.scroll) {
1390
+ const element = resolveLocator(scroll.locator, SCROLL_SELECTOR);
1391
+ if (element)
1392
+ element.scrollTo(scroll.left, scroll.top);
1393
+ }
1394
+ window.scrollTo(snapshot.window.x, snapshot.window.y);
1395
+ };
1396
+ var resetAbsoluteMobileDocumentState = () => {
1397
+ for (const element of document.querySelectorAll(SCROLL_SELECTOR)) {
1398
+ element.scrollTo(0, 0);
1399
+ }
1400
+ window.scrollTo(0, 0);
1401
+ const autofocus = document.querySelector("[autofocus]");
1402
+ if (autofocus && focusElement(autofocus))
1403
+ return;
1404
+ const target = ["[data-absolute-navigation-focus]", "main h1", "h1", "main"].map((selector) => document.querySelector(selector)).find((element) => element !== null);
1405
+ focusElement(target ?? undefined);
1406
+ };
1407
+
1408
+ // src/mobile/shellBootstrap.ts
1409
+ var MANIFEST_PATH = "./absolute-mobile-manifest.json";
1410
+ var STATUS_ID = "absolute-mobile-status";
1411
+ var NAVIGATION_ERROR_ID = "absolute-mobile-navigation-error";
1412
+ var initialNavigationPath = (entry) => {
1413
+ const expoPath = new URLSearchParams(location.search).get("absolutePath");
1414
+ if (expoPath?.startsWith("/") && !expoPath.startsWith("//"))
1415
+ return expoPath;
1416
+ const current = `${location.pathname}${location.search}${location.hash}`;
1417
+ return current === "/" || current === "/index.html" ? entry : current;
1418
+ };
1419
+ var currentNavigationPath = () => {
1420
+ const expoPath = new URLSearchParams(location.search).get("absolutePath");
1421
+ return expoPath?.startsWith("/") && !expoPath.startsWith("//") ? expoPath : `${location.pathname}${location.search}${location.hash}`;
1422
+ };
1423
+ var historyUrl = (path) => {
1424
+ if (!Reflect.get(globalThis, "__absoluteExpoBridge")) {
1425
+ return path;
1426
+ }
1427
+ const url = new URL(location.href);
1428
+ url.search = "";
1429
+ url.hash = "";
1430
+ url.searchParams.set("absolutePath", path);
1431
+ return url.href;
1432
+ };
1433
+ var writeNavigationHistory = (entry, mode) => {
1434
+ history[mode === "push" ? "pushState" : "replaceState"](entry, "", historyUrl(entry.path));
1435
+ notifyExpoNavigationPath(entry.path);
1436
+ };
1437
+ var notifyExpoNavigationPath = (path) => {
1438
+ const bridge = Reflect.get(globalThis, "__absoluteExpoBridge");
1439
+ if (typeof bridge === "object" && bridge !== null && typeof Reflect.get(bridge, "setPath") === "function") {
1440
+ Reflect.get(bridge, "setPath").call(bridge, path);
1441
+ }
1442
+ };
1443
+ var navigationGeneration = 0;
1444
+ var adaptiveShell;
1445
+ var uiPrimitives;
1446
+ var notifyCapacitorSystemBarsDomReady = () => {
1447
+ const provider = Reflect.get(globalThis, "CapacitorSystemBarsAndroidInterface");
1448
+ if (typeof provider !== "object" || provider === null)
1449
+ return;
1450
+ const onDOMReady = Reflect.get(provider, "onDOMReady");
1451
+ if (typeof onDOMReady !== "function")
1452
+ return;
1453
+ try {
1454
+ onDOMReady.call(provider);
1455
+ } catch {}
1456
+ };
1457
+ var readManifest = async () => {
1458
+ const response = await fetch(MANIFEST_PATH, { cache: "no-store" });
1459
+ if (!response.ok) {
1460
+ throw new TypeError(`Mobile manifest failed with HTTP ${response.status}.`);
1461
+ }
1462
+ const manifest = await response.json();
1463
+ return manifest;
1464
+ };
1465
+ var renderStatus = (message, kind = "loading") => {
1466
+ document.title = "AbsoluteJS";
1467
+ document.head.innerHTML = '<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">';
1468
+ document.body.innerHTML = `<main id="${STATUS_ID}" data-absolute-mobile-status="${kind}" role="status" aria-atomic="true" aria-live="polite"></main>`;
1469
+ const status = document.getElementById(STATUS_ID);
1470
+ if (status)
1471
+ status.textContent = message;
1472
+ adaptiveShell?.refreshDocument();
1473
+ };
1474
+ var clearNavigationFailure = () => {
1475
+ document.getElementById(NAVIGATION_ERROR_ID)?.remove();
1476
+ };
1477
+ var renderNavigationFailure = (message, retry) => {
1478
+ clearNavigationFailure();
1479
+ const error = document.createElement("aside");
1480
+ error.id = NAVIGATION_ERROR_ID;
1481
+ error.dataset.absoluteMobileNavigationError = "";
1482
+ error.setAttribute("role", "alert");
1483
+ error.style.cssText = "position:fixed;z-index:2147483646;inset:auto max(1rem,var(--absolute-safe-area-inset-right,0px)) max(1rem,var(--absolute-safe-area-inset-bottom,0px)) max(1rem,var(--absolute-safe-area-inset-left,0px));display:flex;align-items:center;justify-content:space-between;gap:1rem;box-sizing:border-box;padding:.75rem 1rem;border:1px solid color-mix(in srgb,CanvasText 24%,transparent);border-radius:.75rem;background:Canvas;color:CanvasText;box-shadow:0 .5rem 2rem color-mix(in srgb,CanvasText 20%,transparent)";
1484
+ const text = document.createElement("span");
1485
+ text.textContent = message;
1486
+ const button = document.createElement("button");
1487
+ button.type = "button";
1488
+ button.textContent = "Retry";
1489
+ button.addEventListener("click", retry, { once: true });
1490
+ error.append(text, button);
1491
+ document.body.append(error);
1492
+ };
1493
+ var markNavigationPending = (pending) => {
1494
+ if (pending) {
1495
+ document.body.dataset.absoluteMobileNavigationPending = "";
1496
+ document.body.setAttribute("aria-busy", "true");
1497
+ } else {
1498
+ delete document.body.dataset.absoluteMobileNavigationPending;
1499
+ document.body.removeAttribute("aria-busy");
1500
+ }
1501
+ };
1502
+ var renderPageTarget = () => {
1503
+ document.title = "AbsoluteJS";
1504
+ document.head.innerHTML = '<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">';
1505
+ document.body.innerHTML = '<div id="root"></div>';
1506
+ adaptiveShell?.refreshDocument();
1507
+ };
1508
+ var localBundleUrl = (manifest, pageId, contract) => {
1509
+ const page = manifest.pages.find((candidate) => candidate.pageId === pageId && candidate.contract === contract);
1510
+ if (!page) {
1511
+ throw new TypeError(`The embedded app does not contain ${pageId} contract ${contract}.`);
1512
+ }
1513
+ const url = new URL(page.localBundlePath, document.baseURI);
1514
+ url.searchParams.set("absoluteNavigation", String(navigationGeneration += 1));
1515
+ return url.href;
1516
+ };
1517
+ var installLocalPageStyle = (manifest, pageId, contract) => {
1518
+ const page = manifest.pages.find((candidate) => candidate.pageId === pageId && candidate.contract === contract);
1519
+ if (!page?.localStylePath)
1520
+ return;
1521
+ const link = document.createElement("link");
1522
+ link.rel = "stylesheet";
1523
+ link.href = new URL(page.localStylePath, document.baseURI).href;
1524
+ link.dataset.absoluteMobilePageStyle = page.bundleHash;
1525
+ document.head.appendChild(link);
1526
+ };
1527
+ var waitForDocumentPaint = () => new Promise((resolve) => {
1528
+ if (typeof requestAnimationFrame !== "function") {
1529
+ setTimeout(resolve, 0);
1530
+ return;
1531
+ }
1532
+ requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
1533
+ });
1534
+ var commitNavigation = async (manifest, envelope, hadActivePage) => {
1535
+ if (!hadActivePage)
1536
+ renderStatus("Loading…");
1537
+ let activation;
1538
+ const commit = async () => {
1539
+ await disposeAbsoluteMobilePage();
1540
+ activation = await activateAbsoluteMobilePage(envelope, {
1541
+ loadPage: ({ contract, pageId }) => {
1542
+ const page = manifest.pages.find((candidate) => candidate.pageId === pageId && candidate.contract === contract);
1543
+ if (!page) {
1544
+ throw new TypeError(`The embedded app does not contain ${pageId} contract ${contract}.`);
1545
+ }
1546
+ if (page.framework === "html" || page.framework === "htmx") {
1547
+ return installAbsoluteMobileStaticDocument(manifest, page, localBundleUrl(manifest, pageId, contract));
1548
+ }
1549
+ renderPageTarget();
1550
+ installLocalPageStyle(manifest, pageId, contract);
1551
+ return import(localBundleUrl(manifest, pageId, contract));
1552
+ }
1553
+ });
1554
+ };
1555
+ const transitionDocument = document;
1556
+ if (hadActivePage && document.documentElement.dataset.absoluteReducedMotion !== "reduce" && transitionDocument.startViewTransition) {
1557
+ const transition = transitionDocument.startViewTransition(commit);
1558
+ await transition.updateCallbackDone;
1559
+ await transition.finished.catch(() => {
1560
+ return;
1561
+ });
1562
+ } else
1563
+ await commit();
1564
+ if (!activation)
1565
+ throw new TypeError("The mobile page did not produce an activation.");
1566
+ if (activation.kind === "upgrade-required") {
1567
+ renderStatus("This app version must be updated to continue.", "update");
1568
+ return false;
1569
+ }
1570
+ await waitForDocumentPaint();
1571
+ document.body.dataset.absoluteMobilePageActive = "";
1572
+ adaptiveShell?.refreshDocument();
1573
+ return true;
1574
+ };
1575
+ var openExternalLink = (anchor, event) => {
1576
+ if (Reflect.get(globalThis, "__absoluteExpoBridge"))
1577
+ return;
1578
+ try {
1579
+ const url = new URL(anchor.href);
1580
+ if (url.username || url.password || url.protocol !== "http:" && url.protocol !== "https:")
1581
+ return;
1582
+ event.preventDefault();
1583
+ Browser.open({ url: url.href }).catch(() => location.assign(url.href));
1584
+ } catch {}
1585
+ };
1586
+ var installAnchorNavigation = (manifest, onNavigate, onBack) => {
1587
+ const handleClick = (event) => {
1588
+ if (!(event.target instanceof Element))
1589
+ return;
1590
+ const anchor = event.target.closest("a[href]");
1591
+ if (!(anchor instanceof HTMLAnchorElement) || anchor.target)
1592
+ return;
1593
+ const intent = readAbsoluteMobileLinkIntent(anchor);
1594
+ if (intent.kind === "back") {
1595
+ event.preventDefault();
1596
+ if (!uiPrimitives?.requestBack() && !onBack())
1597
+ history.back();
1598
+ return;
1599
+ }
1600
+ if (intent.kind === "external") {
1601
+ openExternalLink(anchor, event);
1602
+ return;
1603
+ }
1604
+ try {
1605
+ const path = resolveAbsoluteMobileNavigation(manifest, anchor.href, location.origin);
1606
+ if (!path)
1607
+ return;
1608
+ event.preventDefault();
1609
+ onNavigate(path, intent.kind === "navigate" && intent.replace ? "replace" : "forward", intent.kind === "navigate" && intent.replace);
1610
+ } catch {}
1611
+ };
1612
+ addEventListener("click", handleClick);
1613
+ return () => removeEventListener("click", handleClick);
1614
+ };
1615
+ var navigateDeepLink = (manifest, onNavigate, url, authRedirectUri) => {
1616
+ if (!url)
1617
+ return;
1618
+ if (authRedirectUri) {
1619
+ const actual = new URL(url);
1620
+ const redirect = new URL(authRedirectUri);
1621
+ if (actual.protocol === redirect.protocol && actual.host === redirect.host && actual.pathname === redirect.pathname)
1622
+ return;
1623
+ }
1624
+ onNavigate(resolveAbsoluteMobileDeepLink(manifest, url));
1625
+ };
1626
+ var deepLinkListener = (manifest, onNavigate, authRedirectUri) => ({ url }) => navigateDeepLink(manifest, onNavigate, url, authRedirectUri);
1627
+ var installDeepLinks = async (manifest, onNavigate, authRedirectUri) => {
1628
+ let listener;
1629
+ try {
1630
+ listener = await App.addListener("appUrlOpen", deepLinkListener(manifest, onNavigate, authRedirectUri));
1631
+ const launch = await App.getLaunchUrl();
1632
+ navigateDeepLink(manifest, onNavigate, launch?.url, authRedirectUri);
1633
+ } catch {}
1634
+ return listener;
1635
+ };
1636
+ var startAbsoluteMobileShell = async (options = {}) => {
1637
+ notifyCapacitorSystemBarsDomReady();
1638
+ await adaptiveShell?.dispose();
1639
+ uiPrimitives?.dispose();
1640
+ adaptiveShell = await installAbsoluteMobileAdaptiveShell();
1641
+ uiPrimitives = installAbsoluteMobileUiPrimitives();
1642
+ const manifest = await readManifest();
1643
+ let activePath = initialNavigationPath(manifest.entry);
1644
+ let activeEntry = readAbsoluteMobileHistoryEntry(history.state) ?? createAbsoluteMobileHistoryEntry(activePath, 0);
1645
+ if (activeEntry.path !== activePath) {
1646
+ activeEntry = createAbsoluteMobileHistoryEntry(activePath, 0);
1647
+ }
1648
+ writeNavigationHistory(activeEntry, "replace");
1649
+ const auth = manifest.auth && options.createAuth ? await options.createAuth(manifest.auth, {
1650
+ beforeSignOut: options.beforeSignOut
1651
+ }) : undefined;
1652
+ const applicationFetch = auth?.fetch ?? options.createFetch?.(manifest) ?? globalThis.fetch;
1653
+ installAbsoluteMobileShellHttp(manifest.productionOrigin, applicationFetch);
1654
+ if (auth)
1655
+ options.connectPush?.(auth);
1656
+ if (auth && manifest.sync?.socketTickets)
1657
+ options.installSync?.(auth, manifest.sync);
1658
+ const snapshots = new Map;
1659
+ const targetEntries = new WeakMap;
1660
+ let suppressNextPop = false;
1661
+ let hasActivePage = false;
1662
+ let removeAnchorNavigation = () => {
1663
+ return;
1664
+ };
1665
+ const coordinator = createAbsoluteMobileNavigationCoordinator({
1666
+ commit: async (envelope, request) => {
1667
+ if (hasActivePage) {
1668
+ snapshots.set(activeEntry.entryId, captureAbsoluteMobileDocumentState());
1669
+ }
1670
+ document.documentElement.dataset.absoluteNavigationDirection = request.direction;
1671
+ try {
1672
+ const previouslyActive = hasActivePage;
1673
+ hasActivePage = false;
1674
+ hasActivePage = await commitNavigation(manifest, envelope, previouslyActive);
1675
+ } finally {
1676
+ reinstallBrowserNavigation();
1677
+ }
1678
+ },
1679
+ load: (request, signal) => fetchAbsoluteMobilePage(manifest, request.path, {
1680
+ fetch: applicationFetch,
1681
+ signal
1682
+ }),
1683
+ onFailure: (error, phase, request) => {
1684
+ console.error("[Absolute Mobile] Navigation failed:", error);
1685
+ markNavigationPending(false);
1686
+ const targetEntry = targetEntries.get(request);
1687
+ if (targetEntry && targetEntry.index !== activeEntry.index) {
1688
+ suppressNextPop = true;
1689
+ history.go(activeEntry.index - targetEntry.index);
1690
+ }
1691
+ const message = document.documentElement.dataset.absoluteNetwork === "offline" ? "You are offline. Reconnect to load this page." : "Unable to load this page. Check your connection and retry.";
1692
+ if (phase === "load" && hasActivePage) {
1693
+ renderNavigationFailure(message, () => {
1694
+ if (targetEntry) {
1695
+ history.go(targetEntry.index - activeEntry.index);
1696
+ } else
1697
+ coordinator.navigate(request);
1698
+ });
1699
+ } else
1700
+ renderStatus(message, "error");
1701
+ },
1702
+ onStart: () => {
1703
+ clearNavigationFailure();
1704
+ markNavigationPending(true);
1705
+ },
1706
+ onSuccess: (request) => {
1707
+ let nextEntry = targetEntries.get(request);
1708
+ if (request.historyMode === "push") {
1709
+ nextEntry = createAbsoluteMobileHistoryEntry(request.path, activeEntry.index + 1);
1710
+ writeNavigationHistory(nextEntry, "push");
1711
+ } else if (request.historyMode === "replace") {
1712
+ nextEntry = createAbsoluteMobileHistoryEntry(request.path, activeEntry.index);
1713
+ writeNavigationHistory(nextEntry, "replace");
1714
+ }
1715
+ nextEntry ??= activeEntry;
1716
+ activeEntry = nextEntry;
1717
+ activePath = request.path;
1718
+ markNavigationPending(false);
1719
+ clearNavigationFailure();
1720
+ const snapshot = snapshots.get(activeEntry.entryId);
1721
+ if (snapshot)
1722
+ restoreAbsoluteMobileDocumentState(snapshot);
1723
+ else if (request.historyMode !== "none")
1724
+ resetAbsoluteMobileDocumentState();
1725
+ uiPrimitives?.navigate({
1726
+ direction: request.direction,
1727
+ from: request.from,
1728
+ to: request.path
1729
+ });
1730
+ }
1731
+ });
1732
+ const cancelPendingNavigation = () => {
1733
+ if (!coordinator.cancelPending())
1734
+ return false;
1735
+ markNavigationPending(false);
1736
+ clearNavigationFailure();
1737
+ return true;
1738
+ };
1739
+ const handlePopState = (event) => {
1740
+ const path = currentNavigationPath();
1741
+ if (suppressNextPop) {
1742
+ suppressNextPop = false;
1743
+ notifyExpoNavigationPath(activePath);
1744
+ return;
1745
+ }
1746
+ const targetEntry = readAbsoluteMobileHistoryEntry(event.state) ?? createAbsoluteMobileHistoryEntry(path, activeEntry.index - 1);
1747
+ notifyExpoNavigationPath(path);
1748
+ const request = {
1749
+ direction: targetEntry.index > activeEntry.index ? "forward" : "back",
1750
+ from: activePath,
1751
+ historyMode: "none",
1752
+ path
1753
+ };
1754
+ targetEntries.set(request, targetEntry);
1755
+ coordinator.navigate(request);
1756
+ };
1757
+ const reinstallBrowserNavigation = () => {
1758
+ removeAnchorNavigation();
1759
+ removeEventListener("popstate", handlePopState);
1760
+ uiPrimitives?.dispose();
1761
+ uiPrimitives = installAbsoluteMobileUiPrimitives();
1762
+ uiPrimitives.refreshDocument(activePath);
1763
+ removeAnchorNavigation = installAnchorNavigation(manifest, onNavigate, cancelPendingNavigation);
1764
+ addEventListener("popstate", handlePopState);
1765
+ };
1766
+ const onNavigate = (path, direction = "forward", replace = false) => {
1767
+ if (path === activePath && !replace && hasActivePage)
1768
+ return;
1769
+ coordinator.navigate({
1770
+ direction,
1771
+ from: activePath,
1772
+ historyMode: replace ? "replace" : "push",
1773
+ path
1774
+ });
1775
+ };
1776
+ reinstallBrowserNavigation();
1777
+ await coordinator.navigate({
1778
+ direction: "replace",
1779
+ from: activePath,
1780
+ historyMode: "none",
1781
+ path: activePath
1782
+ });
1783
+ await options.installUpdates?.(manifest);
1784
+ await installDeepLinks(manifest, onNavigate, auth?.redirectUri);
1785
+ try {
1786
+ await App.addListener("backButton", ({ canGoBack }) => {
1787
+ if (uiPrimitives?.requestBack())
1788
+ return;
1789
+ if (cancelPendingNavigation())
1790
+ return;
1791
+ if (canGoBack)
1792
+ history.back();
1793
+ else
1794
+ App.exitApp();
1795
+ });
1796
+ } catch {}
1797
+ };
1798
+ export {
1799
+ startAbsoluteMobileShell
1800
+ };