[Flight] Simplify Relay row protocol (#20168)
* Simplify Relay protocol integration * Encode Relay rows as tuples instead of objects This is slightly more compact and more ressembles more closely the encoding we use for the raw stream protocol.
Sebastian Markbåge committed
Nov 10, 2020 at 22:54 UTC
35e53b4653fdfb9c1712ce4c07037f5f276b4e91
13 files changed
+132
-208
packages/react-transport-dom-relay/src/ReactFlightDOMRelayClient.js
+18
-1
@@ -7,10 +7,27 @@
7
* @flow
8
*/
9
10
-export {
10
+import type {RowEncoding} from './ReactFlightDOMRelayProtocol';
11
+
12
+import type {Response} from 'react-client/src/ReactFlightClient';
13
+
14
+import {
15
createResponse,
16
resolveModel,
17
resolveModule,
18
resolveError,
19
close,
20
} from 'react-client/src/ReactFlightClient';
21
+
22
+export {createResponse, close};
23
+
24
+export function resolveRow(response: Response, chunk: RowEncoding): void {
25
+ if (chunk[0] === 'J') {
26
+ resolveModel(response, chunk[1], chunk[2]);
27
+ } else if (chunk[0] === 'M') {
28
+ resolveModule(response, chunk[1], chunk[2]);
29
+ } else {
30
+ // $FlowFixMe: Flow doesn't support disjoint unions on tuples.
31
+ resolveError(response, chunk[1], chunk[2].message, chunk[2].stack);
32
+ }
33
+}
packages/react-transport-dom-relay/src/ReactFlightDOMRelayClientHostConfig.js
+1
-1
@@ -26,7 +26,7 @@ export {
26
27
export type {ModuleMetaData} from 'ReactFlightDOMRelayClientIntegration';
28
29
-export opaque type UninitializedModel = JSONValue;
29
+export type UninitializedModel = JSONValue;
30
31
export type Response = ResponseBase;
32
packages/react-transport-dom-relay/src/ReactFlightDOMRelayProtocol.js
new
+31
@@ -0,0 +1,31 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+import type {ModuleMetaData} from 'ReactFlightDOMRelayServerIntegration';
11
+
12
+export type JSONValue =
13
+ | string
14
+ | number
15
+ | boolean
16
+ | null
17
+ | {+[key: string]: JSONValue}
18
+ | Array<JSONValue>;
19
+
20
+export type RowEncoding =
21
+ | ['J', number, JSONValue]
22
+ | ['M', number, ModuleMetaData]
23
+ | [
24
+ 'E',
25
+ number,
26
+ {
27
+ message: string,
28
+ stack: string,
29
+ ...
30
+ },
31
+ ];
packages/react-transport-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js
+12
-53
@@ -7,6 +7,8 @@
7
* @flow
8
*/
9
10
+import type {RowEncoding, JSONValue} from './ReactFlightDOMRelayProtocol';
11
+
12
import type {Request, ReactModel} from 'react-server/src/ReactFlightServer';
13
14
import JSResourceReference from 'JSResourceReference';
@@ -22,9 +24,7 @@ import type {
24
import {resolveModelToJSON} from 'react-server/src/ReactFlightServer';
25
26
import {
25
- emitModel,
26
- emitModule,
27
- emitError,
27
+ emitRow,
28
resolveModuleMetaData as resolveModuleMetaDataImpl,
29
} from 'ReactFlightDOMRelayServerIntegration';
30
@@ -45,34 +45,7 @@ export function resolveModuleMetaData<T>(
45
return resolveModuleMetaDataImpl(config, resource);
46
}
47
48
-type JSONValue =
49
- | string
50
- | number
51
- | boolean
52
- | null
53
- | {+[key: string]: JSONValue}
54
- | Array<JSONValue>;
55
-
56
-export type Chunk =
57
- | {
58
- type: 'json',
59
- id: number,
60
- json: JSONValue,
61
- }
62
- | {
63
- type: 'module',
64
- id: number,
65
- json: ModuleMetaData,
66
- }
67
- | {
68
- type: 'error',
69
- id: number,
70
- json: {
71
- message: string,
72
- stack: string,
73
- ...
74
- },
75
- };
48
+export type Chunk = RowEncoding;
49
50
export function processErrorChunk(
51
request: Request,
@@ -80,14 +53,14 @@ export function processErrorChunk(
53
message: string,
54
stack: string,
55
): Chunk {
83
- return {
84
- type: 'error',
85
- id: id,
86
- json: {
56
+ return [
57
+ 'E',
58
+ id,
59
+ {
60
message,
61
stack,
62
},
90
- };
63
+ ];
64
}
65
66
function convertModelToJSON(
@@ -126,11 +99,7 @@ export function processModelChunk(
99
model: ReactModel,
100
): Chunk {
101
const json = convertModelToJSON(request, {}, '', model);
129
- return {
130
- type: 'json',
131
- id: id,
132
- json: json,
133
- };
102
+ return ['J', id, json];
103
}
104
105
export function processModuleChunk(
@@ -139,11 +108,7 @@ export function processModuleChunk(
108
moduleMetaData: ModuleMetaData,
109
): Chunk {
110
// The moduleMetaData is already a JSON serializable value.
142
- return {
143
- type: 'module',
144
- id: id,
145
- json: moduleMetaData,
146
- };
111
+ return ['M', id, moduleMetaData];
112
}
113
114
export function scheduleWork(callback: () => void) {
@@ -155,13 +120,7 @@ export function flushBuffered(destination: Destination) {}
120
export function beginWriting(destination: Destination) {}
121
122
export function writeChunk(destination: Destination, chunk: Chunk): boolean {
158
- if (chunk.type === 'json') {
159
- emitModel(destination, chunk.id, chunk.json);
160
- } else if (chunk.type === 'module') {
161
- emitModule(destination, chunk.id, chunk.json);
162
- } else {
163
- emitError(destination, chunk.id, chunk.json.message, chunk.json.stack);
164
- }
123
+ emitRow(destination, chunk);
124
return true;
125
}
126
packages/react-transport-dom-relay/src/__mocks__/ReactFlightDOMRelayServerIntegration.js
+2
-20
@@ -8,26 +8,8 @@
8
'use strict';
9
10
const ReactFlightDOMRelayServerIntegration = {
11
- emitModel(destination, id, json) {
12
- destination.push({
13
- type: 'json',
14
- id: id,
15
- json: json,
16
- });
17
- },
18
- emitModule(destination, id, json) {
19
- destination.push({
20
- type: 'module',
21
- id: id,
22
- json: json,
23
- });
24
- },
25
- emitError(destination, id, message, stack) {
26
- destination.push({
27
- type: 'error',
28
- id: id,
29
- json: {message, stack},
30
- });
11
+ emitRow(destination, json) {
12
+ destination.push(json);
13
},
14
close(destination) {},
15
resolveModuleMetaData(config, resource) {
packages/react-transport-dom-relay/src/__tests__/ReactFlightDOMRelay-test.internal.js
+1
-12
@@ -30,18 +30,7 @@ describe('ReactFlightDOMRelay', () => {
30
const response = ReactDOMFlightRelayClient.createResponse();
31
for (let i = 0; i < data.length; i++) {
32
const chunk = data[i];
33
- if (chunk.type === 'json') {
34
- ReactDOMFlightRelayClient.resolveModel(response, chunk.id, chunk.json);
35
- } else if (chunk.type === 'module') {
36
- ReactDOMFlightRelayClient.resolveModule(response, chunk.id, chunk.json);
37
- } else {
38
- ReactDOMFlightRelayClient.resolveError(
39
- response,
40
- chunk.id,
41
- chunk.json.message,
42
- chunk.json.stack,
43
- );
44
- }
33
+ ReactDOMFlightRelayClient.resolveRow(response, chunk);
34
}
35
ReactDOMFlightRelayClient.close(response);
36
const model = response.readRoot();
packages/react-transport-native-relay/src/ReactFlightNativeRelayClient.js
+18
-1
@@ -7,10 +7,27 @@
7
* @flow
8
*/
9
10
-export {
10
+import type {RowEncoding} from './ReactFlightNativeRelayProtocol';
11
+
12
+import type {Response} from 'react-client/src/ReactFlightClient';
13
+
14
+import {
15
createResponse,
16
resolveModel,
17
resolveModule,
18
resolveError,
19
close,
20
} from 'react-client/src/ReactFlightClient';
21
+
22
+export {createResponse, close};
23
+
24
+export function resolveRow(response: Response, chunk: RowEncoding): void {
25
+ if (chunk[0] === 'J') {
26
+ resolveModel(response, chunk[1], chunk[2]);
27
+ } else if (chunk[0] === 'M') {
28
+ resolveModule(response, chunk[1], chunk[2]);
29
+ } else {
30
+ // $FlowFixMe: Flow doesn't support disjoint unions on tuples.
31
+ resolveError(response, chunk[1], chunk[2].message, chunk[2].stack);
32
+ }
33
+}
packages/react-transport-native-relay/src/ReactFlightNativeRelayClientHostConfig.js
+1
-1
@@ -26,7 +26,7 @@ export {
26
27
export type {ModuleMetaData} from 'ReactFlightNativeRelayClientIntegration';
28
29
-export opaque type UninitializedModel = JSONValue;
29
+export type UninitializedModel = JSONValue;
30
31
export type Response = ResponseBase;
32
packages/react-transport-native-relay/src/ReactFlightNativeRelayProtocol.js
new
+31
@@ -0,0 +1,31 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+import type {ModuleMetaData} from 'ReactFlightNativeRelayServerIntegration';
11
+
12
+export type JSONValue =
13
+ | string
14
+ | number
15
+ | boolean
16
+ | null
17
+ | {+[key: string]: JSONValue}
18
+ | Array<JSONValue>;
19
+
20
+export type RowEncoding =
21
+ | ['J', number, JSONValue]
22
+ | ['M', number, ModuleMetaData]
23
+ | [
24
+ 'E',
25
+ number,
26
+ {
27
+ message: string,
28
+ stack: string,
29
+ ...
30
+ },
31
+ ];
packages/react-transport-native-relay/src/ReactFlightNativeRelayServerHostConfig.js
+12
-53
@@ -7,6 +7,8 @@
7
* @flow
8
*/
9
10
+import type {RowEncoding, JSONValue} from './ReactFlightNativeRelayProtocol';
11
+
12
import type {Request, ReactModel} from 'react-server/src/ReactFlightServer';
13
14
import JSResourceReferenceImpl from 'JSResourceReferenceImpl';
@@ -22,9 +24,7 @@ import type {
24
import {resolveModelToJSON} from 'react-server/src/ReactFlightServer';
25
26
import {
25
- emitModel,
26
- emitModule,
27
- emitError,
27
+ emitRow,
28
resolveModuleMetaData as resolveModuleMetaDataImpl,
29
} from 'ReactFlightNativeRelayServerIntegration';
30
@@ -45,34 +45,7 @@ export function resolveModuleMetaData<T>(
45
return resolveModuleMetaDataImpl(config, resource);
46
}
47
48
-type JSONValue =
49
- | string
50
- | number
51
- | boolean
52
- | null
53
- | {+[key: string]: JSONValue}
54
- | Array<JSONValue>;
55
-
56
-export type Chunk =
57
- | {
58
- type: 'json',
59
- id: number,
60
- json: JSONValue,
61
- }
62
- | {
63
- type: 'module',
64
- id: number,
65
- json: ModuleMetaData,
66
- }
67
- | {
68
- type: 'error',
69
- id: number,
70
- json: {
71
- message: string,
72
- stack: string,
73
- ...
74
- },
75
- };
48
+export type Chunk = RowEncoding;
49
50
export function processErrorChunk(
51
request: Request,
@@ -80,14 +53,14 @@ export function processErrorChunk(
53
message: string,
54
stack: string,
55
): Chunk {
83
- return {
84
- type: 'error',
85
- id: id,
86
- json: {
56
+ return [
57
+ 'E',
58
+ id,
59
+ {
60
message,
61
stack,
62
},
90
- };
63
+ ];
64
}
65
66
function convertModelToJSON(
@@ -126,11 +99,7 @@ export function processModelChunk(
99
model: ReactModel,
100
): Chunk {
101
const json = convertModelToJSON(request, {}, '', model);
129
- return {
130
- type: 'json',
131
- id: id,
132
- json: json,
133
- };
102
+ return ['J', id, json];
103
}
104
105
export function processModuleChunk(
@@ -139,11 +108,7 @@ export function processModuleChunk(
108
moduleMetaData: ModuleMetaData,
109
): Chunk {
110
// The moduleMetaData is already a JSON serializable value.
142
- return {
143
- type: 'module',
144
- id: id,
145
- json: moduleMetaData,
146
- };
111
+ return ['M', id, moduleMetaData];
112
}
113
114
export function scheduleWork(callback: () => void) {
@@ -155,13 +120,7 @@ export function flushBuffered(destination: Destination) {}
120
export function beginWriting(destination: Destination) {}
121
122
export function writeChunk(destination: Destination, chunk: Chunk): boolean {
158
- if (chunk.type === 'json') {
159
- emitModel(destination, chunk.id, chunk.json);
160
- } else if (chunk.type === 'module') {
161
- emitModule(destination, chunk.id, chunk.json);
162
- } else {
163
- emitError(destination, chunk.id, chunk.json.message, chunk.json.stack);
164
- }
123
+ emitRow(destination, chunk);
124
return true;
125
}
126
packages/react-transport-native-relay/src/__mocks__/ReactFlightNativeRelayServerIntegration.js
+2
-20
@@ -8,26 +8,8 @@
8
'use strict';
9
10
const ReactFlightNativeRelayServerIntegration = {
11
- emitModel(destination, id, json) {
12
- destination.push({
13
- type: 'json',
14
- id: id,
15
- json: json,
16
- });
17
- },
18
- emitModule(destination, id, json) {
19
- destination.push({
20
- type: 'module',
21
- id: id,
22
- json: json,
23
- });
24
- },
25
- emitError(destination, id, message, stack) {
26
- destination.push({
27
- type: 'error',
28
- id: id,
29
- json: {message, stack},
30
- });
11
+ emitRow(destination, json) {
12
+ destination.push(json);
13
},
14
close(destination) {},
15
resolveModuleMetaData(config, resource) {
packages/react-transport-native-relay/src/__tests__/ReactFlightNativeRelay-test.internal.js
+1
-20
@@ -45,26 +45,7 @@ describe('ReactFlightNativeRelay', () => {
45
const response = ReactNativeFlightRelayClient.createResponse();
46
for (let i = 0; i < data.length; i++) {
47
const chunk = data[i];
48
- if (chunk.type === 'json') {
49
- ReactNativeFlightRelayClient.resolveModel(
50
- response,
51
- chunk.id,
52
- chunk.json,
53
- );
54
- } else if (chunk.type === 'module') {
55
- ReactNativeFlightRelayClient.resolveModule(
56
- response,
57
- chunk.id,
58
- chunk.json,
59
- );
60
- } else {
61
- ReactNativeFlightRelayClient.resolveError(
62
- response,
63
- chunk.id,
64
- chunk.json.message,
65
- chunk.json.stack,
66
- );
67
- }
48
+ ReactNativeFlightRelayClient.resolveRow(response, chunk);
49
}
50
ReactNativeFlightRelayClient.close(response);
51
const model = response.readRoot();
scripts/flow/react-relay-hooks.js
+2
-26
@@ -35,22 +35,10 @@ declare module 'JSResourceReferenceImpl' {
35
declare module 'ReactFlightDOMRelayServerIntegration' {
36
declare export opaque type Destination;
37
declare export opaque type BundlerConfig;
38
- declare export function emitModel(
38
+ declare export function emitRow(
39
destination: Destination,
40
- id: number,
40
json: JSONValue,
41
): void;
43
- declare export function emitModule(
44
- destination: Destination,
45
- id: number,
46
- json: ModuleMetaData,
47
- ): void;
48
- declare export function emitError(
49
- destination: Destination,
50
- id: number,
51
- message: string,
52
- stack: string,
53
- ): void;
42
declare export function close(destination: Destination): void;
43
44
declare export type ModuleMetaData = JSONValue;
@@ -76,22 +64,10 @@ declare module 'ReactFlightDOMRelayClientIntegration' {
64
declare module 'ReactFlightNativeRelayServerIntegration' {
65
declare export opaque type Destination;
66
declare export opaque type BundlerConfig;
79
- declare export function emitModel(
67
+ declare export function emitRow(
68
destination: Destination,
81
- id: number,
69
json: JSONValue,
70
): void;
84
- declare export function emitModule(
85
- destination: Destination,
86
- id: number,
87
- json: ModuleMetaData,
88
- ): void;
89
- declare export function emitError(
90
- destination: Destination,
91
- id: number,
92
- message: string,
93
- stack: string,
94
- ): void;
71
declare export function close(destination: Destination): void;
72
73
declare export type ModuleMetaData = JSONValue;