akanjs 3.0.0-beta.29 → 3.0.0-beta.30

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.
package/README.ko.md CHANGED
@@ -57,7 +57,7 @@ const config: AppConfig = {};
57
57
  export default config;
58
58
  ```
59
59
 
60
- 프로덕션 앱은 `akan.config.ts`에서 routes, base paths, domains, mobile metadata, deployment options로
60
+ 프로덕션 앱은 `akan.config.ts`에서 routes, base paths, domains, native app settings, deployment options로
61
61
  자연스럽게 확장됩니다.
62
62
 
63
63
  ## 오래 읽히는 코드
@@ -66,7 +66,7 @@ Akan은 오래 운영되는 애플리케이션을 이해하기 쉽게 유지하
66
66
  가능한 형태로 둡니다.
67
67
 
68
68
  - **하나의 설정 표면**: `akan.config.ts`가 앱 단위 설정의 중심입니다. routes, domains, base paths,
69
- mobile settings, deployment options가 필요해질 때까지 비워둘 수 있습니다.
69
+ native app settings, deployment options가 필요해질 때까지 비워둘 수 있습니다.
70
70
  - **모노리포 기반 재사용**: shared library와 domain module이 기본 단위이므로 검증된 비즈니스 코드를
71
71
  앱마다 다시 만들지 않고 함께 사용할 수 있습니다.
72
72
  - **엄격한 구조, 익숙한 코드**: 파일 위치, 파일명, 선언 방식, module boundary가 워크스페이스 전반에서
@@ -229,7 +229,7 @@ akan update
229
229
  ## 애플리케이션 설정
230
230
 
231
231
  `akan.config.ts`는 앱 단위 설정의 중심입니다. 단순한 앱에서는 비어 있을 수 있고, 필요할 때만
232
- routes, domains, base paths, mobile settings로 확장됩니다.
232
+ routes, domains, base paths, native app settings로 확장됩니다.
233
233
 
234
234
  ```ts
235
235
  import type { AppConfig } from "akanjs";
@@ -239,7 +239,8 @@ const config: AppConfig = {
239
239
  { domains: { main: ["example.com", "www.example.com"] }, basePath: "web" },
240
240
  { domains: {}, basePath: "app" },
241
241
  ],
242
- mobile: {
242
+ native: {
243
+ basePath: "app",
243
244
  appName: "Example",
244
245
  appId: "com.example.app",
245
246
  version: "1.0.0",
package/README.md CHANGED
@@ -56,7 +56,7 @@ const config: AppConfig = {};
56
56
  export default config;
57
57
  ```
58
58
 
59
- Production apps can grow into routes, base paths, domains, mobile metadata, and deployment options through
59
+ Production apps can grow into routes, base paths, domains, native app settings, and deployment options through
60
60
  `akan.config.ts`.
61
61
 
62
62
  ## Readable By Design
@@ -64,7 +64,7 @@ Production apps can grow into routes, base paths, domains, mobile metadata, and
64
64
  Akan keeps long-lived applications understandable by making the important decisions explicit and repeatable.
65
65
 
66
66
  - **One configuration surface**: `akan.config.ts` is the app-level control plane. It can stay empty until the
67
- app actually needs routes, domains, base paths, mobile settings, or deployment options.
67
+ app actually needs routes, domains, base paths, native app settings, or deployment options.
68
68
  - **Monorepo-native reuse**: shared libraries and domain modules are first-class, so validated business code can
69
69
  be reused without rebuilding the same wheel in each app.
70
70
  - **Strict structure, familiar code**: file locations, names, declarations, and module boundaries follow the same
@@ -227,7 +227,7 @@ Common areas:
227
227
  ## Application Configuration
228
228
 
229
229
  `akan.config.ts` is the single place for app-level configuration. It can be empty for simple apps, then
230
- grow only when the app needs routes, domains, base paths, or mobile settings.
230
+ grow only when the app needs routes, domains, base paths, or native app settings.
231
231
 
232
232
  ```ts
233
233
  import type { AppConfig } from "akanjs";
@@ -237,7 +237,8 @@ const config: AppConfig = {
237
237
  { domains: { main: ["example.com", "www.example.com"] }, basePath: "web" },
238
238
  { domains: {}, basePath: "app" },
239
239
  ],
240
- mobile: {
240
+ native: {
241
+ basePath: "app",
241
242
  appName: "Example",
242
243
  appId: "com.example.app",
243
244
  version: "1.0.0",
package/index.ts CHANGED
@@ -99,79 +99,24 @@ export interface AkanAssetsConfig {
99
99
  export type LibAssetsConfig = Pick<AkanAssetsConfig, "keepFonts">;
100
100
 
101
101
  export type DatabaseMode = "single" | "multiple" | "cluster";
102
- export type MobileEnv = "local" | "debug" | "develop" | "main";
103
- export type MobilePermission = "camera" | "contacts" | "location" | "push" | "speech";
102
+ export type NativeEnv = "local" | "debug" | "develop" | "main";
103
+ export type NativePermission = "camera" | "contacts" | "location" | "push" | "speech";
104
104
 
105
- export interface AkanMobileTargetAssets {
106
- /** A square PNG, relative to the app folder, or it with the color behind its transparent areas. */
107
- icon?: string | { image: string; backgroundColor?: string };
108
- /** A PNG shown centered at launch, or the launch screen's color, image and when it hides. */
109
- splash?: string | NonNullable<AkanNativeConfig["splash"]>;
110
- }
111
-
112
- export interface AkanMobileTargetDeepLinks {
113
- schemes?: string[];
114
- domains?: string[];
115
- ios?: {
116
- teamId?: string;
117
- };
118
- android?: {
119
- sha256CertFingerprints?: string[];
120
- };
121
- }
105
+ export type AkanNativeValue = string | number | boolean | AkanNativeValue[] | { [key: string]: AkanNativeValue };
122
106
 
123
107
  /**
124
- * Files copied into the app, keyed by where they land: `ios/<path>` (the app bundle), `android/res/<type>/<file>` or
125
- * `android/assets/<path>`. The value is the source, relative to the app folder.
108
+ * One bundle id, or one per platform, for an app whose store listings already carry different ids. A platform without
109
+ * its own takes `default`.
126
110
  */
127
- export type AkanMobileTargetFiles = Record<string, string>;
128
-
129
- export type AkanNativeValue = string | number | boolean | AkanNativeValue[] | { [key: string]: AkanNativeValue };
111
+ export type AkanNativeAppId =
112
+ | string
113
+ | { default?: string; ios?: string; android?: string; macos?: string; windows?: string; linux?: string };
130
114
 
131
- export interface AkanMobileNativeConfig {
132
- /** Native runtime plugins beyond the ones the permissions bring, by builtin id (`"iap"`) or absolute folder. */
133
- plugins?: string[];
134
- ios?: {
135
- infoPlist?: Record<string, AkanNativeValue>;
136
- entitlements?: Record<string, AkanNativeValue>;
137
- };
138
- android?: {
139
- /** XML at the `<manifest>` level; `${applicationId}` is replaced. */
140
- manifest?: string[];
141
- /** XML inside `<application>`. */
142
- application?: string[];
143
- /** XML inside the app's activity. */
144
- activity?: string[];
145
- /** The Firebase project's google-services.json, relative to the app folder, for FCM push on Android. */
146
- googleServices?: string;
147
- /** Media plays with sound without a tap first, as it does on iOS and the desktop: a signage screen. */
148
- autoplay?: boolean;
149
- };
150
- /** Android's notification channel, status bar icon (relative to the app folder) and accent color. */
151
- push?: AkanNativeConfig["push"];
152
- /** The app's part of the iOS privacy manifest (PrivacyInfo.xcprivacy), which an App Store upload requires. */
153
- privacy?: AkanNativeConfig["privacy"];
154
- desktop?: {
155
- /**
156
- * The desktop app carries the app's server (API only, database mode single, on loopback) and its pages call
157
- * nothing else. `build-desktop`, `start-desktop` and `publish-update` all read it, and an installed app refuses
158
- * a release that carries a server when it has none, or none when it has one.
159
- */
160
- server?: boolean;
161
- /**
162
- * `"reload"`: a window whose page's process ends (a crash, a hang) loads it again every time, waiting
163
- * longer after each end in a row, and the app relaunches when the webview's browser process ends — for an
164
- * app nobody attends. `"errorPage"` (default): one reload, then an error page, and a quit for the browser.
165
- */
166
- recovery?: "errorPage" | "reload";
167
- /** The main window from its first frame: borderless fullscreen, and no taskbar button (Windows, Linux). */
168
- window?: { fullscreen?: boolean; skipTaskbar?: boolean };
169
- /**
170
- * Windows: `"auto"` answers getDisplayMedia() with the first screen at once, no picker or gesture — remote
171
- * support on an unattended screen; it covers every media request, so not for an app that asks for a camera.
172
- */
173
- screenCapture?: "picker" | "auto";
174
- };
115
+ export interface AkanNativeDeepLinks {
116
+ /** Custom URL schemes the app opens (`board://…`). */
117
+ schemes?: string[];
118
+ /** Hosts whose https links open the app: universal links (iOS, `ios.teamId`) and app links (Android). */
119
+ domains?: string[];
175
120
  }
176
121
 
177
122
  /**
@@ -179,7 +124,7 @@ export interface AkanMobileNativeConfig {
179
124
  * desktop, the web bundle on a phone. `akan publish-update` writes and signs a release with a key on this machine;
180
125
  * `akan pack-update` writes an unsigned phone bundle for whoever holds the key to sign.
181
126
  */
182
- export interface AkanMobileUpdatesConfig {
127
+ export interface AkanNativeUpdatesConfig {
183
128
  /**
184
129
  * A static base URL, https in a release build: `<url>/<os>-<arch>/<channel>.json` (desktop) or `<url>/<platform>/…`
185
130
  * (a phone's web bundle).
@@ -196,43 +141,115 @@ export interface AkanMobileUpdatesConfig {
196
141
  readyTimeout?: number;
197
142
  }
198
143
 
144
+ export interface AkanNativeIosConfig {
145
+ /** The Apple team id: the universal links of `deepLinks.domains` (apple-app-site-association). */
146
+ teamId?: string;
147
+ infoPlist?: Record<string, AkanNativeValue>;
148
+ entitlements?: Record<string, AkanNativeValue>;
149
+ /** The app's part of the iOS privacy manifest (PrivacyInfo.xcprivacy), which an App Store upload requires. */
150
+ privacy?: AkanNativeConfig["privacy"];
151
+ /** Files copied into the app bundle, keyed by their path there; the value is the source, relative to the app folder. */
152
+ files?: Record<string, string>;
153
+ }
154
+
155
+ export interface AkanNativeAndroidConfig {
156
+ /** The signing certificates' SHA-256 fingerprints: the app links of `deepLinks.domains` (assetlinks.json). */
157
+ sha256CertFingerprints?: string[];
158
+ /** The Firebase project's google-services.json, relative to the app folder, for FCM push. */
159
+ googleServices?: string;
160
+ /** The notification channel, status bar icon (relative to the app folder) and accent color of pushes. */
161
+ push?: NonNullable<AkanNativeConfig["push"]>["android"];
162
+ /** Media plays with sound without a tap first, as it does on iOS and the desktop: a signage screen. */
163
+ autoplay?: boolean;
164
+ /** XML at the `<manifest>` level; `${applicationId}` is replaced. */
165
+ manifest?: string[];
166
+ /** XML inside `<application>`. */
167
+ application?: string[];
168
+ /** XML inside the app's activity. */
169
+ activity?: string[];
170
+ /** Files copied into the app, keyed `res/<type>/<file>` or `assets/<path>`; the value is the source. */
171
+ files?: Record<string, string>;
172
+ }
173
+
174
+ export interface AkanNativeDesktopConfig {
175
+ /**
176
+ * The desktop app carries the app's server (API only, database mode single, on loopback) and its pages call
177
+ * nothing else. `build-desktop`, `start-desktop` and `publish-update` all read it, and an installed app refuses
178
+ * a release that carries a server when it has none, or none when it has one.
179
+ */
180
+ server?: boolean;
181
+ /**
182
+ * `"reload"`: a window whose page's process ends (a crash, a hang) loads it again every time, waiting
183
+ * longer after each end in a row, and the app relaunches when the webview's browser process ends — for an
184
+ * app nobody attends. `"errorPage"` (default): one reload, then an error page, and a quit for the browser.
185
+ */
186
+ recovery?: "errorPage" | "reload";
187
+ /** The main window from its first frame: borderless fullscreen, and no taskbar button (Windows, Linux). */
188
+ window?: { fullscreen?: boolean; skipTaskbar?: boolean };
189
+ /**
190
+ * Windows: `"auto"` answers getDisplayMedia() with the first screen at once, no picker or gesture — remote
191
+ * support on an unattended screen; it covers every media request, so not for an app that asks for a camera.
192
+ */
193
+ screenCapture?: "picker" | "auto";
194
+ }
195
+
199
196
  /**
200
- * One bundle id, or one per platform, for an app whose store listings already carry different ids. A platform without
201
- * its own takes `default`.
197
+ * What a native app (iOS, Android, desktop) is: the app's `native` section and each of its targets have this shape. A
198
+ * target takes the section and overrides it field by field: objects merge key by key, and any other value — a list
199
+ * included — replaces the section's.
202
200
  */
203
- export type AkanMobileAppId =
204
- | string
205
- | { default?: string; ios?: string; android?: string; macos?: string; windows?: string; linux?: string };
206
-
207
- export interface AkanMobileTargetConfig {
208
- name: string;
201
+ export interface AkanNativeSettings {
202
+ /** The client the app opens, a basePath the routes declare; an app without basePaths leaves it out. */
209
203
  basePath?: string;
204
+ /** Where the app starts, and falls back to for a deep link's stack and a back with no history. Default `/`. */
210
205
  indexPath?: string;
211
- appName: string;
212
- appId: AkanMobileAppId;
206
+ /** Default: the app's folder name. */
207
+ appName?: string;
208
+ /** Default: one made from the repository's and the app's names. */
209
+ appId?: AkanNativeAppId;
213
210
  /** Executables, archives and the Swift module; letters, digits, `.`, `_` and `-`. Default: the app's folder name. */
214
211
  fileName?: string;
212
+ /** Default `0.0.1`. */
213
+ version?: string;
214
+ /** Default 1. */
215
+ buildNum?: number;
216
+ /** A square PNG, relative to the app folder, or it with the color behind its transparent areas. */
217
+ icon?: string | { image: string; backgroundColor?: string };
218
+ /** A PNG shown centered at launch, or the launch screen's color, image and when it hides. */
219
+ splash?: string | NonNullable<AkanNativeConfig["splash"]>;
220
+ permissions?: NativePermission[];
221
+ /** Native runtime plugins beyond the ones the permissions bring: a builtin id (`"iap"`) or a folder, from the app's. */
222
+ plugins?: string[];
223
+ deepLinks?: AkanNativeDeepLinks;
224
+ updates?: Partial<AkanNativeUpdatesConfig>;
225
+ ios?: AkanNativeIosConfig;
226
+ android?: AkanNativeAndroidConfig;
227
+ desktop?: AkanNativeDesktopConfig;
228
+ }
229
+
230
+ /** `native` in akan.config.ts. Without `targets` the app has one target, named `default`. */
231
+ export interface AkanNativeAppConfig extends AkanNativeSettings {
232
+ targets?: Record<string, AkanNativeSettings>;
233
+ }
234
+
235
+ /** One target of the app, its fields over the app's `native` section and the defaults. */
236
+ export interface AkanNativeTarget
237
+ extends Omit<AkanNativeSettings, "appName" | "appId" | "version" | "buildNum" | "updates"> {
238
+ name: string;
239
+ appName: string;
240
+ appId: AkanNativeAppId;
215
241
  version: string;
216
242
  buildNum: number;
217
- assets?: AkanMobileTargetAssets;
218
- permissions?: MobilePermission[];
219
- deepLinks?: AkanMobileTargetDeepLinks;
220
- files?: AkanMobileTargetFiles;
221
- native?: AkanMobileNativeConfig;
222
- /** Over mobile.updates, field by field: a pilot target names its own channel. */
223
- updates?: Partial<AkanMobileUpdatesConfig>;
243
+ updates?: AkanNativeUpdatesConfig;
224
244
  }
225
245
 
226
- export interface AkanMobileConfig {
246
+ export interface AkanNativeAppResult {
227
247
  appName: string;
228
- appId: AkanMobileAppId;
248
+ appId: AkanNativeAppId;
229
249
  fileName?: string;
230
250
  version: string;
231
251
  buildNum: number;
232
- files?: AkanMobileTargetFiles;
233
- native?: AkanMobileNativeConfig;
234
- updates?: AkanMobileUpdatesConfig;
235
- targets: Record<string, AkanMobileTargetConfig>;
252
+ targets: Record<string, AkanNativeTarget>;
236
253
  }
237
254
 
238
255
  export interface AkanScanInfo {
@@ -271,9 +288,9 @@ export interface AkanSyncContext {
271
288
  readEnvClient(): Promise<Record<string, unknown> | null>;
272
289
  }
273
290
 
274
- /** What a plugin adds to a mobile app whose target asks for its permission; the native build merges every one. */
291
+ /** What a plugin adds to a native app whose target asks for its permission; the native build merges every one. */
275
292
  export interface AkanPluginNativeConfig {
276
- permission: MobilePermission;
293
+ permission: NativePermission;
277
294
  /** Native runtime plugins, by builtin id (`"camera"`). */
278
295
  plugins?: string[];
279
296
  /** iOS usage texts by description name (`cameraUsageDescription`); `$(PRODUCT_NAME)` becomes the app name. */
@@ -335,7 +352,7 @@ export interface AppConfigResult {
335
352
  images: AkanImageConfig;
336
353
  i18n: AkanI18nConfig;
337
354
  publicEnv: string[];
338
- mobile: AkanMobileConfig;
355
+ native: AkanNativeAppResult;
339
356
  secrets: string[];
340
357
  assets: AkanAssetsConfig;
341
358
  }
@@ -365,10 +382,11 @@ export interface LibConfigContext {
365
382
  readonly type: "lib";
366
383
  }
367
384
 
368
- export type AppConfigInput = Omit<DeepPartial<AppConfigResult>, "docker" | "web" | "bin"> & {
385
+ export type AppConfigInput = Omit<DeepPartial<AppConfigResult>, "docker" | "web" | "bin" | "native"> & {
369
386
  docker?: DockerOption;
370
387
  web?: AkanWebOption;
371
388
  bin?: AkanBinConfig;
389
+ native?: AkanNativeAppConfig;
372
390
  plugins?: AkanPlugin[];
373
391
  };
374
392
  export type LibConfigInput = Omit<DeepPartial<LibConfigResult>, "bin"> & {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "3.0.0-beta.29",
3
+ "version": "3.0.0-beta.30",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
package/types/index.d.ts CHANGED
@@ -82,89 +82,35 @@ export interface AkanAssetsConfig {
82
82
  /** A lib picks only which of its own fonts must survive; whether to prune at all belongs to the app. */
83
83
  export type LibAssetsConfig = Pick<AkanAssetsConfig, "keepFonts">;
84
84
  export type DatabaseMode = "single" | "multiple" | "cluster";
85
- export type MobileEnv = "local" | "debug" | "develop" | "main";
86
- export type MobilePermission = "camera" | "contacts" | "location" | "push" | "speech";
87
- export interface AkanMobileTargetAssets {
88
- /** A square PNG, relative to the app folder, or it with the color behind its transparent areas. */
89
- icon?: string | {
90
- image: string;
91
- backgroundColor?: string;
92
- };
93
- /** A PNG shown centered at launch, or the launch screen's color, image and when it hides. */
94
- splash?: string | NonNullable<AkanNativeConfig["splash"]>;
95
- }
96
- export interface AkanMobileTargetDeepLinks {
97
- schemes?: string[];
98
- domains?: string[];
99
- ios?: {
100
- teamId?: string;
101
- };
102
- android?: {
103
- sha256CertFingerprints?: string[];
104
- };
105
- }
106
- /**
107
- * Files copied into the app, keyed by where they land: `ios/<path>` (the app bundle), `android/res/<type>/<file>` or
108
- * `android/assets/<path>`. The value is the source, relative to the app folder.
109
- */
110
- export type AkanMobileTargetFiles = Record<string, string>;
85
+ export type NativeEnv = "local" | "debug" | "develop" | "main";
86
+ export type NativePermission = "camera" | "contacts" | "location" | "push" | "speech";
111
87
  export type AkanNativeValue = string | number | boolean | AkanNativeValue[] | {
112
88
  [key: string]: AkanNativeValue;
113
89
  };
114
- export interface AkanMobileNativeConfig {
115
- /** Native runtime plugins beyond the ones the permissions bring, by builtin id (`"iap"`) or absolute folder. */
116
- plugins?: string[];
117
- ios?: {
118
- infoPlist?: Record<string, AkanNativeValue>;
119
- entitlements?: Record<string, AkanNativeValue>;
120
- };
121
- android?: {
122
- /** XML at the `<manifest>` level; `${applicationId}` is replaced. */
123
- manifest?: string[];
124
- /** XML inside `<application>`. */
125
- application?: string[];
126
- /** XML inside the app's activity. */
127
- activity?: string[];
128
- /** The Firebase project's google-services.json, relative to the app folder, for FCM push on Android. */
129
- googleServices?: string;
130
- /** Media plays with sound without a tap first, as it does on iOS and the desktop: a signage screen. */
131
- autoplay?: boolean;
132
- };
133
- /** Android's notification channel, status bar icon (relative to the app folder) and accent color. */
134
- push?: AkanNativeConfig["push"];
135
- /** The app's part of the iOS privacy manifest (PrivacyInfo.xcprivacy), which an App Store upload requires. */
136
- privacy?: AkanNativeConfig["privacy"];
137
- desktop?: {
138
- /**
139
- * The desktop app carries the app's server (API only, database mode single, on loopback) and its pages call
140
- * nothing else. `build-desktop`, `start-desktop` and `publish-update` all read it, and an installed app refuses
141
- * a release that carries a server when it has none, or none when it has one.
142
- */
143
- server?: boolean;
144
- /**
145
- * `"reload"`: a window whose page's process ends (a crash, a hang) loads it again every time, waiting
146
- * longer after each end in a row, and the app relaunches when the webview's browser process ends — for an
147
- * app nobody attends. `"errorPage"` (default): one reload, then an error page, and a quit for the browser.
148
- */
149
- recovery?: "errorPage" | "reload";
150
- /** The main window from its first frame: borderless fullscreen, and no taskbar button (Windows, Linux). */
151
- window?: {
152
- fullscreen?: boolean;
153
- skipTaskbar?: boolean;
154
- };
155
- /**
156
- * Windows: `"auto"` answers getDisplayMedia() with the first screen at once, no picker or gesture — remote
157
- * support on an unattended screen; it covers every media request, so not for an app that asks for a camera.
158
- */
159
- screenCapture?: "picker" | "auto";
160
- };
90
+ /**
91
+ * One bundle id, or one per platform, for an app whose store listings already carry different ids. A platform without
92
+ * its own takes `default`.
93
+ */
94
+ export type AkanNativeAppId = string | {
95
+ default?: string;
96
+ ios?: string;
97
+ android?: string;
98
+ macos?: string;
99
+ windows?: string;
100
+ linux?: string;
101
+ };
102
+ export interface AkanNativeDeepLinks {
103
+ /** Custom URL schemes the app opens (`board://…`). */
104
+ schemes?: string[];
105
+ /** Hosts whose https links open the app: universal links (iOS, `ios.teamId`) and app links (Android). */
106
+ domains?: string[];
161
107
  }
162
108
  /**
163
109
  * Where an installed app looks for newer releases of itself (the native runtime's updates plugin): the whole app on a
164
110
  * desktop, the web bundle on a phone. `akan publish-update` writes and signs a release with a key on this machine;
165
111
  * `akan pack-update` writes an unsigned phone bundle for whoever holds the key to sign.
166
112
  */
167
- export interface AkanMobileUpdatesConfig {
113
+ export interface AkanNativeUpdatesConfig {
168
114
  /**
169
115
  * A static base URL, https in a release build: `<url>/<os>-<arch>/<channel>.json` (desktop) or `<url>/<platform>/…`
170
116
  * (a phone's web bundle).
@@ -180,46 +126,114 @@ export interface AkanMobileUpdatesConfig {
180
126
  /** How long a newly applied release has to call notifyReady() before it is rolled back, ms. Default 10000. */
181
127
  readyTimeout?: number;
182
128
  }
129
+ export interface AkanNativeIosConfig {
130
+ /** The Apple team id: the universal links of `deepLinks.domains` (apple-app-site-association). */
131
+ teamId?: string;
132
+ infoPlist?: Record<string, AkanNativeValue>;
133
+ entitlements?: Record<string, AkanNativeValue>;
134
+ /** The app's part of the iOS privacy manifest (PrivacyInfo.xcprivacy), which an App Store upload requires. */
135
+ privacy?: AkanNativeConfig["privacy"];
136
+ /** Files copied into the app bundle, keyed by their path there; the value is the source, relative to the app folder. */
137
+ files?: Record<string, string>;
138
+ }
139
+ export interface AkanNativeAndroidConfig {
140
+ /** The signing certificates' SHA-256 fingerprints: the app links of `deepLinks.domains` (assetlinks.json). */
141
+ sha256CertFingerprints?: string[];
142
+ /** The Firebase project's google-services.json, relative to the app folder, for FCM push. */
143
+ googleServices?: string;
144
+ /** The notification channel, status bar icon (relative to the app folder) and accent color of pushes. */
145
+ push?: NonNullable<AkanNativeConfig["push"]>["android"];
146
+ /** Media plays with sound without a tap first, as it does on iOS and the desktop: a signage screen. */
147
+ autoplay?: boolean;
148
+ /** XML at the `<manifest>` level; `${applicationId}` is replaced. */
149
+ manifest?: string[];
150
+ /** XML inside `<application>`. */
151
+ application?: string[];
152
+ /** XML inside the app's activity. */
153
+ activity?: string[];
154
+ /** Files copied into the app, keyed `res/<type>/<file>` or `assets/<path>`; the value is the source. */
155
+ files?: Record<string, string>;
156
+ }
157
+ export interface AkanNativeDesktopConfig {
158
+ /**
159
+ * The desktop app carries the app's server (API only, database mode single, on loopback) and its pages call
160
+ * nothing else. `build-desktop`, `start-desktop` and `publish-update` all read it, and an installed app refuses
161
+ * a release that carries a server when it has none, or none when it has one.
162
+ */
163
+ server?: boolean;
164
+ /**
165
+ * `"reload"`: a window whose page's process ends (a crash, a hang) loads it again every time, waiting
166
+ * longer after each end in a row, and the app relaunches when the webview's browser process ends — for an
167
+ * app nobody attends. `"errorPage"` (default): one reload, then an error page, and a quit for the browser.
168
+ */
169
+ recovery?: "errorPage" | "reload";
170
+ /** The main window from its first frame: borderless fullscreen, and no taskbar button (Windows, Linux). */
171
+ window?: {
172
+ fullscreen?: boolean;
173
+ skipTaskbar?: boolean;
174
+ };
175
+ /**
176
+ * Windows: `"auto"` answers getDisplayMedia() with the first screen at once, no picker or gesture — remote
177
+ * support on an unattended screen; it covers every media request, so not for an app that asks for a camera.
178
+ */
179
+ screenCapture?: "picker" | "auto";
180
+ }
183
181
  /**
184
- * One bundle id, or one per platform, for an app whose store listings already carry different ids. A platform without
185
- * its own takes `default`.
182
+ * What a native app (iOS, Android, desktop) is: the app's `native` section and each of its targets have this shape. A
183
+ * target takes the section and overrides it field by field: objects merge key by key, and any other value — a list
184
+ * included — replaces the section's.
186
185
  */
187
- export type AkanMobileAppId = string | {
188
- default?: string;
189
- ios?: string;
190
- android?: string;
191
- macos?: string;
192
- windows?: string;
193
- linux?: string;
194
- };
195
- export interface AkanMobileTargetConfig {
196
- name: string;
186
+ export interface AkanNativeSettings {
187
+ /** The client the app opens, a basePath the routes declare; an app without basePaths leaves it out. */
197
188
  basePath?: string;
189
+ /** Where the app starts, and falls back to for a deep link's stack and a back with no history. Default `/`. */
198
190
  indexPath?: string;
199
- appName: string;
200
- appId: AkanMobileAppId;
191
+ /** Default: the app's folder name. */
192
+ appName?: string;
193
+ /** Default: one made from the repository's and the app's names. */
194
+ appId?: AkanNativeAppId;
201
195
  /** Executables, archives and the Swift module; letters, digits, `.`, `_` and `-`. Default: the app's folder name. */
202
196
  fileName?: string;
197
+ /** Default `0.0.1`. */
198
+ version?: string;
199
+ /** Default 1. */
200
+ buildNum?: number;
201
+ /** A square PNG, relative to the app folder, or it with the color behind its transparent areas. */
202
+ icon?: string | {
203
+ image: string;
204
+ backgroundColor?: string;
205
+ };
206
+ /** A PNG shown centered at launch, or the launch screen's color, image and when it hides. */
207
+ splash?: string | NonNullable<AkanNativeConfig["splash"]>;
208
+ permissions?: NativePermission[];
209
+ /** Native runtime plugins beyond the ones the permissions bring: a builtin id (`"iap"`) or a folder, from the app's. */
210
+ plugins?: string[];
211
+ deepLinks?: AkanNativeDeepLinks;
212
+ updates?: Partial<AkanNativeUpdatesConfig>;
213
+ ios?: AkanNativeIosConfig;
214
+ android?: AkanNativeAndroidConfig;
215
+ desktop?: AkanNativeDesktopConfig;
216
+ }
217
+ /** `native` in akan.config.ts. Without `targets` the app has one target, named `default`. */
218
+ export interface AkanNativeAppConfig extends AkanNativeSettings {
219
+ targets?: Record<string, AkanNativeSettings>;
220
+ }
221
+ /** One target of the app, its fields over the app's `native` section and the defaults. */
222
+ export interface AkanNativeTarget extends Omit<AkanNativeSettings, "appName" | "appId" | "version" | "buildNum" | "updates"> {
223
+ name: string;
224
+ appName: string;
225
+ appId: AkanNativeAppId;
203
226
  version: string;
204
227
  buildNum: number;
205
- assets?: AkanMobileTargetAssets;
206
- permissions?: MobilePermission[];
207
- deepLinks?: AkanMobileTargetDeepLinks;
208
- files?: AkanMobileTargetFiles;
209
- native?: AkanMobileNativeConfig;
210
- /** Over mobile.updates, field by field: a pilot target names its own channel. */
211
- updates?: Partial<AkanMobileUpdatesConfig>;
212
- }
213
- export interface AkanMobileConfig {
228
+ updates?: AkanNativeUpdatesConfig;
229
+ }
230
+ export interface AkanNativeAppResult {
214
231
  appName: string;
215
- appId: AkanMobileAppId;
232
+ appId: AkanNativeAppId;
216
233
  fileName?: string;
217
234
  version: string;
218
235
  buildNum: number;
219
- files?: AkanMobileTargetFiles;
220
- native?: AkanMobileNativeConfig;
221
- updates?: AkanMobileUpdatesConfig;
222
- targets: Record<string, AkanMobileTargetConfig>;
236
+ targets: Record<string, AkanNativeTarget>;
223
237
  }
224
238
  export interface AkanScanInfo {
225
239
  /** Transitive lib dependencies of the app (or direct deps of a lib). */
@@ -265,9 +279,9 @@ export interface AkanSyncContext {
265
279
  /** Resolves `env/env.client.ts` and returns its exported `env`, or null when absent/invalid. */
266
280
  readEnvClient(): Promise<Record<string, unknown> | null>;
267
281
  }
268
- /** What a plugin adds to a mobile app whose target asks for its permission; the native build merges every one. */
282
+ /** What a plugin adds to a native app whose target asks for its permission; the native build merges every one. */
269
283
  export interface AkanPluginNativeConfig {
270
- permission: MobilePermission;
284
+ permission: NativePermission;
271
285
  /** Native runtime plugins, by builtin id (`"camera"`). */
272
286
  plugins?: string[];
273
287
  /** iOS usage texts by description name (`cameraUsageDescription`); `$(PRODUCT_NAME)` becomes the app name. */
@@ -325,7 +339,7 @@ export interface AppConfigResult {
325
339
  images: AkanImageConfig;
326
340
  i18n: AkanI18nConfig;
327
341
  publicEnv: string[];
328
- mobile: AkanMobileConfig;
342
+ native: AkanNativeAppResult;
329
343
  secrets: string[];
330
344
  assets: AkanAssetsConfig;
331
345
  }
@@ -350,10 +364,11 @@ export interface LibConfigContext {
350
364
  readonly name: string;
351
365
  readonly type: "lib";
352
366
  }
353
- export type AppConfigInput = Omit<DeepPartial<AppConfigResult>, "docker" | "web" | "bin"> & {
367
+ export type AppConfigInput = Omit<DeepPartial<AppConfigResult>, "docker" | "web" | "bin" | "native"> & {
354
368
  docker?: DockerOption;
355
369
  web?: AkanWebOption;
356
370
  bin?: AkanBinConfig;
371
+ native?: AkanNativeAppConfig;
357
372
  plugins?: AkanPlugin[];
358
373
  };
359
374
  export type LibConfigInput = Omit<DeepPartial<LibConfigResult>, "bin"> & {