gthinking 1.2.1 → 2.1.1

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 (271) hide show
  1. package/.eslintrc.js +34 -0
  2. package/ANALYSIS_SUMMARY.md +363 -0
  3. package/README.md +230 -245
  4. package/dist/analysis/analysis-engine.d.ts +63 -0
  5. package/dist/analysis/analysis-engine.d.ts.map +1 -0
  6. package/dist/analysis/analysis-engine.js +322 -0
  7. package/dist/analysis/analysis-engine.js.map +1 -0
  8. package/dist/core/config.d.ts +1419 -0
  9. package/dist/core/config.d.ts.map +1 -0
  10. package/dist/core/config.js +361 -0
  11. package/dist/core/config.js.map +1 -0
  12. package/dist/core/engine.d.ts +176 -0
  13. package/dist/core/engine.d.ts.map +1 -0
  14. package/dist/core/engine.js +604 -0
  15. package/dist/core/engine.js.map +1 -0
  16. package/dist/core/errors.d.ts +153 -0
  17. package/dist/core/errors.d.ts.map +1 -0
  18. package/dist/core/errors.js +287 -0
  19. package/dist/core/errors.js.map +1 -0
  20. package/dist/core/index.d.ts +7 -0
  21. package/dist/core/index.d.ts.map +1 -0
  22. package/dist/{types.js → core/index.js} +8 -4
  23. package/dist/core/index.js.map +1 -0
  24. package/dist/core/pipeline.d.ts +121 -0
  25. package/dist/core/pipeline.d.ts.map +1 -0
  26. package/dist/core/pipeline.js +289 -0
  27. package/dist/core/pipeline.js.map +1 -0
  28. package/dist/core/rate-limiter.d.ts +58 -0
  29. package/dist/core/rate-limiter.d.ts.map +1 -0
  30. package/dist/core/rate-limiter.js +133 -0
  31. package/dist/core/rate-limiter.js.map +1 -0
  32. package/dist/core/session-manager.d.ts +96 -0
  33. package/dist/core/session-manager.d.ts.map +1 -0
  34. package/dist/core/session-manager.js +223 -0
  35. package/dist/core/session-manager.js.map +1 -0
  36. package/dist/creativity/creativity-engine.d.ts +6 -0
  37. package/dist/creativity/creativity-engine.d.ts.map +1 -0
  38. package/dist/creativity/creativity-engine.js +17 -0
  39. package/dist/creativity/creativity-engine.js.map +1 -0
  40. package/dist/index.d.ts +24 -32
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +130 -104
  43. package/dist/index.js.map +1 -1
  44. package/dist/learning/learning-engine.d.ts +6 -0
  45. package/dist/learning/learning-engine.d.ts.map +1 -0
  46. package/dist/learning/learning-engine.js +17 -0
  47. package/dist/learning/learning-engine.js.map +1 -0
  48. package/dist/llm/index.d.ts +10 -0
  49. package/dist/llm/index.d.ts.map +1 -0
  50. package/dist/llm/index.js +26 -0
  51. package/dist/llm/index.js.map +1 -0
  52. package/dist/llm/llm-service.d.ts +109 -0
  53. package/dist/llm/llm-service.d.ts.map +1 -0
  54. package/dist/llm/llm-service.js +224 -0
  55. package/dist/llm/llm-service.js.map +1 -0
  56. package/dist/llm/providers/base.d.ts +85 -0
  57. package/dist/llm/providers/base.d.ts.map +1 -0
  58. package/dist/llm/providers/base.js +57 -0
  59. package/dist/llm/providers/base.js.map +1 -0
  60. package/dist/llm/providers/cli.d.ts +23 -0
  61. package/dist/llm/providers/cli.d.ts.map +1 -0
  62. package/dist/llm/providers/cli.js +158 -0
  63. package/dist/llm/providers/cli.js.map +1 -0
  64. package/dist/llm/providers/gemini.d.ts +30 -0
  65. package/dist/llm/providers/gemini.d.ts.map +1 -0
  66. package/dist/llm/providers/gemini.js +168 -0
  67. package/dist/llm/providers/gemini.js.map +1 -0
  68. package/dist/llm/sanitization.d.ts +50 -0
  69. package/dist/llm/sanitization.d.ts.map +1 -0
  70. package/dist/llm/sanitization.js +149 -0
  71. package/dist/llm/sanitization.js.map +1 -0
  72. package/dist/{server.d.ts.map → mcp/server.d.ts.map} +1 -1
  73. package/dist/mcp/server.js +108 -0
  74. package/dist/mcp/server.js.map +1 -0
  75. package/dist/planning/planning-engine.d.ts +6 -0
  76. package/dist/planning/planning-engine.d.ts.map +1 -0
  77. package/dist/planning/planning-engine.js +17 -0
  78. package/dist/planning/planning-engine.js.map +1 -0
  79. package/dist/reasoning/reasoning-engine.d.ts +6 -0
  80. package/dist/reasoning/reasoning-engine.d.ts.map +1 -0
  81. package/dist/reasoning/reasoning-engine.js +17 -0
  82. package/dist/reasoning/reasoning-engine.js.map +1 -0
  83. package/dist/search/search-engine.d.ts +99 -0
  84. package/dist/search/search-engine.d.ts.map +1 -0
  85. package/dist/search/search-engine.js +271 -0
  86. package/dist/search/search-engine.js.map +1 -0
  87. package/dist/synthesis/synthesis-engine.d.ts +6 -0
  88. package/dist/synthesis/synthesis-engine.d.ts.map +1 -0
  89. package/dist/synthesis/synthesis-engine.js +17 -0
  90. package/dist/synthesis/synthesis-engine.js.map +1 -0
  91. package/dist/types/analysis.d.ts +1534 -49
  92. package/dist/types/analysis.d.ts.map +1 -1
  93. package/dist/types/analysis.js +250 -0
  94. package/dist/types/analysis.js.map +1 -1
  95. package/dist/types/core.d.ts +257 -30
  96. package/dist/types/core.d.ts.map +1 -1
  97. package/dist/types/core.js +148 -18
  98. package/dist/types/core.js.map +1 -1
  99. package/dist/types/creativity.d.ts +2871 -56
  100. package/dist/types/creativity.d.ts.map +1 -1
  101. package/dist/types/creativity.js +195 -0
  102. package/dist/types/creativity.js.map +1 -1
  103. package/dist/types/index.d.ts +6 -2
  104. package/dist/types/index.d.ts.map +1 -1
  105. package/dist/types/index.js +17 -2
  106. package/dist/types/index.js.map +1 -1
  107. package/dist/types/learning.d.ts +851 -61
  108. package/dist/types/learning.d.ts.map +1 -1
  109. package/dist/types/learning.js +155 -0
  110. package/dist/types/learning.js.map +1 -1
  111. package/dist/types/planning.d.ts +2223 -71
  112. package/dist/types/planning.d.ts.map +1 -1
  113. package/dist/types/planning.js +190 -0
  114. package/dist/types/planning.js.map +1 -1
  115. package/dist/types/reasoning.d.ts +2209 -72
  116. package/dist/types/reasoning.d.ts.map +1 -1
  117. package/dist/types/reasoning.js +200 -1
  118. package/dist/types/reasoning.js.map +1 -1
  119. package/dist/types/search.d.ts +981 -53
  120. package/dist/types/search.d.ts.map +1 -1
  121. package/dist/types/search.js +137 -0
  122. package/dist/types/search.js.map +1 -1
  123. package/dist/types/synthesis.d.ts +583 -37
  124. package/dist/types/synthesis.d.ts.map +1 -1
  125. package/dist/types/synthesis.js +138 -0
  126. package/dist/types/synthesis.js.map +1 -1
  127. package/dist/utils/cache.d.ts +144 -0
  128. package/dist/utils/cache.d.ts.map +1 -0
  129. package/dist/utils/cache.js +288 -0
  130. package/dist/utils/cache.js.map +1 -0
  131. package/dist/utils/id-generator.d.ts +89 -0
  132. package/dist/utils/id-generator.d.ts.map +1 -0
  133. package/dist/utils/id-generator.js +132 -0
  134. package/dist/utils/id-generator.js.map +1 -0
  135. package/dist/utils/index.d.ts +11 -0
  136. package/dist/utils/index.d.ts.map +1 -0
  137. package/dist/utils/index.js +33 -0
  138. package/dist/utils/index.js.map +1 -0
  139. package/dist/utils/logger.d.ts +142 -0
  140. package/dist/utils/logger.d.ts.map +1 -0
  141. package/dist/utils/logger.js +248 -0
  142. package/dist/utils/logger.js.map +1 -0
  143. package/dist/utils/metrics.d.ts +149 -0
  144. package/dist/utils/metrics.d.ts.map +1 -0
  145. package/dist/utils/metrics.js +296 -0
  146. package/dist/utils/metrics.js.map +1 -0
  147. package/dist/utils/timer.d.ts +7 -0
  148. package/dist/utils/timer.d.ts.map +1 -0
  149. package/dist/utils/timer.js +17 -0
  150. package/dist/utils/timer.js.map +1 -0
  151. package/dist/utils/validation.d.ts +147 -0
  152. package/dist/utils/validation.d.ts.map +1 -0
  153. package/dist/utils/validation.js +275 -0
  154. package/dist/utils/validation.js.map +1 -0
  155. package/docs/API.md +411 -0
  156. package/docs/ARCHITECTURE.md +271 -0
  157. package/docs/CHANGELOG.md +283 -0
  158. package/jest.config.js +28 -0
  159. package/package.json +43 -30
  160. package/src/analysis/analysis-engine.ts +383 -0
  161. package/src/core/config.ts +406 -0
  162. package/src/core/engine.ts +785 -0
  163. package/src/core/errors.ts +349 -0
  164. package/src/core/index.ts +12 -0
  165. package/src/core/pipeline.ts +424 -0
  166. package/src/core/rate-limiter.ts +155 -0
  167. package/src/core/session-manager.ts +269 -0
  168. package/src/creativity/creativity-engine.ts +14 -0
  169. package/src/index.ts +178 -0
  170. package/src/learning/learning-engine.ts +14 -0
  171. package/src/llm/index.ts +10 -0
  172. package/src/llm/llm-service.ts +285 -0
  173. package/src/llm/providers/base.ts +146 -0
  174. package/src/llm/providers/cli.ts +186 -0
  175. package/src/llm/providers/gemini.ts +201 -0
  176. package/src/llm/sanitization.ts +178 -0
  177. package/src/mcp/server.ts +117 -0
  178. package/src/planning/planning-engine.ts +14 -0
  179. package/src/reasoning/reasoning-engine.ts +14 -0
  180. package/src/search/search-engine.ts +333 -0
  181. package/src/synthesis/synthesis-engine.ts +14 -0
  182. package/src/types/analysis.ts +337 -0
  183. package/src/types/core.ts +342 -0
  184. package/src/types/creativity.ts +268 -0
  185. package/src/types/index.ts +31 -0
  186. package/src/types/learning.ts +215 -0
  187. package/src/types/planning.ts +251 -0
  188. package/src/types/reasoning.ts +288 -0
  189. package/src/types/search.ts +192 -0
  190. package/src/types/synthesis.ts +187 -0
  191. package/src/utils/cache.ts +363 -0
  192. package/src/utils/id-generator.ts +135 -0
  193. package/src/utils/index.ts +22 -0
  194. package/src/utils/logger.ts +290 -0
  195. package/src/utils/metrics.ts +380 -0
  196. package/src/utils/timer.ts +15 -0
  197. package/src/utils/validation.ts +297 -0
  198. package/tests/setup.ts +22 -0
  199. package/tests/unit/cache.test.ts +189 -0
  200. package/tests/unit/engine.test.ts +179 -0
  201. package/tests/unit/validation.test.ts +218 -0
  202. package/tsconfig.json +17 -12
  203. package/GEMINI.md +0 -68
  204. package/analysis.ts +0 -1063
  205. package/creativity.ts +0 -1055
  206. package/dist/analysis.d.ts +0 -54
  207. package/dist/analysis.d.ts.map +0 -1
  208. package/dist/analysis.js +0 -866
  209. package/dist/analysis.js.map +0 -1
  210. package/dist/creativity.d.ts +0 -81
  211. package/dist/creativity.d.ts.map +0 -1
  212. package/dist/creativity.js +0 -828
  213. package/dist/creativity.js.map +0 -1
  214. package/dist/engine.d.ts +0 -90
  215. package/dist/engine.d.ts.map +0 -1
  216. package/dist/engine.js +0 -677
  217. package/dist/engine.js.map +0 -1
  218. package/dist/examples.d.ts +0 -7
  219. package/dist/examples.d.ts.map +0 -1
  220. package/dist/examples.js +0 -506
  221. package/dist/examples.js.map +0 -1
  222. package/dist/learning.d.ts +0 -72
  223. package/dist/learning.d.ts.map +0 -1
  224. package/dist/learning.js +0 -615
  225. package/dist/learning.js.map +0 -1
  226. package/dist/llm-service.d.ts +0 -21
  227. package/dist/llm-service.d.ts.map +0 -1
  228. package/dist/llm-service.js +0 -100
  229. package/dist/llm-service.js.map +0 -1
  230. package/dist/planning.d.ts +0 -58
  231. package/dist/planning.d.ts.map +0 -1
  232. package/dist/planning.js +0 -824
  233. package/dist/planning.js.map +0 -1
  234. package/dist/reasoning.d.ts +0 -73
  235. package/dist/reasoning.d.ts.map +0 -1
  236. package/dist/reasoning.js +0 -845
  237. package/dist/reasoning.js.map +0 -1
  238. package/dist/search-discovery.d.ts +0 -73
  239. package/dist/search-discovery.d.ts.map +0 -1
  240. package/dist/search-discovery.js +0 -548
  241. package/dist/search-discovery.js.map +0 -1
  242. package/dist/server.js +0 -113
  243. package/dist/server.js.map +0 -1
  244. package/dist/types/engine.d.ts +0 -55
  245. package/dist/types/engine.d.ts.map +0 -1
  246. package/dist/types/engine.js +0 -3
  247. package/dist/types/engine.js.map +0 -1
  248. package/dist/types.d.ts +0 -6
  249. package/dist/types.d.ts.map +0 -1
  250. package/dist/types.js.map +0 -1
  251. package/engine.ts +0 -947
  252. package/examples.ts +0 -717
  253. package/index.ts +0 -106
  254. package/learning.ts +0 -779
  255. package/llm-service.ts +0 -120
  256. package/planning.ts +0 -1028
  257. package/reasoning.ts +0 -1079
  258. package/search-discovery.ts +0 -700
  259. package/server.ts +0 -115
  260. package/types/analysis.ts +0 -69
  261. package/types/core.ts +0 -90
  262. package/types/creativity.ts +0 -72
  263. package/types/engine.ts +0 -60
  264. package/types/index.ts +0 -9
  265. package/types/learning.ts +0 -69
  266. package/types/planning.ts +0 -85
  267. package/types/reasoning.ts +0 -92
  268. package/types/search.ts +0 -58
  269. package/types/synthesis.ts +0 -42
  270. package/types.ts +0 -6
  271. /package/dist/{server.d.ts → mcp/server.d.ts} +0 -0
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Validation Utility for gthinking v2.0.0
3
+ * Input sanitization and validation functions
4
+ */
5
+
6
+ import { z } from 'zod';
7
+ import { ValidationError } from '../core/errors';
8
+
9
+ /**
10
+ * Sanitize user input to prevent injection attacks
11
+ * @param input - The input string to sanitize
12
+ * @returns Sanitized string
13
+ */
14
+ export function sanitizeInput(input: string): string {
15
+ if (!input) return '';
16
+
17
+ // Remove potentially dangerous characters
18
+ return input
19
+ .replace(/[<>]/g, '') // Remove HTML tags
20
+ .replace(/[&;|`$(){}[\]\\]/g, '') // Remove shell metacharacters
21
+ .replace(/\0/g, '') // Remove null bytes
22
+ .trim();
23
+ }
24
+
25
+ /**
26
+ * Sanitize a string for use in shell commands
27
+ * @param input - The input string to sanitize
28
+ * @returns Shell-safe string
29
+ */
30
+ export function sanitizeForShell(input: string): string {
31
+ if (!input) return '';
32
+
33
+ // More aggressive sanitization for shell usage
34
+ // Only allow alphanumeric, underscore, hyphen, dot, and space
35
+ return input
36
+ .replace(/[^a-zA-Z0-9_\-\s.]/g, '')
37
+ .trim();
38
+ }
39
+
40
+ /**
41
+ * Validate and sanitize an email address
42
+ * @param email - The email to validate
43
+ * @returns Validated email or null if invalid
44
+ */
45
+ export function validateEmail(email: string): string | null {
46
+ const emailSchema = z.string().email();
47
+ const result = emailSchema.safeParse(email);
48
+ return result.success ? result.data : null;
49
+ }
50
+
51
+ /**
52
+ * Validate a URL string
53
+ * @param url - The URL to validate
54
+ * @returns True if the URL is valid
55
+ */
56
+ export function isValidURL(url: string): boolean {
57
+ try {
58
+ new URL(url);
59
+ return true;
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Validate that a string is not empty or whitespace only
67
+ * @param str - The string to validate
68
+ * @returns True if the string has content
69
+ */
70
+ export function isNonEmptyString(str: unknown): str is string {
71
+ return typeof str === 'string' && str.trim().length > 0;
72
+ }
73
+
74
+ /**
75
+ * Validate that a value is a positive integer
76
+ * @param value - The value to validate
77
+ * @returns True if the value is a positive integer
78
+ */
79
+ export function isPositiveInteger(value: unknown): value is number {
80
+ return typeof value === 'number' && Number.isInteger(value) && value > 0;
81
+ }
82
+
83
+ /**
84
+ * Validate that a value is within a range
85
+ * @param value - The value to validate
86
+ * @param min - The minimum allowed value
87
+ * @param max - The maximum allowed value
88
+ * @returns True if the value is within the range
89
+ */
90
+ export function isInRange(value: number, min: number, max: number): boolean {
91
+ return value >= min && value <= max;
92
+ }
93
+
94
+ /**
95
+ * Validate an array has unique elements
96
+ * @param arr - The array to validate
97
+ * @returns True if all elements are unique
98
+ */
99
+ export function hasUniqueElements<T>(arr: T[]): boolean {
100
+ return new Set(arr).size === arr.length;
101
+ }
102
+
103
+ /**
104
+ * Safe JSON parse with error handling
105
+ * @param json - The JSON string to parse
106
+ * @returns Parsed object or null if invalid
107
+ */
108
+ export function safeJsonParse<T>(json: string): T | null {
109
+ try {
110
+ return JSON.parse(json) as T;
111
+ } catch {
112
+ return null;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Safe JSON stringify with error handling
118
+ * @param obj - The object to stringify
119
+ * @returns JSON string or null if failed
120
+ */
121
+ export function safeJsonStringify(obj: unknown): string | null {
122
+ try {
123
+ return JSON.stringify(obj);
124
+ } catch {
125
+ return null;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Validate schema with detailed error messages
131
+ * @param schema - The Zod schema to validate against
132
+ * @param data - The data to validate
133
+ * @returns Validated data
134
+ * @throws ValidationError if validation fails
135
+ */
136
+ export function validateSchema<T>(schema: z.ZodType<T>, data: unknown): T {
137
+ const result = schema.safeParse(data);
138
+
139
+ if (!result.success) {
140
+ const errors = result.error.errors.map(err => ({
141
+ path: err.path.join('.'),
142
+ message: err.message,
143
+ code: err.code,
144
+ }));
145
+
146
+ throw new ValidationError(
147
+ `Validation failed: ${errors.map(e => `${e.path}: ${e.message}`).join(', ')}`,
148
+ { errors }
149
+ );
150
+ }
151
+
152
+ return result.data;
153
+ }
154
+
155
+ /**
156
+ * Validate schema asynchronously
157
+ * @param schema - The Zod schema to validate against
158
+ * @param data - The data to validate
159
+ * @returns Promise of validated data
160
+ * @throws ValidationError if validation fails
161
+ */
162
+ export async function validateSchemaAsync<T>(
163
+ schema: z.ZodType<T>,
164
+ data: unknown
165
+ ): Promise<T> {
166
+ const result = await schema.safeParseAsync(data);
167
+
168
+ if (!result.success) {
169
+ const errors = result.error.errors.map(err => ({
170
+ path: err.path.join('.'),
171
+ message: err.message,
172
+ code: err.code,
173
+ }));
174
+
175
+ throw new ValidationError(
176
+ `Validation failed: ${errors.map(e => `${e.path}: ${e.message}`).join(', ')}`,
177
+ { errors }
178
+ );
179
+ }
180
+
181
+ return result.data;
182
+ }
183
+
184
+ /**
185
+ * Check if an object has all required properties
186
+ * @param obj - The object to check
187
+ * @param requiredProps - The required property names
188
+ * @returns True if all properties exist
189
+ */
190
+ export function hasRequiredProperties<T extends object>(
191
+ obj: T,
192
+ requiredProps: (keyof T)[]
193
+ ): boolean {
194
+ return requiredProps.every(prop => prop in obj && obj[prop] !== undefined);
195
+ }
196
+
197
+ /**
198
+ * Validate that a string length is within bounds
199
+ * @param str - The string to validate
200
+ * @param min - Minimum length (inclusive)
201
+ * @param max - Maximum length (inclusive)
202
+ * @returns True if the string length is valid
203
+ */
204
+ export function isValidLength(str: string, min: number, max: number): boolean {
205
+ const length = str.length;
206
+ return length >= min && length <= max;
207
+ }
208
+
209
+ /**
210
+ * Remove null and undefined values from an object
211
+ * @param obj - The object to clean
212
+ * @returns Object with null/undefined values removed
213
+ */
214
+ export function removeNullValues<T extends object>(obj: T): Partial<T> {
215
+ return Object.fromEntries(
216
+ Object.entries(obj).filter(([, v]) => v !== null && v !== undefined)
217
+ ) as Partial<T>;
218
+ }
219
+
220
+ /**
221
+ * Deep clone an object
222
+ * @param obj - The object to clone
223
+ * @returns Deep cloned object
224
+ */
225
+ export function deepClone<T>(obj: T): T {
226
+ return JSON.parse(JSON.stringify(obj)) as T;
227
+ }
228
+
229
+ /**
230
+ * Check if a value is a plain object
231
+ * @param value - The value to check
232
+ * @returns True if the value is a plain object
233
+ */
234
+ export function isPlainObject(value: unknown): value is Record<string, unknown> {
235
+ return typeof value === 'object'
236
+ && value !== null
237
+ && !Array.isArray(value)
238
+ && value.constructor === Object;
239
+ }
240
+
241
+ /**
242
+ * Rate limiter for validation attempts
243
+ */
244
+ export class ValidationRateLimiter {
245
+ private attempts: Map<string, number[]> = new Map();
246
+ private readonly maxAttempts: number;
247
+ private readonly windowMs: number;
248
+
249
+ constructor(maxAttempts = 5, windowMs = 60000) {
250
+ this.maxAttempts = maxAttempts;
251
+ this.windowMs = windowMs;
252
+ }
253
+
254
+ /**
255
+ * Check if a key is rate limited
256
+ * @param key - The key to check
257
+ * @returns True if the key is rate limited
258
+ */
259
+ public isRateLimited(key: string): boolean {
260
+ const now = Date.now();
261
+ const attempts = this.attempts.get(key) || [];
262
+
263
+ // Remove old attempts outside the window
264
+ const validAttempts = attempts.filter(time => now - time < this.windowMs);
265
+
266
+ return validAttempts.length >= this.maxAttempts;
267
+ }
268
+
269
+ /**
270
+ * Record an attempt for a key
271
+ * @param key - The key to record
272
+ */
273
+ public recordAttempt(key: string): void {
274
+ const now = Date.now();
275
+ const attempts = this.attempts.get(key) || [];
276
+ attempts.push(now);
277
+ this.attempts.set(key, attempts);
278
+ }
279
+
280
+ /**
281
+ * Reset attempts for a key
282
+ * @param key - The key to reset
283
+ */
284
+ public reset(key: string): void {
285
+ this.attempts.delete(key);
286
+ }
287
+
288
+ /**
289
+ * Clear all attempts
290
+ */
291
+ public clear(): void {
292
+ this.attempts.clear();
293
+ }
294
+ }
295
+
296
+ // Export a singleton instance
297
+ export const validationRateLimiter = new ValidationRateLimiter();
package/tests/setup.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Jest Test Setup
3
+ * Configuration and global mocks for testing
4
+ */
5
+
6
+ // Mock console methods to reduce noise in tests
7
+ global.console = {
8
+ ...console,
9
+ log: jest.fn(),
10
+ debug: jest.fn(),
11
+ info: jest.fn(),
12
+ warn: jest.fn(),
13
+ error: console.error,
14
+ };
15
+
16
+ // Set test timeout
17
+ jest.setTimeout(30000);
18
+
19
+ // Clean up after each test
20
+ afterEach(() => {
21
+ jest.clearAllMocks();
22
+ });
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Unit Tests for Cache
3
+ */
4
+
5
+ import { Cache, CacheManager, cacheManager } from '../../src/utils/cache';
6
+
7
+ describe('Cache', () => {
8
+ let cache: Cache<string>;
9
+
10
+ beforeEach(() => {
11
+ cache = new Cache<string>({ maxSize: 100, defaultTTL: 1000 });
12
+ });
13
+
14
+ afterEach(() => {
15
+ cache.dispose();
16
+ });
17
+
18
+ describe('set and get', () => {
19
+ it('should store and retrieve values', () => {
20
+ cache.set('key1', 'value1');
21
+ expect(cache.get('key1')).toBe('value1');
22
+ });
23
+
24
+ it('should return undefined for non-existent keys', () => {
25
+ expect(cache.get('nonexistent')).toBeUndefined();
26
+ });
27
+
28
+ it('should return undefined for expired keys', async () => {
29
+ cache.set('key1', 'value1', 10); // 10ms TTL
30
+ expect(cache.get('key1')).toBe('value1');
31
+
32
+ await new Promise(resolve => setTimeout(resolve, 20));
33
+ expect(cache.get('key1')).toBeUndefined();
34
+ });
35
+ });
36
+
37
+ describe('has', () => {
38
+ it('should return true for existing keys', () => {
39
+ cache.set('key1', 'value1');
40
+ expect(cache.has('key1')).toBe(true);
41
+ });
42
+
43
+ it('should return false for non-existent keys', () => {
44
+ expect(cache.has('nonexistent')).toBe(false);
45
+ });
46
+
47
+ it('should return false for expired keys', async () => {
48
+ cache.set('key1', 'value1', 10);
49
+ await new Promise(resolve => setTimeout(resolve, 20));
50
+ expect(cache.has('key1')).toBe(false);
51
+ });
52
+ });
53
+
54
+ describe('delete', () => {
55
+ it('should delete existing keys', () => {
56
+ cache.set('key1', 'value1');
57
+ expect(cache.delete('key1')).toBe(true);
58
+ expect(cache.get('key1')).toBeUndefined();
59
+ });
60
+
61
+ it('should return false for non-existent keys', () => {
62
+ expect(cache.delete('nonexistent')).toBe(false);
63
+ });
64
+ });
65
+
66
+ describe('clear', () => {
67
+ it('should remove all entries', () => {
68
+ cache.set('key1', 'value1');
69
+ cache.set('key2', 'value2');
70
+
71
+ cache.clear();
72
+
73
+ expect(cache.get('key1')).toBeUndefined();
74
+ expect(cache.get('key2')).toBeUndefined();
75
+ expect(cache.size()).toBe(0);
76
+ });
77
+ });
78
+
79
+ describe('LRU eviction', () => {
80
+ it('should evict least recently used entries when full', () => {
81
+ const smallCache = new Cache<string>({ maxSize: 2, defaultTTL: 10000 });
82
+
83
+ smallCache.set('key1', 'value1');
84
+ smallCache.set('key2', 'value2');
85
+ smallCache.set('key3', 'value3'); // Should evict key1
86
+
87
+ expect(smallCache.get('key1')).toBeUndefined();
88
+ expect(smallCache.get('key2')).toBe('value2');
89
+ expect(smallCache.get('key3')).toBe('value3');
90
+
91
+ smallCache.dispose();
92
+ });
93
+ });
94
+
95
+ describe('statistics', () => {
96
+ it('should track cache statistics', () => {
97
+ cache.set('key1', 'value1');
98
+ cache.get('key1'); // Hit
99
+ cache.get('key2'); // Miss
100
+
101
+ const stats = cache.getStats();
102
+
103
+ expect(stats.size).toBe(1);
104
+ expect(stats.hits).toBe(1);
105
+ expect(stats.misses).toBe(1);
106
+ expect(stats.totalAccesses).toBe(2);
107
+ expect(stats.hitRate).toBe(0.5);
108
+ });
109
+
110
+ it('should reset statistics', () => {
111
+ cache.set('key1', 'value1');
112
+ cache.get('key1');
113
+
114
+ cache.resetStats();
115
+
116
+ const stats = cache.getStats();
117
+ expect(stats.hits).toBe(0);
118
+ expect(stats.misses).toBe(0);
119
+ expect(stats.totalAccesses).toBe(0);
120
+ });
121
+ });
122
+
123
+ describe('keys and size', () => {
124
+ it('should return all keys', () => {
125
+ cache.set('key1', 'value1');
126
+ cache.set('key2', 'value2');
127
+
128
+ const keys = cache.keys();
129
+ expect(keys).toContain('key1');
130
+ expect(keys).toContain('key2');
131
+ });
132
+
133
+ it('should return correct size', () => {
134
+ expect(cache.size()).toBe(0);
135
+ cache.set('key1', 'value1');
136
+ expect(cache.size()).toBe(1);
137
+ });
138
+ });
139
+ });
140
+
141
+ describe('CacheManager', () => {
142
+ afterEach(() => {
143
+ cacheManager.dispose();
144
+ });
145
+
146
+ it('should create and retrieve caches', () => {
147
+ const cache1 = cacheManager.getCache<string>('cache1');
148
+ const cache2 = cacheManager.getCache<string>('cache2');
149
+
150
+ expect(cache1).toBeDefined();
151
+ expect(cache2).toBeDefined();
152
+ expect(cache1).not.toBe(cache2);
153
+
154
+ // Should return same instance for same name
155
+ const cache1Again = cacheManager.getCache<string>('cache1');
156
+ expect(cache1).toBe(cache1Again);
157
+ });
158
+
159
+ it('should delete caches', () => {
160
+ cacheManager.getCache<string>('cache1');
161
+ cacheManager.deleteCache('cache1');
162
+
163
+ // Getting again should create new instance
164
+ const newCache = cacheManager.getCache<string>('cache1');
165
+ expect(newCache).toBeDefined();
166
+ });
167
+
168
+ it('should clear all caches', () => {
169
+ const cache1 = cacheManager.getCache<string>('cache1');
170
+ const cache2 = cacheManager.getCache<string>('cache2');
171
+
172
+ cache1.set('key1', 'value1');
173
+ cache2.set('key2', 'value2');
174
+
175
+ cacheManager.clearAll();
176
+
177
+ expect(cache1.get('key1')).toBeUndefined();
178
+ expect(cache2.get('key2')).toBeUndefined();
179
+ });
180
+
181
+ it('should return cache names', () => {
182
+ cacheManager.getCache<string>('cache1');
183
+ cacheManager.getCache<string>('cache2');
184
+
185
+ const names = cacheManager.getCacheNames();
186
+ expect(names).toContain('cache1');
187
+ expect(names).toContain('cache2');
188
+ });
189
+ });
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Unit Tests for SequentialThinkingEngine
3
+ */
4
+
5
+ import { SequentialThinkingEngine } from '../../src/core/engine';
6
+ import { ThinkingStage } from '../../src/types';
7
+ import { GThinkingError, ValidationError } from '../../src/core/errors';
8
+
9
+ describe('SequentialThinkingEngine', () => {
10
+ let engine: SequentialThinkingEngine;
11
+
12
+ beforeEach(() => {
13
+ engine = new SequentialThinkingEngine();
14
+ });
15
+
16
+ afterEach(() => {
17
+ engine.dispose();
18
+ });
19
+
20
+ describe('constructor', () => {
21
+ it('should create an engine with default config', () => {
22
+ expect(engine).toBeDefined();
23
+ expect(engine.getStats().sessions).toBe(0);
24
+ });
25
+
26
+ it('should create an engine with custom config', () => {
27
+ const customEngine = new SequentialThinkingEngine({
28
+ cache: { enabled: false },
29
+ });
30
+ expect(customEngine).toBeDefined();
31
+ customEngine.dispose();
32
+ });
33
+ });
34
+
35
+ describe('think', () => {
36
+ it('should validate request schema', async () => {
37
+ const invalidRequest = {
38
+ query: '', // Empty query should fail validation
39
+ };
40
+
41
+ await expect(engine.think(invalidRequest)).rejects.toThrow(ValidationError);
42
+ });
43
+
44
+ it('should process a valid request', async () => {
45
+ const request = {
46
+ query: 'Test query',
47
+ preferredStages: [ThinkingStage.SEARCH, ThinkingStage.SYNTHESIS],
48
+ };
49
+
50
+ const result = await engine.think(request);
51
+
52
+ expect(result).toBeDefined();
53
+ expect(result.query).toBe(request.query);
54
+ expect(result.success).toBe(true);
55
+ expect(result.confidence).toBeGreaterThan(0);
56
+ });
57
+
58
+ it('should emit events during processing', async () => {
59
+ const events: string[] = [];
60
+
61
+ engine.on('thinking:start', () => events.push('start'));
62
+ engine.on('thinking:stage:start', () => events.push('stage:start'));
63
+ engine.on('thinking:stage:complete', () => events.push('stage:complete'));
64
+ engine.on('thinking:complete', () => events.push('complete'));
65
+
66
+ await engine.think({
67
+ query: 'Test query',
68
+ preferredStages: [ThinkingStage.SYNTHESIS],
69
+ });
70
+
71
+ expect(events).toContain('start');
72
+ expect(events).toContain('complete');
73
+ });
74
+
75
+ it('should cache results when enabled', async () => {
76
+ const request = {
77
+ query: 'Cached query',
78
+ preferredStages: [ThinkingStage.SYNTHESIS],
79
+ };
80
+
81
+ // First call
82
+ const result1 = await engine.think(request);
83
+
84
+ // Second call should use cache
85
+ const result2 = await engine.think(request);
86
+
87
+ expect(result1.query).toBe(result2.query);
88
+ });
89
+ });
90
+
91
+ describe('quickSearch', () => {
92
+ it('should execute quick search', async () => {
93
+ const result = await engine.quickSearch('Quick search test');
94
+
95
+ expect(result).toBeDefined();
96
+ expect(result.query).toBe('Quick search test');
97
+ expect(result.success).toBe(true);
98
+ });
99
+ });
100
+
101
+ describe('deepAnalysis', () => {
102
+ it('should execute deep analysis', async () => {
103
+ const result = await engine.deepAnalysis('Deep analysis test');
104
+
105
+ expect(result).toBeDefined();
106
+ expect(result.query).toBe('Deep analysis test');
107
+ expect(result.success).toBe(true);
108
+ });
109
+ });
110
+
111
+ describe('session management', () => {
112
+ it('should track active sessions', async () => {
113
+ expect(engine.getActiveSessions()).toHaveLength(0);
114
+
115
+ const promise = engine.think({ query: 'Session test' });
116
+
117
+ // Session should be active while processing
118
+ expect(engine.getActiveSessions().length).toBeGreaterThanOrEqual(0);
119
+
120
+ await promise;
121
+ });
122
+
123
+ it('should get session by ID', async () => {
124
+ const request = { query: 'Session ID test' };
125
+
126
+ // Start a session
127
+ const thinkPromise = engine.think(request);
128
+
129
+ // Get the session
130
+ const sessions = engine.getActiveSessions();
131
+ if (sessions.length > 0) {
132
+ const session = engine.getSession(sessions[0].id);
133
+ expect(session).toBeDefined();
134
+ }
135
+
136
+ await thinkPromise;
137
+ });
138
+
139
+ it('should cancel a running session', async () => {
140
+ const request = { query: 'Cancel test' };
141
+
142
+ // Start a session
143
+ const thinkPromise = engine.think(request);
144
+
145
+ // Try to cancel (may or may not work depending on timing)
146
+ const sessions = engine.getActiveSessions();
147
+ if (sessions.length > 0) {
148
+ const cancelled = engine.cancelSession(sessions[0].id);
149
+ expect(typeof cancelled).toBe('boolean');
150
+ }
151
+
152
+ await thinkPromise.catch(() => {
153
+ // Cancellation might cause an error, which is fine
154
+ });
155
+ });
156
+ });
157
+
158
+ describe('statistics', () => {
159
+ it('should return engine statistics', () => {
160
+ const stats = engine.getStats();
161
+
162
+ expect(stats).toHaveProperty('sessions');
163
+ expect(stats).toHaveProperty('activeSessions');
164
+ expect(stats).toHaveProperty('cacheStats');
165
+ expect(typeof stats.sessions).toBe('number');
166
+ expect(typeof stats.activeSessions).toBe('number');
167
+ });
168
+ });
169
+
170
+ describe('dispose', () => {
171
+ it('should clean up resources', () => {
172
+ engine.dispose();
173
+
174
+ const stats = engine.getStats();
175
+ expect(stats.sessions).toBe(0);
176
+ expect(stats.activeSessions).toBe(0);
177
+ });
178
+ });
179
+ });