@foss.global/gitmanager 2.2.0 → 2.4.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.
@@ -2,6 +2,8 @@ import * as plugins from './plugins.js';
2
2
  import { GitManagerError, isGitManagerErrorCode } from './classes.gitmanagererror.js';
3
3
  import type {
4
4
  IGitManagerAncestryRequest,
5
+ IGitManagerAtomicPushRequest,
6
+ IGitManagerAtomicPushResult,
5
7
  IGitManagerCommitDetail,
6
8
  IGitManagerCommitRequest,
7
9
  IGitManagerCommitSummary,
@@ -16,6 +18,7 @@ import type {
16
18
  IGitManagerMergeResult,
17
19
  IGitManagerReadFileRequest,
18
20
  IGitManagerRefRequest,
21
+ IGitManagerRefreshMirrorRequest,
19
22
  IGitManagerProtectedRefsApplyResult,
20
23
  IGitManagerSmartHttpRepositoryProtectedRefs,
21
24
  IGitManagerRepositoryBundle,
@@ -23,10 +26,11 @@ import type {
23
26
  IGitManagerRepositoryArchiveOptions,
24
27
  IGitManagerRepositoryArchiveRequest,
25
28
  IGitManagerRepositoryLfsPointer,
26
- IGitManagerRepositoryServiceRequest,
27
29
  IGitManagerRepositoryInfo,
28
30
  IGitManagerRepositorySelector,
31
+ IGitManagerRepositorySize,
29
32
  IGitManagerRestoreBundleRequest,
33
+ IGitManagerSetDefaultBranchRequest,
30
34
  IGitManagerSmartHttpRepositoryAccess,
31
35
  IGitManagerSmartHttpRepositoryVisibility,
32
36
  IGitManagerStatus,
@@ -34,9 +38,11 @@ import type {
34
38
  IGitManagerTagRequest,
35
39
  IGitManagerTreeEntry,
36
40
  IGitManagerTreeRequest,
41
+ IGitManagerTransferControlOptions,
37
42
  IRustGitManagerConfig,
38
43
  TGitManagerRefInventoryResult,
39
44
  IRustGitManagerRepositoryServiceDescriptor,
45
+ IRustGitManagerRepositoryServicePrepareRequest,
40
46
  } from './models.js';
41
47
 
42
48
  type TRepositoryArchiveCancelStatus =
@@ -54,6 +60,14 @@ interface IRepositoryArchiveCancelRequest {
54
60
  operationId: string;
55
61
  }
56
62
 
63
+ /** A remote transfer under the operation ID that `cancelTransfer` names. */
64
+ interface ITransferOperationRequest<TRequest> {
65
+ operationId: string;
66
+ request: TRequest;
67
+ }
68
+
69
+ type TTransferCancelStatus = 'cancelling' | 'not_found';
70
+
57
71
  type TGitManagerCommands = {
58
72
  start: { params: { config: IRustGitManagerConfig }; result: Record<string, never> };
59
73
  stop: { params: Record<string, never>; result: Record<string, never> };
@@ -62,13 +76,27 @@ type TGitManagerCommands = {
62
76
  setSmartHttpRepositoryVisibility: { params: IGitManagerSmartHttpRepositoryVisibility; result: Record<string, never> };
63
77
  setSmartHttpRepositoryProtectedRefs: { params: IGitManagerSmartHttpRepositoryProtectedRefs; result: IGitManagerProtectedRefsApplyResult };
64
78
  initBareRepository: { params: IGitManagerInitBareRepositoryRequest; result: IGitManagerRepositoryInfo };
65
- importMirror: { params: IGitManagerImportMirrorRequest; result: IGitManagerRepositoryInfo };
66
- refreshMirror: { params: IGitManagerRepositorySelector; result: IGitManagerRepositoryInfo };
79
+ importMirror: {
80
+ params: ITransferOperationRequest<IGitManagerImportMirrorRequest>;
81
+ result: IGitManagerRepositoryInfo;
82
+ };
83
+ refreshMirror: {
84
+ params: ITransferOperationRequest<IGitManagerRefreshMirrorRequest>;
85
+ result: IGitManagerRepositoryInfo;
86
+ };
87
+ pushRefs: {
88
+ params: ITransferOperationRequest<IGitManagerAtomicPushRequest>;
89
+ result: IGitManagerAtomicPushResult;
90
+ };
91
+ cancelTransfer: { params: { operationId: string }; result: { status: TTransferCancelStatus } };
92
+ setDefaultBranch: { params: IGitManagerSetDefaultBranchRequest; result: IGitManagerRepositoryInfo };
67
93
  verifyRepository: { params: IGitManagerRepositorySelector; result: IGitManagerRepositoryInfo };
94
+ getRepositorySize: { params: IGitManagerRepositorySelector; result: IGitManagerRepositorySize };
68
95
  createRepositoryBundle: { params: IGitManagerRepositorySelector; result: IGitManagerRepositoryBundle };
69
96
  createRepositoryArchive: { params: IRepositoryArchiveOperationRequest; result: IGitManagerRepositoryArchive };
70
97
  cancelRepositoryArchive: { params: IRepositoryArchiveCancelRequest; result: { status: TRepositoryArchiveCancelStatus } };
71
- prepareRepositoryService: { params: IGitManagerRepositoryServiceRequest; result: IRustGitManagerRepositoryServiceDescriptor };
98
+ prepareRepositoryService: { params: IRustGitManagerRepositoryServicePrepareRequest; result: IRustGitManagerRepositoryServiceDescriptor };
99
+ releaseRepositoryService: { params: { ticketId: string }; result: { released: boolean } };
72
100
  restoreRepositoryBundle: { params: IGitManagerRestoreBundleRequest; result: IGitManagerRepositoryInfo };
73
101
  listRepositories: { params: Record<string, never>; result: IGitManagerRepositoryInfo[] };
74
102
  listRefs: { params: IGitManagerRefRequest; result: TGitManagerRefInventoryResult };
@@ -84,6 +112,19 @@ type TGitManagerCommands = {
84
112
  getTag: { params: IGitManagerTagRequest; result: IGitManagerTag };
85
113
  };
86
114
 
115
+ /** Engine default for a remote transfer without `timeoutMs`. */
116
+ const defaultRemoteTransferTimeoutMs = 120_000;
117
+
118
+ /**
119
+ * The bridge waits past the engine's own transfer limit, so the engine reports
120
+ * a timeout and cleans up before the request is abandoned.
121
+ */
122
+ const remoteTransferResponseTimeoutMs = (timeoutMsArg: number | undefined): number =>
123
+ (timeoutMsArg ?? defaultRemoteTransferTimeoutMs) + 30_000;
124
+
125
+ /** The cancel command is answered at once; the transfer reports its own end. */
126
+ const transferCancelTimeoutMs = 30_000;
127
+
87
128
  function getPackageRoot(): string {
88
129
  const thisDir = plugins.path.dirname(plugins.url.fileURLToPath(import.meta.url));
89
130
  return plugins.path.resolve(thisDir, '..');
@@ -214,15 +255,79 @@ export class RustGitManagerBridge extends plugins.EventEmitter {
214
255
  }
215
256
 
216
257
  public async initBareRepository(requestArg: IGitManagerInitBareRepositoryRequest): Promise<IGitManagerRepositoryInfo> {
217
- return this.bridge.sendCommand('initBareRepository', requestArg);
258
+ return this.bridge.sendCommand('initBareRepository', requestArg).catch(rethrowCodedError);
218
259
  }
219
260
 
220
- public async importMirror(requestArg: IGitManagerImportMirrorRequest): Promise<IGitManagerRepositoryInfo> {
221
- return this.bridge.sendCommand('importMirror', requestArg);
261
+ public async importMirror(
262
+ requestArg: IGitManagerImportMirrorRequest,
263
+ optionsArg: IGitManagerTransferControlOptions = {},
264
+ ): Promise<IGitManagerRepositoryInfo> {
265
+ return this.runTransfer('importMirror', requestArg, optionsArg);
222
266
  }
223
267
 
224
- public async refreshMirror(requestArg: IGitManagerRepositorySelector): Promise<IGitManagerRepositoryInfo> {
225
- return this.bridge.sendCommand('refreshMirror', requestArg);
268
+ public async refreshMirror(
269
+ requestArg: IGitManagerRefreshMirrorRequest,
270
+ optionsArg: IGitManagerTransferControlOptions = {},
271
+ ): Promise<IGitManagerRepositoryInfo> {
272
+ return this.runTransfer('refreshMirror', requestArg, optionsArg);
273
+ }
274
+
275
+ public async pushRefs(
276
+ requestArg: IGitManagerAtomicPushRequest,
277
+ optionsArg: IGitManagerTransferControlOptions = {},
278
+ ): Promise<IGitManagerAtomicPushResult> {
279
+ return this.runTransfer('pushRefs', requestArg, optionsArg);
280
+ }
281
+
282
+ /**
283
+ * Runs a remote transfer under a fresh operation ID. Aborting the signal
284
+ * sends `cancelTransfer`; the engine then terminates the transfer's Git
285
+ * process and answers the transfer itself with `transferAborted`. A signal
286
+ * that is already aborted rejects before anything is sent.
287
+ */
288
+ private async runTransfer<TMethod extends 'importMirror' | 'refreshMirror' | 'pushRefs'>(
289
+ methodArg: TMethod,
290
+ requestArg: TGitManagerCommands[TMethod]['params']['request'],
291
+ optionsArg: IGitManagerTransferControlOptions,
292
+ ): Promise<TGitManagerCommands[TMethod]['result']> {
293
+ const signal = optionsArg.signal;
294
+ if (signal?.aborted) {
295
+ throw new GitManagerError('transferAborted', 'The transfer was aborted before it started', {
296
+ cause: signal.reason,
297
+ });
298
+ }
299
+ const operationId = plugins.crypto.randomUUID();
300
+ const params = { operationId, request: requestArg } as TGitManagerCommands[TMethod]['params'];
301
+ const transfer = this.bridge.sendCommand(methodArg, params, {
302
+ timeoutMs: remoteTransferResponseTimeoutMs(requestArg.timeoutMs),
303
+ });
304
+ const cancelTransfer = (): void => {
305
+ // Only the transfer's own response settles the call.
306
+ void this.bridge.sendCommand('cancelTransfer', { operationId }, {
307
+ timeoutMs: transferCancelTimeoutMs,
308
+ }).catch(() => undefined);
309
+ };
310
+ signal?.addEventListener('abort', cancelTransfer, { once: true });
311
+ try {
312
+ return await transfer;
313
+ } catch (error) {
314
+ return rethrowCodedError(error);
315
+ } finally {
316
+ signal?.removeEventListener('abort', cancelTransfer);
317
+ }
318
+ }
319
+
320
+ public async setDefaultBranch(
321
+ requestArg: IGitManagerSetDefaultBranchRequest,
322
+ ): Promise<IGitManagerRepositoryInfo> {
323
+ return this.bridge.sendCommand('setDefaultBranch', requestArg).catch(rethrowCodedError);
324
+ }
325
+
326
+ public async getRepositorySize(requestArg: IGitManagerRepositorySelector): Promise<IGitManagerRepositorySize> {
327
+ // The engine measures for up to 30 minutes.
328
+ return this.bridge.sendCommand('getRepositorySize', requestArg, {
329
+ timeoutMs: 31 * 60 * 1000,
330
+ });
226
331
  }
227
332
 
228
333
  public async verifyRepository(requestArg: IGitManagerRepositorySelector): Promise<IGitManagerRepositoryInfo> {
@@ -296,15 +401,20 @@ export class RustGitManagerBridge extends plugins.EventEmitter {
296
401
  }
297
402
 
298
403
  public async prepareRepositoryService(
299
- requestArg: IGitManagerRepositoryServiceRequest,
404
+ requestArg: IRustGitManagerRepositoryServicePrepareRequest,
300
405
  ): Promise<IRustGitManagerRepositoryServiceDescriptor> {
301
- return this.bridge.sendCommand('prepareRepositoryService', requestArg);
406
+ return this.bridge.sendCommand('prepareRepositoryService', requestArg).catch(rethrowCodedError);
407
+ }
408
+
409
+ /** Releases a receive-pack guard ticket; unknown or released tickets are a no-op. */
410
+ public async releaseRepositoryService(ticketIdArg: string): Promise<void> {
411
+ await this.bridge.sendCommand('releaseRepositoryService', { ticketId: ticketIdArg });
302
412
  }
303
413
 
304
414
  public async restoreRepositoryBundle(
305
415
  requestArg: IGitManagerRestoreBundleRequest,
306
416
  ): Promise<IGitManagerRepositoryInfo> {
307
- return this.bridge.sendCommand('restoreRepositoryBundle', requestArg);
417
+ return this.bridge.sendCommand('restoreRepositoryBundle', requestArg).catch(rethrowCodedError);
308
418
  }
309
419
 
310
420
  public async listRepositories(): Promise<IGitManagerRepositoryInfo[]> {
@@ -326,7 +436,7 @@ export class RustGitManagerBridge extends plugins.EventEmitter {
326
436
  public async mergeRepository(
327
437
  requestArg: IGitManagerMergeRequest,
328
438
  ): Promise<IGitManagerMergeResult> {
329
- return this.bridge.sendCommand('mergeRepository', requestArg);
439
+ return this.bridge.sendCommand('mergeRepository', requestArg).catch(rethrowCodedError);
330
440
  }
331
441
 
332
442
  public async listTree(requestArg: IGitManagerTreeRequest): Promise<IGitManagerTreeEntry[]> {