Skip to content

Commit 1de6c8f

Browse files
Merge pull request #3 from phuongdnguyen/js-example
Add adapter & example for js/ts
2 parents 9aea41b + 7f2f18e commit 1de6c8f

31 files changed

+6558
-16
lines changed

.gitignore

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.DS_Store
12
.idea/
23
__debug_bin**
34
**/__tdlv_debug_bin*
@@ -213,4 +214,13 @@ marimo/_lsp/
213214
__marimo__/
214215

215216
# Streamlit
216-
.streamlit/secrets.toml
217+
.streamlit/secrets.toml
218+
219+
220+
#Nodejs
221+
222+
node_modules
223+
build
224+
npm-debug.log
225+
.env
226+
js-debug

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
go-simple:
2-
cd custom-debugger && go clean && go build -o ../tdlv.build && cd ../example/go//simple-workflow && ../../tdlv.build --lang=go
2+
cd custom-debugger && go clean && go build -o ../tdlv.build && cd ../example/go/simple-workflow && ../../../tdlv.build --lang=go
33

44
go-structured-ide-integrated:
55
cd custom-debugger && go clean && go build -o ../tdlv.build && cd ../example/go/structured-workflow/replay-debug-ide-integrated && ../../../../tdlv.build --lang=go
@@ -10,6 +10,10 @@ go-structured-standalone:
1010
python:
1111
cd custom-debugger && go clean && go build -o ../tdlv.build && cd .. && ./tdlv.build --lang=python
1212

13+
# Node.js/TypeScript replayer
14+
js:
15+
cd custom-debugger && go clean && go build -o ../tdlv.build && cd ../example/js && ../../tdlv.build --lang=js
16+
1317
build:
1418
cd custom-debugger && go clean && go build -o ../tdlv.build
1519

custom-debugger/main.go

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ func main() {
3434
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options]\n\n", os.Args[0])
3535
flag.PrintDefaults()
3636
}
37+
var install bool
38+
flag.BoolVar(&install, "install", false, "install required language debugger if missing")
3739

3840
flag.Parse()
3941

@@ -46,11 +48,13 @@ func main() {
4648
debuggerStopCh := make(chan struct{}, 1)
4749
switch lang {
4850
case "go":
49-
startDelve(debuggerStopCh)
51+
startDelve(debuggerStopCh, install)
5052
case "python":
51-
startDebugPy(debuggerStopCh)
53+
startDebugPy(debuggerStopCh, install)
54+
case "js":
55+
startJsDebug(debuggerStopCh, install)
5256
default:
53-
log.Fatalf("Unsupported lang: %s", lang)
57+
log.Printf("Running with lang %s, expect a language debugger to be started on port 2345", lang)
5458
}
5559

5660
addr := fmt.Sprintf(":%d", proxyPort)
@@ -91,7 +95,10 @@ func main() {
9195
}
9296
}
9397

94-
func startDelve(stopCh <-chan struct{}) {
98+
func startDelve(stopCh <-chan struct{}, install bool) {
99+
if install {
100+
// Install delve
101+
}
95102
// Listen on TCP port for Delve server
96103
l, err := net.Listen("tcp", ":2345")
97104
if err != nil {
@@ -154,30 +161,52 @@ func startDelve(stopCh <-chan struct{}) {
154161
}()
155162
}
156163

157-
func startDebugPy(debuggerStopCh <-chan struct{}) {
164+
func startDebugPy(stopCh <-chan struct{}, install bool) {
165+
if install {
166+
// Install debugpy
167+
}
158168
ctx := context.Background()
159169
cmd := exec.CommandContext(ctx, "python", "-m", "debugpy", "--listen", "2345", "--wait-for-client", "standalone_replay.py")
160170
cmd.Dir = "example/python" // Set working directory to the Python example
161171
cmd.Stdout = os.Stdout
162172
cmd.Stderr = os.Stderr
163173
go func() {
164-
165174
log.Println("Starting Python debugpy server on :2345")
166175
if err := cmd.Run(); err != nil {
167176
log.Printf("Error running Python debugpy: %v", err)
168177
}
169178
}()
170179
go func() {
171-
select {
172-
case <-debuggerStopCh:
173-
if err := cmd.Process.Kill(); err != nil {
174-
log.Printf("Error killing Python debugpy: %v", err)
175-
}
176-
log.Println("Python debugger stopped")
180+
<-stopCh
181+
if err := cmd.Process.Kill(); err != nil {
182+
log.Printf("Error killing Python debugpy: %v", err)
177183
}
184+
log.Println("Python debugger stopped")
178185
}()
179186
}
180187

188+
func startJsDebug(stopCh <-chan struct{}, install bool) {
189+
if install {
190+
// Install js-debug
191+
}
192+
ctx := context.Background()
193+
cmd := exec.CommandContext(ctx, "node", "dapDebugServer.js", "2345", "127.0.0.1")
194+
go func() {
195+
log.Println("Starting JS debug on :2345")
196+
if err := cmd.Run(); err != nil {
197+
log.Printf("Error running JS Debug: %v", err)
198+
}
199+
}()
200+
go func() {
201+
<-stopCh
202+
if err := cmd.Process.Kill(); err != nil {
203+
log.Printf("Error killing JS debug: %v", err)
204+
}
205+
log.Println("JS debug stopped")
206+
}()
207+
208+
}
209+
181210
func init() {
182211
log.SetOutput(os.Stdout)
183212
}

custom-debugger/pkg/dap-interceptors/response_interceptor.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,5 +241,4 @@ func (rir *ResponseInterceptingReader) handleStoppedEvent(event *dap.StoppedEven
241241
}
242242

243243
return extractors.BuildDAPMessages(jsonObj, remaining)
244-
return extractors.BuildDAPMessages(jsonObj, remaining)
245244
}

custom-debugger/pkg/locators/locators.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ func IsInAdapterCodeByPath(filePath string) bool {
3434
strings.Contains(filePath, "/runtime/") ||
3535
strings.Contains(filePath, "/reflect/") ||
3636
strings.Contains(filePath, "replayer-adapter-python/") ||
37-
strings.Contains(filePath, "replayer.py")
37+
strings.Contains(filePath, "replayer.py") ||
38+
strings.Contains(filePath, "replayer-adapter-nodejs/") ||
39+
strings.Contains(filePath, "replayer.ts")
3840
}
3941

4042
// Pwd returns the current working directory
@@ -81,6 +83,7 @@ func IsUserCodeFile(filePath, workingDir string) bool {
8183
if strings.Contains(filePath, "replayer-adapter/") ||
8284
strings.Contains(filePath, "custom-debugger/") ||
8385
strings.Contains(filePath, "replayer-adapter-python/") ||
86+
strings.Contains(filePath, "replayer-adapter-nodejs/") ||
8487
strings.Contains(filePath, "vendor/") ||
8588
strings.Contains(filePath, ".git/") {
8689
return false

example/js/.vscode/launch.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"version": "0.2.0", // VS Code launch configuration schema version
3+
"configurations": [
4+
{
5+
"name": "Debug with ts-node",
6+
"type": "pwa-node",
7+
"request": "launch",
8+
"program": "${workspaceFolder}/main.ts",
9+
"runtimeExecutable": "npx",
10+
"runtimeArgs": ["ts-node"],
11+
"console": "integratedTerminal",
12+
"args": ["replay"],
13+
"outputCapture": "console",
14+
"stopOnEntry": false,
15+
"trace": true,
16+
"timeout": 5000,
17+
"env": {
18+
"TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.json"
19+
}
20+
}
21+
]
22+
}

example/js/README.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Temporal TypeScript Example
2+
3+
This example demonstrates a complete Temporal workflow setup including:
4+
- **Workflow Definition**: A multi-step workflow with activities, signals, and queries
5+
- **Worker**: A worker that executes workflows and activities
6+
- **Workflow Starter**: A client that starts and interacts with workflows
7+
- **Replayer**: Functionality to replay workflow history for testing and debugging
8+
9+
## Prerequisites
10+
11+
1. **Temporal Server**: Make sure you have Temporal server running locally
12+
```bash
13+
# Using Temporal CLI (recommended)
14+
temporal server start-dev
15+
16+
# Or using Docker
17+
git clone https://github.com/temporalio/docker-compose.git
18+
cd docker-compose
19+
docker-compose up
20+
```
21+
22+
2. **Node.js**: Version 16 or higher
23+
24+
## Setup
25+
26+
1. Install dependencies:
27+
```bash
28+
npm install
29+
```
30+
31+
2. Build the TypeScript code:
32+
```bash
33+
npm run build
34+
```
35+
36+
## Usage
37+
38+
### 1. Start the Worker
39+
40+
In one terminal, start the worker to listen for workflow tasks:
41+
42+
```bash
43+
npm run worker
44+
```
45+
46+
You should see:
47+
```
48+
Starting worker...
49+
Worker started. Listening for workflows...
50+
```
51+
52+
### 2. Start a Workflow
53+
54+
In another terminal, start a workflow:
55+
56+
```bash
57+
npm run start
58+
```
59+
60+
This will:
61+
- Start a new workflow with a unique ID
62+
- Send signals to update the workflow state
63+
- Query the workflow status
64+
- Wait for the workflow to complete
65+
- Display the final result
66+
67+
Expected output:
68+
```
69+
Starting workflow...
70+
Started workflow with ID: example-workflow-1234567890
71+
Sending signal to update name...
72+
Current status: name updated to Updated Developer
73+
Sending completion signal...
74+
Workflow result: Workflow completed successfully! Greeting: Hello, Updated Developer!, Result: 42, Data: Processed: SAMPLE WORKFLOW DATA
75+
```
76+
77+
### 3. Replay a Workflow
78+
79+
To replay a completed workflow for testing/debugging:
80+
81+
```bash
82+
npm run replay <workflow-id>
83+
```
84+
85+
For example:
86+
```bash
87+
npm run replay example-workflow-1234567890
88+
```
89+
90+
## Workflow Features
91+
92+
### Activities
93+
- **greetActivity**: Simple greeting with a name
94+
- **calculateActivity**: Performs a calculation with retry logic
95+
- **processDataActivity**: Processes and transforms data
96+
97+
### Signals
98+
- **updateNameSignal**: Updates the name used in the workflow
99+
- **completeWorkflowSignal**: Triggers workflow completion
100+
101+
### Queries
102+
- **getCurrentStatusQuery**: Returns the current workflow status
103+
104+
### Workflow Flow
105+
1. **Greeting**: Calls the greet activity with the initial name
106+
2. **Calculation**: Performs a mathematical operation
107+
3. **Data Processing**: Transforms input data
108+
4. **Wait for Completion**: Waits for a signal or timeout (30 seconds)
109+
5. **Return Result**: Combines all results into a final message
110+
111+
## Advanced Usage
112+
113+
### Direct TypeScript Execution
114+
115+
You can also run the commands directly with ts-node:
116+
117+
```bash
118+
# Start worker
119+
npx ts-node main.ts worker
120+
121+
# Start workflow
122+
npx ts-node main.ts start
123+
124+
# Replay workflow
125+
npx ts-node main.ts replay <workflow-id>
126+
```
127+
128+
### Customizing the Example
129+
130+
The workflow accepts input parameters that you can modify in the `startWorkflow()` function:
131+
132+
```typescript
133+
const workflowInput: WorkflowInput = {
134+
initialName: 'Your Name Here',
135+
numbers: { a: 5, b: 10 },
136+
data: 'your custom data',
137+
};
138+
```
139+
140+
### Integration with Replayer Adapter
141+
142+
This example is designed to work with the `replayer-adapter-nodejs` package for enhanced replay capabilities. You can integrate it by:
143+
144+
1. Installing the replayer adapter:
145+
```bash
146+
npm install ../../../replayer-adapter-nodejs
147+
```
148+
149+
2. Using the replayer adapter in your workflow code for advanced debugging features.
150+
151+
## Project Structure
152+
153+
```
154+
example/js/
155+
├── main.ts # Complete workflow example
156+
├── package.json # Dependencies and scripts
157+
├── tsconfig.json # TypeScript configuration
158+
├── README.md # This file
159+
└── dist/ # Compiled JavaScript (after build)
160+
```
161+
162+
## Troubleshooting
163+
164+
1. **Connection Errors**: Make sure Temporal server is running on `localhost:7233`
165+
2. **Build Errors**: Ensure you have TypeScript installed and run `npm run build`
166+
3. **Worker Not Receiving Tasks**: Check that both worker and client use the same task queue name
167+
4. **Signal/Query Errors**: Ensure the workflow is running before sending signals or queries
168+
169+
## Next Steps
170+
171+
- Explore the [Temporal TypeScript SDK documentation](https://docs.temporal.io/typescript)
172+
- Check out more [Temporal samples](https://github.com/temporalio/samples-typescript)
173+
- Learn about [Temporal best practices](https://docs.temporal.io/typescript/best-practices)

example/js/activities.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// ====================
2+
// ACTIVITY DEFINITIONS
3+
// ====================
4+
5+
export async function greetActivity(name: string): Promise<string> {
6+
return `Hello, ${name}!`;
7+
}
8+
9+
export async function calculateActivity(a: number, b: number): Promise<number> {
10+
console.log(`Calculating ${a} + ${b}`);
11+
await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate work
12+
return a + b;
13+
}
14+
15+
export async function processDataActivity(data: string): Promise<string> {
16+
console.log(`Processing data: ${data}`);
17+
await new Promise(resolve => setTimeout(resolve, 2000)); // Simulate work
18+
return `Processed: ${data.toUpperCase()}`;
19+
}

0 commit comments

Comments
 (0)