ai 6.0.233 → 6.0.234

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.
@@ -13,6 +13,7 @@ import { writeToServerResponse } from '../util/write-to-server-response';
13
13
  * @param options.statusText - Optional HTTP status text.
14
14
  * @param options.headers - Optional response headers.
15
15
  * @param options.textStream - The text stream to pipe.
16
+ * @returns A promise that resolves when the stream has been written.
16
17
  */
17
18
  export function pipeTextStreamToResponse({
18
19
  response,
@@ -23,8 +24,8 @@ export function pipeTextStreamToResponse({
23
24
  }: {
24
25
  response: ServerResponse;
25
26
  textStream: ReadableStream<string>;
26
- } & ResponseInit): void {
27
- writeToServerResponse({
27
+ } & ResponseInit): Promise<void> {
28
+ return writeToServerResponse({
28
29
  response,
29
30
  status,
30
31
  statusText,
@@ -16,6 +16,7 @@ import type { UIMessageStreamResponseInit } from './ui-message-stream-response-i
16
16
  * @param options.headers - Additional HTTP headers to include in the response.
17
17
  * @param options.stream - The UI message chunk stream to send.
18
18
  * @param options.consumeSseStream - Optional callback to consume a copy of the SSE stream independently.
19
+ * @returns A promise that resolves when the stream has been written.
19
20
  */
20
21
  export function pipeUIMessageStreamToResponse({
21
22
  response,
@@ -27,7 +28,7 @@ export function pipeUIMessageStreamToResponse({
27
28
  }: {
28
29
  response: ServerResponse;
29
30
  stream: ReadableStream<UIMessageChunk>;
30
- } & UIMessageStreamResponseInit): void {
31
+ } & UIMessageStreamResponseInit): Promise<void> {
31
32
  let sseStream = stream.pipeThrough(new JsonToSseTransformStream());
32
33
 
33
34
  // when the consumeSseStream is provided, we need to tee the stream
@@ -39,7 +40,7 @@ export function pipeUIMessageStreamToResponse({
39
40
  consumeSseStream({ stream: stream2 }); // no await (do not block the response)
40
41
  }
41
42
 
42
- writeToServerResponse({
43
+ return writeToServerResponse({
43
44
  response,
44
45
  status,
45
46
  statusText,
@@ -168,31 +168,54 @@ export const videoMediaTypeSignatures = [
168
168
  },
169
169
  ] as const;
170
170
 
171
- const stripID3 = (data: Uint8Array | string) => {
172
- const bytes =
173
- typeof data === 'string' ? convertBase64ToUint8Array(data) : data;
171
+ const DEFAULT_SNIFF_BYTES = 18;
172
+
173
+ // Longest signature prefix in the tables above (e.g. image/avif = 12 bytes).
174
+ const MAX_SIGNATURE_BYTES = 12;
175
+
176
+ // Largest ID3v2 tag (10-byte header + body) skipped to reach the audio frame.
177
+ // Covers typical tags including embedded cover art while keeping the decode
178
+ // bounded and O(1) in the attachment size. Exported for boundary tests.
179
+ export const MAX_ID3_TAG_BYTES = 128 * 1024;
180
+
181
+ // Total prefix decoded when an ID3 tag is present: the tag plus room for the
182
+ // trailing signature, so a tag right at the size limit stays detectable.
183
+ const ID3_SCAN_BYTES = MAX_ID3_TAG_BYTES + MAX_SIGNATURE_BYTES;
184
+
185
+ // Decode/view exactly the first `maxBytes` bytes from the front of the input.
186
+ // The base64 and raw-byte paths yield the same length, so detection does not
187
+ // depend on the input's representation.
188
+ function decodePrefix(data: Uint8Array | string, maxBytes: number): Uint8Array {
189
+ if (typeof data !== 'string') {
190
+ return data.length > maxBytes ? data.subarray(0, maxBytes) : data;
191
+ }
192
+ // base64: 4 chars -> 3 bytes. Decode whole 4-char groups, then trim the 0-2
193
+ // extra bytes so the result matches the raw-byte path exactly.
194
+ const maxChars = Math.ceil(maxBytes / 3) * 4;
195
+ const bytes = convertBase64ToUint8Array(
196
+ data.substring(0, Math.min(data.length, maxChars)),
197
+ );
198
+ return bytes.length > maxBytes ? bytes.subarray(0, maxBytes) : bytes;
199
+ }
200
+
201
+ function hasID3(bytes: Uint8Array): boolean {
202
+ return (
203
+ bytes.length > 10 &&
204
+ bytes[0] === 0x49 && // 'I'
205
+ bytes[1] === 0x44 && // 'D'
206
+ bytes[2] === 0x33 // '3'
207
+ );
208
+ }
209
+
210
+ const stripID3 = (bytes: Uint8Array): Uint8Array => {
174
211
  const id3Size =
175
212
  ((bytes[6] & 0x7f) << 21) |
176
213
  ((bytes[7] & 0x7f) << 14) |
177
214
  ((bytes[8] & 0x7f) << 7) |
178
215
  (bytes[9] & 0x7f);
179
-
180
- // The raw MP3 starts here
181
- return bytes.slice(id3Size + 10);
216
+ return bytes.subarray(id3Size + 10);
182
217
  };
183
218
 
184
- function stripID3TagsIfPresent(data: Uint8Array | string): Uint8Array | string {
185
- const hasId3 =
186
- (typeof data === 'string' && data.startsWith('SUQz')) ||
187
- (typeof data !== 'string' &&
188
- data.length > 10 &&
189
- data[0] === 0x49 && // 'I'
190
- data[1] === 0x44 && // 'D'
191
- data[2] === 0x33); // '3'
192
-
193
- return hasId3 ? stripID3(data) : data;
194
- }
195
-
196
219
  /**
197
220
  * Detect the media IANA media type of a file using a list of signatures.
198
221
  *
@@ -210,15 +233,13 @@ export function detectMediaType({
210
233
  | typeof imageMediaTypeSignatures
211
234
  | typeof videoMediaTypeSignatures;
212
235
  }): (typeof signatures)[number]['mediaType'] | undefined {
213
- const processedData = stripID3TagsIfPresent(data);
236
+ let bytes = decodePrefix(data, DEFAULT_SNIFF_BYTES);
214
237
 
215
- // Convert the first ~18 bytes (24 base64 chars) for consistent detection logic:
216
- const bytes =
217
- typeof processedData === 'string'
218
- ? convertBase64ToUint8Array(
219
- processedData.substring(0, Math.min(processedData.length, 24)),
220
- )
221
- : processedData;
238
+ // ID3v2-tagged MP3s carry the audio frame after the tag; scan a bounded
239
+ // prefix past it rather than decoding the whole input.
240
+ if (hasID3(bytes)) {
241
+ bytes = stripID3(decodePrefix(data, ID3_SCAN_BYTES));
242
+ }
222
243
 
223
244
  for (const signature of signatures) {
224
245
  if (
@@ -15,7 +15,7 @@ export function writeToServerResponse({
15
15
  statusText?: string;
16
16
  headers?: Record<string, string | number | string[]>;
17
17
  stream: ReadableStream<Uint8Array>;
18
- }): void {
18
+ }): Promise<void> {
19
19
  const statusCode = status ?? 200;
20
20
  if (statusText !== undefined) {
21
21
  response.writeHead(statusCode, statusText, headers);
@@ -45,5 +45,5 @@ export function writeToServerResponse({
45
45
  }
46
46
  };
47
47
 
48
- read();
48
+ return read();
49
49
  }