@wrongstack/techstack 0.319.0 → 0.319.1
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/index.js +198 -59
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -66,8 +66,8 @@ function parsePurl(purl) {
|
|
|
66
66
|
const eqIdx = pair.indexOf("=");
|
|
67
67
|
if (eqIdx > 0) {
|
|
68
68
|
qualifiers.set(
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
decodePurlSegment(pair.slice(0, eqIdx)),
|
|
70
|
+
decodePurlSegment(pair.slice(eqIdx + 1))
|
|
71
71
|
);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -2224,7 +2224,13 @@ var TrendStore = class {
|
|
|
2224
2224
|
active.add(key);
|
|
2225
2225
|
const vulnerable = dependency.status === "vulnerable";
|
|
2226
2226
|
const history = versionHistory.get(key) ?? [];
|
|
2227
|
-
history.push({
|
|
2227
|
+
history.push({
|
|
2228
|
+
at,
|
|
2229
|
+
version: dependency.locked ?? dependency.installed ?? dependency.requested,
|
|
2230
|
+
vulnerable,
|
|
2231
|
+
name: dependency.name,
|
|
2232
|
+
ecosystem: dependency.ecosystem
|
|
2233
|
+
});
|
|
2228
2234
|
versionHistory.set(key, history);
|
|
2229
2235
|
if (vulnerable && !openVulnerabilities.has(key)) openVulnerabilities.set(key, at);
|
|
2230
2236
|
if (!vulnerable && openVulnerabilities.has(key)) {
|
|
@@ -2281,7 +2287,9 @@ var TrendStore = class {
|
|
|
2281
2287
|
snapshotId: snapshot.id,
|
|
2282
2288
|
createdAt: snapshot.createdAt,
|
|
2283
2289
|
dependencies: snapshot.dependencies.length,
|
|
2284
|
-
outdated: snapshot.dependencies.filter(
|
|
2290
|
+
outdated: snapshot.dependencies.filter(
|
|
2291
|
+
(dependency) => dependency.status.startsWith("update_available")
|
|
2292
|
+
).length,
|
|
2285
2293
|
vulnerable: snapshot.dependencies.filter((dependency) => dependency.status === "vulnerable").length
|
|
2286
2294
|
}))
|
|
2287
2295
|
};
|
|
@@ -2289,10 +2297,19 @@ var TrendStore = class {
|
|
|
2289
2297
|
};
|
|
2290
2298
|
function renderTrendMarkdown(report) {
|
|
2291
2299
|
const lines = ["# TechStack Trend", "", `**Snapshots:** ${report.snapshots}`];
|
|
2292
|
-
if (report.vulnerabilityHalfLifeMs !== void 0)
|
|
2293
|
-
|
|
2300
|
+
if (report.vulnerabilityHalfLifeMs !== void 0)
|
|
2301
|
+
lines.push(
|
|
2302
|
+
`**Vulnerability half-life:** ${(report.vulnerabilityHalfLifeMs / 864e5).toFixed(1)} days`
|
|
2303
|
+
);
|
|
2304
|
+
lines.push(
|
|
2305
|
+
"",
|
|
2306
|
+
"| Dependency | Version | Age (days) | Changes | Velocity/day |",
|
|
2307
|
+
"|---|---|---:|---:|---:|"
|
|
2308
|
+
);
|
|
2294
2309
|
for (const dependency of report.dependencies) {
|
|
2295
|
-
lines.push(
|
|
2310
|
+
lines.push(
|
|
2311
|
+
`| ${dependency.ecosystem}:${dependency.name} | ${dependency.currentVersion ?? "\u2014"} | ${(dependency.lockedVersionAgeMs / 864e5).toFixed(1)} | ${dependency.versionChanges} | ${dependency.upgradeVelocityPerDay.toFixed(3)} |`
|
|
2312
|
+
);
|
|
2296
2313
|
}
|
|
2297
2314
|
return lines.join("\n");
|
|
2298
2315
|
}
|
|
@@ -2601,11 +2618,14 @@ function requestOnce(options) {
|
|
|
2601
2618
|
);
|
|
2602
2619
|
}
|
|
2603
2620
|
});
|
|
2604
|
-
response.on(
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2621
|
+
response.on(
|
|
2622
|
+
"end",
|
|
2623
|
+
() => resolve3({
|
|
2624
|
+
statusCode: response.statusCode ?? 0,
|
|
2625
|
+
headers: response.headers,
|
|
2626
|
+
body
|
|
2627
|
+
})
|
|
2628
|
+
);
|
|
2609
2629
|
};
|
|
2610
2630
|
const request = method === "GET" ? (isHttp ? httpGet : httpsGet)(requestOptions, callback) : (isHttp ? httpRequest : httpsRequest)(requestOptions, callback);
|
|
2611
2631
|
request.on("error", reject);
|
|
@@ -2629,7 +2649,8 @@ async function requestWithRetry(options) {
|
|
|
2629
2649
|
const exponential = (options.baseBackoffMs ?? 1e3) * 2 ** attempt;
|
|
2630
2650
|
await delay(serverDelay ?? exponential, options.signal);
|
|
2631
2651
|
} catch (error) {
|
|
2632
|
-
if (options.signal?.aborted || error instanceof DOMException && error.name === "AbortError")
|
|
2652
|
+
if (options.signal?.aborted || error instanceof DOMException && error.name === "AbortError")
|
|
2653
|
+
throw error;
|
|
2633
2654
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
2634
2655
|
if (attempt < maxAttempts - 1) {
|
|
2635
2656
|
await delay((options.baseBackoffMs ?? 1e3) * 2 ** attempt, options.signal);
|
|
@@ -2955,10 +2976,14 @@ async function lookupRegistry(ecosystem, name, options = {}) {
|
|
|
2955
2976
|
throw new RegistryRateLimitError(response.statusCode, fetcher.host);
|
|
2956
2977
|
}
|
|
2957
2978
|
if (response.statusCode >= 500) {
|
|
2958
|
-
throw new RegistryNetworkError(
|
|
2979
|
+
throw new RegistryNetworkError(
|
|
2980
|
+
`Registry ${fetcher.host} returned ${response.statusCode} after 3 attempts`
|
|
2981
|
+
);
|
|
2959
2982
|
}
|
|
2960
2983
|
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
2961
|
-
throw new RegistryNetworkError(
|
|
2984
|
+
throw new RegistryNetworkError(
|
|
2985
|
+
`Unexpected registry response ${response.statusCode} from ${fetcher.host}`
|
|
2986
|
+
);
|
|
2962
2987
|
}
|
|
2963
2988
|
const json = parseJsonResponse(response, `${fetcher.host}${path}`);
|
|
2964
2989
|
const parsed = fetcher.parser(json, name, ecosystem);
|
|
@@ -3071,15 +3096,18 @@ async function queryOsvBatch(purls, options = {}) {
|
|
|
3071
3096
|
if (!purl) continue;
|
|
3072
3097
|
const vulns = result.results[i]?.vulns;
|
|
3073
3098
|
if (!vulns || vulns.length === 0) continue;
|
|
3074
|
-
advisories.set(
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
vuln.
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3099
|
+
advisories.set(
|
|
3100
|
+
purl,
|
|
3101
|
+
vulns.map((vuln) => ({
|
|
3102
|
+
id: vuln.id,
|
|
3103
|
+
summary: vuln.summary ?? vuln.details ?? "No summary available",
|
|
3104
|
+
severity: mapSeverity(
|
|
3105
|
+
vuln.severity,
|
|
3106
|
+
vuln.database_specific?.severity ?? vuln.affected?.[0]?.database_specific?.severity
|
|
3107
|
+
),
|
|
3108
|
+
aliases: vuln.aliases ?? []
|
|
3109
|
+
}))
|
|
3110
|
+
);
|
|
3083
3111
|
}
|
|
3084
3112
|
}
|
|
3085
3113
|
const evidence = {
|
|
@@ -3149,7 +3177,7 @@ async function runNpmAudit(workspaceRoot2, runner = defaultAuditCommandRunner) {
|
|
|
3149
3177
|
id: advisory.cve ?? advisory.ghsa ?? `npm-${pkg}-${name ?? "unknown"}`,
|
|
3150
3178
|
packageName: pkg,
|
|
3151
3179
|
severity: npmSeverity(info.severity ?? "info"),
|
|
3152
|
-
summary: advisory.title ??
|
|
3180
|
+
summary: advisory.title ?? name ?? "No summary",
|
|
3153
3181
|
fixVersion: info.fixAvailable ?? void 0,
|
|
3154
3182
|
url: advisory.url ?? void 0,
|
|
3155
3183
|
aliases: advisory.cve ? [advisory.cve] : []
|
|
@@ -3858,7 +3886,9 @@ import { randomUUID as randomUUID2 } from "node:crypto";
|
|
|
3858
3886
|
|
|
3859
3887
|
// src/service/finding-factory.ts
|
|
3860
3888
|
function computeDependencyFingerprint(dependencies) {
|
|
3861
|
-
const parts = dependencies.map(
|
|
3889
|
+
const parts = dependencies.map(
|
|
3890
|
+
(dependency) => `${dependency.name}@${dependency.locked ?? dependency.requested ?? "unknown"}`
|
|
3891
|
+
).sort().join(",");
|
|
3862
3892
|
let hash = 0;
|
|
3863
3893
|
for (let index = 0; index < parts.length; index++) {
|
|
3864
3894
|
hash = (hash << 5) - hash + parts.charCodeAt(index);
|
|
@@ -3870,17 +3900,60 @@ function createFindingForStatus(dependencyId, status) {
|
|
|
3870
3900
|
const common = { dependencyId, confidence: 1, evidence: [] };
|
|
3871
3901
|
switch (status) {
|
|
3872
3902
|
case "vulnerable":
|
|
3873
|
-
return {
|
|
3903
|
+
return {
|
|
3904
|
+
...common,
|
|
3905
|
+
id: `finding-${dependencyId}-vuln`,
|
|
3906
|
+
type: "vulnerability",
|
|
3907
|
+
severity: "high",
|
|
3908
|
+
action: "upgrade_patch",
|
|
3909
|
+
rationale: "Known security advisory found for this package"
|
|
3910
|
+
};
|
|
3874
3911
|
case "deprecated":
|
|
3875
|
-
return {
|
|
3912
|
+
return {
|
|
3913
|
+
...common,
|
|
3914
|
+
id: `finding-${dependencyId}-dep`,
|
|
3915
|
+
type: "deprecated",
|
|
3916
|
+
severity: "medium",
|
|
3917
|
+
action: "replace",
|
|
3918
|
+
rationale: "Package is deprecated in the registry"
|
|
3919
|
+
};
|
|
3876
3920
|
case "yanked":
|
|
3877
|
-
return {
|
|
3921
|
+
return {
|
|
3922
|
+
...common,
|
|
3923
|
+
id: `finding-${dependencyId}-yank`,
|
|
3924
|
+
type: "deprecated",
|
|
3925
|
+
severity: "high",
|
|
3926
|
+
action: "replace",
|
|
3927
|
+
rationale: "Package version has been yanked from the registry"
|
|
3928
|
+
};
|
|
3878
3929
|
case "update_available_safe":
|
|
3879
|
-
return {
|
|
3930
|
+
return {
|
|
3931
|
+
...common,
|
|
3932
|
+
id: `finding-${dependencyId}-update`,
|
|
3933
|
+
type: "upgrade",
|
|
3934
|
+
severity: "info",
|
|
3935
|
+
action: "upgrade_minor",
|
|
3936
|
+
rationale: "A newer compatible version is available"
|
|
3937
|
+
};
|
|
3880
3938
|
case "update_available_breaking":
|
|
3881
|
-
return {
|
|
3939
|
+
return {
|
|
3940
|
+
...common,
|
|
3941
|
+
id: `finding-${dependencyId}-major`,
|
|
3942
|
+
type: "upgrade",
|
|
3943
|
+
severity: "low",
|
|
3944
|
+
action: "upgrade_major",
|
|
3945
|
+
rationale: "A newer version is available that may require breaking changes"
|
|
3946
|
+
};
|
|
3882
3947
|
default:
|
|
3883
|
-
return {
|
|
3948
|
+
return {
|
|
3949
|
+
...common,
|
|
3950
|
+
confidence: 0.5,
|
|
3951
|
+
id: `finding-${dependencyId}-investigate`,
|
|
3952
|
+
type: "investigate",
|
|
3953
|
+
severity: "info",
|
|
3954
|
+
action: "investigate",
|
|
3955
|
+
rationale: `Package status is "${status}" \u2014 may need investigation`
|
|
3956
|
+
};
|
|
3884
3957
|
}
|
|
3885
3958
|
}
|
|
3886
3959
|
|
|
@@ -3889,7 +3962,8 @@ async function runEnrichPhase(snapshot, options = {}) {
|
|
|
3889
3962
|
if (options.online === false || options.signal?.aborted) return snapshot;
|
|
3890
3963
|
const grouped = /* @__PURE__ */ new Map();
|
|
3891
3964
|
for (const dependency of snapshot.dependencies) {
|
|
3892
|
-
if (!dependency.purl || dependency.sourceType === "path" || dependency.sourceType === "git")
|
|
3965
|
+
if (!dependency.purl || dependency.sourceType === "path" || dependency.sourceType === "git")
|
|
3966
|
+
continue;
|
|
3893
3967
|
grouped.set(dependency.ecosystem, [...grouped.get(dependency.ecosystem) ?? [], dependency]);
|
|
3894
3968
|
}
|
|
3895
3969
|
const enriched = /* @__PURE__ */ new Map();
|
|
@@ -3909,16 +3983,37 @@ async function runEnrichPhase(snapshot, options = {}) {
|
|
|
3909
3983
|
latestStable: registryEntry.latestStable,
|
|
3910
3984
|
deprecated: registryEntry.deprecated,
|
|
3911
3985
|
yanked: registryEntry.yanked,
|
|
3912
|
-
evidence: [
|
|
3986
|
+
evidence: [
|
|
3987
|
+
{
|
|
3988
|
+
kind: "registry",
|
|
3989
|
+
source: registryEntry.source,
|
|
3990
|
+
retrievedAt: registryEntry.retrievedAt,
|
|
3991
|
+
detail: `latestStable: ${registryEntry.latestStable ?? "N/A"}, license: ${registryEntry.license ?? "N/A"}`
|
|
3992
|
+
}
|
|
3993
|
+
]
|
|
3913
3994
|
} : { privateOrUnresolved: true };
|
|
3914
3995
|
} catch (error) {
|
|
3915
3996
|
const unresolved = error instanceof RegistryNotFoundError || error instanceof RegistryAuthError;
|
|
3916
3997
|
registryStatus = unresolved ? {
|
|
3917
3998
|
privateOrUnresolved: true,
|
|
3918
|
-
evidence: [
|
|
3999
|
+
evidence: [
|
|
4000
|
+
{
|
|
4001
|
+
kind: "registry",
|
|
4002
|
+
source: `${ecosystem} registry for ${name}`,
|
|
4003
|
+
retrievedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4004
|
+
detail: error.message
|
|
4005
|
+
}
|
|
4006
|
+
]
|
|
3919
4007
|
} : {
|
|
3920
4008
|
lookupFailed: true,
|
|
3921
|
-
evidence: [
|
|
4009
|
+
evidence: [
|
|
4010
|
+
{
|
|
4011
|
+
kind: "registry",
|
|
4012
|
+
source: `${ecosystem} registry for ${name}`,
|
|
4013
|
+
retrievedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4014
|
+
detail: error instanceof Error ? error.message : "Registry lookup failed"
|
|
4015
|
+
}
|
|
4016
|
+
]
|
|
3922
4017
|
};
|
|
3923
4018
|
}
|
|
3924
4019
|
let advisoryStatus;
|
|
@@ -3935,7 +4030,11 @@ async function runEnrichPhase(snapshot, options = {}) {
|
|
|
3935
4030
|
for (const dependency of dependencies) {
|
|
3936
4031
|
if (dependency.name !== name) continue;
|
|
3937
4032
|
const status = classifyStatus(dependency, registryStatus, advisoryStatus);
|
|
3938
|
-
const evidence = [
|
|
4033
|
+
const evidence = [
|
|
4034
|
+
...dependency.evidence,
|
|
4035
|
+
...registryStatus?.evidence ?? [],
|
|
4036
|
+
...advisoryStatus?.evidence ?? []
|
|
4037
|
+
];
|
|
3939
4038
|
const license = registryEntry?.license ?? dependency.license;
|
|
3940
4039
|
enriched.set(dependency.id, {
|
|
3941
4040
|
...dependency,
|
|
@@ -3946,13 +4045,16 @@ async function runEnrichPhase(snapshot, options = {}) {
|
|
|
3946
4045
|
status,
|
|
3947
4046
|
evidence
|
|
3948
4047
|
});
|
|
3949
|
-
if (status !== "current" && status !== "local_path" && status !== "git_dependency")
|
|
4048
|
+
if (status !== "current" && status !== "local_path" && status !== "git_dependency")
|
|
4049
|
+
findings.push(createFindingForStatus(dependency.id, status));
|
|
3950
4050
|
const licenseFinding = createLicenseFinding(dependency.id, dependency.name, license);
|
|
3951
4051
|
if (licenseFinding) findings.push(licenseFinding);
|
|
3952
4052
|
}
|
|
3953
4053
|
}
|
|
3954
4054
|
}
|
|
3955
|
-
const enrichedDeps = snapshot.dependencies.map(
|
|
4055
|
+
const enrichedDeps = snapshot.dependencies.map(
|
|
4056
|
+
(dependency) => enriched.get(dependency.id) ?? dependency
|
|
4057
|
+
);
|
|
3956
4058
|
const misalignmentFindings = detectWorkspaceMisalignments(enrichedDeps, snapshot.workspaces);
|
|
3957
4059
|
findings.push(...misalignmentFindings);
|
|
3958
4060
|
return { ...snapshot, dependencies: enrichedDeps, findings };
|
|
@@ -3986,11 +4088,13 @@ async function runInventoryPhase(store, projectId, targetRoot, options = {}) {
|
|
|
3986
4088
|
for (const workspace of workspaces) {
|
|
3987
4089
|
if (options.signal?.aborted) throw new DOMException("TechStack job cancelled", "AbortError");
|
|
3988
4090
|
try {
|
|
3989
|
-
dependencies.push(
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
4091
|
+
dependencies.push(
|
|
4092
|
+
...await ADAPTERS[workspace.ecosystem].inventory(workspace, {
|
|
4093
|
+
projectRoot: targetRoot,
|
|
4094
|
+
includeTransitive: options.includeTransitive,
|
|
4095
|
+
signal: options.signal
|
|
4096
|
+
})
|
|
4097
|
+
);
|
|
3994
4098
|
} catch {
|
|
3995
4099
|
}
|
|
3996
4100
|
if (workspace.coverage === "unsupported") coverage = "partial";
|
|
@@ -4031,32 +4135,56 @@ function generateReport(snapshot, format = "md") {
|
|
|
4031
4135
|
""
|
|
4032
4136
|
];
|
|
4033
4137
|
if (snapshot.workspaces.length > 0) {
|
|
4034
|
-
lines.push(
|
|
4138
|
+
lines.push(
|
|
4139
|
+
"## Workspaces",
|
|
4140
|
+
"",
|
|
4141
|
+
"| Workspace | Ecosystem | Coverage | Deps |",
|
|
4142
|
+
"|---|---|---|---|"
|
|
4143
|
+
);
|
|
4035
4144
|
for (const workspace of snapshot.workspaces) {
|
|
4036
|
-
const count = snapshot.dependencies.filter(
|
|
4037
|
-
|
|
4145
|
+
const count = snapshot.dependencies.filter(
|
|
4146
|
+
(dependency) => dependency.workspaceId === workspace.id
|
|
4147
|
+
).length;
|
|
4148
|
+
lines.push(
|
|
4149
|
+
`| ${workspace.relativeRoot} | ${workspace.ecosystem} | ${workspace.coverage} | ${count} |`
|
|
4150
|
+
);
|
|
4038
4151
|
}
|
|
4039
4152
|
lines.push("");
|
|
4040
4153
|
}
|
|
4041
4154
|
if (snapshot.findings.length > 0) {
|
|
4042
4155
|
lines.push("## Findings", "");
|
|
4043
4156
|
const bySeverity = /* @__PURE__ */ new Map();
|
|
4044
|
-
for (const finding of snapshot.findings)
|
|
4157
|
+
for (const finding of snapshot.findings)
|
|
4158
|
+
bySeverity.set(finding.severity, [...bySeverity.get(finding.severity) ?? [], finding]);
|
|
4045
4159
|
for (const severity of ["critical", "high", "medium", "low", "info"]) {
|
|
4046
4160
|
const findings = bySeverity.get(severity);
|
|
4047
4161
|
if (!findings?.length) continue;
|
|
4048
|
-
lines.push(
|
|
4162
|
+
lines.push(
|
|
4163
|
+
`### ${severity.charAt(0).toUpperCase() + severity.slice(1)} (${findings.length})`,
|
|
4164
|
+
""
|
|
4165
|
+
);
|
|
4049
4166
|
for (const finding of findings) {
|
|
4050
|
-
const dependency = snapshot.dependencies.find(
|
|
4051
|
-
|
|
4167
|
+
const dependency = snapshot.dependencies.find(
|
|
4168
|
+
(candidate) => candidate.id === finding.dependencyId
|
|
4169
|
+
);
|
|
4170
|
+
lines.push(
|
|
4171
|
+
`- **${dependency?.name ?? finding.dependencyId}** \u2014 ${finding.type} \u2014 ${finding.rationale}`
|
|
4172
|
+
);
|
|
4052
4173
|
}
|
|
4053
4174
|
lines.push("");
|
|
4054
4175
|
}
|
|
4055
4176
|
}
|
|
4056
4177
|
if (snapshot.dependencies.length > 0) {
|
|
4057
|
-
lines.push(
|
|
4178
|
+
lines.push(
|
|
4179
|
+
"## Dependencies",
|
|
4180
|
+
"",
|
|
4181
|
+
"| Name | Ecosystem | Status | Locked | Latest |",
|
|
4182
|
+
"|---|---|---|---|---|"
|
|
4183
|
+
);
|
|
4058
4184
|
for (const dependency of snapshot.dependencies) {
|
|
4059
|
-
lines.push(
|
|
4185
|
+
lines.push(
|
|
4186
|
+
`| ${dependency.name} | ${dependency.ecosystem} | ${dependency.status} | ${dependency.locked ?? "\u2014"} | ${dependency.latestStable ?? "\u2014"} |`
|
|
4187
|
+
);
|
|
4060
4188
|
}
|
|
4061
4189
|
}
|
|
4062
4190
|
return lines.join("\n");
|
|
@@ -4184,10 +4312,16 @@ var TechStackEngine = class {
|
|
|
4184
4312
|
try {
|
|
4185
4313
|
throwIfAborted();
|
|
4186
4314
|
updateJob("discovering", { phase: "discovering", completed: 0, total: 1 });
|
|
4187
|
-
let snapshot = await this.inventory(
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4315
|
+
let snapshot = await this.inventory(
|
|
4316
|
+
projectId,
|
|
4317
|
+
options.targetRoot,
|
|
4318
|
+
jobId,
|
|
4319
|
+
(phase, completed, total) => {
|
|
4320
|
+
throwIfAborted();
|
|
4321
|
+
updateJob(phase, { phase, completed, total });
|
|
4322
|
+
},
|
|
4323
|
+
{ includeTransitive: options.includeTransitive, signal: options.signal }
|
|
4324
|
+
);
|
|
4191
4325
|
throwIfAborted();
|
|
4192
4326
|
if (options.online !== false) {
|
|
4193
4327
|
updateJob("enriching", { phase: "enriching", completed: 0, total: 1 });
|
|
@@ -4203,9 +4337,14 @@ var TechStackEngine = class {
|
|
|
4203
4337
|
}
|
|
4204
4338
|
this.store.saveSnapshot(snapshot);
|
|
4205
4339
|
updateJob("completed", { phase: "completed", completed: 1, total: 1 });
|
|
4206
|
-
return {
|
|
4340
|
+
return {
|
|
4341
|
+
snapshot,
|
|
4342
|
+
job: { ...job, status: "completed", completedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
4343
|
+
};
|
|
4207
4344
|
} catch (error) {
|
|
4208
|
-
updateJob(
|
|
4345
|
+
updateJob(
|
|
4346
|
+
options.signal?.aborted || error instanceof DOMException && error.name === "AbortError" ? "cancelled" : "failed"
|
|
4347
|
+
);
|
|
4209
4348
|
throw error;
|
|
4210
4349
|
}
|
|
4211
4350
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/techstack",
|
|
3
|
-
"version": "0.319.
|
|
3
|
+
"version": "0.319.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack TechStack — cross-language dependency intelligence for the active target project: discover, inventory, enrich, analyze, and report.",
|
|
6
6
|
"repository": {
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"!dist/**/*.map"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@wrongstack/core": "0.319.
|
|
30
|
-
"@wrongstack/persistence": "0.319.
|
|
31
|
-
"@wrongstack/tools": "0.319.
|
|
29
|
+
"@wrongstack/core": "0.319.1",
|
|
30
|
+
"@wrongstack/persistence": "0.319.1",
|
|
31
|
+
"@wrongstack/tools": "0.319.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^26.2.0",
|