bluera-knowledge 0.9.32 → 0.9.36

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 (198) hide show
  1. package/.claude/hooks/post-edit-check.sh +5 -3
  2. package/.claude/skills/atomic-commits/SKILL.md +3 -1
  3. package/.husky/pre-commit +3 -2
  4. package/.prettierrc +9 -0
  5. package/.versionrc.json +1 -1
  6. package/CHANGELOG.md +70 -0
  7. package/CLAUDE.md +6 -0
  8. package/README.md +25 -13
  9. package/bun.lock +277 -33
  10. package/dist/{chunk-L2YVNC63.js → chunk-6FHWC36B.js} +9 -1
  11. package/dist/chunk-6FHWC36B.js.map +1 -0
  12. package/dist/{chunk-RST4XGRL.js → chunk-DC7CGSGT.js} +288 -241
  13. package/dist/chunk-DC7CGSGT.js.map +1 -0
  14. package/dist/{chunk-6PBP5DVD.js → chunk-WFNPNAAP.js} +3212 -3054
  15. package/dist/chunk-WFNPNAAP.js.map +1 -0
  16. package/dist/{chunk-WT2DAEO7.js → chunk-Z2KKVH45.js} +548 -482
  17. package/dist/chunk-Z2KKVH45.js.map +1 -0
  18. package/dist/index.js +871 -758
  19. package/dist/index.js.map +1 -1
  20. package/dist/mcp/server.js +3 -3
  21. package/dist/watch.service-BJV3TI3F.js +7 -0
  22. package/dist/workers/background-worker-cli.js +97 -71
  23. package/dist/workers/background-worker-cli.js.map +1 -1
  24. package/eslint.config.js +43 -1
  25. package/package.json +18 -11
  26. package/plugin.json +8 -0
  27. package/python/requirements.txt +1 -1
  28. package/src/analysis/ast-parser.test.ts +12 -11
  29. package/src/analysis/ast-parser.ts +28 -22
  30. package/src/analysis/code-graph.test.ts +52 -62
  31. package/src/analysis/code-graph.ts +9 -13
  32. package/src/analysis/dependency-usage-analyzer.test.ts +91 -271
  33. package/src/analysis/dependency-usage-analyzer.ts +52 -24
  34. package/src/analysis/go-ast-parser.test.ts +22 -22
  35. package/src/analysis/go-ast-parser.ts +18 -25
  36. package/src/analysis/parser-factory.test.ts +9 -9
  37. package/src/analysis/parser-factory.ts +3 -3
  38. package/src/analysis/python-ast-parser.test.ts +27 -27
  39. package/src/analysis/python-ast-parser.ts +2 -2
  40. package/src/analysis/repo-url-resolver.test.ts +82 -82
  41. package/src/analysis/rust-ast-parser.test.ts +19 -19
  42. package/src/analysis/rust-ast-parser.ts +17 -27
  43. package/src/analysis/tree-sitter-parser.test.ts +3 -3
  44. package/src/analysis/tree-sitter-parser.ts +10 -16
  45. package/src/cli/commands/crawl.test.ts +40 -24
  46. package/src/cli/commands/crawl.ts +186 -166
  47. package/src/cli/commands/index-cmd.test.ts +90 -90
  48. package/src/cli/commands/index-cmd.ts +52 -36
  49. package/src/cli/commands/mcp.test.ts +6 -6
  50. package/src/cli/commands/mcp.ts +2 -2
  51. package/src/cli/commands/plugin-api.test.ts +16 -18
  52. package/src/cli/commands/plugin-api.ts +9 -6
  53. package/src/cli/commands/search.test.ts +16 -7
  54. package/src/cli/commands/search.ts +124 -87
  55. package/src/cli/commands/serve.test.ts +67 -25
  56. package/src/cli/commands/serve.ts +18 -3
  57. package/src/cli/commands/setup.test.ts +176 -101
  58. package/src/cli/commands/setup.ts +140 -117
  59. package/src/cli/commands/store.test.ts +82 -53
  60. package/src/cli/commands/store.ts +56 -37
  61. package/src/cli/program.ts +2 -2
  62. package/src/crawl/article-converter.test.ts +4 -1
  63. package/src/crawl/article-converter.ts +46 -31
  64. package/src/crawl/bridge.test.ts +240 -132
  65. package/src/crawl/bridge.ts +87 -30
  66. package/src/crawl/claude-client.test.ts +124 -56
  67. package/src/crawl/claude-client.ts +7 -15
  68. package/src/crawl/intelligent-crawler.test.ts +65 -22
  69. package/src/crawl/intelligent-crawler.ts +86 -53
  70. package/src/crawl/markdown-utils.ts +1 -4
  71. package/src/db/embeddings.ts +4 -6
  72. package/src/db/lance.test.ts +4 -4
  73. package/src/db/lance.ts +16 -12
  74. package/src/index.ts +26 -17
  75. package/src/logging/index.ts +1 -5
  76. package/src/logging/logger.ts +3 -5
  77. package/src/logging/payload.test.ts +1 -1
  78. package/src/logging/payload.ts +3 -5
  79. package/src/mcp/commands/index.ts +2 -2
  80. package/src/mcp/commands/job.commands.ts +12 -18
  81. package/src/mcp/commands/meta.commands.ts +13 -13
  82. package/src/mcp/commands/registry.ts +5 -8
  83. package/src/mcp/commands/store.commands.ts +19 -19
  84. package/src/mcp/handlers/execute.handler.test.ts +10 -10
  85. package/src/mcp/handlers/execute.handler.ts +4 -5
  86. package/src/mcp/handlers/index.ts +10 -14
  87. package/src/mcp/handlers/job.handler.test.ts +10 -10
  88. package/src/mcp/handlers/job.handler.ts +22 -25
  89. package/src/mcp/handlers/search.handler.test.ts +36 -65
  90. package/src/mcp/handlers/search.handler.ts +135 -104
  91. package/src/mcp/handlers/store.handler.test.ts +41 -52
  92. package/src/mcp/handlers/store.handler.ts +108 -88
  93. package/src/mcp/schemas/index.test.ts +73 -68
  94. package/src/mcp/schemas/index.ts +18 -12
  95. package/src/mcp/server.test.ts +1 -1
  96. package/src/mcp/server.ts +59 -46
  97. package/src/plugin/commands.test.ts +230 -95
  98. package/src/plugin/commands.ts +24 -25
  99. package/src/plugin/dependency-analyzer.test.ts +52 -52
  100. package/src/plugin/dependency-analyzer.ts +85 -22
  101. package/src/plugin/git-clone.test.ts +24 -13
  102. package/src/plugin/git-clone.ts +3 -7
  103. package/src/server/app.test.ts +109 -109
  104. package/src/server/app.ts +32 -23
  105. package/src/server/index.test.ts +64 -66
  106. package/src/services/chunking.service.test.ts +32 -32
  107. package/src/services/chunking.service.ts +16 -9
  108. package/src/services/code-graph.service.test.ts +30 -36
  109. package/src/services/code-graph.service.ts +24 -10
  110. package/src/services/code-unit.service.test.ts +55 -11
  111. package/src/services/code-unit.service.ts +85 -11
  112. package/src/services/config.service.test.ts +37 -18
  113. package/src/services/config.service.ts +30 -7
  114. package/src/services/index.service.test.ts +49 -18
  115. package/src/services/index.service.ts +98 -48
  116. package/src/services/index.ts +6 -9
  117. package/src/services/job.service.test.ts +22 -22
  118. package/src/services/job.service.ts +18 -18
  119. package/src/services/project-root.service.test.ts +1 -3
  120. package/src/services/search.service.test.ts +248 -120
  121. package/src/services/search.service.ts +286 -156
  122. package/src/services/services.test.ts +1 -1
  123. package/src/services/snippet.service.test.ts +14 -6
  124. package/src/services/snippet.service.ts +7 -5
  125. package/src/services/store.service.test.ts +68 -29
  126. package/src/services/store.service.ts +41 -12
  127. package/src/services/watch.service.test.ts +34 -14
  128. package/src/services/watch.service.ts +11 -1
  129. package/src/types/brands.test.ts +3 -1
  130. package/src/types/index.ts +2 -13
  131. package/src/types/search.ts +10 -8
  132. package/src/utils/type-guards.test.ts +20 -15
  133. package/src/utils/type-guards.ts +1 -1
  134. package/src/workers/background-worker-cli.ts +28 -30
  135. package/src/workers/background-worker.test.ts +54 -40
  136. package/src/workers/background-worker.ts +76 -60
  137. package/src/workers/pid-file.test.ts +167 -0
  138. package/src/workers/pid-file.ts +82 -0
  139. package/src/workers/spawn-worker.test.ts +22 -10
  140. package/src/workers/spawn-worker.ts +6 -6
  141. package/tests/analysis/ast-parser.test.ts +3 -3
  142. package/tests/analysis/code-graph.test.ts +5 -5
  143. package/tests/fixtures/code-snippets/api/error-handling.ts +4 -15
  144. package/tests/fixtures/code-snippets/api/rest-controller.ts +3 -9
  145. package/tests/fixtures/code-snippets/auth/jwt-auth.ts +5 -21
  146. package/tests/fixtures/code-snippets/auth/oauth-flow.ts +4 -4
  147. package/tests/fixtures/code-snippets/database/repository-pattern.ts +11 -3
  148. package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/handler.ts +2 -2
  149. package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-pages/handler.ts +1 -1
  150. package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/serve-static.ts +2 -2
  151. package/tests/fixtures/corpus/oss-repos/hono/src/client/client.ts +2 -2
  152. package/tests/fixtures/corpus/oss-repos/hono/src/client/types.ts +22 -20
  153. package/tests/fixtures/corpus/oss-repos/hono/src/context.ts +13 -10
  154. package/tests/fixtures/corpus/oss-repos/hono/src/helper/accepts/accepts.ts +10 -7
  155. package/tests/fixtures/corpus/oss-repos/hono/src/helper/adapter/index.ts +2 -2
  156. package/tests/fixtures/corpus/oss-repos/hono/src/helper/css/index.ts +1 -1
  157. package/tests/fixtures/corpus/oss-repos/hono/src/helper/factory/index.ts +16 -16
  158. package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/ssg.ts +2 -2
  159. package/tests/fixtures/corpus/oss-repos/hono/src/hono-base.ts +3 -3
  160. package/tests/fixtures/corpus/oss-repos/hono/src/hono.ts +1 -1
  161. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/css.ts +2 -2
  162. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/intrinsic-element/components.ts +1 -1
  163. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/render.ts +7 -7
  164. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/hooks/index.ts +3 -3
  165. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-element/components.ts +1 -1
  166. package/tests/fixtures/corpus/oss-repos/hono/src/jsx/utils.ts +6 -6
  167. package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jsx-renderer/index.ts +3 -3
  168. package/tests/fixtures/corpus/oss-repos/hono/src/middleware/serve-static/index.ts +1 -1
  169. package/tests/fixtures/corpus/oss-repos/hono/src/preset/quick.ts +1 -1
  170. package/tests/fixtures/corpus/oss-repos/hono/src/preset/tiny.ts +1 -1
  171. package/tests/fixtures/corpus/oss-repos/hono/src/router/pattern-router/router.ts +2 -2
  172. package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/node.ts +4 -4
  173. package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/router.ts +1 -1
  174. package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/node.ts +1 -1
  175. package/tests/fixtures/corpus/oss-repos/hono/src/types.ts +166 -169
  176. package/tests/fixtures/corpus/oss-repos/hono/src/utils/body.ts +8 -8
  177. package/tests/fixtures/corpus/oss-repos/hono/src/utils/color.ts +3 -3
  178. package/tests/fixtures/corpus/oss-repos/hono/src/utils/cookie.ts +2 -2
  179. package/tests/fixtures/corpus/oss-repos/hono/src/utils/encode.ts +2 -2
  180. package/tests/fixtures/corpus/oss-repos/hono/src/utils/types.ts +30 -33
  181. package/tests/fixtures/corpus/oss-repos/hono/src/validator/validator.ts +2 -2
  182. package/tests/fixtures/test-server.ts +3 -2
  183. package/tests/helpers/performance-metrics.ts +8 -25
  184. package/tests/helpers/search-relevance.ts +14 -69
  185. package/tests/integration/cli-consistency.test.ts +6 -5
  186. package/tests/integration/python-bridge.test.ts +13 -3
  187. package/tests/mcp/server.test.ts +1 -1
  188. package/tests/services/code-unit.service.test.ts +48 -0
  189. package/tests/services/job.service.test.ts +124 -0
  190. package/tests/services/search.progressive-context.test.ts +2 -2
  191. package/.claude-plugin/plugin.json +0 -13
  192. package/dist/chunk-6PBP5DVD.js.map +0 -1
  193. package/dist/chunk-L2YVNC63.js.map +0 -1
  194. package/dist/chunk-RST4XGRL.js.map +0 -1
  195. package/dist/chunk-WT2DAEO7.js.map +0 -1
  196. package/dist/watch.service-YAIKKDCF.js +0 -7
  197. package/skills/atomic-commits/SKILL.md +0 -77
  198. /package/dist/{watch.service-YAIKKDCF.js.map → watch.service-BJV3TI3F.js.map} +0 -0
@@ -0,0 +1,167 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtempSync, rmSync, existsSync, chmodSync, writeFileSync, readFileSync } from 'fs';
3
+ import { tmpdir } from 'os';
4
+ import { join } from 'path';
5
+ import { writePidFile, deletePidFile, buildPidFilePath } from './pid-file.js';
6
+
7
+ /**
8
+ * PID File Operations Tests
9
+ *
10
+ * SAFETY: All tests use fake PID 999999999 - never real PIDs.
11
+ * This prevents accidentally killing VSCode, terminals, or other processes.
12
+ */
13
+ describe('PID File Operations', () => {
14
+ let tempDir: string;
15
+ let pidFile: string;
16
+
17
+ // Fake PID - guaranteed not to be a real process
18
+ const FAKE_PID = 999999999;
19
+
20
+ beforeEach(() => {
21
+ tempDir = mkdtempSync(join(tmpdir(), 'pid-file-test-'));
22
+ pidFile = join(tempDir, 'test_job.pid');
23
+ });
24
+
25
+ afterEach(() => {
26
+ if (existsSync(tempDir)) {
27
+ // Restore permissions before cleanup (in case test made it read-only)
28
+ try {
29
+ chmodSync(tempDir, 0o755);
30
+ } catch {
31
+ // Ignore - might not exist
32
+ }
33
+ rmSync(tempDir, { recursive: true, force: true });
34
+ }
35
+ });
36
+
37
+ describe('writePidFile', () => {
38
+ it('should write PID to file successfully', () => {
39
+ writePidFile(pidFile, FAKE_PID);
40
+
41
+ expect(existsSync(pidFile)).toBe(true);
42
+ const content = readFileSync(pidFile, 'utf-8');
43
+ expect(content).toBe('999999999');
44
+ });
45
+
46
+ it('should overwrite existing PID file', () => {
47
+ writeFileSync(pidFile, '123456', 'utf-8');
48
+
49
+ writePidFile(pidFile, FAKE_PID);
50
+
51
+ const content = readFileSync(pidFile, 'utf-8');
52
+ expect(content).toBe('999999999');
53
+ });
54
+
55
+ it('should throw with CRITICAL message when write fails (permission denied)', () => {
56
+ // Make directory read-only to prevent file creation
57
+ chmodSync(tempDir, 0o444);
58
+
59
+ expect(() => writePidFile(pidFile, FAKE_PID)).toThrow(/CRITICAL/);
60
+ expect(() => writePidFile(pidFile, FAKE_PID)).toThrow(/Failed to write PID file/);
61
+ expect(() => writePidFile(pidFile, FAKE_PID)).toThrow(/Job cannot be cancelled/);
62
+ });
63
+
64
+ it('should include file path in error message', () => {
65
+ chmodSync(tempDir, 0o444);
66
+
67
+ try {
68
+ writePidFile(pidFile, FAKE_PID);
69
+ expect.fail('Should have thrown');
70
+ } catch (error) {
71
+ expect(error).toBeInstanceOf(Error);
72
+ expect((error as Error).message).toContain(pidFile);
73
+ }
74
+ });
75
+
76
+ it('should throw when path directory does not exist', () => {
77
+ const invalidPath = '/nonexistent/directory/test.pid';
78
+
79
+ expect(() => writePidFile(invalidPath, FAKE_PID)).toThrow(/CRITICAL/);
80
+ });
81
+ });
82
+
83
+ describe('deletePidFile', () => {
84
+ it('should delete PID file successfully', () => {
85
+ writeFileSync(pidFile, FAKE_PID.toString(), 'utf-8');
86
+
87
+ const result = deletePidFile(pidFile, 'success');
88
+
89
+ expect(result.success).toBe(true);
90
+ expect(result.error).toBeUndefined();
91
+ expect(existsSync(pidFile)).toBe(false);
92
+ });
93
+
94
+ it('should return success when PID file does not exist', () => {
95
+ // File doesn't exist
96
+ expect(existsSync(pidFile)).toBe(false);
97
+
98
+ const result = deletePidFile(pidFile, 'success');
99
+
100
+ expect(result.success).toBe(true);
101
+ expect(result.error).toBeUndefined();
102
+ });
103
+
104
+ it('should return failure (NOT throw) when delete fails', () => {
105
+ writeFileSync(pidFile, FAKE_PID.toString(), 'utf-8');
106
+ // Make directory read-only to prevent deletion
107
+ chmodSync(tempDir, 0o444);
108
+
109
+ // Should NOT throw
110
+ const result = deletePidFile(pidFile, 'success');
111
+
112
+ expect(result.success).toBe(false);
113
+ expect(result.error).toBeInstanceOf(Error);
114
+ });
115
+
116
+ it('should never throw on delete failure - returns result instead', () => {
117
+ writeFileSync(pidFile, FAKE_PID.toString(), 'utf-8');
118
+ chmodSync(tempDir, 0o444);
119
+
120
+ // Must not throw - this is best-effort cleanup
121
+ expect(() => deletePidFile(pidFile, 'failure')).not.toThrow();
122
+ expect(() => deletePidFile(pidFile, 'sigterm')).not.toThrow();
123
+ expect(() => deletePidFile(pidFile, 'success')).not.toThrow();
124
+ });
125
+
126
+ it('should handle sigterm context', () => {
127
+ writeFileSync(pidFile, FAKE_PID.toString(), 'utf-8');
128
+
129
+ const result = deletePidFile(pidFile, 'sigterm');
130
+
131
+ expect(result.success).toBe(true);
132
+ expect(existsSync(pidFile)).toBe(false);
133
+ });
134
+
135
+ it('should handle failure context', () => {
136
+ writeFileSync(pidFile, FAKE_PID.toString(), 'utf-8');
137
+
138
+ const result = deletePidFile(pidFile, 'failure');
139
+
140
+ expect(result.success).toBe(true);
141
+ expect(existsSync(pidFile)).toBe(false);
142
+ });
143
+ });
144
+
145
+ describe('buildPidFilePath', () => {
146
+ it('should build correct PID file path', () => {
147
+ const result = buildPidFilePath('/data/jobs', 'job_123');
148
+
149
+ expect(result).toBe('/data/jobs/job_123.pid');
150
+ });
151
+
152
+ it('should handle job IDs with various formats', () => {
153
+ expect(buildPidFilePath('/jobs', 'abc123def')).toBe('/jobs/abc123def.pid');
154
+ expect(buildPidFilePath('/jobs', 'test-job')).toBe('/jobs/test-job.pid');
155
+ expect(buildPidFilePath('/jobs', 'job_with_underscore')).toBe(
156
+ '/jobs/job_with_underscore.pid'
157
+ );
158
+ });
159
+
160
+ it('should handle paths with trailing slash', () => {
161
+ // path.join normalizes this
162
+ const result = buildPidFilePath('/data/jobs/', 'job_123');
163
+
164
+ expect(result).toBe('/data/jobs/job_123.pid');
165
+ });
166
+ });
167
+ });
@@ -0,0 +1,82 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Result of a PID file delete operation.
6
+ * Delete operations are best-effort and should not throw.
7
+ */
8
+ export interface PidFileResult {
9
+ success: boolean;
10
+ error?: Error;
11
+ }
12
+
13
+ /**
14
+ * Context for PID file deletion - indicates when the delete is happening.
15
+ * Used for logging/debugging purposes.
16
+ */
17
+ export type PidFileDeleteContext = 'sigterm' | 'success' | 'failure';
18
+
19
+ /**
20
+ * Write PID file - CRITICAL operation that must succeed.
21
+ *
22
+ * If the PID file cannot be written, the job cannot be cancelled through
23
+ * the job management system. This is a critical failure and the job
24
+ * should not proceed.
25
+ *
26
+ * @param pidFile - Absolute path to the PID file
27
+ * @param pid - Process ID to write
28
+ * @throws Error if PID file cannot be written
29
+ */
30
+ export function writePidFile(pidFile: string, pid: number): void {
31
+ try {
32
+ fs.writeFileSync(pidFile, pid.toString(), 'utf-8');
33
+ } catch (error) {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ throw new Error(
36
+ `CRITICAL: Failed to write PID file ${pidFile}. ` +
37
+ `Job cannot be cancelled without PID file. ` +
38
+ `Original error: ${message}`
39
+ );
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Delete PID file - best-effort cleanup during shutdown.
45
+ *
46
+ * This operation should NEVER throw. During process shutdown (SIGTERM,
47
+ * job success, job failure), failing to delete a PID file should not
48
+ * prevent the process from exiting cleanly.
49
+ *
50
+ * Stale PID files are cleaned up by JobService.cleanupOldJobs().
51
+ *
52
+ * @param pidFile - Absolute path to the PID file
53
+ * @param _context - Context indicating when the delete is happening (for future logging)
54
+ * @returns Result indicating success or failure with error details
55
+ */
56
+ export function deletePidFile(pidFile: string, _context: PidFileDeleteContext): PidFileResult {
57
+ try {
58
+ fs.unlinkSync(pidFile);
59
+ return { success: true };
60
+ } catch (error) {
61
+ // ENOENT = file doesn't exist - that's success (nothing to delete)
62
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
63
+ return { success: true };
64
+ }
65
+ // Any other error = failure (permission denied, etc.)
66
+ return {
67
+ success: false,
68
+ error: error instanceof Error ? error : new Error(String(error)),
69
+ };
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Build the path to a PID file for a given job.
75
+ *
76
+ * @param jobsDir - Directory where job files are stored
77
+ * @param jobId - Job identifier
78
+ * @returns Absolute path to the PID file
79
+ */
80
+ export function buildPidFilePath(jobsDir: string, jobId: string): string {
81
+ return path.join(jobsDir, `${jobId}.pid`);
82
+ }
@@ -3,11 +3,11 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
3
3
  // Mock child_process before importing spawn-worker
4
4
  const mockUnref = vi.fn();
5
5
  const mockSpawn = vi.fn(() => ({
6
- unref: mockUnref
6
+ unref: mockUnref,
7
7
  }));
8
8
 
9
9
  vi.mock('child_process', () => ({
10
- spawn: mockSpawn
10
+ spawn: mockSpawn,
11
11
  }));
12
12
 
13
13
  // Import after mocking
@@ -50,11 +50,15 @@ describe('spawnBackgroundWorker', () => {
50
50
  spawnBackgroundWorker('test-job', '/test/data/dir');
51
51
 
52
52
  expect(mockSpawn).toHaveBeenCalledTimes(1);
53
- const [, , options] = mockSpawn.mock.calls[0] as [string, string[], { detached: boolean; stdio: string }];
53
+ const [, , options] = mockSpawn.mock.calls[0] as [
54
+ string,
55
+ string[],
56
+ { detached: boolean; stdio: string },
57
+ ];
54
58
 
55
59
  expect(options).toMatchObject({
56
60
  detached: true,
57
- stdio: 'ignore'
61
+ stdio: 'ignore',
58
62
  });
59
63
  });
60
64
 
@@ -63,11 +67,15 @@ describe('spawnBackgroundWorker', () => {
63
67
  spawnBackgroundWorker('test-job', dataDir);
64
68
 
65
69
  expect(mockSpawn).toHaveBeenCalledTimes(1);
66
- const [, , options] = mockSpawn.mock.calls[0] as [string, string[], { env: Record<string, string> }];
70
+ const [, , options] = mockSpawn.mock.calls[0] as [
71
+ string,
72
+ string[],
73
+ { env: Record<string, string> },
74
+ ];
67
75
 
68
76
  expect(options.env).toMatchObject({
69
77
  ...process.env,
70
- BLUERA_DATA_DIR: dataDir
78
+ BLUERA_DATA_DIR: dataDir,
71
79
  });
72
80
  });
73
81
 
@@ -76,7 +84,11 @@ describe('spawnBackgroundWorker', () => {
76
84
  spawnBackgroundWorker('job-456', testDataDir);
77
85
 
78
86
  expect(mockSpawn).toHaveBeenCalledTimes(1);
79
- const [, , options] = mockSpawn.mock.calls[0] as [string, string[], { env: Record<string, string> }];
87
+ const [, , options] = mockSpawn.mock.calls[0] as [
88
+ string,
89
+ string[],
90
+ { env: Record<string, string> },
91
+ ];
80
92
 
81
93
  expect(options.env.BLUERA_DATA_DIR).toBe(testDataDir);
82
94
  });
@@ -86,7 +98,7 @@ describe('spawnBackgroundWorker', () => {
86
98
  describe('spawnBackgroundWorker (production mode)', () => {
87
99
  const mockUnrefProd = vi.fn();
88
100
  const mockSpawnProd = vi.fn(() => ({
89
- unref: mockUnrefProd
101
+ unref: mockUnrefProd,
90
102
  }));
91
103
 
92
104
  beforeEach(() => {
@@ -102,12 +114,12 @@ describe('spawnBackgroundWorker (production mode)', () => {
102
114
  it('should use Node.js directly in production mode (dist folder)', async () => {
103
115
  // Mock child_process
104
116
  vi.doMock('child_process', () => ({
105
- spawn: mockSpawnProd
117
+ spawn: mockSpawnProd,
106
118
  }));
107
119
 
108
120
  // Mock url module to return a path containing /dist/
109
121
  vi.doMock('url', () => ({
110
- fileURLToPath: () => '/app/dist/workers/spawn-worker.js'
122
+ fileURLToPath: () => '/app/dist/workers/spawn-worker.js',
111
123
  }));
112
124
 
113
125
  // Import fresh module with production path
@@ -1,6 +1,6 @@
1
1
  import { spawn } from 'child_process';
2
- import { fileURLToPath } from 'url';
3
2
  import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
4
 
5
5
  /**
6
6
  * Spawn a background worker process to execute a job
@@ -36,12 +36,12 @@ export function spawnBackgroundWorker(jobId: string, dataDir: string): void {
36
36
 
37
37
  // Spawn the worker process
38
38
  const worker = spawn(command, args, {
39
- detached: true, // Detach from parent process
40
- stdio: 'ignore', // Don't pipe stdio (fully independent)
39
+ detached: true, // Detach from parent process
40
+ stdio: 'ignore', // Don't pipe stdio (fully independent)
41
41
  env: {
42
- ...process.env, // Inherit environment variables
43
- BLUERA_DATA_DIR: dataDir // Pass dataDir to worker
44
- }
42
+ ...process.env, // Inherit environment variables
43
+ BLUERA_DATA_DIR: dataDir, // Pass dataDir to worker
44
+ },
45
45
  });
46
46
 
47
47
  // Unref the worker so the parent can exit
@@ -43,7 +43,7 @@ export class UserService {
43
43
  const parser = new ASTParser();
44
44
  const nodes = parser.parse(code, 'typescript');
45
45
 
46
- const classNode = nodes.find(n => n.type === 'class');
46
+ const classNode = nodes.find((n) => n.type === 'class');
47
47
  expect(classNode).toBeDefined();
48
48
  expect(classNode?.name).toBe('UserService');
49
49
  expect(classNode?.methods).toHaveLength(3); // constructor + create + delete
@@ -78,11 +78,11 @@ class Bar {
78
78
  const parser = new ASTParser();
79
79
  const nodes = parser.parse(code, 'typescript');
80
80
 
81
- const fooNode = nodes.find(n => n.name === 'foo');
81
+ const fooNode = nodes.find((n) => n.name === 'foo');
82
82
  expect(fooNode?.startLine).toBe(2);
83
83
  expect(fooNode?.endLine).toBe(4);
84
84
 
85
- const barNode = nodes.find(n => n.name === 'Bar');
85
+ const barNode = nodes.find((n) => n.name === 'Bar');
86
86
  expect(barNode?.startLine).toBe(6);
87
87
  expect(barNode?.endLine).toBe(8);
88
88
  });
@@ -10,15 +10,15 @@ describe('CodeGraph', () => {
10
10
  name: 'validateToken',
11
11
  exported: true,
12
12
  startLine: 1,
13
- endLine: 5
13
+ endLine: 5,
14
14
  },
15
15
  {
16
16
  type: 'function',
17
17
  name: 'parseToken',
18
18
  exported: false,
19
19
  startLine: 7,
20
- endLine: 10
21
- }
20
+ endLine: 10,
21
+ },
22
22
  ];
23
23
 
24
24
  const graph = new CodeGraph();
@@ -52,9 +52,9 @@ export function handler(req) {
52
52
  graph.analyzeCallRelationships(code, 'src/handler.ts', 'handler');
53
53
 
54
54
  const edges = graph.getEdges('src/handler.ts:handler');
55
- const callEdges = edges.filter(e => e.type === 'calls');
55
+ const callEdges = edges.filter((e) => e.type === 'calls');
56
56
 
57
57
  expect(callEdges.length).toBeGreaterThan(0);
58
- expect(callEdges.some(e => e.to.includes('validateToken'))).toBe(true);
58
+ expect(callEdges.some((e) => e.to.includes('validateToken'))).toBe(true);
59
59
  });
60
60
  });
@@ -17,12 +17,7 @@ export class ApiError extends Error {
17
17
  public readonly isOperational: boolean;
18
18
  public readonly timestamp: Date;
19
19
 
20
- constructor(
21
- statusCode: number,
22
- message: string,
23
- code?: string,
24
- isOperational = true
25
- ) {
20
+ constructor(statusCode: number, message: string, code?: string, isOperational = true) {
26
21
  super(message);
27
22
  this.statusCode = statusCode;
28
23
  this.code = code || 'API_ERROR';
@@ -77,9 +72,7 @@ export class NotFoundError extends ApiError {
77
72
  public readonly resource: string;
78
73
 
79
74
  constructor(resource: string, id?: string) {
80
- const message = id
81
- ? `${resource} with ID '${id}' not found`
82
- : `${resource} not found`;
75
+ const message = id ? `${resource} with ID '${id}' not found` : `${resource} not found`;
83
76
  super(404, message, 'NOT_FOUND');
84
77
  this.resource = resource;
85
78
  Object.setPrototypeOf(this, NotFoundError.prototype);
@@ -106,9 +99,7 @@ export class ValidationError extends ApiError {
106
99
  value?: unknown;
107
100
  }>;
108
101
 
109
- constructor(
110
- errors: Array<{ field: string; message: string; value?: unknown }>
111
- ) {
102
+ constructor(errors: Array<{ field: string; message: string; value?: unknown }>) {
112
103
  super(422, 'Validation failed', 'VALIDATION_ERROR');
113
104
  this.errors = errors;
114
105
  Object.setPrototypeOf(this, ValidationError.prototype);
@@ -239,9 +230,7 @@ export const errorHandler: ErrorRequestHandler = (
239
230
 
240
231
  // Handle unknown errors
241
232
  const internalError = new InternalError(
242
- process.env.NODE_ENV === 'production'
243
- ? 'An unexpected error occurred'
244
- : err.message
233
+ process.env.NODE_ENV === 'production' ? 'An unexpected error occurred' : err.message
245
234
  );
246
235
 
247
236
  const response = formatErrorResponse(internalError, req, requestId);
@@ -92,7 +92,7 @@ function validateBody<T>(schema: z.ZodSchema<T>, data: unknown): T {
92
92
  const result = schema.safeParse(data);
93
93
 
94
94
  if (!result.success) {
95
- const errors = result.error.errors.map(err => ({
95
+ const errors = result.error.errors.map((err) => ({
96
96
  field: err.path.join('.'),
97
97
  message: err.message,
98
98
  }));
@@ -132,11 +132,7 @@ export class UserController {
132
132
  asyncHandler(this.createUser.bind(this))
133
133
  );
134
134
 
135
- this.router.put(
136
- '/:id',
137
- authenticateToken,
138
- asyncHandler(this.updateUser.bind(this))
139
- );
135
+ this.router.put('/:id', authenticateToken, asyncHandler(this.updateUser.bind(this)));
140
136
 
141
137
  this.router.delete(
142
138
  '/:id',
@@ -217,9 +213,7 @@ export class UserController {
217
213
  const data = validateBody(createUserSchema, req.body);
218
214
 
219
215
  // Check for existing user
220
- const existingUser = Array.from(users.values()).find(
221
- u => u.email === data.email
222
- );
216
+ const existingUser = Array.from(users.values()).find((u) => u.email === data.email);
223
217
 
224
218
  if (existingUser) {
225
219
  throw new BadRequestError('User with this email already exists', {
@@ -51,11 +51,7 @@ declare global {
51
51
  * @param user - User object containing id, email, and roles
52
52
  * @returns JWT access token
53
53
  */
54
- export function generateAccessToken(user: {
55
- id: string;
56
- email: string;
57
- roles: string[];
58
- }): string {
54
+ export function generateAccessToken(user: { id: string; email: string; roles: string[] }): string {
59
55
  const payload: JwtPayload = {
60
56
  userId: user.id,
61
57
  email: user.email,
@@ -76,11 +72,7 @@ export function generateAccessToken(user: {
76
72
  export function generateRefreshToken(userId: string): string {
77
73
  const tokenId = randomBytes(16).toString('hex');
78
74
 
79
- return jwt.sign(
80
- { userId, tokenId },
81
- REFRESH_SECRET,
82
- { expiresIn: REFRESH_TOKEN_EXPIRY }
83
- );
75
+ return jwt.sign({ userId, tokenId }, REFRESH_SECRET, { expiresIn: REFRESH_TOKEN_EXPIRY });
84
76
  }
85
77
 
86
78
  /**
@@ -88,11 +80,7 @@ export function generateRefreshToken(userId: string): string {
88
80
  * @param user - User object
89
81
  * @returns Token pair with access token, refresh token, and expiry time
90
82
  */
91
- export function generateTokenPair(user: {
92
- id: string;
93
- email: string;
94
- roles: string[];
95
- }): TokenPair {
83
+ export function generateTokenPair(user: { id: string; email: string; roles: string[] }): TokenPair {
96
84
  return {
97
85
  accessToken: generateAccessToken(user),
98
86
  refreshToken: generateRefreshToken(user.id),
@@ -137,11 +125,7 @@ export function verifyRefreshToken(token: string): { userId: string; tokenId: st
137
125
  * Express middleware to authenticate requests using JWT
138
126
  * Extracts the token from the Authorization header (Bearer scheme)
139
127
  */
140
- export function authenticateToken(
141
- req: Request,
142
- res: Response,
143
- next: NextFunction
144
- ): void {
128
+ export function authenticateToken(req: Request, res: Response, next: NextFunction): void {
145
129
  const authHeader = req.headers['authorization'];
146
130
  const token = authHeader && authHeader.split(' ')[1];
147
131
 
@@ -179,7 +163,7 @@ export function requireRoles(...requiredRoles: string[]) {
179
163
  return;
180
164
  }
181
165
 
182
- const hasRole = req.user.roles.some(role => requiredRoles.includes(role));
166
+ const hasRole = req.user.roles.some((role) => requiredRoles.includes(role));
183
167
 
184
168
  if (!hasRole) {
185
169
  res.status(403).json({
@@ -180,7 +180,7 @@ export async function exchangeCodeForTokens(
180
180
  method: 'POST',
181
181
  headers: {
182
182
  'Content-Type': 'application/x-www-form-urlencoded',
183
- 'Accept': 'application/json',
183
+ Accept: 'application/json',
184
184
  },
185
185
  body: body.toString(),
186
186
  });
@@ -202,8 +202,8 @@ export async function fetchUserInfo(
202
202
  ): Promise<Record<string, unknown>> {
203
203
  const response = await fetch(config.userInfoUrl, {
204
204
  headers: {
205
- 'Authorization': `Bearer ${accessToken}`,
206
- 'Accept': 'application/json',
205
+ Authorization: `Bearer ${accessToken}`,
206
+ Accept: 'application/json',
207
207
  },
208
208
  });
209
209
 
@@ -232,7 +232,7 @@ export async function refreshAccessToken(
232
232
  method: 'POST',
233
233
  headers: {
234
234
  'Content-Type': 'application/x-www-form-urlencoded',
235
- 'Accept': 'application/json',
235
+ Accept: 'application/json',
236
236
  },
237
237
  body: body.toString(),
238
238
  });
@@ -45,7 +45,11 @@ export interface IRepository<T extends Entity> {
45
45
  findById(id: string): Promise<T | null>;
46
46
  findOne(options: QueryOptions<T>): Promise<T | null>;
47
47
  findMany(options?: QueryOptions<T>): Promise<T[]>;
48
- findPaginated(page: number, pageSize: number, options?: QueryOptions<T>): Promise<PaginatedResult<T>>;
48
+ findPaginated(
49
+ page: number,
50
+ pageSize: number,
51
+ options?: QueryOptions<T>
52
+ ): Promise<PaginatedResult<T>>;
49
53
  create(data: Omit<T, 'id'>): Promise<T>;
50
54
  update(id: string, data: Partial<T>): Promise<T>;
51
55
  delete(id: string): Promise<void>;
@@ -75,7 +79,7 @@ export abstract class BaseRepository<T extends Entity> implements IRepository<T>
75
79
 
76
80
  // Apply where filters
77
81
  if (options?.where) {
78
- result = result.filter(item => {
82
+ result = result.filter((item) => {
79
83
  return Object.entries(options.where!).every(([key, value]) => {
80
84
  return item[key as keyof T] === value;
81
85
  });
@@ -126,7 +130,11 @@ export abstract class BaseRepository<T extends Entity> implements IRepository<T>
126
130
  options?: QueryOptions<T>
127
131
  ): Promise<PaginatedResult<T>> {
128
132
  const allItems = Array.from(this.items.values());
129
- const filtered = this.applyFilters(allItems, { ...options, limit: undefined, offset: undefined });
133
+ const filtered = this.applyFilters(allItems, {
134
+ ...options,
135
+ limit: undefined,
136
+ offset: undefined,
137
+ });
130
138
  const total = filtered.length;
131
139
 
132
140
  const offset = (page - 1) * pageSize;
@@ -108,7 +108,7 @@ const streamToNodeStream = async (
108
108
  export const streamHandle = <
109
109
  E extends Env = Env,
110
110
  S extends Schema = {},
111
- BasePath extends string = '/'
111
+ BasePath extends string = '/',
112
112
  >(
113
113
  app: Hono<E, S, BasePath>
114
114
  ): Handler => {
@@ -206,7 +206,7 @@ abstract class EventProcessor<E extends LambdaEvent> {
206
206
  const domainName =
207
207
  event.requestContext && 'domainName' in event.requestContext
208
208
  ? event.requestContext.domainName
209
- : event.headers?.['host'] ?? event.multiValueHeaders?.['host']?.[0]
209
+ : (event.headers?.['host'] ?? event.multiValueHeaders?.['host']?.[0])
210
210
  const path = this.getPath(event)
211
211
  const urlPath = `https://${domainName}${path}`
212
212
  const url = queryString ? `${urlPath}?${queryString}` : urlPath
@@ -24,7 +24,7 @@ declare type PagesFunction<
24
24
  Env = unknown,
25
25
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
26
  Params extends string = any,
27
- Data extends Record<string, unknown> = Record<string, unknown>
27
+ Data extends Record<string, unknown> = Record<string, unknown>,
28
28
  > = (context: EventContext<Env, Params, Data>) => Response | Promise<Response>
29
29
 
30
30
  export const handle =
@@ -27,8 +27,8 @@ export const serveStatic = <E extends Env = Env>(
27
27
  namespace: options.namespace
28
28
  ? options.namespace
29
29
  : c.env
30
- ? c.env.__STATIC_CONTENT
31
- : undefined,
30
+ ? c.env.__STATIC_CONTENT
31
+ : undefined,
32
32
  })
33
33
  }
34
34
  return baseServeStatic({
@@ -96,8 +96,8 @@ class ClientRequestImpl {
96
96
  ...(typeof opt?.headers === 'function'
97
97
  ? await opt.headers()
98
98
  : opt?.headers
99
- ? opt.headers
100
- : {}),
99
+ ? opt.headers
100
+ : {}),
101
101
  }
102
102
 
103
103
  if (args?.cookie) {