lurqrun 0.1.1 → 0.1.3

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.d.ts CHANGED
@@ -1,6 +1,77 @@
1
1
  import { z } from 'zod';
2
2
  import { Command } from 'commander';
3
3
 
4
+ /**
5
+ * The single answer to "is it safe to install this?".
6
+ *
7
+ * This exists because lurq had several. `verify` rolled risk flags into a
8
+ * level, `evaluate` reported a health score and a confidence tier, and `audit`
9
+ * emitted findings — three code paths deriving safety from overlapping inputs
10
+ * and disagreeing about the same package. A tool that answers one question
11
+ * three ways has not answered it, and the disagreement always surfaced at the
12
+ * worst moment, because the paths diverge exactly where the evidence is thin.
13
+ *
14
+ * Every surface now calls this and renders what it returns. Adding a signal
15
+ * means editing one file, and no caller can accidentally be more reassuring
16
+ * than the evidence supports.
17
+ *
18
+ * Three rules, each of which was a real defect:
19
+ *
20
+ * 1. A SECURITY FINDING OVERRIDES QUALITY. A package with a known advisory is
21
+ * not "safe" because it is popular and well maintained. The health score
22
+ * stays a quality measure — it is not fudged — but it never sets the
23
+ * verdict, and the verdict is what every surface leads with.
24
+ *
25
+ * 2. UNKNOWN IS NOT CLEAN. `advisories` is NULL until the package has been
26
+ * analysed and `[]` once it has, and collapsing those with `?? []` reported
27
+ * "0 advisories" for a package nobody had looked at yet. Missing evidence
28
+ * is carried in `unknowns` and suppresses reassurance outright.
29
+ *
30
+ * 3. A NAME THAT DOES NOT EXIST IS NOT A RISK RATING. It is bad input, and
31
+ * saying "high risk" invites the reader to weigh it against a benefit. There
32
+ * is no package to weigh.
33
+ */
34
+
35
+ /**
36
+ * `invalid` is deliberately outside `RiskLevel`: it is a statement about the
37
+ * INPUT, not about a package, and flattening it into `high` loses the only
38
+ * thing the caller can act on — that there is nothing here to install.
39
+ */
40
+ type VerdictLevel = RiskLevel | 'invalid';
41
+ interface SecurityVerdict {
42
+ level: VerdictLevel;
43
+ /** Why this is not clean, worst first. Empty only when nothing was found. */
44
+ reasons: string[];
45
+ /**
46
+ * What was NOT established. Non-empty means `level` is provisional: it is the
47
+ * worst we can currently justify, not the worst that is true.
48
+ */
49
+ unknowns: string[];
50
+ /** Every input needed to justify a clean bill was present. */
51
+ complete: boolean;
52
+ /**
53
+ * Safe to render as reassurance ("looks safe", a green tick).
54
+ *
55
+ * The one flag every renderer must consult. It requires a low level AND no
56
+ * findings AND nothing unchecked, so a caller cannot produce a green tick
57
+ * over a gap in the evidence by reading `level` alone.
58
+ */
59
+ reassuring: boolean;
60
+ /** Advisories actually recorded. Null when none have been fetched yet. */
61
+ advisoryCount: number | null;
62
+ /**
63
+ * WHICH version this verdict is about, in words a reader can act on.
64
+ *
65
+ * Without it the surfaces agree and still mislead. `verify vitest` answers
66
+ * about the latest release and says "no problems found"; the audit answers
67
+ * about the 2.1.9 you have installed and says "vulnerable". Both are correct
68
+ * and they read as a contradiction, because neither said what it was talking
69
+ * about. A clean bill for the newest version is not a clean bill for the one
70
+ * running in your node_modules.
71
+ */
72
+ scope: string;
73
+ }
74
+
4
75
  /**
5
76
  * Shared domain types and the v1 category taxonomy (§8.3).
6
77
  * These are the stable contracts the rest of the system builds on.
@@ -124,8 +195,13 @@ type PeerMeta = Record<string, {
124
195
  /** One incompatibility found across a set of packages. */
125
196
  interface CompatConflict {
126
197
  /** Where the conflict came from. `peer-deps`/`engines` are Tier-1 (metadata,
127
- * instant, deterministic); `sandbox` is Tier-2 (empirical co-install). */
128
- source: 'peer-deps' | 'engines' | 'sandbox';
198
+ * instant, deterministic); `resolve` is npm's own ERESOLVE on the whole set;
199
+ * `sandbox` is the empirical co-install under VM isolation.
200
+ *
201
+ * A `resolve` conflict is about the SET, not a pair — npm reports that these
202
+ * packages cannot coexist, not which two are at fault — so `packages` carries
203
+ * every member and `enumeratePairs` deliberately does not attribute it. */
204
+ source: 'peer-deps' | 'engines' | 'sandbox' | 'resolve';
129
205
  /** The packages involved in the conflict. */
130
206
  packages: string[];
131
207
  detail: string;
@@ -172,19 +248,43 @@ interface CompatEvidence {
172
248
  * only corrected by a backfill, which has not been run. */
173
249
  witnessCount: number;
174
250
  }
251
+ /**
252
+ * How the set-level verdict was established.
253
+ *
254
+ * `source` distinguishes a cache hit from a resolve we just ran, which matters
255
+ * for a caller deciding whether the answer is worth re-checking: a cached entry
256
+ * is exact for those versions and immutable, but it was minted at `at` and the
257
+ * publish that would have invalidated it may not have been seen yet.
258
+ */
259
+ interface CompatResolution {
260
+ source: 'cache' | 'resolved';
261
+ /** True when npm produced a lockfile for the exact set. */
262
+ resolved: boolean;
263
+ /** 'ERESOLVE' for a proven conflict; null when it resolved. */
264
+ reason: 'ERESOLVE' | null;
265
+ /** npm's own message on a conflict — names the packages and ranges. */
266
+ detail?: string;
267
+ /** ISO timestamp the resolution was established. */
268
+ at: string;
269
+ }
175
270
  /**
176
271
  * `compat` output: whole-architecture compatibility for a set of packages.
177
272
  * Tier-1 peer-dependency/engine analysis + any Tier-2 sandbox conflicts.
178
273
  */
179
274
  interface CompatOutput {
180
275
  packages: string[];
181
- /** Evidence-graded verdict:
182
- * - `conflict` — a proven incompatibility (peer-range violation or conflict edge).
183
- * - `compatible` — every pair is *positively evidenced* (verified/observed edge).
184
- * - `likely` — no conflict found, but not proven; absence of a declared
185
- * conflict is not proof, so an unverified set is never `compatible`.
186
- * - `unknown` — a member has no metadata yet (not ingested). */
187
- overall: 'compatible' | 'likely' | 'conflict' | 'unknown';
276
+ /** Verdict about the *set*, not its pairs:
277
+ * - `conflict` — proven broken: a peer-range violation, a sandbox conflict,
278
+ * or npm's own ERESOLVE on this exact set.
279
+ * - `compatible` — npm resolved these exact versions together.
280
+ * - `unknown` — genuinely undetermined: a member we have no version for, or
281
+ * a resolve that could not complete. Never a hedge on a set
282
+ * we did resolve.
283
+ *
284
+ * There is deliberately no `likely`. It meant "no pair is known to be broken",
285
+ * which is a statement about our coverage rather than about the packages, and
286
+ * it was the answer real stacks got almost every time. */
287
+ overall: 'compatible' | 'conflict' | 'unknown';
188
288
  conflicts: CompatConflict[];
189
289
  /** Packages lurq has no compatibility metadata for (counted toward `unknown`). */
190
290
  unverified: string[];
@@ -194,9 +294,12 @@ interface CompatOutput {
194
294
  name: string;
195
295
  version: string | null;
196
296
  }[];
197
- /** Stored edges among these packages with their evidence strength (§4B) — lets
198
- * agents (and metrics) see *why* a pair is called compatible, not just that. */
297
+ /** Sandbox-established edges among these packages (verified/conflict) — the
298
+ * one pairwise signal that is not derivable from a set resolution. */
199
299
  evidence: CompatEvidence[];
300
+ /** How the set-level verdict was reached. Absent when a declared conflict made
301
+ * a resolve unnecessary, or when a member had no version to key on. */
302
+ resolution?: CompatResolution;
200
303
  /** Every unordered pair among `checked`, each with its own verdict — exactly
201
304
  * n(n−1)/2 entries. `conflicts` lists only what went wrong; this lists what
202
305
  * was compared, which is what lets a caller show coverage rather than just
@@ -349,6 +452,8 @@ interface EvaluateOutput {
349
452
  bundleMinGzipKb: number | null;
350
453
  deprecated: boolean;
351
454
  archived: boolean;
455
+ /** The shared safety call — identical to what `verify` and `audit` return. */
456
+ verdict: SecurityVerdict;
352
457
  advisories: Advisory[];
353
458
  summary: string | null;
354
459
  usageGuide: UsageGuide | null;
@@ -364,6 +469,7 @@ interface EvaluateOutput {
364
469
  /** `verify` output (§12.3.4). */
365
470
  /** Supply-chain risk gate: high = review before installing (see security/risk). */
366
471
  type RiskLevel = 'low' | 'medium' | 'high';
472
+
367
473
  interface VerifyOutput {
368
474
  exists: boolean;
369
475
  tracked: boolean;
@@ -377,7 +483,10 @@ interface VerifyOutput {
377
483
  /** Suspected typosquat target — a popular package this name closely mimics. */
378
484
  typosquatOf: string | null;
379
485
  confidence: Confidence | null;
380
- advisoryCount: number;
486
+ /** Null when this package has never been analysed — NOT the same as zero. */
487
+ advisoryCount: number | null;
488
+ /** The shared safety call. Read `verdict.reassuring` before showing a tick. */
489
+ verdict: SecurityVerdict;
381
490
  }
382
491
 
383
492
  declare const SERVER_NAME = "lurq";
@@ -446,6 +555,27 @@ declare const EnvSchema: z.ZodObject<{
446
555
  SUMMARY_API_KEY: z.ZodOptional<z.ZodString>;
447
556
  SUMMARY_MODEL: z.ZodDefault<z.ZodString>;
448
557
  SUMMARY_BASE_URL: z.ZodDefault<z.ZodString>;
558
+ /**
559
+ * How many `npm install --package-lock-only` processes may run at once.
560
+ *
561
+ * Each one holds npm's arborist tree in memory — 200-500MB for a real stack —
562
+ * so this is a memory cap wearing a concurrency hat. Eight concurrent resolves
563
+ * is ~3GB and will OOM a small container; the work is network-bound anyway, so
564
+ * raising it buys throughput only until the box runs out of RAM.
565
+ */
566
+ LURQ_RESOLVE_CONCURRENCY: z.ZodDefault<z.ZodNumber>;
567
+ /**
568
+ * Where npm keeps its metadata cache for stack resolution.
569
+ *
570
+ * Point this at a persistent volume in production. A warm cache is the
571
+ * difference between 8.6s and 19.2s on a 15-package stack (measured), and an
572
+ * ephemeral container filesystem means every redeploy pays cold cost again for
573
+ * the first few hundred resolves. Unset = npm's default location.
574
+ */
575
+ LURQ_NPM_CACHE_DIR: z.ZodOptional<z.ZodString>;
576
+ /** Budget for one stack resolve. Past this the answer is `unknown`, never a
577
+ * guess — and nothing is cached, so the next ask gets a real attempt. */
578
+ LURQ_RESOLVE_TIMEOUT_MS: z.ZodDefault<z.ZodNumber>;
449
579
  LURQ_SYNC_CONCURRENCY: z.ZodDefault<z.ZodNumber>;
450
580
  /** Stalest non-seed packages the daily sync refreshes on top of the seed list.
451
581
  * This is the rotation rate for everything discovery ingested: index size /
@@ -465,6 +595,11 @@ declare const EnvSchema: z.ZodObject<{
465
595
  LURQ_RATE_LIMIT_MAX: z.ZodDefault<z.ZodNumber>;
466
596
  /** Coarser per-IP rate limit (blunts unauthenticated floods before auth). */
467
597
  LURQ_IP_RATE_LIMIT_MAX: z.ZodDefault<z.ZodNumber>;
598
+ /** Per-IP limit for `POST /scan/public`, the one unauthenticated route that
599
+ * reads GitHub, queries the database, and queues ingestion work. The coarse
600
+ * IP limit is the wrong ceiling for it: 240 scans a minute is nothing for a
601
+ * JSON-RPC caller and a great deal of somebody else's GitHub budget. */
602
+ LURQ_SCAN_RATE_LIMIT_MAX: z.ZodDefault<z.ZodNumber>;
468
603
  /** Rate-limit window, milliseconds (applies to both limiters). */
469
604
  LURQ_RATE_LIMIT_WINDOW_MS: z.ZodDefault<z.ZodNumber>;
470
605
  /** Bearer token guarding `/metrics`. Unset → the endpoint is disabled (404). */
@@ -473,10 +608,32 @@ declare const EnvSchema: z.ZodObject<{
473
608
  * authenticated web app presents it to mint a key for a signed-in user. Unset
474
609
  * → the endpoint is disabled (404). Keep it server-side, never in the client. */
475
610
  LURQ_ISSUER_SECRET: z.ZodOptional<z.ZodString>;
611
+ /** PostHog project token (the public `phc_…` one, same as the web app's).
612
+ * Set → the hosted service sends account-keyed product events (key issued,
613
+ * tool called) that join the web app's identified visitors. Unset → nothing
614
+ * is sent, which is every local and stdio install. */
615
+ LURQ_POSTHOG_KEY: z.ZodOptional<z.ZodString>;
476
616
  STRIPE_SECRET_KEY: z.ZodOptional<z.ZodString>;
477
617
  /** Signing secret for `POST /billing/webhook`. Unset → the endpoint is 404. */
478
618
  STRIPE_WEBHOOK_SECRET: z.ZodOptional<z.ZodString>;
479
619
  /** Price id backing the Pro plan. Unset → Pro cannot be checked out. */
620
+ /**
621
+ * Whether Stripe Managed Payments applies to our Checkout Sessions: Stripe as
622
+ * merchant of record, taking over indirect tax registration and remittance,
623
+ * disputes and fraud — the half `automatic_tax` alone does not buy.
624
+ *
625
+ * ALWAYS SENT, in both directions, and that is the point. Managed Payments has
626
+ * an account-level default (on, for accounts created since it shipped), so
627
+ * omitting the parameter hands the decision to a dashboard toggle instead of
628
+ * to config. That is action at a distance: the same deploy behaves differently
629
+ * per account, and the failure it produces is a 400 at the moment someone
630
+ * tries to pay. Sending it explicitly makes the deployment the authority.
631
+ *
632
+ * Defaults true to match the account default. Every product must carry an
633
+ * eligible `tax_code` (see billing/provision.ts) or Stripe rejects the session,
634
+ * so `false` is the one-variable escape hatch if eligibility is ever in doubt.
635
+ */
636
+ STRIPE_MANAGED_PAYMENTS: z.ZodEffects<z.ZodDefault<z.ZodEnum<["true", "false"]>>, boolean, "true" | "false" | undefined>;
480
637
  STRIPE_PRICE_PRO: z.ZodOptional<z.ZodString>;
481
638
  /** Price id backing Enterprise. Normally unset: Enterprise is sold by
482
639
  * conversation, and `contactOnly` in core/plans.ts is what the page reads. */
@@ -503,20 +660,26 @@ declare const EnvSchema: z.ZodObject<{
503
660
  SUMMARY_PROVIDER: "openai" | "none";
504
661
  SUMMARY_MODEL: string;
505
662
  SUMMARY_BASE_URL: string;
663
+ LURQ_RESOLVE_CONCURRENCY: number;
664
+ LURQ_RESOLVE_TIMEOUT_MS: number;
506
665
  LURQ_SYNC_CONCURRENCY: number;
507
666
  LURQ_SYNC_REFRESH_CAP: number;
508
667
  LOG_LEVEL: "info" | "error" | "warn" | "debug";
509
668
  PORT: number;
510
669
  LURQ_RATE_LIMIT_MAX: number;
511
670
  LURQ_IP_RATE_LIMIT_MAX: number;
671
+ LURQ_SCAN_RATE_LIMIT_MAX: number;
512
672
  LURQ_RATE_LIMIT_WINDOW_MS: number;
673
+ STRIPE_MANAGED_PAYMENTS: boolean;
513
674
  LURQ_WEB_URL: string;
514
675
  DATABASE_URL?: string | undefined;
515
676
  GITHUB_TOKEN?: string | undefined;
516
677
  EMBEDDING_API_KEY?: string | undefined;
517
678
  SUMMARY_API_KEY?: string | undefined;
679
+ LURQ_NPM_CACHE_DIR?: string | undefined;
518
680
  LURQ_METRICS_TOKEN?: string | undefined;
519
681
  LURQ_ISSUER_SECRET?: string | undefined;
682
+ LURQ_POSTHOG_KEY?: string | undefined;
520
683
  STRIPE_SECRET_KEY?: string | undefined;
521
684
  STRIPE_WEBHOOK_SECRET?: string | undefined;
522
685
  STRIPE_PRICE_PRO?: string | undefined;
@@ -540,17 +703,23 @@ declare const EnvSchema: z.ZodObject<{
540
703
  SUMMARY_API_KEY?: string | undefined;
541
704
  SUMMARY_MODEL?: string | undefined;
542
705
  SUMMARY_BASE_URL?: string | undefined;
706
+ LURQ_RESOLVE_CONCURRENCY?: number | undefined;
707
+ LURQ_NPM_CACHE_DIR?: string | undefined;
708
+ LURQ_RESOLVE_TIMEOUT_MS?: number | undefined;
543
709
  LURQ_SYNC_CONCURRENCY?: number | undefined;
544
710
  LURQ_SYNC_REFRESH_CAP?: number | undefined;
545
711
  LOG_LEVEL?: "info" | "error" | "warn" | "debug" | undefined;
546
712
  PORT?: number | undefined;
547
713
  LURQ_RATE_LIMIT_MAX?: number | undefined;
548
714
  LURQ_IP_RATE_LIMIT_MAX?: number | undefined;
715
+ LURQ_SCAN_RATE_LIMIT_MAX?: number | undefined;
549
716
  LURQ_RATE_LIMIT_WINDOW_MS?: number | undefined;
550
717
  LURQ_METRICS_TOKEN?: string | undefined;
551
718
  LURQ_ISSUER_SECRET?: string | undefined;
719
+ LURQ_POSTHOG_KEY?: string | undefined;
552
720
  STRIPE_SECRET_KEY?: string | undefined;
553
721
  STRIPE_WEBHOOK_SECRET?: string | undefined;
722
+ STRIPE_MANAGED_PAYMENTS?: "true" | "false" | undefined;
554
723
  STRIPE_PRICE_PRO?: string | undefined;
555
724
  STRIPE_PRICE_ENTERPRISE?: string | undefined;
556
725
  LURQ_WEB_URL?: string | undefined;
@@ -592,4 +761,4 @@ declare const logger: {
592
761
 
593
762
  declare function buildProgram(): Command;
594
763
 
595
- export { API_KEY_PREFIX, type Advisory, type AdvisorySeverity, type BuildSignal, type BuildVerified, CACHE_TTL, CATEGORIES, type Candidate, type Category, type CategorySource, type CompatConflict, type CompatEvidence, type CompatOutput, type CompatPair, type CompatProvenance, type CompatStatus, type Confidence, type Config, ConfigError, DEFAULT_ECOSYSTEM, DEFAULT_ENDPOINT, type DependencyRanges, type DiscoverySource, type DiscoveryStatus, ECOSYSTEMS, EMBEDDING_DIM, type Ecosystem, type EvaluateOutput, type ExportKind, type ExportSymbol, FRONTEND_CATEGORIES, PACKAGE_NAME, PROVENANCE_RANK, type PeerMeta, type RiskLevel, type Runtime, SERVER_NAME, STALENESS_DAYS, type ScoreBreakdown, type SurfaceDelta, type UsageGuide, type UsageOutput, VERSION, type VerifyOutput, buildProgram, getConfig, isCategory, isFrontendCategory, loadEnv, logger, requireConfig };
764
+ export { API_KEY_PREFIX, type Advisory, type AdvisorySeverity, type BuildSignal, type BuildVerified, CACHE_TTL, CATEGORIES, type Candidate, type Category, type CategorySource, type CompatConflict, type CompatEvidence, type CompatOutput, type CompatPair, type CompatProvenance, type CompatResolution, type CompatStatus, type Confidence, type Config, ConfigError, DEFAULT_ECOSYSTEM, DEFAULT_ENDPOINT, type DependencyRanges, type DiscoverySource, type DiscoveryStatus, ECOSYSTEMS, EMBEDDING_DIM, type Ecosystem, type EvaluateOutput, type ExportKind, type ExportSymbol, FRONTEND_CATEGORIES, PACKAGE_NAME, PROVENANCE_RANK, type PeerMeta, type RiskLevel, type Runtime, SERVER_NAME, STALENESS_DAYS, type ScoreBreakdown, type SurfaceDelta, type UsageGuide, type UsageOutput, VERSION, type VerifyOutput, buildProgram, getConfig, isCategory, isFrontendCategory, loadEnv, logger, requireConfig };