tosijs-ui 1.7.3 → 1.8.0

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/dist/doc-browser.d.ts +11 -0
  3. package/dist/doc-browser.js +63 -7
  4. package/dist/doc-system/doc-system.js +1 -0
  5. package/dist/doc-system/nav-tree.d.ts +24 -5
  6. package/dist/doc-system/nav-tree.js +0 -0
  7. package/dist/doc-system/site/audit-guard.d.ts +124 -0
  8. package/dist/doc-system/site/audit-guard.js +555 -0
  9. package/dist/doc-system/site/dev-server.js +59 -3
  10. package/dist/doc-system/site/generate-site.d.ts +2 -0
  11. package/dist/doc-system/site/generate-site.js +1 -0
  12. package/dist/doc-system/site/index.d.ts +4 -0
  13. package/dist/doc-system/site/index.js +2 -0
  14. package/dist/doc-system/site/open-browser.d.ts +53 -0
  15. package/dist/doc-system/site/open-browser.js +231 -0
  16. package/dist/doc-system/site/orchestrator.d.ts +3 -1
  17. package/dist/doc-system/site/orchestrator.js +14 -1
  18. package/dist/doc-system/site/site-config.d.ts +48 -0
  19. package/dist/hydrate/{ajs-language-qm5w38ae.js → ajs-language-xvpytqfm.js} +2 -2
  20. package/dist/hydrate/{code-editor-cm-95n31m4f.js → code-editor-cm-59je4d8g.js} +6 -6
  21. package/dist/hydrate/{code-editor-cm-95n31m4f.js.map → code-editor-cm-59je4d8g.js.map} +4 -4
  22. package/dist/hydrate/hydrate.js +52 -52
  23. package/dist/hydrate/hydrate.js.map +6 -6
  24. package/dist/hydrate/index-iife-2q1gndgz.js +27 -0
  25. package/dist/hydrate/{index-iife-kwhe0yt8.js.map → index-iife-2q1gndgz.js.map} +3 -3
  26. package/dist/hydrate/{index-iife-ype8sch3.js → index-iife-xv69gy4m.js} +6 -6
  27. package/dist/hydrate/index-iife-xv69gy4m.js.map +12 -0
  28. package/dist/hydrate/{index-0ps8jzdn.js → index-vk8ek4jm.js} +2 -2
  29. package/dist/iife.js +75 -75
  30. package/dist/iife.js.map +10 -10
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/dist/version.d.ts +1 -1
  33. package/dist/version.js +1 -1
  34. package/llms.txt +1 -1
  35. package/package.json +17 -13
  36. package/tls/create-dev-certs.sh +1 -1
  37. package/dist/hydrate/index-iife-kwhe0yt8.js +0 -27
  38. package/dist/hydrate/index-iife-ype8sch3.js.map +0 -12
  39. /package/dist/hydrate/{ajs-language-qm5w38ae.js.map → ajs-language-xvpytqfm.js.map} +0 -0
  40. /package/dist/hydrate/{index-0ps8jzdn.js.map → index-vk8ek4jm.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,94 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.8.0
4
+
5
+ > **Read this first if you use `tosijs-ui/site`.** This release adds a **dependency-audit
6
+ > gate that is ON BY DEFAULT and can fail your build** — on advisories in _your_ dependency
7
+ > tree, not ours. That is deliberate, and there are three ways out (below). Minor, not patch,
8
+ > precisely because `bun run build` can now exit 1 where it previously didn't.
9
+
10
+ - **New: dependency-audit gate** (`src/doc-system/site/audit-guard.ts`, exported from
11
+ `tosijs-ui/site`). `bun audit` knows the registry advisory database; nothing in a normal
12
+ build ever asked it, so a high-severity advisory in a transitive dep stayed invisible until
13
+ someone ran it by hand. Now it's asked at the point a human is looking.
14
+ - **Runs synchronously in every mode** — inside `buildSite` for `bun run build` / `--test`,
15
+ and in `devServer` **just before it binds the port** (it throws, so the server never comes
16
+ up). Sub-second in practice; a gate you wait for can't be raced. **Watch rebuilds never
17
+ audit** — that would put a network call in your edit loop and break offline dev.
18
+ - **What blocks:** any advisory at or above `level` (default `high`) that isn't gated.
19
+ Consumer-facing and developer-facing advisories block **alike** — a dev-only dependency
20
+ still runs on your machine, and the escape hatch makes over-blocking cheap.
21
+ - **Three ways out:** `audit: { allow: [{ advisory, reason, expires }] }` to accept a risk
22
+ **on a deadline** (an expired or malformed gate stops suppressing, so it comes back for
23
+ review instead of living in an allowlist nobody re-reads); `audit: { mode: 'warn' }` to
24
+ report without failing; `audit: false` or `TOSIJS_AUDIT=off` to disable.
25
+ - **Fails open, never closed, when it can't check** — offline, registry down, `bun` too old,
26
+ or past its 20s timeout: it warns and proceeds. It fails **closed** only on a real finding.
27
+ - **Not downgraded in CI.** Unlike the machine-health preflight (a heuristic about someone's
28
+ local box), an advisory is deterministic and environment-independent.
29
+ - **Findings are grouped, sorted worst-first, and annotated** with the _nature_ of the risk
30
+ (`LEAK/ALTER` / `DoS-only` / `DoS?+ESCALATABLE` / `UNCLASSIFIED`), parsed from the CVSS
31
+ vector (3.x and 4.0) plus CWEs. Annotation only — it never changes what blocks, and it
32
+ fails **closed**: ~20% of real advisories carry no CVSS vector at all, and those skew
33
+ severe. Sub-threshold advisories are listed compactly (previously collected and never
34
+ shown), plus an **advisories-per-package tally** — a dependency that keeps producing them
35
+ is a code smell worth replacing rather than patching.
36
+ - **Fix (issue #24, reported by a consumer): nav `order` now sorts numerically.** Section nav
37
+ and the generated `<!-- toc -->` built a zero-padded *string* key and compared it lexically,
38
+ so a fractional order silently sorted to the **end** of its section — `order: 1.5` landed
39
+ after `2`, `3`, … because `"01.5" > "0002"` as text. "Insert a page at N.5" reads like a
40
+ supported idiom and did the opposite with no warning. The same padding capped `order` at 4
41
+ digits (10000+ overflowed the width) and mis-sorted negatives. `pinnedSort` is now a
42
+ multi-key comparator (pin bucket → numeric order → title → filename), which fixes all three.
43
+ **Removed `navSortKey`** (the string-key builder) — it was never in a public index, but was
44
+ reachable via the `./*` subpath, so: technically breaking. A string key cannot express this
45
+ ordering, so there is no corrected version of it.
46
+ - **`src/doc-system/nav-tree.ts` was a binary file.** The old sort key joined title and
47
+ filename with a raw NUL byte, which makes macOS `grep` silently match **nothing** in that
48
+ file (exit 1, no message) — so searching it for any symbol came back empty and the sort bug
49
+ above was effectively unfindable. The comparator needs no delimiter; the NUL is gone.
50
+ - **New: `openBrowser`** site-config option — opens the doc site on dev-server start, reusing
51
+ an existing tab on macOS rather than piling up new ones.
52
+ - **New: `DEV_NO_WATCH=1`** serves the built site without watching. The E2E lane used to run
53
+ `bun start`, so a file watcher stayed live for the whole suite and could `rm -rf docs/` and
54
+ regenerate underneath a test mid-navigation — the lane flaked against its own build system,
55
+ which presents as a product bug.
56
+ - **New public API on `tosijs-ui/site`:** `auditDependencies`, `reportAudit`,
57
+ `resolveAuditMode`, `classifyRisk`, `groupAdvisories`, `openBrowser`, plus their types.
58
+ `buildSite` takes an optional second argument (`{ skipAudit }`).
59
+ - **Fix: `bun run test-browser` no longer reports success on a failed build.** It computed the
60
+ build result and launched the test run regardless, so a failing build (or a blocking
61
+ advisory) still went green. One-shot modes now exit 1.
62
+ - **Fix: the E2E lane can register ServiceWorkers over the dev cert.** `ignoreHTTPSErrors` is a
63
+ browser-_context_ option and never covered a ServiceWorker's own script fetch, so the
64
+ import-resolver worker failed to register and `hydration.pw.ts` caught it as a console error.
65
+ Chromium now launches with `--ignore-certificate-errors`.
66
+ - **Dependency pins:** `overrides` for `flatted` (`>=3.4.2`) and `brace-expansion` (`>=5.0.8`)
67
+ clear live high-severity advisories. Added `.github/dependabot.yml` so advisories published
68
+ later against an unchanged lockfile still surface.
69
+ - Includes the unpublished 1.7.5 (header-logo sizing) — npm goes 1.7.4 → 1.8.0.
70
+
71
+ ## 1.7.5
72
+
73
+ - **Header logo size/spacing are now CSS-tunable** (follow-up to 1.7.4). The brand mark no
74
+ longer hard-wires a pixel height inline; it carries a `.logo-mark` class driven by two
75
+ variables — `--tosi-logo-mark-size` (default **32px**, was a fixed 40px) and
76
+ `--tosi-logo-mark-gap` (default 10px) — for icon, `<img>`, and inline-`<svg>` logos alike.
77
+ (Icon `<svg>`s take their height from `--tosi-icon-size`, set inline by `makeIcon()`, so the
78
+ class points that knob at the same variable rather than fighting the inline height.) Retune a
79
+ logo from CSS instead of editing the build.
80
+
81
+ ## 1.7.4
82
+
83
+ - **Configurable header logo.** A new `logo` option lets a doc site set the brand mark shown
84
+ left of the site title, instead of always inheriting the tosijs-ui logo. Accepts the name of a
85
+ known icon (from `tosijs-ui`'s `icons`, e.g. `'tosiUi'`), an image URL / path / `data:` URI
86
+ (rendered as an `<img>`), or a raw inline `<svg>…</svg>` string. Threaded through
87
+ `SiteConfig.logo` (`tosijs-ui/site`), `createDocBrowser({ logo })`, and an embedded
88
+ `<tosi-doc-system config='{"logo":"…"}'>`. Omit it to keep the prior behavior: the tosijs-ui
89
+ logo when `projectLinks.tosijs` is set, otherwise no mark. Previously the header logo was
90
+ hard-wired to the tosijs-ui mark — the one branding element `SiteConfig` couldn't override.
91
+
3
92
  ## 1.7.3
4
93
 
5
94
  - **New `<tosi-pocket-bar>` component.** A "pocket buttonbar": a single translucent icon at
@@ -61,6 +61,17 @@ export interface DocBrowserOptions {
61
61
  context?: Record<string, any>;
62
62
  projectName?: string;
63
63
  projectLinks?: ProjectLinks;
64
+ /**
65
+ * The brand mark shown left of the site title in the header. One of:
66
+ * - the name of a known icon (from `icons`), e.g. `'tosiUi'`
67
+ * - an image URL / path / data: URI (rendered as an `<img>`)
68
+ * - a raw inline `<svg …>…</svg>` string
69
+ * Omit to fall back to the tosijs-ui logo when `projectLinks.tosijs` is set,
70
+ * or to no mark at all otherwise. Size and spacing are CSS-tunable via
71
+ * `--tosi-logo-mark-size` (default 32px) and `--tosi-logo-mark-gap` (default
72
+ * 10px) on the `.logo-mark` element.
73
+ */
74
+ logo?: string;
64
75
  navSize?: number;
65
76
  minSize?: number;
66
77
  routing?: DocRoutingMode;
@@ -163,7 +163,7 @@ import { tosiLocalized } from './localize';
163
163
  import { popMenu } from './menu';
164
164
  import { codeEditor } from './code-editor';
165
165
  import { tosiDiff } from './diff';
166
- const { div, span, a, header, button, template, input, h2, details, summary, ul, li, } = elements;
166
+ const { div, span, a, header, button, template, input, h2, img, details, summary, ul, li, } = elements;
167
167
  // Test colors
168
168
  const testColor = {
169
169
  pass: varDefault.testColorPass('#0a0'),
@@ -254,11 +254,69 @@ const testIndicatorStyleSpec = {
254
254
  borderRadius: vars.roundedRadius,
255
255
  },
256
256
  };
257
+ // The brand mark shown left of the site title. `logo` (from config) may name a
258
+ // known icon, be an inline `<svg>` string, or be an image URL; when absent we
259
+ // keep the legacy behavior — the tosijs-ui logo iff this is a tosijs project,
260
+ // else nothing. Size and spacing are class-driven (`.logo-mark`, see
261
+ // logoMarkStyleSpec) so a consumer retunes them with one CSS variable each,
262
+ // rather than a hard-wired pixel value.
263
+ // `icons` is a Proxy that returns a factory for ANY string (unknown names get a
264
+ // placeholder glyph), so we can't ask it whether a name is real — we route by the
265
+ // shape of the string instead: an inline `<svg>` renders as-is, a URL/path/data:
266
+ // URI renders as an <img>, and anything else is treated as an icon(-composition)
267
+ // name.
268
+ function looksLikeImageUrl(s) {
269
+ return (/^(https?:)?\/\//.test(s) ||
270
+ s.startsWith('data:') ||
271
+ s.startsWith('/') ||
272
+ s.startsWith('./') ||
273
+ s.startsWith('../') ||
274
+ /\.(png|jpe?g|gif|webp|avif|svg|ico)(\?.*)?$/i.test(s));
275
+ }
276
+ function logoMark(logo, projectLinks) {
277
+ let mark = null;
278
+ if (logo) {
279
+ if (logo.trimStart().startsWith('<')) {
280
+ // Inline <svg>: wrap in a flex holder so `.logo-mark`'s height sizes it.
281
+ mark = span({ style: { display: 'inline-flex', alignItems: 'center' } });
282
+ mark.innerHTML = logo;
283
+ }
284
+ else if (looksLikeImageUrl(logo)) {
285
+ mark = img({ src: logo, alt: '' });
286
+ }
287
+ else {
288
+ mark = icons[logo]();
289
+ }
290
+ }
291
+ else if (projectLinks.tosijs) {
292
+ mark = icons.tosiUi();
293
+ }
294
+ // No mark → an empty span with NO class, so it carries no margin (the title
295
+ // stays flush; a flex `gap` on the header link couldn't do that).
296
+ if (!mark)
297
+ return span();
298
+ mark.classList.add('logo-mark');
299
+ return mark;
300
+ }
301
+ const logoMarkStyleSpec = {
302
+ '.logo-mark': {
303
+ height: 'var(--tosi-logo-mark-size, 32px)',
304
+ // An icon <svg> takes its height from --tosi-icon-size, set INLINE on the svg
305
+ // by makeIcon() — which beats a class `height` rule. Point that knob at the
306
+ // same variable so one --tosi-logo-mark-size drives an icon, an <img>, or an
307
+ // inline <svg> alike.
308
+ _tosiIconSize: 'var(--tosi-logo-mark-size, 32px)',
309
+ marginRight: 'var(--tosi-logo-mark-gap, 10px)',
310
+ flexShrink: '0',
311
+ },
312
+ // Inline-<svg> logos are wrapped in a flex holder; scale the embedded svg to it.
313
+ '.logo-mark > svg': { height: '100%', width: 'auto' },
314
+ };
257
315
  // Monotonic per-page counter so each createDocBrowser() call gets a distinct
258
316
  // tosi() registry key (see stateKey below) and two browsers can't share state.
259
317
  let docBrowserSeq = 0;
260
318
  export function createDocBrowser(options) {
261
- const { docs, context = {}, projectName = '', projectLinks = {}, navSize = 200, minSize = 600, routing = 'query', initialRoute, onRouteChange, navbarLinks, contentElement, } = options;
319
+ const { docs, context = {}, projectName = '', projectLinks = {}, logo, navSize = 200, minSize = 600, routing = 'query', initialRoute, onRouteChange, navbarLinks, contentElement, } = options;
262
320
  // Memory routing is fully self-contained: it never reads or writes
263
321
  // window.history/location, so an embedded or nested browser can't hijack the
264
322
  // host page's URL (or recurse into it).
@@ -522,11 +580,7 @@ export function createDocBrowser(options) {
522
580
  alignItems: 'center',
523
581
  borderBottom: 'none',
524
582
  },
525
- }, projectLinks.tosijs
526
- ? icons.tosiUi({
527
- style: { _xinIconSize: 40, marginRight: 10 },
528
- })
529
- : span(), h2(projectName)));
583
+ }, logoMark(logo, projectLinks), h2(projectName)));
530
584
  }
531
585
  headerContent.push(span({ class: 'elastic' }));
532
586
  // A header link renders as an icon (if `icon` names a known icon) or its label.
@@ -1147,6 +1201,8 @@ export function createDocBrowser(options) {
1147
1201
  showDoc(currentDoc);
1148
1202
  // Inject test indicator styles
1149
1203
  StyleSheet('test-indicators', testIndicatorStyleSpec);
1204
+ // Header brand-mark sizing (icon / <img> / inline-<svg>), one CSS var each.
1205
+ StyleSheet('doc-logo-mark', logoMarkStyleSpec);
1150
1206
  // Floating widget for test status
1151
1207
  const testWidget = button({
1152
1208
  class: 'test-widget',
@@ -409,6 +409,7 @@ export class TosiDocSystem extends Component {
409
409
  },
410
410
  projectName: config.projectName,
411
411
  projectLinks: config.projectLinks,
412
+ logo: config.logo,
412
413
  navbarLinks: navbarLinks.length ? navbarLinks : undefined,
413
414
  contentElement,
414
415
  });
@@ -14,12 +14,31 @@ export interface NavNode<T extends NavDoc = NavDoc> {
14
14
  depth: number;
15
15
  }
16
16
  /**
17
- * Total-order sort key: pin bucket (top<none<bottom), then `order` (default
18
- * 500), then title, then filename. The filename tiebreak guarantees a stable
19
- * total order even for equal titles — required so the generated TOC blocks are
20
- * idempotent (no build-to-build churn).
17
+ * Total order: pin bucket (top<none<bottom), then `order` (NUMERIC, default 500),
18
+ * then title, then filename. The filename tiebreak guarantees a stable total order
19
+ * even for equal titles — required so the generated TOC blocks are idempotent (no
20
+ * build-to-build churn).
21
+ *
22
+ * This is a multi-key COMPARATOR, not a concatenated string key, and that is the
23
+ * whole point (issue #24, reported by a consumer). The old version built
24
+ * `bucket + String(order).padStart(4,'0') + title + filename` and compared it
25
+ * lexically, so `order` sorted as TEXT:
26
+ *
27
+ * order 1 -> "0001"
28
+ * order 2 -> "0002"
29
+ * order 1.5 -> String(1.5) = "1.5" -> padStart(4,'0') = "01.5"
30
+ *
31
+ * and lexically "01.5" > "0002" (at index 1, '1' > '0'), so a doc at 1.5 sorted
32
+ * AFTER 2, 3, ... — last in its section. "Insert a page at N.5" reads like a
33
+ * supported idiom and silently did the opposite, with no warning. The same padding
34
+ * also capped `order` at 4 digits (10000+ overflowed the width) and mis-sorted
35
+ * negatives. Comparing numbers as numbers fixes all three.
36
+ *
37
+ * The old key also joined title and filename with a raw NUL byte, which made this
38
+ * file BINARY: macOS grep then silently matches nothing in it (exit 1, no message),
39
+ * so searching this file for any symbol came back empty and the padStart bug was
40
+ * effectively unfindable. A comparator needs no delimiter, so the NUL is gone.
21
41
  */
22
- export declare function navSortKey(doc: NavDoc): string;
23
42
  export declare function pinnedSort(a: NavDoc, b: NavDoc): number;
24
43
  /**
25
44
  * Build the forest of top-level nav nodes. Children are grouped under their
Binary file
@@ -0,0 +1,124 @@
1
+ export type AuditSeverity = 'info' | 'low' | 'moderate' | 'high' | 'critical';
2
+ export type AuditMode = 'fail' | 'warn' | 'off';
3
+ /** One advisory as `bun audit --json` reports it, flattened with its package. */
4
+ export interface AuditAdvisory {
5
+ package: string;
6
+ id: number;
7
+ url: string;
8
+ title: string;
9
+ severity: AuditSeverity;
10
+ vulnerableVersions?: string;
11
+ /** GHSA id parsed from `url`, e.g. 'GHSA-25h7-pfq9-p65f' (if present) */
12
+ ghsa?: string;
13
+ /** what KIND of harm this is — annotation only, never changes whether it blocks */
14
+ risk?: Classification;
15
+ }
16
+ export type RiskNature =
17
+ /** C or I impact — can leak or alter data / execute code */
18
+ 'compromise'
19
+ /** availability-only — resource exhaustion, hang, crash */
20
+ | 'dos'
21
+ /** no or unparseable vector, or an escalatable CWE — assume the worst */
22
+ | 'unknown';
23
+ export interface Classification {
24
+ nature: RiskNature;
25
+ /** short label for the report line */
26
+ label: string;
27
+ /** what drove the call, so the reader can second-guess it */
28
+ basis: string;
29
+ }
30
+ /** Classify one advisory's nature from its CVSS vector + CWEs. Fails CLOSED. */
31
+ export declare function classifyRisk(raw: {
32
+ cvss?: {
33
+ vectorString?: string;
34
+ } | null;
35
+ cwe?: string[] | null;
36
+ }): Classification;
37
+ /**
38
+ * A time-boxed exception. A gate suppresses a matching advisory ONLY while it is
39
+ * valid and unexpired — after `expires` it stops suppressing and the build fails
40
+ * again, which is the point: an accepted risk gets re-evaluated on a deadline
41
+ * rather than living forever in an allowlist nobody re-reads.
42
+ */
43
+ export interface AuditGate {
44
+ /** GHSA id, the numeric advisory id, or a package name (package match is broad) */
45
+ advisory: string;
46
+ /** why this is temporarily allowed — required, non-empty */
47
+ reason: string;
48
+ /** YYYY-MM-DD; on/after this date the gate no longer suppresses — required */
49
+ expires: string;
50
+ }
51
+ export interface AuditConfig {
52
+ /** 'fail' (default) blocks the build; 'warn' reports and proceeds; 'off' skips it */
53
+ mode?: AuditMode;
54
+ /** minimum severity that blocks, default 'high' */
55
+ level?: AuditSeverity;
56
+ /** time-boxed exceptions */
57
+ allow?: AuditGate[];
58
+ }
59
+ export interface GatedFinding {
60
+ advisory: AuditAdvisory;
61
+ reason: string;
62
+ /** whole days until the gate expires (active gate) */
63
+ daysLeft?: number;
64
+ /** whole days since the gate expired (expired gate) */
65
+ daysAgo?: number;
66
+ }
67
+ export interface AuditResult {
68
+ /** did the audit actually run and parse? false => fail-open, treated as pass */
69
+ ran: boolean;
70
+ /** no blocking findings (or couldn't run) */
71
+ ok: boolean;
72
+ mode: AuditMode;
73
+ level: AuditSeverity;
74
+ /** at/above threshold and NOT suppressed — these fail the build */
75
+ blocking: AuditAdvisory[];
76
+ /** suppressed by a valid, unexpired gate */
77
+ gated: GatedFinding[];
78
+ /** matched a gate whose expiry has passed — NOT suppressed (also in `blocking`) */
79
+ expired: GatedFinding[];
80
+ /** gates that are structurally invalid (missing reason/expires) — ignored */
81
+ invalid: Array<{
82
+ gate: AuditGate;
83
+ problem: string;
84
+ }>;
85
+ /** valid gates that matched no current advisory — safe to delete */
86
+ stale: AuditGate[];
87
+ /** findings below the blocking threshold (reported, never blocking) */
88
+ belowThreshold: AuditAdvisory[];
89
+ }
90
+ /** Injectable subprocess seam for tests. */
91
+ export type AuditRunner = () => Promise<string>;
92
+ export declare const AUDIT_TIMEOUT_MS = 20000;
93
+ /**
94
+ * Parse `bun audit --json` output — `{ "<pkg>": [advisory, …] }` — into a flat
95
+ * advisory list. Returns null when the text is not a JSON object (offline, an
96
+ * error dump, an incompatible bun): the caller reads null as "couldn't check".
97
+ */
98
+ export declare function parseAuditJson(text: string): AuditAdvisory[] | null;
99
+ /** Resolve the effective mode from config + the TOSIJS_AUDIT env override. */
100
+ export declare function resolveAuditMode(config: boolean | AuditConfig | undefined): AuditMode;
101
+ /** Test-only: forget the per-process audit memo. */
102
+ export declare function resetAuditMemo(): void;
103
+ /**
104
+ * Run `bun audit`, classify findings against the configured threshold and the
105
+ * time-boxed allowlist, and return a structured verdict. Never exits.
106
+ *
107
+ * Memoized per process (see above) unless `opts.runAudit` is injected.
108
+ */
109
+ export declare function auditDependencies(config: boolean | AuditConfig | undefined, opts?: {
110
+ now?: Date;
111
+ runAudit?: AuditRunner;
112
+ }): Promise<AuditResult>;
113
+ export interface GroupedAdvisory {
114
+ advisory: AuditAdvisory;
115
+ /** every vulnerable range this advisory matched for this package */
116
+ ranges: string[];
117
+ }
118
+ export declare function groupAdvisories(advisories: AuditAdvisory[]): GroupedAdvisory[];
119
+ /**
120
+ * Print the verdict. Blocking findings first (why the build is failing), then
121
+ * active gates, expired/invalid gates, stale gates, and — when blocking — the
122
+ * due-diligence footer. Returns nothing; the caller decides what to do with `ok`.
123
+ */
124
+ export declare function reportAudit(result: AuditResult, label?: string): void;