@absolutejs/absolute 0.20.0-beta.4 → 0.20.0-beta.41

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 (110) hide show
  1. package/README.md +203 -0
  2. package/dist/angular/browser.js +15 -1
  3. package/dist/angular/browser.js.map +3 -3
  4. package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
  5. package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
  6. package/dist/angular/index.js +361 -30
  7. package/dist/angular/index.js.map +9 -6
  8. package/dist/angular/server.js +361 -30
  9. package/dist/angular/server.js.map +9 -6
  10. package/dist/build.js +2097 -1114
  11. package/dist/build.js.map +20 -16
  12. package/dist/cli/config/server.js +4 -0
  13. package/dist/cli/index.js +6986 -1561
  14. package/dist/dev/client/cssUtils.ts +16 -2
  15. package/dist/dev/client/handlers/rebuild.ts +11 -1
  16. package/dist/dev/client/hmrClient.ts +35 -3
  17. package/dist/dev/client/hmrTiming.ts +20 -7
  18. package/dist/dev/client/syncDevtools.ts +237 -0
  19. package/dist/dev/serverBootstrap.js +28 -0
  20. package/dist/index.js +2947 -1670
  21. package/dist/index.js.map +37 -31
  22. package/dist/mobile/browser.js +123 -1
  23. package/dist/mobile/browser.js.map +6 -4
  24. package/dist/mobile/index.js +4579 -375
  25. package/dist/mobile/index.js.map +39 -18
  26. package/dist/mobile/remoteMacAgentEntry.js +14 -14
  27. package/dist/mobile/shellAuth.js +36 -0
  28. package/dist/mobile/shellBootstrap.js +837 -0
  29. package/dist/mobile/shellExpoDevices.js +99 -0
  30. package/dist/mobile/shellSync.js +173 -0
  31. package/dist/react/index.js +5 -1
  32. package/dist/react/index.js.map +3 -3
  33. package/dist/react/server.js +5 -1
  34. package/dist/react/server.js.map +3 -3
  35. package/dist/src/angular/pageHandler.d.ts +3 -0
  36. package/dist/src/build/pwa.d.ts +16 -0
  37. package/dist/src/cli/config/server.d.ts +1 -1
  38. package/dist/src/cli/instanceStatus.d.ts +1 -0
  39. package/dist/src/cli/scripts/dev.d.ts +2 -0
  40. package/dist/src/core/devBuild.d.ts +1 -0
  41. package/dist/src/core/pageHandlers.d.ts +11 -2
  42. package/dist/src/core/prepare.d.ts +376 -0
  43. package/dist/src/dev/clientManager.d.ts +1 -0
  44. package/dist/src/dev/devCert.d.ts +6 -4
  45. package/dist/src/dev/serverEntryCopies.d.ts +3 -0
  46. package/dist/src/mobile/androidEmulatorController.d.ts +14 -2
  47. package/dist/src/mobile/androidRelease.d.ts +6 -0
  48. package/dist/src/mobile/androidTestReport.d.ts +14 -0
  49. package/dist/src/mobile/androidUpgradeConformance.d.ts +47 -0
  50. package/dist/src/mobile/androidWebView.d.ts +1 -0
  51. package/dist/src/mobile/browser.d.ts +1 -0
  52. package/dist/src/mobile/buildPipeline.d.ts +1 -0
  53. package/dist/src/mobile/capacitorBundle.d.ts +22 -1
  54. package/dist/src/mobile/ciWorkflow.d.ts +26 -0
  55. package/dist/src/mobile/client.d.ts +4 -0
  56. package/dist/src/mobile/config.d.ts +4 -1
  57. package/dist/src/mobile/devDeviceAdapter.d.ts +3 -0
  58. package/dist/src/mobile/deviceCapabilities.d.ts +46 -0
  59. package/dist/src/mobile/expoBridge.d.ts +91 -0
  60. package/dist/src/mobile/expoDevController.d.ts +49 -0
  61. package/dist/src/mobile/expoProject.d.ts +16 -0
  62. package/dist/src/mobile/index.d.ts +14 -0
  63. package/dist/src/mobile/iosDeviceAcceptance.d.ts +32 -0
  64. package/dist/src/mobile/iosPhysicalDeviceTransport.d.ts +16 -0
  65. package/dist/src/mobile/iosRelease.d.ts +4 -1
  66. package/dist/src/mobile/iosSimulatorController.d.ts +20 -2
  67. package/dist/src/mobile/iosTestReport.d.ts +24 -0
  68. package/dist/src/mobile/mobileBundleInspection.d.ts +55 -0
  69. package/dist/src/mobile/mobileInspect.d.ts +136 -0
  70. package/dist/src/mobile/mobilePreview.d.ts +174 -0
  71. package/dist/src/mobile/mobilePreviewClient.d.ts +1 -0
  72. package/dist/src/mobile/nativeAuth.d.ts +17 -0
  73. package/dist/src/mobile/nativeBackgroundSync.d.ts +4 -0
  74. package/dist/src/mobile/nativeDeviceCapabilities.d.ts +6 -0
  75. package/dist/src/mobile/nativeTestReport.d.ts +91 -0
  76. package/dist/src/mobile/releaseArtifact.d.ts +2 -0
  77. package/dist/src/mobile/releaseDoctor.d.ts +23 -1
  78. package/dist/src/mobile/remoteMacProtocol.d.ts +11 -0
  79. package/dist/src/mobile/shellAuth.d.ts +15 -0
  80. package/dist/src/mobile/shellBootstrap.d.ts +23 -1
  81. package/dist/src/mobile/shellExpoDevices.d.ts +3 -0
  82. package/dist/src/mobile/shellHttp.d.ts +2 -0
  83. package/dist/src/mobile/shellPush.d.ts +11 -0
  84. package/dist/src/mobile/shellSync.d.ts +47 -0
  85. package/dist/src/mobile/staticDocument.d.ts +5 -0
  86. package/dist/src/mobile/syncRemediation.d.ts +10 -0
  87. package/dist/src/mobile/syncSchema.d.ts +9 -0
  88. package/dist/src/mobile/transport.d.ts +16 -1
  89. package/dist/src/plugins/hmr.d.ts +3 -0
  90. package/dist/src/plugins/imageOptimizer.d.ts +1 -1
  91. package/dist/src/react/hooks/useMediaQuery.d.ts +1 -1
  92. package/dist/src/svelte/pageHandler.d.ts +3 -0
  93. package/dist/src/utils/imageProcessing.d.ts +3 -0
  94. package/dist/src/utils/loadConfig.d.ts +1 -0
  95. package/dist/src/utils/logger.d.ts +1 -0
  96. package/dist/src/utils/startupBanner.d.ts +1 -0
  97. package/dist/src/utils/userAgentFunctions.d.ts +1 -1
  98. package/dist/src/vue/pageHandler.d.ts +3 -0
  99. package/dist/svelte/index.js +316 -23
  100. package/dist/svelte/index.js.map +8 -5
  101. package/dist/svelte/server.js +311 -18
  102. package/dist/svelte/server.js.map +8 -5
  103. package/dist/types/build.d.ts +50 -3
  104. package/dist/types/cli.d.ts +1 -0
  105. package/dist/types/messages.d.ts +1 -1
  106. package/dist/vue/index.js +316 -23
  107. package/dist/vue/index.js.map +8 -5
  108. package/dist/vue/server.js +311 -18
  109. package/dist/vue/server.js.map +8 -5
  110. package/package.json +41 -11
@@ -22,9 +22,27 @@ export type BuildOptions = {
22
22
  baseManifest?: Record<string, string>;
23
23
  };
24
24
  export type MobilePlatform = 'android' | 'ios';
25
- export type MobileConfig = {
26
- /** Capacitor is the universal v1 engine. Expo remains a future React-only engine. */
27
- engine?: 'capacitor';
25
+ export type PwaConfig = {
26
+ /** Optional web-app manifest. `path` defaults to `/manifest.webmanifest`. */
27
+ manifest?: import('@absolutejs/pwa').WebAppManifestConfig & {
28
+ path?: string;
29
+ };
30
+ /** Service-worker behavior. Sync is controlled by the sibling `sync` field. */
31
+ serviceWorker?: Omit<import('@absolutejs/pwa').ServiceWorkerOptions, 'resubscribe' | 'sync'>;
32
+ /** Public worker URL. Defaults to `/sw.js`. */
33
+ serviceWorkerPath?: string;
34
+ /** Provision Auth-aware IndexedDB/background Sync without page-level code. */
35
+ sync?: boolean | Omit<import('@absolutejs/pwa/client').PwaSyncOptions, 'storageSchema'>;
36
+ /** Provision provider-neutral Web Push behind @absolutejs/devices. This is
37
+ * automatic when pushNotifications is imported; set false to opt out. The
38
+ * public VAPID key defaults to VAPID_PUBLIC_KEY at build time. */
39
+ push?: boolean | {
40
+ applicationServerKey?: string;
41
+ /** Root-relative trusted Auth route. Defaults to /auth/push. */
42
+ route?: string;
43
+ };
44
+ };
45
+ type MobileSharedConfig = {
28
46
  /** Native application identifier, for example `com.example.app`. */
29
47
  appId: string;
30
48
  /** Human-facing native application name. */
@@ -49,6 +67,13 @@ export type MobileConfig = {
49
67
  /** App Store marketing version written to CFBundleShortVersionString. */
50
68
  version: string;
51
69
  };
70
+ /** Provider setup for portable push notifications. Page code still uses @absolutejs/devices. */
71
+ pushNotifications?: {
72
+ android?: {
73
+ /** Firebase config copied into the native app module. Defaults to `google-services.json`. */
74
+ googleServicesFile?: string;
75
+ };
76
+ };
52
77
  deepLinks?: {
53
78
  /** Optional custom URL scheme in addition to HTTPS app/universal links. */
54
79
  scheme?: string;
@@ -64,6 +89,26 @@ export type MobileConfig = {
64
89
  };
65
90
  };
66
91
  };
92
+ export type CapacitorMobileConfig = MobileSharedConfig & {
93
+ /** Capacitor is the default, universal all-framework mobile engine. */
94
+ engine?: 'capacitor';
95
+ };
96
+ export type ExpoMobileConfig = MobileSharedConfig & {
97
+ /** Experimental hybrid Expo Router shell. Capacitor remains the default. */
98
+ engine: 'expo';
99
+ /** Explicit route ownership for React Native replacement screens. */
100
+ routes?: {
101
+ /** Unclaimed routes remain ordinary AbsoluteJS web routes. */
102
+ default?: 'web';
103
+ /** Absolute route path to a React Native module, relative to the app root. */
104
+ native?: Readonly<Record<string, string>>;
105
+ };
106
+ expo?: {
107
+ /** Expo SDK line used by the generated shell. Currently only 57 is supported. */
108
+ sdkVersion?: 57;
109
+ };
110
+ };
111
+ export type MobileConfig = CapacitorMobileConfig | ExpoMobileConfig;
67
112
  export type BuildPassError = {
68
113
  /** Internal pass id, e.g. `global-css`, `non-react-client`. */
69
114
  pass: string;
@@ -246,6 +291,8 @@ export type BaseBuildConfig = {
246
291
  htmxDirectory?: string;
247
292
  /** Build metadata and retained server producers for installed applications. */
248
293
  mobile?: MobileConfig;
294
+ /** Generate and bootstrap one framework-agnostic installable web app. */
295
+ pwa?: PwaConfig;
249
296
  stylesConfig?: string | StylesConfig;
250
297
  stylePreprocessors?: StylePreprocessorConfig;
251
298
  postcss?: PostCSSConfig;
@@ -23,6 +23,7 @@ export type InstanceRecord = {
23
23
  frameworks: string[];
24
24
  host: string;
25
25
  https: boolean;
26
+ iosRemoteMac?: string;
26
27
  logFile: string | null;
27
28
  name: string;
28
29
  pid: number;
@@ -1,4 +1,4 @@
1
- export type HMRClientTarget = 'capacitor-android' | 'capacitor-ios' | 'capacitor-native' | 'web';
1
+ export type HMRClientTarget = 'capacitor-android' | 'capacitor-ios' | 'capacitor-native' | 'expo-android' | 'expo-ios' | 'mobile-preview' | 'web';
2
2
  export type HMRApplyKind = 'component' | 'css' | 'full-reload' | 'html' | 'htmx' | 'script';
3
3
  export type HMRApplyOutcome = 'applied' | 'failed' | 'reloaded';
4
4
  export type PingMessage = {
package/dist/vue/index.js CHANGED
@@ -829,7 +829,7 @@ var SLOT_ID_PREFIX = "abs-slot-", CLOSING_BODY_TAG = "</body>", CLOSING_HEAD_TAG
829
829
  getStreamingSlotsRuntimeScript()
830
830
  ].filter(Boolean).join(";");
831
831
  return `<script${createNonceAttr(nonce)}>${escapeScriptContent(runtimeBody)}</script>`;
832
- }, toUint8 = (value, encoder) => encoder.encode(value), isRecord = (value) => Boolean(value) && typeof value === "object", isSafeHtmlLike = (value) => isRecord(value) && typeof value.changingThisBreaksApplicationSecurity === "string", normalizeSafeHtml = (value) => {
832
+ }, toUint8 = (value, encoder) => encoder.encode(value), isRecord2 = (value) => Boolean(value) && typeof value === "object", isSafeHtmlLike = (value) => isRecord2(value) && typeof value.changingThisBreaksApplicationSecurity === "string", normalizeSafeHtml = (value) => {
833
833
  if (isSafeHtmlLike(value)) {
834
834
  return value.changingThisBreaksApplicationSecurity;
835
835
  }
@@ -890,7 +890,7 @@ var SLOT_ID_PREFIX = "abs-slot-", CLOSING_BODY_TAG = "</body>", CLOSING_HEAD_TAG
890
890
  }, createTimeoutError = (slot, timeoutMs) => {
891
891
  const error = Object.assign(new Error(`Streaming slot "${slot.id}" timed out after ${timeoutMs}ms`), { __absTimeout: true });
892
892
  return error;
893
- }, isSlotPatchPayloadObject = (value) => isRecord(value) && ("html" in value), isTimeoutError = (error) => isRecord(error) && error.__absTimeout === true, toStreamingSlot = (slot, policy) => ({
893
+ }, isSlotPatchPayloadObject = (value) => isRecord2(value) && ("html" in value), isTimeoutError = (error) => isRecord2(error) && error.__absTimeout === true, toStreamingSlot = (slot, policy) => ({
894
894
  errorHtml: slot.errorHtml === undefined ? policy.errorHtml : slot.errorHtml,
895
895
  fallbackHtml: normalizeSlotText(slot.fallbackHtml, policy.fallbackHtml),
896
896
  id: slot.id ?? createStreamingSlotId(),
@@ -1402,6 +1402,7 @@ var colors, MONTHS, formatTimestamp = () => {
1402
1402
  port,
1403
1403
  host,
1404
1404
  networkUrl,
1405
+ mobilePreviewUrl,
1405
1406
  protocol = "http"
1406
1407
  } = options;
1407
1408
  const name = `${colors.cyan}${colors.bold}ABSOLUTEJS${colors.reset}`;
@@ -1415,6 +1416,9 @@ var colors, MONTHS, formatTimestamp = () => {
1415
1416
  if (networkUrl) {
1416
1417
  console.log(` ${colors.green}\u279C${colors.reset} ${colors.bold}Network:${colors.reset} ${networkUrl}`);
1417
1418
  }
1419
+ if (mobilePreviewUrl) {
1420
+ console.log(` ${colors.green}\u279C${colors.reset} ${colors.bold}Mobile:${colors.reset} ${mobilePreviewUrl}`);
1421
+ }
1418
1422
  console.log("");
1419
1423
  };
1420
1424
  var init_startupBanner = __esm(() => {
@@ -1721,8 +1725,8 @@ var toIslandFrameworkSegment = (framework) => framework[0]?.toUpperCase() + fram
1721
1725
  return found ? entries : undefined;
1722
1726
  }, getIslandManifestEntries = (manifest) => {
1723
1727
  const islands = {};
1724
- const frameworks = ["react", "svelte", "vue", "angular"];
1725
- for (const framework of frameworks) {
1728
+ const frameworks2 = ["react", "svelte", "vue", "angular"];
1729
+ for (const framework of frameworks2) {
1726
1730
  const prefix = `Island${toIslandFrameworkSegment(framework)}`;
1727
1731
  const entries = collectFrameworkIslands(manifest, prefix);
1728
1732
  if (entries)
@@ -1742,14 +1746,14 @@ var defineIslandComponent = (component, options) => ({
1742
1746
  component,
1743
1747
  export: options.export,
1744
1748
  source: options.source
1745
- }), defineIslandRegistry = (registry) => registry, isRecord3 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component) => {
1749
+ }), defineIslandRegistry = (registry) => registry, isRecord4 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component) => {
1746
1750
  if (!isIslandComponentDefinition(component))
1747
1751
  return null;
1748
1752
  return {
1749
1753
  export: component.export,
1750
1754
  source: component.source
1751
1755
  };
1752
- }, isIslandComponentDefinition = (value) => isRecord3(value) && ("component" in value) && ("source" in value) && typeof value.source === "string", parseIslandProps = (rawProps) => {
1756
+ }, isIslandComponentDefinition = (value) => isRecord4(value) && ("component" in value) && ("source" in value) && typeof value.source === "string", parseIslandProps = (rawProps) => {
1753
1757
  if (!rawProps)
1754
1758
  return {};
1755
1759
  return JSON.parse(rawProps);
@@ -4037,7 +4041,7 @@ var init_vueServerModule = __esm(() => {
4037
4041
  var islandSequence = 0, resolvedServerComponentCache, resolvedServerBuildComponentCache, nextIslandId = () => {
4038
4042
  islandSequence += 1;
4039
4043
  return `island-${islandSequence}`;
4040
- }, isRecord4 = (value) => typeof value === "object" && value !== null, isReactServerIslandComponent = (value) => typeof value === "function", isSvelteServerIslandComponent = (value) => typeof value === "function", isVueServerIslandComponent = (value) => typeof value === "function" || isRecord4(value), isAngularServerIslandComponent = (value) => typeof value === "function", resolveBuildReferencePath = (source, registryPath) => {
4044
+ }, isRecord5 = (value) => typeof value === "object" && value !== null, isReactServerIslandComponent = (value) => typeof value === "function", isSvelteServerIslandComponent = (value) => typeof value === "function", isVueServerIslandComponent = (value) => typeof value === "function" || isRecord5(value), isAngularServerIslandComponent = (value) => typeof value === "function", resolveBuildReferencePath = (source, registryPath) => {
4041
4045
  if (source.startsWith("file://"))
4042
4046
  return new URL(source).pathname;
4043
4047
  if (source.startsWith("."))
@@ -4937,6 +4941,278 @@ var setCurrentIslandManifest = (manifest) => {
4937
4941
 
4938
4942
  // src/vue/pageHandler.ts
4939
4943
  init_devRouteRegistrationCallsite();
4944
+
4945
+ // src/core/requestContext.ts
4946
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
4947
+ import { Elysia as Elysia2 } from "elysia";
4948
+ var REQUEST_STORAGE_KEY = Symbol.for("absolutejs.requestAsyncLocalStorage");
4949
+ var isAbsoluteRequestStorage = (value) => typeof value === "object" && value !== null && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
4950
+ var getRequestStorage = () => {
4951
+ const value = Reflect.get(globalThis, REQUEST_STORAGE_KEY);
4952
+ return isAbsoluteRequestStorage(value) ? value : undefined;
4953
+ };
4954
+ var ensureRequestStorage = () => {
4955
+ const existing = getRequestStorage();
4956
+ if (existing)
4957
+ return existing;
4958
+ const storage = new AsyncLocalStorage2;
4959
+ Reflect.set(globalThis, REQUEST_STORAGE_KEY, storage);
4960
+ return storage;
4961
+ };
4962
+ var absoluteRequestContext = new Elysia2({
4963
+ name: "absolutejs-request-context"
4964
+ }).request(({ request }) => {
4965
+ ensureRequestStorage().enterWith({ request });
4966
+ }).as("global");
4967
+ var getCurrentAbsoluteRequest = () => getRequestStorage()?.getStore()?.request;
4968
+ var runWithAbsoluteRequest = (request, callback) => ensureRequestStorage().run({ request }, callback);
4969
+
4970
+ // src/mobile/producerContextState.ts
4971
+ var ABSOLUTE_MOBILE_PRODUCER_STORAGE_KEY = Symbol.for("absolutejs.mobileProducerAsyncLocalStorage");
4972
+ var frameworks = new Set([
4973
+ "angular",
4974
+ "ember",
4975
+ "html",
4976
+ "htmx",
4977
+ "react",
4978
+ "svelte",
4979
+ "vue"
4980
+ ]);
4981
+ var isCompatibilityPage = (value) => typeof value === "object" && value !== null && ("bundleHash" in value) && typeof value.bundleHash === "string" && ("contract" in value) && typeof value.contract === "string" && ("framework" in value) && typeof value.framework === "string" && frameworks.has(value.framework) && ("pageId" in value) && typeof value.pageId === "string" && ("propsSchemaHash" in value) && typeof value.propsSchemaHash === "string";
4982
+ var getCurrentAbsoluteMobileProducerContext = () => {
4983
+ const value = Reflect.get(globalThis, ABSOLUTE_MOBILE_PRODUCER_STORAGE_KEY);
4984
+ if (typeof value !== "object" || value === null || !("getStore" in value) || typeof value.getStore !== "function") {
4985
+ return;
4986
+ }
4987
+ const context = value.getStore();
4988
+ if (typeof context !== "object" || context === null || !("page" in context) || !isCompatibilityPage(context.page) || !("releaseId" in context) || typeof context.releaseId !== "string") {
4989
+ return;
4990
+ }
4991
+ return { page: context.page, releaseId: context.releaseId };
4992
+ };
4993
+
4994
+ // src/mobile/pageProtocol.ts
4995
+ var ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE = "application/vnd.absolute.page+json";
4996
+ var ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION = 1;
4997
+ var BAD_REQUEST_STATUS = 400;
4998
+ var OK_STATUS = 200;
4999
+ var SERVER_ERROR_STATUS = 500;
5000
+ var UPGRADE_REQUIRED_STATUS = 426;
5001
+ var MOBILE_PAGE_REQUEST_HEADERS = {
5002
+ appBuild: "x-absolute-mobile-app-build",
5003
+ pageBundle: "x-absolute-mobile-page-bundle",
5004
+ pageContracts: "x-absolute-mobile-page-contracts",
5005
+ pageId: "x-absolute-mobile-page-id",
5006
+ protocol: "x-absolute-mobile-protocol",
5007
+ runtime: "x-absolute-mobile-runtime"
5008
+ };
5009
+ var parseMediaType = (value) => value.split(";", 1)[0]?.trim().toLowerCase();
5010
+ var acceptsAbsoluteMobilePage = (request) => {
5011
+ if (!request)
5012
+ return false;
5013
+ return (request.headers.get("accept") ?? "").split(",").some((value) => parseMediaType(value) === ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE);
5014
+ };
5015
+ var readRequiredHeader = (request, name) => {
5016
+ const value = request.headers.get(name)?.trim();
5017
+ return value ? value : undefined;
5018
+ };
5019
+ var parseAbsoluteMobilePageRequest = (request) => {
5020
+ if (!request || !acceptsAbsoluteMobilePage(request)) {
5021
+ return { kind: "not-mobile" };
5022
+ }
5023
+ const protocolValue = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.protocol);
5024
+ const runtime = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.runtime);
5025
+ const appBuild = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.appBuild);
5026
+ const pageBundle = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.pageBundle);
5027
+ const pageContractsValue = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.pageContracts);
5028
+ const pageId = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.pageId);
5029
+ if (!protocolValue || !/^\d+$/.test(protocolValue)) {
5030
+ return {
5031
+ kind: "invalid",
5032
+ message: `Missing or invalid ${MOBILE_PAGE_REQUEST_HEADERS.protocol} header.`
5033
+ };
5034
+ }
5035
+ if (!runtime) {
5036
+ return {
5037
+ kind: "invalid",
5038
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.runtime} header.`
5039
+ };
5040
+ }
5041
+ if (!appBuild) {
5042
+ return {
5043
+ kind: "invalid",
5044
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.appBuild} header.`
5045
+ };
5046
+ }
5047
+ if (!pageBundle) {
5048
+ return {
5049
+ kind: "invalid",
5050
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.pageBundle} header.`
5051
+ };
5052
+ }
5053
+ if (!pageContractsValue) {
5054
+ return {
5055
+ kind: "invalid",
5056
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.pageContracts} header.`
5057
+ };
5058
+ }
5059
+ if (!pageId) {
5060
+ return {
5061
+ kind: "invalid",
5062
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.pageId} header.`
5063
+ };
5064
+ }
5065
+ const pageContracts = [
5066
+ ...new Set(pageContractsValue.split(",").map((value) => value.trim()).filter(Boolean))
5067
+ ];
5068
+ if (pageContracts.length === 0) {
5069
+ return {
5070
+ kind: "invalid",
5071
+ message: `${MOBILE_PAGE_REQUEST_HEADERS.pageContracts} must contain at least one contract.`
5072
+ };
5073
+ }
5074
+ return {
5075
+ client: {
5076
+ appBuild,
5077
+ pageBundle,
5078
+ pageContracts,
5079
+ pageId,
5080
+ protocol: Number(protocolValue),
5081
+ runtime
5082
+ },
5083
+ kind: "mobile"
5084
+ };
5085
+ };
5086
+ var responseHeaders = () => {
5087
+ const headers = new Headers({
5088
+ "cache-control": "no-store",
5089
+ "content-type": `${ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE}; version=${ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION}`
5090
+ });
5091
+ headerVaryValues.forEach((value) => headers.append("vary", value));
5092
+ return headers;
5093
+ };
5094
+ var headerVaryValues = [
5095
+ "Accept",
5096
+ MOBILE_PAGE_REQUEST_HEADERS.protocol,
5097
+ MOBILE_PAGE_REQUEST_HEADERS.runtime,
5098
+ MOBILE_PAGE_REQUEST_HEADERS.appBuild,
5099
+ MOBILE_PAGE_REQUEST_HEADERS.pageBundle,
5100
+ MOBILE_PAGE_REQUEST_HEADERS.pageContracts,
5101
+ MOBILE_PAGE_REQUEST_HEADERS.pageId
5102
+ ];
5103
+ var envelopeResponse = (response, status) => new Response(JSON.stringify({
5104
+ protocol: ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
5105
+ response
5106
+ }), { headers: responseHeaders(), status });
5107
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
5108
+ var normalizeJsonValue = (value) => {
5109
+ const serialized = JSON.stringify(value);
5110
+ if (serialized === undefined) {
5111
+ throw new TypeError("Mobile page props must be JSON-serializable.");
5112
+ }
5113
+ const parsed = JSON.parse(serialized);
5114
+ if (!isRecord(parsed)) {
5115
+ throw new TypeError("Mobile page props must serialize to an object.");
5116
+ }
5117
+ return parsed;
5118
+ };
5119
+ var finalizeArchivedMobilePage = (context, input) => {
5120
+ const { page } = context;
5121
+ if (page.pageId !== input.compatibility.pageId || page.framework !== input.compatibility.framework) {
5122
+ return envelopeResponse({
5123
+ kind: "invalid-request",
5124
+ message: "Archived producer page identity does not match its release artifact."
5125
+ }, BAD_REQUEST_STATUS);
5126
+ }
5127
+ try {
5128
+ const [currentRepresentation] = input.compatibility.representations;
5129
+ if (!currentRepresentation) {
5130
+ throw new TypeError("Mobile page has no current representation.");
5131
+ }
5132
+ return envelopeResponse({
5133
+ contract: page.contract,
5134
+ framework: page.framework,
5135
+ kind: "page",
5136
+ pageId: page.pageId,
5137
+ props: normalizeJsonValue(currentRepresentation.mapProps(input.props)),
5138
+ status: input.status ?? OK_STATUS
5139
+ }, input.status ?? OK_STATUS);
5140
+ } catch (error) {
5141
+ console.error(`[Mobile] Archived producer ${context.releaseId} failed for ${page.pageId}:`, error);
5142
+ return createAbsoluteMobilePageErrorResponse(page.pageId);
5143
+ }
5144
+ };
5145
+ var createAbsoluteMobileInvalidRequestResponse = (message) => envelopeResponse({ kind: "invalid-request", message }, BAD_REQUEST_STATUS);
5146
+ var createAbsoluteMobilePageErrorResponse = (pageId) => envelopeResponse({
5147
+ code: "representation-failed",
5148
+ kind: "error",
5149
+ pageId,
5150
+ status: SERVER_ERROR_STATUS
5151
+ }, SERVER_ERROR_STATUS);
5152
+ var createAbsoluteMobileUpgradeResponse = (result) => envelopeResponse(result, UPGRADE_REQUIRED_STATUS);
5153
+ var finalizeAbsoluteMobilePage = (input) => {
5154
+ const parsed = parseAbsoluteMobilePageRequest(input.request);
5155
+ if (parsed.kind === "not-mobile")
5156
+ return;
5157
+ if (parsed.kind === "invalid") {
5158
+ return envelopeResponse({ kind: "invalid-request", message: parsed.message }, BAD_REQUEST_STATUS);
5159
+ }
5160
+ const { client } = parsed;
5161
+ if (client.pageId !== input.compatibility.pageId) {
5162
+ return envelopeResponse({
5163
+ kind: "invalid-request",
5164
+ message: `Requested mobile page ${client.pageId} does not match ${input.compatibility.pageId}.`
5165
+ }, BAD_REQUEST_STATUS);
5166
+ }
5167
+ const producerContext = getCurrentAbsoluteMobileProducerContext();
5168
+ if (producerContext)
5169
+ return finalizeArchivedMobilePage(producerContext, input);
5170
+ if (client.protocol !== ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION) {
5171
+ return envelopeResponse({
5172
+ kind: "upgrade-required",
5173
+ pageId: input.compatibility.pageId,
5174
+ reason: "protocol"
5175
+ }, UPGRADE_REQUIRED_STATUS);
5176
+ }
5177
+ if (!input.compatibility.runtimes.includes(client.runtime)) {
5178
+ return envelopeResponse({
5179
+ kind: "upgrade-required",
5180
+ pageId: input.compatibility.pageId,
5181
+ reason: "runtime",
5182
+ supportedRuntimes: [...input.compatibility.runtimes]
5183
+ }, UPGRADE_REQUIRED_STATUS);
5184
+ }
5185
+ const clientContracts = new Set(client.pageContracts);
5186
+ const representation = input.compatibility.representations.find(({ contract }) => clientContracts.has(contract));
5187
+ if (!representation) {
5188
+ return envelopeResponse({
5189
+ kind: "upgrade-required",
5190
+ pageId: input.compatibility.pageId,
5191
+ reason: "page-contract",
5192
+ supportedContracts: input.compatibility.representations.map(({ contract }) => contract)
5193
+ }, UPGRADE_REQUIRED_STATUS);
5194
+ }
5195
+ try {
5196
+ return envelopeResponse({
5197
+ contract: representation.contract,
5198
+ framework: input.compatibility.framework,
5199
+ kind: "page",
5200
+ pageId: input.compatibility.pageId,
5201
+ props: normalizeJsonValue(representation.mapProps(input.props)),
5202
+ status: input.status ?? OK_STATUS
5203
+ }, input.status ?? OK_STATUS);
5204
+ } catch (error) {
5205
+ console.error(`[Mobile] Failed to produce ${input.compatibility.pageId} representation ${representation.contract}:`, error);
5206
+ return envelopeResponse({
5207
+ code: "representation-failed",
5208
+ kind: "error",
5209
+ pageId: input.compatibility.pageId,
5210
+ status: SERVER_ERROR_STATUS
5211
+ }, SERVER_ERROR_STATUS);
5212
+ }
5213
+ };
5214
+
5215
+ // src/vue/pageHandler.ts
4940
5216
  init_pageResponseCache();
4941
5217
 
4942
5218
  // src/core/responseEnhancers.ts
@@ -4993,7 +5269,7 @@ var withRegisteredStreamingSlots = async (renderResponse, options = {}) => {
4993
5269
 
4994
5270
  // src/core/streamingSlotWarningScope.ts
4995
5271
  init_logger();
4996
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
5272
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
4997
5273
  var STREAMING_SLOT_WARNING_STORAGE_KEY2 = Symbol.for("absolutejs.streamingSlotWarningAsyncLocalStorage");
4998
5274
  var isObjectRecord5 = (value) => Boolean(value) && typeof value === "object";
4999
5275
  var isAsyncLocalStorage3 = (value) => isObjectRecord5(value) && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
@@ -5009,7 +5285,7 @@ var ensureWarningStorage = () => {
5009
5285
  if (existing) {
5010
5286
  return existing;
5011
5287
  }
5012
- const storage = new AsyncLocalStorage2;
5288
+ const storage = new AsyncLocalStorage3;
5013
5289
  Reflect.set(globalThis, STREAMING_SLOT_WARNING_STORAGE_KEY2, storage);
5014
5290
  return storage;
5015
5291
  };
@@ -5091,23 +5367,23 @@ var ABSOLUTE_TELEPORT_TARGET_ID = "absolute-teleports";
5091
5367
  var ABSOLUTE_TELEPORT_TARGET = `#${ABSOLUTE_TELEPORT_TARGET_ID}`;
5092
5368
 
5093
5369
  // src/vue/pageHandler.ts
5094
- var isRecord2 = (value) => typeof value === "object" && value !== null;
5095
- var isGenericVueComponent = (value) => typeof value === "function" || isRecord2(value);
5370
+ var isRecord3 = (value) => typeof value === "object" && value !== null;
5371
+ var isGenericVueComponent = (value) => typeof value === "function" || isRecord3(value);
5096
5372
  var readHasIslands = (value) => {
5097
- if (!isRecord2(value))
5373
+ if (!isRecord3(value))
5098
5374
  return false;
5099
5375
  const hasIslands = value["__ABSOLUTE_PAGE_HAS_ISLANDS__"];
5100
5376
  return typeof hasIslands === "boolean" ? hasIslands : false;
5101
5377
  };
5102
5378
  var isVueSetupApp = (value) => typeof value === "function";
5103
5379
  var readSetupAppHook = (value) => {
5104
- if (!isRecord2(value))
5380
+ if (!isRecord3(value))
5105
5381
  return null;
5106
5382
  const { setupApp } = value;
5107
5383
  return isVueSetupApp(setupApp) ? setupApp : null;
5108
5384
  };
5109
- var readHasSpaRoutes = (value) => isRecord2(value) && Array.isArray(value["routes"]);
5110
- var readDefaultExport = (value) => isRecord2(value) ? value.default : undefined;
5385
+ var readHasSpaRoutes = (value) => isRecord3(value) && Array.isArray(value["routes"]);
5386
+ var readDefaultExport = (value) => isRecord3(value) ? value.default : undefined;
5111
5387
  var resolveRequestRenderUrl = (request) => {
5112
5388
  if (!request)
5113
5389
  return "/";
@@ -5137,12 +5413,29 @@ var getSsrOnlyHmrShim = () => {
5137
5413
  };
5138
5414
  var handleVuePageRequest = async (input) => {
5139
5415
  const passedPageComponent = input.Page;
5416
+ const request = input.request ?? getCurrentAbsoluteRequest();
5417
+ const pageId = input.__absoluteMobile?.pageId ?? derivePageName(input.pagePath);
5418
+ const currentContract = input.__absoluteMobile?.contract ?? `vue:${pageId}:${ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION}`;
5419
+ const mobileResponse = finalizeAbsoluteMobilePage({
5420
+ compatibility: {
5421
+ framework: "vue",
5422
+ pageId,
5423
+ representations: [
5424
+ { contract: currentContract, mapProps: (props) => props }
5425
+ ],
5426
+ runtimes: [String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION)]
5427
+ },
5428
+ props: input.props ?? Object.create(null),
5429
+ request
5430
+ });
5431
+ if (mobileResponse)
5432
+ return mobileResponse;
5140
5433
  const userHeadTag = input.headTag ?? "<head></head>";
5141
5434
  const resolvedOptions = input;
5142
5435
  const resolvedPagePath = input.pagePath;
5143
5436
  const [siblingCss, spaChildCss] = await Promise.all([
5144
5437
  readSiblingCss(resolvedPagePath),
5145
- resolveSpaChildCss(resolvedPagePath, input.request?.url)
5438
+ resolveSpaChildCss(resolvedPagePath, request?.url)
5146
5439
  ]);
5147
5440
  const resolvedHeadTag = injectInlineCss(injectInlineCss(userHeadTag, siblingCss), spaChildCss);
5148
5441
  const maybeProps = input.props;
@@ -5155,9 +5448,9 @@ var handleVuePageRequest = async (input) => {
5155
5448
  throw new Error('handleVuePageRequest: `indexPath` is required when `client` is `"auto"` (the default). Pass `client: "none"` to ship a SSR-only page with no client bundle.');
5156
5449
  }
5157
5450
  try {
5158
- const spaNotFound = await renderSpaNotFound("vue", derivePageName(resolvedPagePath), input.request);
5451
+ const spaNotFound = await renderSpaNotFound("vue", derivePageName(resolvedPagePath), request);
5159
5452
  if (spaNotFound)
5160
- return withPageCacheHeaders(spaNotFound, input.request);
5453
+ return withPageCacheHeaders(spaNotFound, request);
5161
5454
  const handlerCallsite = resolvedOptions?.collectStreamingSlots === true ? undefined : getCurrentRouteRegistrationCallsite() ?? captureStreamingSlotWarningCallsite();
5162
5455
  const renderPageResponse = async () => {
5163
5456
  const resolvePageComponent = async () => {
@@ -5190,7 +5483,7 @@ var handleVuePageRequest = async (input) => {
5190
5483
  let pendingRedirect = null;
5191
5484
  let pendingNotFound = false;
5192
5485
  if (resolvedPage.setupApp) {
5193
- const url = resolveRequestRenderUrl(input.request);
5486
+ const url = resolveRequestRenderUrl(request);
5194
5487
  await resolvedPage.setupApp(app, {
5195
5488
  isServer: true,
5196
5489
  router: null,
@@ -5271,7 +5564,7 @@ var handleVuePageRequest = async (input) => {
5271
5564
  });
5272
5565
  };
5273
5566
  const pageResponse = await runWithStreamingSlotWarningScope(() => resolvedOptions?.collectStreamingSlots === true ? withRegisteredStreamingSlots(renderPageResponse, resolvedOptions) : renderPageResponse(), { handlerCallsite });
5274
- return withPageCacheHeaders(pageResponse, input.request, {
5567
+ return withPageCacheHeaders(pageResponse, request, {
5275
5568
  bufferStreamForEtag: input.bufferStreamForEtag
5276
5569
  });
5277
5570
  } catch (error) {
@@ -5279,12 +5572,12 @@ var handleVuePageRequest = async (input) => {
5279
5572
  const pageName = derivePageName(resolvedPagePath);
5280
5573
  const conventionResponse = await renderConventionError("vue", pageName, error);
5281
5574
  if (conventionResponse) {
5282
- return withPageCacheHeaders(conventionResponse, input.request);
5575
+ return withPageCacheHeaders(conventionResponse, request);
5283
5576
  }
5284
5577
  return withPageCacheHeaders(new Response(ssrErrorPage("vue", error), {
5285
5578
  headers: { "Content-Type": "text/html" },
5286
5579
  status: 500
5287
- }), input.request);
5580
+ }), request);
5288
5581
  }
5289
5582
  };
5290
5583
  // src/vue/routerRedirectProviders.ts
@@ -5747,5 +6040,5 @@ export {
5747
6040
  ABSOLUTE_TELEPORT_TARGET
5748
6041
  };
5749
6042
 
5750
- //# debugId=80A3F4966DF7C3ED64756E2164756E21
6043
+ //# debugId=9386C6084827BAD464756E2164756E21
5751
6044
  //# sourceMappingURL=index.js.map