mirror of
https://github.com/sasjs/server.git
synced 2026-07-24 05:32:15 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c3a1086b6 | ||
|
|
05768890a2 | ||
|
|
386185d1a0 | ||
|
|
681f0123ad | ||
|
|
daa5274fb0 | ||
|
|
50fa4320cd | ||
|
|
40b9fa8735 | ||
|
|
fd31fe94ea | ||
|
|
dec5191491 | ||
|
|
4858245372 | ||
|
|
4f75fd290f | ||
|
|
66232aefd2 | ||
|
|
bf35791655 | ||
|
|
2dc11630e4 | ||
|
|
1473925896 | ||
|
|
b472f1bd61 |
@@ -1,3 +1,18 @@
|
|||||||
|
## [0.39.7](https://github.com/sasjs/server/compare/v0.39.6...v0.39.7) (2026-07-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **jsonwebtoken:** bumped version to avoid vulnerability ([1473925](https://github.com/sasjs/server/commit/1473925896db0e4472c9ef5ba64955527a1be2de))
|
||||||
|
|
||||||
|
## [0.39.6](https://github.com/sasjs/server/compare/v0.39.5...v0.39.6) (2026-07-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **api:** isolate drive.spec.ts's files folder from the real drive ([dec5191](https://github.com/sasjs/server/commit/dec51914911c10b66b43ebe95f77b0af63e2d03e))
|
||||||
|
* **api:** stop overwriting a failed JS/PY/R session back to completed ([4858245](https://github.com/sasjs/server/commit/4858245372fa19929b6e59c28148f5d004239c9d))
|
||||||
|
|
||||||
## [0.39.5](https://github.com/sasjs/server/compare/v0.39.4...v0.39.5) (2026-07-14)
|
## [0.39.5](https://github.com/sasjs/server/compare/v0.39.4...v0.39.5) (2026-07-14)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Generated
+36
-6
@@ -49,7 +49,8 @@
|
|||||||
"@types/swagger-ui-express": "^4.1.3",
|
"@types/swagger-ui-express": "^4.1.3",
|
||||||
"@types/unzipper": "^0.10.5",
|
"@types/unzipper": "^0.10.5",
|
||||||
"adm-zip": "^0.5.9",
|
"adm-zip": "^0.5.9",
|
||||||
"axios": "^1.12.2",
|
"axios": "1.12.2",
|
||||||
|
"cpr": "^3.0.1",
|
||||||
"csrf": "^3.1.0",
|
"csrf": "^3.1.0",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"http-headers-validation": "^0.0.1",
|
"http-headers-validation": "^0.0.1",
|
||||||
@@ -58,7 +59,7 @@
|
|||||||
"nodejs-file-downloader": "4.10.2",
|
"nodejs-file-downloader": "4.10.2",
|
||||||
"nodemon": "^3.0.0",
|
"nodemon": "^3.0.0",
|
||||||
"pkg": "5.6.0",
|
"pkg": "5.6.0",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"supertest": "^6.1.3",
|
"supertest": "^6.1.3",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
@@ -3588,11 +3589,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.13.2",
|
"version": "1.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
|
||||||
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
|
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
"form-data": "^4.0.4",
|
"form-data": "^4.0.4",
|
||||||
@@ -4490,6 +4490,36 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cpr": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cpr/-/cpr-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-Xch4PXQ/KC8lJ+KfJ9JI6eG/nmppLrPPWg5Q+vh65Qr9EjuJEubxh/H/Le1TmCZ7+Xv7iJuNRqapyOFZB+wsxA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.1.5",
|
||||||
|
"minimist": "^1.2.0",
|
||||||
|
"mkdirp": "~0.5.1",
|
||||||
|
"rimraf": "^2.5.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"cpr": "bin/cpr"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cpr/node_modules/rimraf": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||||
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/create-jest": {
|
"node_modules/create-jest": {
|
||||||
"version": "29.7.0",
|
"version": "29.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
|
||||||
|
|||||||
+7
-6
@@ -16,10 +16,10 @@
|
|||||||
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
|
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
|
||||||
"exe": "npm run build && pkg .",
|
"exe": "npm run build && pkg .",
|
||||||
"copy:files": "npm run public:copy && npm run sasjsbuild:copy && npm run sas:copy && npm run web:copy",
|
"copy:files": "npm run public:copy && npm run sasjsbuild:copy && npm run sas:copy && npm run web:copy",
|
||||||
"public:copy": "cp -r ./public/ ./build/public/",
|
"public:copy": "cpr ./public/ ./build/public/",
|
||||||
"sasjsbuild:copy": "cp -r ./sasjsbuild/ ./build/sasjsbuild/",
|
"sasjsbuild:copy": "cpr ./sasjsbuild/ ./build/sasjsbuild/",
|
||||||
"sas:copy": "cp -r ./sas/ ./build/sas/",
|
"sas:copy": "cpr ./sas/ ./build/sas/",
|
||||||
"web:copy": "rimraf web && mkdir web && cp -r ../web/build/ ./web/build/",
|
"web:copy": "rimraf web && mkdir web && cpr ../web/build/ ./web/build/",
|
||||||
"compileSysInit": "ts-node ./scripts/compileSysInit.ts",
|
"compileSysInit": "ts-node ./scripts/compileSysInit.ts",
|
||||||
"copySASjsCore": "ts-node ./scripts/copySASjsCore.ts",
|
"copySASjsCore": "ts-node ./scripts/copySASjsCore.ts",
|
||||||
"downloadMacros": "ts-node ./scripts/downloadMacros.ts"
|
"downloadMacros": "ts-node ./scripts/downloadMacros.ts"
|
||||||
@@ -84,7 +84,8 @@
|
|||||||
"@types/swagger-ui-express": "^4.1.3",
|
"@types/swagger-ui-express": "^4.1.3",
|
||||||
"@types/unzipper": "^0.10.5",
|
"@types/unzipper": "^0.10.5",
|
||||||
"adm-zip": "^0.5.9",
|
"adm-zip": "^0.5.9",
|
||||||
"axios": "^1.12.2",
|
"axios": "1.12.2",
|
||||||
|
"cpr": "^3.0.1",
|
||||||
"csrf": "^3.1.0",
|
"csrf": "^3.1.0",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"http-headers-validation": "^0.0.1",
|
"http-headers-validation": "^0.0.1",
|
||||||
@@ -93,7 +94,7 @@
|
|||||||
"nodejs-file-downloader": "4.10.2",
|
"nodejs-file-downloader": "4.10.2",
|
||||||
"nodemon": "^3.0.0",
|
"nodemon": "^3.0.0",
|
||||||
"pkg": "5.6.0",
|
"pkg": "5.6.0",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"supertest": "^6.1.3",
|
"supertest": "^6.1.3",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
|
|||||||
@@ -144,7 +144,16 @@ export class ExecutionController {
|
|||||||
: ''
|
: ''
|
||||||
|
|
||||||
// it should be deleted by scheduleSessionDestroy
|
// it should be deleted by scheduleSessionDestroy
|
||||||
|
//
|
||||||
|
// Guarded: for JS/PY/R, processProgram sets state to `failed` itself
|
||||||
|
// (without throwing) when the interpreter process exits non-zero - if
|
||||||
|
// we unconditionally set `completed` here we'd silently overwrite that,
|
||||||
|
// and anything downstream inspecting session.state (e.g.
|
||||||
|
// scheduleSessionDestroy's expiresAfterMins branch in Session.ts) would
|
||||||
|
// see a crashed session mis-reported as successful.
|
||||||
|
if ((session.state as SessionState) !== SessionState.failed) {
|
||||||
session.state = SessionState.completed
|
session.state = SessionState.completed
|
||||||
|
}
|
||||||
|
|
||||||
const resultParts = []
|
const resultParts = []
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const preProgramVariables: PreProgramVars = {
|
|||||||
httpHeaders: []
|
httpHeaders: []
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('ExecutionController.executeProgram (SAS failure path)', () => {
|
describe('ExecutionController.executeProgram', () => {
|
||||||
let session: Session
|
let session: Session
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -33,6 +33,57 @@ describe('ExecutionController.executeProgram (SAS failure path)', () => {
|
|||||||
await deleteFolder(session.path)
|
await deleteFolder(session.path)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Regression coverage: for JS/PY/R, processProgram sets session.state to
|
||||||
|
// `failed` itself (without throwing) on a non-zero interpreter exit.
|
||||||
|
// ExecutionController.executeProgram used to unconditionally overwrite
|
||||||
|
// that back to `completed` right after processProgram returned, silently
|
||||||
|
// losing the failure for anything downstream that inspects session.state.
|
||||||
|
describe('JS/PY/R failure path', () => {
|
||||||
|
it('does not overwrite a failed session state back to completed', async () => {
|
||||||
|
// mirrors processProgram's real JS/PY/R branch on a non-zero exit:
|
||||||
|
// it sets state/failureReason itself and resolves - it does not throw
|
||||||
|
jest
|
||||||
|
.spyOn(ProcessProgramModule, 'processProgram')
|
||||||
|
.mockImplementation(async () => {
|
||||||
|
session.state = SessionState.failed
|
||||||
|
session.failureReason = 'Error: process exited with code 1'
|
||||||
|
})
|
||||||
|
|
||||||
|
const controller = new ExecutionController()
|
||||||
|
|
||||||
|
await controller.executeProgram({
|
||||||
|
program: 'throw new Error("boom")',
|
||||||
|
preProgramVariables,
|
||||||
|
vars: {},
|
||||||
|
session,
|
||||||
|
runTime: RunTimeType.JS
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(session.state).toBe(SessionState.failed)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('still marks a genuinely successful session as completed', async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(ProcessProgramModule, 'processProgram')
|
||||||
|
.mockImplementation(async () => {
|
||||||
|
session.state = SessionState.completed
|
||||||
|
})
|
||||||
|
|
||||||
|
const controller = new ExecutionController()
|
||||||
|
|
||||||
|
await controller.executeProgram({
|
||||||
|
program: 'console.log("hello")',
|
||||||
|
preProgramVariables,
|
||||||
|
vars: {},
|
||||||
|
session,
|
||||||
|
runTime: RunTimeType.JS
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(session.state).toBe(SessionState.completed)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('SAS failure path', () => {
|
||||||
it('throws a SessionExecutionError carrying the complete log when the session fails', async () => {
|
it('throws a SessionExecutionError carrying the complete log when the session fails', async () => {
|
||||||
const logPath = path.join(session.path, 'log.log')
|
const logPath = path.join(session.path, 'log.log')
|
||||||
const logContent =
|
const logContent =
|
||||||
@@ -63,3 +114,4 @@ describe('ExecutionController.executeProgram (SAS failure path)', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|||||||
@@ -28,6 +28,15 @@ jest
|
|||||||
.spyOn(fileUtilModules, 'getUploadsFolder')
|
.spyOn(fileUtilModules, 'getUploadsFolder')
|
||||||
.mockImplementation(() => path.join(tmpFolder, 'uploads'))
|
.mockImplementation(() => path.join(tmpFolder, 'uploads'))
|
||||||
|
|
||||||
|
// getFilesFolder() resolves via getSasjsDriveFolder()/process.driveLoc, a
|
||||||
|
// separate root from getSasjsRootFolder()/process.sasjsRoot above - without
|
||||||
|
// this, every test in this file that creates content under the drive (e.g.
|
||||||
|
// 'level1', 'my/path/...') writes into the real, shared sasjs_root/drive
|
||||||
|
// instead of this run's isolated tmpFolder, leaking state into later runs.
|
||||||
|
jest
|
||||||
|
.spyOn(fileUtilModules, 'getFilesFolder')
|
||||||
|
.mockImplementation(() => path.join(tmpFolder, 'drive', 'files'))
|
||||||
|
|
||||||
import appPromise from '../../../app'
|
import appPromise from '../../../app'
|
||||||
import {
|
import {
|
||||||
UserController,
|
UserController,
|
||||||
|
|||||||
Generated
+13
-11
@@ -20,7 +20,7 @@
|
|||||||
"@types/jest": "^26.0.24",
|
"@types/jest": "^26.0.24",
|
||||||
"@types/node": "^12.20.28",
|
"@types/node": "^12.20.28",
|
||||||
"@types/react": "^17.0.27",
|
"@types/react": "^17.0.27",
|
||||||
"axios": "^1.12.2",
|
"axios": "1.12.2",
|
||||||
"monaco-editor": "^0.33.0",
|
"monaco-editor": "^0.33.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-copy-to-clipboard": "^5.1.0",
|
"react-copy-to-clipboard": "^5.1.0",
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
"html-webpack-plugin": "5.5.0",
|
"html-webpack-plugin": "5.5.0",
|
||||||
"monaco-editor-webpack-plugin": "^7.0.1",
|
"monaco-editor-webpack-plugin": "^7.0.1",
|
||||||
"path": "0.12.7",
|
"path": "0.12.7",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^3.0.3",
|
||||||
"sass": "^1.44.0",
|
"sass": "^1.44.0",
|
||||||
"sass-loader": "^12.3.0",
|
"sass-loader": "^12.3.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
@@ -4334,7 +4334,6 @@
|
|||||||
"version": "1.12.2",
|
"version": "1.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
|
||||||
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
|
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
"form-data": "^4.0.4",
|
"form-data": "^4.0.4",
|
||||||
@@ -9713,15 +9712,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "2.4.1",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
|
||||||
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
|
"integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin-prettier.js"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.13.0"
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pretty-error": {
|
"node_modules/pretty-error": {
|
||||||
@@ -19127,9 +19129,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"version": "2.4.1",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
|
||||||
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
|
"integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pretty-error": {
|
"pretty-error": {
|
||||||
|
|||||||
+5
-3
@@ -4,7 +4,9 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack-dev-server --config webpack.dev.ts --hot",
|
"start": "webpack-dev-server --config webpack.dev.ts --hot",
|
||||||
"build": "webpack --config webpack.prod.ts"
|
"build": "webpack --config webpack.prod.ts",
|
||||||
|
"lint": "npx prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
|
||||||
|
"lint:fix": "npx prettier --write \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.4.1",
|
"@emotion/react": "^11.4.1",
|
||||||
@@ -19,7 +21,7 @@
|
|||||||
"@types/jest": "^26.0.24",
|
"@types/jest": "^26.0.24",
|
||||||
"@types/node": "^12.20.28",
|
"@types/node": "^12.20.28",
|
||||||
"@types/react": "^17.0.27",
|
"@types/react": "^17.0.27",
|
||||||
"axios": "^1.12.2",
|
"axios": "1.12.2",
|
||||||
"monaco-editor": "^0.33.0",
|
"monaco-editor": "^0.33.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-copy-to-clipboard": "^5.1.0",
|
"react-copy-to-clipboard": "^5.1.0",
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
"html-webpack-plugin": "5.5.0",
|
"html-webpack-plugin": "5.5.0",
|
||||||
"monaco-editor-webpack-plugin": "^7.0.1",
|
"monaco-editor-webpack-plugin": "^7.0.1",
|
||||||
"path": "0.12.7",
|
"path": "0.12.7",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^3.0.3",
|
||||||
"sass": "^1.44.0",
|
"sass": "^1.44.0",
|
||||||
"sass-loader": "^12.3.0",
|
"sass-loader": "^12.3.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user