@basictech/cli 0.0.28 → 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.
Files changed (2) hide show
  1. package/dist/index.js +3471 -190
  2. package/package.json +9 -5
package/dist/index.js CHANGED
@@ -1,243 +1,3524 @@
1
1
  #!/usr/bin/env node
2
- import { program } from 'commander';
3
- import { MESSAGES } from './lib/constants.js';
4
- import { getVersion } from './lib/version.js';
5
- import { formatError, handleError } from './lib/errors.js';
6
- import { findSimilarCommands } from './lib/platform.js';
7
- // Set up global error handlers
8
- process.on('uncaughtException', (error) => {
9
- console.error('Fatal error:', error.message);
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
- process.on('SIGINT', () => {
13
- console.log('\nOperation cancelled');
14
- process.exit(0);
886
+
887
+ // dist/commands/debug.js
888
+ var debug_exports = {};
889
+ __export(debug_exports, {
890
+ DebugCommand: () => DebugCommand
15
891
  });
16
- // Configure the CLI
17
- program
18
- .name('basic')
19
- .description('Basic CLI for creating & managing your projects')
20
- .version(getVersion());
21
- // Login command
22
- program
23
- .command('login')
24
- .description('Login to your Basic account')
25
- .action(async () => {
26
- try {
27
- const { LoginCommand } = await import('./commands/login.js');
28
- await LoginCommand();
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
- catch (error) {
32
- const cliError = handleError(error);
33
- console.error(formatError(cliError));
34
- process.exit(1);
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
- // Logout command
38
- program
39
- .command('logout')
40
- .description('Logout from your Basic account')
41
- .action(async () => {
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
- const { LogoutCommand } = await import('./commands/logout.js');
44
- await LogoutCommand();
45
- process.exit(0);
1035
+ await copyToClipboard(row.id);
1036
+ } catch (error) {
46
1037
  }
47
- catch (error) {
48
- const cliError = handleError(error);
49
- console.error(formatError(cliError));
50
- process.exit(1);
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
- // Account command
54
- program
55
- .command('account')
56
- .description('Show account information')
57
- .action(async () => {
58
- try {
59
- const { AccountCommand } = await import('./commands/account.js');
60
- await AccountCommand();
61
- process.exit(0);
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
- catch (error) {
64
- const cliError = handleError(error);
65
- console.error(formatError(cliError));
66
- process.exit(1);
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
- // Version command (override default to show update info)
70
- program
71
- .command('version')
72
- .description('Show CLI version')
73
- .action(async () => {
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
- const { VersionCommand } = await import('./commands/version.js');
76
- await VersionCommand();
77
- process.exit(0);
1314
+ await copyToClipboard(row.id);
1315
+ } catch (error) {
78
1316
  }
79
- catch (error) {
80
- const cliError = handleError(error);
81
- console.error(formatError(cliError));
82
- process.exit(1);
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
- // Update command
86
- program
87
- .command('update')
88
- .description('Update CLI to the latest version')
89
- .action(async () => {
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
- const { UpdateCommand } = await import('./commands/update.js');
92
- await UpdateCommand();
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
- catch (error) {
96
- const cliError = handleError(error);
97
- console.error(formatError(cliError));
98
- process.exit(1);
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
- // Help command
102
- program
103
- .command('help')
104
- .description('Show help information')
105
- .action(() => {
106
- program.help();
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
- // Debug command
109
- program
110
- .command('debug')
111
- .description('Show Basic config directory location')
112
- .action(async () => {
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
- const { DebugCommand } = await import('./commands/debug.js');
115
- await DebugCommand();
116
- process.exit(0);
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
- catch (error) {
119
- const cliError = handleError(error);
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
- // Projects command
125
- program
126
- .command('projects')
127
- .description('List and browse your projects')
128
- .action(async () => {
129
- try {
130
- const { ProjectsCommand } = await import('./commands/projects.js');
131
- await ProjectsCommand();
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
- catch (error) {
134
- const cliError = handleError(error);
135
- console.error(formatError(cliError));
136
- process.exit(1);
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
- // Teams command
140
- program
141
- .command('teams')
142
- .argument('[action]', 'Teams action (new)', 'list')
143
- .description('List teams or create a new team')
144
- .action(async (action) => {
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
- const { TeamsCommand } = await import('./commands/teams.js');
147
- await TeamsCommand(action);
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
- catch (error) {
150
- const cliError = handleError(error);
151
- console.error(formatError(cliError));
152
- process.exit(1);
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
- // Status command
156
- program
157
- .command('status')
158
- .description('Show project status')
159
- .action(async () => {
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
- const { StatusCommand } = await import('./commands/status.js');
162
- await StatusCommand();
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
- catch (error) {
165
- const cliError = handleError(error);
166
- console.error(formatError(cliError));
167
- process.exit(1);
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
- // Pull command
171
- program
172
- .command('pull')
173
- .description('Pull schema from remote')
174
- .action(async () => {
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
- const { PullCommand } = await import('./commands/pull.js');
177
- await PullCommand();
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
- catch (error) {
180
- const cliError = handleError(error);
181
- console.error(formatError(cliError));
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
- // Push command
186
- program
187
- .command('push')
188
- .description('Push schema to remote')
189
- .action(async () => {
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
- const { PushCommand } = await import('./commands/push.js');
192
- await PushCommand();
193
- }
194
- catch (error) {
195
- const cliError = handleError(error);
196
- console.error(formatError(cliError));
197
- process.exit(1);
198
- }
199
- });
200
- // Init command
201
- program
202
- .command('init')
203
- .description('Create a new project or import an existing project')
204
- .option('--new', 'Create a new project (skip project type selection)')
205
- .option('--existing', 'Import an existing project')
206
- .option('--name <name>', 'Project name for new projects')
207
- .option('--project <id>', 'Project ID for existing projects')
208
- .option('--ts', 'Use TypeScript configuration template')
209
- .option('--js', 'Use JavaScript configuration template')
210
- .action(async (options) => {
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
- const { InitCommand } = await import('./commands/init.js');
213
- await InitCommand(options);
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
- catch (error) {
216
- const cliError = handleError(error);
217
- console.error(formatError(cliError));
218
- process.exit(1);
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
- // Handle unknown commands
222
- program.on('command:*', (operands) => {
223
- const unknownCommand = operands[0];
224
- const suggestions = findSimilarCommands(unknownCommand);
225
- console.error(`Unknown command: ${unknownCommand}\n`);
226
- if (suggestions.length > 0) {
227
- console.error('Did you mean:');
228
- suggestions.forEach(suggestion => {
229
- console.error(` - ${suggestion}`);
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
- console.error('');
3297
+ }
232
3298
  }
233
- console.error("Use 'basic help' to see all commands.");
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
- // Show welcome message if no command provided
237
- if (process.argv.length <= 2) {
238
- console.log(MESSAGES.WELCOME);
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
- // Parse command line arguments
242
- program.parse();
243
- //# sourceMappingURL=index.js.map
3524
+ import_commander.program.parse();