1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | import {Deferred} from '../util/Deferred.js';
|
8 | import {rewriteError} from '../util/ErrorLike.js';
|
9 | import {createIncrementalIdGenerator} from '../util/incremental-id-generator.js';
|
10 |
|
11 | import {ProtocolError, TargetCloseError} from './Errors.js';
|
12 | import {debugError} from './util.js';
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | export class CallbackRegistry {
|
20 | #callbacks = new Map<number, Callback>();
|
21 | #idGenerator = createIncrementalIdGenerator();
|
22 |
|
23 | create(
|
24 | label: string,
|
25 | timeout: number | undefined,
|
26 | request: (id: number) => void,
|
27 | ): Promise<unknown> {
|
28 | const callback = new Callback(this.#idGenerator(), label, timeout);
|
29 | this.#callbacks.set(callback.id, callback);
|
30 | try {
|
31 | request(callback.id);
|
32 | } catch (error) {
|
33 |
|
34 |
|
35 | callback.promise.catch(debugError).finally(() => {
|
36 | this.#callbacks.delete(callback.id);
|
37 | });
|
38 | callback.reject(error as Error);
|
39 | throw error;
|
40 | }
|
41 |
|
42 | return callback.promise.finally(() => {
|
43 | this.#callbacks.delete(callback.id);
|
44 | });
|
45 | }
|
46 |
|
47 | reject(id: number, message: string, originalMessage?: string): void {
|
48 | const callback = this.#callbacks.get(id);
|
49 | if (!callback) {
|
50 | return;
|
51 | }
|
52 | this._reject(callback, message, originalMessage);
|
53 | }
|
54 |
|
55 | rejectRaw(id: number, error: object): void {
|
56 | const callback = this.#callbacks.get(id);
|
57 | if (!callback) {
|
58 | return;
|
59 | }
|
60 | callback.reject(error as any);
|
61 | }
|
62 |
|
63 | _reject(
|
64 | callback: Callback,
|
65 | errorMessage: string | ProtocolError,
|
66 | originalMessage?: string,
|
67 | ): void {
|
68 | let error: ProtocolError;
|
69 | let message: string;
|
70 | if (errorMessage instanceof ProtocolError) {
|
71 | error = errorMessage;
|
72 | error.cause = callback.error;
|
73 | message = errorMessage.message;
|
74 | } else {
|
75 | error = callback.error;
|
76 | message = errorMessage;
|
77 | }
|
78 |
|
79 | callback.reject(
|
80 | rewriteError(
|
81 | error,
|
82 | `Protocol error (${callback.label}): ${message}`,
|
83 | originalMessage,
|
84 | ),
|
85 | );
|
86 | }
|
87 |
|
88 | resolve(id: number, value: unknown): void {
|
89 | const callback = this.#callbacks.get(id);
|
90 | if (!callback) {
|
91 | return;
|
92 | }
|
93 | callback.resolve(value);
|
94 | }
|
95 |
|
96 | clear(): void {
|
97 | for (const callback of this.#callbacks.values()) {
|
98 |
|
99 | this._reject(callback, new TargetCloseError('Target closed'));
|
100 | }
|
101 | this.#callbacks.clear();
|
102 | }
|
103 |
|
104 | |
105 |
|
106 |
|
107 | getPendingProtocolErrors(): Error[] {
|
108 | const result: Error[] = [];
|
109 | for (const callback of this.#callbacks.values()) {
|
110 | result.push(
|
111 | new Error(
|
112 | `${callback.label} timed out. Trace: ${callback.error.stack}`,
|
113 | ),
|
114 | );
|
115 | }
|
116 | return result;
|
117 | }
|
118 | }
|
119 |
|
120 |
|
121 |
|
122 |
|
123 | export class Callback {
|
124 | #id: number;
|
125 | #error = new ProtocolError();
|
126 | #deferred = Deferred.create<unknown>();
|
127 | #timer?: ReturnType<typeof setTimeout>;
|
128 | #label: string;
|
129 |
|
130 | constructor(id: number, label: string, timeout?: number) {
|
131 | this.#id = id;
|
132 | this.#label = label;
|
133 | if (timeout) {
|
134 | this.#timer = setTimeout(() => {
|
135 | this.#deferred.reject(
|
136 | rewriteError(
|
137 | this.#error,
|
138 | `${label} timed out. Increase the 'protocolTimeout' setting in launch/connect calls for a higher timeout if needed.`,
|
139 | ),
|
140 | );
|
141 | }, timeout);
|
142 | }
|
143 | }
|
144 |
|
145 | resolve(value: unknown): void {
|
146 | clearTimeout(this.#timer);
|
147 | this.#deferred.resolve(value);
|
148 | }
|
149 |
|
150 | reject(error: Error): void {
|
151 | clearTimeout(this.#timer);
|
152 | this.#deferred.reject(error);
|
153 | }
|
154 |
|
155 | get id(): number {
|
156 | return this.#id;
|
157 | }
|
158 |
|
159 | get promise(): Promise<unknown> {
|
160 | return this.#deferred.valueOrThrow();
|
161 | }
|
162 |
|
163 | get error(): ProtocolError {
|
164 | return this.#error;
|
165 | }
|
166 |
|
167 | get label(): string {
|
168 | return this.#label;
|
169 | }
|
170 | }
|