@github/copilot-sdk 1.0.14 → 1.0.15-preview.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -0
- package/dist/cjs/cliVersion.js +1 -1
- package/dist/cjs/client.js +15 -13
- package/dist/cjs/copilotRequestHandler.js +156 -20
- package/dist/cjs/generated/rpc.js +248 -3
- package/dist/cjs/schema.js +40 -0
- package/dist/cjs/session.js +129 -3
- package/dist/cliVersion.d.ts +1 -1
- package/dist/cliVersion.js +1 -1
- package/dist/client.d.ts +2 -0
- package/dist/client.js +13 -11
- package/dist/copilotRequestHandler.js +156 -20
- package/dist/generated/rpc.d.ts +2164 -166
- package/dist/generated/rpc.js +248 -3
- package/dist/generated/session-events.d.ts +493 -1
- package/dist/index.d.ts +1 -1
- package/dist/schema.d.ts +4 -0
- package/dist/schema.js +14 -0
- package/dist/session.d.ts +10 -3
- package/dist/session.js +129 -3
- package/dist/types.d.ts +37 -5
- package/package.json +16 -14
package/README.md
CHANGED
|
@@ -118,6 +118,7 @@ new CopilotClient(options?: CopilotClientOptions)
|
|
|
118
118
|
- `mode?: "empty" | "copilot-cli"` - Defaulting strategy. Use `"empty"` for multi-user server mode; defaults to `"copilot-cli"`.
|
|
119
119
|
- `workingDirectory?: string` - Working directory for the runtime process (default: current process cwd).
|
|
120
120
|
- `baseDirectory?: string` - Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned runtime. When not set, the runtime defaults to `~/.copilot`. Ignored when connecting via `RuntimeConnection.forUri`.
|
|
121
|
+
- `extensionLaunchProvider?: ExtensionLaunchProvider` - Experimental connection-level resolver for extension launch profiles. The client installs the reverse-RPC handler and registers the provider during startup before sessions can be created.
|
|
121
122
|
- `logLevel?: "none" | "error" | "warning" | "info" | "debug" | "all"` - Log level. When omitted, the runtime uses its own default (currently `"info"`).
|
|
122
123
|
- `env?: Record<string, string | undefined>` - Environment variables for the runtime process. When omitted, inherits `process.env`.
|
|
123
124
|
- `gitHubToken?: string` - GitHub token for authentication. When provided, takes priority over other auth methods.
|
|
@@ -305,6 +306,87 @@ Send a message and wait until the session becomes idle.
|
|
|
305
306
|
|
|
306
307
|
Returns the final assistant message event, or undefined if none was received.
|
|
307
308
|
|
|
309
|
+
##### Structured output (preview)
|
|
310
|
+
|
|
311
|
+
Requires a runtime build with `responseFormat` and `originatingMessageId` support.
|
|
312
|
+
Pass a raw JSON Schema or a Zod schema as `responseSchema` to `send` or
|
|
313
|
+
`sendAndWait`. As with custom tool parameters, the SDK converts Zod schemas to
|
|
314
|
+
JSON Schema before sending them:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
import { z } from "zod";
|
|
318
|
+
|
|
319
|
+
const answerSchema = z.object({ answer: z.number().int() });
|
|
320
|
+
const message = await session.sendAndWait({
|
|
321
|
+
prompt: "What is 19 + 23?",
|
|
322
|
+
responseSchema: answerSchema,
|
|
323
|
+
});
|
|
324
|
+
console.log(message?.data.content); // JSON text
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
For a typed result, pass the Zod schema as the **second argument** instead:
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
const answer = await session.sendAndWait("What is 19 + 23?", answerSchema);
|
|
331
|
+
console.log(answer.answer); // number; TResult is inferred from answerSchema
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
`sendAndWait<TResult>(options, schema, timeout?)` generates the JSON Schema from
|
|
335
|
+
the schema value, parses the final JSON, and validates it with the schema's
|
|
336
|
+
`parse` method. TypeScript cannot derive a runtime schema from an erased type
|
|
337
|
+
parameter alone. Invalid JSON, a schema mismatch, or a completed run without a
|
|
338
|
+
matching assistant message throws. Do not also set `options.responseSchema` when
|
|
339
|
+
using the typed overload.
|
|
340
|
+
|
|
341
|
+
The schema belongs to the submitted run, including its tool-call iterations.
|
|
342
|
+
Internally generated stop-hook corrections retain the schema and originating
|
|
343
|
+
message ID, so the wait returns the corrected answer. Independent subsequent
|
|
344
|
+
sends do not inherit it. Ordinary immediate steering inherits the active schema
|
|
345
|
+
and originating message ID, even when it arrives too late for the current model
|
|
346
|
+
request and is promoted into a follow-up run. Specifying a schema with
|
|
347
|
+
`mode: "immediate"` is rejected, even while idle.
|
|
348
|
+
The generated `session.rpc.send` and `session.rpc.sendMessages` wrappers expose
|
|
349
|
+
the full `responseFormat` contract when you need to set its name, description,
|
|
350
|
+
or strict option rather than using the convenience defaults (`name: "response"`,
|
|
351
|
+
`strict: true`).
|
|
352
|
+
Each batch starts one run: the final returned message ID is its origin, preceding
|
|
353
|
+
messages are context, and an empty batch has no origin. An immediate batch
|
|
354
|
+
steers the active run instead and retains its origin.
|
|
355
|
+
The schema is not a persisted session default: autonomous resume-pending work
|
|
356
|
+
after a restart does not restore it. A terminal tool that clears context ends
|
|
357
|
+
the old run; its fresh seed does not inherit the schema or origin. Such a run
|
|
358
|
+
can finish without a structured result, in which case the typed wait throws.
|
|
359
|
+
After a successful terminal tool, the runtime disables tools while the model
|
|
360
|
+
produces the structured result. Stop-hook corrections remain supported.
|
|
361
|
+
Remote sessions and known HydraFusion routes reject response formats before
|
|
362
|
+
admission. Schemas larger than 32 MiB when JSON-encoded are also rejected before
|
|
363
|
+
admission, using the runtime's existing request-size ceiling. This does not
|
|
364
|
+
guarantee the schema plus conversation and tools fits the provider's budget.
|
|
365
|
+
|
|
366
|
+
Structured waits select the last root-agent message whose `originatingMessageId`
|
|
367
|
+
matches the ID returned by their send, then return at a non-autopilot
|
|
368
|
+
`session.idle`. Other queued work can delay that idle, but cannot replace the
|
|
369
|
+
selected result. The existing unformatted overload retains its session-wide
|
|
370
|
+
behavior. `turnId` identifies an individual model/tool iteration, not the whole
|
|
371
|
+
run; telemetry interaction IDs are not unique run identifiers.
|
|
372
|
+
|
|
373
|
+
For event-driven consumption with `send`, subscribe before sending and collect
|
|
374
|
+
root `assistant.message` events whose `data.originatingMessageId` matches the ID
|
|
375
|
+
returned by `send`; events may arrive before that acknowledgement. Wait for
|
|
376
|
+
`session.idle`, then parse the last matching message without tool requests.
|
|
377
|
+
An earlier response may be superseded by a stop-hook correction. Handle
|
|
378
|
+
`session.error` and aborted idle events rather than returning a partial result.
|
|
379
|
+
|
|
380
|
+
Streaming still delivers ordinary text events, including intermediate messages
|
|
381
|
+
and tool calls. Only the final selected message is parsed by the typed overload;
|
|
382
|
+
not every event is necessarily a complete schema-conforming JSON document.
|
|
383
|
+
Provider errors, refusals, cancellation, truncation, session errors, and timeouts
|
|
384
|
+
can prevent a typed result. A timeout stops waiting, not the runtime's work.
|
|
385
|
+
Use a model and endpoint that support native structured output. An API-compatible
|
|
386
|
+
gateway may ignore format fields even when it accepts the request; for example,
|
|
387
|
+
the Claude Chat-completions compatibility route is not equivalent to Anthropic's
|
|
388
|
+
native `output_config.format` endpoint.
|
|
389
|
+
|
|
308
390
|
##### `on(eventType: string, handler: TypedSessionEventHandler): () => void`
|
|
309
391
|
|
|
310
392
|
Subscribe to a specific event type. The handler receives properly typed events.
|
package/dist/cjs/cliVersion.js
CHANGED
|
@@ -22,7 +22,7 @@ __export(cliVersion_exports, {
|
|
|
22
22
|
COPILOT_CLI_VERSION: () => COPILOT_CLI_VERSION
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(cliVersion_exports);
|
|
25
|
-
const COPILOT_CLI_VERSION = "1.0.
|
|
25
|
+
const COPILOT_CLI_VERSION = "1.0.87-0";
|
|
26
26
|
const COPILOT_CLI_USE_NPM_PACKAGE = false;
|
|
27
27
|
// Annotate the CommonJS export names for ESM import in node:
|
|
28
28
|
0 && (module.exports = {
|
package/dist/cjs/client.js
CHANGED
|
@@ -45,13 +45,11 @@ var import_cliVersion = require("./cliVersion.js");
|
|
|
45
45
|
var import_sessionFsProvider = require("./sessionFsProvider.js");
|
|
46
46
|
var import_copilotRequestHandler = require("./copilotRequestHandler.js");
|
|
47
47
|
var import_telemetry = require("./telemetry.js");
|
|
48
|
+
var import_schema = require("./schema.js");
|
|
48
49
|
var import_toolSet = require("./toolSet.js");
|
|
49
50
|
var import_types = require("./types.js");
|
|
50
51
|
const MIN_PROTOCOL_VERSION = 3;
|
|
51
52
|
const RUNTIME_SHUTDOWN_TIMEOUT_MS = 1e4;
|
|
52
|
-
function isZodSchema(value) {
|
|
53
|
-
return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
|
|
54
|
-
}
|
|
55
53
|
async function withTimeout(promise, timeoutMs, message) {
|
|
56
54
|
let timeout;
|
|
57
55
|
try {
|
|
@@ -96,13 +94,6 @@ async function waitForChildExit(child, timeoutMs) {
|
|
|
96
94
|
}
|
|
97
95
|
});
|
|
98
96
|
}
|
|
99
|
-
function toJsonSchema(parameters) {
|
|
100
|
-
if (!parameters) return void 0;
|
|
101
|
-
if (isZodSchema(parameters)) {
|
|
102
|
-
return parameters.toJSONSchema();
|
|
103
|
-
}
|
|
104
|
-
return parameters;
|
|
105
|
-
}
|
|
106
97
|
const DEFAULT_PROVIDER_NAME = "default";
|
|
107
98
|
function extractBearerTokenProviders(provider, providers) {
|
|
108
99
|
const callbacks = /* @__PURE__ */ new Map();
|
|
@@ -235,6 +226,7 @@ class CopilotClient {
|
|
|
235
226
|
cliProcess = null;
|
|
236
227
|
ffiHost = null;
|
|
237
228
|
connection = null;
|
|
229
|
+
requestAdapter = null;
|
|
238
230
|
messageWriter = null;
|
|
239
231
|
connectionClosed = false;
|
|
240
232
|
socket = null;
|
|
@@ -272,6 +264,7 @@ class CopilotClient {
|
|
|
272
264
|
/** Connection-level session filesystem config, set via constructor option. */
|
|
273
265
|
sessionFsConfig = null;
|
|
274
266
|
requestHandler = null;
|
|
267
|
+
extensionLaunchProvider;
|
|
275
268
|
builtinPluginDirectories = [];
|
|
276
269
|
onGitHubTelemetry;
|
|
277
270
|
clientGlobalHandlers = {};
|
|
@@ -429,6 +422,7 @@ class CopilotClient {
|
|
|
429
422
|
this.onGetTraceContext = options.onGetTraceContext;
|
|
430
423
|
this.sessionFsConfig = options.sessionFs ?? null;
|
|
431
424
|
this.requestHandler = options.requestHandler ?? null;
|
|
425
|
+
this.extensionLaunchProvider = options.extensionLaunchProvider;
|
|
432
426
|
this.onGitHubTelemetry = options.onGitHubTelemetry;
|
|
433
427
|
this.setupClientGlobalHandlers();
|
|
434
428
|
const connEnv = conn.kind === "stdio" || conn.kind === "tcp" ? conn.env : void 0;
|
|
@@ -531,14 +525,16 @@ class CopilotClient {
|
|
|
531
525
|
}
|
|
532
526
|
setupClientGlobalHandlers() {
|
|
533
527
|
const handlers = {};
|
|
528
|
+
handlers.extensionLaunchProvider = this.extensionLaunchProvider;
|
|
534
529
|
if (this.requestHandler) {
|
|
535
|
-
|
|
530
|
+
this.requestAdapter = (0, import_copilotRequestHandler.createCopilotRequestAdapter)(this.requestHandler, () => {
|
|
536
531
|
if (!this.connection) {
|
|
537
532
|
return void 0;
|
|
538
533
|
}
|
|
539
534
|
this._rpc ??= (0, import_rpc.createServerRpc)(this.connection);
|
|
540
535
|
return this._rpc;
|
|
541
536
|
});
|
|
537
|
+
handlers.llmInference = this.requestAdapter;
|
|
542
538
|
}
|
|
543
539
|
if (this.onGitHubTelemetry) {
|
|
544
540
|
const onGitHubTelemetry = this.onGitHubTelemetry;
|
|
@@ -643,6 +639,9 @@ class CopilotClient {
|
|
|
643
639
|
}
|
|
644
640
|
await this.connectToServer();
|
|
645
641
|
await this.verifyProtocolVersion();
|
|
642
|
+
if (this.extensionLaunchProvider) {
|
|
643
|
+
await this.rpc.registerExtensionLaunchProvider();
|
|
644
|
+
}
|
|
646
645
|
if (this.builtinPluginDirectories.length > 0) {
|
|
647
646
|
try {
|
|
648
647
|
await this.connection.sendRequest("plugins.builtin.set", {
|
|
@@ -731,6 +730,7 @@ class CopilotClient {
|
|
|
731
730
|
}
|
|
732
731
|
this.sessions.clear();
|
|
733
732
|
this.githubTokenProviders.clear();
|
|
733
|
+
this.requestAdapter?.cancelPending();
|
|
734
734
|
if (this.connection && !this.connectionClosed && (this.cliProcess || this.ffiHost) && !this.isExternalServer) {
|
|
735
735
|
const runtimeShutdownStart = Date.now();
|
|
736
736
|
const shutdownPromise = this.rpc.runtime.shutdown();
|
|
@@ -886,6 +886,7 @@ class CopilotClient {
|
|
|
886
886
|
}
|
|
887
887
|
this.sessions.clear();
|
|
888
888
|
this.githubTokenProviders.clear();
|
|
889
|
+
this.requestAdapter?.cancelPending();
|
|
889
890
|
if (this.messageWriter) {
|
|
890
891
|
this.messageWriter.suppressWriteErrors = true;
|
|
891
892
|
}
|
|
@@ -1188,7 +1189,7 @@ class CopilotClient {
|
|
|
1188
1189
|
tools: config.tools?.map((tool) => ({
|
|
1189
1190
|
name: tool.name,
|
|
1190
1191
|
description: tool.description,
|
|
1191
|
-
parameters: toJsonSchema(tool.parameters),
|
|
1192
|
+
parameters: (0, import_schema.toJsonSchema)(tool.parameters),
|
|
1192
1193
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
1193
1194
|
skipPermission: tool.skipPermission,
|
|
1194
1195
|
defer: tool.defer,
|
|
@@ -1432,7 +1433,7 @@ class CopilotClient {
|
|
|
1432
1433
|
tools: config.tools?.map((tool) => ({
|
|
1433
1434
|
name: tool.name,
|
|
1434
1435
|
description: tool.description,
|
|
1435
|
-
parameters: toJsonSchema(tool.parameters),
|
|
1436
|
+
parameters: (0, import_schema.toJsonSchema)(tool.parameters),
|
|
1436
1437
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
1437
1438
|
skipPermission: tool.skipPermission,
|
|
1438
1439
|
defer: tool.defer,
|
|
@@ -2294,6 +2295,7 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2294
2295
|
}
|
|
2295
2296
|
this.sessions.clear();
|
|
2296
2297
|
this.githubTokenProviders.clear();
|
|
2298
|
+
this.requestAdapter?.cancelPending();
|
|
2297
2299
|
};
|
|
2298
2300
|
this.connection.onClose(markDisconnected);
|
|
2299
2301
|
this.connection.onError(() => {
|
|
@@ -27,6 +27,7 @@ __export(copilotRequestHandler_exports, {
|
|
|
27
27
|
module.exports = __toCommonJS(copilotRequestHandler_exports);
|
|
28
28
|
const sharedTextDecoder = new TextDecoder("utf-8", { fatal: false });
|
|
29
29
|
const sharedTextEncoder = new TextEncoder();
|
|
30
|
+
const HTTP_RESPONSE_READ_AHEAD_BYTES = 32 * 1024;
|
|
30
31
|
const kBridge = /* @__PURE__ */ Symbol("copilotWebSocketResponseBridge");
|
|
31
32
|
const kCompletion = /* @__PURE__ */ Symbol("copilotWebSocketCompletion");
|
|
32
33
|
const kOpen = /* @__PURE__ */ Symbol("copilotWebSocketOpen");
|
|
@@ -265,7 +266,9 @@ function createCopilotRequestAdapter(handler, getServerRpc) {
|
|
|
265
266
|
const message = err instanceof Error ? err.message : String(err);
|
|
266
267
|
await finalize(exchange, 502, message);
|
|
267
268
|
} finally {
|
|
268
|
-
pending.
|
|
269
|
+
if (pending.get(exchange.requestId) === exchange) {
|
|
270
|
+
pending.delete(exchange.requestId);
|
|
271
|
+
}
|
|
269
272
|
}
|
|
270
273
|
}
|
|
271
274
|
return {
|
|
@@ -278,6 +281,13 @@ function createCopilotRequestAdapter(handler, getServerRpc) {
|
|
|
278
281
|
async httpRequestChunk(params) {
|
|
279
282
|
routeChunk(getOrCreate(params.requestId), params);
|
|
280
283
|
return {};
|
|
284
|
+
},
|
|
285
|
+
cancelPending() {
|
|
286
|
+
const exchanges = [...pending.values()];
|
|
287
|
+
pending.clear();
|
|
288
|
+
for (const exchange of exchanges) {
|
|
289
|
+
exchange.pushCancel("RPC connection closed");
|
|
290
|
+
}
|
|
281
291
|
}
|
|
282
292
|
};
|
|
283
293
|
}
|
|
@@ -526,30 +536,156 @@ async function drainAsync(stream) {
|
|
|
526
536
|
return out;
|
|
527
537
|
}
|
|
528
538
|
async function streamResponse(response, exchange) {
|
|
529
|
-
|
|
530
|
-
status: response.status,
|
|
531
|
-
statusText: response.statusText || void 0,
|
|
532
|
-
headers: headersToMultiMap(response.headers)
|
|
533
|
-
});
|
|
534
|
-
const body = response.body;
|
|
535
|
-
if (!body) {
|
|
536
|
-
await exchange.endResponse();
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
const reader = body.getReader();
|
|
539
|
+
const reader = response.body ? new HttpResponseReader(response.body, exchange.signal) : void 0;
|
|
540
540
|
try {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
541
|
+
await exchange.startResponse({
|
|
542
|
+
status: response.status,
|
|
543
|
+
statusText: response.statusText || void 0,
|
|
544
|
+
headers: headersToMultiMap(response.headers)
|
|
545
|
+
});
|
|
546
|
+
if (reader) {
|
|
547
|
+
for (; ; ) {
|
|
548
|
+
const chunk = await reader.nextChunk();
|
|
549
|
+
if (!chunk) {
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
await exchange.writeResponse(chunk);
|
|
548
553
|
}
|
|
549
554
|
}
|
|
550
555
|
await exchange.endResponse();
|
|
551
556
|
} finally {
|
|
552
|
-
reader
|
|
557
|
+
await reader?.dispose();
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
class HttpResponseReader {
|
|
561
|
+
#reader;
|
|
562
|
+
#signal;
|
|
563
|
+
#frames = [];
|
|
564
|
+
#pump;
|
|
565
|
+
#onAbort;
|
|
566
|
+
#queued = 0;
|
|
567
|
+
#done = false;
|
|
568
|
+
#cancelled = false;
|
|
569
|
+
#hasError = false;
|
|
570
|
+
#error;
|
|
571
|
+
#dataWaker;
|
|
572
|
+
#spaceWaker;
|
|
573
|
+
#cancelPromise;
|
|
574
|
+
constructor(body, signal) {
|
|
575
|
+
this.#reader = body.getReader();
|
|
576
|
+
this.#signal = signal;
|
|
577
|
+
this.#onAbort = () => {
|
|
578
|
+
void this.cancel(signal.reason);
|
|
579
|
+
};
|
|
580
|
+
signal.addEventListener("abort", this.#onAbort, { once: true });
|
|
581
|
+
this.#pump = this.#pumpSource();
|
|
582
|
+
if (signal.aborted) {
|
|
583
|
+
this.#onAbort();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
async nextChunk() {
|
|
587
|
+
while (this.#frames.length === 0 && !this.#done && !this.#hasError) {
|
|
588
|
+
await new Promise((resolve) => {
|
|
589
|
+
this.#dataWaker = resolve;
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
if (this.#frames.length > 0) {
|
|
593
|
+
const chunk = this.#takeQueued();
|
|
594
|
+
this.#wakeSpace();
|
|
595
|
+
return chunk;
|
|
596
|
+
}
|
|
597
|
+
if (this.#hasError) {
|
|
598
|
+
this.#hasError = false;
|
|
599
|
+
throw this.#error;
|
|
600
|
+
}
|
|
601
|
+
return void 0;
|
|
602
|
+
}
|
|
603
|
+
#takeQueued() {
|
|
604
|
+
const frames = this.#frames;
|
|
605
|
+
const total = this.#queued;
|
|
606
|
+
this.#queued = 0;
|
|
607
|
+
if (frames.length === 1) {
|
|
608
|
+
return frames.pop();
|
|
609
|
+
}
|
|
610
|
+
const chunk = new Uint8Array(total);
|
|
611
|
+
let offset = 0;
|
|
612
|
+
for (const frame of frames) {
|
|
613
|
+
chunk.set(frame, offset);
|
|
614
|
+
offset += frame.byteLength;
|
|
615
|
+
}
|
|
616
|
+
frames.length = 0;
|
|
617
|
+
return chunk;
|
|
618
|
+
}
|
|
619
|
+
async dispose() {
|
|
620
|
+
this.#signal.removeEventListener("abort", this.#onAbort);
|
|
621
|
+
if (!this.#done) {
|
|
622
|
+
await this.cancel();
|
|
623
|
+
} else if (this.#cancelPromise) {
|
|
624
|
+
await this.#cancelPromise;
|
|
625
|
+
}
|
|
626
|
+
await this.#pump;
|
|
627
|
+
}
|
|
628
|
+
cancel(reason) {
|
|
629
|
+
if (this.#done) {
|
|
630
|
+
return Promise.resolve();
|
|
631
|
+
}
|
|
632
|
+
if (!this.#cancelPromise) {
|
|
633
|
+
this.#cancelled = true;
|
|
634
|
+
this.#done = true;
|
|
635
|
+
this.#hasError = true;
|
|
636
|
+
this.#error = reason instanceof Error ? reason : new Error("HTTP response body cancelled.");
|
|
637
|
+
this.#frames.length = 0;
|
|
638
|
+
this.#queued = 0;
|
|
639
|
+
this.#wakeData();
|
|
640
|
+
this.#wakeSpace();
|
|
641
|
+
this.#cancelPromise = this.#reader.cancel(reason).catch(() => void 0);
|
|
642
|
+
}
|
|
643
|
+
return this.#cancelPromise;
|
|
644
|
+
}
|
|
645
|
+
async #pumpSource() {
|
|
646
|
+
try {
|
|
647
|
+
while (!this.#cancelled) {
|
|
648
|
+
while (this.#queued >= HTTP_RESPONSE_READ_AHEAD_BYTES && !this.#cancelled) {
|
|
649
|
+
await new Promise((resolve) => {
|
|
650
|
+
this.#spaceWaker = resolve;
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
if (this.#cancelled) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const { value, done } = await this.#reader.read();
|
|
657
|
+
if (done) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (value.byteLength > 0) {
|
|
661
|
+
this.#frames.push(value);
|
|
662
|
+
this.#queued += value.byteLength;
|
|
663
|
+
this.#wakeData();
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
667
|
+
}
|
|
668
|
+
} catch (error) {
|
|
669
|
+
if (!this.#cancelled) {
|
|
670
|
+
this.#hasError = true;
|
|
671
|
+
this.#error = error;
|
|
672
|
+
}
|
|
673
|
+
} finally {
|
|
674
|
+
this.#done = true;
|
|
675
|
+
this.#wakeData();
|
|
676
|
+
this.#wakeSpace();
|
|
677
|
+
this.#reader.releaseLock();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
#wakeData() {
|
|
681
|
+
const waker = this.#dataWaker;
|
|
682
|
+
this.#dataWaker = void 0;
|
|
683
|
+
waker?.();
|
|
684
|
+
}
|
|
685
|
+
#wakeSpace() {
|
|
686
|
+
const waker = this.#spaceWaker;
|
|
687
|
+
this.#spaceWaker = void 0;
|
|
688
|
+
waker?.();
|
|
553
689
|
}
|
|
554
690
|
}
|
|
555
691
|
function headersToMultiMap(headers) {
|