@trebired/git-host 0.2.0 → 1.0.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 +2 -1
- package/README.md +37 -3
- package/dist/api/socket_events.d.ts +7 -0
- package/dist/api/socket_events.d.ts.map +1 -0
- package/dist/api/socket_events.js +7 -0
- package/dist/api/socket_events.js.map +1 -0
- package/dist/api/socket_server.d.ts +6 -0
- package/dist/api/socket_server.d.ts.map +1 -0
- package/dist/api/socket_server.js +156 -0
- package/dist/api/socket_server.js.map +1 -0
- package/dist/core/inspect/linguist_progress.d.ts +14 -0
- package/dist/core/inspect/linguist_progress.d.ts.map +1 -0
- package/dist/core/inspect/linguist_progress.js +92 -0
- package/dist/core/inspect/linguist_progress.js.map +1 -0
- package/dist/core/inspect.d.ts +3 -1
- package/dist/core/inspect.d.ts.map +1 -1
- package/dist/core/inspect.js +74 -8
- package/dist/core/inspect.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/react/client/types.d.ts +45 -2
- package/dist/react/client/types.d.ts.map +1 -1
- package/dist/react/client.d.ts +1 -1
- package/dist/react/client.d.ts.map +1 -1
- package/dist/react/client.js +147 -0
- package/dist/react/client.js.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/types/host.d.ts +3 -1
- package/dist/types/host.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/repository.d.ts +19 -1
- package/dist/types/repository.d.ts.map +1 -1
- package/dist/types/transports.d.ts +9 -3
- package/dist/types/transports.d.ts.map +1 -1
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,9 +6,10 @@ This project follows semantic versioning once published.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
-
## 0.
|
|
9
|
+
## 1.0.0
|
|
10
10
|
|
|
11
11
|
- Added repository linguist analysis through `readLinguist()` with ref-based text blob inspection powered by `linguist-js`.
|
|
12
|
+
- Added live linguist progress reporting through `readLinguist(..., { onProgress })`, plus Socket.IO delivery through `createGitApiSocketServer()` and the typed `openLinguistSocket()` client helper.
|
|
12
13
|
- Added optional tree entry enrichment for detected file languages and inline SVG icons from `material-icon-theme`.
|
|
13
14
|
- Added tag APIs for listing, reading, creating, and deleting tags.
|
|
14
15
|
- Added path- and ref-scoped commit history plus path-scoped diff filtering.
|
package/README.md
CHANGED
|
@@ -70,7 +70,12 @@ await gitHost.ensureRepository("demo", {
|
|
|
70
70
|
const summary = await gitHost.readSummary("demo");
|
|
71
71
|
console.log(summary.repository.current_branch);
|
|
72
72
|
|
|
73
|
-
const linguist = await gitHost.readLinguist("demo", {
|
|
73
|
+
const linguist = await gitHost.readLinguist("demo", {
|
|
74
|
+
ref: "main",
|
|
75
|
+
onProgress(event) {
|
|
76
|
+
console.log(event.stage, event.percent);
|
|
77
|
+
},
|
|
78
|
+
});
|
|
74
79
|
console.log(linguist.languages.results);
|
|
75
80
|
|
|
76
81
|
const tree = await gitHost.listTree("demo", {
|
|
@@ -198,7 +203,7 @@ JSON API hosting:
|
|
|
198
203
|
|
|
199
204
|
```ts
|
|
200
205
|
import { createServer } from "node:http";
|
|
201
|
-
import { createGitApiHandler, createGitHost } from "@trebired/git-host";
|
|
206
|
+
import { createGitApiHandler, createGitApiSocketServer, createGitHost } from "@trebired/git-host";
|
|
202
207
|
import { createLog } from "@trebired/logger";
|
|
203
208
|
|
|
204
209
|
const log = createLog({
|
|
@@ -225,6 +230,16 @@ const apiServer = createServer(createGitApiHandler({
|
|
|
225
230
|
},
|
|
226
231
|
}));
|
|
227
232
|
|
|
233
|
+
createGitApiSocketServer({
|
|
234
|
+
basePath: "/api/git",
|
|
235
|
+
gitHost,
|
|
236
|
+
httpServer: apiServer,
|
|
237
|
+
logger: log,
|
|
238
|
+
authorize({ action, repositoryId }) {
|
|
239
|
+
return canReadRepository(repositoryId, action);
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
228
243
|
apiServer.listen(3100);
|
|
229
244
|
```
|
|
230
245
|
|
|
@@ -275,6 +290,22 @@ function App() {
|
|
|
275
290
|
}
|
|
276
291
|
```
|
|
277
292
|
|
|
293
|
+
For long-running repository scans, the typed client also exposes a live Socket.IO linguist stream:
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
const socket = gitClient.openLinguistSocket("demo", {
|
|
297
|
+
ref: "main",
|
|
298
|
+
onProgress(event) {
|
|
299
|
+
console.log(event.stage, event.percent);
|
|
300
|
+
},
|
|
301
|
+
onResult(event) {
|
|
302
|
+
console.log(event.data.languages.results);
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
await socket.completed;
|
|
307
|
+
```
|
|
308
|
+
|
|
278
309
|
The React entry is intentionally headless. It helps apps fetch and mutate Git data consistently, but it does not ship a bundled styled UI.
|
|
279
310
|
|
|
280
311
|
## Current API
|
|
@@ -287,6 +318,7 @@ The first public slice is intentionally small:
|
|
|
287
318
|
- `buildGitEnv()`
|
|
288
319
|
- `RepositoryLockManager`
|
|
289
320
|
- `createGitApiHandler()`
|
|
321
|
+
- `createGitApiSocketServer()`
|
|
290
322
|
- `createGitHttpHandler()`
|
|
291
323
|
- `generateSshKeyPair()`
|
|
292
324
|
- `normalizeSshPublicKey()`
|
|
@@ -338,6 +370,7 @@ The React entry currently exports:
|
|
|
338
370
|
|
|
339
371
|
- `createGitApiClient()`
|
|
340
372
|
- `GitApiClientProvider`
|
|
373
|
+
- `openLinguistSocket()` through the typed client instance
|
|
341
374
|
- `useGitRepositorySummary()`
|
|
342
375
|
- `useGitBranches()`
|
|
343
376
|
- `useGitCommits()`
|
|
@@ -411,6 +444,7 @@ Hosted transports keep identity and permission policy in your app.
|
|
|
411
444
|
- `createGitHttpHandler()` supports host-owned repository resolution, optional identity resolution, permission checks, and request audit events.
|
|
412
445
|
- `createGitSshServer()` supports host-owned public key authentication, permission checks, and command audit events.
|
|
413
446
|
- `createGitApiHandler()` supports host-owned repository id mapping and per-route authorization.
|
|
447
|
+
- `createGitApiSocketServer()` supports host-owned Socket.IO progress delivery for long-running linguist scans with the same repository mapping and authorization hooks.
|
|
414
448
|
- `generateSshKeyPair()`, `normalizeSshPublicKey()`, `compareSshPublicKeys()`, and `fingerprintSshPublicKey()` help host apps manage SSH transport setup without owning the parsing details themselves.
|
|
415
449
|
|
|
416
450
|
## Platform Fit
|
|
@@ -454,7 +488,7 @@ log.info("git-host", "initializing repository", { repositoryId: "demo" });
|
|
|
454
488
|
|
|
455
489
|
comes from `@trebired/logger`.
|
|
456
490
|
|
|
457
|
-
You can pass that same `log` object into `createGitHost()`, `createGitHttpHandler()`, `createGitSshServer()`, and `
|
|
491
|
+
You can pass that same `log` object into `createGitHost()`, `createGitHttpHandler()`, `createGitSshServer()`, `createGitApiHandler()`, and `createGitApiSocketServer()` through their `logger` option.
|
|
458
492
|
|
|
459
493
|
If you do not pass a logger and `@trebired/logger` is installed in the host app, git-host will create a quiet console-only logger automatically before falling back to raw `console`.
|
|
460
494
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare const LINGUIST_START_EVENT = "git:linguist:start";
|
|
2
|
+
declare const LINGUIST_PROGRESS_EVENT = "git:linguist:progress";
|
|
3
|
+
declare const LINGUIST_RESULT_EVENT = "git:linguist:result";
|
|
4
|
+
declare const LINGUIST_DONE_EVENT = "git:linguist:done";
|
|
5
|
+
declare const LINGUIST_ERROR_EVENT = "git:linguist:error";
|
|
6
|
+
export { LINGUIST_DONE_EVENT, LINGUIST_ERROR_EVENT, LINGUIST_PROGRESS_EVENT, LINGUIST_RESULT_EVENT, LINGUIST_START_EVENT, };
|
|
7
|
+
//# sourceMappingURL=socket_events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket_events.d.ts","sourceRoot":"","sources":["../../src/api/socket_events.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,oBAAoB,uBAAuB,CAAC;AAClD,QAAA,MAAM,uBAAuB,0BAA0B,CAAC;AACxD,QAAA,MAAM,qBAAqB,wBAAwB,CAAC;AACpD,QAAA,MAAM,mBAAmB,sBAAsB,CAAC;AAChD,QAAA,MAAM,oBAAoB,uBAAuB,CAAC;AAElD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,GACrB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const LINGUIST_START_EVENT = "git:linguist:start";
|
|
2
|
+
const LINGUIST_PROGRESS_EVENT = "git:linguist:progress";
|
|
3
|
+
const LINGUIST_RESULT_EVENT = "git:linguist:result";
|
|
4
|
+
const LINGUIST_DONE_EVENT = "git:linguist:done";
|
|
5
|
+
const LINGUIST_ERROR_EVENT = "git:linguist:error";
|
|
6
|
+
export { LINGUIST_DONE_EVENT, LINGUIST_ERROR_EVENT, LINGUIST_PROGRESS_EVENT, LINGUIST_RESULT_EVENT, LINGUIST_START_EVENT, };
|
|
7
|
+
//# sourceMappingURL=socket_events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket_events.js","sourceRoot":"","sources":["../../src/api/socket_events.ts"],"names":[],"mappings":"AAAA,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAClD,MAAM,uBAAuB,GAAG,uBAAuB,CAAC;AACxD,MAAM,qBAAqB,GAAG,qBAAqB,CAAC;AACpD,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;AAChD,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAElD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,GACrB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Server as SocketIoServer } from "socket.io";
|
|
2
|
+
import type { CreateGitApiSocketServerOptions } from "../types.js";
|
|
3
|
+
import { LINGUIST_DONE_EVENT, LINGUIST_ERROR_EVENT, LINGUIST_PROGRESS_EVENT, LINGUIST_RESULT_EVENT, LINGUIST_START_EVENT } from "./socket_events.js";
|
|
4
|
+
declare function createGitApiSocketServer(options: CreateGitApiSocketServerOptions): SocketIoServer<import("socket.io").DefaultEventsMap, import("socket.io").DefaultEventsMap, import("socket.io").DefaultEventsMap, any>;
|
|
5
|
+
export { createGitApiSocketServer, LINGUIST_DONE_EVENT, LINGUIST_ERROR_EVENT, LINGUIST_PROGRESS_EVENT, LINGUIST_RESULT_EVENT, LINGUIST_START_EVENT, };
|
|
6
|
+
//# sourceMappingURL=socket_server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket_server.d.ts","sourceRoot":"","sources":["../../src/api/socket_server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,WAAW,CAAC;AAGrD,OAAO,KAAK,EACV,+BAA+B,EAEhC,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAc5B,iBAAS,wBAAwB,CAAC,OAAO,EAAE,+BAA+B,yIAgJzE;AAED,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,GACrB,CAAC"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Server as SocketIoServer } from "socket.io";
|
|
2
|
+
import { resolveLogger } from "../logging.js";
|
|
3
|
+
import { text } from "../utils/text.js";
|
|
4
|
+
import { LINGUIST_DONE_EVENT, LINGUIST_ERROR_EVENT, LINGUIST_PROGRESS_EVENT, LINGUIST_RESULT_EVENT, LINGUIST_START_EVENT, } from "./socket_events.js";
|
|
5
|
+
import { authorizationAllowed, serializeError, statusForError } from "./handler/response.js";
|
|
6
|
+
function normalizeSocketPath(basePathInput, socketPathInput) {
|
|
7
|
+
const socketPath = text(socketPathInput).replace(/\/+$/g, "");
|
|
8
|
+
if (socketPath) {
|
|
9
|
+
return socketPath.startsWith("/") ? socketPath : `/${socketPath}`;
|
|
10
|
+
}
|
|
11
|
+
const basePath = text(basePathInput).replace(/\/+$/g, "");
|
|
12
|
+
if (!basePath || basePath === "/")
|
|
13
|
+
return "/socket.io";
|
|
14
|
+
return `${basePath.startsWith("/") ? basePath : `/${basePath}`}/socket.io`;
|
|
15
|
+
}
|
|
16
|
+
function createGitApiSocketServer(options) {
|
|
17
|
+
if (!options || typeof options.gitHost !== "object") {
|
|
18
|
+
throw new TypeError("createGitApiSocketServer() requires a gitHost instance.");
|
|
19
|
+
}
|
|
20
|
+
if (!options.httpServer) {
|
|
21
|
+
throw new TypeError("createGitApiSocketServer() requires an httpServer.");
|
|
22
|
+
}
|
|
23
|
+
const logger = resolveLogger(options.logger);
|
|
24
|
+
const verbose = options.verbose === true;
|
|
25
|
+
const logGroup = "git-host.api.socket";
|
|
26
|
+
const basePath = text(options.basePath, "/api/git");
|
|
27
|
+
const path = normalizeSocketPath(basePath, options.socketPath);
|
|
28
|
+
const io = new SocketIoServer(options.httpServer, {
|
|
29
|
+
...(options.socketOptions || {}),
|
|
30
|
+
path,
|
|
31
|
+
});
|
|
32
|
+
io.on("connection", (socket) => {
|
|
33
|
+
socket.on(LINGUIST_START_EVENT, async (payload) => {
|
|
34
|
+
const repositoryKey = text(payload && payload.repositoryKey);
|
|
35
|
+
const ref = text(payload && payload.ref);
|
|
36
|
+
const pathname = `${basePath.replace(/\/+$/g, "") || ""}/repositories/${encodeURIComponent(repositoryKey)}/linguist/socket`;
|
|
37
|
+
if (!repositoryKey) {
|
|
38
|
+
socket.emit(LINGUIST_ERROR_EVENT, {
|
|
39
|
+
error: {
|
|
40
|
+
code: "repository_not_found",
|
|
41
|
+
message: "Repository key is required.",
|
|
42
|
+
},
|
|
43
|
+
status: 404,
|
|
44
|
+
});
|
|
45
|
+
socket.emit(LINGUIST_DONE_EVENT, { ok: false });
|
|
46
|
+
socket.disconnect();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const repositoryId = text(options.resolveRepositoryId
|
|
50
|
+
? await options.resolveRepositoryId(repositoryKey, socket.request)
|
|
51
|
+
: repositoryKey);
|
|
52
|
+
if (!repositoryId) {
|
|
53
|
+
socket.emit(LINGUIST_ERROR_EVENT, {
|
|
54
|
+
error: {
|
|
55
|
+
code: "repository_not_found",
|
|
56
|
+
message: "Repository not found.",
|
|
57
|
+
},
|
|
58
|
+
status: 404,
|
|
59
|
+
});
|
|
60
|
+
socket.emit(LINGUIST_DONE_EVENT, { ok: false, repository_key: repositoryKey });
|
|
61
|
+
socket.disconnect();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const searchParams = new URLSearchParams();
|
|
65
|
+
if (ref)
|
|
66
|
+
searchParams.set("ref", ref);
|
|
67
|
+
const auth = authorizationAllowed(options.authorize
|
|
68
|
+
? await options.authorize({
|
|
69
|
+
action: "linguist_socket",
|
|
70
|
+
method: "SOCKET",
|
|
71
|
+
pathname,
|
|
72
|
+
remoteAddress: text(socket.handshake.address),
|
|
73
|
+
repositoryId,
|
|
74
|
+
repositoryKey,
|
|
75
|
+
request: socket.request,
|
|
76
|
+
searchParams,
|
|
77
|
+
})
|
|
78
|
+
: undefined);
|
|
79
|
+
if (!auth.allowed) {
|
|
80
|
+
logger.warn(logGroup, "api socket permission denied", {
|
|
81
|
+
action: "linguist_socket",
|
|
82
|
+
pathname,
|
|
83
|
+
repositoryId,
|
|
84
|
+
repositoryKey,
|
|
85
|
+
status: auth.status || 403,
|
|
86
|
+
});
|
|
87
|
+
socket.emit(LINGUIST_ERROR_EVENT, {
|
|
88
|
+
error: {
|
|
89
|
+
code: "permission_denied",
|
|
90
|
+
message: auth.message || "Permission denied.",
|
|
91
|
+
},
|
|
92
|
+
status: auth.status || 403,
|
|
93
|
+
});
|
|
94
|
+
socket.emit(LINGUIST_DONE_EVENT, {
|
|
95
|
+
ok: false,
|
|
96
|
+
repository_id: repositoryId,
|
|
97
|
+
repository_key: repositoryKey,
|
|
98
|
+
});
|
|
99
|
+
socket.disconnect();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const data = await options.gitHost.readLinguist(repositoryId, {
|
|
104
|
+
onProgress(progressEvent) {
|
|
105
|
+
socket.emit(LINGUIST_PROGRESS_EVENT, progressEvent);
|
|
106
|
+
},
|
|
107
|
+
ref,
|
|
108
|
+
});
|
|
109
|
+
socket.emit(LINGUIST_RESULT_EVENT, {
|
|
110
|
+
action: "linguist",
|
|
111
|
+
data,
|
|
112
|
+
repository_id: repositoryId,
|
|
113
|
+
repository_key: repositoryKey,
|
|
114
|
+
});
|
|
115
|
+
socket.emit(LINGUIST_DONE_EVENT, {
|
|
116
|
+
ok: true,
|
|
117
|
+
repository_id: repositoryId,
|
|
118
|
+
repository_key: repositoryKey,
|
|
119
|
+
});
|
|
120
|
+
if (verbose) {
|
|
121
|
+
logger.info(logGroup, "api linguist socket completed", {
|
|
122
|
+
repositoryId,
|
|
123
|
+
repositoryKey,
|
|
124
|
+
socketId: socket.id,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
logger.error(logGroup, "api linguist socket failed", {
|
|
130
|
+
error: error instanceof Error ? error.message : String(error),
|
|
131
|
+
repositoryId,
|
|
132
|
+
repositoryKey,
|
|
133
|
+
socketId: socket.id,
|
|
134
|
+
});
|
|
135
|
+
socket.emit(LINGUIST_ERROR_EVENT, {
|
|
136
|
+
...serializeError(error),
|
|
137
|
+
ok: false,
|
|
138
|
+
repository_id: repositoryId,
|
|
139
|
+
repository_key: repositoryKey,
|
|
140
|
+
status: statusForError(error),
|
|
141
|
+
});
|
|
142
|
+
socket.emit(LINGUIST_DONE_EVENT, {
|
|
143
|
+
ok: false,
|
|
144
|
+
repository_id: repositoryId,
|
|
145
|
+
repository_key: repositoryKey,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
socket.disconnect();
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
return io;
|
|
154
|
+
}
|
|
155
|
+
export { createGitApiSocketServer, LINGUIST_DONE_EVENT, LINGUIST_ERROR_EVENT, LINGUIST_PROGRESS_EVENT, LINGUIST_RESULT_EVENT, LINGUIST_START_EVENT, };
|
|
156
|
+
//# sourceMappingURL=socket_server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket_server.js","sourceRoot":"","sources":["../../src/api/socket_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GitLinguistProgressEvent, GitLinguistProgressStage, GitRepositoryHandle, MaybePromise } from "../../types.js";
|
|
2
|
+
type GitLinguistProgressCallback = (event: GitLinguistProgressEvent) => MaybePromise<void>;
|
|
3
|
+
type CreateLinguistProgressReporterOptions = {
|
|
4
|
+
onProgress?: GitLinguistProgressCallback;
|
|
5
|
+
repository: GitRepositoryHandle;
|
|
6
|
+
ref?: string;
|
|
7
|
+
};
|
|
8
|
+
declare function createLinguistProgressReporter(options: CreateLinguistProgressReporterOptions): {
|
|
9
|
+
emit: (stage: GitLinguistProgressStage, update?: Partial<Omit<GitLinguistProgressEvent, "emitted_at" | "percent" | "repository_id" | "scan_id" | "stage">>) => Promise<void>;
|
|
10
|
+
scanId: `${string}-${string}-${string}-${string}-${string}`;
|
|
11
|
+
};
|
|
12
|
+
export { createLinguistProgressReporter };
|
|
13
|
+
export type { GitLinguistProgressCallback };
|
|
14
|
+
//# sourceMappingURL=linguist_progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linguist_progress.d.ts","sourceRoot":"","sources":["../../../src/core/inspect/linguist_progress.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,YAAY,EACb,MAAM,gBAAgB,CAAC;AAGxB,KAAK,2BAA2B,GAAG,CAAC,KAAK,EAAE,wBAAwB,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;AAE3F,KAAK,qCAAqC,GAAG;IAC3C,UAAU,CAAC,EAAE,2BAA2B,CAAC;IACzC,UAAU,EAAE,mBAAmB,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAoDF,iBAAS,8BAA8B,CAAC,OAAO,EAAE,qCAAqC;kBAY3E,wBAAwB,WACvB,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,YAAY,GAAG,SAAS,GAAG,eAAe,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC,KAChH,OAAO,CAAC,IAAI,CAAC;;EA6BjB;AAED,OAAO,EAAE,8BAA8B,EAAE,CAAC;AAC1C,YAAY,EAAE,2BAA2B,EAAE,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { text } from "../../utils/text.js";
|
|
3
|
+
function computeLinguistPercent(stage, state) {
|
|
4
|
+
if (stage === "queued")
|
|
5
|
+
return 0;
|
|
6
|
+
if (stage === "resolving_ref")
|
|
7
|
+
return 5;
|
|
8
|
+
if (stage === "listing_tree")
|
|
9
|
+
return 10;
|
|
10
|
+
if (stage === "analyzing")
|
|
11
|
+
return 90;
|
|
12
|
+
if (stage === "completed")
|
|
13
|
+
return 100;
|
|
14
|
+
if (stage === "failed") {
|
|
15
|
+
if (state.total_blobs > 0) {
|
|
16
|
+
const ratio = state.processed_blobs / state.total_blobs;
|
|
17
|
+
return Math.max(0, Math.min(95, Math.round(15 + (ratio * 70))));
|
|
18
|
+
}
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
if (state.total_blobs <= 0)
|
|
22
|
+
return 15;
|
|
23
|
+
const ratio = state.processed_blobs / state.total_blobs;
|
|
24
|
+
return Math.max(15, Math.min(85, Math.round(15 + (ratio * 70))));
|
|
25
|
+
}
|
|
26
|
+
function defaultLinguistMessage(stage, state) {
|
|
27
|
+
switch (stage) {
|
|
28
|
+
case "queued":
|
|
29
|
+
return "Queued linguist scan.";
|
|
30
|
+
case "resolving_ref":
|
|
31
|
+
return "Resolving repository ref.";
|
|
32
|
+
case "listing_tree":
|
|
33
|
+
return "Listing repository tree.";
|
|
34
|
+
case "reading_blobs":
|
|
35
|
+
return state.total_blobs > 0
|
|
36
|
+
? `Reading repository files (${state.processed_blobs}/${state.total_blobs}).`
|
|
37
|
+
: "Reading repository files.";
|
|
38
|
+
case "analyzing":
|
|
39
|
+
return "Analyzing repository languages.";
|
|
40
|
+
case "completed":
|
|
41
|
+
return "Completed linguist scan.";
|
|
42
|
+
case "failed":
|
|
43
|
+
return "Linguist scan failed.";
|
|
44
|
+
default:
|
|
45
|
+
return "Processing linguist scan.";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function createLinguistProgressReporter(options) {
|
|
49
|
+
const onProgress = typeof options.onProgress === "function" ? options.onProgress : null;
|
|
50
|
+
const scanId = randomUUID();
|
|
51
|
+
const state = {
|
|
52
|
+
commit: "",
|
|
53
|
+
processed_blobs: 0,
|
|
54
|
+
ref: text(options.ref, "HEAD"),
|
|
55
|
+
total_blobs: 0,
|
|
56
|
+
total_entries: 0,
|
|
57
|
+
};
|
|
58
|
+
async function emit(stage, update = {}) {
|
|
59
|
+
if (!onProgress)
|
|
60
|
+
return;
|
|
61
|
+
if (typeof update.ref === "string")
|
|
62
|
+
state.ref = text(update.ref, state.ref);
|
|
63
|
+
if (typeof update.commit === "string")
|
|
64
|
+
state.commit = text(update.commit, state.commit);
|
|
65
|
+
if (typeof update.total_entries === "number")
|
|
66
|
+
state.total_entries = Math.max(0, Number(update.total_entries) || 0);
|
|
67
|
+
if (typeof update.total_blobs === "number")
|
|
68
|
+
state.total_blobs = Math.max(0, Number(update.total_blobs) || 0);
|
|
69
|
+
if (typeof update.processed_blobs === "number")
|
|
70
|
+
state.processed_blobs = Math.max(0, Number(update.processed_blobs) || 0);
|
|
71
|
+
await onProgress({
|
|
72
|
+
commit: text(update.commit, state.commit) || undefined,
|
|
73
|
+
emitted_at: new Date().toISOString(),
|
|
74
|
+
error: update.error,
|
|
75
|
+
message: text(update.message, defaultLinguistMessage(stage, state)),
|
|
76
|
+
percent: computeLinguistPercent(stage, state),
|
|
77
|
+
processed_blobs: state.processed_blobs,
|
|
78
|
+
ref: text(update.ref, state.ref),
|
|
79
|
+
repository_id: options.repository.id,
|
|
80
|
+
scan_id: scanId,
|
|
81
|
+
stage,
|
|
82
|
+
total_blobs: state.total_blobs,
|
|
83
|
+
total_entries: state.total_entries,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
emit,
|
|
88
|
+
scanId,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export { createLinguistProgressReporter };
|
|
92
|
+
//# sourceMappingURL=linguist_progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linguist_progress.js","sourceRoot":"","sources":["../../../src/core/inspect/linguist_progress.ts"],"names":[],"mappings":"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"}
|
package/dist/core/inspect.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { GitArchive, GitBlob, GitBlame, GitCommitDetail, GitCompareSummary, GitRepositoryHandle, GitRepositoryLinguist, GitSearchResult, GitTreeEntry } from "../types.js";
|
|
1
|
+
import type { GitArchive, GitBlob, GitBlame, GitCommitDetail, GitCompareSummary, GitLinguistProgressEvent, GitRepositoryHandle, GitRepositoryLinguist, GitSearchResult, GitTreeEntry } from "../types.js";
|
|
2
2
|
declare function readRepositoryLinguist(repository: GitRepositoryHandle, options?: {
|
|
3
|
+
onProgress?: (event: GitLinguistProgressEvent) => Promise<void> | void;
|
|
3
4
|
ref?: string;
|
|
4
5
|
}): Promise<GitRepositoryLinguist>;
|
|
5
6
|
declare function listRepositoryTree(repository: GitRepositoryHandle, options?: {
|
|
6
7
|
icons?: boolean;
|
|
7
8
|
linguist?: boolean;
|
|
9
|
+
onLinguistProgress?: (event: GitLinguistProgressEvent) => Promise<void> | void;
|
|
8
10
|
path?: string;
|
|
9
11
|
recursive?: boolean;
|
|
10
12
|
ref?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../src/core/inspect.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EACV,OAAO,EACP,QAAQ,EAER,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EAGrB,eAAe,EACf,YAAY,EACb,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../src/core/inspect.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EACV,OAAO,EACP,QAAQ,EAER,eAAe,EACf,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,EACnB,qBAAqB,EAGrB,eAAe,EACf,YAAY,EACb,MAAM,aAAa,CAAC;AAkPrB,iBAAe,sBAAsB,CACnC,UAAU,EAAE,mBAAmB,EAC/B,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;CACT,GACL,OAAO,CAAC,qBAAqB,CAAC,CAgDhC;AAED,iBAAe,kBAAkB,CAC/B,UAAU,EAAE,mBAAmB,EAC/B,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACT,GACL,OAAO,CAAC,YAAY,EAAE,CAAC,CAsBzB;AAED,iBAAe,kBAAkB,CAC/B,UAAU,EAAE,mBAAmB,EAC/B,OAAO,EAAE;IACP,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GACA,OAAO,CAAC,OAAO,CAAC,CAiDlB;AAED,iBAAe,oBAAoB,CAAC,UAAU,EAAE,mBAAmB,EAAE,cAAc,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAqDtH;AAED,iBAAe,qBAAqB,CAClC,UAAU,EAAE,mBAAmB,EAC/B,OAAO,EAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GACA,OAAO,CAAC,iBAAiB,CAAC,CAmD5B;AAED,iBAAe,mBAAmB,CAChC,UAAU,EAAE,mBAAmB,EAC/B,OAAO,EAAE;IACP,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GACA,OAAO,CAAC,QAAQ,CAAC,CAiEnB;AAED,iBAAe,gBAAgB,CAC7B,UAAU,EAAE,mBAAmB,EAC/B,OAAO,EAAE;IACP,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GACA,OAAO,CAAC,eAAe,CAAC,CAqE1B;AAED,iBAAe,qBAAqB,CAClC,UAAU,EAAE,mBAAmB,EAC/B,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACT,GACL,OAAO,CAAC,UAAU,CAAC,CAyBrB;AAED,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,gBAAgB,GACjB,CAAC"}
|
package/dist/core/inspect.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import analyseRawContent from "linguist-js/dist/entry/analyseRaw.js";
|
|
2
|
-
import { GitHostError } from "../errors.js";
|
|
2
|
+
import { GitHostError, isGitHostError } from "../errors.js";
|
|
3
3
|
import { normalizeRepositoryRelativePath } from "../utils/paths.js";
|
|
4
4
|
import { text } from "../utils/text.js";
|
|
5
5
|
import { resolveTreeEntryIcon } from "./inspect/icon_theme.js";
|
|
6
|
+
import { createLinguistProgressReporter } from "./inspect/linguist_progress.js";
|
|
6
7
|
import { parseCommitLogOutput, parseNameStatusOutput, parseNumstatOutput } from "./repository.js";
|
|
7
8
|
import { runGit, runGitBuffer } from "./run_git.js";
|
|
8
9
|
import { assertRepositoryReady, decodeBlobContent, parseCommitMeta, parseLsTreeBuffer, resolveCommitForRef, summarizeFileLines, withFileStats, } from "./inspect/helpers.js";
|
|
@@ -77,10 +78,18 @@ async function readTreeEntryBlob(repository, ref, entry) {
|
|
|
77
78
|
...decodeBlobContent(contentRes.stdout),
|
|
78
79
|
};
|
|
79
80
|
}
|
|
80
|
-
async function readRepositoryLinguistInput(repository, ref) {
|
|
81
|
+
async function readRepositoryLinguistInput(repository, ref, onProgress) {
|
|
81
82
|
const entries = await readRepositoryTreeEntries(repository, { recursive: true, ref });
|
|
82
83
|
const blobEntries = entries.filter((entry) => entry.type === "blob");
|
|
83
84
|
const input = {};
|
|
85
|
+
let processedBlobs = 0;
|
|
86
|
+
if (onProgress) {
|
|
87
|
+
await onProgress("reading_blobs", {
|
|
88
|
+
processed_blobs: 0,
|
|
89
|
+
total_blobs: blobEntries.length,
|
|
90
|
+
total_entries: entries.length,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
84
93
|
for (let index = 0; index < blobEntries.length; index += 16) {
|
|
85
94
|
const chunk = blobEntries.slice(index, index + 16);
|
|
86
95
|
const blobs = await Promise.all(chunk.map(async (entry) => {
|
|
@@ -91,8 +100,20 @@ async function readRepositoryLinguistInput(repository, ref) {
|
|
|
91
100
|
input[blob.path] = blob.content;
|
|
92
101
|
}
|
|
93
102
|
}
|
|
103
|
+
processedBlobs += chunk.length;
|
|
104
|
+
if (onProgress) {
|
|
105
|
+
await onProgress("reading_blobs", {
|
|
106
|
+
processed_blobs: processedBlobs,
|
|
107
|
+
total_blobs: blobEntries.length,
|
|
108
|
+
total_entries: entries.length,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
94
111
|
}
|
|
95
|
-
return
|
|
112
|
+
return {
|
|
113
|
+
input,
|
|
114
|
+
total_blobs: blobEntries.length,
|
|
115
|
+
total_entries: entries.length,
|
|
116
|
+
};
|
|
96
117
|
}
|
|
97
118
|
function normalizeLinguistLanguage(language, value, repositoryMetadata) {
|
|
98
119
|
const name = text(language);
|
|
@@ -158,10 +179,52 @@ function normalizeLinguistResults(result, target) {
|
|
|
158
179
|
}
|
|
159
180
|
async function readRepositoryLinguist(repository, options = {}) {
|
|
160
181
|
await assertRepositoryReady(repository);
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
182
|
+
const progress = createLinguistProgressReporter({
|
|
183
|
+
onProgress: options.onProgress,
|
|
184
|
+
ref: text(options.ref, "HEAD"),
|
|
185
|
+
repository,
|
|
186
|
+
});
|
|
187
|
+
let target = {
|
|
188
|
+
commit: "",
|
|
189
|
+
ref: text(options.ref, "HEAD"),
|
|
190
|
+
};
|
|
191
|
+
try {
|
|
192
|
+
await progress.emit("queued");
|
|
193
|
+
await progress.emit("resolving_ref", { ref: target.ref });
|
|
194
|
+
target = await resolveCommitForRef(repository.path, target.ref, "Repository ref does not exist.");
|
|
195
|
+
await progress.emit("listing_tree", { commit: target.commit, ref: target.ref });
|
|
196
|
+
const linguistInput = await readRepositoryLinguistInput(repository, target.ref, progress.emit);
|
|
197
|
+
await progress.emit("analyzing", {
|
|
198
|
+
commit: target.commit,
|
|
199
|
+
processed_blobs: linguistInput.total_blobs,
|
|
200
|
+
ref: target.ref,
|
|
201
|
+
total_blobs: linguistInput.total_blobs,
|
|
202
|
+
total_entries: linguistInput.total_entries,
|
|
203
|
+
});
|
|
204
|
+
const result = await analyseRawContent(linguistInput.input, { offline: true });
|
|
205
|
+
const normalized = normalizeLinguistResults(result, target);
|
|
206
|
+
await progress.emit("completed", {
|
|
207
|
+
commit: target.commit,
|
|
208
|
+
processed_blobs: linguistInput.total_blobs,
|
|
209
|
+
ref: target.ref,
|
|
210
|
+
total_blobs: linguistInput.total_blobs,
|
|
211
|
+
total_entries: linguistInput.total_entries,
|
|
212
|
+
});
|
|
213
|
+
return normalized;
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
await progress.emit("failed", {
|
|
217
|
+
commit: text(target.commit) || undefined,
|
|
218
|
+
error: {
|
|
219
|
+
code: isGitHostError(error) ? error.code : (error instanceof Error ? "internal_error" : "internal_error"),
|
|
220
|
+
message: error instanceof Error ? error.message : "Linguist scan failed.",
|
|
221
|
+
},
|
|
222
|
+
ref: target.ref,
|
|
223
|
+
}).catch(() => {
|
|
224
|
+
return undefined;
|
|
225
|
+
});
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
165
228
|
}
|
|
166
229
|
async function listRepositoryTree(repository, options = {}) {
|
|
167
230
|
await assertRepositoryReady(repository);
|
|
@@ -174,7 +237,10 @@ async function listRepositoryTree(repository, options = {}) {
|
|
|
174
237
|
if (options.icons !== true && options.linguist !== true)
|
|
175
238
|
return entries;
|
|
176
239
|
const linguist = options.linguist === true
|
|
177
|
-
? await readRepositoryLinguist(repository, {
|
|
240
|
+
? await readRepositoryLinguist(repository, {
|
|
241
|
+
onProgress: options.onLinguistProgress,
|
|
242
|
+
ref,
|
|
243
|
+
})
|
|
178
244
|
: null;
|
|
179
245
|
return entries.map((entry) => ({
|
|
180
246
|
...entry,
|