bluera-knowledge 0.9.31 → 0.9.34
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.
- package/.claude/commands/code-review.md +15 -0
- package/.claude/hooks/post-edit-check.sh +5 -3
- package/.claude/skills/atomic-commits/SKILL.md +3 -1
- package/.claude/skills/code-review-repo/skill.md +62 -0
- package/.husky/pre-commit +3 -2
- package/.prettierrc +9 -0
- package/.versionrc.json +1 -1
- package/CHANGELOG.md +35 -0
- package/CLAUDE.md +6 -0
- package/README.md +25 -13
- package/bun.lock +277 -33
- package/dist/{chunk-L2YVNC63.js → chunk-6FHWC36B.js} +9 -1
- package/dist/chunk-6FHWC36B.js.map +1 -0
- package/dist/{chunk-2SJHNRXD.js → chunk-DC7CGSGT.js} +288 -241
- package/dist/chunk-DC7CGSGT.js.map +1 -0
- package/dist/{chunk-RWSXP3PQ.js → chunk-WFNPNAAP.js} +3194 -3024
- package/dist/chunk-WFNPNAAP.js.map +1 -0
- package/dist/{chunk-OGEY66FZ.js → chunk-Z2KKVH45.js} +548 -482
- package/dist/chunk-Z2KKVH45.js.map +1 -0
- package/dist/index.js +871 -754
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +3 -3
- package/dist/watch.service-BJV3TI3F.js +7 -0
- package/dist/workers/background-worker-cli.js +46 -45
- package/dist/workers/background-worker-cli.js.map +1 -1
- package/eslint.config.js +43 -1
- package/package.json +18 -11
- package/plugin.json +8 -0
- package/python/requirements.txt +1 -1
- package/src/analysis/ast-parser.test.ts +12 -11
- package/src/analysis/ast-parser.ts +28 -22
- package/src/analysis/code-graph.test.ts +52 -62
- package/src/analysis/code-graph.ts +9 -13
- package/src/analysis/dependency-usage-analyzer.test.ts +91 -271
- package/src/analysis/dependency-usage-analyzer.ts +52 -24
- package/src/analysis/go-ast-parser.test.ts +22 -22
- package/src/analysis/go-ast-parser.ts +18 -25
- package/src/analysis/parser-factory.test.ts +9 -9
- package/src/analysis/parser-factory.ts +3 -3
- package/src/analysis/python-ast-parser.test.ts +27 -27
- package/src/analysis/python-ast-parser.ts +2 -2
- package/src/analysis/repo-url-resolver.test.ts +82 -82
- package/src/analysis/rust-ast-parser.test.ts +19 -19
- package/src/analysis/rust-ast-parser.ts +17 -27
- package/src/analysis/tree-sitter-parser.test.ts +3 -3
- package/src/analysis/tree-sitter-parser.ts +10 -16
- package/src/cli/commands/crawl.test.ts +40 -24
- package/src/cli/commands/crawl.ts +186 -161
- package/src/cli/commands/index-cmd.test.ts +90 -90
- package/src/cli/commands/index-cmd.ts +52 -36
- package/src/cli/commands/mcp.test.ts +6 -6
- package/src/cli/commands/mcp.ts +2 -2
- package/src/cli/commands/plugin-api.test.ts +16 -18
- package/src/cli/commands/plugin-api.ts +9 -6
- package/src/cli/commands/search.test.ts +16 -7
- package/src/cli/commands/search.ts +124 -87
- package/src/cli/commands/serve.test.ts +67 -25
- package/src/cli/commands/serve.ts +18 -3
- package/src/cli/commands/setup.test.ts +176 -101
- package/src/cli/commands/setup.ts +140 -117
- package/src/cli/commands/store.test.ts +82 -53
- package/src/cli/commands/store.ts +56 -37
- package/src/cli/program.ts +2 -2
- package/src/crawl/article-converter.test.ts +4 -1
- package/src/crawl/article-converter.ts +46 -31
- package/src/crawl/bridge.test.ts +240 -132
- package/src/crawl/bridge.ts +87 -30
- package/src/crawl/claude-client.test.ts +124 -56
- package/src/crawl/claude-client.ts +7 -15
- package/src/crawl/intelligent-crawler.test.ts +65 -22
- package/src/crawl/intelligent-crawler.ts +86 -53
- package/src/crawl/markdown-utils.ts +1 -4
- package/src/db/embeddings.ts +4 -6
- package/src/db/lance.test.ts +63 -4
- package/src/db/lance.ts +31 -12
- package/src/index.ts +26 -17
- package/src/logging/index.ts +1 -5
- package/src/logging/logger.ts +3 -5
- package/src/logging/payload.test.ts +1 -1
- package/src/logging/payload.ts +3 -5
- package/src/mcp/commands/index.ts +2 -2
- package/src/mcp/commands/job.commands.ts +12 -18
- package/src/mcp/commands/meta.commands.ts +13 -13
- package/src/mcp/commands/registry.ts +5 -8
- package/src/mcp/commands/store.commands.ts +19 -19
- package/src/mcp/handlers/execute.handler.test.ts +10 -10
- package/src/mcp/handlers/execute.handler.ts +4 -5
- package/src/mcp/handlers/index.ts +10 -14
- package/src/mcp/handlers/job.handler.test.ts +10 -10
- package/src/mcp/handlers/job.handler.ts +22 -25
- package/src/mcp/handlers/search.handler.test.ts +36 -65
- package/src/mcp/handlers/search.handler.ts +135 -104
- package/src/mcp/handlers/store.handler.test.ts +41 -52
- package/src/mcp/handlers/store.handler.ts +108 -88
- package/src/mcp/schemas/index.test.ts +73 -68
- package/src/mcp/schemas/index.ts +18 -12
- package/src/mcp/server.test.ts +1 -1
- package/src/mcp/server.ts +59 -46
- package/src/plugin/commands.test.ts +230 -95
- package/src/plugin/commands.ts +24 -25
- package/src/plugin/dependency-analyzer.test.ts +52 -52
- package/src/plugin/dependency-analyzer.ts +85 -22
- package/src/plugin/git-clone.test.ts +24 -13
- package/src/plugin/git-clone.ts +3 -7
- package/src/server/app.test.ts +109 -109
- package/src/server/app.ts +32 -23
- package/src/server/index.test.ts +64 -66
- package/src/services/chunking.service.test.ts +32 -32
- package/src/services/chunking.service.ts +16 -9
- package/src/services/code-graph.service.test.ts +30 -36
- package/src/services/code-graph.service.ts +24 -10
- package/src/services/code-unit.service.test.ts +55 -11
- package/src/services/code-unit.service.ts +85 -11
- package/src/services/config.service.test.ts +37 -18
- package/src/services/config.service.ts +30 -7
- package/src/services/index.service.test.ts +49 -18
- package/src/services/index.service.ts +98 -48
- package/src/services/index.ts +8 -10
- package/src/services/job.service.test.ts +22 -22
- package/src/services/job.service.ts +18 -18
- package/src/services/project-root.service.test.ts +1 -3
- package/src/services/search.service.test.ts +248 -120
- package/src/services/search.service.ts +286 -156
- package/src/services/services.test.ts +36 -0
- package/src/services/snippet.service.test.ts +14 -6
- package/src/services/snippet.service.ts +7 -5
- package/src/services/store.service.test.ts +68 -29
- package/src/services/store.service.ts +41 -12
- package/src/services/watch.service.test.ts +34 -14
- package/src/services/watch.service.ts +11 -1
- package/src/types/brands.test.ts +3 -1
- package/src/types/index.ts +2 -13
- package/src/types/search.ts +10 -8
- package/src/utils/type-guards.test.ts +20 -15
- package/src/utils/type-guards.ts +1 -1
- package/src/workers/background-worker-cli.ts +2 -2
- package/src/workers/background-worker.test.ts +54 -40
- package/src/workers/background-worker.ts +76 -60
- package/src/workers/spawn-worker.test.ts +22 -10
- package/src/workers/spawn-worker.ts +6 -6
- package/tests/analysis/ast-parser.test.ts +3 -3
- package/tests/analysis/code-graph.test.ts +5 -5
- package/tests/fixtures/code-snippets/api/error-handling.ts +4 -15
- package/tests/fixtures/code-snippets/api/rest-controller.ts +3 -9
- package/tests/fixtures/code-snippets/auth/jwt-auth.ts +5 -21
- package/tests/fixtures/code-snippets/auth/oauth-flow.ts +4 -4
- package/tests/fixtures/code-snippets/database/repository-pattern.ts +11 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/handler.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-pages/handler.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/serve-static.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/client/client.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/client/types.ts +22 -20
- package/tests/fixtures/corpus/oss-repos/hono/src/context.ts +13 -10
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/accepts/accepts.ts +10 -7
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/adapter/index.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/css/index.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/factory/index.ts +16 -16
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/ssg.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/hono-base.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/hono.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/css.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/intrinsic-element/components.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/render.ts +7 -7
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/hooks/index.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-element/components.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/utils.ts +6 -6
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jsx-renderer/index.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/serve-static/index.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/quick.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/tiny.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/router/pattern-router/router.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/node.ts +4 -4
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/router.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/node.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/types.ts +166 -169
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/body.ts +8 -8
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/color.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/cookie.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/encode.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/types.ts +30 -33
- package/tests/fixtures/corpus/oss-repos/hono/src/validator/validator.ts +2 -2
- package/tests/fixtures/test-server.ts +3 -2
- package/tests/helpers/performance-metrics.ts +8 -25
- package/tests/helpers/search-relevance.ts +14 -69
- package/tests/integration/cli-consistency.test.ts +5 -4
- package/tests/integration/e2e-workflow.test.ts +2 -0
- package/tests/integration/python-bridge.test.ts +13 -3
- package/tests/mcp/server.test.ts +1 -1
- package/tests/services/code-unit.service.test.ts +48 -0
- package/tests/services/job.service.test.ts +124 -0
- package/tests/services/search.progressive-context.test.ts +2 -2
- package/.claude-plugin/plugin.json +0 -13
- package/BUGS-FOUND.md +0 -71
- package/dist/chunk-2SJHNRXD.js.map +0 -1
- package/dist/chunk-L2YVNC63.js.map +0 -1
- package/dist/chunk-OGEY66FZ.js.map +0 -1
- package/dist/chunk-RWSXP3PQ.js.map +0 -1
- package/dist/watch.service-YAIKKDCF.js +0 -7
- package/skills/atomic-commits/SKILL.md +0 -77
- /package/dist/{watch.service-YAIKKDCF.js.map → watch.service-BJV3TI3F.js.map} +0 -0
|
@@ -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 [
|
|
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 [
|
|
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 [
|
|
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,
|
|
40
|
-
stdio: 'ignore',
|
|
39
|
+
detached: true, // Detach from parent process
|
|
40
|
+
stdio: 'ignore', // Don't pipe stdio (fully independent)
|
|
41
41
|
env: {
|
|
42
|
-
...process.env,
|
|
43
|
-
BLUERA_DATA_DIR: dataDir
|
|
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
|
-
|
|
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
|
-
|
|
206
|
-
|
|
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
|
-
|
|
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(
|
|
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, {
|
|
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 =
|
|
@@ -54,8 +54,8 @@ type BlankRecordToNever<T> = T extends any
|
|
|
54
54
|
? T extends null
|
|
55
55
|
? null
|
|
56
56
|
: keyof T extends never
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
? never
|
|
58
|
+
: T
|
|
59
59
|
: never
|
|
60
60
|
|
|
61
61
|
type ClientResponseOfEndpoint<T extends Endpoint = Endpoint> = T extends {
|
|
@@ -69,15 +69,16 @@ type ClientResponseOfEndpoint<T extends Endpoint = Endpoint> = T extends {
|
|
|
69
69
|
export interface ClientResponse<
|
|
70
70
|
T,
|
|
71
71
|
U extends number = StatusCode,
|
|
72
|
-
F extends ResponseFormat = ResponseFormat
|
|
73
|
-
>
|
|
72
|
+
F extends ResponseFormat = ResponseFormat,
|
|
73
|
+
>
|
|
74
|
+
extends globalThis.Response {
|
|
74
75
|
readonly body: ReadableStream | null
|
|
75
76
|
readonly bodyUsed: boolean
|
|
76
77
|
ok: U extends SuccessStatusCode
|
|
77
78
|
? true
|
|
78
79
|
: U extends Exclude<StatusCode, SuccessStatusCode>
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
? false
|
|
81
|
+
: boolean
|
|
81
82
|
status: U
|
|
82
83
|
statusText: string
|
|
83
84
|
headers: Headers
|
|
@@ -87,8 +88,8 @@ export interface ClientResponse<
|
|
|
87
88
|
json(): F extends 'text'
|
|
88
89
|
? Promise<never>
|
|
89
90
|
: F extends 'json'
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
? Promise<BlankRecordToNever<T>>
|
|
92
|
+
: Promise<unknown>
|
|
92
93
|
text(): F extends 'text' ? (T extends string ? Promise<T> : Promise<never>) : Promise<string>
|
|
93
94
|
blob(): Promise<Blob>
|
|
94
95
|
formData(): Promise<FormData>
|
|
@@ -147,25 +148,26 @@ export type InferRequestOptionsType<T> = T extends (
|
|
|
147
148
|
type PathToChain<
|
|
148
149
|
Path extends string,
|
|
149
150
|
E extends Schema,
|
|
150
|
-
Original extends string = Path
|
|
151
|
+
Original extends string = Path,
|
|
151
152
|
> = Path extends `/${infer P}`
|
|
152
153
|
? PathToChain<P, E, Path>
|
|
153
154
|
: Path extends `${infer P}/${infer R}`
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
? { [K in P]: PathToChain<R, E, Original> }
|
|
156
|
+
: {
|
|
157
|
+
[K in Path extends '' ? 'index' : Path]: ClientRequest<
|
|
158
|
+
E extends Record<string, unknown> ? E[Original] : never
|
|
159
|
+
>
|
|
160
|
+
}
|
|
160
161
|
|
|
161
162
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
162
|
-
export type Client<T> =
|
|
163
|
-
|
|
164
|
-
?
|
|
165
|
-
?
|
|
163
|
+
export type Client<T> =
|
|
164
|
+
T extends Hono<any, infer S, any>
|
|
165
|
+
? S extends Record<infer K, Schema>
|
|
166
|
+
? K extends string
|
|
167
|
+
? PathToChain<K, S>
|
|
168
|
+
: never
|
|
166
169
|
: never
|
|
167
170
|
: never
|
|
168
|
-
: never
|
|
169
171
|
|
|
170
172
|
export type Callback = (opts: CallbackOptions) => unknown
|
|
171
173
|
|
|
@@ -134,8 +134,10 @@ interface TextRespond {
|
|
|
134
134
|
status?: U,
|
|
135
135
|
headers?: HeaderRecord
|
|
136
136
|
): Response & TypedResponse<T, U, 'text'>
|
|
137
|
-
<T extends string, U extends StatusCode = StatusCode>(
|
|
138
|
-
|
|
137
|
+
<T extends string, U extends StatusCode = StatusCode>(
|
|
138
|
+
text: T,
|
|
139
|
+
init?: ResponseInit
|
|
140
|
+
): Response & TypedResponse<T, U, 'text'>
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
/**
|
|
@@ -154,7 +156,7 @@ interface TextRespond {
|
|
|
154
156
|
interface JSONRespond {
|
|
155
157
|
<
|
|
156
158
|
T extends JSONValue | SimplifyDeepArray<unknown> | InvalidJSONValue,
|
|
157
|
-
U extends StatusCode = StatusCode
|
|
159
|
+
U extends StatusCode = StatusCode,
|
|
158
160
|
>(
|
|
159
161
|
object: T,
|
|
160
162
|
status?: U,
|
|
@@ -162,7 +164,7 @@ interface JSONRespond {
|
|
|
162
164
|
): JSONRespondReturn<T, U>
|
|
163
165
|
<
|
|
164
166
|
T extends JSONValue | SimplifyDeepArray<unknown> | InvalidJSONValue,
|
|
165
|
-
U extends StatusCode = StatusCode
|
|
167
|
+
U extends StatusCode = StatusCode,
|
|
166
168
|
>(
|
|
167
169
|
object: T,
|
|
168
170
|
init?: ResponseInit
|
|
@@ -177,7 +179,7 @@ interface JSONRespond {
|
|
|
177
179
|
*/
|
|
178
180
|
type JSONRespondReturn<
|
|
179
181
|
T extends JSONValue | SimplifyDeepArray<unknown> | InvalidJSONValue,
|
|
180
|
-
U extends StatusCode
|
|
182
|
+
U extends StatusCode,
|
|
181
183
|
> = Response &
|
|
182
184
|
TypedResponse<
|
|
183
185
|
SimplifyDeepArray<T> extends JSONValue
|
|
@@ -205,9 +207,10 @@ interface HTMLRespond {
|
|
|
205
207
|
status?: StatusCode,
|
|
206
208
|
headers?: HeaderRecord
|
|
207
209
|
): T extends string ? Response : Promise<Response>
|
|
208
|
-
<T extends string | Promise<string>>(
|
|
209
|
-
|
|
210
|
-
|
|
210
|
+
<T extends string | Promise<string>>(
|
|
211
|
+
html: T,
|
|
212
|
+
init?: ResponseInit
|
|
213
|
+
): T extends string ? Response : Promise<Response>
|
|
211
214
|
}
|
|
212
215
|
|
|
213
216
|
/**
|
|
@@ -345,7 +348,7 @@ export class Context<
|
|
|
345
348
|
E extends Env = any,
|
|
346
349
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
347
350
|
P extends string = any,
|
|
348
|
-
I extends Input = {}
|
|
351
|
+
I extends Input = {},
|
|
349
352
|
> {
|
|
350
353
|
#rawRequest: Request
|
|
351
354
|
#req: HonoRequest<P, I['out']> | undefined
|
|
@@ -835,7 +838,7 @@ export class Context<
|
|
|
835
838
|
*/
|
|
836
839
|
json: JSONRespond = <
|
|
837
840
|
T extends JSONValue | SimplifyDeepArray<unknown> | InvalidJSONValue,
|
|
838
|
-
U extends StatusCode = StatusCode
|
|
841
|
+
U extends StatusCode = StatusCode,
|
|
839
842
|
>(
|
|
840
843
|
object: T,
|
|
841
844
|
arg?: U | ResponseInit,
|
|
@@ -34,13 +34,16 @@ export const parseAccept = (acceptHeader: string): Accept[] => {
|
|
|
34
34
|
const params = parts.slice(1) // ['q=0.9', 'image/webp']
|
|
35
35
|
const q = params.find((param) => param.startsWith('q='))
|
|
36
36
|
|
|
37
|
-
const paramsObject = params.reduce(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
const paramsObject = params.reduce(
|
|
38
|
+
(acc, param) => {
|
|
39
|
+
const keyValue = param.split('=')
|
|
40
|
+
const key = keyValue[0].trim()
|
|
41
|
+
const value = keyValue[1].trim()
|
|
42
|
+
acc[key] = value
|
|
43
|
+
return acc
|
|
44
|
+
},
|
|
45
|
+
{} as { [key: string]: string }
|
|
46
|
+
)
|
|
44
47
|
|
|
45
48
|
return {
|
|
46
49
|
type: type,
|
|
@@ -28,8 +28,8 @@ export const env = <T extends Record<string, unknown>, C extends Context = Conte
|
|
|
28
28
|
},
|
|
29
29
|
workerd: () => c.env,
|
|
30
30
|
// On Fastly Compute, you can use the ConfigStore to manage user-defined data.
|
|
31
|
-
fastly: () => ({} as T
|
|
32
|
-
other: () => ({} as T
|
|
31
|
+
fastly: () => ({}) as T,
|
|
32
|
+
other: () => ({}) as T,
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
return runtimeEnvHandlers[runtime]()
|
|
@@ -26,7 +26,7 @@ type CssClassName = HtmlEscapedString & CssClassNameCommon
|
|
|
26
26
|
|
|
27
27
|
type usedClassNameData = [
|
|
28
28
|
Record<string, string>, // class name to add
|
|
29
|
-
Record<string, true
|
|
29
|
+
Record<string, true>, // class name already added
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
interface CssType {
|