akanjs 3.0.0-beta.26 → 3.0.0-beta.28

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 (226) hide show
  1. package/base/baseEnv.ts +19 -5
  2. package/client/csrTypes.ts +2 -0
  3. package/client/device.ts +28 -12
  4. package/client/index.ts +1 -0
  5. package/client/native/accessibility.ts +1 -0
  6. package/client/native/app-state.ts +1 -0
  7. package/client/native/app.ts +1 -0
  8. package/client/native/appearance.ts +1 -0
  9. package/client/native/auth-session.ts +1 -0
  10. package/client/native/autostart.ts +1 -0
  11. package/client/native/badge.ts +1 -0
  12. package/client/native/biometric.ts +1 -0
  13. package/client/native/browser.ts +1 -0
  14. package/client/native/camera.ts +1 -0
  15. package/client/native/clipboard.ts +1 -0
  16. package/client/native/contacts.ts +1 -0
  17. package/client/native/device.ts +1 -0
  18. package/client/native/dialog.ts +1 -0
  19. package/client/native/dock.ts +1 -0
  20. package/client/native/file-picker.ts +1 -0
  21. package/client/native/filesystem.ts +1 -0
  22. package/client/native/geolocation.ts +1 -0
  23. package/client/native/global-shortcut.ts +1 -0
  24. package/client/native/haptics.ts +1 -0
  25. package/client/native/http.ts +1 -0
  26. package/client/native/iap.ts +1 -0
  27. package/client/native/keep-awake.ts +1 -0
  28. package/client/native/keyboard.ts +1 -0
  29. package/client/native/local-notifications.ts +1 -0
  30. package/client/native/menu.ts +1 -0
  31. package/client/native/network.ts +1 -0
  32. package/client/native/opener.ts +1 -0
  33. package/client/native/preferences.ts +1 -0
  34. package/client/native/push.ts +1 -0
  35. package/client/native/screen-orientation.ts +1 -0
  36. package/client/native/screen.ts +1 -0
  37. package/client/native/secure-storage.ts +1 -0
  38. package/client/native/share.ts +1 -0
  39. package/client/native/single-instance.ts +1 -0
  40. package/client/native/splash-screen.ts +1 -0
  41. package/client/native/sqlite.ts +1 -0
  42. package/client/native/toast.ts +1 -0
  43. package/client/native/tray.ts +1 -0
  44. package/client/native/updates.ts +1 -0
  45. package/client/native/volume.ts +1 -0
  46. package/client/native/window-state.ts +1 -0
  47. package/client/native/window.ts +1 -0
  48. package/client/native.ts +31 -1
  49. package/client/resolveServerUrl.ts +11 -0
  50. package/client/router.ts +8 -0
  51. package/common/Logger.ts +26 -2
  52. package/common/RouteLayering.ts +52 -0
  53. package/common/index.ts +1 -0
  54. package/index.ts +115 -6
  55. package/native/desktop.ts +5 -0
  56. package/package.json +11 -1
  57. package/server/akanApp.ts +23 -3
  58. package/server/akanAppHeaders.ts +4 -1
  59. package/server/akanServer.ts +27 -7
  60. package/server/index.ts +1 -0
  61. package/server/lifecycle/orphanGroup.ts +21 -0
  62. package/server/nativeFile.ts +107 -0
  63. package/server/ops/opsRoute.ts +3 -2
  64. package/server/routeTreeBuilder.ts +14 -39
  65. package/server/routing/apiRouter.ts +26 -3
  66. package/server/routing/hostAllowlist.ts +24 -0
  67. package/server/selfExec.ts +25 -0
  68. package/server/types.tsx +3 -1
  69. package/server/webRouter.ts +7 -4
  70. package/service/ipcTypes.ts +4 -0
  71. package/signal/CrossSiteGuard.ts +7 -0
  72. package/test/signalTestRuntime.ts +8 -2
  73. package/types/base/baseEnv.d.ts +1 -0
  74. package/types/client/csrTypes.d.ts +2 -0
  75. package/types/client/index.d.ts +1 -0
  76. package/types/client/native/accessibility.d.ts +1 -0
  77. package/types/client/native/app-state.d.ts +1 -0
  78. package/types/client/native/app.d.ts +1 -0
  79. package/types/client/native/appearance.d.ts +1 -0
  80. package/types/client/native/auth-session.d.ts +1 -0
  81. package/types/client/native/autostart.d.ts +1 -0
  82. package/types/client/native/badge.d.ts +1 -0
  83. package/types/client/native/biometric.d.ts +1 -0
  84. package/types/client/native/browser.d.ts +1 -0
  85. package/types/client/native/camera.d.ts +1 -0
  86. package/types/client/native/clipboard.d.ts +1 -0
  87. package/types/client/native/contacts.d.ts +1 -0
  88. package/types/client/native/device.d.ts +1 -0
  89. package/types/client/native/dialog.d.ts +1 -0
  90. package/types/client/native/dock.d.ts +1 -0
  91. package/types/client/native/file-picker.d.ts +1 -0
  92. package/types/client/native/filesystem.d.ts +1 -0
  93. package/types/client/native/geolocation.d.ts +1 -0
  94. package/types/client/native/global-shortcut.d.ts +1 -0
  95. package/types/client/native/haptics.d.ts +1 -0
  96. package/types/client/native/http.d.ts +1 -0
  97. package/types/client/native/iap.d.ts +1 -0
  98. package/types/client/native/keep-awake.d.ts +1 -0
  99. package/types/client/native/keyboard.d.ts +1 -0
  100. package/types/client/native/local-notifications.d.ts +1 -0
  101. package/types/client/native/menu.d.ts +1 -0
  102. package/types/client/native/network.d.ts +1 -0
  103. package/types/client/native/opener.d.ts +1 -0
  104. package/types/client/native/preferences.d.ts +1 -0
  105. package/types/client/native/push.d.ts +1 -0
  106. package/types/client/native/screen-orientation.d.ts +1 -0
  107. package/types/client/native/screen.d.ts +1 -0
  108. package/types/client/native/secure-storage.d.ts +1 -0
  109. package/types/client/native/share.d.ts +1 -0
  110. package/types/client/native/single-instance.d.ts +1 -0
  111. package/types/client/native/splash-screen.d.ts +1 -0
  112. package/types/client/native/sqlite.d.ts +1 -0
  113. package/types/client/native/toast.d.ts +1 -0
  114. package/types/client/native/tray.d.ts +1 -0
  115. package/types/client/native/updates.d.ts +1 -0
  116. package/types/client/native/volume.d.ts +1 -0
  117. package/types/client/native/window-state.d.ts +1 -0
  118. package/types/client/native/window.d.ts +1 -0
  119. package/types/client/native.d.ts +14 -2
  120. package/types/client/resolveServerUrl.d.ts +1 -0
  121. package/types/client/router.d.ts +3 -0
  122. package/types/common/RouteLayering.d.ts +28 -0
  123. package/types/common/index.d.ts +1 -0
  124. package/types/index.d.ts +112 -6
  125. package/types/native/desktop.d.ts +3 -0
  126. package/types/server/akanAppHeaders.d.ts +2 -0
  127. package/types/server/index.d.ts +1 -0
  128. package/types/server/lifecycle/orphanGroup.d.ts +7 -0
  129. package/types/server/nativeFile.d.ts +10 -0
  130. package/types/server/routing/apiRouter.d.ts +3 -1
  131. package/types/server/routing/hostAllowlist.d.ts +8 -0
  132. package/types/server/selfExec.d.ts +8 -0
  133. package/types/server/types.d.ts +3 -1
  134. package/types/service/ipcTypes.d.ts +16 -0
  135. package/types/signal/CrossSiteGuard.d.ts +1 -0
  136. package/types/ui/recipe/buttonRecipe.d.ts +2 -2
  137. package/types/vendor/@akanjs/native/packages/cli/src/api.d.ts +57 -4
  138. package/types/vendor/@akanjs/native/packages/cli/src/config.d.ts +48 -0
  139. package/types/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.d.ts +8 -0
  140. package/types/vendor/@akanjs/native/packages/cli/src/lib/prepare.d.ts +8 -2
  141. package/types/vendor/@akanjs/native/packages/cli/src/lib/project.d.ts +10 -0
  142. package/types/vendor/@akanjs/native/packages/cli/src/lib/publish.d.ts +24 -0
  143. package/types/vendor/@akanjs/native/packages/cli/src/lib/updates.d.ts +24 -0
  144. package/types/vendor/@akanjs/native/packages/cli/src/platforms/desktop-update.d.ts +4 -1
  145. package/types/vendor/@akanjs/native/packages/cli/src/platforms/macos.d.ts +5 -0
  146. package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.d.ts +38 -0
  147. package/types/vendor/@akanjs/native/packages/desktop/src/dispatcher.d.ts +8 -1
  148. package/types/vendor/@akanjs/native/packages/desktop/src/grants.d.ts +11 -0
  149. package/types/vendor/@akanjs/native/packages/desktop/src/main.d.ts +11 -0
  150. package/types/vendor/@akanjs/native/packages/desktop/src/paths.d.ts +13 -2
  151. package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +27 -0
  152. package/types/vendor/@akanjs/native/packages/desktop/src/relaunch.d.ts +21 -0
  153. package/types/vendor/@akanjs/native/packages/desktop/src/server.d.ts +79 -0
  154. package/types/vendor/@akanjs/native/plugins/app/src/index.d.ts +5 -0
  155. package/types/vendor/@akanjs/native/plugins/file-picker/src/index.d.ts +20 -1
  156. package/types/vendor/@akanjs/native/plugins/push/src/index.d.ts +9 -0
  157. package/types/vendor/@akanjs/native/plugins/updates/src/desktop.d.ts +28 -0
  158. package/types/vendor/@akanjs/native/plugins/volume/src/common.d.ts +10 -0
  159. package/types/vendor/@akanjs/native/plugins/volume/src/desktop.d.ts +18 -0
  160. package/types/vendor/@akanjs/native/plugins/volume/src/index.d.ts +28 -0
  161. package/types/vendor/@akanjs/native/plugins/volume/src.d.ts +1 -0
  162. package/types/webkit/csrFrameDump.d.ts +42 -0
  163. package/types/webkit/nativeNavigation.d.ts +1 -0
  164. package/types/webkit/nativePurchase.d.ts +6 -2
  165. package/types/webkit/nativeUpdates.d.ts +8 -0
  166. package/ui/Agent/Attach.tsx +6 -2
  167. package/ui/CsrImage.tsx +3 -2
  168. package/ui/System/CSR.tsx +4 -0
  169. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeActivity.kt +5 -1
  170. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeBridge.kt +19 -0
  171. package/vendor/@akanjs/native/native/desktop/Cargo.toml +1 -1
  172. package/vendor/@akanjs/native/native/desktop/src/lib.rs +161 -25
  173. package/vendor/@akanjs/native/native/desktop/src/volume.rs +184 -0
  174. package/vendor/@akanjs/native/native/desktop/src/win/mod.rs +3 -1
  175. package/vendor/@akanjs/native/native/desktop/src/win/volume.rs +71 -0
  176. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeShell.swift +12 -2
  177. package/vendor/@akanjs/native/packages/cli/src/api.ts +142 -12
  178. package/vendor/@akanjs/native/packages/cli/src/commands/build.ts +5 -3
  179. package/vendor/@akanjs/native/packages/cli/src/commands/update.ts +10 -71
  180. package/vendor/@akanjs/native/packages/cli/src/config.ts +41 -0
  181. package/vendor/@akanjs/native/packages/cli/src/lib/configkeys.ts +9 -2
  182. package/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.ts +58 -0
  183. package/vendor/@akanjs/native/packages/cli/src/lib/hmr.ts +2 -0
  184. package/vendor/@akanjs/native/packages/cli/src/lib/prepare.ts +10 -2
  185. package/vendor/@akanjs/native/packages/cli/src/lib/project.ts +38 -2
  186. package/vendor/@akanjs/native/packages/cli/src/lib/publish.ts +104 -0
  187. package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +85 -5
  188. package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +1 -0
  189. package/vendor/@akanjs/native/packages/cli/src/platforms/desktop-update.ts +6 -4
  190. package/vendor/@akanjs/native/packages/cli/src/platforms/desktop.ts +16 -0
  191. package/vendor/@akanjs/native/packages/cli/src/platforms/macos.ts +44 -1
  192. package/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.ts +436 -0
  193. package/vendor/@akanjs/native/packages/cli/src/platforms/windows.ts +2 -0
  194. package/vendor/@akanjs/native/packages/core/src/runtime.ts +26 -5
  195. package/vendor/@akanjs/native/packages/desktop/src/dispatcher.ts +20 -0
  196. package/vendor/@akanjs/native/packages/desktop/src/grants.ts +47 -0
  197. package/vendor/@akanjs/native/packages/desktop/src/host.ts +102 -14
  198. package/vendor/@akanjs/native/packages/desktop/src/main.ts +59 -11
  199. package/vendor/@akanjs/native/packages/desktop/src/paths.ts +38 -4
  200. package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +28 -0
  201. package/vendor/@akanjs/native/packages/desktop/src/relaunch.ts +159 -0
  202. package/vendor/@akanjs/native/packages/desktop/src/server.ts +493 -0
  203. package/vendor/@akanjs/native/plugins/app/android/AppPlugin.kt +14 -0
  204. package/vendor/@akanjs/native/plugins/app/native-plugin.json +2 -2
  205. package/vendor/@akanjs/native/plugins/app/src/desktop.ts +5 -0
  206. package/vendor/@akanjs/native/plugins/app/src/index.ts +6 -1
  207. package/vendor/@akanjs/native/plugins/app/src/web.ts +3 -0
  208. package/vendor/@akanjs/native/plugins/file-picker/src/desktop.ts +39 -2
  209. package/vendor/@akanjs/native/plugins/file-picker/src/index.ts +36 -1
  210. package/vendor/@akanjs/native/plugins/push/android/PushPlugin.kt +9 -1
  211. package/vendor/@akanjs/native/plugins/push/ios/PushPlugin.swift +4 -0
  212. package/vendor/@akanjs/native/plugins/push/src/index.ts +10 -0
  213. package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +464 -452
  214. package/vendor/@akanjs/native/plugins/volume/android/VolumePlugin.kt +107 -0
  215. package/vendor/@akanjs/native/plugins/volume/native-plugin.json +14 -0
  216. package/vendor/@akanjs/native/plugins/volume/src/common.ts +28 -0
  217. package/vendor/@akanjs/native/plugins/volume/src/desktop.ts +185 -0
  218. package/vendor/@akanjs/native/plugins/volume/src/index.ts +32 -0
  219. package/webkit/CsrRouteTable.ts +15 -35
  220. package/webkit/RenderLayer.tsx +71 -6
  221. package/webkit/bootCsr.tsx +11 -8
  222. package/webkit/csrFrameDump.ts +67 -0
  223. package/webkit/nativeNavigation.ts +17 -0
  224. package/webkit/nativePurchase.ts +12 -2
  225. package/webkit/nativeUpdates.ts +55 -0
  226. package/webkit/useCsrValues.ts +27 -0
package/base/baseEnv.ts CHANGED
@@ -112,11 +112,15 @@ export const resetEnvCache = () => {
112
112
  };
113
113
 
114
114
  const csrServerUrl = (): URL | null => {
115
- const value = process.env.AKAN_PUBLIC_SERVER_URL;
115
+ const runtime = (globalThis as { __AKAN_NATIVE__?: { env?: Record<string, string | undefined> } }).__AKAN_NATIVE__
116
+ ?.env?.PUBLIC_AKAN_SERVER_URL;
117
+ const [key, value] = runtime
118
+ ? ["PUBLIC_AKAN_SERVER_URL", runtime]
119
+ : ["AKAN_PUBLIC_SERVER_URL", process.env.AKAN_PUBLIC_SERVER_URL];
116
120
  if (!value) return null;
117
121
  const url = new URL(value);
118
122
  if (url.protocol !== "http:" && url.protocol !== "https:")
119
- throw new Error(`AKAN_PUBLIC_SERVER_URL must be an http(s) URL, got "${value}".`);
123
+ throw new Error(`${key} must be an http(s) URL, got "${value}".`);
120
124
  return url;
121
125
  };
122
126
 
@@ -125,10 +129,12 @@ const nativeDevGatewayOrigin = (): string | null =>
125
129
  ? `${window.location.protocol}//${window.location.host}`
126
130
  : null;
127
131
 
132
+ const devServerOrigin = () => `http://localhost:${process.env.AKAN_PUBLIC_SERVER_PORT ?? "8282"}`;
133
+
128
134
  const nativeDevServerUrl = (operationMode: BaseEnv["operationMode"]): URL | null => {
129
135
  const platform = (globalThis as { __AKAN_NATIVE__?: { platform?: string } }).__AKAN_NATIVE__?.platform;
130
136
  if (operationMode !== "local" || !platform || platform === "web") return null;
131
- return new URL(`http://localhost:${process.env.AKAN_PUBLIC_SERVER_PORT ?? "8282"}`);
137
+ return new URL(devServerOrigin());
132
138
  };
133
139
 
134
140
  const missingPublicEnv = (key: string) =>
@@ -179,8 +185,9 @@ export const getEnv = (): ClientEnv => {
179
185
  : "https:";
180
186
  const clientHttpUri = `${clientHttpProtocol}//${clientHost}${clientPort === 443 ? "" : `:${clientPort}`}`;
181
187
  const csrClient = side === "client" && renderMode === "csr";
182
- const pageOrigin = csrClient && !process.env.AKAN_PUBLIC_SERVER_URL ? nativeDevGatewayOrigin() : null;
183
- const serverUrl = csrClient && !pageOrigin ? (csrServerUrl() ?? nativeDevServerUrl(operationMode)) : null;
188
+ const pinnedServerUrl = csrClient ? csrServerUrl() : null;
189
+ const pageOrigin = csrClient && !pinnedServerUrl ? nativeDevGatewayOrigin() : null;
190
+ const serverUrl = csrClient && !pageOrigin ? (pinnedServerUrl ?? nativeDevServerUrl(operationMode)) : null;
184
191
 
185
192
  const hostFromPage =
186
193
  side === "client" && !serverUrl && (!!pageOrigin || operationMode === "local" || renderMode !== "csr");
@@ -251,3 +258,10 @@ export const getEnv = (): ClientEnv => {
251
258
  cachedEnv = env;
252
259
  return env;
253
260
  };
261
+
262
+ export const getServerOrigin = (): string => {
263
+ const env = getEnv();
264
+ const behindGateway =
265
+ env.side === "client" && env.renderMode === "csr" && !csrServerUrl() && !!nativeDevGatewayOrigin();
266
+ return behindGateway ? devServerOrigin() : new URL(env.serverHttpUri).origin;
267
+ };
@@ -174,6 +174,8 @@ export interface Route {
174
174
  path: string;
175
175
  renderPage?: RouteRender;
176
176
  renderLayout?: RouteRender;
177
+ /** `renderLayout` is a generated `__root_layout`: a root boundary the page generator found. */
178
+ isRootLayout?: boolean;
177
179
  /** Synthetic layout render from a `_overrides.tsx` at this node; wraps the subtree in a UI-override provider. */
178
180
  renderOverrides?: RouteRender;
179
181
  pageIncludesOwnLayout?: boolean;
package/client/device.ts CHANGED
@@ -13,27 +13,43 @@ const globalWithProcess = globalThis as typeof globalThis & { process?: ProcessE
13
13
  const debugSessionId = Math.random().toString(36).slice(2, 8);
14
14
  let debugSeq = 0;
15
15
 
16
- const isFrameDebugEnabled = () => {
17
- if (typeof window === "undefined") return false;
18
- const windowWithTarget = window as typeof window & { __AKAN_MOBILE_TARGET__?: unknown };
19
- const search = new URLSearchParams(window.location.search);
20
- return (
21
- Boolean(windowWithTarget.__AKAN_MOBILE_TARGET__) ||
22
- search.has("akanMobileTarget") ||
23
- search.get("akanFrameDebug") === "1" ||
24
- window.localStorage.getItem("akan:debug:frame") === "1"
25
- );
16
+ const FRAME_TRACE_LIMIT = 2000;
17
+ const frameTrace: { seq: number; event: string; details: DebugPayload }[] = [];
18
+
19
+ const readFrameDebugFlag = () => {
20
+ const fromSearch = new URLSearchParams(window.location.search).get("akanFrameDebug");
21
+ if (fromSearch) return fromSearch;
22
+ try {
23
+ return window.localStorage.getItem("akan:debug:frame");
24
+ } catch {
25
+
26
+ return null;
27
+ }
28
+ };
29
+
30
+ const frameDebugMode = (): "console" | "memory" | null => {
31
+ if (typeof window === "undefined") return null;
32
+ const flag = readFrameDebugFlag();
33
+ return flag === "1" ? "console" : flag === "memory" ? "memory" : null;
26
34
  };
27
35
 
28
36
  export function debugFrame(event: string, payload: DebugPayload = {}) {
29
- if (!isFrameDebugEnabled()) return;
37
+ const mode = frameDebugMode();
38
+ if (!mode) return;
30
39
  debugSeq += 1;
31
40
  const details = {
32
41
  href: window.location.href,
33
42
  now: Math.round(performance.now()),
34
43
  ...payload,
35
44
  };
36
- console.info(`[akan:frame:${debugSessionId}:${debugSeq}] ${event}`, details, JSON.stringify(details));
45
+ if (mode === "memory") {
46
+ frameTrace.push({ seq: debugSeq, event, details });
47
+ if (frameTrace.length > FRAME_TRACE_LIMIT) frameTrace.splice(0, frameTrace.length - FRAME_TRACE_LIMIT);
48
+ (window as { __AKAN_FRAME_TRACE__?: typeof frameTrace }).__AKAN_FRAME_TRACE__ = frameTrace;
49
+ return;
50
+ }
51
+
52
+ console.debug(`[akan:frame:${debugSessionId}:${debugSeq}] ${event}`, details);
37
53
  }
38
54
 
39
55
  interface DeviceInitOption {
package/client/index.ts CHANGED
@@ -8,6 +8,7 @@ export * from "./device";
8
8
  export * from "./frameConfig";
9
9
  export * from "./locale";
10
10
  export * from "./makePageProto";
11
+ export * from "./resolveServerUrl";
11
12
  export * from "./route";
12
13
  export * from "./router";
13
14
  export * from "./rscNavigation";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/accessibility/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/app-state/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/app/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/appearance/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/auth-session/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/autostart/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/badge/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/biometric/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/browser/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/camera/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/clipboard/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/contacts/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/device/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/dialog/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/dock/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/file-picker/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/filesystem/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/geolocation/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/global-shortcut/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/haptics/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/http/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/iap/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/keep-awake/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/keyboard/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/local-notifications/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/menu/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/network/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/opener/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/preferences/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/push/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/screen-orientation/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/screen/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/secure-storage/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/share/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/single-instance/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/splash-screen/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/sqlite/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/toast/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/tray/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/updates/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/volume/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/window-state/src/index.ts";
@@ -0,0 +1 @@
1
+ export * from "../../vendor/@akanjs/native/plugins/window/src/index.ts";
package/client/native.ts CHANGED
@@ -2,14 +2,36 @@ import { isNative, platform } from "../vendor/@akanjs/native/packages/core/src/i
2
2
  import { browser } from "../vendor/@akanjs/native/plugins/browser/src/index.ts";
3
3
  import { opener } from "../vendor/@akanjs/native/plugins/opener/src/index.ts";
4
4
 
5
- export { AkanNativeError, fileBlob, releaseFile } from "../vendor/@akanjs/native/packages/core/src/index.ts";
5
+ export type { LiveValue, Plugin } from "../vendor/@akanjs/native/packages/core/src/index.ts";
6
+ export {
7
+ AkanNativeError,
8
+ createLiveValue,
9
+ definePlugin,
10
+ defineWebPlugin,
11
+ fileBlob,
12
+ isAkanNativeError,
13
+ releaseFile,
14
+ shallowEqual,
15
+ } from "../vendor/@akanjs/native/packages/core/src/index.ts";
6
16
  export { app } from "../vendor/@akanjs/native/plugins/app/src/index.ts";
7
17
  export { appState } from "../vendor/@akanjs/native/plugins/app-state/src/index.ts";
18
+ export type { AuthSessionApi } from "../vendor/@akanjs/native/plugins/auth-session/src/index.ts";
19
+ export { authSession } from "../vendor/@akanjs/native/plugins/auth-session/src/index.ts";
8
20
  export type { Photo } from "../vendor/@akanjs/native/plugins/camera/src/index.ts";
9
21
  export type { Contact } from "../vendor/@akanjs/native/plugins/contacts/src/index.ts";
10
22
  export type { DeviceInfo } from "../vendor/@akanjs/native/plugins/device/src/index.ts";
11
23
  export { device } from "../vendor/@akanjs/native/plugins/device/src/index.ts";
12
24
  export { dialog } from "../vendor/@akanjs/native/plugins/dialog/src/index.ts";
25
+ export type {
26
+ DirectoryFile,
27
+ PickDirectoryOptions,
28
+ PickDirectoryResult,
29
+ PickedFile,
30
+ PickFilesOptions,
31
+ SaveFileOptions,
32
+ SaveFileResult,
33
+ } from "../vendor/@akanjs/native/plugins/file-picker/src/index.ts";
34
+ export { filePicker } from "../vendor/@akanjs/native/plugins/file-picker/src/index.ts";
13
35
  export type { Position } from "../vendor/@akanjs/native/plugins/geolocation/src/index.ts";
14
36
  export { haptics } from "../vendor/@akanjs/native/plugins/haptics/src/index.ts";
15
37
  export type { IapProduct, IapTransaction } from "../vendor/@akanjs/native/plugins/iap/src/index.ts";
@@ -23,6 +45,11 @@ export type {
23
45
  } from "../vendor/@akanjs/native/plugins/push/src/index.ts";
24
46
  export { push } from "../vendor/@akanjs/native/plugins/push/src/index.ts";
25
47
  export { secureStorage } from "../vendor/@akanjs/native/plugins/secure-storage/src/index.ts";
48
+ export type { UpdateCheck, UpdateProgress, UpdateState } from "../vendor/@akanjs/native/plugins/updates/src/index.ts";
49
+ export { markReady, updates, useUpdateState } from "../vendor/@akanjs/native/plugins/updates/src/index.ts";
50
+ export type { VolumeState } from "../vendor/@akanjs/native/plugins/volume/src/index.ts";
51
+ export { volume } from "../vendor/@akanjs/native/plugins/volume/src/index.ts";
52
+ export { useLiveValue, usePluginEvent } from "../vendor/@akanjs/native/packages/react/src/index.ts";
26
53
  export { browser, opener };
27
54
 
28
55
  export const loadCamera = () => import("../vendor/@akanjs/native/plugins/camera/src/index.ts");
@@ -33,6 +60,9 @@ export const loadIap = () => import("../vendor/@akanjs/native/plugins/iap/src/in
33
60
  /** Inside an iOS or Android shell of the native runtime; a mobile target opened in a browser is not. */
34
61
  export const isNativeApp = () => isNative && (platform === "ios" || platform === "android");
35
62
 
63
+ /** Inside any shell of the native runtime, desktop included. */
64
+ export const isNativeShell = () => isNative;
65
+
36
66
  export const nativePlatform = () => (isNativeApp() ? (platform as "ios" | "android") : null);
37
67
 
38
68
  export const desktopPlatform = () =>
@@ -0,0 +1,11 @@
1
+ import { getEnv } from "akanjs/base";
2
+
3
+ export const resolveServerUrl = (url: string): string => {
4
+ if (!url.startsWith("/") || url.startsWith("//")) return url;
5
+ const env = getEnv();
6
+ if (env.side !== "client" || env.renderMode !== "csr") return url;
7
+ if (url !== env.apiPrefix && !url.startsWith(`${env.apiPrefix}/`)) return url;
8
+ const server = new URL(env.serverHttpUri);
9
+ const serverOrigin = `${server.protocol}//${server.host}`;
10
+ return serverOrigin === `${window.location.protocol}//${window.location.host}` ? url : `${serverOrigin}${url}`;
11
+ };
package/client/router.ts CHANGED
@@ -160,6 +160,7 @@ class Router {
160
160
  #indexPath = "/";
161
161
  #navigation: Promise<void> = Promise.resolve();
162
162
  #historyIdx = 0;
163
+ #redirects = 0;
163
164
  #instance: RouterInstance = {
164
165
  push: (href: string) => {
165
166
  const { href: fullHref } = this.#getPathInfo(href);
@@ -407,6 +408,11 @@ class Router {
407
408
  this.#instance.refresh();
408
409
  return undefined as never;
409
410
  }
411
+ /** Client-side redirects so far: a render that saw this change while it ran redirected instead of drawing. A method
412
+ * for the reason `navigation()` is one. */
413
+ redirectCount(): number {
414
+ return this.#redirects;
415
+ }
410
416
  redirect(href: string, options: RedirectOptions = {}): never {
411
417
  const method = options.method ?? "replace";
412
418
  const status = options.status ?? 307;
@@ -422,6 +428,7 @@ class Router {
422
428
  Logger.info(`redirect to:${pathname}`);
423
429
  throw new AkanRedirectError(fullHref, method, status);
424
430
  } else {
431
+ this.#redirects += 1;
425
432
  this.#instance[method](href);
426
433
  }
427
434
  return undefined as never;
@@ -432,6 +439,7 @@ class Router {
432
439
  throw new AkanNotFoundError();
433
440
  }
434
441
  this.#checkInitialized();
442
+ this.#redirects += 1;
435
443
  this.#instance.replace("/404");
436
444
  return undefined as never;
437
445
  }
package/common/Logger.ts CHANGED
@@ -77,6 +77,15 @@ const colorizeMap: { [key in LogLevel]: (text: string) => string } = {
77
77
  error: clc.red,
78
78
  };
79
79
 
80
+ const consoleMethodMap = {
81
+ trace: "debug",
82
+ verbose: "debug",
83
+ debug: "debug",
84
+ info: "info",
85
+ warn: "warn",
86
+ error: "error",
87
+ } as const satisfies { [key in LogLevel]: "debug" | "info" | "warn" | "error" };
88
+
80
89
  const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`, "g");
81
90
 
82
91
  const redactedAttrKey = /password|passwd|token|jwt|authorization|cookie|secret|api[-_]?key|private[-_]?key/i;
@@ -332,7 +341,22 @@ export class Logger {
332
341
  if (!Logger.consoleOutput) return;
333
342
  if (typeof window === "undefined")
334
343
  (process[entry.stream] as unknown as NodeJS.WriteStream | undefined)?.write(entry.message);
335
- else console.log(entry.message);
344
+ else Logger.#writeBrowserConsole(entry.record, entry.message);
345
+ }
346
+
347
+ static #writeBrowserConsole(record: LogRecord, rendered: string) {
348
+ const level = record.level ?? "info";
349
+ const context = record.context ? `[${record.context}] ` : "";
350
+ const attrs = record.attrs ? ` ${Logger.formatAttrs(record.attrs)}` : "";
351
+ const text = Logger.#inNativeShell()
352
+ ? `[${record.name}] ${context}${record.message}${attrs}`
353
+ : rendered.replace(/\n+$/, "");
354
+
355
+ console[consoleMethodMap[level]](text);
356
+ }
357
+ static #inNativeShell() {
358
+ const platform = (globalThis as { __AKAN_NATIVE__?: { platform?: string } }).__AKAN_NATIVE__?.platform;
359
+ return platform !== undefined && platform !== "web";
336
360
  }
337
361
  static #write(name: string | undefined, message: string, context: string, logLevel: LogLevel, attrs?: LogAttrs) {
338
362
  const stream = logLevel === "error" ? "stderr" : "stdout";
@@ -367,7 +391,7 @@ export class Logger {
367
391
  if (!Logger.consoleOutput) return;
368
392
  if (typeof window === "undefined" && method !== "console" && (process as unknown as NodeJS.Process | undefined))
369
393
  process[stream].write(msg);
370
- else console[outputStream === "error" ? "error" : "log"](msg.trim());
394
+ else console[outputStream === "error" ? "error" : Logger.#inNativeShell() ? "debug" : "log"](msg.trim());
371
395
  }
372
396
  static {
373
397
  if (envValue("AKAN_PUBLIC_LOG_LEVEL") === "log" && typeof window === "undefined")
@@ -0,0 +1,52 @@
1
+ interface LayeredRoute<Render> {
2
+ /** The tree segment: `/`, `/:lang`, `/(group)`, `/foo`. */
3
+ path: string;
4
+ renderLayout?: Render;
5
+ /** A generated `__root_layout`: one of the root boundaries the page generator found. */
6
+ isRootLayout?: boolean;
7
+ renderOverrides?: Render;
8
+ /** `false` for a named page file, which sits beside its directory's layout rather than under it. */
9
+ pageIncludesOwnLayout?: boolean;
10
+ }
11
+
12
+ export interface RouteLayer<Render> {
13
+ /** The URL pattern down to this node; a route group adds nothing to it. */
14
+ path: string;
15
+ pathSegments: string[];
16
+ isRoot: boolean;
17
+ /** The root layout above or at this node, overrides left out: once there is one, nothing below is a root. */
18
+ rootLayouts: Render[];
19
+ renderRootLayouts: Render[];
20
+ renderLayouts: Render[];
21
+ /** What a page file at this node renders inside: without this node's layout for a named page file. */
22
+ pageRenderRootLayouts: Render[];
23
+ pageRenderLayouts: Render[];
24
+ }
25
+
26
+ export class RouteLayering {
27
+ static readonly #group = /^\/\(.*\)$/;
28
+
29
+ static of<Render>(route: LayeredRoute<Render>, parent: RouteLayer<Render> | null): RouteLayer<Render> {
30
+ const parentSegments = parent?.pathSegments ?? [];
31
+ const parentRootLayouts = parent?.rootLayouts ?? [];
32
+ const parentRootRenders = parent?.renderRootLayouts ?? [];
33
+ const parentLayouts = parent?.renderLayouts ?? [];
34
+ const segment = RouteLayering.#group.test(route.path) ? "" : route.path;
35
+ const path = parentSegments.filter((part) => part !== "/").join("") + segment;
36
+ const isRoot = parentRootLayouts.length === 0 && (route.isRootLayout === true || !route.renderLayout);
37
+ const overrideRenders = route.renderOverrides ? [route.renderOverrides] : [];
38
+
39
+ const nodeRenders = [...overrideRenders, ...(route.renderLayout ? [route.renderLayout] : [])];
40
+ const pageNodeRenders = route.pageIncludesOwnLayout === false ? overrideRenders : nodeRenders;
41
+ return {
42
+ path,
43
+ pathSegments: [...parentSegments, ...(segment ? [segment] : [])],
44
+ isRoot,
45
+ rootLayouts: isRoot && route.renderLayout ? [...parentRootLayouts, route.renderLayout] : parentRootLayouts,
46
+ renderRootLayouts: isRoot ? [...parentRootRenders, ...nodeRenders] : parentRootRenders,
47
+ renderLayouts: isRoot ? parentLayouts : [...parentLayouts, ...nodeRenders],
48
+ pageRenderRootLayouts: isRoot ? [...parentRootRenders, ...pageNodeRenders] : parentRootRenders,
49
+ pageRenderLayouts: isRoot ? parentLayouts : [...parentLayouts, ...pageNodeRenders],
50
+ };
51
+ }
52
+ }
package/common/index.ts CHANGED
@@ -128,6 +128,7 @@ export {
128
128
  export { deepObjectify, objectify, plainFieldsOf } from "./objectify";
129
129
  export { pathGet, pathGetLoose, pathSet, toPathSegments } from "./objectPath";
130
130
  export { formatPhone, isPhoneNumber } from "./phone";
131
+ export { type RouteLayer, RouteLayering } from "./RouteLayering";
131
132
  export { randomPick, randomPicks } from "./randomPick";
132
133
  export { hostFromRequest, isJsonContentType, originFromRequest } from "./requestOrigin";
133
134
  export { RestClient, type RestClientOptions, type RestRequestOptions } from "./restClient";