lurqrun 0.1.7 → 0.1.9
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/README.md +312 -148
- package/dist/{auditHandler-JCOJSUYW.js → auditHandler-G7PHKMZR.js} +12 -11
- package/dist/autopilotInit-JO2SH5K3.js +266 -0
- package/dist/bin/lurq.js +238 -83
- package/dist/{browserAuth-B6G27USY.js → browserAuth-PVYODBNR.js} +4 -3
- package/dist/{cache-DKI34SVS.js → cache-ND4VOUVY.js} +2 -2
- package/dist/check-A3FJE62G.js +2613 -0
- package/dist/{check-2FDGUOV7.js → check-PQSQ2MMA.js} +1 -1
- package/dist/checkUpgrade-Q4CV7GXG.js +24 -0
- package/dist/{chunk-BCANRN5B.js → chunk-2GAYNIHG.js} +8 -1
- package/dist/chunk-2LXYRDUN.js +92 -0
- package/dist/chunk-2XMD4H2I.js +243 -0
- package/dist/chunk-3D2MPGOQ.js +181 -0
- package/dist/chunk-53SG6XWA.js +141 -0
- package/dist/chunk-5ABNDB3I.js +37 -0
- package/dist/chunk-5ALIJFDA.js +114 -0
- package/dist/{chunk-LYMPYETA.js → chunk-6353IJJ5.js} +270 -6
- package/dist/{chunk-M6NYEXCW.js → chunk-64DPQSXV.js} +1 -1
- package/dist/chunk-64YOP5BQ.js +320 -0
- package/dist/{chunk-2VMOWSRY.js → chunk-6DM6UQFD.js} +19 -1
- package/dist/{chunk-NH5I4KHH.js → chunk-75LMEMJ3.js} +1 -5
- package/dist/{chunk-FW6BD2NO.js → chunk-ABH4BTK4.js} +458 -121
- package/dist/chunk-AHRDDKNO.js +90 -0
- package/dist/chunk-AQ6IVSFB.js +119 -0
- package/dist/chunk-BM3LF5T6.js +132 -0
- package/dist/chunk-D3K6IZVR.js +12845 -0
- package/dist/chunk-DONTW3NE.js +171 -0
- package/dist/{chunk-UDVNA7EA.js → chunk-ETY4FSOW.js} +161 -8
- package/dist/{chunk-VL2E5T2A.js → chunk-FHRT32JH.js} +6 -3
- package/dist/{chunk-DWAGCNEL.js → chunk-GM5Y555O.js} +36 -103
- package/dist/{chunk-6L3U7DZ5.js → chunk-GNNGXVCG.js} +1 -1
- package/dist/{chunk-PSX3GIAN.js → chunk-HWFX4BC4.js} +2 -1
- package/dist/{checkUpgrade-F2WESBPM.js → chunk-ILKYIVYV.js} +72 -10
- package/dist/{chunk-HWBBVLOT.js → chunk-J3NUERU7.js} +1 -1
- package/dist/chunk-J3U2CPSK.js +136 -0
- package/dist/{chunk-Y5GI7RL3.js → chunk-J6XQ7OPZ.js} +9 -1
- package/dist/{chunk-3N3Q3DSW.js → chunk-JGIJZJ3X.js} +8 -8
- package/dist/{chunk-DB2VF5UD.js → chunk-JJCQVOC6.js} +16 -11
- package/dist/{chunk-LBFGSSF2.js → chunk-KMV4Y6R3.js} +2 -1
- package/dist/{chunk-L6N4JFW2.js → chunk-KP5ZDDPE.js} +81 -4
- package/dist/{chunk-CW5HJ3LX.js → chunk-KT7PWDNV.js} +1 -1
- package/dist/{chunk-SJOVFF7B.js → chunk-KYKSZ3XZ.js} +3 -2
- package/dist/{chunk-GPTKSNR6.js → chunk-L3V7AVM6.js} +2 -1
- package/dist/{chunk-CUP36ND3.js → chunk-M2CYYBAH.js} +145 -36
- package/dist/{chunk-IQ4SDKKY.js → chunk-N2NYPTRU.js} +31 -228
- package/dist/chunk-N4TJO44E.js +603 -0
- package/dist/chunk-NEZNAPPN.js +41 -0
- package/dist/{chunk-WQ23REYT.js → chunk-NGMJQBOV.js} +1 -1
- package/dist/{chunk-BULZAX2U.js → chunk-POHTPT37.js} +55 -38
- package/dist/{chunk-EA3DP5HG.js → chunk-PZZU3FQY.js} +155 -25
- package/dist/{chunk-4F7ZESPO.js → chunk-Q7CMOU5C.js} +3 -5
- package/dist/{chunk-LRD4GBYE.js → chunk-QUIMQ7YL.js} +1 -1
- package/dist/chunk-RG3YLU6E.js +49 -0
- package/dist/{chunk-4ZYCIMY4.js → chunk-SFY5MPJE.js} +25 -4
- package/dist/{chunk-ST2X2GYN.js → chunk-SHZFOSXC.js} +109 -20
- package/dist/{chunk-CIDWNUAP.js → chunk-SOXFMMXC.js} +8 -2
- package/dist/{chunk-B43LCXBM.js → chunk-SWMWVK2R.js} +81 -16
- package/dist/{chunk-J5FZYN4A.js → chunk-TVOCVIM7.js} +1 -1
- package/dist/{chunk-U5AT3TD4.js → chunk-X4W64MHU.js} +1735 -1858
- package/dist/{chunk-BV32UNLL.js → chunk-XA2SLNBR.js} +2 -2
- package/dist/{chunk-AHBP533R.js → chunk-XBTYJQEK.js} +7 -5
- package/dist/chunk-XGOV6KV4.js +413 -0
- package/dist/{chunk-OQS4GUSI.js → chunk-XIFQIFSZ.js} +210 -661
- package/dist/{chunk-XBIDMHSK.js → chunk-XLXPVN3Y.js} +2 -34
- package/dist/{chunk-LO35HLQZ.js → chunk-ZJ5XHA77.js} +25 -4
- package/dist/{client-IRKAMDYZ.js → client-SJIWK4CK.js} +3 -3
- package/dist/{commands-FIL5ET6P.js → commands-6BBLN5N5.js} +145 -33
- package/dist/{compat-R3UTFQ5Q.js → compat-6B3CRT5F.js} +6 -6
- package/dist/constants-HX3RTD6Q.js +24 -0
- package/dist/{dashboardIssue-WYYMBFIB.js → dashboardIssue-KBKLQMGC.js} +105 -23
- package/dist/{diff-GZA2774U.js → diff-I4SNUHU3.js} +1 -1
- package/dist/domains-RKRPVP5A.js +14 -0
- package/dist/env-UN6KGZ7S.js +15 -0
- package/dist/envCheck-IL22OTSC.js +71 -0
- package/dist/{extract-NKGKLIVT.js → extract-6WG265BL.js} +14 -3
- package/dist/{fetch-QYTUJVG2.js → fetch-GHIP3VHN.js} +4 -4
- package/dist/{firstTouch-QQTJH4QX.js → firstTouch-M64T2MWF.js} +1 -1
- package/dist/fix-Z7ZY55HB.js +220 -0
- package/dist/{handlers-GEJAVZDK.js → handlers-GOXW2GWZ.js} +19 -17
- package/dist/hook-TWFLIDK3.js +412 -0
- package/dist/{http-ETPYMBGD.js → http-7VF2RE2D.js} +2097 -509
- package/dist/{ingestQueue-VKN4KBIF.js → ingestQueue-RSEPMORK.js} +14 -12
- package/dist/{install-EK3W2COX.js → install-55EPX34C.js} +6 -6
- package/dist/{installSkill-VQJWJVCL.js → installSkill-A7KKPDMS.js} +21 -3
- package/dist/manifest-ENMIC54B.js +13 -0
- package/dist/{mcp-3JHD5SUZ.js → mcp-CM6CDLHM.js} +60 -32
- package/dist/mcpConfig-FENV6NI7.js +80 -0
- package/dist/{mcpHandlers-QFN4MCFL.js → mcpHandlers-2UCLKMWN.js} +7 -7
- package/dist/{mcpScan-QZHWENIP.js → mcpScan-CKBQNK5H.js} +306 -442
- package/dist/{mcpScanWorkflow-JPYCBGC4.js → mcpScanWorkflow-NFUD3S7U.js} +29 -12
- package/dist/{mcpStack-BYJCZNIV.js → mcpStack-FLQ6XY2P.js} +1 -1
- package/dist/model-ASMZWLM7.js +214 -0
- package/dist/{packages-THVFMWFP.js → packages-2XRHNBJ7.js} +3 -5
- package/dist/{plan-V4OEI3J4.js → plan-XRZS6C7Z.js} +23 -21
- package/dist/{planView-JRXGCG2N.js → planView-ZAKRTGVS.js} +4 -1
- package/dist/{policy-3P6BGRDN.js → policy-EQT43P7S.js} +5 -5
- package/dist/probe-ZEZFNIQZ.js +610 -0
- package/dist/publicUpgrades-JZEOV24A.js +42 -0
- package/dist/{references-6N7VEPHT.js → references-I6Y4BSRR.js} +3 -1
- package/dist/{release-4U4ABNSJ.js → release-EUOM5I3R.js} +5 -5
- package/dist/{remote-YGCXYI4V.js → remote-I5O43WAY.js} +13 -3
- package/dist/rename-OS6JYBZQ.js +204 -0
- package/dist/{reportRuns-CKQQYLY4.js → reportRuns-O4X7ZFTX.js} +15 -2
- package/dist/safeFetch-COERXGFN.js +19 -0
- package/dist/{sandbox-MEZLAODF.js → sandbox-46TZNYZT.js} +4 -4
- package/dist/sarif-WLVJFJ7U.js +125 -0
- package/dist/server-LROMHS62.js +59 -0
- package/dist/{surface-YL6O67W3.js → surface-CZMHQLV3.js} +5 -3
- package/dist/{surface-V62GE554.js → surface-OH4TZR32.js} +18 -16
- package/dist/{surfaceHandlers-X2IZKA3E.js → surfaceHandlers-BDYRZMAC.js} +5 -5
- package/dist/{typecheck-X7U5ELMR.js → typecheck-NGOKNAJY.js} +23 -9
- package/dist/{uninstall-P7RGBX6G.js → uninstall-4E5YUDQO.js} +30 -4
- package/dist/{upgrade-NQ5IWFZB.js → upgrade-S72W7JVC.js} +86 -32
- package/dist/{upgradePlan-A4LRKLEG.js → upgradePlan-F3OUNL3E.js} +46 -11
- package/dist/upkeepHandler-7Q56A4UA.js +13 -0
- package/dist/{userConfig-Z3GQE776.js → userConfig-ICASHI47.js} +1 -1
- package/dist/{watch-DGFMWUDL.js → watch-LZPOKWCL.js} +24 -34
- package/package.json +3 -2
- package/templates/skill-instructions.md +22 -3
- package/dist/chunk-6AAEVQ7I.js +0 -6336
- package/dist/chunk-I2JUKN6Z.js +0 -24
- package/dist/server-KFUD5X57.js +0 -55
|
@@ -5,16 +5,12 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
installKey,
|
|
7
7
|
queryVulnerableInstalls
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import {
|
|
10
|
-
DEFAULT_REPO_POLICY,
|
|
11
|
-
REPO_DRIFT_DETAIL_CAP,
|
|
12
|
-
TRANSITIVE_DETAIL_CAP
|
|
13
|
-
} from "./chunk-BCANRN5B.js";
|
|
8
|
+
} from "./chunk-QUIMQ7YL.js";
|
|
14
9
|
import {
|
|
15
10
|
ADOPTION,
|
|
16
11
|
COMPOSITE,
|
|
17
12
|
CONFIDENCE,
|
|
13
|
+
DISCOVERY,
|
|
18
14
|
EFFICIENCY,
|
|
19
15
|
FIELD,
|
|
20
16
|
HEALTH_WEIGHTS,
|
|
@@ -23,30 +19,24 @@ import {
|
|
|
23
19
|
QUALITY,
|
|
24
20
|
QUALITY_WEIGHTS,
|
|
25
21
|
RELIABILITY
|
|
26
|
-
} from "./chunk-
|
|
27
|
-
import {
|
|
28
|
-
withBudget
|
|
29
|
-
} from "./chunk-4ESO33J6.js";
|
|
22
|
+
} from "./chunk-6DM6UQFD.js";
|
|
30
23
|
import {
|
|
31
24
|
getCompatMetadata
|
|
32
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-KT7PWDNV.js";
|
|
33
26
|
import {
|
|
34
27
|
fetchNpmCompatAtVersion,
|
|
35
28
|
fetchNpmRegistry,
|
|
36
29
|
npmPackageExists
|
|
37
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-XBTYJQEK.js";
|
|
38
31
|
import {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
httpRequest
|
|
42
|
-
} from "./chunk-SJOVFF7B.js";
|
|
32
|
+
withBudget
|
|
33
|
+
} from "./chunk-4ESO33J6.js";
|
|
43
34
|
import {
|
|
44
|
-
ensureSeedEntry,
|
|
45
35
|
getPackageByName,
|
|
46
36
|
stampFirstRequester,
|
|
47
37
|
upsertPackage,
|
|
48
38
|
upsertPackageVersions
|
|
49
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-75LMEMJ3.js";
|
|
50
40
|
import {
|
|
51
41
|
CATEGORIES,
|
|
52
42
|
DEFAULT_ECOSYSTEM,
|
|
@@ -54,13 +44,33 @@ import {
|
|
|
54
44
|
isFrontendCategory
|
|
55
45
|
} from "./chunk-VLRHFMGJ.js";
|
|
56
46
|
import {
|
|
47
|
+
discoveryQueue,
|
|
57
48
|
packageVersions,
|
|
58
49
|
packages,
|
|
59
50
|
recommendationOutcomes,
|
|
60
51
|
repoAlerts,
|
|
52
|
+
repoPolicyDefaults,
|
|
61
53
|
repos,
|
|
62
|
-
seedPackages
|
|
63
|
-
|
|
54
|
+
seedPackages,
|
|
55
|
+
upgradeRuns
|
|
56
|
+
} from "./chunk-6353IJJ5.js";
|
|
57
|
+
import {
|
|
58
|
+
GithubAppError,
|
|
59
|
+
installationPost
|
|
60
|
+
} from "./chunk-GM5Y555O.js";
|
|
61
|
+
import {
|
|
62
|
+
DEFAULT_REPO_POLICY,
|
|
63
|
+
REPO_DRIFT_DETAIL_CAP,
|
|
64
|
+
TRANSITIVE_DETAIL_CAP
|
|
65
|
+
} from "./chunk-2GAYNIHG.js";
|
|
66
|
+
import {
|
|
67
|
+
HttpError,
|
|
68
|
+
httpGetJson,
|
|
69
|
+
httpRequest
|
|
70
|
+
} from "./chunk-KYKSZ3XZ.js";
|
|
71
|
+
import {
|
|
72
|
+
WORKFLOW_PATH
|
|
73
|
+
} from "./chunk-SWMWVK2R.js";
|
|
64
74
|
import {
|
|
65
75
|
require_semver
|
|
66
76
|
} from "./chunk-3GHJR7XR.js";
|
|
@@ -73,7 +83,7 @@ import {
|
|
|
73
83
|
import {
|
|
74
84
|
CACHE_TTL,
|
|
75
85
|
EMBEDDING_DIM
|
|
76
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-L3V7AVM6.js";
|
|
77
87
|
import {
|
|
78
88
|
__toESM,
|
|
79
89
|
init_esm_shims
|
|
@@ -82,9 +92,46 @@ import {
|
|
|
82
92
|
// src/pipeline/ingestQueue.ts
|
|
83
93
|
init_esm_shims();
|
|
84
94
|
|
|
95
|
+
// src/db/discovery.ts
|
|
96
|
+
init_esm_shims();
|
|
97
|
+
import { eq, sql } from "drizzle-orm";
|
|
98
|
+
async function getQueuedNames(db) {
|
|
99
|
+
const rows = await db.select({ name: discoveryQueue.name }).from(discoveryQueue);
|
|
100
|
+
return new Set(rows.map((r) => r.name));
|
|
101
|
+
}
|
|
102
|
+
async function enqueueCandidates(db, candidates) {
|
|
103
|
+
if (candidates.length === 0) return 0;
|
|
104
|
+
const rows = candidates.map((c) => ({ name: c.name, discoveredVia: c.via }));
|
|
105
|
+
const inserted = await db.insert(discoveryQueue).values(rows).onConflictDoNothing({ target: discoveryQueue.name }).returning({ id: discoveryQueue.id });
|
|
106
|
+
return inserted.length;
|
|
107
|
+
}
|
|
108
|
+
async function enqueueDemand(db, name, requestedByOwnerId) {
|
|
109
|
+
await db.insert(discoveryQueue).values({ name, discoveredVia: "reactive", requestedByOwnerId }).onConflictDoUpdate({
|
|
110
|
+
target: discoveryQueue.name,
|
|
111
|
+
set: {
|
|
112
|
+
discoveredVia: "reactive",
|
|
113
|
+
status: sql`case when ${discoveryQueue.status} = 'failed' then 'failed' else 'pending' end`,
|
|
114
|
+
requestedByOwnerId: sql`coalesce(${discoveryQueue.requestedByOwnerId}, excluded.requested_by_owner_id)`
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async function recordIngestFailure(db, name, maxAttempts) {
|
|
119
|
+
const [row] = await db.update(discoveryQueue).set({
|
|
120
|
+
attempts: sql`${discoveryQueue.attempts} + 1`,
|
|
121
|
+
status: sql`case when ${discoveryQueue.attempts} + 1 >= ${maxAttempts} then 'failed' else ${discoveryQueue.status} end`
|
|
122
|
+
}).where(eq(discoveryQueue.name, name)).returning({ attempts: discoveryQueue.attempts });
|
|
123
|
+
return row?.attempts ?? 0;
|
|
124
|
+
}
|
|
125
|
+
async function setDiscoveryStatus(db, name, data) {
|
|
126
|
+
await db.update(discoveryQueue).set({
|
|
127
|
+
status: data.status,
|
|
128
|
+
...data.preScore !== void 0 ? { preScore: data.preScore } : {}
|
|
129
|
+
}).where(eq(discoveryQueue.name, name));
|
|
130
|
+
}
|
|
131
|
+
|
|
85
132
|
// src/pipeline/single.ts
|
|
86
133
|
init_esm_shims();
|
|
87
|
-
import { and as and5, eq as
|
|
134
|
+
import { and as and5, eq as eq6, isNotNull, sql as sql6 } from "drizzle-orm";
|
|
88
135
|
|
|
89
136
|
// src/ingestion/collect.ts
|
|
90
137
|
init_esm_shims();
|
|
@@ -181,9 +228,7 @@ async function fetchAdvisories(name, version, fetchImpl) {
|
|
|
181
228
|
})
|
|
182
229
|
)
|
|
183
230
|
);
|
|
184
|
-
return details.filter(
|
|
185
|
-
(d) => d.status === "fulfilled"
|
|
186
|
-
).map((d) => parseAdvisoryDetail(d.value.data));
|
|
231
|
+
return details.filter((d) => d.status === "fulfilled").map((d) => parseAdvisoryDetail(d.value.data));
|
|
187
232
|
}
|
|
188
233
|
async function fetchDependents(name, version, fetchImpl) {
|
|
189
234
|
const url = `https://${HOST2}/v3alpha/systems/npm/packages/${encodeName(name)}/versions/${encodeURIComponent(version)}:dependents`;
|
|
@@ -292,10 +337,12 @@ function last90DayRange(now = /* @__PURE__ */ new Date()) {
|
|
|
292
337
|
}
|
|
293
338
|
async function fetchWeeklyDownloads(name, fetchImpl) {
|
|
294
339
|
try {
|
|
295
|
-
const { data } = await httpGetJson(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
340
|
+
const { data } = await httpGetJson(`https://${HOST4}/downloads/point/last-week/${name}`, {
|
|
341
|
+
host: HOST4,
|
|
342
|
+
ttlMs: CACHE_TTL.npmDownloads,
|
|
343
|
+
retries: 5,
|
|
344
|
+
fetchImpl
|
|
345
|
+
});
|
|
299
346
|
return parseWeeklyDownloads(data);
|
|
300
347
|
} catch (err) {
|
|
301
348
|
if (err instanceof HttpError && err.status === 404) return null;
|
|
@@ -328,7 +375,11 @@ async function attempt(source, errors, fn) {
|
|
|
328
375
|
async function collectSignals(name, category, deps = {}) {
|
|
329
376
|
const { githubToken, fetchImpl } = deps;
|
|
330
377
|
const errors = [];
|
|
331
|
-
const registry = await attempt(
|
|
378
|
+
const registry = await attempt(
|
|
379
|
+
"npm-registry",
|
|
380
|
+
errors,
|
|
381
|
+
() => fetchNpmRegistry(name, fetchImpl, { fresh: deps.freshRegistry })
|
|
382
|
+
);
|
|
332
383
|
const repo = registry?.repo ?? null;
|
|
333
384
|
const version = registry?.latestVersion ?? null;
|
|
334
385
|
const [downloads, github, depsDev, bundle] = await Promise.all([
|
|
@@ -337,7 +388,11 @@ async function collectSignals(name, category, deps = {}) {
|
|
|
337
388
|
const downloadGrowth90d = await fetchDownloadGrowth(name, fetchImpl);
|
|
338
389
|
return { weeklyDownloads, downloadGrowth90d };
|
|
339
390
|
}),
|
|
340
|
-
repo && githubToken ? attempt(
|
|
391
|
+
repo && githubToken ? attempt(
|
|
392
|
+
"github",
|
|
393
|
+
errors,
|
|
394
|
+
() => fetchGithubRepo(repo.owner, repo.repo, githubToken, fetchImpl)
|
|
395
|
+
) : Promise.resolve(null),
|
|
341
396
|
attempt("deps-dev", errors, () => fetchDepsDev(name, version, repo, fetchImpl)),
|
|
342
397
|
attempt("bundlephobia", errors, () => fetchBundlephobia(name, category, fetchImpl))
|
|
343
398
|
]);
|
|
@@ -502,7 +557,9 @@ var OpenAISummaryProvider = class {
|
|
|
502
557
|
inferredCategory
|
|
503
558
|
};
|
|
504
559
|
} catch (err) {
|
|
505
|
-
logger.warn(
|
|
560
|
+
logger.warn(
|
|
561
|
+
`summary LLM failed for ${input.name}, using fallback: ${err.message}`
|
|
562
|
+
);
|
|
506
563
|
return new FallbackSummaryProvider().generate(input);
|
|
507
564
|
}
|
|
508
565
|
}
|
|
@@ -542,30 +599,84 @@ async function buildSummaryInput(signals, category, fetchImpl) {
|
|
|
542
599
|
// src/search/categoryInference.ts
|
|
543
600
|
init_esm_shims();
|
|
544
601
|
var RULES = [
|
|
545
|
-
{
|
|
546
|
-
|
|
602
|
+
{
|
|
603
|
+
category: "meta-framework",
|
|
604
|
+
patterns: /\b(meta-?framework|next\.?js|nuxt|remix|astro|gatsby|sveltekit|full-?stack framework)\b/
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
category: "state-management",
|
|
608
|
+
patterns: /\b(state management|global state|store|redux|zustand|jotai|mobx|atoms?)\b/
|
|
609
|
+
},
|
|
547
610
|
{ category: "routing", patterns: /\b(rout(e|er|ing)|navigation|url matching)\b/ },
|
|
548
|
-
{
|
|
549
|
-
|
|
550
|
-
|
|
611
|
+
{
|
|
612
|
+
category: "orm",
|
|
613
|
+
patterns: /\b(orm|object-?relational|query builder|prisma|drizzle|sequelize|typeorm)\b/
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
category: "database-client",
|
|
617
|
+
patterns: /\b(database (driver|client)|postgres|mysql|mongo(db)?|redis|sqlite|db driver)\b/
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
category: "ui-component-library",
|
|
621
|
+
patterns: /\b(component library|ui kit|ui components?|design system|buttons?|modal|dialog)\b/
|
|
622
|
+
},
|
|
551
623
|
{ category: "styling", patterns: /\b(styl(e|ing)|css|tailwind|sass|scss|class ?names?|theme)\b/ },
|
|
552
624
|
{ category: "forms", patterns: /\b(forms?|form (state|library|handling)|input handling)\b/ },
|
|
553
|
-
{
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
{
|
|
558
|
-
|
|
559
|
-
|
|
625
|
+
{
|
|
626
|
+
category: "validation",
|
|
627
|
+
patterns: /\b(validat(e|ion|or)|schema|parse input|type ?safe parsing)\b/
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
category: "data-fetching",
|
|
631
|
+
patterns: /\b(data fetching|server state|react query|swr|graphql client|caching queries)\b/
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
category: "http-client",
|
|
635
|
+
patterns: /\b(http client|fetch wrapper|rest client|make (a )?request|ajax|api calls?)\b/
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
category: "auth",
|
|
639
|
+
patterns: /\b(auth(entication|orization)?|login|session|jwt|oauth|password hashing)\b/
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
category: "testing",
|
|
643
|
+
patterns: /\b(test(ing|s)?|unit test|e2e|assertion|mocking|test runner)\b/
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
category: "bundler",
|
|
647
|
+
patterns: /\b(bundler|bundle (modules?|code)|webpack|rollup|esbuild|parcel)\b/
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
category: "build-tool",
|
|
651
|
+
patterns: /\b(build tool|dev server|monorepo|task runner|compile|transpile)\b/
|
|
652
|
+
},
|
|
560
653
|
// date-time precedes linting: "date formatting" must not be captured by linting's
|
|
561
654
|
// bare "formatting" token before the date rule is reached (first-match-wins).
|
|
562
|
-
{
|
|
563
|
-
|
|
655
|
+
{
|
|
656
|
+
category: "date-time",
|
|
657
|
+
patterns: /\b(dates?|date ?time|time(zone)?s?|calendar|parse dates?|format dates?)\b/
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
category: "linting",
|
|
661
|
+
patterns: /\b(lint(er|ing)?|formatter|formatting|code (quality|style|format)|prettier|eslint)\b/
|
|
662
|
+
},
|
|
564
663
|
{ category: "animation", patterns: /\b(animat(e|ion)|motion|transition|spring|gsap|3d)\b/ },
|
|
565
|
-
{
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
664
|
+
{
|
|
665
|
+
category: "charts",
|
|
666
|
+
patterns: /\b(charts?|graphs?|data ?vis(ualization)?|plots?|dashboards?)\b/
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
category: "i18n",
|
|
670
|
+
patterns: /\b(i18n|internationali[sz]ation|localization|translat(e|ion)|locale)\b/
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
category: "framework",
|
|
674
|
+
patterns: /\b(framework|react|vue|svelte|angular|web server|backend framework)\b/
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
category: "utility",
|
|
678
|
+
patterns: /\b(debounce|throttle|deep ?clone|slugify|uuid|util(ity|ities)?|helper|lodash|retry)\b/
|
|
679
|
+
}
|
|
569
680
|
];
|
|
570
681
|
function inferCategory(need) {
|
|
571
682
|
const text = need.toLowerCase();
|
|
@@ -636,17 +747,24 @@ function computeMaintenance(input, now) {
|
|
|
636
747
|
const components = [];
|
|
637
748
|
const days = daysSince(input.lastReleaseAt, now);
|
|
638
749
|
if (days !== null) {
|
|
639
|
-
const recency = days <= MAINTENANCE.freshDays ? 100 : clamp(
|
|
750
|
+
const recency = days <= MAINTENANCE.freshDays ? 100 : clamp(
|
|
751
|
+
100 * (MAINTENANCE.staleDays - days) / (MAINTENANCE.staleDays - MAINTENANCE.freshDays)
|
|
752
|
+
);
|
|
640
753
|
components.push({ value: recency, weight: MAINTENANCE_WEIGHTS.recency });
|
|
641
754
|
}
|
|
642
755
|
if (input.releasesLast12mo !== null) {
|
|
643
|
-
const cadence = clamp(
|
|
756
|
+
const cadence = clamp(
|
|
757
|
+
Math.min(input.releasesLast12mo, MAINTENANCE.cadenceCap) / MAINTENANCE.cadenceCap * 100
|
|
758
|
+
);
|
|
644
759
|
components.push({ value: cadence, weight: MAINTENANCE_WEIGHTS.cadence });
|
|
645
760
|
}
|
|
646
761
|
if (input.openIssues !== null && input.closedIssues !== null) {
|
|
647
762
|
const total = input.openIssues + input.closedIssues;
|
|
648
763
|
if (total > 0) {
|
|
649
|
-
components.push({
|
|
764
|
+
components.push({
|
|
765
|
+
value: input.closedIssues / total * 100,
|
|
766
|
+
weight: MAINTENANCE_WEIGHTS.closeRatio
|
|
767
|
+
});
|
|
650
768
|
}
|
|
651
769
|
}
|
|
652
770
|
return weightedAverage(components);
|
|
@@ -905,7 +1023,7 @@ function createEmbeddingProvider(fetchImpl) {
|
|
|
905
1023
|
|
|
906
1024
|
// src/db/outcomes.ts
|
|
907
1025
|
init_esm_shims();
|
|
908
|
-
import { and, count, desc, eq, inArray, sql } from "drizzle-orm";
|
|
1026
|
+
import { and, count, desc, eq as eq2, inArray, sql as sql2 } from "drizzle-orm";
|
|
909
1027
|
import { alias } from "drizzle-orm/pg-core";
|
|
910
1028
|
|
|
911
1029
|
// src/core/successors.ts
|
|
@@ -999,13 +1117,13 @@ function buildLearnedSuccessors(observed, isSuperseded) {
|
|
|
999
1117
|
async function recordOutcome(db, outcome) {
|
|
1000
1118
|
await db.insert(recommendationOutcomes).values(outcome);
|
|
1001
1119
|
}
|
|
1002
|
-
var SUCCESS_PREDICATE =
|
|
1120
|
+
var SUCCESS_PREDICATE = sql2`${recommendationOutcomes.accepted} AND (${recommendationOutcomes.buildSignal} IS NULL OR ${recommendationOutcomes.buildSignal} <> 'failed')`;
|
|
1003
1121
|
async function getFieldEvidence(db, names) {
|
|
1004
1122
|
if (names && names.length === 0) return /* @__PURE__ */ new Map();
|
|
1005
1123
|
const rows = await db.select({
|
|
1006
1124
|
packageName: recommendationOutcomes.packageName,
|
|
1007
1125
|
reports: count(),
|
|
1008
|
-
successes:
|
|
1126
|
+
successes: sql2`count(*) FILTER (WHERE ${SUCCESS_PREDICATE})`.mapWith(Number)
|
|
1009
1127
|
}).from(recommendationOutcomes).where(names ? inArray(recommendationOutcomes.packageName, names) : void 0).groupBy(recommendationOutcomes.packageName);
|
|
1010
1128
|
return new Map(
|
|
1011
1129
|
rows.map((r) => [r.packageName, { reports: Number(r.reports), successes: r.successes }])
|
|
@@ -1018,20 +1136,20 @@ async function learnSuccessions(db) {
|
|
|
1018
1136
|
const rows = await db.select({
|
|
1019
1137
|
from: failed.packageName,
|
|
1020
1138
|
to: worked.packageName,
|
|
1021
|
-
owners:
|
|
1022
|
-
observations:
|
|
1139
|
+
owners: sql2`count(DISTINCT ${failed.ownerId})`.mapWith(Number),
|
|
1140
|
+
observations: sql2`count(*)`.mapWith(Number)
|
|
1023
1141
|
}).from(failed).innerJoin(
|
|
1024
1142
|
worked,
|
|
1025
|
-
|
|
1143
|
+
sql2`${worked.ownerId} = ${failed.ownerId}
|
|
1026
1144
|
AND ${worked.need} = ${failed.need}
|
|
1027
1145
|
AND ${worked.packageName} <> ${failed.packageName}
|
|
1028
1146
|
AND ${worked.createdAt} > ${failed.createdAt}`
|
|
1029
1147
|
).where(
|
|
1030
|
-
|
|
1148
|
+
sql2`${failed.need} IS NOT NULL
|
|
1031
1149
|
AND ${failed.ownerId} IS NOT NULL
|
|
1032
1150
|
AND NOT (${failed.accepted} AND (${failed.buildSignal} IS NULL OR ${failed.buildSignal} <> 'failed'))
|
|
1033
1151
|
AND (${worked.accepted} AND (${worked.buildSignal} IS NULL OR ${worked.buildSignal} <> 'failed'))`
|
|
1034
|
-
).groupBy(failed.packageName, worked.packageName).having(
|
|
1152
|
+
).groupBy(failed.packageName, worked.packageName).having(sql2`count(DISTINCT ${failed.ownerId}) >= ${MIN_SUCCESSION_OWNERS}`);
|
|
1035
1153
|
return rows.map((r) => ({
|
|
1036
1154
|
from: r.from,
|
|
1037
1155
|
to: r.to,
|
|
@@ -1049,14 +1167,14 @@ async function loadLearnedSuccessors(db, now = Date.now()) {
|
|
|
1049
1167
|
return learnedCache.value;
|
|
1050
1168
|
}
|
|
1051
1169
|
const names = [...new Set(observed.map((o) => o.from))];
|
|
1052
|
-
const rows = await db.select({ name: packages.name, deprecated: packages.deprecated, archived: packages.archived }).from(packages).where(and(inArray(packages.name, names),
|
|
1170
|
+
const rows = await db.select({ name: packages.name, deprecated: packages.deprecated, archived: packages.archived }).from(packages).where(and(inArray(packages.name, names), eq2(packages.ecosystem, DEFAULT_ECOSYSTEM)));
|
|
1053
1171
|
const dead = new Set(rows.filter((r) => r.deprecated || r.archived).map((r) => r.name));
|
|
1054
1172
|
const value = buildLearnedSuccessors(observed, (name) => dead.has(name));
|
|
1055
1173
|
learnedCache = { at: now, value };
|
|
1056
1174
|
return value;
|
|
1057
1175
|
}
|
|
1058
1176
|
async function getOutcomesByOwner(db, ownerId, opts = {}) {
|
|
1059
|
-
return db.select().from(recommendationOutcomes).where(
|
|
1177
|
+
return db.select().from(recommendationOutcomes).where(eq2(recommendationOutcomes.ownerId, ownerId)).orderBy(desc(recommendationOutcomes.createdAt)).limit(opts.limit ?? 50);
|
|
1060
1178
|
}
|
|
1061
1179
|
|
|
1062
1180
|
// src/github/alerts.ts
|
|
@@ -1065,26 +1183,49 @@ var import_semver2 = __toESM(require_semver(), 1);
|
|
|
1065
1183
|
|
|
1066
1184
|
// src/db/alerts.ts
|
|
1067
1185
|
init_esm_shims();
|
|
1068
|
-
import { and as and2, desc as desc2, eq as
|
|
1186
|
+
import { and as and2, desc as desc2, eq as eq3, gte, isNull, sql as sql3 } from "drizzle-orm";
|
|
1069
1187
|
var ALERT_FEED_LIMIT = 50;
|
|
1188
|
+
var CLI_WATCH_DAYS = 90;
|
|
1070
1189
|
async function insertAlerts(db, rows) {
|
|
1071
|
-
if (rows.length === 0) return
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1190
|
+
if (rows.length === 0) return [];
|
|
1191
|
+
return db.insert(repoAlerts).values(rows).onConflictDoNothing().returning({ id: repoAlerts.id, repoId: repoAlerts.repoId });
|
|
1192
|
+
}
|
|
1193
|
+
async function cliUpgradeWatchers(db, packageName, sinceDays = CLI_WATCH_DAYS) {
|
|
1194
|
+
const rows = await db.select({
|
|
1195
|
+
ownerId: upgradeRuns.ownerId,
|
|
1196
|
+
repoFullName: upgradeRuns.repoFullName,
|
|
1197
|
+
toVersion: upgradeRuns.toVersion
|
|
1198
|
+
}).from(upgradeRuns).where(
|
|
1199
|
+
and2(
|
|
1200
|
+
eq3(upgradeRuns.packageName, packageName),
|
|
1201
|
+
isNull(upgradeRuns.repoId),
|
|
1202
|
+
gte(upgradeRuns.createdAt, sql3`now() - make_interval(days => ${sinceDays})`)
|
|
1203
|
+
)
|
|
1204
|
+
).orderBy(desc2(upgradeRuns.createdAt)).limit(5e3);
|
|
1205
|
+
const newest = /* @__PURE__ */ new Map();
|
|
1206
|
+
for (const r of rows) {
|
|
1207
|
+
const key = `${r.ownerId}\0${r.repoFullName}`;
|
|
1208
|
+
if (!newest.has(key)) {
|
|
1209
|
+
newest.set(key, {
|
|
1210
|
+
ownerId: r.ownerId,
|
|
1211
|
+
repoFullName: r.repoFullName,
|
|
1212
|
+
lastToVersion: r.toVersion
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
return [...newest.values()];
|
|
1076
1217
|
}
|
|
1077
1218
|
async function listAlerts(db, ownerId, limit = ALERT_FEED_LIMIT) {
|
|
1078
|
-
return db.select().from(repoAlerts).where(
|
|
1219
|
+
return db.select().from(repoAlerts).where(eq3(repoAlerts.ownerId, ownerId)).orderBy(desc2(repoAlerts.createdAt)).limit(Math.min(limit, ALERT_FEED_LIMIT));
|
|
1079
1220
|
}
|
|
1080
1221
|
async function deleteAlertsForRepo(db, ownerId, repoId) {
|
|
1081
|
-
await db.delete(repoAlerts).where(and2(
|
|
1222
|
+
await db.delete(repoAlerts).where(and2(eq3(repoAlerts.ownerId, ownerId), eq3(repoAlerts.repoId, repoId)));
|
|
1082
1223
|
}
|
|
1083
1224
|
|
|
1084
1225
|
// src/db/repos.ts
|
|
1085
1226
|
init_esm_shims();
|
|
1086
|
-
import { and as and3, eq as
|
|
1087
|
-
async function upsertRepos(db, rows) {
|
|
1227
|
+
import { and as and3, eq as eq4, inArray as inArray2, sql as sql4 } from "drizzle-orm";
|
|
1228
|
+
async function upsertRepos(db, rows, policy = DEFAULT_REPO_POLICY) {
|
|
1088
1229
|
if (rows.length === 0) return 0;
|
|
1089
1230
|
await db.insert(repos).values(
|
|
1090
1231
|
rows.map((row) => ({
|
|
@@ -1093,36 +1234,36 @@ async function upsertRepos(db, rows) {
|
|
|
1093
1234
|
fullName: row.fullName,
|
|
1094
1235
|
defaultBranch: row.defaultBranch,
|
|
1095
1236
|
isPrivate: row.isPrivate,
|
|
1096
|
-
policy
|
|
1237
|
+
policy
|
|
1097
1238
|
}))
|
|
1098
1239
|
).onConflictDoUpdate({
|
|
1099
1240
|
target: [repos.ownerId, repos.fullName],
|
|
1100
1241
|
set: {
|
|
1101
|
-
installationId:
|
|
1102
|
-
defaultBranch:
|
|
1103
|
-
isPrivate:
|
|
1242
|
+
installationId: sql4`excluded.installation_id`,
|
|
1243
|
+
defaultBranch: sql4`excluded.default_branch`,
|
|
1244
|
+
isPrivate: sql4`excluded.is_private`
|
|
1104
1245
|
}
|
|
1105
1246
|
});
|
|
1106
1247
|
return rows.length;
|
|
1107
1248
|
}
|
|
1108
1249
|
async function listRepos(db, ownerId) {
|
|
1109
|
-
return db.select().from(repos).where(
|
|
1250
|
+
return db.select().from(repos).where(eq4(repos.ownerId, ownerId)).orderBy(repos.fullName);
|
|
1110
1251
|
}
|
|
1111
1252
|
async function reposDeclaring(db, name) {
|
|
1112
1253
|
return db.select().from(repos).where(
|
|
1113
|
-
|
|
1254
|
+
sql4`exists (
|
|
1114
1255
|
select 1 from jsonb_array_elements(${repos.manifests}) as m
|
|
1115
1256
|
where jsonb_exists(m -> 'deps', ${name})
|
|
1116
1257
|
)`
|
|
1117
1258
|
);
|
|
1118
1259
|
}
|
|
1119
1260
|
async function ownerForInstallation(db, installationId) {
|
|
1120
|
-
const rows = await db.select({ ownerId: repos.ownerId }).from(repos).where(
|
|
1261
|
+
const rows = await db.select({ ownerId: repos.ownerId }).from(repos).where(eq4(repos.installationId, installationId)).limit(1);
|
|
1121
1262
|
return rows[0]?.ownerId ?? null;
|
|
1122
1263
|
}
|
|
1123
1264
|
async function deleteReposByInstallation(db, installationId, fullNames) {
|
|
1124
1265
|
const only = fullNames ? new Set(fullNames) : null;
|
|
1125
|
-
const rows = await db.select({ id: repos.id, ownerId: repos.ownerId, fullName: repos.fullName }).from(repos).where(
|
|
1266
|
+
const rows = await db.select({ id: repos.id, ownerId: repos.ownerId, fullName: repos.fullName }).from(repos).where(eq4(repos.installationId, installationId));
|
|
1126
1267
|
let deleted = 0;
|
|
1127
1268
|
for (const row of rows) {
|
|
1128
1269
|
if (only && !only.has(row.fullName)) continue;
|
|
@@ -1131,11 +1272,11 @@ async function deleteReposByInstallation(db, installationId, fullNames) {
|
|
|
1131
1272
|
return deleted;
|
|
1132
1273
|
}
|
|
1133
1274
|
async function getRepo(db, ownerId, id) {
|
|
1134
|
-
const rows = await db.select().from(repos).where(and3(
|
|
1275
|
+
const rows = await db.select().from(repos).where(and3(eq4(repos.ownerId, ownerId), eq4(repos.id, id))).limit(1);
|
|
1135
1276
|
return rows[0] ?? null;
|
|
1136
1277
|
}
|
|
1137
1278
|
async function findPolicyByFullName(db, ownerId, fullName) {
|
|
1138
|
-
const rows = await db.select({ policy: repos.policy }).from(repos).where(and3(
|
|
1279
|
+
const rows = await db.select({ policy: repos.policy }).from(repos).where(and3(eq4(repos.ownerId, ownerId), eq4(repos.fullName, fullName))).limit(1);
|
|
1139
1280
|
return rows[0]?.policy ?? null;
|
|
1140
1281
|
}
|
|
1141
1282
|
async function saveScan(db, id, scan) {
|
|
@@ -1145,25 +1286,40 @@ async function saveScan(db, id, scan) {
|
|
|
1145
1286
|
installCommand: scan.installCommand,
|
|
1146
1287
|
lastScanAt: /* @__PURE__ */ new Date(),
|
|
1147
1288
|
lastScanError: null
|
|
1148
|
-
}).where(
|
|
1289
|
+
}).where(eq4(repos.id, id));
|
|
1149
1290
|
}
|
|
1150
1291
|
async function saveScanError(db, id, message) {
|
|
1151
|
-
await db.update(repos).set({ lastScanAt: /* @__PURE__ */ new Date(), lastScanError: message.slice(0, 500) }).where(
|
|
1292
|
+
await db.update(repos).set({ lastScanAt: /* @__PURE__ */ new Date(), lastScanError: message.slice(0, 500) }).where(eq4(repos.id, id));
|
|
1152
1293
|
}
|
|
1153
1294
|
async function setRepoPolicy(db, ownerId, id, policy) {
|
|
1154
|
-
const updated = await db.update(repos).set({ policy }).where(and3(
|
|
1295
|
+
const updated = await db.update(repos).set({ policy }).where(and3(eq4(repos.ownerId, ownerId), eq4(repos.id, id))).returning({ id: repos.id });
|
|
1155
1296
|
return updated.length > 0;
|
|
1156
1297
|
}
|
|
1157
1298
|
async function deleteRepo(db, ownerId, id) {
|
|
1158
1299
|
await deleteAlertsForRepo(db, ownerId, id);
|
|
1159
|
-
const deleted = await db.delete(repos).where(and3(
|
|
1300
|
+
const deleted = await db.delete(repos).where(and3(eq4(repos.ownerId, ownerId), eq4(repos.id, id))).returning({ id: repos.id });
|
|
1160
1301
|
return deleted.length > 0;
|
|
1161
1302
|
}
|
|
1303
|
+
async function getRepoPolicyDefault(db, ownerId) {
|
|
1304
|
+
const [row] = await db.select({ policy: repoPolicyDefaults.policy }).from(repoPolicyDefaults).where(eq4(repoPolicyDefaults.ownerId, ownerId)).limit(1);
|
|
1305
|
+
return row?.policy ?? null;
|
|
1306
|
+
}
|
|
1307
|
+
async function setRepoPolicyDefault(db, ownerId, policy) {
|
|
1308
|
+
await db.insert(repoPolicyDefaults).values({ ownerId, policy }).onConflictDoUpdate({
|
|
1309
|
+
target: repoPolicyDefaults.ownerId,
|
|
1310
|
+
set: { policy, updatedAt: /* @__PURE__ */ new Date() }
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
async function applyPolicyToRepos(db, ownerId, ids, policy) {
|
|
1314
|
+
if (ids.length === 0) return 0;
|
|
1315
|
+
const updated = await db.update(repos).set({ policy }).where(and3(eq4(repos.ownerId, ownerId), inArray2(repos.id, ids))).returning({ id: repos.id });
|
|
1316
|
+
return updated.length;
|
|
1317
|
+
}
|
|
1162
1318
|
|
|
1163
1319
|
// src/github/drift.ts
|
|
1164
1320
|
init_esm_shims();
|
|
1165
1321
|
var import_semver = __toESM(require_semver(), 1);
|
|
1166
|
-
import { and as and4, eq as
|
|
1322
|
+
import { and as and4, eq as eq5, inArray as inArray3, sql as sql5 } from "drizzle-orm";
|
|
1167
1323
|
|
|
1168
1324
|
// src/github/attribute.ts
|
|
1169
1325
|
init_esm_shims();
|
|
@@ -1180,9 +1336,7 @@ function buildAttribution(deps, edges, directNames) {
|
|
|
1180
1336
|
if (list) list.push(edge.parent);
|
|
1181
1337
|
else parentIds.set(edge.child, [edge.parent]);
|
|
1182
1338
|
}
|
|
1183
|
-
const informative = edges.some(
|
|
1184
|
-
(edge) => nameOf.has(edge.parent) && nameOf.has(edge.child)
|
|
1185
|
-
);
|
|
1339
|
+
const informative = edges.some((edge) => nameOf.has(edge.parent) && nameOf.has(edge.child));
|
|
1186
1340
|
if (!informative) return { available: false, parentsOf: /* @__PURE__ */ new Map() };
|
|
1187
1341
|
const parentsOf = /* @__PURE__ */ new Map();
|
|
1188
1342
|
for (const dep of deps) {
|
|
@@ -1313,8 +1467,8 @@ async function loadIndexed(db, names, ecosystem = DEFAULT_ECOSYSTEM) {
|
|
|
1313
1467
|
advisories: packages.advisories
|
|
1314
1468
|
}).from(packages).where(
|
|
1315
1469
|
and4(
|
|
1316
|
-
|
|
1317
|
-
|
|
1470
|
+
inArray3(packages.name, names.slice(i, i + NAME_CHUNK)),
|
|
1471
|
+
eq5(packages.ecosystem, ecosystem)
|
|
1318
1472
|
)
|
|
1319
1473
|
);
|
|
1320
1474
|
for (const row of rows) {
|
|
@@ -1330,7 +1484,7 @@ async function loadIndexed(db, names, ecosystem = DEFAULT_ECOSYSTEM) {
|
|
|
1330
1484
|
async function loadVersions(db, names) {
|
|
1331
1485
|
const out = /* @__PURE__ */ new Map();
|
|
1332
1486
|
for (let i = 0; i < names.length; i += NAME_CHUNK) {
|
|
1333
|
-
const rows = await db.select({ name: packageVersions.packageName, version: packageVersions.version }).from(packageVersions).where(
|
|
1487
|
+
const rows = await db.select({ name: packageVersions.packageName, version: packageVersions.version }).from(packageVersions).where(inArray3(packageVersions.packageName, names.slice(i, i + NAME_CHUNK)));
|
|
1334
1488
|
for (const row of rows) {
|
|
1335
1489
|
const list = out.get(row.name);
|
|
1336
1490
|
if (list) list.push(row.version);
|
|
@@ -1342,7 +1496,8 @@ async function loadVersions(db, names) {
|
|
|
1342
1496
|
function depDrift(name, declared, indexed, knownVersions) {
|
|
1343
1497
|
const { range } = declared;
|
|
1344
1498
|
const latest = indexed.latestVersion;
|
|
1345
|
-
const
|
|
1499
|
+
const fromIndex = bestSatisfying(knownVersions, range);
|
|
1500
|
+
const resolved = fromIndex ?? rangeFloor(range);
|
|
1346
1501
|
let majorsBehind = 0;
|
|
1347
1502
|
if (resolved && latest && import_semver.default.valid(resolved) && import_semver.default.valid(latest)) {
|
|
1348
1503
|
majorsBehind = Math.max(0, import_semver.default.major(latest) - import_semver.default.major(resolved));
|
|
@@ -1355,9 +1510,20 @@ function depDrift(name, declared, indexed, knownVersions) {
|
|
|
1355
1510
|
latest,
|
|
1356
1511
|
majorsBehind,
|
|
1357
1512
|
deprecated: indexed.deprecated,
|
|
1358
|
-
advisories: indexed.advisories
|
|
1513
|
+
advisories: indexed.advisories,
|
|
1514
|
+
status: driftStatus(resolved, latest),
|
|
1515
|
+
resolvedFrom: fromIndex ? "index" : "range-floor",
|
|
1516
|
+
// Until computeDrift checks the resolved version against OSV, this is the
|
|
1517
|
+
// package-level count, and says so.
|
|
1518
|
+
advisoriesAt: "package"
|
|
1359
1519
|
};
|
|
1360
1520
|
}
|
|
1521
|
+
function driftStatus(resolved, latest) {
|
|
1522
|
+
if (!resolved || !latest || !import_semver.default.valid(resolved) || !import_semver.default.valid(latest)) return "unknown";
|
|
1523
|
+
if (!import_semver.default.lt(resolved, latest)) return "current";
|
|
1524
|
+
const breaking = import_semver.default.major(latest) > import_semver.default.major(resolved) || import_semver.default.major(resolved) === 0 && import_semver.default.major(latest) === 0 && import_semver.default.minor(latest) > import_semver.default.minor(resolved);
|
|
1525
|
+
return breaking ? "major" : "behind";
|
|
1526
|
+
}
|
|
1361
1527
|
function isBehind(dep) {
|
|
1362
1528
|
if (!dep.resolved || !dep.latest) return false;
|
|
1363
1529
|
if (!import_semver.default.valid(dep.resolved) || !import_semver.default.valid(dep.latest)) return false;
|
|
@@ -1436,9 +1602,9 @@ async function conflictsAtCurrent(db, deps) {
|
|
|
1436
1602
|
peerDependencies: packageVersions.peerDependencies,
|
|
1437
1603
|
engines: packageVersions.engines
|
|
1438
1604
|
}).from(packageVersions).where(
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
pinned.map((d) =>
|
|
1605
|
+
inArray3(
|
|
1606
|
+
sql5`(${packageVersions.packageName}, ${packageVersions.version})`,
|
|
1607
|
+
pinned.map((d) => sql5`(${d.name}, ${d.resolved})`)
|
|
1442
1608
|
)
|
|
1443
1609
|
);
|
|
1444
1610
|
const members = rows.map((r) => ({
|
|
@@ -1452,7 +1618,7 @@ async function conflictsAtCurrent(db, deps) {
|
|
|
1452
1618
|
}
|
|
1453
1619
|
function queueUntracked(db, names, ownerId) {
|
|
1454
1620
|
if (names.length === 0) return;
|
|
1455
|
-
void import("./ingestQueue-
|
|
1621
|
+
void import("./ingestQueue-RSEPMORK.js").then(({ enqueueIngest: enqueueIngest2 }) => {
|
|
1456
1622
|
for (const name of names) enqueueIngest2(db, name, ownerId);
|
|
1457
1623
|
}).catch((err) => {
|
|
1458
1624
|
logger.debug(`untracked-dep enqueue failed: ${String(err)}`);
|
|
@@ -1477,16 +1643,14 @@ async function computeDrift(db, manifests, resolvedTree = null, requestedByOwner
|
|
|
1477
1643
|
anyDrift: 0,
|
|
1478
1644
|
deprecated: 0,
|
|
1479
1645
|
advisories: 0,
|
|
1646
|
+
advisoriesExact: true,
|
|
1480
1647
|
deps: [],
|
|
1481
1648
|
transitive,
|
|
1482
1649
|
conflictsAtLatest: [],
|
|
1483
1650
|
conflictsAtCurrent: []
|
|
1484
1651
|
};
|
|
1485
1652
|
}
|
|
1486
|
-
const [indexed, versions] = await Promise.all([
|
|
1487
|
-
loadIndexed(db, names),
|
|
1488
|
-
loadVersions(db, names)
|
|
1489
|
-
]);
|
|
1653
|
+
const [indexed, versions] = await Promise.all([loadIndexed(db, names), loadVersions(db, names)]);
|
|
1490
1654
|
queueUntracked(
|
|
1491
1655
|
db,
|
|
1492
1656
|
names.filter((name) => !indexed.has(name)),
|
|
@@ -1498,9 +1662,22 @@ async function computeDrift(db, manifests, resolvedTree = null, requestedByOwner
|
|
|
1498
1662
|
if (!row) continue;
|
|
1499
1663
|
deps.push(depDrift(name, entry, row, versions.get(name) ?? []));
|
|
1500
1664
|
}
|
|
1665
|
+
const checkable = deps.filter((d) => d.resolved && import_semver.default.valid(d.resolved));
|
|
1666
|
+
const exact = await queryVulnerableInstalls(
|
|
1667
|
+
checkable.map((d) => ({ name: d.name, version: d.resolved }))
|
|
1668
|
+
);
|
|
1669
|
+
if (exact.complete) {
|
|
1670
|
+
for (const d of checkable) {
|
|
1671
|
+
d.advisories = exact.affected.get(installKey(d.name, d.resolved))?.length ?? 0;
|
|
1672
|
+
d.advisoriesAt = "resolved";
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1501
1675
|
deps.sort((a, b) => severity(b) - severity(a) || a.name.localeCompare(b.name));
|
|
1502
1676
|
const [conflicts, currentConflicts] = await Promise.all([
|
|
1503
|
-
conflictsAtLatest(
|
|
1677
|
+
conflictsAtLatest(
|
|
1678
|
+
db,
|
|
1679
|
+
deps.map((d) => d.name)
|
|
1680
|
+
),
|
|
1504
1681
|
conflictsAtCurrent(db, deps)
|
|
1505
1682
|
]);
|
|
1506
1683
|
return {
|
|
@@ -1510,6 +1687,7 @@ async function computeDrift(db, manifests, resolvedTree = null, requestedByOwner
|
|
|
1510
1687
|
anyDrift: deps.filter(isBehind).length,
|
|
1511
1688
|
deprecated: deps.filter((d) => d.deprecated).length,
|
|
1512
1689
|
advisories: deps.reduce((sum, d) => sum + d.advisories, 0),
|
|
1690
|
+
advisoriesExact: deps.every((d) => d.advisoriesAt === "resolved"),
|
|
1513
1691
|
deps: deps.slice(0, REPO_DRIFT_DETAIL_CAP),
|
|
1514
1692
|
transitive,
|
|
1515
1693
|
conflictsAtLatest: conflicts,
|
|
@@ -1517,6 +1695,116 @@ async function computeDrift(db, manifests, resolvedTree = null, requestedByOwner
|
|
|
1517
1695
|
};
|
|
1518
1696
|
}
|
|
1519
1697
|
|
|
1698
|
+
// src/github/dispatch.ts
|
|
1699
|
+
init_esm_shims();
|
|
1700
|
+
|
|
1701
|
+
// src/github/scope.ts
|
|
1702
|
+
init_esm_shims();
|
|
1703
|
+
function permits(policy, check) {
|
|
1704
|
+
return policy?.checks?.[check] !== false;
|
|
1705
|
+
}
|
|
1706
|
+
function isUnassessed(u) {
|
|
1707
|
+
return u.verdict === "unknown";
|
|
1708
|
+
}
|
|
1709
|
+
function breaksSomething(u) {
|
|
1710
|
+
return u.verdict === "removes-exports" || u.verdict === "arity-changed";
|
|
1711
|
+
}
|
|
1712
|
+
function scopeVerdict(u, scope) {
|
|
1713
|
+
if (scope === "all") return { inScope: true };
|
|
1714
|
+
if (u.advisories > 0) return { inScope: true };
|
|
1715
|
+
if (scope === "security") {
|
|
1716
|
+
return { inScope: false, reason: "no advisory \u2014 scope is security-only" };
|
|
1717
|
+
}
|
|
1718
|
+
if (isUnassessed(u)) {
|
|
1719
|
+
return {
|
|
1720
|
+
inScope: false,
|
|
1721
|
+
reason: "surface not yet analysed \u2014 nothing for the agent to rewrite"
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
if (breaksSomething(u)) return { inScope: true };
|
|
1725
|
+
return { inScope: false, reason: "nothing referenced breaks \u2014 scope is security + blocking" };
|
|
1726
|
+
}
|
|
1727
|
+
function repoMode(policy) {
|
|
1728
|
+
if (!policy.enabled) return "comment";
|
|
1729
|
+
return policy.mode ?? "pr";
|
|
1730
|
+
}
|
|
1731
|
+
function applyScope(upgrades, policy) {
|
|
1732
|
+
if (!policy) {
|
|
1733
|
+
return {
|
|
1734
|
+
upgrades: upgrades.map((u) => ({ ...u, inScope: true })),
|
|
1735
|
+
scope: "all",
|
|
1736
|
+
scopeSource: "unconnected",
|
|
1737
|
+
outOfScope: 0
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
const scoped = upgrades.map((u) => {
|
|
1741
|
+
const verdict = scopeVerdict(u, policy.scope);
|
|
1742
|
+
return verdict.inScope ? { ...u, inScope: true } : { ...u, inScope: false, scopeReason: verdict.reason };
|
|
1743
|
+
});
|
|
1744
|
+
return {
|
|
1745
|
+
upgrades: scoped,
|
|
1746
|
+
scope: policy.scope,
|
|
1747
|
+
scopeSource: "repo-policy",
|
|
1748
|
+
outOfScope: scoped.filter((u) => !u.inScope).length,
|
|
1749
|
+
// Only on this branch. The unconnected branch above deliberately omits it:
|
|
1750
|
+
// "no policy" is not "policy says comment", and conflating them would turn
|
|
1751
|
+
// an unconnected checkout into a silent disarm.
|
|
1752
|
+
mode: repoMode(policy)
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
// src/github/dispatch.ts
|
|
1757
|
+
var WORKFLOW_FILE = WORKFLOW_PATH.split("/").pop();
|
|
1758
|
+
function shouldDispatch(repo) {
|
|
1759
|
+
return repoMode(repo.policy) !== "comment";
|
|
1760
|
+
}
|
|
1761
|
+
async function dispatchUpgradeWorkflow(repo) {
|
|
1762
|
+
if (!shouldDispatch(repo)) return "not-armed";
|
|
1763
|
+
try {
|
|
1764
|
+
await installationPost(
|
|
1765
|
+
repo.installationId,
|
|
1766
|
+
`/repos/${repo.fullName}/actions/workflows/${WORKFLOW_FILE}/dispatches`,
|
|
1767
|
+
{ ref: repo.defaultBranch ?? "main" }
|
|
1768
|
+
);
|
|
1769
|
+
return "dispatched";
|
|
1770
|
+
} catch (err) {
|
|
1771
|
+
if (err instanceof GithubAppError && err.status === 403) return "permission-denied";
|
|
1772
|
+
if (err instanceof GithubAppError && err.status === 404) return "no-workflow";
|
|
1773
|
+
logger.warn(
|
|
1774
|
+
`dispatch: could not start a run for ${repo.fullName}: ${err instanceof Error ? err.message : String(err)}`
|
|
1775
|
+
);
|
|
1776
|
+
return "failed";
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
async function dispatchForAlerts(repos2) {
|
|
1780
|
+
const tally = {
|
|
1781
|
+
dispatched: 0,
|
|
1782
|
+
"not-armed": 0,
|
|
1783
|
+
"permission-denied": 0,
|
|
1784
|
+
"no-workflow": 0,
|
|
1785
|
+
failed: 0
|
|
1786
|
+
};
|
|
1787
|
+
for (const repo of repos2) {
|
|
1788
|
+
try {
|
|
1789
|
+
tally[await dispatchUpgradeWorkflow(repo)] += 1;
|
|
1790
|
+
} catch (err) {
|
|
1791
|
+
tally.failed += 1;
|
|
1792
|
+
logger.warn(
|
|
1793
|
+
`dispatch: skipped ${repo.fullName}: ${err instanceof Error ? err.message : String(err)}`
|
|
1794
|
+
);
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
if (tally.dispatched > 0) {
|
|
1798
|
+
logger.info(`dispatch: started ${tally.dispatched} upgrade run(s) on a new major`);
|
|
1799
|
+
}
|
|
1800
|
+
if (tally["permission-denied"] > 0) {
|
|
1801
|
+
logger.warn(
|
|
1802
|
+
`dispatch: ${tally["permission-denied"]} run(s) not started \u2014 the lurq GitHub App needs Actions: write. The weekly schedule is unaffected.`
|
|
1803
|
+
);
|
|
1804
|
+
}
|
|
1805
|
+
return tally;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1520
1808
|
// src/github/alerts.ts
|
|
1521
1809
|
function newMajorRelease(before, after) {
|
|
1522
1810
|
const from = before?.latestVersion;
|
|
@@ -1548,18 +1836,44 @@ function draftAlert(repo, packageName, toVersion) {
|
|
|
1548
1836
|
inRange: import_semver2.default.validRange(declared.range) ? import_semver2.default.satisfies(toVersion, declared.range, { includePrerelease: false }) : false
|
|
1549
1837
|
};
|
|
1550
1838
|
}
|
|
1839
|
+
function draftCliAlert(watcher, packageName, toVersion) {
|
|
1840
|
+
const last = import_semver2.default.valid(watcher.lastToVersion) ?? import_semver2.default.coerce(watcher.lastToVersion)?.version ?? null;
|
|
1841
|
+
if (last && import_semver2.default.major(last) >= import_semver2.default.major(toVersion)) return null;
|
|
1842
|
+
return {
|
|
1843
|
+
ownerId: watcher.ownerId,
|
|
1844
|
+
repoId: null,
|
|
1845
|
+
repoFullName: watcher.repoFullName,
|
|
1846
|
+
packageName,
|
|
1847
|
+
range: watcher.lastToVersion,
|
|
1848
|
+
fromVersion: watcher.lastToVersion,
|
|
1849
|
+
toVersion,
|
|
1850
|
+
inRange: false
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1551
1853
|
async function emitPublishAlerts(db, name, before, after) {
|
|
1552
1854
|
const toVersion = newMajorRelease(before, after);
|
|
1553
1855
|
if (!toVersion) return 0;
|
|
1554
1856
|
try {
|
|
1555
|
-
const affected = await
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1857
|
+
const [affected, watchers] = await Promise.all([
|
|
1858
|
+
reposDeclaring(db, name),
|
|
1859
|
+
cliUpgradeWatchers(db, name)
|
|
1860
|
+
]);
|
|
1861
|
+
const connected = new Set(affected.map((r) => `${r.ownerId}\0${r.fullName}`));
|
|
1862
|
+
const rows = [
|
|
1863
|
+
...affected.map((repo) => draftAlert(repo, name, toVersion)),
|
|
1864
|
+
...watchers.filter((w) => !connected.has(`${w.ownerId}\0${w.repoFullName}`)).map((w) => draftCliAlert(w, name, toVersion))
|
|
1865
|
+
].filter((row) => row !== null);
|
|
1866
|
+
if (rows.length === 0) return 0;
|
|
1867
|
+
const inserted = await insertAlerts(db, rows);
|
|
1868
|
+
if (inserted.length > 0) {
|
|
1869
|
+
logger.info(
|
|
1870
|
+
`alerts: ${name}@${toVersion} is a new major, notified ${inserted.length} repo(s)`
|
|
1871
|
+
);
|
|
1561
1872
|
}
|
|
1562
|
-
|
|
1873
|
+
const byId = new Map(affected.map((repo) => [repo.id, repo]));
|
|
1874
|
+
const freshlyAlerted = inserted.map((row) => row.repoId === null ? void 0 : byId.get(row.repoId)).filter((repo) => repo !== void 0);
|
|
1875
|
+
if (freshlyAlerted.length > 0) await dispatchForAlerts(freshlyAlerted);
|
|
1876
|
+
return inserted.length;
|
|
1563
1877
|
} catch (err) {
|
|
1564
1878
|
logger.warn(`alerts: could not fan out ${name}@${toVersion}: ${err.message}`);
|
|
1565
1879
|
return 0;
|
|
@@ -1698,13 +2012,13 @@ function assemblePackageRow(p) {
|
|
|
1698
2012
|
// src/pipeline/single.ts
|
|
1699
2013
|
var FIRST_TOUCH_BUDGET_MS = 4e3;
|
|
1700
2014
|
async function getSeedCategory(db, name) {
|
|
1701
|
-
const [row] = await db.select({ category: seedPackages.category }).from(seedPackages).where(
|
|
2015
|
+
const [row] = await db.select({ category: seedPackages.category }).from(seedPackages).where(eq6(seedPackages.name, name)).limit(1);
|
|
1702
2016
|
return row?.category ?? null;
|
|
1703
2017
|
}
|
|
1704
2018
|
async function getCategoryMedianBundle(db, category) {
|
|
1705
2019
|
const [row] = await db.select({
|
|
1706
|
-
m:
|
|
1707
|
-
}).from(packages).where(and5(
|
|
2020
|
+
m: sql6`percentile_cont(0.5) within group (order by ${packages.bundleMinGzipKb})`
|
|
2021
|
+
}).from(packages).where(and5(eq6(packages.category, category), isNotNull(packages.bundleMinGzipKb)));
|
|
1708
2022
|
return row?.m ?? null;
|
|
1709
2023
|
}
|
|
1710
2024
|
async function syncOnePackage(db, name, opts = {}) {
|
|
@@ -1716,7 +2030,8 @@ async function syncOnePackage(db, name, opts = {}) {
|
|
|
1716
2030
|
const prefetchedWeekly = await fetchWeeklyDownloads(name).catch(() => null);
|
|
1717
2031
|
const signals = await collectSignals(name, initialCategory, {
|
|
1718
2032
|
githubToken: config.GITHUB_TOKEN,
|
|
1719
|
-
prefetchedWeekly
|
|
2033
|
+
prefetchedWeekly,
|
|
2034
|
+
freshRegistry: opts.fresh
|
|
1720
2035
|
});
|
|
1721
2036
|
const embProvider = createEmbeddingProvider();
|
|
1722
2037
|
const reusable = canReuse(existing, signals.registry?.latestVersion ?? null, embProvider.id);
|
|
@@ -1805,7 +2120,10 @@ async function getOrFetchPackage(db, name, opts = {}) {
|
|
|
1805
2120
|
const exists = await npmPackageExists(name);
|
|
1806
2121
|
if (!exists) return { row: null, wasTracked: false, existsOnNpm: false };
|
|
1807
2122
|
if (opts.blockMs && opts.blockMs > 0) {
|
|
1808
|
-
const row = await withBudget(
|
|
2123
|
+
const row = await withBudget(
|
|
2124
|
+
runIngest(db, name, opts.requestedByOwnerId ?? null),
|
|
2125
|
+
opts.blockMs
|
|
2126
|
+
);
|
|
1809
2127
|
if (row) return { row, wasTracked: false, existsOnNpm: true };
|
|
1810
2128
|
return { row: null, wasTracked: false, existsOnNpm: true, queued: true };
|
|
1811
2129
|
}
|
|
@@ -1814,6 +2132,11 @@ async function getOrFetchPackage(db, name, opts = {}) {
|
|
|
1814
2132
|
}
|
|
1815
2133
|
|
|
1816
2134
|
// src/pipeline/ingestQueue.ts
|
|
2135
|
+
async function persistDemand(db, name, owner) {
|
|
2136
|
+
await enqueueDemand(db, name, owner).catch(
|
|
2137
|
+
(err) => logger.warn(`ingest queue: could not persist request for ${name}: ${String(err)}`)
|
|
2138
|
+
);
|
|
2139
|
+
}
|
|
1817
2140
|
var MAX_CONCURRENT = 3;
|
|
1818
2141
|
var MAX_PENDING = 500;
|
|
1819
2142
|
var pending = [];
|
|
@@ -1847,6 +2170,7 @@ function enqueueIngest(db, name, requestedByOwnerId = null) {
|
|
|
1847
2170
|
queuedNames.add(name);
|
|
1848
2171
|
owners.set(name, requestedByOwnerId);
|
|
1849
2172
|
pending.push(name);
|
|
2173
|
+
void persistDemand(db, name, requestedByOwnerId);
|
|
1850
2174
|
pump(db);
|
|
1851
2175
|
}
|
|
1852
2176
|
function pump(db) {
|
|
@@ -1865,15 +2189,18 @@ function pump(db) {
|
|
|
1865
2189
|
}
|
|
1866
2190
|
}
|
|
1867
2191
|
async function runIngest(db, name, requestedByOwnerId = null) {
|
|
2192
|
+
const persisted = persistDemand(db, name, requestedByOwnerId);
|
|
1868
2193
|
try {
|
|
1869
2194
|
const row = await syncOnePackage(db, name, { requestedByOwnerId });
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
}
|
|
2195
|
+
await persisted;
|
|
2196
|
+
await setDiscoveryStatus(db, name, { status: "ingested" }).catch(() => {
|
|
2197
|
+
});
|
|
1874
2198
|
return row;
|
|
1875
2199
|
} catch (err) {
|
|
1876
2200
|
logger.warn(`on-demand ingest failed for ${name}: ${String(err)}`);
|
|
2201
|
+
await persisted;
|
|
2202
|
+
await recordIngestFailure(db, name, DISCOVERY.maxIngestAttempts).catch(() => {
|
|
2203
|
+
});
|
|
1877
2204
|
return null;
|
|
1878
2205
|
}
|
|
1879
2206
|
}
|
|
@@ -1900,6 +2227,11 @@ export {
|
|
|
1900
2227
|
saveScanError,
|
|
1901
2228
|
setRepoPolicy,
|
|
1902
2229
|
deleteRepo,
|
|
2230
|
+
getRepoPolicyDefault,
|
|
2231
|
+
setRepoPolicyDefault,
|
|
2232
|
+
applyPolicyToRepos,
|
|
2233
|
+
getQueuedNames,
|
|
2234
|
+
enqueueCandidates,
|
|
1903
2235
|
ingestQueueDepth,
|
|
1904
2236
|
drainIngestQueue,
|
|
1905
2237
|
resetIngestQueue,
|
|
@@ -1908,6 +2240,11 @@ export {
|
|
|
1908
2240
|
declaredDeps,
|
|
1909
2241
|
loadVersions,
|
|
1910
2242
|
computeDrift,
|
|
2243
|
+
permits,
|
|
2244
|
+
breaksSomething,
|
|
2245
|
+
repoMode,
|
|
2246
|
+
applyScope,
|
|
2247
|
+
dispatchUpgradeWorkflow,
|
|
1911
2248
|
classifyRuntimeTarget,
|
|
1912
2249
|
FIRST_TOUCH_BUDGET_MS,
|
|
1913
2250
|
syncOnePackage,
|