(options?: StreamOptions)
| 115 | * @since 4.0.0 |
| 116 | */ |
| 117 | export const makeNdjson = (options?: StreamOptions): RpcSerialization["Service"] => { |
| 118 | const maxBufferSize = options?.maxBufferSize ?? defaultMaxBufferSize |
| 119 | return RpcSerialization.of({ |
| 120 | contentType: "application/ndjson", |
| 121 | includesFraming: true, |
| 122 | makeUnsafe: () => { |
| 123 | const decoder = new TextDecoder() |
| 124 | let buffer = "" |
| 125 | const failMaxBufferSize = (maxBufferSize: number): never => { |
| 126 | buffer = "" |
| 127 | throw new MaxBufferSizeExceeded({ maxBufferSize }) |
| 128 | } |
| 129 | return ({ |
| 130 | decode: (bytes) => { |
| 131 | buffer += typeof bytes === "string" ? bytes : decoder.decode(bytes, { stream: true }) |
| 132 | let position = 0 |
| 133 | let nlIndex = buffer.indexOf("\n", position) |
| 134 | const items: Array<unknown> = [] |
| 135 | while (nlIndex !== -1) { |
| 136 | if (isBufferSizeExceeded(nlIndex - position, maxBufferSize)) { |
| 137 | failMaxBufferSize(maxBufferSize) |
| 138 | } |
| 139 | const item = JSON.parse(buffer.slice(position, nlIndex)) |
| 140 | items.push(item) |
| 141 | position = nlIndex + 1 |
| 142 | nlIndex = buffer.indexOf("\n", position) |
| 143 | } |
| 144 | buffer = buffer.slice(position) |
| 145 | if (isBufferSizeExceeded(buffer.length, maxBufferSize)) { |
| 146 | failMaxBufferSize(maxBufferSize) |
| 147 | } |
| 148 | return items |
| 149 | }, |
| 150 | encode: (response) => { |
| 151 | if (Array.isArray(response)) { |
| 152 | if (response.length === 0) return undefined |
| 153 | let data = "" |
| 154 | for (let i = 0; i < response.length; i++) { |
| 155 | data += JSON.stringify(response[i]) + "\n" |
| 156 | } |
| 157 | return data |
| 158 | } |
| 159 | return JSON.stringify(response) + "\n" |
| 160 | } |
| 161 | }) |
| 162 | } |
| 163 | }) |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Default newline-delimited JSON RPC serialization. |
no test coverage detected