@foss.global/codefeed 1.11.0 → 1.12.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 +28 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.forgeinventory.d.ts +45 -0
- package/dist_ts/classes.forgeinventory.js +318 -0
- package/dist_ts/classes.forgeinventoryscanner.d.ts +25 -0
- package/dist_ts/classes.forgeinventoryscanner.js +139 -0
- package/dist_ts/forgeinventory.validation.d.ts +15 -0
- package/dist_ts/forgeinventory.validation.js +93 -0
- package/dist_ts/index.d.ts +2 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts/plugins.d.ts +2 -0
- package/dist_ts/plugins.js +3 -1
- package/package.json +7 -8
- package/readme.md +46 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.forgeinventory.ts +311 -0
- package/ts/classes.forgeinventoryscanner.ts +152 -0
- package/ts/forgeinventory.validation.ts +88 -0
- package/ts/index.ts +2 -0
- package/ts/plugins.ts +3 -0
package/changelog.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-09-05 - 1.12.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- add read-only forge inventory scanner for Gitea and GitLab (forge-inventory)
|
|
8
|
+
- Add ForgeInventoryAdapter and ForgeInventoryScanner exports for bounded repository and namespace observations.
|
|
9
|
+
- Validate provider payloads, cursors, URLs, response budgets, aborts, and rate-limit metrics without leaking tokens or response bodies.
|
|
10
|
+
- Document scanner usage and add protocol fixture tests for Gitea and GitLab inventory behavior.
|
|
11
|
+
- Update @foss.global/interfaces to ^0.12.0 for inventory DTO support.
|
|
12
|
+
|
|
13
|
+
### Fixes
|
|
14
|
+
|
|
15
|
+
- update package manager and development tooling dependencies (deps)
|
|
16
|
+
- Updated @git.zone/tsbundle to ^2.13.0
|
|
17
|
+
- Updated @git.zone/tsrun to ^2.0.6
|
|
18
|
+
- Updated @git.zone/tstest to ^4.0.0
|
|
19
|
+
- Updated @types/node to ^26.4.1
|
|
20
|
+
- Updated pnpm to 11.25.0
|
|
21
|
+
- correct default tag workflow repository URL and remove release job (ci)
|
|
22
|
+
- Set the computed repository URL to include the code.foss.global host.
|
|
23
|
+
- Remove the tag-triggered npm publish release job from the default tags workflow.
|
|
24
|
+
|
|
25
|
+
## 2026-07-21 - 1.11.1
|
|
26
|
+
|
|
27
|
+
### Fixes
|
|
28
|
+
|
|
29
|
+
- remove the unused `@push.rocks/smartxml` 1.x dependency so consumers no longer inherit declarations that are incompatible with current TypeScript libraries
|
|
30
|
+
|
|
3
31
|
## 2026-07-21 - 1.11.0
|
|
4
32
|
|
|
5
33
|
### Features
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@foss.global/codefeed',
|
|
6
|
-
version: '1.
|
|
6
|
+
version: '1.12.0',
|
|
7
7
|
description: 'The @foss.global/codefeed module generates commit/activity feeds from forge providers including Gitea and GitManager.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx1QkFBdUI7SUFDN0IsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHVIQUF1SDtDQUNySSxDQUFBIn0=
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
export { ForgeInventoryError } from './forgeinventory.validation.js';
|
|
3
|
+
export interface IForgeInventoryOptions {
|
|
4
|
+
kind: 'gitea' | 'gitlab';
|
|
5
|
+
instanceId: string;
|
|
6
|
+
/** Instance root, including an optional deployment subpath. HTTPS is required. */
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
/** Personal access token, retained only in this adapter's memory. */
|
|
9
|
+
token?: string;
|
|
10
|
+
pageSize?: number;
|
|
11
|
+
maxPages?: number;
|
|
12
|
+
requestTimeoutMs?: number;
|
|
13
|
+
maxResponseBytes?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface IForgeInventoryRequest {
|
|
16
|
+
cursor?: string;
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
/** Optional lower response-body budget for this request. */
|
|
19
|
+
maxResponseBytes?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface IForgeInventoryMetrics {
|
|
22
|
+
requests: number;
|
|
23
|
+
responseBytes: number;
|
|
24
|
+
rateLimitedRequests: number;
|
|
25
|
+
}
|
|
26
|
+
/** Read-only, caller-visible API observations. This adapter never claims admin completeness. */
|
|
27
|
+
export declare class ForgeInventoryAdapter {
|
|
28
|
+
#private;
|
|
29
|
+
constructor(optionsArg: IForgeInventoryOptions);
|
|
30
|
+
get instance(): plugins.fossInterfaces.data.IForgeInventoryInstance;
|
|
31
|
+
get metrics(): IForgeInventoryMetrics;
|
|
32
|
+
observation(startedAt: string): plugins.fossInterfaces.data.IForgeInventoryObservation;
|
|
33
|
+
listRepositories(requestArg?: IForgeInventoryRequest): Promise<plugins.fossInterfaces.data.IForgeInventoryPage<plugins.fossInterfaces.data.IForgeInventoryRepository>>;
|
|
34
|
+
/** Organizations/groups only. User namespaces are not enumerated by these endpoints. */
|
|
35
|
+
listNamespaces(requestArg?: IForgeInventoryRequest): Promise<plugins.fossInterfaces.data.IForgeInventoryPage<plugins.fossInterfaces.data.IForgeInventoryNamespace>>;
|
|
36
|
+
private identity;
|
|
37
|
+
private namespace;
|
|
38
|
+
private repository;
|
|
39
|
+
private url;
|
|
40
|
+
private cursor;
|
|
41
|
+
private continuation;
|
|
42
|
+
private page;
|
|
43
|
+
private next;
|
|
44
|
+
private request;
|
|
45
|
+
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import * as validation from './forgeinventory.validation.js';
|
|
3
|
+
export { ForgeInventoryError } from './forgeinventory.validation.js';
|
|
4
|
+
/** Read-only, caller-visible API observations. This adapter never claims admin completeness. */
|
|
5
|
+
export class ForgeInventoryAdapter {
|
|
6
|
+
#options;
|
|
7
|
+
#cursorKey = plugins.crypto.randomBytes(32);
|
|
8
|
+
#metrics = { requests: 0, responseBytes: 0, rateLimitedRequests: 0 };
|
|
9
|
+
constructor(optionsArg) {
|
|
10
|
+
const baseUrl = validation.httpUrl(optionsArg.baseUrl);
|
|
11
|
+
if (!baseUrl || !baseUrl.startsWith('https:') || !optionsArg.instanceId.trim() ||
|
|
12
|
+
!['gitea', 'gitlab'].includes(optionsArg.kind) ||
|
|
13
|
+
(optionsArg.token !== undefined && (!optionsArg.token || /[\s\u0000-\u001f\u007f]/.test(optionsArg.token)))) {
|
|
14
|
+
throw new validation.ForgeInventoryError('invalid-options');
|
|
15
|
+
}
|
|
16
|
+
this.#options = {
|
|
17
|
+
...optionsArg,
|
|
18
|
+
baseUrl: baseUrl.replace(/\/$/, ''),
|
|
19
|
+
pageSize: optionsArg.pageSize ?? 50,
|
|
20
|
+
maxPages: optionsArg.maxPages ?? 1000,
|
|
21
|
+
requestTimeoutMs: optionsArg.requestTimeoutMs ?? 30_000,
|
|
22
|
+
maxResponseBytes: optionsArg.maxResponseBytes ?? 8 * 1024 * 1024,
|
|
23
|
+
};
|
|
24
|
+
for (const [name, maximum] of [
|
|
25
|
+
['pageSize', 100], ['maxPages', 100_000], ['requestTimeoutMs', 300_000], ['maxResponseBytes', 64 * 1024 * 1024],
|
|
26
|
+
]) {
|
|
27
|
+
const value = this.#options[name];
|
|
28
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > maximum)
|
|
29
|
+
throw new validation.ForgeInventoryError('invalid-limits');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
get instance() {
|
|
33
|
+
return { instanceId: this.#options.instanceId, kind: this.#options.kind, version: null };
|
|
34
|
+
}
|
|
35
|
+
get metrics() { return { ...this.#metrics }; }
|
|
36
|
+
observation(startedAt) {
|
|
37
|
+
return {
|
|
38
|
+
startedAt, finishedAt: new Date().toISOString(),
|
|
39
|
+
access: this.#options.token ? 'callerVisible' : 'publicOnly', consistency: { status: 'live' },
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
listRepositories(requestArg = {}) {
|
|
43
|
+
return this.page('repositories', (item) => this.repository(item), requestArg);
|
|
44
|
+
}
|
|
45
|
+
/** Organizations/groups only. User namespaces are not enumerated by these endpoints. */
|
|
46
|
+
listNamespaces(requestArg = {}) {
|
|
47
|
+
return this.page('namespaces', (item) => this.namespace(item), requestArg);
|
|
48
|
+
}
|
|
49
|
+
identity(idNamespace, value) {
|
|
50
|
+
const objectId = validation.decimal(value);
|
|
51
|
+
if (objectId === '0')
|
|
52
|
+
throw new validation.ForgeInventoryError('invalid-identity');
|
|
53
|
+
return { status: 'identified', ref: { instanceId: this.#options.instanceId, idNamespace, objectId, parentObjectId: null } };
|
|
54
|
+
}
|
|
55
|
+
namespace(itemArg) {
|
|
56
|
+
const item = validation.record(itemArg);
|
|
57
|
+
if (this.#options.kind === 'gitea') {
|
|
58
|
+
const pathSegments = validation.segments(item.name);
|
|
59
|
+
if (pathSegments.length !== 1)
|
|
60
|
+
throw new validation.ForgeInventoryError('invalid-organization-path');
|
|
61
|
+
return {
|
|
62
|
+
identity: this.identity('gitea.user.id', item.id), kind: 'organization', parent: null,
|
|
63
|
+
location: { pathSegments, webUrl: null },
|
|
64
|
+
visibility: validation.visibility(item.visibility === 'limited' ? 'internal' : item.visibility),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
identity: this.identity('gitlab.group.id', item.id), kind: 'group',
|
|
69
|
+
parent: item.parent_id === null ? null : item.parent_id === undefined
|
|
70
|
+
? { status: 'unresolved', reason: 'Parent group was not exposed.' }
|
|
71
|
+
: this.identity('gitlab.group.id', item.parent_id),
|
|
72
|
+
location: { pathSegments: validation.segments(item.full_path), webUrl: validation.httpUrl(item.web_url) },
|
|
73
|
+
visibility: validation.visibility(item.visibility),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
repository(itemArg) {
|
|
77
|
+
const item = validation.record(itemArg);
|
|
78
|
+
if (this.#options.kind === 'gitea') {
|
|
79
|
+
const owner = validation.record(item.owner);
|
|
80
|
+
const pathSegments = [...validation.segments(owner.login), ...validation.segments(item.name)];
|
|
81
|
+
if (pathSegments.length !== 2)
|
|
82
|
+
throw new validation.ForgeInventoryError('invalid-repository-path');
|
|
83
|
+
const privateRepo = validation.optionalBoolean(item.private);
|
|
84
|
+
const internalRepo = validation.optionalBoolean(item.internal);
|
|
85
|
+
return {
|
|
86
|
+
identity: this.identity('gitea.repository.id', item.id), namespace: this.identity('gitea.user.id', owner.id),
|
|
87
|
+
location: { pathSegments, webUrl: validation.httpUrl(item.html_url) },
|
|
88
|
+
description: validation.optionalString(item.description),
|
|
89
|
+
visibility: privateRepo === true ? 'private' : internalRepo === true ? 'internal' : privateRepo === false && internalRepo === false ? 'public' : null,
|
|
90
|
+
archived: validation.optionalBoolean(item.archived), defaultBranch: validation.optionalString(item.default_branch),
|
|
91
|
+
httpCloneUrl: validation.httpUrl(item.clone_url), sshCloneUrl: validation.sshUrl(item.ssh_url),
|
|
92
|
+
gitSizeBytes: validation.unknownSize('Gitea size is not a separate Git byte measurement.'),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const namespace = validation.record(item.namespace);
|
|
96
|
+
if (namespace.kind !== 'group' && namespace.kind !== 'user')
|
|
97
|
+
throw new validation.ForgeInventoryError('invalid-namespace-kind');
|
|
98
|
+
return {
|
|
99
|
+
identity: this.identity('gitlab.project.id', item.id),
|
|
100
|
+
namespace: this.identity(namespace.kind === 'group' ? 'gitlab.group.id' : 'gitlab.namespace.id', namespace.id),
|
|
101
|
+
location: { pathSegments: validation.segments(item.path_with_namespace), webUrl: validation.httpUrl(item.web_url) },
|
|
102
|
+
description: validation.optionalString(item.description), visibility: validation.visibility(item.visibility),
|
|
103
|
+
archived: validation.optionalBoolean(item.archived), defaultBranch: validation.optionalString(item.default_branch),
|
|
104
|
+
httpCloneUrl: validation.httpUrl(item.http_url_to_repo), sshCloneUrl: validation.sshUrl(item.ssh_url_to_repo),
|
|
105
|
+
gitSizeBytes: validation.gitlabSize(item.statistics),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
url(state) {
|
|
109
|
+
const gitea = this.#options.kind === 'gitea';
|
|
110
|
+
const endpoint = state.collection === 'repositories' ? (gitea ? 'repos/search' : 'projects') : (gitea ? 'orgs' : 'groups');
|
|
111
|
+
const url = new URL(`${this.#options.baseUrl}/api/${gitea ? 'v1' : 'v4'}/${endpoint}`);
|
|
112
|
+
url.searchParams.set(gitea ? 'limit' : 'per_page', String(this.#options.pageSize));
|
|
113
|
+
if (gitea) {
|
|
114
|
+
url.searchParams.set('page', String(state.page));
|
|
115
|
+
if (state.collection === 'repositories') {
|
|
116
|
+
url.searchParams.set('sort', 'id');
|
|
117
|
+
url.searchParams.set('order', 'asc');
|
|
118
|
+
url.searchParams.set('private', 'true');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
url.searchParams.set('order_by', 'id');
|
|
123
|
+
url.searchParams.set('sort', 'asc');
|
|
124
|
+
if (state.collection === 'repositories') {
|
|
125
|
+
url.searchParams.set('pagination', 'keyset');
|
|
126
|
+
url.searchParams.set('statistics', 'true');
|
|
127
|
+
if (state.after)
|
|
128
|
+
url.searchParams.set('id_after', state.after);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
url.searchParams.set('all_available', 'true');
|
|
132
|
+
url.searchParams.set('page', String(state.page));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return url;
|
|
136
|
+
}
|
|
137
|
+
cursor(state) {
|
|
138
|
+
const payload = Buffer.from(JSON.stringify(state)).toString('base64url');
|
|
139
|
+
return `${payload}.${plugins.crypto.createHmac('sha256', this.#cursorKey).update(payload).digest('base64url')}`;
|
|
140
|
+
}
|
|
141
|
+
continuation(collection, cursor) {
|
|
142
|
+
if (cursor === undefined)
|
|
143
|
+
return { version: 1, collection, page: 1, after: null };
|
|
144
|
+
try {
|
|
145
|
+
if (cursor.length > 2048)
|
|
146
|
+
throw new Error();
|
|
147
|
+
const [payload, signature, extra] = cursor.split('.');
|
|
148
|
+
const expected = plugins.crypto.createHmac('sha256', this.#cursorKey).update(payload).digest('base64url');
|
|
149
|
+
if (extra !== undefined || !signature || signature.length !== expected.length ||
|
|
150
|
+
!plugins.crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected)))
|
|
151
|
+
throw new Error();
|
|
152
|
+
const state = JSON.parse(Buffer.from(payload, 'base64url').toString());
|
|
153
|
+
if (state.version !== 1 || state.collection !== collection || !Number.isSafeInteger(state.page) ||
|
|
154
|
+
state.page < 2 || state.page > this.#options.maxPages ||
|
|
155
|
+
(state.after !== null && validation.decimal(state.after) !== state.after))
|
|
156
|
+
throw new Error();
|
|
157
|
+
return state;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
throw new validation.ForgeInventoryError('invalid-cursor');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async page(collection, map, requestArg) {
|
|
164
|
+
const state = this.continuation(collection, requestArg.cursor);
|
|
165
|
+
const startedAt = new Date().toISOString();
|
|
166
|
+
const url = this.url(state);
|
|
167
|
+
const { body, headers } = await this.request(url, requestArg);
|
|
168
|
+
let rows = body;
|
|
169
|
+
if (this.#options.kind === 'gitea' && collection === 'repositories') {
|
|
170
|
+
const envelope = validation.record(body);
|
|
171
|
+
if (envelope.ok !== true)
|
|
172
|
+
throw new validation.ForgeInventoryError('search-not-successful');
|
|
173
|
+
rows = envelope.data;
|
|
174
|
+
}
|
|
175
|
+
if (!Array.isArray(rows) || rows.length > this.#options.pageSize)
|
|
176
|
+
throw new validation.ForgeInventoryError('invalid-page');
|
|
177
|
+
const items = rows.map(map);
|
|
178
|
+
let pagination;
|
|
179
|
+
try {
|
|
180
|
+
const next = this.next(state, url, headers, rows);
|
|
181
|
+
pagination = next === null ? { status: 'exhausted' } : state.page >= this.#options.maxPages
|
|
182
|
+
? { status: 'incomplete', reason: 'Configured page limit reached.' }
|
|
183
|
+
: { status: 'more', cursor: this.cursor(next) };
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
pagination = { status: 'incomplete', reason: 'Invalid or inconsistent pagination metadata.' };
|
|
187
|
+
}
|
|
188
|
+
return { schemaVersion: 1, instance: this.instance, scope: { kind: 'instance' }, observation: this.observation(startedAt), items, pagination };
|
|
189
|
+
}
|
|
190
|
+
next(state, current, headers, rows) {
|
|
191
|
+
const keyset = this.#options.kind === 'gitlab' && state.collection === 'repositories';
|
|
192
|
+
let previousId = BigInt(state.after ?? '0');
|
|
193
|
+
if (keyset)
|
|
194
|
+
for (const row of rows) {
|
|
195
|
+
const id = BigInt(validation.decimal(validation.record(row).id));
|
|
196
|
+
if (id <= previousId)
|
|
197
|
+
throw new Error();
|
|
198
|
+
previousId = id;
|
|
199
|
+
}
|
|
200
|
+
const link = headers.get('link');
|
|
201
|
+
let nextUrl = null;
|
|
202
|
+
if (link !== null) {
|
|
203
|
+
if (link.length > 16_384)
|
|
204
|
+
throw new Error();
|
|
205
|
+
for (const entry of link.split(',')) {
|
|
206
|
+
const match = entry.trim().match(/^<([^<>]+)>;\s*rel="(next|prev|first|last)"$/);
|
|
207
|
+
if (!match)
|
|
208
|
+
throw new Error();
|
|
209
|
+
if (match[2] !== 'next')
|
|
210
|
+
continue;
|
|
211
|
+
if (nextUrl !== null)
|
|
212
|
+
throw new Error();
|
|
213
|
+
nextUrl = new URL(match[1], current);
|
|
214
|
+
if (nextUrl.origin !== current.origin || nextUrl.pathname !== current.pathname || nextUrl.username || nextUrl.password || nextUrl.hash)
|
|
215
|
+
throw new Error();
|
|
216
|
+
const expected = new URL(current);
|
|
217
|
+
const changing = keyset ? 'id_after' : 'page';
|
|
218
|
+
expected.searchParams.delete(changing);
|
|
219
|
+
const actual = new URL(nextUrl);
|
|
220
|
+
if (actual.searchParams.getAll(changing).length !== 1)
|
|
221
|
+
throw new Error();
|
|
222
|
+
actual.searchParams.delete(changing);
|
|
223
|
+
expected.searchParams.sort();
|
|
224
|
+
actual.searchParams.sort();
|
|
225
|
+
if (expected.search !== actual.search)
|
|
226
|
+
throw new Error();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const xNext = headers.get('x-next-page');
|
|
230
|
+
if (!keyset && xNext !== null && xNext !== '' && validation.decimal(xNext) !== String(state.page + 1))
|
|
231
|
+
throw new Error();
|
|
232
|
+
if (rows.length === 0) {
|
|
233
|
+
if (nextUrl || xNext)
|
|
234
|
+
throw new Error();
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
if (keyset) {
|
|
238
|
+
if (!nextUrl)
|
|
239
|
+
return null; // GitLab keyset exhaustion is signalled by no next Link.
|
|
240
|
+
const after = validation.decimal(nextUrl.searchParams.get('id_after'));
|
|
241
|
+
if (after !== previousId.toString())
|
|
242
|
+
throw new Error();
|
|
243
|
+
return { ...state, page: state.page + 1, after };
|
|
244
|
+
}
|
|
245
|
+
if (nextUrl && validation.decimal(nextUrl.searchParams.get('page')) !== String(state.page + 1))
|
|
246
|
+
throw new Error();
|
|
247
|
+
if (nextUrl && xNext === '')
|
|
248
|
+
throw new Error();
|
|
249
|
+
// A short page can reflect a server page-size cap. Probe the following page
|
|
250
|
+
// even without Link rather than silently truncating at that cap.
|
|
251
|
+
return { ...state, page: state.page + 1 };
|
|
252
|
+
}
|
|
253
|
+
async request(url, requestArg) {
|
|
254
|
+
const { signal } = requestArg;
|
|
255
|
+
const maxBytes = requestArg.maxResponseBytes ?? this.#options.maxResponseBytes;
|
|
256
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1)
|
|
257
|
+
throw new validation.ForgeInventoryError('invalid-response-budget');
|
|
258
|
+
const controller = new AbortController();
|
|
259
|
+
const abort = () => controller.abort();
|
|
260
|
+
if (signal?.aborted)
|
|
261
|
+
throw new validation.ForgeInventoryError('request-aborted');
|
|
262
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
263
|
+
const timeout = setTimeout(abort, this.#options.requestTimeoutMs);
|
|
264
|
+
const deadline = Date.now() + this.#options.requestTimeoutMs;
|
|
265
|
+
let reader;
|
|
266
|
+
try {
|
|
267
|
+
const headers = new Headers({ accept: 'application/json' });
|
|
268
|
+
if (this.#options.token)
|
|
269
|
+
headers.set(this.#options.kind === 'gitea' ? 'authorization' : 'private-token', this.#options.kind === 'gitea' ? `token ${this.#options.token}` : this.#options.token);
|
|
270
|
+
this.#metrics.requests++;
|
|
271
|
+
const response = await fetch(url, { method: 'GET', headers, redirect: 'error', credentials: 'omit', signal: controller.signal });
|
|
272
|
+
reader = response.body?.getReader();
|
|
273
|
+
if (response.status === 429)
|
|
274
|
+
this.#metrics.rateLimitedRequests++;
|
|
275
|
+
if (response.status !== 200)
|
|
276
|
+
throw new validation.ForgeInventoryError(`http-${response.status}`);
|
|
277
|
+
if (response.headers.get('content-type')?.split(';')[0].trim().toLowerCase() !== 'application/json' || !reader)
|
|
278
|
+
throw new validation.ForgeInventoryError('invalid-content-type');
|
|
279
|
+
const chunks = [];
|
|
280
|
+
let bytes = 0;
|
|
281
|
+
while (true) {
|
|
282
|
+
const result = await reader.read();
|
|
283
|
+
if (result.done)
|
|
284
|
+
break;
|
|
285
|
+
bytes += result.value.byteLength;
|
|
286
|
+
this.#metrics.responseBytes += result.value.byteLength;
|
|
287
|
+
if (bytes > Math.min(maxBytes, this.#options.maxResponseBytes))
|
|
288
|
+
throw new validation.ForgeInventoryError('response-size-limit');
|
|
289
|
+
if (controller.signal.aborted || Date.now() >= deadline)
|
|
290
|
+
throw new validation.ForgeInventoryError('request-aborted-or-timed-out');
|
|
291
|
+
chunks.push(result.value);
|
|
292
|
+
}
|
|
293
|
+
const text = new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(chunks));
|
|
294
|
+
const body = JSON.parse(text);
|
|
295
|
+
if (controller.signal.aborted || Date.now() >= deadline)
|
|
296
|
+
throw new validation.ForgeInventoryError('request-aborted-or-timed-out');
|
|
297
|
+
return { body, headers: response.headers };
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
if (error instanceof validation.ForgeInventoryError)
|
|
301
|
+
throw error;
|
|
302
|
+
throw new validation.ForgeInventoryError(controller.signal.aborted ? 'request-aborted-or-timed-out' : 'request-or-json-failed');
|
|
303
|
+
}
|
|
304
|
+
finally {
|
|
305
|
+
controller.abort();
|
|
306
|
+
clearTimeout(timeout);
|
|
307
|
+
signal?.removeEventListener('abort', abort);
|
|
308
|
+
if (reader) {
|
|
309
|
+
try {
|
|
310
|
+
await reader.cancel();
|
|
311
|
+
}
|
|
312
|
+
catch { /* The request has already been aborted. */ }
|
|
313
|
+
reader.releaseLock();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5mb3JnZWludmVudG9yeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMuZm9yZ2VpbnZlbnRvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLFVBQVUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUU3RCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQW9DckUsZ0dBQWdHO0FBQ2hHLE1BQU0sT0FBTyxxQkFBcUI7SUFDdkIsUUFBUSxDQUF1RTtJQUMvRSxVQUFVLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDNUMsUUFBUSxHQUEyQixFQUFFLFFBQVEsRUFBRSxDQUFDLEVBQUUsYUFBYSxFQUFFLENBQUMsRUFBRSxtQkFBbUIsRUFBRSxDQUFDLEVBQUUsQ0FBQztJQUV0RyxZQUFZLFVBQWtDO1FBQzVDLE1BQU0sT0FBTyxHQUFHLFVBQVUsQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3ZELElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUU7WUFDNUUsQ0FBQyxDQUFDLE9BQU8sRUFBRSxRQUFRLENBQUMsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQztZQUM5QyxDQUFDLFVBQVUsQ0FBQyxLQUFLLEtBQUssU0FBUyxJQUFJLENBQUMsQ0FBQyxVQUFVLENBQUMsS0FBSyxJQUFJLHlCQUF5QixDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDOUcsTUFBTSxJQUFJLFVBQVUsQ0FBQyxtQkFBbUIsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FB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
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type * as plugins from './plugins.js';
|
|
2
|
+
import type { IForgeInventoryMetrics, IForgeInventoryOptions } from './classes.forgeinventory.js';
|
|
3
|
+
export interface IForgeInventoryScanOptions {
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
/** Maximum distinct items retained per collection. */
|
|
6
|
+
maxItems?: number;
|
|
7
|
+
/** Deadline for the complete scan, including body reads. */
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
/** Maximum response bytes consumed across both collections. */
|
|
10
|
+
maxScanResponseBytes?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface IForgeInventoryScan {
|
|
13
|
+
report: plugins.fossInterfaces.data.IForgeInventoryReport;
|
|
14
|
+
repositories: plugins.fossInterfaces.data.IForgeInventoryRepository[];
|
|
15
|
+
namespaces: plugins.fossInterfaces.data.IForgeInventoryNamespace[];
|
|
16
|
+
metrics: IForgeInventoryMetrics;
|
|
17
|
+
}
|
|
18
|
+
/** A bounded, disposable observation; callers own any durable storage and migration decisions. */
|
|
19
|
+
export declare class ForgeInventoryScanner {
|
|
20
|
+
#private;
|
|
21
|
+
constructor(optionsArg: IForgeInventoryOptions);
|
|
22
|
+
scan(optionsArg?: IForgeInventoryScanOptions): Promise<IForgeInventoryScan>;
|
|
23
|
+
private capability;
|
|
24
|
+
private collect;
|
|
25
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { ForgeInventoryAdapter, ForgeInventoryError } from './classes.forgeinventory.js';
|
|
2
|
+
import { unknownSize } from './forgeinventory.validation.js';
|
|
3
|
+
const entityClasses = {
|
|
4
|
+
namespaces: true, users: true, teams: true, memberships: true, repositories: true,
|
|
5
|
+
gitRefs: true, gitObjects: true, protectedRefs: true, publicSshKeys: true, deployKeys: true,
|
|
6
|
+
issues: true, issueComments: true, pullRequests: true, reviews: true, reviewComments: true,
|
|
7
|
+
labels: true, milestones: true, assignees: true, reactions: true, releases: true,
|
|
8
|
+
releaseAssets: true, lfsObjects: true, packages: true, packageVersions: true, packageBlobs: true,
|
|
9
|
+
webhooks: true, webhookDeliveries: true, ciWorkflows: true, ciRuns: true, ciArtifacts: true,
|
|
10
|
+
wikis: true, projects: true,
|
|
11
|
+
};
|
|
12
|
+
/** A bounded, disposable observation; callers own any durable storage and migration decisions. */
|
|
13
|
+
export class ForgeInventoryScanner {
|
|
14
|
+
#adapter;
|
|
15
|
+
#running = false;
|
|
16
|
+
constructor(optionsArg) {
|
|
17
|
+
this.#adapter = new ForgeInventoryAdapter(optionsArg);
|
|
18
|
+
}
|
|
19
|
+
async scan(optionsArg = {}) {
|
|
20
|
+
const maxItems = optionsArg.maxItems ?? 100_000;
|
|
21
|
+
const timeoutMs = optionsArg.timeoutMs ?? 60_000;
|
|
22
|
+
const maxScanResponseBytes = optionsArg.maxScanResponseBytes ?? 64 * 1024 * 1024;
|
|
23
|
+
if (!Number.isSafeInteger(maxItems) || maxItems < 1 || maxItems > 1_000_000 ||
|
|
24
|
+
!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 3_600_000 ||
|
|
25
|
+
!Number.isSafeInteger(maxScanResponseBytes) || maxScanResponseBytes < 1 || maxScanResponseBytes > 1024 * 1024 * 1024) {
|
|
26
|
+
throw new ForgeInventoryError('invalid-scan-limits');
|
|
27
|
+
}
|
|
28
|
+
if (this.#running)
|
|
29
|
+
throw new ForgeInventoryError('scan-already-running');
|
|
30
|
+
this.#running = true;
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const abort = () => controller.abort();
|
|
33
|
+
if (optionsArg.signal?.aborted)
|
|
34
|
+
controller.abort();
|
|
35
|
+
optionsArg.signal?.addEventListener('abort', abort, { once: true });
|
|
36
|
+
const timeout = setTimeout(abort, timeoutMs);
|
|
37
|
+
const before = this.#adapter.metrics;
|
|
38
|
+
const deadline = Date.now() + timeoutMs;
|
|
39
|
+
const remainingBytes = () => maxScanResponseBytes - (this.#adapter.metrics.responseBytes - before.responseBytes);
|
|
40
|
+
const collections = Object.fromEntries(Object.keys(entityClasses).map((key) => [key, {
|
|
41
|
+
capability: { status: 'unverified', reason: 'No inventory reader has inspected this entity class.' },
|
|
42
|
+
result: { status: 'notInspected', reason: 'Outside this repository and organization/group scan.' },
|
|
43
|
+
}]));
|
|
44
|
+
try {
|
|
45
|
+
const namespaces = await this.collect((request) => this.#adapter.listNamespaces(request), maxItems, controller.signal, deadline, remainingBytes);
|
|
46
|
+
const repositories = await this.collect((request) => this.#adapter.listRepositories(request), maxItems, controller.signal, deadline, remainingBytes);
|
|
47
|
+
const namespaceLimitation = 'Only organization/group namespaces were inspected; personal namespaces and full private namespace coverage remain uninspected.';
|
|
48
|
+
collections.namespaces = {
|
|
49
|
+
capability: this.capability(namespaces, [namespaceLimitation, 'Live API visibility does not prove administrator completeness.']),
|
|
50
|
+
result: namespaces.result.status === 'enumerated' ? {
|
|
51
|
+
status: 'incomplete', observation: namespaces.result.observation,
|
|
52
|
+
observedCount: namespaces.result.count, reason: namespaceLimitation,
|
|
53
|
+
} : namespaces.result,
|
|
54
|
+
};
|
|
55
|
+
const repositoryResult = repositories.result;
|
|
56
|
+
if (repositoryResult.status === 'enumerated') {
|
|
57
|
+
const measurements = repositories.items.map((item) => item.gitSizeBytes);
|
|
58
|
+
repositoryResult.sizeBytes = measurements.length > 0 && measurements.every((size) => size.status === 'measured')
|
|
59
|
+
? { status: 'measured', value: measurements.reduce((sum, size) => sum + (size.status === 'measured' ? BigInt(size.value) : 0n), 0n).toString(), accuracy: 'estimate' }
|
|
60
|
+
: unknownSize('Separate Git byte measurements were not available for every repository.');
|
|
61
|
+
}
|
|
62
|
+
collections.repositories = {
|
|
63
|
+
capability: this.capability(repositories, ['Only API-visible repositories are enumerated.', 'Live pagination is not a frozen export.', 'Git size statistics do not measure outgoing push bytes.']),
|
|
64
|
+
result: repositoryResult,
|
|
65
|
+
};
|
|
66
|
+
const after = this.#adapter.metrics;
|
|
67
|
+
return {
|
|
68
|
+
report: {
|
|
69
|
+
schemaVersion: 1, instance: this.#adapter.instance, scope: { kind: 'instance' }, collections,
|
|
70
|
+
additionalCollections: [{
|
|
71
|
+
name: this.#adapter.instance.kind === 'gitea' ? 'gitea.organizations' : 'gitlab.groups',
|
|
72
|
+
capability: this.capability(namespaces, ['Endpoint visibility and live pagination limits apply.']),
|
|
73
|
+
result: namespaces.result,
|
|
74
|
+
}],
|
|
75
|
+
},
|
|
76
|
+
repositories: repositories.items, namespaces: namespaces.items,
|
|
77
|
+
metrics: {
|
|
78
|
+
requests: after.requests - before.requests,
|
|
79
|
+
responseBytes: after.responseBytes - before.responseBytes,
|
|
80
|
+
rateLimitedRequests: after.rateLimitedRequests - before.rateLimitedRequests,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
controller.abort();
|
|
86
|
+
clearTimeout(timeout);
|
|
87
|
+
optionsArg.signal?.removeEventListener('abort', abort);
|
|
88
|
+
this.#running = false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
capability(collection, limitations) {
|
|
92
|
+
return collection.result.status === 'enumerated' || collection.items.length > 0
|
|
93
|
+
? { status: 'available', limitations }
|
|
94
|
+
: { status: 'unverified', reason: 'No successful enumeration or entity observation established this reader capability.' };
|
|
95
|
+
}
|
|
96
|
+
async collect(read, maxItems, signal, deadline, remainingBytes) {
|
|
97
|
+
const startedAt = new Date().toISOString();
|
|
98
|
+
const items = [];
|
|
99
|
+
const identities = new Set();
|
|
100
|
+
let cursor;
|
|
101
|
+
const incomplete = (reason) => ({ items, result: {
|
|
102
|
+
status: 'incomplete', observation: this.#adapter.observation(startedAt), observedCount: String(items.length), reason,
|
|
103
|
+
} });
|
|
104
|
+
while (true) {
|
|
105
|
+
if (signal.aborted || Date.now() >= deadline)
|
|
106
|
+
return incomplete('Scan aborted or deadline reached.');
|
|
107
|
+
if (remainingBytes() <= 0)
|
|
108
|
+
return incomplete('Configured scan response-byte limit reached.');
|
|
109
|
+
let page;
|
|
110
|
+
try {
|
|
111
|
+
page = await read({ cursor, signal, maxResponseBytes: remainingBytes() });
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return incomplete(error instanceof ForgeInventoryError ? error.message : 'Inventory request failed.');
|
|
115
|
+
}
|
|
116
|
+
for (const item of page.items) {
|
|
117
|
+
if (item.identity.status !== 'identified')
|
|
118
|
+
return incomplete('Stable provider identity was not available.');
|
|
119
|
+
const key = JSON.stringify(item.identity.ref);
|
|
120
|
+
if (identities.has(key))
|
|
121
|
+
return incomplete('Repeated provider identity during live pagination.');
|
|
122
|
+
if (items.length >= maxItems)
|
|
123
|
+
return incomplete('Configured item limit reached.');
|
|
124
|
+
identities.add(key);
|
|
125
|
+
items.push(item);
|
|
126
|
+
}
|
|
127
|
+
if (signal.aborted || Date.now() >= deadline)
|
|
128
|
+
return incomplete('Scan aborted or deadline reached.');
|
|
129
|
+
if (page.pagination.status === 'incomplete')
|
|
130
|
+
return incomplete(page.pagination.reason);
|
|
131
|
+
if (page.pagination.status === 'exhausted')
|
|
132
|
+
return {
|
|
133
|
+
items, result: { status: 'enumerated', observation: this.#adapter.observation(startedAt), count: String(items.length), sizeBytes: unknownSize('No byte measurement for this collection.') },
|
|
134
|
+
};
|
|
135
|
+
cursor = page.pagination.cursor;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5mb3JnZWludmVudG9yeXNjYW5uZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLmZvcmdlaW52ZW50b3J5c2Nhbm5lci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUV6RixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFtQjdELE1BQU0sYUFBYSxHQUFHO0lBQ3BCLFVBQVUsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFLElBQUk7SUFDakYsT0FBTyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLGFBQWEsRUFBRSxJQUFJLEVBQUUsYUFBYSxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsSUFBSTtJQUMzRixNQUFNLEVBQUUsSUFBSSxFQUFFLGFBQWEsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLGNBQWMsRUFBRSxJQUFJO0lBQzFGLE1BQU0sRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUk7SUFDaEYsYUFBYSxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsZUFBZSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsSUFBSTtJQUNoRyxRQUFRLEVBQUUsSUFBSSxFQUFFLGlCQUFpQixFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLElBQUk7SUFDM0YsS0FBSyxFQUFFLElBQUksRUFB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
|