@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/cloud/cloudApi.ts CHANGED
@@ -1,9 +1,5 @@
1
- import {
2
- type AccessToken,
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, localPath: string): Promise<void> {
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
- return await this.refreshAuthToken();
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 response = await this.#api.post<AccessTokenDto>(
116
- `/refreshRemoteAuthToken`,
117
- {
118
- refreshToken: this.#accessToken?.refreshToken,
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", () => {