diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.nycrc b/.nycrc new file mode 100644 index 0000000..22d8df7 --- /dev/null +++ b/.nycrc @@ -0,0 +1,8 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "check-coverage": true, + "all": true, + "include": ["src/**/*.[tj]s?(x)"], + "reporter": ["html", "lcov", "text", "text-summary"], + "report-dir": "coverage" +} diff --git a/extension/src/activateAvmDebug.ts b/extension/src/activateAvmDebug.ts index fc206b4..af3a379 100644 --- a/extension/src/activateAvmDebug.ts +++ b/extension/src/activateAvmDebug.ts @@ -1,5 +1,3 @@ -'use strict'; - import * as vscode from 'vscode'; import { AvmDebugConfigProvider } from './configuration'; diff --git a/extension/src/configuration.ts b/extension/src/configuration.ts index 415b606..89319f1 100644 --- a/extension/src/configuration.ts +++ b/extension/src/configuration.ts @@ -1,5 +1,3 @@ -'use strict'; - import * as vscode from 'vscode'; import { WorkspaceFolder, diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 3ba50b3..9c42ec3 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -1,5 +1,3 @@ -'use strict'; - import * as vscode from 'vscode'; import { activateAvmDebug } from './activateAvmDebug'; import { ServerDebugAdapterFactory } from './serverDescriptorFactory'; diff --git a/extension/src/fileAccessor.ts b/extension/src/fileAccessor.ts index 26e0998..6d199f8 100644 --- a/extension/src/fileAccessor.ts +++ b/extension/src/fileAccessor.ts @@ -1,7 +1,5 @@ -'use strict'; - import * as vscode from 'vscode'; -import { FileAccessor } from '../../src'; +import { FileAccessor } from '../../src/common'; export const workspaceFileAccessor: FileAccessor = { isWindows: typeof process !== 'undefined' && process.platform === 'win32', diff --git a/extension/src/internalDescriptorFactory.ts b/extension/src/internalDescriptorFactory.ts index 68e8640..97971f8 100644 --- a/extension/src/internalDescriptorFactory.ts +++ b/extension/src/internalDescriptorFactory.ts @@ -1,8 +1,6 @@ -'use strict'; - import * as vscode from 'vscode'; import { ProviderResult } from 'vscode'; -import { AvmDebugSession } from '../../src'; +import { AvmDebugSession } from '../../src/common'; import { workspaceFileAccessor } from './fileAccessor'; export class InlineDebugAdapterFactory diff --git a/extension/src/serverDescriptorFactory.ts b/extension/src/serverDescriptorFactory.ts index 4db1fde..094bbc8 100644 --- a/extension/src/serverDescriptorFactory.ts +++ b/extension/src/serverDescriptorFactory.ts @@ -1,9 +1,7 @@ -'use strict'; - import * as Net from 'net'; import * as vscode from 'vscode'; import { ProviderResult } from 'vscode'; -import { AvmDebugSession } from '../../src'; +import { AvmDebugSession } from '../../src/common'; import { workspaceFileAccessor } from './fileAccessor'; export class ServerDebugAdapterFactory diff --git a/package.json b/package.json index ee68210..dbac4a9 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "avm-debug", + "name": "avm-debug-adapter", "version": "0.1.0", "description": "Algorand AVM transaction and smart contract debugger using the Debugger Adapter Protocol", "author": { @@ -16,25 +16,40 @@ "bugs": { "url": "https://github.com/algorand/avm-debugger/issues" }, + "exports": { + ".": { + "types": "./dist/common/index.d.ts", + "default": "./dist/common/index.js" + }, + "./node": { + "types": "./dist/node/index.d.ts", + "default": "./dist/node/index.js" + } + }, + "bin": { + "avm-debug": "./dist/cli.js" + }, + "files": [ + "dist" + ], "scripts": { - "compile": "tsc -p ./", + "compile": "rimraf out && rimraf dist && tsc -p ./ && cp -R out/src dist", "lint": "eslint src --ext ts", "typecheck": "tsc -p tsconfig.json --noEmit", "check-format": "prettier . --check", "format": "prettier . --write", - "esbuild-base": "esbuild ./extension/src/extension.ts --bundle --tsconfig=./tsconfig.json --external:vscode --format=cjs --platform=node --outfile=extension/dist/extension.js", - "watch": "npm run -S esbuild-base -- --sourcemap --sources-content=false --watch", - "esbuild-web": "esbuild ./extension/src/web-extension.ts --bundle --tsconfig=./tsconfig.json --external:vscode --format=cjs --platform=browser --outfile=extension/dist/web-extension.js", - "watch-web": "npm run -S esbuild-web -- --sourcemap --sources-content=false --watch", - "build": "npm run -S esbuild-base -- --sourcemap --sources-content=false && npm run -S esbuild-web -- --sourcemap --sources-content=false", + "extension:esbuild-base": "esbuild ./extension/src/extension.ts --bundle --tsconfig=./tsconfig.json --external:vscode --format=cjs --platform=node --outfile=extension/dist/extension.js", + "extension:watch": "npm run -S extension:esbuild-base -- --sourcemap --sources-content=false --watch", + "extension:esbuild-web": "esbuild ./extension/src/web-extension.ts --bundle --tsconfig=./tsconfig.json --external:vscode --format=cjs --platform=browser --outfile=extension/dist/web-extension.js", + "extension:watch-web": "npm run -S extension:esbuild-web -- --sourcemap --sources-content=false --watch", + "extension:build": "npm run -S extension:esbuild-base -- --sourcemap --sources-content=false && npm run -S extension:esbuild-web -- --sourcemap --sources-content=false", "package": "vsce package", "publish": "vsce publish", "publish-pre-release": "vsce publish --pre-release", - "vscode:prepublish": "rimraf dist && npm run -S esbuild-base -- --minify && npm run -S esbuild-web -- --minify", + "vscode:prepublish": "rimraf extension/dist && npm run -S esbuild-base -- --minify && npm run -S esbuild-web -- --minify", "test": "ts-mocha -p tsconfig.json tests/*test.ts --timeout 30s --diff false", "test:coverage": "nyc npm run test" }, - "main": "./dist/debugAdapter/index.js", "dependencies": { "@vscode/debugadapter": "^1.64.0", "algosdk": "github:jasonpaulos/js-algorand-sdk#teal-source-map-improvements", @@ -64,23 +79,5 @@ "ts-mocha": "^10.0.0", "typescript": "^4.6.3", "url": "^0.11.3" - }, - "nyc": { - "extends": "@istanbuljs/nyc-config-typescript", - "check-coverage": true, - "all": true, - "include": [ - "src/**/!(*.test.*).[tj]s?(x)" - ], - "exclude": [ - "src/_tests_/**/*.*" - ], - "reporter": [ - "html", - "lcov", - "text", - "text-summary" - ], - "report-dir": "coverage" } } diff --git a/src/basicServer.ts b/src/basicServer.ts deleted file mode 100644 index 076a2d4..0000000 --- a/src/basicServer.ts +++ /dev/null @@ -1,31 +0,0 @@ -import * as Net from 'net'; -import { AvmDebugSession } from './debugSession'; -import { FileAccessor } from './fileAccessor'; - -export class BasicServer { - private server: Net.Server; - - constructor(fileAccessor: FileAccessor) { - this.server = Net.createServer((socket) => { - const session = new AvmDebugSession(fileAccessor); - session.setRunAsServer(true); - session.start(socket as NodeJS.ReadableStream, socket); - socket.on('error', (err) => { - throw err; - }); - }).listen(0); - this.server.on('error', (err) => { - throw err; - }); - } - - port(): number { - return (this.server.address() as Net.AddressInfo).port; - } - - dispose() { - if (this.server) { - this.server.close(); - } - } -} diff --git a/src/cli.ts b/src/cli.ts index 4c0c59f..a8544e4 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,29 +1,16 @@ -import * as fs from 'fs/promises'; -import { basename } from 'path'; -import * as Net from 'net'; -import { FileAccessor } from './fileAccessor'; -import { AvmDebugSession } from './debugSession'; +#!/usr/bin/env node -/* - * cli.js is the entrypoint of the debug adapter when it runs as a separate process. - */ +import { AvmDebugSession } from './common'; +import { Server, nodeFileAccessor } from './node'; + +function handleError(err: Error) { + console.error(err); + process.exit(1); +} /* - * Since here we run the debug adapter as a separate ("external") process, it has no access to VS Code API. - * So we can only use node.js API for accessing files. + * cli.js is the entrypoint of the debug adapter when it runs as a separate process. */ -const fsAccessor: FileAccessor = { - isWindows: process.platform === 'win32', - readFile(path: string): Promise { - return fs.readFile(path); - }, - writeFile(path: string, contents: Uint8Array): Promise { - return fs.writeFile(path, contents); - }, - basename(path: string): string { - return basename(path); - }, -}; async function run() { /* @@ -37,37 +24,39 @@ async function run() { */ // first parse command line arguments to see whether the debug adapter should run as a server - let port = 0; + let port: number | undefined; const args = process.argv.slice(2); - args.forEach(function (val, index, array) { - const portMatch = /^--server=(\d{4,5})$/.exec(val); + args.forEach(function (val) { + const portMatch = /^--port=(\d+)$/.exec(val); if (portMatch) { port = parseInt(portMatch[1], 10); } }); - if (port > 0) { + if (typeof port !== 'undefined') { + console.log('>> running as a server with port ' + port); + // start a server that creates a new session for every connection request - console.error(`waiting for debug protocol on port ${port}`); - const server = Net.createServer((socket) => { - console.log('>> accepted connection from client'); - socket.on('error', (err) => { - throw err; - }); - socket.on('end', () => { - console.error('>> client connection closed\n'); - }); - const session = new AvmDebugSession(fsAccessor); - session.setRunAsServer(true); - session.start(socket, socket); - }).listen(port); - server.on('error', (err) => { - throw err; + + const server = new Server({ + fileAccessor: nodeFileAccessor, + port, + ready: () => { + console.log(`Waiting for debug protocol on ${server.address()}`); + }, + onSocketError: (err) => { + console.error('>> client connection error: ', err); + }, + onServerError: handleError, + }); + + process.on('SIGTERM', () => { + server.close(); }); } else { // start a single session that communicates via stdin/stdout - const session = new AvmDebugSession(fsAccessor); + const session = new AvmDebugSession(nodeFileAccessor); process.on('SIGTERM', () => { session.shutdown(); }); @@ -75,7 +64,4 @@ async function run() { } } -run().catch((err) => { - console.error(err); - process.exit(1); -}); +run().catch(handleError); diff --git a/src/appState.ts b/src/common/appState.ts similarity index 100% rename from src/appState.ts rename to src/common/appState.ts diff --git a/src/debugSession.ts b/src/common/debugSession.ts similarity index 100% rename from src/debugSession.ts rename to src/common/debugSession.ts diff --git a/src/fileAccessor.ts b/src/common/fileAccessor.ts similarity index 100% rename from src/fileAccessor.ts rename to src/common/fileAccessor.ts diff --git a/src/index.ts b/src/common/index.ts similarity index 100% rename from src/index.ts rename to src/common/index.ts diff --git a/src/runtime.ts b/src/common/runtime.ts similarity index 100% rename from src/runtime.ts rename to src/common/runtime.ts diff --git a/src/traceReplayEngine.ts b/src/common/traceReplayEngine.ts similarity index 100% rename from src/traceReplayEngine.ts rename to src/common/traceReplayEngine.ts diff --git a/src/utils.ts b/src/common/utils.ts similarity index 100% rename from src/utils.ts rename to src/common/utils.ts diff --git a/src/node/fileAccessor.ts b/src/node/fileAccessor.ts new file mode 100644 index 0000000..2e01888 --- /dev/null +++ b/src/node/fileAccessor.ts @@ -0,0 +1,16 @@ +import { readFile, writeFile } from 'fs/promises'; +import { basename } from 'path'; +import { FileAccessor } from '../common'; + +export const nodeFileAccessor: FileAccessor = { + isWindows: process.platform === 'win32', + readFile(path: string): Promise { + return readFile(path); + }, + writeFile(path: string, contents: Uint8Array): Promise { + return writeFile(path, contents); + }, + basename(path: string): string { + return basename(path); + }, +}; diff --git a/src/node/index.ts b/src/node/index.ts new file mode 100644 index 0000000..5bcfcf0 --- /dev/null +++ b/src/node/index.ts @@ -0,0 +1,2 @@ +export { Server } from './server'; +export { nodeFileAccessor } from './fileAccessor'; diff --git a/src/node/server.ts b/src/node/server.ts new file mode 100644 index 0000000..8f82443 --- /dev/null +++ b/src/node/server.ts @@ -0,0 +1,43 @@ +import * as Net from 'net'; +import { AvmDebugSession, FileAccessor } from '../common'; + +export class Server { + private readonly server: Net.Server; + + constructor({ + fileAccessor, + port, + ready, + onSocketError, + onServerError, + }: { + fileAccessor: FileAccessor; + port: number; + ready?: () => void; + onSocketError: (err: Error) => void; + onServerError: (err: Error) => void; + }) { + this.server = Net.createServer((socket) => { + const session = new AvmDebugSession(fileAccessor); + session.setRunAsServer(true); + session.start(socket, socket); + socket.on('error', onSocketError); + if (ready) { + ready(); + } + }).listen(port); + this.server.on('error', onServerError); + } + + address(): Net.AddressInfo { + return this.server.address() as Net.AddressInfo; + } + + port(): number { + return this.address().port; + } + + close() { + this.server.close(); + } +} diff --git a/tests/adapter.test.ts b/tests/adapter.test.ts index 2e2a352..94652d1 100644 --- a/tests/adapter.test.ts +++ b/tests/adapter.test.ts @@ -2,7 +2,7 @@ import * as assert from 'assert'; import * as path from 'path'; import * as algosdk from 'algosdk'; import { DebugProtocol } from '@vscode/debugprotocol'; -import { ByteArrayMap } from '../src/utils'; +import { ByteArrayMap } from '../src/common/utils'; import { TestFixture, assertVariables, advanceTo, DATA_ROOT } from './testing'; describe('Debug Adapter Tests', () => { diff --git a/tests/client.ts b/tests/client.ts index e884f8b..43234f2 100644 --- a/tests/client.ts +++ b/tests/client.ts @@ -2,7 +2,7 @@ import * as assert from 'assert'; import { SpawnOptions } from 'child_process'; import { DebugClient as DebugClientBase } from '@vscode/debugadapter-testsupport'; import { DebugProtocol } from '@vscode/debugprotocol'; -import { ILaunchRequestArguments } from '../src/debugSession'; +import { ILaunchRequestArguments } from '../src/common/debugSession'; import { ILocation, IPartialLocation, diff --git a/tests/testing.ts b/tests/testing.ts index 9a73b71..9f3724e 100644 --- a/tests/testing.ts +++ b/tests/testing.ts @@ -1,10 +1,8 @@ import * as assert from 'assert'; import * as path from 'path'; -import * as fs from 'fs/promises'; import { DebugClient } from './client'; -import { BasicServer } from '../src/basicServer'; -import { FileAccessor } from '../src/fileAccessor'; -import { ByteArrayMap } from '../src/utils'; +import { Server, nodeFileAccessor } from '../src/node'; +import { ByteArrayMap } from '../src/common/utils'; export const PROJECT_ROOT = path.join(__dirname, '../'); const DEBUG_CLIENT_PATH = path.join( @@ -13,22 +11,9 @@ const DEBUG_CLIENT_PATH = path.join( ); export const DATA_ROOT = path.join(PROJECT_ROOT, 'sampleWorkspace/'); -export const testFileAccessor: FileAccessor = { - isWindows: typeof process !== 'undefined' && process.platform === 'win32', - async readFile(path: string): Promise { - return await fs.readFile(path); - }, - async writeFile(path: string, contents: Uint8Array) { - return await fs.writeFile(path, contents); - }, - basename(p: string): string { - return path.basename(p); - }, -}; - export class TestFixture { private _client: DebugClient | undefined; - private _server: BasicServer | undefined; + private _server: Server | undefined; public get client(): DebugClient { if (!this._client) { @@ -37,7 +22,7 @@ export class TestFixture { return this._client; } - private get server(): BasicServer { + private get server(): Server { if (!this._server) { throw new Error('Not initialized'); } @@ -45,7 +30,16 @@ export class TestFixture { } public async init() { - this._server = new BasicServer(testFileAccessor); + this._server = new Server({ + fileAccessor: nodeFileAccessor, + port: 0, + onServerError(err) { + throw err; + }, + onSocketError(err) { + throw err; + }, + }); this._client = new DebugClient('node', DEBUG_CLIENT_PATH, 'avm'); await this._client.start(this._server.port()); @@ -68,7 +62,7 @@ export class TestFixture { public async stop() { await this.client.stop(); - this.server.dispose(); + this.server.close(); this._client = undefined; this._server = undefined; } diff --git a/tsconfig.json b/tsconfig.json index c67ee81..5749b2b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "outDir": "out", "lib": ["WebWorker", "ES2020"], "sourceMap": true, + "declaration": true, "rootDir": ".", "strict": true /* enable all strict type-checking options */, /* Additional Checks */