@adversarylabs/sdk 0.1.15 → 0.1.17

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.
@@ -0,0 +1,272 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { DatabaseSync } from "node:sqlite";
4
+ export const ADVERSARY_REPO_GRAPH_ENV = "ADVERSARY_REPO_GRAPH";
5
+ export const REPO_GRAPH_SCHEMA_VERSION = "v2";
6
+ export const REPO_GRAPH_ADAPTER_REVISION = "go-ast-v1+ts-syntax-v1";
7
+ export class RepoGraphUnavailableError extends Error {
8
+ code = "repo_graph_unavailable";
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "RepoGraphUnavailableError";
12
+ }
13
+ }
14
+ export async function openRepoGraph(dir) {
15
+ const raw = await readFile(join(dir, "meta.json"), "utf8");
16
+ const meta = JSON.parse(raw);
17
+ if (meta.schemaVersion !== REPO_GRAPH_SCHEMA_VERSION ||
18
+ meta.adapterRevision !== REPO_GRAPH_ADAPTER_REVISION) {
19
+ throw new RepoGraphUnavailableError(`unsupported repo-graph schema ${meta.schemaVersion}/${meta.adapterRevision}`);
20
+ }
21
+ const database = new DatabaseSync(join(dir, "graph.sqlite"), { readOnly: true });
22
+ return new SQLiteRepoGraph(dir, meta, database);
23
+ }
24
+ export async function repoGraphFromEnvironment(env = process.env) {
25
+ const dir = env[ADVERSARY_REPO_GRAPH_ENV]?.trim();
26
+ if (!dir)
27
+ return null;
28
+ try {
29
+ return await openRepoGraph(dir);
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ class SQLiteRepoGraph {
36
+ dir;
37
+ meta;
38
+ database;
39
+ constructor(dir, meta, database) {
40
+ this.dir = dir;
41
+ this.meta = meta;
42
+ this.database = database;
43
+ }
44
+ files(query = {}) {
45
+ const { limit, cursor } = bounds(query.limit, query.cursor);
46
+ const glob = query.glob === undefined ? "" : globToLike(query.glob);
47
+ const rows = this.database
48
+ .prepare(`SELECT id,path,language,size,hash,module FROM files
49
+ WHERE id > ? AND (? = '' OR language = ?) AND (? = '' OR path LIKE ? ESCAPE '\\')
50
+ ORDER BY id LIMIT ?`)
51
+ .all(cursor, query.language ?? "", query.language ?? "", glob, glob, limit + 1);
52
+ return page(rows.map(fileRow), limit, (item) => item.id);
53
+ }
54
+ symbolAt(path, line, column = 0) {
55
+ validPath(path);
56
+ if (!Number.isInteger(line) || line < 1 || !Number.isInteger(column) || column < 0) {
57
+ throw new Error("line must be positive and column non-negative");
58
+ }
59
+ const row = this.database
60
+ .prepare(`${symbolSelect}
61
+ WHERE f.path=? AND (s.start_line < ? OR (s.start_line=? AND s.start_col<=?))
62
+ AND (s.end_line > ? OR (s.end_line=? AND s.end_col>=?))
63
+ ORDER BY (s.end_line-s.start_line) ASC, s.id ASC LIMIT 1`)
64
+ .get(normalizePath(path), line, line, column, line, line, column);
65
+ return row === undefined ? undefined : symbolRow(row);
66
+ }
67
+ symbols(query = {}) {
68
+ if (query.path !== undefined)
69
+ validPath(query.path);
70
+ const { limit, cursor } = bounds(query.limit, query.cursor);
71
+ const rows = this.database
72
+ .prepare(`${symbolSelect}
73
+ WHERE s.id>? AND (?='' OR f.path=?) AND (?='' OR s.name=?) AND (?='' OR s.kind=?)
74
+ ORDER BY s.id LIMIT ?`)
75
+ .all(cursor, query.path ?? "", normalizePath(query.path ?? ""), query.name ?? "", query.name ?? "", query.kind ?? "", query.kind ?? "", limit + 1);
76
+ return page(rows.map(symbolRow), limit, (item) => item.id);
77
+ }
78
+ definitions(query) {
79
+ return this.symbols(query);
80
+ }
81
+ references(query) {
82
+ return this.relations(query, "references", false);
83
+ }
84
+ callers(query) {
85
+ return this.relations(query, "calls", false);
86
+ }
87
+ callees(query) {
88
+ return this.relations(query, "calls", true);
89
+ }
90
+ implementations(query) {
91
+ return this.relations(query, "implements", false);
92
+ }
93
+ importsOf(path, cursor, limit) {
94
+ return this.fileRelations(path, cursor, limit, true);
95
+ }
96
+ importersOf(path, cursor, limit) {
97
+ return this.fileRelations(path, cursor, limit, false);
98
+ }
99
+ relatedTests(options) {
100
+ if (options.path !== undefined)
101
+ validPath(options.path);
102
+ const symbolId = options.symbolId ?? 0;
103
+ if (!Number.isInteger(symbolId) || symbolId < 0)
104
+ throw new Error("symbolId must be non-negative");
105
+ const { limit, cursor } = bounds(options.limit, options.cursor);
106
+ const rows = this.database
107
+ .prepare(`SELECT tl.id,sf.path AS source_path,
108
+ tl.source_symbol_id,tf.path AS test_path,tl.test_symbol_id,tl.confidence,tl.reason
109
+ FROM test_links tl JOIN files sf ON sf.id=tl.source_file_id
110
+ JOIN files tf ON tf.id=tl.test_file_id
111
+ WHERE tl.id>? AND (?='' OR sf.path=?)
112
+ AND (?=0 OR tl.source_symbol_id=? OR sf.id=(SELECT file_id FROM symbols WHERE id=?))
113
+ ORDER BY tl.id LIMIT ?`)
114
+ .all(cursor, options.path ?? "", normalizePath(options.path ?? ""), symbolId, symbolId, symbolId, limit + 1);
115
+ return testLinkPage(rows.map(testLinkRow), limit);
116
+ }
117
+ close() {
118
+ this.database.close();
119
+ }
120
+ relations(query, kind, outgoing) {
121
+ if (!Number.isInteger(query.symbolId) || query.symbolId < 1) {
122
+ throw new Error("symbolId must be positive");
123
+ }
124
+ const { limit, cursor } = bounds(query.limit, query.cursor);
125
+ const column = outgoing ? "from_symbol_id" : "to_symbol_id";
126
+ const rows = this.database
127
+ .prepare(`${edgeSelect}
128
+ WHERE e.id>? AND e.kind=? AND e.${column}=? ORDER BY e.id LIMIT ?`)
129
+ .all(cursor, kind, query.symbolId, limit + 1);
130
+ return page(rows.map(edgeRow), limit, (item) => item.id);
131
+ }
132
+ fileRelations(path, cursorValue, limitValue, outgoing) {
133
+ validPath(path);
134
+ const { limit, cursor } = bounds(limitValue, cursorValue);
135
+ const condition = outgoing ? "ff.path=?" : "tf.module=(SELECT module FROM files WHERE path=?)";
136
+ const rows = this.database
137
+ .prepare(`${edgeSelect}
138
+ WHERE e.id>? AND e.kind='imports' AND ${condition} ORDER BY e.id LIMIT ?`)
139
+ .all(cursor, normalizePath(path), limit + 1);
140
+ return page(rows.map(edgeRow), limit, (item) => item.id);
141
+ }
142
+ }
143
+ const symbolSelect = `SELECT s.id,s.name,s.kind,f.path,s.start_line,s.start_col,
144
+ s.end_line,s.end_col,s.container_id,s.exported,f.language,s.adapter_data
145
+ FROM symbols s JOIN files f ON f.id=s.file_id`;
146
+ const edgeSelect = `SELECT e.id,ff.path AS from_path,e.from_symbol_id,
147
+ COALESCE(tf.path,'') AS to_path,e.to_symbol_id,
148
+ COALESCE(e.unresolved_target,'') AS unresolved_target,e.kind,e.line,e.column,
149
+ e.confidence,e.adapter FROM edges e JOIN files ff ON ff.id=e.from_file_id
150
+ LEFT JOIN files tf ON tf.id=e.to_file_id`;
151
+ function fileRow(row) {
152
+ return {
153
+ id: number(row.id),
154
+ path: text(row.path),
155
+ language: text(row.language),
156
+ size: number(row.size),
157
+ hash: text(row.hash),
158
+ ...(text(row.module) === "" ? {} : { module: text(row.module) }),
159
+ };
160
+ }
161
+ function symbolRow(row) {
162
+ return {
163
+ id: number(row.id),
164
+ name: text(row.name),
165
+ kind: text(row.kind),
166
+ path: text(row.path),
167
+ startLine: number(row.start_line),
168
+ startColumn: number(row.start_col),
169
+ endLine: number(row.end_line),
170
+ endColumn: number(row.end_col),
171
+ ...(row.container_id === null ? {} : { containerId: number(row.container_id) }),
172
+ exported: number(row.exported) !== 0,
173
+ language: text(row.language),
174
+ ...(text(row.adapter_data) === "" ? {} : { metadata: text(row.adapter_data) }),
175
+ };
176
+ }
177
+ function edgeRow(row) {
178
+ return {
179
+ id: number(row.id),
180
+ fromPath: text(row.from_path),
181
+ ...(row.from_symbol_id === null ? {} : { fromSymbolId: number(row.from_symbol_id) }),
182
+ ...(text(row.to_path) === "" ? {} : { toPath: text(row.to_path) }),
183
+ ...(row.to_symbol_id === null ? {} : { toSymbolId: number(row.to_symbol_id) }),
184
+ ...(text(row.unresolved_target) === ""
185
+ ? {}
186
+ : { unresolvedTarget: text(row.unresolved_target) }),
187
+ kind: text(row.kind),
188
+ line: number(row.line),
189
+ column: number(row.column),
190
+ confidence: number(row.confidence),
191
+ adapter: text(row.adapter),
192
+ };
193
+ }
194
+ function testLinkRow(row) {
195
+ return {
196
+ id: number(row.id),
197
+ sourcePath: text(row.source_path),
198
+ ...(row.source_symbol_id === null ? {} : { sourceSymbolId: number(row.source_symbol_id) }),
199
+ testPath: text(row.test_path),
200
+ ...(row.test_symbol_id === null ? {} : { testSymbolId: number(row.test_symbol_id) }),
201
+ confidence: number(row.confidence),
202
+ reason: text(row.reason),
203
+ };
204
+ }
205
+ function page(items, limit, id) {
206
+ const hasMore = items.length > limit;
207
+ const bounded = hasMore ? items.slice(0, limit) : items;
208
+ const nextCursor = hasMore ? String(id(bounded[bounded.length - 1])) : undefined;
209
+ return {
210
+ items: bounded,
211
+ ...(nextCursor === undefined ? {} : { nextCursor }),
212
+ };
213
+ }
214
+ function testLinkPage(items, limit) {
215
+ const bounded = page(items, limit, (item) => item.id);
216
+ return {
217
+ items: bounded.items.map(({ id: _id, ...item }) => item),
218
+ ...(bounded.nextCursor === undefined ? {} : { nextCursor: bounded.nextCursor }),
219
+ };
220
+ }
221
+ function bounds(limitValue, cursorValue) {
222
+ const limit = limitValue ?? 100;
223
+ if (!Number.isInteger(limit) || limit < 1 || limit > 500) {
224
+ throw new Error("limit must be an integer from 1 through 500");
225
+ }
226
+ const cursor = cursorValue === undefined || cursorValue === "" ? 0 : Number(cursorValue);
227
+ if (!Number.isSafeInteger(cursor) || cursor < 0) {
228
+ throw new Error("cursor must be a non-negative integer");
229
+ }
230
+ return { limit, cursor };
231
+ }
232
+ function validPath(path) {
233
+ const normalized = normalizePath(path);
234
+ if (normalized === "" ||
235
+ normalized.startsWith("/") ||
236
+ normalized === ".." ||
237
+ normalized.startsWith("../") ||
238
+ normalized.includes("/../") ||
239
+ normalized.includes("\0") ||
240
+ normalized.includes("//")) {
241
+ throw new Error("path must be normalized and repository-relative");
242
+ }
243
+ }
244
+ function normalizePath(path) {
245
+ return path.replaceAll("\\", "/").replace(/^\.\//, "");
246
+ }
247
+ function globToLike(glob) {
248
+ if (glob.includes("..") || glob.startsWith("/") || glob.includes("\0")) {
249
+ throw new Error("glob must be repository-relative");
250
+ }
251
+ return normalizePath(glob)
252
+ .replaceAll("\\", "\\\\")
253
+ .replaceAll("%", "\\%")
254
+ .replaceAll("_", "\\_")
255
+ .replaceAll("*", "%")
256
+ .replaceAll("?", "_");
257
+ }
258
+ function text(value) {
259
+ if (typeof value === "string")
260
+ return value;
261
+ if (value === null || value === undefined)
262
+ return "";
263
+ throw new Error("repo graph returned a non-string value");
264
+ }
265
+ function number(value) {
266
+ if (typeof value === "number")
267
+ return value;
268
+ if (typeof value === "bigint")
269
+ return Number(value);
270
+ throw new Error("repo graph returned a non-number value");
271
+ }
272
+ //# sourceMappingURL=repo-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-graph.js","sourceRoot":"","sources":["../src/repo-graph.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { DatabaseSync } from \"node:sqlite\";\n\nexport const ADVERSARY_REPO_GRAPH_ENV = \"ADVERSARY_REPO_GRAPH\";\nexport const REPO_GRAPH_SCHEMA_VERSION = \"v2\";\nexport const REPO_GRAPH_ADAPTER_REVISION = \"go-ast-v1+ts-syntax-v1\";\n\nexport interface RepoGraphMeta {\n schemaVersion: string;\n adapterRevision: string;\n fingerprint: string;\n repoPath: string;\n builtAt: string;\n durationMs: number;\n fileCount: number;\n symbolCount: number;\n edgeCount: number;\n testLinkCount: number;\n parseFailures?: readonly RepoGraphDiagnostic[];\n}\n\nexport interface RepoGraphDiagnostic {\n path: string;\n adapter: string;\n message: string;\n}\n\nexport interface RepoGraphFile {\n id: number;\n path: string;\n language: string;\n size: number;\n hash: string;\n module?: string;\n}\n\nexport interface RepoGraphSymbol {\n id: number;\n name: string;\n kind: string;\n path: string;\n startLine: number;\n startColumn: number;\n endLine: number;\n endColumn: number;\n containerId?: number;\n exported: boolean;\n language: string;\n metadata?: string;\n}\n\nexport interface RepoGraphEdge {\n id: number;\n fromPath: string;\n fromSymbolId?: number;\n toPath?: string;\n toSymbolId?: number;\n unresolvedTarget?: string;\n kind: string;\n line: number;\n column: number;\n confidence: number;\n adapter: string;\n}\n\nexport interface RepoGraphTestLink {\n sourcePath: string;\n sourceSymbolId?: number;\n testPath: string;\n testSymbolId?: number;\n confidence: number;\n reason: string;\n}\n\nexport interface RepoGraphPage<T> {\n items: readonly T[];\n nextCursor?: string;\n}\n\nexport interface RepoGraphFileQuery {\n language?: string;\n glob?: string;\n cursor?: string;\n limit?: number;\n}\n\nexport interface RepoGraphSymbolQuery {\n path?: string;\n name?: string;\n kind?: string;\n cursor?: string;\n limit?: number;\n}\n\nexport interface RepoGraphRelationQuery {\n symbolId: number;\n cursor?: string;\n limit?: number;\n}\n\nexport interface RepoGraph {\n readonly dir: string;\n readonly meta: RepoGraphMeta;\n files(query?: RepoGraphFileQuery): RepoGraphPage<RepoGraphFile>;\n symbolAt(path: string, line: number, column?: number): RepoGraphSymbol | undefined;\n symbols(query?: RepoGraphSymbolQuery): RepoGraphPage<RepoGraphSymbol>;\n definitions(query: RepoGraphSymbolQuery): RepoGraphPage<RepoGraphSymbol>;\n references(query: RepoGraphRelationQuery): RepoGraphPage<RepoGraphEdge>;\n callers(query: RepoGraphRelationQuery): RepoGraphPage<RepoGraphEdge>;\n callees(query: RepoGraphRelationQuery): RepoGraphPage<RepoGraphEdge>;\n implementations(query: RepoGraphRelationQuery): RepoGraphPage<RepoGraphEdge>;\n importsOf(path: string, cursor?: string, limit?: number): RepoGraphPage<RepoGraphEdge>;\n importersOf(path: string, cursor?: string, limit?: number): RepoGraphPage<RepoGraphEdge>;\n relatedTests(options: {\n path?: string;\n symbolId?: number;\n cursor?: string;\n limit?: number;\n }): RepoGraphPage<RepoGraphTestLink>;\n close(): void;\n}\n\nexport class RepoGraphUnavailableError extends Error {\n readonly code = \"repo_graph_unavailable\";\n\n constructor(message: string) {\n super(message);\n this.name = \"RepoGraphUnavailableError\";\n }\n}\n\nexport async function openRepoGraph(dir: string): Promise<RepoGraph> {\n const raw = await readFile(join(dir, \"meta.json\"), \"utf8\");\n const meta = JSON.parse(raw) as RepoGraphMeta;\n if (\n meta.schemaVersion !== REPO_GRAPH_SCHEMA_VERSION ||\n meta.adapterRevision !== REPO_GRAPH_ADAPTER_REVISION\n ) {\n throw new RepoGraphUnavailableError(\n `unsupported repo-graph schema ${meta.schemaVersion}/${meta.adapterRevision}`,\n );\n }\n const database = new DatabaseSync(join(dir, \"graph.sqlite\"), { readOnly: true });\n return new SQLiteRepoGraph(dir, meta, database);\n}\n\nexport async function repoGraphFromEnvironment(\n env: Record<string, string | undefined> = process.env,\n): Promise<RepoGraph | null> {\n const dir = env[ADVERSARY_REPO_GRAPH_ENV]?.trim();\n if (!dir) return null;\n try {\n return await openRepoGraph(dir);\n } catch {\n return null;\n }\n}\n\nclass SQLiteRepoGraph implements RepoGraph {\n constructor(\n readonly dir: string,\n readonly meta: RepoGraphMeta,\n private readonly database: DatabaseSync,\n ) {}\n\n files(query: RepoGraphFileQuery = {}): RepoGraphPage<RepoGraphFile> {\n const { limit, cursor } = bounds(query.limit, query.cursor);\n const glob = query.glob === undefined ? \"\" : globToLike(query.glob);\n const rows = this.database\n .prepare(`SELECT id,path,language,size,hash,module FROM files\n WHERE id > ? AND (? = '' OR language = ?) AND (? = '' OR path LIKE ? ESCAPE '\\\\')\n ORDER BY id LIMIT ?`)\n .all(cursor, query.language ?? \"\", query.language ?? \"\", glob, glob, limit + 1);\n return page(rows.map(fileRow), limit, (item) => item.id);\n }\n\n symbolAt(path: string, line: number, column = 0): RepoGraphSymbol | undefined {\n validPath(path);\n if (!Number.isInteger(line) || line < 1 || !Number.isInteger(column) || column < 0) {\n throw new Error(\"line must be positive and column non-negative\");\n }\n const row = this.database\n .prepare(`${symbolSelect}\n WHERE f.path=? AND (s.start_line < ? OR (s.start_line=? AND s.start_col<=?))\n AND (s.end_line > ? OR (s.end_line=? AND s.end_col>=?))\n ORDER BY (s.end_line-s.start_line) ASC, s.id ASC LIMIT 1`)\n .get(normalizePath(path), line, line, column, line, line, column);\n return row === undefined ? undefined : symbolRow(row);\n }\n\n symbols(query: RepoGraphSymbolQuery = {}): RepoGraphPage<RepoGraphSymbol> {\n if (query.path !== undefined) validPath(query.path);\n const { limit, cursor } = bounds(query.limit, query.cursor);\n const rows = this.database\n .prepare(`${symbolSelect}\n WHERE s.id>? AND (?='' OR f.path=?) AND (?='' OR s.name=?) AND (?='' OR s.kind=?)\n ORDER BY s.id LIMIT ?`)\n .all(\n cursor,\n query.path ?? \"\",\n normalizePath(query.path ?? \"\"),\n query.name ?? \"\",\n query.name ?? \"\",\n query.kind ?? \"\",\n query.kind ?? \"\",\n limit + 1,\n );\n return page(rows.map(symbolRow), limit, (item) => item.id);\n }\n\n definitions(query: RepoGraphSymbolQuery): RepoGraphPage<RepoGraphSymbol> {\n return this.symbols(query);\n }\n\n references(query: RepoGraphRelationQuery): RepoGraphPage<RepoGraphEdge> {\n return this.relations(query, \"references\", false);\n }\n\n callers(query: RepoGraphRelationQuery): RepoGraphPage<RepoGraphEdge> {\n return this.relations(query, \"calls\", false);\n }\n\n callees(query: RepoGraphRelationQuery): RepoGraphPage<RepoGraphEdge> {\n return this.relations(query, \"calls\", true);\n }\n\n implementations(query: RepoGraphRelationQuery): RepoGraphPage<RepoGraphEdge> {\n return this.relations(query, \"implements\", false);\n }\n\n importsOf(path: string, cursor?: string, limit?: number): RepoGraphPage<RepoGraphEdge> {\n return this.fileRelations(path, cursor, limit, true);\n }\n\n importersOf(path: string, cursor?: string, limit?: number): RepoGraphPage<RepoGraphEdge> {\n return this.fileRelations(path, cursor, limit, false);\n }\n\n relatedTests(options: {\n path?: string;\n symbolId?: number;\n cursor?: string;\n limit?: number;\n }): RepoGraphPage<RepoGraphTestLink> {\n if (options.path !== undefined) validPath(options.path);\n const symbolId = options.symbolId ?? 0;\n if (!Number.isInteger(symbolId) || symbolId < 0)\n throw new Error(\"symbolId must be non-negative\");\n const { limit, cursor } = bounds(options.limit, options.cursor);\n const rows = this.database\n .prepare(`SELECT tl.id,sf.path AS source_path,\n tl.source_symbol_id,tf.path AS test_path,tl.test_symbol_id,tl.confidence,tl.reason\n FROM test_links tl JOIN files sf ON sf.id=tl.source_file_id\n JOIN files tf ON tf.id=tl.test_file_id\n WHERE tl.id>? AND (?='' OR sf.path=?)\n AND (?=0 OR tl.source_symbol_id=? OR sf.id=(SELECT file_id FROM symbols WHERE id=?))\n ORDER BY tl.id LIMIT ?`)\n .all(\n cursor,\n options.path ?? \"\",\n normalizePath(options.path ?? \"\"),\n symbolId,\n symbolId,\n symbolId,\n limit + 1,\n );\n return testLinkPage(rows.map(testLinkRow), limit);\n }\n\n close(): void {\n this.database.close();\n }\n\n private relations(\n query: RepoGraphRelationQuery,\n kind: string,\n outgoing: boolean,\n ): RepoGraphPage<RepoGraphEdge> {\n if (!Number.isInteger(query.symbolId) || query.symbolId < 1) {\n throw new Error(\"symbolId must be positive\");\n }\n const { limit, cursor } = bounds(query.limit, query.cursor);\n const column = outgoing ? \"from_symbol_id\" : \"to_symbol_id\";\n const rows = this.database\n .prepare(`${edgeSelect}\n WHERE e.id>? AND e.kind=? AND e.${column}=? ORDER BY e.id LIMIT ?`)\n .all(cursor, kind, query.symbolId, limit + 1);\n return page(rows.map(edgeRow), limit, (item) => item.id);\n }\n\n private fileRelations(\n path: string,\n cursorValue: string | undefined,\n limitValue: number | undefined,\n outgoing: boolean,\n ): RepoGraphPage<RepoGraphEdge> {\n validPath(path);\n const { limit, cursor } = bounds(limitValue, cursorValue);\n const condition = outgoing ? \"ff.path=?\" : \"tf.module=(SELECT module FROM files WHERE path=?)\";\n const rows = this.database\n .prepare(`${edgeSelect}\n WHERE e.id>? AND e.kind='imports' AND ${condition} ORDER BY e.id LIMIT ?`)\n .all(cursor, normalizePath(path), limit + 1);\n return page(rows.map(edgeRow), limit, (item) => item.id);\n }\n}\n\nconst symbolSelect = `SELECT s.id,s.name,s.kind,f.path,s.start_line,s.start_col,\n s.end_line,s.end_col,s.container_id,s.exported,f.language,s.adapter_data\n FROM symbols s JOIN files f ON f.id=s.file_id`;\n\nconst edgeSelect = `SELECT e.id,ff.path AS from_path,e.from_symbol_id,\n COALESCE(tf.path,'') AS to_path,e.to_symbol_id,\n COALESCE(e.unresolved_target,'') AS unresolved_target,e.kind,e.line,e.column,\n e.confidence,e.adapter FROM edges e JOIN files ff ON ff.id=e.from_file_id\n LEFT JOIN files tf ON tf.id=e.to_file_id`;\n\ninterface RowRecord {\n [key: string]: unknown;\n}\n\nfunction fileRow(row: RowRecord): RepoGraphFile {\n return {\n id: number(row.id),\n path: text(row.path),\n language: text(row.language),\n size: number(row.size),\n hash: text(row.hash),\n ...(text(row.module) === \"\" ? {} : { module: text(row.module) }),\n };\n}\n\nfunction symbolRow(row: RowRecord): RepoGraphSymbol {\n return {\n id: number(row.id),\n name: text(row.name),\n kind: text(row.kind),\n path: text(row.path),\n startLine: number(row.start_line),\n startColumn: number(row.start_col),\n endLine: number(row.end_line),\n endColumn: number(row.end_col),\n ...(row.container_id === null ? {} : { containerId: number(row.container_id) }),\n exported: number(row.exported) !== 0,\n language: text(row.language),\n ...(text(row.adapter_data) === \"\" ? {} : { metadata: text(row.adapter_data) }),\n };\n}\n\nfunction edgeRow(row: RowRecord): RepoGraphEdge {\n return {\n id: number(row.id),\n fromPath: text(row.from_path),\n ...(row.from_symbol_id === null ? {} : { fromSymbolId: number(row.from_symbol_id) }),\n ...(text(row.to_path) === \"\" ? {} : { toPath: text(row.to_path) }),\n ...(row.to_symbol_id === null ? {} : { toSymbolId: number(row.to_symbol_id) }),\n ...(text(row.unresolved_target) === \"\"\n ? {}\n : { unresolvedTarget: text(row.unresolved_target) }),\n kind: text(row.kind),\n line: number(row.line),\n column: number(row.column),\n confidence: number(row.confidence),\n adapter: text(row.adapter),\n };\n}\n\ninterface TestLinkWithID extends RepoGraphTestLink {\n id: number;\n}\n\nfunction testLinkRow(row: RowRecord): TestLinkWithID {\n return {\n id: number(row.id),\n sourcePath: text(row.source_path),\n ...(row.source_symbol_id === null ? {} : { sourceSymbolId: number(row.source_symbol_id) }),\n testPath: text(row.test_path),\n ...(row.test_symbol_id === null ? {} : { testSymbolId: number(row.test_symbol_id) }),\n confidence: number(row.confidence),\n reason: text(row.reason),\n };\n}\n\nfunction page<T>(items: T[], limit: number, id: (item: T) => number): RepoGraphPage<T> {\n const hasMore = items.length > limit;\n const bounded = hasMore ? items.slice(0, limit) : items;\n const nextCursor = hasMore ? String(id(bounded[bounded.length - 1] as T)) : undefined;\n return {\n items: bounded,\n ...(nextCursor === undefined ? {} : { nextCursor }),\n };\n}\n\nfunction testLinkPage(items: TestLinkWithID[], limit: number): RepoGraphPage<RepoGraphTestLink> {\n const bounded = page(items, limit, (item) => item.id);\n return {\n items: bounded.items.map(({ id: _id, ...item }) => item),\n ...(bounded.nextCursor === undefined ? {} : { nextCursor: bounded.nextCursor }),\n };\n}\n\nfunction bounds(limitValue?: number, cursorValue?: string): { limit: number; cursor: number } {\n const limit = limitValue ?? 100;\n if (!Number.isInteger(limit) || limit < 1 || limit > 500) {\n throw new Error(\"limit must be an integer from 1 through 500\");\n }\n const cursor = cursorValue === undefined || cursorValue === \"\" ? 0 : Number(cursorValue);\n if (!Number.isSafeInteger(cursor) || cursor < 0) {\n throw new Error(\"cursor must be a non-negative integer\");\n }\n return { limit, cursor };\n}\n\nfunction validPath(path: string): void {\n const normalized = normalizePath(path);\n if (\n normalized === \"\" ||\n normalized.startsWith(\"/\") ||\n normalized === \"..\" ||\n normalized.startsWith(\"../\") ||\n normalized.includes(\"/../\") ||\n normalized.includes(\"\\0\") ||\n normalized.includes(\"//\")\n ) {\n throw new Error(\"path must be normalized and repository-relative\");\n }\n}\n\nfunction normalizePath(path: string): string {\n return path.replaceAll(\"\\\\\", \"/\").replace(/^\\.\\//, \"\");\n}\n\nfunction globToLike(glob: string): string {\n if (glob.includes(\"..\") || glob.startsWith(\"/\") || glob.includes(\"\\0\")) {\n throw new Error(\"glob must be repository-relative\");\n }\n return normalizePath(glob)\n .replaceAll(\"\\\\\", \"\\\\\\\\\")\n .replaceAll(\"%\", \"\\\\%\")\n .replaceAll(\"_\", \"\\\\_\")\n .replaceAll(\"*\", \"%\")\n .replaceAll(\"?\", \"_\");\n}\n\nfunction text(value: unknown): string {\n if (typeof value === \"string\") return value;\n if (value === null || value === undefined) return \"\";\n throw new Error(\"repo graph returned a non-string value\");\n}\n\nfunction number(value: unknown): number {\n if (typeof value === \"number\") return value;\n if (typeof value === \"bigint\") return Number(value);\n throw new Error(\"repo graph returned a non-number value\");\n}\n"]}
@@ -0,0 +1,54 @@
1
+ /** Environment variable set by the CLI pointing at an on-disk repo index directory. */
2
+ export declare const ADVERSARY_REPO_INDEX_ENV = "ADVERSARY_REPO_INDEX";
3
+ export declare const REPO_INDEX_SCHEMA_VERSION = "v1";
4
+ export interface RepoIndexMeta {
5
+ schemaVersion: string;
6
+ fingerprint: string;
7
+ repoPath: string;
8
+ builtAt?: string;
9
+ fileCount: number;
10
+ edgeCount: number;
11
+ }
12
+ export interface RepoIndexFile {
13
+ path: string;
14
+ language: string;
15
+ size: number;
16
+ hash: string;
17
+ }
18
+ export interface RepoIndexEdge {
19
+ from: string;
20
+ to: string;
21
+ kind: string;
22
+ }
23
+ export interface ListFilesOptions {
24
+ /** Exact language match: go | typescript | javascript */
25
+ language?: string;
26
+ /** Maximum files to return (default 5000). */
27
+ limit?: number;
28
+ }
29
+ /**
30
+ * Read-only navigation over a CLI-built repository index (files + import edges).
31
+ */
32
+ export interface RepoIndex {
33
+ readonly dir: string;
34
+ readonly meta: RepoIndexMeta;
35
+ listFiles(options?: ListFilesOptions): Promise<RepoIndexFile[]>;
36
+ file(path: string): Promise<RepoIndexFile | undefined>;
37
+ importsOf(path: string): Promise<RepoIndexEdge[]>;
38
+ importersOf(path: string): Promise<RepoIndexEdge[]>;
39
+ }
40
+ export declare class RepoIndexUnavailableError extends Error {
41
+ readonly code = "repo_index_unavailable";
42
+ constructor(message: string);
43
+ }
44
+ /** Open an index directory written by the adversary CLI (JSONL schema v1). */
45
+ export declare function openRepoIndex(dir: string): Promise<RepoIndex>;
46
+ /**
47
+ * Load index from ADVERSARY_REPO_INDEX when set; otherwise return null.
48
+ *
49
+ * A missing, deleted, or malformed index is treated as unavailable (null), not a
50
+ * hard run failure — rules already null-check `ctx.repoIndex`. Explicit opens via
51
+ * `openRepoIndex` still throw `RepoIndexUnavailableError`.
52
+ */
53
+ export declare function repoIndexFromEnvironment(env?: Record<string, string | undefined>): Promise<RepoIndex | null>;
54
+ //# sourceMappingURL=repo-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-index.d.ts","sourceRoot":"","sources":["../src/repo-index.ts"],"names":[],"mappings":"AAIA,uFAAuF;AACvF,eAAO,MAAM,wBAAwB,yBAAyB,CAAC;AAE/D,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,SAAS,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAChE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IACvD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAClD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;CACrD;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,4BAA4B;gBAE7B,OAAO,EAAE,MAAM;CAI5B;AAED,8EAA8E;AAC9E,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAWnE;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAU3B"}
@@ -0,0 +1,120 @@
1
+ import { open, readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { createInterface } from "node:readline";
4
+ /** Environment variable set by the CLI pointing at an on-disk repo index directory. */
5
+ export const ADVERSARY_REPO_INDEX_ENV = "ADVERSARY_REPO_INDEX";
6
+ export const REPO_INDEX_SCHEMA_VERSION = "v1";
7
+ export class RepoIndexUnavailableError extends Error {
8
+ code = "repo_index_unavailable";
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "RepoIndexUnavailableError";
12
+ }
13
+ }
14
+ /** Open an index directory written by the adversary CLI (JSONL schema v1). */
15
+ export async function openRepoIndex(dir) {
16
+ const metaRaw = await readFile(join(dir, "meta.json"), "utf8");
17
+ const meta = JSON.parse(metaRaw);
18
+ if (meta.schemaVersion !== REPO_INDEX_SCHEMA_VERSION) {
19
+ throw new RepoIndexUnavailableError(`unsupported repo-index schema ${meta.schemaVersion} (want ${REPO_INDEX_SCHEMA_VERSION})`);
20
+ }
21
+ const files = await readJsonl(join(dir, "files.jsonl"));
22
+ const edges = await readJsonl(join(dir, "edges.jsonl"));
23
+ return new MemoryRepoIndex(dir, meta, files, edges);
24
+ }
25
+ /**
26
+ * Load index from ADVERSARY_REPO_INDEX when set; otherwise return null.
27
+ *
28
+ * A missing, deleted, or malformed index is treated as unavailable (null), not a
29
+ * hard run failure — rules already null-check `ctx.repoIndex`. Explicit opens via
30
+ * `openRepoIndex` still throw `RepoIndexUnavailableError`.
31
+ */
32
+ export async function repoIndexFromEnvironment(env = process.env) {
33
+ const dir = env[ADVERSARY_REPO_INDEX_ENV]?.trim();
34
+ if (!dir) {
35
+ return null;
36
+ }
37
+ try {
38
+ return await openRepoIndex(dir);
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ }
44
+ class MemoryRepoIndex {
45
+ dir;
46
+ meta;
47
+ files;
48
+ edges;
49
+ constructor(dir, meta, files, edges) {
50
+ this.dir = dir;
51
+ this.meta = meta;
52
+ this.files = files;
53
+ this.edges = edges;
54
+ }
55
+ async listFiles(options = {}) {
56
+ const limit = options.limit && options.limit > 0 ? options.limit : 5000;
57
+ const language = options.language?.trim();
58
+ const out = [];
59
+ for (const file of this.files) {
60
+ if (language && file.language !== language) {
61
+ continue;
62
+ }
63
+ out.push(file);
64
+ if (out.length >= limit) {
65
+ break;
66
+ }
67
+ }
68
+ return out;
69
+ }
70
+ async file(path) {
71
+ const normalized = normalizePath(path);
72
+ return this.files.find((file) => file.path === normalized);
73
+ }
74
+ async importsOf(path) {
75
+ const normalized = normalizePath(path);
76
+ return this.edges.filter((edge) => edge.from === normalized && edge.kind === "import");
77
+ }
78
+ async importersOf(path) {
79
+ const normalized = normalizePath(path);
80
+ const dir = dirOf(normalized);
81
+ return this.edges.filter((edge) => {
82
+ if (edge.kind !== "import") {
83
+ return false;
84
+ }
85
+ return edge.to === normalized || edge.to === dir;
86
+ });
87
+ }
88
+ }
89
+ function normalizePath(path) {
90
+ return path.replaceAll("\\", "/").replace(/^\.\//, "");
91
+ }
92
+ function dirOf(path) {
93
+ const idx = path.lastIndexOf("/");
94
+ if (idx <= 0) {
95
+ return "";
96
+ }
97
+ return path.slice(0, idx);
98
+ }
99
+ async function readJsonl(path) {
100
+ const handle = await open(path, "r");
101
+ try {
102
+ const rl = createInterface({
103
+ input: handle.createReadStream(),
104
+ crlfDelay: Number.POSITIVE_INFINITY,
105
+ });
106
+ const out = [];
107
+ for await (const line of rl) {
108
+ const trimmed = line.trim();
109
+ if (!trimmed) {
110
+ continue;
111
+ }
112
+ out.push(JSON.parse(trimmed));
113
+ }
114
+ return out;
115
+ }
116
+ finally {
117
+ await handle.close();
118
+ }
119
+ }
120
+ //# sourceMappingURL=repo-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-index.js","sourceRoot":"","sources":["../src/repo-index.ts"],"names":[],"mappings":"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","sourcesContent":["import { open, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { createInterface } from \"node:readline\";\n\n/** Environment variable set by the CLI pointing at an on-disk repo index directory. */\nexport const ADVERSARY_REPO_INDEX_ENV = \"ADVERSARY_REPO_INDEX\";\n\nexport const REPO_INDEX_SCHEMA_VERSION = \"v1\";\n\nexport interface RepoIndexMeta {\n schemaVersion: string;\n fingerprint: string;\n repoPath: string;\n builtAt?: string;\n fileCount: number;\n edgeCount: number;\n}\n\nexport interface RepoIndexFile {\n path: string;\n language: string;\n size: number;\n hash: string;\n}\n\nexport interface RepoIndexEdge {\n from: string;\n to: string;\n kind: string;\n}\n\nexport interface ListFilesOptions {\n /** Exact language match: go | typescript | javascript */\n language?: string;\n /** Maximum files to return (default 5000). */\n limit?: number;\n}\n\n/**\n * Read-only navigation over a CLI-built repository index (files + import edges).\n */\nexport interface RepoIndex {\n readonly dir: string;\n readonly meta: RepoIndexMeta;\n listFiles(options?: ListFilesOptions): Promise<RepoIndexFile[]>;\n file(path: string): Promise<RepoIndexFile | undefined>;\n importsOf(path: string): Promise<RepoIndexEdge[]>;\n importersOf(path: string): Promise<RepoIndexEdge[]>;\n}\n\nexport class RepoIndexUnavailableError extends Error {\n readonly code = \"repo_index_unavailable\";\n\n constructor(message: string) {\n super(message);\n this.name = \"RepoIndexUnavailableError\";\n }\n}\n\n/** Open an index directory written by the adversary CLI (JSONL schema v1). */\nexport async function openRepoIndex(dir: string): Promise<RepoIndex> {\n const metaRaw = await readFile(join(dir, \"meta.json\"), \"utf8\");\n const meta = JSON.parse(metaRaw) as RepoIndexMeta;\n if (meta.schemaVersion !== REPO_INDEX_SCHEMA_VERSION) {\n throw new RepoIndexUnavailableError(\n `unsupported repo-index schema ${meta.schemaVersion} (want ${REPO_INDEX_SCHEMA_VERSION})`,\n );\n }\n const files = await readJsonl<RepoIndexFile>(join(dir, \"files.jsonl\"));\n const edges = await readJsonl<RepoIndexEdge>(join(dir, \"edges.jsonl\"));\n return new MemoryRepoIndex(dir, meta, files, edges);\n}\n\n/**\n * Load index from ADVERSARY_REPO_INDEX when set; otherwise return null.\n *\n * A missing, deleted, or malformed index is treated as unavailable (null), not a\n * hard run failure — rules already null-check `ctx.repoIndex`. Explicit opens via\n * `openRepoIndex` still throw `RepoIndexUnavailableError`.\n */\nexport async function repoIndexFromEnvironment(\n env: Record<string, string | undefined> = process.env,\n): Promise<RepoIndex | null> {\n const dir = env[ADVERSARY_REPO_INDEX_ENV]?.trim();\n if (!dir) {\n return null;\n }\n try {\n return await openRepoIndex(dir);\n } catch {\n return null;\n }\n}\n\nclass MemoryRepoIndex implements RepoIndex {\n constructor(\n readonly dir: string,\n readonly meta: RepoIndexMeta,\n private readonly files: RepoIndexFile[],\n private readonly edges: RepoIndexEdge[],\n ) {}\n\n async listFiles(options: ListFilesOptions = {}): Promise<RepoIndexFile[]> {\n const limit = options.limit && options.limit > 0 ? options.limit : 5000;\n const language = options.language?.trim();\n const out: RepoIndexFile[] = [];\n for (const file of this.files) {\n if (language && file.language !== language) {\n continue;\n }\n out.push(file);\n if (out.length >= limit) {\n break;\n }\n }\n return out;\n }\n\n async file(path: string): Promise<RepoIndexFile | undefined> {\n const normalized = normalizePath(path);\n return this.files.find((file) => file.path === normalized);\n }\n\n async importsOf(path: string): Promise<RepoIndexEdge[]> {\n const normalized = normalizePath(path);\n return this.edges.filter((edge) => edge.from === normalized && edge.kind === \"import\");\n }\n\n async importersOf(path: string): Promise<RepoIndexEdge[]> {\n const normalized = normalizePath(path);\n const dir = dirOf(normalized);\n return this.edges.filter((edge) => {\n if (edge.kind !== \"import\") {\n return false;\n }\n return edge.to === normalized || edge.to === dir;\n });\n }\n}\n\nfunction normalizePath(path: string): string {\n return path.replaceAll(\"\\\\\", \"/\").replace(/^\\.\\//, \"\");\n}\n\nfunction dirOf(path: string): string {\n const idx = path.lastIndexOf(\"/\");\n if (idx <= 0) {\n return \"\";\n }\n return path.slice(0, idx);\n}\n\nasync function readJsonl<T>(path: string): Promise<T[]> {\n const handle = await open(path, \"r\");\n try {\n const rl = createInterface({\n input: handle.createReadStream(),\n crlfDelay: Number.POSITIVE_INFINITY,\n });\n const out: T[] = [];\n for await (const line of rl) {\n const trimmed = line.trim();\n if (!trimmed) {\n continue;\n }\n out.push(JSON.parse(trimmed) as T);\n }\n return out;\n } finally {\n await handle.close();\n }\n}\n"]}
@@ -27,6 +27,12 @@ export interface ModelRepositoryRetrieval {
27
27
  directoriesListed: number;
28
28
  exhausted: boolean;
29
29
  }
30
+ export interface ModelRepositoryChange {
31
+ baseRef?: string;
32
+ headRef?: string;
33
+ changedFiles: readonly string[];
34
+ worktree: boolean;
35
+ }
30
36
  export declare function resolveModelCitation(citations: readonly ModelRepositoryCitation[] | undefined, citationId: string, line: number): ModelRepositoryCitation | undefined;
31
- export declare function reviewWithRepositoryTools<T>(model: ReviewModel, repositoryRoot: string | undefined, request: ModelReviewRequest): Promise<ModelReviewResult<T>>;
37
+ export declare function reviewWithRepositoryTools<T>(model: ReviewModel, repositoryRoot: string | undefined, request: ModelReviewRequest, change?: ModelRepositoryChange | null): Promise<ModelReviewResult<T>>;
32
38
  //# sourceMappingURL=repository-model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repository-model.d.ts","sourceRoot":"","sources":["../src/repository-model.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EAEtB,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAkCpB,MAAM,WAAW,0BAA0B;IACzC,kFAAkF;IAClF,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,uEAAuE;IACvE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,SAAS,uBAAuB,EAAE,GAAG,SAAS,EACzD,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,uBAAuB,GAAG,SAAS,CAOrC;AA0FD,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,KAAK,EAAE,WAAW,EAClB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAoK/B"}
1
+ {"version":3,"file":"repository-model.d.ts","sourceRoot":"","sources":["../src/repository-model.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EAEtB,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAmCpB,MAAM,WAAW,0BAA0B;IACzC,kFAAkF;IAClF,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,uEAAuE;IACvE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,SAAS,uBAAuB,EAAE,GAAG,SAAS,EACzD,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,uBAAuB,GAAG,SAAS,CAOrC;AAgHD,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,KAAK,EAAE,WAAW,EAClB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,OAAO,EAAE,kBAAkB,EAC3B,MAAM,CAAC,EAAE,qBAAqB,GAAG,IAAI,GACpC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAkL/B"}