@akanjs/devkit 2.1.1 → 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/akanConfig/akanConfig.test.ts +167 -32
- package/akanConfig/akanConfig.ts +130 -31
- package/artifact/implicitRootLayout.test.ts +2 -0
- package/artifact/implicitRootLayout.ts +2 -2
- package/cloud/cloudApi.ts +25 -48
- package/executors.test.ts +47 -0
- package/executors.ts +166 -577
- package/package.json +2 -2
package/cloud/cloudApi.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type AccessTokenDto,
|
|
4
|
-
akanCloudHost,
|
|
5
|
-
type HostConfig,
|
|
6
|
-
} from "./constants";
|
|
1
|
+
import type { Workspace } from "../commandDecorators";
|
|
2
|
+
import { type AccessToken, type AccessTokenDto, akanCloudHost, type HostConfig } from "./constants";
|
|
7
3
|
import { GlobalConfig } from "./globalConfig";
|
|
8
4
|
|
|
9
5
|
class HttpClient {
|
|
@@ -13,10 +9,7 @@ class HttpClient {
|
|
|
13
9
|
this.baseUrl = baseUrl;
|
|
14
10
|
this.headers = headers;
|
|
15
11
|
}
|
|
16
|
-
async get<T>(
|
|
17
|
-
url: string,
|
|
18
|
-
{ headers }: { headers?: Record<string, string> } = {},
|
|
19
|
-
): Promise<T> {
|
|
12
|
+
async get<T>(url: string, { headers }: { headers?: Record<string, string> } = {}): Promise<T> {
|
|
20
13
|
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
21
14
|
headers: {
|
|
22
15
|
"Content-Type": "application/json",
|
|
@@ -26,25 +19,14 @@ class HttpClient {
|
|
|
26
19
|
});
|
|
27
20
|
return response.json();
|
|
28
21
|
}
|
|
29
|
-
async getFile(
|
|
30
|
-
url: string,
|
|
31
|
-
localPath: string,
|
|
32
|
-
headers?: Record<string, string>,
|
|
33
|
-
): Promise<void> {
|
|
22
|
+
async getFile(url: string, localPath: string, headers?: Record<string, string>): Promise<void> {
|
|
34
23
|
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
35
24
|
headers: { ...this.headers, ...headers },
|
|
36
25
|
});
|
|
37
|
-
if (!response.ok)
|
|
38
|
-
throw new Error(
|
|
39
|
-
`Failed to download file: ${response.status} ${response.statusText}`,
|
|
40
|
-
);
|
|
26
|
+
if (!response.ok) throw new Error(`Failed to download file: ${response.status} ${response.statusText}`);
|
|
41
27
|
await Bun.write(localPath, response);
|
|
42
28
|
}
|
|
43
|
-
async post<T>(
|
|
44
|
-
url: string,
|
|
45
|
-
data: unknown,
|
|
46
|
-
{ headers }: { headers?: Record<string, string> } = {},
|
|
47
|
-
): Promise<T> {
|
|
29
|
+
async post<T>(url: string, data: unknown, { headers }: { headers?: Record<string, string> } = {}): Promise<T> {
|
|
48
30
|
const isFormData = data instanceof FormData;
|
|
49
31
|
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
50
32
|
method: "POST",
|
|
@@ -64,12 +46,14 @@ class HttpClient {
|
|
|
64
46
|
export class CloudApi {
|
|
65
47
|
readonly #api: HttpClient;
|
|
66
48
|
#accessToken: AccessToken | null = null;
|
|
49
|
+
#workspace: Workspace;
|
|
67
50
|
|
|
68
|
-
static async fromHost(host?: string) {
|
|
51
|
+
static async fromHost(workspace: Workspace, host?: string) {
|
|
69
52
|
const hostConfig = await GlobalConfig.getHostConfig(host);
|
|
70
|
-
return new CloudApi(hostConfig);
|
|
53
|
+
return new CloudApi(workspace, hostConfig);
|
|
71
54
|
}
|
|
72
|
-
constructor(hostConfig: HostConfig) {
|
|
55
|
+
constructor(workspace: Workspace, hostConfig: HostConfig) {
|
|
56
|
+
this.#workspace = workspace;
|
|
73
57
|
const host = akanCloudHost;
|
|
74
58
|
this.#api = new HttpClient(`${host}/api`);
|
|
75
59
|
this.#accessToken = hostConfig.auth?.accessToken ?? null;
|
|
@@ -83,25 +67,20 @@ export class CloudApi {
|
|
|
83
67
|
const formData = new FormData();
|
|
84
68
|
formData.append("devProjectId", devProjectId);
|
|
85
69
|
formData.append("file", file);
|
|
86
|
-
const data = await this.#api.post<boolean>(
|
|
87
|
-
`/uploadEnv/${devProjectId}`,
|
|
88
|
-
formData,
|
|
89
|
-
);
|
|
70
|
+
const data = await this.#api.post<boolean>(`/uploadEnv/${devProjectId}`, formData);
|
|
90
71
|
return data;
|
|
91
72
|
}
|
|
92
|
-
async downloadEnv(devProjectId: string
|
|
73
|
+
async downloadEnv(devProjectId: string): Promise<void> {
|
|
74
|
+
const localPath = `${this.#workspace.workspaceRoot}/local/env.tar`;
|
|
93
75
|
await this.#api.getFile(`/downloadEnv/${devProjectId}`, localPath);
|
|
94
76
|
}
|
|
95
77
|
async getRemoteAuthToken(remoteId: string): Promise<AccessToken | null> {
|
|
96
78
|
try {
|
|
97
79
|
if (this.#accessToken) {
|
|
98
|
-
if (GlobalConfig.needRefreshToken(this.#accessToken))
|
|
99
|
-
|
|
100
|
-
else return await this.refreshAuthToken();
|
|
80
|
+
if (GlobalConfig.needRefreshToken(this.#accessToken)) return await this.#refreshAuthToken();
|
|
81
|
+
else return await this.#refreshAuthToken();
|
|
101
82
|
}
|
|
102
|
-
const accessToken = await this.#api.get<AccessTokenDto>(
|
|
103
|
-
`/getRemoteAuthToken/${remoteId}`,
|
|
104
|
-
);
|
|
83
|
+
const accessToken = await this.#api.get<AccessTokenDto>(`/getRemoteAuthToken/${remoteId}`);
|
|
105
84
|
this.#accessToken = GlobalConfig.toAccessToken(accessToken);
|
|
106
85
|
this.#api.setHeaders({
|
|
107
86
|
Authorization: `Bearer ${this.#accessToken.jwt}`,
|
|
@@ -111,22 +90,20 @@ export class CloudApi {
|
|
|
111
90
|
return null;
|
|
112
91
|
}
|
|
113
92
|
}
|
|
114
|
-
async refreshAuthToken(): Promise<AccessToken> {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
93
|
+
async #refreshAuthToken(): Promise<AccessToken> {
|
|
94
|
+
const refreshToken = this.#accessToken?.refreshToken;
|
|
95
|
+
if (!refreshToken) throw new Error("No refresh token");
|
|
96
|
+
return await this.refreshAuthToken(refreshToken);
|
|
97
|
+
}
|
|
98
|
+
async refreshAuthToken(refreshToken: string): Promise<AccessToken> {
|
|
99
|
+
const response = await this.#api.post<AccessTokenDto>(`/refreshRemoteAuthToken`, { refreshToken });
|
|
121
100
|
this.#accessToken = GlobalConfig.toAccessToken(response);
|
|
122
101
|
this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
|
|
123
102
|
return this.#accessToken;
|
|
124
103
|
}
|
|
125
104
|
async getRemoteSelf(): Promise<{ id: string; nickname: string } | null> {
|
|
126
105
|
try {
|
|
127
|
-
const data = await this.#api.get<{ id: string; nickname: string }>(
|
|
128
|
-
`/getRemoteSelf`,
|
|
129
|
-
);
|
|
106
|
+
const data = await this.#api.get<{ id: string; nickname: string }>(`/getRemoteSelf`);
|
|
130
107
|
return data;
|
|
131
108
|
} catch {
|
|
132
109
|
return null;
|
package/executors.test.ts
CHANGED
|
@@ -251,6 +251,53 @@ describe("Workspace and app executor environment contracts", () => {
|
|
|
251
251
|
expect((await stat(path.join(root, "dist/apps/demo/private"))).isDirectory()).toBe(true);
|
|
252
252
|
expect((await stat(path.join(root, "dist/apps/demo/public"))).isDirectory()).toBe(true);
|
|
253
253
|
});
|
|
254
|
+
|
|
255
|
+
test("assigns start command ports from sorted app order", async () => {
|
|
256
|
+
const root = await makeTempRoot();
|
|
257
|
+
process.env.AKAN_PUBLIC_REPO_NAME = "repo";
|
|
258
|
+
process.env.AKAN_PUBLIC_SERVE_DOMAIN = "example.com";
|
|
259
|
+
process.env.AKAN_PUBLIC_ENV = "local";
|
|
260
|
+
|
|
261
|
+
await writeJson(path.join(root, "package.json"), rootPackageJson());
|
|
262
|
+
for (const appName of ["minimal", "akan"]) {
|
|
263
|
+
await mkdir(path.join(root, `apps/${appName}`), { recursive: true });
|
|
264
|
+
await writeFile(
|
|
265
|
+
path.join(root, `apps/${appName}/akan.config.ts`),
|
|
266
|
+
[
|
|
267
|
+
"export default {",
|
|
268
|
+
` routes: [{ basePath: "${appName}", domains: { debug: ["${appName}.local:8282"] } }],`,
|
|
269
|
+
"};",
|
|
270
|
+
"",
|
|
271
|
+
].join("\n"),
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const workspace = new WorkspaceExecutor({ workspaceRoot: root, repoName: "repo" });
|
|
276
|
+
const akan = AppExecutor.from(workspace, "akan");
|
|
277
|
+
const minimal = AppExecutor.from(workspace, "minimal");
|
|
278
|
+
|
|
279
|
+
const akanStart = await akan.prepareCommand("start");
|
|
280
|
+
expect(akanStart.env.PORT).toBe("8282");
|
|
281
|
+
expect(akanStart.env.AKAN_PUBLIC_CLIENT_PORT).toBe("8282");
|
|
282
|
+
expect(akanStart.env.AKAN_PUBLIC_SERVER_PORT).toBe("8282");
|
|
283
|
+
|
|
284
|
+
const minimalStart = await minimal.prepareCommand("start");
|
|
285
|
+
expect(minimalStart.env.PORT).toBe("8283");
|
|
286
|
+
expect(minimalStart.env.AKAN_PUBLIC_CLIENT_PORT).toBe("8283");
|
|
287
|
+
expect(minimalStart.env.AKAN_PUBLIC_SERVER_PORT).toBe("8283");
|
|
288
|
+
|
|
289
|
+
process.env.PORT_OFFSET = "3";
|
|
290
|
+
|
|
291
|
+
const offsetAkanStart = await akan.prepareCommand("start");
|
|
292
|
+
expect(offsetAkanStart.env.PORT).toBe("8285");
|
|
293
|
+
expect(offsetAkanStart.env.AKAN_PUBLIC_CLIENT_PORT).toBe("8285");
|
|
294
|
+
expect(offsetAkanStart.env.AKAN_PUBLIC_SERVER_PORT).toBe("8285");
|
|
295
|
+
|
|
296
|
+
const offsetMinimalStart = await minimal.prepareCommand("start");
|
|
297
|
+
expect(offsetMinimalStart.env.PORT).toBe("8286");
|
|
298
|
+
expect(offsetMinimalStart.env.AKAN_PUBLIC_CLIENT_PORT).toBe("8286");
|
|
299
|
+
expect(offsetMinimalStart.env.AKAN_PUBLIC_SERVER_PORT).toBe("8286");
|
|
300
|
+
});
|
|
254
301
|
});
|
|
255
302
|
|
|
256
303
|
describe("PkgExecutor package generation", () => {
|