@base44-preview/cli 0.0.1-pr.10.3ef6431 → 0.0.1-pr.11.72924bc

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.
Files changed (133) hide show
  1. package/dist/cli/index.js +509 -13
  2. package/package.json +5 -9
  3. package/dist/cli/commands/auth/login.d.ts +0 -3
  4. package/dist/cli/commands/auth/login.d.ts.map +0 -1
  5. package/dist/cli/commands/auth/login.js +0 -71
  6. package/dist/cli/commands/auth/login.js.map +0 -1
  7. package/dist/cli/commands/auth/logout.d.ts +0 -3
  8. package/dist/cli/commands/auth/logout.d.ts.map +0 -1
  9. package/dist/cli/commands/auth/logout.js +0 -14
  10. package/dist/cli/commands/auth/logout.js.map +0 -1
  11. package/dist/cli/commands/auth/whoami.d.ts +0 -3
  12. package/dist/cli/commands/auth/whoami.d.ts.map +0 -1
  13. package/dist/cli/commands/auth/whoami.js +0 -14
  14. package/dist/cli/commands/auth/whoami.js.map +0 -1
  15. package/dist/cli/commands/project/show-project.d.ts +0 -3
  16. package/dist/cli/commands/project/show-project.d.ts.map +0 -1
  17. package/dist/cli/commands/project/show-project.js +0 -20
  18. package/dist/cli/commands/project/show-project.js.map +0 -1
  19. package/dist/cli/index.d.ts +0 -3
  20. package/dist/cli/index.d.ts.map +0 -1
  21. package/dist/cli/index.js.map +0 -1
  22. package/dist/cli/utils/index.d.ts +0 -4
  23. package/dist/cli/utils/index.d.ts.map +0 -1
  24. package/dist/cli/utils/index.js +0 -4
  25. package/dist/cli/utils/index.js.map +0 -1
  26. package/dist/cli/utils/packageVersion.d.ts +0 -2
  27. package/dist/cli/utils/packageVersion.d.ts.map +0 -1
  28. package/dist/cli/utils/packageVersion.js +0 -12
  29. package/dist/cli/utils/packageVersion.js.map +0 -1
  30. package/dist/cli/utils/runCommand.d.ts +0 -8
  31. package/dist/cli/utils/runCommand.d.ts.map +0 -1
  32. package/dist/cli/utils/runCommand.js +0 -25
  33. package/dist/cli/utils/runCommand.js.map +0 -1
  34. package/dist/cli/utils/runTask.d.ts +0 -14
  35. package/dist/cli/utils/runTask.d.ts.map +0 -1
  36. package/dist/cli/utils/runTask.js +0 -24
  37. package/dist/cli/utils/runTask.js.map +0 -1
  38. package/dist/core/auth/api.d.ts +0 -5
  39. package/dist/core/auth/api.d.ts.map +0 -1
  40. package/dist/core/auth/api.js +0 -81
  41. package/dist/core/auth/api.js.map +0 -1
  42. package/dist/core/auth/authClient.d.ts +0 -7
  43. package/dist/core/auth/authClient.d.ts.map +0 -1
  44. package/dist/core/auth/authClient.js +0 -14
  45. package/dist/core/auth/authClient.js.map +0 -1
  46. package/dist/core/auth/config.d.ts +0 -15
  47. package/dist/core/auth/config.d.ts.map +0 -1
  48. package/dist/core/auth/config.js +0 -91
  49. package/dist/core/auth/config.js.map +0 -1
  50. package/dist/core/auth/index.d.ts +0 -4
  51. package/dist/core/auth/index.d.ts.map +0 -1
  52. package/dist/core/auth/index.js +0 -4
  53. package/dist/core/auth/index.js.map +0 -1
  54. package/dist/core/auth/schema.d.ts +0 -58
  55. package/dist/core/auth/schema.d.ts.map +0 -1
  56. package/dist/core/auth/schema.js +0 -57
  57. package/dist/core/auth/schema.js.map +0 -1
  58. package/dist/core/config/app.d.ts +0 -22
  59. package/dist/core/config/app.d.ts.map +0 -1
  60. package/dist/core/config/app.js +0 -14
  61. package/dist/core/config/app.js.map +0 -1
  62. package/dist/core/config/index.d.ts +0 -4
  63. package/dist/core/config/index.d.ts.map +0 -1
  64. package/dist/core/config/index.js +0 -3
  65. package/dist/core/config/index.js.map +0 -1
  66. package/dist/core/config/project.d.ts +0 -25
  67. package/dist/core/config/project.d.ts.map +0 -1
  68. package/dist/core/config/project.js +0 -62
  69. package/dist/core/config/project.js.map +0 -1
  70. package/dist/core/config/resource.d.ts +0 -4
  71. package/dist/core/config/resource.d.ts.map +0 -1
  72. package/dist/core/config/resource.js +0 -2
  73. package/dist/core/config/resource.js.map +0 -1
  74. package/dist/core/consts.d.ts +0 -8
  75. package/dist/core/consts.d.ts.map +0 -1
  76. package/dist/core/consts.js +0 -24
  77. package/dist/core/consts.js.map +0 -1
  78. package/dist/core/errors.d.ts +0 -8
  79. package/dist/core/errors.d.ts.map +0 -1
  80. package/dist/core/errors.js +0 -15
  81. package/dist/core/errors.js.map +0 -1
  82. package/dist/core/index.d.ts +0 -6
  83. package/dist/core/index.d.ts.map +0 -1
  84. package/dist/core/index.js +0 -6
  85. package/dist/core/index.js.map +0 -1
  86. package/dist/core/resources/entity/config.d.ts +0 -3
  87. package/dist/core/resources/entity/config.d.ts.map +0 -1
  88. package/dist/core/resources/entity/config.js +0 -25
  89. package/dist/core/resources/entity/config.js.map +0 -1
  90. package/dist/core/resources/entity/index.d.ts +0 -4
  91. package/dist/core/resources/entity/index.d.ts.map +0 -1
  92. package/dist/core/resources/entity/index.js +0 -4
  93. package/dist/core/resources/entity/index.js.map +0 -1
  94. package/dist/core/resources/entity/resource.d.ts +0 -4
  95. package/dist/core/resources/entity/resource.d.ts.map +0 -1
  96. package/dist/core/resources/entity/resource.js +0 -5
  97. package/dist/core/resources/entity/resource.js.map +0 -1
  98. package/dist/core/resources/entity/schema.d.ts +0 -47
  99. package/dist/core/resources/entity/schema.d.ts.map +0 -1
  100. package/dist/core/resources/entity/schema.js +0 -29
  101. package/dist/core/resources/entity/schema.js.map +0 -1
  102. package/dist/core/resources/function/config.d.ts +0 -4
  103. package/dist/core/resources/function/config.d.ts.map +0 -1
  104. package/dist/core/resources/function/config.js +0 -26
  105. package/dist/core/resources/function/config.js.map +0 -1
  106. package/dist/core/resources/function/index.d.ts +0 -4
  107. package/dist/core/resources/function/index.d.ts.map +0 -1
  108. package/dist/core/resources/function/index.js +0 -4
  109. package/dist/core/resources/function/index.js.map +0 -1
  110. package/dist/core/resources/function/resource.d.ts +0 -4
  111. package/dist/core/resources/function/resource.d.ts.map +0 -1
  112. package/dist/core/resources/function/resource.js +0 -5
  113. package/dist/core/resources/function/resource.js.map +0 -1
  114. package/dist/core/resources/function/schema.d.ts +0 -91
  115. package/dist/core/resources/function/schema.d.ts.map +0 -1
  116. package/dist/core/resources/function/schema.js +0 -36
  117. package/dist/core/resources/function/schema.js.map +0 -1
  118. package/dist/core/resources/index.d.ts +0 -3
  119. package/dist/core/resources/index.d.ts.map +0 -1
  120. package/dist/core/resources/index.js +0 -3
  121. package/dist/core/resources/index.js.map +0 -1
  122. package/dist/core/utils/fs.d.ts +0 -5
  123. package/dist/core/utils/fs.d.ts.map +0 -1
  124. package/dist/core/utils/fs.js +0 -56
  125. package/dist/core/utils/fs.js.map +0 -1
  126. package/dist/core/utils/httpClient.d.ts +0 -3
  127. package/dist/core/utils/httpClient.d.ts.map +0 -1
  128. package/dist/core/utils/httpClient.js +0 -57
  129. package/dist/core/utils/httpClient.js.map +0 -1
  130. package/dist/core/utils/index.d.ts +0 -3
  131. package/dist/core/utils/index.d.ts.map +0 -1
  132. package/dist/core/utils/index.js +0 -3
  133. package/dist/core/utils/index.js.map +0 -1
package/dist/cli/index.js CHANGED
@@ -1,21 +1,517 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
- import { getPackageVersion } from "./utils/index.js";
4
- import { loginCommand } from "./commands/auth/login.js";
5
- import { whoamiCommand } from "./commands/auth/whoami.js";
6
- import { logoutCommand } from "./commands/auth/logout.js";
7
- import { showProjectCommand } from "./commands/project/show-project.js";
3
+ import { readFileSync } from "node:fs";
4
+ import { fileURLToPath } from "node:url";
5
+ import { dirname, join } from "node:path";
6
+ import { intro, log, spinner } from "@clack/prompts";
7
+ import chalk from "chalk";
8
+ import pWaitFor from "p-wait-for";
9
+ import { z } from "zod";
10
+ import { homedir } from "node:os";
11
+ import { access, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
12
+ import { parse, printParseErrorCode } from "jsonc-parser";
13
+ import { globby } from "globby";
14
+
15
+ //#region src/cli/utils/packageVersion.ts
16
+ function getPackageVersion() {
17
+ const packageJsonPath = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "package.json");
18
+ return JSON.parse(readFileSync(packageJsonPath, "utf-8")).version;
19
+ }
20
+
21
+ //#endregion
22
+ //#region src/core/errors.ts
23
+ var AuthApiError = class extends Error {
24
+ constructor(message, cause) {
25
+ super(message);
26
+ this.cause = cause;
27
+ this.name = "AuthApiError";
28
+ }
29
+ };
30
+ var AuthValidationError = class extends Error {
31
+ constructor(message, issues) {
32
+ super(message);
33
+ this.issues = issues;
34
+ this.name = "AuthValidationError";
35
+ }
36
+ };
37
+
38
+ //#endregion
39
+ //#region src/cli/utils/runCommand.ts
40
+ const base44Color = chalk.bgHex("#E86B3C");
41
+ /**
42
+ * Wraps a command function with the Base44 intro banner.
43
+ * All CLI commands should use this utility to ensure consistent branding.
44
+ *
45
+ * @param commandFn - The async function to execute as the command
46
+ */
47
+ async function runCommand(commandFn) {
48
+ intro(base44Color(" Base 44 "));
49
+ try {
50
+ await commandFn();
51
+ } catch (e) {
52
+ if (e instanceof AuthValidationError) {
53
+ const issues = e.issues.map((i) => i.message).join(", ");
54
+ log.error(`Invalid response from server: ${issues}`);
55
+ } else if (e instanceof AuthApiError || e instanceof Error) log.error(e.message);
56
+ else log.error(String(e));
57
+ process.exit(1);
58
+ }
59
+ }
60
+
61
+ //#endregion
62
+ //#region src/cli/utils/runTask.ts
63
+ /**
64
+ * Wraps an async operation with automatic spinner management.
65
+ * The spinner is automatically started, and stopped on both success and error.
66
+ *
67
+ * @param startMessage - Message to show when spinner starts
68
+ * @param operation - The async operation to execute
69
+ * @param options - Optional configuration
70
+ * @returns The result of the operation
71
+ */
72
+ async function runTask(startMessage, operation, options) {
73
+ const s = spinner();
74
+ s.start(startMessage);
75
+ try {
76
+ const result = await operation();
77
+ s.stop(options?.successMessage || startMessage);
78
+ return result;
79
+ } catch (error) {
80
+ s.stop(options?.errorMessage || "Failed");
81
+ throw error;
82
+ }
83
+ }
84
+
85
+ //#endregion
86
+ //#region src/core/auth/schema.ts
87
+ const AuthDataSchema = z.object({
88
+ token: z.string().min(1, "Token cannot be empty"),
89
+ email: z.email(),
90
+ name: z.string().min(1, "Name cannot be empty")
91
+ });
92
+ const DeviceCodeResponseSchema = z.object({
93
+ deviceCode: z.string().min(1, "Device code cannot be empty"),
94
+ userCode: z.string().min(1, "User code cannot be empty"),
95
+ verificationUrl: z.url("Invalid verification URL"),
96
+ expiresIn: z.number().int().positive("Expires in must be a positive integer")
97
+ });
98
+ const TokenResponseSchema = z.object({
99
+ token: z.string().min(1, "Token cannot be empty"),
100
+ email: z.email("Invalid email address"),
101
+ name: z.string().min(1, "Name cannot be empty")
102
+ });
103
+
104
+ //#endregion
105
+ //#region src/core/auth/api.ts
106
+ async function delay(ms) {
107
+ return new Promise((resolve) => setTimeout(resolve, ms));
108
+ }
109
+ const deviceCodeToTokenMap = /* @__PURE__ */ new Map();
110
+ async function generateDeviceCode() {
111
+ try {
112
+ await delay(1e3);
113
+ const deviceCode = `device-code-${Date.now()}`;
114
+ deviceCodeToTokenMap.set(deviceCode, {
115
+ startTime: Date.now(),
116
+ readyAfter: 5e3
117
+ });
118
+ const mockResponse = {
119
+ deviceCode,
120
+ userCode: "ABCD-1234",
121
+ verificationUrl: "https://app.base44.com/verify",
122
+ expiresIn: 600
123
+ };
124
+ const result = DeviceCodeResponseSchema.safeParse(mockResponse);
125
+ if (!result.success) throw new AuthValidationError("Invalid device code response from server", result.error.issues.map((issue) => ({
126
+ message: issue.message,
127
+ path: issue.path.map(String)
128
+ })));
129
+ return result.data;
130
+ } catch (error) {
131
+ if (error instanceof AuthValidationError) throw error;
132
+ throw new AuthApiError("Failed to generate device code", error instanceof Error ? error : new Error(String(error)));
133
+ }
134
+ }
135
+ async function getTokenFromDeviceCode(deviceCode) {
136
+ try {
137
+ await delay(1e3);
138
+ const deviceInfo = deviceCodeToTokenMap.get(deviceCode);
139
+ if (!deviceInfo) return null;
140
+ if (Date.now() - deviceInfo.startTime < deviceInfo.readyAfter) return null;
141
+ const mockResponse = {
142
+ token: `mock-token-${Date.now()}`,
143
+ email: "shahart@base44.com",
144
+ name: "Shahar Talmi"
145
+ };
146
+ const result = TokenResponseSchema.safeParse(mockResponse);
147
+ if (!result.success) throw new AuthValidationError("Invalid token response from server", result.error.issues.map((issue) => ({
148
+ message: issue.message,
149
+ path: issue.path.map(String)
150
+ })));
151
+ deviceCodeToTokenMap.delete(deviceCode);
152
+ return result.data;
153
+ } catch (error) {
154
+ if (error instanceof AuthValidationError || error instanceof AuthApiError) throw error;
155
+ throw new AuthApiError("Failed to retrieve token from device code", error instanceof Error ? error : new Error(String(error)));
156
+ }
157
+ }
158
+
159
+ //#endregion
160
+ //#region src/core/consts.ts
161
+ const PROJECT_SUBDIR = "base44";
162
+ const FUNCTION_CONFIG_FILE = "function.jsonc";
163
+ function getBase44Dir() {
164
+ return join(homedir(), ".base44");
165
+ }
166
+ function getAuthFilePath() {
167
+ return join(getBase44Dir(), "auth", "auth.json");
168
+ }
169
+ function getProjectConfigPatterns() {
170
+ return [
171
+ `${PROJECT_SUBDIR}/config.jsonc`,
172
+ `${PROJECT_SUBDIR}/config.json`,
173
+ "config.jsonc",
174
+ "config.json"
175
+ ];
176
+ }
177
+
178
+ //#endregion
179
+ //#region src/core/utils/fs.ts
180
+ function pathExists(path) {
181
+ return access(path).then(() => true).catch(() => false);
182
+ }
183
+ async function readJsonFile(filePath) {
184
+ if (!await pathExists(filePath)) throw new Error(`File not found: ${filePath}`);
185
+ try {
186
+ const fileContent = await readFile(filePath, "utf-8");
187
+ const errors = [];
188
+ const result = parse(fileContent, errors, { allowTrailingComma: true });
189
+ if (errors.length > 0) {
190
+ const errorMessages = errors.map((e) => `${printParseErrorCode(e.error)} at offset ${e.offset}`).join(", ");
191
+ throw new Error(`File contains invalid JSONC: ${filePath} (${errorMessages})`);
192
+ }
193
+ return result;
194
+ } catch (error) {
195
+ if (error instanceof Error && error.message.includes("invalid JSONC")) throw error;
196
+ throw new Error(`Failed to read file ${filePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
197
+ }
198
+ }
199
+ async function writeJsonFile(filePath, data) {
200
+ try {
201
+ const dir = dirname(filePath);
202
+ if (!await pathExists(dir)) await mkdir(dir, { recursive: true });
203
+ await writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
204
+ } catch (error) {
205
+ throw new Error(`Failed to write file ${filePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
206
+ }
207
+ }
208
+ async function deleteFile(filePath) {
209
+ if (!await pathExists(filePath)) return;
210
+ try {
211
+ await unlink(filePath);
212
+ } catch (error) {
213
+ throw new Error(`Failed to delete file ${filePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
214
+ }
215
+ }
216
+
217
+ //#endregion
218
+ //#region src/core/auth/config.ts
219
+ async function readAuth() {
220
+ try {
221
+ const parsed = await readJsonFile(getAuthFilePath());
222
+ const result = AuthDataSchema.safeParse(parsed);
223
+ if (!result.success) throw new Error(`Invalid authentication data: ${result.error.issues.map((e) => e.message).join(", ")}`);
224
+ return result.data;
225
+ } catch (error) {
226
+ if (error instanceof Error && error.message.includes("Authentication")) throw error;
227
+ if (error instanceof Error && error.message.includes("File not found")) throw new Error("Authentication file not found. Please login first.");
228
+ throw new Error(`Failed to read authentication file: ${error instanceof Error ? error.message : "Unknown error"}`);
229
+ }
230
+ }
231
+ async function writeAuth(authData) {
232
+ const result = AuthDataSchema.safeParse(authData);
233
+ if (!result.success) throw new Error(`Invalid authentication data: ${result.error.issues.map((e) => e.message).join(", ")}`);
234
+ try {
235
+ await writeJsonFile(getAuthFilePath(), result.data);
236
+ } catch (error) {
237
+ throw new Error(`Failed to write authentication file: ${error instanceof Error ? error.message : "Unknown error"}`);
238
+ }
239
+ }
240
+ async function deleteAuth() {
241
+ try {
242
+ await deleteFile(getAuthFilePath());
243
+ } catch (error) {
244
+ throw new Error(`Failed to delete authentication file: ${error instanceof Error ? error.message : "Unknown error"}`);
245
+ }
246
+ }
247
+
248
+ //#endregion
249
+ //#region src/cli/commands/auth/login.ts
250
+ async function generateAndDisplayDeviceCode() {
251
+ const deviceCodeResponse = await runTask("Generating device code...", async () => {
252
+ return await generateDeviceCode();
253
+ }, {
254
+ successMessage: "Device code generated",
255
+ errorMessage: "Failed to generate device code"
256
+ });
257
+ log.info(`Please visit: ${deviceCodeResponse.verificationUrl}\nEnter your device code: ${deviceCodeResponse.userCode}`);
258
+ return deviceCodeResponse;
259
+ }
260
+ async function waitForAuthentication(deviceCode, expiresIn) {
261
+ let tokenResponse;
262
+ try {
263
+ await runTask("Waiting for you to complete authentication...", async () => {
264
+ await pWaitFor(async () => {
265
+ const result = await getTokenFromDeviceCode(deviceCode);
266
+ if (result !== null) {
267
+ tokenResponse = result;
268
+ return true;
269
+ }
270
+ return false;
271
+ }, {
272
+ interval: 2e3,
273
+ timeout: expiresIn * 1e3
274
+ });
275
+ }, {
276
+ successMessage: "Authentication completed!",
277
+ errorMessage: "Authentication failed"
278
+ });
279
+ } catch (error) {
280
+ if (error instanceof Error && error.message.includes("timed out")) throw new Error("Authentication timed out. Please try again.");
281
+ throw error;
282
+ }
283
+ if (tokenResponse === void 0) throw new Error("Failed to retrieve authentication token.");
284
+ return tokenResponse;
285
+ }
286
+ async function saveAuthData(token) {
287
+ await writeAuth({
288
+ token: token.token,
289
+ email: token.email,
290
+ name: token.name
291
+ });
292
+ }
293
+ async function login() {
294
+ const deviceCodeResponse = await generateAndDisplayDeviceCode();
295
+ const token = await waitForAuthentication(deviceCodeResponse.deviceCode, deviceCodeResponse.expiresIn);
296
+ await saveAuthData(token);
297
+ log.success(`Logged in as ${token.name}`);
298
+ }
299
+ const loginCommand = new Command("login").description("Authenticate with Base44").action(async () => {
300
+ await runCommand(login);
301
+ });
302
+
303
+ //#endregion
304
+ //#region src/cli/commands/auth/whoami.ts
305
+ async function whoami() {
306
+ const auth = await readAuth();
307
+ log.info(`Logged in as: ${auth.name} (${auth.email})`);
308
+ }
309
+ const whoamiCommand = new Command("whoami").description("Display current authenticated user").action(async () => {
310
+ await runCommand(whoami);
311
+ });
312
+
313
+ //#endregion
314
+ //#region src/cli/commands/auth/logout.ts
315
+ async function logout() {
316
+ await deleteAuth();
317
+ log.info("Logged out successfully");
318
+ }
319
+ const logoutCommand = new Command("logout").description("Logout from current device").action(async () => {
320
+ await runCommand(logout);
321
+ });
322
+
323
+ //#endregion
324
+ //#region src/core/resources/entity/schema.ts
325
+ const EntityPropertySchema = z.object({
326
+ type: z.string(),
327
+ description: z.string().optional(),
328
+ enum: z.array(z.string()).optional(),
329
+ default: z.union([
330
+ z.string(),
331
+ z.number(),
332
+ z.boolean()
333
+ ]).optional(),
334
+ format: z.string().optional(),
335
+ items: z.any().optional(),
336
+ relation: z.object({
337
+ entity: z.string(),
338
+ type: z.string()
339
+ }).optional()
340
+ });
341
+ const EntityPoliciesSchema = z.object({
342
+ read: z.string().optional(),
343
+ create: z.string().optional(),
344
+ update: z.string().optional(),
345
+ delete: z.string().optional()
346
+ });
347
+ const EntitySchema = z.object({
348
+ name: z.string().min(1, "Entity name cannot be empty"),
349
+ type: z.literal("object"),
350
+ properties: z.record(z.string(), EntityPropertySchema),
351
+ required: z.array(z.string()).optional(),
352
+ policies: EntityPoliciesSchema.optional()
353
+ });
354
+
355
+ //#endregion
356
+ //#region src/core/resources/entity/config.ts
357
+ async function readEntityFile(entityPath) {
358
+ const parsed = await readJsonFile(entityPath);
359
+ const result = EntitySchema.safeParse(parsed);
360
+ if (!result.success) throw new Error(`Invalid entity configuration in ${entityPath}: ${result.error.issues.map((e) => e.message).join(", ")}`);
361
+ return result.data;
362
+ }
363
+ async function readAllEntities(entitiesDir) {
364
+ if (!await pathExists(entitiesDir)) return [];
365
+ const files = await globby("*.{json,jsonc}", {
366
+ cwd: entitiesDir,
367
+ absolute: true
368
+ });
369
+ return await Promise.all(files.map((filePath) => readEntityFile(filePath)));
370
+ }
371
+
372
+ //#endregion
373
+ //#region src/core/resources/entity/resource.ts
374
+ const entityResource = { readAll: readAllEntities };
375
+
376
+ //#endregion
377
+ //#region src/core/resources/function/schema.ts
378
+ const HttpTriggerSchema = z.object({
379
+ id: z.string().optional(),
380
+ name: z.string().optional(),
381
+ description: z.string().optional(),
382
+ type: z.literal("http"),
383
+ path: z.string().min(1, "Path cannot be empty")
384
+ });
385
+ const ScheduleTriggerSchema = z.object({
386
+ id: z.string().optional(),
387
+ name: z.string().optional(),
388
+ description: z.string().optional(),
389
+ type: z.literal("schedule"),
390
+ scheduleMode: z.enum(["recurring", "once"]).optional(),
391
+ cron: z.string().min(1, "Cron expression cannot be empty"),
392
+ isActive: z.boolean().optional(),
393
+ timezone: z.string().optional()
394
+ });
395
+ const EventTriggerSchema = z.object({
396
+ id: z.string().optional(),
397
+ name: z.string().optional(),
398
+ description: z.string().optional(),
399
+ type: z.literal("event"),
400
+ entity: z.string().min(1, "Entity name cannot be empty"),
401
+ event: z.string().min(1, "Event type cannot be empty")
402
+ });
403
+ const TriggerSchema = z.discriminatedUnion("type", [
404
+ HttpTriggerSchema,
405
+ ScheduleTriggerSchema,
406
+ EventTriggerSchema
407
+ ]);
408
+ const FunctionConfigSchema = z.object({
409
+ entry: z.string().min(1, "Entry point cannot be empty"),
410
+ triggers: z.array(TriggerSchema).optional()
411
+ });
412
+
413
+ //#endregion
414
+ //#region src/core/resources/function/config.ts
415
+ async function readFunctionConfig(configPath) {
416
+ const parsed = await readJsonFile(configPath);
417
+ const result = FunctionConfigSchema.safeParse(parsed);
418
+ if (!result.success) throw new Error(`Invalid function configuration in ${configPath}: ${result.error.issues.map((e) => e.message).join(", ")}`);
419
+ return result.data;
420
+ }
421
+ async function readAllFunctions(functionsDir) {
422
+ if (!await pathExists(functionsDir)) return [];
423
+ const configFiles = await globby(`*/${FUNCTION_CONFIG_FILE}`, {
424
+ cwd: functionsDir,
425
+ absolute: true
426
+ });
427
+ return await Promise.all(configFiles.map((configPath) => readFunctionConfig(configPath)));
428
+ }
429
+
430
+ //#endregion
431
+ //#region src/core/resources/function/resource.ts
432
+ const functionResource = { readAll: readAllFunctions };
433
+
434
+ //#endregion
435
+ //#region src/core/config/project.ts
436
+ const ProjectConfigSchema = z.looseObject({
437
+ name: z.string().min(1, "Project name cannot be empty"),
438
+ entitySrc: z.string().default("./entities"),
439
+ functionSrc: z.string().default("./functions")
440
+ });
441
+ async function findConfigInDir(dir) {
442
+ return (await globby(getProjectConfigPatterns(), {
443
+ cwd: dir,
444
+ absolute: true
445
+ }))[0] ?? null;
446
+ }
447
+ async function findProjectRoot(startPath) {
448
+ let current = startPath || process.cwd();
449
+ while (current !== dirname(current)) {
450
+ const configPath = await findConfigInDir(current);
451
+ if (configPath) return {
452
+ root: current,
453
+ configPath
454
+ };
455
+ current = dirname(current);
456
+ }
457
+ return null;
458
+ }
459
+ async function readProjectConfig(projectRoot) {
460
+ let found;
461
+ if (projectRoot) {
462
+ const configPath$1 = await findConfigInDir(projectRoot);
463
+ found = configPath$1 ? {
464
+ root: projectRoot,
465
+ configPath: configPath$1
466
+ } : null;
467
+ } else found = await findProjectRoot();
468
+ if (!found) throw new Error(`Project root not found. Please ensure config.jsonc or config.json exists in the project directory or ${PROJECT_SUBDIR}/ subdirectory.`);
469
+ const { root, configPath } = found;
470
+ const parsed = await readJsonFile(configPath);
471
+ const result = ProjectConfigSchema.safeParse(parsed);
472
+ if (!result.success) {
473
+ const errors = result.error.issues.map((e) => e.message).join(", ");
474
+ throw new Error(`Invalid project configuration: ${errors}`);
475
+ }
476
+ const project = result.data;
477
+ const configDir = dirname(configPath);
478
+ const [entities, functions] = await Promise.all([entityResource.readAll(join(configDir, project.entitySrc)), functionResource.readAll(join(configDir, project.functionSrc))]);
479
+ return {
480
+ project: {
481
+ ...project,
482
+ root,
483
+ configPath
484
+ },
485
+ entities,
486
+ functions
487
+ };
488
+ }
489
+
490
+ //#endregion
491
+ //#region src/cli/commands/project/show-project.ts
492
+ async function showProject() {
493
+ const projectData = await runTask("Reading project configuration", async () => {
494
+ return await readProjectConfig();
495
+ }, {
496
+ successMessage: "Project configuration loaded",
497
+ errorMessage: "Failed to load project configuration"
498
+ });
499
+ const jsonOutput = JSON.stringify(projectData, null, 2);
500
+ log.info(jsonOutput);
501
+ }
502
+ const showProjectCommand = new Command("show-project").description("Display project configuration, entities, and functions").action(async () => {
503
+ await runCommand(showProject);
504
+ });
505
+
506
+ //#endregion
507
+ //#region src/cli/index.ts
8
508
  const program = new Command();
9
- program
10
- .name("base44")
11
- .description("Base44 CLI - Unified interface for managing Base44 applications")
12
- .version(getPackageVersion());
13
- // Register authentication commands
509
+ program.name("base44").description("Base44 CLI - Unified interface for managing Base44 applications").version(getPackageVersion());
14
510
  program.addCommand(loginCommand);
15
511
  program.addCommand(whoamiCommand);
16
512
  program.addCommand(logoutCommand);
17
- // Register project commands
18
513
  program.addCommand(showProjectCommand);
19
- // Parse command line arguments
20
514
  program.parse();
21
- //# sourceMappingURL=index.js.map
515
+
516
+ //#endregion
517
+ export { };
package/package.json CHANGED
@@ -1,22 +1,19 @@
1
1
  {
2
2
  "name": "@base44-preview/cli",
3
- "version": "0.0.1-pr.10.3ef6431",
3
+ "version": "0.0.1-pr.11.72924bc",
4
4
  "description": "Base44 CLI - Unified interface for managing Base44 applications",
5
5
  "type": "module",
6
6
  "main": "./dist/cli/index.js",
7
- "types": "./dist/cli/index.d.ts",
8
7
  "bin": "./dist/cli/index.js",
9
8
  "exports": {
10
- ".": {
11
- "types": "./dist/cli/index.d.ts",
12
- "default": "./dist/cli/index.js"
13
- }
9
+ ".": "./dist/cli/index.js"
14
10
  },
15
11
  "files": [
16
12
  "dist"
17
13
  ],
18
14
  "scripts": {
19
- "build": "tsc && tsc-alias",
15
+ "build": "tsdown",
16
+ "typecheck": "tsc --noEmit",
20
17
  "dev": "tsx src/cli/index.ts",
21
18
  "start": "node dist/cli/index.js",
22
19
  "clean": "rm -rf dist",
@@ -41,7 +38,6 @@
41
38
  "commander": "^12.1.0",
42
39
  "globby": "^16.1.0",
43
40
  "jsonc-parser": "^3.3.1",
44
- "ky": "^1.14.2",
45
41
  "p-wait-for": "^6.0.0",
46
42
  "zod": "^4.3.5"
47
43
  },
@@ -53,7 +49,7 @@
53
49
  "eslint": "^9.39.2",
54
50
  "eslint-plugin-import": "^2.32.0",
55
51
  "eslint-plugin-unicorn": "^62.0.0",
56
- "tsc-alias": "^1.8.16",
52
+ "tsdown": "^0.12.4",
57
53
  "tsx": "^4.19.2",
58
54
  "typescript": "^5.7.2",
59
55
  "typescript-eslint": "^8.52.0",
@@ -1,3 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const loginCommand: Command;
3
- //# sourceMappingURL=login.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuGpC,eAAO,MAAM,YAAY,SAIrB,CAAC"}
@@ -1,71 +0,0 @@
1
- import { Command } from "commander";
2
- import { log } from "@clack/prompts";
3
- import pWaitFor from "p-wait-for";
4
- import { writeAuth, generateDeviceCode, getTokenFromDeviceCode, } from "../../../core/auth/index.js";
5
- import { runCommand, runTask } from "../../utils/index.js";
6
- async function generateAndDisplayDeviceCode() {
7
- const deviceCodeResponse = await runTask("Generating device code...", async () => {
8
- return await generateDeviceCode();
9
- }, {
10
- successMessage: "Device code generated",
11
- errorMessage: "Failed to generate device code",
12
- });
13
- log.info(`Please visit: ${deviceCodeResponse.verificationUri}\n` +
14
- `Enter your device code: ${deviceCodeResponse.userCode}`);
15
- return deviceCodeResponse;
16
- }
17
- async function waitForAuthentication(deviceCode, expiresIn, interval) {
18
- let tokenResponse;
19
- try {
20
- await runTask("Waiting for you to complete authentication...", async () => {
21
- await pWaitFor(async () => {
22
- const result = await getTokenFromDeviceCode(deviceCode);
23
- if (result !== null) {
24
- tokenResponse = result;
25
- return true;
26
- }
27
- return false;
28
- }, {
29
- interval: interval * 1000,
30
- timeout: expiresIn * 1000,
31
- });
32
- }, {
33
- successMessage: "Authentication completed!",
34
- errorMessage: "Authentication failed",
35
- });
36
- }
37
- catch (error) {
38
- if (error instanceof Error && error.message.includes("timed out")) {
39
- throw new Error("Authentication timed out. Please try again.");
40
- }
41
- throw error;
42
- }
43
- if (tokenResponse === undefined) {
44
- throw new Error("Failed to retrieve authentication token.");
45
- }
46
- return tokenResponse;
47
- }
48
- async function saveAuthData(response) {
49
- // TODO: Fetch user info (email, name) from the server after authentication
50
- // For now, we store placeholder values until a /userinfo endpoint is available
51
- const expiresAt = Date.now() + response.expiresIn * 1000;
52
- await writeAuth({
53
- accessToken: response.accessToken,
54
- refreshToken: response.refreshToken,
55
- expiresAt,
56
- email: "user@base44.com",
57
- name: "Base44 User",
58
- });
59
- }
60
- async function login() {
61
- const deviceCodeResponse = await generateAndDisplayDeviceCode();
62
- const token = await waitForAuthentication(deviceCodeResponse.deviceCode, deviceCodeResponse.expiresIn, deviceCodeResponse.interval);
63
- await saveAuthData(token);
64
- log.success("Successfully logged in!");
65
- }
66
- export const loginCommand = new Command("login")
67
- .description("Authenticate with Base44")
68
- .action(async () => {
69
- await runCommand(login);
70
- });
71
- //# sourceMappingURL=login.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../../../src/cli/commands/auth/login.ts"],"names":[],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const logoutCommand: Command;
3
- //# sourceMappingURL=logout.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/auth/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,eAAO,MAAM,aAAa,SAItB,CAAC"}
@@ -1,14 +0,0 @@
1
- import { Command } from "commander";
2
- import { log } from "@clack/prompts";
3
- import { deleteAuth } from "../../../core/auth/index.js";
4
- import { runCommand } from "../../utils/index.js";
5
- async function logout() {
6
- await deleteAuth();
7
- log.info("Logged out successfully");
8
- }
9
- export const logoutCommand = new Command("logout")
10
- .description("Logout from current device")
11
- .action(async () => {
12
- await runCommand(logout);
13
- });
14
- //# sourceMappingURL=logout.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../../../src/cli/commands/auth/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,KAAK,UAAU,MAAM;IACnB,MAAM,UAAU,EAAE,CAAC;IACnB,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,4BAA4B,CAAC;KACzC,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC,CAAC,CAAC"}