1+ #!/usr/bin/env node
2+ import fs from 'fs' ;
3+ import https from 'https' ;
4+ import { URL } from 'url' ;
5+
6+ const SWAGGER_URL = 'https://news.purelymail.com/api/swagger-spec.js' ;
7+ const OUTPUT_JS = 'swagger-spec.js' ;
8+ const OUTPUT_JSON = 'purelymail-api-spec.json' ;
9+
10+ console . error ( 'Fetching PurelyMail swagger specification...' ) ;
11+
12+ async function fetchSwagger ( ) {
13+ try {
14+ const response = await fetch ( SWAGGER_URL ) ;
15+ if ( ! response . ok ) {
16+ throw new Error ( `HTTP ${ response . status } : ${ response . statusText } ` ) ;
17+ }
18+
19+ const jsContent = await response . text ( ) ;
20+
21+ // Write the original JS file
22+ fs . writeFileSync ( OUTPUT_JS , jsContent ) ;
23+ console . error ( `✓ Downloaded ${ OUTPUT_JS } ` ) ;
24+
25+ // Extract the JSON spec from the JS file
26+ const spec = await extractSpecFromJS ( jsContent ) ;
27+
28+ // Write the JSON spec
29+ fs . writeFileSync ( OUTPUT_JSON , JSON . stringify ( spec , null , 2 ) ) ;
30+ console . error ( `✓ Extracted ${ OUTPUT_JSON } ` ) ;
31+
32+ console . error ( 'Swagger specification updated successfully!' ) ;
33+
34+ } catch ( error ) {
35+ console . error ( `✗ Failed to fetch from ${ SWAGGER_URL } : ${ error . message } ` ) ;
36+
37+ // Check if we have existing files to fall back to
38+ if ( fs . existsSync ( OUTPUT_JS ) && fs . existsSync ( OUTPUT_JSON ) ) {
39+ console . error ( `Using existing files as fallback` ) ;
40+ process . exit ( 0 ) ;
41+ } else {
42+ console . error ( `No existing files found. Please check your internet connection or the URL.` ) ;
43+ process . exit ( 1 ) ;
44+ }
45+ }
46+ }
47+
48+ async function extractSpecFromJS ( jsContent ) {
49+ try {
50+ // Try to extract the spec from the JS content
51+ // The swagger-spec.js contains: window.swaggerSpec = { ... }
52+ // We need to safely evaluate it
53+
54+ // Create a safe evaluation context with window object
55+ const windowContext = { swaggerSpec : null } ;
56+
57+ // Replace window.swaggerSpec with our context
58+ const safeJs = jsContent . replace ( / w i n d o w \. s w a g g e r S p e c \s * = / , 'windowContext.swaggerSpec =' ) ;
59+
60+ // Use Function constructor for safer evaluation than eval
61+ const fn = new Function ( 'windowContext' , safeJs + '; return windowContext.swaggerSpec;' ) ;
62+ const spec = fn ( windowContext ) ;
63+
64+ if ( ! spec || typeof spec !== 'object' ) {
65+ throw new Error ( 'Invalid swagger spec format' ) ;
66+ }
67+
68+ // Validate it looks like a swagger spec
69+ if ( ! spec . openapi && ! spec . swagger && ! spec . info ) {
70+ throw new Error ( 'Not a valid OpenAPI/Swagger specification' ) ;
71+ }
72+
73+ return spec ;
74+
75+ } catch ( error ) {
76+ console . error ( `Failed to extract spec from JS: ${ error . message } ` ) ;
77+
78+ // Fallback: try to use existing JSON file
79+ if ( fs . existsSync ( OUTPUT_JSON ) ) {
80+ console . error ( 'Using existing JSON file as fallback' ) ;
81+ return JSON . parse ( fs . readFileSync ( OUTPUT_JSON , 'utf8' ) ) ;
82+ }
83+
84+ throw new Error ( 'Could not extract or find existing swagger specification' ) ;
85+ }
86+ }
87+
88+ // Polyfill for Node.js < 18
89+ if ( ! globalThis . fetch ) {
90+ globalThis . fetch = async function ( url ) {
91+ return new Promise ( ( resolve , reject ) => {
92+ const parsedUrl = new URL ( url ) ;
93+ const options = {
94+ hostname : parsedUrl . hostname ,
95+ port : parsedUrl . port ,
96+ path : parsedUrl . pathname + parsedUrl . search ,
97+ method : 'GET' ,
98+ headers : {
99+ 'User-Agent' : 'PurelyMail MCP Server'
100+ }
101+ } ;
102+
103+ const req = https . request ( options , ( res ) => {
104+ let data = '' ;
105+ res . on ( 'data' , chunk => data += chunk ) ;
106+ res . on ( 'end' , ( ) => {
107+ resolve ( {
108+ ok : res . statusCode >= 200 && res . statusCode < 300 ,
109+ status : res . statusCode ,
110+ statusText : res . statusMessage ,
111+ text : async ( ) => data
112+ } ) ;
113+ } ) ;
114+ } ) ;
115+
116+ req . on ( 'error' , reject ) ;
117+ req . end ( ) ;
118+ } ) ;
119+ } ;
120+ }
121+
122+ fetchSwagger ( ) ;
0 commit comments