stigmergy 1.1.6 → 1.2.6

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 (118) hide show
  1. package/LICENSE +18 -18
  2. package/README.md +28 -223
  3. package/STIGMERGY.md +61 -61
  4. package/docs/PROJECT_CONSTITUTION.md +433 -433
  5. package/docs/PROJECT_STRUCTURE_CURRENT.md +80 -80
  6. package/examples/calculator-example.js +72 -72
  7. package/examples/cline_usage_examples.md +364 -364
  8. package/examples/encryption-example.js +67 -67
  9. package/examples/json-parser-example.js +120 -120
  10. package/examples/json-validation-example.js +64 -64
  11. package/examples/rest-client-example.js +52 -52
  12. package/examples/rest_client_example.js +54 -54
  13. package/package.json +15 -7
  14. package/scripts/build.js +74 -74
  15. package/scripts/post-deployment-config.js +296 -296
  16. package/scripts/preinstall-check.js +173 -111
  17. package/scripts/publish.js +58 -268
  18. package/scripts/run-layered-tests.js +247 -0
  19. package/scripts/safe-install.js +139 -139
  20. package/scripts/simple-publish.js +57 -59
  21. package/src/adapters/claude/install_claude_integration.js +292 -0
  22. package/src/adapters/codebuddy/install_codebuddy_integration.js +349 -0
  23. package/src/adapters/codex/install_codex_integration.js +395 -0
  24. package/src/adapters/copilot/install_copilot_integration.js +716 -0
  25. package/src/adapters/gemini/install_gemini_integration.js +304 -0
  26. package/src/adapters/iflow/install_iflow_integration.js +304 -0
  27. package/src/adapters/qoder/install_qoder_integration.js +1090 -0
  28. package/src/adapters/qwen/install_qwen_integration.js +285 -0
  29. package/src/auth.js +173 -173
  30. package/src/auth_command.js +208 -208
  31. package/src/calculator.js +313 -313
  32. package/src/cli/router.js +792 -67
  33. package/src/core/cache_cleaner.js +767 -0
  34. package/src/core/cli_help_analyzer.js +680 -674
  35. package/src/core/cli_parameter_handler.js +132 -127
  36. package/src/core/cli_tools.js +89 -89
  37. package/src/core/coordination/index.js +16 -16
  38. package/src/core/coordination/nodejs/AdapterManager.js +102 -89
  39. package/src/core/coordination/nodejs/CLCommunication.js +132 -124
  40. package/src/core/coordination/nodejs/CLIIntegrationManager.js +272 -236
  41. package/src/core/coordination/nodejs/HealthChecker.js +76 -77
  42. package/src/core/coordination/nodejs/HookDeploymentManager.js +263 -190
  43. package/src/core/coordination/nodejs/StatisticsCollector.js +71 -71
  44. package/src/core/coordination/nodejs/index.js +90 -72
  45. package/src/core/coordination/nodejs/utils/Logger.js +29 -29
  46. package/src/core/enhanced_installer.js +479 -0
  47. package/src/core/enhanced_uninstaller.js +638 -0
  48. package/src/core/error_handler.js +406 -406
  49. package/src/core/installer.js +816 -294
  50. package/src/core/memory_manager.js +83 -83
  51. package/src/core/rest_client.js +160 -160
  52. package/src/core/smart_router.js +249 -146
  53. package/src/core/upgrade_manager.js +420 -0
  54. package/src/data_encryption.js +143 -143
  55. package/src/data_structures.js +440 -440
  56. package/src/deploy.js +55 -55
  57. package/src/index.js +30 -30
  58. package/src/test/cli-availability-checker.js +194 -0
  59. package/src/test/test-environment.js +289 -0
  60. package/src/utils/helpers.js +35 -35
  61. package/src/utils.js +921 -915
  62. package/src/weatherProcessor.js +228 -228
  63. package/test/calculator.test.js +0 -215
  64. package/test/collision-test.js +0 -26
  65. package/test/comprehensive-execution-test.js +0 -428
  66. package/test/conflict-prevention-test.js +0 -95
  67. package/test/cross-cli-detection-test.js +0 -33
  68. package/test/csv-processing-test.js +0 -36
  69. package/test/deploy-hooks-test.js +0 -250
  70. package/test/e2e/claude-cli-test.js +0 -128
  71. package/test/e2e/collaboration-test.js +0 -75
  72. package/test/e2e/comprehensive-test.js +0 -431
  73. package/test/e2e/error-handling-test.js +0 -90
  74. package/test/e2e/individual-tool-test.js +0 -143
  75. package/test/e2e/other-cli-test.js +0 -130
  76. package/test/e2e/qoder-cli-test.js +0 -128
  77. package/test/e2e/run-e2e-tests.js +0 -73
  78. package/test/e2e/test-data.js +0 -88
  79. package/test/e2e/test-utils.js +0 -222
  80. package/test/encryption-simple-test.js +0 -110
  81. package/test/encryption.test.js +0 -129
  82. package/test/enhanced-main-alignment.test.js +0 -298
  83. package/test/error-handling-test.js +0 -341
  84. package/test/fibonacci.test.js +0 -178
  85. package/test/final-deploy-test.js +0 -221
  86. package/test/final-install-test.js +0 -226
  87. package/test/hash-table-demo.js +0 -33
  88. package/test/hash-table-test.js +0 -26
  89. package/test/hash_table_test.js +0 -114
  90. package/test/hook-system-integration-test.js +0 -307
  91. package/test/iflow-integration-test.js +0 -292
  92. package/test/improved-install-test.js +0 -362
  93. package/test/install-command-test.js +0 -370
  94. package/test/json-parser-test.js +0 -161
  95. package/test/json-validation-test.js +0 -164
  96. package/test/natural-language-skills-test.js +0 -320
  97. package/test/nl-integration-test.js +0 -179
  98. package/test/parameter-parsing-test.js +0 -143
  99. package/test/plugin-deployment-test.js +0 -316
  100. package/test/postinstall-test.js +0 -269
  101. package/test/python-plugins-test.js +0 -259
  102. package/test/real-test.js +0 -435
  103. package/test/remaining-adapters-test.js +0 -256
  104. package/test/rest-client-test.js +0 -56
  105. package/test/rest_client.test.js +0 -85
  106. package/test/simple-iflow-hook-test.js +0 -137
  107. package/test/system-compatibility-test.js +0 -467
  108. package/test/tdd-deploy-fix-test.js +0 -324
  109. package/test/tdd-fixes-test.js +0 -211
  110. package/test/third-party-skills-test.js +0 -321
  111. package/test/tool-selection-integration-test.js +0 -158
  112. package/test/unit/calculator-full.test.js +0 -191
  113. package/test/unit/calculator-simple.test.js +0 -96
  114. package/test/unit/calculator.test.js +0 -97
  115. package/test/unit/cli-scanner.test.js +0 -291
  116. package/test/unit/cli_parameter_handler.test.js +0 -116
  117. package/test/unit/cross-cli-executor.test.js +0 -399
  118. package/test/weather-processor.test.js +0 -104
@@ -1,440 +1,440 @@
1
- /**
2
- * Data Structures for the Stigmergy CLI
3
- */
4
-
5
- /**
6
- * HashTable implementation with collision handling using chaining and open addressing
7
- */
8
- class HashTable {
9
- /**
10
- * Create a new HashTable
11
- * @param {number} size - Initial size of the hash table
12
- * @param {string} collisionStrategy - Collision handling strategy ('chaining' or 'openAddressing')
13
- */
14
- constructor(size = 53, collisionStrategy = 'chaining') {
15
- this.size = size;
16
- this.collisionStrategy = collisionStrategy;
17
-
18
- if (collisionStrategy === 'chaining') {
19
- this.buckets = new Array(size).fill(null).map(() => []);
20
- } else if (collisionStrategy === 'openAddressing') {
21
- this.buckets = new Array(size).fill(null);
22
- this.deleted = new Array(size).fill(false);
23
- } else {
24
- throw new Error(
25
- 'Invalid collision strategy. Use "chaining" or "openAddressing"',
26
- );
27
- }
28
-
29
- this.count = 0;
30
- this.loadFactorThreshold = 0.75;
31
- }
32
-
33
- /**
34
- * Hash function to convert a key to an index
35
- * @param {string|number} key - Key to hash
36
- * @returns {number} Index in the hash table
37
- */
38
- _hash(key) {
39
- if (typeof key === 'number') {
40
- return key % this.size;
41
- }
42
-
43
- let hash = 0;
44
- const PRIME = 31;
45
-
46
- for (let i = 0; i < key.length; i++) {
47
- hash = (hash * PRIME + key.charCodeAt(i)) % this.size;
48
- }
49
-
50
- return hash;
51
- }
52
-
53
- /**
54
- * Secondary hash function for double hashing in open addressing
55
- * @param {string|number} key - Key to hash
56
- * @returns {number} Secondary hash value
57
- */
58
- _hash2(key) {
59
- if (typeof key === 'number') {
60
- return 7 - (key % 7);
61
- }
62
-
63
- let hash = 0;
64
- for (let i = 0; i < key.length; i++) {
65
- hash = (hash * 3 + key.charCodeAt(i)) % this.size;
66
- }
67
-
68
- return 7 - (hash % 7);
69
- }
70
-
71
- /**
72
- * Resize the hash table when load factor exceeds threshold
73
- */
74
- _resize() {
75
- const oldBuckets = this.buckets;
76
- const oldDeleted = this.deleted;
77
- const oldSize = this.size;
78
-
79
- // Double the size
80
- this.size *= 2;
81
- this.count = 0;
82
-
83
- if (this.collisionStrategy === 'chaining') {
84
- this.buckets = new Array(this.size).fill(null).map(() => []);
85
- } else {
86
- this.buckets = new Array(this.size).fill(null);
87
- this.deleted = new Array(this.size).fill(false);
88
- }
89
-
90
- // Rehash all existing elements
91
- if (this.collisionStrategy === 'chaining') {
92
- for (let i = 0; i < oldSize; i++) {
93
- const bucket = oldBuckets[i];
94
- if (bucket) {
95
- for (const [key, value] of bucket) {
96
- this.set(key, value);
97
- }
98
- }
99
- }
100
- } else {
101
- for (let i = 0; i < oldSize; i++) {
102
- if (oldBuckets[i] !== null && !oldDeleted[i]) {
103
- this.set(oldBuckets[i][0], oldBuckets[i][1]);
104
- }
105
- }
106
- }
107
- }
108
-
109
- /**
110
- * Set a key-value pair in the hash table
111
- * @param {string|number} key - Key to store
112
- * @param {*} value - Value to store
113
- * @returns {HashTable} The hash table instance
114
- */
115
- set(key, value) {
116
- // Check if resize is needed
117
- if (this.count >= this.size * this.loadFactorThreshold) {
118
- this._resize();
119
- }
120
-
121
- if (this.collisionStrategy === 'chaining') {
122
- return this._setChaining(key, value);
123
- } else {
124
- return this._setOpenAddressing(key, value);
125
- }
126
- }
127
-
128
- /**
129
- * Set a key-value pair using chaining
130
- * @private
131
- */
132
- _setChaining(key, value) {
133
- const index = this._hash(key);
134
- const bucket = this.buckets[index];
135
-
136
- // Check if key already exists
137
- for (let i = 0; i < bucket.length; i++) {
138
- if (bucket[i][0] === key) {
139
- bucket[i][1] = value; // Update existing value
140
- return this;
141
- }
142
- }
143
-
144
- // Add new key-value pair
145
- bucket.push([key, value]);
146
- this.count++;
147
- return this;
148
- }
149
-
150
- /**
151
- * Set a key-value pair using open addressing with double hashing
152
- * @private
153
- */
154
- _setOpenAddressing(key, value) {
155
- let index = this._hash(key);
156
- let originalIndex = index;
157
- let i = 0;
158
-
159
- // Probe until we find an empty slot or deleted slot
160
- while (
161
- this.buckets[index] !== null &&
162
- !this.deleted[index] &&
163
- this.buckets[index][0] !== key
164
- ) {
165
- i++;
166
- index = (originalIndex + i * this._hash2(key)) % this.size;
167
-
168
- // If we've checked all slots, the table is full
169
- if (i >= this.size) {
170
- // Instead of throwing an error, we'll resize and try again
171
- this._resize();
172
- return this.set(key, value);
173
- }
174
- }
175
-
176
- // If key already exists, update the value
177
- if (
178
- this.buckets[index] !== null &&
179
- !this.deleted[index] &&
180
- this.buckets[index][0] === key
181
- ) {
182
- this.buckets[index][1] = value;
183
- } else {
184
- // Insert new key-value pair
185
- this.buckets[index] = [key, value];
186
- this.deleted[index] = false;
187
- this.count++;
188
- }
189
-
190
- return this;
191
- }
192
-
193
- /**
194
- * Get a value by its key
195
- * @param {string|number} key - Key to look up
196
- * @returns {*} The value associated with the key, or undefined if not found
197
- */
198
- get(key) {
199
- if (this.collisionStrategy === 'chaining') {
200
- return this._getChaining(key);
201
- } else {
202
- return this._getOpenAddressing(key);
203
- }
204
- }
205
-
206
- /**
207
- * Get a value using chaining
208
- * @private
209
- */
210
- _getChaining(key) {
211
- const index = this._hash(key);
212
- const bucket = this.buckets[index];
213
-
214
- for (let i = 0; i < bucket.length; i++) {
215
- if (bucket[i][0] === key) {
216
- return bucket[i][1];
217
- }
218
- }
219
-
220
- return undefined;
221
- }
222
-
223
- /**
224
- * Get a value using open addressing
225
- * @private
226
- */
227
- _getOpenAddressing(key) {
228
- let index = this._hash(key);
229
- let i = 0;
230
-
231
- // Probe until we find the key or an empty slot
232
- while (this.buckets[index] !== null) {
233
- if (!this.deleted[index] && this.buckets[index][0] === key) {
234
- return this.buckets[index][1];
235
- }
236
-
237
- i++;
238
- index = (this._hash(key) + i * this._hash2(key)) % this.size;
239
-
240
- // Prevent infinite loop
241
- if (i >= this.size) {
242
- break;
243
- }
244
- }
245
-
246
- return undefined;
247
- }
248
-
249
- /**
250
- * Delete a key-value pair from the hash table
251
- * @param {string|number} key - Key to delete
252
- * @returns {boolean} True if the key was found and deleted, false otherwise
253
- */
254
- delete(key) {
255
- if (this.collisionStrategy === 'chaining') {
256
- return this._deleteChaining(key);
257
- } else {
258
- return this._deleteOpenAddressing(key);
259
- }
260
- }
261
-
262
- /**
263
- * Delete a key-value pair using chaining
264
- * @private
265
- */
266
- _deleteChaining(key) {
267
- const index = this._hash(key);
268
- const bucket = this.buckets[index];
269
-
270
- for (let i = 0; i < bucket.length; i++) {
271
- if (bucket[i][0] === key) {
272
- bucket.splice(i, 1);
273
- this.count--;
274
- return true;
275
- }
276
- }
277
-
278
- return false;
279
- }
280
-
281
- /**
282
- * Delete a key-value pair using open addressing
283
- * @private
284
- */
285
- _deleteOpenAddressing(key) {
286
- let index = this._hash(key);
287
- let i = 0;
288
-
289
- // Probe until we find the key or an empty slot
290
- while (this.buckets[index] !== null) {
291
- if (!this.deleted[index] && this.buckets[index][0] === key) {
292
- this.deleted[index] = true;
293
- this.count--;
294
- return true;
295
- }
296
-
297
- i++;
298
- index = (this._hash(key) + i * this._hash2(key)) % this.size;
299
-
300
- // Prevent infinite loop
301
- if (i >= this.size) {
302
- break;
303
- }
304
- }
305
-
306
- return false;
307
- }
308
-
309
- /**
310
- * Check if a key exists in the hash table
311
- * @param {string|number} key - Key to check
312
- * @returns {boolean} True if the key exists, false otherwise
313
- */
314
- has(key) {
315
- return this.get(key) !== undefined;
316
- }
317
-
318
- /**
319
- * Get all keys in the hash table
320
- * @returns {Array} Array of all keys
321
- */
322
- keys() {
323
- const keysArr = [];
324
-
325
- if (this.collisionStrategy === 'chaining') {
326
- for (let i = 0; i < this.size; i++) {
327
- const bucket = this.buckets[i];
328
- if (bucket) {
329
- for (const [key, __] of bucket) {
330
- keysArr.push(key);
331
- }
332
- }
333
- }
334
- } else {
335
- for (let i = 0; i < this.size; i++) {
336
- if (this.buckets[i] !== null && !this.deleted[i]) {
337
- keysArr.push(this.buckets[i][0]);
338
- }
339
- }
340
- }
341
-
342
- return keysArr;
343
- }
344
-
345
- /**
346
- * Get all values in the hash table
347
- * @returns {Array} Array of all values
348
- */
349
- values() {
350
- const valuesArr = [];
351
-
352
- if (this.collisionStrategy === 'chaining') {
353
- for (let i = 0; i < this.size; i++) {
354
- const bucket = this.buckets[i];
355
- if (bucket) {
356
- for (const [__, value] of bucket) {
357
- valuesArr.push(value);
358
- }
359
- }
360
- }
361
- } else {
362
- for (let i = 0; i < this.size; i++) {
363
- if (this.buckets[i] !== null && !this.deleted[i]) {
364
- valuesArr.push(this.buckets[i][1]);
365
- }
366
- }
367
- }
368
-
369
- return valuesArr;
370
- }
371
-
372
- /**
373
- * Get all key-value pairs in the hash table
374
- * @returns {Array} Array of all key-value pairs
375
- */
376
- entries() {
377
- const entriesArr = [];
378
-
379
- if (this.collisionStrategy === 'chaining') {
380
- for (let i = 0; i < this.size; i++) {
381
- const bucket = this.buckets[i];
382
- if (bucket) {
383
- for (const [key, value] of bucket) {
384
- entriesArr.push([key, value]);
385
- }
386
- }
387
- }
388
- } else {
389
- for (let i = 0; i < this.size; i++) {
390
- if (this.buckets[i] !== null && !this.deleted[i]) {
391
- entriesArr.push([this.buckets[i][0], this.buckets[i][1]]);
392
- }
393
- }
394
- }
395
-
396
- return entriesArr;
397
- }
398
-
399
- /**
400
- * Clear the hash table
401
- */
402
- clear() {
403
- if (this.collisionStrategy === 'chaining') {
404
- this.buckets = new Array(this.size).fill(null).map(() => []);
405
- } else {
406
- this.buckets = new Array(this.size).fill(null);
407
- this.deleted = new Array(this.size).fill(false);
408
- }
409
-
410
- this.count = 0;
411
- }
412
-
413
- /**
414
- * Get the size of the hash table
415
- * @returns {number} Number of key-value pairs in the hash table
416
- */
417
- getSize() {
418
- return this.count;
419
- }
420
-
421
- /**
422
- * Check if the hash table is empty
423
- * @returns {boolean} True if the hash table is empty, false otherwise
424
- */
425
- isEmpty() {
426
- return this.count === 0;
427
- }
428
-
429
- /**
430
- * Get the load factor of the hash table
431
- * @returns {number} Load factor (number of elements / table size)
432
- */
433
- getLoadFactor() {
434
- return this.count / this.size;
435
- }
436
- }
437
-
438
- module.exports = {
439
- HashTable,
440
- };
1
+ /**
2
+ * Data Structures for the Stigmergy CLI
3
+ */
4
+
5
+ /**
6
+ * HashTable implementation with collision handling using chaining and open addressing
7
+ */
8
+ class HashTable {
9
+ /**
10
+ * Create a new HashTable
11
+ * @param {number} size - Initial size of the hash table
12
+ * @param {string} collisionStrategy - Collision handling strategy ('chaining' or 'openAddressing')
13
+ */
14
+ constructor(size = 53, collisionStrategy = 'chaining') {
15
+ this.size = size;
16
+ this.collisionStrategy = collisionStrategy;
17
+
18
+ if (collisionStrategy === 'chaining') {
19
+ this.buckets = new Array(size).fill(null).map(() => []);
20
+ } else if (collisionStrategy === 'openAddressing') {
21
+ this.buckets = new Array(size).fill(null);
22
+ this.deleted = new Array(size).fill(false);
23
+ } else {
24
+ throw new Error(
25
+ 'Invalid collision strategy. Use "chaining" or "openAddressing"',
26
+ );
27
+ }
28
+
29
+ this.count = 0;
30
+ this.loadFactorThreshold = 0.75;
31
+ }
32
+
33
+ /**
34
+ * Hash function to convert a key to an index
35
+ * @param {string|number} key - Key to hash
36
+ * @returns {number} Index in the hash table
37
+ */
38
+ _hash(key) {
39
+ if (typeof key === 'number') {
40
+ return key % this.size;
41
+ }
42
+
43
+ let hash = 0;
44
+ const PRIME = 31;
45
+
46
+ for (let i = 0; i < key.length; i++) {
47
+ hash = (hash * PRIME + key.charCodeAt(i)) % this.size;
48
+ }
49
+
50
+ return hash;
51
+ }
52
+
53
+ /**
54
+ * Secondary hash function for double hashing in open addressing
55
+ * @param {string|number} key - Key to hash
56
+ * @returns {number} Secondary hash value
57
+ */
58
+ _hash2(key) {
59
+ if (typeof key === 'number') {
60
+ return 7 - (key % 7);
61
+ }
62
+
63
+ let hash = 0;
64
+ for (let i = 0; i < key.length; i++) {
65
+ hash = (hash * 3 + key.charCodeAt(i)) % this.size;
66
+ }
67
+
68
+ return 7 - (hash % 7);
69
+ }
70
+
71
+ /**
72
+ * Resize the hash table when load factor exceeds threshold
73
+ */
74
+ _resize() {
75
+ const oldBuckets = this.buckets;
76
+ const oldDeleted = this.deleted;
77
+ const oldSize = this.size;
78
+
79
+ // Double the size
80
+ this.size *= 2;
81
+ this.count = 0;
82
+
83
+ if (this.collisionStrategy === 'chaining') {
84
+ this.buckets = new Array(this.size).fill(null).map(() => []);
85
+ } else {
86
+ this.buckets = new Array(this.size).fill(null);
87
+ this.deleted = new Array(this.size).fill(false);
88
+ }
89
+
90
+ // Rehash all existing elements
91
+ if (this.collisionStrategy === 'chaining') {
92
+ for (let i = 0; i < oldSize; i++) {
93
+ const bucket = oldBuckets[i];
94
+ if (bucket) {
95
+ for (const [key, value] of bucket) {
96
+ this.set(key, value);
97
+ }
98
+ }
99
+ }
100
+ } else {
101
+ for (let i = 0; i < oldSize; i++) {
102
+ if (oldBuckets[i] !== null && !oldDeleted[i]) {
103
+ this.set(oldBuckets[i][0], oldBuckets[i][1]);
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Set a key-value pair in the hash table
111
+ * @param {string|number} key - Key to store
112
+ * @param {*} value - Value to store
113
+ * @returns {HashTable} The hash table instance
114
+ */
115
+ set(key, value) {
116
+ // Check if resize is needed
117
+ if (this.count >= this.size * this.loadFactorThreshold) {
118
+ this._resize();
119
+ }
120
+
121
+ if (this.collisionStrategy === 'chaining') {
122
+ return this._setChaining(key, value);
123
+ } else {
124
+ return this._setOpenAddressing(key, value);
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Set a key-value pair using chaining
130
+ * @private
131
+ */
132
+ _setChaining(key, value) {
133
+ const index = this._hash(key);
134
+ const bucket = this.buckets[index];
135
+
136
+ // Check if key already exists
137
+ for (let i = 0; i < bucket.length; i++) {
138
+ if (bucket[i][0] === key) {
139
+ bucket[i][1] = value; // Update existing value
140
+ return this;
141
+ }
142
+ }
143
+
144
+ // Add new key-value pair
145
+ bucket.push([key, value]);
146
+ this.count++;
147
+ return this;
148
+ }
149
+
150
+ /**
151
+ * Set a key-value pair using open addressing with double hashing
152
+ * @private
153
+ */
154
+ _setOpenAddressing(key, value) {
155
+ let index = this._hash(key);
156
+ let originalIndex = index;
157
+ let i = 0;
158
+
159
+ // Probe until we find an empty slot or deleted slot
160
+ while (
161
+ this.buckets[index] !== null &&
162
+ !this.deleted[index] &&
163
+ this.buckets[index][0] !== key
164
+ ) {
165
+ i++;
166
+ index = (originalIndex + i * this._hash2(key)) % this.size;
167
+
168
+ // If we've checked all slots, the table is full
169
+ if (i >= this.size) {
170
+ // Instead of throwing an error, we'll resize and try again
171
+ this._resize();
172
+ return this.set(key, value);
173
+ }
174
+ }
175
+
176
+ // If key already exists, update the value
177
+ if (
178
+ this.buckets[index] !== null &&
179
+ !this.deleted[index] &&
180
+ this.buckets[index][0] === key
181
+ ) {
182
+ this.buckets[index][1] = value;
183
+ } else {
184
+ // Insert new key-value pair
185
+ this.buckets[index] = [key, value];
186
+ this.deleted[index] = false;
187
+ this.count++;
188
+ }
189
+
190
+ return this;
191
+ }
192
+
193
+ /**
194
+ * Get a value by its key
195
+ * @param {string|number} key - Key to look up
196
+ * @returns {*} The value associated with the key, or undefined if not found
197
+ */
198
+ get(key) {
199
+ if (this.collisionStrategy === 'chaining') {
200
+ return this._getChaining(key);
201
+ } else {
202
+ return this._getOpenAddressing(key);
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Get a value using chaining
208
+ * @private
209
+ */
210
+ _getChaining(key) {
211
+ const index = this._hash(key);
212
+ const bucket = this.buckets[index];
213
+
214
+ for (let i = 0; i < bucket.length; i++) {
215
+ if (bucket[i][0] === key) {
216
+ return bucket[i][1];
217
+ }
218
+ }
219
+
220
+ return undefined;
221
+ }
222
+
223
+ /**
224
+ * Get a value using open addressing
225
+ * @private
226
+ */
227
+ _getOpenAddressing(key) {
228
+ let index = this._hash(key);
229
+ let i = 0;
230
+
231
+ // Probe until we find the key or an empty slot
232
+ while (this.buckets[index] !== null) {
233
+ if (!this.deleted[index] && this.buckets[index][0] === key) {
234
+ return this.buckets[index][1];
235
+ }
236
+
237
+ i++;
238
+ index = (this._hash(key) + i * this._hash2(key)) % this.size;
239
+
240
+ // Prevent infinite loop
241
+ if (i >= this.size) {
242
+ break;
243
+ }
244
+ }
245
+
246
+ return undefined;
247
+ }
248
+
249
+ /**
250
+ * Delete a key-value pair from the hash table
251
+ * @param {string|number} key - Key to delete
252
+ * @returns {boolean} True if the key was found and deleted, false otherwise
253
+ */
254
+ delete(key) {
255
+ if (this.collisionStrategy === 'chaining') {
256
+ return this._deleteChaining(key);
257
+ } else {
258
+ return this._deleteOpenAddressing(key);
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Delete a key-value pair using chaining
264
+ * @private
265
+ */
266
+ _deleteChaining(key) {
267
+ const index = this._hash(key);
268
+ const bucket = this.buckets[index];
269
+
270
+ for (let i = 0; i < bucket.length; i++) {
271
+ if (bucket[i][0] === key) {
272
+ bucket.splice(i, 1);
273
+ this.count--;
274
+ return true;
275
+ }
276
+ }
277
+
278
+ return false;
279
+ }
280
+
281
+ /**
282
+ * Delete a key-value pair using open addressing
283
+ * @private
284
+ */
285
+ _deleteOpenAddressing(key) {
286
+ let index = this._hash(key);
287
+ let i = 0;
288
+
289
+ // Probe until we find the key or an empty slot
290
+ while (this.buckets[index] !== null) {
291
+ if (!this.deleted[index] && this.buckets[index][0] === key) {
292
+ this.deleted[index] = true;
293
+ this.count--;
294
+ return true;
295
+ }
296
+
297
+ i++;
298
+ index = (this._hash(key) + i * this._hash2(key)) % this.size;
299
+
300
+ // Prevent infinite loop
301
+ if (i >= this.size) {
302
+ break;
303
+ }
304
+ }
305
+
306
+ return false;
307
+ }
308
+
309
+ /**
310
+ * Check if a key exists in the hash table
311
+ * @param {string|number} key - Key to check
312
+ * @returns {boolean} True if the key exists, false otherwise
313
+ */
314
+ has(key) {
315
+ return this.get(key) !== undefined;
316
+ }
317
+
318
+ /**
319
+ * Get all keys in the hash table
320
+ * @returns {Array} Array of all keys
321
+ */
322
+ keys() {
323
+ const keysArr = [];
324
+
325
+ if (this.collisionStrategy === 'chaining') {
326
+ for (let i = 0; i < this.size; i++) {
327
+ const bucket = this.buckets[i];
328
+ if (bucket) {
329
+ for (const [key, __] of bucket) {
330
+ keysArr.push(key);
331
+ }
332
+ }
333
+ }
334
+ } else {
335
+ for (let i = 0; i < this.size; i++) {
336
+ if (this.buckets[i] !== null && !this.deleted[i]) {
337
+ keysArr.push(this.buckets[i][0]);
338
+ }
339
+ }
340
+ }
341
+
342
+ return keysArr;
343
+ }
344
+
345
+ /**
346
+ * Get all values in the hash table
347
+ * @returns {Array} Array of all values
348
+ */
349
+ values() {
350
+ const valuesArr = [];
351
+
352
+ if (this.collisionStrategy === 'chaining') {
353
+ for (let i = 0; i < this.size; i++) {
354
+ const bucket = this.buckets[i];
355
+ if (bucket) {
356
+ for (const [__, value] of bucket) {
357
+ valuesArr.push(value);
358
+ }
359
+ }
360
+ }
361
+ } else {
362
+ for (let i = 0; i < this.size; i++) {
363
+ if (this.buckets[i] !== null && !this.deleted[i]) {
364
+ valuesArr.push(this.buckets[i][1]);
365
+ }
366
+ }
367
+ }
368
+
369
+ return valuesArr;
370
+ }
371
+
372
+ /**
373
+ * Get all key-value pairs in the hash table
374
+ * @returns {Array} Array of all key-value pairs
375
+ */
376
+ entries() {
377
+ const entriesArr = [];
378
+
379
+ if (this.collisionStrategy === 'chaining') {
380
+ for (let i = 0; i < this.size; i++) {
381
+ const bucket = this.buckets[i];
382
+ if (bucket) {
383
+ for (const [key, value] of bucket) {
384
+ entriesArr.push([key, value]);
385
+ }
386
+ }
387
+ }
388
+ } else {
389
+ for (let i = 0; i < this.size; i++) {
390
+ if (this.buckets[i] !== null && !this.deleted[i]) {
391
+ entriesArr.push([this.buckets[i][0], this.buckets[i][1]]);
392
+ }
393
+ }
394
+ }
395
+
396
+ return entriesArr;
397
+ }
398
+
399
+ /**
400
+ * Clear the hash table
401
+ */
402
+ clear() {
403
+ if (this.collisionStrategy === 'chaining') {
404
+ this.buckets = new Array(this.size).fill(null).map(() => []);
405
+ } else {
406
+ this.buckets = new Array(this.size).fill(null);
407
+ this.deleted = new Array(this.size).fill(false);
408
+ }
409
+
410
+ this.count = 0;
411
+ }
412
+
413
+ /**
414
+ * Get the size of the hash table
415
+ * @returns {number} Number of key-value pairs in the hash table
416
+ */
417
+ getSize() {
418
+ return this.count;
419
+ }
420
+
421
+ /**
422
+ * Check if the hash table is empty
423
+ * @returns {boolean} True if the hash table is empty, false otherwise
424
+ */
425
+ isEmpty() {
426
+ return this.count === 0;
427
+ }
428
+
429
+ /**
430
+ * Get the load factor of the hash table
431
+ * @returns {number} Load factor (number of elements / table size)
432
+ */
433
+ getLoadFactor() {
434
+ return this.count / this.size;
435
+ }
436
+ }
437
+
438
+ module.exports = {
439
+ HashTable,
440
+ };