@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,99 @@
1
+ /**
2
+ * DSL to JSON Converter
3
+ * Converts parsed Takyonic DSL schema to JSON format expected by server
4
+ */
5
+
6
+ import { TakyonicSchema, TableDefinition } from './parser';
7
+ import { dslTypeToPostgres } from './type-mapper';
8
+
9
+ export interface ColumnSchema {
10
+ name: string;
11
+ data_type: string;
12
+ nullable: boolean;
13
+ default_value?: string;
14
+ }
15
+
16
+ export interface IndexInfo {
17
+ name: string;
18
+ columns: string[];
19
+ unique: boolean;
20
+ }
21
+
22
+ export interface TableSchema {
23
+ name: string;
24
+ columns: ColumnSchema[];
25
+ indexes?: IndexInfo[];
26
+ foreign_keys?: any[];
27
+ cache_ttl?: number;
28
+ }
29
+
30
+ /**
31
+ * Convert Takyonic DSL schema to server JSON format
32
+ */
33
+ export function dslToJson(schema: TakyonicSchema): TableSchema[] {
34
+ return schema.tables.map(table => convertTable(table));
35
+ }
36
+
37
+ /**
38
+ * Convert a single table definition to server format
39
+ */
40
+ function convertTable(table: TableDefinition): TableSchema {
41
+ const columns: ColumnSchema[] = [];
42
+ const indexes: IndexInfo[] = [];
43
+ const primaryKeyColumns: string[] = [];
44
+
45
+ // Convert fields to columns
46
+ for (const field of table.fields) {
47
+ const postgresType = dslTypeToPostgres(field.type);
48
+
49
+ // Check if field is nullable (default to false, unless explicitly marked)
50
+ // For now, we'll assume all fields are non-nullable unless we add nullable decorator
51
+ const nullable = false;
52
+
53
+ columns.push({
54
+ name: field.name,
55
+ data_type: postgresType,
56
+ nullable,
57
+ default_value: field.decorators.default,
58
+ });
59
+
60
+ // Track primary key fields
61
+ if (field.decorators.primary) {
62
+ primaryKeyColumns.push(field.name);
63
+ }
64
+ }
65
+
66
+ // Create primary key index if we have primary key fields
67
+ if (primaryKeyColumns.length > 0) {
68
+ indexes.push({
69
+ name: `${table.name}_pkey`,
70
+ columns: primaryKeyColumns,
71
+ unique: true,
72
+ });
73
+ }
74
+
75
+ // Always ensure we have an 'id' column for Takyonic (if not present, add it)
76
+ const hasIdColumn = columns.some(col => col.name === 'id');
77
+ if (!hasIdColumn && primaryKeyColumns.length === 0) {
78
+ // Add id column if no primary key is defined
79
+ columns.unshift({
80
+ name: 'id',
81
+ data_type: 'text',
82
+ nullable: false,
83
+ });
84
+
85
+ indexes.push({
86
+ name: `${table.name}_pkey`,
87
+ columns: ['id'],
88
+ unique: true,
89
+ });
90
+ }
91
+
92
+ return {
93
+ name: table.name,
94
+ columns,
95
+ indexes: indexes.length > 0 ? indexes : undefined,
96
+ foreign_keys: [],
97
+ };
98
+ }
99
+
@@ -0,0 +1,178 @@
1
+ interface ColumnSchema {
2
+ name: string;
3
+ data_type: string;
4
+ nullable: boolean;
5
+ }
6
+
7
+ interface TableSchema {
8
+ name: string;
9
+ columns: ColumnSchema[];
10
+ }
11
+
12
+ /**
13
+ * Maps PostgreSQL data types to TypeScript types
14
+ */
15
+ function mapPostgresToTypeScript(dataType: string): string {
16
+ const normalized = dataType.toLowerCase();
17
+
18
+ switch (normalized) {
19
+ case 'text':
20
+ case 'varchar':
21
+ case 'character varying':
22
+ case 'char':
23
+ case 'uuid':
24
+ return 'string';
25
+ case 'integer':
26
+ case 'int':
27
+ case 'int4':
28
+ case 'bigint':
29
+ case 'int8':
30
+ case 'numeric':
31
+ case 'decimal':
32
+ case 'double precision':
33
+ case 'real':
34
+ case 'float':
35
+ case 'smallint':
36
+ case 'int2':
37
+ return 'number';
38
+ case 'boolean':
39
+ case 'bool':
40
+ return 'boolean';
41
+ case 'jsonb':
42
+ case 'json':
43
+ return 'Record<string, unknown>';
44
+ case 'timestamp':
45
+ case 'timestamp without time zone':
46
+ case 'timestamp with time zone':
47
+ case 'date':
48
+ case 'time':
49
+ case 'time without time zone':
50
+ case 'time with time zone':
51
+ return 'Date';
52
+ default:
53
+ return 'unknown';
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Converts table name to PascalCase for interface names
59
+ */
60
+ function toPascalCase(str: string): string {
61
+ return str
62
+ .split('_')
63
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
64
+ .join('');
65
+ }
66
+
67
+ /**
68
+ * Generates TypeScript interface for a table
69
+ */
70
+ function generateInterface(table: TableSchema): string {
71
+ const interfaceName = toPascalCase(table.name);
72
+ const columns = table.columns
73
+ .map(col => {
74
+ const tsType = mapPostgresToTypeScript(col.data_type);
75
+ const optional = col.nullable ? '?' : '';
76
+ return ` ${col.name}${optional}: ${tsType};`;
77
+ })
78
+ .join('\n');
79
+
80
+ return `export interface ${interfaceName} {\n${columns}\n}`;
81
+ }
82
+
83
+ /**
84
+ * Generates TypeScript code from schema
85
+ *
86
+ * This generates type definitions that work with the @takyonic/sdk package.
87
+ * Instead of generating a full client, it produces interfaces that can be
88
+ * used with the SDK's generic table methods.
89
+ */
90
+ export function generateTypeScript(schema: TableSchema[]): string {
91
+ const interfaces = schema.map(generateInterface).join('\n\n');
92
+
93
+ // Generate table name type union
94
+ const tableNames = schema.map(t => `'${t.name}'`).join(' | ');
95
+
96
+ // Generate table type mapping
97
+ const tableTypeMap = schema
98
+ .map(t => ` ${t.name}: ${toPascalCase(t.name)};`)
99
+ .join('\n');
100
+
101
+ // Generate usage examples
102
+ const exampleTable = schema[0];
103
+ const exampleType = exampleTable ? toPascalCase(exampleTable.name) : 'YourTable';
104
+ const exampleName = exampleTable ? exampleTable.name : 'your_table';
105
+
106
+ const code = `/**
107
+ * Auto-generated by Takyonic CLI
108
+ *
109
+ * This file contains TypeScript interfaces for your database schema.
110
+ * Use these types with the @takyonic/sdk package for type-safe queries.
111
+ *
112
+ * Do not edit this file manually - regenerate with: takyonic-cli generate
113
+ */
114
+
115
+ import type { TakyonicClient } from '@takyonic/sdk';
116
+
117
+ // =============================================================================
118
+ // Table Interfaces
119
+ // =============================================================================
120
+
121
+ ${interfaces}
122
+
123
+ // =============================================================================
124
+ // Type Utilities
125
+ // =============================================================================
126
+
127
+ /**
128
+ * Union type of all table names in the schema
129
+ */
130
+ export type TableName = ${tableNames || 'never'};
131
+
132
+ /**
133
+ * Mapping from table names to their interface types
134
+ */
135
+ export interface TableTypes {
136
+ ${tableTypeMap}
137
+ }
138
+
139
+ /**
140
+ * Helper type to get the interface type for a table name
141
+ */
142
+ export type TableType<T extends TableName> = TableTypes[T];
143
+
144
+ // =============================================================================
145
+ // Usage Examples
146
+ // =============================================================================
147
+
148
+ /**
149
+ * Example usage with @takyonic/sdk:
150
+ *
151
+ * \`\`\`typescript
152
+ * import { TakyonicClient } from '@takyonic/sdk';
153
+ * import type { ${exampleType} } from './generated';
154
+ *
155
+ * const db = new TakyonicClient({
156
+ * endpoint: 'http://localhost:8080',
157
+ * token: process.env.TAKYONIC_API_KEY!
158
+ * });
159
+ *
160
+ * // Type-safe queries
161
+ * const records = await db.table<${exampleType}>('${exampleName}').get();
162
+ *
163
+ * // Type-safe inserts
164
+ * await db.table<${exampleType}>('${exampleName}').insert({
165
+ * id: 'new-id',
166
+ * // ... other fields
167
+ * });
168
+ *
169
+ * // Type-safe search with filters
170
+ * const filtered = await db.table<${exampleType}>('${exampleName}')
171
+ * .where('id', '=', 'some-id')
172
+ * .get();
173
+ * \`\`\`
174
+ */
175
+ `;
176
+
177
+ return code;
178
+ }
package/src/index.ts ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import dotenv from 'dotenv';
5
+ import { inspectCommand } from './commands/inspect';
6
+ import { initCommand } from './commands/init';
7
+ import { generateCommand } from './commands/generate';
8
+ import { pullCommand } from './commands/pull';
9
+ import { pushCommand } from './commands/push';
10
+
11
+ // Load environment variables
12
+ dotenv.config();
13
+
14
+ const program = new Command();
15
+
16
+ program
17
+ .name('takyonic-cli')
18
+ .description('⚡ Takyonic CLI - High-performance Data Framework for PostgreSQL')
19
+ .version('1.0.0');
20
+
21
+ program.addCommand(initCommand);
22
+ program.addCommand(generateCommand);
23
+ program.addCommand(pullCommand);
24
+ program.addCommand(pushCommand);
25
+
26
+ program
27
+ .command('inspect')
28
+ .description('Inspect database schema via Takyonic server API')
29
+ .action(inspectCommand);
30
+
31
+ program.parse();
32
+
package/src/parser.ts ADDED
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Takyonic DSL Parser
3
+ * Parses .takyonic schema files into structured data
4
+ */
5
+
6
+ export interface EngineConfig {
7
+ provider: string;
8
+ db?: string;
9
+ cache?: string;
10
+ port?: number;
11
+ }
12
+
13
+ export interface TableField {
14
+ name: string;
15
+ type: string;
16
+ decorators: {
17
+ primary?: boolean;
18
+ default?: string;
19
+ };
20
+ }
21
+
22
+ export interface TableDefinition {
23
+ name: string;
24
+ fields: TableField[];
25
+ }
26
+
27
+ export interface TakyonicSchema {
28
+ engine: EngineConfig;
29
+ tables: TableDefinition[];
30
+ }
31
+
32
+ export class ParseError extends Error {
33
+ constructor(message: string, public line?: number, public column?: number) {
34
+ super(message);
35
+ this.name = 'ParseError';
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Parse a Takyonic DSL file into structured schema
41
+ */
42
+ export function parseTakyonicSchema(content: string): TakyonicSchema {
43
+ const lines = content.split('\n');
44
+ let currentLine = 0;
45
+ let currentColumn = 0;
46
+
47
+ const schema: TakyonicSchema = {
48
+ engine: { provider: 'takyonic' },
49
+ tables: [],
50
+ };
51
+
52
+ let i = 0;
53
+ while (i < lines.length) {
54
+ const line = lines[i].trim();
55
+ currentLine = i + 1;
56
+
57
+ // Skip empty lines and comments
58
+ if (!line || line.startsWith('//')) {
59
+ i++;
60
+ continue;
61
+ }
62
+
63
+ // Parse engine block
64
+ if (line.startsWith('engine')) {
65
+ const engineEnd = findBlockEnd(lines, i);
66
+ schema.engine = parseEngineBlock(lines.slice(i, engineEnd + 1));
67
+ i = engineEnd + 1;
68
+ continue;
69
+ }
70
+
71
+ // Parse table definitions
72
+ if (line.startsWith('table')) {
73
+ const tableEnd = findBlockEnd(lines, i);
74
+ const table = parseTableBlock(lines.slice(i, tableEnd + 1));
75
+ schema.tables.push(table);
76
+ i = tableEnd + 1;
77
+ continue;
78
+ }
79
+
80
+ i++;
81
+ }
82
+
83
+ // Validate engine has provider
84
+ if (!schema.engine.provider) {
85
+ throw new ParseError('Engine block must specify provider', currentLine);
86
+ }
87
+
88
+ if (schema.engine.provider !== 'takyonic') {
89
+ throw new ParseError('Provider must be "takyonic"', currentLine);
90
+ }
91
+
92
+ return schema;
93
+ }
94
+
95
+ /**
96
+ * Find the closing brace for a block starting at startLine
97
+ */
98
+ function findBlockEnd(lines: string[], startLine: number): number {
99
+ let depth = 0;
100
+ let foundOpen = false;
101
+
102
+ for (let i = startLine; i < lines.length; i++) {
103
+ const line = lines[i];
104
+ for (let j = 0; j < line.length; j++) {
105
+ if (line[j] === '{') {
106
+ depth++;
107
+ foundOpen = true;
108
+ } else if (line[j] === '}') {
109
+ depth--;
110
+ if (foundOpen && depth === 0) {
111
+ return i;
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ throw new ParseError('Unclosed block', startLine + 1);
118
+ }
119
+
120
+ /**
121
+ * Parse engine block
122
+ */
123
+ function parseEngineBlock(lines: string[]): EngineConfig {
124
+ const config: EngineConfig = { provider: 'takyonic' };
125
+ const content = lines.join('\n');
126
+
127
+ // Extract provider
128
+ const providerMatch = content.match(/provider\s*=\s*"([^"]+)"/);
129
+ if (providerMatch) {
130
+ config.provider = providerMatch[1];
131
+ }
132
+
133
+ // Extract db
134
+ const dbMatch = content.match(/db\s*=\s*(env\([^)]+\)|"[^"]+")/);
135
+ if (dbMatch) {
136
+ config.db = dbMatch[1];
137
+ }
138
+
139
+ // Extract cache
140
+ const cacheMatch = content.match(/cache\s*=\s*"([^"]+)"/);
141
+ if (cacheMatch) {
142
+ config.cache = cacheMatch[1];
143
+ }
144
+
145
+ // Extract port
146
+ const portMatch = content.match(/port\s*=\s*(\d+)/);
147
+ if (portMatch) {
148
+ config.port = parseInt(portMatch[1], 10);
149
+ }
150
+
151
+ return config;
152
+ }
153
+
154
+ /**
155
+ * Parse table block
156
+ */
157
+ function parseTableBlock(lines: string[]): TableDefinition {
158
+ // Extract table name from first line
159
+ const firstLine = lines[0].trim();
160
+ const tableNameMatch = firstLine.match(/table\s+(\w+)\s*\{/);
161
+ if (!tableNameMatch) {
162
+ throw new ParseError('Invalid table definition', 1);
163
+ }
164
+
165
+ const tableName = tableNameMatch[1];
166
+ const fields: TableField[] = [];
167
+
168
+ // Parse field definitions (skip first and last lines which are table declaration and closing brace)
169
+ for (let i = 1; i < lines.length - 1; i++) {
170
+ const line = lines[i].trim();
171
+
172
+ // Skip empty lines and comments
173
+ if (!line || line.startsWith('//')) {
174
+ continue;
175
+ }
176
+
177
+ const field = parseFieldLine(line);
178
+ if (field) {
179
+ fields.push(field);
180
+ }
181
+ }
182
+
183
+ return {
184
+ name: tableName,
185
+ fields,
186
+ };
187
+ }
188
+
189
+ /**
190
+ * Parse a single field line
191
+ * Supports both formats:
192
+ * - Space syntax: "id string @primary"
193
+ * - Colon syntax: "id: string @primary"
194
+ * Example: "price int @default(now())"
195
+ */
196
+ function parseFieldLine(line: string): TableField | null {
197
+ // Remove comments
198
+ const cleanLine = line.split('//')[0].trim();
199
+ if (!cleanLine) {
200
+ return null;
201
+ }
202
+
203
+ // Extract decorators first (they start with @)
204
+ const decorators: TableField['decorators'] = {};
205
+ const decoratorPattern = /@(\w+)/g;
206
+ let decoratorMatch;
207
+
208
+ while ((decoratorMatch = decoratorPattern.exec(cleanLine)) !== null) {
209
+ const decoratorName = decoratorMatch[1];
210
+ const decoratorStart = decoratorMatch.index + decoratorMatch[0].length;
211
+
212
+ if (decoratorName === 'primary') {
213
+ decorators.primary = true;
214
+ } else if (decoratorName === 'default') {
215
+ // Extract the value with balanced parentheses
216
+ const valueStart = cleanLine.indexOf('(', decoratorStart);
217
+ if (valueStart !== -1) {
218
+ const value = extractBalancedParens(cleanLine, valueStart);
219
+ if (value) {
220
+ // Remove outer quotes if present
221
+ decorators.default = value.replace(/^["']|["']$/g, '');
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ // Remove all decorators from the line to parse name and type
228
+ const lineWithoutDecorators = cleanLine.replace(/@\w+(\([^)]*\)|\(.*?\)\))?/g, '').trim();
229
+
230
+ // Handle both "name: type" and "name type" formats
231
+ let name: string;
232
+ let type: string;
233
+
234
+ if (lineWithoutDecorators.includes(':')) {
235
+ // Colon syntax: "id: string" or "id : string"
236
+ const colonParts = lineWithoutDecorators.split(':').map(p => p.trim());
237
+ if (colonParts.length < 2 || !colonParts[0] || !colonParts[1]) {
238
+ return null;
239
+ }
240
+ name = colonParts[0];
241
+ type = colonParts[1].split(/\s+/)[0]; // Take first word after colon as type
242
+ } else {
243
+ // Space syntax: "id string"
244
+ const parts = lineWithoutDecorators.split(/\s+/);
245
+ if (parts.length < 2) {
246
+ return null;
247
+ }
248
+ name = parts[0];
249
+ type = parts[1];
250
+ }
251
+
252
+ return {
253
+ name,
254
+ type,
255
+ decorators,
256
+ };
257
+ }
258
+
259
+ /**
260
+ * Extract content within balanced parentheses
261
+ * For input "@default(now())" starting at '(' position, returns "now()"
262
+ */
263
+ function extractBalancedParens(str: string, startPos: number): string | null {
264
+ if (str[startPos] !== '(') {
265
+ return null;
266
+ }
267
+
268
+ let depth = 0;
269
+ let start = startPos + 1;
270
+
271
+ for (let i = startPos; i < str.length; i++) {
272
+ if (str[i] === '(') {
273
+ depth++;
274
+ } else if (str[i] === ')') {
275
+ depth--;
276
+ if (depth === 0) {
277
+ return str.substring(start, i);
278
+ }
279
+ }
280
+ }
281
+
282
+ // Unbalanced parentheses - return what we have
283
+ return str.substring(start);
284
+ }
285
+
@@ -0,0 +1,38 @@
1
+ import axios from 'axios';
2
+
3
+ const TOTAL_RECORDS = 10000;
4
+ const BATCH_SIZE = 1000; // Her istekte 1000 kayıt
5
+
6
+ async function runBulkTest() {
7
+ console.log(`🚀 Takyonic BULK Test: Sending ${TOTAL_RECORDS} records in batches of ${BATCH_SIZE}...`);
8
+ const startTime = Date.now();
9
+
10
+ for (let i = 0; i < TOTAL_RECORDS; i += BATCH_SIZE) {
11
+ const batch = Array.from({ length: BATCH_SIZE }).map((_, j) => ({
12
+ id: `B-${i + j}`,
13
+ event: 'bulk_test_event',
14
+ payload: { value: Math.random(), batch_group: i / BATCH_SIZE },
15
+ severity: Math.floor(Math.random() * 5) + 1
16
+ }));
17
+
18
+ try {
19
+ await axios.post(`http://localhost:8080/v1/bulk-write/logs`, batch, {
20
+ headers: { 'Authorization': 'Bearer takyonic_admin_secret' }
21
+ });
22
+ console.log(`✅ Sent batch starting at ${i}...`);
23
+ } catch (error) {
24
+ if (axios.isAxiosError(error)) {
25
+ console.error(`❌ Error at batch ${i}:`, error.response?.data || error.message);
26
+ } else if (error instanceof Error) {
27
+ console.error(`❌ Error at batch ${i}:`, error.message);
28
+ } else {
29
+ console.error(`❌ Error at batch ${i}:`, String(error));
30
+ }
31
+ }
32
+ }
33
+
34
+ const duration = (Date.now() - startTime) / 1000;
35
+ console.log(`🏁 Bulk test completed in ${duration} seconds!`);
36
+ }
37
+
38
+ runBulkTest();
@@ -0,0 +1,28 @@
1
+ import axios from 'axios';
2
+
3
+ const TOTAL_RECORDS = 10000;
4
+ const BATCH_SIZE = 100; // 100'erli gruplar halinde gönderelim
5
+
6
+ async function runLoadTest() {
7
+ console.log(`⚡ Takyonic Load Test: Sending ${TOTAL_RECORDS} logs...`);
8
+
9
+ for (let i = 0; i < TOTAL_RECORDS; i += BATCH_SIZE) {
10
+ const promises = Array.from({ length: BATCH_SIZE }).map((_, j) => {
11
+ const id = `L-${i + j}`;
12
+ return axios.post('http://localhost:8080/v1/write/logs', {
13
+ id,
14
+ event: 'load_test_event',
15
+ payload: { value: Math.random(), batch: i },
16
+ severity: Math.floor(Math.random() * 5)
17
+ }, {
18
+ headers: { 'Authorization': 'Bearer takyonic_admin_secret' }
19
+ });
20
+ });
21
+
22
+ await Promise.all(promises);
23
+ if (i % 1000 === 0) console.log(`✅ Sent ${i} records...`);
24
+ }
25
+ console.log('🏁 Load test completed!');
26
+ }
27
+
28
+ runLoadTest();