milieu-cli 0.1.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 (159) hide show
  1. package/LICENSE +200 -0
  2. package/README.md +153 -0
  3. package/dist/bridges/index.d.ts +5 -0
  4. package/dist/bridges/index.d.ts.map +1 -0
  5. package/dist/bridges/index.js +6 -0
  6. package/dist/bridges/index.js.map +1 -0
  7. package/dist/bridges/reachability/crawler-policy.d.ts +36 -0
  8. package/dist/bridges/reachability/crawler-policy.d.ts.map +1 -0
  9. package/dist/bridges/reachability/crawler-policy.js +110 -0
  10. package/dist/bridges/reachability/crawler-policy.js.map +1 -0
  11. package/dist/bridges/reachability/http-status.d.ts +7 -0
  12. package/dist/bridges/reachability/http-status.d.ts.map +1 -0
  13. package/dist/bridges/reachability/http-status.js +74 -0
  14. package/dist/bridges/reachability/http-status.js.map +1 -0
  15. package/dist/bridges/reachability/https-check.d.ts +14 -0
  16. package/dist/bridges/reachability/https-check.d.ts.map +1 -0
  17. package/dist/bridges/reachability/https-check.js +38 -0
  18. package/dist/bridges/reachability/https-check.js.map +1 -0
  19. package/dist/bridges/reachability/index.d.ts +13 -0
  20. package/dist/bridges/reachability/index.d.ts.map +1 -0
  21. package/dist/bridges/reachability/index.js +115 -0
  22. package/dist/bridges/reachability/index.js.map +1 -0
  23. package/dist/bridges/reachability/meta-robots.d.ts +16 -0
  24. package/dist/bridges/reachability/meta-robots.d.ts.map +1 -0
  25. package/dist/bridges/reachability/meta-robots.js +119 -0
  26. package/dist/bridges/reachability/meta-robots.js.map +1 -0
  27. package/dist/bridges/reachability/robots-parser.d.ts +26 -0
  28. package/dist/bridges/reachability/robots-parser.d.ts.map +1 -0
  29. package/dist/bridges/reachability/robots-parser.js +105 -0
  30. package/dist/bridges/reachability/robots-parser.js.map +1 -0
  31. package/dist/bridges/reachability/robots-txt.d.ts +14 -0
  32. package/dist/bridges/reachability/robots-txt.d.ts.map +1 -0
  33. package/dist/bridges/reachability/robots-txt.js +80 -0
  34. package/dist/bridges/reachability/robots-txt.js.map +1 -0
  35. package/dist/bridges/separation/api-presence.d.ts +14 -0
  36. package/dist/bridges/separation/api-presence.d.ts.map +1 -0
  37. package/dist/bridges/separation/api-presence.js +96 -0
  38. package/dist/bridges/separation/api-presence.js.map +1 -0
  39. package/dist/bridges/separation/developer-docs.d.ts +21 -0
  40. package/dist/bridges/separation/developer-docs.d.ts.map +1 -0
  41. package/dist/bridges/separation/developer-docs.js +81 -0
  42. package/dist/bridges/separation/developer-docs.js.map +1 -0
  43. package/dist/bridges/separation/index.d.ts +20 -0
  44. package/dist/bridges/separation/index.d.ts.map +1 -0
  45. package/dist/bridges/separation/index.js +63 -0
  46. package/dist/bridges/separation/index.js.map +1 -0
  47. package/dist/bridges/separation/sdk-references.d.ts +12 -0
  48. package/dist/bridges/separation/sdk-references.d.ts.map +1 -0
  49. package/dist/bridges/separation/sdk-references.js +93 -0
  50. package/dist/bridges/separation/sdk-references.js.map +1 -0
  51. package/dist/bridges/separation/webhook-support.d.ts +19 -0
  52. package/dist/bridges/separation/webhook-support.d.ts.map +1 -0
  53. package/dist/bridges/separation/webhook-support.js +94 -0
  54. package/dist/bridges/separation/webhook-support.js.map +1 -0
  55. package/dist/bridges/standards/index.d.ts +13 -0
  56. package/dist/bridges/standards/index.d.ts.map +1 -0
  57. package/dist/bridges/standards/index.js +79 -0
  58. package/dist/bridges/standards/index.js.map +1 -0
  59. package/dist/bridges/standards/json-ld.d.ts +16 -0
  60. package/dist/bridges/standards/json-ld.d.ts.map +1 -0
  61. package/dist/bridges/standards/json-ld.js +63 -0
  62. package/dist/bridges/standards/json-ld.js.map +1 -0
  63. package/dist/bridges/standards/llms-txt.d.ts +19 -0
  64. package/dist/bridges/standards/llms-txt.d.ts.map +1 -0
  65. package/dist/bridges/standards/llms-txt.js +64 -0
  66. package/dist/bridges/standards/llms-txt.js.map +1 -0
  67. package/dist/bridges/standards/mcp.d.ts +13 -0
  68. package/dist/bridges/standards/mcp.d.ts.map +1 -0
  69. package/dist/bridges/standards/mcp.js +72 -0
  70. package/dist/bridges/standards/mcp.js.map +1 -0
  71. package/dist/bridges/standards/openapi.d.ts +14 -0
  72. package/dist/bridges/standards/openapi.d.ts.map +1 -0
  73. package/dist/bridges/standards/openapi.js +424 -0
  74. package/dist/bridges/standards/openapi.js.map +1 -0
  75. package/dist/bridges/standards/schema-org.d.ts +12 -0
  76. package/dist/bridges/standards/schema-org.d.ts.map +1 -0
  77. package/dist/bridges/standards/schema-org.js +101 -0
  78. package/dist/bridges/standards/schema-org.js.map +1 -0
  79. package/dist/bridges/standards/well-known.d.ts +16 -0
  80. package/dist/bridges/standards/well-known.d.ts.map +1 -0
  81. package/dist/bridges/standards/well-known.js +77 -0
  82. package/dist/bridges/standards/well-known.js.map +1 -0
  83. package/dist/bridges/stubs.d.ts +4 -0
  84. package/dist/bridges/stubs.d.ts.map +1 -0
  85. package/dist/bridges/stubs.js +25 -0
  86. package/dist/bridges/stubs.js.map +1 -0
  87. package/dist/cli/index.d.ts +4 -0
  88. package/dist/cli/index.d.ts.map +1 -0
  89. package/dist/cli/index.js +83 -0
  90. package/dist/cli/index.js.map +1 -0
  91. package/dist/core/explanations.d.ts +11 -0
  92. package/dist/core/explanations.d.ts.map +1 -0
  93. package/dist/core/explanations.js +128 -0
  94. package/dist/core/explanations.js.map +1 -0
  95. package/dist/core/index.d.ts +6 -0
  96. package/dist/core/index.d.ts.map +1 -0
  97. package/dist/core/index.js +6 -0
  98. package/dist/core/index.js.map +1 -0
  99. package/dist/core/scan.d.ts +3 -0
  100. package/dist/core/scan.d.ts.map +1 -0
  101. package/dist/core/scan.js +89 -0
  102. package/dist/core/scan.js.map +1 -0
  103. package/dist/core/types.d.ts +119 -0
  104. package/dist/core/types.d.ts.map +1 -0
  105. package/dist/core/types.js +3 -0
  106. package/dist/core/types.js.map +1 -0
  107. package/dist/core/version.d.ts +2 -0
  108. package/dist/core/version.d.ts.map +1 -0
  109. package/dist/core/version.js +7 -0
  110. package/dist/core/version.js.map +1 -0
  111. package/dist/index.d.ts +2 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +4 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/render/colors.d.ts +7 -0
  116. package/dist/render/colors.d.ts.map +1 -0
  117. package/dist/render/colors.js +28 -0
  118. package/dist/render/colors.js.map +1 -0
  119. package/dist/render/format-bridge.d.ts +3 -0
  120. package/dist/render/format-bridge.d.ts.map +1 -0
  121. package/dist/render/format-bridge.js +39 -0
  122. package/dist/render/format-bridge.js.map +1 -0
  123. package/dist/render/format-scan.d.ts +3 -0
  124. package/dist/render/format-scan.d.ts.map +1 -0
  125. package/dist/render/format-scan.js +44 -0
  126. package/dist/render/format-scan.js.map +1 -0
  127. package/dist/render/format-verbose.d.ts +3 -0
  128. package/dist/render/format-verbose.d.ts.map +1 -0
  129. package/dist/render/format-verbose.js +14 -0
  130. package/dist/render/format-verbose.js.map +1 -0
  131. package/dist/render/index.d.ts +7 -0
  132. package/dist/render/index.d.ts.map +1 -0
  133. package/dist/render/index.js +8 -0
  134. package/dist/render/index.js.map +1 -0
  135. package/dist/render/progress-bar.d.ts +10 -0
  136. package/dist/render/progress-bar.d.ts.map +1 -0
  137. package/dist/render/progress-bar.js +21 -0
  138. package/dist/render/progress-bar.js.map +1 -0
  139. package/dist/render/symbols.d.ts +10 -0
  140. package/dist/render/symbols.d.ts.map +1 -0
  141. package/dist/render/symbols.js +21 -0
  142. package/dist/render/symbols.js.map +1 -0
  143. package/dist/utils/http-client.d.ts +25 -0
  144. package/dist/utils/http-client.d.ts.map +1 -0
  145. package/dist/utils/http-client.js +235 -0
  146. package/dist/utils/http-client.js.map +1 -0
  147. package/dist/utils/index.d.ts +6 -0
  148. package/dist/utils/index.d.ts.map +1 -0
  149. package/dist/utils/index.js +7 -0
  150. package/dist/utils/index.js.map +1 -0
  151. package/dist/utils/ssrf.d.ts +29 -0
  152. package/dist/utils/ssrf.d.ts.map +1 -0
  153. package/dist/utils/ssrf.js +134 -0
  154. package/dist/utils/ssrf.js.map +1 -0
  155. package/dist/utils/url.d.ts +53 -0
  156. package/dist/utils/url.d.ts.map +1 -0
  157. package/dist/utils/url.js +64 -0
  158. package/dist/utils/url.js.map +1 -0
  159. package/package.json +74 -0
@@ -0,0 +1,28 @@
1
+ import chalk from "chalk";
2
+ function isColorEnabled() {
3
+ const noColor = process.env["NO_COLOR"];
4
+ if (noColor !== undefined && noColor !== "") {
5
+ return false;
6
+ }
7
+ return true;
8
+ }
9
+ const colorEnabled = isColorEnabled();
10
+ export function green(text) {
11
+ return colorEnabled ? chalk.green(text) : text;
12
+ }
13
+ export function yellow(text) {
14
+ return colorEnabled ? chalk.yellow(text) : text;
15
+ }
16
+ export function red(text) {
17
+ return colorEnabled ? chalk.red(text) : text;
18
+ }
19
+ export function cyan(text) {
20
+ return colorEnabled ? chalk.cyan(text) : text;
21
+ }
22
+ export function dim(text) {
23
+ return colorEnabled ? chalk.dim(text) : text;
24
+ }
25
+ export function bold(text) {
26
+ return colorEnabled ? chalk.bold(text) : text;
27
+ }
28
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/render/colors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,SAAS,cAAc;IACrB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACxC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,YAAY,GAAG,cAAc,EAAE,CAAC;AAEtC,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,OAAO,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AACD,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,OAAO,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAClD,CAAC;AACD,MAAM,UAAU,GAAG,CAAC,IAAY;IAC9B,OAAO,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC;AACD,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,OAAO,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AACD,MAAM,UAAU,GAAG,CAAC,IAAY;IAC9B,OAAO,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC;AACD,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,OAAO,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { BridgeResult } from "../core/types.js";
2
+ export declare function formatBridge(bridge: BridgeResult, verbose: boolean): string;
3
+ //# sourceMappingURL=format-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-bridge.d.ts","sourceRoot":"","sources":["../../src/render/format-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAgB/D,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAQ3E"}
@@ -0,0 +1,39 @@
1
+ import { cyan, dim, bold } from "./colors.js";
2
+ import { progressBar } from "./progress-bar.js";
3
+ const BRIDGE_QUESTIONS = {
4
+ 1: "Is my site accessible to AI agents?",
5
+ 2: "Does my site publish machine-readable standards?",
6
+ 3: "Does my site expose API infrastructure?",
7
+ 4: "Can agents use the APIs correctly?",
8
+ 5: "Can agents trust and leverage the context?",
9
+ };
10
+ function bridgeLabel(bridge) {
11
+ return `${bridge.name}: ${BRIDGE_QUESTIONS[bridge.id]}`;
12
+ }
13
+ export function formatBridge(bridge, verbose) {
14
+ if (bridge.status === "not_evaluated") {
15
+ return formatStubBridge(bridge);
16
+ }
17
+ if (bridge.score !== null) {
18
+ return formatScoredBridge(bridge, verbose);
19
+ }
20
+ return formatDetectionBridge(bridge, verbose);
21
+ }
22
+ function formatScoredBridge(bridge, _verbose) {
23
+ const bar = progressBar(bridge.score);
24
+ const label = bridgeLabel(bridge);
25
+ const timing = dim(`(${bridge.durationMs}ms)`);
26
+ return ` ${bold(label)} ${bar} ${bridge.score} ${timing}`;
27
+ }
28
+ function formatDetectionBridge(bridge, _verbose) {
29
+ const detected = bridge.checks.filter((c) => c.status === "pass").length;
30
+ const total = bridge.checks.length;
31
+ const countText = `${detected} of ${total} signals detected`;
32
+ const label = bridgeLabel(bridge);
33
+ const timing = dim(`(${bridge.durationMs}ms)`);
34
+ return ` ${bold(label)} ${cyan(countText)} ${timing}`;
35
+ }
36
+ function formatStubBridge(bridge) {
37
+ return dim(` ${bridgeLabel(bridge)} not evaluated`);
38
+ }
39
+ //# sourceMappingURL=format-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-bridge.js","sourceRoot":"","sources":["../../src/render/format-bridge.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,gBAAgB,GAA6B;IACjD,CAAC,EAAE,qCAAqC;IACxC,CAAC,EAAE,kDAAkD;IACrD,CAAC,EAAE,yCAAyC;IAC5C,CAAC,EAAE,oCAAoC;IACvC,CAAC,EAAE,4CAA4C;CAChD,CAAC;AAEF,SAAS,WAAW,CAAC,MAAoB;IACvC,OAAO,GAAG,MAAM,CAAC,IAAI,KAAK,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAoB,EAAE,OAAgB;IACjE,IAAI,MAAM,CAAC,MAAM,KAAK,eAAe,EAAE,CAAC;QACtC,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1B,OAAO,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAoB,EAAE,QAAiB;IACjE,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,KAAe,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,MAAM,CAAC,UAAU,KAAK,CAAC,CAAC;IAC/C,OAAO,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,MAAM,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;AAChE,CAAC;AAED,SAAS,qBAAqB,CAC5B,MAAoB,EACpB,QAAiB;IAEjB,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;IACnC,MAAM,SAAS,GAAG,GAAG,QAAQ,OAAO,KAAK,mBAAmB,CAAC;IAC7D,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,MAAM,CAAC,UAAU,KAAK,CAAC,CAAC;IAC/C,OAAO,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,MAAM,EAAE,CAAC;AAC3D,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAoB;IAC5C,OAAO,GAAG,CAAC,KAAK,WAAW,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ScanResult } from "../core/types.js";
2
+ export declare function formatScanOutput(result: ScanResult, verbose: boolean, explainAll?: boolean): string;
3
+ //# sourceMappingURL=format-scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-scan.d.ts","sourceRoot":"","sources":["../../src/render/format-scan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAwBnD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,OAAO,EAChB,UAAU,UAAQ,GACjB,MAAM,CA4BR"}
@@ -0,0 +1,44 @@
1
+ import { green, yellow, red, bold, dim } from "./colors.js";
2
+ import { formatBridge } from "./format-bridge.js";
3
+ import { formatVerboseChecks } from "./format-verbose.js";
4
+ function formatTimestamp(isoTimestamp) {
5
+ const d = new Date(isoTimestamp);
6
+ const pad = (n) => String(n).padStart(2, "0");
7
+ return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}`;
8
+ }
9
+ function formatTotalTime(ms) {
10
+ if (ms < 1000)
11
+ return `${ms}ms`;
12
+ return `${(ms / 1000).toFixed(1)}s`;
13
+ }
14
+ function scoreColorFn(label) {
15
+ if (label === "pass")
16
+ return green;
17
+ if (label === "partial")
18
+ return yellow;
19
+ return red;
20
+ }
21
+ export function formatScanOutput(result, verbose, explainAll = false) {
22
+ const lines = [];
23
+ // Header
24
+ const domain = result.url.replace(/^https?:\/\//, "").replace(/\/$/, "");
25
+ lines.push(bold(`Milieu Scan: ${domain}`));
26
+ lines.push(dim(`Scanned: ${formatTimestamp(result.timestamp)}`));
27
+ lines.push("");
28
+ // Bridges
29
+ for (const bridge of result.bridges) {
30
+ lines.push(formatBridge(bridge, verbose));
31
+ if (verbose && bridge.checks.length > 0) {
32
+ lines.push(formatVerboseChecks(bridge.checks, explainAll));
33
+ }
34
+ }
35
+ lines.push("");
36
+ // Overall score (colored by score label)
37
+ const colorize = scoreColorFn(result.overallScoreLabel);
38
+ const scoreLine = `Overall Score: ${colorize(String(result.overallScore))} (${result.overallScoreLabel})`;
39
+ lines.push(bold(scoreLine));
40
+ // Total time
41
+ lines.push(dim(`Total: ${formatTotalTime(result.durationMs)}`));
42
+ return lines.join("\n");
43
+ }
44
+ //# sourceMappingURL=format-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-scan.js","sourceRoot":"","sources":["../../src/render/format-scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Check } from "../core/types.js";
2
+ export declare function formatVerboseChecks(checks: Check[], explainAll?: boolean): string;
3
+ //# sourceMappingURL=format-verbose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-verbose.d.ts","sourceRoot":"","sources":["../../src/render/format-verbose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAI9C,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,UAAQ,GAAG,MAAM,CAU/E"}
@@ -0,0 +1,14 @@
1
+ import { statusSymbol } from "./symbols.js";
2
+ import { dim } from "./colors.js";
3
+ export function formatVerboseChecks(checks, explainAll = false) {
4
+ return checks
5
+ .map((check) => {
6
+ const symbol = statusSymbol(check.status);
7
+ const detail = check.detail ? dim(` (${check.detail})`) : "";
8
+ const showWhy = check.why && (explainAll || check.status !== "pass");
9
+ const why = showWhy ? `\n ${dim(check.why)}` : "";
10
+ return ` ${symbol} ${check.label}${detail}${why}`;
11
+ })
12
+ .join("\n");
13
+ }
14
+ //# sourceMappingURL=format-verbose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-verbose.js","sourceRoot":"","sources":["../../src/render/format-verbose.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC,MAAM,UAAU,mBAAmB,CAAC,MAAe,EAAE,UAAU,GAAG,KAAK;IACrE,OAAO,MAAM;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;QACrE,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,KAAK,CAAC,GAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,OAAO,OAAO,MAAM,IAAI,KAAK,CAAC,KAAK,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC;IACvD,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { green, yellow, red, cyan, dim, bold } from "./colors.js";
2
+ export { progressBar } from "./progress-bar.js";
3
+ export { statusSymbol } from "./symbols.js";
4
+ export { formatBridge } from "./format-bridge.js";
5
+ export { formatVerboseChecks } from "./format-verbose.js";
6
+ export { formatScanOutput } from "./format-scan.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,8 @@
1
+ // Terminal rendering and output formatting (Phase 6)
2
+ export { green, yellow, red, cyan, dim, bold } from "./colors.js";
3
+ export { progressBar } from "./progress-bar.js";
4
+ export { statusSymbol } from "./symbols.js";
5
+ export { formatBridge } from "./format-bridge.js";
6
+ export { formatVerboseChecks } from "./format-verbose.js";
7
+ export { formatScanOutput } from "./format-scan.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Render a 12-character progress bar for a scored bridge.
3
+ * Score 0-100 maps to 0-12 filled characters.
4
+ *
5
+ * Uses Unicode block characters:
6
+ * - U+2588 (full block) for filled
7
+ * - U+2591 (light shade) for empty
8
+ */
9
+ export declare function progressBar(score: number): string;
10
+ //# sourceMappingURL=progress-bar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress-bar.d.ts","sourceRoot":"","sources":["../../src/render/progress-bar.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASjD"}
@@ -0,0 +1,21 @@
1
+ import { green, yellow, red } from "./colors.js";
2
+ /**
3
+ * Render a 12-character progress bar for a scored bridge.
4
+ * Score 0-100 maps to 0-12 filled characters.
5
+ *
6
+ * Uses Unicode block characters:
7
+ * - U+2588 (full block) for filled
8
+ * - U+2591 (light shade) for empty
9
+ */
10
+ export function progressBar(score) {
11
+ const width = 12;
12
+ const filled = Math.round((score / 100) * width);
13
+ const empty = width - filled;
14
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
15
+ if (score >= 80)
16
+ return green(bar);
17
+ if (score >= 40)
18
+ return yellow(bar);
19
+ return red(bar);
20
+ }
21
+ //# sourceMappingURL=progress-bar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress-bar.js","sourceRoot":"","sources":["../../src/render/progress-bar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,KAAK,GAAG,EAAE,CAAC;IACjB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC7B,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE7D,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACpC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Return a colored status symbol for verbose check display.
3
+ *
4
+ * pass -> green checkmark
5
+ * partial -> yellow warning
6
+ * fail -> red x
7
+ * error -> red x
8
+ */
9
+ export declare function statusSymbol(status: "pass" | "partial" | "fail" | "error"): string;
10
+ //# sourceMappingURL=symbols.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symbols.d.ts","sourceRoot":"","sources":["../../src/render/symbols.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAC5C,MAAM,CAUR"}
@@ -0,0 +1,21 @@
1
+ import { green, red, yellow } from "./colors.js";
2
+ /**
3
+ * Return a colored status symbol for verbose check display.
4
+ *
5
+ * pass -> green checkmark
6
+ * partial -> yellow warning
7
+ * fail -> red x
8
+ * error -> red x
9
+ */
10
+ export function statusSymbol(status) {
11
+ switch (status) {
12
+ case "pass":
13
+ return green("\u2714");
14
+ case "partial":
15
+ return yellow("\u26A0");
16
+ case "fail":
17
+ case "error":
18
+ return red("\u2718");
19
+ }
20
+ }
21
+ //# sourceMappingURL=symbols.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symbols.js","sourceRoot":"","sources":["../../src/render/symbols.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,MAA6C;IAE7C,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,QAAQ,CAAC,CAAC;QACzB,KAAK,SAAS;YACZ,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1B,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO;YACV,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { HttpResponse } from "../core/types.js";
2
+ import { type DnsCache } from "./ssrf.js";
3
+ /** Options for httpGet */
4
+ export interface HttpGetOptions {
5
+ /** HTTP method (default: "GET") */
6
+ method?: "GET" | "HEAD";
7
+ /** Per-request timeout in milliseconds (default: 10000) */
8
+ timeout?: number;
9
+ /** Maximum number of redirects to follow (default: 5) */
10
+ maxRedirects?: number;
11
+ /** Maximum response body size in bytes (default: 5MB) */
12
+ maxBodyBytes?: number;
13
+ /** Scan-scoped DNS cache -- caller creates and reuses across requests */
14
+ dnsCache?: DnsCache;
15
+ /** Custom headers to merge with defaults */
16
+ headers?: Record<string, string>;
17
+ }
18
+ /**
19
+ * Perform an HTTP GET (or HEAD) request with SSRF protection, redirect
20
+ * tracking, retry logic, and discriminated union error handling.
21
+ *
22
+ * NEVER throws -- all errors are returned as HttpFailure values.
23
+ */
24
+ export declare function httpGet(url: string, options?: Partial<HttpGetOptions>): Promise<HttpResponse>;
25
+ //# sourceMappingURL=http-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../src/utils/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAGb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGvD,0BAA0B;AAC1B,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAkQD;;;;;GAKG;AACH,wBAAsB,OAAO,CAC3B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAChC,OAAO,CAAC,YAAY,CAAC,CA8BvB"}
@@ -0,0 +1,235 @@
1
+ import { validateDns } from "./ssrf.js";
2
+ import { resolveRedirectUrl } from "./url.js";
3
+ const DEFAULT_OPTIONS = {
4
+ method: "GET",
5
+ timeout: 10_000,
6
+ maxRedirects: 5,
7
+ maxBodyBytes: 5 * 1024 * 1024,
8
+ };
9
+ const USER_AGENT = "milieu-cli/0.1.0";
10
+ // ---------------------------------------------------------------------------
11
+ // Error classification
12
+ // ---------------------------------------------------------------------------
13
+ function classifyFetchError(error, url) {
14
+ // AbortSignal.timeout produces DOMException with name "TimeoutError"
15
+ if (error instanceof DOMException && error.name === "TimeoutError") {
16
+ return { ok: false, error: { kind: "timeout", message: "Request timed out", url } };
17
+ }
18
+ if (error instanceof TypeError && error.cause) {
19
+ const cause = error.cause;
20
+ switch (cause.code) {
21
+ case "ENOTFOUND":
22
+ return { ok: false, error: { kind: "dns", message: `DNS resolution failed for ${url}`, url } };
23
+ case "ECONNREFUSED":
24
+ return { ok: false, error: { kind: "connection_refused", message: "Connection refused", url } };
25
+ case "CERT_HAS_EXPIRED":
26
+ case "DEPTH_ZERO_SELF_SIGNED_CERT":
27
+ case "UNABLE_TO_VERIFY_LEAF_SIGNATURE":
28
+ case "ERR_TLS_CERT_ALTNAME_INVALID":
29
+ return { ok: false, error: { kind: "ssl_error", message: `SSL error: ${cause.code}`, url } };
30
+ default:
31
+ break;
32
+ }
33
+ }
34
+ return { ok: false, error: { kind: "unknown", message: String(error), url } };
35
+ }
36
+ // ---------------------------------------------------------------------------
37
+ // Bot protection detection
38
+ // ---------------------------------------------------------------------------
39
+ function isBotProtected(status, headers) {
40
+ const server = (headers["server"] ?? "").toLowerCase();
41
+ // Cloudflare 403 with server header or cf-ray
42
+ if (status === 403 && (server.includes("cloudflare") || headers["cf-ray"] !== undefined)) {
43
+ return true;
44
+ }
45
+ // 429 rate limit from any server
46
+ if (status === 429)
47
+ return true;
48
+ // Cloudflare 503 challenge
49
+ if (status === 503 && server.includes("cloudflare"))
50
+ return true;
51
+ return false;
52
+ }
53
+ // ---------------------------------------------------------------------------
54
+ // Convert Response headers to plain object
55
+ // ---------------------------------------------------------------------------
56
+ function headersToRecord(headers) {
57
+ const result = {};
58
+ headers.forEach((value, key) => {
59
+ result[key] = value;
60
+ });
61
+ return result;
62
+ }
63
+ // ---------------------------------------------------------------------------
64
+ // Single fetch attempt (no retry)
65
+ // ---------------------------------------------------------------------------
66
+ async function fetchOnce(url, options) {
67
+ let currentUrl = url;
68
+ const redirects = [];
69
+ // Validate initial URL
70
+ try {
71
+ new URL(currentUrl);
72
+ }
73
+ catch {
74
+ return { ok: false, error: { kind: "unknown", message: "Invalid URL", url } };
75
+ }
76
+ for (let hop = 0; hop <= options.maxRedirects; hop++) {
77
+ // SSRF pre-flight at every hop
78
+ const hostname = new URL(currentUrl).hostname;
79
+ const ssrfResult = await validateDns(hostname, options.dnsCache);
80
+ if (!ssrfResult.safe) {
81
+ // Distinguish DNS failure from SSRF block
82
+ const kind = ssrfResult.error.startsWith("DNS resolution failed")
83
+ ? "dns"
84
+ : "ssrf_blocked";
85
+ return { ok: false, error: { kind, message: ssrfResult.error, url: currentUrl } };
86
+ }
87
+ let response;
88
+ try {
89
+ response = await fetch(currentUrl, {
90
+ method: options.method,
91
+ // Manual redirects: required for SSRF re-validation at each hop and redirect chain tracking
92
+ redirect: "manual",
93
+ signal: AbortSignal.timeout(options.timeout),
94
+ headers: options.headers,
95
+ });
96
+ }
97
+ catch (err) {
98
+ return classifyFetchError(err, currentUrl);
99
+ }
100
+ // Handle redirects (3xx)
101
+ if (response.status >= 300 && response.status < 400) {
102
+ const location = response.headers.get("location");
103
+ if (!location) {
104
+ // No Location header -- treat as final response
105
+ break;
106
+ }
107
+ const resolved = resolveRedirectUrl(location, currentUrl);
108
+ if (!resolved.ok) {
109
+ return {
110
+ ok: false,
111
+ error: { kind: "http_error", message: `Invalid redirect: ${resolved.error}`, statusCode: response.status, url: currentUrl },
112
+ };
113
+ }
114
+ redirects.push(currentUrl);
115
+ // Check redirect limit BEFORE following
116
+ if (redirects.length >= options.maxRedirects) {
117
+ return {
118
+ ok: false,
119
+ error: { kind: "http_error", message: `Too many redirects (max ${options.maxRedirects})`, url: currentUrl },
120
+ };
121
+ }
122
+ currentUrl = resolved.url;
123
+ continue;
124
+ }
125
+ // Convert headers
126
+ const headerRecord = headersToRecord(response.headers);
127
+ // Bot protection detection
128
+ if (isBotProtected(response.status, headerRecord)) {
129
+ return {
130
+ ok: false,
131
+ error: { kind: "bot_protected", message: "Bot protection detected", statusCode: response.status, url: currentUrl },
132
+ };
133
+ }
134
+ // 4xx/5xx errors (non-bot)
135
+ if (response.status >= 400) {
136
+ return {
137
+ ok: false,
138
+ error: { kind: "http_error", message: `HTTP ${response.status} ${response.statusText}`, statusCode: response.status, url: currentUrl },
139
+ };
140
+ }
141
+ // Success (2xx) -- read body
142
+ let body = "";
143
+ if (options.method !== "HEAD") {
144
+ // Check Content-Length before reading
145
+ const contentLength = response.headers.get("content-length");
146
+ if (contentLength && parseInt(contentLength, 10) > options.maxBodyBytes) {
147
+ return {
148
+ ok: false,
149
+ error: { kind: "body_too_large", message: `Response body exceeds ${options.maxBodyBytes} bytes`, url: currentUrl },
150
+ };
151
+ }
152
+ body = await response.text();
153
+ // Truncate if body exceeds limit (no Content-Length header case)
154
+ if (body.length > options.maxBodyBytes) {
155
+ body = body.slice(0, options.maxBodyBytes);
156
+ }
157
+ }
158
+ const success = {
159
+ ok: true,
160
+ url: currentUrl,
161
+ status: response.status,
162
+ headers: headerRecord,
163
+ body,
164
+ redirects,
165
+ durationMs: 0, // Set by outer wrapper
166
+ };
167
+ return success;
168
+ }
169
+ // Fell through without returning -- shouldn't happen, but handle gracefully
170
+ return {
171
+ ok: false,
172
+ error: { kind: "http_error", message: `Too many redirects (max ${options.maxRedirects})`, url: currentUrl },
173
+ };
174
+ }
175
+ // ---------------------------------------------------------------------------
176
+ // Retry wrapper
177
+ // ---------------------------------------------------------------------------
178
+ function isRetriable(result) {
179
+ if (result.ok)
180
+ return false;
181
+ const { kind } = result.error;
182
+ // Retry on timeout or connection_refused
183
+ if (kind === "timeout" || kind === "connection_refused")
184
+ return true;
185
+ // Retry on 5xx server errors
186
+ if (kind === "http_error" && result.error.statusCode !== undefined && result.error.statusCode >= 500) {
187
+ return true;
188
+ }
189
+ return false;
190
+ }
191
+ async function fetchWithRetry(url, options) {
192
+ const result = await fetchOnce(url, options);
193
+ if (isRetriable(result)) {
194
+ // Wait 2 seconds before retry
195
+ await new Promise((r) => setTimeout(r, 2000));
196
+ return fetchOnce(url, options);
197
+ }
198
+ return result;
199
+ }
200
+ // ---------------------------------------------------------------------------
201
+ // Public API
202
+ // ---------------------------------------------------------------------------
203
+ /**
204
+ * Perform an HTTP GET (or HEAD) request with SSRF protection, redirect
205
+ * tracking, retry logic, and discriminated union error handling.
206
+ *
207
+ * NEVER throws -- all errors are returned as HttpFailure values.
208
+ */
209
+ export async function httpGet(url, options) {
210
+ const method = options?.method ?? DEFAULT_OPTIONS.method;
211
+ const timeout = options?.timeout ?? DEFAULT_OPTIONS.timeout;
212
+ const maxRedirects = options?.maxRedirects ?? DEFAULT_OPTIONS.maxRedirects;
213
+ const maxBodyBytes = options?.maxBodyBytes ?? DEFAULT_OPTIONS.maxBodyBytes;
214
+ const dnsCache = options?.dnsCache ?? new Map();
215
+ const headers = {
216
+ "User-Agent": USER_AGENT,
217
+ ...(options?.headers ?? {}),
218
+ };
219
+ const start = performance.now();
220
+ const result = await fetchWithRetry(url, {
221
+ method,
222
+ timeout,
223
+ maxRedirects,
224
+ maxBodyBytes,
225
+ dnsCache,
226
+ headers,
227
+ });
228
+ const durationMs = Math.round(performance.now() - start);
229
+ // Attach durationMs to success results
230
+ if (result.ok) {
231
+ return { ...result, durationMs };
232
+ }
233
+ return result;
234
+ }
235
+ //# sourceMappingURL=http-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/utils/http-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { normalizeUrl, extractDomain, resolveRedirectUrl } from "./url.js";
2
+ export { isPrivateIp, validateDns } from "./ssrf.js";
3
+ export type { DnsCache, SsrfResult } from "./ssrf.js";
4
+ export { httpGet } from "./http-client.js";
5
+ export type { HttpGetOptions } from "./http-client.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAG3E,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGtD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,7 @@
1
+ // URL normalization and domain extraction
2
+ export { normalizeUrl, extractDomain, resolveRedirectUrl } from "./url.js";
3
+ // SSRF protection
4
+ export { isPrivateIp, validateDns } from "./ssrf.js";
5
+ // HTTP client
6
+ export { httpGet } from "./http-client.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE3E,kBAAkB;AAClB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGrD,cAAc;AACd,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,29 @@
1
+ /** Scan-scoped DNS cache: hostname -> resolved IP */
2
+ export type DnsCache = Map<string, string>;
3
+ /** SSRF validation result -- discriminated union */
4
+ export type SsrfResult = {
5
+ safe: true;
6
+ ip: string;
7
+ } | {
8
+ safe: false;
9
+ error: string;
10
+ ip?: string;
11
+ };
12
+ /**
13
+ * Check whether an IP address falls in a private/reserved range.
14
+ *
15
+ * Covers all RFC 1918 ranges, loopback, link-local, CGNAT, IPv6 ULA,
16
+ * and IPv4-mapped IPv6 addresses.
17
+ */
18
+ export declare function isPrivateIp(ip: string): boolean;
19
+ /**
20
+ * Validate that a hostname does not resolve to a private/reserved IP.
21
+ *
22
+ * - Checks cache first (scan-scoped, avoids duplicate lookups)
23
+ * - Skips DNS if hostname is already an IP literal
24
+ * - Resolves ALL addresses and rejects if ANY is private
25
+ * - Caches the first resolved IP on success
26
+ * - Uses a 3-second DNS timeout via AbortSignal
27
+ */
28
+ export declare function validateDns(hostname: string, cache: DnsCache): Promise<SsrfResult>;
29
+ //# sourceMappingURL=ssrf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssrf.d.ts","sourceRoot":"","sources":["../../src/utils/ssrf.ts"],"names":[],"mappings":"AAGA,qDAAqD;AACrD,MAAM,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE3C,oDAAoD;AACpD,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA2DhD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAI/C;AAED;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,QAAQ,GACd,OAAO,CAAC,UAAU,CAAC,CAuDrB"}