projex-cli 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +321 -0
- package/dist/cli.js.map +1 -0
- package/dist/event-bus.d.ts +35 -0
- package/dist/event-bus.d.ts.map +1 -0
- package/dist/event-bus.js +71 -0
- package/dist/event-bus.js.map +1 -0
- package/dist/events/event-bus.d.ts +35 -0
- package/dist/events/event-bus.d.ts.map +1 -0
- package/dist/events/event-bus.js +71 -0
- package/dist/events/event-bus.js.map +1 -0
- package/dist/events/index.d.ts +3 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +3 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/types.d.ts +143 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/events/types.js +6 -0
- package/dist/events/types.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/inference/enricher.d.ts +18 -0
- package/dist/inference/enricher.d.ts.map +1 -0
- package/dist/inference/enricher.js +290 -0
- package/dist/inference/enricher.js.map +1 -0
- package/dist/inference/index.d.ts +5 -0
- package/dist/inference/index.d.ts.map +1 -0
- package/dist/inference/index.js +5 -0
- package/dist/inference/index.js.map +1 -0
- package/dist/inference/lifecycle-tracker.d.ts +40 -0
- package/dist/inference/lifecycle-tracker.d.ts.map +1 -0
- package/dist/inference/lifecycle-tracker.js +132 -0
- package/dist/inference/lifecycle-tracker.js.map +1 -0
- package/dist/inference/portfolio-generator.d.ts +44 -0
- package/dist/inference/portfolio-generator.d.ts.map +1 -0
- package/dist/inference/portfolio-generator.js +239 -0
- package/dist/inference/portfolio-generator.js.map +1 -0
- package/dist/inference/project-detector.d.ts +17 -0
- package/dist/inference/project-detector.d.ts.map +1 -0
- package/dist/inference/project-detector.js +127 -0
- package/dist/inference/project-detector.js.map +1 -0
- package/dist/ingestion/github-client.d.ts +56 -0
- package/dist/ingestion/github-client.d.ts.map +1 -0
- package/dist/ingestion/github-client.js +213 -0
- package/dist/ingestion/github-client.js.map +1 -0
- package/dist/ingestion/github-poller.d.ts +41 -0
- package/dist/ingestion/github-poller.d.ts.map +1 -0
- package/dist/ingestion/github-poller.js +121 -0
- package/dist/ingestion/github-poller.js.map +1 -0
- package/dist/ingestion/normalizer.d.ts +41 -0
- package/dist/ingestion/normalizer.d.ts.map +1 -0
- package/dist/ingestion/normalizer.js +173 -0
- package/dist/ingestion/normalizer.js.map +1 -0
- package/dist/integrations/analyzer.d.ts +74 -0
- package/dist/integrations/analyzer.d.ts.map +1 -0
- package/dist/integrations/analyzer.js +287 -0
- package/dist/integrations/analyzer.js.map +1 -0
- package/dist/integrations/index.d.ts +4 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +5 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/injectors.d.ts +60 -0
- package/dist/integrations/injectors.d.ts.map +1 -0
- package/dist/integrations/injectors.js +361 -0
- package/dist/integrations/injectors.js.map +1 -0
- package/dist/integrations/template-generator.d.ts +45 -0
- package/dist/integrations/template-generator.d.ts.map +1 -0
- package/dist/integrations/template-generator.js +187 -0
- package/dist/integrations/template-generator.js.map +1 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/project-store.d.ts +23 -0
- package/dist/storage/project-store.d.ts.map +1 -0
- package/dist/storage/project-store.js +107 -0
- package/dist/storage/project-store.js.map +1 -0
- package/dist/types.d.ts +143 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub API client wrapper using Octokit.
|
|
3
|
+
* Provides typed access to repository data and events.
|
|
4
|
+
*/
|
|
5
|
+
import { Octokit } from '@octokit/rest';
|
|
6
|
+
export class GitHubClient {
|
|
7
|
+
octokit;
|
|
8
|
+
username;
|
|
9
|
+
constructor(token, username) {
|
|
10
|
+
this.octokit = new Octokit({ auth: token });
|
|
11
|
+
this.username = username;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Fetch all repositories for the configured user
|
|
15
|
+
*/
|
|
16
|
+
async listUserRepos() {
|
|
17
|
+
const repos = [];
|
|
18
|
+
// Paginate through all repos
|
|
19
|
+
for await (const response of this.octokit.paginate.iterator(this.octokit.repos.listForAuthenticatedUser, { per_page: 100, sort: 'updated' })) {
|
|
20
|
+
for (const repo of response.data) {
|
|
21
|
+
repos.push({
|
|
22
|
+
id: repo.id,
|
|
23
|
+
name: repo.name,
|
|
24
|
+
fullName: repo.full_name,
|
|
25
|
+
owner: repo.owner?.login ?? this.username,
|
|
26
|
+
url: repo.html_url,
|
|
27
|
+
description: repo.description,
|
|
28
|
+
topics: repo.topics ?? [],
|
|
29
|
+
isPrivate: repo.private,
|
|
30
|
+
isArchived: repo.archived ?? false,
|
|
31
|
+
defaultBranch: repo.default_branch,
|
|
32
|
+
createdAt: new Date(repo.created_at),
|
|
33
|
+
updatedAt: new Date(repo.updated_at),
|
|
34
|
+
pushedAt: repo.pushed_at ? new Date(repo.pushed_at) : null,
|
|
35
|
+
starCount: repo.stargazers_count ?? 0,
|
|
36
|
+
forkCount: repo.forks_count ?? 0,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return repos;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get detailed repo info including topics
|
|
44
|
+
*/
|
|
45
|
+
async getRepo(owner, repo) {
|
|
46
|
+
const { data } = await this.octokit.repos.get({ owner, repo });
|
|
47
|
+
return {
|
|
48
|
+
id: data.id,
|
|
49
|
+
name: data.name,
|
|
50
|
+
fullName: data.full_name,
|
|
51
|
+
owner: data.owner.login,
|
|
52
|
+
url: data.html_url,
|
|
53
|
+
description: data.description,
|
|
54
|
+
topics: data.topics ?? [],
|
|
55
|
+
isPrivate: data.private,
|
|
56
|
+
isArchived: data.archived,
|
|
57
|
+
defaultBranch: data.default_branch,
|
|
58
|
+
createdAt: new Date(data.created_at),
|
|
59
|
+
updatedAt: new Date(data.updated_at),
|
|
60
|
+
pushedAt: data.pushed_at ? new Date(data.pushed_at) : null,
|
|
61
|
+
starCount: data.stargazers_count ?? 0,
|
|
62
|
+
forkCount: data.forks_count ?? 0,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Fetch enrichment data: languages, readme, package files
|
|
67
|
+
*/
|
|
68
|
+
async getEnrichmentData(owner, repo) {
|
|
69
|
+
const [languages, readme, packageFiles] = await Promise.all([
|
|
70
|
+
this.getLanguages(owner, repo),
|
|
71
|
+
this.getReadme(owner, repo),
|
|
72
|
+
this.getPackageFiles(owner, repo),
|
|
73
|
+
]);
|
|
74
|
+
return { languages, readme, packageFiles };
|
|
75
|
+
}
|
|
76
|
+
async getLanguages(owner, repo) {
|
|
77
|
+
try {
|
|
78
|
+
const { data } = await this.octokit.repos.listLanguages({ owner, repo });
|
|
79
|
+
return data;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async getReadme(owner, repo) {
|
|
86
|
+
try {
|
|
87
|
+
const { data } = await this.octokit.repos.getReadme({ owner, repo });
|
|
88
|
+
// Decode base64 content
|
|
89
|
+
const content = Buffer.from(data.content, 'base64').toString('utf-8');
|
|
90
|
+
return content;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async getPackageFiles(owner, repo) {
|
|
97
|
+
const packageFiles = [];
|
|
98
|
+
// Check for common package files
|
|
99
|
+
const filesToCheck = [
|
|
100
|
+
{ path: 'package.json', type: 'npm' },
|
|
101
|
+
{ path: 'Cargo.toml', type: 'cargo' },
|
|
102
|
+
{ path: 'requirements.txt', type: 'pip' },
|
|
103
|
+
{ path: 'pyproject.toml', type: 'pip' },
|
|
104
|
+
{ path: 'go.mod', type: 'go' },
|
|
105
|
+
{ path: 'Gemfile', type: 'gem' },
|
|
106
|
+
{ path: 'composer.json', type: 'composer' },
|
|
107
|
+
];
|
|
108
|
+
for (const file of filesToCheck) {
|
|
109
|
+
try {
|
|
110
|
+
const { data } = await this.octokit.repos.getContent({
|
|
111
|
+
owner,
|
|
112
|
+
repo,
|
|
113
|
+
path: file.path,
|
|
114
|
+
});
|
|
115
|
+
if ('content' in data) {
|
|
116
|
+
const content = Buffer.from(data.content, 'base64').toString('utf-8');
|
|
117
|
+
const parsed = this.parsePackageFile(file.type, content);
|
|
118
|
+
if (parsed) {
|
|
119
|
+
packageFiles.push({
|
|
120
|
+
type: file.type,
|
|
121
|
+
filename: file.path,
|
|
122
|
+
dependencies: parsed.dependencies,
|
|
123
|
+
devDependencies: parsed.devDependencies,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// File doesn't exist, skip
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return packageFiles;
|
|
133
|
+
}
|
|
134
|
+
parsePackageFile(type, content) {
|
|
135
|
+
try {
|
|
136
|
+
switch (type) {
|
|
137
|
+
case 'npm': {
|
|
138
|
+
const pkg = JSON.parse(content);
|
|
139
|
+
return {
|
|
140
|
+
dependencies: Object.keys(pkg.dependencies ?? {}),
|
|
141
|
+
devDependencies: Object.keys(pkg.devDependencies ?? {}),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
case 'pip': {
|
|
145
|
+
// Simple requirements.txt parsing
|
|
146
|
+
const deps = content
|
|
147
|
+
.split('\n')
|
|
148
|
+
.map(line => line.trim())
|
|
149
|
+
.filter(line => line && !line.startsWith('#'))
|
|
150
|
+
.map(line => line.split(/[=<>!]/)[0].trim());
|
|
151
|
+
return { dependencies: deps, devDependencies: [] };
|
|
152
|
+
}
|
|
153
|
+
case 'cargo': {
|
|
154
|
+
// Basic Cargo.toml parsing (extract dependency names)
|
|
155
|
+
const depMatches = content.match(/^\[dependencies\][\s\S]*?(?=\n\[|$)/m);
|
|
156
|
+
const deps = [];
|
|
157
|
+
if (depMatches) {
|
|
158
|
+
const lines = depMatches[0].split('\n');
|
|
159
|
+
for (const line of lines) {
|
|
160
|
+
const match = line.match(/^(\w[\w-]*)\s*=/);
|
|
161
|
+
if (match)
|
|
162
|
+
deps.push(match[1]);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { dependencies: deps, devDependencies: [] };
|
|
166
|
+
}
|
|
167
|
+
case 'go': {
|
|
168
|
+
// Extract require statements from go.mod
|
|
169
|
+
const deps = [];
|
|
170
|
+
const requireBlock = content.match(/require\s*\(([\s\S]*?)\)/);
|
|
171
|
+
if (requireBlock) {
|
|
172
|
+
const lines = requireBlock[1].split('\n');
|
|
173
|
+
for (const line of lines) {
|
|
174
|
+
const match = line.trim().match(/^([^\s]+)\s+/);
|
|
175
|
+
if (match)
|
|
176
|
+
deps.push(match[1]);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return { dependencies: deps, devDependencies: [] };
|
|
180
|
+
}
|
|
181
|
+
default:
|
|
182
|
+
return { dependencies: [], devDependencies: [] };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get recent commits for activity tracking
|
|
191
|
+
*/
|
|
192
|
+
async getRecentCommits(owner, repo, since) {
|
|
193
|
+
try {
|
|
194
|
+
const params = {
|
|
195
|
+
owner,
|
|
196
|
+
repo,
|
|
197
|
+
per_page: 30,
|
|
198
|
+
};
|
|
199
|
+
if (since) {
|
|
200
|
+
params.since = since.toISOString();
|
|
201
|
+
}
|
|
202
|
+
const { data } = await this.octokit.repos.listCommits(params);
|
|
203
|
+
return data.map(commit => ({
|
|
204
|
+
sha: commit.sha,
|
|
205
|
+
date: new Date(commit.commit.committer?.date ?? commit.commit.author?.date ?? new Date()),
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=github-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-client.js","sourceRoot":"","sources":["../../src/ingestion/github-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Poller - periodically fetches repos and emits change events.
|
|
3
|
+
* Uses Normalizer to detect changes and EventBus to publish events.
|
|
4
|
+
*/
|
|
5
|
+
import { Normalizer } from './normalizer.js';
|
|
6
|
+
import { EventBus } from '../events/event-bus.js';
|
|
7
|
+
import type { Config, SignalEvent } from '../events/types.js';
|
|
8
|
+
export declare class GitHubPoller {
|
|
9
|
+
private client;
|
|
10
|
+
private normalizer;
|
|
11
|
+
private eventBus;
|
|
12
|
+
private config;
|
|
13
|
+
private pollInterval;
|
|
14
|
+
private isPolling;
|
|
15
|
+
constructor(config: Config, eventBus: EventBus);
|
|
16
|
+
/**
|
|
17
|
+
* Start periodic polling
|
|
18
|
+
*/
|
|
19
|
+
start(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Stop polling
|
|
22
|
+
*/
|
|
23
|
+
stop(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Manual poll trigger (useful for testing)
|
|
26
|
+
*/
|
|
27
|
+
poll(): Promise<SignalEvent[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Enrich a specific repo with detailed data
|
|
30
|
+
*/
|
|
31
|
+
private enrichRepo;
|
|
32
|
+
/**
|
|
33
|
+
* Force enrichment for a specific repo by ID
|
|
34
|
+
*/
|
|
35
|
+
enrichRepoById(repoId: string): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Get normalizer for snapshot persistence
|
|
38
|
+
*/
|
|
39
|
+
getNormalizer(): Normalizer;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=github-poller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-poller.d.ts","sourceRoot":"","sources":["../../src/ingestion/github-poller.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE9D,qBAAa,YAAY;IACrB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAO9C;;OAEG;IACH,KAAK,IAAI,IAAI;IAgBb;;OAEG;IACH,IAAI,IAAI,IAAI;IAQZ;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IA0CpC;;OAEG;YACW,UAAU;IAexB;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnD;;OAEG;IACH,aAAa,IAAI,UAAU;CAG9B"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Poller - periodically fetches repos and emits change events.
|
|
3
|
+
* Uses Normalizer to detect changes and EventBus to publish events.
|
|
4
|
+
*/
|
|
5
|
+
import { GitHubClient } from './github-client.js';
|
|
6
|
+
import { Normalizer } from './normalizer.js';
|
|
7
|
+
export class GitHubPoller {
|
|
8
|
+
client;
|
|
9
|
+
normalizer;
|
|
10
|
+
eventBus;
|
|
11
|
+
config;
|
|
12
|
+
pollInterval = null;
|
|
13
|
+
isPolling = false;
|
|
14
|
+
constructor(config, eventBus) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.client = new GitHubClient(config.github.token, config.github.username);
|
|
17
|
+
this.normalizer = new Normalizer();
|
|
18
|
+
this.eventBus = eventBus;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Start periodic polling
|
|
22
|
+
*/
|
|
23
|
+
start() {
|
|
24
|
+
if (this.pollInterval) {
|
|
25
|
+
console.log('[GitHubPoller] Already running');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const intervalMs = this.config.github.pollingIntervalMinutes * 60 * 1000;
|
|
29
|
+
console.log(`[GitHubPoller] Starting with ${this.config.github.pollingIntervalMinutes}min interval`);
|
|
30
|
+
// Run immediately on start
|
|
31
|
+
this.poll();
|
|
32
|
+
// Then set up interval
|
|
33
|
+
this.pollInterval = setInterval(() => this.poll(), intervalMs);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Stop polling
|
|
37
|
+
*/
|
|
38
|
+
stop() {
|
|
39
|
+
if (this.pollInterval) {
|
|
40
|
+
clearInterval(this.pollInterval);
|
|
41
|
+
this.pollInterval = null;
|
|
42
|
+
console.log('[GitHubPoller] Stopped');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Manual poll trigger (useful for testing)
|
|
47
|
+
*/
|
|
48
|
+
async poll() {
|
|
49
|
+
if (this.isPolling) {
|
|
50
|
+
console.log('[GitHubPoller] Poll already in progress, skipping');
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
this.isPolling = true;
|
|
54
|
+
const allEvents = [];
|
|
55
|
+
try {
|
|
56
|
+
console.log('[GitHubPoller] Fetching repositories...');
|
|
57
|
+
const repos = await this.client.listUserRepos();
|
|
58
|
+
console.log(`[GitHubPoller] Found ${repos.length} repositories`);
|
|
59
|
+
// Detect changes
|
|
60
|
+
const changeEvents = this.normalizer.detectChanges(repos);
|
|
61
|
+
console.log(`[GitHubPoller] Detected ${changeEvents.length} change events`);
|
|
62
|
+
// Publish events
|
|
63
|
+
for (const event of changeEvents) {
|
|
64
|
+
await this.eventBus.publish(event);
|
|
65
|
+
allEvents.push(event);
|
|
66
|
+
}
|
|
67
|
+
// Enrichment for new repos (repo.created events)
|
|
68
|
+
const newRepoEvents = changeEvents.filter(e => e.eventType === 'repo.created');
|
|
69
|
+
for (const event of newRepoEvents) {
|
|
70
|
+
const repo = repos.find(r => `github:${r.fullName}` === event.repo.id);
|
|
71
|
+
if (repo) {
|
|
72
|
+
await this.enrichRepo(repo, allEvents);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error('[GitHubPoller] Error during poll:', error);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
this.isPolling = false;
|
|
81
|
+
}
|
|
82
|
+
return allEvents;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Enrich a specific repo with detailed data
|
|
86
|
+
*/
|
|
87
|
+
async enrichRepo(repo, allEvents) {
|
|
88
|
+
try {
|
|
89
|
+
console.log(`[GitHubPoller] Enriching ${repo.fullName}...`);
|
|
90
|
+
const enrichmentData = await this.client.getEnrichmentData(repo.owner, repo.name);
|
|
91
|
+
const enrichmentEvent = this.normalizer.createEnrichmentEvent(repo, enrichmentData);
|
|
92
|
+
await this.eventBus.publish(enrichmentEvent);
|
|
93
|
+
allEvents.push(enrichmentEvent);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.error(`[GitHubPoller] Error enriching ${repo.fullName}:`, error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Force enrichment for a specific repo by ID
|
|
101
|
+
*/
|
|
102
|
+
async enrichRepoById(repoId) {
|
|
103
|
+
// Parse "github:owner/repo" format
|
|
104
|
+
const match = repoId.match(/^github:(.+)\/(.+)$/);
|
|
105
|
+
if (!match) {
|
|
106
|
+
throw new Error(`Invalid repo ID format: ${repoId}`);
|
|
107
|
+
}
|
|
108
|
+
const [, owner, name] = match;
|
|
109
|
+
const repo = await this.client.getRepo(owner, name);
|
|
110
|
+
const enrichmentData = await this.client.getEnrichmentData(owner, name);
|
|
111
|
+
const enrichmentEvent = this.normalizer.createEnrichmentEvent(repo, enrichmentData);
|
|
112
|
+
await this.eventBus.publish(enrichmentEvent);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get normalizer for snapshot persistence
|
|
116
|
+
*/
|
|
117
|
+
getNormalizer() {
|
|
118
|
+
return this.normalizer;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=github-poller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-poller.js","sourceRoot":"","sources":["../../src/ingestion/github-poller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizer transforms raw GitHub data into normalized SignalEvents.
|
|
3
|
+
* Compares current state with previous state to detect changes.
|
|
4
|
+
*/
|
|
5
|
+
import type { SignalEvent, RepoEnrichedEvent } from '../events/types.js';
|
|
6
|
+
import type { GitHubRepoData, GitHubEnrichmentData } from './github-client.js';
|
|
7
|
+
export interface RepoSnapshot {
|
|
8
|
+
id: string;
|
|
9
|
+
description: string | null;
|
|
10
|
+
topics: string[];
|
|
11
|
+
isArchived: boolean;
|
|
12
|
+
pushedAt: Date | null;
|
|
13
|
+
updatedAt: Date;
|
|
14
|
+
}
|
|
15
|
+
export declare class Normalizer {
|
|
16
|
+
private previousSnapshots;
|
|
17
|
+
/**
|
|
18
|
+
* Compare current repo data with previous snapshot and emit change events
|
|
19
|
+
*/
|
|
20
|
+
detectChanges(repos: GitHubRepoData[]): SignalEvent[];
|
|
21
|
+
/**
|
|
22
|
+
* Create enrichment event from enrichment data
|
|
23
|
+
*/
|
|
24
|
+
createEnrichmentEvent(repo: GitHubRepoData, enrichment: GitHubEnrichmentData): RepoEnrichedEvent;
|
|
25
|
+
/**
|
|
26
|
+
* Load previous snapshots (for persistence across restarts)
|
|
27
|
+
*/
|
|
28
|
+
loadSnapshots(snapshots: RepoSnapshot[]): void;
|
|
29
|
+
/**
|
|
30
|
+
* Export current snapshots for persistence
|
|
31
|
+
*/
|
|
32
|
+
exportSnapshots(): RepoSnapshot[];
|
|
33
|
+
private hasPushActivity;
|
|
34
|
+
private topicsChanged;
|
|
35
|
+
private createRepoCreatedEvent;
|
|
36
|
+
private createRepoPushedEvent;
|
|
37
|
+
private createTopicsChangedEvent;
|
|
38
|
+
private createDescriptionChangedEvent;
|
|
39
|
+
private createArchivedEvent;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=normalizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../../src/ingestion/normalizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACR,WAAW,EAMX,iBAAiB,EAEpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE/E,MAAM,WAAW,YAAY;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;CACnB;AAED,qBAAa,UAAU;IACnB,OAAO,CAAC,iBAAiB,CAAwC;IAEjE;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,WAAW,EAAE;IAqDrD;;OAEG;IACH,qBAAqB,CACjB,IAAI,EAAE,cAAc,EACpB,UAAU,EAAE,oBAAoB,GACjC,iBAAiB;IAuBpB;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,IAAI;IAO9C;;OAEG;IACH,eAAe,IAAI,YAAY,EAAE;IAIjC,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,sBAAsB;IAkB9B,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,wBAAwB;IAkBhC,OAAO,CAAC,6BAA6B;IAkBrC,OAAO,CAAC,mBAAmB;CAe9B"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizer transforms raw GitHub data into normalized SignalEvents.
|
|
3
|
+
* Compares current state with previous state to detect changes.
|
|
4
|
+
*/
|
|
5
|
+
import { randomUUID } from 'crypto';
|
|
6
|
+
export class Normalizer {
|
|
7
|
+
previousSnapshots = new Map();
|
|
8
|
+
/**
|
|
9
|
+
* Compare current repo data with previous snapshot and emit change events
|
|
10
|
+
*/
|
|
11
|
+
detectChanges(repos) {
|
|
12
|
+
const events = [];
|
|
13
|
+
const currentRepoIds = new Set();
|
|
14
|
+
for (const repo of repos) {
|
|
15
|
+
const repoId = `github:${repo.fullName}`;
|
|
16
|
+
currentRepoIds.add(repoId);
|
|
17
|
+
const repoIdentifier = {
|
|
18
|
+
id: repoId,
|
|
19
|
+
name: repo.name,
|
|
20
|
+
url: repo.url,
|
|
21
|
+
owner: repo.owner,
|
|
22
|
+
};
|
|
23
|
+
const previous = this.previousSnapshots.get(repoId);
|
|
24
|
+
if (!previous) {
|
|
25
|
+
// New repo detected
|
|
26
|
+
events.push(this.createRepoCreatedEvent(repo, repoIdentifier));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// Check for changes
|
|
30
|
+
if (this.hasPushActivity(repo, previous)) {
|
|
31
|
+
events.push(this.createRepoPushedEvent(repo, repoIdentifier));
|
|
32
|
+
}
|
|
33
|
+
if (this.topicsChanged(repo, previous)) {
|
|
34
|
+
events.push(this.createTopicsChangedEvent(repo, previous, repoIdentifier));
|
|
35
|
+
}
|
|
36
|
+
if (repo.description !== previous.description) {
|
|
37
|
+
events.push(this.createDescriptionChangedEvent(repo, previous, repoIdentifier));
|
|
38
|
+
}
|
|
39
|
+
if (repo.isArchived && !previous.isArchived) {
|
|
40
|
+
events.push(this.createArchivedEvent(repo, repoIdentifier));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Update snapshot
|
|
44
|
+
this.previousSnapshots.set(repoId, {
|
|
45
|
+
id: repoId,
|
|
46
|
+
description: repo.description,
|
|
47
|
+
topics: [...repo.topics],
|
|
48
|
+
isArchived: repo.isArchived,
|
|
49
|
+
pushedAt: repo.pushedAt,
|
|
50
|
+
updatedAt: repo.updatedAt,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return events;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create enrichment event from enrichment data
|
|
57
|
+
*/
|
|
58
|
+
createEnrichmentEvent(repo, enrichment) {
|
|
59
|
+
return {
|
|
60
|
+
eventId: randomUUID(),
|
|
61
|
+
eventType: 'repo.enriched',
|
|
62
|
+
timestamp: new Date(),
|
|
63
|
+
source: 'github',
|
|
64
|
+
repo: {
|
|
65
|
+
id: `github:${repo.fullName}`,
|
|
66
|
+
name: repo.name,
|
|
67
|
+
url: repo.url,
|
|
68
|
+
owner: repo.owner,
|
|
69
|
+
},
|
|
70
|
+
payload: {
|
|
71
|
+
languages: enrichment.languages,
|
|
72
|
+
readme: enrichment.readme,
|
|
73
|
+
packageFiles: enrichment.packageFiles,
|
|
74
|
+
defaultBranch: repo.defaultBranch,
|
|
75
|
+
starCount: repo.starCount,
|
|
76
|
+
forkCount: repo.forkCount,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Load previous snapshots (for persistence across restarts)
|
|
82
|
+
*/
|
|
83
|
+
loadSnapshots(snapshots) {
|
|
84
|
+
this.previousSnapshots.clear();
|
|
85
|
+
for (const snapshot of snapshots) {
|
|
86
|
+
this.previousSnapshots.set(snapshot.id, snapshot);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Export current snapshots for persistence
|
|
91
|
+
*/
|
|
92
|
+
exportSnapshots() {
|
|
93
|
+
return Array.from(this.previousSnapshots.values());
|
|
94
|
+
}
|
|
95
|
+
hasPushActivity(current, previous) {
|
|
96
|
+
if (!current.pushedAt || !previous.pushedAt)
|
|
97
|
+
return false;
|
|
98
|
+
return current.pushedAt.getTime() > previous.pushedAt.getTime();
|
|
99
|
+
}
|
|
100
|
+
topicsChanged(current, previous) {
|
|
101
|
+
if (current.topics.length !== previous.topics.length)
|
|
102
|
+
return true;
|
|
103
|
+
const sortedCurrent = [...current.topics].sort();
|
|
104
|
+
const sortedPrevious = [...previous.topics].sort();
|
|
105
|
+
return sortedCurrent.some((topic, i) => topic !== sortedPrevious[i]);
|
|
106
|
+
}
|
|
107
|
+
createRepoCreatedEvent(repo, repoIdentifier) {
|
|
108
|
+
return {
|
|
109
|
+
eventId: randomUUID(),
|
|
110
|
+
eventType: 'repo.created',
|
|
111
|
+
timestamp: repo.createdAt,
|
|
112
|
+
source: 'github',
|
|
113
|
+
repo: repoIdentifier,
|
|
114
|
+
payload: {
|
|
115
|
+
description: repo.description,
|
|
116
|
+
topics: repo.topics,
|
|
117
|
+
isPrivate: repo.isPrivate,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
createRepoPushedEvent(repo, repoIdentifier) {
|
|
122
|
+
return {
|
|
123
|
+
eventId: randomUUID(),
|
|
124
|
+
eventType: 'repo.pushed',
|
|
125
|
+
timestamp: repo.pushedAt ?? new Date(),
|
|
126
|
+
source: 'github',
|
|
127
|
+
repo: repoIdentifier,
|
|
128
|
+
payload: {
|
|
129
|
+
commitCount: 1, // Approximate, would need commit API for exact count
|
|
130
|
+
branch: repo.defaultBranch,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
createTopicsChangedEvent(repo, previous, repoIdentifier) {
|
|
135
|
+
return {
|
|
136
|
+
eventId: randomUUID(),
|
|
137
|
+
eventType: 'repo.topics_changed',
|
|
138
|
+
timestamp: new Date(),
|
|
139
|
+
source: 'github',
|
|
140
|
+
repo: repoIdentifier,
|
|
141
|
+
payload: {
|
|
142
|
+
topics: repo.topics,
|
|
143
|
+
previousTopics: previous.topics,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
createDescriptionChangedEvent(repo, previous, repoIdentifier) {
|
|
148
|
+
return {
|
|
149
|
+
eventId: randomUUID(),
|
|
150
|
+
eventType: 'repo.description_changed',
|
|
151
|
+
timestamp: new Date(),
|
|
152
|
+
source: 'github',
|
|
153
|
+
repo: repoIdentifier,
|
|
154
|
+
payload: {
|
|
155
|
+
description: repo.description,
|
|
156
|
+
previousDescription: previous.description,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
createArchivedEvent(repo, repoIdentifier) {
|
|
161
|
+
return {
|
|
162
|
+
eventId: randomUUID(),
|
|
163
|
+
eventType: 'repo.archived',
|
|
164
|
+
timestamp: new Date(),
|
|
165
|
+
source: 'github',
|
|
166
|
+
repo: repoIdentifier,
|
|
167
|
+
payload: {
|
|
168
|
+
archivedAt: new Date(),
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=normalizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizer.js","sourceRoot":"","sources":["../../src/ingestion/normalizer.ts"],"names":[],"mappings":"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"}
|