@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.
- package/index-dev.js +6891 -8408
- package/index.js +6891 -8408
- package/minified_sas9.js +1723 -3936
- package/node/index.js +5205 -4449
- package/node/request/RequestClient.d.ts +3 -0
- package/node/request/Sas9RequestClient.d.ts +1 -0
- package/package.json +30 -14
- package/request/RequestClient.d.ts +3 -0
- package/request/Sas9RequestClient.d.ts +1 -0
- package/src/auth/AuthManager.ts +1 -1
- package/src/request/RequestClient.ts +64 -1
- package/src/request/Sas9RequestClient.ts +7 -0
|
@@ -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/
|
|
27
|
-
|
|
28
|
-
|
|
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": "
|
|
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.
|
|
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": "
|
|
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": "
|
|
66
|
-
"terser-webpack-plugin": "5.
|
|
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.
|
|
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.
|
|
96
|
+
"axios": "1.18.1",
|
|
81
97
|
"axios-cookiejar-support": "5.0.5",
|
|
82
|
-
"form-data": "4.0.
|
|
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.
|
|
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;
|
package/src/auth/AuthManager.ts
CHANGED
|
@@ -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 =
|
|
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) {
|