akanjs 2.0.0-rc.7 → 2.0.0
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/base/primitiveRegistry.ts +28 -2
- package/cli/application/application.command.ts +11 -3
- package/cli/application/application.runner.ts +17 -1
- package/cli/guidelines/databaseModule/databaseModule.instruction.md +1 -1
- package/cli/guidelines/modelConstant/modelConstant.instruction.md +5 -5
- package/cli/guidelines/modelDocument/modelDocument.instruction.md +34 -61
- package/cli/guidelines/modelService/modelService.instruction.md +1 -1
- package/cli/index.js +9321 -19222
- package/cli/library/library.runner.ts +14 -13
- package/cli/package/package.runner.ts +31 -6
- package/cli/package/package.script.ts +2 -2
- package/cli/templates/app/page/_index.tsx +200 -79
- package/cli/templates/app/page/_layout.tsx +0 -1
- package/cli/templates/app/public/favicon.ico.template +0 -0
- package/cli/templates/app/public/logo.png.template +0 -0
- package/cli/templates/module/__Model__.Zone.tsx +1 -1
- package/cli/templates/module/__model__.document.ts +1 -1
- package/cli/templates/workspaceRoot/.gitignore.template +1 -11
- package/cli/templates/workspaceRoot/biome.json.template +16 -0
- package/cli/templates/workspaceRoot/package.json.template +1 -5
- package/cli/workspace/workspace.command.ts +7 -9
- package/cli/workspace/workspace.runner.ts +3 -13
- package/cli/workspace/workspace.script.ts +24 -9
- package/client/csrTypes.ts +1 -1
- package/constant/fieldInfo.ts +1 -1
- package/constant/serialize.ts +7 -1
- package/devkit/capacitor.base.config.ts +1 -1
- package/devkit/capacitorApp.ts +5 -1
- package/devkit/commandDecorators/argMeta.ts +28 -14
- package/devkit/commandDecorators/command.ts +41 -15
- package/devkit/commandDecorators/commandBuilder.ts +78 -42
- package/devkit/commandDecorators/helpFormatter.ts +7 -4
- package/devkit/dependencyScanner.ts +121 -15
- package/devkit/executors.ts +35 -23
- package/devkit/frontendBuild/cssCompiler.ts +9 -3
- package/devkit/incrementalBuilder/incrementalBuilder.proc.ts +2 -1
- package/devkit/lint/no-deep-internal-import.grit +25 -0
- package/devkit/lint/no-import-external-library.grit +1 -0
- package/devkit/mobile/mobileTarget.ts +48 -8
- package/devkit/scanInfo.ts +4 -1
- package/devkit/src/capacitorApp.ts +277 -0
- package/devkit/transforms/barrelImportsPlugin.ts +6 -0
- package/fetch/client/fetchClient.ts +1 -0
- package/fetch/client/httpClient.ts +13 -1
- package/package.json +37 -31
- package/server/akanServer.ts +21 -7
- package/server/hmr/clientScript.ts +8 -5
- package/server/resolver/resolver.contract.fixture.ts +1 -1
- package/test/index.ts +14 -0
- package/test/signalTest.preload.ts +10 -0
- package/test/signalTestRuntime.ts +126 -0
- package/test/testServer.ts +130 -25
- package/ui/Constant/Doc.tsx +696 -0
- package/ui/Constant/Mermaid.tsx +149 -0
- package/ui/Constant/index.ts +6 -0
- package/ui/Constant/schemaDoc.ts +324 -0
- package/ui/Field.tsx +0 -1
- package/ui/Portal.tsx +2 -0
- package/ui/System/CSR.tsx +6 -5
- package/ui/System/SSR.tsx +1 -1
- package/ui/System/SelectLanguage.tsx +1 -1
- package/ui/index.ts +1 -0
- package/ui/styles.css +0 -1
- package/webkit/bootCsr.tsx +8 -5
- package/base/test-globals.d.ts +0 -4
- package/cli/templates/app/common/commonLogic.ts +0 -12
- package/cli/templates/app/common/index.ts +0 -10
- package/cli/templates/app/public/favicon.ico +0 -0
- package/cli/templates/app/public/icons/icon-128x128.png +0 -0
- package/cli/templates/app/public/icons/icon-144x144.png +0 -0
- package/cli/templates/app/public/icons/icon-152x152.png +0 -0
- package/cli/templates/app/public/icons/icon-192x192.png +0 -0
- package/cli/templates/app/public/icons/icon-256x256.png +0 -0
- package/cli/templates/app/public/icons/icon-384x384.png +0 -0
- package/cli/templates/app/public/icons/icon-48x48.png +0 -0
- package/cli/templates/app/public/icons/icon-512x512.png +0 -0
- package/cli/templates/app/public/icons/icon-72x72.png +0 -0
- package/cli/templates/app/public/icons/icon-96x96.png +0 -0
- package/cli/templates/app/public/logo.svg +0 -70
- package/cli/templates/app/public/manifest.json.template +0 -67
- package/cli/templates/app/srvkit/backendLogic.ts +0 -12
- package/cli/templates/app/srvkit/index.ts +0 -10
- package/cli/templates/app/ui/UiComponent.ts +0 -16
- package/cli/templates/app/ui/index.ts +0 -10
- package/cli/templates/app/webkit/frontendLogic.ts +0 -12
- package/cli/templates/app/webkit/index.ts +0 -10
- package/cli/templates/module/index.tsx +0 -44
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import type { CapacitorConfig } from "@capacitor/cli";
|
|
2
|
+
import { MobileProject } from "@trapezedev/project";
|
|
3
|
+
import type { AndroidProject } from "@trapezedev/project/dist/android/project";
|
|
4
|
+
import type { IosProject } from "@trapezedev/project/dist/ios/project";
|
|
5
|
+
import { capitalize } from "akanjs/common";
|
|
6
|
+
import { type AppExecutor, FileSys } from "akanjs/devkit";
|
|
7
|
+
|
|
8
|
+
import { FileEditor } from "./fileEditor";
|
|
9
|
+
|
|
10
|
+
interface RunConfig extends CapacitorConfig {
|
|
11
|
+
operation: "local" | "release";
|
|
12
|
+
version: string;
|
|
13
|
+
buildNum: number;
|
|
14
|
+
appId?: string;
|
|
15
|
+
host?: "local" | "debug" | "develop" | "main";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class CapacitorApp {
|
|
19
|
+
project: MobileProject & { ios: IosProject; android: AndroidProject };
|
|
20
|
+
iosTargetName = "App";
|
|
21
|
+
constructor(private readonly app: AppExecutor) {
|
|
22
|
+
this.project = new MobileProject(this.app.cwdPath, {
|
|
23
|
+
android: { path: "android" },
|
|
24
|
+
ios: { path: "ios/App" },
|
|
25
|
+
}) as MobileProject & { ios: IosProject; android: AndroidProject };
|
|
26
|
+
}
|
|
27
|
+
async init() {
|
|
28
|
+
const project = this.project as MobileProject;
|
|
29
|
+
await this.project.load();
|
|
30
|
+
const hasAndroid = await FileSys.fileExists(`${this.app.cwdPath}/android/app/build.gradle`);
|
|
31
|
+
const hasIos = await FileSys.fileExists(`${this.app.cwdPath}/ios/App/App.xcodeproj/project.pbxproj`);
|
|
32
|
+
if (!project.android && !hasAndroid) {
|
|
33
|
+
await this.app.spawn("npx", ["cap", "add", "android"]);
|
|
34
|
+
await this.project.load();
|
|
35
|
+
}
|
|
36
|
+
if (!project.ios && !hasIos) {
|
|
37
|
+
await this.app.spawn("npx", ["cap", "add", "ios"]);
|
|
38
|
+
await this.project.load();
|
|
39
|
+
}
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
async save() {
|
|
43
|
+
await this.project.commit();
|
|
44
|
+
}
|
|
45
|
+
async #prepareIos() {
|
|
46
|
+
const isAdded = await FileSys.fileExists(`${this.app.cwdPath}/ios/App/App.xcodeproj/project.pbxproj`);
|
|
47
|
+
if (!isAdded) {
|
|
48
|
+
await this.app.spawn("npx", ["cap", "add", "ios"]);
|
|
49
|
+
await this.app.spawn("npx", ["@capacitor/assets", "generate"]);
|
|
50
|
+
} else this.app.verbose(`iOS already added, skip adding process`);
|
|
51
|
+
this.app.verbose(`syncing iOS`);
|
|
52
|
+
await this.app.spawn("npx", ["cap", "sync", "ios"]);
|
|
53
|
+
this.app.verbose(`sync completed.`);
|
|
54
|
+
}
|
|
55
|
+
async buildIos() {
|
|
56
|
+
await this.#prepareIos();
|
|
57
|
+
this.app.verbose(`build completed iOS.`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
async syncIos() {
|
|
61
|
+
await this.app.spawn("npx", ["cap", "sync", "ios"]);
|
|
62
|
+
}
|
|
63
|
+
async openIos() {
|
|
64
|
+
await this.app.spawn("npx", ["cap", "open", "ios"]);
|
|
65
|
+
}
|
|
66
|
+
async runIos({ operation, appId, version = "0.0.1", buildNum = 1, host = "local" }: RunConfig) {
|
|
67
|
+
const defaultAppId = `com.${this.app.name}.app`;
|
|
68
|
+
await this.#prepareIos();
|
|
69
|
+
this.project.ios.setBundleId("App", "Debug", appId ?? defaultAppId);
|
|
70
|
+
this.project.ios.setBundleId("App", "Release", appId ?? defaultAppId);
|
|
71
|
+
await this.project.ios.setVersion("App", "Debug", version);
|
|
72
|
+
await this.project.ios.setVersion("App", "Release", version);
|
|
73
|
+
await this.project.ios.setBuild("App", "Debug", buildNum);
|
|
74
|
+
await this.project.ios.setBuild("App", "Release", buildNum);
|
|
75
|
+
await this.project.commit();
|
|
76
|
+
await this.app.spawn(
|
|
77
|
+
"npx",
|
|
78
|
+
["cross-env", `APP_OPERATION_MODE=${operation}`, `BUN_PUBLIC_ENV=${host}`, "npx", "cap", "run", "ios"],
|
|
79
|
+
{
|
|
80
|
+
stdio: "inherit",
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async #prepareAndroid() {
|
|
87
|
+
const isAdded = await Bun.file(`${this.app.cwdPath}/android/app/build.gradle`).exists();
|
|
88
|
+
if (!isAdded) {
|
|
89
|
+
await this.app.spawn("npx", ["cap", "add", "android"]);
|
|
90
|
+
} else this.app.verbose(`Android already added, skip adding process`);
|
|
91
|
+
await this.app.spawn("npx", ["@capacitor/assets", "generate"]);
|
|
92
|
+
await this.app.spawn("npx", ["cap", "sync", "android"]);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async #updateAndroidBuildTypes() {
|
|
96
|
+
|
|
97
|
+
const appGradle = await FileEditor.create(`${this.app.cwdPath}/android/app/build.gradle`);
|
|
98
|
+
const buildTypesBlock = `
|
|
99
|
+
debug {
|
|
100
|
+
applicationIdSuffix ".debug"
|
|
101
|
+
versionNameSuffix "-DEBUG"
|
|
102
|
+
debuggable true
|
|
103
|
+
minifyEnabled false
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
const singinConfigBlock = `
|
|
107
|
+
signingConfigs {
|
|
108
|
+
debug {
|
|
109
|
+
storeFile file('debug.keystore')
|
|
110
|
+
storePassword 'android'
|
|
111
|
+
keyAlias 'androiddebugkey'
|
|
112
|
+
keyPassword 'android'
|
|
113
|
+
}
|
|
114
|
+
release {
|
|
115
|
+
if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
|
|
116
|
+
storeFile file(MYAPP_RELEASE_STORE_FILE)
|
|
117
|
+
storePassword MYAPP_RELEASE_STORE_PASSWORD
|
|
118
|
+
keyAlias MYAPP_RELEASE_KEY_ALIAS
|
|
119
|
+
keyPassword MYAPP_RELEASE_KEY_PASSWORD
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
`;
|
|
124
|
+
if (appGradle.find("signingConfigs {") === -1) {
|
|
125
|
+
appGradle.insertBefore("buildTypes {", singinConfigBlock);
|
|
126
|
+
}
|
|
127
|
+
if (appGradle.find(`applicationIdSuffix ".debug"`) === -1) {
|
|
128
|
+
appGradle.insertAfter("buildTypes {", buildTypesBlock);
|
|
129
|
+
}
|
|
130
|
+
await appGradle.save();
|
|
131
|
+
}
|
|
132
|
+
async buildAndroid(assembleType: "apk" | "aab") {
|
|
133
|
+
await this.#prepareAndroid();
|
|
134
|
+
await this.#updateAndroidBuildTypes();
|
|
135
|
+
|
|
136
|
+
const isWindows = process.platform === "win32";
|
|
137
|
+
const gradleCommand = isWindows ? "gradlew.bat" : "./gradlew";
|
|
138
|
+
|
|
139
|
+
await this.app.spawn(gradleCommand, [assembleType === "apk" ? "assembleRelease" : "bundleRelease"], {
|
|
140
|
+
stdio: "inherit",
|
|
141
|
+
cwd: `${this.app.cwdPath}/android`,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async openAndroid() {
|
|
145
|
+
await this.app.spawn("npx", ["cap", "open", "android"]);
|
|
146
|
+
}
|
|
147
|
+
async syncAndroid() {
|
|
148
|
+
await this.#prepareAndroid();
|
|
149
|
+
this.app.log(`Sync Android Completed.`);
|
|
150
|
+
}
|
|
151
|
+
async runAndroid({ operation, appName, appId, version = "0.0.1", buildNum = 1, host = "local" }: RunConfig) {
|
|
152
|
+
const defaultAppId = `com.${this.app.name}.app`;
|
|
153
|
+
const defaultAppName = this.app.name;
|
|
154
|
+
await this.project.android.setVersionName(version);
|
|
155
|
+
await this.project.android.setPackageName(appId ?? defaultAppId);
|
|
156
|
+
await this.project.android.setVersionCode(buildNum);
|
|
157
|
+
const versionName = await this.project.android.getVersionName();
|
|
158
|
+
const versionCode = await this.project.android.getVersionCode();
|
|
159
|
+
await this.project.android.setAppName(appName ?? defaultAppName);
|
|
160
|
+
await this.project.commit();
|
|
161
|
+
await this.#prepareAndroid();
|
|
162
|
+
|
|
163
|
+
this.app.logger.info(`Running Android in ${operation} mode on ${host} host`);
|
|
164
|
+
await this.app.spawn(
|
|
165
|
+
"npx",
|
|
166
|
+
["cross-env", `BUN_PUBLIC_ENV=${host}`, `APP_OPERATION_MODE=${operation}`, "npx", "cap", "run", "android"],
|
|
167
|
+
{
|
|
168
|
+
stdio: "inherit",
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async updateAndroidVersion(version: string, buildNum: number) {
|
|
174
|
+
|
|
175
|
+
await this.project.android.setVersionName(version);
|
|
176
|
+
await this.project.android.setVersionCode(buildNum);
|
|
177
|
+
const versionName = await this.project.android.getVersionName();
|
|
178
|
+
const versionCode = await this.project.android.getVersionCode();
|
|
179
|
+
await this.project.commit();
|
|
180
|
+
}
|
|
181
|
+
async releaseIos() {
|
|
182
|
+
|
|
183
|
+
const isAdded = await Bun.file(`${this.app.cwdPath}/ios/App/App.xcodeproj/project.pbxproj`).exists();
|
|
184
|
+
if (!isAdded) {
|
|
185
|
+
await this.app.spawn("npx cap add ios");
|
|
186
|
+
await this.app.spawn("npx @capacitor/assets generate");
|
|
187
|
+
} else this.app.log(`iOS already added, skip adding process`);
|
|
188
|
+
await this.app.spawn("cross-env", ["APP_OPERATION_MODE=release", "npx", "cap", "sync", "ios"]);
|
|
189
|
+
}
|
|
190
|
+
async releaseAndroid() {
|
|
191
|
+
|
|
192
|
+
const isAdded = await Bun.file(`${this.app.cwdPath}/android/app/build.gradle`).exists();
|
|
193
|
+
if (!isAdded) {
|
|
194
|
+
await this.app.spawn("npx cap add android");
|
|
195
|
+
await this.app.spawn("npx @capacitor/assets generate");
|
|
196
|
+
} else this.app.log(`android already added, skip adding process`);
|
|
197
|
+
await this.app.spawn("cross-env", ["APP_OPERATION_MODE=release", "npx", "cap", "sync", "android"]);
|
|
198
|
+
}
|
|
199
|
+
async addCamera() {
|
|
200
|
+
await this.#setPermissionInIos({
|
|
201
|
+
cameraUsageDescription: "$(PRODUCT_NAME) requires access to the camera to take photos.",
|
|
202
|
+
photoAddUsageDescription: "$(PRODUCT_NAME) requires access to the photo library to take photos.",
|
|
203
|
+
photoUsageDescription: "$(PRODUCT_NAME) requires access to the photo library to take photos.",
|
|
204
|
+
});
|
|
205
|
+
this.#setPermissionsInAndroid(["READ_MEDIA_IMAGES", "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE"]);
|
|
206
|
+
}
|
|
207
|
+
async addContact() {
|
|
208
|
+
await this.#setPermissionInIos({
|
|
209
|
+
contactsUsageDescription: "$(PRODUCT_NAME) requires access to the contacts to add new contacts.",
|
|
210
|
+
});
|
|
211
|
+
this.#setPermissionsInAndroid(["READ_CONTACTS", "WRITE_CONTACTS"]);
|
|
212
|
+
}
|
|
213
|
+
async addLocation() {
|
|
214
|
+
await this.#setPermissionInIos({
|
|
215
|
+
locationAlwaysUsageDescription: "$(PRODUCT_NAME) requires access to the location to get the user's location.",
|
|
216
|
+
locationWhenInUseUsageDescription: "$(PRODUCT_NAME) requires access to the location to get the user's location.",
|
|
217
|
+
});
|
|
218
|
+
this.#setPermissionsInAndroid(["ACCESS_COARSE_LOCATION", "ACCESS_FINE_LOCATION"]);
|
|
219
|
+
this.#setFeaturesInAndroid(["android.hardware.location.gps"]);
|
|
220
|
+
}
|
|
221
|
+
async #setPermissionInIos(permissions: { [key: string]: string }) {
|
|
222
|
+
const updateNs = Object.fromEntries(
|
|
223
|
+
Object.entries(permissions).map(([key, value]) => [`NS${capitalize(key)}`, value]),
|
|
224
|
+
);
|
|
225
|
+
await Promise.all([
|
|
226
|
+
this.project.ios.updateInfoPlist(this.iosTargetName, "Debug", updateNs),
|
|
227
|
+
this.project.ios.updateInfoPlist(this.iosTargetName, "Release", updateNs),
|
|
228
|
+
]);
|
|
229
|
+
}
|
|
230
|
+
#setFeaturesInAndroid(features: string[]) {
|
|
231
|
+
for (const feature of features) {
|
|
232
|
+
if (this.#hasFeatureInAndroid(feature)) {
|
|
233
|
+
this.app.logger.info(`${feature} already exists in android`);
|
|
234
|
+
return this;
|
|
235
|
+
}
|
|
236
|
+
this.app.logger.info(`Adding ${feature} to android`);
|
|
237
|
+
this.project.android
|
|
238
|
+
.getAndroidManifest()
|
|
239
|
+
.injectFragment("manifest", `<uses-feature android:name="${feature}" />`);
|
|
240
|
+
}
|
|
241
|
+
return this;
|
|
242
|
+
}
|
|
243
|
+
#getFeaturesInAndroid() {
|
|
244
|
+
const androidManifest = this.project.android.getAndroidManifest();
|
|
245
|
+
const element = androidManifest.getDocumentElement();
|
|
246
|
+
if (!element) throw new Error("manifest not found");
|
|
247
|
+
const usesFeature = element.getElementsByTagName("uses-feature");
|
|
248
|
+
return Array.from(usesFeature).map((feature) => feature.getAttribute("android:name"));
|
|
249
|
+
}
|
|
250
|
+
#hasFeatureInAndroid(feature: string) {
|
|
251
|
+
return this.#getFeaturesInAndroid().includes(feature);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
#setPermissionsInAndroid(permissions: string[]) {
|
|
255
|
+
for (const permission of permissions) {
|
|
256
|
+
if (this.#hasPermissionInAndroid(permission)) {
|
|
257
|
+
this.app.logger.info(`${permission} already exists in android`);
|
|
258
|
+
return this;
|
|
259
|
+
}
|
|
260
|
+
this.app.logger.info(`Adding ${permission} to android`);
|
|
261
|
+
this.project.android
|
|
262
|
+
.getAndroidManifest()
|
|
263
|
+
.injectFragment("manifest", `<uses-permission android:name="android.permission.${permission}" />`);
|
|
264
|
+
}
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
#getPermissionsInAndroid() {
|
|
268
|
+
const androidManifest = this.project.android.getAndroidManifest();
|
|
269
|
+
const element = androidManifest.getDocumentElement();
|
|
270
|
+
if (!element) throw new Error("manifest not found");
|
|
271
|
+
const usesPermission = element.getElementsByTagName("uses-permission");
|
|
272
|
+
return Array.from(usesPermission).map((permission) => permission.getAttribute("android:name"));
|
|
273
|
+
}
|
|
274
|
+
#hasPermissionInAndroid(permission: string) {
|
|
275
|
+
return this.#getPermissionsInAndroid().includes(permission);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
@@ -421,6 +421,10 @@ const rewriteSingleStatement = (stmt: ImportStatement, map: BarrelExportMap): st
|
|
|
421
421
|
|
|
422
422
|
const tail = ";";
|
|
423
423
|
|
|
424
|
+
if (shouldPreserveBarrelSideEffects(stmt.specifier)) {
|
|
425
|
+
lines.push(`import "${stmt.specifier}"${tail}`);
|
|
426
|
+
}
|
|
427
|
+
|
|
424
428
|
if (clause.defaultImport || remaining.length > 0) {
|
|
425
429
|
const parts: string[] = [];
|
|
426
430
|
if (clause.defaultImport) parts.push(clause.defaultImport);
|
|
@@ -437,6 +441,8 @@ const rewriteSingleStatement = (stmt: ImportStatement, map: BarrelExportMap): st
|
|
|
437
441
|
return lines.join("\n");
|
|
438
442
|
};
|
|
439
443
|
|
|
444
|
+
const shouldPreserveBarrelSideEffects = (specifier: string): boolean => /^@(apps|libs)\/[^/]+\/client$/.test(specifier);
|
|
445
|
+
|
|
440
446
|
const serializeNamedItem = (item: NamedImportItem): string => {
|
|
441
447
|
const prefix = item.isType ? "type " : "";
|
|
442
448
|
if (item.imported === item.local) return `${prefix}${item.imported}`;
|
|
@@ -495,6 +495,7 @@ export class FetchClient {
|
|
|
495
495
|
args.forEach((arg) => {
|
|
496
496
|
if (arg.type === "param") paramArgs.push(arg);
|
|
497
497
|
else if (arg.type === "search") searchArgs.push(arg);
|
|
498
|
+
else if (arg.type === "body" && arg.refName === "Upload") uploadArgs.push(arg);
|
|
498
499
|
else if (arg.type === "body") bodyArgs.push(arg);
|
|
499
500
|
else if (arg.type === "upload") uploadArgs.push(arg);
|
|
500
501
|
});
|
|
@@ -126,9 +126,21 @@ export class HttpClient {
|
|
|
126
126
|
const formData = new FormData();
|
|
127
127
|
uploadArgs.forEach((arg) => {
|
|
128
128
|
const argValue = argMap.get(arg.name);
|
|
129
|
+
if (arg.nullable && (argValue === null || argValue === undefined)) return;
|
|
129
130
|
if (!arg.nullable && (argValue === null || argValue === undefined))
|
|
130
131
|
throw new Error(`Argument ${arg.name} is required`);
|
|
131
|
-
|
|
132
|
+
if (Array.isArray(argValue)) {
|
|
133
|
+
argValue.forEach((value) => {
|
|
134
|
+
formData.append(arg.name, value as Blob | string);
|
|
135
|
+
});
|
|
136
|
+
} else formData.append(arg.name, argValue as Blob | string);
|
|
137
|
+
});
|
|
138
|
+
bodyArgs.forEach((arg) => {
|
|
139
|
+
const argValue = argMap.get(arg.name);
|
|
140
|
+
if (arg.nullable && (argValue === null || argValue === undefined)) return;
|
|
141
|
+
if (!arg.nullable && (argValue === null || argValue === undefined))
|
|
142
|
+
throw new Error(`Argument ${arg.name} is required`);
|
|
143
|
+
formData.append(arg.name, typeof argValue === "string" ? argValue : JSON.stringify(argValue));
|
|
132
144
|
});
|
|
133
145
|
return formData;
|
|
134
146
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akanjs",
|
|
3
|
-
"version": "2.0.0
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -120,55 +120,62 @@
|
|
|
120
120
|
}
|
|
121
121
|
},
|
|
122
122
|
"dependencies": {
|
|
123
|
-
"@capacitor-community/contacts": "^7.
|
|
123
|
+
"@capacitor-community/contacts": "^7.2.0",
|
|
124
124
|
"@capacitor-community/fcm": "^8.1.0",
|
|
125
125
|
"@capacitor/app": "^8.1.0",
|
|
126
126
|
"@capacitor/browser": "^8.0.3",
|
|
127
|
-
"@capacitor/camera": "^8.0
|
|
127
|
+
"@capacitor/camera": "^8.2.0",
|
|
128
|
+
"@capacitor/core": "^8.3.4",
|
|
128
129
|
"@capacitor/device": "^8.0.2",
|
|
129
|
-
"@capacitor/geolocation": "^8.
|
|
130
|
+
"@capacitor/geolocation": "^8.2.0",
|
|
130
131
|
"@capacitor/haptics": "^8.0.2",
|
|
131
|
-
"@capacitor/keyboard": "^8.0.
|
|
132
|
+
"@capacitor/keyboard": "^8.0.3",
|
|
132
133
|
"@capacitor/preferences": "^8.0.1",
|
|
133
|
-
"@capacitor/push-notifications": "^8.
|
|
134
|
-
"@capgo/capacitor-updater": "^8.
|
|
134
|
+
"@capacitor/push-notifications": "^8.1.1",
|
|
135
|
+
"@capgo/capacitor-updater": "^8.46.1",
|
|
135
136
|
"@formatjs/intl-localematcher": "^0.8.8",
|
|
136
|
-
"@inquirer/prompts": "^8.3
|
|
137
|
-
"@langchain/core": "^1.1.
|
|
138
|
-
"@langchain/deepseek": "^1.0.
|
|
139
|
-
"@langchain/openai": "^1.4.
|
|
137
|
+
"@inquirer/prompts": "^8.4.3",
|
|
138
|
+
"@langchain/core": "^1.1.47",
|
|
139
|
+
"@langchain/deepseek": "^1.0.26",
|
|
140
|
+
"@langchain/openai": "^1.4.6",
|
|
140
141
|
"@libsql/client": "^0.17.3",
|
|
141
|
-
"@playwright/test": "^1.
|
|
142
|
+
"@playwright/test": "^1.60.0",
|
|
142
143
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
143
144
|
"@react-spring/web": "^9.7.5",
|
|
144
|
-
"@trapezedev/project": "^7.1.
|
|
145
|
+
"@trapezedev/project": "^7.1.4",
|
|
145
146
|
"@use-gesture/react": "^10.3.1",
|
|
146
|
-
"bullmq": "^5.
|
|
147
|
+
"bullmq": "^5.76.10",
|
|
147
148
|
"capacitor-plugin-safe-area": "^5.0.0",
|
|
148
149
|
"chalk": "^5.6.2",
|
|
149
150
|
"chance": "^1.1.13",
|
|
150
151
|
"clsx": "^2.1.1",
|
|
151
152
|
"commander": "^14.0.3",
|
|
152
|
-
"
|
|
153
|
+
"compare-versions": "^6.1.1",
|
|
154
|
+
"cordova-plugin-purchase": "^13.16.0",
|
|
153
155
|
"croner": "^10.0.1",
|
|
156
|
+
"daisyui": "^5.5.20",
|
|
154
157
|
"dataloader": "^2.2.3",
|
|
155
|
-
"dayjs": "^1.11.
|
|
158
|
+
"dayjs": "^1.11.20",
|
|
156
159
|
"file-saver": "^2.0.5",
|
|
157
160
|
"fontaine": "^0.8.0",
|
|
158
161
|
"fonteditor-core": "^2.6.3",
|
|
159
162
|
"ignore": "^7.0.5",
|
|
160
|
-
"immer": "^11.1.
|
|
163
|
+
"immer": "^11.1.8",
|
|
161
164
|
"ink": "^6.8.0",
|
|
162
|
-
"ioredis": "^5.
|
|
163
|
-
"js-cookie": "^3.0.
|
|
165
|
+
"ioredis": "^5.10.1",
|
|
166
|
+
"js-cookie": "^3.0.7",
|
|
164
167
|
"js-yaml": "^4.1.1",
|
|
165
168
|
"jwt-decode": "^4.0.0",
|
|
166
|
-
"
|
|
169
|
+
"latest-version": "^9.0.0",
|
|
170
|
+
"lodash": "^4.18.1",
|
|
171
|
+
"mermaid": "^11.15.0",
|
|
167
172
|
"negotiator": "^1.0.0",
|
|
168
|
-
"
|
|
173
|
+
"open": "^11.0.0",
|
|
174
|
+
"ora": "^9.4.0",
|
|
169
175
|
"pluralize": "^8.0.0",
|
|
170
176
|
"postgres": "^3.4.9",
|
|
171
|
-
"protobufjs": "^8.
|
|
177
|
+
"protobufjs": "^8.4.0",
|
|
178
|
+
"qrcode": "^1.5.4",
|
|
172
179
|
"react": "19.2.4",
|
|
173
180
|
"react-copy-to-clipboard": "^5.1.1",
|
|
174
181
|
"react-datepicker": "^9.1.0",
|
|
@@ -176,22 +183,21 @@
|
|
|
176
183
|
"react-dom": "19.2.4",
|
|
177
184
|
"react-icons": "^5.6.0",
|
|
178
185
|
"react-refresh": "^0.18.0",
|
|
179
|
-
"react-server-dom-webpack": "^19.2.
|
|
186
|
+
"react-server-dom-webpack": "^19.2.6",
|
|
180
187
|
"react-simple-pull-to-refresh": "^1.3.4",
|
|
181
188
|
"react-spring": "^9.7.5",
|
|
189
|
+
"scheduler": "^0.27.0",
|
|
182
190
|
"sharp": "^0.34.5",
|
|
183
191
|
"ssh2": "^1.17.0",
|
|
184
192
|
"subset-font": "^2.5.0",
|
|
185
|
-
"
|
|
186
|
-
"
|
|
193
|
+
"tailwind-scrollbar": "^4.0.2",
|
|
194
|
+
"tailwindcss": "^4.3.0",
|
|
195
|
+
"typescript": "^6.0.3",
|
|
196
|
+
"uuid": "^13.0.2"
|
|
187
197
|
},
|
|
188
198
|
"devDependencies": {
|
|
189
|
-
"@
|
|
190
|
-
"@
|
|
191
|
-
"bullmq": "^5.69.3",
|
|
192
|
-
"dataloader": "^2.2.3",
|
|
193
|
-
"ioredis": "^5.9.3",
|
|
194
|
-
"react-spring": "^9.7.5"
|
|
199
|
+
"@biomejs/biome": "2.4.4",
|
|
200
|
+
"@capacitor/cli": "^8.3.4"
|
|
195
201
|
},
|
|
196
202
|
"bun": {
|
|
197
203
|
"platform": "bun"
|
package/server/akanServer.ts
CHANGED
|
@@ -35,7 +35,7 @@ interface AkanAppPrepared {
|
|
|
35
35
|
renderEnvRoutes: HttpRoutes;
|
|
36
36
|
hmrHub: HmrWsHub | null;
|
|
37
37
|
builderRpc: BuilderRpc | null;
|
|
38
|
-
webRouter: WebRouter;
|
|
38
|
+
webRouter: WebRouter | null;
|
|
39
39
|
webProxyRunner: WebProxyRunner | null;
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -120,7 +120,7 @@ export class AkanServer {
|
|
|
120
120
|
return this.#di.getAdaptor<T>(refName);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
async init({ routes: initRoutes = true }: { routes?: boolean } = {}) {
|
|
123
|
+
async init({ routes: initRoutes = true, web = true }: { routes?: boolean; web?: boolean } = {}) {
|
|
124
124
|
if (this.status !== "stopped") throw new Error("AkanServer is not able to init. It is already running.");
|
|
125
125
|
this.status = "initializing";
|
|
126
126
|
const { routes, wsRoutes, routeOptions } = await this.#di.initializeAll();
|
|
@@ -129,6 +129,20 @@ export class AkanServer {
|
|
|
129
129
|
this.status = "initialized";
|
|
130
130
|
return this;
|
|
131
131
|
}
|
|
132
|
+
if (!web) {
|
|
133
|
+
this.#prepared = {
|
|
134
|
+
routes,
|
|
135
|
+
routeOptions,
|
|
136
|
+
wsRoutes,
|
|
137
|
+
renderEnvRoutes: {},
|
|
138
|
+
hmrHub: null,
|
|
139
|
+
builderRpc: null,
|
|
140
|
+
webRouter: null,
|
|
141
|
+
webProxyRunner: null,
|
|
142
|
+
};
|
|
143
|
+
this.status = "initialized";
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
132
146
|
const { WebRouter } = await import("./webRouter");
|
|
133
147
|
const webRouter = await WebRouter.create({
|
|
134
148
|
upgradeHmrWs: (req, data) => this.#server?.upgrade(req, { data }) ?? false,
|
|
@@ -156,7 +170,7 @@ export class AkanServer {
|
|
|
156
170
|
wsRoutes,
|
|
157
171
|
registry: this.#di.registry,
|
|
158
172
|
hmrHub,
|
|
159
|
-
hmrState: { state: webRouter.renderState },
|
|
173
|
+
hmrState: webRouter ? { state: webRouter.renderState } : null,
|
|
160
174
|
logger: this.logger,
|
|
161
175
|
}),
|
|
162
176
|
|
|
@@ -233,10 +247,10 @@ export class AkanServer {
|
|
|
233
247
|
return this;
|
|
234
248
|
}
|
|
235
249
|
|
|
236
|
-
async start({ listen }: { listen?: boolean } = {}) {
|
|
250
|
+
async start({ listen, web = true }: { listen?: boolean; web?: boolean } = {}) {
|
|
237
251
|
const isScriptCommand = process.env.AKAN_COMMAND_TYPE === "script";
|
|
238
252
|
const shouldListen = (listen ?? !isScriptCommand) && this.serverMode !== "batch";
|
|
239
|
-
await this.init({ routes: shouldListen });
|
|
253
|
+
await this.init({ routes: shouldListen, web });
|
|
240
254
|
if (!shouldListen) {
|
|
241
255
|
const websocket = this.#di.getWebsocketAdaptor();
|
|
242
256
|
if (websocket) SignalResolver.setLocalPublish((roomId, data) => this.#localPublish?.(roomId, data), websocket);
|
|
@@ -276,7 +290,7 @@ export class AkanServer {
|
|
|
276
290
|
this.#server = null;
|
|
277
291
|
this.#wsServer = null;
|
|
278
292
|
|
|
279
|
-
this.#prepared?.webRouter
|
|
293
|
+
this.#prepared?.webRouter?.dispose();
|
|
280
294
|
await this.#withShutdownTimeout(this.#di.destroyAll());
|
|
281
295
|
this.#prepared = null;
|
|
282
296
|
this.status = "stopped";
|
|
@@ -313,7 +327,7 @@ export class AkanServer {
|
|
|
313
327
|
async #reportMetrics() {
|
|
314
328
|
const metrics = await ProcessMetricsCollector.collect({
|
|
315
329
|
role: this.serverMode,
|
|
316
|
-
...(this.#prepared?.webRouter
|
|
330
|
+
...(this.#prepared?.webRouter?.getMetrics() ?? {}),
|
|
317
331
|
});
|
|
318
332
|
process.send?.({ type: "metrics.report", pid: process.pid, metrics } satisfies AkanIpcMessage);
|
|
319
333
|
if (process.env.AKAN_MEMORY_LOG === "1") {
|
|
@@ -45,11 +45,11 @@ export const HMR_CLIENT_SCRIPT = `(function(){
|
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
if (msg.type === "rsc-refresh") {
|
|
48
|
-
|
|
48
|
+
reloadForHmr(msg);
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
if (msg.type === "client-refresh") {
|
|
52
|
-
|
|
52
|
+
reloadForHmr(msg);
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
55
|
if (msg.type === "css-update") {
|
|
@@ -73,6 +73,12 @@ export const HMR_CLIENT_SCRIPT = `(function(){
|
|
|
73
73
|
setTimeout(connect, delay);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
function reloadForHmr(msg){
|
|
77
|
+
try { self.__AKAN_RSC_CLEAR_CACHE__ && self.__AKAN_RSC_CLEAR_CACHE__(); } catch(e){}
|
|
78
|
+
if (msg && msg.buildId != null) lastBuildId = msg.buildId;
|
|
79
|
+
location.reload();
|
|
80
|
+
}
|
|
81
|
+
|
|
76
82
|
function ensureOverlay(){
|
|
77
83
|
if (overlayEl && overlayLabelEl) return overlayEl;
|
|
78
84
|
if (!overlayStyleEl) {
|
|
@@ -303,8 +309,5 @@ export const HMR_CLIENT_SCRIPT = `(function(){
|
|
|
303
309
|
return cssAssets[""] && cssAssets[""].cssUrl ? cssAssets[""].cssUrl : null;
|
|
304
310
|
}
|
|
305
311
|
|
|
306
|
-
ensureRefreshRuntime().catch(function(err){
|
|
307
|
-
console.warn("[akan-hmr] React Refresh runtime preload failed", err);
|
|
308
|
-
});
|
|
309
312
|
connect();
|
|
310
313
|
})();`;
|
|
@@ -31,7 +31,7 @@ const ServerResolverTestFull = via(ServerResolverTestObject, ServerResolverTestL
|
|
|
31
31
|
resolvedLabel: r(String),
|
|
32
32
|
}));
|
|
33
33
|
const ServerResolverTestInsight = via(ServerResolverTestFull, (f) => ({
|
|
34
|
-
total: f(Int, { default: 0, accumulate: {
|
|
34
|
+
total: f(Int, { default: 0, accumulate: {} }),
|
|
35
35
|
}));
|
|
36
36
|
export const serverResolverTestConstant = ConstantRegistry.buildModel(
|
|
37
37
|
"serverResolverTestItem",
|
package/test/index.ts
CHANGED
|
@@ -1,2 +1,16 @@
|
|
|
1
1
|
export { sample } from "./sample";
|
|
2
2
|
export { sampleOf } from "./sampleOf";
|
|
3
|
+
export {
|
|
4
|
+
configureSignalTest,
|
|
5
|
+
getOrSetupSignalTestContext,
|
|
6
|
+
getOrSetupSignalTestFetch,
|
|
7
|
+
getSignalTestContext,
|
|
8
|
+
getSignalTestFetch,
|
|
9
|
+
hasSignalTestContext,
|
|
10
|
+
type SignalTestContext,
|
|
11
|
+
type SignalTestOptions,
|
|
12
|
+
type SignalTestTarget,
|
|
13
|
+
setupSignalTestTarget,
|
|
14
|
+
terminateSignalTestContext,
|
|
15
|
+
} from "./signalTestRuntime";
|
|
16
|
+
export { TestServer, type TestServerOptions } from "./testServer";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { afterAll } from "bun:test";
|
|
2
|
+
import { terminateSignalTestContext } from "./signalTestRuntime";
|
|
3
|
+
|
|
4
|
+
const isSignalTarget = process.env.AKAN_TEST_SIGNAL === "1";
|
|
5
|
+
|
|
6
|
+
if (isSignalTarget) {
|
|
7
|
+
afterAll(async () => {
|
|
8
|
+
await terminateSignalTestContext();
|
|
9
|
+
});
|
|
10
|
+
}
|