Skip to content

Commit e295b77

Browse files
committed
fix(marshal): Add CapData encode/decode consistency assertions
Mirrors those of Smallcaps
1 parent d83be67 commit e295b77

File tree

1 file changed

+62
-5
lines changed

1 file changed

+62
-5
lines changed

packages/marshal/src/encodeToCapData.js

+62-5
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ export { QCLASS };
5353
*/
5454
const hasQClass = encoded => hasOwnPropertyOf(encoded, QCLASS);
5555

56+
/**
57+
* @param {Encoding} encoded
58+
* @param {string} qclass
59+
* @returns {boolean}
60+
*/
61+
const qclassMatches = (encoded, qclass) =>
62+
isObject(encoded) &&
63+
!isArray(encoded) &&
64+
hasQClass(encoded) &&
65+
encoded[QCLASS] === qclass;
66+
5667
/**
5768
* @typedef {object} EncodeToCapDataOptions
5869
* @property {(
@@ -203,13 +214,40 @@ export const makeEncodeToCapData = ({
203214
};
204215
}
205216
case 'remotable': {
206-
return encodeRemotableToCapData(passable, encodeToCapDataRecur);
217+
const encoded = encodeRemotableToCapData(
218+
passable,
219+
encodeToCapDataRecur,
220+
);
221+
if (qclassMatches(encoded, 'slot')) {
222+
return encoded;
223+
}
224+
assert.fail(
225+
X`internal: Remotable encoding must be an object with ${q(
226+
QCLASS,
227+
)} ${q('slot')}: ${encoded}`,
228+
);
207229
}
208230
case 'error': {
209-
return encodeErrorToCapData(passable, encodeToCapDataRecur);
231+
const encoded = encodeErrorToCapData(passable, encodeToCapDataRecur);
232+
if (qclassMatches(encoded, 'error')) {
233+
return encoded;
234+
}
235+
assert.fail(
236+
X`internal: Error encoding must be an object with ${q(QCLASS)} ${q(
237+
'error',
238+
)}: ${encoded}`,
239+
);
210240
}
211241
case 'promise': {
212-
return encodePromiseToCapData(passable, encodeToCapDataRecur);
242+
const encoded = encodePromiseToCapData(passable, encodeToCapDataRecur);
243+
if (qclassMatches(encoded, 'slot')) {
244+
return encoded;
245+
}
246+
assert.fail(
247+
X`internal: Promise encoding must be an object with ${q(QCLASS)} ${q(
248+
'slot',
249+
)}: ${encoded}`,
250+
);
213251
}
214252
default: {
215253
assert.fail(
@@ -370,14 +408,33 @@ export const makeDecodeFromCapData = ({
370408
}
371409

372410
case 'error': {
373-
return decodeErrorFromCapData(jsonEncoded, decodeFromCapData);
411+
const decoded = decodeErrorFromCapData(
412+
jsonEncoded,
413+
decodeFromCapData,
414+
);
415+
if (passStyleOf(decoded) === 'error') {
416+
return decoded;
417+
}
418+
assert.fail(
419+
X`internal: decodeErrorFromCapData option must return an error: ${decoded}`,
420+
);
374421
}
375422

376423
case 'slot': {
377424
// See note above about how the current encoding cannot reliably
378425
// distinguish which we should call, so in the non-default case
379426
// both must be the same and it doesn't matter which we call.
380-
return decodeRemotableFromCapData(jsonEncoded, decodeFromCapData);
427+
const decoded = decodeRemotableFromCapData(
428+
jsonEncoded,
429+
decodeFromCapData,
430+
);
431+
const passStyle = passStyleOf(decoded);
432+
if (passStyle === 'remotable' || passStyle === 'promise') {
433+
return decoded;
434+
}
435+
assert.fail(
436+
X`internal: decodeRemotableFromCapData option must return a remotable or promise: ${decoded}`,
437+
);
381438
}
382439

383440
case 'hilbert': {

0 commit comments

Comments
 (0)