@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.
Files changed (39) hide show
  1. package/CHANGELOG.md +2 -1
  2. package/README.md +37 -3
  3. package/dist/api/socket_events.d.ts +7 -0
  4. package/dist/api/socket_events.d.ts.map +1 -0
  5. package/dist/api/socket_events.js +7 -0
  6. package/dist/api/socket_events.js.map +1 -0
  7. package/dist/api/socket_server.d.ts +6 -0
  8. package/dist/api/socket_server.d.ts.map +1 -0
  9. package/dist/api/socket_server.js +156 -0
  10. package/dist/api/socket_server.js.map +1 -0
  11. package/dist/core/inspect/linguist_progress.d.ts +14 -0
  12. package/dist/core/inspect/linguist_progress.d.ts.map +1 -0
  13. package/dist/core/inspect/linguist_progress.js +92 -0
  14. package/dist/core/inspect/linguist_progress.js.map +1 -0
  15. package/dist/core/inspect.d.ts +3 -1
  16. package/dist/core/inspect.d.ts.map +1 -1
  17. package/dist/core/inspect.js +74 -8
  18. package/dist/core/inspect.js.map +1 -1
  19. package/dist/index.d.ts +2 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +1 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/react/client/types.d.ts +45 -2
  24. package/dist/react/client/types.d.ts.map +1 -1
  25. package/dist/react/client.d.ts +1 -1
  26. package/dist/react/client.d.ts.map +1 -1
  27. package/dist/react/client.js +147 -0
  28. package/dist/react/client.js.map +1 -1
  29. package/dist/react/index.d.ts +1 -1
  30. package/dist/react/index.d.ts.map +1 -1
  31. package/dist/types/host.d.ts +3 -1
  32. package/dist/types/host.d.ts.map +1 -1
  33. package/dist/types/index.d.ts +2 -2
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/dist/types/repository.d.ts +19 -1
  36. package/dist/types/repository.d.ts.map +1 -1
  37. package/dist/types/transports.d.ts +9 -3
  38. package/dist/types/transports.d.ts.map +1 -1
  39. 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.2.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", { ref: "main" });
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 `createGitApiHandler()` through their `logger` option.
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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"}
@@ -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;AAmNrB,iBAAe,sBAAsB,CACnC,UAAU,EAAE,mBAAmB,EAC/B,OAAO,GAAE;IACP,GAAG,CAAC,EAAE,MAAM,CAAC;CACT,GACL,OAAO,CAAC,qBAAqB,CAAC,CAMhC;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,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACT,GACL,OAAO,CAAC,YAAY,EAAE,CAAC,CAmBzB;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"}
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"}
@@ -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 input;
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 target = await resolveCommitForRef(repository.path, text(options.ref, "HEAD"), "Repository ref does not exist.");
162
- const input = await readRepositoryLinguistInput(repository, target.ref);
163
- const result = await analyseRawContent(input, { offline: true });
164
- return normalizeLinguistResults(result, target);
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, { ref })
240
+ ? await readRepositoryLinguist(repository, {
241
+ onProgress: options.onLinguistProgress,
242
+ ref,
243
+ })
178
244
  : null;
179
245
  return entries.map((entry) => ({
180
246
  ...entry,