@foss.global/codefeed 3.0.0 → 3.2.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.
- package/changelog.md +26 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.giteasourceinventory.js +3 -3
- package/dist_ts/classes.giteasourcetransfer.d.ts +85 -0
- package/dist_ts/classes.giteasourcetransfer.js +164 -0
- package/dist_ts/classes.lfsbatchclient.d.ts +126 -0
- package/dist_ts/classes.lfsbatchclient.js +582 -0
- package/dist_ts/forgeinventory.transport.d.ts +33 -0
- package/dist_ts/forgeinventory.transport.js +98 -37
- package/dist_ts/giteasourceinventory.git.d.ts +25 -5
- package/dist_ts/giteasourceinventory.git.js +57 -25
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +4 -1
- package/dist_ts/plugins.d.ts +4 -1
- package/dist_ts/plugins.js +5 -2
- package/package.json +5 -5
- package/readme.md +71 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.giteasourceinventory.ts +3 -3
- package/ts/classes.giteasourcetransfer.ts +213 -0
- package/ts/classes.lfsbatchclient.ts +614 -0
- package/ts/forgeinventory.transport.ts +115 -33
- package/ts/giteasourceinventory.git.ts +69 -27
- package/ts/index.ts +3 -0
- package/ts/plugins.ts +4 -1
|
@@ -32,43 +32,125 @@ export const globalFetchTransport: TForgeTransport = async (url, headers, signal
|
|
|
32
32
|
return { status: response.status, headers: response.headers, body: response.body };
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
+
/** The agents a node request may use; a URL whose protocol has no agent is refused. */
|
|
36
|
+
export interface INodeRequestAgents {
|
|
37
|
+
http?: InstanceType<typeof plugins.http.Agent>;
|
|
38
|
+
https?: InstanceType<typeof plugins.https.Agent>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface INodeRequest {
|
|
42
|
+
method: string;
|
|
43
|
+
headers: Record<string, string>;
|
|
44
|
+
/** Sent in full, or streamed with backpressure; a streamed body needs its own `content-length` header. */
|
|
45
|
+
body?: Uint8Array | AsyncIterable<Uint8Array>;
|
|
46
|
+
signal: AbortSignal;
|
|
47
|
+
agents: INodeRequestAgents;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface INodeResponse extends IForgeTransportResponse {
|
|
51
|
+
/**
|
|
52
|
+
* Settles once the request body has been sent, or rejects with the error
|
|
53
|
+
* that ended it, such as the body source's own error. Always handled.
|
|
54
|
+
*/
|
|
55
|
+
sent: Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Idle keep-alive agents that unreference their pooled sockets, so pooled
|
|
60
|
+
* connections never keep the process alive.
|
|
61
|
+
*/
|
|
62
|
+
export const createNodeAgents = (caCertificatesArg?: string[], allowHttpArg = false): INodeRequestAgents => ({
|
|
63
|
+
https: new plugins.https.Agent({ ...(caCertificatesArg ? { ca: caCertificatesArg } : {}), keepAlive: true, timeout: idleSocketTimeoutMs }),
|
|
64
|
+
...(allowHttpArg ? { http: new plugins.http.Agent({ keepAlive: true, timeout: idleSocketTimeoutMs }) } : {}),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/** Waits until the request can take more body bytes; rejects once it has closed. */
|
|
68
|
+
const drained = (requestArg: InstanceType<typeof plugins.http.ClientRequest>): Promise<void> => new Promise((resolve, reject) => {
|
|
69
|
+
const done = () => { requestArg.off('drain', done); requestArg.off('close', closed); resolve(); };
|
|
70
|
+
const closed = () => { requestArg.off('drain', done); requestArg.off('close', closed); reject(new Error('Request closed.')); };
|
|
71
|
+
requestArg.on('drain', done);
|
|
72
|
+
requestArg.on('close', closed);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Sends one request through `node:http` or `node:https` and resolves once
|
|
77
|
+
* response headers arrive. A redirect status rejects and is never followed;
|
|
78
|
+
* aborting `signal` discards the connection, also while a body is still being
|
|
79
|
+
* sent or read. A streamed body that fails destroys the request and rejects.
|
|
80
|
+
*/
|
|
81
|
+
export const sendNodeRequest = (url: URL, requestArg: INodeRequest): Promise<INodeResponse> => new Promise<INodeResponse>((resolve, reject) => {
|
|
82
|
+
const { signal } = requestArg;
|
|
83
|
+
if (signal.aborted) return reject(signal.reason);
|
|
84
|
+
const agent = url.protocol === 'https:' ? requestArg.agents.https : url.protocol === 'http:' ? requestArg.agents.http : undefined;
|
|
85
|
+
if (!agent) return reject(new Error('Refused protocol.'));
|
|
86
|
+
const module = url.protocol === 'https:' ? plugins.https : plugins.http;
|
|
87
|
+
// Bodies are read as sent, so ask for them without a content coding.
|
|
88
|
+
const request = module.request(url, { method: requestArg.method, headers: { ...requestArg.headers, 'accept-encoding': 'identity' }, agent });
|
|
89
|
+
let settled = false;
|
|
90
|
+
const fail = (errorArg: unknown) => {
|
|
91
|
+
if (!settled) {
|
|
92
|
+
settled = true;
|
|
93
|
+
reject(errorArg);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
// Stays attached for the request's lifetime, so a connection failure while
|
|
97
|
+
// the body is read never becomes an unhandled 'error' event.
|
|
98
|
+
request.on('error', fail);
|
|
99
|
+
// Destroyed without an error on purpose: an error would be re-emitted on the
|
|
100
|
+
// socket, which no longer has a listener once a keep-alive response completed.
|
|
101
|
+
const abort = () => {
|
|
102
|
+
fail(signal.reason);
|
|
103
|
+
request.destroy();
|
|
104
|
+
};
|
|
105
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
106
|
+
request.once('close', () => signal.removeEventListener('abort', abort));
|
|
107
|
+
const sent = (async () => {
|
|
108
|
+
const { body } = requestArg;
|
|
109
|
+
if (body === undefined || body instanceof Uint8Array) {
|
|
110
|
+
request.end(body);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
for await (const chunk of body) {
|
|
115
|
+
if (request.destroyed) throw new Error('Request closed.');
|
|
116
|
+
if (!request.write(chunk)) await drained(request);
|
|
117
|
+
}
|
|
118
|
+
request.end();
|
|
119
|
+
} catch (error) {
|
|
120
|
+
fail(error);
|
|
121
|
+
request.destroy();
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
})();
|
|
125
|
+
sent.catch(() => undefined);
|
|
126
|
+
request.on('response', (response) => {
|
|
127
|
+
try {
|
|
128
|
+
const status = response.statusCode ?? 0;
|
|
129
|
+
if (redirectStatuses.has(status)) throw new Error('Refused redirect.');
|
|
130
|
+
// Same combination rules as fetch: repeated fields join with ", ".
|
|
131
|
+
const responseHeaders = new Headers();
|
|
132
|
+
for (let index = 0; index < response.rawHeaders.length; index += 2) {
|
|
133
|
+
responseHeaders.append(response.rawHeaders[index], response.rawHeaders[index + 1]);
|
|
134
|
+
}
|
|
135
|
+
if (settled) return void request.destroy();
|
|
136
|
+
settled = true;
|
|
137
|
+
resolve({ status, headers: responseHeaders, body: response, sent });
|
|
138
|
+
} catch (error) {
|
|
139
|
+
fail(error);
|
|
140
|
+
request.destroy();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
35
145
|
/**
|
|
36
146
|
* `node:https` with an owned keep-alive agent whose `ca` list replaces the
|
|
37
147
|
* default trust store for these connections only. The agent unreferences idle
|
|
38
148
|
* sockets, so pooled connections never keep the process alive.
|
|
39
149
|
*/
|
|
40
150
|
export const createHttpsTransport = (caCertificates: string[]): TForgeTransport => {
|
|
41
|
-
const
|
|
42
|
-
return (url, headers, signal, body) =>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// Stays attached for the request's lifetime, so a connection failure while
|
|
47
|
-
// the body is read never becomes an unhandled 'error' event.
|
|
48
|
-
request.on('error', reject);
|
|
49
|
-
// Destroyed without an error on purpose: an error would be re-emitted on the
|
|
50
|
-
// socket, which no longer has a listener once a keep-alive response completed.
|
|
51
|
-
const abort = () => {
|
|
52
|
-
reject(signal.reason);
|
|
53
|
-
request.destroy();
|
|
54
|
-
};
|
|
55
|
-
signal.addEventListener('abort', abort, { once: true });
|
|
56
|
-
request.once('close', () => signal.removeEventListener('abort', abort));
|
|
57
|
-
request.on('response', (response) => {
|
|
58
|
-
try {
|
|
59
|
-
const status = response.statusCode ?? 0;
|
|
60
|
-
if (redirectStatuses.has(status)) throw new Error('Refused redirect.');
|
|
61
|
-
// Same combination rules as fetch: repeated fields join with ", ".
|
|
62
|
-
const responseHeaders = new Headers();
|
|
63
|
-
for (let index = 0; index < response.rawHeaders.length; index += 2) {
|
|
64
|
-
responseHeaders.append(response.rawHeaders[index], response.rawHeaders[index + 1]);
|
|
65
|
-
}
|
|
66
|
-
resolve({ status, headers: responseHeaders, body: response });
|
|
67
|
-
} catch (error) {
|
|
68
|
-
reject(error);
|
|
69
|
-
request.destroy();
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
request.end(body);
|
|
73
|
-
});
|
|
151
|
+
const agents = createNodeAgents(caCertificates);
|
|
152
|
+
return async (url, headers, signal, body) => {
|
|
153
|
+
const { sent: _sent, ...response } = await sendNodeRequest(url, { method: body ? 'POST' : 'GET', headers, body, signal, agents });
|
|
154
|
+
return response;
|
|
155
|
+
};
|
|
74
156
|
};
|
|
@@ -5,6 +5,14 @@ import type { ForgeRequestClient } from './forgeinventory.client.js';
|
|
|
5
5
|
type TMeasure = plugins.fossInterfaces.data.TForgeSourceMeasure;
|
|
6
6
|
type IGitCounts = Omit<plugins.fossInterfaces.data.IForgeSourceGitCounts, 'reportedSizeKiB'>;
|
|
7
7
|
type ILfsCounts = plugins.fossInterfaces.data.IForgeSourceLfsCounts;
|
|
8
|
+
type IHead = plugins.fossInterfaces.data.IForgeSourceRepositoryHead;
|
|
9
|
+
|
|
10
|
+
/** The Git level measure of one repository; `head` is null when the mirror's refs could not be read. */
|
|
11
|
+
export interface IGiteaMirrorMeasure {
|
|
12
|
+
git: IGitCounts;
|
|
13
|
+
lfs: ILfsCounts;
|
|
14
|
+
head: IHead | null;
|
|
15
|
+
}
|
|
8
16
|
|
|
9
17
|
export interface IGiteaMirrorSource {
|
|
10
18
|
/** Instance root without a trailing slash. */
|
|
@@ -98,6 +106,21 @@ export class GitScratchBudget {
|
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
|
|
109
|
+
/**
|
|
110
|
+
* The process-wide budget of the file system that holds `directoryArg`,
|
|
111
|
+
* keyed by its device; every mirror clone and every scratch GitManager in
|
|
112
|
+
* the process reserves through it.
|
|
113
|
+
*/
|
|
114
|
+
static async forDirectory(directoryArg: string): Promise<GitScratchBudget> {
|
|
115
|
+
const device = (await plugins.fs.stat(directoryArg, { bigint: true })).dev;
|
|
116
|
+
let budget = scratchBudgets.get(device);
|
|
117
|
+
if (!budget) {
|
|
118
|
+
budget = new GitScratchBudget();
|
|
119
|
+
scratchBudgets.set(device, budget);
|
|
120
|
+
}
|
|
121
|
+
return budget;
|
|
122
|
+
}
|
|
123
|
+
|
|
101
124
|
#releaser(bytesArg: bigint): () => void {
|
|
102
125
|
let held = true;
|
|
103
126
|
return () => {
|
|
@@ -127,16 +150,28 @@ export class GitScratchBudget {
|
|
|
127
150
|
/** One budget per file system, shared by every reader in the process. */
|
|
128
151
|
const scratchBudgets = new Map<bigint, GitScratchBudget>();
|
|
129
152
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Reserves the scratch of one mirror clone into `directoryArg` in the
|
|
155
|
+
* process-wide budget of its file system and resolves with the function that
|
|
156
|
+
* releases it. Rejects with `git-workdir-too-small` when the clone does not
|
|
157
|
+
* fit even alone, `git-workdir-failed` when the directory or its free space
|
|
158
|
+
* cannot be read and `request-aborted` when `signalArg` aborts first.
|
|
159
|
+
*/
|
|
160
|
+
export const reserveMirrorScratch = async (directoryArg: string, reportedSizeKiBArg: bigint, signalArg?: AbortSignal): Promise<() => void> => {
|
|
161
|
+
let release: (() => void) | null;
|
|
162
|
+
try {
|
|
163
|
+
release = await (await GitScratchBudget.forDirectory(directoryArg)).reserve(directoryArg, mirrorScratchBytes(reportedSizeKiBArg), signalArg);
|
|
164
|
+
} catch {
|
|
165
|
+
throw new validation.ForgeInventoryError(signalArg?.aborted ? 'request-aborted' : 'git-workdir-failed');
|
|
136
166
|
}
|
|
137
|
-
|
|
167
|
+
if (release === null) throw new validation.ForgeInventoryError('git-workdir-too-small');
|
|
168
|
+
return release;
|
|
138
169
|
};
|
|
139
170
|
|
|
171
|
+
/** The HTTPS Git URL of a Gitea repository; it never carries credentials. */
|
|
172
|
+
export const giteaCloneUrl = (baseUrlArg: string, ownerArg: string, nameArg: string): string =>
|
|
173
|
+
`${baseUrlArg}/${encodeURIComponent(ownerArg)}/${encodeURIComponent(nameArg)}.git`;
|
|
174
|
+
|
|
140
175
|
const complete = (valueArg: bigint | number): TMeasure => ({ status: 'complete', value: BigInt(valueArg).toString() });
|
|
141
176
|
const partial = (observedArg: bigint | number, reasonArg: string): TMeasure => ({ status: 'partial', observedValue: BigInt(observedArg).toString(), reason: reasonArg });
|
|
142
177
|
|
|
@@ -158,23 +193,22 @@ const stepFailure = (errorArg: unknown, stepArg: string, signalArg?: AbortSignal
|
|
|
158
193
|
|
|
159
194
|
/**
|
|
160
195
|
* Measures one repository at the Git level: clones every ref Gitea advertises
|
|
161
|
-
* into a temporary mirror, counts its refs and objects, finds the LFS pointers
|
|
196
|
+
* into a temporary mirror, records its HEAD, counts its refs and objects, finds the LFS pointers
|
|
162
197
|
* of every reachable revision and checks through the LFS batch API which of
|
|
163
198
|
* their objects Gitea holds. The mirror is removed afterwards. The clone starts
|
|
164
199
|
* only once its scratch reservation fits the work directory's free space; a
|
|
165
200
|
* repository too large for it fails with `git-workdir-too-small`.
|
|
166
201
|
*/
|
|
167
|
-
export const measureGiteaMirror = async (sourceArg: IGiteaMirrorSource): Promise<
|
|
202
|
+
export const measureGiteaMirror = async (sourceArg: IGiteaMirrorSource): Promise<IGiteaMirrorMeasure> => {
|
|
168
203
|
const { signal } = sourceArg;
|
|
169
204
|
if (signal?.aborted) throw new validation.ForgeInventoryError('request-aborted');
|
|
170
|
-
let release: (
|
|
205
|
+
let release: () => void;
|
|
171
206
|
try {
|
|
172
|
-
release = await (
|
|
173
|
-
} catch {
|
|
174
|
-
const failure =
|
|
175
|
-
return { git: failedGit(failure), lfs: failedLfs(failure) };
|
|
207
|
+
release = await reserveMirrorScratch(sourceArg.workDir, sourceArg.reportedSizeKiB, signal);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
const failure = stepFailure(error, 'git-workdir-failed', signal);
|
|
210
|
+
return { git: failedGit(failure), lfs: failedLfs(failure), head: null };
|
|
176
211
|
}
|
|
177
|
-
if (release === null) return { git: failedGit('git-workdir-too-small'), lfs: failedLfs('git-workdir-too-small') };
|
|
178
212
|
try {
|
|
179
213
|
return await measureReservedMirror(sourceArg);
|
|
180
214
|
} finally {
|
|
@@ -182,17 +216,18 @@ export const measureGiteaMirror = async (sourceArg: IGiteaMirrorSource): Promise
|
|
|
182
216
|
}
|
|
183
217
|
};
|
|
184
218
|
|
|
185
|
-
const measureReservedMirror = async (sourceArg: IGiteaMirrorSource): Promise<
|
|
219
|
+
const measureReservedMirror = async (sourceArg: IGiteaMirrorSource): Promise<IGiteaMirrorMeasure> => {
|
|
186
220
|
const { signal } = sourceArg;
|
|
187
221
|
const { GitManager } = await plugins.getGitManager();
|
|
188
222
|
let directory: string;
|
|
189
223
|
try {
|
|
190
224
|
directory = await plugins.fs.mkdtemp(plugins.path.join(sourceArg.workDir, 'mirror-'));
|
|
191
225
|
} catch {
|
|
192
|
-
return { git: failedGit('git-workdir-failed'), lfs: failedLfs('git-workdir-failed') };
|
|
226
|
+
return { git: failedGit('git-workdir-failed'), lfs: failedLfs('git-workdir-failed'), head: null };
|
|
193
227
|
}
|
|
194
228
|
const manager = new GitManager({ dataDir: directory });
|
|
195
|
-
//
|
|
229
|
+
// The engine serves this measurement alone; stopping it ends the ref, object
|
|
230
|
+
// and LFS scans in flight, which take no signal. The clone takes the signal.
|
|
196
231
|
const abort = () => { void manager.stop().catch(() => undefined); };
|
|
197
232
|
signal?.addEventListener('abort', abort, { once: true });
|
|
198
233
|
const selector = { org: 'source', repo: 'mirror' };
|
|
@@ -203,23 +238,23 @@ const measureReservedMirror = async (sourceArg: IGiteaMirrorSource): Promise<{ g
|
|
|
203
238
|
await manager.start();
|
|
204
239
|
await manager.importMirror({
|
|
205
240
|
...selector,
|
|
206
|
-
remoteUrl:
|
|
241
|
+
remoteUrl: giteaCloneUrl(sourceArg.baseUrl, sourceArg.owner, sourceArg.name),
|
|
207
242
|
credential: { kind: 'basic', username: sourceArg.username, password: sourceArg.token },
|
|
208
243
|
caCertificates: sourceArg.caCertificates,
|
|
209
244
|
timeoutMs: sourceArg.timeoutMs,
|
|
210
|
-
});
|
|
245
|
+
}, { signal });
|
|
211
246
|
} catch (error) {
|
|
212
247
|
const failure = stepFailure(error, 'git-mirror-failed', signal);
|
|
213
|
-
return { git: failedGit(failure), lfs: failedLfs(failure) };
|
|
248
|
+
return { git: failedGit(failure), lfs: failedLfs(failure), head: null };
|
|
214
249
|
}
|
|
215
|
-
const git = await measureRefsAndObjects(manager, selector, signal);
|
|
250
|
+
const { git, head } = await measureRefsAndObjects(manager, selector, signal);
|
|
216
251
|
let pointers: Array<{ oid: string; sizeBytes: number }>;
|
|
217
252
|
try {
|
|
218
253
|
pointers = await manager.listRepositoryLfsPointers(selector);
|
|
219
254
|
} catch (error) {
|
|
220
|
-
return { git, lfs: failedLfs(stepFailure(error, 'lfs-pointer-scan-failed', signal)) };
|
|
255
|
+
return { git, lfs: failedLfs(stepFailure(error, 'lfs-pointer-scan-failed', signal)), head };
|
|
221
256
|
}
|
|
222
|
-
return { git, lfs: await giteaLfsPresence(sourceArg, pointers) };
|
|
257
|
+
return { git, lfs: await giteaLfsPresence(sourceArg, pointers), head };
|
|
223
258
|
} finally {
|
|
224
259
|
signal?.removeEventListener('abort', abort);
|
|
225
260
|
await manager.stop().catch(() => undefined);
|
|
@@ -231,8 +266,9 @@ const measureRefsAndObjects = async (
|
|
|
231
266
|
managerArg: plugins.TGitManager,
|
|
232
267
|
selectorArg: { org: string; repo: string },
|
|
233
268
|
signalArg?: AbortSignal,
|
|
234
|
-
): Promise<
|
|
269
|
+
): Promise<Pick<IGiteaMirrorMeasure, 'git' | 'head'>> => {
|
|
235
270
|
const counts = { branches: 0, tags: 0, pullRequestRefs: 0, otherRefs: 0 };
|
|
271
|
+
let head: IHead | null = null;
|
|
236
272
|
let refsFailure: string | null = null;
|
|
237
273
|
try {
|
|
238
274
|
let cursor: string | undefined;
|
|
@@ -240,6 +276,8 @@ const measureRefsAndObjects = async (
|
|
|
240
276
|
const page = await managerArg.listRefs({ ...selectorArg, cursor });
|
|
241
277
|
// Nothing writes to the private mirror, so a changed inventory is a failure.
|
|
242
278
|
if (page.status === 'stale') throw new validation.ForgeInventoryError('git-refs-changed');
|
|
279
|
+
// Every page carries HEAD; its digest binds it, so all pages agree. An unborn HEAD has no object.
|
|
280
|
+
head = { target: page.head.symbolicTarget, resolves: page.head.objectId !== null };
|
|
243
281
|
for (const ref of page.refs) {
|
|
244
282
|
if (ref.name.startsWith('refs/heads/')) counts.branches++;
|
|
245
283
|
else if (ref.name.startsWith('refs/tags/')) counts.tags++;
|
|
@@ -249,6 +287,7 @@ const measureRefsAndObjects = async (
|
|
|
249
287
|
cursor = page.pagination.status === 'more' ? page.pagination.cursor : undefined;
|
|
250
288
|
} while (cursor !== undefined);
|
|
251
289
|
} catch (error) {
|
|
290
|
+
head = null;
|
|
252
291
|
refsFailure = stepFailure(error, 'git-refs-failed', signalArg);
|
|
253
292
|
}
|
|
254
293
|
const refs = (valueArg: number) => refsFailure === null ? complete(valueArg) : partial(0, refsFailure);
|
|
@@ -262,8 +301,11 @@ const measureRefsAndObjects = async (
|
|
|
262
301
|
objects = objectBytes = partial(0, stepFailure(error, 'git-size-failed', signalArg));
|
|
263
302
|
}
|
|
264
303
|
return {
|
|
265
|
-
|
|
266
|
-
|
|
304
|
+
git: {
|
|
305
|
+
branches: refs(counts.branches), tags: refs(counts.tags), pullRequestRefs: refs(counts.pullRequestRefs), otherRefs: refs(counts.otherRefs),
|
|
306
|
+
objects, objectBytes,
|
|
307
|
+
},
|
|
308
|
+
head,
|
|
267
309
|
};
|
|
268
310
|
};
|
|
269
311
|
|
package/ts/index.ts
CHANGED
|
@@ -488,3 +488,6 @@ export class CodeFeed {
|
|
|
488
488
|
export * from './classes.forgeinventory.js';
|
|
489
489
|
export * from './classes.forgeinventoryscanner.js';
|
|
490
490
|
export * from './classes.giteasourceinventory.js';
|
|
491
|
+
export * from './classes.giteasourcetransfer.js';
|
|
492
|
+
export * from './classes.lfsbatchclient.js';
|
|
493
|
+
export { GitScratchBudget, mirrorScratchBytes } from './giteasourceinventory.git.js';
|
package/ts/plugins.ts
CHANGED
|
@@ -2,9 +2,12 @@ import type * as fossInterfaces from '@foss.global/interfaces';
|
|
|
2
2
|
|
|
3
3
|
import * as crypto from 'node:crypto';
|
|
4
4
|
import * as fs from 'node:fs/promises';
|
|
5
|
+
import * as http from 'node:http';
|
|
5
6
|
import * as https from 'node:https';
|
|
7
|
+
import * as net from 'node:net';
|
|
6
8
|
import * as path from 'node:path';
|
|
7
|
-
|
|
9
|
+
import * as stream from 'node:stream';
|
|
10
|
+
export { crypto, fs, http, https, net, path, stream };
|
|
8
11
|
|
|
9
12
|
// module
|
|
10
13
|
import * as interfaces from './interfaces/index.js';
|