@atproto/common 0.8.6 → 0.8.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @atproto/common
2
2
 
3
+ ## 0.8.8
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`07c25d4`](https://github.com/bluesky-social/atproto/commit/07c25d410d4f4ad8f6ec80fc14efa0dd8ec55545), [`07c25d4`](https://github.com/bluesky-social/atproto/commit/07c25d410d4f4ad8f6ec80fc14efa0dd8ec55545)]:
8
+ - @atproto/lex-cbor@0.1.8
9
+ - @atproto/lex-data@0.1.9
10
+ - @atproto/common-web@0.5.16
11
+
12
+ ## 0.8.7
13
+
14
+ ### Patch Changes
15
+
16
+ - [#5753](https://github.com/bluesky-social/atproto/pull/5753) [`3bfd00f`](https://github.com/bluesky-social/atproto/commit/3bfd00f484dc144482c82a4ed248adc49e6be769) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Use dedicated `MaxSizeError` error by default from `MaxSizeChecker`
17
+
18
+ - [#5753](https://github.com/bluesky-social/atproto/pull/5753) [`3bfd00f`](https://github.com/bluesky-social/atproto/commit/3bfd00f484dc144482c82a4ed248adc49e6be769) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Avoid leaking the `this` context when calling `createError` from `MaxSizeChecker`
19
+
20
+ - [#5753](https://github.com/bluesky-social/atproto/pull/5753) [`3bfd00f`](https://github.com/bluesky-social/atproto/commit/3bfd00f484dc144482c82a4ed248adc49e6be769) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Mark `MaxSizeChecker` properties as `readonly`
21
+
22
+ - [#5753](https://github.com/bluesky-social/atproto/pull/5753) [`3bfd00f`](https://github.com/bluesky-social/atproto/commit/3bfd00f484dc144482c82a4ed248adc49e6be769) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add decompression and size limits when decoding streams
23
+
3
24
  ## 0.8.6
4
25
 
5
26
  ### Patch Changes
package/dist/streams.d.ts CHANGED
@@ -78,12 +78,17 @@ export declare const byteIterableToStream: (iter: AsyncIterable<Uint8Array>) =>
78
78
  export declare const coalesceByteStream: (stream: Iterable<Uint8Array> | AsyncIterable<Uint8Array>, minChunkSize: number) => Readable;
79
79
  export declare const bytesToStream: (bytes: Uint8Array) => Readable;
80
80
  export declare class MaxSizeChecker extends Transform {
81
- maxSize: number;
82
- createError: () => Error;
81
+ readonly maxSize: number;
82
+ readonly createError: () => Error;
83
83
  totalSize: number;
84
84
  constructor(maxSize: number, createError?: () => Error);
85
85
  _transform(chunk: Uint8Array, _enc: BufferEncoding, cb: TransformCallback): void;
86
86
  }
87
+ export declare class MaxSizeError extends TypeError {
88
+ readonly maxSize: number;
89
+ name: string;
90
+ constructor(maxSize: number, options?: ErrorOptions);
91
+ }
87
92
  export declare class HashPassThrough extends Transform {
88
93
  #private;
89
94
  private readonly hash;
@@ -94,11 +99,22 @@ export declare class HashPassThrough extends Transform {
94
99
  }
95
100
  export declare function decodeStream(stream: Readable, contentEncoding?: string | string[]): Readable;
96
101
  export declare function decodeStream(stream: AsyncIterable<Uint8Array>, contentEncoding?: string | string[]): AsyncIterable<Uint8Array> | Readable;
102
+ /**
103
+ * Maximum number of content-codings accepted by {@link createDecoders} unless a
104
+ * caller passes its own. Generous on purpose: a header may legally list several
105
+ * codings (RFC 9110 §8.4.1).
106
+ */
107
+ export declare const DEFAULT_MAX_CODINGS = 8;
108
+ export type CreateDecodersOptions = {
109
+ maxCodings?: number;
110
+ maxSize?: number;
111
+ maxSizeError?: () => Error;
112
+ };
97
113
  /**
98
114
  * Create a series of decoding streams based on the content-encoding header. The
99
115
  * resulting streams should be piped together to decode the content.
100
116
  *
101
117
  * @see {@link https://datatracker.ietf.org/doc/html/rfc9110#section-8.4.1}
102
118
  */
103
- export declare function createDecoders(contentEncoding?: string | string[]): Duplex[];
119
+ export declare function createDecoders(contentEncoding?: string | string[], { maxCodings, maxSize, maxSizeError, }?: CreateDecodersOptions): Duplex[];
104
120
  //# sourceMappingURL=streams.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"streams.d.ts","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"streams.d.ts","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"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"}
package/dist/streams.js CHANGED
@@ -303,7 +303,7 @@ export const bytesToStream = (bytes) => {
303
303
  return stream;
304
304
  };
305
305
  export class MaxSizeChecker extends Transform {
306
- constructor(maxSize, createError = () => new Error(`Max size of ${maxSize} bytes exceeded`)) {
306
+ constructor(maxSize, createError = () => new MaxSizeError(maxSize)) {
307
307
  super();
308
308
  this.maxSize = maxSize;
309
309
  this.createError = createError;
@@ -312,13 +312,20 @@ export class MaxSizeChecker extends Transform {
312
312
  _transform(chunk, _enc, cb) {
313
313
  this.totalSize += chunk.length;
314
314
  if (this.totalSize > this.maxSize) {
315
- cb(this.createError());
315
+ cb(this.createError.call(null));
316
316
  }
317
317
  else {
318
318
  cb(null, chunk);
319
319
  }
320
320
  }
321
321
  }
322
+ export class MaxSizeError extends TypeError {
323
+ constructor(maxSize, options) {
324
+ super(`Max size of ${maxSize} bytes exceeded`, options);
325
+ this.maxSize = maxSize;
326
+ this.name = 'MaxSizeError';
327
+ }
328
+ }
322
329
  export class HashPassThrough extends Transform {
323
330
  #digest;
324
331
  constructor(algorithm, options) {
@@ -347,30 +354,75 @@ export function decodeStream(stream, contentEncoding) {
347
354
  return stream;
348
355
  return pipeline([stream, ...decoders], () => { });
349
356
  }
357
+ /**
358
+ * Maximum number of content-codings accepted by {@link createDecoders} unless a
359
+ * caller passes its own. Generous on purpose: a header may legally list several
360
+ * codings (RFC 9110 §8.4.1).
361
+ */
362
+ export const DEFAULT_MAX_CODINGS = 8;
350
363
  /**
351
364
  * Create a series of decoding streams based on the content-encoding header. The
352
365
  * resulting streams should be piped together to decode the content.
353
366
  *
354
367
  * @see {@link https://datatracker.ietf.org/doc/html/rfc9110#section-8.4.1}
355
368
  */
356
- export function createDecoders(contentEncoding) {
357
- const decoders = [];
358
- if (contentEncoding?.length) {
359
- const encodings = Array.isArray(contentEncoding)
360
- ? contentEncoding.flatMap(commaSplit)
361
- : contentEncoding.split(',');
362
- for (const encoding of encodings) {
363
- const normalizedEncoding = normalizeEncoding(encoding);
364
- // @NOTE
365
- // > The default (identity) encoding [...] is used only in the
366
- // > Accept-Encoding header, and SHOULD NOT be used in the
367
- // > Content-Encoding header.
368
- if (normalizedEncoding === 'identity')
369
- continue;
370
- decoders.push(createDecoder(normalizedEncoding));
371
- }
369
+ export function createDecoders(contentEncoding, { maxCodings = DEFAULT_MAX_CODINGS, maxSize = undefined, maxSizeError = undefined, } = {}) {
370
+ const codings = (Array.isArray(contentEncoding)
371
+ ? contentEncoding.flatMap(commaSplit)
372
+ : contentEncoding?.length
373
+ ? contentEncoding.split(',')
374
+ : [])
375
+ .map(normalizeEncoding)
376
+ // @NOTE
377
+ // > The default (identity) encoding [...] is used only in the
378
+ // > Accept-Encoding header, and SHOULD NOT be used in the
379
+ // > Content-Encoding header.
380
+ .filter(isNotIdentity);
381
+ // Checked before any decoder is constructed.
382
+ if (codings.length > maxCodings) {
383
+ const seen = codings.slice(0, 4).join(', ') + (codings.length > 4 ? ', …' : '');
384
+ throw new TypeError(`Unsupported content-encoding: ${codings.length} codings (${seen}) exceed the maximum of ${maxCodings}`);
372
385
  }
373
- return decoders.reverse();
386
+ const decoders = codings.map(createDecoder).reverse();
387
+ // Size limits are applied only if maxSize is specified.
388
+ if (maxSize == null)
389
+ return decoders;
390
+ return [
391
+ // @NOTE if there is only one decoder, no need to apply the decoding budget
392
+ // limiter (MaxSizeChecker will be enough).
393
+ ...(decoders.length <= 1
394
+ ? decoders
395
+ : decodingBudgetLimiter(decoders,
396
+ // Using `maxSize * decoders.length` here would be equivalent to
397
+ // adding a simple MaxSizeChecker for each decoder in the chain. The
398
+ // threshold provides a guarantee that each re-encoding in the chain
399
+ // was worth its size contribution (averaging 95% per decoder):
400
+ //
401
+ // <=> maxSize + maxSize * 0.95 + maxSize * 0.95^2 + …
402
+ // <=> maxSize * (0.95^0 + 0.95^1 + 0.95^2 + …)
403
+ //
404
+ // @NOTE that we are not enforcing the compression ratio on every
405
+ // decoder in the chain. Instead, we are applying a cumulative size
406
+ // limit that accounts for the expected compression ratio of each
407
+ // decoder.
408
+ maxSize * decoders.reduce((acc, _, i) => acc + Math.pow(0.95, i), 0), maxSizeError)),
409
+ new MaxSizeChecker(maxSize, maxSizeError),
410
+ ];
411
+ }
412
+ function decodingBudgetLimiter(decoders, totalSizeLimit, maxSizeError = () => new MaxSizeError(totalSizeLimit)) {
413
+ let totalSize = 0;
414
+ return decoders.flatMap((decoder) => [
415
+ decoder,
416
+ new Transform({
417
+ transform(chunk, _encoding, callback) {
418
+ totalSize += chunk.length;
419
+ if (totalSize > totalSizeLimit)
420
+ callback(maxSizeError());
421
+ else
422
+ callback(null, chunk);
423
+ },
424
+ }),
425
+ ]);
374
426
  }
375
427
  function commaSplit(header) {
376
428
  return header.split(',');
@@ -380,6 +432,9 @@ function normalizeEncoding(encoding) {
380
432
  // > All content-coding values are case-insensitive...
381
433
  return encoding.trim().toLowerCase();
382
434
  }
435
+ function isNotIdentity(encoding) {
436
+ return encoding !== 'identity';
437
+ }
383
438
  function createDecoder(normalizedEncoding) {
384
439
  switch (normalizedEncoding) {
385
440
  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2
@@ -1 +1 @@
1
- {"version":3,"file":"streams.js","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Hash, type HashOptions, createHash } from 'node:crypto'\nimport {\n type Duplex,\n PassThrough,\n Readable,\n type Stream,\n Transform,\n type TransformCallback,\n Writable,\n pipeline,\n} from 'node:stream'\nimport { createBrotliDecompress, createGunzip, createInflate } from 'node:zlib'\n\n/**\n * @deprecated use native stream piping instead.\n */\nexport const forwardStreamErrors = (...streams: Stream[]) => {\n for (let i = 1; i < streams.length; ++i) {\n const prev = streams[i - 1]\n const next = streams[i]\n\n prev.once('error', (err) => next.emit('error', err))\n }\n}\n\n/**\n * @deprecated This function has several flaws:\n * - It only works if all the stream are setup during the same tick\n * - It does not apply any backpressure to the source stream, which can lead to\n * memory issues\n * Use {@link Tee} instead, which is a more robust implementation of the same\n * concept.\n */\nexport const cloneStream = (stream: Readable): Readable => {\n const passthrough = new PassThrough()\n forwardStreamErrors(stream, passthrough)\n return stream.pipe(passthrough)\n}\n\n/**\n * A {@link Transform} that forwards every chunk downstream while mirroring a\n * copy into a \"branch\" {@link Writable} (exposed through {@link Tee.branch}, e.g.\n * to be cached). The tee is paced by the slower of its two consumers, bounding\n * how much data gets buffered. The branch is completed when the source ends,\n * and torn down if the tee errors or is destroyed early (e.g. the client\n * disconnected).\n *\n * Consuming the branch is best-effort: its failures are swallowed here and must\n * never break the main stream.\n *\n * Because the tee is a {@link Transform}, its *main* output is a downstream\n * pipeline stage — so `pipeline([readable, tee, main])` tears the tee (and the\n * source) down as soon as `main` fails. If you need the branch to keep going\n * after the main consumer disappears (or vice-versa), use {@link fanOut}, which\n * owns both outputs instead of exposing one as a pipeline stage.\n */\nexport class Tee extends Transform {\n readonly branch: Writable\n #branchFailed = false\n\n constructor(branch: Writable | ((readable: Readable) => void)) {\n super()\n if (typeof branch === 'function') {\n const passthrough = new PassThrough({ autoDestroy: true })\n branch(passthrough)\n this.branch = passthrough\n } else {\n this.branch = branch\n }\n\n // The branch is best-effort: its failures must never break or stall the\n // main stream.\n this.branch.on('error', () => {\n this.#branchFailed = true\n })\n }\n\n get branchWritable() {\n return !this.#branchFailed && this.branch.writable\n }\n\n _transform(chunk: unknown, _enc: BufferEncoding, cb: TransformCallback) {\n // Forward downstream, applying backpressure if either branch is slower.\n if (!this.branchWritable || this.branch.write(chunk)) {\n cb(null, chunk)\n } else {\n const done = () => {\n this.branch.off('drain', done)\n this.branch.off('close', done)\n this.branch.off('error', done)\n cb(null, chunk)\n }\n // Resume on 'drain' (branch caught up) but also on 'close'/'error' so the\n // tee is never stuck if the branch dies mid-write — e.g. a branch created\n // with `autoDestroy: false`, which errors without emitting 'close'.\n this.branch.once('drain', done)\n this.branch.once('close', done)\n this.branch.once('error', done)\n }\n }\n\n _flush(cb: TransformCallback) {\n if (this.branchWritable) this.branch.end()\n cb()\n }\n\n _destroy(err: Error | null, cb: (err?: Error | null) => void) {\n // A failed branch has already torn itself down (or given up); leave it be\n // rather than emitting a second, spurious error on it.\n if (this.branchWritable) {\n this.branch.destroy(err ?? new Error('Tee destroyed'))\n }\n\n cb(err)\n }\n}\n\n/**\n * A {@link Writable} that mirrors everything written to it into several\n * independent \"sinks\", pacing the input by the slowest sink that is still\n * alive. Unlike {@link Tee}, the sinks are *owned* by the fan-out rather than\n * being downstream pipeline stages, which lets the two error domains be kept\n * apart:\n *\n * - An *output* (sink) that errors or ends early is simply dropped; the other\n * sinks — and the input — keep going. Its error is swallowed (best-effort),\n * exactly like a {@link Tee} branch.\n * - The *input* (writable side) is only torn down once *every* sink has died,\n * or if the input itself fails — in which case the failure is propagated to\n * every surviving sink.\n *\n * Designed to be the destination of a pipeline:\n *\n * ```\n * pipeline([readable, ...transforms, fanOut(main, branch)])\n * ```\n *\n * If `main` disconnects (e.g. an HTTP response whose client went away), `branch`\n * still receives the full stream (e.g. to finish populating a cache), and\n * vice-versa. A sink may also be provided as a function receiving a\n * {@link Readable} to consume, mirroring {@link Tee}'s constructor.\n */\nexport function fanOut(\n ...outputs: ReadonlyArray<Writable | ((readable: Readable) => void)>\n): Writable {\n const sinks = outputs.map((output) => {\n if (typeof output !== 'function') return output\n const passthrough = new PassThrough()\n output(passthrough)\n return passthrough\n })\n\n // Sinks still able to receive data. A sink that errors or closes is dropped\n // and never written to again; its 'error' is swallowed here so a dead sink can\n // neither crash the process nor disturb the surviving sinks (other listeners,\n // e.g. a reader consuming it, still observe the error).\n const alive = new Set<Writable>(sinks)\n const drop = (sink: Writable) => alive.delete(sink)\n for (const sink of sinks) {\n sink.on('error', () => drop(sink))\n sink.on('close', () => drop(sink))\n }\n\n // Whether the input ended cleanly, so we can distinguish a normal completion\n // (let the sinks finish the data we handed them) from a teardown (destroy\n // them).\n let ended = false\n\n return new Writable({\n write(chunk, _enc, cb) {\n // Write to every live sink, then invoke `cb` once they have all either\n // accepted the chunk, drained, or died — pacing the input by the slowest\n // surviving sink.\n let waiting = 1 // guard so `cb` can't fire before the loop completes\n const settle = () => {\n if (--waiting > 0) return\n // If every sink died while handling this chunk, tear the input down.\n cb(alive.size === 0 ? new Error('fanOut: all sinks ended') : null)\n }\n\n for (const sink of alive) {\n if (!sink.writable) {\n drop(sink)\n continue\n }\n if (sink.write(chunk)) continue\n\n waiting++\n const done = () => {\n sink.off('drain', done)\n sink.off('close', done)\n sink.off('error', done)\n settle()\n }\n // Resume on 'drain', but also on 'close'/'error' so a sink that dies\n // mid-write (e.g. one created with autoDestroy:false, which errors\n // without emitting 'close') never leaves the fan-out stuck.\n sink.once('drain', done)\n sink.once('close', done)\n sink.once('error', done)\n }\n\n settle()\n },\n final(cb) {\n ended = true\n for (const sink of alive) {\n if (sink.writable) sink.end()\n }\n cb()\n },\n destroy(err, cb) {\n // A clean end (`_final` already ran) leaves the sinks to flush what they\n // were handed; any other teardown propagates to the sinks we own.\n if (!ended || err) {\n for (const sink of alive) {\n if (sink.writable) sink.destroy(err ?? undefined)\n }\n }\n cb(err)\n },\n })\n}\n\nexport const streamSize = async (stream: Readable): Promise<number> => {\n let size = 0\n for await (const chunk of stream) {\n size += Buffer.byteLength(chunk)\n }\n return size\n}\n\nexport const streamToBytes = async (stream: AsyncIterable<Uint8Array>) =>\n // @NOTE Though Buffer is a sub-class of Uint8Array, we have observed\n // inconsistencies when using a Buffer in place of Uint8Array. For this\n // reason, we convert the Buffer to a Uint8Array.\n new Uint8Array(await streamToNodeBuffer(stream))\n\n// streamToBuffer identifier name already taken by @atproto/common-web\nexport const streamToNodeBuffer = async (\n stream: Iterable<Uint8Array> | AsyncIterable<Uint8Array>,\n): Promise<Buffer> => {\n const chunks: Uint8Array[] = []\n let totalLength = 0 // keep track of total length for Buffer.concat\n for await (const chunk of stream) {\n if (chunk instanceof Uint8Array) {\n chunks.push(chunk)\n totalLength += Buffer.byteLength(chunk)\n } else {\n throw new TypeError('expected Uint8Array')\n }\n }\n return Buffer.concat(chunks, totalLength)\n}\n\nexport const byteIterableToStream = (\n iter: AsyncIterable<Uint8Array>,\n): Readable => {\n return Readable.from(iter, { objectMode: false })\n}\n\n/**\n * Coalesce a stream of Uint8Array chunks into larger chunks of at least the\n * specified size ({@link minChunkSize}). This is useful for optimizing\n * downstream processing that benefits from larger chunk sizes, such as\n * compression or hashing.\n */\nexport const coalesceByteStream = (\n stream: Iterable<Uint8Array> | AsyncIterable<Uint8Array>,\n minChunkSize: number,\n): Readable => {\n if (!Number.isInteger(minChunkSize) || minChunkSize < 1) {\n throw new TypeError('minChunkSize must be a positive integer')\n }\n\n // @NOTE On Node 22, this *does* return a PassThrough (\"@types/node\"\n // incorrectly types it as Writable).\n return pipeline(stream, coalesce, (_err) => {\n // Errors are expected to be handled through the stream\n }) as PassThrough\n\n // @NOTE This implementation is not NodeJS specific and could be exported as\n // utility (from \"@atproto/common-web\") if needed. We don't do it now to avoid\n // increasing the API surface of our packages.\n async function* coalesce(\n iter: Iterable<Uint8Array> | AsyncIterable<Uint8Array>,\n ): AsyncGenerator<Uint8Array> {\n // @NOTE This implementation avoids as many un-necessary copies as possible\n // and only buffers incoming chunks when they are smaller than the\n // coalescing buffer.\n\n let buffer = new Uint8Array(minChunkSize)\n let offset = 0\n\n for await (const chunk of iter) {\n const freeSpace = buffer.length - offset\n if (freeSpace > chunk.length) {\n // If the incoming chunk is smaller than the free space, we copy the\n // entire chunk into the coalescing buffer and continue to the next\n // chunk\n buffer.set(chunk, offset)\n offset += chunk.length\n } else if (offset === 0 && chunk.length >= minChunkSize) {\n // If the coalescing buffer is empty and the incoming chunk is larger\n // than the coalescing buffer, we can skip any copying and yield the\n // incoming chunk directly\n yield chunk\n } else {\n // Otherwise, we need to copy as much of the incoming chunk as we can\n // into the coalescing buffer and yield the full coalescing buffer.\n buffer.set(chunk.subarray(0, freeSpace), offset)\n yield buffer\n\n // We create a new coalescing buffer (for future use)\n buffer = new Uint8Array(minChunkSize)\n offset = 0\n\n const remainingBytes = chunk.subarray(freeSpace)\n if (remainingBytes.length > minChunkSize) {\n // If the remaining of chunk is still too big to fit in the\n // coalescing buffer, we yield it directly without copying it\n yield remainingBytes\n } else if (remainingBytes.length > 0) {\n // Otherwise, we copy the remaining bytes into the coalescing buffer\n // and continue to the next chunk\n buffer.set(remainingBytes, offset)\n offset += remainingBytes.length\n }\n }\n }\n\n // Yield any remaining bytes in the coalescing buffer\n if (offset > 0) {\n yield buffer.subarray(0, offset)\n }\n }\n}\n\nexport const bytesToStream = (bytes: Uint8Array): Readable => {\n const stream = new Readable()\n stream.push(bytes)\n stream.push(null)\n return stream\n}\n\nexport class MaxSizeChecker extends Transform {\n totalSize = 0\n constructor(\n public maxSize: number,\n public createError: () => Error = () =>\n new Error(`Max size of ${maxSize} bytes exceeded`),\n ) {\n super()\n }\n _transform(chunk: Uint8Array, _enc: BufferEncoding, cb: TransformCallback) {\n this.totalSize += chunk.length\n if (this.totalSize > this.maxSize) {\n cb(this.createError())\n } else {\n cb(null, chunk)\n }\n }\n}\n\nexport class HashPassThrough extends Transform {\n private readonly hash: Hash\n #digest?: Buffer<ArrayBuffer>\n\n constructor(algorithm: string, options?: HashOptions) {\n super()\n this.hash = createHash(algorithm, options)\n }\n\n _transform(chunk: Uint8Array, _enc: BufferEncoding, cb: TransformCallback) {\n this.hash.update(chunk)\n cb(null, chunk)\n }\n\n _flush(cb: TransformCallback) {\n const digest = this.hash.digest()\n this.#digest = digest\n this.emit('hash', digest)\n cb()\n }\n\n get digest(): Buffer<ArrayBuffer> {\n if (this.#digest) return this.#digest\n throw new Error('Hash not yet computed. Wait for the stream to finish.')\n }\n}\n\nexport function decodeStream(\n stream: Readable,\n contentEncoding?: string | string[],\n): Readable\nexport function decodeStream(\n stream: AsyncIterable<Uint8Array>,\n contentEncoding?: string | string[],\n): AsyncIterable<Uint8Array> | Readable\nexport function decodeStream(\n stream: Readable | AsyncIterable<Uint8Array>,\n contentEncoding?: string | string[],\n): Readable | AsyncIterable<Uint8Array> {\n const decoders = createDecoders(contentEncoding)\n if (decoders.length === 0) return stream\n return pipeline([stream as Readable, ...decoders], () => {}) as Duplex\n}\n\n/**\n * Create a series of decoding streams based on the content-encoding header. The\n * resulting streams should be piped together to decode the content.\n *\n * @see {@link https://datatracker.ietf.org/doc/html/rfc9110#section-8.4.1}\n */\nexport function createDecoders(contentEncoding?: string | string[]): Duplex[] {\n const decoders: Duplex[] = []\n\n if (contentEncoding?.length) {\n const encodings: string[] = Array.isArray(contentEncoding)\n ? contentEncoding.flatMap(commaSplit)\n : contentEncoding.split(',')\n for (const encoding of encodings) {\n const normalizedEncoding = normalizeEncoding(encoding)\n\n // @NOTE\n // > The default (identity) encoding [...] is used only in the\n // > Accept-Encoding header, and SHOULD NOT be used in the\n // > Content-Encoding header.\n if (normalizedEncoding === 'identity') continue\n\n decoders.push(createDecoder(normalizedEncoding))\n }\n }\n\n return decoders.reverse()\n}\n\nfunction commaSplit(header: string): string[] {\n return header.split(',')\n}\n\nfunction normalizeEncoding(encoding: string) {\n // https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1\n // > All content-coding values are case-insensitive...\n return encoding.trim().toLowerCase()\n}\n\nfunction createDecoder(normalizedEncoding: string): Duplex {\n switch (normalizedEncoding) {\n // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2\n case 'gzip':\n case 'x-gzip':\n return createGunzip()\n case 'deflate':\n return createInflate()\n case 'br':\n return createBrotliDecompress()\n case 'identity':\n return new PassThrough()\n default:\n throw new TypeError(\n `Unsupported content-encoding: \"${normalizedEncoding}\"`,\n )\n }\n}\n"]}
1
+ {"version":3,"file":"streams.js","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Hash, type HashOptions, createHash } from 'node:crypto'\nimport {\n type Duplex,\n PassThrough,\n Readable,\n type Stream,\n Transform,\n type TransformCallback,\n Writable,\n pipeline,\n} from 'node:stream'\nimport { createBrotliDecompress, createGunzip, createInflate } from 'node:zlib'\n\n/**\n * @deprecated use native stream piping instead.\n */\nexport const forwardStreamErrors = (...streams: Stream[]) => {\n for (let i = 1; i < streams.length; ++i) {\n const prev = streams[i - 1]\n const next = streams[i]\n\n prev.once('error', (err) => next.emit('error', err))\n }\n}\n\n/**\n * @deprecated This function has several flaws:\n * - It only works if all the stream are setup during the same tick\n * - It does not apply any backpressure to the source stream, which can lead to\n * memory issues\n * Use {@link Tee} instead, which is a more robust implementation of the same\n * concept.\n */\nexport const cloneStream = (stream: Readable): Readable => {\n const passthrough = new PassThrough()\n forwardStreamErrors(stream, passthrough)\n return stream.pipe(passthrough)\n}\n\n/**\n * A {@link Transform} that forwards every chunk downstream while mirroring a\n * copy into a \"branch\" {@link Writable} (exposed through {@link Tee.branch}, e.g.\n * to be cached). The tee is paced by the slower of its two consumers, bounding\n * how much data gets buffered. The branch is completed when the source ends,\n * and torn down if the tee errors or is destroyed early (e.g. the client\n * disconnected).\n *\n * Consuming the branch is best-effort: its failures are swallowed here and must\n * never break the main stream.\n *\n * Because the tee is a {@link Transform}, its *main* output is a downstream\n * pipeline stage — so `pipeline([readable, tee, main])` tears the tee (and the\n * source) down as soon as `main` fails. If you need the branch to keep going\n * after the main consumer disappears (or vice-versa), use {@link fanOut}, which\n * owns both outputs instead of exposing one as a pipeline stage.\n */\nexport class Tee extends Transform {\n readonly branch: Writable\n #branchFailed = false\n\n constructor(branch: Writable | ((readable: Readable) => void)) {\n super()\n if (typeof branch === 'function') {\n const passthrough = new PassThrough({ autoDestroy: true })\n branch(passthrough)\n this.branch = passthrough\n } else {\n this.branch = branch\n }\n\n // The branch is best-effort: its failures must never break or stall the\n // main stream.\n this.branch.on('error', () => {\n this.#branchFailed = true\n })\n }\n\n get branchWritable() {\n return !this.#branchFailed && this.branch.writable\n }\n\n _transform(chunk: unknown, _enc: BufferEncoding, cb: TransformCallback) {\n // Forward downstream, applying backpressure if either branch is slower.\n if (!this.branchWritable || this.branch.write(chunk)) {\n cb(null, chunk)\n } else {\n const done = () => {\n this.branch.off('drain', done)\n this.branch.off('close', done)\n this.branch.off('error', done)\n cb(null, chunk)\n }\n // Resume on 'drain' (branch caught up) but also on 'close'/'error' so the\n // tee is never stuck if the branch dies mid-write — e.g. a branch created\n // with `autoDestroy: false`, which errors without emitting 'close'.\n this.branch.once('drain', done)\n this.branch.once('close', done)\n this.branch.once('error', done)\n }\n }\n\n _flush(cb: TransformCallback) {\n if (this.branchWritable) this.branch.end()\n cb()\n }\n\n _destroy(err: Error | null, cb: (err?: Error | null) => void) {\n // A failed branch has already torn itself down (or given up); leave it be\n // rather than emitting a second, spurious error on it.\n if (this.branchWritable) {\n this.branch.destroy(err ?? new Error('Tee destroyed'))\n }\n\n cb(err)\n }\n}\n\n/**\n * A {@link Writable} that mirrors everything written to it into several\n * independent \"sinks\", pacing the input by the slowest sink that is still\n * alive. Unlike {@link Tee}, the sinks are *owned* by the fan-out rather than\n * being downstream pipeline stages, which lets the two error domains be kept\n * apart:\n *\n * - An *output* (sink) that errors or ends early is simply dropped; the other\n * sinks — and the input — keep going. Its error is swallowed (best-effort),\n * exactly like a {@link Tee} branch.\n * - The *input* (writable side) is only torn down once *every* sink has died,\n * or if the input itself fails — in which case the failure is propagated to\n * every surviving sink.\n *\n * Designed to be the destination of a pipeline:\n *\n * ```\n * pipeline([readable, ...transforms, fanOut(main, branch)])\n * ```\n *\n * If `main` disconnects (e.g. an HTTP response whose client went away), `branch`\n * still receives the full stream (e.g. to finish populating a cache), and\n * vice-versa. A sink may also be provided as a function receiving a\n * {@link Readable} to consume, mirroring {@link Tee}'s constructor.\n */\nexport function fanOut(\n ...outputs: ReadonlyArray<Writable | ((readable: Readable) => void)>\n): Writable {\n const sinks = outputs.map((output) => {\n if (typeof output !== 'function') return output\n const passthrough = new PassThrough()\n output(passthrough)\n return passthrough\n })\n\n // Sinks still able to receive data. A sink that errors or closes is dropped\n // and never written to again; its 'error' is swallowed here so a dead sink can\n // neither crash the process nor disturb the surviving sinks (other listeners,\n // e.g. a reader consuming it, still observe the error).\n const alive = new Set<Writable>(sinks)\n const drop = (sink: Writable) => alive.delete(sink)\n for (const sink of sinks) {\n sink.on('error', () => drop(sink))\n sink.on('close', () => drop(sink))\n }\n\n // Whether the input ended cleanly, so we can distinguish a normal completion\n // (let the sinks finish the data we handed them) from a teardown (destroy\n // them).\n let ended = false\n\n return new Writable({\n write(chunk, _enc, cb) {\n // Write to every live sink, then invoke `cb` once they have all either\n // accepted the chunk, drained, or died — pacing the input by the slowest\n // surviving sink.\n let waiting = 1 // guard so `cb` can't fire before the loop completes\n const settle = () => {\n if (--waiting > 0) return\n // If every sink died while handling this chunk, tear the input down.\n cb(alive.size === 0 ? new Error('fanOut: all sinks ended') : null)\n }\n\n for (const sink of alive) {\n if (!sink.writable) {\n drop(sink)\n continue\n }\n if (sink.write(chunk)) continue\n\n waiting++\n const done = () => {\n sink.off('drain', done)\n sink.off('close', done)\n sink.off('error', done)\n settle()\n }\n // Resume on 'drain', but also on 'close'/'error' so a sink that dies\n // mid-write (e.g. one created with autoDestroy:false, which errors\n // without emitting 'close') never leaves the fan-out stuck.\n sink.once('drain', done)\n sink.once('close', done)\n sink.once('error', done)\n }\n\n settle()\n },\n final(cb) {\n ended = true\n for (const sink of alive) {\n if (sink.writable) sink.end()\n }\n cb()\n },\n destroy(err, cb) {\n // A clean end (`_final` already ran) leaves the sinks to flush what they\n // were handed; any other teardown propagates to the sinks we own.\n if (!ended || err) {\n for (const sink of alive) {\n if (sink.writable) sink.destroy(err ?? undefined)\n }\n }\n cb(err)\n },\n })\n}\n\nexport const streamSize = async (stream: Readable): Promise<number> => {\n let size = 0\n for await (const chunk of stream) {\n size += Buffer.byteLength(chunk)\n }\n return size\n}\n\nexport const streamToBytes = async (stream: AsyncIterable<Uint8Array>) =>\n // @NOTE Though Buffer is a sub-class of Uint8Array, we have observed\n // inconsistencies when using a Buffer in place of Uint8Array. For this\n // reason, we convert the Buffer to a Uint8Array.\n new Uint8Array(await streamToNodeBuffer(stream))\n\n// streamToBuffer identifier name already taken by @atproto/common-web\nexport const streamToNodeBuffer = async (\n stream: Iterable<Uint8Array> | AsyncIterable<Uint8Array>,\n): Promise<Buffer> => {\n const chunks: Uint8Array[] = []\n let totalLength = 0 // keep track of total length for Buffer.concat\n for await (const chunk of stream) {\n if (chunk instanceof Uint8Array) {\n chunks.push(chunk)\n totalLength += Buffer.byteLength(chunk)\n } else {\n throw new TypeError('expected Uint8Array')\n }\n }\n return Buffer.concat(chunks, totalLength)\n}\n\nexport const byteIterableToStream = (\n iter: AsyncIterable<Uint8Array>,\n): Readable => {\n return Readable.from(iter, { objectMode: false })\n}\n\n/**\n * Coalesce a stream of Uint8Array chunks into larger chunks of at least the\n * specified size ({@link minChunkSize}). This is useful for optimizing\n * downstream processing that benefits from larger chunk sizes, such as\n * compression or hashing.\n */\nexport const coalesceByteStream = (\n stream: Iterable<Uint8Array> | AsyncIterable<Uint8Array>,\n minChunkSize: number,\n): Readable => {\n if (!Number.isInteger(minChunkSize) || minChunkSize < 1) {\n throw new TypeError('minChunkSize must be a positive integer')\n }\n\n // @NOTE On Node 22, this *does* return a PassThrough (\"@types/node\"\n // incorrectly types it as Writable).\n return pipeline(stream, coalesce, (_err) => {\n // Errors are expected to be handled through the stream\n }) as PassThrough\n\n // @NOTE This implementation is not NodeJS specific and could be exported as\n // utility (from \"@atproto/common-web\") if needed. We don't do it now to avoid\n // increasing the API surface of our packages.\n async function* coalesce(\n iter: Iterable<Uint8Array> | AsyncIterable<Uint8Array>,\n ): AsyncGenerator<Uint8Array> {\n // @NOTE This implementation avoids as many un-necessary copies as possible\n // and only buffers incoming chunks when they are smaller than the\n // coalescing buffer.\n\n let buffer = new Uint8Array(minChunkSize)\n let offset = 0\n\n for await (const chunk of iter) {\n const freeSpace = buffer.length - offset\n if (freeSpace > chunk.length) {\n // If the incoming chunk is smaller than the free space, we copy the\n // entire chunk into the coalescing buffer and continue to the next\n // chunk\n buffer.set(chunk, offset)\n offset += chunk.length\n } else if (offset === 0 && chunk.length >= minChunkSize) {\n // If the coalescing buffer is empty and the incoming chunk is larger\n // than the coalescing buffer, we can skip any copying and yield the\n // incoming chunk directly\n yield chunk\n } else {\n // Otherwise, we need to copy as much of the incoming chunk as we can\n // into the coalescing buffer and yield the full coalescing buffer.\n buffer.set(chunk.subarray(0, freeSpace), offset)\n yield buffer\n\n // We create a new coalescing buffer (for future use)\n buffer = new Uint8Array(minChunkSize)\n offset = 0\n\n const remainingBytes = chunk.subarray(freeSpace)\n if (remainingBytes.length > minChunkSize) {\n // If the remaining of chunk is still too big to fit in the\n // coalescing buffer, we yield it directly without copying it\n yield remainingBytes\n } else if (remainingBytes.length > 0) {\n // Otherwise, we copy the remaining bytes into the coalescing buffer\n // and continue to the next chunk\n buffer.set(remainingBytes, offset)\n offset += remainingBytes.length\n }\n }\n }\n\n // Yield any remaining bytes in the coalescing buffer\n if (offset > 0) {\n yield buffer.subarray(0, offset)\n }\n }\n}\n\nexport const bytesToStream = (bytes: Uint8Array): Readable => {\n const stream = new Readable()\n stream.push(bytes)\n stream.push(null)\n return stream\n}\n\nexport class MaxSizeChecker extends Transform {\n totalSize = 0\n constructor(\n public readonly maxSize: number,\n public readonly createError: () => Error = () => new MaxSizeError(maxSize),\n ) {\n super()\n }\n\n _transform(chunk: Uint8Array, _enc: BufferEncoding, cb: TransformCallback) {\n this.totalSize += chunk.length\n if (this.totalSize > this.maxSize) {\n cb(this.createError.call(null))\n } else {\n cb(null, chunk)\n }\n }\n}\n\nexport class MaxSizeError extends TypeError {\n name = 'MaxSizeError'\n constructor(\n readonly maxSize: number,\n options?: ErrorOptions,\n ) {\n super(`Max size of ${maxSize} bytes exceeded`, options)\n }\n}\n\nexport class HashPassThrough extends Transform {\n private readonly hash: Hash\n #digest?: Buffer<ArrayBuffer>\n\n constructor(algorithm: string, options?: HashOptions) {\n super()\n this.hash = createHash(algorithm, options)\n }\n\n _transform(chunk: Uint8Array, _enc: BufferEncoding, cb: TransformCallback) {\n this.hash.update(chunk)\n cb(null, chunk)\n }\n\n _flush(cb: TransformCallback) {\n const digest = this.hash.digest()\n this.#digest = digest\n this.emit('hash', digest)\n cb()\n }\n\n get digest(): Buffer<ArrayBuffer> {\n if (this.#digest) return this.#digest\n throw new Error('Hash not yet computed. Wait for the stream to finish.')\n }\n}\n\nexport function decodeStream(\n stream: Readable,\n contentEncoding?: string | string[],\n): Readable\nexport function decodeStream(\n stream: AsyncIterable<Uint8Array>,\n contentEncoding?: string | string[],\n): AsyncIterable<Uint8Array> | Readable\nexport function decodeStream(\n stream: Readable | AsyncIterable<Uint8Array>,\n contentEncoding?: string | string[],\n): Readable | AsyncIterable<Uint8Array> {\n const decoders = createDecoders(contentEncoding)\n if (decoders.length === 0) return stream\n return pipeline([stream as Readable, ...decoders], () => {}) as Duplex\n}\n\n/**\n * Maximum number of content-codings accepted by {@link createDecoders} unless a\n * caller passes its own. Generous on purpose: a header may legally list several\n * codings (RFC 9110 §8.4.1).\n */\nexport const DEFAULT_MAX_CODINGS = 8\n\nexport type CreateDecodersOptions = {\n maxCodings?: number\n maxSize?: number\n maxSizeError?: () => Error\n}\n\n/**\n * Create a series of decoding streams based on the content-encoding header. The\n * resulting streams should be piped together to decode the content.\n *\n * @see {@link https://datatracker.ietf.org/doc/html/rfc9110#section-8.4.1}\n */\nexport function createDecoders(\n contentEncoding?: string | string[],\n {\n maxCodings = DEFAULT_MAX_CODINGS,\n maxSize = undefined,\n maxSizeError = undefined,\n }: CreateDecodersOptions = {},\n): Duplex[] {\n const codings = (\n Array.isArray(contentEncoding)\n ? contentEncoding.flatMap(commaSplit)\n : contentEncoding?.length\n ? contentEncoding.split(',')\n : []\n )\n .map(normalizeEncoding)\n // @NOTE\n // > The default (identity) encoding [...] is used only in the\n // > Accept-Encoding header, and SHOULD NOT be used in the\n // > Content-Encoding header.\n .filter(isNotIdentity)\n\n // Checked before any decoder is constructed.\n if (codings.length > maxCodings) {\n const seen =\n codings.slice(0, 4).join(', ') + (codings.length > 4 ? ', …' : '')\n throw new TypeError(\n `Unsupported content-encoding: ${codings.length} codings (${seen}) exceed the maximum of ${maxCodings}`,\n )\n }\n\n const decoders: Duplex[] = codings.map(createDecoder).reverse()\n\n // Size limits are applied only if maxSize is specified.\n if (maxSize == null) return decoders\n\n return [\n // @NOTE if there is only one decoder, no need to apply the decoding budget\n // limiter (MaxSizeChecker will be enough).\n ...(decoders.length <= 1\n ? decoders\n : decodingBudgetLimiter(\n decoders,\n // Using `maxSize * decoders.length` here would be equivalent to\n // adding a simple MaxSizeChecker for each decoder in the chain. The\n // threshold provides a guarantee that each re-encoding in the chain\n // was worth its size contribution (averaging 95% per decoder):\n //\n // <=> maxSize + maxSize * 0.95 + maxSize * 0.95^2 + …\n // <=> maxSize * (0.95^0 + 0.95^1 + 0.95^2 + …)\n //\n // @NOTE that we are not enforcing the compression ratio on every\n // decoder in the chain. Instead, we are applying a cumulative size\n // limit that accounts for the expected compression ratio of each\n // decoder.\n maxSize * decoders.reduce((acc, _, i) => acc + Math.pow(0.95, i), 0),\n maxSizeError,\n )),\n new MaxSizeChecker(maxSize, maxSizeError),\n ]\n}\n\nfunction decodingBudgetLimiter(\n decoders: Duplex[],\n totalSizeLimit: number,\n maxSizeError: () => Error = () => new MaxSizeError(totalSizeLimit),\n): Duplex[] {\n let totalSize = 0\n return decoders.flatMap((decoder) => [\n decoder,\n new Transform({\n transform(chunk, _encoding, callback) {\n totalSize += chunk.length\n if (totalSize > totalSizeLimit) callback(maxSizeError())\n else callback(null, chunk)\n },\n }),\n ])\n}\n\nfunction commaSplit(header: string): string[] {\n return header.split(',')\n}\n\nfunction normalizeEncoding(encoding: string) {\n // https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1\n // > All content-coding values are case-insensitive...\n return encoding.trim().toLowerCase()\n}\n\nfunction isNotIdentity<T extends string>(\n encoding: T,\n): encoding is Exclude<T, 'identity'> {\n return encoding !== 'identity'\n}\n\nfunction createDecoder(normalizedEncoding: string): Duplex {\n switch (normalizedEncoding) {\n // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2\n case 'gzip':\n case 'x-gzip':\n return createGunzip()\n case 'deflate':\n return createInflate()\n case 'br':\n return createBrotliDecompress()\n case 'identity':\n return new PassThrough()\n default:\n throw new TypeError(\n `Unsupported content-encoding: \"${normalizedEncoding}\"`,\n )\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/common",
3
- "version": "0.8.6",
3
+ "version": "0.8.8",
4
4
  "license": "MIT",
5
5
  "description": "Shared web-platform-friendly code for atproto libraries",
6
6
  "keywords": [
@@ -28,11 +28,11 @@
28
28
  "node": ">=22"
29
29
  },
30
30
  "dependencies": {
31
+ "@atproto/common-web": "^0.5.16",
32
+ "@atproto/lex-cbor": "^0.1.8",
33
+ "@atproto/lex-data": "^0.1.9",
31
34
  "multiformats": "^13.0.0",
32
- "pino": "^10.3.1",
33
- "@atproto/common-web": "^0.5.15",
34
- "@atproto/lex-cbor": "^0.1.7",
35
- "@atproto/lex-data": "^0.1.8"
35
+ "pino": "^10.3.1"
36
36
  },
37
37
  "devDependencies": {
38
38
  "uint8arrays": "^5.0.0",