@takyonic/cli 1.0.2

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 (56) hide show
  1. package/README.md +37 -0
  2. package/dist/commands/generate.d.ts +3 -0
  3. package/dist/commands/generate.d.ts.map +1 -0
  4. package/dist/commands/generate.js +107 -0
  5. package/dist/commands/generate.js.map +1 -0
  6. package/dist/commands/init.d.ts +3 -0
  7. package/dist/commands/init.d.ts.map +1 -0
  8. package/dist/commands/init.js +98 -0
  9. package/dist/commands/init.js.map +1 -0
  10. package/dist/commands/inspect.d.ts +2 -0
  11. package/dist/commands/inspect.d.ts.map +1 -0
  12. package/dist/commands/inspect.js +77 -0
  13. package/dist/commands/inspect.js.map +1 -0
  14. package/dist/commands/pull.d.ts +3 -0
  15. package/dist/commands/pull.d.ts.map +1 -0
  16. package/dist/commands/pull.js +100 -0
  17. package/dist/commands/pull.js.map +1 -0
  18. package/dist/commands/push.d.ts +3 -0
  19. package/dist/commands/push.d.ts.map +1 -0
  20. package/dist/commands/push.js +135 -0
  21. package/dist/commands/push.js.map +1 -0
  22. package/dist/dsl-to-json.d.ts +28 -0
  23. package/dist/dsl-to-json.d.ts.map +1 -0
  24. package/dist/dsl-to-json.js +69 -0
  25. package/dist/dsl-to-json.js.map +1 -0
  26. package/dist/generator.d.ts +19 -0
  27. package/dist/generator.d.ts.map +1 -0
  28. package/dist/generator.js +161 -0
  29. package/dist/generator.js.map +1 -0
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +30 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/parser.d.ts +36 -0
  35. package/dist/parser.d.ts.map +1 -0
  36. package/dist/parser.js +234 -0
  37. package/dist/parser.js.map +1 -0
  38. package/dist/type-mapper.d.ts +17 -0
  39. package/dist/type-mapper.d.ts.map +1 -0
  40. package/dist/type-mapper.js +90 -0
  41. package/dist/type-mapper.js.map +1 -0
  42. package/generated/index.ts +89 -0
  43. package/package.json +33 -0
  44. package/src/commands/generate.ts +78 -0
  45. package/src/commands/init.ts +63 -0
  46. package/src/commands/inspect.ts +98 -0
  47. package/src/commands/pull.ts +68 -0
  48. package/src/commands/push.ts +106 -0
  49. package/src/dsl-to-json.ts +99 -0
  50. package/src/generator.ts +178 -0
  51. package/src/index.ts +32 -0
  52. package/src/parser.ts +285 -0
  53. package/src/test-bulk.ts +38 -0
  54. package/src/test-load.ts +28 -0
  55. package/src/type-mapper.ts +90 -0
  56. package/tsconfig.json +21 -0
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ /**
3
+ * Type Mapping Utilities
4
+ * Maps between Takyonic DSL types and PostgreSQL types
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.dslTypeToPostgres = dslTypeToPostgres;
8
+ exports.postgresTypeToDsl = postgresTypeToDsl;
9
+ exports.isNullableType = isNullableType;
10
+ /**
11
+ * Map Takyonic DSL type to PostgreSQL type
12
+ */
13
+ function dslTypeToPostgres(dslType) {
14
+ const normalized = dslType.toLowerCase().trim();
15
+ switch (normalized) {
16
+ case 'string':
17
+ return 'text';
18
+ case 'int':
19
+ case 'integer':
20
+ return 'integer';
21
+ case 'float':
22
+ case 'double':
23
+ return 'double precision';
24
+ case 'json':
25
+ return 'jsonb';
26
+ case 'bool':
27
+ case 'boolean':
28
+ return 'boolean';
29
+ case 'timestamp':
30
+ return 'timestamp';
31
+ case 'date':
32
+ return 'date';
33
+ case 'uuid':
34
+ return 'uuid';
35
+ default:
36
+ // Return as-is for unknown types (might be custom PostgreSQL types)
37
+ return normalized;
38
+ }
39
+ }
40
+ /**
41
+ * Map PostgreSQL type to Takyonic DSL type
42
+ */
43
+ function postgresTypeToDsl(postgresType) {
44
+ const normalized = postgresType.toLowerCase().trim();
45
+ switch (normalized) {
46
+ case 'text':
47
+ case 'varchar':
48
+ case 'character varying':
49
+ case 'char':
50
+ return 'string';
51
+ case 'integer':
52
+ case 'int':
53
+ case 'int4':
54
+ return 'int';
55
+ case 'bigint':
56
+ case 'int8':
57
+ return 'int';
58
+ case 'double precision':
59
+ case 'real':
60
+ case 'float':
61
+ return 'float';
62
+ case 'jsonb':
63
+ case 'json':
64
+ return 'json';
65
+ case 'boolean':
66
+ case 'bool':
67
+ return 'bool';
68
+ case 'timestamp':
69
+ case 'timestamp without time zone':
70
+ case 'timestamp with time zone':
71
+ return 'timestamp';
72
+ case 'date':
73
+ return 'date';
74
+ case 'uuid':
75
+ return 'uuid';
76
+ case 'numeric':
77
+ case 'decimal':
78
+ return 'float';
79
+ default:
80
+ // Return as-is for unknown types
81
+ return normalized;
82
+ }
83
+ }
84
+ /**
85
+ * Check if a type is nullable based on PostgreSQL type information
86
+ */
87
+ function isNullableType(postgresType, isNullable) {
88
+ return isNullable;
89
+ }
90
+ //# sourceMappingURL=type-mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-mapper.js","sourceRoot":"","sources":["../src/type-mapper.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAKH,8CA2BC;AAKD,8CAyCC;AAKD,wCAEC;AAnFD;;GAEG;AACH,SAAgB,iBAAiB,CAAC,OAAe;IAC/C,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAEhD,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC;QAChB,KAAK,KAAK,CAAC;QACX,KAAK,SAAS;YACZ,OAAO,SAAS,CAAC;QACnB,KAAK,OAAO,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,kBAAkB,CAAC;QAC5B,KAAK,MAAM;YACT,OAAO,OAAO,CAAC;QACjB,KAAK,MAAM,CAAC;QACZ,KAAK,SAAS;YACZ,OAAO,SAAS,CAAC;QACnB,KAAK,WAAW;YACd,OAAO,WAAW,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB;YACE,oEAAoE;YACpE,OAAO,UAAU,CAAC;IACtB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAAC,YAAoB;IACpD,MAAM,UAAU,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAErD,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,MAAM,CAAC;QACZ,KAAK,SAAS,CAAC;QACf,KAAK,mBAAmB,CAAC;QACzB,KAAK,MAAM;YACT,OAAO,QAAQ,CAAC;QAClB,KAAK,SAAS,CAAC;QACf,KAAK,KAAK,CAAC;QACX,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,QAAQ,CAAC;QACd,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,kBAAkB,CAAC;QACxB,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO;YACV,OAAO,OAAO,CAAC;QACjB,KAAK,OAAO,CAAC;QACb,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,SAAS,CAAC;QACf,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,WAAW,CAAC;QACjB,KAAK,6BAA6B,CAAC;QACnC,KAAK,0BAA0B;YAC7B,OAAO,WAAW,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,SAAS,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,OAAO,CAAC;QACjB;YACE,iCAAiC;YACjC,OAAO,UAAU,CAAC;IACtB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAAC,YAAoB,EAAE,UAAmB;IACtE,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Auto-generated by Takyonic CLI
3
+ *
4
+ * This file contains TypeScript interfaces for your database schema.
5
+ * Use these types with the @takyonic/sdk package for type-safe queries.
6
+ *
7
+ * Do not edit this file manually - regenerate with: takyonic-cli generate
8
+ */
9
+
10
+ import type { TakyonicClient } from '@takyonic/sdk';
11
+
12
+ // =============================================================================
13
+ // Table Interfaces
14
+ // =============================================================================
15
+
16
+ export interface Products {
17
+ id: string;
18
+ data: Record<string, unknown>;
19
+ }
20
+
21
+ export interface Users {
22
+ id: string;
23
+ data: Record<string, unknown>;
24
+ created_at: Date;
25
+ updated_at: Date;
26
+ }
27
+
28
+ export interface Logs {
29
+ id: string;
30
+ event: string;
31
+ payload: Record<string, unknown>;
32
+ severity: number;
33
+ status: string;
34
+ }
35
+
36
+ // =============================================================================
37
+ // Type Utilities
38
+ // =============================================================================
39
+
40
+ /**
41
+ * Union type of all table names in the schema
42
+ */
43
+ export type TableName = 'products' | 'users' | 'logs';
44
+
45
+ /**
46
+ * Mapping from table names to their interface types
47
+ */
48
+ export interface TableTypes {
49
+ products: Products;
50
+ users: Users;
51
+ logs: Logs;
52
+ }
53
+
54
+ /**
55
+ * Helper type to get the interface type for a table name
56
+ */
57
+ export type TableType<T extends TableName> = TableTypes[T];
58
+
59
+ // =============================================================================
60
+ // Usage Examples
61
+ // =============================================================================
62
+
63
+ /**
64
+ * Example usage with @takyonic/sdk:
65
+ *
66
+ * ```typescript
67
+ * import { TakyonicClient } from '@takyonic/sdk';
68
+ * import type { Products } from './generated';
69
+ *
70
+ * const db = new TakyonicClient({
71
+ * endpoint: 'http://localhost:8080',
72
+ * token: process.env.TAKYONIC_API_KEY!
73
+ * });
74
+ *
75
+ * // Type-safe queries
76
+ * const records = await db.table<Products>('products').get();
77
+ *
78
+ * // Type-safe inserts
79
+ * await db.table<Products>('products').insert({
80
+ * id: 'new-id',
81
+ * // ... other fields
82
+ * });
83
+ *
84
+ * // Type-safe search with filters
85
+ * const filtered = await db.table<Products>('products')
86
+ * .where('id', '=', 'some-id')
87
+ * .get();
88
+ * ```
89
+ */
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@takyonic/cli",
3
+ "version": "1.0.2",
4
+ "description": "Takyonic CLI - Developer tools for Takyonic Smart Cache Proxy",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "takyonic": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "start": "ts-node src/index.ts",
12
+ "dev": "ts-node src/index.ts"
13
+ },
14
+ "keywords": [
15
+ "takyonic",
16
+ "postgresql",
17
+ "cache",
18
+ "cli"
19
+ ],
20
+ "author": "",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "@takyonic/sdk": "^1.0.2",
24
+ "chalk": "^4.1.2",
25
+ "commander": "^11.1.0",
26
+ "dotenv": "^16.3.1"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^20.10.0",
30
+ "ts-node": "^10.9.2",
31
+ "typescript": "^5.3.3"
32
+ }
33
+ }
@@ -0,0 +1,78 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import { generateTypeScript } from '../generator';
6
+ import { parseTakyonicSchema, ParseError } from '../parser';
7
+ import { dslToJson, TableSchema } from '../dsl-to-json';
8
+
9
+ export const generateCommand = new Command('generate')
10
+ .description('Generate TypeScript client code from .takyonic schema file')
11
+ .action(async () => {
12
+ // Determine input file (.takyonic in project root)
13
+ const inputFile = path.join(process.cwd(), '.takyonic');
14
+
15
+ if (!fs.existsSync(inputFile)) {
16
+ console.error(chalk.red(`Error: ${inputFile} not found`));
17
+ console.error(chalk.yellow('Please run "takyonic init" to create a .takyonic file'));
18
+ console.error(chalk.yellow('Or run "takyonic pull" to fetch schema from server'));
19
+ process.exit(1);
20
+ }
21
+
22
+ try {
23
+ console.log(chalk.blue('⚡ Takyonic: Reading schema from .takyonic file...\n'));
24
+
25
+ // Read and parse DSL file
26
+ const fileContent = fs.readFileSync(inputFile, 'utf-8');
27
+ const dslSchema = parseTakyonicSchema(fileContent);
28
+
29
+ if (!dslSchema.tables || dslSchema.tables.length === 0) {
30
+ console.log(chalk.yellow('No tables found in schema'));
31
+ console.log(chalk.yellow('Cannot generate code without tables.'));
32
+ process.exit(1);
33
+ }
34
+
35
+ // Convert DSL to JSON format for generator
36
+ const schema = dslToJson(dslSchema);
37
+
38
+ console.log(chalk.blue('⚡ Takyonic: Generating TypeScript code...\n'));
39
+
40
+ // Generate TypeScript code
41
+ const generatedCode = generateTypeScript(schema);
42
+
43
+ // Determine output directory (cli/generated)
44
+ const outputDir = path.join(process.cwd(), 'generated');
45
+ const outputFile = path.join(outputDir, 'index.ts');
46
+
47
+ // Create directory if it doesn't exist
48
+ if (!fs.existsSync(outputDir)) {
49
+ fs.mkdirSync(outputDir, { recursive: true });
50
+ console.log(chalk.green(`Created directory: ${outputDir}`));
51
+ }
52
+
53
+ // Write file
54
+ fs.writeFileSync(outputFile, generatedCode, 'utf-8');
55
+
56
+ console.log(chalk.green(`✓ Generated TypeScript client code`));
57
+ console.log(chalk.gray(` Output: ${outputFile}`));
58
+ console.log(chalk.gray(` Tables: ${schema.length}`));
59
+ const totalColumns = schema.reduce((sum, table) => sum + table.columns.length, 0);
60
+ console.log(chalk.gray(` Columns: ${totalColumns}\n`));
61
+
62
+ } catch (error) {
63
+ console.error(chalk.red('⚡ Takyonic: Error generating code:'));
64
+ if (error instanceof ParseError) {
65
+ console.error(chalk.red(`Parse error: ${error.message}`));
66
+ if (error.line) {
67
+ console.error(chalk.yellow(` Line: ${error.line}`));
68
+ }
69
+ console.error(chalk.yellow('Please check your .takyonic file syntax'));
70
+ } else if (error instanceof Error) {
71
+ console.error(chalk.red(error.message));
72
+ } else {
73
+ console.error(chalk.red(String(error)));
74
+ }
75
+ process.exit(1);
76
+ }
77
+ });
78
+
@@ -0,0 +1,63 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+
6
+ const DEFAULT_TAKYONIC_FILE = `// Takyonic Core Configuration
7
+ engine {
8
+ provider = "takyonic"
9
+ db = env("DATABASE_URL")
10
+ cache = "in-memory"
11
+ port = 8080
12
+ }
13
+
14
+ // Takyonic Data Definitions
15
+ table products {
16
+ id string @primary
17
+ name string
18
+ price int @default(0)
19
+ data json
20
+ }
21
+
22
+ table orders {
23
+ id string @primary
24
+ amount float
25
+ status string @default("pending")
26
+ }
27
+ `;
28
+
29
+ export const initCommand = new Command('init')
30
+ .description('Initialize a new Takyonic project with a .takyonic schema file')
31
+ .action(async () => {
32
+ const takyonicFile = path.join(process.cwd(), '.takyonic');
33
+
34
+ // Check if .takyonic file already exists
35
+ if (fs.existsSync(takyonicFile)) {
36
+ console.error(chalk.red('Error: .takyonic file already exists'));
37
+ console.error(chalk.yellow(` File: ${takyonicFile}`));
38
+ console.error(chalk.yellow(' Remove it first or use a different directory\n'));
39
+ process.exit(1);
40
+ }
41
+
42
+ try {
43
+ // Write default .takyonic file
44
+ fs.writeFileSync(takyonicFile, DEFAULT_TAKYONIC_FILE, 'utf-8');
45
+
46
+ console.log(chalk.green('✓ Takyonic project initialized'));
47
+ console.log(chalk.gray(` Created: ${takyonicFile}\n`));
48
+ console.log(chalk.blue('⚡ Next steps:'));
49
+ console.log(chalk.gray(' 1. Edit .takyonic to define your tables'));
50
+ console.log(chalk.gray(' 2. Run "takyonic push" to sync schema to server'));
51
+ console.log(chalk.gray(' 3. Run "takyonic generate" to create TypeScript client\n'));
52
+
53
+ } catch (error) {
54
+ console.error(chalk.red('⚡ Takyonic: Error initializing project:'));
55
+ if (error instanceof Error) {
56
+ console.error(chalk.red(error.message));
57
+ } else {
58
+ console.error(chalk.red(String(error)));
59
+ }
60
+ process.exit(1);
61
+ }
62
+ });
63
+
@@ -0,0 +1,98 @@
1
+ import chalk from 'chalk';
2
+
3
+ interface ColumnSchema {
4
+ name: string;
5
+ data_type: string;
6
+ nullable: boolean;
7
+ }
8
+
9
+ interface TableSchema {
10
+ name: string;
11
+ columns: ColumnSchema[];
12
+ }
13
+
14
+ export async function inspectCommand() {
15
+ const takyonicUrl = process.env.TAKYONIC_URL || 'http://localhost:8080';
16
+ const apiKey = process.env.TAKYONIC_API_KEY;
17
+
18
+ if (!apiKey) {
19
+ console.error(chalk.red('Error: TAKYONIC_API_KEY environment variable is not set'));
20
+ console.error(chalk.yellow('Please set TAKYONIC_API_KEY to your API key'));
21
+ process.exit(1);
22
+ }
23
+
24
+ try {
25
+ console.log(chalk.blue('⚡ Takyonic: Fetching schema from server...\n'));
26
+
27
+ const response = await fetch(`${takyonicUrl}/v1/schema`, {
28
+ headers: {
29
+ 'Authorization': `Bearer ${apiKey}`,
30
+ },
31
+ });
32
+
33
+ if (!response.ok) {
34
+ if (response.status === 404) {
35
+ console.error(chalk.red('Error: Server endpoint not found'));
36
+ console.error(chalk.yellow('Please ensure Takyonic server is running and accessible'));
37
+ process.exit(1);
38
+ }
39
+ const errorText = await response.text();
40
+ console.error(chalk.red(`Error: Server returned status ${response.status}`));
41
+ console.error(chalk.red(errorText));
42
+ process.exit(1);
43
+ }
44
+
45
+ const schema = (await response.json()) as TableSchema[];
46
+
47
+ if (schema.length === 0) {
48
+ console.log(chalk.yellow('No tables found in the database'));
49
+ return;
50
+ }
51
+
52
+ // Display schema
53
+ console.log(chalk.bold.cyan('Database Schema:\n'));
54
+
55
+ for (const table of schema) {
56
+ console.log(chalk.bold.magenta(`Table: ${table.name}`));
57
+ console.log(chalk.gray('─'.repeat(80)));
58
+
59
+ // Table header
60
+ console.log(
61
+ chalk.bold(
62
+ `${'Column Name'.padEnd(30)} ${'Data Type'.padEnd(20)} ${'Nullable'.padEnd(10)}`
63
+ )
64
+ );
65
+ console.log(chalk.gray('─'.repeat(80)));
66
+
67
+ // Columns
68
+ for (const col of table.columns) {
69
+ const nullable = col.nullable
70
+ ? chalk.yellow('YES')
71
+ : chalk.green('NO');
72
+ console.log(
73
+ `${col.name.padEnd(30)} ${col.data_type.padEnd(20)} ${nullable}`
74
+ );
75
+ }
76
+
77
+ console.log('');
78
+ }
79
+
80
+ const totalColumns = schema.reduce((sum, table) => sum + table.columns.length, 0);
81
+ console.log(chalk.gray(`Total: ${schema.length} table(s), ${totalColumns} column(s)\n`));
82
+
83
+ } catch (error) {
84
+ console.error(chalk.red('⚡ Takyonic: Error fetching schema:'));
85
+ if (error instanceof Error) {
86
+ if (error.message.includes('fetch')) {
87
+ console.error(chalk.red('Failed to connect to Takyonic server'));
88
+ console.error(chalk.yellow(`Please ensure the server is running at ${takyonicUrl}`));
89
+ console.error(chalk.yellow('You can set TAKYONIC_URL environment variable to specify a different URL'));
90
+ } else {
91
+ console.error(chalk.red(error.message));
92
+ }
93
+ } else {
94
+ console.error(chalk.red(String(error)));
95
+ }
96
+ process.exit(1);
97
+ }
98
+ }
@@ -0,0 +1,68 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import { TakyonicClient, AuthenticationError, NetworkError } from '@takyonic/sdk';
6
+
7
+ export const pullCommand = new Command('pull')
8
+ .description('Pull schema from Takyonic server and save to .takyonic file')
9
+ .action(async () => {
10
+ const takyonicUrl = process.env.TAKYONIC_URL || 'http://localhost:8080';
11
+ const apiKey = process.env.TAKYONIC_API_KEY;
12
+
13
+ if (!apiKey) {
14
+ console.error(chalk.red('Error: TAKYONIC_API_KEY environment variable is not set'));
15
+ console.error(chalk.yellow('Please set TAKYONIC_API_KEY to your API key'));
16
+ process.exit(1);
17
+ }
18
+
19
+ try {
20
+ console.log(chalk.blue('⚡ Takyonic: Pulling schema from server...\n'));
21
+
22
+ // Use SDK to fetch pre-formatted DSL from server
23
+ const client = new TakyonicClient({
24
+ endpoint: takyonicUrl,
25
+ token: apiKey,
26
+ });
27
+
28
+ // Fetch the DSL string directly from the Rust backend
29
+ // This is the authoritative, pre-formatted schema
30
+ const dslContent = await client.schemaDsl();
31
+
32
+ if (!dslContent || dslContent.trim().length === 0) {
33
+ console.log(chalk.yellow('No schema found on the server'));
34
+ console.log(chalk.yellow('Cannot save empty schema.'));
35
+ process.exit(1);
36
+ }
37
+
38
+ // Determine output file (.takyonic in project root)
39
+ const outputFile = path.join(process.cwd(), '.takyonic');
40
+
41
+ // Write the pre-formatted DSL directly to file
42
+ fs.writeFileSync(outputFile, dslContent, 'utf-8');
43
+
44
+ // Count tables for display
45
+ const tableCount = (dslContent.match(/^table\s+\w+\s*\{/gm) || []).length;
46
+
47
+ console.log(chalk.green(`✓ Schema pulled successfully`));
48
+ console.log(chalk.gray(` Output: ${outputFile}`));
49
+ console.log(chalk.gray(` Tables: ${tableCount}\n`));
50
+
51
+ } catch (error) {
52
+ console.error(chalk.red('⚡ Takyonic: Error pulling schema:'));
53
+
54
+ if (error instanceof AuthenticationError) {
55
+ console.error(chalk.red('Error: Unauthorized'));
56
+ console.error(chalk.yellow('Please check your TAKYONIC_API_KEY'));
57
+ } else if (error instanceof NetworkError) {
58
+ console.error(chalk.red('Failed to connect to Takyonic server'));
59
+ console.error(chalk.yellow(`Please ensure the server is running at ${takyonicUrl}`));
60
+ console.error(chalk.yellow('You can set TAKYONIC_URL environment variable to specify a different URL'));
61
+ } else if (error instanceof Error) {
62
+ console.error(chalk.red(error.message));
63
+ } else {
64
+ console.error(chalk.red(String(error)));
65
+ }
66
+ process.exit(1);
67
+ }
68
+ });
@@ -0,0 +1,106 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import { AdminClient, AuthenticationError, ValidationError, NetworkError } from '@takyonic/sdk';
6
+ import { parseTakyonicSchema, ParseError } from '../parser';
7
+ import { dslToJson } from '../dsl-to-json';
8
+
9
+ export const pushCommand = new Command('push')
10
+ .description('Push schema from .takyonic file to Takyonic server')
11
+ .action(async () => {
12
+ const takyonicUrl = process.env.TAKYONIC_URL || 'http://localhost:8080';
13
+ const adminSecret = process.env.TAKYONIC_ADMIN_SECRET;
14
+
15
+ if (!adminSecret) {
16
+ console.error(chalk.red('Error: TAKYONIC_ADMIN_SECRET environment variable is not set'));
17
+ console.error(chalk.yellow('Please set TAKYONIC_ADMIN_SECRET to your admin secret'));
18
+ process.exit(1);
19
+ }
20
+
21
+ // Determine input file (.takyonic in project root)
22
+ const inputFile = path.join(process.cwd(), '.takyonic');
23
+
24
+ if (!fs.existsSync(inputFile)) {
25
+ console.error(chalk.red(`Error: ${inputFile} not found`));
26
+ console.error(chalk.yellow('Please run "takyonic init" to create a .takyonic file'));
27
+ console.error(chalk.yellow('Or run "takyonic pull" to fetch schema from server'));
28
+ process.exit(1);
29
+ }
30
+
31
+ try {
32
+ console.log(chalk.blue('⚡ Takyonic: Reading schema from .takyonic file...\n'));
33
+
34
+ // Read and parse DSL file
35
+ const fileContent = fs.readFileSync(inputFile, 'utf-8');
36
+ const dslSchema = parseTakyonicSchema(fileContent);
37
+
38
+ if (!dslSchema.tables || dslSchema.tables.length === 0) {
39
+ console.error(chalk.red('Error: No tables found in schema'));
40
+ console.error(chalk.yellow('Please define at least one table in your .takyonic file'));
41
+ process.exit(1);
42
+ }
43
+
44
+ // Convert DSL to JSON format for server
45
+ const schema = dslToJson(dslSchema);
46
+
47
+ console.log(chalk.blue('⚡ Takyonic Engine: Synchronizing schema...\n'));
48
+ console.log(chalk.gray(` Tables: ${schema.length}\n`));
49
+
50
+ // Use AdminClient from SDK to push schema
51
+ const admin = new AdminClient({
52
+ endpoint: takyonicUrl,
53
+ adminSecret: adminSecret,
54
+ });
55
+
56
+ const result = await admin.migrate(schema);
57
+
58
+ console.log(chalk.green(`✓ Migration completed successfully`));
59
+ console.log(chalk.gray(` Server: ${takyonicUrl}`));
60
+
61
+ if (result.tables_created && result.tables_created.length > 0) {
62
+ console.log(chalk.green(` Tables created: ${result.tables_created.join(', ')}`));
63
+ }
64
+
65
+ if (result.columns_added && Object.keys(result.columns_added).length > 0) {
66
+ for (const [table, columns] of Object.entries(result.columns_added)) {
67
+ console.log(chalk.yellow(` Columns added to ${table}: ${columns.join(', ')}`));
68
+ }
69
+ }
70
+
71
+ if (result.tables_updated && result.tables_updated.length > 0) {
72
+ console.log(chalk.gray(` Tables updated: ${result.tables_updated.join(', ')}`));
73
+ }
74
+
75
+ if (result.message) {
76
+ console.log(chalk.gray(` ${result.message}\n`));
77
+ } else {
78
+ console.log(); // New line
79
+ }
80
+
81
+ } catch (error) {
82
+ console.error(chalk.red('⚡ Takyonic: Error pushing schema:'));
83
+
84
+ if (error instanceof ParseError) {
85
+ console.error(chalk.red(`Parse error: ${error.message}`));
86
+ if (error.line) {
87
+ console.error(chalk.yellow(` Line: ${error.line}`));
88
+ }
89
+ console.error(chalk.yellow('Please check your .takyonic file syntax'));
90
+ } else if (error instanceof AuthenticationError) {
91
+ console.error(chalk.red('Error: Unauthorized'));
92
+ console.error(chalk.yellow('Please check your TAKYONIC_ADMIN_SECRET'));
93
+ } else if (error instanceof ValidationError) {
94
+ console.error(chalk.red(`Validation error: ${error.message}`));
95
+ } else if (error instanceof NetworkError) {
96
+ console.error(chalk.red('Failed to connect to Takyonic server'));
97
+ console.error(chalk.yellow(`Please ensure the server is running at ${takyonicUrl}`));
98
+ console.error(chalk.yellow('You can set TAKYONIC_URL environment variable to specify a different URL'));
99
+ } else if (error instanceof Error) {
100
+ console.error(chalk.red(error.message));
101
+ } else {
102
+ console.error(chalk.red(String(error)));
103
+ }
104
+ process.exit(1);
105
+ }
106
+ });