@akanjs/cli 2.1.1-rc.2 → 2.1.2-rc.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/guidelines/modelConstant/modelConstant.generate.json +1 -0
- package/guidelines/modelDictionary/modelDictionary.generate.json +1 -0
- package/guidelines/scalarConstant/scalarConstant.generate.json +1 -0
- package/guidelines/scalarDictionary/scalarDictionary.generate.json +1 -0
- package/incrementalBuilder.proc.js +447 -134
- package/index.js +614 -256
- package/package.json +2 -2
- package/templates/lib/dict.ts +5 -2
- package/templates/lib/useClient.ts +6 -2
- package/templates/module/__Model__.Unit.tsx +5 -2
- package/typecheck.proc.js +19 -0
package/index.js
CHANGED
|
@@ -203,11 +203,13 @@ class HttpClient {
|
|
|
203
203
|
class CloudApi {
|
|
204
204
|
#api;
|
|
205
205
|
#accessToken = null;
|
|
206
|
-
|
|
206
|
+
#workspace;
|
|
207
|
+
static async fromHost(workspace, host) {
|
|
207
208
|
const hostConfig = await GlobalConfig.getHostConfig(host);
|
|
208
|
-
return new CloudApi(hostConfig);
|
|
209
|
+
return new CloudApi(workspace, hostConfig);
|
|
209
210
|
}
|
|
210
|
-
constructor(hostConfig) {
|
|
211
|
+
constructor(workspace, hostConfig) {
|
|
212
|
+
this.#workspace = workspace;
|
|
211
213
|
const host = akanCloudHost;
|
|
212
214
|
this.#api = new HttpClient(`${host}/api`);
|
|
213
215
|
this.#accessToken = hostConfig.auth?.accessToken ?? null;
|
|
@@ -223,16 +225,17 @@ class CloudApi {
|
|
|
223
225
|
const data = await this.#api.post(`/uploadEnv/${devProjectId}`, formData);
|
|
224
226
|
return data;
|
|
225
227
|
}
|
|
226
|
-
async downloadEnv(devProjectId
|
|
228
|
+
async downloadEnv(devProjectId) {
|
|
229
|
+
const localPath = `${this.#workspace.workspaceRoot}/local/env.tar`;
|
|
227
230
|
await this.#api.getFile(`/downloadEnv/${devProjectId}`, localPath);
|
|
228
231
|
}
|
|
229
232
|
async getRemoteAuthToken(remoteId) {
|
|
230
233
|
try {
|
|
231
234
|
if (this.#accessToken) {
|
|
232
235
|
if (GlobalConfig.needRefreshToken(this.#accessToken))
|
|
233
|
-
return await this
|
|
236
|
+
return await this.#refreshAuthToken();
|
|
234
237
|
else
|
|
235
|
-
return await this
|
|
238
|
+
return await this.#refreshAuthToken();
|
|
236
239
|
}
|
|
237
240
|
const accessToken = await this.#api.get(`/getRemoteAuthToken/${remoteId}`);
|
|
238
241
|
this.#accessToken = GlobalConfig.toAccessToken(accessToken);
|
|
@@ -244,10 +247,14 @@ class CloudApi {
|
|
|
244
247
|
return null;
|
|
245
248
|
}
|
|
246
249
|
}
|
|
247
|
-
async
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
250
|
+
async#refreshAuthToken() {
|
|
251
|
+
const refreshToken = this.#accessToken?.refreshToken;
|
|
252
|
+
if (!refreshToken)
|
|
253
|
+
throw new Error("No refresh token");
|
|
254
|
+
return await this.refreshAuthToken(refreshToken);
|
|
255
|
+
}
|
|
256
|
+
async refreshAuthToken(refreshToken) {
|
|
257
|
+
const response = await this.#api.post(`/refreshRemoteAuthToken`, { refreshToken });
|
|
251
258
|
this.#accessToken = GlobalConfig.toAccessToken(response);
|
|
252
259
|
this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
|
|
253
260
|
return this.#accessToken;
|
|
@@ -327,6 +334,31 @@ var supportedLlmModels = [
|
|
|
327
334
|
"deepseek-chat",
|
|
328
335
|
"deepseek-reasoner"
|
|
329
336
|
];
|
|
337
|
+
var parseTypescriptFileBlocks = (text) => {
|
|
338
|
+
const fileBlocks = [];
|
|
339
|
+
const codeBlockRegex = /```(?:typescript|ts|tsx)\s*\n([\s\S]*?)```/gi;
|
|
340
|
+
const filePathRegex = /^\s*\/\/\s*File:\s*(.+?)\s*$/im;
|
|
341
|
+
for (const codeBlock of text.matchAll(codeBlockRegex)) {
|
|
342
|
+
const content = codeBlock[1]?.trim();
|
|
343
|
+
if (!content)
|
|
344
|
+
continue;
|
|
345
|
+
const filePath = filePathRegex.exec(content)?.[1]?.trim();
|
|
346
|
+
if (!filePath)
|
|
347
|
+
continue;
|
|
348
|
+
fileBlocks.push({
|
|
349
|
+
filePath,
|
|
350
|
+
content: content.replace(filePathRegex, "").trim()
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
return fileBlocks;
|
|
354
|
+
};
|
|
355
|
+
var preserveTypescriptResponseContent = (previousContent, nextContent) => {
|
|
356
|
+
const previousWrites = parseTypescriptFileBlocks(previousContent);
|
|
357
|
+
const nextWrites = parseTypescriptFileBlocks(nextContent);
|
|
358
|
+
if (previousWrites.length > 0 && nextWrites.length === 0)
|
|
359
|
+
return previousContent;
|
|
360
|
+
return nextContent;
|
|
361
|
+
};
|
|
330
362
|
|
|
331
363
|
class AiSession {
|
|
332
364
|
static #cacheDir = "node_modules/.cache/akan/aiSession";
|
|
@@ -446,7 +478,7 @@ class AiSession {
|
|
|
446
478
|
const humanMessage = new HumanMessage(question);
|
|
447
479
|
this.messageHistory.push(humanMessage);
|
|
448
480
|
const stream = await AiSession.#chat.stream(this.messageHistory);
|
|
449
|
-
let reasoningResponse = "", fullResponse = ""
|
|
481
|
+
let reasoningResponse = "", fullResponse = "";
|
|
450
482
|
for await (const chunk of stream) {
|
|
451
483
|
if (loader.isSpinning())
|
|
452
484
|
loader.succeed(`${AiSession.#chat.model} responded`);
|
|
@@ -467,7 +499,6 @@ class AiSession {
|
|
|
467
499
|
fullResponse += content;
|
|
468
500
|
onChunk(content);
|
|
469
501
|
}
|
|
470
|
-
tokenIdx++;
|
|
471
502
|
}
|
|
472
503
|
fullResponse += `
|
|
473
504
|
`;
|
|
@@ -475,7 +506,7 @@ class AiSession {
|
|
|
475
506
|
`);
|
|
476
507
|
this.messageHistory.push(new AIMessage(fullResponse));
|
|
477
508
|
return { content: fullResponse, messageHistory: this.messageHistory };
|
|
478
|
-
} catch
|
|
509
|
+
} catch {
|
|
479
510
|
loader.fail(`${AiSession.#chat.model} failed to respond`);
|
|
480
511
|
throw new Error("Failed to stream response");
|
|
481
512
|
}
|
|
@@ -485,7 +516,8 @@ class AiSession {
|
|
|
485
516
|
onReasoning,
|
|
486
517
|
maxTry = MAX_ASK_TRY,
|
|
487
518
|
validate,
|
|
488
|
-
approve
|
|
519
|
+
approve,
|
|
520
|
+
fallbackToPreviousTypescript
|
|
489
521
|
} = {}) {
|
|
490
522
|
for (let tryCount = 0;tryCount < maxTry; tryCount++) {
|
|
491
523
|
let response = await this.ask(question, { onChunk, onReasoning });
|
|
@@ -493,7 +525,14 @@ class AiSession {
|
|
|
493
525
|
const validateQuestion = `Double check if the response meets the requirements and conditions, and follow the instructions. If not, rewrite it.
|
|
494
526
|
${validate.map((v) => `- ${v}`).join(`
|
|
495
527
|
`)}`;
|
|
496
|
-
|
|
528
|
+
const validateResponse = await this.ask(validateQuestion, {
|
|
529
|
+
onChunk,
|
|
530
|
+
onReasoning
|
|
531
|
+
});
|
|
532
|
+
response = {
|
|
533
|
+
...validateResponse,
|
|
534
|
+
content: fallbackToPreviousTypescript ? preserveTypescriptResponseContent(response.content, validateResponse.content) : validateResponse.content
|
|
535
|
+
};
|
|
497
536
|
}
|
|
498
537
|
const isConfirmed = approve ? true : await select({
|
|
499
538
|
message: "Do you want to edit the response?",
|
|
@@ -526,15 +565,28 @@ ${validate.map((v) => `- ${v}`).join(`
|
|
|
526
565
|
return this;
|
|
527
566
|
}
|
|
528
567
|
async writeTypescripts(question, executor, options = {}) {
|
|
529
|
-
const content = await this.edit(question,
|
|
568
|
+
const content = await this.edit(question, {
|
|
569
|
+
...options,
|
|
570
|
+
fallbackToPreviousTypescript: true
|
|
571
|
+
});
|
|
530
572
|
const writes = this.#getTypescriptCodes(content);
|
|
573
|
+
if (!writes.length)
|
|
574
|
+
throw new Error("No parseable TypeScript file blocks were found in the AI response. Include `// File: <path>` in each code block.");
|
|
531
575
|
for (const write of writes)
|
|
532
576
|
await executor.writeFile(write.filePath, write.content);
|
|
533
577
|
return await this.#tryFixTypescripts(writes, executor, options);
|
|
534
578
|
}
|
|
535
|
-
async#editTypescripts(question, options = {}) {
|
|
536
|
-
const content = await this.edit(question,
|
|
537
|
-
|
|
579
|
+
async#editTypescripts(question, options = {}, fallbackWrites) {
|
|
580
|
+
const content = await this.edit(question, {
|
|
581
|
+
...options,
|
|
582
|
+
fallbackToPreviousTypescript: true
|
|
583
|
+
});
|
|
584
|
+
const writes = this.#getTypescriptCodes(content);
|
|
585
|
+
if (!writes.length && fallbackWrites?.length)
|
|
586
|
+
return fallbackWrites;
|
|
587
|
+
if (!writes.length)
|
|
588
|
+
throw new Error("No parseable TypeScript file blocks were found in the AI response. Include `// File: <path>` in each code block.");
|
|
589
|
+
return writes;
|
|
538
590
|
}
|
|
539
591
|
async#tryFixTypescripts(writes, executor, options = {}) {
|
|
540
592
|
const MAX_EDIT_TRY = 5;
|
|
@@ -543,13 +595,15 @@ ${validate.map((v) => `- ${v}`).join(`
|
|
|
543
595
|
prefix: `\uD83E\uDD16akan-editor`
|
|
544
596
|
}).start();
|
|
545
597
|
const fileChecks = await Promise.all(writes.map(async ({ filePath }) => {
|
|
546
|
-
const
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
|
|
598
|
+
const lintResult = await executor.lint(filePath, { fix: true });
|
|
599
|
+
const typeCheckResult = await executor.typeCheckAsync(filePath);
|
|
600
|
+
const hasTypeErrors = typeCheckResult.fileErrors.length > 0;
|
|
601
|
+
const hasLintErrors = lintResult.errors.length > 0;
|
|
602
|
+
const needFix = hasTypeErrors || hasLintErrors;
|
|
603
|
+
return { filePath, typeCheckResult, lintResult, needFix };
|
|
550
604
|
}));
|
|
551
|
-
const
|
|
552
|
-
if (
|
|
605
|
+
const hasAnyFix = fileChecks.some((fileCheck) => fileCheck.needFix);
|
|
606
|
+
if (hasAnyFix) {
|
|
553
607
|
loader.fail("Type checking and linting has some errors, try to fix them");
|
|
554
608
|
fileChecks.forEach((fileCheck) => {
|
|
555
609
|
Logger2.rawLog(`TypeCheck Result
|
|
@@ -565,7 +619,7 @@ ${fileCheck.lintResult.message}`);
|
|
|
565
619
|
...options,
|
|
566
620
|
validate: undefined,
|
|
567
621
|
approve: true
|
|
568
|
-
});
|
|
622
|
+
}, writes);
|
|
569
623
|
for (const write of writes)
|
|
570
624
|
await executor.writeFile(write.filePath, write.content);
|
|
571
625
|
} else {
|
|
@@ -576,21 +630,7 @@ ${fileCheck.lintResult.message}`);
|
|
|
576
630
|
throw new Error("Failed to create scalar");
|
|
577
631
|
}
|
|
578
632
|
#getTypescriptCodes(text) {
|
|
579
|
-
|
|
580
|
-
if (!codes)
|
|
581
|
-
return [];
|
|
582
|
-
const result = codes.map((code) => {
|
|
583
|
-
const content = /```(typescript|tsx)([\s\S]*?)```/.exec(code)?.[2];
|
|
584
|
-
if (!content)
|
|
585
|
-
return null;
|
|
586
|
-
const filePath = /\/\/ File: (.*?)(?:\n|$)/.exec(content)?.[1]?.trim();
|
|
587
|
-
if (!filePath)
|
|
588
|
-
return null;
|
|
589
|
-
const contentWithoutFilepath = content.replace(`// File: ${filePath}
|
|
590
|
-
`, "").trim();
|
|
591
|
-
return { filePath, content: contentWithoutFilepath };
|
|
592
|
-
});
|
|
593
|
-
return result.filter((code) => code !== null);
|
|
633
|
+
return parseTypescriptFileBlocks(text);
|
|
594
634
|
}
|
|
595
635
|
async editMarkdown(request, options = {}) {
|
|
596
636
|
const content = await this.edit(request, options);
|
|
@@ -608,13 +648,13 @@ ${fileCheck.lintResult.message}`);
|
|
|
608
648
|
}
|
|
609
649
|
// pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
|
|
610
650
|
import path9 from "path";
|
|
611
|
-
import { Logger as
|
|
651
|
+
import { Logger as Logger5 } from "akanjs/common";
|
|
612
652
|
|
|
613
653
|
// pkgs/@akanjs/devkit/executors.ts
|
|
614
654
|
import {
|
|
615
655
|
exec,
|
|
616
656
|
fork,
|
|
617
|
-
spawn
|
|
657
|
+
spawn as spawn2
|
|
618
658
|
} from "child_process";
|
|
619
659
|
import { readFileSync as readFileSync3 } from "fs";
|
|
620
660
|
import { copyFile, mkdir as mkdir2, readdir as readDirEntries, stat as stat2 } from "fs/promises";
|
|
@@ -622,7 +662,7 @@ import path7 from "path";
|
|
|
622
662
|
import {
|
|
623
663
|
capitalize,
|
|
624
664
|
isRouteSourceFile,
|
|
625
|
-
Logger as
|
|
665
|
+
Logger as Logger3,
|
|
626
666
|
parseRouteModuleKey,
|
|
627
667
|
validatePageSourceFile,
|
|
628
668
|
validateSubRoutePageKey
|
|
@@ -669,8 +709,18 @@ var DEFAULT_OPTIMIZE_IMPORTS = [
|
|
|
669
709
|
"mui-core",
|
|
670
710
|
"react-icons/*"
|
|
671
711
|
];
|
|
672
|
-
var WORKSPACE_BARREL_FACETS = [
|
|
673
|
-
|
|
712
|
+
var WORKSPACE_BARREL_FACETS = [
|
|
713
|
+
"ui",
|
|
714
|
+
"webkit",
|
|
715
|
+
"common",
|
|
716
|
+
"client",
|
|
717
|
+
"server"
|
|
718
|
+
];
|
|
719
|
+
var SSR_RUNTIME_PACKAGES = [
|
|
720
|
+
"react",
|
|
721
|
+
"react-dom",
|
|
722
|
+
"react-server-dom-webpack"
|
|
723
|
+
];
|
|
674
724
|
var NATIVE_RUNTIME_PACKAGES = ["sharp"];
|
|
675
725
|
var DEFAULT_BACKEND_RUNTIME_PACKAGES = ["croner"];
|
|
676
726
|
var DATABASE_MODE_RUNTIME_PACKAGES = {
|
|
@@ -730,7 +780,12 @@ class AkanAppConfig {
|
|
|
730
780
|
...libs.flatMap((lib) => WORKSPACE_BARREL_FACETS.map((facet) => `@libs/${lib}/${facet}`)),
|
|
731
781
|
...config?.barrelImports ?? []
|
|
732
782
|
];
|
|
733
|
-
this.optimizeImports = [
|
|
783
|
+
this.optimizeImports = [
|
|
784
|
+
...new Set([
|
|
785
|
+
...DEFAULT_OPTIMIZE_IMPORTS,
|
|
786
|
+
...config?.optimizeImports ?? []
|
|
787
|
+
])
|
|
788
|
+
];
|
|
734
789
|
this.images = mergeImageConfig(config?.images);
|
|
735
790
|
this.i18n = resolveAkanI18nConfig(config?.i18n);
|
|
736
791
|
process.env.AKAN_PUBLIC_DEFAULT_LOCALE = this.i18n.defaultLocale;
|
|
@@ -841,7 +896,13 @@ class AkanAppConfig {
|
|
|
841
896
|
}
|
|
842
897
|
#makeDockerContent(docker) {
|
|
843
898
|
if (docker.content)
|
|
844
|
-
return {
|
|
899
|
+
return {
|
|
900
|
+
content: docker.content,
|
|
901
|
+
image: {},
|
|
902
|
+
preRuns: [],
|
|
903
|
+
postRuns: [],
|
|
904
|
+
command: []
|
|
905
|
+
};
|
|
845
906
|
const preRunScripts = this.#getDockerRunScripts(docker.preRuns ?? []);
|
|
846
907
|
const postRunScripts = this.#getDockerRunScripts(docker.postRuns ?? []);
|
|
847
908
|
const imageScript = docker.image ? this.#getDockerImageScript(docker.image, "oven/bun:1-slim") : "FROM oven/bun:1-slim";
|
|
@@ -872,7 +933,13 @@ ENV AKAN_PUBLIC_LOCALES=${this.i18n.locales.join(",")}
|
|
|
872
933
|
ENV AKAN_PUBLIC_OPERATION_MODE=cloud
|
|
873
934
|
|
|
874
935
|
CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
875
|
-
return {
|
|
936
|
+
return {
|
|
937
|
+
content,
|
|
938
|
+
image: imageScript,
|
|
939
|
+
preRuns: docker.preRuns ?? [],
|
|
940
|
+
postRuns: docker.postRuns ?? [],
|
|
941
|
+
command
|
|
942
|
+
};
|
|
876
943
|
}
|
|
877
944
|
static async from(app) {
|
|
878
945
|
const [configImp, baseDevEnv, libs, rootPackageJson] = await Promise.all([
|
|
@@ -898,9 +965,24 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
898
965
|
...SSR_RUNTIME_PACKAGES,
|
|
899
966
|
...NATIVE_RUNTIME_PACKAGES,
|
|
900
967
|
...DEFAULT_BACKEND_RUNTIME_PACKAGES,
|
|
901
|
-
...
|
|
968
|
+
...this.getDatabaseModeRuntimePackages()
|
|
902
969
|
];
|
|
903
970
|
}
|
|
971
|
+
getDatabaseModeRuntimePackages(databaseMode = this.defaultDatabaseMode) {
|
|
972
|
+
return [...DATABASE_MODE_RUNTIME_PACKAGES[databaseMode]];
|
|
973
|
+
}
|
|
974
|
+
getMissingDatabaseModeDependencySpecs(databaseMode = this.defaultDatabaseMode) {
|
|
975
|
+
const rootDependencies = {
|
|
976
|
+
...this.rootPackageJson.dependencies,
|
|
977
|
+
...this.rootPackageJson.devDependencies
|
|
978
|
+
};
|
|
979
|
+
return this.getDatabaseModeRuntimePackages(databaseMode).filter((lib) => !rootDependencies[lib]).map((lib) => {
|
|
980
|
+
const version = this.#resolveProductionDependencyVersion(lib);
|
|
981
|
+
if (!version)
|
|
982
|
+
throw new Error(`Dependency ${lib} not found in package.json`);
|
|
983
|
+
return `${lib}@${version}`;
|
|
984
|
+
});
|
|
985
|
+
}
|
|
904
986
|
getProductionPackageJson(data = {}) {
|
|
905
987
|
return {
|
|
906
988
|
name: this.app.name,
|
|
@@ -936,7 +1018,12 @@ function getAkanPackageJson() {
|
|
|
936
1018
|
return akanPackageJson;
|
|
937
1019
|
} catch {}
|
|
938
1020
|
}
|
|
939
|
-
akanPackageJson = {
|
|
1021
|
+
akanPackageJson = {
|
|
1022
|
+
name: "akanjs",
|
|
1023
|
+
version: "0.0.0",
|
|
1024
|
+
description: "akanjs",
|
|
1025
|
+
dependencies: {}
|
|
1026
|
+
};
|
|
940
1027
|
return akanPackageJson;
|
|
941
1028
|
}
|
|
942
1029
|
function mergeImageConfig(config = {}) {
|
|
@@ -960,7 +1047,9 @@ class AkanLibConfig {
|
|
|
960
1047
|
this.externalLibs = config?.externalLibs ?? [];
|
|
961
1048
|
}
|
|
962
1049
|
static async from(lib) {
|
|
963
|
-
const [configImp] = await Promise.all([
|
|
1050
|
+
const [configImp] = await Promise.all([
|
|
1051
|
+
import(`${lib.cwdPath}/akan.config.ts`).then((mod) => mod.default)
|
|
1052
|
+
]);
|
|
964
1053
|
const config = typeof configImp === "function" ? configImp(lib) : configImp;
|
|
965
1054
|
return new AkanLibConfig(lib, config);
|
|
966
1055
|
}
|
|
@@ -985,23 +1074,146 @@ import path2 from "path";
|
|
|
985
1074
|
var getDirname = (url) => path2.dirname(new URL(url).pathname);
|
|
986
1075
|
|
|
987
1076
|
// pkgs/@akanjs/devkit/linter.ts
|
|
1077
|
+
import { spawn } from "child_process";
|
|
988
1078
|
import { existsSync, readFileSync } from "fs";
|
|
989
|
-
import
|
|
990
|
-
import { Logger as Logger3 } from "akanjs/common";
|
|
1079
|
+
import path3 from "path";
|
|
991
1080
|
import chalk2 from "chalk";
|
|
992
1081
|
|
|
993
1082
|
class Linter {
|
|
994
|
-
#logger = new Logger3("Linter");
|
|
995
1083
|
lintRoot;
|
|
1084
|
+
#biomeBin;
|
|
996
1085
|
constructor(cwdPath) {
|
|
997
|
-
this.lintRoot = this.#
|
|
1086
|
+
this.lintRoot = this.#findBiomeRootPath(cwdPath);
|
|
1087
|
+
const localBiomeBin = path3.join(this.lintRoot, "node_modules/.bin/biome");
|
|
1088
|
+
this.#biomeBin = existsSync(localBiomeBin) ? localBiomeBin : "biome";
|
|
998
1089
|
}
|
|
999
|
-
#
|
|
1000
|
-
const configPath2 = path3.join(dir, "
|
|
1090
|
+
#findBiomeRootPath(dir) {
|
|
1091
|
+
const configPath2 = path3.join(dir, "biome.json");
|
|
1001
1092
|
if (existsSync(configPath2))
|
|
1002
1093
|
return dir;
|
|
1003
1094
|
const parentDir = path3.dirname(dir);
|
|
1004
|
-
|
|
1095
|
+
if (parentDir === dir)
|
|
1096
|
+
throw new Error(`biome.json not found from ${dir}`);
|
|
1097
|
+
return this.#findBiomeRootPath(parentDir);
|
|
1098
|
+
}
|
|
1099
|
+
#toBiomePath(filePath) {
|
|
1100
|
+
const relativePath = path3.relative(this.lintRoot, filePath);
|
|
1101
|
+
if (!relativePath.startsWith("..") && !path3.isAbsolute(relativePath))
|
|
1102
|
+
return relativePath;
|
|
1103
|
+
return filePath;
|
|
1104
|
+
}
|
|
1105
|
+
#resolveFilePath(filePath) {
|
|
1106
|
+
return path3.isAbsolute(filePath) ? filePath : path3.join(this.lintRoot, filePath);
|
|
1107
|
+
}
|
|
1108
|
+
async#runBiome(args, input2) {
|
|
1109
|
+
return await new Promise((resolve, reject) => {
|
|
1110
|
+
const proc = spawn(this.#biomeBin, args, {
|
|
1111
|
+
cwd: this.lintRoot,
|
|
1112
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1113
|
+
});
|
|
1114
|
+
let stdout = "";
|
|
1115
|
+
let stderr = "";
|
|
1116
|
+
proc.stdout.on("data", (data) => {
|
|
1117
|
+
stdout += data.toString();
|
|
1118
|
+
});
|
|
1119
|
+
proc.stderr.on("data", (data) => {
|
|
1120
|
+
stderr += data.toString();
|
|
1121
|
+
});
|
|
1122
|
+
proc.on("error", reject);
|
|
1123
|
+
proc.on("close", (code) => resolve({ stdout, stderr, code }));
|
|
1124
|
+
proc.stdin.end(input2);
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
#parseBiomeReport(output) {
|
|
1128
|
+
const jsonStart = output.indexOf("{");
|
|
1129
|
+
const jsonEnd = output.lastIndexOf("}");
|
|
1130
|
+
if (jsonStart === -1 || jsonEnd === -1 || jsonEnd < jsonStart)
|
|
1131
|
+
throw new Error(output.trim() || "No Biome JSON output");
|
|
1132
|
+
return JSON.parse(output.slice(jsonStart, jsonEnd + 1));
|
|
1133
|
+
}
|
|
1134
|
+
#diagnosticFilePath(diagnostic, fallbackFilePath) {
|
|
1135
|
+
const diagnosticPath = diagnostic.location?.path;
|
|
1136
|
+
if (!diagnosticPath)
|
|
1137
|
+
return fallbackFilePath;
|
|
1138
|
+
return path3.isAbsolute(diagnosticPath) ? diagnosticPath : path3.join(this.lintRoot, diagnosticPath);
|
|
1139
|
+
}
|
|
1140
|
+
#createLintMessage(diagnostic) {
|
|
1141
|
+
const start = diagnostic.location?.start;
|
|
1142
|
+
const end = diagnostic.location?.end;
|
|
1143
|
+
return {
|
|
1144
|
+
line: Math.max(1, start?.line ?? 1),
|
|
1145
|
+
column: Math.max(1, start?.column ?? 1),
|
|
1146
|
+
endLine: end?.line,
|
|
1147
|
+
endColumn: end?.column,
|
|
1148
|
+
message: diagnostic.message,
|
|
1149
|
+
ruleId: diagnostic.category ?? null,
|
|
1150
|
+
severity: diagnostic.severity === "error" ? 2 : 1
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
#toLintResults(report, filePath) {
|
|
1154
|
+
const resultsByPath = new Map;
|
|
1155
|
+
for (const diagnostic of report.diagnostics ?? []) {
|
|
1156
|
+
if (diagnostic.severity !== "error" && diagnostic.severity !== "warning")
|
|
1157
|
+
continue;
|
|
1158
|
+
const diagnosticFilePath = this.#diagnosticFilePath(diagnostic, filePath);
|
|
1159
|
+
const result = resultsByPath.get(diagnosticFilePath) ?? {
|
|
1160
|
+
filePath: diagnosticFilePath,
|
|
1161
|
+
messages: [],
|
|
1162
|
+
errorCount: 0,
|
|
1163
|
+
warningCount: 0,
|
|
1164
|
+
fixableErrorCount: 0,
|
|
1165
|
+
fixableWarningCount: 0
|
|
1166
|
+
};
|
|
1167
|
+
const message = this.#createLintMessage(diagnostic);
|
|
1168
|
+
result.messages.push(message);
|
|
1169
|
+
if (message.severity === 2)
|
|
1170
|
+
result.errorCount += 1;
|
|
1171
|
+
else
|
|
1172
|
+
result.warningCount += 1;
|
|
1173
|
+
resultsByPath.set(diagnosticFilePath, result);
|
|
1174
|
+
}
|
|
1175
|
+
return [
|
|
1176
|
+
resultsByPath.get(filePath) ?? {
|
|
1177
|
+
filePath,
|
|
1178
|
+
messages: [],
|
|
1179
|
+
errorCount: 0,
|
|
1180
|
+
warningCount: 0,
|
|
1181
|
+
fixableErrorCount: 0,
|
|
1182
|
+
fixableWarningCount: 0
|
|
1183
|
+
},
|
|
1184
|
+
...[...resultsByPath.entries()].filter(([resultPath]) => resultPath !== filePath).map(([, result]) => result)
|
|
1185
|
+
];
|
|
1186
|
+
}
|
|
1187
|
+
#splitMessages(results) {
|
|
1188
|
+
const messages = results.flatMap((result) => result.messages);
|
|
1189
|
+
return {
|
|
1190
|
+
errors: messages.filter((message) => message.severity === 2),
|
|
1191
|
+
warnings: messages.filter((message) => message.severity === 1)
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
async#checkFile(filePath, { write = false } = {}) {
|
|
1195
|
+
const originalContent = existsSync(filePath) ? readFileSync(filePath, "utf8") : "";
|
|
1196
|
+
const { stdout, stderr } = await this.#runBiome([
|
|
1197
|
+
"check",
|
|
1198
|
+
...write ? ["--write"] : [],
|
|
1199
|
+
"--reporter=json",
|
|
1200
|
+
"--max-diagnostics=none",
|
|
1201
|
+
"--no-errors-on-unmatched",
|
|
1202
|
+
"--config-path",
|
|
1203
|
+
path3.join(this.lintRoot, "biome.json"),
|
|
1204
|
+
this.#toBiomePath(filePath)
|
|
1205
|
+
]);
|
|
1206
|
+
const report = this.#parseBiomeReport(stdout || stderr);
|
|
1207
|
+
const results = this.#toLintResults(report, filePath);
|
|
1208
|
+
const { errors, warnings } = this.#splitMessages(results);
|
|
1209
|
+
const output = write && existsSync(filePath) ? readFileSync(filePath, "utf8") : undefined;
|
|
1210
|
+
return {
|
|
1211
|
+
fixed: write && output !== originalContent,
|
|
1212
|
+
output,
|
|
1213
|
+
results,
|
|
1214
|
+
errors,
|
|
1215
|
+
warnings
|
|
1216
|
+
};
|
|
1005
1217
|
}
|
|
1006
1218
|
async lint(filePath, { fix = false, dryRun = false } = {}) {
|
|
1007
1219
|
if (fix)
|
|
@@ -1009,9 +1221,10 @@ class Linter {
|
|
|
1009
1221
|
return await this.lintFile(filePath);
|
|
1010
1222
|
}
|
|
1011
1223
|
async lintFile(filePath) {
|
|
1012
|
-
|
|
1224
|
+
const resolvedFilePath = this.#resolveFilePath(filePath);
|
|
1225
|
+
if (!existsSync(resolvedFilePath))
|
|
1013
1226
|
throw new Error(`File not found: ${filePath}`);
|
|
1014
|
-
return
|
|
1227
|
+
return await this.#checkFile(resolvedFilePath);
|
|
1015
1228
|
}
|
|
1016
1229
|
formatLintResults(results) {
|
|
1017
1230
|
if (results.length === 0)
|
|
@@ -1031,12 +1244,12 @@ ${chalk2.cyan(result.filePath)}`);
|
|
|
1031
1244
|
const sourceContent = readFileSync(result.filePath, "utf8");
|
|
1032
1245
|
sourceLines = sourceContent.split(`
|
|
1033
1246
|
`);
|
|
1034
|
-
} catch
|
|
1247
|
+
} catch {}
|
|
1035
1248
|
}
|
|
1036
1249
|
result.messages.forEach((message) => {
|
|
1037
1250
|
const type = message.severity === 2 ? "error" : "warning";
|
|
1038
1251
|
const typeColor = message.severity === 2 ? chalk2.red : chalk2.yellow;
|
|
1039
|
-
const icon = message.severity === 2 ? "
|
|
1252
|
+
const icon = message.severity === 2 ? "x" : "!";
|
|
1040
1253
|
const ruleInfo = message.ruleId ? chalk2.dim(` (${message.ruleId})`) : "";
|
|
1041
1254
|
output.push(`
|
|
1042
1255
|
${icon} ${typeColor(type)}: ${message.message}${ruleInfo}`);
|
|
@@ -1055,7 +1268,7 @@ ${chalk2.dim(`${lineNumber} |`)} ${sourceLine}`);
|
|
|
1055
1268
|
}
|
|
1056
1269
|
});
|
|
1057
1270
|
if (totalErrors === 0 && totalWarnings === 0)
|
|
1058
|
-
return chalk2.bold("
|
|
1271
|
+
return chalk2.bold("No Biome errors or warnings found");
|
|
1059
1272
|
const errorText = totalErrors > 0 ? chalk2.red(`${totalErrors} error(s)`) : "0 errors";
|
|
1060
1273
|
const warningText = totalWarnings > 0 ? chalk2.yellow(`${totalWarnings} warning(s)`) : "0 warnings";
|
|
1061
1274
|
const summary = [`
|
|
@@ -1070,23 +1283,26 @@ ${errorText}, ${warningText} found`];
|
|
|
1070
1283
|
column: message.column,
|
|
1071
1284
|
message: message.message,
|
|
1072
1285
|
ruleId: message.ruleId,
|
|
1073
|
-
severity: message.severity === 2 ? "error" : "warning"
|
|
1074
|
-
fix: message.fix,
|
|
1075
|
-
suggestions: message.suggestions
|
|
1286
|
+
severity: message.severity === 2 ? "error" : "warning"
|
|
1076
1287
|
})));
|
|
1077
1288
|
const stats = results.reduce((acc, result) => ({
|
|
1078
1289
|
errorCount: acc.errorCount + result.errorCount,
|
|
1079
1290
|
warningCount: acc.warningCount + result.warningCount,
|
|
1080
1291
|
fixableErrorCount: acc.fixableErrorCount + result.fixableErrorCount,
|
|
1081
1292
|
fixableWarningCount: acc.fixableWarningCount + result.fixableWarningCount
|
|
1082
|
-
}), {
|
|
1293
|
+
}), {
|
|
1294
|
+
errorCount: 0,
|
|
1295
|
+
warningCount: 0,
|
|
1296
|
+
fixableErrorCount: 0,
|
|
1297
|
+
fixableWarningCount: 0
|
|
1298
|
+
});
|
|
1083
1299
|
return { results, details, stats };
|
|
1084
1300
|
}
|
|
1085
1301
|
async hasNoLintErrors(filePath) {
|
|
1086
1302
|
try {
|
|
1087
1303
|
const { results } = await this.lintFile(filePath);
|
|
1088
1304
|
return results.every((result) => result.errorCount === 0);
|
|
1089
|
-
} catch
|
|
1305
|
+
} catch {
|
|
1090
1306
|
return false;
|
|
1091
1307
|
}
|
|
1092
1308
|
}
|
|
@@ -1099,12 +1315,28 @@ ${errorText}, ${warningText} found`];
|
|
|
1099
1315
|
return results.flatMap((result) => result.messages.filter((message) => message.severity === 1));
|
|
1100
1316
|
}
|
|
1101
1317
|
async fixFile(filePath, dryRun = false) {
|
|
1102
|
-
|
|
1318
|
+
const resolvedFilePath = this.#resolveFilePath(filePath);
|
|
1319
|
+
if (!existsSync(resolvedFilePath))
|
|
1103
1320
|
throw new Error(`File not found: ${filePath}`);
|
|
1104
|
-
|
|
1321
|
+
if (!dryRun)
|
|
1322
|
+
return await this.#checkFile(resolvedFilePath, { write: true });
|
|
1323
|
+
const source = readFileSync(resolvedFilePath, "utf8");
|
|
1324
|
+
const { stdout } = await this.#runBiome([
|
|
1325
|
+
"check",
|
|
1326
|
+
"--write",
|
|
1327
|
+
"--config-path",
|
|
1328
|
+
path3.join(this.lintRoot, "biome.json"),
|
|
1329
|
+
"--stdin-file-path",
|
|
1330
|
+
this.#toBiomePath(resolvedFilePath)
|
|
1331
|
+
], source);
|
|
1332
|
+
const lintResult = await this.lintFile(resolvedFilePath);
|
|
1333
|
+
return { ...lintResult, fixed: stdout !== source, output: stdout };
|
|
1105
1334
|
}
|
|
1106
1335
|
async getConfigForFile(filePath) {
|
|
1107
|
-
|
|
1336
|
+
const resolvedFilePath = this.#resolveFilePath(filePath);
|
|
1337
|
+
if (!existsSync(resolvedFilePath))
|
|
1338
|
+
throw new Error(`File not found: ${filePath}`);
|
|
1339
|
+
return JSON.parse(readFileSync(path3.join(this.lintRoot, "biome.json"), "utf8"));
|
|
1108
1340
|
}
|
|
1109
1341
|
async getProblematicRules(filePath) {
|
|
1110
1342
|
const { results } = await this.lintFile(filePath);
|
|
@@ -1494,23 +1726,23 @@ async function assertScanConvention(exec, libRoot) {
|
|
|
1494
1726
|
files.filter((filename) => !appRootAllowedFiles.has(filename)).forEach((filename) => {
|
|
1495
1727
|
addViolation(filename, "unsupported app root file");
|
|
1496
1728
|
});
|
|
1497
|
-
dirs.filter((
|
|
1498
|
-
addViolation(
|
|
1729
|
+
dirs.filter((dirname) => !appRootAllowedDirs.has(dirname)).forEach((dirname) => {
|
|
1730
|
+
addViolation(dirname, "unsupported app root folder");
|
|
1499
1731
|
});
|
|
1500
1732
|
}
|
|
1501
1733
|
libRoot.files.filter((filename) => !isAllowedLibRootFile(filename)).forEach((filename) => {
|
|
1502
1734
|
addViolation(path5.join("lib", filename), "unsupported lib root file");
|
|
1503
1735
|
});
|
|
1504
|
-
libRoot.dirs.filter((
|
|
1505
|
-
addViolation(path5.join("lib",
|
|
1736
|
+
libRoot.dirs.filter((dirname) => dirname.startsWith("__") && !internalLibDirs.has(dirname)).forEach((dirname) => {
|
|
1737
|
+
addViolation(path5.join("lib", dirname), "unsupported internal lib folder");
|
|
1506
1738
|
});
|
|
1507
|
-
const databaseDirs = libRoot.dirs.filter((
|
|
1508
|
-
const serviceDirs = libRoot.dirs.filter((
|
|
1739
|
+
const databaseDirs = libRoot.dirs.filter((dirname) => !dirname.startsWith("_"));
|
|
1740
|
+
const serviceDirs = libRoot.dirs.filter((dirname) => dirname.startsWith("_") && !dirname.startsWith("__"));
|
|
1509
1741
|
const scalarDirs = await exec.readdir("lib/__scalar");
|
|
1510
1742
|
await Promise.all([
|
|
1511
|
-
...databaseDirs.map((
|
|
1512
|
-
...serviceDirs.map((
|
|
1513
|
-
...scalarDirs.map((
|
|
1743
|
+
...databaseDirs.map((dirname) => validateModuleFiles(exec, violations, "database", path5.join("lib", dirname))),
|
|
1744
|
+
...serviceDirs.map((dirname) => validateModuleFiles(exec, violations, "service", path5.join("lib", dirname))),
|
|
1745
|
+
...scalarDirs.map((dirname) => validateModuleFiles(exec, violations, "scalar", path5.join("lib/__scalar", dirname)))
|
|
1514
1746
|
]);
|
|
1515
1747
|
if (violations.length > 0) {
|
|
1516
1748
|
throw new Error(`[scan-convention]
|
|
@@ -1520,8 +1752,8 @@ ${violations.sort().map((violation) => `- ${violation}`).join(`
|
|
|
1520
1752
|
}
|
|
1521
1753
|
async function validateModuleFiles(exec, violations, kind, modulePath) {
|
|
1522
1754
|
const { files, dirs } = await exec.getFilesAndDirs(modulePath);
|
|
1523
|
-
dirs.forEach((
|
|
1524
|
-
violations.push(`${getScanPath(exec, path5.join(modulePath,
|
|
1755
|
+
dirs.forEach((dirname) => {
|
|
1756
|
+
violations.push(`${getScanPath(exec, path5.join(modulePath, dirname))}: unsupported module folder`);
|
|
1525
1757
|
});
|
|
1526
1758
|
files.forEach((filename) => {
|
|
1527
1759
|
const filePath = path5.join(modulePath, filename);
|
|
@@ -1620,9 +1852,9 @@ class ScanInfo {
|
|
|
1620
1852
|
files.zone.databases.push(name);
|
|
1621
1853
|
});
|
|
1622
1854
|
}),
|
|
1623
|
-
...serviceDirs.map(async (
|
|
1624
|
-
const name =
|
|
1625
|
-
const filenames = await exec.readdir(path5.join("lib",
|
|
1855
|
+
...serviceDirs.map(async (dirname) => {
|
|
1856
|
+
const name = dirname.slice(1);
|
|
1857
|
+
const filenames = await exec.readdir(path5.join("lib", dirname));
|
|
1626
1858
|
filenames.forEach((filename) => {
|
|
1627
1859
|
if (filename.endsWith(".dictionary.ts"))
|
|
1628
1860
|
files.dictionary.services.push(name);
|
|
@@ -2252,9 +2484,11 @@ var ROOT_LAYOUT_EXPORTS = new Set([
|
|
|
2252
2484
|
"reconnect",
|
|
2253
2485
|
"layoutStyle",
|
|
2254
2486
|
"gaTrackingId",
|
|
2255
|
-
"Loading"
|
|
2487
|
+
"Loading",
|
|
2488
|
+
"NotFound",
|
|
2489
|
+
"Error"
|
|
2256
2490
|
]);
|
|
2257
|
-
var LAYOUT_ROUTE_EXPORTS = new Set(["default", "head", "generateHead", "Loading"]);
|
|
2491
|
+
var LAYOUT_ROUTE_EXPORTS = new Set(["default", "head", "generateHead", "Loading", "NotFound", "Error"]);
|
|
2258
2492
|
function validateRouteSourceExports(source, filePath, kind, options = {}) {
|
|
2259
2493
|
const sourceFile = ts3.createSourceFile(filePath, source, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TSX);
|
|
2260
2494
|
const allowed = kind === "page" ? PAGE_ROUTE_EXPORTS : options.rootLayout ? ROOT_LAYOUT_EXPORTS : LAYOUT_ROUTE_EXPORTS;
|
|
@@ -2323,16 +2557,16 @@ class Executor {
|
|
|
2323
2557
|
linter = null;
|
|
2324
2558
|
constructor(name, cwdPath) {
|
|
2325
2559
|
this.name = name;
|
|
2326
|
-
this.logger = new
|
|
2560
|
+
this.logger = new Logger3(name);
|
|
2327
2561
|
this.logs = [];
|
|
2328
2562
|
this.cwdPath = cwdPath;
|
|
2329
2563
|
}
|
|
2330
2564
|
#stdout(data) {
|
|
2331
2565
|
if (Executor.verbose)
|
|
2332
|
-
|
|
2566
|
+
Logger3.raw(chalk4.dim(data.toString()));
|
|
2333
2567
|
}
|
|
2334
2568
|
#stderr(data) {
|
|
2335
|
-
|
|
2569
|
+
Logger3.raw(chalk4.red(data.toString()));
|
|
2336
2570
|
}
|
|
2337
2571
|
exec(command, options = {}) {
|
|
2338
2572
|
const cwd = options.cwd?.toString() ?? this.cwdPath;
|
|
@@ -2376,7 +2610,7 @@ class Executor {
|
|
|
2376
2610
|
}
|
|
2377
2611
|
spawn(command, args = [], options = {}) {
|
|
2378
2612
|
const cwd = options.cwd?.toString() ?? this.cwdPath;
|
|
2379
|
-
const proc =
|
|
2613
|
+
const proc = spawn2(command, args, {
|
|
2380
2614
|
cwd: this.cwdPath,
|
|
2381
2615
|
...options
|
|
2382
2616
|
});
|
|
@@ -2422,7 +2656,7 @@ class Executor {
|
|
|
2422
2656
|
});
|
|
2423
2657
|
}
|
|
2424
2658
|
spawnSync(command, args = [], options = {}) {
|
|
2425
|
-
const proc =
|
|
2659
|
+
const proc = spawn2(command, args, {
|
|
2426
2660
|
cwd: this.cwdPath,
|
|
2427
2661
|
...options
|
|
2428
2662
|
});
|
|
@@ -2554,7 +2788,7 @@ class Executor {
|
|
|
2554
2788
|
contentStr = currentContent;
|
|
2555
2789
|
} else {
|
|
2556
2790
|
await FileSys.writeText(writePath, contentStr);
|
|
2557
|
-
if (
|
|
2791
|
+
if (Logger3.isVerbose())
|
|
2558
2792
|
this.logger.rawLog(chalk4.yellow(`File Update: ${filePath}`));
|
|
2559
2793
|
}
|
|
2560
2794
|
} else {
|
|
@@ -2663,8 +2897,8 @@ class Executor {
|
|
|
2663
2897
|
return null;
|
|
2664
2898
|
const filename = typeof result === "object" ? result.filename : path7.basename(targetPath).replace(".js", ".ts");
|
|
2665
2899
|
const content = typeof result === "object" ? result.content : result;
|
|
2666
|
-
const
|
|
2667
|
-
const convertedTargetPath = Object.entries(dict).reduce((path8, [key, value]) => path8.replace(new RegExp(`__${key}__`, "g"), value), `${
|
|
2900
|
+
const dirname2 = path7.dirname(targetPath);
|
|
2901
|
+
const convertedTargetPath = Object.entries(dict).reduce((path8, [key, value]) => path8.replace(new RegExp(`__${key}__`, "g"), value), `${dirname2}/${filename}`);
|
|
2668
2902
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2669
2903
|
return this.writeFile(convertedTargetPath, content, { overwrite });
|
|
2670
2904
|
} else if (targetPath.endsWith(".template")) {
|
|
@@ -2678,9 +2912,9 @@ class Executor {
|
|
|
2678
2912
|
} else if (staticTemplateFileExtensions.has(path7.extname(targetPath).toLowerCase())) {
|
|
2679
2913
|
const convertedTargetPath = Object.entries(dict).reduce((path8, [key, value]) => path8.replace(new RegExp(`__${key}__`, "g"), value), targetPath);
|
|
2680
2914
|
const writePath = this.getPath(convertedTargetPath);
|
|
2681
|
-
const
|
|
2682
|
-
if (!await FileSys.dirExists(
|
|
2683
|
-
await mkdir2(
|
|
2915
|
+
const dirname2 = path7.dirname(writePath);
|
|
2916
|
+
if (!await FileSys.dirExists(dirname2))
|
|
2917
|
+
await mkdir2(dirname2, { recursive: true });
|
|
2684
2918
|
await copyFile(templatePath, writePath);
|
|
2685
2919
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2686
2920
|
return { filePath: writePath, content: "" };
|
|
@@ -2761,6 +2995,48 @@ class Executor {
|
|
|
2761
2995
|
const message = typeChecker.formatDiagnostics(fileDiagnostics);
|
|
2762
2996
|
return { fileDiagnostics, fileErrors, fileWarnings, message };
|
|
2763
2997
|
}
|
|
2998
|
+
async typeCheckAsync(filePath) {
|
|
2999
|
+
const path8 = this.getPath(filePath);
|
|
3000
|
+
const entry = await this.#resolveTypecheckWorkerEntry();
|
|
3001
|
+
const proc = Bun.spawn([process.execPath, entry], {
|
|
3002
|
+
cwd: this.cwdPath,
|
|
3003
|
+
env: {
|
|
3004
|
+
...process.env,
|
|
3005
|
+
AKAN_TYPECHECK_CWD: this.cwdPath,
|
|
3006
|
+
AKAN_TYPECHECK_FILE: path8
|
|
3007
|
+
},
|
|
3008
|
+
stdout: "pipe",
|
|
3009
|
+
stderr: "pipe"
|
|
3010
|
+
});
|
|
3011
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
3012
|
+
new Response(proc.stdout).text(),
|
|
3013
|
+
new Response(proc.stderr).text(),
|
|
3014
|
+
proc.exited
|
|
3015
|
+
]);
|
|
3016
|
+
if (exitCode !== 0)
|
|
3017
|
+
throw new Error((stderr || stdout).trim() || `Typecheck failed with exit code ${exitCode}`);
|
|
3018
|
+
const result = JSON.parse(stdout);
|
|
3019
|
+
return {
|
|
3020
|
+
fileDiagnostics: Array.from({ length: result.fileDiagnosticsCount }),
|
|
3021
|
+
fileErrors: Array.from({ length: result.fileErrorsCount }),
|
|
3022
|
+
fileWarnings: Array.from({ length: result.fileWarningsCount }),
|
|
3023
|
+
message: result.message
|
|
3024
|
+
};
|
|
3025
|
+
}
|
|
3026
|
+
async#resolveTypecheckWorkerEntry() {
|
|
3027
|
+
const dirname2 = getDirname(import.meta.url);
|
|
3028
|
+
const candidates = [
|
|
3029
|
+
path7.join(process.cwd(), "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
3030
|
+
path7.join(process.cwd(), "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
3031
|
+
path7.join(dirname2, "typecheck/typecheck.proc.ts"),
|
|
3032
|
+
path7.join(dirname2, "typecheck.proc.js"),
|
|
3033
|
+
path7.join(dirname2, "typecheck.proc.ts")
|
|
3034
|
+
];
|
|
3035
|
+
for (const candidate of candidates)
|
|
3036
|
+
if (await Bun.file(candidate).exists())
|
|
3037
|
+
return candidate;
|
|
3038
|
+
throw new Error(`[devkit] typecheck worker entry not found; looked in: ${candidates.join(", ")}`);
|
|
3039
|
+
}
|
|
2764
3040
|
getLinter() {
|
|
2765
3041
|
this.linter ??= new Linter(this.cwdPath);
|
|
2766
3042
|
return this.linter;
|
|
@@ -2808,7 +3084,15 @@ class WorkspaceExecutor extends Executor {
|
|
|
2808
3084
|
const env = sourceEnv.AKAN_PUBLIC_ENV ?? "debug";
|
|
2809
3085
|
if (!env)
|
|
2810
3086
|
throw new Error("AKAN_PUBLIC_ENV is not set");
|
|
2811
|
-
return {
|
|
3087
|
+
return {
|
|
3088
|
+
...appName ? { appName } : {},
|
|
3089
|
+
workspaceRoot,
|
|
3090
|
+
repoName,
|
|
3091
|
+
serveDomain,
|
|
3092
|
+
env,
|
|
3093
|
+
portOffset,
|
|
3094
|
+
workspaceId
|
|
3095
|
+
};
|
|
2812
3096
|
}
|
|
2813
3097
|
getWorkspaceId({
|
|
2814
3098
|
allowEmpty
|
|
@@ -2868,12 +3152,12 @@ class WorkspaceExecutor extends Executor {
|
|
|
2868
3152
|
}
|
|
2869
3153
|
async getDirInModule(basePath2, name) {
|
|
2870
3154
|
const AVOID_DIRS = ["__lib", "__scalar", `_`, `_${name}`];
|
|
2871
|
-
const getDirs = async (
|
|
2872
|
-
const dirs = await this.readdir(
|
|
3155
|
+
const getDirs = async (dirname2, maxDepth = 3, results = [], prefix = "") => {
|
|
3156
|
+
const dirs = await this.readdir(dirname2);
|
|
2873
3157
|
await Promise.all(dirs.map(async (dir) => {
|
|
2874
3158
|
if (dir.includes("_") || AVOID_DIRS.includes(dir))
|
|
2875
3159
|
return;
|
|
2876
|
-
const dirPath = path7.join(
|
|
3160
|
+
const dirPath = path7.join(dirname2, dir);
|
|
2877
3161
|
if ((await stat2(dirPath)).isDirectory()) {
|
|
2878
3162
|
results.push(`${prefix}${dir}`);
|
|
2879
3163
|
if (maxDepth > 0)
|
|
@@ -2893,12 +3177,12 @@ class WorkspaceExecutor extends Executor {
|
|
|
2893
3177
|
}
|
|
2894
3178
|
async#getDirHasFile(basePath2, targetFilename) {
|
|
2895
3179
|
const AVOID_DIRS = ["node_modules", "dist", "public", "webkit"];
|
|
2896
|
-
const getDirs = async (
|
|
2897
|
-
const dirs = await this.readdir(
|
|
3180
|
+
const getDirs = async (dirname2, maxDepth = 3, results = [], prefix = "") => {
|
|
3181
|
+
const dirs = await this.readdir(dirname2);
|
|
2898
3182
|
await Promise.all(dirs.map(async (dir) => {
|
|
2899
3183
|
if (AVOID_DIRS.includes(dir))
|
|
2900
3184
|
return;
|
|
2901
|
-
const dirPath = path7.join(
|
|
3185
|
+
const dirPath = path7.join(dirname2, dir);
|
|
2902
3186
|
if ((await stat2(dirPath)).isDirectory()) {
|
|
2903
3187
|
const hasTargetFile = await FileSys.fileExists(path7.join(dirPath, targetFilename));
|
|
2904
3188
|
if (hasTargetFile)
|
|
@@ -3163,6 +3447,13 @@ class AppExecutor extends SysExecutor {
|
|
|
3163
3447
|
getEnv() {
|
|
3164
3448
|
return WorkspaceExecutor.getBaseDevEnv().env;
|
|
3165
3449
|
}
|
|
3450
|
+
async getDevPort() {
|
|
3451
|
+
const basePort = 8282;
|
|
3452
|
+
const appNames = (await this.workspace.getApps()).sort((a, b) => a.localeCompare(b));
|
|
3453
|
+
const appIndex = Math.max(appNames.indexOf(this.name), 0);
|
|
3454
|
+
const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
|
|
3455
|
+
return basePort + appIndex + portOffset;
|
|
3456
|
+
}
|
|
3166
3457
|
getCommandEnv(env = {}) {
|
|
3167
3458
|
const basePort = 8282;
|
|
3168
3459
|
const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
|
|
@@ -3196,7 +3487,12 @@ class AppExecutor extends SysExecutor {
|
|
|
3196
3487
|
]);
|
|
3197
3488
|
} else
|
|
3198
3489
|
await this.removeDir(".akan");
|
|
3199
|
-
const
|
|
3490
|
+
const devPort = type === "start" ? (await this.getDevPort()).toString() : undefined;
|
|
3491
|
+
const env = this.getCommandEnv({
|
|
3492
|
+
AKAN_COMMAND_TYPE: type,
|
|
3493
|
+
...routeEnv,
|
|
3494
|
+
...devPort ? { PORT: devPort, AKAN_PUBLIC_CLIENT_PORT: devPort, AKAN_PUBLIC_SERVER_PORT: devPort } : {}
|
|
3495
|
+
});
|
|
3200
3496
|
return { env };
|
|
3201
3497
|
}
|
|
3202
3498
|
#publicEnv = null;
|
|
@@ -3615,7 +3911,7 @@ var createTunnel = async (service, { app, environment, port = service === "postg
|
|
|
3615
3911
|
|
|
3616
3912
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.host.ts
|
|
3617
3913
|
import path8 from "path";
|
|
3618
|
-
import { Logger as
|
|
3914
|
+
import { Logger as Logger4 } from "akanjs/common";
|
|
3619
3915
|
var builderMsgTypeSet = new Set([
|
|
3620
3916
|
"build-route-res",
|
|
3621
3917
|
"builder-ready",
|
|
@@ -3627,7 +3923,7 @@ var builderMsgTypeSet = new Set([
|
|
|
3627
3923
|
class IncrementalBuilderHost {
|
|
3628
3924
|
static #restartBaseDelayMs = 1000;
|
|
3629
3925
|
static #restartMaxDelayMs = 30000;
|
|
3630
|
-
logger = new
|
|
3926
|
+
logger = new Logger4("IncrementalBuilderHost");
|
|
3631
3927
|
entry;
|
|
3632
3928
|
env;
|
|
3633
3929
|
app;
|
|
@@ -3875,7 +4171,7 @@ class BackendImportGraph {
|
|
|
3875
4171
|
|
|
3876
4172
|
class AkanAppHost {
|
|
3877
4173
|
app;
|
|
3878
|
-
logger = new
|
|
4174
|
+
logger = new Logger5("AkanAppHost");
|
|
3879
4175
|
withInk;
|
|
3880
4176
|
env;
|
|
3881
4177
|
#backend = null;
|
|
@@ -4158,19 +4454,19 @@ class AkanAppHost {
|
|
|
4158
4454
|
}
|
|
4159
4455
|
}
|
|
4160
4456
|
// pkgs/@akanjs/devkit/applicationBuildReporter.ts
|
|
4161
|
-
import { Logger as
|
|
4457
|
+
import { Logger as Logger6 } from "akanjs/common";
|
|
4162
4458
|
|
|
4163
4459
|
class ApplicationBuildReporter {
|
|
4164
4460
|
static create() {
|
|
4165
4461
|
return {
|
|
4166
|
-
phaseDone: (phase) =>
|
|
4462
|
+
phaseDone: (phase) => Logger6.rawLog(ApplicationBuildReporter.formatPhaseLine(phase))
|
|
4167
4463
|
};
|
|
4168
4464
|
}
|
|
4169
4465
|
static printSummary(result) {
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4466
|
+
Logger6.rawLog("");
|
|
4467
|
+
Logger6.rawLog(`Route artifacts: ${result.artifactDir}`);
|
|
4468
|
+
Logger6.rawLog(`Server output: ${result.outputDir}`);
|
|
4469
|
+
Logger6.rawLog(`Done in ${ApplicationBuildReporter.formatDuration(result.durationMs)}`);
|
|
4174
4470
|
}
|
|
4175
4471
|
static formatError(error) {
|
|
4176
4472
|
if (error instanceof AggregateError) {
|
|
@@ -4322,6 +4618,9 @@ export async function generateHead(props: PageProps) {
|
|
|
4322
4618
|
return inheritedLayout.head;
|
|
4323
4619
|
}
|
|
4324
4620
|
|
|
4621
|
+
export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
|
|
4622
|
+
export const Error = userLayout.Error ?? inheritedLayout.Error;
|
|
4623
|
+
|
|
4325
4624
|
export default function GeneratedLayout({ children, params, searchParams }: LayoutProps) {
|
|
4326
4625
|
return (
|
|
4327
4626
|
<System.Provider
|
|
@@ -4351,6 +4650,9 @@ export async function generateHead(props: PageProps) {
|
|
|
4351
4650
|
return inheritedLayout.head;
|
|
4352
4651
|
}
|
|
4353
4652
|
|
|
4653
|
+
export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
|
|
4654
|
+
export const Error = userLayout.Error ?? inheritedLayout.Error;
|
|
4655
|
+
|
|
4354
4656
|
export default function GeneratedLayout({ children, params, searchParams }: LayoutProps) {
|
|
4355
4657
|
return <UserLayout params={params} searchParams={searchParams}>{children}</UserLayout>;
|
|
4356
4658
|
}
|
|
@@ -4469,13 +4771,13 @@ import path14 from "path";
|
|
|
4469
4771
|
|
|
4470
4772
|
// pkgs/@akanjs/devkit/transforms/barrelAnalyzer.ts
|
|
4471
4773
|
import path12 from "path";
|
|
4472
|
-
import { Logger as
|
|
4774
|
+
import { Logger as Logger7 } from "akanjs/common";
|
|
4473
4775
|
var REEXPORT_RE = /(?:^|\n)\s*export\s+(?:type\s+)?(?:(\*)(?:\s+as\s+(\w+))?|\{\s*([^}]*?)\s*\})\s+from\s+(["'])([^"']+)\4;?/g;
|
|
4474
4776
|
var LOCAL_NAMED_RE = /(?:^|\n)\s*export\s+\{\s*([^}]*?)\s*\}(?!\s*from)/g;
|
|
4475
4777
|
var CANDIDATE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
4476
4778
|
|
|
4477
4779
|
class BarrelAnalyzer {
|
|
4478
|
-
#logger = new
|
|
4780
|
+
#logger = new Logger7("BarrelAnalyzer");
|
|
4479
4781
|
#opts;
|
|
4480
4782
|
#cache = new Map;
|
|
4481
4783
|
#tsTranspiler = new Bun.Transpiler({ loader: "ts" });
|
|
@@ -6175,7 +6477,7 @@ ${CsrArtifactBuilder.escapeInlineScript(await loadScript(src))}
|
|
|
6175
6477
|
}
|
|
6176
6478
|
// pkgs/@akanjs/devkit/frontendBuild/cssCompiler.ts
|
|
6177
6479
|
import path23 from "path";
|
|
6178
|
-
import { Logger as
|
|
6480
|
+
import { Logger as Logger8 } from "akanjs/common";
|
|
6179
6481
|
import { compile } from "tailwindcss";
|
|
6180
6482
|
|
|
6181
6483
|
// pkgs/@akanjs/devkit/frontendBuild/cssImportResolver.ts
|
|
@@ -6320,7 +6622,7 @@ var NODE_MODULES_RE3 = /[\\/]node_modules[\\/]/;
|
|
|
6320
6622
|
var AKANJS_NODE_MODULE_RE3 = /[\\/]node_modules[\\/]akanjs[\\/]/;
|
|
6321
6623
|
|
|
6322
6624
|
class CssCompiler {
|
|
6323
|
-
#logger = new
|
|
6625
|
+
#logger = new Logger8("CssCompiler");
|
|
6324
6626
|
#transpiler = new Bun.Transpiler({ loader: "tsx" });
|
|
6325
6627
|
#app;
|
|
6326
6628
|
#cssImportResolver = null;
|
|
@@ -7728,7 +8030,7 @@ class ApplicationBuildRunner {
|
|
|
7728
8030
|
import { cp, mkdir as mkdir8, rm as rm3 } from "fs/promises";
|
|
7729
8031
|
|
|
7730
8032
|
// pkgs/@akanjs/devkit/uploadRelease.ts
|
|
7731
|
-
import { HttpClient as HttpClient2, Logger as
|
|
8033
|
+
import { HttpClient as HttpClient2, Logger as Logger9 } from "akanjs/common";
|
|
7732
8034
|
var spinning = (message) => {
|
|
7733
8035
|
const spinner = new Spinner(message, { prefix: message, enableSpin: true }).start();
|
|
7734
8036
|
return spinner;
|
|
@@ -7741,7 +8043,7 @@ var uploadRelease = async (appName, {
|
|
|
7741
8043
|
os,
|
|
7742
8044
|
local
|
|
7743
8045
|
}) => {
|
|
7744
|
-
const logger = new
|
|
8046
|
+
const logger = new Logger9("uploadRelease");
|
|
7745
8047
|
const basePath2 = local ? "http://localhost:8282/backend" : "https://cloud.akanjs.com/backend";
|
|
7746
8048
|
const httpClient = new HttpClient2(basePath2);
|
|
7747
8049
|
const buildPath = `${workspaceRoot}/releases/builds/${appName}-release.tar.gz`;
|
|
@@ -8727,7 +9029,7 @@ var Workspace = createInternalArgToken("Workspace");
|
|
|
8727
9029
|
// pkgs/@akanjs/devkit/commandDecorators/command.ts
|
|
8728
9030
|
import path36 from "path";
|
|
8729
9031
|
import { confirm, input as input2, select as select2 } from "@inquirer/prompts";
|
|
8730
|
-
import { Logger as
|
|
9032
|
+
import { Logger as Logger10 } from "akanjs/common";
|
|
8731
9033
|
import chalk6 from "chalk";
|
|
8732
9034
|
import { program } from "commander";
|
|
8733
9035
|
|
|
@@ -9012,7 +9314,7 @@ var printCliError = (error) => {
|
|
|
9012
9314
|
if (loggedCliErrorMessages.has(message))
|
|
9013
9315
|
return;
|
|
9014
9316
|
loggedCliErrorMessages.add(message);
|
|
9015
|
-
|
|
9317
|
+
Logger10.rawLog(`
|
|
9016
9318
|
${chalk6.red(message)}`);
|
|
9017
9319
|
};
|
|
9018
9320
|
var handleOption = (programCommand, argMeta) => {
|
|
@@ -9241,7 +9543,7 @@ var runCommands = async (...commands) => {
|
|
|
9241
9543
|
const hasCommand = process.argv.length > 2 && !process.argv[2]?.startsWith("-");
|
|
9242
9544
|
if (hasHelpFlag || !hasCommand) {
|
|
9243
9545
|
if (process.argv.length === 2 || process.argv.length === 3 && hasHelpFlag) {
|
|
9244
|
-
|
|
9546
|
+
Logger10.rawLog(formatHelp(commands, process.env.AKAN_VERSION));
|
|
9245
9547
|
process.exit(0);
|
|
9246
9548
|
}
|
|
9247
9549
|
}
|
|
@@ -9250,7 +9552,7 @@ var runCommands = async (...commands) => {
|
|
|
9250
9552
|
});
|
|
9251
9553
|
const installedAkanPackageJson = await FileSys.fileExists("./node_modules/akanjs/package.json") ? await FileSys.readJson("./node_modules/akanjs/package.json") : null;
|
|
9252
9554
|
if (installedAkanPackageJson && installedAkanPackageJson.version !== process.env.AKAN_VERSION) {
|
|
9253
|
-
|
|
9555
|
+
Logger10.rawLog(chalk6.yellow(`
|
|
9254
9556
|
Akan CLI version is mismatch with installed package. ${process.env.AKAN_VERSION} (global) vs ${installedAkanPackageJson.version} (akanjs)
|
|
9255
9557
|
It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`));
|
|
9256
9558
|
}
|
|
@@ -9286,7 +9588,7 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
|
|
|
9286
9588
|
return formatCommandHelp(command, targetMeta.key);
|
|
9287
9589
|
};
|
|
9288
9590
|
programCommand.action(async (...args) => {
|
|
9289
|
-
|
|
9591
|
+
Logger10.rawLog();
|
|
9290
9592
|
const cmdArgs = args.slice(0, args.length - 2);
|
|
9291
9593
|
const opt = args[args.length - 2];
|
|
9292
9594
|
const commandArgs = [];
|
|
@@ -9310,7 +9612,7 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
|
|
|
9310
9612
|
const cmd = CommandContainer.get(command);
|
|
9311
9613
|
try {
|
|
9312
9614
|
await targetMeta.handler.call(cmd, ...commandArgs);
|
|
9313
|
-
|
|
9615
|
+
Logger10.rawLog();
|
|
9314
9616
|
} catch (e) {
|
|
9315
9617
|
printCliError(e);
|
|
9316
9618
|
throw e;
|
|
@@ -9650,8 +9952,11 @@ import fsPromise from "fs/promises";
|
|
|
9650
9952
|
import { input as input3, select as select3 } from "@inquirer/prompts";
|
|
9651
9953
|
class Prompter {
|
|
9652
9954
|
static async#getGuidelineRoot() {
|
|
9653
|
-
const
|
|
9654
|
-
const candidates = [
|
|
9955
|
+
const dirname2 = getDirname(import.meta.url);
|
|
9956
|
+
const candidates = [
|
|
9957
|
+
`${dirname2}/guidelines`,
|
|
9958
|
+
`${dirname2}/../cli/guidelines`
|
|
9959
|
+
];
|
|
9655
9960
|
for (const candidate of candidates) {
|
|
9656
9961
|
try {
|
|
9657
9962
|
await fsPromise.access(candidate);
|
|
@@ -9663,7 +9968,10 @@ class Prompter {
|
|
|
9663
9968
|
static async selectGuideline() {
|
|
9664
9969
|
const guidelineRoot = await Prompter.#getGuidelineRoot();
|
|
9665
9970
|
const guideNames = (await fsPromise.readdir(guidelineRoot)).filter((name) => !name.startsWith("_"));
|
|
9666
|
-
return await select3({
|
|
9971
|
+
return await select3({
|
|
9972
|
+
message: "Select a guideline",
|
|
9973
|
+
choices: guideNames.map((name) => ({ name, value: name }))
|
|
9974
|
+
});
|
|
9667
9975
|
}
|
|
9668
9976
|
static async getGuideJson(guideName) {
|
|
9669
9977
|
const guidelineRoot = await Prompter.#getGuidelineRoot();
|
|
@@ -9678,13 +9986,18 @@ class Prompter {
|
|
|
9678
9986
|
return content;
|
|
9679
9987
|
}
|
|
9680
9988
|
static async getUpdateRequest(guideName) {
|
|
9681
|
-
return await input3({
|
|
9989
|
+
return await input3({
|
|
9990
|
+
message: `What do you want to update in ${guideName}?`
|
|
9991
|
+
});
|
|
9682
9992
|
}
|
|
9683
9993
|
async makeTsFileUpdatePrompt({ context, request }) {
|
|
9684
9994
|
return `You are a senior developer writing TypeScript-based programs using Akan.js, an in-house framework. Here's an overview of the Akan.js framework:
|
|
9685
9995
|
${await this.getDocumentation("framework")}
|
|
9686
9996
|
Please understand the following background information, write code that meets the requirements, verify that it satisfies the validation conditions, and return the result.
|
|
9687
9997
|
|
|
9998
|
+
# Code Style
|
|
9999
|
+
- Use double quotes for all string literals in TypeScript/TSX code. Do not use single quotes.
|
|
10000
|
+
|
|
9688
10001
|
# Background Information
|
|
9689
10002
|
\`\`\`markdown
|
|
9690
10003
|
${context}
|
|
@@ -9734,7 +10047,8 @@ import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime"
|
|
|
9734
10047
|
import { select as select6 } from "@inquirer/prompts";
|
|
9735
10048
|
|
|
9736
10049
|
// pkgs/@akanjs/cli/application/application.script.ts
|
|
9737
|
-
import {
|
|
10050
|
+
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
10051
|
+
import { Logger as Logger12 } from "akanjs/common";
|
|
9738
10052
|
|
|
9739
10053
|
// pkgs/@akanjs/cli/semver.ts
|
|
9740
10054
|
function parseVersion(version) {
|
|
@@ -9852,14 +10166,14 @@ import { StringOutputParser } from "@langchain/core/output_parsers";
|
|
|
9852
10166
|
import { PromptTemplate as PromptTemplate2 } from "@langchain/core/prompts";
|
|
9853
10167
|
import { RunnableSequence as RunnableSequence2 } from "@langchain/core/runnables";
|
|
9854
10168
|
import { ChatOpenAI as ChatOpenAI3 } from "@langchain/openai";
|
|
9855
|
-
import { Logger as
|
|
10169
|
+
import { Logger as Logger11 } from "akanjs/common";
|
|
9856
10170
|
import ora3 from "ora";
|
|
9857
10171
|
|
|
9858
10172
|
// pkgs/@akanjs/cli/openBrowser.ts
|
|
9859
|
-
import { spawn as
|
|
10173
|
+
import { spawn as spawn3 } from "child_process";
|
|
9860
10174
|
function openBrowser(url) {
|
|
9861
10175
|
const command3 = process.platform === "darwin" ? ["open", url] : process.platform === "win32" ? ["cmd", "/c", "start", "", url] : ["xdg-open", url];
|
|
9862
|
-
const child =
|
|
10176
|
+
const child = spawn3(command3[0], command3.slice(1), { detached: true, stdio: "ignore" });
|
|
9863
10177
|
child.on("error", () => {});
|
|
9864
10178
|
child.unref();
|
|
9865
10179
|
return Promise.resolve();
|
|
@@ -9937,7 +10251,7 @@ class ApplicationRunner extends runner("application") {
|
|
|
9937
10251
|
const appHost = await new AkanAppHost(app, { env, withInk }).start();
|
|
9938
10252
|
onStart?.();
|
|
9939
10253
|
if (open)
|
|
9940
|
-
setTimeout(() => openBrowser(
|
|
10254
|
+
setTimeout(() => openBrowser(`http://localhost:${env.AKAN_PUBLIC_CLIENT_PORT ?? env.PORT ?? "8282"}`), 3000);
|
|
9941
10255
|
return appHost;
|
|
9942
10256
|
}
|
|
9943
10257
|
async buildIos(app, { target, env = "debug", regenerate = false } = {}) {
|
|
@@ -10045,7 +10359,7 @@ class ApplicationRunner extends runner("application") {
|
|
|
10045
10359
|
return;
|
|
10046
10360
|
for (const failure of failures) {
|
|
10047
10361
|
const message = failure.error instanceof Error ? failure.error.message : String(failure.error);
|
|
10048
|
-
|
|
10362
|
+
Logger11.rawLog(`Mobile target ${failure.target} failed: ${message}`, undefined, "error");
|
|
10049
10363
|
}
|
|
10050
10364
|
throw new Error(`${failures.length}/${results.length} mobile targets failed`);
|
|
10051
10365
|
}
|
|
@@ -10162,7 +10476,38 @@ ${detail}`
|
|
|
10162
10476
|
}
|
|
10163
10477
|
|
|
10164
10478
|
// pkgs/@akanjs/cli/application/application.script.ts
|
|
10165
|
-
class ApplicationScript extends script("application", [
|
|
10479
|
+
class ApplicationScript extends script("application", [
|
|
10480
|
+
ApplicationRunner,
|
|
10481
|
+
LibraryScript
|
|
10482
|
+
]) {
|
|
10483
|
+
async confirmDatabaseModeDependencyInstall(databaseMode, installSpecs) {
|
|
10484
|
+
return await confirm3({
|
|
10485
|
+
message: [
|
|
10486
|
+
`Database mode '${databaseMode}' requires missing dependencies: ${installSpecs.join(", ")}.`,
|
|
10487
|
+
"Install them now?"
|
|
10488
|
+
].join(" "),
|
|
10489
|
+
default: true
|
|
10490
|
+
});
|
|
10491
|
+
}
|
|
10492
|
+
async syncDatabaseModeDependencies(app, akanConfig3, databaseMode) {
|
|
10493
|
+
const installSpecs = akanConfig3.getMissingDatabaseModeDependencySpecs(databaseMode);
|
|
10494
|
+
if (installSpecs.length === 0)
|
|
10495
|
+
return;
|
|
10496
|
+
const shouldInstall = await this.confirmDatabaseModeDependencyInstall(databaseMode, installSpecs);
|
|
10497
|
+
if (!shouldInstall)
|
|
10498
|
+
throw new Error(`Database mode '${databaseMode}' requires missing dependencies: ${installSpecs.join(", ")}.`);
|
|
10499
|
+
const spinner2 = app.workspace.spinning(`Installing database dependencies for ${databaseMode} mode...`);
|
|
10500
|
+
try {
|
|
10501
|
+
await app.workspace.spawn("bun", ["add", ...installSpecs], {
|
|
10502
|
+
stdio: "inherit"
|
|
10503
|
+
});
|
|
10504
|
+
await app.workspace.getPackageJson({ refresh: true });
|
|
10505
|
+
spinner2.succeed(`Installed database dependencies for ${databaseMode} mode`);
|
|
10506
|
+
} catch (error) {
|
|
10507
|
+
spinner2.fail(`Failed to install database dependencies for ${databaseMode} mode`);
|
|
10508
|
+
throw error;
|
|
10509
|
+
}
|
|
10510
|
+
}
|
|
10166
10511
|
async createApplication(appName, workspace, { start = false, libs = [] } = {}) {
|
|
10167
10512
|
const spinner2 = workspace.spinning("Creating application...");
|
|
10168
10513
|
const app = await this.applicationRunner.createApplication(appName, workspace, libs);
|
|
@@ -10187,25 +10532,33 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
10187
10532
|
await app.scanSync();
|
|
10188
10533
|
await this.applicationRunner.runScript(app, scriptFilename);
|
|
10189
10534
|
}
|
|
10190
|
-
async build(app, {
|
|
10535
|
+
async build(app, {
|
|
10536
|
+
write = true,
|
|
10537
|
+
fast = false,
|
|
10538
|
+
quiet = false
|
|
10539
|
+
} = {}) {
|
|
10191
10540
|
await app.scanSync({ write });
|
|
10192
10541
|
if (!quiet)
|
|
10193
|
-
|
|
10542
|
+
Logger12.rawLog(`Creating an optimized production build for ${app.name}...`);
|
|
10194
10543
|
try {
|
|
10195
10544
|
const result = await this.applicationRunner.build(app, {
|
|
10196
10545
|
fast,
|
|
10197
10546
|
spinner: !quiet
|
|
10198
10547
|
});
|
|
10199
|
-
|
|
10548
|
+
Logger12.rawLog(`${app.name} built in dist/apps/${app.name}`);
|
|
10200
10549
|
if (!quiet)
|
|
10201
10550
|
ApplicationBuildReporter.printSummary(result);
|
|
10202
10551
|
} catch (error) {
|
|
10203
|
-
|
|
10204
|
-
|
|
10552
|
+
Logger12.rawLog(`${app.name} build failed in dist/apps/${app.name}`, undefined, "error");
|
|
10553
|
+
Logger12.rawLog(ApplicationBuildReporter.formatError(error), undefined, "error");
|
|
10205
10554
|
throw error;
|
|
10206
10555
|
}
|
|
10207
10556
|
}
|
|
10208
|
-
async typecheck(app, {
|
|
10557
|
+
async typecheck(app, {
|
|
10558
|
+
write = true,
|
|
10559
|
+
clean = false,
|
|
10560
|
+
incremental = true
|
|
10561
|
+
} = {}) {
|
|
10209
10562
|
await app.scanSync({ write });
|
|
10210
10563
|
const spinner2 = app.spinning(`Typechecking ${app.name}...`);
|
|
10211
10564
|
try {
|
|
@@ -10213,7 +10566,7 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
10213
10566
|
spinner2.succeed(`${app.name} typechecked`);
|
|
10214
10567
|
} catch (error) {
|
|
10215
10568
|
spinner2.fail(`${app.name} typecheck failed`);
|
|
10216
|
-
|
|
10569
|
+
Logger12.rawLog(ApplicationBuildReporter.formatError(error), undefined, "error");
|
|
10217
10570
|
throw error;
|
|
10218
10571
|
}
|
|
10219
10572
|
}
|
|
@@ -10247,6 +10600,7 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
10247
10600
|
await app.scanSync({ write });
|
|
10248
10601
|
const akanConfig3 = await app.getConfig();
|
|
10249
10602
|
const databaseMode = process.env.AKAN_DATABASE_MODE ?? akanConfig3.defaultDatabaseMode ?? "single";
|
|
10603
|
+
await this.syncDatabaseModeDependencies(app, akanConfig3, databaseMode);
|
|
10250
10604
|
if (app.getEnv() === "local" && dbup && databaseMode !== "single") {
|
|
10251
10605
|
const wasDbAlreadyUp = await this.dbup(app.workspace, databaseMode);
|
|
10252
10606
|
if (!wasDbAlreadyUp)
|
|
@@ -10265,7 +10619,12 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
10265
10619
|
});
|
|
10266
10620
|
return akanAppHost;
|
|
10267
10621
|
}
|
|
10268
|
-
async buildIos(app, {
|
|
10622
|
+
async buildIos(app, {
|
|
10623
|
+
write = true,
|
|
10624
|
+
target,
|
|
10625
|
+
env = "debug",
|
|
10626
|
+
regenerate = false
|
|
10627
|
+
} = {}) {
|
|
10269
10628
|
await app.scanSync({ write });
|
|
10270
10629
|
await this.applicationRunner.buildIos(app, { target, env, regenerate });
|
|
10271
10630
|
}
|
|
@@ -10278,15 +10637,32 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
10278
10637
|
regenerate = false
|
|
10279
10638
|
} = {}) {
|
|
10280
10639
|
await app.scanSync({ write });
|
|
10281
|
-
await this.applicationRunner.startIos(app, {
|
|
10640
|
+
await this.applicationRunner.startIos(app, {
|
|
10641
|
+
open,
|
|
10642
|
+
operation,
|
|
10643
|
+
env,
|
|
10644
|
+
target,
|
|
10645
|
+
regenerate
|
|
10646
|
+
});
|
|
10282
10647
|
}
|
|
10283
|
-
async releaseIos(app, {
|
|
10648
|
+
async releaseIos(app, {
|
|
10649
|
+
write = true,
|
|
10650
|
+
target,
|
|
10651
|
+
env = "main",
|
|
10652
|
+
regenerate = false,
|
|
10653
|
+
allowLocalRelease = false
|
|
10654
|
+
} = {}) {
|
|
10284
10655
|
await app.scanSync({ write });
|
|
10285
10656
|
if (env === "local" && !allowLocalRelease)
|
|
10286
10657
|
throw new Error("releaseIos --env local is blocked. Pass allowLocalRelease only for explicit local release testing.");
|
|
10287
10658
|
await this.applicationRunner.releaseIos(app, { target, env, regenerate });
|
|
10288
10659
|
}
|
|
10289
|
-
async buildAndroid(app, {
|
|
10660
|
+
async buildAndroid(app, {
|
|
10661
|
+
write = true,
|
|
10662
|
+
target,
|
|
10663
|
+
env = "debug",
|
|
10664
|
+
regenerate = false
|
|
10665
|
+
} = {}) {
|
|
10290
10666
|
await app.scanSync({ write });
|
|
10291
10667
|
await this.applicationRunner.buildAndroid(app, { target, env, regenerate });
|
|
10292
10668
|
}
|
|
@@ -10299,13 +10675,29 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
10299
10675
|
regenerate = false
|
|
10300
10676
|
} = {}) {
|
|
10301
10677
|
await app.scanSync({ write });
|
|
10302
|
-
await this.applicationRunner.startAndroid(app, {
|
|
10678
|
+
await this.applicationRunner.startAndroid(app, {
|
|
10679
|
+
open,
|
|
10680
|
+
operation,
|
|
10681
|
+
env,
|
|
10682
|
+
target,
|
|
10683
|
+
regenerate
|
|
10684
|
+
});
|
|
10303
10685
|
}
|
|
10304
|
-
async releaseAndroid(app, assembleType, {
|
|
10686
|
+
async releaseAndroid(app, assembleType, {
|
|
10687
|
+
write = true,
|
|
10688
|
+
target,
|
|
10689
|
+
env = "main",
|
|
10690
|
+
regenerate = false,
|
|
10691
|
+
allowLocalRelease = false
|
|
10692
|
+
} = {}) {
|
|
10305
10693
|
await app.scanSync({ write });
|
|
10306
10694
|
if (env === "local" && !allowLocalRelease)
|
|
10307
10695
|
throw new Error("releaseAndroid --env local is blocked. Pass allowLocalRelease only for explicit local release testing.");
|
|
10308
|
-
await this.applicationRunner.releaseAndroid(app, assembleType, {
|
|
10696
|
+
await this.applicationRunner.releaseAndroid(app, assembleType, {
|
|
10697
|
+
target,
|
|
10698
|
+
env,
|
|
10699
|
+
regenerate
|
|
10700
|
+
});
|
|
10309
10701
|
}
|
|
10310
10702
|
async configureApp(app) {
|
|
10311
10703
|
await this.applicationRunner.configureApp(app);
|
|
@@ -10477,11 +10869,11 @@ class ApplicationCommand extends command("application", [ApplicationScript], ({
|
|
|
10477
10869
|
}
|
|
10478
10870
|
|
|
10479
10871
|
// pkgs/@akanjs/cli/cloud/cloud.script.ts
|
|
10480
|
-
import { Logger as
|
|
10872
|
+
import { Logger as Logger15 } from "akanjs/common";
|
|
10481
10873
|
|
|
10482
10874
|
// pkgs/@akanjs/cli/package/package.runner.ts
|
|
10483
10875
|
import path37 from "path";
|
|
10484
|
-
import { Logger as
|
|
10876
|
+
import { Logger as Logger13 } from "akanjs/common";
|
|
10485
10877
|
var {$: $2 } = globalThis.Bun;
|
|
10486
10878
|
|
|
10487
10879
|
class PackageRunner extends runner("package") {
|
|
@@ -10489,7 +10881,7 @@ class PackageRunner extends runner("package") {
|
|
|
10489
10881
|
const pkgJson = process.env.USE_AKANJS_PKGS === "true" ? await FileSys.readJson(`${workspace?.workspaceRoot ?? process.cwd()}/pkgs/akanjs/package.json`) : await this.#getInstalledPackageJson();
|
|
10490
10882
|
const version = pkgJson.name === "akanjs" ? pkgJson.version : pkgJson.dependencies?.akanjs ?? pkgJson.version;
|
|
10491
10883
|
if (log)
|
|
10492
|
-
|
|
10884
|
+
Logger13.rawLog(`akanjs@${version}`);
|
|
10493
10885
|
return version;
|
|
10494
10886
|
}
|
|
10495
10887
|
async#getInstalledPackageJson() {
|
|
@@ -10616,8 +11008,8 @@ class PackageScript extends script("package", [PackageRunner]) {
|
|
|
10616
11008
|
|
|
10617
11009
|
// pkgs/@akanjs/cli/cloud/cloud.runner.ts
|
|
10618
11010
|
import path38 from "path";
|
|
10619
|
-
import { confirm as
|
|
10620
|
-
import { Logger as
|
|
11011
|
+
import { confirm as confirm4, input as input5, select as select7 } from "@inquirer/prompts";
|
|
11012
|
+
import { Logger as Logger14, sleep } from "akanjs/common";
|
|
10621
11013
|
import chalk7 from "chalk";
|
|
10622
11014
|
import * as QRcode from "qrcode";
|
|
10623
11015
|
|
|
@@ -10642,12 +11034,7 @@ var addRemoteEnvServerValue = "__addRemoteEnvServer";
|
|
|
10642
11034
|
var removeRemoteEnvServerValue = "__removeRemoteEnvServer";
|
|
10643
11035
|
|
|
10644
11036
|
class CloudRunner extends runner("cloud") {
|
|
10645
|
-
#akanFrameworkPackages = new Set([
|
|
10646
|
-
"akanjs",
|
|
10647
|
-
"@akanjs/devkit",
|
|
10648
|
-
"@akanjs/cli",
|
|
10649
|
-
"create-akan-workspace"
|
|
10650
|
-
]);
|
|
11037
|
+
#akanFrameworkPackages = new Set(["akanjs", "@akanjs/devkit", "@akanjs/cli", "create-akan-workspace"]);
|
|
10651
11038
|
#getRegistryArgs(registryUrl) {
|
|
10652
11039
|
return registryUrl ? ["--registry", getNpmRegistryUrl(registryUrl)] : [];
|
|
10653
11040
|
}
|
|
@@ -10697,7 +11084,7 @@ class CloudRunner extends runner("cloud") {
|
|
|
10697
11084
|
const servers = await GlobalConfig.getRemoteEnvServers();
|
|
10698
11085
|
const serverEntries = Object.entries(servers).sort(([nameA], [nameB]) => nameA.localeCompare(nameB));
|
|
10699
11086
|
if (serverEntries.length === 0) {
|
|
10700
|
-
|
|
11087
|
+
Logger14.info("No remote env servers configured. Add the first remote server for SCP mode.");
|
|
10701
11088
|
return await this.#addRemoteEnvServer();
|
|
10702
11089
|
}
|
|
10703
11090
|
const selectedName = await select7({
|
|
@@ -10730,14 +11117,14 @@ class CloudRunner extends runner("cloud") {
|
|
|
10730
11117
|
value: name
|
|
10731
11118
|
}))
|
|
10732
11119
|
});
|
|
10733
|
-
const shouldRemove = await
|
|
11120
|
+
const shouldRemove = await confirm4({
|
|
10734
11121
|
message: `Remove remote env server "${selectedName}"?`,
|
|
10735
11122
|
default: false
|
|
10736
11123
|
});
|
|
10737
11124
|
if (!shouldRemove)
|
|
10738
11125
|
return;
|
|
10739
11126
|
await GlobalConfig.removeRemoteEnvServer(selectedName);
|
|
10740
|
-
|
|
11127
|
+
Logger14.info(`Removed remote env server "${selectedName}"`);
|
|
10741
11128
|
}
|
|
10742
11129
|
async#getRemoteEnvServerWithUsername() {
|
|
10743
11130
|
const remoteServer = await this.#selectRemoteEnvServer();
|
|
@@ -10768,35 +11155,27 @@ class CloudRunner extends runner("cloud") {
|
|
|
10768
11155
|
return `${config.username ? `${config.username}@` : ""}${config.host}`;
|
|
10769
11156
|
}
|
|
10770
11157
|
#getScpArgs(config, source, target) {
|
|
10771
|
-
return [
|
|
10772
|
-
...config.port ? ["-P", config.port.toString()] : [],
|
|
10773
|
-
source,
|
|
10774
|
-
target
|
|
10775
|
-
];
|
|
11158
|
+
return [...config.port ? ["-P", config.port.toString()] : [], source, target];
|
|
10776
11159
|
}
|
|
10777
11160
|
#getSshArgs(config, command3) {
|
|
10778
|
-
return [
|
|
10779
|
-
...config.port ? ["-p", config.port.toString()] : [],
|
|
10780
|
-
this.#getSshTarget(config),
|
|
10781
|
-
command3
|
|
10782
|
-
];
|
|
11161
|
+
return [...config.port ? ["-p", config.port.toString()] : [], this.#getSshTarget(config), command3];
|
|
10783
11162
|
}
|
|
10784
|
-
async login() {
|
|
11163
|
+
async login(workspace) {
|
|
10785
11164
|
const config = await GlobalConfig.getHostConfig();
|
|
10786
|
-
const cloudApi2 = new CloudApi(config);
|
|
11165
|
+
const cloudApi2 = new CloudApi(workspace, config);
|
|
10787
11166
|
const self = config.auth ? await cloudApi2.getRemoteSelf() : null;
|
|
10788
11167
|
if (self) {
|
|
10789
|
-
|
|
11168
|
+
Logger14.rawLog(chalk7.green(`
|
|
10790
11169
|
\u2713 Already logged in akan cloud as ${self.nickname}
|
|
10791
11170
|
`));
|
|
10792
11171
|
return true;
|
|
10793
11172
|
}
|
|
10794
11173
|
const remoteId = crypto.randomUUID();
|
|
10795
11174
|
const signinUrl = `${akanCloudUrl}/signin?remoteId=${remoteId}`;
|
|
10796
|
-
|
|
11175
|
+
Logger14.rawLog(chalk7.bold(`
|
|
10797
11176
|
${chalk7.green("\u27A4")} Authentication Required`));
|
|
10798
|
-
|
|
10799
|
-
|
|
11177
|
+
Logger14.rawLog(chalk7.dim("Please visit or click the following URL:"));
|
|
11178
|
+
Logger14.rawLog(`${chalk7.cyan.underline(signinUrl)}
|
|
10800
11179
|
`);
|
|
10801
11180
|
try {
|
|
10802
11181
|
const qrcode = await new Promise((resolve, reject) => {
|
|
@@ -10806,13 +11185,13 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
10806
11185
|
resolve(data);
|
|
10807
11186
|
});
|
|
10808
11187
|
});
|
|
10809
|
-
|
|
11188
|
+
Logger14.rawLog(qrcode);
|
|
10810
11189
|
await openBrowser(signinUrl);
|
|
10811
|
-
|
|
11190
|
+
Logger14.rawLog(chalk7.dim("Opening browser..."));
|
|
10812
11191
|
} catch {
|
|
10813
|
-
|
|
11192
|
+
Logger14.rawLog(chalk7.yellow("Could not open browser. Please visit the URL manually."));
|
|
10814
11193
|
}
|
|
10815
|
-
|
|
11194
|
+
Logger14.rawLog(chalk7.dim("Waiting for authentication..."));
|
|
10816
11195
|
const MAX_RETRY = 300;
|
|
10817
11196
|
for (let i = 0;i < MAX_RETRY; i++) {
|
|
10818
11197
|
const accessToken = await cloudApi2.getRemoteAuthToken(remoteId);
|
|
@@ -10821,10 +11200,10 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
10821
11200
|
await GlobalConfig.setHostConfig(akanCloudHost, {
|
|
10822
11201
|
auth: { accessToken, self: self2 }
|
|
10823
11202
|
});
|
|
10824
|
-
|
|
10825
|
-
|
|
11203
|
+
Logger14.rawLog(chalk7.green(`\r\u2713 Authentication successful!`));
|
|
11204
|
+
Logger14.rawLog(chalk7.green.bold(`
|
|
10826
11205
|
\u2728 Welcome aboard, ${self2.nickname}!`));
|
|
10827
|
-
|
|
11206
|
+
Logger14.rawLog(chalk7.dim(`You're now ready to use Akan CLI!
|
|
10828
11207
|
`));
|
|
10829
11208
|
return true;
|
|
10830
11209
|
}
|
|
@@ -10836,17 +11215,17 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
10836
11215
|
const config = await GlobalConfig.getHostConfig();
|
|
10837
11216
|
if (config.auth?.self) {
|
|
10838
11217
|
await GlobalConfig.setHostConfig(akanCloudHost, {});
|
|
10839
|
-
|
|
11218
|
+
Logger14.rawLog(chalk7.magenta.bold(`
|
|
10840
11219
|
\uD83D\uDC4B Goodbye, ${config.auth.self.nickname}!`));
|
|
10841
|
-
|
|
11220
|
+
Logger14.rawLog(chalk7.dim(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
10842
11221
|
`));
|
|
10843
|
-
|
|
10844
|
-
|
|
11222
|
+
Logger14.rawLog(chalk7.cyan("You have been successfully logged out."));
|
|
11223
|
+
Logger14.rawLog(chalk7.dim(`Thank you for using Akan CLI. Come back soon! \uD83C\uDF1F
|
|
10845
11224
|
`));
|
|
10846
11225
|
} else {
|
|
10847
|
-
|
|
11226
|
+
Logger14.rawLog(chalk7.yellow.bold(`
|
|
10848
11227
|
\u26A0\uFE0F No active session found`));
|
|
10849
|
-
|
|
11228
|
+
Logger14.rawLog(chalk7.dim(`You were not logged in to begin with
|
|
10850
11229
|
`));
|
|
10851
11230
|
}
|
|
10852
11231
|
}
|
|
@@ -10855,7 +11234,7 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
10855
11234
|
}
|
|
10856
11235
|
resetLlm() {
|
|
10857
11236
|
AiSession.setLlmConfig(null);
|
|
10858
|
-
|
|
11237
|
+
Logger14.rawLog(chalk7.green("\u2611\uFE0F LLM model config is cleared. Please run `akan set-llm` to set a new LLM model."));
|
|
10859
11238
|
}
|
|
10860
11239
|
async getAkanPkgs(workspace) {
|
|
10861
11240
|
const pkgs = await workspace.getPkgs();
|
|
@@ -10879,8 +11258,8 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
10879
11258
|
}
|
|
10880
11259
|
};
|
|
10881
11260
|
const { nextVersion, latestPublishedVersion } = await getNextVersion(targetVersionPrefix, tag);
|
|
10882
|
-
|
|
10883
|
-
|
|
11261
|
+
Logger14.info(`Latest published version of akanjs: ${latestPublishedVersion ?? "none"}`);
|
|
11262
|
+
Logger14.info(`Next version of akanjs: ${nextVersion}`);
|
|
10884
11263
|
for (const library of akanPkgs) {
|
|
10885
11264
|
const packageJson = await workspace.readJson(`pkgs/${library}/package.json`);
|
|
10886
11265
|
const newPackageJsonStr = JSON.stringify(this.#normalizeAkanPackageJson(packageJson, library, nextVersion), null, 2);
|
|
@@ -10890,30 +11269,24 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
10890
11269
|
await workspace.writeJson(`dist/pkgs/${library}/package.json`, newDistPackageJson);
|
|
10891
11270
|
}
|
|
10892
11271
|
if (confirmPublish) {
|
|
10893
|
-
const isDeployConfirmed = await
|
|
11272
|
+
const isDeployConfirmed = await confirm4({
|
|
10894
11273
|
message: "Are you sure you want to deploy the libraries?"
|
|
10895
11274
|
});
|
|
10896
11275
|
if (!isDeployConfirmed) {
|
|
10897
|
-
|
|
11276
|
+
Logger14.error("Deployment cancelled");
|
|
10898
11277
|
return;
|
|
10899
11278
|
}
|
|
10900
11279
|
}
|
|
10901
11280
|
await Promise.all(akanPkgs.map(async (library) => {
|
|
10902
|
-
|
|
10903
|
-
await workspace.spawn("npm", [
|
|
10904
|
-
"publish",
|
|
10905
|
-
"--tag",
|
|
10906
|
-
tag,
|
|
10907
|
-
...this.#getRegistryArgs(registry),
|
|
10908
|
-
...this.#getLocalRegistryAuthArgs(registry)
|
|
10909
|
-
], {
|
|
11281
|
+
Logger14.info(`Publishing ${library}@${nextVersion} to ${registry ?? "npm"}...`);
|
|
11282
|
+
await workspace.spawn("npm", ["publish", "--tag", tag, ...this.#getRegistryArgs(registry), ...this.#getLocalRegistryAuthArgs(registry)], {
|
|
10910
11283
|
cwd: path38.join(workspace.workspaceRoot, "dist/pkgs", library),
|
|
10911
11284
|
env: this.#getRegistryEnv(registry),
|
|
10912
11285
|
stdio: "inherit"
|
|
10913
11286
|
});
|
|
10914
|
-
|
|
11287
|
+
Logger14.info(`${library}@${nextVersion} is published to ${registry ?? "npm"}`);
|
|
10915
11288
|
}));
|
|
10916
|
-
|
|
11289
|
+
Logger14.info(`All libraries are published to ${registry ?? "npm"}`);
|
|
10917
11290
|
}
|
|
10918
11291
|
async update(workspace, tag = "latest", { registryUrl } = {}) {
|
|
10919
11292
|
const registry = registryUrl ? getNpmRegistryUrl(registryUrl) : undefined;
|
|
@@ -10923,14 +11296,7 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
10923
11296
|
await workspace.spawn("bun", ["update", "-g", "akanjs", "--latest", `--tag=${tag}`, ...registryArgs], { env });
|
|
10924
11297
|
else
|
|
10925
11298
|
await Promise.all([
|
|
10926
|
-
workspace.spawn("bun", [
|
|
10927
|
-
"update",
|
|
10928
|
-
"-g",
|
|
10929
|
-
"akanjs",
|
|
10930
|
-
"--latest",
|
|
10931
|
-
`--tag=${tag}`,
|
|
10932
|
-
...registryArgs
|
|
10933
|
-
], { env }),
|
|
11299
|
+
workspace.spawn("bun", ["update", "-g", "akanjs", "--latest", `--tag=${tag}`, ...registryArgs], { env }),
|
|
10934
11300
|
this.#updateAkanPkgs(workspace, tag, registry)
|
|
10935
11301
|
]);
|
|
10936
11302
|
}
|
|
@@ -10954,12 +11320,7 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
10954
11320
|
}
|
|
10955
11321
|
#normalizeAkanPackageJson(packageJson, packageName, version) {
|
|
10956
11322
|
const normalized = { ...packageJson, version };
|
|
10957
|
-
for (const field of [
|
|
10958
|
-
"dependencies",
|
|
10959
|
-
"devDependencies",
|
|
10960
|
-
"peerDependencies",
|
|
10961
|
-
"optionalDependencies"
|
|
10962
|
-
]) {
|
|
11323
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
10963
11324
|
const dependencies = normalized[field];
|
|
10964
11325
|
if (!dependencies)
|
|
10965
11326
|
continue;
|
|
@@ -10974,7 +11335,7 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
10974
11335
|
const envArchivePath = "local/env.tar";
|
|
10975
11336
|
await workspace.mkdir("local");
|
|
10976
11337
|
await workspace.remove(envArchivePath);
|
|
10977
|
-
await cloudApi2.downloadEnv(workspaceId
|
|
11338
|
+
await cloudApi2.downloadEnv(workspaceId);
|
|
10978
11339
|
await workspace.spawn("tar", ["-xf", envArchivePath], {
|
|
10979
11340
|
cwd: workspace.workspaceRoot
|
|
10980
11341
|
});
|
|
@@ -10992,7 +11353,7 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
10992
11353
|
await workspace.mkdir("local");
|
|
10993
11354
|
await workspace.remove(envArchivePath);
|
|
10994
11355
|
try {
|
|
10995
|
-
|
|
11356
|
+
Logger14.info(`Downloading env archive from remote server "${remoteServer.name}"...`);
|
|
10996
11357
|
await workspace.spawn("scp", this.#getScpArgs(remoteServer.config, remoteTarget, envArchivePath), {
|
|
10997
11358
|
cwd: workspace.workspaceRoot,
|
|
10998
11359
|
stdio: "inherit"
|
|
@@ -11015,7 +11376,7 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
11015
11376
|
cwd: workspace.workspaceRoot,
|
|
11016
11377
|
stdio: "inherit"
|
|
11017
11378
|
});
|
|
11018
|
-
|
|
11379
|
+
Logger14.info(`Uploading env archive to remote server "${remoteServer.name}"...`);
|
|
11019
11380
|
await workspace.spawn("scp", this.#getScpArgs(remoteServer.config, filePath, remoteTarget), {
|
|
11020
11381
|
cwd: workspace.workspaceRoot,
|
|
11021
11382
|
stdio: "inherit"
|
|
@@ -11039,19 +11400,15 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
11039
11400
|
await workspace.spawn("tar", ["-cf", "local/env.tar", ...envFilePaths], {
|
|
11040
11401
|
cwd: workspace.workspaceRoot
|
|
11041
11402
|
});
|
|
11042
|
-
|
|
11403
|
+
Logger14.info(`Archived ${envFilePaths.length} environment files to local/env.tar`);
|
|
11043
11404
|
return { files: envFilePaths, path: "local/env.tar" };
|
|
11044
11405
|
}
|
|
11045
11406
|
}
|
|
11046
11407
|
|
|
11047
11408
|
// pkgs/@akanjs/cli/cloud/cloud.script.ts
|
|
11048
|
-
class CloudScript extends script("cloud", [
|
|
11049
|
-
CloudRunner,
|
|
11050
|
-
ApplicationScript,
|
|
11051
|
-
PackageScript
|
|
11052
|
-
]) {
|
|
11409
|
+
class CloudScript extends script("cloud", [CloudRunner, ApplicationScript, PackageScript]) {
|
|
11053
11410
|
async login(workspace) {
|
|
11054
|
-
await this.cloudRunner.login();
|
|
11411
|
+
await this.cloudRunner.login(workspace);
|
|
11055
11412
|
}
|
|
11056
11413
|
async logout(workspace) {
|
|
11057
11414
|
await this.cloudRunner.logout();
|
|
@@ -11069,7 +11426,7 @@ class CloudScript extends script("cloud", [
|
|
|
11069
11426
|
async downloadEnv(workspace) {
|
|
11070
11427
|
const workspaceId = workspace.getWorkspaceId({ allowEmpty: true });
|
|
11071
11428
|
if (workspaceId) {
|
|
11072
|
-
const cloudApi2 = await CloudApi.fromHost();
|
|
11429
|
+
const cloudApi2 = await CloudApi.fromHost(workspace);
|
|
11073
11430
|
await this.cloudRunner.downloadEnv(cloudApi2, workspace, workspaceId);
|
|
11074
11431
|
return;
|
|
11075
11432
|
}
|
|
@@ -11079,7 +11436,7 @@ class CloudScript extends script("cloud", [
|
|
|
11079
11436
|
const workspaceId = workspace.getWorkspaceId({ allowEmpty: true });
|
|
11080
11437
|
const { path: path39 } = await this.cloudRunner.gatherEnvFiles(workspace);
|
|
11081
11438
|
if (workspaceId) {
|
|
11082
|
-
const cloudApi2 = await CloudApi.fromHost();
|
|
11439
|
+
const cloudApi2 = await CloudApi.fromHost(workspace);
|
|
11083
11440
|
await this.cloudRunner.uploadEnv(cloudApi2, workspaceId, path39);
|
|
11084
11441
|
return;
|
|
11085
11442
|
}
|
|
@@ -11100,7 +11457,7 @@ class CloudScript extends script("cloud", [
|
|
|
11100
11457
|
const spinner2 = workspace.spinning("Updating Akan.js packages and CLI...");
|
|
11101
11458
|
await this.cloudRunner.update(workspace, tag, { registryUrl });
|
|
11102
11459
|
spinner2.succeed("Akan.js packages and CLI updated, global version is below");
|
|
11103
|
-
|
|
11460
|
+
Logger15.raw("> Akan version: ");
|
|
11104
11461
|
await workspace.spawn("akan", ["--version"], { stdio: "inherit" });
|
|
11105
11462
|
}
|
|
11106
11463
|
}
|
|
@@ -11154,7 +11511,8 @@ class CloudCommand extends command("cloud", [CloudScript], ({ public: target })
|
|
|
11154
11511
|
enum: [
|
|
11155
11512
|
{ label: "npm", value: "npm" },
|
|
11156
11513
|
{ label: "local", value: "local" }
|
|
11157
|
-
]
|
|
11514
|
+
],
|
|
11515
|
+
default: process.env.USE_AKANJS_PKGS === "true" ? undefined : "npm"
|
|
11158
11516
|
}).exec(async function(workspace, tag, registry) {
|
|
11159
11517
|
await this.cloudScript.update(workspace, tag, {
|
|
11160
11518
|
registryUrl: resolveRegistryUrl(registry)
|
|
@@ -11499,7 +11857,7 @@ class LibraryCommand extends command("library", [LibraryScript], ({ public: targ
|
|
|
11499
11857
|
// pkgs/@akanjs/cli/localRegistry/localRegistry.runner.ts
|
|
11500
11858
|
import { mkdir as mkdir11, rm as rm5 } from "fs/promises";
|
|
11501
11859
|
import path39 from "path";
|
|
11502
|
-
import { Logger as
|
|
11860
|
+
import { Logger as Logger16 } from "akanjs/common";
|
|
11503
11861
|
var defaultLocalRegistryUrl = "http://127.0.0.1:4873";
|
|
11504
11862
|
var containerName = "akan-verdaccio";
|
|
11505
11863
|
var smokeRepoName = "akan-local-smoke";
|
|
@@ -11513,7 +11871,7 @@ class LocalRegistryRunner extends runner("localRegistry") {
|
|
|
11513
11871
|
const registry = this.getRegistryUrl(registryUrl);
|
|
11514
11872
|
try {
|
|
11515
11873
|
await workspace.spawn("docker", ["inspect", containerName]);
|
|
11516
|
-
|
|
11874
|
+
Logger16.info(`Local registry is already running at ${registry}`);
|
|
11517
11875
|
return registry;
|
|
11518
11876
|
} catch {}
|
|
11519
11877
|
const configPath2 = path39.join(workspace.workspaceRoot, "pkgs/@akanjs/cli/localRegistry/verdaccio.yaml");
|
|
@@ -11533,7 +11891,7 @@ class LocalRegistryRunner extends runner("localRegistry") {
|
|
|
11533
11891
|
`${storagePath}:/verdaccio/storage`,
|
|
11534
11892
|
"verdaccio/verdaccio:6"
|
|
11535
11893
|
], { stdio: "inherit" });
|
|
11536
|
-
|
|
11894
|
+
Logger16.info(`Local registry is running at ${registry}`);
|
|
11537
11895
|
return registry;
|
|
11538
11896
|
}
|
|
11539
11897
|
async reset(workspace) {
|
|
@@ -11541,7 +11899,7 @@ class LocalRegistryRunner extends runner("localRegistry") {
|
|
|
11541
11899
|
await workspace.spawn("docker", ["rm", "-f", containerName], { stdio: "inherit" });
|
|
11542
11900
|
} catch {}
|
|
11543
11901
|
await rm5(path39.join(workspace.workspaceRoot, ".akan/verdaccio"), { recursive: true, force: true });
|
|
11544
|
-
|
|
11902
|
+
Logger16.info("Local registry storage has been reset");
|
|
11545
11903
|
}
|
|
11546
11904
|
async smoke(workspace, { registryUrl } = {}) {
|
|
11547
11905
|
const registry = this.getRegistryUrl(registryUrl);
|
|
@@ -11567,7 +11925,7 @@ class LocalRegistryRunner extends runner("localRegistry") {
|
|
|
11567
11925
|
env: { ...process.env, AKAN_NPM_REGISTRY: registry, NPM_CONFIG_REGISTRY: registry },
|
|
11568
11926
|
stdio: "inherit"
|
|
11569
11927
|
});
|
|
11570
|
-
|
|
11928
|
+
Logger16.info(`Local registry smoke test completed for ${smokeRepoName}/${smokeAppName}`);
|
|
11571
11929
|
}
|
|
11572
11930
|
}
|
|
11573
11931
|
|
|
@@ -12433,7 +12791,7 @@ class ScalarCommand extends command("scalar", [ScalarScript], ({ public: target
|
|
|
12433
12791
|
|
|
12434
12792
|
// pkgs/@akanjs/cli/workspace/workspace.script.ts
|
|
12435
12793
|
import path41 from "path";
|
|
12436
|
-
import { Logger as
|
|
12794
|
+
import { Logger as Logger17 } from "akanjs/common";
|
|
12437
12795
|
|
|
12438
12796
|
// pkgs/@akanjs/cli/workspace/workspace.runner.ts
|
|
12439
12797
|
import path40 from "path";
|
|
@@ -12457,16 +12815,16 @@ var defaultWorkspacePeerDependencies = new Set([
|
|
|
12457
12815
|
|
|
12458
12816
|
class WorkspaceRunner extends runner("workspace") {
|
|
12459
12817
|
async createWorkspace(repoName, appName, {
|
|
12460
|
-
dirname:
|
|
12818
|
+
dirname: dirname2 = ".",
|
|
12461
12819
|
init = true,
|
|
12462
12820
|
akanVersion,
|
|
12463
12821
|
registryUrl
|
|
12464
12822
|
}) {
|
|
12465
12823
|
const cwdPath = process.cwd();
|
|
12466
|
-
const workspaceRoot = path40.join(cwdPath,
|
|
12824
|
+
const workspaceRoot = path40.join(cwdPath, dirname2, repoName);
|
|
12467
12825
|
const normalizedRegistryUrl = registryUrl ? getNpmRegistryUrl(registryUrl) : undefined;
|
|
12468
12826
|
const workspace = WorkspaceExecutor.fromRoot({ workspaceRoot, repoName });
|
|
12469
|
-
const templateSpinner = workspace.spinning(`Creating workspace template files in ${
|
|
12827
|
+
const templateSpinner = workspace.spinning(`Creating workspace template files in ${dirname2}/${repoName}...`);
|
|
12470
12828
|
const [latestBiomeVersion, latestTypesBunVersion] = await Promise.all([
|
|
12471
12829
|
getLatestPackageVersion("@biomejs/biome", "latest", normalizedRegistryUrl),
|
|
12472
12830
|
getLatestPackageVersion("@types/bun", "latest", normalizedRegistryUrl)
|
|
@@ -12479,7 +12837,7 @@ class WorkspaceRunner extends runner("workspace") {
|
|
|
12479
12837
|
if (normalizedRegistryUrl)
|
|
12480
12838
|
await workspace.writeFile(".npmrc", `registry=${normalizedRegistryUrl}/
|
|
12481
12839
|
`);
|
|
12482
|
-
templateSpinner.succeed(`Workspace files created in ${
|
|
12840
|
+
templateSpinner.succeed(`Workspace files created in ${dirname2}/${repoName}`);
|
|
12483
12841
|
const [rootPackageJson, peerDependencies] = await Promise.all([
|
|
12484
12842
|
workspace.getPackageJson(),
|
|
12485
12843
|
this.#getAkanPeerDependencies()
|
|
@@ -12584,14 +12942,14 @@ class WorkspaceScript extends script("workspace", [
|
|
|
12584
12942
|
PackageScript
|
|
12585
12943
|
]) {
|
|
12586
12944
|
async createWorkspace(repoName, appName, {
|
|
12587
|
-
dirname:
|
|
12945
|
+
dirname: dirname2 = ".",
|
|
12588
12946
|
installLibs = false,
|
|
12589
12947
|
init = true,
|
|
12590
12948
|
registryUrl
|
|
12591
12949
|
}) {
|
|
12592
12950
|
const akanVersion = await this.packageScript.version(null, { log: false });
|
|
12593
12951
|
const workspace = await this.workspaceRunner.createWorkspace(repoName, appName, {
|
|
12594
|
-
dirname:
|
|
12952
|
+
dirname: dirname2,
|
|
12595
12953
|
init,
|
|
12596
12954
|
akanVersion,
|
|
12597
12955
|
...registryUrl ? { registryUrl } : {}
|
|
@@ -12608,11 +12966,11 @@ class WorkspaceScript extends script("workspace", [
|
|
|
12608
12966
|
} catch (_) {
|
|
12609
12967
|
gitSpinner.fail("Git repository initialization failed. It's not fatal, you can commit manually");
|
|
12610
12968
|
}
|
|
12611
|
-
const workspacePath = path41.join(
|
|
12612
|
-
|
|
12613
|
-
\uD83C\uDF89 Welcome aboard! Workspace created in ${
|
|
12614
|
-
|
|
12615
|
-
|
|
12969
|
+
const workspacePath = path41.join(dirname2, repoName);
|
|
12970
|
+
Logger17.rawLog(`
|
|
12971
|
+
\uD83C\uDF89 Welcome aboard! Workspace created in ${dirname2}/${repoName}`);
|
|
12972
|
+
Logger17.rawLog(`\uD83D\uDE80 Run \`cd ${workspacePath} && akan start ${appName}\` to start the development server.`);
|
|
12973
|
+
Logger17.rawLog(`
|
|
12616
12974
|
\uD83D\uDC4B Happy coding!`);
|
|
12617
12975
|
}
|
|
12618
12976
|
async lint(exec2, workspace, { fix = true } = {}) {
|