1+ import * as ChannelSchema from "@effect/platform/ChannelSchema"
2+ import * as Socket from "@effect/platform/Socket"
13import * as Array from "effect/Array"
24import * as Data from "effect/Data"
35import * as Effect from "effect/Effect"
6+ import * as Function from "effect/Function"
7+ import * as Mailbox from "effect/Mailbox"
48import * as Option from "effect/Option"
59import type * as ParseResult from "effect/ParseResult"
10+ import * as Predicate from "effect/Predicate"
611import * as Schema from "effect/Schema"
712import * as SchemaAST from "effect/SchemaAST"
13+ import * as Stream from "effect/Stream"
14+ import * as ws from "ws"
815import * as VsCode from "./VsCode"
916
10- export class DebugChannelError extends Data . TaggedError ( "VariableExtractError " ) < {
17+ export class DebugChannelError extends Data . TaggedError ( "DebugChannelError " ) < {
1118 readonly message : string
1219} > { }
1320
@@ -31,8 +38,44 @@ export class DebugChannel extends Effect.Tag("effect-vscode/DebugChannel")<Debug
3138 threadId : number | undefined
3239 }
3340 ) => Effect . Effect < VariableReference , DebugChannelError , never >
41+ evaluateOnEveryExecutionContext : (
42+ opts : {
43+ expression : string
44+ }
45+ ) => Effect . Effect < void , DebugChannelError , never >
3446} > ( ) { }
3547
48+ export interface CdpProxyClient {
49+ readonly queue : Mailbox . ReadonlyMailbox < unknown >
50+ readonly request : ( _ : unknown ) => Effect . Effect < void >
51+ }
52+
53+ export const run = Effect . fnUntraced (
54+ function * < R , E , _ > ( socket : Socket . Socket , handle : ( client : CdpProxyClient ) => Effect . Effect < _ , E , R > ) {
55+ const responses = yield * Mailbox . make < unknown > ( )
56+ const requests = yield * Mailbox . make < unknown > ( )
57+
58+ const client : CdpProxyClient = {
59+ queue : requests ,
60+ request : ( res ) => responses . offer ( res )
61+ }
62+
63+ yield * Mailbox . toStream ( responses ) . pipe (
64+ Stream . pipeThroughChannel (
65+ ChannelSchema . duplexUnknown ( Socket . toChannelString ( socket ) , {
66+ inputSchema : Schema . parseJson ( Schema . Unknown ) ,
67+ outputSchema : Schema . parseJson ( Schema . Unknown )
68+ } )
69+ ) ,
70+ Stream . runForEach ( ( req ) => requests . offer ( req ) ) ,
71+ Effect . ensuring ( Effect . zipRight ( responses . shutdown , requests . shutdown ) ) ,
72+ Effect . forkScoped
73+ )
74+
75+ yield * handle ( client )
76+ }
77+ )
78+
3679interface DapVariableReference {
3780 readonly name : string
3881 readonly value : string
@@ -142,7 +185,12 @@ export const makeVsCodeDebugSession = (debugSession: VsCode.VsCodeDebugSession["
142185 } )
143186 }
144187 const elementAst = index < ast . elements . length ? ast . elements [ index ] : ast . rest [ 0 ]
145- return extractValue ( indexProperty , elementAst . type )
188+ return extractValue ( indexProperty , elementAst . type ) . pipe (
189+ Effect . catchTag (
190+ "DebugChannelError" ,
191+ ( e ) => new DebugChannelError ( { message : "at index " + index + " " + e . message } )
192+ )
193+ )
146194 } )
147195 return yield * Effect . all ( elements , { concurrency : "unbounded" } )
148196 }
@@ -173,6 +221,14 @@ export const makeVsCodeDebugSession = (debugSession: VsCode.VsCodeDebugSession["
173221 result [ propertySignature . name ] = extractValue (
174222 propertyVariableReference ,
175223 propertySignature . type
224+ ) . pipe (
225+ Effect . catchTag (
226+ "DebugChannelError" ,
227+ ( e ) =>
228+ new DebugChannelError ( {
229+ message : "in property " + String ( propertySignature . name ) + " " + e . message
230+ } )
231+ )
176232 )
177233 }
178234 return yield * Effect . all ( result , { concurrency : "unbounded" } )
@@ -207,6 +263,66 @@ export const makeVsCodeDebugSession = (debugSession: VsCode.VsCodeDebugSession["
207263 }
208264
209265 return DebugChannel . of ( {
266+ evaluateOnEveryExecutionContext : ( _opts ) =>
267+ Effect . gen ( function * ( ) {
268+ const addr = yield * VsCode . executeCommand < { host : string ; port : number ; path ?: string } > (
269+ "extension.js-debug.requestCDPProxy" ,
270+ debugSession . id
271+ )
272+ const uri = `ws://${ addr . host } :${ addr . port } ${ addr . path || "" } `
273+
274+ const cdpSocket = yield * Socket . fromWebSocket (
275+ Effect . sync ( ( ) => {
276+ const wss = new ws . WebSocket ( uri , {
277+ perMessageDeflate : false ,
278+ maxPayload : 256 * 1024 * 1024
279+ } )
280+
281+ return wss as any
282+ } )
283+ )
284+
285+ yield * run (
286+ cdpSocket ,
287+ Effect . fn ( function * ( client ) {
288+ // we enable reporting of execution contexts
289+ yield * client . request ( {
290+ method : "Runtime.enable"
291+ } )
292+
293+ while ( true ) {
294+ const response = yield * client . queue . take
295+ if (
296+ Predicate . hasProperty ( response , "method" ) && response . method === "Runtime.executionContextCreated" &&
297+ Predicate . hasProperty ( response , "params" ) && Predicate . hasProperty ( response . params , "context" ) &&
298+ Predicate . hasProperty ( response . params . context , "id" )
299+ ) {
300+ const contextId = response . params . context . id
301+
302+ // we send as notification and silent because we don't want to pollute the output
303+ yield * client . request ( {
304+ method : "Runtime.evaluate" ,
305+ params : {
306+ expression : _opts . expression ,
307+ contextId,
308+ silent : true
309+ }
310+ } )
311+ }
312+ }
313+ } )
314+ )
315+ } ) . pipe (
316+ Effect . scoped ,
317+ Effect . timeoutTo ( {
318+ onTimeout : Function . constUndefined ,
319+ onSuccess : Function . identity ,
320+ duration : 1000
321+ } ) ,
322+ Effect . catchAll ( ( socketError ) => {
323+ return new DebugChannelError ( { message : String ( socketError ) } )
324+ } )
325+ ) ,
210326 evaluate : ( opts ) =>
211327 Effect . gen ( function * ( ) {
212328 let request : any = {
@@ -224,7 +340,7 @@ export const makeVsCodeDebugSession = (debugSession: VsCode.VsCodeDebugSession["
224340 const stackTraces = yield * debugRequest < DapStackTracesResponse > ( "stackTrace" , {
225341 threadId
226342 } )
227- const stackTrace = stackTraces . stackFrames [ stackTraces . stackFrames . length - 1 ]
343+ const stackTrace = stackTraces . stackFrames [ 0 ]
228344 if ( stackTrace ) {
229345 request = {
230346 expression : opts . expression ,
0 commit comments