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