@anthropic-ai/sdk 0.5.1 → 0.5.3
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/LICENSE +8 -0
- package/README.md +305 -0
- package/core.d.ts.map +1 -1
- package/core.js +17 -6
- package/core.js.map +1 -1
- package/core.mjs +17 -6
- package/core.mjs.map +1 -1
- package/package.json +1 -1
- package/src/_shims/fileFromPath.node.ts +1 -1
- package/src/_shims/fileFromPath.ts +1 -1
- package/src/_shims/getMultipartRequestOptions.node.ts +1 -1
- package/src/_shims/getMultipartRequestOptions.ts +1 -1
- package/src/core.ts +13 -5
- package/src/index.ts +1 -1
- package/src/resources/completions.ts +4 -4
- package/src/streaming.ts +138 -77
- package/src/uploads.ts +10 -9
- package/src/version.ts +1 -1
- package/streaming.d.ts.map +1 -1
- package/streaming.js +104 -56
- package/streaming.js.map +1 -1
- package/streaming.mjs +104 -56
- package/streaming.mjs.map +1 -1
- package/uploads.d.ts +1 -1
- package/uploads.d.ts.map +1 -1
- package/uploads.js +2 -1
- package/uploads.js.map +1 -1
- package/uploads.mjs +2 -1
- package/uploads.mjs.map +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.js.map +1 -1
- package/version.mjs +1 -1
- package/version.mjs.map +1 -1
package/src/core.ts
CHANGED
|
@@ -2,15 +2,15 @@ import * as qs from 'qs';
|
|
|
2
2
|
import { VERSION } from './version';
|
|
3
3
|
import { Stream } from './streaming';
|
|
4
4
|
import { APIError, APIConnectionError, APIConnectionTimeoutError } from './error';
|
|
5
|
-
import type { Readable } from '
|
|
6
|
-
import { getDefaultAgent, type Agent } from '
|
|
5
|
+
import type { Readable } from './_shims/node-readable';
|
|
6
|
+
import { getDefaultAgent, type Agent } from './_shims/agent';
|
|
7
7
|
import {
|
|
8
8
|
fetch,
|
|
9
9
|
isPolyfilled as fetchIsPolyfilled,
|
|
10
10
|
type RequestInfo,
|
|
11
11
|
type RequestInit,
|
|
12
12
|
type Response,
|
|
13
|
-
} from '
|
|
13
|
+
} from './_shims/fetch.js';
|
|
14
14
|
import { isMultipartBody } from './uploads';
|
|
15
15
|
export {
|
|
16
16
|
maybeMultipartFormRequestOptions,
|
|
@@ -132,9 +132,17 @@ export abstract class APIClient {
|
|
|
132
132
|
const contentLength = typeof body === 'string' ? body.length.toString() : null;
|
|
133
133
|
|
|
134
134
|
const url = this.buildURL(path!, query);
|
|
135
|
-
|
|
135
|
+
if ('timeout' in options) validatePositiveInteger('timeout', options.timeout);
|
|
136
136
|
const timeout = options.timeout ?? this.timeout;
|
|
137
|
-
|
|
137
|
+
const httpAgent = options.httpAgent ?? this.httpAgent ?? getDefaultAgent(url);
|
|
138
|
+
const minAgentTimeout = timeout + 1000;
|
|
139
|
+
if ((httpAgent as any)?.options && minAgentTimeout > ((httpAgent as any).options.timeout ?? 0)) {
|
|
140
|
+
// Allow any given request to bump our agent active socket timeout.
|
|
141
|
+
// This may seem strange, but leaking active sockets should be rare and not particularly problematic,
|
|
142
|
+
// and without mutating agent we would need to create more of them.
|
|
143
|
+
// This tradeoff optimizes for performance.
|
|
144
|
+
(httpAgent as any).options.timeout = minAgentTimeout;
|
|
145
|
+
}
|
|
138
146
|
|
|
139
147
|
if (this.idempotencyHeader && method !== 'get') {
|
|
140
148
|
if (!options.idempotencyKey) options.idempotencyKey = this.defaultIdempotencyKey();
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import * as qs from 'qs';
|
|
|
4
4
|
import * as Core from './core';
|
|
5
5
|
import * as API from './resources/index';
|
|
6
6
|
import * as Errors from './error';
|
|
7
|
-
import type { Agent } from '
|
|
7
|
+
import type { Agent } from './_shims/agent';
|
|
8
8
|
import * as Uploads from './uploads';
|
|
9
9
|
|
|
10
10
|
type Config = {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// File generated from our OpenAPI spec by Stainless.
|
|
2
2
|
|
|
3
|
-
import * as Core from '
|
|
4
|
-
import { APIResource } from '
|
|
5
|
-
import * as API from '
|
|
6
|
-
import { Stream } from '
|
|
3
|
+
import * as Core from '../core';
|
|
4
|
+
import { APIResource } from '../resource';
|
|
5
|
+
import * as API from '.';
|
|
6
|
+
import { Stream } from '../streaming';
|
|
7
7
|
|
|
8
8
|
export class Completions extends APIResource {
|
|
9
9
|
/**
|
package/src/streaming.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import type { Response } from '
|
|
1
|
+
import type { Response } from './_shims/fetch.js';
|
|
2
|
+
|
|
2
3
|
import { APIResponse, Headers, createResponseHeaders } from './core';
|
|
3
|
-
|
|
4
|
-
import {
|
|
4
|
+
|
|
5
|
+
import { safeJSON } from './core';
|
|
6
|
+
import { APIError } from './error';
|
|
7
|
+
|
|
8
|
+
type Bytes = string | ArrayBuffer | Uint8Array | Buffer | null | undefined;
|
|
5
9
|
|
|
6
10
|
type ServerSentEvent = {
|
|
7
11
|
event: string | null;
|
|
@@ -9,61 +13,6 @@ type ServerSentEvent = {
|
|
|
9
13
|
raw: string[];
|
|
10
14
|
};
|
|
11
15
|
|
|
12
|
-
class SSEDecoder {
|
|
13
|
-
private data: string[];
|
|
14
|
-
private event: string | null;
|
|
15
|
-
private chunks: string[];
|
|
16
|
-
|
|
17
|
-
constructor() {
|
|
18
|
-
this.event = null;
|
|
19
|
-
this.data = [];
|
|
20
|
-
this.chunks = [];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
decode(line: string) {
|
|
24
|
-
if (line.endsWith('\r')) {
|
|
25
|
-
line = line.substring(0, line.length - 1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!line) {
|
|
29
|
-
// empty line and we didn't previously encounter any messages
|
|
30
|
-
if (!this.event && !this.data.length) return null;
|
|
31
|
-
|
|
32
|
-
const sse: ServerSentEvent = {
|
|
33
|
-
event: this.event,
|
|
34
|
-
data: this.data.join('\n'),
|
|
35
|
-
raw: this.chunks,
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
this.event = null;
|
|
39
|
-
this.data = [];
|
|
40
|
-
this.chunks = [];
|
|
41
|
-
|
|
42
|
-
return sse;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
this.chunks.push(line);
|
|
46
|
-
|
|
47
|
-
if (line.startsWith(':')) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
let [fieldname, _, value] = partition(line, ':');
|
|
52
|
-
|
|
53
|
-
if (value.startsWith(' ')) {
|
|
54
|
-
value = value.substring(1);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (fieldname === 'event') {
|
|
58
|
-
this.event = value;
|
|
59
|
-
} else if (fieldname === 'data') {
|
|
60
|
-
this.data.push(value);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
16
|
export class Stream<Item> implements AsyncIterable<Item>, APIResponse<Stream<Item>> {
|
|
68
17
|
/** @deprecated - please use the async iterator instead. We plan to add additional helper methods shortly. */
|
|
69
18
|
response: Response;
|
|
@@ -85,21 +34,11 @@ export class Stream<Item> implements AsyncIterable<Item>, APIResponse<Stream<Ite
|
|
|
85
34
|
this.controller.abort();
|
|
86
35
|
throw new Error(`Attempted to iterate over a response with no body`);
|
|
87
36
|
}
|
|
88
|
-
|
|
89
37
|
const lineDecoder = new LineDecoder();
|
|
90
38
|
|
|
91
|
-
|
|
92
|
-
for await (const chunk of
|
|
93
|
-
|
|
94
|
-
if (chunk instanceof Buffer) {
|
|
95
|
-
text = chunk.toString();
|
|
96
|
-
} else if ((chunk as any) instanceof Uint8Array) {
|
|
97
|
-
text = Buffer.from(chunk).toString();
|
|
98
|
-
} else {
|
|
99
|
-
text = chunk;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
for (const line of lineDecoder.decode(text)) {
|
|
39
|
+
const iter = readableStreamAsyncIterable<Bytes>(this.response.body);
|
|
40
|
+
for await (const chunk of iter) {
|
|
41
|
+
for (const line of lineDecoder.decode(chunk)) {
|
|
103
42
|
const sse = this.decoder.decode(line);
|
|
104
43
|
if (sse) yield sse;
|
|
105
44
|
}
|
|
@@ -147,7 +86,60 @@ export class Stream<Item> implements AsyncIterable<Item>, APIResponse<Stream<Ite
|
|
|
147
86
|
}
|
|
148
87
|
}
|
|
149
88
|
|
|
150
|
-
|
|
89
|
+
class SSEDecoder {
|
|
90
|
+
private data: string[];
|
|
91
|
+
private event: string | null;
|
|
92
|
+
private chunks: string[];
|
|
93
|
+
|
|
94
|
+
constructor() {
|
|
95
|
+
this.event = null;
|
|
96
|
+
this.data = [];
|
|
97
|
+
this.chunks = [];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
decode(line: string) {
|
|
101
|
+
if (line.endsWith('\r')) {
|
|
102
|
+
line = line.substring(0, line.length - 1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!line) {
|
|
106
|
+
// empty line and we didn't previously encounter any messages
|
|
107
|
+
if (!this.event && !this.data.length) return null;
|
|
108
|
+
|
|
109
|
+
const sse: ServerSentEvent = {
|
|
110
|
+
event: this.event,
|
|
111
|
+
data: this.data.join('\n'),
|
|
112
|
+
raw: this.chunks,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
this.event = null;
|
|
116
|
+
this.data = [];
|
|
117
|
+
this.chunks = [];
|
|
118
|
+
|
|
119
|
+
return sse;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.chunks.push(line);
|
|
123
|
+
|
|
124
|
+
if (line.startsWith(':')) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let [fieldname, _, value] = partition(line, ':');
|
|
129
|
+
|
|
130
|
+
if (value.startsWith(' ')) {
|
|
131
|
+
value = value.substring(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (fieldname === 'event') {
|
|
135
|
+
this.event = value;
|
|
136
|
+
} else if (fieldname === 'data') {
|
|
137
|
+
this.data.push(value);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
151
143
|
|
|
152
144
|
/**
|
|
153
145
|
* A re-implementation of httpx's `LineDecoder` in Python that handles incrementally
|
|
@@ -156,15 +148,22 @@ const NEWLINE_CHARS = '\n\r\x0b\x0c\x1c\x1d\x1e\x85\u2028\u2029';
|
|
|
156
148
|
* https://github.com/encode/httpx/blob/920333ea98118e9cf617f246905d7b202510941c/httpx/_decoders.py#L258
|
|
157
149
|
*/
|
|
158
150
|
class LineDecoder {
|
|
151
|
+
// prettier-ignore
|
|
152
|
+
static NEWLINE_CHARS = new Set(['\n', '\r', '\x0b', '\x0c', '\x1c', '\x1d', '\x1e', '\x85', '\u2028', '\u2029']);
|
|
153
|
+
static NEWLINE_REGEXP = /\r\n|[\n\r\x0b\x0c\x1c\x1d\x1e\x85\u2028\u2029]/g;
|
|
154
|
+
|
|
159
155
|
buffer: string[];
|
|
160
156
|
trailingCR: boolean;
|
|
157
|
+
textDecoder: any; // TextDecoder found in browsers; not typed to avoid pulling in either "dom" or "node" types.
|
|
161
158
|
|
|
162
159
|
constructor() {
|
|
163
160
|
this.buffer = [];
|
|
164
161
|
this.trailingCR = false;
|
|
165
162
|
}
|
|
166
163
|
|
|
167
|
-
decode(
|
|
164
|
+
decode(chunk: Bytes): string[] {
|
|
165
|
+
let text = this.decodeText(chunk);
|
|
166
|
+
|
|
168
167
|
if (this.trailingCR) {
|
|
169
168
|
text = '\r' + text;
|
|
170
169
|
this.trailingCR = false;
|
|
@@ -178,10 +177,10 @@ class LineDecoder {
|
|
|
178
177
|
return [];
|
|
179
178
|
}
|
|
180
179
|
|
|
181
|
-
const
|
|
182
|
-
let lines = text.split(
|
|
180
|
+
const trailingNewline = LineDecoder.NEWLINE_CHARS.has(text[text.length - 1] || '');
|
|
181
|
+
let lines = text.split(LineDecoder.NEWLINE_REGEXP);
|
|
183
182
|
|
|
184
|
-
if (lines.length === 1 && !
|
|
183
|
+
if (lines.length === 1 && !trailingNewline) {
|
|
185
184
|
this.buffer.push(lines[0]!);
|
|
186
185
|
return [];
|
|
187
186
|
}
|
|
@@ -191,13 +190,50 @@ class LineDecoder {
|
|
|
191
190
|
this.buffer = [];
|
|
192
191
|
}
|
|
193
192
|
|
|
194
|
-
if (!
|
|
193
|
+
if (!trailingNewline) {
|
|
195
194
|
this.buffer = [lines.pop() || ''];
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
return lines;
|
|
199
198
|
}
|
|
200
199
|
|
|
200
|
+
decodeText(bytes: Bytes): string {
|
|
201
|
+
if (bytes == null) return '';
|
|
202
|
+
if (typeof bytes === 'string') return bytes;
|
|
203
|
+
|
|
204
|
+
// Node:
|
|
205
|
+
if (typeof Buffer !== 'undefined') {
|
|
206
|
+
if (bytes instanceof Buffer) {
|
|
207
|
+
return bytes.toString();
|
|
208
|
+
}
|
|
209
|
+
if (bytes instanceof Uint8Array) {
|
|
210
|
+
return Buffer.from(bytes).toString();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
throw new Error(
|
|
214
|
+
`Unexpected: received non-Uint8Array (${bytes.constructor.name}) stream chunk in an environment with a global "Buffer" defined, which this library assumes to be Node. Please report this error.`,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Browser
|
|
219
|
+
if (typeof TextDecoder !== 'undefined') {
|
|
220
|
+
if (bytes instanceof Uint8Array || bytes instanceof ArrayBuffer) {
|
|
221
|
+
this.textDecoder ??= new TextDecoder('utf8');
|
|
222
|
+
return this.textDecoder.decode(bytes);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Unexpected: received non-Uint8Array/ArrayBuffer (${
|
|
227
|
+
(bytes as any).constructor.name
|
|
228
|
+
}) in a web platform. Please report this error.`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
throw new Error(
|
|
233
|
+
`Unexpected: neither Buffer nor TextDecoder are available as globals. Please report this error.`,
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
201
237
|
flush(): string[] {
|
|
202
238
|
if (!this.buffer.length && !this.trailingCR) {
|
|
203
239
|
return [];
|
|
@@ -218,3 +254,28 @@ function partition(str: string, delimiter: string): [string, string, string] {
|
|
|
218
254
|
|
|
219
255
|
return [str, '', ''];
|
|
220
256
|
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Most browsers don't yet have async iterable support for ReadableStream,
|
|
260
|
+
* and Node has a very different way of reading bytes from its "ReadableStream".
|
|
261
|
+
*
|
|
262
|
+
* This polyfill was pulled from https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1624185965
|
|
263
|
+
*/
|
|
264
|
+
function readableStreamAsyncIterable<T>(stream: any): AsyncIterableIterator<T> {
|
|
265
|
+
if (stream[Symbol.asyncIterator]) return stream;
|
|
266
|
+
|
|
267
|
+
const reader = stream.getReader();
|
|
268
|
+
return {
|
|
269
|
+
next() {
|
|
270
|
+
return reader.read();
|
|
271
|
+
},
|
|
272
|
+
async return() {
|
|
273
|
+
reader.cancel();
|
|
274
|
+
reader.releaseLock();
|
|
275
|
+
return { done: true, value: undefined };
|
|
276
|
+
},
|
|
277
|
+
[Symbol.asyncIterator]() {
|
|
278
|
+
return this;
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
}
|
package/src/uploads.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type RequestOptions } from './core';
|
|
2
|
-
import { type Readable } from '
|
|
3
|
-
import { type BodyInit } from '
|
|
4
|
-
import { FormData, File, type FilePropertyBag } from '
|
|
5
|
-
import { getMultipartRequestOptions } from '
|
|
6
|
-
import { fileFromPath } from '
|
|
7
|
-
import { type FsReadStream, isFsReadStream } from '
|
|
2
|
+
import { type Readable } from './_shims/node-readable';
|
|
3
|
+
import { type BodyInit } from './_shims/fetch.js';
|
|
4
|
+
import { FormData, File, type FilePropertyBag } from './_shims/formdata.js';
|
|
5
|
+
import { getMultipartRequestOptions } from './_shims/getMultipartRequestOptions';
|
|
6
|
+
import { fileFromPath } from './_shims/fileFromPath';
|
|
7
|
+
import { type FsReadStream, isFsReadStream } from './_shims/node-readable';
|
|
8
8
|
|
|
9
9
|
export { fileFromPath };
|
|
10
10
|
|
|
@@ -96,7 +96,7 @@ export type ToFileInput = Uploadable | Exclude<BlobPart, string> | AsyncIterable
|
|
|
96
96
|
* @returns a {@link File} with the given properties
|
|
97
97
|
*/
|
|
98
98
|
export async function toFile(
|
|
99
|
-
value: ToFileInput
|
|
99
|
+
value: ToFileInput | PromiseLike<ToFileInput>,
|
|
100
100
|
name?: string | null | undefined,
|
|
101
101
|
options: FilePropertyBag | undefined = {},
|
|
102
102
|
): Promise<FileLike> {
|
|
@@ -121,8 +121,9 @@ export async function toFile(
|
|
|
121
121
|
return new File(bits, name, options);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
async function getBytes(value: ToFileInput): Promise<Array<BlobPart>> {
|
|
125
|
-
|
|
124
|
+
async function getBytes(value: ToFileInput | PromiseLike<ToFileInput>): Promise<Array<BlobPart>> {
|
|
125
|
+
// resolve input promise or promiselike object
|
|
126
|
+
value = await value;
|
|
126
127
|
|
|
127
128
|
let parts: Array<BlobPart> = [];
|
|
128
129
|
if (
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.5.
|
|
1
|
+
export const VERSION = '0.5.3'; // x-release-please-version
|
package/streaming.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["src/streaming.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["src/streaming.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE/D,OAAO,EAAE,WAAW,EAAE,OAAO,EAAyB,MAAM,QAAQ,CAAC;AAarE,qBAAa,MAAM,CAAC,IAAI,CAAE,YAAW,aAAa,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjF,6GAA6G;IAC7G,QAAQ,EAAE,QAAQ,CAAC;IACnB,+FAA+F;IAC/F,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,eAAe,CAAC;IAE5B,OAAO,CAAC,OAAO,CAAa;gBAEhB,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe;YAO5C,YAAY;IAqBpB,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,CAAC;CAkCrE"}
|
package/streaming.js
CHANGED
|
@@ -4,45 +4,6 @@ exports.Stream = void 0;
|
|
|
4
4
|
const core_1 = require('./core.js');
|
|
5
5
|
const core_2 = require('@anthropic-ai/sdk/core');
|
|
6
6
|
const error_1 = require('@anthropic-ai/sdk/error');
|
|
7
|
-
class SSEDecoder {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.event = null;
|
|
10
|
-
this.data = [];
|
|
11
|
-
this.chunks = [];
|
|
12
|
-
}
|
|
13
|
-
decode(line) {
|
|
14
|
-
if (line.endsWith('\r')) {
|
|
15
|
-
line = line.substring(0, line.length - 1);
|
|
16
|
-
}
|
|
17
|
-
if (!line) {
|
|
18
|
-
// empty line and we didn't previously encounter any messages
|
|
19
|
-
if (!this.event && !this.data.length) return null;
|
|
20
|
-
const sse = {
|
|
21
|
-
event: this.event,
|
|
22
|
-
data: this.data.join('\n'),
|
|
23
|
-
raw: this.chunks,
|
|
24
|
-
};
|
|
25
|
-
this.event = null;
|
|
26
|
-
this.data = [];
|
|
27
|
-
this.chunks = [];
|
|
28
|
-
return sse;
|
|
29
|
-
}
|
|
30
|
-
this.chunks.push(line);
|
|
31
|
-
if (line.startsWith(':')) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
let [fieldname, _, value] = partition(line, ':');
|
|
35
|
-
if (value.startsWith(' ')) {
|
|
36
|
-
value = value.substring(1);
|
|
37
|
-
}
|
|
38
|
-
if (fieldname === 'event') {
|
|
39
|
-
this.event = value;
|
|
40
|
-
} else if (fieldname === 'data') {
|
|
41
|
-
this.data.push(value);
|
|
42
|
-
}
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
7
|
class Stream {
|
|
47
8
|
constructor(response, controller) {
|
|
48
9
|
this.response = response;
|
|
@@ -56,17 +17,9 @@ class Stream {
|
|
|
56
17
|
throw new Error(`Attempted to iterate over a response with no body`);
|
|
57
18
|
}
|
|
58
19
|
const lineDecoder = new LineDecoder();
|
|
59
|
-
|
|
60
|
-
for await (const chunk of
|
|
61
|
-
|
|
62
|
-
if (chunk instanceof Buffer) {
|
|
63
|
-
text = chunk.toString();
|
|
64
|
-
} else if (chunk instanceof Uint8Array) {
|
|
65
|
-
text = Buffer.from(chunk).toString();
|
|
66
|
-
} else {
|
|
67
|
-
text = chunk;
|
|
68
|
-
}
|
|
69
|
-
for (const line of lineDecoder.decode(text)) {
|
|
20
|
+
const iter = readableStreamAsyncIterable(this.response.body);
|
|
21
|
+
for await (const chunk of iter) {
|
|
22
|
+
for (const line of lineDecoder.decode(chunk)) {
|
|
70
23
|
const sse = this.decoder.decode(line);
|
|
71
24
|
if (sse) yield sse;
|
|
72
25
|
}
|
|
@@ -109,7 +62,45 @@ class Stream {
|
|
|
109
62
|
}
|
|
110
63
|
}
|
|
111
64
|
exports.Stream = Stream;
|
|
112
|
-
|
|
65
|
+
class SSEDecoder {
|
|
66
|
+
constructor() {
|
|
67
|
+
this.event = null;
|
|
68
|
+
this.data = [];
|
|
69
|
+
this.chunks = [];
|
|
70
|
+
}
|
|
71
|
+
decode(line) {
|
|
72
|
+
if (line.endsWith('\r')) {
|
|
73
|
+
line = line.substring(0, line.length - 1);
|
|
74
|
+
}
|
|
75
|
+
if (!line) {
|
|
76
|
+
// empty line and we didn't previously encounter any messages
|
|
77
|
+
if (!this.event && !this.data.length) return null;
|
|
78
|
+
const sse = {
|
|
79
|
+
event: this.event,
|
|
80
|
+
data: this.data.join('\n'),
|
|
81
|
+
raw: this.chunks,
|
|
82
|
+
};
|
|
83
|
+
this.event = null;
|
|
84
|
+
this.data = [];
|
|
85
|
+
this.chunks = [];
|
|
86
|
+
return sse;
|
|
87
|
+
}
|
|
88
|
+
this.chunks.push(line);
|
|
89
|
+
if (line.startsWith(':')) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
let [fieldname, _, value] = partition(line, ':');
|
|
93
|
+
if (value.startsWith(' ')) {
|
|
94
|
+
value = value.substring(1);
|
|
95
|
+
}
|
|
96
|
+
if (fieldname === 'event') {
|
|
97
|
+
this.event = value;
|
|
98
|
+
} else if (fieldname === 'data') {
|
|
99
|
+
this.data.push(value);
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
113
104
|
/**
|
|
114
105
|
* A re-implementation of httpx's `LineDecoder` in Python that handles incrementally
|
|
115
106
|
* reading lines from text.
|
|
@@ -121,7 +112,8 @@ class LineDecoder {
|
|
|
121
112
|
this.buffer = [];
|
|
122
113
|
this.trailingCR = false;
|
|
123
114
|
}
|
|
124
|
-
decode(
|
|
115
|
+
decode(chunk) {
|
|
116
|
+
let text = this.decodeText(chunk);
|
|
125
117
|
if (this.trailingCR) {
|
|
126
118
|
text = '\r' + text;
|
|
127
119
|
this.trailingCR = false;
|
|
@@ -133,9 +125,9 @@ class LineDecoder {
|
|
|
133
125
|
if (!text) {
|
|
134
126
|
return [];
|
|
135
127
|
}
|
|
136
|
-
const
|
|
137
|
-
let lines = text.split(
|
|
138
|
-
if (lines.length === 1 && !
|
|
128
|
+
const trailingNewline = LineDecoder.NEWLINE_CHARS.has(text[text.length - 1] || '');
|
|
129
|
+
let lines = text.split(LineDecoder.NEWLINE_REGEXP);
|
|
130
|
+
if (lines.length === 1 && !trailingNewline) {
|
|
139
131
|
this.buffer.push(lines[0]);
|
|
140
132
|
return [];
|
|
141
133
|
}
|
|
@@ -143,11 +135,41 @@ class LineDecoder {
|
|
|
143
135
|
lines = [this.buffer.join('') + lines[0], ...lines.slice(1)];
|
|
144
136
|
this.buffer = [];
|
|
145
137
|
}
|
|
146
|
-
if (!
|
|
138
|
+
if (!trailingNewline) {
|
|
147
139
|
this.buffer = [lines.pop() || ''];
|
|
148
140
|
}
|
|
149
141
|
return lines;
|
|
150
142
|
}
|
|
143
|
+
decodeText(bytes) {
|
|
144
|
+
var _a;
|
|
145
|
+
if (bytes == null) return '';
|
|
146
|
+
if (typeof bytes === 'string') return bytes;
|
|
147
|
+
// Node:
|
|
148
|
+
if (typeof Buffer !== 'undefined') {
|
|
149
|
+
if (bytes instanceof Buffer) {
|
|
150
|
+
return bytes.toString();
|
|
151
|
+
}
|
|
152
|
+
if (bytes instanceof Uint8Array) {
|
|
153
|
+
return Buffer.from(bytes).toString();
|
|
154
|
+
}
|
|
155
|
+
throw new Error(
|
|
156
|
+
`Unexpected: received non-Uint8Array (${bytes.constructor.name}) stream chunk in an environment with a global "Buffer" defined, which this library assumes to be Node. Please report this error.`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
// Browser
|
|
160
|
+
if (typeof TextDecoder !== 'undefined') {
|
|
161
|
+
if (bytes instanceof Uint8Array || bytes instanceof ArrayBuffer) {
|
|
162
|
+
(_a = this.textDecoder) !== null && _a !== void 0 ? _a : (this.textDecoder = new TextDecoder('utf8'));
|
|
163
|
+
return this.textDecoder.decode(bytes);
|
|
164
|
+
}
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Unexpected: received non-Uint8Array/ArrayBuffer (${bytes.constructor.name}) in a web platform. Please report this error.`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
throw new Error(
|
|
170
|
+
`Unexpected: neither Buffer nor TextDecoder are available as globals. Please report this error.`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
151
173
|
flush() {
|
|
152
174
|
if (!this.buffer.length && !this.trailingCR) {
|
|
153
175
|
return [];
|
|
@@ -158,6 +180,9 @@ class LineDecoder {
|
|
|
158
180
|
return lines;
|
|
159
181
|
}
|
|
160
182
|
}
|
|
183
|
+
// prettier-ignore
|
|
184
|
+
LineDecoder.NEWLINE_CHARS = new Set(['\n', '\r', '\x0b', '\x0c', '\x1c', '\x1d', '\x1e', '\x85', '\u2028', '\u2029']);
|
|
185
|
+
LineDecoder.NEWLINE_REGEXP = /\r\n|[\n\r\x0b\x0c\x1c\x1d\x1e\x85\u2028\u2029]/g;
|
|
161
186
|
function partition(str, delimiter) {
|
|
162
187
|
const index = str.indexOf(delimiter);
|
|
163
188
|
if (index !== -1) {
|
|
@@ -165,4 +190,27 @@ function partition(str, delimiter) {
|
|
|
165
190
|
}
|
|
166
191
|
return [str, '', ''];
|
|
167
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Most browsers don't yet have async iterable support for ReadableStream,
|
|
195
|
+
* and Node has a very different way of reading bytes from its "ReadableStream".
|
|
196
|
+
*
|
|
197
|
+
* This polyfill was pulled from https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1624185965
|
|
198
|
+
*/
|
|
199
|
+
function readableStreamAsyncIterable(stream) {
|
|
200
|
+
if (stream[Symbol.asyncIterator]) return stream;
|
|
201
|
+
const reader = stream.getReader();
|
|
202
|
+
return {
|
|
203
|
+
next() {
|
|
204
|
+
return reader.read();
|
|
205
|
+
},
|
|
206
|
+
async return() {
|
|
207
|
+
reader.cancel();
|
|
208
|
+
reader.releaseLock();
|
|
209
|
+
return { done: true, value: undefined };
|
|
210
|
+
},
|
|
211
|
+
[Symbol.asyncIterator]() {
|
|
212
|
+
return this;
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
168
216
|
//# sourceMappingURL=streaming.js.map
|
package/streaming.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming.js","sourceRoot":"","sources":["src/streaming.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"streaming.js","sourceRoot":"","sources":["src/streaming.ts"],"names":[],"mappings":";;;AAEA,oCAAqE;AAErE,iDAAkD;AAClD,mDAAmD;AAUnD,MAAa,MAAM;IASjB,YAAY,QAAkB,EAAE,UAA2B;QACzD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,UAAU,EAAE,CAAC;QAChC,IAAI,CAAC,eAAe,GAAG,IAAA,4BAAqB,EAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjE,CAAC;IAEO,KAAK,CAAC,CAAC,YAAY;QACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;YACvB,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;SACtE;QACD,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;QAEtC,MAAM,IAAI,GAAG,2BAA2B,CAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpE,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI,EAAE;YAC9B,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;gBAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACtC,IAAI,GAAG;oBAAE,MAAM,GAAG,CAAC;aACpB;SACF;QAED,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,EAAE;YACtC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,GAAG;gBAAE,MAAM,GAAG,CAAC;SACpB;IACH,CAAC;IAED,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;QAC3B,IAAI;YACF,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,IAAI,CAAC,YAAY,EAAE,EAAE;gBAC3C,IAAI,GAAG,CAAC,KAAK,KAAK,YAAY,EAAE;oBAC9B,IAAI;wBACF,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;qBAC5B;oBAAC,OAAO,CAAC,EAAE;wBACV,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;wBAC9D,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;wBACtC,MAAM,CAAC,CAAC;qBACT;iBACF;gBAED,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,EAAE;oBACxB,SAAS;iBACV;gBAED,IAAI,GAAG,CAAC,KAAK,KAAK,OAAO,EAAE;oBACzB,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC;oBACzB,MAAM,OAAO,GAAG,IAAA,eAAQ,EAAC,OAAO,CAAC,CAAC;oBAClC,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;oBAEjD,MAAM,gBAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;iBAC/E;aACF;SACF;QAAC,OAAO,CAAC,EAAE;YACV,kFAAkF;YAClF,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY;gBAAE,OAAO;YAC1D,MAAM,CAAC,CAAC;SACT;gBAAS;YACR,mDAAmD;YACnD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;SACzB;IACH,CAAC;CACF;AAvED,wBAuEC;AAED,MAAM,UAAU;IAKd;QACE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IACnB,CAAC;IAED,MAAM,CAAC,IAAY;QACjB,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;YACvB,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;SAC3C;QAED,IAAI,CAAC,IAAI,EAAE;YACT,6DAA6D;YAC7D,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC;YAElD,MAAM,GAAG,GAAoB;gBAC3B,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC1B,GAAG,EAAE,IAAI,CAAC,MAAM;aACjB,CAAC;YAEF,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;YAEjB,OAAO,GAAG,CAAC;SACZ;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEvB,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;YACxB,OAAO,IAAI,CAAC;SACb;QAED,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAEjD,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;YACzB,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;SAC5B;QAED,IAAI,SAAS,KAAK,OAAO,EAAE;YACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;SACpB;aAAM,IAAI,SAAS,KAAK,MAAM,EAAE;YAC/B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;SACvB;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,WAAW;IASf;QACE,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED,MAAM,CAAC,KAAY;QACjB,IAAI,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAElC,IAAI,IAAI,CAAC,UAAU,EAAE;YACnB,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;SACzB;QACD,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;YACvB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;SAC1B;QAED,IAAI,CAAC,IAAI,EAAE;YACT,OAAO,EAAE,CAAC;SACX;QAED,MAAM,eAAe,GAAG,WAAW,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACnF,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QAEnD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE;YAC1C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;YAC5B,OAAO,EAAE,CAAC;SACX;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;YAC1B,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7D,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;SAClB;QAED,IAAI,CAAC,eAAe,EAAE;YACpB,IAAI,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;SACnC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,UAAU,CAAC,KAAY;;QACrB,IAAI,KAAK,IAAI,IAAI;YAAE,OAAO,EAAE,CAAC;QAC7B,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAE5C,QAAQ;QACR,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;YACjC,IAAI,KAAK,YAAY,MAAM,EAAE;gBAC3B,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;aACzB;YACD,IAAI,KAAK,YAAY,UAAU,EAAE;gBAC/B,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;aACtC;YAED,MAAM,IAAI,KAAK,CACb,wCAAwC,KAAK,CAAC,WAAW,CAAC,IAAI,mIAAmI,CAClM,CAAC;SACH;QAED,UAAU;QACV,IAAI,OAAO,WAAW,KAAK,WAAW,EAAE;YACtC,IAAI,KAAK,YAAY,UAAU,IAAI,KAAK,YAAY,WAAW,EAAE;gBAC/D,MAAA,IAAI,CAAC,WAAW,oCAAhB,IAAI,CAAC,WAAW,GAAK,IAAI,WAAW,CAAC,MAAM,CAAC,EAAC;gBAC7C,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;aACvC;YAED,MAAM,IAAI,KAAK,CACb,oDACG,KAAa,CAAC,WAAW,CAAC,IAC7B,gDAAgD,CACjD,CAAC;SACH;QAED,MAAM,IAAI,KAAK,CACb,gGAAgG,CACjG,CAAC;IACJ,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;YAC3C,OAAO,EAAE,CAAC;SACX;QAED,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;;AA/FD,kBAAkB;AACX,yBAAa,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC1G,0BAAc,GAAG,kDAAkD,CAAC;AAgG7E,SAAS,SAAS,CAAC,GAAW,EAAE,SAAiB;IAC/C,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE;QAChB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;KACtF;IAED,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AACvB,CAAC;AAED;;;;;GAKG;AACH,SAAS,2BAA2B,CAAI,MAAW;IACjD,IAAI,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC;QAAE,OAAO,MAAM,CAAC;IAEhD,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;IAClC,OAAO;QACL,IAAI;YACF,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;QACvB,CAAC;QACD,KAAK,CAAC,MAAM;YACV,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAC1C,CAAC;QACD,CAAC,MAAM,CAAC,aAAa,CAAC;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC"}
|