@zivis/cli 0.1.0-alpha.4 → 0.1.0-alpha.41
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/LICENSE +201 -0
- package/README.md +31 -0
- package/dist/commands/app/index.d.ts +0 -8
- package/dist/commands/app/index.js +8 -18
- package/dist/commands/assurance/index.d.ts +33 -0
- package/dist/commands/assurance/index.js +149 -0
- package/dist/commands/auth/bind.d.ts +4 -0
- package/dist/commands/auth/bind.js +97 -0
- package/dist/commands/auth/index.js +21 -2
- package/dist/commands/auth/init.d.ts +2 -0
- package/dist/commands/auth/init.js +249 -73
- package/dist/commands/auth/login.d.ts +2 -0
- package/dist/commands/auth/login.js +136 -18
- package/dist/commands/auth/logout.js +19 -7
- package/dist/commands/auth/sessions.js +7 -4
- package/dist/commands/auth/status.js +33 -12
- package/dist/commands/auth/token.d.ts +4 -0
- package/dist/commands/auth/token.js +73 -0
- package/dist/commands/check/index.d.ts +2 -0
- package/dist/commands/check/index.js +463 -0
- package/dist/commands/credits/index.js +2 -3
- package/dist/commands/gate/index.d.ts +2 -0
- package/dist/commands/gate/index.js +171 -0
- package/dist/commands/inspect/index.d.ts +2 -0
- package/dist/commands/inspect/index.js +136 -0
- package/dist/commands/mcp/index.js +113 -14
- package/dist/commands/open/index.d.ts +0 -8
- package/dist/commands/open/index.js +0 -11
- package/dist/commands/run/index.d.ts +2 -0
- package/dist/commands/run/index.js +111 -0
- package/dist/commands/scan/index.js +3 -8
- package/dist/commands/sync/index.d.ts +2 -0
- package/dist/commands/sync/index.js +187 -0
- package/dist/commands/target/index.js +0 -3
- package/dist/commands/test/index.d.ts +2 -0
- package/dist/commands/test/index.js +115 -0
- package/dist/commands/tm/index.js +7 -10
- package/dist/index.js +24 -1
- package/dist/internal/application-binding.d.ts +8 -0
- package/dist/internal/application-binding.js +167 -0
- package/dist/internal/binding-token-mint.d.ts +33 -0
- package/dist/internal/binding-token-mint.js +174 -0
- package/dist/internal/cli-output.d.ts +16 -0
- package/dist/internal/cli-output.js +39 -0
- package/dist/internal/devx-run.d.ts +47 -0
- package/dist/internal/devx-run.js +36 -0
- package/dist/internal/extractor/adapters/go/index.d.ts +0 -4
- package/dist/internal/extractor/adapters/go/index.js +0 -6
- package/dist/internal/extractor/adapters/go/manifest.d.ts +0 -17
- package/dist/internal/extractor/adapters/go/manifest.js +19 -25
- package/dist/internal/extractor/adapters/go/parser.d.ts +0 -4
- package/dist/internal/extractor/adapters/go/parser.js +0 -5
- package/dist/internal/extractor/adapters/go/scanner.d.ts +0 -20
- package/dist/internal/extractor/adapters/go/scanner.js +0 -43
- package/dist/internal/extractor/adapters/python/index.d.ts +0 -3
- package/dist/internal/extractor/adapters/python/index.js +0 -4
- package/dist/internal/extractor/adapters/python/manifest.d.ts +0 -8
- package/dist/internal/extractor/adapters/python/manifest.js +21 -18
- package/dist/internal/extractor/adapters/python/parser.d.ts +0 -3
- package/dist/internal/extractor/adapters/python/parser.js +0 -4
- package/dist/internal/extractor/adapters/python/scanner.d.ts +0 -18
- package/dist/internal/extractor/adapters/python/scanner.js +1 -54
- package/dist/internal/extractor/adapters/typescript/index.d.ts +0 -7
- package/dist/internal/extractor/adapters/typescript/index.js +1 -21
- package/dist/internal/extractor/adapters/typescript/manifest.d.ts +0 -14
- package/dist/internal/extractor/adapters/typescript/manifest.js +26 -17
- package/dist/internal/extractor/adapters/typescript/parser.d.ts +0 -8
- package/dist/internal/extractor/adapters/typescript/parser.js +0 -9
- package/dist/internal/extractor/adapters/typescript/scanner.d.ts +0 -24
- package/dist/internal/extractor/adapters/typescript/scanner.js +0 -43
- package/dist/internal/extractor/graph-builder.d.ts +10 -0
- package/dist/internal/extractor/graph-builder.js +245 -0
- package/dist/internal/extractor/graph-types.d.ts +89 -0
- package/dist/internal/extractor/graph-types.js +1 -0
- package/dist/internal/extractor/index.d.ts +0 -14
- package/dist/internal/extractor/index.js +0 -13
- package/dist/internal/extractor/types.d.ts +0 -45
- package/dist/internal/extractor/types.js +0 -27
- package/dist/internal/extractor/walker.d.ts +0 -18
- package/dist/internal/extractor/walker.js +0 -18
- package/dist/internal/gate-evaluate.d.ts +50 -0
- package/dist/internal/gate-evaluate.js +111 -0
- package/dist/internal/gate-policy.d.ts +38 -0
- package/dist/internal/gate-policy.js +167 -0
- package/dist/internal/git-metadata.d.ts +8 -0
- package/dist/internal/git-metadata.js +38 -0
- package/dist/internal/git.d.ts +8 -0
- package/dist/internal/git.js +30 -0
- package/dist/internal/github-linkage-prompt.d.ts +7 -0
- package/dist/internal/github-linkage-prompt.js +95 -0
- package/dist/internal/ide-setup.d.ts +17 -7
- package/dist/internal/ide-setup.js +377 -46
- package/dist/internal/inventory-sync.d.ts +74 -0
- package/dist/internal/inventory-sync.js +189 -0
- package/dist/internal/org-picker.d.ts +11 -0
- package/dist/internal/org-picker.js +34 -0
- package/dist/internal/orgs.js +0 -1
- package/dist/internal/packs/cache.d.ts +8 -0
- package/dist/internal/packs/cache.js +60 -0
- package/dist/internal/packs/index.d.ts +10 -0
- package/dist/internal/packs/index.js +7 -0
- package/dist/internal/packs/integrity.d.ts +9 -0
- package/dist/internal/packs/integrity.js +24 -0
- package/dist/internal/packs/jcs.d.ts +2 -0
- package/dist/internal/packs/jcs.js +57 -0
- package/dist/internal/packs/local-paths.d.ts +4 -0
- package/dist/internal/packs/local-paths.js +26 -0
- package/dist/internal/packs/registry-client.d.ts +20 -0
- package/dist/internal/packs/registry-client.js +40 -0
- package/dist/internal/packs/resolve.d.ts +8 -0
- package/dist/internal/packs/resolve.js +89 -0
- package/dist/internal/packs/semver.d.ts +9 -0
- package/dist/internal/packs/semver.js +57 -0
- package/dist/internal/packs/signing.d.ts +5 -0
- package/dist/internal/packs/signing.js +49 -0
- package/dist/internal/packs/types.d.ts +70 -0
- package/dist/internal/packs/types.js +20 -0
- package/dist/internal/project-binding.d.ts +1 -0
- package/dist/internal/project-binding.js +14 -9
- package/dist/internal/run-workdir.d.ts +1 -0
- package/dist/internal/run-workdir.js +11 -0
- package/dist/internal/security-context.d.ts +62 -0
- package/dist/internal/security-context.js +50 -0
- package/dist/internal/sync-outbox.d.ts +40 -0
- package/dist/internal/sync-outbox.js +66 -0
- package/dist/internal/target-auth/api-config.d.ts +0 -17
- package/dist/internal/target-auth/api-config.js +0 -18
- package/dist/internal/target-auth/config.d.ts +0 -12
- package/dist/internal/target-auth/config.js +0 -13
- package/dist/internal/target-auth/index.d.ts +0 -18
- package/dist/internal/target-auth/index.js +0 -19
- package/dist/internal/target-auth/keychain.d.ts +0 -18
- package/dist/internal/target-auth/keychain.js +3 -23
- package/dist/internal/target-auth/types.d.ts +0 -26
- package/dist/internal/target-auth/types.js +0 -10
- package/dist/internal/target-auth/workos.d.ts +0 -15
- package/dist/internal/target-auth/workos.js +9 -18
- package/dist/internal/test-scope.d.ts +68 -0
- package/dist/internal/test-scope.js +69 -0
- package/dist/internal/zivis-local-state.d.ts +1 -0
- package/dist/internal/zivis-local-state.js +22 -0
- package/package.json +20 -5
- package/dist/commands/app/index.js.map +0 -1
- package/dist/commands/auth/index.js.map +0 -1
- package/dist/commands/auth/init.js.map +0 -1
- package/dist/commands/auth/login.js.map +0 -1
- package/dist/commands/auth/logout.js.map +0 -1
- package/dist/commands/auth/sessions.js.map +0 -1
- package/dist/commands/auth/status.js.map +0 -1
- package/dist/commands/credits/index.js.map +0 -1
- package/dist/commands/mcp/index.js.map +0 -1
- package/dist/commands/open/index.js.map +0 -1
- package/dist/commands/scan/index.js.map +0 -1
- package/dist/commands/target/index.js.map +0 -1
- package/dist/commands/tm/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal/extractor/adapters/go/index.js.map +0 -1
- package/dist/internal/extractor/adapters/go/manifest.js.map +0 -1
- package/dist/internal/extractor/adapters/go/parser.js.map +0 -1
- package/dist/internal/extractor/adapters/go/scanner.js.map +0 -1
- package/dist/internal/extractor/adapters/python/index.js.map +0 -1
- package/dist/internal/extractor/adapters/python/manifest.js.map +0 -1
- package/dist/internal/extractor/adapters/python/parser.js.map +0 -1
- package/dist/internal/extractor/adapters/python/scanner.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/index.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/manifest.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/parser.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/scanner.js.map +0 -1
- package/dist/internal/extractor/index.js.map +0 -1
- package/dist/internal/extractor/types.js.map +0 -1
- package/dist/internal/extractor/walker.js.map +0 -1
- package/dist/internal/ide-setup.js.map +0 -1
- package/dist/internal/orgs.js.map +0 -1
- package/dist/internal/project-binding.js.map +0 -1
- package/dist/internal/target-auth/api-config.js.map +0 -1
- package/dist/internal/target-auth/config.js.map +0 -1
- package/dist/internal/target-auth/index.js.map +0 -1
- package/dist/internal/target-auth/keychain.js.map +0 -1
- package/dist/internal/target-auth/types.js.map +0 -1
- package/dist/internal/target-auth/workos.js.map +0 -1
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AST scanner for TypeScript / JavaScript source files.
|
|
3
|
-
*
|
|
4
|
-
* For each file: parses with tree-sitter-typescript, walks the tree
|
|
5
|
-
* looking for:
|
|
6
|
-
*
|
|
7
|
-
* 1. `import` / `require` statements naming any tracked package
|
|
8
|
-
* (AI SDKs, web frameworks, DB clients) → import-evidence + extra
|
|
9
|
-
* ExternalService call sites
|
|
10
|
-
* 2. Express-style route declarations:
|
|
11
|
-
* app.METHOD("/path", ...)
|
|
12
|
-
* router.METHOD("/path", ...)
|
|
13
|
-
* server.METHOD("/path", ...)
|
|
14
|
-
* where METHOD is one of GET/POST/PUT/DELETE/PATCH/ALL/USE
|
|
15
|
-
* → HTTP-route-evidence on a component derived from the file
|
|
16
|
-
*
|
|
17
|
-
* Privacy invariant: we only ever emit identifier names, file paths,
|
|
18
|
-
* line numbers, dependency identifiers, and HTTP method/path strings.
|
|
19
|
-
* Function bodies and inline values are never read past the first arg
|
|
20
|
-
* of a route call.
|
|
21
|
-
*/
|
|
22
1
|
import * as fs from "node:fs/promises";
|
|
23
2
|
import { getParsers, pickParserForFile } from "./parser.js";
|
|
24
3
|
import { AI_SDK_PACKAGES, DATA_STORE_PACKAGES, WEB_FRAMEWORK_PACKAGES, } from "./manifest.js";
|
|
@@ -75,35 +54,22 @@ function walk(node, visit) {
|
|
|
75
54
|
walk(child, visit);
|
|
76
55
|
}
|
|
77
56
|
}
|
|
78
|
-
/** Pull the string literal from `import … from "<here>"`. */
|
|
79
57
|
function extractImportSource(node) {
|
|
80
|
-
// import_statement → ... → (string (string_fragment))
|
|
81
58
|
const stringNode = node.children.find((c) => c?.type === "string");
|
|
82
59
|
if (!stringNode)
|
|
83
60
|
return null;
|
|
84
61
|
const frag = stringNode.namedChildren.find((c) => c?.type === "string_fragment");
|
|
85
62
|
return frag?.text ?? null;
|
|
86
63
|
}
|
|
87
|
-
/** Map a package import to a tracked-package identifier, allowing for sub-paths. */
|
|
88
64
|
function trackedPackageMatch(pkg) {
|
|
89
65
|
if (TRACKED_PACKAGES.has(pkg))
|
|
90
66
|
return pkg;
|
|
91
|
-
// sub-paths: "@anthropic-ai/sdk/foo" → "@anthropic-ai/sdk"
|
|
92
67
|
for (const tracked of TRACKED_PACKAGES) {
|
|
93
68
|
if (pkg.startsWith(tracked + "/"))
|
|
94
69
|
return tracked;
|
|
95
70
|
}
|
|
96
71
|
return null;
|
|
97
72
|
}
|
|
98
|
-
/**
|
|
99
|
-
* Identify Express-style route calls. Only inspects the AST shape; never
|
|
100
|
-
* reads the handler-function body.
|
|
101
|
-
*
|
|
102
|
-
* Matches:
|
|
103
|
-
* <ROUTER_IDENT>.<METHOD>("/path", ...)
|
|
104
|
-
*
|
|
105
|
-
* Where the second-and-onward args are deliberately ignored.
|
|
106
|
-
*/
|
|
107
73
|
function extractRouteCall(node) {
|
|
108
74
|
const fn = node.namedChildren.find((c) => c?.type === "member_expression");
|
|
109
75
|
const args = node.namedChildren.find((c) => c?.type === "arguments");
|
|
@@ -119,7 +85,6 @@ function extractRouteCall(node) {
|
|
|
119
85
|
return null;
|
|
120
86
|
if (!HTTP_METHODS.has(methodName))
|
|
121
87
|
return null;
|
|
122
|
-
// First arg should be a string literal — the route path.
|
|
123
88
|
const firstArg = args.namedChildren[0];
|
|
124
89
|
if (!firstArg || firstArg.type !== "string")
|
|
125
90
|
return null;
|
|
@@ -135,8 +100,6 @@ export function aggregate(scans) {
|
|
|
135
100
|
for (const s of scans) {
|
|
136
101
|
if (s.routes.length === 0 && s.imports.length === 0)
|
|
137
102
|
continue;
|
|
138
|
-
// Tag SDK call sites. Each tracked import of an AI SDK package
|
|
139
|
-
// counts as a call site for that vendor, on top of the manifest hit.
|
|
140
103
|
for (const imp of s.imports) {
|
|
141
104
|
if (AI_SDK_PACKAGES[imp.package]) {
|
|
142
105
|
const arr = aiSdkCallSites.get(imp.package) ?? [];
|
|
@@ -152,8 +115,6 @@ export function aggregate(scans) {
|
|
|
152
115
|
}
|
|
153
116
|
if (s.routes.length === 0)
|
|
154
117
|
continue;
|
|
155
|
-
// Derive a friendly component name from the file path (the deepest
|
|
156
|
-
// meaningful directory). e.g. src/auth/login.ts → "auth".
|
|
157
118
|
const name = deriveComponentName(s.file);
|
|
158
119
|
const framework = detectFrameworkFromImports(s.imports);
|
|
159
120
|
components.push({
|
|
@@ -168,10 +129,7 @@ export function aggregate(scans) {
|
|
|
168
129
|
}
|
|
169
130
|
function deriveComponentName(filePath) {
|
|
170
131
|
const segs = filePath.split("/");
|
|
171
|
-
// Drop "src" / "lib" / "app" prefixes so the name is meaningful.
|
|
172
132
|
const start = ["src", "lib", "app"].includes(segs[0]) ? 1 : 0;
|
|
173
|
-
// Use the parent directory as the component (e.g. "auth"), or the
|
|
174
|
-
// filename root if the file lives at the top level.
|
|
175
133
|
if (segs.length - start >= 2)
|
|
176
134
|
return segs[start];
|
|
177
135
|
const last = segs[segs.length - 1];
|
|
@@ -185,4 +143,3 @@ function detectFrameworkFromImports(imports) {
|
|
|
185
143
|
}
|
|
186
144
|
return undefined;
|
|
187
145
|
}
|
|
188
|
-
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ExtractionResult } from "./types.js";
|
|
2
|
+
import type { RepoGraphArtifactV1, AnalysisMode } from "./graph-types.js";
|
|
3
|
+
export interface BuildOptions {
|
|
4
|
+
applicationId?: string;
|
|
5
|
+
repoId?: string;
|
|
6
|
+
commitSha?: string;
|
|
7
|
+
branch?: string;
|
|
8
|
+
analysisMode?: AnalysisMode;
|
|
9
|
+
}
|
|
10
|
+
export declare function buildRepoGraphArtifact(result: ExtractionResult, opts?: BuildOptions): RepoGraphArtifactV1;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
const TRUSTED_LANE = "trusted";
|
|
2
|
+
const TRUSTED_SOURCE = "inspect.trusted";
|
|
3
|
+
export function buildRepoGraphArtifact(result, opts = {}) {
|
|
4
|
+
const nodes = [];
|
|
5
|
+
const edges = [];
|
|
6
|
+
const serviceById = new Map();
|
|
7
|
+
for (const component of result.components) {
|
|
8
|
+
if (component.kind === "external_service" || component.kind === "data_store") {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (component.evidence.length === 0)
|
|
12
|
+
continue;
|
|
13
|
+
const node = buildServiceNode(component);
|
|
14
|
+
serviceById.set(node.id, node);
|
|
15
|
+
nodes.push(node);
|
|
16
|
+
}
|
|
17
|
+
for (const component of result.components) {
|
|
18
|
+
const owningServiceId = serviceIdFor(component);
|
|
19
|
+
if (!serviceById.has(owningServiceId))
|
|
20
|
+
continue;
|
|
21
|
+
const seenEndpoints = new Set();
|
|
22
|
+
for (const ev of component.evidence) {
|
|
23
|
+
if (ev.kind !== "http_route")
|
|
24
|
+
continue;
|
|
25
|
+
const method = canonicalMethod(ev.method);
|
|
26
|
+
if (!method)
|
|
27
|
+
continue;
|
|
28
|
+
const endpointNode = buildEndpointNode(owningServiceId, component.language, ev, method);
|
|
29
|
+
if (seenEndpoints.has(endpointNode.id))
|
|
30
|
+
continue;
|
|
31
|
+
seenEndpoints.add(endpointNode.id);
|
|
32
|
+
nodes.push(endpointNode);
|
|
33
|
+
edges.push(buildExposesEdge(owningServiceId, endpointNode.id, ev));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const manifestEvidenceByPackage = indexManifestsByPackage(result);
|
|
37
|
+
const depById = new Map();
|
|
38
|
+
for (const ext of result.externalServices) {
|
|
39
|
+
const node = buildExternalDependencyNode(ext, manifestEvidenceByPackage.get(ext.package) ?? []);
|
|
40
|
+
if (depById.has(node.id))
|
|
41
|
+
continue;
|
|
42
|
+
if (node.evidence.length === 0)
|
|
43
|
+
continue;
|
|
44
|
+
depById.set(node.id, node);
|
|
45
|
+
nodes.push(node);
|
|
46
|
+
}
|
|
47
|
+
const fileToServiceId = buildFileToServiceIndex(result.components, serviceById);
|
|
48
|
+
for (const ext of result.externalServices) {
|
|
49
|
+
const depId = externalDepId(ext);
|
|
50
|
+
const seenServiceIds = new Set();
|
|
51
|
+
const evidenceByService = new Map();
|
|
52
|
+
for (const callSite of ext.callSites) {
|
|
53
|
+
const serviceId = fileToServiceId.get(callSite.file);
|
|
54
|
+
if (!serviceId)
|
|
55
|
+
continue;
|
|
56
|
+
seenServiceIds.add(serviceId);
|
|
57
|
+
const list = evidenceByService.get(serviceId) ?? [];
|
|
58
|
+
list.push(toImportEvidence(callSite, ext.package));
|
|
59
|
+
evidenceByService.set(serviceId, list);
|
|
60
|
+
}
|
|
61
|
+
for (const serviceId of seenServiceIds) {
|
|
62
|
+
edges.push(buildDependsOnEdge(serviceId, depId, evidenceByService.get(serviceId) ?? []));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const provenance = {
|
|
66
|
+
root_dir: result.scope.rootDir,
|
|
67
|
+
analysis_mode: opts.analysisMode ?? "local",
|
|
68
|
+
...(opts.repoId ? { repo_id: opts.repoId } : {}),
|
|
69
|
+
...(opts.commitSha ? { commit_sha: opts.commitSha } : {}),
|
|
70
|
+
...(opts.branch ? { branch: opts.branch } : {}),
|
|
71
|
+
...(opts.applicationId ? { application_id: opts.applicationId } : {}),
|
|
72
|
+
};
|
|
73
|
+
const languagesFromManifests = new Set(result.manifests.map((m) => normalizeLanguage(m.language)).filter(Boolean));
|
|
74
|
+
return {
|
|
75
|
+
version: "1.0.0",
|
|
76
|
+
kind: "repo_graph_artifact",
|
|
77
|
+
extracted_at: result.extractedAt,
|
|
78
|
+
extractor: result.extractor,
|
|
79
|
+
provenance,
|
|
80
|
+
scope: {
|
|
81
|
+
files_scanned: result.scope.filesScanned,
|
|
82
|
+
files_skipped: result.scope.filesSkipped,
|
|
83
|
+
duration_ms: result.scope.durationMs,
|
|
84
|
+
languages: [...languagesFromManifests],
|
|
85
|
+
},
|
|
86
|
+
nodes,
|
|
87
|
+
edges,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function buildServiceNode(component) {
|
|
91
|
+
const id = serviceIdFor(component);
|
|
92
|
+
return {
|
|
93
|
+
id,
|
|
94
|
+
type: "service",
|
|
95
|
+
name: component.name,
|
|
96
|
+
lane: TRUSTED_LANE,
|
|
97
|
+
source: TRUSTED_SOURCE,
|
|
98
|
+
confidence: 1,
|
|
99
|
+
language: normalizeLanguage(component.language),
|
|
100
|
+
evidence: component.evidence.map(toEvidenceSpan),
|
|
101
|
+
attributes: {
|
|
102
|
+
...(component.framework ? { framework: component.framework } : {}),
|
|
103
|
+
source_files: component.sourceFiles,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function buildEndpointNode(serviceId, language, ev, method) {
|
|
108
|
+
const id = `endpoint::${stripPrefix(serviceId, "service::")}::${method}::${ev.path}`;
|
|
109
|
+
return {
|
|
110
|
+
id,
|
|
111
|
+
type: "endpoint",
|
|
112
|
+
name: `${method} ${ev.path}`,
|
|
113
|
+
lane: TRUSTED_LANE,
|
|
114
|
+
source: TRUSTED_SOURCE,
|
|
115
|
+
confidence: 1,
|
|
116
|
+
language: normalizeLanguage(language),
|
|
117
|
+
evidence: [toEvidenceSpan(ev)],
|
|
118
|
+
attributes: { method, path: ev.path },
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function buildExternalDependencyNode(ext, manifestFallback) {
|
|
122
|
+
const registry = registryFor(ext);
|
|
123
|
+
const evidence = ext.callSites.length > 0
|
|
124
|
+
? ext.callSites.map((cs) => toImportEvidence(cs, ext.package))
|
|
125
|
+
: manifestFallback;
|
|
126
|
+
return {
|
|
127
|
+
id: externalDepId(ext),
|
|
128
|
+
type: "external_dependency",
|
|
129
|
+
name: ext.name,
|
|
130
|
+
lane: TRUSTED_LANE,
|
|
131
|
+
source: TRUSTED_SOURCE,
|
|
132
|
+
confidence: 1,
|
|
133
|
+
evidence,
|
|
134
|
+
attributes: {
|
|
135
|
+
package: ext.package,
|
|
136
|
+
registry,
|
|
137
|
+
...(ext.version ? { version: ext.version } : {}),
|
|
138
|
+
...(ext.kind ? { kind: ext.kind } : {}),
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function indexManifestsByPackage(result) {
|
|
143
|
+
const idx = new Map();
|
|
144
|
+
for (const m of result.manifests) {
|
|
145
|
+
for (const pkg of Object.keys(m.dependencies)) {
|
|
146
|
+
const span = { file: m.file, kind: "manifest", detail: { package: pkg } };
|
|
147
|
+
const list = idx.get(pkg) ?? [];
|
|
148
|
+
list.push(span);
|
|
149
|
+
idx.set(pkg, list);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return idx;
|
|
153
|
+
}
|
|
154
|
+
function buildExposesEdge(serviceId, endpointId, ev) {
|
|
155
|
+
return {
|
|
156
|
+
id: `exposes::${serviceId}::${endpointId}`,
|
|
157
|
+
type: "exposes",
|
|
158
|
+
source_id: serviceId,
|
|
159
|
+
target_id: endpointId,
|
|
160
|
+
lane: TRUSTED_LANE,
|
|
161
|
+
source: TRUSTED_SOURCE,
|
|
162
|
+
confidence: 1,
|
|
163
|
+
evidence: [toEvidenceSpan(ev)],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function buildDependsOnEdge(serviceId, depId, evidence) {
|
|
167
|
+
return {
|
|
168
|
+
id: `depends_on::${serviceId}::${depId}`,
|
|
169
|
+
type: "depends_on",
|
|
170
|
+
source_id: serviceId,
|
|
171
|
+
target_id: depId,
|
|
172
|
+
lane: TRUSTED_LANE,
|
|
173
|
+
source: TRUSTED_SOURCE,
|
|
174
|
+
confidence: 1,
|
|
175
|
+
evidence,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function serviceIdFor(component) {
|
|
179
|
+
return `service::${component.language}::${component.name}`;
|
|
180
|
+
}
|
|
181
|
+
function externalDepId(ext) {
|
|
182
|
+
const registry = registryFor(ext);
|
|
183
|
+
return `external_dependency::${registry}::${ext.package}`;
|
|
184
|
+
}
|
|
185
|
+
function registryFor(ext) {
|
|
186
|
+
if (ext.package.startsWith("github.com/"))
|
|
187
|
+
return "go";
|
|
188
|
+
if (ext.package.startsWith("@") || /^[a-z0-9-]+$/.test(ext.package)) {
|
|
189
|
+
return "npm";
|
|
190
|
+
}
|
|
191
|
+
return "npm";
|
|
192
|
+
}
|
|
193
|
+
const CANONICAL_METHODS = new Set([
|
|
194
|
+
"GET",
|
|
195
|
+
"POST",
|
|
196
|
+
"PUT",
|
|
197
|
+
"PATCH",
|
|
198
|
+
"DELETE",
|
|
199
|
+
"HEAD",
|
|
200
|
+
"OPTIONS",
|
|
201
|
+
"WS",
|
|
202
|
+
"SSE",
|
|
203
|
+
"RPC",
|
|
204
|
+
]);
|
|
205
|
+
function canonicalMethod(method) {
|
|
206
|
+
const upper = method.toUpperCase();
|
|
207
|
+
return CANONICAL_METHODS.has(upper) ? upper : undefined;
|
|
208
|
+
}
|
|
209
|
+
function normalizeLanguage(lang) {
|
|
210
|
+
return lang;
|
|
211
|
+
}
|
|
212
|
+
function buildFileToServiceIndex(components, servicesById) {
|
|
213
|
+
const idx = new Map();
|
|
214
|
+
for (const component of components) {
|
|
215
|
+
const id = serviceIdFor(component);
|
|
216
|
+
if (!servicesById.has(id))
|
|
217
|
+
continue;
|
|
218
|
+
for (const file of component.sourceFiles) {
|
|
219
|
+
if (!idx.has(file))
|
|
220
|
+
idx.set(file, id);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return idx;
|
|
224
|
+
}
|
|
225
|
+
function toEvidenceSpan(ev) {
|
|
226
|
+
const base = { file: ev.file, start_line: ev.line, kind: ev.kind };
|
|
227
|
+
if (ev.kind === "http_route") {
|
|
228
|
+
return { ...base, detail: { method: ev.method, path: ev.path } };
|
|
229
|
+
}
|
|
230
|
+
if (ev.kind === "import") {
|
|
231
|
+
return { ...base, detail: { package: ev.package } };
|
|
232
|
+
}
|
|
233
|
+
return base;
|
|
234
|
+
}
|
|
235
|
+
function toImportEvidence(loc, pkg) {
|
|
236
|
+
return {
|
|
237
|
+
file: loc.file,
|
|
238
|
+
start_line: loc.line,
|
|
239
|
+
kind: "import",
|
|
240
|
+
detail: { package: pkg },
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function stripPrefix(value, prefix) {
|
|
244
|
+
return value.startsWith(prefix) ? value.slice(prefix.length) : value;
|
|
245
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export type Lane = "trusted" | "inferred";
|
|
2
|
+
export type Source = "inspect.trusted" | "inspect.inferred";
|
|
3
|
+
export type GraphLanguage = "typescript" | "javascript" | "python" | "go";
|
|
4
|
+
export type AnalysisMode = "local" | "cloud";
|
|
5
|
+
export type EvidenceKind = "import" | "http_route" | "config" | "manifest" | "decorator" | "annotation";
|
|
6
|
+
export type NodeType = "service" | "endpoint" | "external_dependency";
|
|
7
|
+
export type EdgeType = "exposes" | "depends_on";
|
|
8
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS" | "WS" | "SSE" | "RPC";
|
|
9
|
+
export type DependencyRegistry = "npm" | "pypi" | "go";
|
|
10
|
+
export interface EvidenceSpan {
|
|
11
|
+
file: string;
|
|
12
|
+
start_line?: number;
|
|
13
|
+
end_line?: number;
|
|
14
|
+
kind: EvidenceKind;
|
|
15
|
+
detail?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export interface ServiceAttributes {
|
|
18
|
+
framework?: string;
|
|
19
|
+
source_files?: string[];
|
|
20
|
+
}
|
|
21
|
+
export interface EndpointAttributes {
|
|
22
|
+
method: HttpMethod;
|
|
23
|
+
path: string;
|
|
24
|
+
}
|
|
25
|
+
export interface ExternalDependencyAttributes {
|
|
26
|
+
package: string;
|
|
27
|
+
version?: string;
|
|
28
|
+
registry: DependencyRegistry;
|
|
29
|
+
kind?: string;
|
|
30
|
+
}
|
|
31
|
+
export type NodeAttributes = ServiceAttributes | EndpointAttributes | ExternalDependencyAttributes;
|
|
32
|
+
interface BaseNode {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
lane: Lane;
|
|
36
|
+
source: Source;
|
|
37
|
+
confidence?: number;
|
|
38
|
+
language?: GraphLanguage;
|
|
39
|
+
evidence: EvidenceSpan[];
|
|
40
|
+
}
|
|
41
|
+
export interface ServiceNode extends BaseNode {
|
|
42
|
+
type: "service";
|
|
43
|
+
attributes?: ServiceAttributes;
|
|
44
|
+
}
|
|
45
|
+
export interface EndpointNode extends BaseNode {
|
|
46
|
+
type: "endpoint";
|
|
47
|
+
attributes: EndpointAttributes;
|
|
48
|
+
}
|
|
49
|
+
export interface ExternalDependencyNode extends BaseNode {
|
|
50
|
+
type: "external_dependency";
|
|
51
|
+
attributes: ExternalDependencyAttributes;
|
|
52
|
+
}
|
|
53
|
+
export type GraphNode = ServiceNode | EndpointNode | ExternalDependencyNode;
|
|
54
|
+
export interface GraphEdge {
|
|
55
|
+
id: string;
|
|
56
|
+
type: EdgeType;
|
|
57
|
+
source_id: string;
|
|
58
|
+
target_id: string;
|
|
59
|
+
lane: Lane;
|
|
60
|
+
source: Source;
|
|
61
|
+
confidence?: number;
|
|
62
|
+
evidence?: EvidenceSpan[];
|
|
63
|
+
attributes?: Record<string, unknown>;
|
|
64
|
+
}
|
|
65
|
+
export interface GraphProvenance {
|
|
66
|
+
root_dir: string;
|
|
67
|
+
repo_id?: string;
|
|
68
|
+
commit_sha?: string;
|
|
69
|
+
branch?: string;
|
|
70
|
+
application_id?: string;
|
|
71
|
+
analysis_mode: AnalysisMode;
|
|
72
|
+
}
|
|
73
|
+
export interface GraphScope {
|
|
74
|
+
files_scanned: number;
|
|
75
|
+
files_skipped: number;
|
|
76
|
+
duration_ms: number;
|
|
77
|
+
languages?: GraphLanguage[];
|
|
78
|
+
}
|
|
79
|
+
export interface RepoGraphArtifactV1 {
|
|
80
|
+
version: "1.0.0";
|
|
81
|
+
kind: "repo_graph_artifact";
|
|
82
|
+
extracted_at: string;
|
|
83
|
+
extractor: string;
|
|
84
|
+
provenance: GraphProvenance;
|
|
85
|
+
scope: GraphScope;
|
|
86
|
+
nodes: GraphNode[];
|
|
87
|
+
edges: GraphEdge[];
|
|
88
|
+
}
|
|
89
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,22 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extractor orchestrator.
|
|
3
|
-
*
|
|
4
|
-
* Walks the customer's repo, dispatches matched files to per-language
|
|
5
|
-
* adapters, merges their findings into a single ExtractionResult.
|
|
6
|
-
*
|
|
7
|
-
* Adapters register their own file extensions + manifest filenames; the
|
|
8
|
-
* walker only reads files those filters claim. So adding a Go or Python
|
|
9
|
-
* adapter doesn't change anything here — register the adapter and the
|
|
10
|
-
* orchestrator picks up its files.
|
|
11
|
-
*/
|
|
12
1
|
import type { ExtractionResult } from "./types.js";
|
|
13
2
|
export interface RunOptions {
|
|
14
3
|
rootDir: string;
|
|
15
|
-
/** Subset of language adapters to run; defaults to all registered. */
|
|
16
4
|
languages?: Array<"typescript" | "go" | "python">;
|
|
17
|
-
/** Extra ignore patterns from --ignore flags. */
|
|
18
5
|
extraIgnores?: string[];
|
|
19
|
-
/** Version string for the extractor — usually @zivis/cli's package version. */
|
|
20
6
|
extractorVersion: string;
|
|
21
7
|
}
|
|
22
8
|
export declare function runExtractor(opts: RunOptions): Promise<ExtractionResult>;
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extractor orchestrator.
|
|
3
|
-
*
|
|
4
|
-
* Walks the customer's repo, dispatches matched files to per-language
|
|
5
|
-
* adapters, merges their findings into a single ExtractionResult.
|
|
6
|
-
*
|
|
7
|
-
* Adapters register their own file extensions + manifest filenames; the
|
|
8
|
-
* walker only reads files those filters claim. So adding a Go or Python
|
|
9
|
-
* adapter doesn't change anything here — register the adapter and the
|
|
10
|
-
* orchestrator picks up its files.
|
|
11
|
-
*/
|
|
12
1
|
import * as path from "node:path";
|
|
13
2
|
import { walk } from "./walker.js";
|
|
14
3
|
import { typescriptAdapter } from "./adapters/typescript/index.js";
|
|
@@ -25,7 +14,6 @@ export async function runExtractor(opts) {
|
|
|
25
14
|
const adapters = opts.languages
|
|
26
15
|
? ADAPTERS.filter((a) => opts.languages.includes(a.language))
|
|
27
16
|
: ADAPTERS;
|
|
28
|
-
// Aggregate the file filters from each adapter so we walk the tree once.
|
|
29
17
|
const extensions = new Set();
|
|
30
18
|
const manifests = new Set();
|
|
31
19
|
for (const a of adapters) {
|
|
@@ -104,4 +92,3 @@ function dedupeExternalServices(items) {
|
|
|
104
92
|
}
|
|
105
93
|
return [...out.values()];
|
|
106
94
|
}
|
|
107
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extractor output schema.
|
|
3
|
-
*
|
|
4
|
-
* Matches `THREAT-MODEL-PHASE-J.md`'s J.2 output shape, with additions
|
|
5
|
-
* for privacy-audit metadata. The shape is the contract between the CLI
|
|
6
|
-
* and the eventual `--apply` endpoint that maps into ThreatModelComponent
|
|
7
|
-
* / OrgComponent records (RECON.3-gated).
|
|
8
|
-
*
|
|
9
|
-
* Privacy invariant — what's IN the JSON:
|
|
10
|
-
* component names ("auth-service")
|
|
11
|
-
* file paths ("src/auth/login.ts")
|
|
12
|
-
* line numbers (42)
|
|
13
|
-
* framework + SDK identifiers ("express", "@anthropic-ai/sdk")
|
|
14
|
-
* detected protocols / methods (POST /login)
|
|
15
|
-
* dependency versions (from package.json / go.mod / pyproject.toml)
|
|
16
|
-
*
|
|
17
|
-
* What's NOT:
|
|
18
|
-
* function bodies, file contents, code snippets
|
|
19
|
-
* variable names from inside functions
|
|
20
|
-
* comments
|
|
21
|
-
* raw AST nodes
|
|
22
|
-
*
|
|
23
|
-
* `zivis tm extract --privacy-audit` prints this whole object to stdout
|
|
24
|
-
* before any network call, so the customer can see exactly what would
|
|
25
|
-
* be sent.
|
|
26
|
-
*/
|
|
27
1
|
export type Language = "typescript" | "javascript" | "go" | "python";
|
|
28
2
|
export interface EvidenceLocation {
|
|
29
3
|
file: string;
|
|
@@ -44,21 +18,16 @@ export interface ConfigEvidence extends EvidenceLocation {
|
|
|
44
18
|
export type Evidence = HttpRouteEvidence | ImportEvidence | ConfigEvidence;
|
|
45
19
|
export type ComponentKind = "process" | "data_store" | "external_service" | "frontend";
|
|
46
20
|
export interface ExtractedComponent {
|
|
47
|
-
/** Stable name derived from path / module — never a value from inside a function. */
|
|
48
21
|
name: string;
|
|
49
22
|
kind: ComponentKind;
|
|
50
23
|
language: Language;
|
|
51
|
-
/** Detected web framework, if any (express | fastify | nextjs | trpc | hono | gin | echo | …). */
|
|
52
24
|
framework?: string;
|
|
53
25
|
sourceFiles: string[];
|
|
54
26
|
evidence: Evidence[];
|
|
55
27
|
}
|
|
56
28
|
export interface ExtractedExternalService {
|
|
57
|
-
/** Vendor / SDK name — "Anthropic", "OpenAI", "WorkOS". */
|
|
58
29
|
name: string;
|
|
59
|
-
/** What kind of integration: ai_sdk, payments, auth, observability, etc. */
|
|
60
30
|
kind: string;
|
|
61
|
-
/** npm/PyPI/Go module identifier ("@anthropic-ai/sdk"). */
|
|
62
31
|
package: string;
|
|
63
32
|
version?: string;
|
|
64
33
|
callSites: EvidenceLocation[];
|
|
@@ -84,30 +53,16 @@ export interface ExtractionResult {
|
|
|
84
53
|
components: ExtractedComponent[];
|
|
85
54
|
externalServices: ExtractedExternalService[];
|
|
86
55
|
dataStores: ExtractedDataStore[];
|
|
87
|
-
/**
|
|
88
|
-
* Per-language manifests detected in the scope. Surfaces dependency-only
|
|
89
|
-
* findings (e.g., "this repo uses @anthropic-ai/sdk@^0.30") even when
|
|
90
|
-
* we can't yet match call sites.
|
|
91
|
-
*/
|
|
92
56
|
manifests: ManifestSummary[];
|
|
93
57
|
}
|
|
94
58
|
export interface ManifestSummary {
|
|
95
59
|
language: Language;
|
|
96
60
|
file: string;
|
|
97
|
-
/** Direct dependency names → version (verbatim from manifest). */
|
|
98
61
|
dependencies: Record<string, string>;
|
|
99
62
|
}
|
|
100
|
-
/**
|
|
101
|
-
* Adapter contract — one per language. Each adapter is fed the list of
|
|
102
|
-
* source files matched for it (already filtered by .gitignore + .zivisignore)
|
|
103
|
-
* plus any manifest files (package.json, go.mod, pyproject.toml). Returns
|
|
104
|
-
* structured findings the framework merges into the final ExtractionResult.
|
|
105
|
-
*/
|
|
106
63
|
export interface LanguageAdapter {
|
|
107
64
|
language: Language;
|
|
108
|
-
/** File extensions this adapter handles. */
|
|
109
65
|
extensions: string[];
|
|
110
|
-
/** Manifest filenames this adapter cares about. */
|
|
111
66
|
manifests: string[];
|
|
112
67
|
extract(input: AdapterInput): Promise<AdapterOutput>;
|
|
113
68
|
}
|
|
@@ -1,28 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extractor output schema.
|
|
3
|
-
*
|
|
4
|
-
* Matches `THREAT-MODEL-PHASE-J.md`'s J.2 output shape, with additions
|
|
5
|
-
* for privacy-audit metadata. The shape is the contract between the CLI
|
|
6
|
-
* and the eventual `--apply` endpoint that maps into ThreatModelComponent
|
|
7
|
-
* / OrgComponent records (RECON.3-gated).
|
|
8
|
-
*
|
|
9
|
-
* Privacy invariant — what's IN the JSON:
|
|
10
|
-
* component names ("auth-service")
|
|
11
|
-
* file paths ("src/auth/login.ts")
|
|
12
|
-
* line numbers (42)
|
|
13
|
-
* framework + SDK identifiers ("express", "@anthropic-ai/sdk")
|
|
14
|
-
* detected protocols / methods (POST /login)
|
|
15
|
-
* dependency versions (from package.json / go.mod / pyproject.toml)
|
|
16
|
-
*
|
|
17
|
-
* What's NOT:
|
|
18
|
-
* function bodies, file contents, code snippets
|
|
19
|
-
* variable names from inside functions
|
|
20
|
-
* comments
|
|
21
|
-
* raw AST nodes
|
|
22
|
-
*
|
|
23
|
-
* `zivis tm extract --privacy-audit` prints this whole object to stdout
|
|
24
|
-
* before any network call, so the customer can see exactly what would
|
|
25
|
-
* be sent.
|
|
26
|
-
*/
|
|
27
1
|
export {};
|
|
28
|
-
//# sourceMappingURL=types.js.map
|
|
@@ -1,24 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Filesystem walker — yields source files relative to the scan root,
|
|
3
|
-
* filtered by .gitignore + .zivisignore + per-extractor extension filters.
|
|
4
|
-
*
|
|
5
|
-
* Uses the `ignore` npm package (the same engine git ships with) so the
|
|
6
|
-
* matching semantics mirror what developers expect from .gitignore.
|
|
7
|
-
*
|
|
8
|
-
* Hard limits:
|
|
9
|
-
* - Skips node_modules, dist, build, .next, .git, .yarn, vendor — even
|
|
10
|
-
* if not in .gitignore. These exist by convention and contain
|
|
11
|
-
* third-party code that pollutes the inventory.
|
|
12
|
-
* - Caps total files at 50_000 to refuse to scan a generated monorepo
|
|
13
|
-
* by accident (extractor would take forever; user should narrow scope).
|
|
14
|
-
*/
|
|
15
1
|
export interface WalkOptions {
|
|
16
2
|
rootDir: string;
|
|
17
|
-
/** Extensions to include (with the leading dot, e.g. [".ts", ".tsx"]). */
|
|
18
3
|
extensions: string[];
|
|
19
|
-
/** Manifest basenames to capture regardless of extension (e.g. ["package.json"]). */
|
|
20
4
|
manifests: string[];
|
|
21
|
-
/** Extra ignore patterns from --ignore CLI flags. */
|
|
22
5
|
extraIgnores?: string[];
|
|
23
6
|
}
|
|
24
7
|
export interface WalkResult {
|
|
@@ -28,5 +11,4 @@ export interface WalkResult {
|
|
|
28
11
|
filesSkipped: number;
|
|
29
12
|
}
|
|
30
13
|
export declare function walk(opts: WalkOptions): Promise<WalkResult>;
|
|
31
|
-
/** Convert an absolute path to a posix-style repo-relative path. */
|
|
32
14
|
export declare function repoRelative(rootDir: string, absPath: string): string;
|
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Filesystem walker — yields source files relative to the scan root,
|
|
3
|
-
* filtered by .gitignore + .zivisignore + per-extractor extension filters.
|
|
4
|
-
*
|
|
5
|
-
* Uses the `ignore` npm package (the same engine git ships with) so the
|
|
6
|
-
* matching semantics mirror what developers expect from .gitignore.
|
|
7
|
-
*
|
|
8
|
-
* Hard limits:
|
|
9
|
-
* - Skips node_modules, dist, build, .next, .git, .yarn, vendor — even
|
|
10
|
-
* if not in .gitignore. These exist by convention and contain
|
|
11
|
-
* third-party code that pollutes the inventory.
|
|
12
|
-
* - Caps total files at 50_000 to refuse to scan a generated monorepo
|
|
13
|
-
* by accident (extractor would take forever; user should narrow scope).
|
|
14
|
-
*/
|
|
15
1
|
import * as fs from "node:fs/promises";
|
|
16
2
|
import * as path from "node:path";
|
|
17
3
|
import ignore from "ignore";
|
|
@@ -64,7 +50,6 @@ export async function walk(opts) {
|
|
|
64
50
|
filesSkipped++;
|
|
65
51
|
continue;
|
|
66
52
|
}
|
|
67
|
-
// ignore() expects gitignore-style paths with trailing /
|
|
68
53
|
if (ig.ignores(childRel + "/")) {
|
|
69
54
|
filesSkipped++;
|
|
70
55
|
continue;
|
|
@@ -99,12 +84,9 @@ async function loadIgnoreFile(ig, filePath) {
|
|
|
99
84
|
ig.add(content);
|
|
100
85
|
}
|
|
101
86
|
catch {
|
|
102
|
-
// No ignore file — fine.
|
|
103
87
|
}
|
|
104
88
|
}
|
|
105
|
-
/** Convert an absolute path to a posix-style repo-relative path. */
|
|
106
89
|
export function repoRelative(rootDir, absPath) {
|
|
107
90
|
const rel = path.relative(rootDir, absPath);
|
|
108
91
|
return rel.split(path.sep).join("/");
|
|
109
92
|
}
|
|
110
|
-
//# sourceMappingURL=walker.js.map
|