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 +5 -4
- package/README.md +5 -4
- package/index.ts +114 -96
- package/package.json +1 -1
- package/types/index.d.ts +121 -106
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
103
|
-
export type
|
|
102
|
+
export type NativeEnv = "local" | "debug" | "develop" | "main";
|
|
103
|
+
export type NativePermission = "camera" | "contacts" | "location" | "push" | "speech";
|
|
104
104
|
|
|
105
|
-
export
|
|
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
|
-
*
|
|
125
|
-
*
|
|
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
|
|
128
|
-
|
|
129
|
-
|
|
111
|
+
export type AkanNativeAppId =
|
|
112
|
+
| string
|
|
113
|
+
| { default?: string; ios?: string; android?: string; macos?: string; windows?: string; linux?: string };
|
|
130
114
|
|
|
131
|
-
export interface
|
|
132
|
-
/**
|
|
133
|
-
|
|
134
|
-
ios
|
|
135
|
-
|
|
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
|
|
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
|
-
*
|
|
201
|
-
*
|
|
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
|
|
204
|
-
|
|
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
|
-
|
|
212
|
-
|
|
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
|
-
|
|
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
|
|
246
|
+
export interface AkanNativeAppResult {
|
|
227
247
|
appName: string;
|
|
228
|
-
appId:
|
|
248
|
+
appId: AkanNativeAppId;
|
|
229
249
|
fileName?: string;
|
|
230
250
|
version: string;
|
|
231
251
|
buildNum: number;
|
|
232
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
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
|
|
86
|
-
export type
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
android?:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
|
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
|
-
*
|
|
185
|
-
*
|
|
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
|
|
188
|
-
|
|
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
|
-
|
|
200
|
-
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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:
|
|
232
|
+
appId: AkanNativeAppId;
|
|
216
233
|
fileName?: string;
|
|
217
234
|
version: string;
|
|
218
235
|
buildNum: number;
|
|
219
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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"> & {
|