diffprism 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,231 @@
1
+ import {
2
+ ensureServer,
3
+ submitReviewToServer
4
+ } from "./chunk-ITPHDFOS.js";
5
+ import {
6
+ parseDiff
7
+ } from "./chunk-QGWYCEJN.js";
8
+ import {
9
+ analyze
10
+ } from "./chunk-DHCVZGHE.js";
11
+
12
+ // cli/src/demo-data/sample-diff.ts
13
+ var sampleDiff = `diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts
14
+ new file mode 100644
15
+ index 0000000..a1b2c3d
16
+ --- /dev/null
17
+ +++ b/src/middleware/auth.ts
18
+ @@ -0,0 +1,52 @@
19
+ +import jwt from "jsonwebtoken";
20
+ +import type { Request, Response, NextFunction } from "express";
21
+ +
22
+ +const JWT_SECRET = process.env.JWT_SECRET ?? "dev-secret";
23
+ +const TOKEN_EXPIRY = "24h";
24
+ +
25
+ +export interface AuthPayload {
26
+ + userId: string;
27
+ + email: string;
28
+ + role: "admin" | "user" | "viewer";
29
+ +}
30
+ +
31
+ +/**
32
+ + * Verify JWT token from Authorization header.
33
+ + * Attaches decoded payload to req.auth on success.
34
+ + */
35
+ +export function authenticate(
36
+ + req: Request,
37
+ + res: Response,
38
+ + next: NextFunction,
39
+ +): void {
40
+ + const header = req.headers.authorization;
41
+ +
42
+ + if (!header?.startsWith("Bearer ")) {
43
+ + res.status(401).json({ error: "Missing or invalid Authorization header" });
44
+ + return;
45
+ + }
46
+ +
47
+ + const token = header.slice(7);
48
+ +
49
+ + try {
50
+ + const decoded = jwt.verify(token, JWT_SECRET) as AuthPayload;
51
+ + (req as Request & { auth: AuthPayload }).auth = decoded;
52
+ + next();
53
+ + } catch (err) {
54
+ + if (err instanceof jwt.TokenExpiredError) {
55
+ + res.status(401).json({ error: "Token expired" });
56
+ + return;
57
+ + }
58
+ + // TODO: Add refresh token support
59
+ + console.log("Auth error:", err);
60
+ + res.status(401).json({ error: "Invalid token" });
61
+ + }
62
+ +}
63
+ +
64
+ +/**
65
+ + * Generate a signed JWT for the given user payload.
66
+ + */
67
+ +export function generateToken(payload: AuthPayload): string {
68
+ + return jwt.sign(payload, JWT_SECRET, { expiresIn: TOKEN_EXPIRY });
69
+ +}
70
+ diff --git a/src/routes/users.ts b/src/routes/users.ts
71
+ index d4e5f6a..b7c8d9e 100644
72
+ --- a/src/routes/users.ts
73
+ +++ b/src/routes/users.ts
74
+ @@ -1,6 +1,7 @@
75
+ import { Router } from "express";
76
+ import { db } from "../db/client.js";
77
+ import { validateBody } from "../util/validate.js";
78
+ +import { authenticate, type AuthPayload } from "../middleware/auth.js";
79
+
80
+ const router = Router();
81
+
82
+ @@ -12,6 +13,22 @@
83
+ res.json(users);
84
+ });
85
+
86
+ +// Protected routes \u2014 require valid JWT
87
+ +router.use(authenticate);
88
+ +
89
+ +router.get("/me", (req, res) => {
90
+ + const auth = (req as Request & { auth: AuthPayload }).auth;
91
+ + const user = db.users.findById(auth.userId);
92
+ +
93
+ + if (!user) {
94
+ + res.status(404).json({ error: "User not found" });
95
+ + return;
96
+ + }
97
+ +
98
+ + const { passwordHash, ...profile } = user;
99
+ + res.json(profile);
100
+ +});
101
+ +
102
+ router.post("/", validateBody("createUser"), async (req, res) => {
103
+ const { email, name, role } = req.body;
104
+
105
+ @@ -22,7 +39,7 @@
106
+ return;
107
+ }
108
+
109
+ - const user = await db.users.create({ email, name, role });
110
+ + const user = await db.users.create({ email, name, role: role ?? "user" });
111
+ res.status(201).json(user);
112
+ });
113
+
114
+ diff --git a/src/middleware/__tests__/auth.test.ts b/src/middleware/__tests__/auth.test.ts
115
+ new file mode 100644
116
+ index 0000000..e1f2a3b
117
+ --- /dev/null
118
+ +++ b/src/middleware/__tests__/auth.test.ts
119
+ @@ -0,0 +1,64 @@
120
+ +import { describe, it, expect, vi, beforeEach } from "vitest";
121
+ +import { authenticate, generateToken } from "../auth.js";
122
+ +
123
+ +function createMockReq(authHeader?: string) {
124
+ + return {
125
+ + headers: { authorization: authHeader },
126
+ + } as unknown as Request;
127
+ +}
128
+ +
129
+ +function createMockRes() {
130
+ + const res = {
131
+ + status: vi.fn().mockReturnThis(),
132
+ + json: vi.fn().mockReturnThis(),
133
+ + };
134
+ + return res as unknown as Response;
135
+ +}
136
+ +
137
+ +describe("authenticate middleware", () => {
138
+ + const validPayload = { userId: "u1", email: "test@example.com", role: "user" as const };
139
+ +
140
+ + it("rejects requests without Authorization header", () => {
141
+ + const req = createMockReq();
142
+ + const res = createMockRes();
143
+ + const next = vi.fn();
144
+ +
145
+ + authenticate(req as any, res as any, next);
146
+ +
147
+ + expect(res.status).toHaveBeenCalledWith(401);
148
+ + expect(next).not.toHaveBeenCalled();
149
+ + });
150
+ +
151
+ + it("rejects requests with invalid token", () => {
152
+ + const req = createMockReq("Bearer invalid-token");
153
+ + const res = createMockRes();
154
+ + const next = vi.fn();
155
+ +
156
+ + authenticate(req as any, res as any, next);
157
+ +
158
+ + expect(res.status).toHaveBeenCalledWith(401);
159
+ + expect(next).not.toHaveBeenCalled();
160
+ + });
161
+ +
162
+ + it("passes valid tokens and attaches auth payload", () => {
163
+ + const token = generateToken(validPayload);
164
+ + const req = createMockReq(\`Bearer \${token}\`);
165
+ + const res = createMockRes();
166
+ + const next = vi.fn();
167
+ +
168
+ + authenticate(req as any, res as any, next);
169
+ +
170
+ + expect(next).toHaveBeenCalled();
171
+ + expect((req as any).auth).toMatchObject({
172
+ + userId: "u1",
173
+ + email: "test@example.com",
174
+ + });
175
+ + });
176
+ +});
177
+ +
178
+ +describe("generateToken", () => {
179
+ + it("returns a string token", () => {
180
+ + const token = generateToken({ userId: "u1", email: "a@b.com", role: "admin" });
181
+ + expect(typeof token).toBe("string");
182
+ + expect(token.split(".")).toHaveLength(3); // JWT has 3 parts
183
+ + });
184
+ +});
185
+ `;
186
+
187
+ // cli/src/commands/demo.ts
188
+ async function demo(flags) {
189
+ try {
190
+ console.log("Starting DiffPrism demo...\n");
191
+ const diffSet = parseDiff(sampleDiff, "main", "feature/add-auth");
192
+ const briefing = analyze(diffSet);
193
+ console.log(
194
+ `${diffSet.files.length} files, +${diffSet.files.reduce((s, f) => s + f.additions, 0)} -${diffSet.files.reduce((s, f) => s + f.deletions, 0)}`
195
+ );
196
+ const payload = {
197
+ reviewId: "",
198
+ diffSet,
199
+ rawDiff: sampleDiff,
200
+ briefing,
201
+ metadata: {
202
+ title: "Add user authentication middleware",
203
+ reasoning: "Added JWT-based auth middleware to protect API routes. Included token validation, error handling for expired tokens, and unit tests for the middleware.",
204
+ currentBranch: "feature/add-auth"
205
+ }
206
+ };
207
+ const serverInfo = await ensureServer({ dev: flags.dev });
208
+ const { result } = await submitReviewToServer(serverInfo, "demo", {
209
+ injectedPayload: payload,
210
+ projectPath: "demo",
211
+ diffRef: "demo"
212
+ });
213
+ console.log(`
214
+ Review submitted: ${result.decision}`);
215
+ if (result.comments.length > 0) {
216
+ console.log(`${result.comments.length} comment(s)`);
217
+ }
218
+ console.log("\nNext steps:");
219
+ console.log(" Run `npx diffprism setup` to configure for Claude Code");
220
+ console.log(" Run `npx diffprism review` in a git repo to review real changes\n");
221
+ process.exit(0);
222
+ } catch (err) {
223
+ const message = err instanceof Error ? err.message : String(err);
224
+ console.error(`Error: ${message}`);
225
+ process.exit(1);
226
+ }
227
+ }
228
+
229
+ export {
230
+ demo
231
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ demo
3
+ } from "./chunk-UYZ3A2PB.js";
4
+ import "./chunk-ITPHDFOS.js";
5
+ import "./chunk-QGWYCEJN.js";
6
+ import "./chunk-DHCVZGHE.js";
7
+ import "./chunk-JSBRDJBE.js";
8
+ export {
9
+ demo
10
+ };
@@ -1,15 +1,17 @@
1
1
  import {
2
2
  createGitHubClient,
3
- ensureServer,
4
3
  fetchPullRequest,
5
4
  fetchPullRequestDiff,
6
- isServerAlive,
7
5
  normalizePr,
8
6
  parsePrRef,
9
7
  resolveGitHubToken,
10
- submitGitHubReview,
8
+ submitGitHubReview
9
+ } from "./chunk-OR6PCPZX.js";
10
+ import {
11
+ ensureServer,
12
+ isServerAlive,
11
13
  submitReviewToServer
12
- } from "./chunk-LUUR6LNP.js";
14
+ } from "./chunk-ITPHDFOS.js";
13
15
  import {
14
16
  getDiff
15
17
  } from "./chunk-QGWYCEJN.js";
@@ -27,7 +29,7 @@ var lastGlobalServerInfo = null;
27
29
  async function startMcpServer() {
28
30
  const server = new McpServer({
29
31
  name: "diffprism",
30
- version: true ? "0.35.0" : "0.0.0-dev"
32
+ version: true ? "0.36.0" : "0.0.0-dev"
31
33
  });
32
34
  server.tool(
33
35
  "open_review",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffprism",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "type": "module",
5
5
  "description": "Local-first code review tool for agent-generated code changes",
6
6
  "bin": {