@sasjs/adapter 4.17.0 → 4.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,12 +12,15 @@ export declare class RequestClient implements HttpClient {
12
12
  protected csrfToken: CsrfToken;
13
13
  protected fileUploadCsrfToken: CsrfToken | undefined;
14
14
  protected httpClient: AxiosInstance;
15
+ private isRecoveringFromNetworkError;
15
16
  constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions, requestsLimit?: number, verboseMode?: VerboseMode);
16
17
  setConfig(baseUrl: string, httpsAgentOptions?: https.AgentOptions): void;
17
18
  saveLocalStorageToken(accessToken: string, refreshToken: string): void;
18
19
  getCsrfToken(type?: 'general' | 'file'): CsrfToken | undefined;
19
20
  clearCsrfTokens(): void;
20
21
  clearLocalStorageTokens(): void;
22
+ resetInMemoryAuthState(): void;
23
+ private clearAllCookies;
21
24
  getBaseUrl(): string;
22
25
  /**
23
26
  * this method returns all requests, an array of SASjsRequest type
@@ -7,6 +7,7 @@ import { RequestClient } from './RequestClient';
7
7
  */
8
8
  export declare class Sas9RequestClient extends RequestClient {
9
9
  constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions);
10
+ resetInMemoryAuthState(): void;
10
11
  login(username: string, password: string, jobsPath: string): Promise<void>;
11
12
  get<T>(url: string, accessToken: string | undefined, contentType?: string, overrideHeaders?: {
12
13
  [key: string]: string | number;
package/package.json CHANGED
@@ -23,10 +23,24 @@
23
23
  },
24
24
  "release": {
25
25
  "plugins": [
26
- "@semantic-release/npm",
27
- {
28
- "pkgRoot": "/build"
29
- }
26
+ "@semantic-release/commit-analyzer",
27
+ "@semantic-release/release-notes-generator",
28
+ [
29
+ "@semantic-release/exec",
30
+ {
31
+ "prepareCmd": "npm version ${nextRelease.version} --no-git-tag-version"
32
+ }
33
+ ],
34
+ [
35
+ "@semantic-release/git",
36
+ {
37
+ "assets": [
38
+ "package.json",
39
+ "package-lock.json"
40
+ ]
41
+ }
42
+ ],
43
+ "@semantic-release/github"
30
44
  ]
31
45
  },
32
46
  "keywords": [
@@ -34,14 +48,16 @@
34
48
  "viya",
35
49
  "sasjs"
36
50
  ],
37
- "author": "Allan Bowe <support@macropeople.com>",
51
+ "author": "support@4gl.io",
38
52
  "repository": {
39
53
  "type": "git",
40
- "url": "https://github.com/sasjs/adapter"
54
+ "url": "git+https://github.com/sasjs/adapter.git"
41
55
  },
42
56
  "license": "ISC",
43
57
  "devDependencies": {
44
58
  "@cypress/webpack-preprocessor": "5.9.1",
59
+ "@semantic-release/exec": "6.0.3",
60
+ "@semantic-release/git": "10.0.1",
45
61
  "@types/cors": "^2.8.17",
46
62
  "@types/express": "4.17.13",
47
63
  "@types/jest": "29.5.14",
@@ -53,17 +69,17 @@
53
69
  "cp": "0.2.0",
54
70
  "cypress": "^15.7.1",
55
71
  "dotenv": "16.0.0",
56
- "express": "4.17.3",
72
+ "express": "4.22.2",
57
73
  "jest": "29.7.0",
58
74
  "jest-environment-jsdom": "^29.7.0",
59
75
  "jest-extended": "4.0.2",
60
- "node-polyfill-webpack-plugin": "1.1.4",
76
+ "node-polyfill-webpack-plugin": "4.1.0",
61
77
  "path": "0.12.7",
62
78
  "pem": "1.14.5",
63
79
  "prettier": "3.8.2",
64
80
  "process": "0.11.10",
65
- "semantic-release": "19.0.3",
66
- "terser-webpack-plugin": "5.3.6",
81
+ "semantic-release": "25.0.8",
82
+ "terser-webpack-plugin": "5.6.1",
67
83
  "ts-jest": "29.2.6",
68
84
  "ts-loader": "9.4.0",
69
85
  "tslint": "6.1.3",
@@ -71,17 +87,17 @@
71
87
  "typedoc": "0.23.24",
72
88
  "typedoc-plugin-rename-defaults": "0.6.4",
73
89
  "typescript": "4.9.5",
74
- "webpack": "5.76.2",
90
+ "webpack": "5.108.4",
75
91
  "webpack-cli": "4.9.2"
76
92
  },
77
93
  "main": "index.js",
78
94
  "dependencies": {
79
95
  "@sasjs/utils": "^3.5.6",
80
- "axios": "1.16.0",
96
+ "axios": "1.18.1",
81
97
  "axios-cookiejar-support": "5.0.5",
82
- "form-data": "4.0.4",
98
+ "form-data": "4.0.6",
83
99
  "https": "1.0.0",
84
100
  "tough-cookie": "4.1.3"
85
101
  },
86
- "version": "4.17.0"
102
+ "version": "4.17.2"
87
103
  }
@@ -12,12 +12,15 @@ export declare class RequestClient implements HttpClient {
12
12
  protected csrfToken: CsrfToken;
13
13
  protected fileUploadCsrfToken: CsrfToken | undefined;
14
14
  protected httpClient: AxiosInstance;
15
+ private isRecoveringFromNetworkError;
15
16
  constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions, requestsLimit?: number, verboseMode?: VerboseMode);
16
17
  setConfig(baseUrl: string, httpsAgentOptions?: https.AgentOptions): void;
17
18
  saveLocalStorageToken(accessToken: string, refreshToken: string): void;
18
19
  getCsrfToken(type?: 'general' | 'file'): CsrfToken | undefined;
19
20
  clearCsrfTokens(): void;
20
21
  clearLocalStorageTokens(): void;
22
+ resetInMemoryAuthState(): void;
23
+ private clearAllCookies;
21
24
  getBaseUrl(): string;
22
25
  /**
23
26
  * this method returns all requests, an array of SASjsRequest type
@@ -7,6 +7,7 @@ import { RequestClient } from './RequestClient';
7
7
  */
8
8
  export declare class Sas9RequestClient extends RequestClient {
9
9
  constructor(baseUrl: string, httpsAgentOptions?: https.AgentOptions);
10
+ resetInMemoryAuthState(): void;
10
11
  login(username: string, password: string, jobsPath: string): Promise<void>;
11
12
  get<T>(url: string, accessToken: string | undefined, contentType?: string, overrideHeaders?: {
12
13
  [key: string]: string | number;
@@ -375,7 +375,7 @@ export class AuthManager {
375
375
  *
376
376
  */
377
377
  public async logOut() {
378
- this.requestClient.clearCsrfTokens()
378
+ this.requestClient.resetInMemoryAuthState()
379
379
 
380
380
  return this.requestClient.get(this.logoutUrl, undefined).then(() => true)
381
381
  }
@@ -28,6 +28,9 @@ import {
28
28
  import { InvalidSASjsCsrfError } from '../types/errors/InvalidSASjsCsrfError'
29
29
  import { inspect } from 'util'
30
30
 
31
+ const getLogger = () =>
32
+ (typeof process !== 'undefined' && process.logger) || console
33
+
31
34
  export class RequestClient implements HttpClient {
32
35
  private requests: SASjsRequest[] = []
33
36
  private requestsLimit: number = 10
@@ -37,6 +40,7 @@ export class RequestClient implements HttpClient {
37
40
  protected csrfToken: CsrfToken = { headerName: '', value: '' }
38
41
  protected fileUploadCsrfToken: CsrfToken | undefined
39
42
  protected httpClient!: AxiosInstance
43
+ private isRecoveringFromNetworkError = false
40
44
 
41
45
  constructor(
42
46
  protected baseUrl: string,
@@ -77,6 +81,25 @@ export class RequestClient implements HttpClient {
77
81
  localStorage.setItem('refreshToken', '')
78
82
  }
79
83
 
84
+ public resetInMemoryAuthState() {
85
+ this.clearCsrfTokens()
86
+ if (typeof localStorage !== 'undefined') {
87
+ this.clearLocalStorageTokens()
88
+ }
89
+ if (typeof document !== 'undefined') {
90
+ this.clearAllCookies()
91
+ }
92
+ }
93
+
94
+ private clearAllCookies() {
95
+ const cookies = document.cookie.split(';')
96
+ for (const cookie of cookies) {
97
+ const name = cookie.split('=')[0].trim()
98
+ if (!name) continue
99
+ document.cookie = `${name}=; Max-Age=0; Path=/;`
100
+ }
101
+ }
102
+
80
103
  public getBaseUrl() {
81
104
  return this.httpClient.defaults.baseURL || ''
82
105
  }
@@ -354,6 +377,7 @@ export class RequestClient implements HttpClient {
354
377
  const csrfTokenKey = Object.keys(params).find((k) =>
355
378
  k?.toLowerCase().includes('csrf')
356
379
  )
380
+
357
381
  if (csrfTokenKey) {
358
382
  this.csrfToken.value = params[csrfTokenKey]
359
383
  this.csrfToken.headerName = this.csrfToken.headerName || 'x-csrf-token'
@@ -378,7 +402,7 @@ export class RequestClient implements HttpClient {
378
402
  })
379
403
  .then((res) => res.data)
380
404
  .catch((error) => {
381
- const logger = process.logger || console
405
+ const logger = getLogger()
382
406
  logger.error(error)
383
407
  })
384
408
  }
@@ -687,6 +711,45 @@ ${resHeaders}${parsedResBody ? `\n\n${parsedResBody}` : ''}
687
711
  throw new CertificateError(e.message)
688
712
  }
689
713
 
714
+ if (
715
+ e.isAxiosError &&
716
+ !response &&
717
+ e.code === 'ERR_NETWORK' &&
718
+ !this.isRecoveringFromNetworkError
719
+ ) {
720
+ // Opaque ERR_NETWORK usually means the server rejected stale credentials.
721
+ // Wipe in-memory auth state, re-establish session via GET /,
722
+ // then retry the original request.
723
+ this.resetInMemoryAuthState()
724
+ this.isRecoveringFromNetworkError = true
725
+ try {
726
+ // Re-establish session and CSRF cookie
727
+ const rootResponse = await this.httpClient
728
+ .get('/', { withXSRFToken: true })
729
+ .catch((err) => err.response)
730
+
731
+ if (rootResponse?.data) {
732
+ const cookie =
733
+ /<script>document.cookie = '(XSRF-TOKEN=.*; Max-Age=86400; SameSite=Strict; Path=\/;)'<\/script>/.exec(
734
+ rootResponse.data
735
+ )?.[1]
736
+
737
+ if (cookie && typeof document !== 'undefined') {
738
+ document.cookie = cookie
739
+ }
740
+
741
+ this.parseAndSetCsrfToken(rootResponse)
742
+ }
743
+
744
+ return await callback()
745
+ } catch {
746
+ // Session could not be recovered — surface LoginRequiredError
747
+ throw new LoginRequiredError()
748
+ } finally {
749
+ this.isRecoveringFromNetworkError = false
750
+ }
751
+ }
752
+
690
753
  if (e.message) throw e
691
754
  else throw prefixMessage(e, 'Error while handling error. ')
692
755
  }
@@ -23,6 +23,13 @@ export class Sas9RequestClient extends RequestClient {
23
23
  }
24
24
  }
25
25
 
26
+ public resetInMemoryAuthState() {
27
+ super.resetInMemoryAuthState()
28
+ if (this.httpClient.defaults.jar) {
29
+ ;(this.httpClient.defaults.jar as tough.CookieJar).removeAllCookiesSync()
30
+ }
31
+ }
32
+
26
33
  public async login(username: string, password: string, jobsPath: string) {
27
34
  const codeInjectorPath = `/User Folders/${username}/My Folder/sasjs/runner`
28
35
  if (this.httpClient.defaults.jar) {