guito 0.5.2 → 0.5.4

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.
@@ -5,6 +5,7 @@ import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
5
5
  import fastify from 'fastify';
6
6
  import fastifyStatic from '@fastify/static';
7
7
  import fastifyCors from '@fastify/cors';
8
+ import fastifyCompress from '@fastify/compress';
8
9
  import { simpleGit } from 'simple-git';
9
10
  import { promisify } from 'node:util';
10
11
  const execFileAsync = promisify(execFile);
@@ -15,6 +16,8 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
15
16
  });
16
17
  // Register cors
17
18
  await app.register(fastifyCors);
19
+ // Compress JSON responses (commit history payloads are highly repetitive)
20
+ await app.register(fastifyCompress);
18
21
  if (apiToken) {
19
22
  app.addHook('onRequest', async (request, reply) => {
20
23
  const pathname = request.url.split('?', 1)[0];
@@ -130,47 +133,100 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
130
133
  app.get('/api/repo', async (_req, resp) => {
131
134
  try {
132
135
  const root = (await git.revparse(['--show-toplevel'])).trim();
133
- resp.type('application/json').send({ root, name: basename(root) });
136
+ return resp.type('application/json').send({ root, name: basename(root) });
134
137
  }
135
138
  catch (err) {
136
- resp.status(400).type('application/json').send({ error: err.message });
139
+ return resp.status(400).type('application/json').send({ error: err.message });
137
140
  }
138
141
  });
139
142
  // ==================== Commits ====================
140
- app.get('/api/commits', async (_req, resp) => {
141
- try {
142
- const log = await git.log({
143
+ app.get('/api/commits', async (req, resp) => {
144
+ try {
145
+ const query = (req.query ?? {});
146
+ const limit = Number.parseInt(query.limit ?? '', 10);
147
+ const skip = Number.parseInt(query.skip ?? '', 10);
148
+ // Build simple-git options conditionally: an explicit key with an
149
+ // undefined value would still be emitted as a bare command flag.
150
+ // The commit body is intentionally omitted: it roughly doubles the
151
+ // payload and is only needed when a commit is opened, so it is served
152
+ // on demand by /api/commit/detail instead.
153
+ const options = {
143
154
  format: {
144
155
  hash: '%H',
145
156
  date: '%aI',
146
157
  message: '%s',
147
158
  refs: '%D',
148
- body: '%b',
149
159
  author_name: '%aN',
150
160
  author_email: '%aE',
151
161
  parents: '%P',
152
162
  },
153
- });
163
+ '--all': null,
164
+ };
165
+ if (Number.isFinite(limit) && limit > 0) {
166
+ options.maxCount = limit;
167
+ }
168
+ if (Number.isFinite(skip) && skip > 0) {
169
+ options['--skip'] = String(skip);
170
+ }
171
+ const [log, count] = await Promise.all([
172
+ git.log(options),
173
+ git.raw(['rev-list', '--count', '--all']),
174
+ ]);
154
175
  const commits = log.all.map((commit) => ({
155
176
  ...commit,
156
177
  parents: String(commit.parents ?? '')
157
178
  .split(' ')
158
179
  .filter(Boolean),
159
180
  }));
160
- resp.type('application/json').send(commits);
181
+ return resp
182
+ .type('application/json')
183
+ .send({ commits, total: Number.parseInt(count, 10) || 0 });
184
+ }
185
+ catch (err) {
186
+ // A repository without any commits has no history to list.
187
+ if (/does not have any commits yet|unknown revision|bad default revision/i.test(err.message ?? '')) {
188
+ return resp.type('application/json').send({ commits: [], total: 0 });
189
+ }
190
+ return resp.status(400).type('application/json').send({ error: err.message });
191
+ }
192
+ });
193
+ // Full-text commit search. The list payload omits commit bodies, so body
194
+ // matching runs here (git --grep covers subject and body together) and the
195
+ // client unions the resulting hashes with its local subject/author/hash
196
+ // matches over the loaded history.
197
+ app.get('/api/commits/search', async (req, resp) => {
198
+ const query = String((req.query ?? {}).query ?? '').trim();
199
+ if (!query) {
200
+ return resp.type('application/json').send({ hashes: [] });
201
+ }
202
+ try {
203
+ const raw = await git.raw([
204
+ 'log',
205
+ '--all',
206
+ '-i',
207
+ '--fixed-strings',
208
+ `--grep=${query}`,
209
+ '--format=%H',
210
+ ]);
211
+ return resp
212
+ .type('application/json')
213
+ .send({ hashes: raw.split('\n').map((hash) => hash.trim()).filter(Boolean) });
161
214
  }
162
215
  catch (err) {
163
- resp.status(400).type('application/json').send({ error: err.message });
216
+ if (/does not have any commits yet|unknown revision|bad default revision/i.test(err.message ?? '')) {
217
+ return resp.type('application/json').send({ hashes: [] });
218
+ }
219
+ return resp.status(400).type('application/json').send({ error: err.message });
164
220
  }
165
221
  });
166
222
  app.post('/api/commit', async (req, resp) => {
167
223
  try {
168
224
  const { message, description } = req.body;
169
225
  await git.commit([message, ...(description ? [description] : [])], { '--allow-empty': null });
170
- resp.type('application/json').send({ success: true });
226
+ return resp.type('application/json').send({ success: true });
171
227
  }
172
228
  catch (err) {
173
- resp.status(400).type('application/json').send({ error: err.message });
229
+ return resp.status(400).type('application/json').send({ error: err.message });
174
230
  }
175
231
  });
176
232
  app.post('/api/amend', async (req, resp) => {
@@ -180,57 +236,57 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
180
236
  '--amend': null,
181
237
  '--no-edit': null,
182
238
  });
183
- resp.type('application/json').send({ success: true });
239
+ return resp.type('application/json').send({ success: true });
184
240
  }
185
241
  catch (err) {
186
- resp.status(400).type('application/json').send({ error: err.message });
242
+ return resp.status(400).type('application/json').send({ error: err.message });
187
243
  }
188
244
  });
189
245
  app.post('/api/revert', async (req, resp) => {
190
246
  try {
191
247
  const { commit } = req.body;
192
248
  await git.revert(commit);
193
- resp.type('application/json').send({ success: true });
249
+ return resp.type('application/json').send({ success: true });
194
250
  }
195
251
  catch (err) {
196
- resp.status(400).type('application/json').send({ error: err.message });
252
+ return resp.status(400).type('application/json').send({ error: err.message });
197
253
  }
198
254
  });
199
255
  app.post('/api/cherry-pick', async (req, resp) => {
200
256
  try {
201
257
  await git.raw(['cherry-pick', req.body.commit]);
202
- resp.type('application/json').send({ success: true });
258
+ return resp.type('application/json').send({ success: true });
203
259
  }
204
260
  catch (err) {
205
- resp.status(400).type('application/json').send({ error: err.message });
261
+ return resp.status(400).type('application/json').send({ error: err.message });
206
262
  }
207
263
  });
208
264
  app.post('/api/commit/drop', async (req, resp) => {
209
265
  try {
210
266
  await git.raw(['reset', '--hard', `${req.body.commit}^`]);
211
- resp.type('application/json').send({ success: true });
267
+ return resp.type('application/json').send({ success: true });
212
268
  }
213
269
  catch (err) {
214
- resp.status(400).type('application/json').send({ error: err.message });
270
+ return resp.status(400).type('application/json').send({ error: err.message });
215
271
  }
216
272
  });
217
273
  app.post('/api/reset-commit', async (req, resp) => {
218
274
  try {
219
275
  await git.raw(['reset', '--hard', req.body.commit]);
220
- resp.type('application/json').send({ success: true });
276
+ return resp.type('application/json').send({ success: true });
221
277
  }
222
278
  catch (err) {
223
- resp.status(400).type('application/json').send({ error: err.message });
279
+ return resp.status(400).type('application/json').send({ error: err.message });
224
280
  }
225
281
  });
226
282
  // ==================== Staging ====================
227
283
  app.get('/api/status', async (_req, resp) => {
228
284
  try {
229
285
  const status = await git.status();
230
- resp.type('application/json').send(status);
286
+ return resp.type('application/json').send(status);
231
287
  }
232
288
  catch (err) {
233
- resp.status(400).type('application/json').send({ error: err.message });
289
+ return resp.status(400).type('application/json').send({ error: err.message });
234
290
  }
235
291
  });
236
292
  app.post('/api/stage', async (req, resp) => {
@@ -239,10 +295,10 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
239
295
  if (Array.isArray(files) && files.length > 0) {
240
296
  await git.add(files);
241
297
  }
242
- resp.type('application/json').send({ success: true });
298
+ return resp.type('application/json').send({ success: true });
243
299
  }
244
300
  catch (err) {
245
- resp.status(400).type('application/json').send({ error: err.message });
301
+ return resp.status(400).type('application/json').send({ error: err.message });
246
302
  }
247
303
  });
248
304
  app.post('/api/unstage', async (req, resp) => {
@@ -251,10 +307,10 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
251
307
  if (Array.isArray(files) && files.length > 0) {
252
308
  await git.reset(['HEAD', ...files]);
253
309
  }
254
- resp.type('application/json').send({ success: true });
310
+ return resp.type('application/json').send({ success: true });
255
311
  }
256
312
  catch (err) {
257
- resp.status(400).type('application/json').send({ error: err.message });
313
+ return resp.status(400).type('application/json').send({ error: err.message });
258
314
  }
259
315
  });
260
316
  app.post('/api/stage-lines', async (req, resp) => {
@@ -262,10 +318,10 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
262
318
  const { file, patch } = req.body;
263
319
  // Stage specific lines using git add -p equivalent (patch mode)
264
320
  await git.raw('add --patch', { input: patch });
265
- resp.type('application/json').send({ success: true });
321
+ return resp.type('application/json').send({ success: true });
266
322
  }
267
323
  catch (err) {
268
- resp.status(400).type('application/json').send({ error: err.message });
324
+ return resp.status(400).type('application/json').send({ error: err.message });
269
325
  }
270
326
  });
271
327
  // ==================== Branches ====================
@@ -287,69 +343,69 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
287
343
  const name = refname.replace(/^refs\/(heads|remotes)\//, '');
288
344
  return { name, commit, current: headMarker === '*', remote };
289
345
  });
290
- resp.type('application/json').send(branches);
346
+ return resp.type('application/json').send(branches);
291
347
  }
292
348
  catch (err) {
293
- resp.status(400).type('application/json').send({ error: err.message });
349
+ return resp.status(400).type('application/json').send({ error: err.message });
294
350
  }
295
351
  });
296
352
  app.get('/api/branches', async (_req, resp) => {
297
353
  try {
298
354
  const branchSummary = await git.branchLocal();
299
- resp.type('application/json').send(branchSummary);
355
+ return resp.type('application/json').send(branchSummary);
300
356
  }
301
357
  catch (err) {
302
- resp.status(400).type('application/json').send({ error: err.message });
358
+ return resp.status(400).type('application/json').send({ error: err.message });
303
359
  }
304
360
  });
305
361
  app.post('/api/branch/create', async (req, resp) => {
306
362
  try {
307
363
  const { name, startPoint } = req.body;
308
364
  await git.branch([...(startPoint ? [name, startPoint] : [name])]);
309
- resp.type('application/json').send({ success: true });
365
+ return resp.type('application/json').send({ success: true });
310
366
  }
311
367
  catch (err) {
312
- resp.status(400).type('application/json').send({ error: err.message });
368
+ return resp.status(400).type('application/json').send({ error: err.message });
313
369
  }
314
370
  });
315
371
  app.post('/api/branch/delete', async (req, resp) => {
316
372
  try {
317
373
  const { name, force } = req.body;
318
374
  await git.deleteLocalBranch(name, force);
319
- resp.type('application/json').send({ success: true });
375
+ return resp.type('application/json').send({ success: true });
320
376
  }
321
377
  catch (err) {
322
- resp.status(400).type('application/json').send({ error: err.message });
378
+ return resp.status(400).type('application/json').send({ error: err.message });
323
379
  }
324
380
  });
325
381
  app.post('/api/branch/delete-remote', async (req, resp) => {
326
382
  try {
327
383
  const { remote, branch } = req.body;
328
384
  await git.push([remote, '--delete', branch]);
329
- resp.type('application/json').send({ success: true });
385
+ return resp.type('application/json').send({ success: true });
330
386
  }
331
387
  catch (err) {
332
- resp.status(400).type('application/json').send({ error: err.message });
388
+ return resp.status(400).type('application/json').send({ error: err.message });
333
389
  }
334
390
  });
335
391
  app.post('/api/branch/rename', async (req, resp) => {
336
392
  try {
337
393
  const { oldName, newName } = req.body;
338
394
  await git.branch(['-m', oldName, newName]);
339
- resp.type('application/json').send({ success: true });
395
+ return resp.type('application/json').send({ success: true });
340
396
  }
341
397
  catch (err) {
342
- resp.status(400).type('application/json').send({ error: err.message });
398
+ return resp.status(400).type('application/json').send({ error: err.message });
343
399
  }
344
400
  });
345
401
  app.post('/api/checkout', async (req, resp) => {
346
402
  try {
347
403
  const { ref } = req.body;
348
404
  await git.checkout(ref);
349
- resp.type('application/json').send({ success: true });
405
+ return resp.type('application/json').send({ success: true });
350
406
  }
351
407
  catch (err) {
352
- resp.status(400).type('application/json').send({ error: err.message });
408
+ return resp.status(400).type('application/json').send({ error: err.message });
353
409
  }
354
410
  });
355
411
  // ==================== Merge & Rebase ====================
@@ -357,10 +413,10 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
357
413
  try {
358
414
  const { branch } = req.body;
359
415
  await git.merge([branch]);
360
- resp.type('application/json').send({ success: true });
416
+ return resp.type('application/json').send({ success: true });
361
417
  }
362
418
  catch (err) {
363
- resp.status(400).type('application/json').send({ error: err.message });
419
+ return resp.status(400).type('application/json').send({ error: err.message });
364
420
  }
365
421
  });
366
422
  app.get('/api/archive', async (req, resp) => {
@@ -387,10 +443,10 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
387
443
  try {
388
444
  const { branch } = req.body;
389
445
  await git.rebase([branch]);
390
- resp.type('application/json').send({ success: true });
446
+ return resp.type('application/json').send({ success: true });
391
447
  }
392
448
  catch (err) {
393
- resp.status(400).type('application/json').send({ error: err.message });
449
+ return resp.status(400).type('application/json').send({ error: err.message });
394
450
  }
395
451
  });
396
452
  app.post('/api/squash', async (req, resp) => {
@@ -398,80 +454,80 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
398
454
  const { commits } = req.body;
399
455
  // Squash: rebase -i with squash operation
400
456
  await git.rebase(['-i', 'HEAD~' + commits]);
401
- resp.type('application/json').send({ success: true });
457
+ return resp.type('application/json').send({ success: true });
402
458
  }
403
459
  catch (err) {
404
- resp.status(400).type('application/json').send({ error: err.message });
460
+ return resp.status(400).type('application/json').send({ error: err.message });
405
461
  }
406
462
  });
407
463
  // ==================== Stash ====================
408
464
  app.get('/api/stash/list', async (_req, resp) => {
409
465
  try {
410
466
  const stashList = await git.stashList();
411
- resp.type('application/json').send(stashList);
467
+ return resp.type('application/json').send(stashList);
412
468
  }
413
469
  catch (err) {
414
- resp.status(400).type('application/json').send({ error: err.message });
470
+ return resp.status(400).type('application/json').send({ error: err.message });
415
471
  }
416
472
  });
417
473
  app.post('/api/stash/save', async (req, resp) => {
418
474
  try {
419
475
  const { message } = req.body;
420
476
  await git.stash(['save', message || '']);
421
- resp.type('application/json').send({ success: true });
477
+ return resp.type('application/json').send({ success: true });
422
478
  }
423
479
  catch (err) {
424
- resp.status(400).type('application/json').send({ error: err.message });
480
+ return resp.status(400).type('application/json').send({ error: err.message });
425
481
  }
426
482
  });
427
483
  app.post('/api/stash/apply', async (req, resp) => {
428
484
  try {
429
485
  const { index } = req.body;
430
486
  await git.stash(['apply', `stash@{${index}}`]);
431
- resp.type('application/json').send({ success: true });
487
+ return resp.type('application/json').send({ success: true });
432
488
  }
433
489
  catch (err) {
434
- resp.status(400).type('application/json').send({ error: err.message });
490
+ return resp.status(400).type('application/json').send({ error: err.message });
435
491
  }
436
492
  });
437
493
  app.post('/api/stash/pop', async (req, resp) => {
438
494
  try {
439
495
  const { index } = req.body;
440
496
  await git.stash(['pop', `stash@{${index}}`]);
441
- resp.type('application/json').send({ success: true });
497
+ return resp.type('application/json').send({ success: true });
442
498
  }
443
499
  catch (err) {
444
- resp.status(400).type('application/json').send({ error: err.message });
500
+ return resp.status(400).type('application/json').send({ error: err.message });
445
501
  }
446
502
  });
447
503
  app.post('/api/stash/drop', async (req, resp) => {
448
504
  try {
449
505
  const { index } = req.body;
450
506
  await git.stash(['drop', `stash@{${index}}`]);
451
- resp.type('application/json').send({ success: true });
507
+ return resp.type('application/json').send({ success: true });
452
508
  }
453
509
  catch (err) {
454
- resp.status(400).type('application/json').send({ error: err.message });
510
+ return resp.status(400).type('application/json').send({ error: err.message });
455
511
  }
456
512
  });
457
513
  app.post('/api/stash/show', async (req, resp) => {
458
514
  try {
459
515
  const { index } = req.body;
460
516
  const show = await git.stash(['show', '-p', `stash@{${index}}`]);
461
- resp.type('application/json').send({ preview: show });
517
+ return resp.type('application/json').send({ preview: show });
462
518
  }
463
519
  catch (err) {
464
- resp.status(400).type('application/json').send({ error: err.message });
520
+ return resp.status(400).type('application/json').send({ error: err.message });
465
521
  }
466
522
  });
467
523
  // ==================== Tags ====================
468
524
  app.get('/api/tags', async (_req, resp) => {
469
525
  try {
470
526
  const tags = await git.tags();
471
- resp.type('application/json').send(tags);
527
+ return resp.type('application/json').send(tags);
472
528
  }
473
529
  catch (err) {
474
- resp.status(400).type('application/json').send({ error: err.message });
530
+ return resp.status(400).type('application/json').send({ error: err.message });
475
531
  }
476
532
  });
477
533
  app.post('/api/tag/create', async (req, resp) => {
@@ -483,70 +539,103 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
483
539
  else {
484
540
  await git.tag([name, ...(commit ? [commit] : [])]);
485
541
  }
486
- resp.type('application/json').send({ success: true });
542
+ return resp.type('application/json').send({ success: true });
487
543
  }
488
544
  catch (err) {
489
- resp.status(400).type('application/json').send({ error: err.message });
545
+ return resp.status(400).type('application/json').send({ error: err.message });
490
546
  }
491
547
  });
492
548
  app.post('/api/tag/delete', async (req, resp) => {
493
549
  try {
494
550
  const { name } = req.body;
495
551
  await git.tag(['-d', name]);
496
- resp.type('application/json').send({ success: true });
552
+ return resp.type('application/json').send({ success: true });
497
553
  }
498
554
  catch (err) {
499
- resp.status(400).type('application/json').send({ error: err.message });
555
+ return resp.status(400).type('application/json').send({ error: err.message });
500
556
  }
501
557
  });
502
558
  // ==================== Remote Operations ====================
503
559
  app.get('/api/fetch', async (_req, resp) => {
504
560
  try {
505
561
  await git.fetch();
506
- resp.type('application/json').send({ success: true });
562
+ return resp.type('application/json').send({ success: true });
507
563
  }
508
564
  catch (err) {
509
- resp.status(400).type('application/json').send({ error: err.message });
565
+ return resp.status(400).type('application/json').send({ error: err.message });
510
566
  }
511
567
  });
512
568
  app.post('/api/pull', async (req, resp) => {
513
569
  try {
514
570
  const { remote, branch, rebase } = req.body;
515
571
  await git.pull(remote || 'origin', branch || undefined, rebase ? { '--rebase': null } : {});
516
- resp.type('application/json').send({ success: true });
572
+ return resp.type('application/json').send({ success: true });
517
573
  }
518
574
  catch (err) {
519
- resp.status(400).type('application/json').send({ error: err.message });
575
+ return resp.status(400).type('application/json').send({ error: err.message });
520
576
  }
521
577
  });
522
578
  app.post('/api/push', async (req, resp) => {
523
579
  try {
524
580
  const { remote, branch, force } = req.body;
525
581
  await git.push(remote || 'origin', branch || undefined, force ? { '-f': null } : {});
526
- resp.type('application/json').send({ success: true });
582
+ return resp.type('application/json').send({ success: true });
527
583
  }
528
584
  catch (err) {
529
- resp.status(400).type('application/json').send({ error: err.message });
585
+ return resp.status(400).type('application/json').send({ error: err.message });
530
586
  }
531
587
  });
532
588
  app.post('/api/sync', async (_req, resp) => {
533
589
  try {
534
590
  await git.pull('origin', undefined, {});
535
591
  await git.push('origin', undefined, {});
536
- resp.type('application/json').send({ success: true });
592
+ return resp.type('application/json').send({ success: true });
537
593
  }
538
594
  catch (err) {
539
- resp.status(400).type('application/json').send({ error: err.message });
595
+ return resp.status(400).type('application/json').send({ error: err.message });
540
596
  }
541
597
  });
542
598
  app.post('/api/prune', async (req, resp) => {
543
599
  try {
544
600
  const { remote } = req.body;
545
601
  await git.remote(['prune', remote || 'origin']);
546
- resp.type('application/json').send({ success: true });
602
+ return resp.type('application/json').send({ success: true });
547
603
  }
548
604
  catch (err) {
549
- resp.status(400).type('application/json').send({ error: err.message });
605
+ return resp.status(400).type('application/json').send({ error: err.message });
606
+ }
607
+ });
608
+ // ==================== Commit Detail ====================
609
+ app.post('/api/commit/detail', async (req, resp) => {
610
+ try {
611
+ const { hash } = req.body;
612
+ if (!hash) {
613
+ return resp.status(400).type('application/json').send({ error: 'hash required' });
614
+ }
615
+ // Fields are NUL-separated; %b may span lines and contain anything
616
+ // except NUL, so it cannot be split naively by newline.
617
+ const raw = await git.raw([
618
+ 'show',
619
+ '-s',
620
+ '--format=%H%x00%aI%x00%s%x00%b%x00%aN%x00%aE%x00%P',
621
+ hash,
622
+ ]);
623
+ const [fullHash, date, message, body, authorName, authorEmail, parents] = raw.trimEnd().split('\u0000');
624
+ return resp.type('application/json').send({
625
+ hash: fullHash,
626
+ date,
627
+ message,
628
+ refs: '',
629
+ body: body ?? '',
630
+ author_name: authorName,
631
+ author_email: authorEmail,
632
+ parents: String(parents ?? '')
633
+ .split(' ')
634
+ .filter(Boolean),
635
+ });
636
+ }
637
+ catch (err) {
638
+ return resp.status(400).type('application/json').send({ error: err.message });
550
639
  }
551
640
  });
552
641
  // ==================== Diff Viewer ====================
@@ -566,10 +655,10 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
566
655
  '-m',
567
656
  hash,
568
657
  ]);
569
- resp.type('application/json').send({ hash, files: parseUnifiedDiff(rawDiff) });
658
+ return resp.type('application/json').send({ hash, files: parseUnifiedDiff(rawDiff) });
570
659
  }
571
660
  catch (err) {
572
- resp.status(400).type('application/json').send({ error: err.message });
661
+ return resp.status(400).type('application/json').send({ error: err.message });
573
662
  }
574
663
  });
575
664
  // ==================== Working Changes ====================
@@ -620,7 +709,7 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
620
709
  }
621
710
  const stagedRaw = await git.raw(['diff', '--name-only', '--cached']);
622
711
  const unstagedRaw = await git.raw(['diff', '--name-only']);
623
- resp.type('application/json').send({
712
+ return resp.type('application/json').send({
624
713
  files,
625
714
  staged: stagedRaw.split('\n').filter(Boolean),
626
715
  unstaged: unstagedRaw.split('\n').filter(Boolean),
@@ -628,7 +717,7 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
628
717
  });
629
718
  }
630
719
  catch (err) {
631
- resp.status(400).type('application/json').send({ error: err.message });
720
+ return resp.status(400).type('application/json').send({ error: err.message });
632
721
  }
633
722
  });
634
723
  // ==================== File Content ====================
@@ -665,15 +754,15 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
665
754
  if (content.includes('\u0000')) {
666
755
  return resp.type('application/json').send({ binary: true, content: '' });
667
756
  }
668
- resp.type('application/json').send({ content });
757
+ return resp.type('application/json').send({ content });
669
758
  }
670
759
  catch {
671
760
  // Path did not exist at that revision (added file).
672
- resp.type('application/json').send({ content: '' });
761
+ return resp.type('application/json').send({ content: '' });
673
762
  }
674
763
  }
675
764
  catch (err) {
676
- resp.status(400).type('application/json').send({ error: err.message });
765
+ return resp.status(400).type('application/json').send({ error: err.message });
677
766
  }
678
767
  });
679
768
  // ==================== Discard ====================
@@ -694,29 +783,29 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
694
783
  for (const file of removed) {
695
784
  await rm(join(root, file), { force: true, recursive: true });
696
785
  }
697
- resp.type('application/json').send({ success: true });
786
+ return resp.type('application/json').send({ success: true });
698
787
  }
699
788
  catch (err) {
700
- resp.status(400).type('application/json').send({ error: err.message });
789
+ return resp.status(400).type('application/json').send({ error: err.message });
701
790
  }
702
791
  });
703
792
  // ==================== Working Tree Actions ====================
704
793
  app.post('/api/reset', async (_req, resp) => {
705
794
  try {
706
795
  await git.raw(['reset', '--hard', 'HEAD']);
707
- resp.type('application/json').send({ ok: true });
796
+ return resp.type('application/json').send({ ok: true });
708
797
  }
709
798
  catch (err) {
710
- resp.status(400).type('application/json').send({ error: err.message });
799
+ return resp.status(400).type('application/json').send({ error: err.message });
711
800
  }
712
801
  });
713
802
  app.post('/api/clean', async (_req, resp) => {
714
803
  try {
715
804
  await git.raw(['clean', '-fd']);
716
- resp.type('application/json').send({ ok: true });
805
+ return resp.type('application/json').send({ ok: true });
717
806
  }
718
807
  catch (err) {
719
- resp.status(400).type('application/json').send({ error: err.message });
808
+ return resp.status(400).type('application/json').send({ error: err.message });
720
809
  }
721
810
  });
722
811
  const address = await app.listen({ port, ...(host ? { host } : {}) });