├── .gitignore ├── .vs └── slnx.sqlite ├── CHANGELOG.md ├── README.md ├── index.js ├── lib ├── report.js ├── reportExecution.js ├── reportExecutionUrl.js ├── reportService.js ├── soap.js └── utils.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.vs/slnx.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vision10/mssql-ssrs/182c944eb49b13508b54c037d2669cf6f4597291/.vs/slnx.sqlite -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Change Log 2 | 3 | ### v2.4.2 4 | - packages bump 5 | ### v2.4.1 6 | - bugfix `uploadFiles` - reading files with multiple directories 7 | - packages bump 8 | ### v2.4.0 9 | - HTML render will automatically get associated image streams in base64 inside the html 10 | - packages bump 11 | ### v2.3.0 12 | - package updated to latest 13 | ### v2.1.0 14 | - replace encodeURI with encodeURIComponent for getReport by url 15 | - package updates 16 | ### v2.0.1-v2.0.4 17 | - packages bump 18 | - bug fix 19 | ### v2.0.0 20 | - update packages (soap v40+ dropped request and httpntlm in favour of axios and axios-ntlm) 21 | - drop internal promisify function, using soap async functions (returned result may change) 22 | - rewritten functionality using classes (big changes) 23 | - documentation updates 24 | 25 | ### v1.4.1 26 | - update packages 27 | - new option for upload reports `options` when deleting existing items - `keepDataSource` (default: false) 28 | 29 | ### v1.4.0 30 | - some documentation updates 31 | - droped internal NtlmSecurity, use soap ntlm security (NTLMSecurity) 32 | - update packages (dropped lodash, replace moment=>dayjs) 33 | - new `createClient` on reportService and reportExecution for multiple clients with diferent configurations 34 | 35 | ### v1.3.9 - v1.3.12 36 | - bug fix 37 | 38 | ### v1.3.8 39 | - auto convert definition parameter on `createReport` to base64 string 40 | 41 | ### v1.3.2 - v1.3.7 42 | - added `include` array to `uploadFiles` 43 | - bug fixes to `fixDataSourceReference`, `upload` 44 | - documentation update 45 | - packages update to latest 46 | 47 | ### v1.3.1 48 | 49 | - consistent parameters for all start functions `report.start`, `reportService.start` and `reportExecution.start` 50 | - documentation updates 51 | - minor buxfix 52 | 53 | ### v1.2.2 - v1.2.4 54 | 55 | - bug fix 56 | 57 | ### v1.2.1 58 | 59 | - packages update 60 | 61 | ### v1.2.0 62 | 63 | - modified `download` returned result 64 | - fixed some minor issues with `getReportByUrl` 65 | - exported `setServerUrl` 66 | 67 | ### v1.1.0 68 | 69 | - documentation update 70 | - added `readFiles` 71 | - `upload` and `uploadFiles` options changed 72 | - `deleteReports` option changed to `deleteExistingItems` 73 | - `auth` option changed to `dataSourceOptions` 74 | - removed `debug` option 75 | - added `logger` object with `log` and `warn` functions, or boolean(outputs to console) 76 | - added `exclude` array for `uploadFiles`, can exclude by name, extension, path 77 | - added `log` parameter to `fixDataSourceReference` 78 | 79 | ### v1.0.0 80 | 81 | - documentation update 82 | - replace custom soap package with original 83 | - default to ntlm request, does not override request if othes security is passed 84 | - auth option changed to [soap config](https://www.npmjs.com/package/soap#options) 85 | - export entire original soap not just custom `createClient` and `security` 86 | - other small improvements 87 | 88 | ### v0.3.0 89 | 90 | - documentation updates 91 | - option `useRs2010` on reportService start function has been changed to `useRs2012` 92 | - still defaults to using 2010 93 | - fixed issue with other types of security than ntlm 94 | - suport for basic security 95 | 96 | ### v0.2.0 97 | 98 | - bug fix for: `upload`, added debug option, 99 | - added: `fixDataSourceReference` 100 | - added: `getItemReferences`, `setItemReferences` 101 | - restricted `getServerUrl`, `setServerUrl`, `getRootFolder`, `setRootFolder`, 102 | - not exported outside the package 103 | - documentation updates 104 | 105 | ### v0.1.0 - v0.1.11 bug fix and documentation 106 | 107 | ### v0.1.0 first release -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![npm version](https://badge.fury.io/js/mssql-ssrs.svg)](https://badge.fury.io/js/mssql-ssrs) 3 | # mssql-ssrs 4 | 5 | > Promise based api for MSSQL reporting services 6 | 7 | ## Table of contents 8 | 9 | - [Install](#install) 10 | - [Usage](#usage) 11 | - [Url/serverConfig/path](#url/serverConfig/path) 12 | - [Soap config](#soap-config) 13 | - [Report service options](#report-service-options) 14 | - [Security](#security) 15 | - [Report Service](#report-service) 16 | - [report service client](#report-service-client) 17 | - [client description](#client-description) 18 | - [list children](#list-children) 19 | - [get parameters for specific report](#get-parameters-for-specific-report) 20 | - [update parameters for specifig report](#update-parameters-for-specifig-report) 21 | - [test data source connection](#testing-data-source-connection) 22 | - [get report properties](#get-report-properties) 23 | - [set report properties](#set-report-properties) 24 | - [list all running jobs](#list-all-running-jobs) 25 | - [cancel running job](#cancel-running-job) 26 | - [get item definition](#get-item-definition) 27 | - [create folder](#create-folder) 28 | - [create data source](#create-data-source) 29 | - [create report](#create-report) 30 | - [delete item](#delete-item) 31 | - [get item datasources](#get-item-data-sources) 32 | - [set item datasources](#set-item-data-sources) 33 | - [get item references](#get-item-references) 34 | - [set item references](#set-item-references) 35 | - [create resource](#create-resource) 36 | - [Report Execution](#report-execution) 37 | - [get report execution client](#get-report-execution-client) 38 | - [get client description](#get-client-description) 39 | - [list available rendering extensions](#list-available-rendering-extensions) 40 | - [run report](#run-report) 41 | - [Report Execution with Url](#report-execution-via-url) 42 | - [run report](#run-report-with-url) 43 | - [Report manager](#report-manager) 44 | - [get report list](#get-report-list) 45 | - [cache report list](#cache-report-list) 46 | - [create a copy of a report](#create-a-copy-of-a-report) 47 | - [create link for report builder for specified report](#create-link-for-report-builder-for-specified-report) 48 | - [clear cached reports](#clear-cached-reports) 49 | - [read reports folder](#read-reports-folder) 50 | - [upload reports](#upload-reports) 51 | - [download reports](#download-reports) 52 | - [fix data source reference](#fix-data-source-reference) 53 | - [Soap](#client) 54 | - [create client](#create-client) 55 | - [security](#security) 56 | - [Contributors](#contributors) 57 | 58 | 59 | ## Install 60 | 61 | Install with [npm](http://github.com/isaacs/npm): 62 | 63 | ``` 64 | npm install mssql-ssrs 65 | ``` 66 | 67 | ## Usage 68 | 69 | MSSQL has 2 parts for reporting services: 70 | - report service for report management (create, search...) 71 | - report execution for report rendering (executing report) 72 | 73 | To start using reporting services we need to connect to the server first: 74 | 75 | start both services (reportService, reportExecution) 76 | 77 | ```js 78 | var { ReportManager } = require('mssql-ssrs'); 79 | 80 | var ssrs = new ReportManager([cacheReports]); 81 | await ssrs.start(url/path/serverConfig, soapConfig [, options] [, security]); 82 | 83 | const list = await ssrs.reportService.listChildren(reportPath); 84 | const report = await ssrs.reportExecution.getReport(reportPath, fileType, parameters); 85 | ... 86 | ``` 87 | 88 | or start them separately 89 | 90 | ```js 91 | var { ReportService, ReportExecution } = require('mssql-ssrs'); 92 | 93 | var rs = new ReportService(); 94 | await rs.start(url/Path/serverConfig, soapConfig [, options] [, security]); 95 | 96 | var re = new ReportExecution(); 97 | await re.start(url/Path/serverConfig, soapConfig [, options] [, security]); 98 | ``` 99 | **NOTE**: [Report Execution via Url](#report-execution-via-url) does not have or require `start` 100 | 101 | #### Url/serverConfig/path 102 | 103 | The `url/serverConfig/path` argument accepts a string url, config object or a system file path (the file path option must contain a valid ssrs wsdl file from reporting services): 104 | 105 | ```js 106 | var url = 'http(s)://:/ReportServer_', 107 | var serverConfig = { 108 | server: 'serverName', 109 | instance: 'serverInstance', 110 | isHttps: false, // optional, default: false 111 | port: 80, // optional, default: 80 112 | }; 113 | ``` 114 | 115 | #### Soap Config 116 | 117 | [soapConfig](https://www.npmjs.com/package/soap#options), can include directly on config object or on config.wsdl_options the folowing properties for ssrs connection: 118 | - `username`: '', (required) 119 | - `password`: '', (required) 120 | - `workstation`: '', (optional) 121 | - `domain`: '', (optional) 122 | 123 | #### Report Service Options 124 | 125 | - `rootFolder`: base folder added to `reportPath` parameters, default: '/' 126 | - `useRs2012`: specify witch version of wsdl should client use (2010/2012), default: false (2010) 127 | - `cache`: specify whether to cache report list, default false 128 | - by default hidden reports are not kept 129 | - `cacheOnStart`: specify whether to cache all reports when starting report services, default false 130 | 131 | #### Report Manager 132 | 133 | - `cacheReports` can also be set directly when instatiating ReportManager 134 | - new ReportManager(true/false) - default false 135 | - same as `cache` option on `start` 136 | - `cacheOnStart` option is stil needed if all reports should be cached at `start` 137 | 138 | #### Security 139 | 140 | More information on types of security see [soap security](https://github.com/vpulim/node-soap#security) 141 | 142 | Defaults to NTLM security so no extra steps needed, just start 143 | 144 | - NTLM security, more details here [Usage](#usage) 145 | ```js 146 | // ex: 147 | await ssrs.start(url, { username: username, password: password }); 148 | 149 | // start everything 150 | await ssrs.start(url/Path/serverConfig, soapConfig [, options] [, security]); 151 | // or start separately 152 | var rs = new ReportService(); 153 | await rs.start(url/Path/serverConfig, soapConfig [, options] [, security]); 154 | var re = new ReportExecution(); 155 | await re.start(url/Path/serverConfig, soapConfig [, options] [, security]); 156 | ``` 157 | 158 | - basic security and others 159 | ```js 160 | // added in the same way for any other security type you use 161 | // instanciating security type can differ 162 | 163 | var auth = { username: username, password: password }; 164 | await ssrs.start(url, auth, null, 'basic'); 165 | 166 | // or 167 | 168 | var wsdl_headers = {}; 169 | var security = new ssrs.soap.security.BasicAuthSecurity(auth.username, auth.password); 170 | security.addHeaders(wsdl_headers); // add authorization 171 | 172 | await ssrs.start(url, { wsdl_headers: wsdl_headers }, null, security); 173 | ``` 174 | 175 | ## Report Service 176 | 177 | - list of all reporting services [methods and options](https://docs.microsoft.com/en-us/dotnet/api/reportservice2010.reportingservice2010?view=sqlserver-2016) 178 | - not all methods where implemented 179 | 180 | ```js 181 | var { ReportService } = require('mssql-ssrs'); 182 | var reportService = new ReportService(); 183 | 184 | await reportService.start(url/Path/serverConfig, soapConfig [, options] [, security]); 185 | ``` 186 | 187 | ### Report service client 188 | 189 | ```js 190 | var client = reportService.getClient(); 191 | or 192 | reportService.client['functionName']() 193 | ``` 194 | 195 | ### Client description 196 | 197 | ```js 198 | var description = reportService.getDescription(); 199 | ``` 200 | 201 | ### List children 202 | 203 | List all children down from current specified folder, if recursive is used it will go down into all folders 204 | ```js 205 | var reportList = await reportService.listChildren(reportPath[, isRcursive]); 206 | ``` 207 | 208 | ### Get parameters for specific report 209 | 210 | ```js 211 | var params = await reportService.getReportParams(reportPath[, forRendering]); 212 | ``` 213 | 214 | ### Update parameters for specifig report 215 | 216 | ```js 217 | var params = await reportService.updateReportParams(reportPath, params[, formatParams]); 218 | ``` 219 | 220 | ### Testing data source connection 221 | 222 | For all DataSourceDefinition properties use [microsoft documentation](https://msdn.microsoft.com/en-us/library/reportservice2010.datasourcedefinition%28v=sql.120%29.aspx) 223 | 224 | ```js 225 | var status = await reportService.testDataSourceConnection(userName, password, dataSourceDefinition) 226 | ``` 227 | 228 | Example for `dataSourceDefinition`: 229 | 230 | ```js 231 | DataSourceDefinition: { 232 | Extension: 'SQL', 233 | ConnectString: 'Data Source=\\;Initial Catalog=' 234 | } 235 | ``` 236 | 237 | ### Get report properties 238 | 239 | If properties are given, all report properties are returned. Report custom properties are not available 240 | 241 | ```js 242 | var properties = ['Hidden', 'Description']; 243 | // or 244 | var properties = [{ Name: 'Hidden' }, { Name: 'Description' }]; 245 | var properties = await reportService.getProperties(reportPath[, properties]) 246 | ``` 247 | 248 | ### Set report properties 249 | 250 | ```js 251 | var properties = { Hidden: true, Description: 'my description' }; 252 | // or 253 | var properties = [ 254 | { Name: 'Hidden', Value: true }, 255 | { Name: 'Description', Value: 'my description' } 256 | ]; 257 | var properties = await reportService.setProperties(reportPath, properties) 258 | ``` 259 | 260 | ### List all running jobs 261 | 262 | ```js 263 | var jobs = await reportService.listJobs() 264 | ``` 265 | 266 | ### Cancel running job 267 | 268 | ```js 269 | await reportService.cancelJob(jobId) 270 | ``` 271 | 272 | ### Get item definition 273 | 274 | ```js 275 | var rdl = await reportService.getItemDefinition(reportPath) 276 | ``` 277 | 278 | ### Create folder 279 | 280 | ```js 281 | await reportService.createFolder(folderName, path) 282 | ``` 283 | 284 | ### Create data source 285 | 286 | ```js 287 | var dataSource = await reportService.createDataSource(dataSourceName, folderPath, overwrite, definition, description, isHidden) 288 | ``` 289 | #### Create data source 290 | - `dataSourceName`: The name for the data source including the file name and, in SharePoint mode, the extension (.rsds). 291 | - `folderPath`: The fully qualified URL for the parent folder that will contain the data source. 292 | - `overwrite`: default false, indicates whether an existing data source with the same name in the location specified should be overwritten. 293 | - `definition`: A `DataSourceDefinition` object that describes the connection properties for the data source. 294 | - `description`: report description 295 | - `isHidden`: hide report in ssrs 296 | 297 | #### Data Source Definition 298 | - `ConnectString`: 'data source=server\instance; initial catalog=databaseName' 299 | - `UseOriginalConnectString`: data source should revert to the original connection string 300 | - `OriginalConnectStringExpressionBased`: indicates whether the original connection string for the data source was expression-based. 301 | - `Extension`: SQL, OLEDB, ODBC, or a custom 302 | - `Enabled`: enable/disable datasource 303 | - `EnabledSpecified`: true if the `Enabled` property should be omitted from the Web service call; otherwise, false. The default is false. 304 | - `CredentialRetrieval`: Prompt, Store, Integrated, None 305 | - `WindowsCredentials`: indicates whether the report server passes user-provided or stored credentials as Windows credentials when it connects to a data source. 306 | - `ImpersonateUser`: indicates whether the report server tries to impersonate a user by using stored credentials. 307 | - `ImpersonateUserSpecified`: true if the `ImpersonateUser` property should be omitted from the Web service call; otherwise, false. The default is false. 308 | - `Prompt`: prompt that the report server displays to the user when it prompts for credentials. 309 | - `UserName`: auth 310 | - `Password`: auth 311 | 312 | ### Create report 313 | 314 | Mostly as above but `definition` property is a `ReportDefinition` object 315 | ```js 316 | var report = await reportService.createReport(reportName, folderPath, overwrite, definition, description, isHidden) 317 | - `reportName`: report name 318 | - `folderPath`: report folder destination 319 | - `overwrite`: overwrite if already exists 320 | - `definition`: report definition xml string (will be automaticaly converted to base64) 321 | - `description`: report description 322 | - `isHidden`: report manager property hidden 323 | ``` 324 | 325 | ### Delete item 326 | 327 | ```js 328 | await reportService.deleteItem(path) 329 | ``` 330 | 331 | ### Create resource 332 | 333 | Usually used for creating images 334 | 335 | ```js 336 | var resurce = await reportService.createResource(name, path, fileContents, overwrite, mimeType); 337 | ``` 338 | 339 | ### Get item data sources 340 | 341 | ```js 342 | var references = await reportService.getItemDataSources(itemPath); 343 | ``` 344 | 345 | ### Set item data sources 346 | 347 | ```js 348 | var dataSources = { dataSourceName: 'dataSourcesNewReferencePath' }); 349 | var references = await reportService.setItemDataSources(itemPath, dataSources); 350 | ``` 351 | - `itemPath`: path of the report including the file name 352 | - `dataSources`: object of dataSourceName: newValue type. 353 | 354 | ### Get item references 355 | 356 | ```js 357 | var references = await reportService.getItemReferences(itemPath, referenceType); 358 | ``` 359 | - `itemPath`: path of the report including the file name 360 | - `referenceType`: 'DataSource'|'DataSet'... 361 | 362 | ### Set item references 363 | 364 | ```js 365 | var refs = { 'DataSourceName': '/path/DataSourceName' }; 366 | var refs = [{ Name: 'DataSourceName': Reference: '/path/DataSourceName' }]; 367 | var references = await reportService.setItemReferences(itemPath, refs); 368 | ``` 369 | - `itemPath`: path of the report including the file name 370 | - `refs`: array of objects with name and reference paths 371 | 372 | ## Report Execution 373 | 374 | ### Get report execution client 375 | 376 | - list of all reporting execution [methods and options](https://docs.microsoft.com/en-us/dotnet/api/reportservice2005.reportingservice2005?view=sqlserver-2016) 377 | - not all methods where implemented 378 | 379 | ```js 380 | var { ReportExecution } = require('mssql-ssrs'); 381 | var reportExecution = new ReportExecution(); 382 | 383 | await reportExecution.start(url/Path/serverConfig, soapConfig [, options] [, security]); 384 | ``` 385 | 386 | Using client soap directly 387 | ```js 388 | var client = reportExecution.getClient(); 389 | or 390 | reportExecution.client['functionName']() 391 | ``` 392 | 393 | ### Get client description 394 | 395 | ```js 396 | var description = reportExecution.getDescription() 397 | ``` 398 | 399 | ### List available rendering extensions 400 | 401 | ```js 402 | var extensions = await reportExecution.listRenderingExtensions() 403 | ``` 404 | 405 | ### Run report 406 | 407 | ```js 408 | var reportPath = '/Folder/ReportName'; 409 | var fileType = 'pdf'; 410 | var parameters = { 411 | parameterName1: 1, 412 | parameterName2: false, 413 | parameterName3: 'parameterValue', 414 | multiValue: ['value1', 'value2'] 415 | }; 416 | //or 417 | var parameters = [ 418 | { Name: 'parameterName1', Value: 1 }, 419 | { Name: 'parameterName2', Value: false }, 420 | { Name: 'parameterName3', Value: 'parameterValue' }, 421 | { Name: 'multiValue', Value: ['value1', 'value2'] } 422 | ] 423 | var report = await reportExecution.getReport(reportPath, fileType, parameters) 424 | ``` 425 | - `parameters` can be an object with name, value atributes or instance of `ReportParameterInfo` objects 426 | **NOTE**: HTML render will automatically get associated image streams from the report server in base64 inside the html 427 | 428 | report result: 429 | ```js 430 | { 431 | "Extension": "pdf", 432 | "MimeType": "application/pdf", 433 | "Result:" "", // base64 string, this is the pdf 434 | "StreamIds": null 435 | } 436 | ``` 437 | 438 | ## Report Execution via Url 439 | 440 | ### Run report (with url) 441 | 442 | No need to use `start` function (it does not exist) 443 | 444 | ```js 445 | var { ReportExecutionUrl } = require('mssql-ssrs'); 446 | 447 | var auth = { 448 | username: 'userName', 449 | password: 'password', 450 | workstation: '', // optional 451 | domain: '' // optional 452 | }; 453 | var re = new ReportExecutionUrl(url/path/serverConfig, auth[, options][, axiosConfig]); 454 | 455 | ``` 456 | - `options`: optional 457 | - rootFolder: the folder to look into for reports 458 | - axiosConfig: [config for axios instance](https://www.npmjs.com/package/axios) 459 | 460 | ```js 461 | var report = await re.getReport(reportPath, fileType, parameters, axiosConfig) 462 | ``` 463 | - `reportPath`: path to the report 464 | - `fileType`: the report file tipe of file extension 465 | - `parameters` can be an object with { name: value } properties or instance of `ReportParameterInfo` objects 466 | - `axiosConfig`: local axios config for overriding defaults per request 467 | 468 | returned result is an [axios response schema](https://www.npmjs.com/package/axios#response-schema) 469 | ```js 470 | { 471 | data: Buffer, 472 | status: ..., 473 | statusText: ..., 474 | headers: ..., 475 | config: ..., 476 | request: ... 477 | } 478 | ``` 479 | 480 | ## Report Manager 481 | 482 | ```js 483 | var { ReportManager } = require('mssql-ssrs'); 484 | var ssrs = new ReportManager(); 485 | 486 | await ssrs.start(url/Path/serverConfig, soapConfig [, options] [, security]); 487 | ``` 488 | 489 | ### Fix Data Source Reference 490 | 491 | ```js 492 | var references = await ssrs.fixDataSourceReference(reportPath, dataSourcePath[, logger]); 493 | ``` 494 | - `reportPath`: path to reports 495 | - `dataSourcePath`: path to data source 496 | 497 | - `log`: boolean, outputs to console 498 | or 499 | - `log`: object 500 | - `log`: function for normal log messages 501 | - `warn`: function for log warrning/error messages 502 | 503 | ### Get report list 504 | 505 | Get report list from cache, if path is not found in cache it will be download and cached 506 | 507 | ```js 508 | var reportList = await ssrs.getReportList(reportPath [, forceRefresh]) 509 | ``` 510 | 511 | - if `reportPath` is not present of is the same as rootFolder for reports entire cache is returned 512 | - `forceRefresh` force a recache, if `reportPath` is not present `rootFolder` is used 513 | 514 | ### Cache report list 515 | 516 | ```js 517 | await ssrs.cacheReportList(reportPath[, keepHidden]) 518 | ``` 519 | 520 | ### Clear cached reports 521 | 522 | ```js 523 | await ssrs.clearCache() 524 | ``` 525 | 526 | ### Create report builder link for specified report 527 | 528 | [Report Builder](https://docs.microsoft.com/en-us/sql/reporting-services/install-windows/install-report-builder) only installs from ie/edge 529 | 530 | ```js 531 | var link = await ssrs.reportBuilder(reportPath) 532 | ``` 533 | 534 | ### Create a copy of a report 535 | 536 | Create a copy of a specified report in the same folder and return new report 537 | 538 | ```js 539 | var newReport = await ssrs.createReportCopy(reportPath, options) 540 | ``` 541 | 542 | Inspired from [Report Loader](http://neilsleightholm.blogspot.ro/2008/08/report-loader-for-sql-server-reporting.html) 543 | 544 | ### Download reports 545 | 546 | Download list of all items down from specified path, can also be used for 1 specific report 547 | 548 | ```js 549 | var fileList = await ssrs.download(reportPath) 550 | ``` 551 | 552 | - `reportPath`: string|Array of strings path for base folders in report service from where to create definitions. 553 | 554 | ### Read reports folder 555 | 556 | ```js 557 | var result = await ssrs.readFiles(filePath, exclude, noDefinitions); 558 | ``` 559 | - `filePath`: path to folder to read 560 | - `exclude`: array of strings to exclude specified files paths, names or extensions 561 | - `noDefinitions`: does not read file content(definition) 562 | 563 | ### Upload reports 564 | 565 | Upload items (report/datasource/image) or entire folder structure to reporting services 566 | 567 | ```js 568 | var warrnings = await ssrs.upload(filePath, reportPath, options) 569 | ``` 570 | - `filePath`: root folder path where to read files 571 | - `reportPath`: report path where to upload files 572 | - `options` for `upload` and `uploadFiles` are the same 573 | 574 | ### Upload reports files 575 | 576 | Read file directory and upload reports 577 | 578 | ```js 579 | var warrnings = await ssrs.uploadFiles(filePath [, reportPath] [, options]); 580 | 581 | var warrnings = await ssrs.uploadFiles('.path/to/root/directory', '/newReportFolderName', { 582 | overwrite: false, 583 | keepDataSource: true, // keep existing datasources 584 | deleteExistingItems: false, 585 | fixDataSourceReference: false, 586 | exclude: ['folderName', '.extension', '/path/to/file.rdl'], 587 | include: { folders: [], dataSources: [], reports: [] }, 588 | dataSourceOptions: { 589 | myDataSourceName: { 590 | ConnectString: 'data source=\; initial catalog=', 591 | UserName: '', 592 | Password: '' 593 | }, 594 | mySecondDataSourceName: { 595 | WindowsCredentials: true, 596 | ConnectString: 'data source=\; initial catalog=', 597 | UserName: '', 598 | Password: '' 599 | } 600 | }, 601 | logger: true || { 602 | log: function (msg) { console.log(msg) }, 603 | warn: function (msg) { console.warn(msg) } 604 | } 605 | }}); 606 | 607 | ``` 608 | - `filePath`: root folder from where to read files 609 | - `reportPath`: report path where to upload, if not specified last folder name from `filePath` is used 610 | - `options`: additional properties object, optional 611 | - `exclude`: array of strings to exclude specified files paths, names or extensions 612 | - `overwrite`: overrites reports and datasources on upload, default true 613 | - `deleteExistingItems`: delete items before upload, default false 614 | - `keepDataSource`: do not delete existing datasources, default false 615 | - `fixDataSourceReference`: fix uploaded reports datasource references with uploaded datasources, default true 616 | - `dataSourceOptions`: each dataSourceName and its connection properties 617 | - `dataSourceName`: 618 | - `connectstring`: connection string for data source 619 | - `userName`: userName for data source 620 | - `password`: password for data source 621 | - name, prompt, security, extension type is determined from the .rds and dataSourceOptions file 622 | - `logger`: boolean, outputs to console 623 | - `logger`: object 624 | - `log`: log messages function 625 | - `warn`: log warrning/error messages function 626 | 627 | ## soap 628 | 629 | ### Create client 630 | 631 | Creates [soap clients](https://github.com/vpulim/node-soap#soapcreateclienturl-options-callback---create-a-new-soap-client-from-a-wsdl-url-also-supports-a-local-filesystem-path) (used for creating reportService and reportExecution client) 632 | 633 | ### Security 634 | 635 | types of [soap security](https://www.npmjs.com/package/soap#security) 636 | 637 | ```js 638 | const ssrs = require('mssql-ssrs') 639 | var customSecurity = await ssrs.soap.security.BasicAuthSecurity('username', 'password'); 640 | var customSecurity = await ssrs.soap.security.NTLMSecurity('username', 'password', 'domain', 'workstation'); 641 | var customSecurity = await ssrs.soap.security.NTLMSecurity({ 642 | username: username, 643 | password: password, 644 | domain: domain, 645 | workstation: workstation 646 | }); 647 | ``` 648 | 649 | ```js 650 | const { soap, SsrsSoap } = require('mssql-ssrs') 651 | 652 | const ssrs = new SsrsSoap([url][, options]) 653 | const client = await ssrs.createClient(url, config[, security]) 654 | ``` 655 | - `url`: url/serverConfig/path 656 | - `config`: { username:'', password:'', domain: '', workstation: '', ...otherOptions } 657 | - `security`: 'ntlm' | 'basic' | customSecurity 658 | 659 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const soap = require('soap'); 2 | const SsrsSoap = require('./lib/soap'); 3 | const ReportManager = require('./lib/report'); 4 | const ReportService = require('./lib/reportService'); 5 | const ReportExecution = require('./lib/reportExecution'); 6 | const ReportExecutionUrl = require('./lib/reportExecutionUrl'); 7 | 8 | module.exports = { 9 | soap, SsrsSoap, 10 | ReportManager, ReportService, 11 | ReportExecution, ReportExecutionUrl, 12 | }; -------------------------------------------------------------------------------- /lib/report.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const dayjs = require('dayjs'); 4 | 5 | const ReportService = require('./reportService'); 6 | const ReportExecution = require('./reportExecution'); 7 | 8 | module.exports = class ReportManager { 9 | constructor(cacheReports) { 10 | this.cache = {}; 11 | this.isCacheable = cacheReports || false; 12 | this.reportService = new ReportService(); 13 | this.reportExecution = new ReportExecution(); 14 | } 15 | 16 | async start(urlOrServerConfig, clientConfig, options, security) { 17 | await this.reportService.start(urlOrServerConfig, clientConfig, options, security); 18 | await this.reportExecution.start(urlOrServerConfig, clientConfig, options, security); 19 | if (options) { 20 | if (options.cache) { this.isCacheable = options.cache } 21 | if (this.isCacheable && options.cacheOnStart) { await this.cacheReportList() } 22 | } 23 | } 24 | 25 | reportBuilder(reportPath) { 26 | const rs = this.reportService.soapInstance || this.reportExecution.soapInstance; 27 | return `${rs.getServerUrl()}/ReportBuilder/ReportBuilder_3_0_0_0.application?ReportPath=${reportPath || '/'}` 28 | } 29 | 30 | clearCache() { 31 | for (var key in this.cache) { delete this.cache[key] } 32 | } 33 | 34 | async getReportList(reportPath, forceRefresh) { 35 | if (this.isCacheable) { 36 | if (!(reportPath in this.cache) || forceRefresh) { 37 | await this.cacheReportList(reportPath) 38 | } 39 | return this.cache[reportPath] 40 | } else { 41 | return await this.reportService.listChildren(reportPath) 42 | } 43 | } 44 | 45 | async cacheReportList(reportPath, keepHidden) { 46 | reportPath = (reportPath || (this.reportService.soapInstance || this.reportExecution.soapInstance).getRootFolder()); 47 | const reports = await this.reportService.listChildren(reportPath, true); 48 | if (!reports.length) { return } 49 | this.cache[reportPath] = []; 50 | 51 | for (var i = 0; i < reports.length; i++) { 52 | if (reports[i].TypeName === "DataSource") { continue; } 53 | if (reports[i].TypeName === "Folder") { 54 | reportPath = reports[i].Path.substr(reports[i].Path.lastIndexOf("/")); 55 | this.cache[reportPath] = []; 56 | } else if (reports[i].TypeName === "ReportItem" || reports[i].TypeName === "Report") { 57 | if (keepHidden) { 58 | this.cache[reportPath].push(reports[i]); 59 | } else { 60 | // eliminate hidden reports 61 | var properties = await this.reportService.getProperties(reports[i].Path, [{ Name: 'Hidden' }]) 62 | if (properties && properties[0].Value === "False") { 63 | this.cache[reportPath].push(reports[i]); 64 | } 65 | } 66 | } 67 | } 68 | } 69 | 70 | async createReportCopy(reportPath, options) { 71 | var reportName = reportPath.substr(reportPath.lastIndexOf("/") + 1); 72 | const reportFolder = reportPath.substr(0, reportPath.lastIndexOf("/")); 73 | 74 | if (reportName.indexOf("_custom_") != -1) { 75 | reportName = reportName.substring(0, reportName.lastIndexOf("_") + 1) + dayjs().format("DDMMYYTHHmm") 76 | } else { 77 | reportName = reportName + "_custom_" + dayjs().format("DDMMYYTHHmm") 78 | } 79 | 80 | const definition = await this.reportService.getItemDefinition(reportPath); 81 | const newReport = await this.reportService.createReport( 82 | options.name || reportName, 83 | options.parent || reportFolder, 84 | options.overwrite || false, 85 | definition, 86 | options.description, 87 | options.hidden 88 | ); 89 | 90 | if (isCacheable) { 91 | this.clearCache(); 92 | this.cacheReportList(); 93 | } 94 | 95 | return newReport; 96 | } 97 | 98 | async download(reportPath) { 99 | if (!Array.isArray(reportPath)) reportPath = [reportPath]; 100 | var files = { folders: [], dataSources: [], reports: [], others: [] }; 101 | while (reportPath.length) { 102 | const result = await this.reportService.listChildren(reportPath.shift(), true); 103 | for (var i = 0; i < result.length; i++) { 104 | var file = { name: result[i].Name, path: result[i].Path }; 105 | if (result[i].TypeName !== 'Folder') { 106 | file.definition = await this.reportService.getItemDefinition(result[i].Path) 107 | } 108 | var placement = 'others'; 109 | if (result[i].TypeName === 'Folder') { placement = 'folders' } 110 | else if (result[i].TypeName === 'Report') { placement = 'reports' } 111 | else if (result[i].TypeName === 'DataSource') { placement = 'dataSources' } 112 | files[placement].push(file); 113 | } 114 | } 115 | return files; 116 | } 117 | 118 | async readFiles(filePath, exclude, noDefinitions) { 119 | const init = { folders: [], reports: [], dataSources: [], other: [] }; 120 | return this.read(filePath, '', init, exclude || [], noDefinitions); 121 | } 122 | 123 | read(root, relativePath, files, exclude, noDefinitions) { 124 | var dir = fs.readdirSync(root + relativePath); 125 | for (var i = 0; i < dir.length; i++) { 126 | var path = relativePath + '/' + dir[i]; 127 | if (exclude.indexOf(dir[i]) > -1) { continue } 128 | if (fs.statSync(root + path).isDirectory()) { 129 | if (exclude.indexOf(path) > -1) { continue; } 130 | files.folders.push({ name: dir[i], path: path }); 131 | this.read(root, path, files, exclude, noDefinitions); 132 | } else { 133 | var idx = dir[i].lastIndexOf('.'); 134 | var ext = dir[i].substring(idx); 135 | if (exclude.indexOf(ext) > -1) { continue } 136 | var options = { name: dir[i].substring(0, idx), path: path }; 137 | 138 | if (!noDefinitions) { 139 | var content = fs.readFileSync(root + path).toString(); 140 | options.definition = content; 141 | } else { 142 | options.filePath = root; 143 | } 144 | 145 | var placement = 'other'; 146 | if (ext === '.rds') { placement = 'dataSources' } 147 | else if (ext === '.rdl') { placement = 'reports' } 148 | files[placement].push(options); 149 | } 150 | } 151 | return files; 152 | } 153 | 154 | async uploadFiles(filePath, reportPath, options) { 155 | var files = await this.readFiles(filePath, options && options.exclude); 156 | if (options) { 157 | var inc = options.include || {}; 158 | if (Array.isArray(inc.folders)) { files.folders = files.folders.concat(inc.folders) } 159 | if (Array.isArray(inc.dataSources)) { files.dataSources = files.dataSources.concat(inc.dataSources) } 160 | if (Array.isArray(inc.reports)) { files.reports = files.reports.concat(inc.reports) } 161 | delete options.include; 162 | } 163 | return await this.upload(reportPath, files, options); 164 | } 165 | 166 | async upload(reportPath, files, options) { 167 | var warrnings = [], options = options || {}; 168 | 169 | function logger(msg, type) { 170 | if (!options.logger) return; 171 | if (options.logger === true) console[type](msg); 172 | else if (options.logger[type]) options.logger[type](msg); 173 | } 174 | function log(msg, type) { logger(msg, 'log') } 175 | log.warn = function warn(msg) { logger(msg, 'warn') } 176 | 177 | try { 178 | log('Check report folder...'); 179 | await this.reportService.listChildren(reportPath); 180 | } catch (error) { 181 | try { 182 | log("Create root folder '" + reportPath + "'."); 183 | var parts = reportPath.split('/'); 184 | var result = await this.reportService.createFolder(parts.pop(), '/' + parts.join('/')); 185 | } catch (error) { 186 | log.warn(error.message); 187 | warrnings.push(error); 188 | } 189 | } 190 | 191 | if (options.deleteExistingItems) { 192 | log('Delete existing items' + (options.keepDataSource ? ', keep DataSources' : '') + ' ...'); 193 | var items = await this.reportService.listChildren(reportPath, true); 194 | items = items || []; 195 | 196 | for (var i = 0; i < items.length; i++) { 197 | try { 198 | if (options.keepDataSource && items[i].TypeName == 'DataSource') { continue } 199 | await this.reportService.deleteItem(items[i].Path); 200 | } catch (error) { 201 | log.warn(error.message); 202 | warrnings.push(error); 203 | } 204 | } 205 | } 206 | 207 | reportPath = /^\//.test(reportPath) ? reportPath.substr(1) : reportPath; 208 | var count = 0; 209 | var total = 1 210 | + (files.folders && files.folders.length || 0) 211 | + (files.dataSources && files.dataSources.length || 0) 212 | + (files.reports && files.reports.length || 0); 213 | 214 | if (files.folders && files.folders.length > 0) { 215 | for (var i = 0; i < files.folders.length; i++) { 216 | try { 217 | var path = this.newPath(files.folders[i].path, reportPath, true); 218 | log(`[${(++count)}/${total}] Create folder: ${path}/${files.folders[i].name}`); 219 | await this.reportService.createFolder(files.folders[i].name, path); 220 | } catch (error) { 221 | log.warn(error.message); 222 | warrnings.push(error); 223 | } 224 | } 225 | } 226 | 227 | if (files.dataSources && files.dataSources.length > 0) { 228 | for (var i = 0; i < files.dataSources.length; i++) { 229 | try { 230 | if (!files.dataSources[i].definition) { 231 | files.dataSources[i].definition = fs.readFileSync(files.reports[i].filePath + files.reports[i].path).toString(); 232 | } 233 | var path = this.newPath(files.dataSources[i].path, reportPath, true); 234 | log(`[${(++count)}/${total}] Create datasource: ${path}/${files.dataSources[i].name}`); 235 | await this.createDataSource(path, 236 | files.dataSources[i].overwrite || options.overwrite, 237 | options.dataSourceOptions && options.dataSourceOptions[files.dataSources[i].name] || {}, 238 | files.dataSources[i].definition, 239 | files.dataSources[i].name); 240 | } catch (error) { 241 | log.warn(error.message); 242 | warrnings.push(error); 243 | } 244 | } 245 | } else if (options.dataSourceOptions) { 246 | for (var key in options.dataSourceOptions) { 247 | log(`Create additional datasource: /${reportPath}/${key}`); 248 | await this.reportService.createDataSource(key, '/' + reportPath, true, options.dataSourceOptions[key]); 249 | } 250 | } 251 | 252 | if (files.reports && files.reports.length > 0) { 253 | for (var i = 0; i < files.reports.length; i++) { 254 | try { 255 | if (!files.reports[i].definition) { 256 | files.reports[i].definition = fs.readFileSync(files.reports[i].filePath + files.reports[i].path).toString(); 257 | } 258 | var path = this.newPath(files.reports[i].path, reportPath, true); 259 | log(`[${(++count)}/${total}] Create report: ${path}/${files.reports[i].name}`); 260 | await this.reportService.createReport(files.reports[i].name, path, 261 | files.reports[i].overwrite || options.overwrite, 262 | files.reports[i].definition); 263 | } catch (error) { 264 | log.warn(error.message); 265 | warrnings.push(error); 266 | } 267 | } 268 | } 269 | 270 | // If shared datasources where created => fix references if necessary 271 | if (options.fixDataSourceReference && (files.dataSources.length > 0 || options.dataSourceOptions)) { 272 | var references = {}; 273 | log('Set datasource references...'); 274 | if (files.dataSources.length > 0) { 275 | for (var i = 0; i < files.dataSources.length; i++) { 276 | var path = this.newPath(files.dataSources[i].path, reportPath).replace(/\.rds$/i, ''); 277 | var name = files.dataSources[i].name || path.substr(path.lastIndexOf('/') + 1).replace(/\.rds$/i, ''); 278 | references[name] = path; 279 | } 280 | } 281 | if (options.dataSourceOptions) { 282 | for (var key in options.dataSourceOptions) { 283 | var path = this.newPath(options.dataSources[key].path, reportPath).replace(/\.rds$/i, ''); 284 | references[key] = path; 285 | } 286 | } 287 | var warn = await this.setReferences(files.reports, references, '/' + reportPath, log); 288 | warrnings.concat(warn); 289 | } 290 | 291 | return warrnings; 292 | } 293 | 294 | newPath(path, newPath, removeName) { 295 | var parts = path.split('/'); 296 | if (parts[0] === "" || parts[0] === '.') { parts.shift() } 297 | if (!parts.length) { return '/' + (newPath || '') } 298 | if (newPath) { parts.unshift(newPath) } 299 | if (removeName) { parts.pop(); } 300 | return '/' + parts.join('/'); 301 | } 302 | 303 | async createDataSource(path, overwrite, auth, rdsFile, rdsName) { 304 | var name = rdsFile.Name || this.getAttribute('Name', rdsFile) || rdsName; 305 | var extension = rdsFile.Extension || this.extractBetween('Extension', rdsFile); 306 | if (!auth.ConnectString) { 307 | auth.ConnectString = this.extractBetween('ConnectString', rdsFile); 308 | } 309 | var security = !!(rdsFile.IntegratedSecurity || this.extractBetween('IntegratedSecurity', rdsFile)); 310 | var prompt = rdsFile.Prompt || this.extractBetween('Prompt', rdsFile); 311 | var promptSpecified = !!prompt; 312 | 313 | var dataSourceDefinition = { 314 | ConnectString: auth.ConnectString || rdsFile.ConnectString, 315 | Extension: extension, 316 | Enabled: true, 317 | EnabledSpecified: true, 318 | ImpersonateUserSpecified: rdsFile.ImpersonateUserSpecified || false, 319 | }; 320 | if (auth.WindowsCredentials || rdsFile.WindowsCredentials) { 321 | dataSourceDefinition.WindowsCredentials = true; 322 | } 323 | // Override security if supplied username 324 | if (rdsFile.UserName || auth.UserName) { 325 | dataSourceDefinition.CredentialRetrieval = 'Store'; 326 | dataSourceDefinition.UserName = auth.UserName || rdsFile.UserName; 327 | dataSourceDefinition.Password = auth.Password || rdsFile.Password; 328 | } else { 329 | if (promptSpecified) { 330 | dataSourceDefinition.CredentialRetrieval = 'Prompt'; 331 | dataSourceDefinition.Prompt = prompt; 332 | } else { 333 | dataSourceDefinition.CredentialRetrieval = 'Integrated'; 334 | dataSourceDefinition.Prompt = null; 335 | } 336 | } 337 | 338 | await this.reportService.createDataSource(name, path, overwrite, dataSourceDefinition); 339 | } 340 | 341 | async fixDataSourceReference(reportPath, dataSourcePath, logger) { 342 | var items = await this.reportService.listChildren(reportPath, true); 343 | var reports = items.filter(r => r.TypeName === 'Report'); 344 | 345 | var dataSources; 346 | if (dataSourcePath && dataSourcePath != reportPath) { 347 | dataSources = await this.reportService.listChildren(dataSourcePath, true); 348 | } else { 349 | dataSources = items.filter(r => r.TypeName === 'DataSource') 350 | } 351 | 352 | function doLog(msg, type) { 353 | if (logger === true) console[type](msg); 354 | else if (logger && logger[type]) logger[type](msg); 355 | } 356 | function log(msg) { doLog(msg, 'log') } 357 | log.warn = function warn(msg) { doLog(msg, 'warn') } 358 | 359 | if (!dataSources.length) { 360 | log.warn('No dataSources found!'); 361 | return []; 362 | } 363 | 364 | var ds = {}; 365 | dataSources.forEach(r => { ds[r.Name] = r.Path }); 366 | 367 | var result = await this.setReferences(reports, ds, '', log); 368 | return result; 369 | } 370 | 371 | async setReferences(reports, dataSources, reportPath, log) { 372 | var warrnings = [], path; 373 | for (var i = 0; i < reports.length; i++) { 374 | try { 375 | path = reportPath + (reports[i].Path || reports[i].path).replace(/\.rdl$/i, ''); 376 | log && log("[" + (i + 1) + "/" + (reports.length + 1) + "] Set '" + path + "' datasource references."); 377 | var result = await this.setDataSourceReference(path, dataSources); 378 | if (result) log(result) 379 | } catch (error) { 380 | log && log.warn(error.message); 381 | warrnings.push(error); 382 | } 383 | } 384 | return warrnings; 385 | } 386 | 387 | async setDataSourceReference(path, rds) { 388 | var dataSources = await this.reportService.getItemReferences(path, 'DataSource'); 389 | if (dataSources.length) { 390 | var refs = []; 391 | for (var i = 0; i < dataSources.length; i++) 392 | if (dataSources[i].Name in rds) 393 | refs.push({ Name: dataSources[i].Name, Reference: rds[dataSources[i].Name].replace(/\.rds$/i, '') }); 394 | if (refs.length) { 395 | await this.reportService.setItemReferences(path, refs); 396 | } else { 397 | return 'No compatible datasources found for ' + path; 398 | } 399 | } 400 | } 401 | 402 | async setDataSource(path, rds) { 403 | var dataSources = await this.reportService.getItemReferences(path, 'DataSource'); 404 | // If datasources are found 405 | if (dataSources.length) { 406 | var refs = []; 407 | for (var i = 0; i < dataSources.length; i++) 408 | if (dataSources[i].Name in rds) { 409 | const dsRef = { Reference: rds[dataSources[i].Name].replace(/\.rds$/i, '') }; 410 | refs.push({ Name: dataSources[i].Name, DataSourceReference: dsRef }); 411 | } 412 | if (refs.length) { 413 | await this.reportService.setItemDataSources(path, refs) 414 | } 415 | } 416 | } 417 | 418 | extractBetween(tag, str) { 419 | var match = new RegExp('<' + tag + '>(.*?)<\/' + tag + '>').exec(str); 420 | return match && match[1]; 421 | } 422 | getAttribute(attr, str) { 423 | var match = new RegExp(attr + '="([^"]*)"').exec(str); 424 | return match && match[1]; 425 | } 426 | } -------------------------------------------------------------------------------- /lib/reportExecution.js: -------------------------------------------------------------------------------- 1 | const dayjs = require('dayjs'); 2 | const utils = require('./utils'); 3 | 4 | module.exports = class ReportExecution { 5 | constructor() { } 6 | 7 | getClient() { return this.client } 8 | getDescription() { return this.client.describe() } 9 | 10 | async start(url, clientConfig, options, security) { 11 | try { 12 | this.soapInstance = utils.createSoapInstance(null, options); 13 | if (/^https?:/.test(url)) { url = url + '/ReportExecution2005.asmx' } 14 | this.client = await this.soapInstance.createClient(url, clientConfig, security); 15 | return this.client; 16 | } catch (err) { utils.errorHandler(err) } 17 | } 18 | 19 | async listRenderingExtensions() { 20 | try { 21 | const result = await this.client.ListRenderingExtensionsAsync(); 22 | return result[0].Extensions.Extension; 23 | } catch (err) { utils.errorHandler(err) } 24 | } 25 | 26 | async #getImageForHtmlRendering(report) { 27 | const { Result: renderedReport, StreamIds } = report[0]; 28 | 29 | // Retrieve and encode images in Base64 30 | const imagesBase64 = {}; 31 | for (const streamId of StreamIds.string) { 32 | const imageData = await this.client.RenderStreamAsync({ 33 | Format: 'HTML5', 34 | StreamID: streamId, 35 | DeviceInfo: 'true' 36 | }); 37 | const base64Image = imageData[0].Result.toString('base64'); 38 | imagesBase64[streamId] = `data:image/png;base64,${base64Image}`; 39 | } 40 | 41 | // Convert from base64 to html 42 | let html = Buffer.from(renderedReport, 'base64').toString('utf8'); 43 | 44 | // Replace image references in the HTML with Base64 data 45 | for (const [streamId, base64Image] of Object.entries(imagesBase64)) { 46 | const regex = new RegExp(`src="[^"]*ImageID=${streamId}"`, 'g'); 47 | html = html.replace(regex, `src="${base64Image}"`); 48 | } 49 | 50 | report[0].Result = Buffer.from(html, 'utf8'); 51 | 52 | return report; 53 | } 54 | 55 | async getReport(reportPath, fileType, params) { 56 | try { 57 | reportPath = utils.testReportPath(this.soapInstance.getRootFolder(), reportPath); 58 | fileType = utils.reportFileFormat(fileType); 59 | 60 | // Loads a report from the report server into a new execution. 61 | const execInfo = await this.client.LoadReportAsync({ Report: reportPath }); 62 | 63 | // clear last headers and include executionId as soap header otherwise request fails 64 | // make shure somehow, header does not change from another request 65 | // would be nice to have headers for 1 specific request not global 66 | const executionHeader = { ExecutionHeader: { ExecutionID: execInfo[0].executionInfo.ExecutionID } }; 67 | const xmlns = 'http://schemas.microsoft.com/sqlserver/2005/06/30/reporting/reportingservices'; 68 | 69 | this.client.clearSoapHeaders(); 70 | const index = this.client.addSoapHeader(executionHeader, '', 'h', xmlns); 71 | const header = this.client.getSoapHeaders()[index]; // keep for later 72 | // Sets and validates parameter values associated with the current report execution. 73 | await this.client.SetExecutionParametersAsync({ 74 | Parameters: { ParameterValue: this.formatParameters(params) }, 75 | ExecutionDateTime: new Date() // set start of ExecutionTime 76 | }); 77 | 78 | this.client.clearSoapHeaders(); 79 | this.client.addSoapHeader(header); // skip processing header again 80 | // Process and render loaded report in the specified format. 81 | let result = await this.client.RenderAsync({ Format: fileType }); 82 | 83 | if(fileType === 'HTML5') { 84 | result = await this.#getImageForHtmlRendering(result); 85 | } 86 | 87 | this.client.clearSoapHeaders(); 88 | 89 | return result; 90 | } catch (err) { 91 | this.client.clearSoapHeaders(); 92 | utils.errorHandler(err); 93 | } 94 | } 95 | 96 | /** 97 | * parameters must be formated like => [{ Name: name, Value: value }] 98 | * 99 | * for params with multivalue 100 | * [{ Name: sameName, Value: [] }] => 101 | * 102 | * [{ Name: sameName, Value: value1 }, { Name: sameName, Value: value2 }] 103 | * 104 | * name value is case sensitive 105 | */ 106 | formatParameters(params) { 107 | return Array.isArray(params) ? this.arrayToReport(params) : this.objectToReport(params) 108 | } 109 | 110 | /** 111 | * [{ Name: nume, Value: valoare }] 112 | */ 113 | arrayToReport(params, checkNulls) { 114 | const formated = []; 115 | for (var i = 0; i < params.length; i++) { 116 | if (params[i].ParameterTypeName === "DateTime" || params[i].Value instanceof Date) { 117 | formated.push({ 118 | Name: params[i].Name, 119 | Value: dayjs(params[i].Value).format("MM/DD/YYYY") 120 | }) 121 | } else if ((params[i].AllowBlank === true || params[i].Nullable === true) && (!params[i].Value || params[i].Value === undefined)) { 122 | formated.push({ 123 | Name: params[i].Name, 124 | Value: undefined 125 | }) 126 | } else if (checkNulls && (!params[i].AllowBlank || !params[i].Nullable) && (!params[i].Value || params[i].Value === undefined)) { 127 | throw `Parameter ${params[i].Name} cannot be undefined!` 128 | } else if (Array.isArray(params[i].Value)) { 129 | if (!params[i].Value.length) 130 | formated.push({ Name: params[i].Name, Value: null }) 131 | if (params[i].Value.length === 1 && params[i].Value[0] === "all validValues") { 132 | for (var j = 0; j < params[i].ValidValues.ValidValue.length; j++) { 133 | formated.push({ 134 | Name: params[i].Name, 135 | Value: params[i].ValidValues.ValidValue[j].Value 136 | }) 137 | } 138 | } else { 139 | for (var j = 0; j < params[i].Value.length; j++) { 140 | formated.push({ 141 | Name: params[i].Name, 142 | Value: params[i].Value[j] 143 | }) 144 | } 145 | } 146 | } else { 147 | formated.push({ Name: params[i].Name, Value: params[i].Value }) 148 | } 149 | } 150 | return formated; 151 | } 152 | 153 | /** 154 | * object params { [name]: value } 155 | * 156 | * for multivalue { [name]: [value1, value2] } => 157 | * 158 | * return [{ Name: sameField, Value: value1 }, { Name: sameField, Value: value2 }] 159 | */ 160 | objectToReport(params) { 161 | const formated = []; 162 | for (var key in params) { 163 | if (params[key] instanceof Date && !isNaN(params[key].valueOf())) { 164 | formated.push({ Name: key, Value: dayjs(params[key]).format("MM/DD/YYYY") }) 165 | } else if (Array.isArray(params[key])) { 166 | if (!params[key].length) { 167 | formated.push({ Name: params[key], Value: undefined }) 168 | } else { 169 | for (var i = 0; i < params[key].length; i++) { 170 | formated.push({ Name: key, Value: params[key][i] }) 171 | } 172 | } 173 | } else { 174 | formated.push({ Name: key, Value: params[key] === null ? undefined : params[key] }) 175 | } 176 | } 177 | return formated; 178 | } 179 | } -------------------------------------------------------------------------------- /lib/reportExecutionUrl.js: -------------------------------------------------------------------------------- 1 | const dayjs = require('dayjs'); 2 | const { NtlmClient } = require('axios-ntlm'); 3 | 4 | const utils = require('./utils'); 5 | 6 | module.exports = class ReportExecutionUrl { 7 | constructor(url, auth, options, axiosConfig) { 8 | this.soapInstance = utils.createSoapInstance(url, options); 9 | this.axiosCfg = axiosConfig; 10 | this.auth = auth; 11 | } 12 | 13 | async getReport(reportPath, fileType, params, axiosConfig) { 14 | if (!this.client) { 15 | const credentials = this.soapInstance.createAuthObj(this.auth); 16 | const defaults = { method: 'get', responseType: 'arraybuffer' }; 17 | this.client = NtlmClient(credentials, Object.assign(defaults, this.axiosCfg, axiosConfig)); 18 | } 19 | const urlPath = utils.testReportPath(this.soapInstance.getRootFolder(), reportPath).replace(/\s/g, '+'); 20 | const urlParams = `&rs:Command=Render&rs:Format=${utils.reportFileFormat(fileType)}${formatParamsToUrl(params)}`; 21 | return await this.client(`${this.soapInstance.getServerUrl()}?${encodeURIComponent(urlPath)}${urlParams}`); 22 | } 23 | } 24 | 25 | /** 26 | * param1=value1¶m2=value2¶m3=value3 27 | * 28 | * for multiple params {sameName: [...]} 29 | * 30 | * sameName=value1%2Cvalue2 31 | */ 32 | function formatParamsToUrl(params) { 33 | const urlParts = []; 34 | if (Array.isArray(params)) { // [{Name: nume, Value: valoare}] 35 | for (var i = 0; i < params.length; i++) { 36 | const value = params[i]; 37 | if (value.ParameterTypeName === "DateTime") { 38 | urlParts.push(value.Name + "=" + dayjs(value.Value).format("MM.DD.YYYY")) 39 | } else if (Array.isArray(value.Value)) { 40 | // result paramName=paramValue1%2CparamValue2%2CparamValue3 41 | const parts = []; 42 | for (var j = 0; j < value.Value.length; j++) { 43 | parts.push(encodeURIComponent(value.Value[j].Value)) 44 | } 45 | urlParts.push(`${value.Name}=${parts.join('%2C')}`) 46 | } else { 47 | urlParts.push(`${value.Name}=${encodeURIComponent(value.Value)}`) 48 | } 49 | } 50 | } else { // { name: value } 51 | for (var key in params) { 52 | const value = params[key]; 53 | if (value instanceof Date && !isNaN(value.valueOf())) { 54 | urlParts.push(key + "=" + dayjs(value).format("MM/DD/YYYY")) 55 | } else if (Array.isArray(value)) { 56 | // result paramName=paramValue1%2CparamValue2%2CparamValue3 57 | const parts = []; 58 | for (var j = 0; j < value.length; j++) { 59 | parts.push(encodeURIComponent(value[j])) 60 | } 61 | urlParts.push(value + "=" + parts.join('%2C')) 62 | } else if (typeof value === 'boolean') { 63 | urlParts.push(key + "=" + (value ? 'True' : 'False')) 64 | } else { 65 | urlParts.push(key + (value === null || value === undefined ? ':IsNull=True' : "=" + encodeURIComponent(value))) 66 | } 67 | } 68 | } 69 | return urlParts.length > 0 ? '&' + urlParts.join('&') : '' 70 | } -------------------------------------------------------------------------------- /lib/reportService.js: -------------------------------------------------------------------------------- 1 | const utils = require('./utils'); 2 | const ReportExecution = require('./reportExecution'); 3 | 4 | module.exports = class ReportService { 5 | constructor() { } 6 | 7 | getClient() { return this.client } 8 | getDescription() { return this.client.describe() } 9 | 10 | async start(url, clientConfig, options, security) { 11 | try { 12 | this.soapInstance = await utils.createSoapInstance(null, options); 13 | if (/^https?:/.test(url)) { 14 | url = url + `/ReportService201${options && options.useRs2012 ? '2' : '0'}.asmx` 15 | } 16 | this.client = await this.soapInstance.createClient(url, clientConfig, security); 17 | return this.client; 18 | } catch (err) { utils.errorHandler(err) } 19 | } 20 | 21 | async listChildren(reportPath, isRecursive) { 22 | try { 23 | const reports = await this.client.ListChildrenAsync({ 24 | ItemPath: reportPath, 25 | Recursive: isRecursive 26 | }); 27 | return reports[0].CatalogItems && reports[0].CatalogItems.CatalogItem; 28 | } catch (err) { utils.errorHandler(err) } 29 | } 30 | 31 | async getReportParams(reportPath, forRendering) { 32 | try { 33 | const result = await this.client.GetItemParametersAsync({ 34 | ItemPath: reportPath, 35 | ForRendering: forRendering || false 36 | }); 37 | return result[0].Parameters && result[0].Parameters.ItemParameter; 38 | } catch (err) { utils.errorHandler(err) } 39 | } 40 | 41 | async updateReportParams(reportPath, params, formatParams) { 42 | try { 43 | if (formatParams) { params = (new ReportExecution()).formatParameters(params) } 44 | const result = await this.client.GetItemParametersAsync({ 45 | ItemPath: reportPath, ForRendering: true, 46 | Values: { ParameterValue: params }, 47 | }); 48 | return result[0].Parameters && result[0].Parameters.ItemParameter; 49 | } catch (err) { utils.errorHandler(err) } 50 | } 51 | 52 | // all DataSourceDefinition properties 53 | // https://msdn.microsoft.com/en-us/library/reportservice2010.datasourcedefinition%28v=sql.120%29.aspx 54 | async testDataSourceConnection(userName, password, dataSourceDefinition) { 55 | try { 56 | const result = await this.client.TestConnectForDataSourceDefinitionAsync({ 57 | DataSourceDefinition: dataSourceDefinition, 58 | UserName: userName, 59 | Password: password, 60 | }); 61 | // throw error on ConnectError ???? 62 | if (result[0].TestConnectForDataSourceDefinitionResult) { 63 | return result[0].TestConnectForDataSourceDefinitionResult 64 | } 65 | return result[0].ConnectError 66 | } catch (err) { utils.errorHandler(err) } 67 | } 68 | 69 | async getProperties(reportPath, properties) { 70 | try { 71 | let props = []; 72 | if (properties) { 73 | if (typeof properties[0] === 'string') { 74 | for (var i = 0; i < properties.length; i++) { props.push({ Name: properties[i] }) } 75 | // props = properties.map(function (p) { return { Name: p } }); 76 | } else { props = properties } 77 | } 78 | 79 | // properties = [{ Name: 'Hidden' }, { Name: 'Description' }] 80 | const args = { ItemPath: reportPath }; 81 | if (props.length) { args.Properties = { Property: props } } 82 | const result = await this.client.GetPropertiesAsync(args); 83 | return result[0].Values.Property; 84 | } catch (err) { utils.errorHandler(err) } 85 | } 86 | 87 | async setProperties(reportPath, properties) { 88 | try { 89 | let props = []; 90 | if (!Array.isArray(properties)) { 91 | for (var key in properties) 92 | props.push({ Name: key, Value: properties[key] }) 93 | } else { props = properties } 94 | 95 | // properties = [{ Name: 'Hidden', Value: true }, { Name: 'Description', Value: true }] 96 | const result = this.client.SetPropertiesAsync({ 97 | ItemPath: reportPath, 98 | Properties: { Property: props } 99 | }); 100 | return result[0] 101 | } catch (err) { utils.errorHandler(err) } 102 | } 103 | 104 | async listJobs() { 105 | try { 106 | const jobs = await client.ListJobsAsync(); 107 | return jobs[0].Jobs; 108 | } catch (err) { utils.errorHandler(err) } 109 | } 110 | 111 | async cancelJob(jobId) { 112 | if (!jobId) { throw new Error("Job id required!") } 113 | try { 114 | const result = await this.client.CancelJobAsync({ JobId: jobId }); 115 | return result[0].Jobs; 116 | } catch (err) { utils.errorHandler(err) } 117 | } 118 | 119 | async getItemDefinition(reportPath) { 120 | try { 121 | const rdl = await this.client.GetItemDefinitionAsync({ ItemPath: reportPath }); 122 | return Buffer.from(rdl[0].Definition, 'base64').toString().replace(/\0/g, ''); 123 | } catch (error) { utils.errorHandler(error) } 124 | } 125 | 126 | async deleteItem(path) { 127 | try { 128 | const result = await this.client.DeleteItemAsync({ ItemPath: path }); 129 | return result[0] 130 | } catch (error) { utils.errorHandler(error) } 131 | } 132 | 133 | async createFolder(folderName, path) { 134 | try { 135 | const result = await this.client.CreateFolderAsync({ Folder: folderName, Parent: path }); 136 | return result[0]; 137 | } catch (error) { utils.errorHandler(error) } 138 | } 139 | 140 | async createDataSource(dataSourceName, folder, overwrite, definition, description, isHidden) { 141 | try { 142 | const result = await this.client.CreateDataSourceAsync({ 143 | Parent: folder, // The fully qualified URL for the parent folder that will contain the data source. 144 | DataSource: dataSourceName, // The name for the data source including the file name and, in SharePoint mode, the extension (.rsds). 145 | Overwrite: overwrite || false, // indicates whether an existing data source with the same name in the location specified should be overwritten. 146 | Definition: definition, // A DataSourceDefinition object that describes the connection properties for the data source. 147 | Properties: { // An array of Property objects that defines the property names and values to set for the data source. 148 | Property: [ 149 | { Name: 'Description', Value: description }, 150 | { Name: 'Hidden', Value: isHidden || false } 151 | ] 152 | }, 153 | }) 154 | return result[0].ItemInfo; 155 | } catch (error) { utils.errorHandler(error) } 156 | } 157 | 158 | async createReport(reportName, folder, overwrite, definition, description, isHidden) { 159 | try { 160 | const newReport = await this.client.CreateCatalogItemAsync({ 161 | ItemType: 'Report', 162 | Parent: folder, 163 | Name: reportName, 164 | Overwrite: overwrite || false, 165 | Definition: Buffer.from(definition).toString('base64'), 166 | Properties: { 167 | Property: [ 168 | { Name: 'Description', Value: description }, 169 | { Name: 'Hidden', Value: isHidden || false } 170 | ] 171 | } 172 | }); 173 | return newReport[0].ItemInfo; 174 | } catch (error) { utils.errorHandler(error) } 175 | } 176 | 177 | async createResource(name, path, fileContents, overwrite, mimeType) { 178 | try { 179 | const resource = await this.client.CreateCatalogItemAsync({ 180 | ItemType: 'Resource', 181 | Parent: path, 182 | Name: name, 183 | Overwrite: overwrite, 184 | Definition: fileContents, 185 | Properties: { 186 | Property: [{ Name: 'MimeType', Value: mimeType }] 187 | } 188 | }); 189 | return resource[0].ItemInfo; 190 | } catch (error) { utils.errorHandler(error) } 191 | } 192 | 193 | async setItemDataSources(itemPath, dataSources) { 194 | try { 195 | const ds = []; 196 | if (Array.isArray(dataSources)) { 197 | ds.push(...dataSources); 198 | } else { 199 | for (var key in dataSources) 200 | ds.push({ Name: key, DataSourceReference: { Reference: dataSources[key] } }); 201 | } 202 | const result = await this.client.SetItemDataSourcesAsync({ 203 | ItemPath: itemPath, 204 | DataSources: { DataSource: ds } 205 | }); 206 | return result[0]; 207 | } catch (error) { utils.errorHandler(error) } 208 | } 209 | 210 | async getItemDataSources(itemPath) { 211 | try { 212 | const result = await this.client.GetItemDataSourcesAsync({ ItemPath: itemPath }); 213 | return result[0].DataSources.DataSource; 214 | } catch (error) { utils.errorHandler(error) } 215 | } 216 | 217 | async getItemReferences(itemPath, referenceItemType) { 218 | try { 219 | const result = await this.client.GetItemReferencesAsync({ 220 | ItemPath: itemPath, 221 | ReferenceItemType: referenceItemType 222 | }); 223 | return result[0].ItemReferences && result[0].ItemReferences.ItemReferenceData || []; 224 | } catch (error) { utils.errorHandler(error) } 225 | } 226 | 227 | async setItemReferences(itemPath, itemReferences) { 228 | try { 229 | const result = await this.client.SetItemReferencesAsync({ 230 | ItemPath: itemPath, 231 | ItemReferences: { ItemReference: itemReferences } 232 | }); 233 | return result[0]; 234 | } catch (error) { utils.errorHandler(error) } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /lib/soap.js: -------------------------------------------------------------------------------- 1 | const soap = require('soap'); 2 | 3 | module.exports = class Soap { 4 | constructor(defaultUrlOrServerConfig, rootFolder) { 5 | defaultUrlOrServerConfig && this.setServerUrl(defaultUrlOrServerConfig); 6 | rootFolder && this.setRootFolder(rootFolder); 7 | } 8 | 9 | async createClient(url, config, security) { 10 | if (url) { this.setServerUrl(url) } 11 | config = config || {}; 12 | if (!security) { security = 'ntlm' } 13 | const auth = this.createAuthObj(config); 14 | const secure = this.getSecurity(security, auth); 15 | 16 | const cfg = this.setOptions(config, security, secure); 17 | this.client = await soap.createClientAsync(this.url, cfg); 18 | if (secure) { this.client.setSecurity(secure) } 19 | 20 | return this.client; 21 | } 22 | 23 | getRootFolder() { return this.rootFolder } 24 | setRootFolder(rootFolder) { this.rootFolder = rootFolder || '/' } 25 | 26 | getServerUrl() { return this.url } 27 | setServerUrl(config) { 28 | this.url = typeof config === 'string' 29 | ? config 30 | : (config.isHttps ? 'https' : 'http') + '://' 31 | + config.server + (config.port ? ':' + config.port : '') 32 | + '/ReportServer' + (config.instance ? '_' + config.instance : '') 33 | } 34 | 35 | createAuthObj(config) { 36 | if (config.wsdl_options) { return config.wsdl_options } 37 | return { 38 | username: (config.username || config.userName), 39 | password: config.password || '', 40 | workstation: config.workstation || '', 41 | domain: config.domain || '' 42 | } 43 | } 44 | 45 | setOptions(config, securityType, security) { 46 | switch (securityType) { 47 | case 'ntlm': 48 | config.wsdl_options = security.defaults; 49 | config.wsdl_headers = config.wsdl_headers || {}; 50 | security && security.addHeaders && security.addHeaders(config.wsdl_headers); 51 | break; 52 | case 'basic': 53 | config.wsdl_headers = config.wsdl_headers || {}; 54 | security && security.addHeaders && security.addHeaders(config.wsdl_headers); 55 | break; 56 | } 57 | return config; 58 | } 59 | 60 | getSecurity(security, auth) { 61 | switch (security) { 62 | case 'ntlm': return new soap.security.NTLMSecurity(auth.username, auth.password, auth.domain, auth.workstation); 63 | case 'basic': return new soap.security.BasicAuthSecurity(auth.username, auth.password); 64 | default: return security; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | const Soap = require('./soap'); 2 | 3 | module.exports = { 4 | createSoapInstance: function createSoapInstance(url, options) { 5 | return new Soap(url, (options || {}).rootFolder) 6 | }, 7 | testReportPath: function testReportPath(rootFolder, reportPath) { 8 | return rootFolder && !new RegExp('^' + rootFolder).test(reportPath) ? rootFolder + reportPath : reportPath; 9 | }, 10 | reportFileFormat: function reportFormat(fileType) { 11 | fileType = fileType && fileType.toUpperCase(); 12 | switch (fileType) { 13 | case 'EXCELOPENXML': case 'EXCEL': case 'XLS': case 'XLSX': return 'EXCELOPENXML'; 14 | case 'WORDOPENXML': case 'WORD': case 'DOC': case 'DOCX': return 'WORDOPENXML'; 15 | default: return fileType || 'PDF'; 16 | } 17 | }, 18 | errorHandler: function errorHandler(err) { 19 | const message = err && err.root && err.root.Envelope && err.root.Envelope.Body && err.root.Envelope.Body.Fault.faultstring; 20 | throw new Error(message || (err && err.message) || err); 21 | } 22 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mssql-ssrs", 3 | "version": "2.4.2", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "mssql-ssrs", 9 | "version": "2.4.2", 10 | "license": "MIT", 11 | "dependencies": { 12 | "axios-ntlm": "^1.4.4", 13 | "dayjs": "^1.11.13", 14 | "soap": "^1.1.11" 15 | }, 16 | "engines": { 17 | "node": ">= 12" 18 | } 19 | }, 20 | "node_modules/@noble/hashes": { 21 | "version": "1.8.0", 22 | "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", 23 | "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", 24 | "license": "MIT", 25 | "engines": { 26 | "node": "^14.21.3 || >=16" 27 | }, 28 | "funding": { 29 | "url": "https://paulmillr.com/funding/" 30 | } 31 | }, 32 | "node_modules/@paralleldrive/cuid2": { 33 | "version": "2.2.2", 34 | "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", 35 | "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", 36 | "license": "MIT", 37 | "dependencies": { 38 | "@noble/hashes": "^1.1.5" 39 | } 40 | }, 41 | "node_modules/@xmldom/is-dom-node": { 42 | "version": "1.0.1", 43 | "resolved": "https://registry.npmjs.org/@xmldom/is-dom-node/-/is-dom-node-1.0.1.tgz", 44 | "integrity": "sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q==", 45 | "license": "MIT", 46 | "engines": { 47 | "node": ">= 16" 48 | } 49 | }, 50 | "node_modules/@xmldom/xmldom": { 51 | "version": "0.8.10", 52 | "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", 53 | "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", 54 | "license": "MIT", 55 | "engines": { 56 | "node": ">=10.0.0" 57 | } 58 | }, 59 | "node_modules/asap": { 60 | "version": "2.0.6", 61 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 62 | "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", 63 | "license": "MIT" 64 | }, 65 | "node_modules/asynckit": { 66 | "version": "0.4.0", 67 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 68 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 69 | "license": "MIT" 70 | }, 71 | "node_modules/axios": { 72 | "version": "1.8.4", 73 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", 74 | "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", 75 | "license": "MIT", 76 | "dependencies": { 77 | "follow-redirects": "^1.15.6", 78 | "form-data": "^4.0.0", 79 | "proxy-from-env": "^1.1.0" 80 | } 81 | }, 82 | "node_modules/axios-ntlm": { 83 | "version": "1.4.4", 84 | "resolved": "https://registry.npmjs.org/axios-ntlm/-/axios-ntlm-1.4.4.tgz", 85 | "integrity": "sha512-kpCRdzMfL8gi0Z0o96P3QPAK4XuC8iciGgxGXe+PeQ4oyjI2LZN8WSOKbu0Y9Jo3T/A7pB81n6jYVPIpglEuRA==", 86 | "license": "MIT", 87 | "dependencies": { 88 | "axios": "^1.8.4", 89 | "des.js": "^1.1.0", 90 | "dev-null": "^0.1.1", 91 | "js-md4": "^0.3.2" 92 | } 93 | }, 94 | "node_modules/call-bind-apply-helpers": { 95 | "version": "1.0.2", 96 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 97 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 98 | "license": "MIT", 99 | "dependencies": { 100 | "es-errors": "^1.3.0", 101 | "function-bind": "^1.1.2" 102 | }, 103 | "engines": { 104 | "node": ">= 0.4" 105 | } 106 | }, 107 | "node_modules/combined-stream": { 108 | "version": "1.0.8", 109 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 110 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 111 | "license": "MIT", 112 | "dependencies": { 113 | "delayed-stream": "~1.0.0" 114 | }, 115 | "engines": { 116 | "node": ">= 0.8" 117 | } 118 | }, 119 | "node_modules/dayjs": { 120 | "version": "1.11.13", 121 | "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", 122 | "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", 123 | "license": "MIT" 124 | }, 125 | "node_modules/debug": { 126 | "version": "4.4.0", 127 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 128 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 129 | "license": "MIT", 130 | "dependencies": { 131 | "ms": "^2.1.3" 132 | }, 133 | "engines": { 134 | "node": ">=6.0" 135 | }, 136 | "peerDependenciesMeta": { 137 | "supports-color": { 138 | "optional": true 139 | } 140 | } 141 | }, 142 | "node_modules/delayed-stream": { 143 | "version": "1.0.0", 144 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 145 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 146 | "license": "MIT", 147 | "engines": { 148 | "node": ">=0.4.0" 149 | } 150 | }, 151 | "node_modules/des.js": { 152 | "version": "1.1.0", 153 | "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", 154 | "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", 155 | "license": "MIT", 156 | "dependencies": { 157 | "inherits": "^2.0.1", 158 | "minimalistic-assert": "^1.0.0" 159 | } 160 | }, 161 | "node_modules/dev-null": { 162 | "version": "0.1.1", 163 | "resolved": "https://registry.npmjs.org/dev-null/-/dev-null-0.1.1.tgz", 164 | "integrity": "sha512-nMNZG0zfMgmdv8S5O0TM5cpwNbGKRGPCxVsr0SmA3NZZy9CYBbuNLL0PD3Acx9e5LIUgwONXtM9kM6RlawPxEQ==", 165 | "license": "MIT" 166 | }, 167 | "node_modules/dezalgo": { 168 | "version": "1.0.4", 169 | "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", 170 | "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", 171 | "license": "ISC", 172 | "dependencies": { 173 | "asap": "^2.0.0", 174 | "wrappy": "1" 175 | } 176 | }, 177 | "node_modules/dunder-proto": { 178 | "version": "1.0.1", 179 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 180 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 181 | "license": "MIT", 182 | "dependencies": { 183 | "call-bind-apply-helpers": "^1.0.1", 184 | "es-errors": "^1.3.0", 185 | "gopd": "^1.2.0" 186 | }, 187 | "engines": { 188 | "node": ">= 0.4" 189 | } 190 | }, 191 | "node_modules/es-define-property": { 192 | "version": "1.0.1", 193 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 194 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 195 | "license": "MIT", 196 | "engines": { 197 | "node": ">= 0.4" 198 | } 199 | }, 200 | "node_modules/es-errors": { 201 | "version": "1.3.0", 202 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 203 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 204 | "license": "MIT", 205 | "engines": { 206 | "node": ">= 0.4" 207 | } 208 | }, 209 | "node_modules/es-object-atoms": { 210 | "version": "1.1.1", 211 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 212 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 213 | "license": "MIT", 214 | "dependencies": { 215 | "es-errors": "^1.3.0" 216 | }, 217 | "engines": { 218 | "node": ">= 0.4" 219 | } 220 | }, 221 | "node_modules/es-set-tostringtag": { 222 | "version": "2.1.0", 223 | "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", 224 | "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", 225 | "license": "MIT", 226 | "dependencies": { 227 | "es-errors": "^1.3.0", 228 | "get-intrinsic": "^1.2.6", 229 | "has-tostringtag": "^1.0.2", 230 | "hasown": "^2.0.2" 231 | }, 232 | "engines": { 233 | "node": ">= 0.4" 234 | } 235 | }, 236 | "node_modules/follow-redirects": { 237 | "version": "1.15.9", 238 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", 239 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", 240 | "funding": [ 241 | { 242 | "type": "individual", 243 | "url": "https://github.com/sponsors/RubenVerborgh" 244 | } 245 | ], 246 | "license": "MIT", 247 | "engines": { 248 | "node": ">=4.0" 249 | }, 250 | "peerDependenciesMeta": { 251 | "debug": { 252 | "optional": true 253 | } 254 | } 255 | }, 256 | "node_modules/form-data": { 257 | "version": "4.0.2", 258 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", 259 | "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", 260 | "license": "MIT", 261 | "dependencies": { 262 | "asynckit": "^0.4.0", 263 | "combined-stream": "^1.0.8", 264 | "es-set-tostringtag": "^2.1.0", 265 | "mime-types": "^2.1.12" 266 | }, 267 | "engines": { 268 | "node": ">= 6" 269 | } 270 | }, 271 | "node_modules/formidable": { 272 | "version": "3.5.4", 273 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", 274 | "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", 275 | "license": "MIT", 276 | "dependencies": { 277 | "@paralleldrive/cuid2": "^2.2.2", 278 | "dezalgo": "^1.0.4", 279 | "once": "^1.4.0" 280 | }, 281 | "engines": { 282 | "node": ">=14.0.0" 283 | }, 284 | "funding": { 285 | "url": "https://ko-fi.com/tunnckoCore/commissions" 286 | } 287 | }, 288 | "node_modules/function-bind": { 289 | "version": "1.1.2", 290 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 291 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 292 | "license": "MIT", 293 | "funding": { 294 | "url": "https://github.com/sponsors/ljharb" 295 | } 296 | }, 297 | "node_modules/get-intrinsic": { 298 | "version": "1.3.0", 299 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", 300 | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", 301 | "license": "MIT", 302 | "dependencies": { 303 | "call-bind-apply-helpers": "^1.0.2", 304 | "es-define-property": "^1.0.1", 305 | "es-errors": "^1.3.0", 306 | "es-object-atoms": "^1.1.1", 307 | "function-bind": "^1.1.2", 308 | "get-proto": "^1.0.1", 309 | "gopd": "^1.2.0", 310 | "has-symbols": "^1.1.0", 311 | "hasown": "^2.0.2", 312 | "math-intrinsics": "^1.1.0" 313 | }, 314 | "engines": { 315 | "node": ">= 0.4" 316 | }, 317 | "funding": { 318 | "url": "https://github.com/sponsors/ljharb" 319 | } 320 | }, 321 | "node_modules/get-proto": { 322 | "version": "1.0.1", 323 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 324 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 325 | "license": "MIT", 326 | "dependencies": { 327 | "dunder-proto": "^1.0.1", 328 | "es-object-atoms": "^1.0.0" 329 | }, 330 | "engines": { 331 | "node": ">= 0.4" 332 | } 333 | }, 334 | "node_modules/get-stream": { 335 | "version": "6.0.1", 336 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", 337 | "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", 338 | "license": "MIT", 339 | "engines": { 340 | "node": ">=10" 341 | }, 342 | "funding": { 343 | "url": "https://github.com/sponsors/sindresorhus" 344 | } 345 | }, 346 | "node_modules/gopd": { 347 | "version": "1.2.0", 348 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 349 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 350 | "license": "MIT", 351 | "engines": { 352 | "node": ">= 0.4" 353 | }, 354 | "funding": { 355 | "url": "https://github.com/sponsors/ljharb" 356 | } 357 | }, 358 | "node_modules/has-symbols": { 359 | "version": "1.1.0", 360 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 361 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 362 | "license": "MIT", 363 | "engines": { 364 | "node": ">= 0.4" 365 | }, 366 | "funding": { 367 | "url": "https://github.com/sponsors/ljharb" 368 | } 369 | }, 370 | "node_modules/has-tostringtag": { 371 | "version": "1.0.2", 372 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", 373 | "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", 374 | "license": "MIT", 375 | "dependencies": { 376 | "has-symbols": "^1.0.3" 377 | }, 378 | "engines": { 379 | "node": ">= 0.4" 380 | }, 381 | "funding": { 382 | "url": "https://github.com/sponsors/ljharb" 383 | } 384 | }, 385 | "node_modules/hasown": { 386 | "version": "2.0.2", 387 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 388 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 389 | "license": "MIT", 390 | "dependencies": { 391 | "function-bind": "^1.1.2" 392 | }, 393 | "engines": { 394 | "node": ">= 0.4" 395 | } 396 | }, 397 | "node_modules/inherits": { 398 | "version": "2.0.4", 399 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 400 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 401 | "license": "ISC" 402 | }, 403 | "node_modules/js-md4": { 404 | "version": "0.3.2", 405 | "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", 406 | "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", 407 | "license": "MIT" 408 | }, 409 | "node_modules/lodash": { 410 | "version": "4.17.21", 411 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 412 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 413 | "license": "MIT" 414 | }, 415 | "node_modules/math-intrinsics": { 416 | "version": "1.1.0", 417 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 418 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 419 | "license": "MIT", 420 | "engines": { 421 | "node": ">= 0.4" 422 | } 423 | }, 424 | "node_modules/mime-db": { 425 | "version": "1.52.0", 426 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 427 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 428 | "license": "MIT", 429 | "engines": { 430 | "node": ">= 0.6" 431 | } 432 | }, 433 | "node_modules/mime-types": { 434 | "version": "2.1.35", 435 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 436 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 437 | "license": "MIT", 438 | "dependencies": { 439 | "mime-db": "1.52.0" 440 | }, 441 | "engines": { 442 | "node": ">= 0.6" 443 | } 444 | }, 445 | "node_modules/minimalistic-assert": { 446 | "version": "1.0.1", 447 | "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", 448 | "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", 449 | "license": "ISC" 450 | }, 451 | "node_modules/ms": { 452 | "version": "2.1.3", 453 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 454 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 455 | "license": "MIT" 456 | }, 457 | "node_modules/once": { 458 | "version": "1.4.0", 459 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 460 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 461 | "license": "ISC", 462 | "dependencies": { 463 | "wrappy": "1" 464 | } 465 | }, 466 | "node_modules/proxy-from-env": { 467 | "version": "1.1.0", 468 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 469 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 470 | "license": "MIT" 471 | }, 472 | "node_modules/sax": { 473 | "version": "1.4.1", 474 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", 475 | "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", 476 | "license": "ISC" 477 | }, 478 | "node_modules/soap": { 479 | "version": "1.1.11", 480 | "resolved": "https://registry.npmjs.org/soap/-/soap-1.1.11.tgz", 481 | "integrity": "sha512-wxpKktgEZZvxHIKisFPB4VgURNhSuSJU3mX/1kP11GxlENNzYe0gWk3w/+vLhpx68mMSMjeMR/8sahaPXVBj+Q==", 482 | "license": "MIT", 483 | "dependencies": { 484 | "axios": "^1.8.4", 485 | "axios-ntlm": "^1.4.4", 486 | "debug": "^4.4.0", 487 | "formidable": "^3.5.2", 488 | "get-stream": "^6.0.1", 489 | "lodash": "^4.17.21", 490 | "sax": "^1.4.1", 491 | "strip-bom": "^3.0.0", 492 | "whatwg-mimetype": "4.0.0", 493 | "xml-crypto": "^6.1.1" 494 | }, 495 | "engines": { 496 | "node": ">=14.17.0" 497 | } 498 | }, 499 | "node_modules/strip-bom": { 500 | "version": "3.0.0", 501 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 502 | "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", 503 | "license": "MIT", 504 | "engines": { 505 | "node": ">=4" 506 | } 507 | }, 508 | "node_modules/whatwg-mimetype": { 509 | "version": "4.0.0", 510 | "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", 511 | "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", 512 | "license": "MIT", 513 | "engines": { 514 | "node": ">=18" 515 | } 516 | }, 517 | "node_modules/wrappy": { 518 | "version": "1.0.2", 519 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 520 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 521 | "license": "ISC" 522 | }, 523 | "node_modules/xml-crypto": { 524 | "version": "6.1.1", 525 | "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-6.1.1.tgz", 526 | "integrity": "sha512-ni6H4Xnd5zvpDKvevmFmmKNojF2cAoTcioeSasICsDTkF0pjqS/PlHsMCLjiruH0N6iVa3OCBMHRLwRfcUPo2g==", 527 | "license": "MIT", 528 | "dependencies": { 529 | "@xmldom/is-dom-node": "^1.0.1", 530 | "@xmldom/xmldom": "^0.8.10", 531 | "xpath": "^0.0.33" 532 | }, 533 | "engines": { 534 | "node": ">=16" 535 | } 536 | }, 537 | "node_modules/xpath": { 538 | "version": "0.0.33", 539 | "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.33.tgz", 540 | "integrity": "sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA==", 541 | "license": "MIT", 542 | "engines": { 543 | "node": ">=0.6.0" 544 | } 545 | } 546 | } 547 | } 548 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mssql-ssrs", 3 | "version": "2.4.2", 4 | "description": "Promise based api for MSSQL Reporting Services with ntlm and basic security", 5 | "author": "Sorin Muresan ", 6 | "keywords": [ 7 | "mssql", 8 | "sql", 9 | "ssrs", 10 | "reporting services", 11 | "soap", 12 | "ntlm" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/vision10/mssql-ssrs.git" 17 | }, 18 | "license": "MIT", 19 | "engines": { 20 | "node": ">= 12" 21 | }, 22 | "main": "index.js", 23 | "directories": { 24 | "lib": "./lib" 25 | }, 26 | "scripts": { 27 | "start": "node index.js" 28 | }, 29 | "dependencies": { 30 | "axios-ntlm": "^1.4.4", 31 | "dayjs": "^1.11.13", 32 | "soap": "^1.1.11" 33 | } 34 | } 35 | --------------------------------------------------------------------------------