@ripla/godd-mcp 1.0.2-canary.6 → 1.0.2-canary.7

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.
@@ -98,6 +98,52 @@ describe("formatApiDetail (via loginApi error path)", () => {
98
98
  });
99
99
  });
100
100
 
101
+ describe("delete APIs", () => {
102
+ beforeEach(() => {
103
+ vi.restoreAllMocks();
104
+ localStorage.clear();
105
+ });
106
+
107
+ it("deleteFileApi は HTML エラーを JSON としてパースしない", async () => {
108
+ const { deleteFileApi, setToken } = await import("./api");
109
+ setToken("test-token");
110
+ vi.spyOn(globalThis, "fetch").mockResolvedValue(
111
+ new Response("<!DOCTYPE html><html></html>", {
112
+ status: 500,
113
+ headers: { "Content-Type": "text/html" },
114
+ }),
115
+ );
116
+
117
+ await expect(deleteFileApi("docs/a.md")).rejects.toThrow("Delete failed: 500");
118
+ });
119
+
120
+ it("deleteFileApi は成功時の非 JSON レスポンスを分かるエラーにする", async () => {
121
+ const { deleteFileApi, setToken } = await import("./api");
122
+ setToken("test-token");
123
+ vi.spyOn(globalThis, "fetch").mockResolvedValue(
124
+ new Response("<!DOCTYPE html><html></html>", {
125
+ status: 200,
126
+ headers: { "Content-Type": "text/html" },
127
+ }),
128
+ );
129
+
130
+ await expect(deleteFileApi("docs/a.md")).rejects.toThrow("Delete response was not JSON");
131
+ });
132
+
133
+ it("deleteFolderApi は JSON エラー detail を表示する", async () => {
134
+ const { deleteFolderApi, setToken } = await import("./api");
135
+ setToken("test-token");
136
+ vi.spyOn(globalThis, "fetch").mockResolvedValue(
137
+ new Response(JSON.stringify({ detail: "Directory is empty or not found" }), {
138
+ status: 404,
139
+ headers: { "Content-Type": "application/json" },
140
+ }),
141
+ );
142
+
143
+ await expect(deleteFolderApi("docs/empty")).rejects.toThrow("Directory is empty or not found");
144
+ });
145
+ });
146
+
101
147
  describe("fetchBranchStatusApi", () => {
102
148
  beforeEach(() => {
103
149
  vi.restoreAllMocks();
@@ -139,10 +139,10 @@ export async function deleteFileApi(
139
139
  ): Promise<{ success: boolean }> {
140
140
  const res = await apiFetch(`/files/${encodePath(path)}`, { method: "DELETE" });
141
141
  if (!res.ok) {
142
- const body = await res.json().catch(() => null);
142
+ const body = await readJsonBody(res);
143
143
  throw new Error(formatApiDetail(body, `Delete failed: ${res.status}`));
144
144
  }
145
- return res.json();
145
+ return parseJsonBody(res, "Delete response was not JSON");
146
146
  }
147
147
 
148
148
  export async function deleteFolderApi(
@@ -150,10 +150,10 @@ export async function deleteFolderApi(
150
150
  ): Promise<{ success: boolean; deletedCount: number }> {
151
151
  const res = await apiFetch(`/folder/${encodePath(path)}`, { method: "DELETE" });
152
152
  if (!res.ok) {
153
- const body = await res.json().catch(() => null);
153
+ const body = await readJsonBody(res);
154
154
  throw new Error(formatApiDetail(body, `Folder delete failed: ${res.status}`));
155
155
  }
156
- return res.json();
156
+ return parseJsonBody(res, "Folder delete response was not JSON");
157
157
  }
158
158
 
159
159
  export async function moveFolderApi(
@@ -567,3 +567,15 @@ export function formatApiDetail(body: unknown, fallback: string): string {
567
567
  }
568
568
  return fallback;
569
569
  }
570
+
571
+ async function readJsonBody(res: Response): Promise<unknown | null> {
572
+ const contentType = res.headers.get("content-type") ?? "";
573
+ if (!contentType.toLowerCase().includes("application/json")) return null;
574
+ return res.json().catch(() => null);
575
+ }
576
+
577
+ async function parseJsonBody<T>(res: Response, fallback: string): Promise<T> {
578
+ const body = await readJsonBody(res);
579
+ if (body === null) throw new Error(fallback);
580
+ return body as T;
581
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ripla/godd-mcp",
3
- "version": "1.0.2-canary.6",
3
+ "version": "1.0.2-canary.7",
4
4
  "type": "module",
5
5
  "description": "GoDD (Governance-orchestrated Driven Development) MCP Server - Encrypted prompt distribution via Model Context Protocol",
6
6
  "main": "dist/index.js",