akanjs 2.1.0-rc.6 → 2.1.0-rc.7

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.
@@ -0,0 +1,88 @@
1
+ import os from "node:os";
2
+ import type { CapacitorConfig } from "@capacitor/cli";
3
+ import type { AkanMobileTargetConfig, AppScanResult } from "akanjs";
4
+
5
+ const getLocalIP = () => {
6
+ const interfaces = os.networkInterfaces();
7
+ for (const interfaceName in interfaces) {
8
+ const iface = interfaces[interfaceName];
9
+ if (!iface) continue;
10
+ for (const alias of iface) {
11
+ if (alias.family === "IPv4" && !alias.internal) return alias.address;
12
+ }
13
+ }
14
+ return "127.0.0.1";
15
+ };
16
+
17
+ const normalizeBasePath = (basePath: string | undefined) => basePath?.replace(/^\/+|\/+$/g, "");
18
+
19
+ const routeBasePaths = (appInfo: AppScanResult) =>
20
+ new Set(
21
+ appInfo.routes
22
+ .map((route) => route.replace(/^\.\//, "").split("/")[0])
23
+ .filter((segment): segment is string => !!segment && !segment.startsWith("_") && !segment.startsWith("(")),
24
+ );
25
+
26
+ const resolveTarget = (appInfo: AppScanResult, targetName = process.env.AKAN_MOBILE_TARGET) => {
27
+ const targets = appInfo.akanConfig.mobile.targets;
28
+ if (!targets || Object.keys(targets).length === 0) throw new Error("Akan mobile target metadata is missing.");
29
+ if (targetName) {
30
+ const target = targets[targetName];
31
+ if (!target) {
32
+ const basePath = normalizeBasePath(targetName);
33
+ const [template] = Object.values(targets);
34
+ if (basePath && template && routeBasePaths(appInfo).has(basePath))
35
+ return { ...template, name: basePath, basePath };
36
+ throw new Error(`Akan mobile target '${targetName}' was not found.`);
37
+ }
38
+ return target;
39
+ }
40
+ const entries = Object.entries(targets);
41
+ if (entries.length !== 1) throw new Error("AKAN_MOBILE_TARGET is required when multiple mobile targets exist.");
42
+ return entries[0]?.[1] as AkanMobileTargetConfig;
43
+ };
44
+
45
+ const localCsrUrl = (ip: string, target: AkanMobileTargetConfig) => {
46
+ const basePath = normalizeBasePath(target.basePath);
47
+ const port = process.env.AKAN_PUBLIC_CLIENT_PORT ?? process.env.PORT ?? "8282";
48
+ return `http://${ip}:${port}/${basePath ? `${basePath}` : ""}?csr=true`;
49
+ };
50
+
51
+ export const withBase = (
52
+ configImp: (config: CapacitorConfig, target: AkanMobileTargetConfig) => CapacitorConfig = (config) => config,
53
+ appData?: AppScanResult,
54
+ targetName?: string,
55
+ ) => {
56
+ const ip = getLocalIP();
57
+ const appInfo = appData;
58
+ if (!appInfo) throw new Error("withBase requires apps/<app>/akan.app.json metadata.");
59
+ const target = resolveTarget(appInfo, targetName);
60
+ const baseConfig: CapacitorConfig = {
61
+ ...target,
62
+ appId: target.appId,
63
+ appName: target.appName,
64
+ webDir: "dist",
65
+ server:
66
+ process.env.APP_OPERATION_MODE !== "release"
67
+ ? {
68
+ androidScheme: "http",
69
+ url: localCsrUrl(ip, target),
70
+ cleartext: true,
71
+ allowNavigation: [ip, "localhost"],
72
+ }
73
+ : {
74
+ allowNavigation: ["*"],
75
+ },
76
+ plugins: {
77
+ CapacitorCookies: { enabled: true },
78
+ ...target.plugins,
79
+ },
80
+ android: {
81
+ ...target.android,
82
+ },
83
+ ios: {
84
+ ...target.ios,
85
+ },
86
+ };
87
+ return configImp(baseConfig, target);
88
+ };
package/client/device.ts CHANGED
@@ -50,6 +50,11 @@ const getRenderMode = () => globalWithProcess.process?.env?.AKAN_PUBLIC_RENDER_E
50
50
 
51
51
  const getBrowserLanguage = () => globalThis.navigator?.language?.split("-")[0] ?? "en";
52
52
 
53
+ const isNativeTarget = () => {
54
+ if (typeof window === "undefined") return false;
55
+ return Boolean((window as typeof window & { __AKAN_MOBILE_TARGET__?: unknown }).__AKAN_MOBILE_TARGET__);
56
+ };
57
+
53
58
  export const isMobileDevice = () => {
54
59
  if (typeof navigator === "undefined") return false;
55
60
  if (typeof window !== "undefined" && window.matchMedia?.("(hover: none) and (pointer: coarse)")?.matches) return true;
@@ -88,7 +93,7 @@ export class Device {
88
93
  supportLanguages?: string[] | readonly string[];
89
94
  }) {
90
95
  if (Device.instance) return Device.instance;
91
- if (getRenderMode() !== "csr") {
96
+ if (getRenderMode() !== "csr" || !isNativeTarget()) {
92
97
  const device = createWebDevice({ lang, supportLanguages });
93
98
  Device.instance = device;
94
99
  return device;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "2.1.0-rc.6",
3
+ "version": "2.1.0-rc.7",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -95,6 +95,11 @@
95
95
  "import": "./client/capacitor.ts",
96
96
  "default": "./client/capacitor.ts"
97
97
  },
98
+ "./capacitor.base.config": {
99
+ "types": "./types/capacitor.base.config.d.ts",
100
+ "import": "./capacitor.base.config.ts",
101
+ "default": "./capacitor.base.config.ts"
102
+ },
98
103
  "./webkit": {
99
104
  "types": "./types/webkit/index.d.ts",
100
105
  "import": "./webkit/index.ts",
@@ -159,6 +164,7 @@
159
164
  "@capacitor/app": "^8.1.0",
160
165
  "@capacitor/browser": "^8.0.3",
161
166
  "@capacitor/camera": "^8.2.0",
167
+ "@capacitor/cli": "^8.3.4",
162
168
  "@capacitor/core": "^8.3.4",
163
169
  "@capacitor/device": "^8.0.2",
164
170
  "@capacitor/geolocation": "^8.2.0",
@@ -211,6 +217,9 @@
211
217
  "@capacitor/camera": {
212
218
  "optional": true
213
219
  },
220
+ "@capacitor/cli": {
221
+ "optional": true
222
+ },
214
223
  "@capacitor/core": {
215
224
  "optional": true
216
225
  },
@@ -10,11 +10,11 @@ import {
10
10
  ConsoleLogger,
11
11
  type DatabaseAdaptor,
12
12
  DatabaseAdaptorRole,
13
+ JsonCompressor,
13
14
  LibsqlDatabase,
14
15
  type LoggingAdaptor,
15
16
  LoggingAdaptorRole,
16
17
  PostgresDatabase,
17
- JsonCompressor,
18
18
  type QueueAdaptor,
19
19
  QueueAdaptorRole,
20
20
  RedisCache,
@@ -0,0 +1,3 @@
1
+ import type { CapacitorConfig } from "@capacitor/cli";
2
+ import type { AkanMobileTargetConfig, AppScanResult } from "akanjs";
3
+ export declare const withBase: (configImp?: (config: CapacitorConfig, target: AkanMobileTargetConfig) => CapacitorConfig, appData?: AppScanResult, targetName?: string) => CapacitorConfig;
@@ -1,5 +1,5 @@
1
1
  import type { DatabaseMode } from "akanjs";
2
- import { type AdaptorCls, BlobStorage, type CacheAdaptor, type CompressAdaptor, ConsoleLogger, type DatabaseAdaptor, type LoggingAdaptor, JsonCompressor, type QueueAdaptor, type ScheduleAdaptor, Scheduler, SolidCache, SolidPubSub, SolidQueue, SqliteDatabase, type StorageAdaptor, type WebsocketAdaptor } from "akanjs/service";
2
+ import { type AdaptorCls, BlobStorage, type CacheAdaptor, type CompressAdaptor, ConsoleLogger, type DatabaseAdaptor, JsonCompressor, type LoggingAdaptor, type QueueAdaptor, type ScheduleAdaptor, Scheduler, SolidCache, SolidPubSub, SolidQueue, SqliteDatabase, type StorageAdaptor, type WebsocketAdaptor } from "akanjs/service";
3
3
  export interface PredefinedAdaptor {
4
4
  database: AdaptorCls<DatabaseAdaptor>;
5
5
  cache: AdaptorCls<CacheAdaptor>;