Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: CI
permissions:
contents: read
pull-requests: write

on:
push:
branches:
- main
workflow_dispatch:
pull_request:
branches:
- main

jobs:
build:
name: test (${{ matrix.os }}, node-${{ matrix.node }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
node: [22.x]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.config.node }}
- name: Install Dependencies
run: npm ci
- name: Check Format
run: npm run format && git diff --exit-code
- name: Lint
run: npm run lint
- name: Test
run: npm test
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

release:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
needs: [build]
steps:
- name: Checkout
uses: actions/checkout@v3
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: 22.x
- name: Install Dependencies
run: npm ci
- name: Package
run: npm run package
- name: Load secrets from 1Password
id: load_secrets
uses: 1password/load-secrets-action@v3
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
GITHUB_TOKEN: op://cicd_dankeboy36/GITHUB_TOKEN/credential
NPM_TOKEN: op://cicd_dankeboy36/NPM_TOKEN/credential
- name: Release
id: release
run: npm run release
env:
GITHUB_TOKEN: ${{ steps.load_secrets.outputs.GITHUB_TOKEN }}
NPM_TOKEN: ${{ steps.load_secrets.outputs.NPM_TOKEN }}
outputs:
release_version: ${{ steps.release.outputs.release_version }}
20 changes: 20 additions & 0 deletions .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: PR Title
permissions:
contents: read
pull-requests: write

on:
pull_request_target:
types:
- opened
- reopened
- edited
- synchronize

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@d2ab30dcffc66150340abb5b947d518a3c3ce9cb # v3.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
resources
node_modules
.DS_Store
dist
out
packages/extension/.vsce
test_workspace/fixtures
test_workspace/snapshots
*.vsix
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
out
dist
build
.vscode-test
10 changes: 10 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"dbaeumer.vscode-eslint",
"streetsidesoftware.code-spell-checker",
"Tobermory.es6-string-html",
"esbenp.prettier-vscode"
]
}
30 changes: 30 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}/packages/extension",
"${workspaceFolder}/test_workspace"
],
"outFiles": ["${workspaceFolder}/packages/extension/dist/extension.js"],
"preLaunchTask": "npm: build"
},
{
"name": "Preview Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}/packages/extension",
"${workspaceFolder}/test_workspace"
],
"outFiles": ["${workspaceFolder}/packages/extension/dist/extension.js"]
}
]
}
22 changes: 22 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"files.exclude": {
"out": false
},
"search.exclude": {
"out": true,
"test_workspace": true
},
"typescript.tsc.autoDetect": "off",
"typescript.tsdk": "./node_modules/typescript/lib",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"[javascript][javascriptreact][json][typescript][typescriptreact][css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnPaste": true,
"editor.formatOnSave": true
},
"eslint.format.enable": true,
"js/ts.implicitProjectConfig.checkJs": true,
"js/ts.implicitProjectConfig.module": "NodeNext"
}
15 changes: 15 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"label": "npm: watch",
"isBackground": true,
"problemMatcher": "$tsc-watch",
"presentation": {
"reveal": "never"
}
}
]
}
12 changes: 12 additions & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Exclude everything by default
**/*

# Only include the packaged extension artifacts
!packages/extension/dist/**
!packages/extension/package.json
!packages/extension/README.md
!packages/extension/LICENSE

# Optionally keep top-level metadata
!LICENSE
!README.md
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,29 @@
# error-replay-debug-adapter
Postmortem error replay in VS Code via a debug adapter
# Error Replay Debug Adapter (PoC)

Prototype VS Code debug adapter that replays post-mortem errors through the native debug UI (Call Stack, Variables, restart frame, breakpoints). There is no live engine: the adapter consumes a simple async API that loads a stack trace and lazily loads variables per frame.

![Error replay – debug](media/debug.gif)

This PoC explores replaying ESP32 crashes by decoding backtraces/coredumps with [ESP Exception Decoder](https://github.com/dankeboy36/esp-exception-decoder) and its [TraceBreaker](https://github.com/dankeboy36/trbr) engine, while borrowing ideas from Datadog Exception Replay ([VS Code plugin](https://docs.datadoghq.com/developers/ide_plugins/vscode/#exception-replay), [tracing docs](https://docs.datadoghq.com/tracing/error_tracking/exception_replay/)).

## Packages

- [`adapter`](packages/adapter/README.md): generic inline debug adapter (includes shared types) fed by your data source.
- [`mock`](packages/mock/README.md): mock generator that captures a Node script at the first uncaught exception.
- [`extension`](packages/extension/README.md): sample VS Code extension wiring the adapter and mock data.

## Quick start

- Clone, open in VS Code, then `npm install`.
- Open Debug view (<kbd>Ctrl/⌘</kbd>+<kbd>Shift</kbd>+<kbd>D</kbd>), choose the `Run Extension` config, and start (<kbd>F5</kbd>).
- In the test workspace, run the `Error Replay: Generate Mock Error` command, pick a sample JS module (or any JS that throws soon), and wait for the JSON fixture to be generated.
- Generated fixtures land in `fixtures/generated/` under your workspace; a CodeLens will appear at the error line once the file is created.
- A CodeLens appears at the error location: click it to replay. The debugger stops just before the error; restart frame to walk earlier frames. Continue jumps to the next recorded frame or breakpoint; step in/out are no-ops; evaluation is unsupported but you can watch variables, scopes, and frame metadata. If multiple errors exist for one location, you’ll pick via Quick Pick.

## Notes

> [!NOTE]
> Stepping moves across recorded frames; restart frame is supported. Evaluate/setVariable are intentionally not supported—this is a replay, not a live debugger.

- Variables are fetched lazily per snapshot; frames can be deemphasized via skip globs.
- See each package README for details and API surface.
58 changes: 58 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// @ts-check

import importPlugin from 'eslint-plugin-import'
import prettierPlugin from 'eslint-plugin-prettier'
import { defineConfig } from 'eslint/config'
import neostandard from 'neostandard'

const baseRules = neostandard({
semi: false,
ts: true,
ignores: [
'dist',
'**/dist/**',
'node_modules',
'coverage',
'out',
'**/out/**',
],
})

export default defineConfig([
...baseRules,
{
plugins: {
import: importPlugin,
prettier: prettierPlugin,
},
rules: {
curly: 'warn',
eqeqeq: 'warn',
'@stylistic/comma-dangle': 'off',
'@stylistic/generator-star-spacing': 'off',
'@stylistic/indent': 'off',
'@stylistic/no-tabs': 'off',
'@stylistic/space-before-function-paren': [
'error',
{ anonymous: 'always', named: 'never', asyncArrow: 'always' },
],
'generator-star-spacing': 'off',
'@stylistic/jsx-quotes': ['error', 'prefer-double'],
'import/first': 'error',
'import/order': [
'error',
{
'newlines-between': 'always',
groups: [
'builtin',
'external',
'internal',
['parent', 'sibling', 'index'],
],
},
],
'import/newline-after-import': 'error',
'prettier/prettier': ['warn', { proseWrap: 'always' }],
},
},
])
Binary file added media/debug.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added media/gen-mock.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading