@basictech/cli 0.0.29 → 0.0.31
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/dist/index.js +3445 -189
- package/dist/lib/schema.d.ts +1 -0
- package/dist/lib/schema.d.ts.map +1 -1
- package/dist/lib/schema.js +9 -0
- package/dist/lib/schema.js.map +1 -1
- package/package.json +9 -5
package/dist/index.js
CHANGED
|
@@ -1,243 +1,3499 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// dist/lib/constants.js
|
|
13
|
+
var CONSTANTS, COMMANDS, MESSAGES;
|
|
14
|
+
var init_constants = __esm({
|
|
15
|
+
"dist/lib/constants.js"() {
|
|
16
|
+
"use strict";
|
|
17
|
+
CONSTANTS = {
|
|
18
|
+
CLI_DIR: ".basic-cli",
|
|
19
|
+
TOKEN_FILE: "token.json",
|
|
20
|
+
API_BASE: "https://api.basic.tech",
|
|
21
|
+
OAUTH_CLIENT_ID: "9c3f6704-87e7-4af9-8dd0-36dcb9b5c18c",
|
|
22
|
+
OAUTH_REDIRECT: "http://localhost:8080/callback",
|
|
23
|
+
OAUTH_SCOPES: "profile,admin",
|
|
24
|
+
MAX_WIDTH: 80,
|
|
25
|
+
SIMILARITY_THRESHOLD: 0.4,
|
|
26
|
+
GITHUB_REPO: "basicdb/basic-cli"
|
|
27
|
+
};
|
|
28
|
+
COMMANDS = [
|
|
29
|
+
"account",
|
|
30
|
+
"login",
|
|
31
|
+
"logout",
|
|
32
|
+
"status",
|
|
33
|
+
"projects",
|
|
34
|
+
"teams",
|
|
35
|
+
"init",
|
|
36
|
+
"version",
|
|
37
|
+
"help",
|
|
38
|
+
"push",
|
|
39
|
+
"pull",
|
|
40
|
+
"debug",
|
|
41
|
+
"update"
|
|
42
|
+
];
|
|
43
|
+
MESSAGES = {
|
|
44
|
+
OFFLINE: "you are offline. please check your internet connection.",
|
|
45
|
+
LOGGED_OUT: "you are not logged in. please login with 'basic login'",
|
|
46
|
+
WELCOME: "welcome to basic-cli! use 'basic help' to see all commands"
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// dist/lib/version.js
|
|
52
|
+
import { readFileSync } from "fs";
|
|
53
|
+
import { join, dirname } from "path";
|
|
54
|
+
import { fileURLToPath } from "url";
|
|
55
|
+
function getVersion() {
|
|
56
|
+
if (cachedVersion) {
|
|
57
|
+
return cachedVersion;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
61
|
+
const __dirname = dirname(__filename);
|
|
62
|
+
const packageJsonPath = join(__dirname, "../../package.json");
|
|
63
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
64
|
+
cachedVersion = packageJson.version;
|
|
65
|
+
return cachedVersion;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error("Error reading version:", error);
|
|
68
|
+
const fallbackVersion = "0.0.22";
|
|
69
|
+
cachedVersion = fallbackVersion;
|
|
70
|
+
return fallbackVersion;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
var cachedVersion;
|
|
74
|
+
var init_version = __esm({
|
|
75
|
+
"dist/lib/version.js"() {
|
|
76
|
+
"use strict";
|
|
77
|
+
cachedVersion = null;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// dist/lib/errors.js
|
|
82
|
+
function handleError(error) {
|
|
83
|
+
if (error instanceof BasicCliError) {
|
|
84
|
+
return error;
|
|
85
|
+
}
|
|
86
|
+
if (error instanceof Error) {
|
|
87
|
+
if (error.message.includes("ENOTFOUND") || error.message.includes("network")) {
|
|
88
|
+
return new NetworkError();
|
|
89
|
+
}
|
|
90
|
+
if (error.message.includes("unauthorized") || error.message.includes("401")) {
|
|
91
|
+
return new AuthError("Authentication failed", [
|
|
92
|
+
"Try logging in again with 'basic login'",
|
|
93
|
+
"Check if your token has expired"
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
if (error.message.includes("invalid character")) {
|
|
97
|
+
return new SchemaError("Invalid schema format", [
|
|
98
|
+
"Check for trailing commas in your schema",
|
|
99
|
+
"Ensure valid JSON syntax"
|
|
100
|
+
]);
|
|
101
|
+
}
|
|
102
|
+
return new BasicCliError(error.message, "UNKNOWN_ERROR");
|
|
103
|
+
}
|
|
104
|
+
return new BasicCliError("An unknown error occurred", "UNKNOWN_ERROR");
|
|
105
|
+
}
|
|
106
|
+
function formatError(error) {
|
|
107
|
+
let output = `Error: ${error.message}`;
|
|
108
|
+
if (error.suggestions && error.suggestions.length > 0) {
|
|
109
|
+
output += "\n\nSuggestions:";
|
|
110
|
+
error.suggestions.forEach((suggestion) => {
|
|
111
|
+
output += `
|
|
112
|
+
- ${suggestion}`;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return output;
|
|
116
|
+
}
|
|
117
|
+
var BasicCliError, AuthError, ApiError, SchemaError, NetworkError;
|
|
118
|
+
var init_errors = __esm({
|
|
119
|
+
"dist/lib/errors.js"() {
|
|
120
|
+
"use strict";
|
|
121
|
+
BasicCliError = class extends Error {
|
|
122
|
+
code;
|
|
123
|
+
suggestions;
|
|
124
|
+
constructor(message, code, suggestions) {
|
|
125
|
+
super(message);
|
|
126
|
+
this.code = code;
|
|
127
|
+
this.suggestions = suggestions;
|
|
128
|
+
this.name = "BasicCliError";
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
AuthError = class extends BasicCliError {
|
|
132
|
+
constructor(message, suggestions) {
|
|
133
|
+
super(message, "AUTH_ERROR", suggestions);
|
|
134
|
+
this.name = "AuthError";
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
ApiError = class extends BasicCliError {
|
|
138
|
+
statusCode;
|
|
139
|
+
constructor(message, statusCode, suggestions) {
|
|
140
|
+
super(message, "API_ERROR", suggestions);
|
|
141
|
+
this.statusCode = statusCode;
|
|
142
|
+
this.name = "ApiError";
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
SchemaError = class extends BasicCliError {
|
|
146
|
+
constructor(message, suggestions) {
|
|
147
|
+
super(message, "SCHEMA_ERROR", suggestions);
|
|
148
|
+
this.name = "SchemaError";
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
NetworkError = class extends BasicCliError {
|
|
152
|
+
constructor(message = "Network connection failed") {
|
|
153
|
+
super(message, "NETWORK_ERROR", [
|
|
154
|
+
"Check your internet connection",
|
|
155
|
+
"Try again in a moment"
|
|
156
|
+
]);
|
|
157
|
+
this.name = "NetworkError";
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// dist/lib/platform.js
|
|
164
|
+
import { exec } from "child_process";
|
|
165
|
+
import { promisify } from "util";
|
|
166
|
+
import * as os from "os";
|
|
167
|
+
import * as path from "path";
|
|
168
|
+
function getConfigPath() {
|
|
169
|
+
const home = os.homedir();
|
|
170
|
+
return path.join(home, CONSTANTS.CLI_DIR, CONSTANTS.TOKEN_FILE);
|
|
171
|
+
}
|
|
172
|
+
function getConfigDir() {
|
|
173
|
+
const home = os.homedir();
|
|
174
|
+
return path.join(home, CONSTANTS.CLI_DIR);
|
|
175
|
+
}
|
|
176
|
+
async function openBrowser(url) {
|
|
177
|
+
const platform = process.platform;
|
|
178
|
+
const commands = {
|
|
179
|
+
darwin: "open",
|
|
180
|
+
win32: "start",
|
|
181
|
+
linux: "xdg-open"
|
|
182
|
+
};
|
|
183
|
+
const command = commands[platform];
|
|
184
|
+
if (!command) {
|
|
185
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
if (platform === "win32") {
|
|
189
|
+
await execAsync(`${command} "" "${url}"`);
|
|
190
|
+
} else {
|
|
191
|
+
await execAsync(`${command} "${url}"`);
|
|
192
|
+
}
|
|
193
|
+
} catch (error) {
|
|
194
|
+
throw new Error(`Failed to open browser: ${error}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async function copyToClipboard(text) {
|
|
198
|
+
const platform = process.platform;
|
|
199
|
+
try {
|
|
200
|
+
if (platform === "darwin") {
|
|
201
|
+
await execAsync(`echo "${text}" | pbcopy`);
|
|
202
|
+
} else if (platform === "win32") {
|
|
203
|
+
await execAsync(`echo ${text} | clip`);
|
|
204
|
+
} else {
|
|
205
|
+
try {
|
|
206
|
+
await execAsync(`echo "${text}" | xclip -selection clipboard`);
|
|
207
|
+
} catch {
|
|
208
|
+
await execAsync(`echo "${text}" | xsel --clipboard --input`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} catch (error) {
|
|
212
|
+
throw new Error(`Failed to copy to clipboard: ${error}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function isOnline() {
|
|
216
|
+
return fetch(CONSTANTS.API_BASE, { method: "HEAD" }).then(() => true).catch(() => false);
|
|
217
|
+
}
|
|
218
|
+
function similarity(s1, s2) {
|
|
219
|
+
const d = levenshteinDistance(s1, s2);
|
|
220
|
+
const maxLen = Math.max(s1.length, s2.length);
|
|
221
|
+
if (maxLen === 0)
|
|
222
|
+
return 1;
|
|
223
|
+
return 1 - d / maxLen;
|
|
224
|
+
}
|
|
225
|
+
function levenshteinDistance(s1, s2) {
|
|
226
|
+
if (s1.length === 0)
|
|
227
|
+
return s2.length;
|
|
228
|
+
if (s2.length === 0)
|
|
229
|
+
return s1.length;
|
|
230
|
+
const matrix = [];
|
|
231
|
+
for (let i = 0; i <= s1.length; i++) {
|
|
232
|
+
matrix[i] = [];
|
|
233
|
+
matrix[i][0] = i;
|
|
234
|
+
}
|
|
235
|
+
for (let j = 0; j <= s2.length; j++) {
|
|
236
|
+
matrix[0][j] = j;
|
|
237
|
+
}
|
|
238
|
+
for (let i = 1; i <= s1.length; i++) {
|
|
239
|
+
for (let j = 1; j <= s2.length; j++) {
|
|
240
|
+
const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;
|
|
241
|
+
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return matrix[s1.length][s2.length];
|
|
245
|
+
}
|
|
246
|
+
function findSimilarCommands(input) {
|
|
247
|
+
const commands = COMMANDS.filter((cmd) => cmd !== input);
|
|
248
|
+
const suggestions = [];
|
|
249
|
+
for (const command of commands) {
|
|
250
|
+
const sim = similarity(input, command);
|
|
251
|
+
if (sim >= CONSTANTS.SIMILARITY_THRESHOLD) {
|
|
252
|
+
suggestions.push({ command, similarity: sim });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return suggestions.sort((a, b) => b.similarity - a.similarity).slice(0, 3).map((s) => s.command);
|
|
256
|
+
}
|
|
257
|
+
function generateSlug(name) {
|
|
258
|
+
return name.toLowerCase().trim().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
259
|
+
}
|
|
260
|
+
var execAsync;
|
|
261
|
+
var init_platform = __esm({
|
|
262
|
+
"dist/lib/platform.js"() {
|
|
263
|
+
"use strict";
|
|
264
|
+
init_constants();
|
|
265
|
+
execAsync = promisify(exec);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// dist/lib/auth.js
|
|
270
|
+
import { createServer } from "http";
|
|
271
|
+
import { promises as fs } from "fs";
|
|
272
|
+
var AuthService;
|
|
273
|
+
var init_auth = __esm({
|
|
274
|
+
"dist/lib/auth.js"() {
|
|
275
|
+
"use strict";
|
|
276
|
+
init_constants();
|
|
277
|
+
init_platform();
|
|
278
|
+
init_errors();
|
|
279
|
+
AuthService = class _AuthService {
|
|
280
|
+
static instance;
|
|
281
|
+
static getInstance() {
|
|
282
|
+
if (!_AuthService.instance) {
|
|
283
|
+
_AuthService.instance = new _AuthService();
|
|
284
|
+
}
|
|
285
|
+
return _AuthService.instance;
|
|
286
|
+
}
|
|
287
|
+
async login() {
|
|
288
|
+
try {
|
|
289
|
+
const token = await this.startOAuthFlow();
|
|
290
|
+
await this.saveToken(token);
|
|
291
|
+
} catch (error) {
|
|
292
|
+
throw handleError(error);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async logout() {
|
|
296
|
+
try {
|
|
297
|
+
await this.deleteToken();
|
|
298
|
+
} catch (error) {
|
|
299
|
+
throw handleError(error);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async getToken() {
|
|
303
|
+
try {
|
|
304
|
+
const tokenPath = getConfigPath();
|
|
305
|
+
const tokenData = await fs.readFile(tokenPath, "utf8");
|
|
306
|
+
const token = JSON.parse(tokenData);
|
|
307
|
+
if (token.expires_at && Date.now() > token.expires_at) {
|
|
308
|
+
const refreshedToken = await this.refreshToken(token);
|
|
309
|
+
await this.saveToken(refreshedToken);
|
|
310
|
+
return refreshedToken;
|
|
311
|
+
}
|
|
312
|
+
return token;
|
|
313
|
+
} catch (error) {
|
|
314
|
+
if (error.code === "ENOENT") {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
throw handleError(error);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async getUserInfo() {
|
|
321
|
+
const token = await this.getToken();
|
|
322
|
+
if (!token) {
|
|
323
|
+
throw new AuthError("Not logged in");
|
|
324
|
+
}
|
|
325
|
+
const response = await fetch(`${CONSTANTS.API_BASE}/auth/userInfo`, {
|
|
326
|
+
headers: {
|
|
327
|
+
Authorization: `Bearer ${token.access_token}`
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
if (!response.ok) {
|
|
331
|
+
throw new AuthError("Failed to fetch user info");
|
|
332
|
+
}
|
|
333
|
+
return await response.json();
|
|
334
|
+
}
|
|
335
|
+
async startOAuthFlow() {
|
|
336
|
+
return new Promise((resolve, reject) => {
|
|
337
|
+
const server = createServer((req, res) => {
|
|
338
|
+
if (req.url?.startsWith("/callback")) {
|
|
339
|
+
const url = new URL(req.url, CONSTANTS.OAUTH_REDIRECT);
|
|
340
|
+
const code = url.searchParams.get("code");
|
|
341
|
+
const state = url.searchParams.get("state");
|
|
342
|
+
if (!code) {
|
|
343
|
+
res.writeHead(400);
|
|
344
|
+
res.end("Authorization code not found");
|
|
345
|
+
server.close();
|
|
346
|
+
reject(new AuthError("Authorization code not found"));
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
this.exchangeCodeForToken(code).then((token) => {
|
|
350
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
351
|
+
res.end(this.getSuccessHtml());
|
|
352
|
+
server.close();
|
|
353
|
+
resolve(token);
|
|
354
|
+
}).catch((error) => {
|
|
355
|
+
res.writeHead(500);
|
|
356
|
+
res.end("Authentication failed");
|
|
357
|
+
server.close();
|
|
358
|
+
reject(error);
|
|
359
|
+
});
|
|
360
|
+
} else {
|
|
361
|
+
res.writeHead(404);
|
|
362
|
+
res.end("Not found");
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
server.listen(8080, async () => {
|
|
366
|
+
const state = this.generateState();
|
|
367
|
+
const authUrl = `${CONSTANTS.API_BASE}/auth/authorize?client_id=${CONSTANTS.OAUTH_CLIENT_ID}&redirect_uri=${encodeURIComponent(CONSTANTS.OAUTH_REDIRECT)}&response_type=code&scope=${encodeURIComponent(CONSTANTS.OAUTH_SCOPES)}&state=${state}`;
|
|
368
|
+
try {
|
|
369
|
+
await openBrowser(authUrl);
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.log(`Please visit this URL to log in: ${authUrl}`);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
setTimeout(() => {
|
|
375
|
+
server.close();
|
|
376
|
+
reject(new AuthError("Authentication timeout"));
|
|
377
|
+
}, 5 * 60 * 1e3);
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
async exchangeCodeForToken(code) {
|
|
381
|
+
const response = await fetch(`${CONSTANTS.API_BASE}/auth/token`, {
|
|
382
|
+
method: "POST",
|
|
383
|
+
headers: {
|
|
384
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
385
|
+
},
|
|
386
|
+
body: new URLSearchParams({
|
|
387
|
+
grant_type: "authorization_code",
|
|
388
|
+
client_id: CONSTANTS.OAUTH_CLIENT_ID,
|
|
389
|
+
code,
|
|
390
|
+
redirect_uri: CONSTANTS.OAUTH_REDIRECT
|
|
391
|
+
})
|
|
392
|
+
});
|
|
393
|
+
if (!response.ok) {
|
|
394
|
+
throw new AuthError("Failed to exchange code for token");
|
|
395
|
+
}
|
|
396
|
+
const data = await response.json();
|
|
397
|
+
return {
|
|
398
|
+
access_token: data.access_token,
|
|
399
|
+
refresh_token: data.refresh_token,
|
|
400
|
+
expires_at: Date.now() + data.expires_in * 1e3,
|
|
401
|
+
token_type: data.token_type || "Bearer"
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
async refreshToken(token) {
|
|
405
|
+
const response = await fetch(`${CONSTANTS.API_BASE}/auth/token`, {
|
|
406
|
+
method: "POST",
|
|
407
|
+
headers: {
|
|
408
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
409
|
+
},
|
|
410
|
+
body: new URLSearchParams({
|
|
411
|
+
grant_type: "refresh_token",
|
|
412
|
+
client_id: CONSTANTS.OAUTH_CLIENT_ID,
|
|
413
|
+
code: token.refresh_token
|
|
414
|
+
})
|
|
415
|
+
});
|
|
416
|
+
if (!response.ok) {
|
|
417
|
+
throw new AuthError("Failed to refresh token", [
|
|
418
|
+
"Try logging in again with 'basic login'"
|
|
419
|
+
]);
|
|
420
|
+
}
|
|
421
|
+
const data = await response.json();
|
|
422
|
+
return {
|
|
423
|
+
access_token: data.access_token,
|
|
424
|
+
refresh_token: data.refresh_token || token.refresh_token,
|
|
425
|
+
expires_at: Date.now() + data.expires_in * 1e3,
|
|
426
|
+
token_type: data.token_type || "Bearer"
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
async saveToken(token) {
|
|
430
|
+
const tokenPath = getConfigPath();
|
|
431
|
+
const configDir = getConfigDir();
|
|
432
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
433
|
+
await fs.writeFile(tokenPath, JSON.stringify(token, null, 2), { mode: 384 });
|
|
434
|
+
}
|
|
435
|
+
async deleteToken() {
|
|
436
|
+
const tokenPath = getConfigPath();
|
|
437
|
+
try {
|
|
438
|
+
await fs.unlink(tokenPath);
|
|
439
|
+
} catch (error) {
|
|
440
|
+
if (error.code !== "ENOENT") {
|
|
441
|
+
throw error;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
generateState() {
|
|
446
|
+
const array = new Uint8Array(24);
|
|
447
|
+
crypto.getRandomValues(array);
|
|
448
|
+
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
449
|
+
}
|
|
450
|
+
getSuccessHtml() {
|
|
451
|
+
return `
|
|
452
|
+
<!DOCTYPE html>
|
|
453
|
+
<html>
|
|
454
|
+
<head>
|
|
455
|
+
<meta charset="UTF-8">
|
|
456
|
+
<title>Basic CLI Authentication</title>
|
|
457
|
+
<style>
|
|
458
|
+
:root { color-scheme: light dark; }
|
|
459
|
+
@media (prefers-color-scheme: light) {
|
|
460
|
+
:root {
|
|
461
|
+
--bg-color: #f5f5f5;
|
|
462
|
+
--container-bg: #ffffff;
|
|
463
|
+
--text-color: #000000;
|
|
464
|
+
--shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
@media (prefers-color-scheme: dark) {
|
|
468
|
+
:root {
|
|
469
|
+
--bg-color: #1a1a1a;
|
|
470
|
+
--container-bg: #2d2d2d;
|
|
471
|
+
--text-color: #ffffff;
|
|
472
|
+
--shadow: 0 2px 4px rgba(0,0,0,0.3);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
body {
|
|
476
|
+
font-family: monospace;
|
|
477
|
+
display: flex;
|
|
478
|
+
justify-content: center;
|
|
479
|
+
align-items: center;
|
|
480
|
+
height: 100vh;
|
|
481
|
+
margin: 0;
|
|
482
|
+
background-color: var(--bg-color);
|
|
483
|
+
color: var(--text-color);
|
|
484
|
+
}
|
|
485
|
+
.container {
|
|
486
|
+
text-align: center;
|
|
487
|
+
padding: 2rem;
|
|
488
|
+
background: var(--container-bg);
|
|
489
|
+
border-radius: 8px;
|
|
490
|
+
box-shadow: var(--shadow);
|
|
491
|
+
}
|
|
492
|
+
.success-icon {
|
|
493
|
+
color: #AE87FF;
|
|
494
|
+
font-size: 32px;
|
|
495
|
+
margin-bottom: 1rem;
|
|
496
|
+
}
|
|
497
|
+
h2 { margin: 0 0 1rem 0; }
|
|
498
|
+
p { margin: 0; opacity: 0.8; }
|
|
499
|
+
.help-text {
|
|
500
|
+
margin-top: 1.5rem;
|
|
501
|
+
font-size: 0.9em;
|
|
502
|
+
opacity: 0.7;
|
|
503
|
+
text-align: left;
|
|
504
|
+
}
|
|
505
|
+
.help-text ol {
|
|
506
|
+
margin: 0;
|
|
507
|
+
padding-left: 1.5rem;
|
|
508
|
+
}
|
|
509
|
+
.help-text li { margin: 0.3rem 0; }
|
|
510
|
+
code {
|
|
511
|
+
background: var(--bg-color);
|
|
512
|
+
padding: 0.2em 0.4em;
|
|
513
|
+
border-radius: 4px;
|
|
514
|
+
font-family: monospace;
|
|
515
|
+
font-size: 0.9em;
|
|
516
|
+
}
|
|
517
|
+
a {
|
|
518
|
+
color: #AE87FF;
|
|
519
|
+
text-decoration: none;
|
|
520
|
+
}
|
|
521
|
+
a:hover { text-decoration: underline; }
|
|
522
|
+
</style>
|
|
523
|
+
</head>
|
|
524
|
+
<body>
|
|
525
|
+
<div class="container">
|
|
526
|
+
<div class="success-icon">\u2705</div>
|
|
527
|
+
<h2>Authentication Successful!</h2>
|
|
528
|
+
<p>You can close this window and return to the CLI.</p>
|
|
529
|
+
<div class="help-text">
|
|
530
|
+
<ol>
|
|
531
|
+
<li>Use command <code>basic help</code> to get started with the CLI</li>
|
|
532
|
+
<li>Visit the <a href="https://docs.basic.tech" target="_blank">Basic docs</a> for more info</li>
|
|
533
|
+
</ol>
|
|
534
|
+
</div>
|
|
535
|
+
</div>
|
|
536
|
+
<script>
|
|
537
|
+
setTimeout(() => window.close(), 3000);
|
|
538
|
+
</script>
|
|
539
|
+
</body>
|
|
540
|
+
</html>
|
|
541
|
+
`;
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
// dist/commands/login.js
|
|
548
|
+
var login_exports = {};
|
|
549
|
+
__export(login_exports, {
|
|
550
|
+
LoginCommand: () => LoginCommand
|
|
551
|
+
});
|
|
552
|
+
async function LoginCommand() {
|
|
553
|
+
if (!await isOnline()) {
|
|
554
|
+
throw new Error(MESSAGES.OFFLINE);
|
|
555
|
+
}
|
|
556
|
+
const authService = AuthService.getInstance();
|
|
557
|
+
const existingToken = await authService.getToken();
|
|
558
|
+
if (existingToken) {
|
|
559
|
+
console.log("Already logged in with a valid token.");
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
console.log("\u{1F510} Opening browser for login...");
|
|
563
|
+
await authService.login();
|
|
564
|
+
console.log("\u2705 Login successful! Hello :)");
|
|
565
|
+
}
|
|
566
|
+
var init_login = __esm({
|
|
567
|
+
"dist/commands/login.js"() {
|
|
568
|
+
"use strict";
|
|
569
|
+
init_auth();
|
|
570
|
+
init_platform();
|
|
571
|
+
init_constants();
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// dist/commands/logout.js
|
|
576
|
+
var logout_exports = {};
|
|
577
|
+
__export(logout_exports, {
|
|
578
|
+
LogoutCommand: () => LogoutCommand
|
|
579
|
+
});
|
|
580
|
+
async function LogoutCommand() {
|
|
581
|
+
const authService = AuthService.getInstance();
|
|
582
|
+
await authService.logout();
|
|
583
|
+
console.log("Logged out successfully");
|
|
584
|
+
}
|
|
585
|
+
var init_logout = __esm({
|
|
586
|
+
"dist/commands/logout.js"() {
|
|
587
|
+
"use strict";
|
|
588
|
+
init_auth();
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
// dist/commands/account.js
|
|
593
|
+
var account_exports = {};
|
|
594
|
+
__export(account_exports, {
|
|
595
|
+
AccountCommand: () => AccountCommand
|
|
596
|
+
});
|
|
597
|
+
async function AccountCommand() {
|
|
598
|
+
if (!await isOnline()) {
|
|
599
|
+
throw new Error(MESSAGES.OFFLINE);
|
|
600
|
+
}
|
|
601
|
+
const authService = AuthService.getInstance();
|
|
602
|
+
const token = await authService.getToken();
|
|
603
|
+
if (!token) {
|
|
604
|
+
throw new Error(MESSAGES.LOGGED_OUT);
|
|
605
|
+
}
|
|
606
|
+
const userInfo = await authService.getUserInfo();
|
|
607
|
+
console.log("Logged in user:", userInfo.email);
|
|
608
|
+
}
|
|
609
|
+
var init_account = __esm({
|
|
610
|
+
"dist/commands/account.js"() {
|
|
611
|
+
"use strict";
|
|
612
|
+
init_auth();
|
|
613
|
+
init_platform();
|
|
614
|
+
init_constants();
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
// dist/lib/api.js
|
|
619
|
+
var ApiClient;
|
|
620
|
+
var init_api = __esm({
|
|
621
|
+
"dist/lib/api.js"() {
|
|
622
|
+
"use strict";
|
|
623
|
+
init_constants();
|
|
624
|
+
init_auth();
|
|
625
|
+
init_errors();
|
|
626
|
+
ApiClient = class _ApiClient {
|
|
627
|
+
static instance;
|
|
628
|
+
authService;
|
|
629
|
+
constructor() {
|
|
630
|
+
this.authService = AuthService.getInstance();
|
|
631
|
+
}
|
|
632
|
+
static getInstance() {
|
|
633
|
+
if (!_ApiClient.instance) {
|
|
634
|
+
_ApiClient.instance = new _ApiClient();
|
|
635
|
+
}
|
|
636
|
+
return _ApiClient.instance;
|
|
637
|
+
}
|
|
638
|
+
async request(endpoint, options = {}) {
|
|
639
|
+
try {
|
|
640
|
+
const token = await this.authService.getToken();
|
|
641
|
+
const response = await fetch(`${CONSTANTS.API_BASE}${endpoint}`, {
|
|
642
|
+
...options,
|
|
643
|
+
headers: {
|
|
644
|
+
"Content-Type": "application/json",
|
|
645
|
+
...token && { Authorization: `Bearer ${token.access_token}` },
|
|
646
|
+
...options.headers
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
if (!response.ok) {
|
|
650
|
+
const errorText = await response.text();
|
|
651
|
+
throw new ApiError(`API Error: ${response.status} - ${errorText}`, response.status);
|
|
652
|
+
}
|
|
653
|
+
return response.json();
|
|
654
|
+
} catch (error) {
|
|
655
|
+
throw handleError(error);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
async getProjects() {
|
|
659
|
+
const response = await this.request("/project");
|
|
660
|
+
return response.data;
|
|
661
|
+
}
|
|
662
|
+
async createProject(data) {
|
|
663
|
+
const response = await this.request("/project", {
|
|
664
|
+
method: "POST",
|
|
665
|
+
body: JSON.stringify(data)
|
|
666
|
+
});
|
|
667
|
+
return response.data;
|
|
668
|
+
}
|
|
669
|
+
async createProjectWithTeam(name, slug, teamId) {
|
|
670
|
+
const response = await this.request("/project", {
|
|
671
|
+
method: "POST",
|
|
672
|
+
body: JSON.stringify({
|
|
673
|
+
name,
|
|
674
|
+
slug,
|
|
675
|
+
team_id: teamId
|
|
676
|
+
})
|
|
677
|
+
});
|
|
678
|
+
return response.data;
|
|
679
|
+
}
|
|
680
|
+
async getProject(projectId) {
|
|
681
|
+
const response = await this.request(`/project/${projectId}`);
|
|
682
|
+
return response.data;
|
|
683
|
+
}
|
|
684
|
+
async getTeams() {
|
|
685
|
+
const response = await this.request("/team");
|
|
686
|
+
return response.data;
|
|
687
|
+
}
|
|
688
|
+
async createTeam(name, slug) {
|
|
689
|
+
const response = await this.request("/team", {
|
|
690
|
+
method: "POST",
|
|
691
|
+
body: JSON.stringify({ name, slug })
|
|
692
|
+
});
|
|
693
|
+
return response.data;
|
|
694
|
+
}
|
|
695
|
+
async checkTeamSlugAvailability(slug) {
|
|
696
|
+
try {
|
|
697
|
+
const response = await this.request(`/team/slug?slug=${encodeURIComponent(slug)}`);
|
|
698
|
+
return response.available;
|
|
699
|
+
} catch (error) {
|
|
700
|
+
return false;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
async getProjectSchema(projectId) {
|
|
704
|
+
try {
|
|
705
|
+
const response = await this.request(`/project/${projectId}/schema`);
|
|
706
|
+
if (response.data.length === 0) {
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
return response.data[0].schema;
|
|
710
|
+
} catch (error) {
|
|
711
|
+
if (error instanceof ApiError && error.statusCode === 404) {
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
throw error;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
async pushProjectSchema(projectId, schema) {
|
|
718
|
+
await this.request(`/project/${projectId}/schema`, {
|
|
719
|
+
method: "POST",
|
|
720
|
+
body: JSON.stringify({ schema })
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
async validateSchema(schema) {
|
|
724
|
+
const response = await this.request("/utils/schema/verifyUpdateSchema", {
|
|
725
|
+
method: "POST",
|
|
726
|
+
body: JSON.stringify({ schema })
|
|
727
|
+
});
|
|
728
|
+
return response;
|
|
729
|
+
}
|
|
730
|
+
async compareSchema(schema) {
|
|
731
|
+
const response = await this.request("/utils/schema/compareSchema", {
|
|
732
|
+
method: "POST",
|
|
733
|
+
body: JSON.stringify({ schema })
|
|
734
|
+
});
|
|
735
|
+
return response;
|
|
736
|
+
}
|
|
737
|
+
async checkLatestRelease() {
|
|
738
|
+
try {
|
|
739
|
+
const response = await fetch(`https://registry.npmjs.org/@basictech/cli/latest`);
|
|
740
|
+
if (!response.ok) {
|
|
741
|
+
throw new Error("Failed to check for updates");
|
|
742
|
+
}
|
|
743
|
+
const data = await response.json();
|
|
744
|
+
return data.version;
|
|
745
|
+
} catch (error) {
|
|
746
|
+
throw handleError(error);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
// dist/commands/version.js
|
|
754
|
+
var version_exports = {};
|
|
755
|
+
__export(version_exports, {
|
|
756
|
+
VersionCommand: () => VersionCommand
|
|
757
|
+
});
|
|
758
|
+
async function VersionCommand() {
|
|
759
|
+
const currentVersion = getVersion();
|
|
760
|
+
console.log(`basic-cli version ${currentVersion}`);
|
|
761
|
+
try {
|
|
762
|
+
const apiClient = ApiClient.getInstance();
|
|
763
|
+
const latestVersion = await apiClient.checkLatestRelease();
|
|
764
|
+
if (latestVersion !== currentVersion) {
|
|
765
|
+
console.log(`New version available: ${latestVersion}`);
|
|
766
|
+
console.log("\nPlease update with 'basic update'");
|
|
767
|
+
} else {
|
|
768
|
+
console.log("You are running the latest version!");
|
|
769
|
+
}
|
|
770
|
+
} catch (error) {
|
|
771
|
+
console.log("\nOopsy - could not check if new version is available.");
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
var init_version2 = __esm({
|
|
775
|
+
"dist/commands/version.js"() {
|
|
776
|
+
"use strict";
|
|
777
|
+
init_version();
|
|
778
|
+
init_api();
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
// dist/commands/update.js
|
|
783
|
+
var update_exports = {};
|
|
784
|
+
__export(update_exports, {
|
|
785
|
+
UpdateCommand: () => UpdateCommand
|
|
786
|
+
});
|
|
787
|
+
import { spawn } from "child_process";
|
|
788
|
+
async function UpdateCommand() {
|
|
789
|
+
try {
|
|
790
|
+
if (!await isOnline()) {
|
|
791
|
+
console.error(MESSAGES.OFFLINE);
|
|
792
|
+
process.exit(1);
|
|
793
|
+
}
|
|
794
|
+
const currentVersion = getVersion();
|
|
795
|
+
console.log(`Current version: ${currentVersion}`);
|
|
796
|
+
console.log("Checking for updates...");
|
|
797
|
+
const apiClient = ApiClient.getInstance();
|
|
798
|
+
const latestVersion = await apiClient.checkLatestRelease();
|
|
799
|
+
if (latestVersion === currentVersion) {
|
|
800
|
+
console.log("\u2705 You are already running the latest version!");
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
console.log(`\u{1F680} New version available: ${latestVersion}`);
|
|
804
|
+
console.log("Updating...");
|
|
805
|
+
await updatePackage();
|
|
806
|
+
} catch (error) {
|
|
807
|
+
console.error("\u274C Error checking for updates:", error instanceof Error ? error.message : "Unknown error");
|
|
808
|
+
console.log("\n\u{1F4A1} You can try updating manually:");
|
|
809
|
+
console.log(" npm update -g @basictech/cli");
|
|
810
|
+
console.log("\n\u{1F4DA} Or visit: https://docs.basic.tech");
|
|
10
811
|
process.exit(1);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
async function updatePackage() {
|
|
815
|
+
return new Promise((resolve, reject) => {
|
|
816
|
+
const updateProcess = spawn("npm", ["update", "-g", "@basictech/cli"], {
|
|
817
|
+
stdio: "pipe",
|
|
818
|
+
shell: true
|
|
819
|
+
});
|
|
820
|
+
let output = "";
|
|
821
|
+
let errorOutput = "";
|
|
822
|
+
updateProcess.stdout?.on("data", (data) => {
|
|
823
|
+
output += data.toString();
|
|
824
|
+
});
|
|
825
|
+
updateProcess.stderr?.on("data", (data) => {
|
|
826
|
+
errorOutput += data.toString();
|
|
827
|
+
});
|
|
828
|
+
updateProcess.on("close", (code) => {
|
|
829
|
+
if (code === 0) {
|
|
830
|
+
console.log("\u2705 Update successful!");
|
|
831
|
+
console.log("\n\u{1F389} Basic CLI has been updated to the latest version.");
|
|
832
|
+
console.log("\u{1F4A1} Run `basic version` to verify the update.");
|
|
833
|
+
resolve();
|
|
834
|
+
} else {
|
|
835
|
+
console.error("\u274C Error updating CLI");
|
|
836
|
+
console.log("\n\u{1F4A1} Please try updating manually:");
|
|
837
|
+
console.log(" npm update -g @basictech/cli");
|
|
838
|
+
console.log("\n\u{1F4DA} Or visit: https://docs.basic.tech");
|
|
839
|
+
if (errorOutput) {
|
|
840
|
+
console.log("\nError details:", errorOutput);
|
|
841
|
+
}
|
|
842
|
+
reject(new Error(`Update process exited with code ${code}`));
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
updateProcess.on("error", (error) => {
|
|
846
|
+
console.error("\u274C Failed to start update process");
|
|
847
|
+
console.log("\n\u{1F4A1} Please try updating manually:");
|
|
848
|
+
console.log(" npm update -g @basictech/cli");
|
|
849
|
+
console.log("\n\u{1F4DA} Or visit: https://docs.basic.tech");
|
|
850
|
+
reject(error);
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
var init_update = __esm({
|
|
855
|
+
"dist/commands/update.js"() {
|
|
856
|
+
"use strict";
|
|
857
|
+
init_version();
|
|
858
|
+
init_api();
|
|
859
|
+
init_platform();
|
|
860
|
+
init_constants();
|
|
861
|
+
}
|
|
11
862
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
863
|
+
|
|
864
|
+
// dist/commands/debug.js
|
|
865
|
+
var debug_exports = {};
|
|
866
|
+
__export(debug_exports, {
|
|
867
|
+
DebugCommand: () => DebugCommand
|
|
15
868
|
});
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
869
|
+
async function DebugCommand() {
|
|
870
|
+
const configDir = getConfigDir();
|
|
871
|
+
console.log(`Basic CLI config directory: ${configDir}`);
|
|
872
|
+
}
|
|
873
|
+
var init_debug = __esm({
|
|
874
|
+
"dist/commands/debug.js"() {
|
|
875
|
+
"use strict";
|
|
876
|
+
init_platform();
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
// dist/components/Table.js
|
|
881
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
882
|
+
import { useState, useEffect } from "react";
|
|
883
|
+
import { Box, Text, useInput } from "ink";
|
|
884
|
+
function Table({ columns, rows, onSelect, onCopy, onOpen, onNew, onExit, helpText }) {
|
|
885
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
886
|
+
const [notification, setNotification] = useState("");
|
|
887
|
+
const defaultHelpText = {
|
|
888
|
+
copyAction: "'c' to copy project ID",
|
|
889
|
+
openAction: "'o' to open in browser",
|
|
890
|
+
newAction: void 0
|
|
891
|
+
};
|
|
892
|
+
const currentHelpText = helpText || defaultHelpText;
|
|
893
|
+
useInput((input, key) => {
|
|
894
|
+
if (key.upArrow && rows.length > 0) {
|
|
895
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
896
|
+
} else if (key.downArrow && rows.length > 0) {
|
|
897
|
+
setSelectedIndex((prev) => Math.min(rows.length - 1, prev + 1));
|
|
898
|
+
} else if (key.return && rows[selectedIndex] && onSelect) {
|
|
899
|
+
onSelect(rows[selectedIndex], selectedIndex);
|
|
900
|
+
} else if (input === "c" && rows[selectedIndex] && onCopy) {
|
|
901
|
+
onCopy(rows[selectedIndex], selectedIndex);
|
|
902
|
+
const itemType = helpText?.copyAction.includes("team") ? "Team ID" : "Project ID";
|
|
903
|
+
setNotification(`${itemType} copied to clipboard!`);
|
|
904
|
+
setTimeout(() => setNotification(""), 3e3);
|
|
905
|
+
} else if (input === "o" && rows[selectedIndex] && onOpen) {
|
|
906
|
+
onOpen(rows[selectedIndex], selectedIndex);
|
|
907
|
+
} else if (input === "n" && onNew) {
|
|
908
|
+
onNew();
|
|
909
|
+
} else if (key.escape || key.ctrl && input === "c") {
|
|
910
|
+
if (onExit) {
|
|
911
|
+
onExit();
|
|
912
|
+
} else {
|
|
29
913
|
process.exit(0);
|
|
914
|
+
}
|
|
30
915
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
916
|
+
});
|
|
917
|
+
useEffect(() => {
|
|
918
|
+
if (selectedIndex >= rows.length) {
|
|
919
|
+
setSelectedIndex(Math.max(0, rows.length - 1));
|
|
35
920
|
}
|
|
921
|
+
}, [rows.length, selectedIndex]);
|
|
922
|
+
const renderHeader = () => _jsx(Box, { borderStyle: "single", borderBottom: true, paddingX: 1, children: _jsx(Box, { children: columns.map((column, index) => _jsx(Box, { width: column.width, marginRight: index < columns.length - 1 ? 1 : 0, children: _jsx(Text, { bold: true, children: column.title }) }, column.key)) }) });
|
|
923
|
+
const renderRow = (row, index) => {
|
|
924
|
+
const isSelected = index === selectedIndex;
|
|
925
|
+
return _jsx(Box, { children: _jsx(Box, { paddingX: 1, children: columns.map((column, colIndex) => _jsx(Box, { width: column.width, marginRight: colIndex < columns.length - 1 ? 1 : 0, children: _jsx(Text, { color: isSelected ? "black" : void 0, backgroundColor: isSelected ? "magenta" : void 0, children: (row[column.key] || "").substring(0, column.width - 1) }) }, column.key)) }) }, index);
|
|
926
|
+
};
|
|
927
|
+
const renderHelp = () => {
|
|
928
|
+
const mainActionsText = [
|
|
929
|
+
currentHelpText.copyAction,
|
|
930
|
+
currentHelpText.openAction
|
|
931
|
+
].filter(Boolean).join(" \u2022 ");
|
|
932
|
+
return _jsxs(Box, { marginTop: 2, children: [notification && _jsx(Box, { marginBottom: 2, children: _jsx(Text, { color: "blue", children: notification }) }), _jsxs(Box, { flexDirection: "column", children: [currentHelpText.newAction && _jsx(Text, { color: "gray", children: currentHelpText.newAction }), _jsx(Text, { color: "gray", children: mainActionsText }), _jsx(Text, { color: "gray", children: "\u2191/\u2193 to navigate \u2022 esc to quit" })] })] });
|
|
933
|
+
};
|
|
934
|
+
if (rows.length === 0) {
|
|
935
|
+
const itemType = helpText?.copyAction.includes("team") ? "teams" : "projects";
|
|
936
|
+
return _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["No ", itemType, " found."] }), _jsxs(Box, { marginTop: 2, flexDirection: "column", children: [currentHelpText.newAction && _jsx(Text, { color: "gray", children: currentHelpText.newAction }), _jsx(Text, { color: "gray", children: "Press esc to quit" })] })] });
|
|
937
|
+
}
|
|
938
|
+
return _jsxs(Box, { flexDirection: "column", children: [renderHeader(), _jsx(Box, { flexDirection: "column", children: rows.map((row, index) => renderRow(row, index)) }), renderHelp()] });
|
|
939
|
+
}
|
|
940
|
+
var init_Table = __esm({
|
|
941
|
+
"dist/components/Table.js"() {
|
|
942
|
+
"use strict";
|
|
943
|
+
}
|
|
36
944
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
945
|
+
|
|
946
|
+
// dist/components/Spinner.js
|
|
947
|
+
import { jsx as _jsx2, jsxs as _jsxs2 } from "react/jsx-runtime";
|
|
948
|
+
import React from "react";
|
|
949
|
+
import { Text as Text2 } from "ink";
|
|
950
|
+
var spinnerFrames, Spinner;
|
|
951
|
+
var init_Spinner = __esm({
|
|
952
|
+
"dist/components/Spinner.js"() {
|
|
953
|
+
"use strict";
|
|
954
|
+
spinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
955
|
+
Spinner = ({ text = "Loading..." }) => {
|
|
956
|
+
const [frame, setFrame] = React.useState(0);
|
|
957
|
+
React.useEffect(() => {
|
|
958
|
+
const timer = setInterval(() => {
|
|
959
|
+
setFrame((prev) => (prev + 1) % spinnerFrames.length);
|
|
960
|
+
}, 80);
|
|
961
|
+
return () => clearInterval(timer);
|
|
962
|
+
}, []);
|
|
963
|
+
return _jsxs2(Text2, { children: [_jsx2(Text2, { color: "cyan", children: spinnerFrames[frame] }), " ", text] });
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
// dist/commands/projects.js
|
|
969
|
+
var projects_exports = {};
|
|
970
|
+
__export(projects_exports, {
|
|
971
|
+
ProjectsCommand: () => ProjectsCommand
|
|
972
|
+
});
|
|
973
|
+
import { jsx as _jsx3 } from "react/jsx-runtime";
|
|
974
|
+
import React2 from "react";
|
|
975
|
+
import { render, Text as Text3 } from "ink";
|
|
976
|
+
function ProjectsApp() {
|
|
977
|
+
const [state, setState] = React2.useState({
|
|
978
|
+
loading: true,
|
|
979
|
+
projects: [],
|
|
980
|
+
error: null
|
|
981
|
+
});
|
|
982
|
+
React2.useEffect(() => {
|
|
983
|
+
async function loadProjects() {
|
|
984
|
+
try {
|
|
985
|
+
if (!await isOnline()) {
|
|
986
|
+
setState((prev) => ({ ...prev, loading: false, error: MESSAGES.OFFLINE }));
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
const authService = AuthService.getInstance();
|
|
990
|
+
const token = await authService.getToken();
|
|
991
|
+
if (!token) {
|
|
992
|
+
setState((prev) => ({ ...prev, loading: false, error: MESSAGES.LOGGED_OUT }));
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
const apiClient = ApiClient.getInstance();
|
|
996
|
+
const projects = await apiClient.getProjects();
|
|
997
|
+
setState({
|
|
998
|
+
loading: false,
|
|
999
|
+
projects,
|
|
1000
|
+
error: null
|
|
1001
|
+
});
|
|
1002
|
+
} catch (error) {
|
|
1003
|
+
setState({
|
|
1004
|
+
loading: false,
|
|
1005
|
+
projects: [],
|
|
1006
|
+
error: error instanceof Error ? error.message : "Failed to load projects"
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
loadProjects();
|
|
1011
|
+
}, []);
|
|
1012
|
+
const handleCopy = async (row) => {
|
|
42
1013
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
process.exit(0);
|
|
1014
|
+
await copyToClipboard(row.id);
|
|
1015
|
+
} catch (error) {
|
|
46
1016
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
1017
|
+
};
|
|
1018
|
+
const handleOpen = async (row) => {
|
|
1019
|
+
try {
|
|
1020
|
+
await openBrowser(`https://app.basic.tech/project/${row.id}`);
|
|
1021
|
+
} catch (error) {
|
|
51
1022
|
}
|
|
1023
|
+
};
|
|
1024
|
+
const handleExit = () => {
|
|
1025
|
+
process.exit(0);
|
|
1026
|
+
};
|
|
1027
|
+
if (state.loading) {
|
|
1028
|
+
return _jsx3(Spinner, { text: "Loading projects..." });
|
|
1029
|
+
}
|
|
1030
|
+
if (state.error) {
|
|
1031
|
+
return _jsx3(Text3, { color: "red", children: state.error });
|
|
1032
|
+
}
|
|
1033
|
+
const columns = [
|
|
1034
|
+
{ title: "ID", width: 38, key: "id" },
|
|
1035
|
+
{ title: "Name", width: 25, key: "name" },
|
|
1036
|
+
{ title: "Team", width: 30, key: "team_name" }
|
|
1037
|
+
];
|
|
1038
|
+
const rows = state.projects.map((project) => ({
|
|
1039
|
+
id: project.id,
|
|
1040
|
+
name: project.name,
|
|
1041
|
+
team_name: project.team_name
|
|
1042
|
+
}));
|
|
1043
|
+
return _jsx3(Table, { columns, rows, onCopy: handleCopy, onOpen: handleOpen, onExit: handleExit });
|
|
1044
|
+
}
|
|
1045
|
+
async function ProjectsCommand() {
|
|
1046
|
+
render(_jsx3(ProjectsApp, {}));
|
|
1047
|
+
}
|
|
1048
|
+
var init_projects = __esm({
|
|
1049
|
+
"dist/commands/projects.js"() {
|
|
1050
|
+
"use strict";
|
|
1051
|
+
init_Table();
|
|
1052
|
+
init_Spinner();
|
|
1053
|
+
init_api();
|
|
1054
|
+
init_auth();
|
|
1055
|
+
init_platform();
|
|
1056
|
+
init_constants();
|
|
1057
|
+
}
|
|
52
1058
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
1059
|
+
|
|
1060
|
+
// dist/components/TeamForm.js
|
|
1061
|
+
import { jsx as _jsx4, jsxs as _jsxs3 } from "react/jsx-runtime";
|
|
1062
|
+
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
1063
|
+
import { Box as Box2, Text as Text4, useInput as useInput2 } from "ink";
|
|
1064
|
+
function TeamForm({ title, onSubmit, onCancel }) {
|
|
1065
|
+
const [state, setState] = useState2({
|
|
1066
|
+
teamName: "",
|
|
1067
|
+
teamSlug: "",
|
|
1068
|
+
currentField: "name",
|
|
1069
|
+
isCheckingSlug: false,
|
|
1070
|
+
slugAvailable: null,
|
|
1071
|
+
error: null
|
|
1072
|
+
});
|
|
1073
|
+
useEffect2(() => {
|
|
1074
|
+
if (state.teamName.trim()) {
|
|
1075
|
+
const newSlug = generateSlug(state.teamName);
|
|
1076
|
+
setState((prev) => ({
|
|
1077
|
+
...prev,
|
|
1078
|
+
teamSlug: newSlug,
|
|
1079
|
+
slugAvailable: null,
|
|
1080
|
+
error: null
|
|
1081
|
+
}));
|
|
1082
|
+
} else {
|
|
1083
|
+
setState((prev) => ({
|
|
1084
|
+
...prev,
|
|
1085
|
+
teamSlug: "",
|
|
1086
|
+
slugAvailable: null,
|
|
1087
|
+
error: null
|
|
1088
|
+
}));
|
|
1089
|
+
}
|
|
1090
|
+
}, [state.teamName]);
|
|
1091
|
+
useEffect2(() => {
|
|
1092
|
+
if (state.teamSlug.trim() && (state.currentField === "name" || state.currentField === "slug")) {
|
|
1093
|
+
const checkAvailability = async () => {
|
|
1094
|
+
setState((prev) => ({ ...prev, isCheckingSlug: true, error: null }));
|
|
1095
|
+
try {
|
|
1096
|
+
const apiClient = ApiClient.getInstance();
|
|
1097
|
+
const available = await apiClient.checkTeamSlugAvailability(state.teamSlug);
|
|
1098
|
+
setState((prev) => ({
|
|
1099
|
+
...prev,
|
|
1100
|
+
isCheckingSlug: false,
|
|
1101
|
+
slugAvailable: available,
|
|
1102
|
+
error: available ? null : "Team slug is already taken"
|
|
1103
|
+
}));
|
|
1104
|
+
} catch (error) {
|
|
1105
|
+
setState((prev) => ({
|
|
1106
|
+
...prev,
|
|
1107
|
+
isCheckingSlug: false,
|
|
1108
|
+
slugAvailable: false,
|
|
1109
|
+
error: "Error checking slug availability"
|
|
1110
|
+
}));
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
const timeoutId = setTimeout(checkAvailability, 500);
|
|
1114
|
+
return () => clearTimeout(timeoutId);
|
|
1115
|
+
}
|
|
1116
|
+
}, [state.teamSlug, state.currentField]);
|
|
1117
|
+
useInput2((input, key) => {
|
|
1118
|
+
if (state.currentField === "submitting") {
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
if (key.escape) {
|
|
1122
|
+
onCancel();
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
if (key.return) {
|
|
1126
|
+
if (state.currentField === "name") {
|
|
1127
|
+
if (!state.teamName.trim()) {
|
|
1128
|
+
setState((prev) => ({ ...prev, error: "Team name is required" }));
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
setState((prev) => ({ ...prev, currentField: "slug", error: null }));
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
if (state.currentField === "slug") {
|
|
1135
|
+
if (!state.teamSlug.trim()) {
|
|
1136
|
+
setState((prev) => ({ ...prev, error: "Team slug is required" }));
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
if (!state.slugAvailable) {
|
|
1140
|
+
setState((prev) => ({ ...prev, error: "Please wait for slug availability check or choose a different name" }));
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
setState((prev) => ({ ...prev, currentField: "submitting", error: null }));
|
|
1144
|
+
const result = onSubmit({ teamName: state.teamName, teamSlug: state.teamSlug });
|
|
1145
|
+
if (result instanceof Promise) {
|
|
1146
|
+
result.catch((error) => {
|
|
1147
|
+
setState((prev) => ({
|
|
1148
|
+
...prev,
|
|
1149
|
+
currentField: "slug",
|
|
1150
|
+
error: error.message || "Failed to create team"
|
|
1151
|
+
}));
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
if (key.backspace || key.delete) {
|
|
1158
|
+
if (state.currentField === "name") {
|
|
1159
|
+
setState((prev) => ({
|
|
1160
|
+
...prev,
|
|
1161
|
+
teamName: prev.teamName.slice(0, -1),
|
|
1162
|
+
error: null
|
|
1163
|
+
}));
|
|
1164
|
+
} else if (state.currentField === "slug") {
|
|
1165
|
+
setState((prev) => ({
|
|
1166
|
+
...prev,
|
|
1167
|
+
teamSlug: prev.teamSlug.slice(0, -1),
|
|
1168
|
+
error: null
|
|
1169
|
+
}));
|
|
1170
|
+
}
|
|
1171
|
+
return;
|
|
62
1172
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
1173
|
+
if (input && input.length === 1) {
|
|
1174
|
+
if (state.currentField === "name") {
|
|
1175
|
+
setState((prev) => ({
|
|
1176
|
+
...prev,
|
|
1177
|
+
teamName: prev.teamName + input,
|
|
1178
|
+
error: null
|
|
1179
|
+
}));
|
|
1180
|
+
} else if (state.currentField === "slug") {
|
|
1181
|
+
setState((prev) => ({
|
|
1182
|
+
...prev,
|
|
1183
|
+
teamSlug: prev.teamSlug + input,
|
|
1184
|
+
error: null
|
|
1185
|
+
}));
|
|
1186
|
+
}
|
|
67
1187
|
}
|
|
1188
|
+
});
|
|
1189
|
+
const getSlugStatus = () => {
|
|
1190
|
+
if (!state.teamSlug.trim())
|
|
1191
|
+
return null;
|
|
1192
|
+
if (state.isCheckingSlug)
|
|
1193
|
+
return "checking";
|
|
1194
|
+
if (state.slugAvailable === true)
|
|
1195
|
+
return "available";
|
|
1196
|
+
if (state.slugAvailable === false)
|
|
1197
|
+
return "unavailable";
|
|
1198
|
+
return null;
|
|
1199
|
+
};
|
|
1200
|
+
const getSlugStatusText = () => {
|
|
1201
|
+
const status = getSlugStatus();
|
|
1202
|
+
switch (status) {
|
|
1203
|
+
case "checking":
|
|
1204
|
+
return "\u23F3 Checking availability...";
|
|
1205
|
+
case "available":
|
|
1206
|
+
return "\u2705 Slug available";
|
|
1207
|
+
case "unavailable":
|
|
1208
|
+
return "\u274C Slug not available";
|
|
1209
|
+
default:
|
|
1210
|
+
return "";
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
const getSlugStatusColor = () => {
|
|
1214
|
+
const status = getSlugStatus();
|
|
1215
|
+
switch (status) {
|
|
1216
|
+
case "checking":
|
|
1217
|
+
return "yellow";
|
|
1218
|
+
case "available":
|
|
1219
|
+
return "green";
|
|
1220
|
+
case "unavailable":
|
|
1221
|
+
return "red";
|
|
1222
|
+
default:
|
|
1223
|
+
return "gray";
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
const canSubmit = state.teamName.trim() && state.slugAvailable === true && !state.isCheckingSlug;
|
|
1227
|
+
const getHelpText = () => {
|
|
1228
|
+
if (state.currentField === "name") {
|
|
1229
|
+
return state.teamName.trim() ? "Enter to edit slug \u2022 esc to cancel" : "Type team name \u2022 esc to cancel";
|
|
1230
|
+
} else if (state.currentField === "slug") {
|
|
1231
|
+
return canSubmit ? "Enter to create team \u2022 esc to cancel" : "Edit team slug \u2022 esc to cancel";
|
|
1232
|
+
}
|
|
1233
|
+
return "esc to cancel";
|
|
1234
|
+
};
|
|
1235
|
+
return _jsxs3(Box2, { flexDirection: "column", padding: 1, children: [_jsx4(Box2, { marginBottom: 2, children: _jsx4(Text4, { bold: true, color: "blue", children: title }) }), _jsxs3(Box2, { flexDirection: "column", marginBottom: 1, children: [_jsx4(Box2, { children: _jsxs3(Text4, { color: state.currentField === "name" ? "blue" : "gray", children: [state.currentField === "name" ? ">" : "\u2713", " Team Name:"] }) }), _jsx4(Box2, { marginLeft: 2, children: _jsxs3(Text4, { children: [state.teamName, state.currentField === "name" && _jsx4(Text4, { backgroundColor: "white", color: "black", children: "\u2588" })] }) })] }), state.teamSlug && _jsxs3(Box2, { flexDirection: "column", marginBottom: 1, children: [_jsx4(Box2, { children: _jsxs3(Text4, { color: state.currentField === "slug" ? "blue" : "gray", children: [state.currentField === "slug" ? ">" : "\u2713", " Team Slug", state.currentField === "name" ? " (auto-generated)" : "", ":"] }) }), _jsx4(Box2, { marginLeft: 2, children: _jsxs3(Text4, { children: [state.teamSlug, state.currentField === "slug" && _jsx4(Text4, { backgroundColor: "white", color: "black", children: "\u2588" })] }) }), getSlugStatus() && _jsx4(Box2, { marginLeft: 2, children: _jsx4(Text4, { color: getSlugStatusColor(), children: getSlugStatusText() }) })] }), state.error && _jsx4(Box2, { marginLeft: 2, marginBottom: 1, children: _jsxs3(Text4, { color: "red", children: ["Error: ", state.error] }) }), _jsx4(Box2, { marginTop: 2, children: _jsx4(Text4, { color: "gray", children: getHelpText() }) })] });
|
|
1236
|
+
}
|
|
1237
|
+
var init_TeamForm = __esm({
|
|
1238
|
+
"dist/components/TeamForm.js"() {
|
|
1239
|
+
"use strict";
|
|
1240
|
+
init_api();
|
|
1241
|
+
init_platform();
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
// dist/commands/teams.js
|
|
1246
|
+
var teams_exports = {};
|
|
1247
|
+
__export(teams_exports, {
|
|
1248
|
+
TeamsCommand: () => TeamsCommand
|
|
68
1249
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
1250
|
+
import { jsx as _jsx5, jsxs as _jsxs4 } from "react/jsx-runtime";
|
|
1251
|
+
import React3 from "react";
|
|
1252
|
+
import { render as render2, Box as Box3, Text as Text5 } from "ink";
|
|
1253
|
+
function TeamsApp() {
|
|
1254
|
+
const [state, setState] = React3.useState({
|
|
1255
|
+
loading: true,
|
|
1256
|
+
teams: [],
|
|
1257
|
+
error: null
|
|
1258
|
+
});
|
|
1259
|
+
React3.useEffect(() => {
|
|
1260
|
+
async function loadTeams() {
|
|
1261
|
+
try {
|
|
1262
|
+
if (!await isOnline()) {
|
|
1263
|
+
setState((prev) => ({ ...prev, loading: false, error: MESSAGES.OFFLINE }));
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
const authService = AuthService.getInstance();
|
|
1267
|
+
const token = await authService.getToken();
|
|
1268
|
+
if (!token) {
|
|
1269
|
+
setState((prev) => ({ ...prev, loading: false, error: MESSAGES.LOGGED_OUT }));
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
const apiClient = ApiClient.getInstance();
|
|
1273
|
+
const teams = await apiClient.getTeams();
|
|
1274
|
+
setState({
|
|
1275
|
+
loading: false,
|
|
1276
|
+
teams,
|
|
1277
|
+
error: null
|
|
1278
|
+
});
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
setState({
|
|
1281
|
+
loading: false,
|
|
1282
|
+
teams: [],
|
|
1283
|
+
error: error instanceof Error ? error.message : "Failed to load teams"
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
loadTeams();
|
|
1288
|
+
}, []);
|
|
1289
|
+
const handleCopy = async (row) => {
|
|
74
1290
|
try {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
process.exit(0);
|
|
1291
|
+
await copyToClipboard(row.id);
|
|
1292
|
+
} catch (error) {
|
|
78
1293
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
1294
|
+
};
|
|
1295
|
+
const handleOpen = async (row) => {
|
|
1296
|
+
try {
|
|
1297
|
+
await openBrowser(`https://app.basic.tech/team/${row.slug}`);
|
|
1298
|
+
} catch (error) {
|
|
83
1299
|
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
1300
|
+
};
|
|
1301
|
+
const handleExit = () => {
|
|
1302
|
+
process.exit(0);
|
|
1303
|
+
};
|
|
1304
|
+
const handleNew = () => {
|
|
1305
|
+
render2(_jsx5(NewTeamApp, {}));
|
|
1306
|
+
};
|
|
1307
|
+
if (state.loading) {
|
|
1308
|
+
return _jsx5(Spinner, { text: "Loading teams..." });
|
|
1309
|
+
}
|
|
1310
|
+
if (state.error) {
|
|
1311
|
+
return _jsx5(Text5, { color: "red", children: state.error });
|
|
1312
|
+
}
|
|
1313
|
+
const columns = [
|
|
1314
|
+
{ title: "ID", width: 38, key: "id" },
|
|
1315
|
+
{ title: "Name", width: 25, key: "name" },
|
|
1316
|
+
{ title: "Role", width: 20, key: "role_name" }
|
|
1317
|
+
];
|
|
1318
|
+
const rows = state.teams.map((team) => ({
|
|
1319
|
+
id: team.id,
|
|
1320
|
+
name: team.name,
|
|
1321
|
+
role_name: team.role_name || "Member",
|
|
1322
|
+
slug: team.slug
|
|
1323
|
+
}));
|
|
1324
|
+
return _jsx5(Table, { columns, rows, onCopy: handleCopy, onOpen: handleOpen, onExit: handleExit, onNew: handleNew, helpText: {
|
|
1325
|
+
copyAction: "'c' to copy team ID",
|
|
1326
|
+
openAction: "'o' to open in browser",
|
|
1327
|
+
newAction: "'n' to create a new team"
|
|
1328
|
+
} });
|
|
1329
|
+
}
|
|
1330
|
+
function NewTeamApp() {
|
|
1331
|
+
const [state, setState] = React3.useState({
|
|
1332
|
+
loading: false,
|
|
1333
|
+
error: null,
|
|
1334
|
+
success: false,
|
|
1335
|
+
teamName: "",
|
|
1336
|
+
teamSlug: ""
|
|
1337
|
+
});
|
|
1338
|
+
const handleSubmit = async (data) => {
|
|
1339
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
90
1340
|
try {
|
|
91
|
-
|
|
92
|
-
|
|
1341
|
+
if (!await isOnline()) {
|
|
1342
|
+
setState((prev) => ({ ...prev, loading: false, error: MESSAGES.OFFLINE }));
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
const authService = AuthService.getInstance();
|
|
1346
|
+
const token = await authService.getToken();
|
|
1347
|
+
if (!token) {
|
|
1348
|
+
setState((prev) => ({ ...prev, loading: false, error: MESSAGES.LOGGED_OUT }));
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
const apiClient = ApiClient.getInstance();
|
|
1352
|
+
await apiClient.createTeam(data.teamName, data.teamSlug);
|
|
1353
|
+
setState({
|
|
1354
|
+
loading: false,
|
|
1355
|
+
error: null,
|
|
1356
|
+
success: true,
|
|
1357
|
+
teamName: data.teamName,
|
|
1358
|
+
teamSlug: data.teamSlug
|
|
1359
|
+
});
|
|
1360
|
+
setTimeout(() => {
|
|
93
1361
|
process.exit(0);
|
|
1362
|
+
}, 2e3);
|
|
1363
|
+
} catch (error) {
|
|
1364
|
+
setState((prev) => ({
|
|
1365
|
+
...prev,
|
|
1366
|
+
loading: false,
|
|
1367
|
+
error: error instanceof Error ? error.message : "Failed to create team"
|
|
1368
|
+
}));
|
|
94
1369
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
1370
|
+
};
|
|
1371
|
+
if (state.loading) {
|
|
1372
|
+
return _jsx5(Spinner, { text: "Creating team..." });
|
|
1373
|
+
}
|
|
1374
|
+
if (state.success) {
|
|
1375
|
+
return _jsxs4(Box3, { flexDirection: "column", children: [_jsxs4(Text5, { color: "green", children: ['\u2705 Team "', state.teamName, '" created successfully!'] }), _jsxs4(Text5, { children: ["Team slug: ", state.teamSlug] })] });
|
|
1376
|
+
}
|
|
1377
|
+
if (state.error) {
|
|
1378
|
+
return _jsx5(Text5, { color: "red", children: state.error });
|
|
1379
|
+
}
|
|
1380
|
+
return _jsx5(TeamForm, { title: "Create New Team", onSubmit: handleSubmit, onCancel: () => process.exit(0) });
|
|
1381
|
+
}
|
|
1382
|
+
async function TeamsCommand(action) {
|
|
1383
|
+
if (action === "new") {
|
|
1384
|
+
render2(_jsx5(NewTeamApp, {}));
|
|
1385
|
+
} else {
|
|
1386
|
+
render2(_jsx5(TeamsApp, {}));
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
var init_teams = __esm({
|
|
1390
|
+
"dist/commands/teams.js"() {
|
|
1391
|
+
"use strict";
|
|
1392
|
+
init_Table();
|
|
1393
|
+
init_Spinner();
|
|
1394
|
+
init_TeamForm();
|
|
1395
|
+
init_api();
|
|
1396
|
+
init_auth();
|
|
1397
|
+
init_platform();
|
|
1398
|
+
init_constants();
|
|
1399
|
+
}
|
|
100
1400
|
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
1401
|
+
|
|
1402
|
+
// dist/lib/schema.js
|
|
1403
|
+
var schema_exports = {};
|
|
1404
|
+
__export(schema_exports, {
|
|
1405
|
+
compareVersions: () => compareVersions,
|
|
1406
|
+
parseSchemaFile: () => parseSchemaFile,
|
|
1407
|
+
readSchemaFromConfig: () => readSchemaFromConfig,
|
|
1408
|
+
saveSchemaToConfig: () => saveSchemaToConfig
|
|
107
1409
|
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
1410
|
+
import * as fs2 from "fs/promises";
|
|
1411
|
+
import * as path2 from "path";
|
|
1412
|
+
import { parse } from "@babel/parser";
|
|
1413
|
+
async function readSchemaFromConfig(targetDir = process.cwd()) {
|
|
1414
|
+
const possibleFiles = [
|
|
1415
|
+
"basic.config.ts",
|
|
1416
|
+
"basic.config.js",
|
|
1417
|
+
"basic.config.json"
|
|
1418
|
+
];
|
|
1419
|
+
for (const filename of possibleFiles) {
|
|
1420
|
+
const filePath = path2.join(targetDir, filename);
|
|
113
1421
|
try {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
1422
|
+
await fs2.access(filePath);
|
|
1423
|
+
const content = await fs2.readFile(filePath, "utf8");
|
|
1424
|
+
let schema;
|
|
1425
|
+
if (filename.endsWith(".json")) {
|
|
1426
|
+
schema = JSON.parse(content);
|
|
1427
|
+
} else {
|
|
1428
|
+
schema = extractSchemaFromCode(content);
|
|
1429
|
+
}
|
|
1430
|
+
if (!schema.project_id) {
|
|
1431
|
+
throw new Error("No project_id found in schema");
|
|
1432
|
+
}
|
|
1433
|
+
return {
|
|
1434
|
+
schema,
|
|
1435
|
+
projectId: schema.project_id,
|
|
1436
|
+
filePath
|
|
1437
|
+
};
|
|
1438
|
+
} catch (error) {
|
|
1439
|
+
if (error.code === "ENOENT") {
|
|
1440
|
+
continue;
|
|
1441
|
+
}
|
|
1442
|
+
throw new Error(`Error reading ${filename}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
return null;
|
|
1446
|
+
}
|
|
1447
|
+
function extractSchemaFromCode(content) {
|
|
1448
|
+
try {
|
|
1449
|
+
const ast = parse(content, {
|
|
1450
|
+
sourceType: "module",
|
|
1451
|
+
plugins: ["typescript", "jsx"]
|
|
1452
|
+
});
|
|
1453
|
+
const schemaExport = ast.program.body.find((node) => {
|
|
1454
|
+
if (node.type === "ExportNamedDeclaration") {
|
|
1455
|
+
const declaration = node.declaration;
|
|
1456
|
+
if (declaration?.type === "VariableDeclaration") {
|
|
1457
|
+
const variable = declaration.declarations[0];
|
|
1458
|
+
return variable.id.type === "Identifier" && variable.id.name === "schema";
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
return false;
|
|
1462
|
+
});
|
|
1463
|
+
if (!schemaExport) {
|
|
1464
|
+
throw new Error("Could not find schema export in config file");
|
|
117
1465
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
process.exit(1);
|
|
1466
|
+
const schemaNode = schemaExport.declaration.declarations[0].init;
|
|
1467
|
+
if (!schemaNode || schemaNode.type !== "ObjectExpression") {
|
|
1468
|
+
throw new Error("Schema export must be an object");
|
|
122
1469
|
}
|
|
1470
|
+
const schema = convertAstToObject(schemaNode);
|
|
1471
|
+
if (!schema.project_id || typeof schema.project_id !== "string") {
|
|
1472
|
+
throw new Error("Schema must have a project_id string field");
|
|
1473
|
+
}
|
|
1474
|
+
if (typeof schema.version !== "number") {
|
|
1475
|
+
throw new Error("Schema must have a version number field");
|
|
1476
|
+
}
|
|
1477
|
+
if (!schema.tables || typeof schema.tables !== "object") {
|
|
1478
|
+
throw new Error("Schema must have a tables object field");
|
|
1479
|
+
}
|
|
1480
|
+
return schema;
|
|
1481
|
+
} catch (error) {
|
|
1482
|
+
throw new Error(`Error parsing schema: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
function convertAstToObject(node) {
|
|
1486
|
+
if (node.type === "ObjectExpression") {
|
|
1487
|
+
const obj = {};
|
|
1488
|
+
for (const prop of node.properties) {
|
|
1489
|
+
if (prop.type === "ObjectProperty") {
|
|
1490
|
+
const key = getPropertyKey(prop);
|
|
1491
|
+
obj[key] = convertAstToObject(prop.value);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
return obj;
|
|
1495
|
+
}
|
|
1496
|
+
if (node.type === "ArrayExpression") {
|
|
1497
|
+
return node.elements.map((element) => element ? convertAstToObject(element) : null);
|
|
1498
|
+
}
|
|
1499
|
+
if (node.type === "StringLiteral") {
|
|
1500
|
+
return node.value;
|
|
1501
|
+
}
|
|
1502
|
+
if (node.type === "NumericLiteral") {
|
|
1503
|
+
return node.value;
|
|
1504
|
+
}
|
|
1505
|
+
if (node.type === "BooleanLiteral") {
|
|
1506
|
+
return node.value;
|
|
1507
|
+
}
|
|
1508
|
+
if (node.type === "NullLiteral") {
|
|
1509
|
+
return null;
|
|
1510
|
+
}
|
|
1511
|
+
if (node.type === "Identifier") {
|
|
1512
|
+
if (node.name === "true")
|
|
1513
|
+
return true;
|
|
1514
|
+
if (node.name === "false")
|
|
1515
|
+
return false;
|
|
1516
|
+
if (node.name === "null")
|
|
1517
|
+
return null;
|
|
1518
|
+
throw new Error(`Unexpected identifier: ${node.name}`);
|
|
1519
|
+
}
|
|
1520
|
+
throw new Error(`Unsupported node type: ${node.type}`);
|
|
1521
|
+
}
|
|
1522
|
+
function getPropertyKey(prop) {
|
|
1523
|
+
if (prop.key.type === "Identifier") {
|
|
1524
|
+
return prop.key.name;
|
|
1525
|
+
}
|
|
1526
|
+
if (prop.key.type === "StringLiteral") {
|
|
1527
|
+
return prop.key.value;
|
|
1528
|
+
}
|
|
1529
|
+
throw new Error(`Unsupported property key type: ${prop.key.type}`);
|
|
1530
|
+
}
|
|
1531
|
+
async function saveSchemaToConfig(schema, targetDir = process.cwd()) {
|
|
1532
|
+
const existing = await readSchemaFromConfig(targetDir);
|
|
1533
|
+
if (existing) {
|
|
1534
|
+
const content = await fs2.readFile(existing.filePath, "utf8");
|
|
1535
|
+
const updatedContent = updateSchemaInCode(content, schema);
|
|
1536
|
+
await fs2.writeFile(existing.filePath, updatedContent, "utf8");
|
|
1537
|
+
return existing.filePath;
|
|
1538
|
+
} else {
|
|
1539
|
+
const filePath = path2.join(targetDir, "basic.config.ts");
|
|
1540
|
+
const content = generateConfigContent(schema);
|
|
1541
|
+
await fs2.writeFile(filePath, content, "utf8");
|
|
1542
|
+
return filePath;
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
function updateSchemaInCode(content, newSchema) {
|
|
1546
|
+
const schemaStr = JSON.stringify(newSchema, null, 2);
|
|
1547
|
+
const patterns = [
|
|
1548
|
+
/(const\s+schema\s*=\s*)({[^;]+})(;)/g,
|
|
1549
|
+
/(export\s+const\s+schema\s*=\s*)({[^;]+})(;)/g,
|
|
1550
|
+
/(schema\s*=\s*)({[^;]+})(;)/g
|
|
1551
|
+
];
|
|
1552
|
+
for (const pattern of patterns) {
|
|
1553
|
+
if (pattern.test(content)) {
|
|
1554
|
+
return content.replace(pattern, `$1${schemaStr}$3`);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
throw new Error("Could not update schema in config file");
|
|
1558
|
+
}
|
|
1559
|
+
function generateConfigContent(schema) {
|
|
1560
|
+
return `// Basic Project Configuration
|
|
1561
|
+
// see the docs for more info: https://docs.basic.tech
|
|
1562
|
+
|
|
1563
|
+
const schema = ${JSON.stringify(schema, null, 2)};
|
|
1564
|
+
|
|
1565
|
+
export default schema;
|
|
1566
|
+
`;
|
|
1567
|
+
}
|
|
1568
|
+
function compareVersions(local, remote) {
|
|
1569
|
+
const localVersion = local.version || 0;
|
|
1570
|
+
const remoteVersion = remote.version || 0;
|
|
1571
|
+
let status;
|
|
1572
|
+
if (localVersion === remoteVersion) {
|
|
1573
|
+
status = "equal";
|
|
1574
|
+
} else if (localVersion > remoteVersion) {
|
|
1575
|
+
status = "ahead";
|
|
1576
|
+
} else {
|
|
1577
|
+
status = "behind";
|
|
1578
|
+
}
|
|
1579
|
+
return {
|
|
1580
|
+
status,
|
|
1581
|
+
localVersion,
|
|
1582
|
+
remoteVersion
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
async function parseSchemaFile(filePath) {
|
|
1586
|
+
const content = await fs2.readFile(filePath, "utf8");
|
|
1587
|
+
const ast = parse(content, {
|
|
1588
|
+
sourceType: "module",
|
|
1589
|
+
plugins: ["typescript"]
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
var init_schema = __esm({
|
|
1593
|
+
"dist/lib/schema.js"() {
|
|
1594
|
+
"use strict";
|
|
1595
|
+
}
|
|
123
1596
|
});
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
1597
|
+
|
|
1598
|
+
// dist/commands/status.js
|
|
1599
|
+
var status_exports = {};
|
|
1600
|
+
__export(status_exports, {
|
|
1601
|
+
StatusCommand: () => StatusCommand
|
|
1602
|
+
});
|
|
1603
|
+
import { jsx as _jsx6, jsxs as _jsxs5 } from "react/jsx-runtime";
|
|
1604
|
+
import React4 from "react";
|
|
1605
|
+
import { render as render3, Box as Box4, Text as Text6 } from "ink";
|
|
1606
|
+
function StatusApp() {
|
|
1607
|
+
const [state, setState] = React4.useState({
|
|
1608
|
+
loading: true,
|
|
1609
|
+
error: null
|
|
1610
|
+
});
|
|
1611
|
+
React4.useEffect(() => {
|
|
1612
|
+
async function checkStatus() {
|
|
1613
|
+
try {
|
|
1614
|
+
if (!await isOnline()) {
|
|
1615
|
+
setState({
|
|
1616
|
+
loading: false,
|
|
1617
|
+
error: MESSAGES.OFFLINE
|
|
1618
|
+
});
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
const authService = AuthService.getInstance();
|
|
1622
|
+
const token = await authService.getToken();
|
|
1623
|
+
if (!token) {
|
|
1624
|
+
setState({
|
|
1625
|
+
loading: false,
|
|
1626
|
+
error: MESSAGES.LOGGED_OUT
|
|
1627
|
+
});
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
const localConfig = await readSchemaFromConfig();
|
|
1631
|
+
if (!localConfig) {
|
|
1632
|
+
setState({
|
|
1633
|
+
loading: false,
|
|
1634
|
+
error: null,
|
|
1635
|
+
result: {
|
|
1636
|
+
status: "no-schema",
|
|
1637
|
+
projectId: "",
|
|
1638
|
+
localVersion: 0,
|
|
1639
|
+
remoteVersion: 0,
|
|
1640
|
+
message: ["No schema found in config files"],
|
|
1641
|
+
suggestions: [
|
|
1642
|
+
"Run 'basic init' to create a new project or import an existing project",
|
|
1643
|
+
"Make sure you're in a directory with a basic.config.ts/js file",
|
|
1644
|
+
"Check if your config file has the correct name and format"
|
|
1645
|
+
]
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
const apiClient = ApiClient.getInstance();
|
|
1651
|
+
let remoteSchema = null;
|
|
1652
|
+
try {
|
|
1653
|
+
remoteSchema = await apiClient.getProjectSchema(localConfig.projectId);
|
|
1654
|
+
} catch (error) {
|
|
1655
|
+
setState({
|
|
1656
|
+
loading: false,
|
|
1657
|
+
error: null,
|
|
1658
|
+
result: {
|
|
1659
|
+
status: "invalid",
|
|
1660
|
+
projectId: localConfig.projectId,
|
|
1661
|
+
localVersion: localConfig.schema.version || 0,
|
|
1662
|
+
remoteVersion: 0,
|
|
1663
|
+
message: [
|
|
1664
|
+
`Project ID: ${localConfig.projectId}`,
|
|
1665
|
+
`Error fetching remote schema: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1666
|
+
],
|
|
1667
|
+
suggestions: [
|
|
1668
|
+
"Check if the project ID is correct",
|
|
1669
|
+
"Ensure you have access to this project",
|
|
1670
|
+
"Verify your internet connection",
|
|
1671
|
+
"Try running 'basic login' if authentication has expired"
|
|
1672
|
+
]
|
|
1673
|
+
}
|
|
1674
|
+
});
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
if (!remoteSchema) {
|
|
1678
|
+
remoteSchema = {
|
|
1679
|
+
project_id: localConfig.projectId,
|
|
1680
|
+
version: 0,
|
|
1681
|
+
tables: {}
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
const comparison = compareVersions(localConfig.schema, remoteSchema);
|
|
1685
|
+
const result = await analyzeStatus(localConfig, remoteSchema, comparison);
|
|
1686
|
+
setState({
|
|
1687
|
+
loading: false,
|
|
1688
|
+
error: null,
|
|
1689
|
+
result
|
|
1690
|
+
});
|
|
1691
|
+
} catch (error) {
|
|
1692
|
+
setState({
|
|
1693
|
+
loading: false,
|
|
1694
|
+
error: error instanceof Error ? error.message : "Failed to check status"
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
checkStatus();
|
|
1699
|
+
}, []);
|
|
1700
|
+
if (state.loading) {
|
|
1701
|
+
return _jsx6(Spinner, { text: "Checking status..." });
|
|
1702
|
+
}
|
|
1703
|
+
if (state.error) {
|
|
1704
|
+
return _jsx6(Box4, { flexDirection: "column", children: _jsxs5(Text6, { color: "red", children: ["Error: ", state.error] }) });
|
|
1705
|
+
}
|
|
1706
|
+
if (state.result) {
|
|
1707
|
+
return _jsx6(StatusDisplay, { result: state.result });
|
|
1708
|
+
}
|
|
1709
|
+
return _jsx6(Text6, { children: "Unknown status" });
|
|
1710
|
+
}
|
|
1711
|
+
async function analyzeStatus(localConfig, remoteSchema, comparison) {
|
|
1712
|
+
const apiClient = ApiClient.getInstance();
|
|
1713
|
+
const { schema: localSchema, projectId } = localConfig;
|
|
1714
|
+
const baseResult = {
|
|
1715
|
+
projectId,
|
|
1716
|
+
localVersion: comparison.localVersion,
|
|
1717
|
+
remoteVersion: comparison.remoteVersion,
|
|
1718
|
+
message: [`Project ID: ${projectId}`],
|
|
1719
|
+
suggestions: []
|
|
1720
|
+
};
|
|
1721
|
+
if (comparison.remoteVersion > 0) {
|
|
1722
|
+
baseResult.message.push(`Remote schema version: ${comparison.remoteVersion}`);
|
|
1723
|
+
}
|
|
1724
|
+
switch (comparison.status) {
|
|
1725
|
+
case "behind":
|
|
1726
|
+
return {
|
|
1727
|
+
...baseResult,
|
|
1728
|
+
status: "behind",
|
|
1729
|
+
message: [
|
|
1730
|
+
...baseResult.message,
|
|
1731
|
+
`Schema is out of date! Current: ${comparison.localVersion}, Latest: ${comparison.remoteVersion}`
|
|
1732
|
+
],
|
|
1733
|
+
suggestions: [
|
|
1734
|
+
"Run 'basic pull' to update your local schema",
|
|
1735
|
+
"Review the changes before pulling if you have local modifications",
|
|
1736
|
+
"Consider backing up your current schema if you have unsaved work"
|
|
1737
|
+
]
|
|
1738
|
+
};
|
|
1739
|
+
case "ahead":
|
|
1740
|
+
try {
|
|
1741
|
+
const validation = await apiClient.validateSchema(localSchema);
|
|
1742
|
+
if (validation.valid === false && validation.errors) {
|
|
1743
|
+
return {
|
|
1744
|
+
...baseResult,
|
|
1745
|
+
status: "invalid",
|
|
1746
|
+
message: [
|
|
1747
|
+
...baseResult.message,
|
|
1748
|
+
`Changes found: Local schema version ${comparison.localVersion} is ahead of remote version ${comparison.remoteVersion}`,
|
|
1749
|
+
"Errors found in schema! Please fix:"
|
|
1750
|
+
],
|
|
1751
|
+
validationErrors: validation.errors,
|
|
1752
|
+
suggestions: [
|
|
1753
|
+
"Fix the validation errors shown below",
|
|
1754
|
+
"Run 'basic status' again after fixing errors",
|
|
1755
|
+
"Review your schema syntax and field definitions"
|
|
1756
|
+
]
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
return {
|
|
1760
|
+
...baseResult,
|
|
1761
|
+
status: "ahead",
|
|
1762
|
+
message: [
|
|
1763
|
+
...baseResult.message,
|
|
1764
|
+
`Changes found: Local schema version ${comparison.localVersion} is ahead of remote version ${comparison.remoteVersion}`,
|
|
1765
|
+
"Schema changes are valid!"
|
|
1766
|
+
],
|
|
1767
|
+
suggestions: [
|
|
1768
|
+
"Run 'basic push' to publish your changes",
|
|
1769
|
+
"Review your changes before publishing",
|
|
1770
|
+
"Test your schema locally if possible"
|
|
1771
|
+
]
|
|
1772
|
+
};
|
|
1773
|
+
} catch (error) {
|
|
1774
|
+
return {
|
|
1775
|
+
...baseResult,
|
|
1776
|
+
status: "invalid",
|
|
1777
|
+
message: [
|
|
1778
|
+
...baseResult.message,
|
|
1779
|
+
`Error validating schema: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1780
|
+
],
|
|
1781
|
+
suggestions: [
|
|
1782
|
+
"Check your schema for syntax errors",
|
|
1783
|
+
"Ensure all required fields are present",
|
|
1784
|
+
"Verify your basic.config file is valid JSON/JavaScript"
|
|
1785
|
+
]
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
case "equal":
|
|
1789
|
+
if (comparison.localVersion === 0 && comparison.remoteVersion === 0) {
|
|
1790
|
+
try {
|
|
1791
|
+
const validation = await apiClient.validateSchema(localSchema);
|
|
1792
|
+
if (validation.valid === false && validation.errors) {
|
|
1793
|
+
return {
|
|
1794
|
+
...baseResult,
|
|
1795
|
+
status: "invalid",
|
|
1796
|
+
message: [
|
|
1797
|
+
...baseResult.message,
|
|
1798
|
+
"Errors found in schema! Please fix:"
|
|
1799
|
+
],
|
|
1800
|
+
validationErrors: validation.errors,
|
|
1801
|
+
suggestions: [
|
|
1802
|
+
"Fix the validation errors shown below",
|
|
1803
|
+
"Run 'basic status' again after fixing errors"
|
|
1804
|
+
]
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
return {
|
|
1808
|
+
...baseResult,
|
|
1809
|
+
status: "ahead",
|
|
1810
|
+
message: [
|
|
1811
|
+
...baseResult.message,
|
|
1812
|
+
"",
|
|
1813
|
+
"Schema changes are valid!",
|
|
1814
|
+
"Please increment your version number to 1",
|
|
1815
|
+
"and run 'basic push' if you are ready to publish your changes."
|
|
1816
|
+
],
|
|
1817
|
+
suggestions: [
|
|
1818
|
+
"Update the version field in your schema from 0 to 1",
|
|
1819
|
+
"Run 'basic push' after incrementing the version",
|
|
1820
|
+
"Ensure your schema changes are tested and ready for production"
|
|
1821
|
+
]
|
|
1822
|
+
};
|
|
1823
|
+
} catch (error) {
|
|
1824
|
+
return {
|
|
1825
|
+
...baseResult,
|
|
1826
|
+
status: "invalid",
|
|
1827
|
+
message: [
|
|
1828
|
+
...baseResult.message,
|
|
1829
|
+
`Error validating schema: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1830
|
+
],
|
|
1831
|
+
suggestions: [
|
|
1832
|
+
"Check your schema for syntax errors"
|
|
1833
|
+
]
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
} else {
|
|
1837
|
+
try {
|
|
1838
|
+
const comparison2 = await apiClient.compareSchema(localSchema);
|
|
1839
|
+
if (comparison2.valid) {
|
|
1840
|
+
return {
|
|
1841
|
+
...baseResult,
|
|
1842
|
+
status: "current",
|
|
1843
|
+
message: [
|
|
1844
|
+
...baseResult.message,
|
|
1845
|
+
"Schema is up to date!"
|
|
1846
|
+
],
|
|
1847
|
+
suggestions: [
|
|
1848
|
+
"Continue working on your project",
|
|
1849
|
+
"Make schema modifications if needed",
|
|
1850
|
+
"Run 'basic status' again after making changes"
|
|
1851
|
+
]
|
|
1852
|
+
};
|
|
1853
|
+
} else {
|
|
1854
|
+
return {
|
|
1855
|
+
...baseResult,
|
|
1856
|
+
status: "conflict",
|
|
1857
|
+
message: [
|
|
1858
|
+
...baseResult.message,
|
|
1859
|
+
"",
|
|
1860
|
+
"Schema conflicts found! Your local schema is different from the remote schema."
|
|
1861
|
+
],
|
|
1862
|
+
suggestions: [
|
|
1863
|
+
"Run 'basic pull' to override local changes with remote schema",
|
|
1864
|
+
"Or increment the version number in your local schema",
|
|
1865
|
+
"Compare your local changes with the remote version before deciding",
|
|
1866
|
+
"Consider creating a backup of your local changes"
|
|
1867
|
+
]
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
} catch (error) {
|
|
1871
|
+
return {
|
|
1872
|
+
...baseResult,
|
|
1873
|
+
status: "invalid",
|
|
1874
|
+
message: [
|
|
1875
|
+
...baseResult.message,
|
|
1876
|
+
`Error checking schema conflict: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1877
|
+
],
|
|
1878
|
+
suggestions: [
|
|
1879
|
+
"Check your network connection",
|
|
1880
|
+
"Ensure the project ID is correct"
|
|
1881
|
+
]
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
default:
|
|
1886
|
+
return {
|
|
1887
|
+
...baseResult,
|
|
1888
|
+
status: "invalid",
|
|
1889
|
+
message: [
|
|
1890
|
+
...baseResult.message,
|
|
1891
|
+
"Unknown schema status"
|
|
1892
|
+
],
|
|
1893
|
+
suggestions: [
|
|
1894
|
+
"Try running 'basic status' again"
|
|
1895
|
+
]
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
function StatusDisplay({ result }) {
|
|
1900
|
+
const getStatusColor = () => {
|
|
1901
|
+
switch (result.status) {
|
|
1902
|
+
case "current":
|
|
1903
|
+
return "green";
|
|
1904
|
+
case "ahead":
|
|
1905
|
+
return "blue";
|
|
1906
|
+
case "behind":
|
|
1907
|
+
return "yellow";
|
|
1908
|
+
case "conflict":
|
|
1909
|
+
return "magenta";
|
|
1910
|
+
case "invalid":
|
|
1911
|
+
return "red";
|
|
1912
|
+
case "no-schema":
|
|
1913
|
+
return "gray";
|
|
1914
|
+
default:
|
|
1915
|
+
return "white";
|
|
132
1916
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
1917
|
+
};
|
|
1918
|
+
const getStatusIcon = () => {
|
|
1919
|
+
switch (result.status) {
|
|
1920
|
+
case "current":
|
|
1921
|
+
return "\u2705";
|
|
1922
|
+
case "ahead":
|
|
1923
|
+
return "\u{1F680}";
|
|
1924
|
+
case "behind":
|
|
1925
|
+
return "\u2B07\uFE0F";
|
|
1926
|
+
case "conflict":
|
|
1927
|
+
return "\u26A0\uFE0F";
|
|
1928
|
+
case "invalid":
|
|
1929
|
+
return "\u274C";
|
|
1930
|
+
case "no-schema":
|
|
1931
|
+
return "\u{1F4C4}";
|
|
1932
|
+
default:
|
|
1933
|
+
return "\u2753";
|
|
137
1934
|
}
|
|
1935
|
+
};
|
|
1936
|
+
const getStatusDescription = () => {
|
|
1937
|
+
switch (result.status) {
|
|
1938
|
+
case "current":
|
|
1939
|
+
return "Schema is up to date";
|
|
1940
|
+
case "ahead":
|
|
1941
|
+
return "Schema is ready to push";
|
|
1942
|
+
case "behind":
|
|
1943
|
+
return "Schema is out of date";
|
|
1944
|
+
case "conflict":
|
|
1945
|
+
return "Schema conflicts detected";
|
|
1946
|
+
case "invalid":
|
|
1947
|
+
return "Schema has validation errors";
|
|
1948
|
+
case "no-schema":
|
|
1949
|
+
return "No schema file found";
|
|
1950
|
+
default:
|
|
1951
|
+
return "Unknown status";
|
|
1952
|
+
}
|
|
1953
|
+
};
|
|
1954
|
+
const statusMessages = result.message.filter((msg) => !msg.startsWith("Project ID:") && !msg.startsWith("Remote schema version:"));
|
|
1955
|
+
return _jsxs5(Box4, { flexDirection: "column", children: [result.projectId && _jsxs5(Box4, { flexDirection: "column", marginBottom: 1, children: [_jsxs5(Text6, { color: "cyan", children: ["Project ID: ", result.projectId] }), _jsxs5(Box4, { children: [_jsxs5(Text6, { color: "gray", children: ["Local version: ", result.localVersion] }), result.remoteVersion > 0 && _jsxs5(Text6, { color: "gray", children: [" \u2022 Remote version: ", result.remoteVersion] })] })] }), _jsxs5(Box4, { flexDirection: "column", marginBottom: 1, children: [_jsx6(Box4, { marginBottom: 1, children: _jsxs5(Text6, { color: getStatusColor(), children: [getStatusIcon(), " ", getStatusDescription()] }) }), statusMessages.length > 0 && _jsx6(Box4, { flexDirection: "column", children: statusMessages.map((line, index) => _jsx6(Text6, { children: line }, index)) })] }), result.validationErrors && result.validationErrors.length > 0 && _jsxs5(Box4, { flexDirection: "column", marginBottom: 1, children: [_jsx6(Text6, { color: "red", children: "Validation errors:" }), result.validationErrors.map((error, index) => _jsxs5(Text6, { color: "red", children: ["\u2022 ", error.message, " at ", error.instancePath || "root"] }, index))] }), result.suggestions.length > 0 && _jsxs5(Box4, { flexDirection: "column", marginTop: 1, children: [_jsx6(Text6, { color: "blue", children: "Next steps:" }), result.suggestions.map((suggestion, index) => _jsxs5(Text6, { color: "gray", children: ["\u2022 ", suggestion] }, index))] })] });
|
|
1956
|
+
}
|
|
1957
|
+
async function StatusCommand() {
|
|
1958
|
+
const { waitUntilExit } = render3(_jsx6(StatusApp, {}));
|
|
1959
|
+
await waitUntilExit();
|
|
1960
|
+
process.exit(0);
|
|
1961
|
+
}
|
|
1962
|
+
var init_status = __esm({
|
|
1963
|
+
"dist/commands/status.js"() {
|
|
1964
|
+
"use strict";
|
|
1965
|
+
init_Spinner();
|
|
1966
|
+
init_api();
|
|
1967
|
+
init_auth();
|
|
1968
|
+
init_schema();
|
|
1969
|
+
init_platform();
|
|
1970
|
+
init_constants();
|
|
1971
|
+
}
|
|
1972
|
+
});
|
|
1973
|
+
|
|
1974
|
+
// dist/commands/pull.js
|
|
1975
|
+
var pull_exports = {};
|
|
1976
|
+
__export(pull_exports, {
|
|
1977
|
+
PullCommand: () => PullCommand
|
|
138
1978
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
1979
|
+
import { jsx as _jsx7, jsxs as _jsxs6, Fragment as _Fragment } from "react/jsx-runtime";
|
|
1980
|
+
import React5 from "react";
|
|
1981
|
+
import { render as render4, Box as Box5, Text as Text7, useInput as useInput3 } from "ink";
|
|
1982
|
+
function PullApp() {
|
|
1983
|
+
const [state, setState] = React5.useState({
|
|
1984
|
+
phase: "checking",
|
|
1985
|
+
error: null
|
|
1986
|
+
});
|
|
1987
|
+
const [selectedOption, setSelectedOption] = React5.useState("yes");
|
|
1988
|
+
React5.useEffect(() => {
|
|
1989
|
+
async function checkPullStatus() {
|
|
1990
|
+
try {
|
|
1991
|
+
if (!await isOnline()) {
|
|
1992
|
+
setState({
|
|
1993
|
+
phase: "error",
|
|
1994
|
+
error: MESSAGES.OFFLINE
|
|
1995
|
+
});
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
const authService = AuthService.getInstance();
|
|
1999
|
+
const token = await authService.getToken();
|
|
2000
|
+
if (!token) {
|
|
2001
|
+
setState({
|
|
2002
|
+
phase: "error",
|
|
2003
|
+
error: MESSAGES.LOGGED_OUT
|
|
2004
|
+
});
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
const localConfig = await readSchemaFromConfig();
|
|
2008
|
+
if (!localConfig) {
|
|
2009
|
+
setState({
|
|
2010
|
+
phase: "no-action",
|
|
2011
|
+
error: null,
|
|
2012
|
+
statusResult: {
|
|
2013
|
+
status: "no-schema",
|
|
2014
|
+
projectId: "",
|
|
2015
|
+
localVersion: 0,
|
|
2016
|
+
remoteVersion: 0,
|
|
2017
|
+
message: [
|
|
2018
|
+
"No schema found in config files",
|
|
2019
|
+
"Run 'basic init' to create a new project or import an existing project"
|
|
2020
|
+
],
|
|
2021
|
+
needsConfirmation: false,
|
|
2022
|
+
confirmationTitle: "",
|
|
2023
|
+
confirmationMessage: ""
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
const apiClient = ApiClient.getInstance();
|
|
2029
|
+
let remoteSchema = null;
|
|
2030
|
+
try {
|
|
2031
|
+
remoteSchema = await apiClient.getProjectSchema(localConfig.projectId);
|
|
2032
|
+
} catch (error) {
|
|
2033
|
+
setState({
|
|
2034
|
+
phase: "error",
|
|
2035
|
+
error: `Error fetching remote schema: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2036
|
+
});
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
if (!remoteSchema) {
|
|
2040
|
+
remoteSchema = {
|
|
2041
|
+
project_id: localConfig.projectId,
|
|
2042
|
+
version: 0,
|
|
2043
|
+
tables: {}
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
const comparison = compareVersions(localConfig.schema, remoteSchema);
|
|
2047
|
+
const result = await analyzePullAction(localConfig, remoteSchema, comparison, apiClient);
|
|
2048
|
+
setState({
|
|
2049
|
+
phase: result.needsConfirmation ? "confirming" : "no-action",
|
|
2050
|
+
error: null,
|
|
2051
|
+
statusResult: result
|
|
2052
|
+
});
|
|
2053
|
+
} catch (error) {
|
|
2054
|
+
setState({
|
|
2055
|
+
phase: "error",
|
|
2056
|
+
error: error instanceof Error ? error.message : "Failed to check pull status"
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
checkPullStatus();
|
|
2061
|
+
}, []);
|
|
2062
|
+
useInput3((input, key) => {
|
|
2063
|
+
if (state.phase === "confirming") {
|
|
2064
|
+
if (key.upArrow || key.downArrow) {
|
|
2065
|
+
setSelectedOption((prev) => prev === "yes" ? "no" : "yes");
|
|
2066
|
+
} else if (key.return) {
|
|
2067
|
+
if (selectedOption === "yes") {
|
|
2068
|
+
handlePull();
|
|
2069
|
+
} else {
|
|
2070
|
+
setState((prev) => ({ ...prev, phase: "no-action" }));
|
|
2071
|
+
}
|
|
2072
|
+
} else if (key.escape || input === "q") {
|
|
2073
|
+
setState((prev) => ({ ...prev, phase: "no-action" }));
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
});
|
|
2077
|
+
const handlePull = async () => {
|
|
2078
|
+
setState((prev) => ({ ...prev, phase: "pulling" }));
|
|
145
2079
|
try {
|
|
146
|
-
|
|
147
|
-
|
|
2080
|
+
if (!state.statusResult) {
|
|
2081
|
+
throw new Error("No status result available");
|
|
2082
|
+
}
|
|
2083
|
+
const localConfig = await readSchemaFromConfig();
|
|
2084
|
+
if (!localConfig) {
|
|
2085
|
+
throw new Error("Local schema not found");
|
|
2086
|
+
}
|
|
2087
|
+
const apiClient = ApiClient.getInstance();
|
|
2088
|
+
const remoteSchema = await apiClient.getProjectSchema(localConfig.projectId);
|
|
2089
|
+
if (!remoteSchema) {
|
|
2090
|
+
throw new Error("Remote schema not found");
|
|
2091
|
+
}
|
|
2092
|
+
const filePath = await saveSchemaToConfig(remoteSchema);
|
|
2093
|
+
setState({
|
|
2094
|
+
phase: "success",
|
|
2095
|
+
error: null,
|
|
2096
|
+
pullResult: {
|
|
2097
|
+
projectId: localConfig.projectId,
|
|
2098
|
+
oldVersion: localConfig.schema.version || 0,
|
|
2099
|
+
newVersion: remoteSchema.version || 0,
|
|
2100
|
+
filePath
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
2103
|
+
} catch (error) {
|
|
2104
|
+
setState({
|
|
2105
|
+
phase: "error",
|
|
2106
|
+
error: error instanceof Error ? error.message : "Failed to pull schema"
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
};
|
|
2110
|
+
if (state.phase === "checking") {
|
|
2111
|
+
return _jsx7(Spinner, { text: "Checking pull status..." });
|
|
2112
|
+
}
|
|
2113
|
+
if (state.phase === "pulling") {
|
|
2114
|
+
return _jsx7(Spinner, { text: "Pulling latest schema..." });
|
|
2115
|
+
}
|
|
2116
|
+
if (state.phase === "error") {
|
|
2117
|
+
setTimeout(() => process.exit(1), 0);
|
|
2118
|
+
return _jsxs6(Box5, { flexDirection: "column", children: [_jsxs6(Text7, { color: "red", children: ["Error: ", state.error] }), _jsxs6(Box5, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [_jsx7(Text7, { color: "blue", children: "Next steps:" }), state.error?.includes("offline") || state.error?.includes("network") ? _jsxs6(_Fragment, { children: [_jsx7(Text7, { color: "gray", children: "\u2022 Check your internet connection" }), _jsx7(Text7, { color: "gray", children: "\u2022 Try again in a moment" })] }) : state.error?.includes("logged") || state.error?.includes("auth") ? _jsxs6(_Fragment, { children: [_jsx7(Text7, { color: "gray", children: "\u2022 Run 'basic login' to authenticate" }), _jsx7(Text7, { color: "gray", children: "\u2022 Ensure you have a valid account" })] }) : state.error?.includes("schema") || state.error?.includes("project") ? _jsxs6(_Fragment, { children: [_jsx7(Text7, { color: "gray", children: "\u2022 Check if the project ID is correct" }), _jsx7(Text7, { color: "gray", children: "\u2022 Ensure you have access to this project" }), _jsx7(Text7, { color: "gray", children: "\u2022 Run 'basic status' for more details" })] }) : _jsxs6(_Fragment, { children: [_jsx7(Text7, { color: "gray", children: "\u2022 Try running the command again" }), _jsx7(Text7, { color: "gray", children: "\u2022 Run 'basic status' to check your project state" }), _jsx7(Text7, { color: "gray", children: "\u2022 Check the Basic documentation if the issue persists" })] })] })] });
|
|
2119
|
+
}
|
|
2120
|
+
if (state.phase === "success" && state.pullResult) {
|
|
2121
|
+
setTimeout(() => process.exit(0), 0);
|
|
2122
|
+
return _jsx7(PullSuccessDisplay, { result: state.pullResult });
|
|
2123
|
+
}
|
|
2124
|
+
if (state.phase === "confirming" && state.statusResult) {
|
|
2125
|
+
return _jsx7(PullConfirmationDialog, { statusResult: state.statusResult, selectedOption });
|
|
2126
|
+
}
|
|
2127
|
+
if (state.phase === "no-action" && state.statusResult) {
|
|
2128
|
+
setTimeout(() => process.exit(0), 0);
|
|
2129
|
+
return _jsx7(PullStatusDisplay, { result: state.statusResult });
|
|
2130
|
+
}
|
|
2131
|
+
return _jsx7(Text7, { children: "Unknown state" });
|
|
2132
|
+
}
|
|
2133
|
+
async function analyzePullAction(localConfig, remoteSchema, comparison, apiClient) {
|
|
2134
|
+
const { projectId } = localConfig;
|
|
2135
|
+
const baseResult = {
|
|
2136
|
+
projectId,
|
|
2137
|
+
localVersion: comparison.localVersion,
|
|
2138
|
+
remoteVersion: comparison.remoteVersion,
|
|
2139
|
+
message: [],
|
|
2140
|
+
needsConfirmation: false,
|
|
2141
|
+
confirmationTitle: "",
|
|
2142
|
+
confirmationMessage: ""
|
|
2143
|
+
};
|
|
2144
|
+
switch (comparison.status) {
|
|
2145
|
+
case "behind":
|
|
2146
|
+
return {
|
|
2147
|
+
...baseResult,
|
|
2148
|
+
status: "behind",
|
|
2149
|
+
message: [
|
|
2150
|
+
"Your local schema is behind the remote version.",
|
|
2151
|
+
"Pull the latest changes?"
|
|
2152
|
+
],
|
|
2153
|
+
needsConfirmation: true,
|
|
2154
|
+
confirmationTitle: "Pull Remote Schema",
|
|
2155
|
+
confirmationMessage: "This will update your local schema to the latest version."
|
|
2156
|
+
};
|
|
2157
|
+
case "equal":
|
|
2158
|
+
if (comparison.localVersion === 0 && comparison.remoteVersion === 0) {
|
|
2159
|
+
return {
|
|
2160
|
+
...baseResult,
|
|
2161
|
+
status: "current",
|
|
2162
|
+
message: [
|
|
2163
|
+
"Schema is up to date!",
|
|
2164
|
+
"No pull needed."
|
|
2165
|
+
]
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
try {
|
|
2169
|
+
const comparisonResult = await apiClient.compareSchema(localConfig.schema);
|
|
2170
|
+
if (comparisonResult.valid) {
|
|
2171
|
+
return {
|
|
2172
|
+
...baseResult,
|
|
2173
|
+
status: "current",
|
|
2174
|
+
message: [
|
|
2175
|
+
"Schema is up to date!",
|
|
2176
|
+
"No pull needed."
|
|
2177
|
+
]
|
|
2178
|
+
};
|
|
2179
|
+
} else {
|
|
2180
|
+
return {
|
|
2181
|
+
...baseResult,
|
|
2182
|
+
status: "conflict",
|
|
2183
|
+
message: [
|
|
2184
|
+
"Schema conflicts detected!",
|
|
2185
|
+
"Your local schema differs from the remote schema at the same version.",
|
|
2186
|
+
"Pull the remote version to override local changes?"
|
|
2187
|
+
],
|
|
2188
|
+
needsConfirmation: true,
|
|
2189
|
+
confirmationTitle: "Override Local Changes",
|
|
2190
|
+
confirmationMessage: "This will replace your local schema with the remote version."
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
} catch (error) {
|
|
2194
|
+
return {
|
|
2195
|
+
...baseResult,
|
|
2196
|
+
status: "current",
|
|
2197
|
+
message: [
|
|
2198
|
+
"Schema is up to date!",
|
|
2199
|
+
"No pull needed.",
|
|
2200
|
+
"(Unable to verify schema content - assuming current)"
|
|
2201
|
+
]
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
case "ahead":
|
|
2205
|
+
return {
|
|
2206
|
+
...baseResult,
|
|
2207
|
+
status: "ahead",
|
|
2208
|
+
message: [
|
|
2209
|
+
"Your local schema is ahead of the remote version.",
|
|
2210
|
+
"Did you mean to push instead?",
|
|
2211
|
+
"Use 'basic push' to publish your changes."
|
|
2212
|
+
]
|
|
2213
|
+
};
|
|
2214
|
+
default:
|
|
2215
|
+
return {
|
|
2216
|
+
...baseResult,
|
|
2217
|
+
status: "current",
|
|
2218
|
+
message: [
|
|
2219
|
+
"Schema is up to date!",
|
|
2220
|
+
"No pull needed."
|
|
2221
|
+
]
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
function PullConfirmationDialog({ statusResult, selectedOption }) {
|
|
2226
|
+
const getStatusIcon = () => {
|
|
2227
|
+
switch (statusResult.status) {
|
|
2228
|
+
case "behind":
|
|
2229
|
+
return "\u2B07\uFE0F";
|
|
2230
|
+
case "conflict":
|
|
2231
|
+
return "\u26A0\uFE0F";
|
|
2232
|
+
default:
|
|
2233
|
+
return "\u{1F4E5}";
|
|
148
2234
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
2235
|
+
};
|
|
2236
|
+
const getStatusText = () => {
|
|
2237
|
+
switch (statusResult.status) {
|
|
2238
|
+
case "behind":
|
|
2239
|
+
return "Schema is out of date";
|
|
2240
|
+
case "conflict":
|
|
2241
|
+
return "Schema conflicts detected";
|
|
2242
|
+
default:
|
|
2243
|
+
return "Schema update available";
|
|
2244
|
+
}
|
|
2245
|
+
};
|
|
2246
|
+
const getStatusColor = () => {
|
|
2247
|
+
switch (statusResult.status) {
|
|
2248
|
+
case "behind":
|
|
2249
|
+
return "yellow";
|
|
2250
|
+
case "conflict":
|
|
2251
|
+
return "magenta";
|
|
2252
|
+
default:
|
|
2253
|
+
return "blue";
|
|
2254
|
+
}
|
|
2255
|
+
};
|
|
2256
|
+
return _jsxs6(Box5, { flexDirection: "column", children: [_jsxs6(Box5, { flexDirection: "column", marginBottom: 1, children: [_jsxs6(Text7, { color: "cyan", children: ["Project ID: ", statusResult.projectId] }), _jsxs6(Box5, { children: [_jsxs6(Text7, { color: "gray", children: ["Local version: ", statusResult.localVersion] }), statusResult.remoteVersion > 0 && _jsxs6(Text7, { color: "gray", children: [" \u2022 Remote version: ", statusResult.remoteVersion] })] })] }), _jsx7(Box5, { marginBottom: 1, children: _jsxs6(Text7, { color: getStatusColor(), children: [getStatusIcon(), " ", getStatusText()] }) }), _jsx7(Box5, { flexDirection: "column", marginBottom: 2, children: statusResult.message.map((line, index) => _jsx7(Text7, { children: line }, index)) }), _jsxs6(Box5, { flexDirection: "column", children: [_jsx7(Box5, { children: _jsxs6(Text7, { color: selectedOption === "yes" ? "green" : "gray", children: [selectedOption === "yes" ? "\u276F" : " ", " Yes, pull changes"] }) }), _jsx7(Box5, { children: _jsxs6(Text7, { color: selectedOption === "no" ? "green" : "gray", children: [selectedOption === "no" ? "\u276F" : " ", " No, cancel"] }) })] }), _jsx7(Box5, { marginTop: 1, children: _jsx7(Text7, { color: "gray", children: "Use \u2191\u2193 to navigate, Enter to confirm, Esc to cancel" }) })] });
|
|
2257
|
+
}
|
|
2258
|
+
function PullStatusDisplay({ result }) {
|
|
2259
|
+
const getStatusColor = () => {
|
|
2260
|
+
switch (result.status) {
|
|
2261
|
+
case "current":
|
|
2262
|
+
return "green";
|
|
2263
|
+
case "ahead":
|
|
2264
|
+
return "blue";
|
|
2265
|
+
case "conflict":
|
|
2266
|
+
return "magenta";
|
|
2267
|
+
case "no-schema":
|
|
2268
|
+
return "gray";
|
|
2269
|
+
default:
|
|
2270
|
+
return "white";
|
|
153
2271
|
}
|
|
2272
|
+
};
|
|
2273
|
+
const getStatusIcon = () => {
|
|
2274
|
+
switch (result.status) {
|
|
2275
|
+
case "current":
|
|
2276
|
+
return "\u2705";
|
|
2277
|
+
case "ahead":
|
|
2278
|
+
return "\u{1F680}";
|
|
2279
|
+
case "conflict":
|
|
2280
|
+
return "\u26A0\uFE0F";
|
|
2281
|
+
case "no-schema":
|
|
2282
|
+
return "\u{1F4C4}";
|
|
2283
|
+
default:
|
|
2284
|
+
return "\u2753";
|
|
2285
|
+
}
|
|
2286
|
+
};
|
|
2287
|
+
const getStatusDescription = () => {
|
|
2288
|
+
switch (result.status) {
|
|
2289
|
+
case "current":
|
|
2290
|
+
return "Schema is up to date";
|
|
2291
|
+
case "ahead":
|
|
2292
|
+
return "Local schema is ahead";
|
|
2293
|
+
case "conflict":
|
|
2294
|
+
return "Schema conflicts detected";
|
|
2295
|
+
case "no-schema":
|
|
2296
|
+
return "No schema file found";
|
|
2297
|
+
default:
|
|
2298
|
+
return "Unknown status";
|
|
2299
|
+
}
|
|
2300
|
+
};
|
|
2301
|
+
const getNextSteps = () => {
|
|
2302
|
+
switch (result.status) {
|
|
2303
|
+
case "current":
|
|
2304
|
+
return [
|
|
2305
|
+
"Continue working on your project",
|
|
2306
|
+
"Run 'basic status' to check for changes",
|
|
2307
|
+
"Make schema modifications if needed"
|
|
2308
|
+
];
|
|
2309
|
+
case "ahead":
|
|
2310
|
+
return [
|
|
2311
|
+
"Run 'basic push' to publish your changes",
|
|
2312
|
+
"Or run 'basic status' for more details"
|
|
2313
|
+
];
|
|
2314
|
+
case "conflict":
|
|
2315
|
+
return [
|
|
2316
|
+
"Run 'basic pull' again to override local changes",
|
|
2317
|
+
"Or run 'basic status' to understand the differences",
|
|
2318
|
+
"Consider backing up your local changes first"
|
|
2319
|
+
];
|
|
2320
|
+
case "no-schema":
|
|
2321
|
+
return [
|
|
2322
|
+
"Run 'basic init' to create a new project or import an existing project",
|
|
2323
|
+
"Make sure you're in a directory with a basic.config.ts/js file"
|
|
2324
|
+
];
|
|
2325
|
+
default:
|
|
2326
|
+
return [];
|
|
2327
|
+
}
|
|
2328
|
+
};
|
|
2329
|
+
return _jsxs6(Box5, { flexDirection: "column", children: [result.projectId && _jsxs6(Box5, { flexDirection: "column", marginBottom: 1, children: [_jsxs6(Text7, { color: "cyan", children: ["Project ID: ", result.projectId] }), _jsxs6(Box5, { children: [_jsxs6(Text7, { color: "gray", children: ["Local version: ", result.localVersion] }), result.remoteVersion > 0 && _jsxs6(Text7, { color: "gray", children: [" \u2022 Remote version: ", result.remoteVersion] })] })] }), _jsx7(Box5, { marginBottom: 1, children: _jsxs6(Text7, { color: getStatusColor(), children: [getStatusIcon(), " ", getStatusDescription()] }) }), _jsx7(Box5, { flexDirection: "column", marginBottom: 1, children: result.message.map((line, index) => _jsx7(Text7, { children: line }, index)) }), getNextSteps().length > 0 && _jsxs6(Box5, { flexDirection: "column", marginBottom: 1, children: [_jsx7(Text7, { color: "blue", children: "Next steps:" }), getNextSteps().map((step, index) => _jsxs6(Text7, { color: "gray", children: ["\u2022 ", step] }, index))] })] });
|
|
2330
|
+
}
|
|
2331
|
+
function PullSuccessDisplay({ result }) {
|
|
2332
|
+
return _jsxs6(Box5, { flexDirection: "column", children: [_jsx7(Box5, { marginBottom: 1, children: _jsx7(Text7, { color: "green", children: "\u2705 Schema updated successfully!" }) }), _jsxs6(Box5, { flexDirection: "column", marginBottom: 1, children: [_jsxs6(Text7, { children: ["Updated: ", result.filePath.split("/").pop()] }), _jsxs6(Text7, { children: ["Version: ", result.oldVersion, " \u2192 ", result.newVersion] }), _jsxs6(Text7, { children: ["Project: ", result.projectId] })] }), _jsxs6(Box5, { flexDirection: "column", marginTop: 1, children: [_jsx7(Text7, { color: "blue", children: "Next steps:" }), _jsx7(Text7, { color: "gray", children: "\u2022 Review the updated schema changes" }), _jsx7(Text7, { color: "gray", children: "\u2022 Continue working on your project" }), _jsx7(Text7, { color: "gray", children: "\u2022 Run 'basic status' to check your project state" })] })] });
|
|
2333
|
+
}
|
|
2334
|
+
async function PullCommand() {
|
|
2335
|
+
render4(_jsx7(PullApp, {}));
|
|
2336
|
+
}
|
|
2337
|
+
var init_pull = __esm({
|
|
2338
|
+
"dist/commands/pull.js"() {
|
|
2339
|
+
"use strict";
|
|
2340
|
+
init_Spinner();
|
|
2341
|
+
init_api();
|
|
2342
|
+
init_auth();
|
|
2343
|
+
init_schema();
|
|
2344
|
+
init_platform();
|
|
2345
|
+
init_constants();
|
|
2346
|
+
}
|
|
154
2347
|
});
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
2348
|
+
|
|
2349
|
+
// dist/commands/push.js
|
|
2350
|
+
var push_exports = {};
|
|
2351
|
+
__export(push_exports, {
|
|
2352
|
+
PushCommand: () => PushCommand
|
|
2353
|
+
});
|
|
2354
|
+
import { jsx as _jsx8, jsxs as _jsxs7, Fragment as _Fragment2 } from "react/jsx-runtime";
|
|
2355
|
+
import React6 from "react";
|
|
2356
|
+
import { render as render5, Box as Box6, Text as Text8, useInput as useInput4 } from "ink";
|
|
2357
|
+
function PushApp() {
|
|
2358
|
+
const [state, setState] = React6.useState({
|
|
2359
|
+
phase: "checking",
|
|
2360
|
+
error: null
|
|
2361
|
+
});
|
|
2362
|
+
const [selectedOption, setSelectedOption] = React6.useState("yes");
|
|
2363
|
+
React6.useEffect(() => {
|
|
2364
|
+
async function checkPushStatus() {
|
|
2365
|
+
try {
|
|
2366
|
+
if (!await isOnline()) {
|
|
2367
|
+
setState({
|
|
2368
|
+
phase: "error",
|
|
2369
|
+
error: MESSAGES.OFFLINE
|
|
2370
|
+
});
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
const authService = AuthService.getInstance();
|
|
2374
|
+
const token = await authService.getToken();
|
|
2375
|
+
if (!token) {
|
|
2376
|
+
setState({
|
|
2377
|
+
phase: "error",
|
|
2378
|
+
error: MESSAGES.LOGGED_OUT
|
|
2379
|
+
});
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
const localConfig = await readSchemaFromConfig();
|
|
2383
|
+
if (!localConfig) {
|
|
2384
|
+
setState({
|
|
2385
|
+
phase: "no-action",
|
|
2386
|
+
error: null,
|
|
2387
|
+
statusResult: {
|
|
2388
|
+
status: "no-schema",
|
|
2389
|
+
projectId: "",
|
|
2390
|
+
localVersion: 0,
|
|
2391
|
+
remoteVersion: 0,
|
|
2392
|
+
message: [
|
|
2393
|
+
"No schema found in config files",
|
|
2394
|
+
"Run 'basic init' to create a new project or import an existing project"
|
|
2395
|
+
],
|
|
2396
|
+
needsConfirmation: false,
|
|
2397
|
+
confirmationTitle: "",
|
|
2398
|
+
confirmationMessage: ""
|
|
2399
|
+
}
|
|
2400
|
+
});
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
const apiClient = ApiClient.getInstance();
|
|
2404
|
+
let remoteSchema = null;
|
|
2405
|
+
try {
|
|
2406
|
+
remoteSchema = await apiClient.getProjectSchema(localConfig.projectId);
|
|
2407
|
+
} catch (error) {
|
|
2408
|
+
setState({
|
|
2409
|
+
phase: "error",
|
|
2410
|
+
error: `Error fetching remote schema: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2411
|
+
});
|
|
2412
|
+
return;
|
|
2413
|
+
}
|
|
2414
|
+
if (!remoteSchema) {
|
|
2415
|
+
remoteSchema = {
|
|
2416
|
+
project_id: localConfig.projectId,
|
|
2417
|
+
version: 0,
|
|
2418
|
+
tables: {}
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2421
|
+
const comparison = compareVersions(localConfig.schema, remoteSchema);
|
|
2422
|
+
const result = await analyzePushAction(localConfig, remoteSchema, comparison, apiClient);
|
|
2423
|
+
setState({
|
|
2424
|
+
phase: result.needsConfirmation ? "confirming" : "no-action",
|
|
2425
|
+
error: null,
|
|
2426
|
+
statusResult: result
|
|
2427
|
+
});
|
|
2428
|
+
} catch (error) {
|
|
2429
|
+
setState({
|
|
2430
|
+
phase: "error",
|
|
2431
|
+
error: error instanceof Error ? error.message : "Failed to check push status"
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
checkPushStatus();
|
|
2436
|
+
}, []);
|
|
2437
|
+
useInput4((input, key) => {
|
|
2438
|
+
if (state.phase === "confirming") {
|
|
2439
|
+
if (key.upArrow || key.downArrow) {
|
|
2440
|
+
setSelectedOption((prev) => prev === "yes" ? "no" : "yes");
|
|
2441
|
+
} else if (key.return) {
|
|
2442
|
+
if (selectedOption === "yes") {
|
|
2443
|
+
handlePush();
|
|
2444
|
+
} else {
|
|
2445
|
+
setState((prev) => ({ ...prev, phase: "no-action" }));
|
|
2446
|
+
}
|
|
2447
|
+
} else if (key.escape || input === "q") {
|
|
2448
|
+
setState((prev) => ({ ...prev, phase: "no-action" }));
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
});
|
|
2452
|
+
const handlePush = async () => {
|
|
2453
|
+
setState((prev) => ({ ...prev, phase: "pushing" }));
|
|
160
2454
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
2455
|
+
if (!state.statusResult) {
|
|
2456
|
+
throw new Error("No status result available");
|
|
2457
|
+
}
|
|
2458
|
+
const localConfig = await readSchemaFromConfig();
|
|
2459
|
+
if (!localConfig) {
|
|
2460
|
+
throw new Error("Local schema not found");
|
|
2461
|
+
}
|
|
2462
|
+
const apiClient = ApiClient.getInstance();
|
|
2463
|
+
await apiClient.pushProjectSchema(localConfig.projectId, localConfig.schema);
|
|
2464
|
+
setState({
|
|
2465
|
+
phase: "success",
|
|
2466
|
+
error: null,
|
|
2467
|
+
pushResult: {
|
|
2468
|
+
projectId: localConfig.projectId,
|
|
2469
|
+
oldVersion: state.statusResult.remoteVersion,
|
|
2470
|
+
newVersion: localConfig.schema.version || 0,
|
|
2471
|
+
filePath: localConfig.filePath
|
|
2472
|
+
}
|
|
2473
|
+
});
|
|
2474
|
+
} catch (error) {
|
|
2475
|
+
setState({
|
|
2476
|
+
phase: "error",
|
|
2477
|
+
error: error instanceof Error ? error.message : "Failed to push schema"
|
|
2478
|
+
});
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
if (state.phase === "checking") {
|
|
2482
|
+
return _jsx8(Spinner, { text: "Checking push status..." });
|
|
2483
|
+
}
|
|
2484
|
+
if (state.phase === "pushing") {
|
|
2485
|
+
return _jsx8(Spinner, { text: "Pushing schema to remote..." });
|
|
2486
|
+
}
|
|
2487
|
+
if (state.phase === "error") {
|
|
2488
|
+
setTimeout(() => process.exit(1), 0);
|
|
2489
|
+
return _jsxs7(Box6, { flexDirection: "column", children: [_jsxs7(Text8, { color: "red", children: ["Error: ", state.error] }), _jsxs7(Box6, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [_jsx8(Text8, { color: "blue", children: "Next steps:" }), state.error?.includes("offline") || state.error?.includes("network") ? _jsxs7(_Fragment2, { children: [_jsx8(Text8, { color: "gray", children: "\u2022 Check your internet connection" }), _jsx8(Text8, { color: "gray", children: "\u2022 Try again in a moment" })] }) : state.error?.includes("logged") || state.error?.includes("auth") ? _jsxs7(_Fragment2, { children: [_jsx8(Text8, { color: "gray", children: "\u2022 Run 'basic login' to authenticate" }), _jsx8(Text8, { color: "gray", children: "\u2022 Ensure you have a valid account" })] }) : state.error?.includes("schema") || state.error?.includes("project") ? _jsxs7(_Fragment2, { children: [_jsx8(Text8, { color: "gray", children: "\u2022 Check if the project ID is correct" }), _jsx8(Text8, { color: "gray", children: "\u2022 Ensure you have access to this project" }), _jsx8(Text8, { color: "gray", children: "\u2022 Run 'basic status' for more details" })] }) : _jsxs7(_Fragment2, { children: [_jsx8(Text8, { color: "gray", children: "\u2022 Try running the command again" }), _jsx8(Text8, { color: "gray", children: "\u2022 Run 'basic status' to check your project state" }), _jsx8(Text8, { color: "gray", children: "\u2022 Check the Basic documentation if the issue persists" })] })] })] });
|
|
2490
|
+
}
|
|
2491
|
+
if (state.phase === "success" && state.pushResult) {
|
|
2492
|
+
setTimeout(() => process.exit(0), 0);
|
|
2493
|
+
return _jsx8(PushSuccessDisplay, { result: state.pushResult });
|
|
2494
|
+
}
|
|
2495
|
+
if (state.phase === "confirming" && state.statusResult) {
|
|
2496
|
+
return _jsx8(PushConfirmationDialog, { statusResult: state.statusResult, selectedOption });
|
|
2497
|
+
}
|
|
2498
|
+
if (state.phase === "no-action" && state.statusResult) {
|
|
2499
|
+
setTimeout(() => process.exit(0), 0);
|
|
2500
|
+
return _jsx8(PushStatusDisplay, { result: state.statusResult });
|
|
2501
|
+
}
|
|
2502
|
+
return _jsx8(Text8, { children: "Unknown state" });
|
|
2503
|
+
}
|
|
2504
|
+
async function analyzePushAction(localConfig, remoteSchema, comparison, apiClient) {
|
|
2505
|
+
const { projectId } = localConfig;
|
|
2506
|
+
const baseResult = {
|
|
2507
|
+
projectId,
|
|
2508
|
+
localVersion: comparison.localVersion,
|
|
2509
|
+
remoteVersion: comparison.remoteVersion,
|
|
2510
|
+
message: [],
|
|
2511
|
+
needsConfirmation: false,
|
|
2512
|
+
confirmationTitle: "",
|
|
2513
|
+
confirmationMessage: ""
|
|
2514
|
+
};
|
|
2515
|
+
switch (comparison.status) {
|
|
2516
|
+
case "ahead":
|
|
2517
|
+
try {
|
|
2518
|
+
const validation = await apiClient.validateSchema(localConfig.schema);
|
|
2519
|
+
if (validation.valid === false && validation.errors) {
|
|
2520
|
+
return {
|
|
2521
|
+
...baseResult,
|
|
2522
|
+
status: "invalid",
|
|
2523
|
+
message: [
|
|
2524
|
+
"Errors found in schema! Please fix:",
|
|
2525
|
+
"Your local schema has validation errors that must be resolved before pushing."
|
|
2526
|
+
],
|
|
2527
|
+
validationErrors: validation.errors
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
return {
|
|
2531
|
+
...baseResult,
|
|
2532
|
+
status: "ahead",
|
|
2533
|
+
message: [
|
|
2534
|
+
"Your local schema is ahead of the remote version.",
|
|
2535
|
+
"Push your changes to publish them?"
|
|
2536
|
+
],
|
|
2537
|
+
needsConfirmation: true,
|
|
2538
|
+
confirmationTitle: "Push Schema Changes",
|
|
2539
|
+
confirmationMessage: "This will publish your local schema changes to the remote project."
|
|
2540
|
+
};
|
|
2541
|
+
} catch (error) {
|
|
2542
|
+
return {
|
|
2543
|
+
...baseResult,
|
|
2544
|
+
status: "invalid",
|
|
2545
|
+
message: [
|
|
2546
|
+
`Error validating schema: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
2547
|
+
"Please check your schema for syntax errors."
|
|
2548
|
+
]
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
case "equal":
|
|
2552
|
+
if (comparison.localVersion === 0 && comparison.remoteVersion === 0) {
|
|
2553
|
+
try {
|
|
2554
|
+
const validation = await apiClient.validateSchema(localConfig.schema);
|
|
2555
|
+
if (validation.valid === false && validation.errors) {
|
|
2556
|
+
return {
|
|
2557
|
+
...baseResult,
|
|
2558
|
+
status: "invalid",
|
|
2559
|
+
message: [
|
|
2560
|
+
"Errors found in schema! Please fix:"
|
|
2561
|
+
],
|
|
2562
|
+
validationErrors: validation.errors
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
return {
|
|
2566
|
+
...baseResult,
|
|
2567
|
+
status: "invalid",
|
|
2568
|
+
message: [
|
|
2569
|
+
"Schema changes are valid!",
|
|
2570
|
+
"Please increment your version number to 1",
|
|
2571
|
+
"and run 'basic push' if you are ready to publish your changes."
|
|
2572
|
+
]
|
|
2573
|
+
};
|
|
2574
|
+
} catch (error) {
|
|
2575
|
+
return {
|
|
2576
|
+
...baseResult,
|
|
2577
|
+
status: "invalid",
|
|
2578
|
+
message: [
|
|
2579
|
+
`Error validating schema: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2580
|
+
]
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
try {
|
|
2585
|
+
const comparisonResult = await apiClient.compareSchema(localConfig.schema);
|
|
2586
|
+
if (comparisonResult.valid) {
|
|
2587
|
+
return {
|
|
2588
|
+
...baseResult,
|
|
2589
|
+
status: "current",
|
|
2590
|
+
message: [
|
|
2591
|
+
"Schema is up to date!",
|
|
2592
|
+
"No push needed."
|
|
2593
|
+
]
|
|
2594
|
+
};
|
|
2595
|
+
} else {
|
|
2596
|
+
return {
|
|
2597
|
+
...baseResult,
|
|
2598
|
+
status: "invalid",
|
|
2599
|
+
message: [
|
|
2600
|
+
"Your local schema differs from the remote schema.",
|
|
2601
|
+
"Please increment your version number before pushing changes."
|
|
2602
|
+
]
|
|
2603
|
+
};
|
|
2604
|
+
}
|
|
2605
|
+
} catch (error) {
|
|
2606
|
+
return {
|
|
2607
|
+
...baseResult,
|
|
2608
|
+
status: "current",
|
|
2609
|
+
message: [
|
|
2610
|
+
"Schema appears to be up to date.",
|
|
2611
|
+
"(Unable to verify schema content - assuming current)"
|
|
2612
|
+
]
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
case "behind":
|
|
2616
|
+
return {
|
|
2617
|
+
...baseResult,
|
|
2618
|
+
status: "behind",
|
|
2619
|
+
message: [
|
|
2620
|
+
"Your local schema is behind the remote version.",
|
|
2621
|
+
"Did you mean to pull instead?",
|
|
2622
|
+
"Use 'basic pull' to get the latest changes."
|
|
2623
|
+
]
|
|
2624
|
+
};
|
|
2625
|
+
default:
|
|
2626
|
+
return {
|
|
2627
|
+
...baseResult,
|
|
2628
|
+
status: "current",
|
|
2629
|
+
message: [
|
|
2630
|
+
"Schema is up to date!",
|
|
2631
|
+
"No push needed."
|
|
2632
|
+
]
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
function PushConfirmationDialog({ statusResult, selectedOption }) {
|
|
2637
|
+
const getStatusIcon = () => {
|
|
2638
|
+
switch (statusResult.status) {
|
|
2639
|
+
case "ahead":
|
|
2640
|
+
return "\u2B06\uFE0F";
|
|
2641
|
+
default:
|
|
2642
|
+
return "\u{1F4E4}";
|
|
2643
|
+
}
|
|
2644
|
+
};
|
|
2645
|
+
const getStatusText = () => {
|
|
2646
|
+
switch (statusResult.status) {
|
|
2647
|
+
case "ahead":
|
|
2648
|
+
return "Ready to push changes";
|
|
2649
|
+
default:
|
|
2650
|
+
return "Schema update ready";
|
|
2651
|
+
}
|
|
2652
|
+
};
|
|
2653
|
+
const getStatusColor = () => {
|
|
2654
|
+
switch (statusResult.status) {
|
|
2655
|
+
case "ahead":
|
|
2656
|
+
return "green";
|
|
2657
|
+
default:
|
|
2658
|
+
return "blue";
|
|
2659
|
+
}
|
|
2660
|
+
};
|
|
2661
|
+
return _jsxs7(Box6, { flexDirection: "column", children: [_jsxs7(Box6, { flexDirection: "column", marginBottom: 1, children: [_jsxs7(Text8, { color: "cyan", children: ["Project ID: ", statusResult.projectId] }), _jsxs7(Box6, { children: [_jsxs7(Text8, { color: "gray", children: ["Local version: ", statusResult.localVersion] }), statusResult.remoteVersion > 0 && _jsxs7(Text8, { color: "gray", children: [" \u2022 Remote version: ", statusResult.remoteVersion] })] })] }), _jsx8(Box6, { marginBottom: 1, children: _jsxs7(Text8, { color: getStatusColor(), children: [getStatusIcon(), " ", getStatusText()] }) }), _jsx8(Box6, { flexDirection: "column", marginBottom: 2, children: statusResult.message.map((line, index) => _jsx8(Text8, { children: line }, index)) }), _jsxs7(Box6, { flexDirection: "column", children: [_jsx8(Box6, { children: _jsxs7(Text8, { color: selectedOption === "yes" ? "green" : "gray", children: [selectedOption === "yes" ? "\u276F" : " ", " Yes, push changes"] }) }), _jsx8(Box6, { children: _jsxs7(Text8, { color: selectedOption === "no" ? "green" : "gray", children: [selectedOption === "no" ? "\u276F" : " ", " No, cancel"] }) })] }), _jsx8(Box6, { marginTop: 1, children: _jsx8(Text8, { color: "gray", children: "Use \u2191\u2193 to navigate, Enter to confirm, Esc to cancel" }) })] });
|
|
2662
|
+
}
|
|
2663
|
+
function PushStatusDisplay({ result }) {
|
|
2664
|
+
const getStatusColor = () => {
|
|
2665
|
+
switch (result.status) {
|
|
2666
|
+
case "current":
|
|
2667
|
+
return "green";
|
|
2668
|
+
case "behind":
|
|
2669
|
+
return "yellow";
|
|
2670
|
+
case "invalid":
|
|
2671
|
+
return "red";
|
|
2672
|
+
case "no-schema":
|
|
2673
|
+
return "gray";
|
|
2674
|
+
default:
|
|
2675
|
+
return "white";
|
|
2676
|
+
}
|
|
2677
|
+
};
|
|
2678
|
+
const getStatusIcon = () => {
|
|
2679
|
+
switch (result.status) {
|
|
2680
|
+
case "current":
|
|
2681
|
+
return "\u2705";
|
|
2682
|
+
case "behind":
|
|
2683
|
+
return "\u2B07\uFE0F";
|
|
2684
|
+
case "invalid":
|
|
2685
|
+
return "\u274C";
|
|
2686
|
+
case "no-schema":
|
|
2687
|
+
return "\u{1F4C4}";
|
|
2688
|
+
default:
|
|
2689
|
+
return "\u2753";
|
|
163
2690
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
2691
|
+
};
|
|
2692
|
+
const getStatusDescription = () => {
|
|
2693
|
+
switch (result.status) {
|
|
2694
|
+
case "current":
|
|
2695
|
+
return "Schema is up to date";
|
|
2696
|
+
case "behind":
|
|
2697
|
+
return "Local schema is behind remote";
|
|
2698
|
+
case "invalid":
|
|
2699
|
+
return "Schema has validation errors";
|
|
2700
|
+
case "no-schema":
|
|
2701
|
+
return "No schema file found";
|
|
2702
|
+
default:
|
|
2703
|
+
return "Unknown status";
|
|
168
2704
|
}
|
|
2705
|
+
};
|
|
2706
|
+
const getNextSteps = () => {
|
|
2707
|
+
switch (result.status) {
|
|
2708
|
+
case "current":
|
|
2709
|
+
return [
|
|
2710
|
+
"Continue working on your project",
|
|
2711
|
+
"Run 'basic status' to check for changes",
|
|
2712
|
+
"Make schema modifications if needed"
|
|
2713
|
+
];
|
|
2714
|
+
case "behind":
|
|
2715
|
+
return [
|
|
2716
|
+
"Run 'basic pull' to get the latest changes",
|
|
2717
|
+
"Or run 'basic status' for more details"
|
|
2718
|
+
];
|
|
2719
|
+
case "invalid":
|
|
2720
|
+
return [
|
|
2721
|
+
"Fix the validation errors shown below",
|
|
2722
|
+
"Run 'basic status' again after fixing errors",
|
|
2723
|
+
"Review your schema syntax and field definitions"
|
|
2724
|
+
];
|
|
2725
|
+
case "no-schema":
|
|
2726
|
+
return [
|
|
2727
|
+
"Run 'basic init' to create a new project or import an existing project",
|
|
2728
|
+
"Make sure you're in a directory with a basic.config.ts/js file"
|
|
2729
|
+
];
|
|
2730
|
+
default:
|
|
2731
|
+
return [];
|
|
2732
|
+
}
|
|
2733
|
+
};
|
|
2734
|
+
return _jsxs7(Box6, { flexDirection: "column", children: [result.projectId && _jsxs7(Box6, { flexDirection: "column", marginBottom: 1, children: [_jsxs7(Text8, { color: "cyan", children: ["Project ID: ", result.projectId] }), _jsxs7(Box6, { children: [_jsxs7(Text8, { color: "gray", children: ["Local version: ", result.localVersion] }), result.remoteVersion > 0 && _jsxs7(Text8, { color: "gray", children: [" \u2022 Remote version: ", result.remoteVersion] })] })] }), _jsx8(Box6, { marginBottom: 1, children: _jsxs7(Text8, { color: getStatusColor(), children: [getStatusIcon(), " ", getStatusDescription()] }) }), _jsx8(Box6, { flexDirection: "column", marginBottom: 1, children: result.message.map((line, index) => _jsx8(Text8, { children: line }, index)) }), result.validationErrors && result.validationErrors.length > 0 && _jsx8(Box6, { flexDirection: "column", marginBottom: 1, children: result.validationErrors.map((error, index) => _jsxs7(Text8, { color: "red", children: ["\u2022 ", error.message, " at ", error.instancePath || "root"] }, index)) }), getNextSteps().length > 0 && _jsxs7(Box6, { flexDirection: "column", marginBottom: 1, children: [_jsx8(Text8, { color: "blue", children: "Next steps:" }), getNextSteps().map((step, index) => _jsxs7(Text8, { color: "gray", children: ["\u2022 ", step] }, index))] })] });
|
|
2735
|
+
}
|
|
2736
|
+
function PushSuccessDisplay({ result }) {
|
|
2737
|
+
return _jsxs7(Box6, { flexDirection: "column", children: [_jsx8(Box6, { marginBottom: 1, children: _jsx8(Text8, { color: "green", children: "\u2705 Schema pushed successfully!" }) }), _jsxs7(Box6, { flexDirection: "column", marginBottom: 1, children: [_jsxs7(Text8, { children: ["Source: ", result.filePath.split("/").pop()] }), _jsxs7(Text8, { children: ["Version: ", result.oldVersion, " \u2192 ", result.newVersion] }), _jsxs7(Text8, { children: ["Project: ", result.projectId] })] }), _jsxs7(Box6, { flexDirection: "column", marginTop: 1, children: [_jsx8(Text8, { color: "blue", children: "Next steps:" }), _jsx8(Text8, { color: "gray", children: "\u2022 Your schema changes are now live" }), _jsx8(Text8, { color: "gray", children: "\u2022 Continue working on your project" }), _jsx8(Text8, { color: "gray", children: "\u2022 Run 'basic status' to check your project state" })] })] });
|
|
2738
|
+
}
|
|
2739
|
+
async function PushCommand() {
|
|
2740
|
+
render5(_jsx8(PushApp, {}));
|
|
2741
|
+
}
|
|
2742
|
+
var init_push = __esm({
|
|
2743
|
+
"dist/commands/push.js"() {
|
|
2744
|
+
"use strict";
|
|
2745
|
+
init_Spinner();
|
|
2746
|
+
init_api();
|
|
2747
|
+
init_auth();
|
|
2748
|
+
init_schema();
|
|
2749
|
+
init_platform();
|
|
2750
|
+
init_constants();
|
|
2751
|
+
}
|
|
169
2752
|
});
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
2753
|
+
|
|
2754
|
+
// dist/lib/config-templates.js
|
|
2755
|
+
var config_templates_exports = {};
|
|
2756
|
+
__export(config_templates_exports, {
|
|
2757
|
+
CONFIG_TEMPLATES: () => CONFIG_TEMPLATES,
|
|
2758
|
+
checkForExistingConfig: () => checkForExistingConfig,
|
|
2759
|
+
createConfigFile: () => createConfigFile,
|
|
2760
|
+
generateConfigContent: () => generateConfigContent2,
|
|
2761
|
+
readExistingConfig: () => readExistingConfig
|
|
2762
|
+
});
|
|
2763
|
+
import * as fs3 from "fs/promises";
|
|
2764
|
+
import * as path3 from "path";
|
|
2765
|
+
function generateConfigContent2(template, projectId, projectName) {
|
|
2766
|
+
const baseConfig = {
|
|
2767
|
+
project_id: projectId,
|
|
2768
|
+
version: 0,
|
|
2769
|
+
tables: {
|
|
2770
|
+
example: {
|
|
2771
|
+
type: "collection",
|
|
2772
|
+
fields: {
|
|
2773
|
+
value: {
|
|
2774
|
+
type: "string"
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
};
|
|
2780
|
+
switch (template) {
|
|
2781
|
+
case "typescript":
|
|
2782
|
+
return `// Basic Project Configuration
|
|
2783
|
+
// see the docs for more info: https://docs.basic.tech
|
|
2784
|
+
|
|
2785
|
+
const schema = ${JSON.stringify(baseConfig, null, 2)};
|
|
2786
|
+
|
|
2787
|
+
export default schema;
|
|
2788
|
+
`;
|
|
2789
|
+
case "javascript":
|
|
2790
|
+
return `// Basic Project Configuration
|
|
2791
|
+
// see the docs for more info: https://docs.basic.tech
|
|
2792
|
+
|
|
2793
|
+
const schema = ${JSON.stringify(baseConfig, null, 2)};
|
|
2794
|
+
|
|
2795
|
+
module.exports = schema;
|
|
2796
|
+
`;
|
|
2797
|
+
case "none":
|
|
2798
|
+
return "";
|
|
2799
|
+
default:
|
|
2800
|
+
throw new Error(`Unknown template: ${template}`);
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
async function createConfigFile(template, projectId, projectName, targetDir = process.cwd()) {
|
|
2804
|
+
if (template === "none") {
|
|
2805
|
+
return null;
|
|
2806
|
+
}
|
|
2807
|
+
const templateInfo = CONFIG_TEMPLATES[template];
|
|
2808
|
+
const configPath = path3.join(targetDir, templateInfo.filename);
|
|
2809
|
+
const content = generateConfigContent2(template, projectId, projectName);
|
|
2810
|
+
try {
|
|
2811
|
+
await fs3.writeFile(configPath, content, "utf8");
|
|
2812
|
+
return configPath;
|
|
2813
|
+
} catch (error) {
|
|
2814
|
+
throw new Error(`Failed to create config file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
async function checkForExistingConfig(targetDir = process.cwd()) {
|
|
2818
|
+
const possibleConfigs = [
|
|
2819
|
+
"basic.config.ts",
|
|
2820
|
+
"basic.config.js",
|
|
2821
|
+
"basic.config.json"
|
|
2822
|
+
];
|
|
2823
|
+
for (const filename of possibleConfigs) {
|
|
2824
|
+
const filePath = path3.join(targetDir, filename);
|
|
175
2825
|
try {
|
|
176
|
-
|
|
177
|
-
|
|
2826
|
+
await fs3.access(filePath);
|
|
2827
|
+
return filePath;
|
|
2828
|
+
} catch {
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
return null;
|
|
2832
|
+
}
|
|
2833
|
+
async function readExistingConfig(targetDir = process.cwd()) {
|
|
2834
|
+
const configPath = await checkForExistingConfig(targetDir);
|
|
2835
|
+
if (!configPath) {
|
|
2836
|
+
return null;
|
|
2837
|
+
}
|
|
2838
|
+
try {
|
|
2839
|
+
const content = await fs3.readFile(configPath, "utf8");
|
|
2840
|
+
if (configPath.endsWith(".json")) {
|
|
2841
|
+
const config = JSON.parse(content);
|
|
2842
|
+
return { projectId: config.project_id };
|
|
178
2843
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
process.exit(1);
|
|
2844
|
+
const projectIdMatch = content.match(/["']?project_id["']?\s*:\s*["']([^"']+)["']/);
|
|
2845
|
+
if (projectIdMatch) {
|
|
2846
|
+
return { projectId: projectIdMatch[1] };
|
|
183
2847
|
}
|
|
2848
|
+
return null;
|
|
2849
|
+
} catch (error) {
|
|
2850
|
+
throw new Error(`Failed to read existing config: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
var CONFIG_TEMPLATES;
|
|
2854
|
+
var init_config_templates = __esm({
|
|
2855
|
+
"dist/lib/config-templates.js"() {
|
|
2856
|
+
"use strict";
|
|
2857
|
+
CONFIG_TEMPLATES = {
|
|
2858
|
+
typescript: {
|
|
2859
|
+
name: "TypeScript",
|
|
2860
|
+
description: "",
|
|
2861
|
+
filename: "basic.config.ts",
|
|
2862
|
+
extension: "ts"
|
|
2863
|
+
},
|
|
2864
|
+
javascript: {
|
|
2865
|
+
name: "JavaScript",
|
|
2866
|
+
description: "",
|
|
2867
|
+
filename: "basic.config.js",
|
|
2868
|
+
extension: "js"
|
|
2869
|
+
},
|
|
2870
|
+
none: {
|
|
2871
|
+
name: "None",
|
|
2872
|
+
description: "No configuration file",
|
|
2873
|
+
filename: "",
|
|
2874
|
+
extension: ""
|
|
2875
|
+
}
|
|
2876
|
+
};
|
|
2877
|
+
}
|
|
184
2878
|
});
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
2879
|
+
|
|
2880
|
+
// dist/components/InitForm.js
|
|
2881
|
+
import { jsx as _jsx9, jsxs as _jsxs8, Fragment as _Fragment3 } from "react/jsx-runtime";
|
|
2882
|
+
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
2883
|
+
import { Box as Box7, Text as Text9, useInput as useInput5 } from "ink";
|
|
2884
|
+
function InitForm({ onSuccess, onCancel, initialData }) {
|
|
2885
|
+
const [state, setState] = useState3({
|
|
2886
|
+
step: initialData?.source ? initialData.source === "new" ? "project-details" : "existing-selection" : "source",
|
|
2887
|
+
source: initialData?.source || null,
|
|
2888
|
+
projectName: initialData?.projectName || "",
|
|
2889
|
+
projectSlug: initialData?.projectName ? generateSlug(initialData.projectName) : "",
|
|
2890
|
+
selectedTeamId: null,
|
|
2891
|
+
selectedProjectId: initialData?.projectId || null,
|
|
2892
|
+
configTemplate: initialData?.configTemplate || null,
|
|
2893
|
+
availableTeams: [],
|
|
2894
|
+
availableProjects: [],
|
|
2895
|
+
isLoading: false,
|
|
2896
|
+
error: null
|
|
2897
|
+
});
|
|
2898
|
+
const [selectedOptionIndex, setSelectedOptionIndex] = useState3(0);
|
|
2899
|
+
const [showTeamForm, setShowTeamForm] = useState3(false);
|
|
2900
|
+
useEffect3(() => {
|
|
2901
|
+
async function loadData() {
|
|
2902
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2903
|
+
try {
|
|
2904
|
+
const apiClient = ApiClient.getInstance();
|
|
2905
|
+
const [teams, projects] = await Promise.all([
|
|
2906
|
+
apiClient.getTeams(),
|
|
2907
|
+
apiClient.getProjects()
|
|
2908
|
+
]);
|
|
2909
|
+
setState((prev) => ({
|
|
2910
|
+
...prev,
|
|
2911
|
+
availableTeams: teams,
|
|
2912
|
+
availableProjects: projects,
|
|
2913
|
+
isLoading: false,
|
|
2914
|
+
selectedTeamId: teams.length > 0 ? teams[0].id : null
|
|
2915
|
+
}));
|
|
2916
|
+
} catch (error) {
|
|
2917
|
+
setState((prev) => ({
|
|
2918
|
+
...prev,
|
|
2919
|
+
isLoading: false,
|
|
2920
|
+
error: error instanceof Error ? error.message : "Failed to load data"
|
|
2921
|
+
}));
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
loadData();
|
|
2925
|
+
}, []);
|
|
2926
|
+
useEffect3(() => {
|
|
2927
|
+
if (state.projectName.trim()) {
|
|
2928
|
+
const newSlug = generateSlug(state.projectName);
|
|
2929
|
+
setState((prev) => ({ ...prev, projectSlug: newSlug }));
|
|
2930
|
+
}
|
|
2931
|
+
}, [state.projectName]);
|
|
2932
|
+
useInput5((input, key) => {
|
|
2933
|
+
if (showTeamForm) {
|
|
2934
|
+
return;
|
|
2935
|
+
}
|
|
2936
|
+
if (key.escape) {
|
|
2937
|
+
if (state.step === "source") {
|
|
2938
|
+
onCancel();
|
|
2939
|
+
} else {
|
|
2940
|
+
goToPreviousStep();
|
|
2941
|
+
}
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2944
|
+
if (state.step === "project-details") {
|
|
2945
|
+
handleProjectDetailsInput(input, key);
|
|
2946
|
+
} else if (state.step === "source" || state.step === "team-selection" || state.step === "existing-selection" || state.step === "config-template" || state.step === "confirmation") {
|
|
2947
|
+
handleSelectionInput(input, key);
|
|
2948
|
+
}
|
|
2949
|
+
});
|
|
2950
|
+
const handleProjectDetailsInput = (input, key) => {
|
|
2951
|
+
if (key.return) {
|
|
2952
|
+
if (state.projectName.trim()) {
|
|
2953
|
+
setState((prev) => ({ ...prev, step: "team-selection" }));
|
|
2954
|
+
setSelectedOptionIndex(0);
|
|
2955
|
+
}
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
if (key.backspace || key.delete) {
|
|
2959
|
+
setState((prev) => ({
|
|
2960
|
+
...prev,
|
|
2961
|
+
projectName: prev.projectName.slice(0, -1)
|
|
2962
|
+
}));
|
|
2963
|
+
return;
|
|
2964
|
+
}
|
|
2965
|
+
if (input && input.length === 1) {
|
|
2966
|
+
setState((prev) => ({
|
|
2967
|
+
...prev,
|
|
2968
|
+
projectName: prev.projectName + input
|
|
2969
|
+
}));
|
|
2970
|
+
}
|
|
2971
|
+
};
|
|
2972
|
+
const handleSelectionInput = (input, key) => {
|
|
2973
|
+
const options = getOptionsForCurrentStep();
|
|
2974
|
+
if (key.upArrow) {
|
|
2975
|
+
setSelectedOptionIndex((prev) => prev > 0 ? prev - 1 : options.length - 1);
|
|
2976
|
+
return;
|
|
2977
|
+
}
|
|
2978
|
+
if (key.downArrow) {
|
|
2979
|
+
setSelectedOptionIndex((prev) => prev < options.length - 1 ? prev + 1 : 0);
|
|
2980
|
+
return;
|
|
2981
|
+
}
|
|
2982
|
+
if (key.return) {
|
|
2983
|
+
handleOptionSelection();
|
|
2984
|
+
}
|
|
2985
|
+
};
|
|
2986
|
+
const getOptionsForCurrentStep = () => {
|
|
2987
|
+
switch (state.step) {
|
|
2988
|
+
case "source":
|
|
2989
|
+
return [
|
|
2990
|
+
{ label: "Create new project", value: "new" },
|
|
2991
|
+
{ label: "Import existing project", value: "existing" }
|
|
2992
|
+
];
|
|
2993
|
+
case "team-selection":
|
|
2994
|
+
const teamOptions = state.availableTeams.map((team) => ({
|
|
2995
|
+
label: `${team.name} (${team.slug})`,
|
|
2996
|
+
value: team.id
|
|
2997
|
+
}));
|
|
2998
|
+
teamOptions.push({ label: "Create new team...", value: "new" });
|
|
2999
|
+
return teamOptions;
|
|
3000
|
+
case "existing-selection":
|
|
3001
|
+
return state.availableProjects.map((project) => ({
|
|
3002
|
+
label: `${project.name} (${project.team_name || "Unknown team"})`,
|
|
3003
|
+
value: project.id
|
|
3004
|
+
}));
|
|
3005
|
+
case "config-template":
|
|
3006
|
+
return Object.entries(CONFIG_TEMPLATES).map(([key, template]) => ({
|
|
3007
|
+
label: `${template.name} - ${template.description}`,
|
|
3008
|
+
value: key
|
|
3009
|
+
}));
|
|
3010
|
+
case "confirmation":
|
|
3011
|
+
return [
|
|
3012
|
+
{
|
|
3013
|
+
label: state.source === "new" ? "Yes, create project" : "Yes, import project",
|
|
3014
|
+
value: "confirm"
|
|
3015
|
+
},
|
|
3016
|
+
{ label: "No, go back", value: "back" }
|
|
3017
|
+
];
|
|
3018
|
+
default:
|
|
3019
|
+
return [];
|
|
3020
|
+
}
|
|
3021
|
+
};
|
|
3022
|
+
const handleOptionSelection = () => {
|
|
3023
|
+
const options = getOptionsForCurrentStep();
|
|
3024
|
+
const selectedOption = options[selectedOptionIndex];
|
|
3025
|
+
switch (state.step) {
|
|
3026
|
+
case "source":
|
|
3027
|
+
setState((prev) => ({
|
|
3028
|
+
...prev,
|
|
3029
|
+
source: selectedOption.value,
|
|
3030
|
+
step: selectedOption.value === "new" ? "project-details" : "existing-selection"
|
|
3031
|
+
}));
|
|
3032
|
+
setSelectedOptionIndex(0);
|
|
3033
|
+
break;
|
|
3034
|
+
case "team-selection":
|
|
3035
|
+
if (selectedOption.value === "new") {
|
|
3036
|
+
setShowTeamForm(true);
|
|
3037
|
+
} else {
|
|
3038
|
+
setState((prev) => ({
|
|
3039
|
+
...prev,
|
|
3040
|
+
selectedTeamId: selectedOption.value,
|
|
3041
|
+
step: "config-template"
|
|
3042
|
+
}));
|
|
3043
|
+
setSelectedOptionIndex(0);
|
|
3044
|
+
}
|
|
3045
|
+
break;
|
|
3046
|
+
case "existing-selection":
|
|
3047
|
+
setState((prev) => ({
|
|
3048
|
+
...prev,
|
|
3049
|
+
selectedProjectId: selectedOption.value,
|
|
3050
|
+
step: "config-template"
|
|
3051
|
+
}));
|
|
3052
|
+
setSelectedOptionIndex(0);
|
|
3053
|
+
break;
|
|
3054
|
+
case "config-template":
|
|
3055
|
+
setState((prev) => ({
|
|
3056
|
+
...prev,
|
|
3057
|
+
configTemplate: selectedOption.value,
|
|
3058
|
+
step: "confirmation"
|
|
3059
|
+
}));
|
|
3060
|
+
setSelectedOptionIndex(0);
|
|
3061
|
+
break;
|
|
3062
|
+
case "confirmation":
|
|
3063
|
+
if (selectedOption.value === "confirm") {
|
|
3064
|
+
handleSubmit();
|
|
3065
|
+
} else {
|
|
3066
|
+
goToPreviousStep();
|
|
3067
|
+
}
|
|
3068
|
+
break;
|
|
3069
|
+
}
|
|
3070
|
+
};
|
|
3071
|
+
const goToPreviousStep = () => {
|
|
3072
|
+
switch (state.step) {
|
|
3073
|
+
case "project-details":
|
|
3074
|
+
setState((prev) => ({ ...prev, step: "source" }));
|
|
3075
|
+
break;
|
|
3076
|
+
case "team-selection":
|
|
3077
|
+
setState((prev) => ({ ...prev, step: "project-details" }));
|
|
3078
|
+
break;
|
|
3079
|
+
case "existing-selection":
|
|
3080
|
+
setState((prev) => ({ ...prev, step: "source" }));
|
|
3081
|
+
break;
|
|
3082
|
+
case "config-template":
|
|
3083
|
+
setState((prev) => ({
|
|
3084
|
+
...prev,
|
|
3085
|
+
step: state.source === "new" ? "team-selection" : "existing-selection"
|
|
3086
|
+
}));
|
|
3087
|
+
break;
|
|
3088
|
+
case "confirmation":
|
|
3089
|
+
setState((prev) => ({ ...prev, step: "config-template" }));
|
|
3090
|
+
break;
|
|
3091
|
+
}
|
|
3092
|
+
setSelectedOptionIndex(0);
|
|
3093
|
+
};
|
|
3094
|
+
const handleTeamCreated = async (teamData) => {
|
|
3095
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
190
3096
|
try {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
3097
|
+
const apiClient = ApiClient.getInstance();
|
|
3098
|
+
const newTeam = await apiClient.createTeam(teamData.teamName, teamData.teamSlug);
|
|
3099
|
+
setState((prev) => ({
|
|
3100
|
+
...prev,
|
|
3101
|
+
availableTeams: [...prev.availableTeams, newTeam],
|
|
3102
|
+
selectedTeamId: newTeam.id,
|
|
3103
|
+
step: "config-template",
|
|
3104
|
+
isLoading: false
|
|
3105
|
+
}));
|
|
3106
|
+
setShowTeamForm(false);
|
|
3107
|
+
setSelectedOptionIndex(0);
|
|
3108
|
+
} catch (error) {
|
|
3109
|
+
setState((prev) => ({
|
|
3110
|
+
...prev,
|
|
3111
|
+
isLoading: false,
|
|
3112
|
+
error: error instanceof Error ? error.message : "Failed to create team"
|
|
3113
|
+
}));
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
const handleSubmit = async () => {
|
|
3117
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
211
3118
|
try {
|
|
212
|
-
|
|
213
|
-
|
|
3119
|
+
const apiClient = ApiClient.getInstance();
|
|
3120
|
+
let projectId;
|
|
3121
|
+
let projectName;
|
|
3122
|
+
if (state.source === "new") {
|
|
3123
|
+
if (!state.selectedTeamId || !state.configTemplate) {
|
|
3124
|
+
throw new Error("Missing required data for project creation");
|
|
3125
|
+
}
|
|
3126
|
+
const project = await apiClient.createProjectWithTeam(state.projectName, state.projectSlug, state.selectedTeamId);
|
|
3127
|
+
projectId = project.id;
|
|
3128
|
+
projectName = project.name;
|
|
3129
|
+
} else {
|
|
3130
|
+
if (!state.selectedProjectId) {
|
|
3131
|
+
throw new Error("No project selected");
|
|
3132
|
+
}
|
|
3133
|
+
const project = await apiClient.getProject(state.selectedProjectId);
|
|
3134
|
+
projectId = project.id;
|
|
3135
|
+
projectName = project.name;
|
|
3136
|
+
}
|
|
3137
|
+
let configPath = null;
|
|
3138
|
+
if (state.configTemplate && state.configTemplate !== "none") {
|
|
3139
|
+
const { createConfigFile: createConfigFile2 } = await Promise.resolve().then(() => (init_config_templates(), config_templates_exports));
|
|
3140
|
+
configPath = await createConfigFile2(state.configTemplate, projectId, projectName);
|
|
3141
|
+
try {
|
|
3142
|
+
const remoteSchema = await apiClient.getProjectSchema(projectId);
|
|
3143
|
+
if (remoteSchema && remoteSchema.version > 0) {
|
|
3144
|
+
const { saveSchemaToConfig: saveSchemaToConfig2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
3145
|
+
await saveSchemaToConfig2(remoteSchema);
|
|
3146
|
+
}
|
|
3147
|
+
} catch (error) {
|
|
3148
|
+
console.warn("Failed to pull latest schema during init:", error);
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
onSuccess({ projectId, projectName, configPath });
|
|
3152
|
+
} catch (error) {
|
|
3153
|
+
setState((prev) => ({
|
|
3154
|
+
...prev,
|
|
3155
|
+
isLoading: false,
|
|
3156
|
+
error: error instanceof Error ? error.message : "Failed to create project"
|
|
3157
|
+
}));
|
|
214
3158
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
3159
|
+
};
|
|
3160
|
+
if (showTeamForm) {
|
|
3161
|
+
return _jsx9(TeamForm, { title: "Create New Team", onSubmit: handleTeamCreated, onCancel: () => setShowTeamForm(false) });
|
|
3162
|
+
}
|
|
3163
|
+
if (state.isLoading) {
|
|
3164
|
+
return _jsx9(Spinner, { text: "Loading..." });
|
|
3165
|
+
}
|
|
3166
|
+
if (state.error) {
|
|
3167
|
+
return _jsxs8(Box7, { flexDirection: "column", children: [_jsxs8(Text9, { color: "red", children: ["Error: ", state.error] }), _jsx9(Text9, { color: "gray", children: "Press Esc to go back" })] });
|
|
3168
|
+
}
|
|
3169
|
+
return _jsx9(Box7, { flexDirection: "column", padding: 1, children: renderCurrentStep() });
|
|
3170
|
+
function renderCurrentStep() {
|
|
3171
|
+
const stepNumber = getStepNumber();
|
|
3172
|
+
const totalSteps = getTotalSteps();
|
|
3173
|
+
switch (state.step) {
|
|
3174
|
+
case "source":
|
|
3175
|
+
return _jsxs8(_Fragment3, { children: [_jsxs8(Text9, { bold: true, color: "blue", children: ["Project Setup (", stepNumber, "/", totalSteps, ")"] }), _jsx9(Box7, { marginTop: 1, marginBottom: 2, children: _jsx9(Text9, { children: "How would you like to proceed?" }) }), renderOptions(), _jsx9(Box7, { marginTop: 2, children: _jsx9(Text9, { color: "gray", children: "\u2191/\u2193 select \u2022 enter to continue \u2022 esc to cancel" }) })] });
|
|
3176
|
+
case "project-details":
|
|
3177
|
+
return _jsxs8(_Fragment3, { children: [_jsxs8(Text9, { bold: true, color: "blue", children: ["Create New Project (", stepNumber, "/", totalSteps, ")"] }), _jsx9(Box7, { marginTop: 1, marginBottom: 1, children: _jsxs8(Text9, { color: "blue", children: [">", " Project Name:"] }) }), _jsx9(Box7, { marginLeft: 2, marginBottom: 1, children: _jsxs8(Text9, { children: [state.projectName, _jsx9(Text9, { backgroundColor: "white", color: "black", children: "\u2588" })] }) }), state.projectSlug && _jsxs8(Box7, { marginBottom: 1, children: [_jsx9(Box7, { children: _jsx9(Text9, { color: "gray", children: "\u2713 Project Slug (auto-generated):" }) }), _jsx9(Box7, { marginLeft: 2, children: _jsx9(Text9, { children: state.projectSlug }) })] }), _jsx9(Box7, { marginTop: 2, children: _jsx9(Text9, { color: "gray", children: state.projectName.trim() ? "Enter to continue \u2022 esc to go back" : "Type project name \u2022 esc to go back" }) })] });
|
|
3178
|
+
case "team-selection":
|
|
3179
|
+
return _jsxs8(_Fragment3, { children: [_jsxs8(Text9, { bold: true, color: "blue", children: ["Create New Project (", stepNumber, "/", totalSteps, ")"] }), _jsxs8(Box7, { marginTop: 1, children: [_jsxs8(Text9, { color: "gray", children: ["\u2713 Project Name: ", state.projectName] }), _jsxs8(Text9, { color: "gray", children: ["\u2713 Project Slug: ", state.projectSlug] })] }), _jsx9(Box7, { marginTop: 1, marginBottom: 2, children: _jsx9(Text9, { children: "Select Team:" }) }), renderOptions(), _jsx9(Box7, { marginTop: 2, children: _jsx9(Text9, { color: "gray", children: "\u2191/\u2193 select \u2022 enter to continue \u2022 esc to go back" }) })] });
|
|
3180
|
+
case "existing-selection":
|
|
3181
|
+
return _jsxs8(_Fragment3, { children: [_jsxs8(Text9, { bold: true, color: "blue", children: ["Import Existing Project (", stepNumber, "/", totalSteps, ")"] }), _jsx9(Box7, { marginTop: 1, marginBottom: 2, children: _jsx9(Text9, { children: "Select Project:" }) }), renderOptions(), _jsx9(Box7, { marginTop: 2, children: _jsx9(Text9, { color: "gray", children: "\u2191/\u2193 select \u2022 enter to continue \u2022 esc to go back" }) })] });
|
|
3182
|
+
case "config-template":
|
|
3183
|
+
return _jsxs8(_Fragment3, { children: [_jsxs8(Text9, { bold: true, color: "blue", children: ["Configuration Setup (", stepNumber, "/", totalSteps, ")"] }), _jsx9(Box7, { marginTop: 1, marginBottom: 2, children: _jsx9(Text9, { children: "Choose config template:" }) }), renderOptions(), _jsx9(Box7, { marginTop: 2, children: _jsx9(Text9, { color: "gray", children: "\u2191/\u2193 select \u2022 enter to continue \u2022 esc to go back" }) })] });
|
|
3184
|
+
case "confirmation":
|
|
3185
|
+
return _jsxs8(_Fragment3, { children: [_jsxs8(Text9, { bold: true, color: "blue", children: ["Ready to ", state.source === "new" ? "Create" : "Import", " (", stepNumber, "/", totalSteps, ")"] }), _jsxs8(Box7, { marginTop: 1, marginBottom: 2, flexDirection: "column", children: [state.source === "new" ? _jsxs8(_Fragment3, { children: [_jsxs8(Text9, { children: ["\u2713 Project: ", state.projectName] }), _jsxs8(Text9, { children: ["\u2713 Team: ", getSelectedTeamName()] })] }) : _jsxs8(Text9, { children: ["\u2713 Project: ", getSelectedProjectName()] }), _jsxs8(Text9, { children: ["\u2713 Config: ", getSelectedTemplateName()] }), state.configTemplate !== "none" && _jsxs8(Text9, { children: ["\u2713 Location: ./", CONFIG_TEMPLATES[state.configTemplate].filename] })] }), renderOptions(), _jsx9(Box7, { marginTop: 2, children: _jsx9(Text9, { color: "gray", children: "\u2191/\u2193 select \u2022 enter to confirm \u2022 esc to go back" }) })] });
|
|
3186
|
+
default:
|
|
3187
|
+
return _jsx9(Text9, { children: "Unknown step" });
|
|
219
3188
|
}
|
|
3189
|
+
}
|
|
3190
|
+
function renderOptions() {
|
|
3191
|
+
const options = getOptionsForCurrentStep();
|
|
3192
|
+
return _jsx9(Box7, { flexDirection: "column", children: options.map((option, index) => _jsx9(Box7, { marginLeft: 2, children: _jsxs8(Text9, { color: index === selectedOptionIndex ? "blue" : "white", children: [index === selectedOptionIndex ? "\u25CF" : "\u25CB", " ", option.label] }) }, option.value)) });
|
|
3193
|
+
}
|
|
3194
|
+
function getStepNumber() {
|
|
3195
|
+
const stepOrder = ["source", "project-details", "team-selection", "existing-selection", "config-template", "confirmation"];
|
|
3196
|
+
return stepOrder.indexOf(state.step) + 1;
|
|
3197
|
+
}
|
|
3198
|
+
function getTotalSteps() {
|
|
3199
|
+
return state.source === "new" ? 5 : 4;
|
|
3200
|
+
}
|
|
3201
|
+
function getSelectedTeamName() {
|
|
3202
|
+
const team = state.availableTeams.find((t) => t.id === state.selectedTeamId);
|
|
3203
|
+
return team ? team.name : "Unknown";
|
|
3204
|
+
}
|
|
3205
|
+
function getSelectedProjectName() {
|
|
3206
|
+
const project = state.availableProjects.find((p) => p.id === state.selectedProjectId);
|
|
3207
|
+
return project ? project.name : "Unknown";
|
|
3208
|
+
}
|
|
3209
|
+
function getSelectedTemplateName() {
|
|
3210
|
+
return state.configTemplate ? CONFIG_TEMPLATES[state.configTemplate].name : "None";
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
var init_InitForm = __esm({
|
|
3214
|
+
"dist/components/InitForm.js"() {
|
|
3215
|
+
"use strict";
|
|
3216
|
+
init_api();
|
|
3217
|
+
init_platform();
|
|
3218
|
+
init_config_templates();
|
|
3219
|
+
init_Spinner();
|
|
3220
|
+
init_TeamForm();
|
|
3221
|
+
}
|
|
220
3222
|
});
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
3223
|
+
|
|
3224
|
+
// dist/commands/init.js
|
|
3225
|
+
var init_exports = {};
|
|
3226
|
+
__export(init_exports, {
|
|
3227
|
+
InitCommand: () => InitCommand
|
|
3228
|
+
});
|
|
3229
|
+
import { jsx as _jsx10, jsxs as _jsxs9 } from "react/jsx-runtime";
|
|
3230
|
+
import React7 from "react";
|
|
3231
|
+
import { render as render6, Box as Box8, Text as Text10 } from "ink";
|
|
3232
|
+
function InitApp({ options }) {
|
|
3233
|
+
const [state, setState] = React7.useState({
|
|
3234
|
+
loading: true,
|
|
3235
|
+
error: null,
|
|
3236
|
+
success: false
|
|
3237
|
+
});
|
|
3238
|
+
const [initialData, setInitialData] = React7.useState(null);
|
|
3239
|
+
React7.useEffect(() => {
|
|
3240
|
+
async function initialize() {
|
|
3241
|
+
try {
|
|
3242
|
+
if (!await isOnline()) {
|
|
3243
|
+
setState({ loading: false, error: MESSAGES.OFFLINE, success: false });
|
|
3244
|
+
return;
|
|
3245
|
+
}
|
|
3246
|
+
const authService = AuthService.getInstance();
|
|
3247
|
+
const token = await authService.getToken();
|
|
3248
|
+
if (!token) {
|
|
3249
|
+
setState({ loading: false, error: MESSAGES.LOGGED_OUT, success: false });
|
|
3250
|
+
return;
|
|
3251
|
+
}
|
|
3252
|
+
const existingConfigPath = await checkForExistingConfig();
|
|
3253
|
+
if (existingConfigPath) {
|
|
3254
|
+
const existingConfig = await readExistingConfig();
|
|
3255
|
+
if (existingConfig?.projectId) {
|
|
3256
|
+
setState({
|
|
3257
|
+
loading: false,
|
|
3258
|
+
error: `A basic.config already exists in this directory
|
|
3259
|
+
Project ID: ${existingConfig.projectId}
|
|
3260
|
+
|
|
3261
|
+
To reinitialize, please remove the existing config file first.`,
|
|
3262
|
+
success: false
|
|
3263
|
+
});
|
|
3264
|
+
return;
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
const parsedData = parseCliOptions(options);
|
|
3268
|
+
setInitialData(parsedData);
|
|
3269
|
+
setState({ loading: false, error: null, success: false });
|
|
3270
|
+
} catch (error) {
|
|
3271
|
+
setState({
|
|
3272
|
+
loading: false,
|
|
3273
|
+
error: error instanceof Error ? error.message : "Failed to initialize",
|
|
3274
|
+
success: false
|
|
230
3275
|
});
|
|
231
|
-
|
|
3276
|
+
}
|
|
232
3277
|
}
|
|
233
|
-
|
|
3278
|
+
initialize();
|
|
3279
|
+
}, [options]);
|
|
3280
|
+
const handleSuccess = (result) => {
|
|
3281
|
+
setState({ loading: false, error: null, success: true, result });
|
|
3282
|
+
setTimeout(() => {
|
|
3283
|
+
process.exit(0);
|
|
3284
|
+
}, 2e3);
|
|
3285
|
+
};
|
|
3286
|
+
const handleCancel = () => {
|
|
3287
|
+
process.exit(0);
|
|
3288
|
+
};
|
|
3289
|
+
if (state.loading) {
|
|
3290
|
+
return _jsx10(Spinner, { text: "Initializing..." });
|
|
3291
|
+
}
|
|
3292
|
+
if (state.error) {
|
|
3293
|
+
return _jsxs9(Box8, { flexDirection: "column", children: [_jsxs9(Text10, { color: "red", children: ["Error: ", state.error] }), _jsx10(Text10, { color: "gray", children: "Please resolve the issue and try again." })] });
|
|
3294
|
+
}
|
|
3295
|
+
if (state.success && state.result) {
|
|
3296
|
+
return _jsxs9(Box8, { flexDirection: "column", children: [_jsx10(Text10, { color: "green", children: "\u2705 Project setup complete!" }), _jsx10(Text10, {}), _jsxs9(Text10, { children: ["Project: ", state.result.projectName] }), _jsxs9(Text10, { children: ["Project ID: ", state.result.projectId] }), state.result.configPath && _jsxs9(Text10, { children: ["Config file: ", state.result.configPath] }), _jsx10(Text10, {}), _jsx10(Text10, { color: "gray", children: "Visit https://docs.basic.tech for next steps." })] });
|
|
3297
|
+
}
|
|
3298
|
+
return _jsx10(InitForm, { onSuccess: handleSuccess, onCancel: handleCancel, initialData: initialData || void 0 });
|
|
3299
|
+
}
|
|
3300
|
+
function parseCliOptions(options) {
|
|
3301
|
+
const result = {};
|
|
3302
|
+
if (options.new) {
|
|
3303
|
+
result.source = "new";
|
|
3304
|
+
} else if (options.existing) {
|
|
3305
|
+
result.source = "existing";
|
|
3306
|
+
}
|
|
3307
|
+
if (options.name) {
|
|
3308
|
+
result.projectName = options.name;
|
|
3309
|
+
}
|
|
3310
|
+
if (options.project) {
|
|
3311
|
+
result.projectId = options.project;
|
|
3312
|
+
result.source = "existing";
|
|
3313
|
+
}
|
|
3314
|
+
if (options.ts) {
|
|
3315
|
+
result.configTemplate = "typescript";
|
|
3316
|
+
} else if (options.js) {
|
|
3317
|
+
result.configTemplate = "javascript";
|
|
3318
|
+
}
|
|
3319
|
+
return result;
|
|
3320
|
+
}
|
|
3321
|
+
async function InitCommand(args = {}) {
|
|
3322
|
+
render6(_jsx10(InitApp, { options: args }));
|
|
3323
|
+
}
|
|
3324
|
+
var init_init = __esm({
|
|
3325
|
+
"dist/commands/init.js"() {
|
|
3326
|
+
"use strict";
|
|
3327
|
+
init_InitForm();
|
|
3328
|
+
init_Spinner();
|
|
3329
|
+
init_auth();
|
|
3330
|
+
init_config_templates();
|
|
3331
|
+
init_platform();
|
|
3332
|
+
init_constants();
|
|
3333
|
+
}
|
|
3334
|
+
});
|
|
3335
|
+
|
|
3336
|
+
// dist/index.js
|
|
3337
|
+
init_constants();
|
|
3338
|
+
init_version();
|
|
3339
|
+
init_errors();
|
|
3340
|
+
init_platform();
|
|
3341
|
+
import { program } from "commander";
|
|
3342
|
+
process.on("uncaughtException", (error) => {
|
|
3343
|
+
console.error("Fatal error:", error.message);
|
|
3344
|
+
process.exit(1);
|
|
3345
|
+
});
|
|
3346
|
+
process.on("SIGINT", () => {
|
|
3347
|
+
console.log("\nOperation cancelled");
|
|
3348
|
+
process.exit(0);
|
|
3349
|
+
});
|
|
3350
|
+
program.name("basic").description("Basic CLI for creating & managing your projects").version(getVersion());
|
|
3351
|
+
program.command("login").description("Login to your Basic account").action(async () => {
|
|
3352
|
+
try {
|
|
3353
|
+
const { LoginCommand: LoginCommand2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
3354
|
+
await LoginCommand2();
|
|
3355
|
+
process.exit(0);
|
|
3356
|
+
} catch (error) {
|
|
3357
|
+
const cliError = handleError(error);
|
|
3358
|
+
console.error(formatError(cliError));
|
|
234
3359
|
process.exit(1);
|
|
3360
|
+
}
|
|
235
3361
|
});
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
3362
|
+
program.command("logout").description("Logout from your Basic account").action(async () => {
|
|
3363
|
+
try {
|
|
3364
|
+
const { LogoutCommand: LogoutCommand2 } = await Promise.resolve().then(() => (init_logout(), logout_exports));
|
|
3365
|
+
await LogoutCommand2();
|
|
3366
|
+
process.exit(0);
|
|
3367
|
+
} catch (error) {
|
|
3368
|
+
const cliError = handleError(error);
|
|
3369
|
+
console.error(formatError(cliError));
|
|
3370
|
+
process.exit(1);
|
|
3371
|
+
}
|
|
3372
|
+
});
|
|
3373
|
+
program.command("account").description("Show account information").action(async () => {
|
|
3374
|
+
try {
|
|
3375
|
+
const { AccountCommand: AccountCommand2 } = await Promise.resolve().then(() => (init_account(), account_exports));
|
|
3376
|
+
await AccountCommand2();
|
|
239
3377
|
process.exit(0);
|
|
3378
|
+
} catch (error) {
|
|
3379
|
+
const cliError = handleError(error);
|
|
3380
|
+
console.error(formatError(cliError));
|
|
3381
|
+
process.exit(1);
|
|
3382
|
+
}
|
|
3383
|
+
});
|
|
3384
|
+
program.command("version").description("Show CLI version").action(async () => {
|
|
3385
|
+
try {
|
|
3386
|
+
const { VersionCommand: VersionCommand2 } = await Promise.resolve().then(() => (init_version2(), version_exports));
|
|
3387
|
+
await VersionCommand2();
|
|
3388
|
+
process.exit(0);
|
|
3389
|
+
} catch (error) {
|
|
3390
|
+
const cliError = handleError(error);
|
|
3391
|
+
console.error(formatError(cliError));
|
|
3392
|
+
process.exit(1);
|
|
3393
|
+
}
|
|
3394
|
+
});
|
|
3395
|
+
program.command("update").description("Update CLI to the latest version").action(async () => {
|
|
3396
|
+
try {
|
|
3397
|
+
const { UpdateCommand: UpdateCommand2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
3398
|
+
await UpdateCommand2();
|
|
3399
|
+
process.exit(0);
|
|
3400
|
+
} catch (error) {
|
|
3401
|
+
const cliError = handleError(error);
|
|
3402
|
+
console.error(formatError(cliError));
|
|
3403
|
+
process.exit(1);
|
|
3404
|
+
}
|
|
3405
|
+
});
|
|
3406
|
+
program.command("help").description("Show help information").action(() => {
|
|
3407
|
+
program.help();
|
|
3408
|
+
});
|
|
3409
|
+
program.command("debug").description("Show Basic config directory location").action(async () => {
|
|
3410
|
+
try {
|
|
3411
|
+
const { DebugCommand: DebugCommand2 } = await Promise.resolve().then(() => (init_debug(), debug_exports));
|
|
3412
|
+
await DebugCommand2();
|
|
3413
|
+
process.exit(0);
|
|
3414
|
+
} catch (error) {
|
|
3415
|
+
const cliError = handleError(error);
|
|
3416
|
+
console.error(formatError(cliError));
|
|
3417
|
+
process.exit(1);
|
|
3418
|
+
}
|
|
3419
|
+
});
|
|
3420
|
+
program.command("projects").description("List and browse your projects").action(async () => {
|
|
3421
|
+
try {
|
|
3422
|
+
const { ProjectsCommand: ProjectsCommand2 } = await Promise.resolve().then(() => (init_projects(), projects_exports));
|
|
3423
|
+
await ProjectsCommand2();
|
|
3424
|
+
} catch (error) {
|
|
3425
|
+
const cliError = handleError(error);
|
|
3426
|
+
console.error(formatError(cliError));
|
|
3427
|
+
process.exit(1);
|
|
3428
|
+
}
|
|
3429
|
+
});
|
|
3430
|
+
program.command("teams").argument("[action]", "Teams action (new)", "list").description("List teams or create a new team").action(async (action) => {
|
|
3431
|
+
try {
|
|
3432
|
+
const { TeamsCommand: TeamsCommand2 } = await Promise.resolve().then(() => (init_teams(), teams_exports));
|
|
3433
|
+
await TeamsCommand2(action);
|
|
3434
|
+
} catch (error) {
|
|
3435
|
+
const cliError = handleError(error);
|
|
3436
|
+
console.error(formatError(cliError));
|
|
3437
|
+
process.exit(1);
|
|
3438
|
+
}
|
|
3439
|
+
});
|
|
3440
|
+
program.command("status").description("Show project status").action(async () => {
|
|
3441
|
+
try {
|
|
3442
|
+
const { StatusCommand: StatusCommand2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
3443
|
+
await StatusCommand2();
|
|
3444
|
+
} catch (error) {
|
|
3445
|
+
const cliError = handleError(error);
|
|
3446
|
+
console.error(formatError(cliError));
|
|
3447
|
+
process.exit(1);
|
|
3448
|
+
}
|
|
3449
|
+
});
|
|
3450
|
+
program.command("pull").description("Pull schema from remote").action(async () => {
|
|
3451
|
+
try {
|
|
3452
|
+
const { PullCommand: PullCommand2 } = await Promise.resolve().then(() => (init_pull(), pull_exports));
|
|
3453
|
+
await PullCommand2();
|
|
3454
|
+
} catch (error) {
|
|
3455
|
+
const cliError = handleError(error);
|
|
3456
|
+
console.error(formatError(cliError));
|
|
3457
|
+
process.exit(1);
|
|
3458
|
+
}
|
|
3459
|
+
});
|
|
3460
|
+
program.command("push").description("Push schema to remote").action(async () => {
|
|
3461
|
+
try {
|
|
3462
|
+
const { PushCommand: PushCommand2 } = await Promise.resolve().then(() => (init_push(), push_exports));
|
|
3463
|
+
await PushCommand2();
|
|
3464
|
+
} catch (error) {
|
|
3465
|
+
const cliError = handleError(error);
|
|
3466
|
+
console.error(formatError(cliError));
|
|
3467
|
+
process.exit(1);
|
|
3468
|
+
}
|
|
3469
|
+
});
|
|
3470
|
+
program.command("init").description("Create a new project or import an existing project").option("--new", "Create a new project (skip project type selection)").option("--existing", "Import an existing project").option("--name <name>", "Project name for new projects").option("--project <id>", "Project ID for existing projects").option("--ts", "Use TypeScript configuration template").option("--js", "Use JavaScript configuration template").action(async (options) => {
|
|
3471
|
+
try {
|
|
3472
|
+
const { InitCommand: InitCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
3473
|
+
await InitCommand2(options);
|
|
3474
|
+
} catch (error) {
|
|
3475
|
+
const cliError = handleError(error);
|
|
3476
|
+
console.error(formatError(cliError));
|
|
3477
|
+
process.exit(1);
|
|
3478
|
+
}
|
|
3479
|
+
});
|
|
3480
|
+
program.on("command:*", (operands) => {
|
|
3481
|
+
const unknownCommand = operands[0];
|
|
3482
|
+
const suggestions = findSimilarCommands(unknownCommand);
|
|
3483
|
+
console.error(`Unknown command: ${unknownCommand}
|
|
3484
|
+
`);
|
|
3485
|
+
if (suggestions.length > 0) {
|
|
3486
|
+
console.error("Did you mean:");
|
|
3487
|
+
suggestions.forEach((suggestion) => {
|
|
3488
|
+
console.error(` - ${suggestion}`);
|
|
3489
|
+
});
|
|
3490
|
+
console.error("");
|
|
3491
|
+
}
|
|
3492
|
+
console.error("Use 'basic help' to see all commands.");
|
|
3493
|
+
process.exit(1);
|
|
3494
|
+
});
|
|
3495
|
+
if (process.argv.length <= 2) {
|
|
3496
|
+
console.log(MESSAGES.WELCOME);
|
|
3497
|
+
process.exit(0);
|
|
240
3498
|
}
|
|
241
|
-
// Parse command line arguments
|
|
242
3499
|
program.parse();
|
|
243
|
-
//# sourceMappingURL=index.js.map
|