@mhalder/qdrant-mcp-server 1.4.0 → 1.6.0

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 (92) hide show
  1. package/.codecov.yml +16 -0
  2. package/.github/workflows/claude-code-review.yml +6 -5
  3. package/.releaserc.json +8 -1
  4. package/CHANGELOG.md +34 -0
  5. package/README.md +259 -9
  6. package/build/code/chunker/base.d.ts +19 -0
  7. package/build/code/chunker/base.d.ts.map +1 -0
  8. package/build/code/chunker/base.js +5 -0
  9. package/build/code/chunker/base.js.map +1 -0
  10. package/build/code/chunker/character-chunker.d.ts +22 -0
  11. package/build/code/chunker/character-chunker.d.ts.map +1 -0
  12. package/build/code/chunker/character-chunker.js +111 -0
  13. package/build/code/chunker/character-chunker.js.map +1 -0
  14. package/build/code/chunker/tree-sitter-chunker.d.ts +29 -0
  15. package/build/code/chunker/tree-sitter-chunker.d.ts.map +1 -0
  16. package/build/code/chunker/tree-sitter-chunker.js +213 -0
  17. package/build/code/chunker/tree-sitter-chunker.js.map +1 -0
  18. package/build/code/config.d.ts +11 -0
  19. package/build/code/config.d.ts.map +1 -0
  20. package/build/code/config.js +145 -0
  21. package/build/code/config.js.map +1 -0
  22. package/build/code/indexer.d.ts +42 -0
  23. package/build/code/indexer.d.ts.map +1 -0
  24. package/build/code/indexer.js +508 -0
  25. package/build/code/indexer.js.map +1 -0
  26. package/build/code/metadata.d.ts +32 -0
  27. package/build/code/metadata.d.ts.map +1 -0
  28. package/build/code/metadata.js +128 -0
  29. package/build/code/metadata.js.map +1 -0
  30. package/build/code/scanner.d.ts +35 -0
  31. package/build/code/scanner.d.ts.map +1 -0
  32. package/build/code/scanner.js +108 -0
  33. package/build/code/scanner.js.map +1 -0
  34. package/build/code/sync/merkle.d.ts +45 -0
  35. package/build/code/sync/merkle.d.ts.map +1 -0
  36. package/build/code/sync/merkle.js +116 -0
  37. package/build/code/sync/merkle.js.map +1 -0
  38. package/build/code/sync/snapshot.d.ts +41 -0
  39. package/build/code/sync/snapshot.d.ts.map +1 -0
  40. package/build/code/sync/snapshot.js +91 -0
  41. package/build/code/sync/snapshot.js.map +1 -0
  42. package/build/code/sync/synchronizer.d.ts +53 -0
  43. package/build/code/sync/synchronizer.d.ts.map +1 -0
  44. package/build/code/sync/synchronizer.js +132 -0
  45. package/build/code/sync/synchronizer.js.map +1 -0
  46. package/build/code/types.d.ts +98 -0
  47. package/build/code/types.d.ts.map +1 -0
  48. package/build/code/types.js +5 -0
  49. package/build/code/types.js.map +1 -0
  50. package/build/index.js +252 -1
  51. package/build/index.js.map +1 -1
  52. package/build/qdrant/client.d.ts +1 -1
  53. package/build/qdrant/client.d.ts.map +1 -1
  54. package/build/qdrant/client.js +2 -2
  55. package/build/qdrant/client.js.map +1 -1
  56. package/build/qdrant/client.test.js +16 -0
  57. package/build/qdrant/client.test.js.map +1 -1
  58. package/examples/code-search/README.md +271 -0
  59. package/package.json +15 -2
  60. package/src/code/chunker/base.ts +22 -0
  61. package/src/code/chunker/character-chunker.ts +131 -0
  62. package/src/code/chunker/tree-sitter-chunker.ts +250 -0
  63. package/src/code/config.ts +156 -0
  64. package/src/code/indexer.ts +613 -0
  65. package/src/code/metadata.ts +153 -0
  66. package/src/code/scanner.ts +124 -0
  67. package/src/code/sync/merkle.ts +136 -0
  68. package/src/code/sync/snapshot.ts +110 -0
  69. package/src/code/sync/synchronizer.ts +154 -0
  70. package/src/code/types.ts +117 -0
  71. package/src/index.ts +298 -1
  72. package/src/qdrant/client.test.ts +20 -0
  73. package/src/qdrant/client.ts +2 -2
  74. package/tests/code/chunker/character-chunker.test.ts +141 -0
  75. package/tests/code/chunker/tree-sitter-chunker.test.ts +275 -0
  76. package/tests/code/fixtures/sample-py/calculator.py +32 -0
  77. package/tests/code/fixtures/sample-ts/async-operations.ts +120 -0
  78. package/tests/code/fixtures/sample-ts/auth.ts +31 -0
  79. package/tests/code/fixtures/sample-ts/config.ts +52 -0
  80. package/tests/code/fixtures/sample-ts/database.ts +50 -0
  81. package/tests/code/fixtures/sample-ts/index.ts +39 -0
  82. package/tests/code/fixtures/sample-ts/types-advanced.ts +132 -0
  83. package/tests/code/fixtures/sample-ts/utils.ts +105 -0
  84. package/tests/code/fixtures/sample-ts/validator.ts +169 -0
  85. package/tests/code/indexer.test.ts +828 -0
  86. package/tests/code/integration.test.ts +708 -0
  87. package/tests/code/metadata.test.ts +457 -0
  88. package/tests/code/scanner.test.ts +131 -0
  89. package/tests/code/sync/merkle.test.ts +406 -0
  90. package/tests/code/sync/snapshot.test.ts +360 -0
  91. package/tests/code/sync/synchronizer.test.ts +501 -0
  92. package/vitest.config.ts +1 -0
@@ -0,0 +1,275 @@
1
+ import { beforeEach, describe, expect, it } from "vitest";
2
+ import { TreeSitterChunker } from "../../../src/code/chunker/tree-sitter-chunker.js";
3
+ import type { ChunkerConfig } from "../../../src/code/types.js";
4
+
5
+ describe("TreeSitterChunker", () => {
6
+ let chunker: TreeSitterChunker;
7
+ let config: ChunkerConfig;
8
+
9
+ beforeEach(() => {
10
+ config = {
11
+ chunkSize: 500,
12
+ chunkOverlap: 50,
13
+ maxChunkSize: 1000,
14
+ };
15
+ chunker = new TreeSitterChunker(config);
16
+ });
17
+
18
+ describe("chunk - TypeScript", () => {
19
+ it("should chunk TypeScript functions", async () => {
20
+ const code = `
21
+ function add(a: number, b: number): number {
22
+ return a + b;
23
+ }
24
+
25
+ function multiply(a: number, b: number): number {
26
+ return a * b;
27
+ }
28
+ `;
29
+
30
+ const chunks = await chunker.chunk(code, "test.ts", "typescript");
31
+ expect(chunks.length).toBeGreaterThanOrEqual(2);
32
+ expect(chunks.some((c) => c.metadata.name === "add")).toBe(true);
33
+ expect(chunks.some((c) => c.metadata.name === "multiply")).toBe(true);
34
+ });
35
+
36
+ it("should chunk TypeScript classes", async () => {
37
+ const code = `
38
+ class Calculator {
39
+ add(a: number, b: number): number {
40
+ return a + b;
41
+ }
42
+
43
+ subtract(a: number, b: number): number {
44
+ return a - b;
45
+ }
46
+ }
47
+ `;
48
+
49
+ const chunks = await chunker.chunk(code, "test.ts", "typescript");
50
+ expect(chunks.length).toBeGreaterThan(0);
51
+ expect(chunks.some((c) => c.metadata.chunkType === "class")).toBe(true);
52
+ });
53
+
54
+ it("should chunk TypeScript interfaces", async () => {
55
+ const code = `
56
+ interface User {
57
+ id: string;
58
+ name: string;
59
+ email: string;
60
+ createdAt: Date;
61
+ updatedAt: Date;
62
+ }
63
+
64
+ interface Product {
65
+ id: string;
66
+ name: string;
67
+ description: string;
68
+ price: number;
69
+ quantity: number;
70
+ category: string;
71
+ }
72
+
73
+ interface Order {
74
+ id: string;
75
+ userId: string;
76
+ productId: string;
77
+ quantity: number;
78
+ totalPrice: number;
79
+ status: string;
80
+ }
81
+ `;
82
+
83
+ const chunks = await chunker.chunk(code, "test.ts", "typescript");
84
+ expect(chunks.length).toBeGreaterThanOrEqual(1);
85
+ });
86
+ });
87
+
88
+ describe("chunk - Python", () => {
89
+ it("should chunk Python functions", async () => {
90
+ const code = `
91
+ def calculate_sum(numbers):
92
+ """Calculate the sum of a list of numbers."""
93
+ total = 0
94
+ for num in numbers:
95
+ total += num
96
+ return total
97
+
98
+ def calculate_product(numbers):
99
+ """Calculate the product of a list of numbers."""
100
+ result = 1
101
+ for num in numbers:
102
+ result *= num
103
+ return result
104
+
105
+ def calculate_average(numbers):
106
+ """Calculate the average of a list of numbers."""
107
+ if not numbers:
108
+ return 0
109
+ return calculate_sum(numbers) / len(numbers)
110
+ `;
111
+
112
+ const chunks = await chunker.chunk(code, "test.py", "python");
113
+ expect(chunks.length).toBeGreaterThanOrEqual(1);
114
+ if (chunks.length > 0) {
115
+ expect(
116
+ chunks.some(
117
+ (c) => c.metadata.name === "calculate_sum" || c.metadata.name === "calculate_product"
118
+ )
119
+ ).toBe(true);
120
+ }
121
+ });
122
+
123
+ it("should chunk Python classes", async () => {
124
+ const code = `
125
+ class Calculator:
126
+ def add(self, a, b):
127
+ return a + b
128
+
129
+ def multiply(self, a, b):
130
+ return a * b
131
+ `;
132
+
133
+ const chunks = await chunker.chunk(code, "test.py", "python");
134
+ expect(chunks.length).toBeGreaterThan(0);
135
+ });
136
+ });
137
+
138
+ describe("chunk - JavaScript", () => {
139
+ it("should chunk JavaScript functions", async () => {
140
+ const code = `
141
+ function greet(name) {
142
+ return 'Hello, ' + name;
143
+ }
144
+
145
+ function farewell(name) {
146
+ return 'Goodbye, ' + name;
147
+ }
148
+ `;
149
+
150
+ const chunks = await chunker.chunk(code, "test.js", "javascript");
151
+ expect(chunks.length).toBeGreaterThanOrEqual(2);
152
+ });
153
+ });
154
+
155
+ describe("fallback behavior", () => {
156
+ it("should fallback to character chunker for unsupported language", async () => {
157
+ const code =
158
+ "Some random text that is long enough to not be filtered out by the minimum chunk size requirement.\n" +
159
+ "This is another line with enough content to make a valid chunk.\n" +
160
+ "And here is a third line to ensure we have sufficient text content.";
161
+ const chunks = await chunker.chunk(code, "test.txt", "unknown");
162
+
163
+ expect(chunks.length).toBeGreaterThan(0);
164
+ });
165
+
166
+ it("should fallback for very large chunks", async () => {
167
+ const largeFunction = `
168
+ function veryLargeFunction() {
169
+ ${Array(100).fill('console.log("line");').join("\n ")}
170
+ }
171
+ `;
172
+
173
+ const chunks = await chunker.chunk(largeFunction, "test.js", "javascript");
174
+ expect(chunks.length).toBeGreaterThan(0);
175
+ });
176
+
177
+ it("should fallback on parsing errors", async () => {
178
+ const invalidCode = "function broken( { invalid syntax";
179
+ const chunks = await chunker.chunk(invalidCode, "test.js", "javascript");
180
+
181
+ // Should handle gracefully and fallback
182
+ expect(Array.isArray(chunks)).toBe(true);
183
+ });
184
+ });
185
+
186
+ describe("metadata extraction", () => {
187
+ it("should extract function names", async () => {
188
+ const code = `
189
+ function myFunction() {
190
+ console.log('Processing data');
191
+ return 42;
192
+ }
193
+ `;
194
+
195
+ const chunks = await chunker.chunk(code, "test.ts", "typescript");
196
+ expect(chunks[0].metadata.name).toBe("myFunction");
197
+ expect(chunks[0].metadata.chunkType).toBe("function");
198
+ });
199
+
200
+ it("should include file path and language", async () => {
201
+ const code = "function test() {\n console.log('Test function');\n return true;\n}";
202
+ const chunks = await chunker.chunk(code, "/path/to/file.ts", "typescript");
203
+
204
+ expect(chunks[0].metadata.filePath).toBe("/path/to/file.ts");
205
+ expect(chunks[0].metadata.language).toBe("typescript");
206
+ });
207
+
208
+ it("should set correct line numbers", async () => {
209
+ const code = `
210
+ line1
211
+ function test() {
212
+ console.log('Testing line numbers');
213
+ return 1;
214
+ }
215
+ `;
216
+
217
+ const chunks = await chunker.chunk(code, "test.ts", "typescript");
218
+ expect(chunks[0].startLine).toBeGreaterThan(0);
219
+ expect(chunks[0].endLine).toBeGreaterThan(chunks[0].startLine);
220
+ });
221
+ });
222
+
223
+ describe("supportsLanguage", () => {
224
+ it("should support TypeScript", () => {
225
+ expect(chunker.supportsLanguage("typescript")).toBe(true);
226
+ });
227
+
228
+ it("should support Python", () => {
229
+ expect(chunker.supportsLanguage("python")).toBe(true);
230
+ });
231
+
232
+ it("should not support unknown languages", () => {
233
+ expect(chunker.supportsLanguage("unknown")).toBe(false);
234
+ });
235
+ });
236
+
237
+ describe("getStrategyName", () => {
238
+ it("should return tree-sitter", () => {
239
+ expect(chunker.getStrategyName()).toBe("tree-sitter");
240
+ });
241
+ });
242
+
243
+ describe("edge cases", () => {
244
+ it("should handle empty code", async () => {
245
+ const chunks = await chunker.chunk("", "test.ts", "typescript");
246
+ expect(chunks).toHaveLength(0);
247
+ });
248
+
249
+ it("should skip very small chunks", async () => {
250
+ const code = "const x = 1;";
251
+ const chunks = await chunker.chunk(code, "test.ts", "typescript");
252
+ // Very small chunks should be skipped
253
+ expect(chunks.length).toBeGreaterThanOrEqual(0);
254
+ });
255
+
256
+ it("should handle nested structures", async () => {
257
+ const code = `
258
+ class Outer {
259
+ method1() {
260
+ function inner() {
261
+ return 1;
262
+ }
263
+ }
264
+
265
+ method2() {
266
+ return 2;
267
+ }
268
+ }
269
+ `;
270
+
271
+ const chunks = await chunker.chunk(code, "test.ts", "typescript");
272
+ expect(chunks.length).toBeGreaterThan(0);
273
+ });
274
+ });
275
+ });
@@ -0,0 +1,32 @@
1
+ """Simple calculator module for testing."""
2
+
3
+
4
+ class Calculator:
5
+ """A basic calculator class."""
6
+
7
+ def add(self, a: float, b: float) -> float:
8
+ """Add two numbers."""
9
+ return a + b
10
+
11
+ def subtract(self, a: float, b: float) -> float:
12
+ """Subtract b from a."""
13
+ return a - b
14
+
15
+ def multiply(self, a: float, b: float) -> float:
16
+ """Multiply two numbers."""
17
+ return a * b
18
+
19
+ def divide(self, a: float, b: float) -> float:
20
+ """Divide a by b."""
21
+ if b == 0:
22
+ raise ValueError("Cannot divide by zero")
23
+ return a / b
24
+
25
+
26
+ def factorial(n: int) -> int:
27
+ """Calculate factorial of n."""
28
+ if n < 0:
29
+ raise ValueError("Factorial not defined for negative numbers")
30
+ if n == 0 or n == 1:
31
+ return 1
32
+ return n * factorial(n - 1)
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Demonstrates various async/await patterns
3
+ */
4
+
5
+ export class AsyncOperations {
6
+ /**
7
+ * Parallel async operations
8
+ */
9
+ async fetchMultiple(urls: string[]): Promise<any[]> {
10
+ const promises = urls.map((url) => this.fetchData(url));
11
+ return await Promise.all(promises);
12
+ }
13
+
14
+ /**
15
+ * Sequential async operations
16
+ */
17
+ async fetchSequential(urls: string[]): Promise<any[]> {
18
+ const results = [];
19
+ for (const url of urls) {
20
+ const data = await this.fetchData(url);
21
+ results.push(data);
22
+ }
23
+ return results;
24
+ }
25
+
26
+ /**
27
+ * Async operation with timeout
28
+ */
29
+ async fetchWithTimeout(url: string, timeout: number): Promise<any> {
30
+ return Promise.race([
31
+ this.fetchData(url),
32
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), timeout)),
33
+ ]);
34
+ }
35
+
36
+ /**
37
+ * Async generator function
38
+ */
39
+ async *streamData(count: number): AsyncGenerator<number> {
40
+ for (let i = 0; i < count; i++) {
41
+ await this.delay(100);
42
+ yield i;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Process items with concurrency limit
48
+ */
49
+ async processWithConcurrency<T, R>(
50
+ items: T[],
51
+ processor: (item: T) => Promise<R>,
52
+ concurrency: number
53
+ ): Promise<R[]> {
54
+ const results: R[] = [];
55
+ const executing: Promise<void>[] = [];
56
+
57
+ for (const item of items) {
58
+ const promise = processor(item).then((result) => {
59
+ results.push(result);
60
+ });
61
+
62
+ executing.push(promise);
63
+
64
+ if (executing.length >= concurrency) {
65
+ await Promise.race(executing);
66
+ const index = executing.indexOf(promise);
67
+ if (index !== -1) {
68
+ executing.splice(index, 1);
69
+ }
70
+ }
71
+ }
72
+
73
+ await Promise.all(executing);
74
+ return results;
75
+ }
76
+
77
+ /**
78
+ * Retry with exponential backoff
79
+ */
80
+ async retryWithBackoff<T>(fn: () => Promise<T>, maxRetries: number = 3): Promise<T> {
81
+ let lastError: Error;
82
+
83
+ for (let i = 0; i <= maxRetries; i++) {
84
+ try {
85
+ return await fn();
86
+ } catch (error) {
87
+ lastError = error as Error;
88
+ if (i < maxRetries) {
89
+ await this.delay(2 ** i * 1000);
90
+ }
91
+ }
92
+ }
93
+
94
+ throw lastError!;
95
+ }
96
+
97
+ private async fetchData(url: string): Promise<any> {
98
+ await this.delay(100);
99
+ return { url, data: "mock data" };
100
+ }
101
+
102
+ private delay(ms: number): Promise<void> {
103
+ return new Promise((resolve) => setTimeout(resolve, ms));
104
+ }
105
+ }
106
+
107
+ export async function parallelMap<T, R>(items: T[], mapper: (item: T) => Promise<R>): Promise<R[]> {
108
+ return Promise.all(items.map(mapper));
109
+ }
110
+
111
+ export async function sequentialMap<T, R>(
112
+ items: T[],
113
+ mapper: (item: T) => Promise<R>
114
+ ): Promise<R[]> {
115
+ const results: R[] = [];
116
+ for (const item of items) {
117
+ results.push(await mapper(item));
118
+ }
119
+ return results;
120
+ }
@@ -0,0 +1,31 @@
1
+ export interface User {
2
+ id: string;
3
+ email: string;
4
+ name: string;
5
+ }
6
+
7
+ export class AuthService {
8
+ private users: Map<string, User> = new Map();
9
+
10
+ async authenticate(email: string, _password: string): Promise<User | null> {
11
+ const user = Array.from(this.users.values()).find((u) => u.email === email);
12
+ if (!user) {
13
+ return null;
14
+ }
15
+ return user;
16
+ }
17
+
18
+ async register(email: string, name: string, _password: string): Promise<User> {
19
+ const user: User = {
20
+ id: Math.random().toString(36),
21
+ email,
22
+ name,
23
+ };
24
+ this.users.set(user.id, user);
25
+ return user;
26
+ }
27
+
28
+ async getUser(id: string): Promise<User | null> {
29
+ return this.users.get(id) || null;
30
+ }
31
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Configuration constants and settings
3
+ */
4
+
5
+ export const API_VERSION = "v1";
6
+ export const API_BASE_URL = "https://api.example.com";
7
+ export const TIMEOUT = 5000;
8
+ export const MAX_RETRIES = 3;
9
+
10
+ export enum Environment {
11
+ Development = "development",
12
+ Staging = "staging",
13
+ Production = "production",
14
+ }
15
+
16
+ export const CONFIG = {
17
+ api: {
18
+ baseUrl: API_BASE_URL,
19
+ version: API_VERSION,
20
+ timeout: TIMEOUT,
21
+ },
22
+ features: {
23
+ analytics: true,
24
+ darkMode: true,
25
+ notifications: false,
26
+ },
27
+ limits: {
28
+ maxUploadSize: 10 * 1024 * 1024, // 10MB
29
+ maxRequests: 100,
30
+ rateWindow: 60000, // 1 minute
31
+ },
32
+ } as const;
33
+
34
+ export type AppConfig = typeof CONFIG;
35
+
36
+ export class ConfigManager {
37
+ private config: Partial<AppConfig> = {};
38
+
39
+ get<K extends keyof AppConfig>(key: K): AppConfig[K] {
40
+ return (this.config[key] || CONFIG[key]) as AppConfig[K];
41
+ }
42
+
43
+ set<K extends keyof AppConfig>(key: K, value: AppConfig[K]): void {
44
+ this.config[key] = value;
45
+ }
46
+
47
+ reset(): void {
48
+ this.config = {};
49
+ }
50
+ }
51
+
52
+ export { Environment as Env };
@@ -0,0 +1,50 @@
1
+ export interface Database<T> {
2
+ get(id: string): Promise<T | null>;
3
+ set(id: string, value: T): Promise<void>;
4
+ delete(id: string): Promise<boolean>;
5
+ list(): Promise<T[]>;
6
+ }
7
+
8
+ export class InMemoryDatabase<T extends { id: string }> implements Database<T> {
9
+ private store: Map<string, T> = new Map();
10
+
11
+ async get(id: string): Promise<T | null> {
12
+ return this.store.get(id) || null;
13
+ }
14
+
15
+ async set(id: string, value: T): Promise<void> {
16
+ this.store.set(id, value);
17
+ }
18
+
19
+ async delete(id: string): Promise<boolean> {
20
+ return this.store.delete(id);
21
+ }
22
+
23
+ async list(): Promise<T[]> {
24
+ return Array.from(this.store.values());
25
+ }
26
+
27
+ async clear(): Promise<void> {
28
+ this.store.clear();
29
+ }
30
+
31
+ get size(): number {
32
+ return this.store.size;
33
+ }
34
+ }
35
+
36
+ export type ConnectionConfig = {
37
+ host: string;
38
+ port: number;
39
+ username?: string;
40
+ password?: string;
41
+ database: string;
42
+ ssl?: boolean;
43
+ };
44
+
45
+ export async function connect(config: ConnectionConfig): Promise<Database<any>> {
46
+ console.log(`Connecting to ${config.host}:${config.port}`);
47
+ return new InMemoryDatabase();
48
+ }
49
+
50
+ export default InMemoryDatabase;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Main entry point - demonstrates various export patterns
3
+ */
4
+
5
+ // Export everything from a module
6
+ export * from "./auth.js";
7
+ // Named exports from other modules
8
+ export { AuthService, User } from "./auth.js";
9
+ // Re-export with alias
10
+ export { CONFIG, ConfigManager, Environment, Environment as Env } from "./config.js";
11
+ // Default export
12
+ export { ConnectionConfig, connect, Database, default, InMemoryDatabase } from "./database.js";
13
+ export { capitalize, debounce, isValidEmail, retry, slugify } from "./utils.js";
14
+ export { ValidationResult, Validator } from "./validator.js";
15
+
16
+ // Local exports
17
+ export const VERSION = "1.0.0";
18
+ export const APP_NAME = "Sample TypeScript App";
19
+
20
+ export function initializeApp(): void {
21
+ console.log(`Initializing ${APP_NAME} v${VERSION}`);
22
+ }
23
+
24
+ // Named export class
25
+ export class Application {
26
+ private initialized = false;
27
+
28
+ async start(): Promise<void> {
29
+ if (this.initialized) {
30
+ throw new Error("Application already initialized");
31
+ }
32
+ initializeApp();
33
+ this.initialized = true;
34
+ }
35
+
36
+ isRunning(): boolean {
37
+ return this.initialized;
38
+ }
39
+ }