diff --git a/.eslintrc.js b/.eslintrc.js index 8a60988..afc6b85 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -40,6 +40,12 @@ module.exports = { 'prettier/prettier': ['error', { endOfLine: 'auto', semi: false }], 'import/prefer-default-export': 'off', 'no-restricted-syntax': ['off', 'ForOfStatement'], + 'lines-between-class-members': [ + 'error', + 'always', + { exceptAfterSingleLine: true }, + ], + 'no-use-before-define': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', @@ -55,4 +61,12 @@ module.exports = { }, ], }, + overrides: [ + { + files: ['**/tests/**/*.js', '**/tests/**/*.ts'], // Adjust the pattern to match your test files + rules: { + 'import/extensions': 'off', // Turn off the rule for test files + }, + }, + ], } diff --git a/jest.config.js b/jest.config.js index 0492673..9e953ad 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,6 +2,8 @@ // const { jsWithTs: tsjPreset } = require('ts-jest/presets') module.exports = { - preset: 'ts-jest/presets/js-with-ts', - testEnvironment: 'node', + preset: 'ts-jest/presets/default-esm', + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', // Redirect .js imports to .ts files + }, } diff --git a/package.json b/package.json index 81e089b..0631702 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "cli": "yarn workspace client start", "lint": "eslint .", "format": "prettier --ignore-path .prettierignore --write app server", - "test": "yarn jest", + "test": "yarn node --experimental-vm-modules --no-warnings $(yarn bin jest)", "test:e2e": "yarn workspace tests test", "test:open": "yarn workspace tests test:open", "analytics": "yarn workspace analytics dev", @@ -29,7 +29,7 @@ "build:quests": "yarn workspace server build:quests" }, "devDependencies": { - "@types/jest": "^29.5.11", + "@types/jest": "^29.5.12", "@types/node": "^20.10.4", "@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/parser": "^6.14.0", diff --git a/packages/constants/index.js b/packages/constants/index.ts similarity index 100% rename from packages/constants/index.js rename to packages/constants/index.ts diff --git a/packages/file-graph/src/file.ts b/packages/file-graph/src/file.ts index 1656e41..5fb5126 100644 --- a/packages/file-graph/src/file.ts +++ b/packages/file-graph/src/file.ts @@ -1,7 +1,6 @@ -/* eslint-disable lines-between-class-members */ import { basename } from './utils.js' -interface FileObject { +export interface FileObject { path: string type: string discovered: boolean diff --git a/packages/file-graph/src/fileGraph.js b/packages/file-graph/src/fileGraph.ts similarity index 82% rename from packages/file-graph/src/fileGraph.js rename to packages/file-graph/src/fileGraph.ts index a2a924e..5d63b18 100644 --- a/packages/file-graph/src/fileGraph.js +++ b/packages/file-graph/src/fileGraph.ts @@ -1,7 +1,14 @@ +import { FileObject } from './file.js' import FileNode from './fileNode.js' +export interface Event { + add?: FileObject[] + remove?: FileObject[] + discover?: FileObject[] +} + export default class FileGraph extends FileNode { - add(files) { + add(files: FileObject[]) { // Logic to add the file node to the graph // You can implement the logic to find the appropriate location in the file graph // If the file node already exists, throw an error @@ -11,7 +18,7 @@ export default class FileGraph extends FileNode { }) } - remove(files) { + remove(files: FileObject[]) { // Logic to remove a file node from the graph // If the file node is a folder, also remove all children // If the file node doesn't exist, throw an error @@ -21,7 +28,7 @@ export default class FileGraph extends FileNode { }) } - discover(eventFiles) { + discover(eventFiles: FileObject[]) { // Logic to handle the discover event // Overwrite the file graph with the provided files // Remove file nodes that don't exist anymore and add new ones @@ -35,7 +42,7 @@ export default class FileGraph extends FileNode { this.merge(newNode) } - handleEvent(event) { + handleEvent(event: Event) { // eslint-disable-next-line default-case switch (true) { case !!event.add: diff --git a/packages/file-graph/src/fileNode.js b/packages/file-graph/src/fileNode.ts similarity index 85% rename from packages/file-graph/src/fileNode.js rename to packages/file-graph/src/fileNode.ts index cf8f089..7c64fe1 100644 --- a/packages/file-graph/src/fileNode.js +++ b/packages/file-graph/src/fileNode.ts @@ -1,19 +1,25 @@ -import File from './file.js' +import File, { FileObject } from './file.js' import { DuplicateItemError, ParentNotExistsError, FileNotExistsError, } from './errors.js' -import { basename, dirname } from './utils.js' +import { dirname } from './utils.js' export default class FileNode extends File { - constructor(file) { + children: FileNode[] + + constructor(file: FileObject | FileNode) { super(file) this.children = [] - if (file.children) this.children = file.children.map((f) => new FileNode(f)) + if ((file).children) + this.children = (file).children.map((f) => new FileNode(f)) } - addChild(fileInput, { makeParents } = {}) { + addChild( + fileInput: FileObject, + { makeParents }: { makeParents?: boolean } = {} + ) { const file = new FileNode(fileInput) if (file.path === this.path) { throw new DuplicateItemError( @@ -39,7 +45,6 @@ export default class FileNode extends File { } else if (makeParents) { const parent = new FileNode({ path: parentPath, - name: basename(parentPath), type: 'folder', discovered: false, children: [file], @@ -53,7 +58,7 @@ export default class FileNode extends File { } } - removeChild(fileToRemove) { + removeChild(fileToRemove: FileObject) { const file = new FileNode(fileToRemove) const parentPath = dirname(file.path) @@ -73,7 +78,7 @@ export default class FileNode extends File { } } - merge(fileNode) { + merge(fileNode: FileNode) { // Merge the children of the provided file node into this node // If a child already exists, merge the children of that child // If a child does not exist, add it to this node @@ -88,7 +93,7 @@ export default class FileNode extends File { if (existingChild) { existingChild.merge(child) } else { - this.children.push(child) + this.children.push(new FileNode(child)) } }) @@ -112,14 +117,12 @@ export default class FileNode extends File { } } else { throw new Error( - 'Cannot merge file nodes with different paths', - this.path, - fileNode.path + `Cannot merge file nodes with different paths: ${this.path}, ${fileNode.path}` ) } } - toString(level = 0) { + toString(level = 0): string { let { name } = this const indent = ' '.repeat(level) name = `${indent}${name}` diff --git a/packages/file-graph/src/index.js b/packages/file-graph/src/index.js deleted file mode 100644 index 4b1fcda..0000000 --- a/packages/file-graph/src/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { default as File } from './file.js' -export { default as FileNode } from './fileNode.js' -export { default as FileGraph } from './fileGraph.js' diff --git a/packages/file-graph/src/index.ts b/packages/file-graph/src/index.ts new file mode 100644 index 0000000..f9dc793 --- /dev/null +++ b/packages/file-graph/src/index.ts @@ -0,0 +1,3 @@ +export { default as File, FileObject } from './file.js' +export { default as FileNode } from './fileNode.js' +export { default as FileGraph, Event } from './fileGraph.js' diff --git a/packages/file-graph/src/utils.js b/packages/file-graph/src/utils.ts similarity index 57% rename from packages/file-graph/src/utils.js rename to packages/file-graph/src/utils.ts index 48fffd9..5a637e4 100644 --- a/packages/file-graph/src/utils.js +++ b/packages/file-graph/src/utils.ts @@ -1,4 +1,4 @@ -export function dirname(path) { +export function dirname(path: string) { const parts = path.split('/') if (parts.length <= 2) { return '/' @@ -7,7 +7,7 @@ export function dirname(path) { return parts.join('/') } -export function basename(path) { +export function basename(path: string): string { const parts = path.split('/') - return parts.pop() + return parts.pop() ?? '' } diff --git a/packages/models/src/command.js b/packages/models/src/command.ts similarity index 100% rename from packages/models/src/command.js rename to packages/models/src/command.ts diff --git a/packages/models/src/commandResponder.js b/packages/models/src/commandResponder.ts similarity index 100% rename from packages/models/src/commandResponder.js rename to packages/models/src/commandResponder.ts diff --git a/packages/models/src/condition.js b/packages/models/src/condition.ts similarity index 100% rename from packages/models/src/condition.js rename to packages/models/src/condition.ts diff --git a/packages/models/src/exception.js b/packages/models/src/exception.ts similarity index 100% rename from packages/models/src/exception.js rename to packages/models/src/exception.ts diff --git a/packages/models/src/globalException.js b/packages/models/src/globalException.ts similarity index 100% rename from packages/models/src/globalException.js rename to packages/models/src/globalException.ts diff --git a/packages/models/src/hint.js b/packages/models/src/hint.ts similarity index 100% rename from packages/models/src/hint.js rename to packages/models/src/hint.ts diff --git a/packages/models/src/index.js b/packages/models/src/index.ts similarity index 67% rename from packages/models/src/index.js rename to packages/models/src/index.ts index c3b835c..d69ae25 100644 --- a/packages/models/src/index.js +++ b/packages/models/src/index.ts @@ -6,10 +6,7 @@ export { default as Session } from './session.js' export { default as Command } from './command.js' export { default as UserProfile } from './userProfile.js' -export default async function connectDB(uri) { - const db = await mongoose.connect(uri, { - useNewUrlParser: true, - useUnifiedTopology: true, - }) +export default async function connectDB(uri: string) { + const db = await mongoose.connect(uri) return db.connection.getClient() } diff --git a/packages/models/src/quest.js b/packages/models/src/quest.ts similarity index 100% rename from packages/models/src/quest.js rename to packages/models/src/quest.ts diff --git a/packages/models/src/requirement.js b/packages/models/src/requirement.ts similarity index 100% rename from packages/models/src/requirement.js rename to packages/models/src/requirement.ts diff --git a/packages/models/src/response.js b/packages/models/src/response.ts similarity index 100% rename from packages/models/src/response.js rename to packages/models/src/response.ts diff --git a/packages/models/src/session.js b/packages/models/src/session.ts similarity index 100% rename from packages/models/src/session.js rename to packages/models/src/session.ts diff --git a/packages/models/src/stage.js b/packages/models/src/stage.ts similarity index 100% rename from packages/models/src/stage.js rename to packages/models/src/stage.ts diff --git a/packages/models/src/stageException.js b/packages/models/src/stageException.ts similarity index 100% rename from packages/models/src/stageException.js rename to packages/models/src/stageException.ts diff --git a/packages/models/src/user.js b/packages/models/src/user.ts similarity index 100% rename from packages/models/src/user.js rename to packages/models/src/user.ts diff --git a/packages/models/src/userProfile.js b/packages/models/src/userProfile.ts similarity index 100% rename from packages/models/src/userProfile.js rename to packages/models/src/userProfile.ts diff --git a/packages/utils/src/auth.js b/packages/utils/src/auth.ts similarity index 87% rename from packages/utils/src/auth.js rename to packages/utils/src/auth.ts index 59caef6..b3170e1 100644 --- a/packages/utils/src/auth.js +++ b/packages/utils/src/auth.ts @@ -1,4 +1,4 @@ -export function isValidUsername(username) { +export function isValidUsername(username: string): boolean { // check if the username is between 1 and 32 characters if (username.length > 32) { return false @@ -29,7 +29,7 @@ export function isValidUsername(username) { return true } -export function isValidEmail(email) { +export function isValidEmail(email: string): boolean { // Define a regular expression pattern for a valid email address. const emailPattern = /^[\w-.+]+@([\w][\w-]*\.)+[\w-]{2,}$/ diff --git a/packages/utils/src/commands.js b/packages/utils/src/commands.ts similarity index 50% rename from packages/utils/src/commands.js rename to packages/utils/src/commands.ts index c64a33a..b40ea2e 100644 --- a/packages/utils/src/commands.js +++ b/packages/utils/src/commands.ts @@ -3,14 +3,21 @@ const fileTypes = { folder: '-d', link: '-L', socket: '-S', + unknown: '-e', } -function sanitizePath(path) { +export type FileType = keyof typeof fileTypes + +export type File = { + path: string + type: FileType +} + +function sanitizePath(path: string): string { return path.replace(/[^a-zA-Z0-9._/-]/g, '') } -// eslint-disable-next-line import/prefer-default-export -export function buildFileCheckCmd(file) { +export function buildFileCheckCmd(file: File): string[] { const { path, type } = file return ['test', fileTypes[type] || '-e', sanitizePath(path)] } diff --git a/packages/utils/src/dag.js b/packages/utils/src/dag.js deleted file mode 100644 index 6a3a911..0000000 --- a/packages/utils/src/dag.js +++ /dev/null @@ -1,73 +0,0 @@ -export default class DAG { - #nodes = new Map() - - #edges = new Set() - - #layers = [] - - constructor(nodes) { - this.#nodes = new Map(nodes.map((node) => [node._id, node])) - this.#edges = new Set() - this.#nodes.forEach((_, id) => { - this.#setLayer(id) - this.#addEdges(id) - }) - } - - getNode(id) { - return this.#nodes.get(id) - } - - getNodes() { - return Array.from(this.#nodes.values()) - } - - getEdges() { - return this.#edges - } - - getEdgesArray() { - return Array.from(this.#edges.values()) - } - - getLayers() { - return this.#layers - } - - getLayer(id) { - const node = this.getNode(id) - if (!node) { - throw new Error(`Node ${id} not found`) - } - return this.#layers[node.layer - 1] - } - - #setLayer(id) { - const node = this.getNode(id) - if (!node) { - throw new Error(`Node ${id} not found`) - } - if (node.layer !== undefined) { - return node.layer - } - const previousLayer = node.requirements.map((rId) => this.#setLayer(rId)) - node.layer = Math.max(...previousLayer, 0) + 1 - if (this.#layers.length < node.layer) { - this.#layers.push(1) - } else { - this.#layers[node.layer - 1] += 1 - } - node.index = this.#layers[node.layer - 1] - return node.layer - } - - #addEdges(id) { - const node = this.getNode(id) - if (!node) { - throw new Error(`Node ${id} not found`) - } - node.requirements.forEach((rId) => { - this.#edges.add([rId, id]) - }) - } -} diff --git a/packages/utils/src/dag.ts b/packages/utils/src/dag.ts new file mode 100644 index 0000000..b5ef439 --- /dev/null +++ b/packages/utils/src/dag.ts @@ -0,0 +1,78 @@ +interface Node { + _id: string + requirements: string[] +} + +export default class DAG { + private nodes = new Map() + private edges = new Set() + private layers: number[] = [] + + constructor(nodes: Node[]) { + this.nodes = new Map(nodes.map((node) => [node._id, node])) + this.edges = new Set() + this.nodes.forEach((_, id) => { + this.setLayer(id) + this.addEdges(id) + }) + } + + getNode(id: string) { + return this.nodes.get(id) + } + + getNodes() { + return Array.from(this.nodes.values()) + } + + getEdges() { + return this.edges + } + + getEdgesArray() { + return Array.from(this.edges.values()) + } + + getLayers() { + return this.layers + } + + getLayer(id: string) { + const node = this.getNode(id) + if (!node) { + throw new Error(`Node ${id} not found`) + } + return this.layers[node.layer - 1] + } + + private setLayer(id: string) { + const node = this.getNode(id) + if (!node) { + throw new Error(`Node ${id} not found`) + } + if (node.layer !== undefined) { + return node.layer + } + const previousLayer = node.requirements.map((rId: string) => + this.setLayer(rId) + ) + node.layer = Math.max(...previousLayer, 0) + 1 + if (this.layers.length < node.layer) { + this.layers.push(1) + } else { + this.layers[node.layer - 1] += 1 + } + node.index = this.layers[node.layer - 1] + return node.layer + } + + addEdges(id: string) { + const node = this.getNode(id) + if (!node) { + throw new Error(`Node ${id} not found`) + } + node.requirements.forEach((rId: string) => { + this.edges.add([rId, id]) + }) + } +} diff --git a/packages/utils/src/index.js b/packages/utils/src/index.ts similarity index 100% rename from packages/utils/src/index.js rename to packages/utils/src/index.ts diff --git a/packages/utils/tests/commands.test.ts b/packages/utils/tests/commands.test.ts index 9ac76ff..e1888ca 100644 --- a/packages/utils/tests/commands.test.ts +++ b/packages/utils/tests/commands.test.ts @@ -1,8 +1,11 @@ -import { buildFileCheckCmd } from '../src/commands' +import { buildFileCheckCmd, File } from '../src/commands' describe('buildFileCheckCmd function', () => { it('builds the file check command correctly', () => { - const testcases = [ + const testcases: { + input: File + output: string[] + }[] = [ { input: { path: '/etc/passwd', type: 'file' }, output: ['test', '-f', '/etc/passwd'], diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json index bf649ac..8f50229 100644 --- a/packages/utils/tsconfig.json +++ b/packages/utils/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "src", - "outDir": "./dist" + "outDir": "./dist", + "allowJs": false }, "include": ["src"], "exclude": ["node_modules", "dist"] diff --git a/server/src/models/sessionManager.js b/server/src/models/sessionManager.js index 03c79a4..80fb01e 100644 --- a/server/src/models/sessionManager.js +++ b/server/src/models/sessionManager.js @@ -14,9 +14,9 @@ async function deactivateSessions(user, quest) { return deleteContainer(session.containerId) .catch((err) => logger.error('Failed to delete container', err)) .finally(() => { - session.status = 'inactive' - session.containerId = null - return session.save() + return Session.findByIdAndUpdate(session._id, { + $set: { status: 'inactive', containerId: null }, + }).catch((err) => logger.error('Failed to update session', err)) }) }) ) diff --git a/tsconfig.json b/tsconfig.json index 3695271..3deb233 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,8 @@ "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, - "outDir": "dist" + "outDir": "dist", + "sourceMap": true, + "declaration": true } } diff --git a/yarn.lock b/yarn.lock index b3ddac4..03133b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1460,7 +1460,7 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:^29.5.11": +"@types/jest@npm:^29.5.12": version: 29.5.12 resolution: "@types/jest@npm:29.5.12" dependencies: @@ -6512,7 +6512,7 @@ __metadata: version: 0.0.0-use.local resolution: "linux-odyssey@workspace:." dependencies: - "@types/jest": "npm:^29.5.11" + "@types/jest": "npm:^29.5.12" "@types/node": "npm:^20.10.4" "@typescript-eslint/eslint-plugin": "npm:^6.14.0" "@typescript-eslint/parser": "npm:^6.14.0"