@absolutejs/absolute 0.20.0-beta.0 → 0.20.0-beta.10

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 (118) hide show
  1. package/README.md +45 -0
  2. package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
  3. package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
  4. package/dist/angular/index.js +354 -22
  5. package/dist/angular/index.js.map +9 -6
  6. package/dist/angular/server.js +354 -22
  7. package/dist/angular/server.js.map +9 -6
  8. package/dist/build.js +742 -359
  9. package/dist/build.js.map +26 -24
  10. package/dist/cli/config/server.js +2 -2
  11. package/dist/cli/index.js +16105 -8429
  12. package/dist/dev/client/cssUtils.ts +112 -41
  13. package/dist/dev/client/handlers/html.ts +75 -15
  14. package/dist/dev/client/handlers/htmx.ts +43 -8
  15. package/dist/dev/client/handlers/react.ts +114 -32
  16. package/dist/dev/client/handlers/rebuild.ts +38 -8
  17. package/dist/dev/client/handlers/svelte.ts +33 -50
  18. package/dist/dev/client/handlers/vue.ts +36 -45
  19. package/dist/dev/client/hmrClient.ts +38 -6
  20. package/dist/dev/client/hmrTiming.ts +148 -0
  21. package/dist/dev/serverBootstrap.js +20 -11
  22. package/dist/index.js +1728 -864
  23. package/dist/index.js.map +38 -33
  24. package/dist/mobile/browser.js +14 -1
  25. package/dist/mobile/browser.js.map +3 -3
  26. package/dist/mobile/index.js +4853 -279
  27. package/dist/mobile/index.js.map +34 -7
  28. package/dist/mobile/remoteMacAgentEntry.js +29 -0
  29. package/dist/mobile/shellAuth.js +43 -0
  30. package/dist/mobile/shellBootstrap.js +581 -0
  31. package/dist/mobile/shellSync.js +74 -0
  32. package/dist/react/index.js +16 -3
  33. package/dist/react/index.js.map +4 -4
  34. package/dist/react/jsxDevRuntimeCompat.js +22 -2
  35. package/dist/react/jsxDevRuntimeCompat.js.map +5 -4
  36. package/dist/react/jsxRuntimeCompat.js +90 -0
  37. package/dist/react/jsxRuntimeCompat.js.map +10 -0
  38. package/dist/react/server.js +16 -3
  39. package/dist/react/server.js.map +4 -4
  40. package/dist/src/angular/pageHandler.d.ts +3 -0
  41. package/dist/src/build/compileVue.d.ts +1 -1
  42. package/dist/src/build/pwa.d.ts +15 -0
  43. package/dist/src/build/resolveBuildMode.d.ts +7 -0
  44. package/dist/src/cli/config/server.d.ts +1 -1
  45. package/dist/src/cli/scripts/dev.d.ts +4 -1
  46. package/dist/src/cli/scripts/mobile.d.ts +1 -0
  47. package/dist/src/cli/utils.d.ts +2 -2
  48. package/dist/src/core/build.d.ts +1 -1
  49. package/dist/src/core/pageHandlers.d.ts +11 -2
  50. package/dist/src/core/prepare.d.ts +6 -0
  51. package/dist/src/dev/clientManager.d.ts +9 -0
  52. package/dist/src/dev/configResolver.d.ts +2 -0
  53. package/dist/src/dev/moduleMapper.d.ts +1 -1
  54. package/dist/src/dev/moduleServer.d.ts +2 -1
  55. package/dist/src/dev/pathUtils.d.ts +1 -1
  56. package/dist/src/dev/reactComponentClassifier.d.ts +1 -1
  57. package/dist/src/mobile/androidConformance.d.ts +42 -0
  58. package/dist/src/mobile/androidEmulatorController.d.ts +105 -0
  59. package/dist/src/mobile/androidNativeWatcher.d.ts +19 -0
  60. package/dist/src/mobile/androidRelease.d.ts +51 -0
  61. package/dist/src/mobile/androidWebView.d.ts +46 -0
  62. package/dist/src/mobile/associationFiles.d.ts +49 -0
  63. package/dist/src/mobile/buildMetadata.d.ts +10 -0
  64. package/dist/src/mobile/buildPipeline.d.ts +9 -0
  65. package/dist/src/mobile/buildRelease.d.ts +24 -0
  66. package/dist/src/mobile/capacitorBundle.d.ts +42 -0
  67. package/dist/src/mobile/capacitorProject.d.ts +9 -0
  68. package/dist/src/mobile/client.d.ts +5 -1
  69. package/dist/src/mobile/config.d.ts +17 -0
  70. package/dist/src/mobile/emulatorDoctor.d.ts +25 -0
  71. package/dist/src/mobile/emulatorInstaller.d.ts +41 -0
  72. package/dist/src/mobile/index.d.ts +19 -0
  73. package/dist/src/mobile/iosConformance.d.ts +15 -0
  74. package/dist/src/mobile/iosNativeWatcher.d.ts +19 -0
  75. package/dist/src/mobile/iosRelease.d.ts +61 -0
  76. package/dist/src/mobile/iosSimulatorController.d.ts +89 -0
  77. package/dist/src/mobile/materializedBundle.d.ts +14 -0
  78. package/dist/src/mobile/nativeAuth.d.ts +17 -0
  79. package/dist/src/mobile/nativeBackgroundSync.d.ts +4 -0
  80. package/dist/src/mobile/nativeDeepLinks.d.ts +5 -0
  81. package/dist/src/mobile/releaseArtifact.d.ts +3 -0
  82. package/dist/src/mobile/releaseDoctor.d.ts +13 -0
  83. package/dist/src/mobile/releasePublisher.d.ts +130 -0
  84. package/dist/src/mobile/remoteMacAgent.d.ts +2 -0
  85. package/dist/src/mobile/remoteMacAgentEntry.d.ts +1 -0
  86. package/dist/src/mobile/remoteMacProtocol.d.ts +114 -0
  87. package/dist/src/mobile/remoteMacWire.d.ts +2 -0
  88. package/dist/src/mobile/routeMatcher.d.ts +3 -0
  89. package/dist/src/mobile/routeMetadataTransform.d.ts +13 -0
  90. package/dist/src/mobile/shellAuth.d.ts +13 -0
  91. package/dist/src/mobile/shellBootstrap.d.ts +18 -0
  92. package/dist/src/mobile/shellSync.d.ts +19 -0
  93. package/dist/src/mobile/staticDocument.d.ts +5 -0
  94. package/dist/src/mobile/transport.d.ts +43 -0
  95. package/dist/src/plugins/hmr.d.ts +3 -0
  96. package/dist/src/plugins/imageOptimizer.d.ts +1 -1
  97. package/dist/src/react/hooks/useMediaQuery.d.ts +1 -1
  98. package/dist/src/react/jsxDevRuntimeCompat.d.ts +1 -1
  99. package/dist/src/react/jsxRuntimeCompat.d.ts +7 -0
  100. package/dist/src/react/pageHandler.d.ts +3 -0
  101. package/dist/src/svelte/pageHandler.d.ts +3 -0
  102. package/dist/src/utils/loadConfig.d.ts +2 -0
  103. package/dist/src/utils/logger.d.ts +2 -0
  104. package/dist/src/utils/userAgentFunctions.d.ts +1 -1
  105. package/dist/src/vue/pageHandler.d.ts +3 -0
  106. package/dist/svelte/index.js +325 -23
  107. package/dist/svelte/index.js.map +8 -5
  108. package/dist/svelte/server.js +320 -18
  109. package/dist/svelte/server.js.map +8 -5
  110. package/dist/types/build.d.ts +59 -0
  111. package/dist/types/cli.d.ts +2 -0
  112. package/dist/types/globals.d.ts +14 -0
  113. package/dist/types/messages.d.ts +12 -0
  114. package/dist/vue/index.js +325 -23
  115. package/dist/vue/index.js.map +8 -5
  116. package/dist/vue/server.js +320 -18
  117. package/dist/vue/server.js.map +8 -5
  118. package/package.json +52 -8
@@ -21,6 +21,61 @@ export type BuildOptions = {
21
21
  /** Base manifest to merge into for incremental builds */
22
22
  baseManifest?: Record<string, string>;
23
23
  };
24
+ export type MobilePlatform = 'android' | 'ios';
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, '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 | import('@absolutejs/pwa/client').PwaSyncOptions;
36
+ };
37
+ export type MobileConfig = {
38
+ /** Capacitor is the universal v1 engine. Expo remains a future React-only engine. */
39
+ engine?: 'capacitor';
40
+ /** Native application identifier, for example `com.example.app`. */
41
+ appId: string;
42
+ /** Human-facing native application name. */
43
+ appName: string;
44
+ /** Initial canonical application route. Defaults to `/`. */
45
+ entry?: string;
46
+ /** Native projects generated and synchronized by AbsoluteJS. */
47
+ platforms?: readonly MobilePlatform[];
48
+ server: {
49
+ /** Deployed AbsoluteJS origin used for page envelopes and API calls. */
50
+ productionOrigin: string;
51
+ };
52
+ /** Capacitor webDir output. Defaults to `.absolutejs/mobile/web`. */
53
+ bundleDirectory?: string;
54
+ nativeProject?: {
55
+ /** v1 keeps native projects as committed, editable source. */
56
+ mode?: 'source';
57
+ /** Parent directory for generated iOS and Android projects. */
58
+ directory?: string;
59
+ };
60
+ ios?: {
61
+ /** App Store marketing version written to CFBundleShortVersionString. */
62
+ version: string;
63
+ };
64
+ deepLinks?: {
65
+ /** Optional custom URL scheme in addition to HTTPS app/universal links. */
66
+ scheme?: string;
67
+ /** HTTPS hosts that may enter the local AbsoluteJS router. */
68
+ hosts?: readonly string[];
69
+ apple?: {
70
+ /** Apple Application Identifier Prefix, commonly the ten-character Team ID. */
71
+ appIdPrefix: string;
72
+ };
73
+ android?: {
74
+ /** SHA-256 fingerprints for every certificate that signs an installed build. */
75
+ sha256CertificateFingerprints: readonly string[];
76
+ };
77
+ };
78
+ };
24
79
  export type BuildPassError = {
25
80
  /** Internal pass id, e.g. `global-css`, `non-react-client`. */
26
81
  pass: string;
@@ -201,6 +256,10 @@ export type BaseBuildConfig = {
201
256
  emberDirectory?: string;
202
257
  htmlDirectory?: string;
203
258
  htmxDirectory?: string;
259
+ /** Build metadata and retained server producers for installed applications. */
260
+ mobile?: MobileConfig;
261
+ /** Generate and bootstrap one framework-agnostic installable web app. */
262
+ pwa?: PwaConfig;
204
263
  stylesConfig?: string | StylesConfig;
205
264
  stylePreprocessors?: StylePreprocessorConfig;
206
265
  postcss?: PostCSSConfig;
@@ -1,11 +1,13 @@
1
1
  export type Action = () => void | Promise<void>;
2
2
  export type Actions = {
3
3
  clear: Action;
4
+ device?: Action;
4
5
  heapSnapshot: Action;
5
6
  help: Action;
6
7
  open: Action;
7
8
  pause: Action;
8
9
  quit: Action;
10
+ relaunchDevice?: Action;
9
11
  restart: Action;
10
12
  shell: (command: string) => Promise<void>;
11
13
  };
@@ -9,6 +9,8 @@ declare global {
9
9
  * watchers and can observe entry/configuration edits. */
10
10
  var __absoluteEntryWatcherReady: boolean | undefined;
11
11
  var __absoluteEntryCopies: Set<string> | undefined;
12
+ var __absoluteEntryBootstrapSequence: number | undefined;
13
+ var __absoluteEntryCleanupRegistered: boolean | undefined;
12
14
  var __absoluteEntryWatcherStarted: boolean | undefined;
13
15
  /** Pinned React module from initial devBuild — used to detect and bridge
14
16
  * duplicate React instances after bun install invalidates the module cache. */
@@ -71,9 +73,21 @@ declare global {
71
73
  __HMR_SERVER_VERSIONS__?: Record<string, number>;
72
74
  __HMR_UPDATE_COUNT__?: number;
73
75
  __HMR_WS__?: WebSocket;
76
+ __ABS_HMR_TARGET__?: import('./messages').HMRClientTarget;
77
+ __ABS_HMR_LAST_APPLY__?: {
78
+ clientMs: number;
79
+ duration: number;
80
+ kind?: import('./messages').HMRApplyKind;
81
+ outcome: import('./messages').HMRApplyOutcome;
82
+ serverMs: number;
83
+ target: import('./messages').HMRClientTarget;
84
+ updateId?: number;
85
+ };
86
+ __ABS_HMR_APPLIES__?: NonNullable<Window['__ABS_HMR_LAST_APPLY__']>[];
74
87
  __ERROR_BOUNDARY__?: { reset: () => void };
75
88
  __INITIAL_PROPS__?: Record<string, unknown>;
76
89
  __ABSOLUTE_PAGE_RENDER_MODE__?: 'client' | 'hydrate';
90
+ __ABS_REACT_REMOUNT__?: (module: Record<string, unknown>) => void;
77
91
  __REACT_COMPONENT_KEY__?: string;
78
92
  __REACT_ROOT__?: { render: (element: unknown) => void };
79
93
  __SVELTE_COMPONENT__?: Record<string, unknown>;
@@ -1,9 +1,13 @@
1
+ export type HMRClientTarget = 'capacitor-android' | 'capacitor-ios' | 'capacitor-native' | 'web';
2
+ export type HMRApplyKind = 'component' | 'css' | 'full-reload' | 'html' | 'htmx' | 'script';
3
+ export type HMRApplyOutcome = 'applied' | 'failed' | 'reloaded';
1
4
  export type PingMessage = {
2
5
  type: 'ping';
3
6
  };
4
7
  export type ReadyMessage = {
5
8
  type: 'ready';
6
9
  framework?: string | null;
10
+ target?: HMRClientTarget;
7
11
  };
8
12
  export type RequestRebuildMessage = {
9
13
  type: 'request-rebuild';
@@ -18,7 +22,13 @@ export type HydrationErrorMessage = {
18
22
  };
19
23
  export type HMRTimingMessage = {
20
24
  type: 'hmr-timing';
25
+ clientMs?: number;
21
26
  duration: number;
27
+ kind?: HMRApplyKind;
28
+ outcome?: HMRApplyOutcome;
29
+ serverMs?: number;
30
+ target?: HMRClientTarget;
31
+ updateId?: number;
22
32
  };
23
33
  export type AngularHmrAckMessage = {
24
34
  type: 'angular:hmr-ack';
@@ -27,6 +37,8 @@ export type AngularHmrAckMessage = {
27
37
  tier: 'tier-0' | 'tier-1a';
28
38
  applyMs: number;
29
39
  error?: string;
40
+ target?: HMRClientTarget;
41
+ updateId?: number;
30
42
  };
31
43
  export type HMRClientMessage = PingMessage | ReadyMessage | RequestRebuildMessage | HydrationErrorMessage | HMRTimingMessage | AngularHmrAckMessage;
32
44
  export type ManifestMessage = {
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(),
@@ -1472,6 +1472,19 @@ var colors2, frameworkColors, formatPath = (filePath) => {
1472
1472
  const errorMsg = error instanceof Error ? error.message : error;
1473
1473
  const fullMessage = `${colors2.red}error${colors2.reset} ${message}${errorMsg ? `: ${errorMsg}` : ""}`;
1474
1474
  console.error(`${timestamp} ${tag} ${fullMessage}`);
1475
+ }, logHmrClientUpdate = (path, framework, duration, target, serverMs, clientMs, outcome = "applied", kind) => {
1476
+ const timestamp = `${colors2.dim}${formatTimestamp()}${colors2.reset}`;
1477
+ const targetLabel = target.startsWith("capacitor-") ? target.slice("capacitor-".length) : target;
1478
+ const tag = `${colors2.cyan}[hmr:${targetLabel}]${colors2.reset}`;
1479
+ const pathColor = framework ? getFrameworkColor(framework) : colors2.white;
1480
+ const breakdown = serverMs === undefined || clientMs === undefined ? "" : `; server ${serverMs}ms, client ${clientMs}ms`;
1481
+ let action = "failed after";
1482
+ if (outcome === "applied")
1483
+ action = "applied in";
1484
+ else if (outcome === "reloaded")
1485
+ action = "falling back to reload after";
1486
+ const kindLabel = kind ? `${kind} ` : "";
1487
+ console.log(`${timestamp} ${tag} ${framework ?? "update"} ${kindLabel}${pathColor}${formatPath(path)}${colors2.reset} ${colors2.dim}${action} ${duration}ms${breakdown}${colors2.reset}`);
1475
1488
  }, logHmrUpdate = (path, framework, duration) => {
1476
1489
  log("hmr update", { duration, framework, path });
1477
1490
  }, logInfo = (message) => {
@@ -1708,8 +1721,8 @@ var toIslandFrameworkSegment = (framework) => framework[0]?.toUpperCase() + fram
1708
1721
  return found ? entries : undefined;
1709
1722
  }, getIslandManifestEntries = (manifest) => {
1710
1723
  const islands = {};
1711
- const frameworks = ["react", "svelte", "vue", "angular"];
1712
- for (const framework of frameworks) {
1724
+ const frameworks2 = ["react", "svelte", "vue", "angular"];
1725
+ for (const framework of frameworks2) {
1713
1726
  const prefix = `Island${toIslandFrameworkSegment(framework)}`;
1714
1727
  const entries = collectFrameworkIslands(manifest, prefix);
1715
1728
  if (entries)
@@ -1729,14 +1742,14 @@ var defineIslandComponent = (component, options) => ({
1729
1742
  component,
1730
1743
  export: options.export,
1731
1744
  source: options.source
1732
- }), defineIslandRegistry = (registry) => registry, isRecord3 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component) => {
1745
+ }), defineIslandRegistry = (registry) => registry, isRecord4 = (value) => typeof value === "object" && value !== null, getIslandBuildReference = (component) => {
1733
1746
  if (!isIslandComponentDefinition(component))
1734
1747
  return null;
1735
1748
  return {
1736
1749
  export: component.export,
1737
1750
  source: component.source
1738
1751
  };
1739
- }, isIslandComponentDefinition = (value) => isRecord3(value) && ("component" in value) && ("source" in value) && typeof value.source === "string", parseIslandProps = (rawProps) => {
1752
+ }, isIslandComponentDefinition = (value) => isRecord4(value) && ("component" in value) && ("source" in value) && typeof value.source === "string", parseIslandProps = (rawProps) => {
1740
1753
  if (!rawProps)
1741
1754
  return {};
1742
1755
  return JSON.parse(rawProps);
@@ -4024,7 +4037,7 @@ var init_vueServerModule = __esm(() => {
4024
4037
  var islandSequence = 0, resolvedServerComponentCache, resolvedServerBuildComponentCache, nextIslandId = () => {
4025
4038
  islandSequence += 1;
4026
4039
  return `island-${islandSequence}`;
4027
- }, 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) => {
4040
+ }, 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) => {
4028
4041
  if (source.startsWith("file://"))
4029
4042
  return new URL(source).pathname;
4030
4043
  if (source.startsWith("."))
@@ -4924,6 +4937,278 @@ var setCurrentIslandManifest = (manifest) => {
4924
4937
 
4925
4938
  // src/vue/pageHandler.ts
4926
4939
  init_devRouteRegistrationCallsite();
4940
+
4941
+ // src/core/requestContext.ts
4942
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
4943
+ import { Elysia as Elysia2 } from "elysia";
4944
+ var REQUEST_STORAGE_KEY = Symbol.for("absolutejs.requestAsyncLocalStorage");
4945
+ var isAbsoluteRequestStorage = (value) => typeof value === "object" && value !== null && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
4946
+ var getRequestStorage = () => {
4947
+ const value = Reflect.get(globalThis, REQUEST_STORAGE_KEY);
4948
+ return isAbsoluteRequestStorage(value) ? value : undefined;
4949
+ };
4950
+ var ensureRequestStorage = () => {
4951
+ const existing = getRequestStorage();
4952
+ if (existing)
4953
+ return existing;
4954
+ const storage = new AsyncLocalStorage2;
4955
+ Reflect.set(globalThis, REQUEST_STORAGE_KEY, storage);
4956
+ return storage;
4957
+ };
4958
+ var absoluteRequestContext = new Elysia2({
4959
+ name: "absolutejs-request-context"
4960
+ }).request(({ request }) => {
4961
+ ensureRequestStorage().enterWith({ request });
4962
+ }).as("global");
4963
+ var getCurrentAbsoluteRequest = () => getRequestStorage()?.getStore()?.request;
4964
+ var runWithAbsoluteRequest = (request, callback) => ensureRequestStorage().run({ request }, callback);
4965
+
4966
+ // src/mobile/producerContextState.ts
4967
+ var ABSOLUTE_MOBILE_PRODUCER_STORAGE_KEY = Symbol.for("absolutejs.mobileProducerAsyncLocalStorage");
4968
+ var frameworks = new Set([
4969
+ "angular",
4970
+ "ember",
4971
+ "html",
4972
+ "htmx",
4973
+ "react",
4974
+ "svelte",
4975
+ "vue"
4976
+ ]);
4977
+ 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";
4978
+ var getCurrentAbsoluteMobileProducerContext = () => {
4979
+ const value = Reflect.get(globalThis, ABSOLUTE_MOBILE_PRODUCER_STORAGE_KEY);
4980
+ if (typeof value !== "object" || value === null || !("getStore" in value) || typeof value.getStore !== "function") {
4981
+ return;
4982
+ }
4983
+ const context = value.getStore();
4984
+ if (typeof context !== "object" || context === null || !("page" in context) || !isCompatibilityPage(context.page) || !("releaseId" in context) || typeof context.releaseId !== "string") {
4985
+ return;
4986
+ }
4987
+ return { page: context.page, releaseId: context.releaseId };
4988
+ };
4989
+
4990
+ // src/mobile/pageProtocol.ts
4991
+ var ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE = "application/vnd.absolute.page+json";
4992
+ var ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION = 1;
4993
+ var BAD_REQUEST_STATUS = 400;
4994
+ var OK_STATUS = 200;
4995
+ var SERVER_ERROR_STATUS = 500;
4996
+ var UPGRADE_REQUIRED_STATUS = 426;
4997
+ var MOBILE_PAGE_REQUEST_HEADERS = {
4998
+ appBuild: "x-absolute-mobile-app-build",
4999
+ pageBundle: "x-absolute-mobile-page-bundle",
5000
+ pageContracts: "x-absolute-mobile-page-contracts",
5001
+ pageId: "x-absolute-mobile-page-id",
5002
+ protocol: "x-absolute-mobile-protocol",
5003
+ runtime: "x-absolute-mobile-runtime"
5004
+ };
5005
+ var parseMediaType = (value) => value.split(";", 1)[0]?.trim().toLowerCase();
5006
+ var acceptsAbsoluteMobilePage = (request) => {
5007
+ if (!request)
5008
+ return false;
5009
+ return (request.headers.get("accept") ?? "").split(",").some((value) => parseMediaType(value) === ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE);
5010
+ };
5011
+ var readRequiredHeader = (request, name) => {
5012
+ const value = request.headers.get(name)?.trim();
5013
+ return value ? value : undefined;
5014
+ };
5015
+ var parseAbsoluteMobilePageRequest = (request) => {
5016
+ if (!request || !acceptsAbsoluteMobilePage(request)) {
5017
+ return { kind: "not-mobile" };
5018
+ }
5019
+ const protocolValue = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.protocol);
5020
+ const runtime = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.runtime);
5021
+ const appBuild = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.appBuild);
5022
+ const pageBundle = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.pageBundle);
5023
+ const pageContractsValue = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.pageContracts);
5024
+ const pageId = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.pageId);
5025
+ if (!protocolValue || !/^\d+$/.test(protocolValue)) {
5026
+ return {
5027
+ kind: "invalid",
5028
+ message: `Missing or invalid ${MOBILE_PAGE_REQUEST_HEADERS.protocol} header.`
5029
+ };
5030
+ }
5031
+ if (!runtime) {
5032
+ return {
5033
+ kind: "invalid",
5034
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.runtime} header.`
5035
+ };
5036
+ }
5037
+ if (!appBuild) {
5038
+ return {
5039
+ kind: "invalid",
5040
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.appBuild} header.`
5041
+ };
5042
+ }
5043
+ if (!pageBundle) {
5044
+ return {
5045
+ kind: "invalid",
5046
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.pageBundle} header.`
5047
+ };
5048
+ }
5049
+ if (!pageContractsValue) {
5050
+ return {
5051
+ kind: "invalid",
5052
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.pageContracts} header.`
5053
+ };
5054
+ }
5055
+ if (!pageId) {
5056
+ return {
5057
+ kind: "invalid",
5058
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.pageId} header.`
5059
+ };
5060
+ }
5061
+ const pageContracts = [
5062
+ ...new Set(pageContractsValue.split(",").map((value) => value.trim()).filter(Boolean))
5063
+ ];
5064
+ if (pageContracts.length === 0) {
5065
+ return {
5066
+ kind: "invalid",
5067
+ message: `${MOBILE_PAGE_REQUEST_HEADERS.pageContracts} must contain at least one contract.`
5068
+ };
5069
+ }
5070
+ return {
5071
+ client: {
5072
+ appBuild,
5073
+ pageBundle,
5074
+ pageContracts,
5075
+ pageId,
5076
+ protocol: Number(protocolValue),
5077
+ runtime
5078
+ },
5079
+ kind: "mobile"
5080
+ };
5081
+ };
5082
+ var responseHeaders = () => {
5083
+ const headers = new Headers({
5084
+ "cache-control": "no-store",
5085
+ "content-type": `${ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE}; version=${ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION}`
5086
+ });
5087
+ headerVaryValues.forEach((value) => headers.append("vary", value));
5088
+ return headers;
5089
+ };
5090
+ var headerVaryValues = [
5091
+ "Accept",
5092
+ MOBILE_PAGE_REQUEST_HEADERS.protocol,
5093
+ MOBILE_PAGE_REQUEST_HEADERS.runtime,
5094
+ MOBILE_PAGE_REQUEST_HEADERS.appBuild,
5095
+ MOBILE_PAGE_REQUEST_HEADERS.pageBundle,
5096
+ MOBILE_PAGE_REQUEST_HEADERS.pageContracts,
5097
+ MOBILE_PAGE_REQUEST_HEADERS.pageId
5098
+ ];
5099
+ var envelopeResponse = (response, status) => new Response(JSON.stringify({
5100
+ protocol: ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
5101
+ response
5102
+ }), { headers: responseHeaders(), status });
5103
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
5104
+ var normalizeJsonValue = (value) => {
5105
+ const serialized = JSON.stringify(value);
5106
+ if (serialized === undefined) {
5107
+ throw new TypeError("Mobile page props must be JSON-serializable.");
5108
+ }
5109
+ const parsed = JSON.parse(serialized);
5110
+ if (!isRecord(parsed)) {
5111
+ throw new TypeError("Mobile page props must serialize to an object.");
5112
+ }
5113
+ return parsed;
5114
+ };
5115
+ var finalizeArchivedMobilePage = (context, input) => {
5116
+ const { page } = context;
5117
+ if (page.pageId !== input.compatibility.pageId || page.framework !== input.compatibility.framework) {
5118
+ return envelopeResponse({
5119
+ kind: "invalid-request",
5120
+ message: "Archived producer page identity does not match its release artifact."
5121
+ }, BAD_REQUEST_STATUS);
5122
+ }
5123
+ try {
5124
+ const [currentRepresentation] = input.compatibility.representations;
5125
+ if (!currentRepresentation) {
5126
+ throw new TypeError("Mobile page has no current representation.");
5127
+ }
5128
+ return envelopeResponse({
5129
+ contract: page.contract,
5130
+ framework: page.framework,
5131
+ kind: "page",
5132
+ pageId: page.pageId,
5133
+ props: normalizeJsonValue(currentRepresentation.mapProps(input.props)),
5134
+ status: input.status ?? OK_STATUS
5135
+ }, input.status ?? OK_STATUS);
5136
+ } catch (error) {
5137
+ console.error(`[Mobile] Archived producer ${context.releaseId} failed for ${page.pageId}:`, error);
5138
+ return createAbsoluteMobilePageErrorResponse(page.pageId);
5139
+ }
5140
+ };
5141
+ var createAbsoluteMobileInvalidRequestResponse = (message) => envelopeResponse({ kind: "invalid-request", message }, BAD_REQUEST_STATUS);
5142
+ var createAbsoluteMobilePageErrorResponse = (pageId) => envelopeResponse({
5143
+ code: "representation-failed",
5144
+ kind: "error",
5145
+ pageId,
5146
+ status: SERVER_ERROR_STATUS
5147
+ }, SERVER_ERROR_STATUS);
5148
+ var createAbsoluteMobileUpgradeResponse = (result) => envelopeResponse(result, UPGRADE_REQUIRED_STATUS);
5149
+ var finalizeAbsoluteMobilePage = (input) => {
5150
+ const parsed = parseAbsoluteMobilePageRequest(input.request);
5151
+ if (parsed.kind === "not-mobile")
5152
+ return;
5153
+ if (parsed.kind === "invalid") {
5154
+ return envelopeResponse({ kind: "invalid-request", message: parsed.message }, BAD_REQUEST_STATUS);
5155
+ }
5156
+ const { client } = parsed;
5157
+ if (client.pageId !== input.compatibility.pageId) {
5158
+ return envelopeResponse({
5159
+ kind: "invalid-request",
5160
+ message: `Requested mobile page ${client.pageId} does not match ${input.compatibility.pageId}.`
5161
+ }, BAD_REQUEST_STATUS);
5162
+ }
5163
+ const producerContext = getCurrentAbsoluteMobileProducerContext();
5164
+ if (producerContext)
5165
+ return finalizeArchivedMobilePage(producerContext, input);
5166
+ if (client.protocol !== ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION) {
5167
+ return envelopeResponse({
5168
+ kind: "upgrade-required",
5169
+ pageId: input.compatibility.pageId,
5170
+ reason: "protocol"
5171
+ }, UPGRADE_REQUIRED_STATUS);
5172
+ }
5173
+ if (!input.compatibility.runtimes.includes(client.runtime)) {
5174
+ return envelopeResponse({
5175
+ kind: "upgrade-required",
5176
+ pageId: input.compatibility.pageId,
5177
+ reason: "runtime",
5178
+ supportedRuntimes: [...input.compatibility.runtimes]
5179
+ }, UPGRADE_REQUIRED_STATUS);
5180
+ }
5181
+ const clientContracts = new Set(client.pageContracts);
5182
+ const representation = input.compatibility.representations.find(({ contract }) => clientContracts.has(contract));
5183
+ if (!representation) {
5184
+ return envelopeResponse({
5185
+ kind: "upgrade-required",
5186
+ pageId: input.compatibility.pageId,
5187
+ reason: "page-contract",
5188
+ supportedContracts: input.compatibility.representations.map(({ contract }) => contract)
5189
+ }, UPGRADE_REQUIRED_STATUS);
5190
+ }
5191
+ try {
5192
+ return envelopeResponse({
5193
+ contract: representation.contract,
5194
+ framework: input.compatibility.framework,
5195
+ kind: "page",
5196
+ pageId: input.compatibility.pageId,
5197
+ props: normalizeJsonValue(representation.mapProps(input.props)),
5198
+ status: input.status ?? OK_STATUS
5199
+ }, input.status ?? OK_STATUS);
5200
+ } catch (error) {
5201
+ console.error(`[Mobile] Failed to produce ${input.compatibility.pageId} representation ${representation.contract}:`, error);
5202
+ return envelopeResponse({
5203
+ code: "representation-failed",
5204
+ kind: "error",
5205
+ pageId: input.compatibility.pageId,
5206
+ status: SERVER_ERROR_STATUS
5207
+ }, SERVER_ERROR_STATUS);
5208
+ }
5209
+ };
5210
+
5211
+ // src/vue/pageHandler.ts
4927
5212
  init_pageResponseCache();
4928
5213
 
4929
5214
  // src/core/responseEnhancers.ts
@@ -4980,7 +5265,7 @@ var withRegisteredStreamingSlots = async (renderResponse, options = {}) => {
4980
5265
 
4981
5266
  // src/core/streamingSlotWarningScope.ts
4982
5267
  init_logger();
4983
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
5268
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
4984
5269
  var STREAMING_SLOT_WARNING_STORAGE_KEY2 = Symbol.for("absolutejs.streamingSlotWarningAsyncLocalStorage");
4985
5270
  var isObjectRecord5 = (value) => Boolean(value) && typeof value === "object";
4986
5271
  var isAsyncLocalStorage3 = (value) => isObjectRecord5(value) && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
@@ -4996,7 +5281,7 @@ var ensureWarningStorage = () => {
4996
5281
  if (existing) {
4997
5282
  return existing;
4998
5283
  }
4999
- const storage = new AsyncLocalStorage2;
5284
+ const storage = new AsyncLocalStorage3;
5000
5285
  Reflect.set(globalThis, STREAMING_SLOT_WARNING_STORAGE_KEY2, storage);
5001
5286
  return storage;
5002
5287
  };
@@ -5078,23 +5363,23 @@ var ABSOLUTE_TELEPORT_TARGET_ID = "absolute-teleports";
5078
5363
  var ABSOLUTE_TELEPORT_TARGET = `#${ABSOLUTE_TELEPORT_TARGET_ID}`;
5079
5364
 
5080
5365
  // src/vue/pageHandler.ts
5081
- var isRecord2 = (value) => typeof value === "object" && value !== null;
5082
- var isGenericVueComponent = (value) => typeof value === "function" || isRecord2(value);
5366
+ var isRecord3 = (value) => typeof value === "object" && value !== null;
5367
+ var isGenericVueComponent = (value) => typeof value === "function" || isRecord3(value);
5083
5368
  var readHasIslands = (value) => {
5084
- if (!isRecord2(value))
5369
+ if (!isRecord3(value))
5085
5370
  return false;
5086
5371
  const hasIslands = value["__ABSOLUTE_PAGE_HAS_ISLANDS__"];
5087
5372
  return typeof hasIslands === "boolean" ? hasIslands : false;
5088
5373
  };
5089
5374
  var isVueSetupApp = (value) => typeof value === "function";
5090
5375
  var readSetupAppHook = (value) => {
5091
- if (!isRecord2(value))
5376
+ if (!isRecord3(value))
5092
5377
  return null;
5093
5378
  const { setupApp } = value;
5094
5379
  return isVueSetupApp(setupApp) ? setupApp : null;
5095
5380
  };
5096
- var readHasSpaRoutes = (value) => isRecord2(value) && Array.isArray(value["routes"]);
5097
- var readDefaultExport = (value) => isRecord2(value) ? value.default : undefined;
5381
+ var readHasSpaRoutes = (value) => isRecord3(value) && Array.isArray(value["routes"]);
5382
+ var readDefaultExport = (value) => isRecord3(value) ? value.default : undefined;
5098
5383
  var resolveRequestRenderUrl = (request) => {
5099
5384
  if (!request)
5100
5385
  return "/";
@@ -5124,12 +5409,29 @@ var getSsrOnlyHmrShim = () => {
5124
5409
  };
5125
5410
  var handleVuePageRequest = async (input) => {
5126
5411
  const passedPageComponent = input.Page;
5412
+ const request = input.request ?? getCurrentAbsoluteRequest();
5413
+ const pageId = input.__absoluteMobile?.pageId ?? derivePageName(input.pagePath);
5414
+ const currentContract = input.__absoluteMobile?.contract ?? `vue:${pageId}:${ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION}`;
5415
+ const mobileResponse = finalizeAbsoluteMobilePage({
5416
+ compatibility: {
5417
+ framework: "vue",
5418
+ pageId,
5419
+ representations: [
5420
+ { contract: currentContract, mapProps: (props) => props }
5421
+ ],
5422
+ runtimes: [String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION)]
5423
+ },
5424
+ props: input.props ?? Object.create(null),
5425
+ request
5426
+ });
5427
+ if (mobileResponse)
5428
+ return mobileResponse;
5127
5429
  const userHeadTag = input.headTag ?? "<head></head>";
5128
5430
  const resolvedOptions = input;
5129
5431
  const resolvedPagePath = input.pagePath;
5130
5432
  const [siblingCss, spaChildCss] = await Promise.all([
5131
5433
  readSiblingCss(resolvedPagePath),
5132
- resolveSpaChildCss(resolvedPagePath, input.request?.url)
5434
+ resolveSpaChildCss(resolvedPagePath, request?.url)
5133
5435
  ]);
5134
5436
  const resolvedHeadTag = injectInlineCss(injectInlineCss(userHeadTag, siblingCss), spaChildCss);
5135
5437
  const maybeProps = input.props;
@@ -5142,9 +5444,9 @@ var handleVuePageRequest = async (input) => {
5142
5444
  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.');
5143
5445
  }
5144
5446
  try {
5145
- const spaNotFound = await renderSpaNotFound("vue", derivePageName(resolvedPagePath), input.request);
5447
+ const spaNotFound = await renderSpaNotFound("vue", derivePageName(resolvedPagePath), request);
5146
5448
  if (spaNotFound)
5147
- return withPageCacheHeaders(spaNotFound, input.request);
5449
+ return withPageCacheHeaders(spaNotFound, request);
5148
5450
  const handlerCallsite = resolvedOptions?.collectStreamingSlots === true ? undefined : getCurrentRouteRegistrationCallsite() ?? captureStreamingSlotWarningCallsite();
5149
5451
  const renderPageResponse = async () => {
5150
5452
  const resolvePageComponent = async () => {
@@ -5177,7 +5479,7 @@ var handleVuePageRequest = async (input) => {
5177
5479
  let pendingRedirect = null;
5178
5480
  let pendingNotFound = false;
5179
5481
  if (resolvedPage.setupApp) {
5180
- const url = resolveRequestRenderUrl(input.request);
5482
+ const url = resolveRequestRenderUrl(request);
5181
5483
  await resolvedPage.setupApp(app, {
5182
5484
  isServer: true,
5183
5485
  router: null,
@@ -5258,7 +5560,7 @@ var handleVuePageRequest = async (input) => {
5258
5560
  });
5259
5561
  };
5260
5562
  const pageResponse = await runWithStreamingSlotWarningScope(() => resolvedOptions?.collectStreamingSlots === true ? withRegisteredStreamingSlots(renderPageResponse, resolvedOptions) : renderPageResponse(), { handlerCallsite });
5261
- return withPageCacheHeaders(pageResponse, input.request, {
5563
+ return withPageCacheHeaders(pageResponse, request, {
5262
5564
  bufferStreamForEtag: input.bufferStreamForEtag
5263
5565
  });
5264
5566
  } catch (error) {
@@ -5266,12 +5568,12 @@ var handleVuePageRequest = async (input) => {
5266
5568
  const pageName = derivePageName(resolvedPagePath);
5267
5569
  const conventionResponse = await renderConventionError("vue", pageName, error);
5268
5570
  if (conventionResponse) {
5269
- return withPageCacheHeaders(conventionResponse, input.request);
5571
+ return withPageCacheHeaders(conventionResponse, request);
5270
5572
  }
5271
5573
  return withPageCacheHeaders(new Response(ssrErrorPage("vue", error), {
5272
5574
  headers: { "Content-Type": "text/html" },
5273
5575
  status: 500
5274
- }), input.request);
5576
+ }), request);
5275
5577
  }
5276
5578
  };
5277
5579
  // src/vue/routerRedirectProviders.ts
@@ -5734,5 +6036,5 @@ export {
5734
6036
  ABSOLUTE_TELEPORT_TARGET
5735
6037
  };
5736
6038
 
5737
- //# debugId=D8D81E80B0032F8A64756E2164756E21
6039
+ //# debugId=296101957B993E2D64756E2164756E21
5738
6040
  //# sourceMappingURL=index.js.map