2121
resolveBuffer(response, id, view);
2122
}
2123
2124
-function processFullRow(
2124
+function processFullBinaryRow(
2125
response: Response,
2126
id: number,
2127
tag: number,
2183
row += readPartialStringChunk(stringDecoder, buffer[i]);
2184
}
2185
row += readFinalStringChunk(stringDecoder, chunk);
2186
+ processFullStringRow(response, id, tag, row);
2187
+}
2188
+
2189
+function processFullStringRow(
2190
+ response: Response,
2191
+ id: number,
2192
+ tag: number,
2193
+ row: string,
2194
+): void {
2195
switch (tag) {
2196
case 73 /* "I" */: {
2197
resolveModule(response, id, row);
2394
// We found the last chunk of the row
2395
const length = lastIdx - i;
2396
const lastChunk = new Uint8Array(chunk.buffer, offset, length);
2388
- processFullRow(response, rowID, rowTag, buffer, lastChunk);
2397
+ processFullBinaryRow(response, rowID, rowTag, buffer, lastChunk);
2398
// Reset state machine for a new row
2399
i = lastIdx;
2400
if (rowState === ROW_CHUNK_BY_NEWLINE) {
2424
response._rowLength = rowLength;
2425
}
2426
2427
+export function processStringChunk(response: Response, chunk: string): void {
2428
+ // This is a fork of processBinaryChunk that takes a string as input.
2429
+ // This can't be just any binary chunk coverted to a string. It needs to be
2430
+ // in the same offsets given from the Flight Server. E.g. if it's shifted by
2431
+ // one byte then it won't line up to the UCS-2 encoding. It also needs to
2432
+ // be valid Unicode. Also binary chunks cannot use this even if they're
2433
+ // value Unicode. Large strings are encoded as binary and cannot be passed
2434
+ // here. Basically, only if Flight Server gave you this string as a chunk,
2435
+ // you can use it here.
2436
+ let i = 0;
2437
+ let rowState = response._rowState;
2438
+ let rowID = response._rowID;
2439
+ let rowTag = response._rowTag;
2440
+ let rowLength = response._rowLength;
2441
+ const buffer = response._buffer;
2442
+ const chunkLength = chunk.length;
2443
+ while (i < chunkLength) {
2444
+ let lastIdx = -1;
2445
+ switch (rowState) {
2446
+ case ROW_ID: {
2447
+ const byte = chunk.charCodeAt(i++);
2448
+ if (byte === 58 /* ":" */) {
2449
+ // Finished the rowID, next we'll parse the tag.
2450
+ rowState = ROW_TAG;
2451
+ } else {
2452
+ rowID = (rowID << 4) | (byte > 96 ? byte - 87 : byte - 48);
2453
+ }
2454
+ continue;
2455
+ }
2456
+ case ROW_TAG: {
2457
+ const resolvedRowTag = chunk.charCodeAt(i);
2458
+ if (
2459
+ resolvedRowTag === 84 /* "T" */ ||
2460
+ (enableBinaryFlight &&
2461
+ (resolvedRowTag === 65 /* "A" */ ||
2462
+ resolvedRowTag === 79 /* "O" */ ||
2463
+ resolvedRowTag === 111 /* "o" */ ||
2464
+ resolvedRowTag === 85 /* "U" */ ||
2465
+ resolvedRowTag === 83 /* "S" */ ||
2466
+ resolvedRowTag === 115 /* "s" */ ||
2467
+ resolvedRowTag === 76 /* "L" */ ||
2468
+ resolvedRowTag === 108 /* "l" */ ||
2469
+ resolvedRowTag === 71 /* "G" */ ||
2470
+ resolvedRowTag === 103 /* "g" */ ||
2471
+ resolvedRowTag === 77 /* "M" */ ||
2472
+ resolvedRowTag === 109 /* "m" */ ||
2473
+ resolvedRowTag === 86)) /* "V" */
2474
+ ) {
2475
+ rowTag = resolvedRowTag;
2476
+ rowState = ROW_LENGTH;
2477
+ i++;
2478
+ } else if (
2479
+ (resolvedRowTag > 64 && resolvedRowTag < 91) /* "A"-"Z" */ ||
2480
+ resolvedRowTag === 114 /* "r" */ ||
2481
+ resolvedRowTag === 120 /* "x" */
2482
+ ) {
2483
+ rowTag = resolvedRowTag;
2484
+ rowState = ROW_CHUNK_BY_NEWLINE;
2485
+ i++;
2486
+ } else {
2487
+ rowTag = 0;
2488
+ rowState = ROW_CHUNK_BY_NEWLINE;
2489
+ // This was an unknown tag so it was probably part of the data.
2490
+ }
2491
+ continue;
2492
+ }
2493
+ case ROW_LENGTH: {
2494
+ const byte = chunk.charCodeAt(i++);
2495
+ if (byte === 44 /* "," */) {
2496
+ // Finished the rowLength, next we'll buffer up to that length.
2497
+ rowState = ROW_CHUNK_BY_LENGTH;
2498
+ } else {
2499
+ rowLength = (rowLength << 4) | (byte > 96 ? byte - 87 : byte - 48);
2500
+ }
2501
+ continue;
2502
+ }
2503
+ case ROW_CHUNK_BY_NEWLINE: {
2504
+ // We're looking for a newline
2505
+ lastIdx = chunk.indexOf('\n', i);
2506
+ break;
2507
+ }
2508
+ case ROW_CHUNK_BY_LENGTH: {
2509
+ if (rowTag !== 84) {
2510
+ throw new Error(
2511
+ 'Binary RSC chunks cannot be encoded as strings. ' +
2512
+ 'This is a bug in the wiring of the React streams.',
2513
+ );
2514
+ }
2515
+ // For a large string by length, we don't know how many unicode characters
2516
+ // we are looking for but we can assume that the raw string will be its own
2517
+ // chunk. We add extra validation that the length is at least within the
2518
+ // possible byte range it could possibly be to catch mistakes.
2519
+ if (rowLength < chunk.length || chunk.length > rowLength * 3) {
2520
+ throw new Error(
2521
+ 'String chunks need to be passed in their original shape. ' +
2522
+ 'Not split into smaller string chunks. ' +
2523
+ 'This is a bug in the wiring of the React streams.',
2524
+ );
2525
+ }
2526
+ lastIdx = chunk.length;
2527
+ break;
2528
+ }
2529
+ }
2530
+ if (lastIdx > -1) {
2531
+ // We found the last chunk of the row
2532
+ if (buffer.length > 0) {
2533
+ // If we had a buffer already, it means that this chunk was split up into
2534
+ // binary chunks preceeding it.
2535
+ throw new Error(
2536
+ 'String chunks need to be passed in their original shape. ' +
2537
+ 'Not split into smaller string chunks. ' +
2538
+ 'This is a bug in the wiring of the React streams.',
2539
+ );
2540
+ }
2541
+ const lastChunk = chunk.slice(i, lastIdx);
2542
+ processFullStringRow(response, rowID, rowTag, lastChunk);
2543
+ // Reset state machine for a new row
2544
+ i = lastIdx;
2545
+ if (rowState === ROW_CHUNK_BY_NEWLINE) {
2546
+ // If we're trailing by a newline we need to skip it.
2547
+ i++;
2548
+ }
2549
+ rowState = ROW_ID;
2550
+ rowTag = 0;
2551
+ rowID = 0;
2552
+ rowLength = 0;
2553
+ buffer.length = 0;
2554
+ } else if (chunk.length !== i) {
2555
+ // The rest of this row is in a future chunk. We only support passing the
2556
+ // string from chunks in their entirety. Not split up into smaller string chunks.
2557
+ // We could support this by buffering them but we shouldn't need to for
2558
+ // this use case.
2559
+ throw new Error(
2560
+ 'String chunks need to be passed in their original shape. ' +
2561
+ 'Not split into smaller string chunks. ' +
2562
+ 'This is a bug in the wiring of the React streams.',
2563
+ );
2564
+ }
2565
+ }
2566
+ response._rowState = rowState;
2567
+ response._rowID = rowID;
2568
+ response._rowTag = rowTag;
2569
+ response._rowLength = rowLength;
2570
+}
2571
+
2572
function parseModel<T>(response: Response, json: UninitializedModel): T {
2573
return JSON.parse(json, response._fromJSON);
2574
}