@akanjs/devkit 2.1.1 → 2.1.2-rc.1

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,21 @@ 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<unknown> {
74
+ const localPath = `${this.#workspace.workspaceRoot}/local/env.tar`;
93
75
  await this.#api.getFile(`/downloadEnv/${devProjectId}`, localPath);
76
+ return localPath;
94
77
  }
95
78
  async getRemoteAuthToken(remoteId: string): Promise<AccessToken | null> {
96
79
  try {
97
80
  if (this.#accessToken) {
98
- if (GlobalConfig.needRefreshToken(this.#accessToken))
99
- return await this.refreshAuthToken();
100
- else return await this.refreshAuthToken();
81
+ if (GlobalConfig.needRefreshToken(this.#accessToken)) return await this.#refreshAuthToken();
82
+ else return await this.#refreshAuthToken();
101
83
  }
102
- const accessToken = await this.#api.get<AccessTokenDto>(
103
- `/getRemoteAuthToken/${remoteId}`,
104
- );
84
+ const accessToken = await this.#api.get<AccessTokenDto>(`/getRemoteAuthToken/${remoteId}`);
105
85
  this.#accessToken = GlobalConfig.toAccessToken(accessToken);
106
86
  this.#api.setHeaders({
107
87
  Authorization: `Bearer ${this.#accessToken.jwt}`,
@@ -111,22 +91,20 @@ export class CloudApi {
111
91
  return null;
112
92
  }
113
93
  }
114
- async refreshAuthToken(): Promise<AccessToken> {
115
- const response = await this.#api.post<AccessTokenDto>(
116
- `/refreshRemoteAuthToken`,
117
- {
118
- refreshToken: this.#accessToken?.refreshToken,
119
- },
120
- );
94
+ async #refreshAuthToken(): Promise<AccessToken> {
95
+ const refreshToken = this.#accessToken?.refreshToken;
96
+ if (!refreshToken) throw new Error("No refresh token");
97
+ return await this.refreshAuthToken(refreshToken);
98
+ }
99
+ async refreshAuthToken(refreshToken: string): Promise<AccessToken> {
100
+ const response = await this.#api.post<AccessTokenDto>(`/refreshRemoteAuthToken`, { refreshToken });
121
101
  this.#accessToken = GlobalConfig.toAccessToken(response);
122
102
  this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
123
103
  return this.#accessToken;
124
104
  }
125
105
  async getRemoteSelf(): Promise<{ id: string; nickname: string } | null> {
126
106
  try {
127
- const data = await this.#api.get<{ id: string; nickname: string }>(
128
- `/getRemoteSelf`,
129
- );
107
+ const data = await this.#api.get<{ id: string; nickname: string }>(`/getRemoteSelf`);
130
108
  return data;
131
109
  } catch {
132
110
  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", () => {