@endo/marshal
Advanced tools
Comparing version 0.7.4 to 0.7.5
@@ -6,2 +6,12 @@ # Change Log | ||
### [0.7.5](https://github.com/endojs/endo/compare/@endo/marshal@0.7.4...@endo/marshal@0.7.5) (2022-09-27) | ||
### Bug Fixes | ||
* import endo/init, not lockdown ([#1299](https://github.com/endojs/endo/issues/1299)) ([f6eb272](https://github.com/endojs/endo/commit/f6eb2723b4f3e48e6c51d98194798a3dcdfb94a0)) | ||
* Provide smallcaps encoding in addition to capData ([#1282](https://github.com/endojs/endo/issues/1282)) ([233dbe2](https://github.com/endojs/endo/commit/233dbe2e159e454fd3bcdd0e08b15c4439b56ba7)) | ||
### [0.7.4](https://github.com/endojs/endo/compare/@endo/marshal@0.7.3...@endo/marshal@0.7.4) (2022-09-14) | ||
@@ -8,0 +18,0 @@ |
User-visible changes in `@endo/marshal`: | ||
# v0.7.5 (2022-09-26) | ||
- Adds "smallcaps" encoding | ||
([#1282](https://github.com/endojs/endo/issues/1282)) | ||
([233dbe2](https://github.com/endojs/endo/commit/233dbe2e159e454fd3bcdd0e08b15c4439b56ba7)) | ||
# v0.7.1 (2022-18-25) | ||
@@ -4,0 +10,0 @@ |
{ | ||
"name": "@endo/marshal", | ||
"version": "0.7.4", | ||
"version": "0.7.5", | ||
"description": "marshal", | ||
@@ -40,9 +40,10 @@ "type": "module", | ||
"dependencies": { | ||
"@endo/eventual-send": "^0.16.4", | ||
"@endo/nat": "^4.1.19", | ||
"@endo/promise-kit": "^0.2.48" | ||
"@endo/eventual-send": "^0.16.5", | ||
"@endo/nat": "^4.1.20", | ||
"@endo/promise-kit": "^0.2.49" | ||
}, | ||
"devDependencies": { | ||
"@endo/lockdown": "^0.1.20", | ||
"@endo/ses-ava": "^0.2.32", | ||
"@endo/init": "^0.5.49", | ||
"@endo/lockdown": "^0.1.21", | ||
"@endo/ses-ava": "^0.2.33", | ||
"ava": "^3.12.1", | ||
@@ -78,3 +79,3 @@ "c8": "^7.7.3" | ||
}, | ||
"gitHead": "44b17d92334592b2da54d91dba73f28db513412a" | ||
"gitHead": "2d3f1a5c472aaef102e8919cbf8d0c53238d155f" | ||
} |
@@ -107,3 +107,3 @@ /* eslint-disable no-use-before-define */ | ||
default: { | ||
assert.fail(X`unrecognized passStyle ${passStyle}`); | ||
assert.fail(X`internal: Unrecognized passStyle ${passStyle}`); | ||
} | ||
@@ -110,0 +110,0 @@ } |
export function makeEncodeToCapData({ encodeRemotableToCapData, encodePromiseToCapData, encodeErrorToCapData, }?: EncodeToCapDataOptions | undefined): (passable: Passable) => any; | ||
export function makeDecodeFromCapData({ decodeRemotableFromCapData, decodePromiseFromCapData, decodeErrorFromCapData, }?: DecodeOptions | undefined): (encoded: any) => Passable; | ||
export type MakeMarshalOptions = import('./types.js').MakeMarshalOptions; | ||
export type ConvertSlotToVal<Slot> = import('./types.js').ConvertSlotToVal<Slot>; | ||
export type ConvertValToSlot<Slot> = import('./types.js').ConvertValToSlot<Slot>; | ||
export type Serialize<Slot> = import('./types.js').Serialize<Slot>; | ||
export type Unserialize<Slot> = import('./types.js').Unserialize<Slot>; | ||
export type Passable = import('./types.js').Passable; | ||
export type InterfaceSpec = import('./types.js').InterfaceSpec; | ||
export type Encoding = import('./types.js').Encoding; | ||
@@ -14,10 +8,10 @@ export type Remotable = import('./types.js').Remotable; | ||
export type EncodeToCapDataOptions = { | ||
encodeRemotableToCapData?: ((remotable: object) => any) | undefined; | ||
encodePromiseToCapData?: ((promise: object) => any) | undefined; | ||
encodeErrorToCapData?: ((error: object) => any) | undefined; | ||
encodeRemotableToCapData?: ((remotable: Remotable, encodeRecur: (p: Passable) => any) => any) | undefined; | ||
encodePromiseToCapData?: ((promise: Promise<any>, encodeRecur: (p: Passable) => any) => any) | undefined; | ||
encodeErrorToCapData?: ((error: Error, encodeRecur: (p: Passable) => any) => any) | undefined; | ||
}; | ||
export type DecodeOptions = { | ||
decodeRemotableFromCapData?: ((encodedRemotable: any) => (Promise<any> | Remotable)) | undefined; | ||
decodePromiseFromCapData?: ((encodedPromise: any) => (Promise<any> | Remotable)) | undefined; | ||
decodeErrorFromCapData?: ((encodedError: any) => Error) | undefined; | ||
decodeRemotableFromCapData?: ((encodedRemotable: any, decodeRecur: (e: any) => Passable) => (Promise<any> | Remotable)) | undefined; | ||
decodePromiseFromCapData?: ((encodedPromise: any, decodeRecur: (e: any) => Passable) => (Promise<any> | Remotable)) | undefined; | ||
decodeErrorFromCapData?: ((encodedError: any, decodeRecur: (e: any) => Passable) => Error) | undefined; | ||
}; | ||
@@ -24,0 +18,0 @@ /** |
@@ -26,9 +26,3 @@ // @ts-check | ||
/** @typedef {import('./types.js').MakeMarshalOptions} MakeMarshalOptions */ | ||
/** @template Slot @typedef {import('./types.js').ConvertSlotToVal<Slot>} ConvertSlotToVal */ | ||
/** @template Slot @typedef {import('./types.js').ConvertValToSlot<Slot>} ConvertValToSlot */ | ||
/** @template Slot @typedef {import('./types.js').Serialize<Slot>} Serialize */ | ||
/** @template Slot @typedef {import('./types.js').Unserialize<Slot>} Unserialize */ | ||
/** @typedef {import('./types.js').Passable} Passable */ | ||
/** @typedef {import('./types.js').InterfaceSpec} InterfaceSpec */ | ||
/** @typedef {import('./types.js').Encoding} Encoding */ | ||
@@ -64,5 +58,14 @@ /** @typedef {import('./types.js').Remotable} Remotable */ | ||
* @typedef {object} EncodeToCapDataOptions | ||
* @property {(remotable: object) => Encoding} [encodeRemotableToCapData] | ||
* @property {(promise: object) => Encoding} [encodePromiseToCapData] | ||
* @property {(error: object) => Encoding} [encodeErrorToCapData] | ||
* @property {( | ||
* remotable: Remotable, | ||
* encodeRecur: (p: Passable) => Encoding | ||
* ) => Encoding} [encodeRemotableToCapData] | ||
* @property {( | ||
* promise: Promise, | ||
* encodeRecur: (p: Passable) => Encoding | ||
* ) => Encoding} [encodePromiseToCapData] | ||
* @property {( | ||
* error: Error, | ||
* encodeRecur: (p: Passable) => Encoding | ||
* ) => Encoding} [encodeErrorToCapData] | ||
*/ | ||
@@ -80,3 +83,3 @@ | ||
/** | ||
* @param {EncodeToCapDataOptions=} encodeOptions | ||
* @param {EncodeToCapDataOptions} [encodeOptions] | ||
* @returns {(passable: Passable) => Encoding} | ||
@@ -205,12 +208,15 @@ */ | ||
case 'remotable': { | ||
return encodeRemotableToCapData(passable); | ||
return encodeRemotableToCapData(passable, encodeToCapDataRecur); | ||
} | ||
case 'error': { | ||
return encodeErrorToCapData(passable); | ||
return encodeErrorToCapData(passable, encodeToCapDataRecur); | ||
} | ||
case 'promise': { | ||
return encodePromiseToCapData(passable); | ||
return encodePromiseToCapData(passable, encodeToCapDataRecur); | ||
} | ||
default: { | ||
assert.fail(X`unrecognized passStyle ${q(passStyle)}`, TypeError); | ||
assert.fail( | ||
X`internal: Unrecognized passStyle ${q(passStyle)}`, | ||
TypeError, | ||
); | ||
} | ||
@@ -231,3 +237,3 @@ } | ||
// this information. | ||
return harden(encodeErrorToCapData(passable)); | ||
return harden(encodeErrorToCapData(passable, encodeToCapDataRecur)); | ||
} | ||
@@ -242,5 +248,14 @@ return harden(encodeToCapDataRecur(passable)); | ||
* @typedef {object} DecodeOptions | ||
* @property {(encodedRemotable: Encoding) => (Promise|Remotable)} [decodeRemotableFromCapData] | ||
* @property {(encodedPromise: Encoding) => (Promise|Remotable)} [decodePromiseFromCapData] | ||
* @property {(encodedError: Encoding) => Error} [decodeErrorFromCapData] | ||
* @property {( | ||
* encodedRemotable: Encoding, | ||
* decodeRecur: (e: Encoding) => Passable | ||
* ) => (Promise|Remotable)} [decodeRemotableFromCapData] | ||
* @property {( | ||
* encodedPromise: Encoding, | ||
* decodeRecur: (e: Encoding) => Passable | ||
* ) => (Promise|Remotable)} [decodePromiseFromCapData] | ||
* @property {( | ||
* encodedError: Encoding, | ||
* decodeRecur: (e: Encoding) => Passable | ||
* ) => Error} [decodeErrorFromCapData] | ||
*/ | ||
@@ -263,3 +278,3 @@ | ||
* | ||
* @param {DecodeOptions=} decodeOptions | ||
* @param {DecodeOptions} [decodeOptions] | ||
* @returns {(encoded: Encoding) => Passable} | ||
@@ -365,3 +380,3 @@ */ | ||
case 'error': { | ||
return decodeErrorFromCapData(jsonEncoded); | ||
return decodeErrorFromCapData(jsonEncoded, decodeFromCapData); | ||
} | ||
@@ -373,3 +388,3 @@ | ||
// both must be the same and it doesn't matter which we call. | ||
return decodeRemotableFromCapData(jsonEncoded); | ||
return decodeRemotableFromCapData(jsonEncoded, decodeFromCapData); | ||
} | ||
@@ -408,7 +423,11 @@ | ||
// @ts-expect-error This is the error case we're testing for | ||
case 'ibid': { | ||
assert.fail( | ||
X`The capData protocol no longer supports ${q(QCLASS)} ${q( | ||
qclass, | ||
)}`, | ||
); | ||
} | ||
default: { | ||
qclass !== 'ibid' || | ||
assert.fail( | ||
X`The protocol no longer supports ibid encoding: ${jsonEncoded}.`, | ||
); | ||
assert.fail(X`unrecognized ${q(QCLASS)} ${q(qclass)}`, TypeError); | ||
@@ -415,0 +434,0 @@ } |
@@ -32,3 +32,7 @@ // @ts-check | ||
doNotConvertSlotToVal, | ||
{ errorTagging: 'off' }, | ||
{ | ||
errorTagging: 'off', | ||
// TODO fix tests to works with smallcaps. | ||
serializeBodyFormat: 'capdata', | ||
}, | ||
); | ||
@@ -35,0 +39,0 @@ |
@@ -1,2 +0,2 @@ | ||
export function makeMarshal<Slot>(convertValToSlot?: ConvertValToSlot<Slot> | undefined, convertSlotToVal?: ConvertSlotToVal<Slot> | undefined, { errorTagging, marshalName, errorIdNum, marshalSaveError, }?: import("./types.js").MakeMarshalOptions | undefined): { | ||
export function makeMarshal<Slot>(convertValToSlot?: ConvertValToSlot<Slot> | undefined, convertSlotToVal?: ConvertSlotToVal<Slot> | undefined, { errorTagging, marshalName, errorIdNum, marshalSaveError, serializeBodyFormat, }?: import("./types.js").MakeMarshalOptions | undefined): { | ||
serialize: Serialize<Slot>; | ||
@@ -13,2 +13,3 @@ unserialize: Unserialize<Slot>; | ||
export type Encoding = import('./types.js').Encoding; | ||
export type Remotable = import('./types.js').Remotable; | ||
//# sourceMappingURL=marshal.d.ts.map |
@@ -15,2 +15,7 @@ // @ts-check | ||
} from './encodeToCapData.js'; | ||
import { | ||
makeDecodeFromSmallcaps, | ||
makeEncodeToSmallcaps, | ||
} from './encodeToSmallcaps.js'; | ||
import { hasOwnPropertyOf } from './helpers/passStyle-helpers.js'; | ||
@@ -25,5 +30,7 @@ /** @typedef {import('./types.js').MakeMarshalOptions} MakeMarshalOptions */ | ||
/** @typedef {import('./types.js').Encoding} Encoding */ | ||
/** @typedef {import('./types.js').Remotable} Remotable */ | ||
const { isArray } = Array; | ||
const { details: X, quote: q } = assert; | ||
const { ownKeys } = Reflect; | ||
@@ -54,2 +61,5 @@ /** @type {ConvertValToSlot<any>} */ | ||
console.log('Temporary logging of sent error', err), | ||
// Default to 'capdata' because it was implemented first. | ||
// Sometimes, ontogeny does recapitulate phylogeny ;) | ||
serializeBodyFormat = 'capdata', | ||
} = {}, | ||
@@ -77,34 +87,19 @@ ) => { | ||
/** | ||
* @param {Passable} val | ||
* @param {InterfaceSpec=} iface | ||
* @returns {Encoding} | ||
* @param {Remotable | Promise} passable | ||
* @returns {{index: number, repeat: boolean}} | ||
*/ | ||
function serializeSlot(val, iface = undefined) { | ||
let slotIndex; | ||
if (slotMap.has(val)) { | ||
const encodeSlotCommon = passable => { | ||
let index = slotMap.get(passable); | ||
if (index !== undefined) { | ||
// TODO assert that it's the same iface as before | ||
slotIndex = slotMap.get(val); | ||
assert.typeof(slotIndex, 'number'); | ||
iface = undefined; | ||
} else { | ||
const slot = convertValToSlot(val); | ||
slotIndex = slots.length; | ||
slots.push(slot); | ||
slotMap.set(val, slotIndex); | ||
assert.typeof(index, 'number'); | ||
return harden({ index, repeat: true }); | ||
} | ||
// TODO explore removing this special case | ||
if (iface === undefined) { | ||
return harden({ | ||
[QCLASS]: 'slot', | ||
index: slotIndex, | ||
}); | ||
} | ||
return harden({ | ||
[QCLASS]: 'slot', | ||
iface, | ||
index: slotIndex, | ||
}); | ||
} | ||
index = slots.length; | ||
const slot = convertValToSlot(passable); | ||
slots.push(slot); | ||
slotMap.set(passable, index); | ||
return harden({ index, repeat: false }); | ||
}; | ||
@@ -118,5 +113,10 @@ /** | ||
* @param {Error} err | ||
* @returns {Encoding} | ||
* @param {(p: Passable) => unknown} encodeRecur | ||
* @returns {{errorId?: string, message: string, name: string}} | ||
*/ | ||
const encodeErrorToCapData = err => { | ||
const encodeErrorCommon = (err, encodeRecur) => { | ||
const message = encodeRecur(`${err.message}`); | ||
assert.typeof(message, 'string'); | ||
const name = encodeRecur(`${err.name}`); | ||
assert.typeof(name, 'string'); | ||
// Must encode `cause`, `errors`. | ||
@@ -132,38 +132,109 @@ // nested non-passable errors must be ok from here. | ||
// with the correlation. | ||
const errorId = nextErrorId(); | ||
const errorId = encodeRecur(nextErrorId()); | ||
assert.typeof(errorId, 'string'); | ||
assert.note(err, X`Sent as ${errorId}`); | ||
marshalSaveError(err); | ||
return harden({ | ||
[QCLASS]: 'error', | ||
errorId, | ||
message: `${err.message}`, | ||
name: `${err.name}`, | ||
}); | ||
return harden({ errorId, message, name }); | ||
} else { | ||
return harden({ | ||
[QCLASS]: 'error', | ||
message: `${err.message}`, | ||
name: `${err.name}`, | ||
}); | ||
return harden({ message, name }); | ||
} | ||
}; | ||
const encodeRemotableToCapData = val => { | ||
const iface = getInterfaceOf(val); | ||
// console.log(`serializeSlot: ${val}`); | ||
return serializeSlot(val, iface); | ||
}; | ||
if (serializeBodyFormat === 'capdata') { | ||
/** | ||
* @param {Passable} passable | ||
* @param {InterfaceSpec} [iface] | ||
* @returns {Encoding} | ||
*/ | ||
const encodeSlotToCapData = (passable, iface = undefined) => { | ||
const { index, repeat } = encodeSlotCommon(passable); | ||
const encode = makeEncodeToCapData({ | ||
encodeRemotableToCapData, | ||
encodePromiseToCapData: serializeSlot, | ||
encodeErrorToCapData, | ||
}); | ||
if (repeat === true || iface === undefined) { | ||
return harden({ [QCLASS]: 'slot', index }); | ||
} else { | ||
return harden({ [QCLASS]: 'slot', iface, index }); | ||
} | ||
}; | ||
const encoded = encode(root); | ||
const encodeRemotableToCapData = (val, _encodeRecur) => | ||
encodeSlotToCapData(val, getInterfaceOf(val)); | ||
return harden({ | ||
body: JSON.stringify(encoded), | ||
slots, | ||
}); | ||
const encodePromiseToCapData = (promise, _encodeRecur) => | ||
encodeSlotToCapData(promise); | ||
/** | ||
* Even if an Error is not actually passable, we'd rather send | ||
* it anyway because the diagnostic info carried by the error | ||
* is more valuable than diagnosing why the error isn't | ||
* passable. See comments in ErrorHelper. | ||
* | ||
* @param {Error} err | ||
* @param {(p: Passable) => Encoding} encodeRecur | ||
* @returns {Encoding} | ||
*/ | ||
const encodeErrorToCapData = (err, encodeRecur) => { | ||
const errData = encodeErrorCommon(err, encodeRecur); | ||
return harden({ [QCLASS]: 'error', ...errData }); | ||
}; | ||
const encodeToCapData = makeEncodeToCapData({ | ||
encodeRemotableToCapData, | ||
encodePromiseToCapData, | ||
encodeErrorToCapData, | ||
}); | ||
const encoded = encodeToCapData(root); | ||
const body = JSON.stringify(encoded); | ||
return harden({ | ||
body, | ||
slots, | ||
}); | ||
} else if (serializeBodyFormat === 'smallcaps') { | ||
/** | ||
* @param {string} prefix | ||
* @param {Passable} passable | ||
* @param {InterfaceSpec} [iface] | ||
* @returns {string} | ||
*/ | ||
const encodeSlotToSmallcaps = (prefix, passable, iface = undefined) => { | ||
const { index, repeat } = encodeSlotCommon(passable); | ||
// TODO explore removing this special case | ||
if (repeat === true || iface === undefined) { | ||
return `${prefix}${index}`; | ||
} | ||
return `${prefix}${index}.${iface}`; | ||
}; | ||
const encodeRemotableToSmallcaps = (remotable, _encodeRecur) => | ||
encodeSlotToSmallcaps('$', remotable, getInterfaceOf(remotable)); | ||
const encodePromiseToSmallcaps = (promise, _encodeRecur) => | ||
encodeSlotToSmallcaps('&', promise); | ||
const encodeErrorToSmallcaps = (err, encodeRecur) => { | ||
const errData = encodeErrorCommon(err, encodeRecur); | ||
const { message, ...rest } = errData; | ||
return harden({ '#error': message, ...rest }); | ||
}; | ||
const encodeToSmallcaps = makeEncodeToSmallcaps({ | ||
encodeRemotableToSmallcaps, | ||
encodePromiseToSmallcaps, | ||
encodeErrorToSmallcaps, | ||
}); | ||
const encoded = encodeToSmallcaps(root); | ||
const smallcapsBody = JSON.stringify(encoded); | ||
return harden({ | ||
// Valid JSON cannot begin with a '#', so this is a valid signal | ||
// indicating smallcaps format. | ||
body: `#${smallcapsBody}`, | ||
slots, | ||
}); | ||
} else { | ||
assert.fail( | ||
X`Unrecognized serializeBodyFormat: ${q(serializeBodyFormat)}`, | ||
); | ||
} | ||
}; | ||
@@ -175,3 +246,10 @@ | ||
function unserializeSlot(index, iface) { | ||
/** | ||
* @param {{iface?: string, index: number}} slotData | ||
* @returns {Remotable | Promise} | ||
*/ | ||
const decodeSlotCommon = slotData => { | ||
const { iface = undefined, index, ...rest } = slotData; | ||
ownKeys(rest).length === 0 || | ||
assert.fail(X`unexpected encoded slot properties ${q(ownKeys(rest))}`); | ||
if (valMap.has(index)) { | ||
@@ -186,29 +264,31 @@ return valMap.get(index); | ||
return val; | ||
} | ||
}; | ||
const decodeErrorFromCapData = rawTree => { | ||
// Must decode `cause` and `errors` properties | ||
const { name, message, errorId } = rawTree; | ||
assert.typeof( | ||
name, | ||
'string', | ||
X`invalid error name typeof ${q(typeof name)}`, | ||
); | ||
assert.typeof( | ||
message, | ||
'string', | ||
X`invalid error message typeof ${q(typeof message)}`, | ||
); | ||
const EC = getErrorConstructor(`${name}`) || Error; | ||
/** | ||
* @param {{errorId?: string, message: string, name: string}} errData | ||
* @param {(e: unknown) => Passable} decodeRecur | ||
* @returns {Error} | ||
*/ | ||
const decodeErrorCommon = (errData, decodeRecur) => { | ||
const { errorId = undefined, message, name, ...rest } = errData; | ||
ownKeys(rest).length === 0 || | ||
assert.fail(X`unexpected encoded error properties ${q(ownKeys(rest))}`); | ||
// TODO Must decode `cause` and `errors` properties | ||
// capData does not transform strings. The calls to `decodeRecur` | ||
// are for reuse by other encodings that do, such as smallcaps. | ||
const dName = decodeRecur(name); | ||
const dMessage = decodeRecur(message); | ||
const dErrorId = errorId && decodeRecur(errorId); | ||
typeof dName === 'string' || | ||
assert.fail(X`invalid error name typeof ${q(typeof dName)}`); | ||
typeof dMessage === 'string' || | ||
assert.fail(X`invalid error message typeof ${q(typeof dMessage)}`); | ||
const EC = getErrorConstructor(dName) || Error; | ||
// errorId is a late addition so be tolerant of its absence. | ||
const errorName = | ||
errorId === undefined | ||
dErrorId === undefined | ||
? `Remote${EC.name}` | ||
: `Remote${EC.name}(${errorId})`; | ||
// Due to a defect in the SES type definition, the next line is | ||
// fails a type check. | ||
// Pending https://github.com/endojs/endo/issues/977 | ||
// @ts-ignore-next-line | ||
const error = assert.error(`${message}`, EC, { errorName }); | ||
return error; | ||
: `Remote${EC.name}(${dErrorId})`; | ||
const error = assert.error(dMessage, EC, { errorName }); | ||
return harden(error); | ||
}; | ||
@@ -221,9 +301,13 @@ | ||
// See https://github.com/Agoric/agoric-sdk/issues/4334 | ||
const decodeRemotableOrPromiseFromCapData = rawTree => { | ||
const { index, iface } = rawTree; | ||
const val = unserializeSlot(index, iface); | ||
return val; | ||
const decodeRemotableOrPromiseFromCapData = (rawTree, _decodeRecur) => { | ||
const { [QCLASS]: _, ...slotData } = rawTree; | ||
return decodeSlotCommon(slotData); | ||
}; | ||
const fullRevive = makeDecodeFromCapData({ | ||
const decodeErrorFromCapData = (rawTree, decodeRecur) => { | ||
const { [QCLASS]: _, ...errData } = rawTree; | ||
return decodeErrorCommon(errData, decodeRecur); | ||
}; | ||
const reviveFromCapData = makeDecodeFromCapData({ | ||
decodeRemotableFromCapData: decodeRemotableOrPromiseFromCapData, | ||
@@ -233,4 +317,33 @@ decodePromiseFromCapData: decodeRemotableOrPromiseFromCapData, | ||
}); | ||
return fullRevive; | ||
const makeDecodeSlotFromSmallcaps = prefix => { | ||
return (stringEncoding, _decodeRecur) => { | ||
assert(stringEncoding.startsWith(prefix)); | ||
// slots: $slotIndex.iface or $slotIndex | ||
const i = stringEncoding.indexOf('.'); | ||
const index = Number(stringEncoding.slice(1, i < 0 ? undefined : i)); | ||
// i < 0 means there was no iface included. | ||
const iface = i < 0 ? undefined : stringEncoding.slice(i + 1); | ||
return decodeSlotCommon({ iface, index }); | ||
}; | ||
}; | ||
const decodeRemotableFromSmallcaps = makeDecodeSlotFromSmallcaps('$'); | ||
const decodePromiseFromSmallcaps = makeDecodeSlotFromSmallcaps('&'); | ||
const decodeErrorFromSmallcaps = (encoding, decodeRecur) => { | ||
const { '#error': message, ...restErrData } = encoding; | ||
!hasOwnPropertyOf(restErrData, 'message') || | ||
assert.fail(X`unexpected encoded error property ${q('message')}`); | ||
return decodeErrorCommon({ message, ...restErrData }, decodeRecur); | ||
}; | ||
const reviveFromSmallcaps = makeDecodeFromSmallcaps({ | ||
decodeRemotableFromSmallcaps, | ||
decodePromiseFromSmallcaps, | ||
decodeErrorFromSmallcaps, | ||
}); | ||
return harden({ reviveFromCapData, reviveFromSmallcaps }); | ||
}; | ||
/** | ||
@@ -240,13 +353,23 @@ * @type {Unserialize<Slot>} | ||
const unserialize = data => { | ||
assert.typeof( | ||
data.body, | ||
'string', | ||
X`unserialize() given non-capdata (.body is ${data.body}, not string)`, | ||
); | ||
(isArray(data.slots)) || | ||
const { body, slots } = data; | ||
typeof body === 'string' || | ||
assert.fail( | ||
X`unserialize() given non-capdata (.body is ${body}, not string)`, | ||
); | ||
isArray(data.slots) || | ||
assert.fail(X`unserialize() given non-capdata (.slots are not Array)`); | ||
const rawTree = harden(JSON.parse(data.body)); | ||
const fullRevive = makeFullRevive(data.slots); | ||
const result = harden(fullRevive(rawTree)); | ||
const { reviveFromCapData, reviveFromSmallcaps } = makeFullRevive(slots); | ||
let result; | ||
// JSON cannot begin with a '#', so this is an unambiguous signal. | ||
if (body.startsWith('#')) { | ||
const smallcapsBody = body.slice(1); | ||
const encoding = harden(JSON.parse(smallcapsBody)); | ||
result = harden(reviveFromSmallcaps(encoding)); | ||
} else { | ||
const rawTree = harden(JSON.parse(body)); | ||
result = harden(reviveFromCapData(rawTree)); | ||
} | ||
// See https://github.com/Agoric/agoric-sdk/issues/4337 | ||
// which should be considered fixed once we've completed the switch | ||
// to smallcaps. | ||
assertPassable(result); | ||
@@ -253,0 +376,0 @@ return result; |
@@ -124,3 +124,3 @@ export type PrimitiveStyle = "undefined" | "null" | "boolean" | "number" | "bigint" | "string" | "symbol"; | ||
*/ | ||
errorTagging?: ('on' | 'off') | undefined; | ||
errorTagging?: "on" | "off" | undefined; | ||
/** | ||
@@ -144,2 +144,13 @@ * Used to identify sent errors. | ||
marshalSaveError?: ((err: Error) => void) | undefined; | ||
/** | ||
* Formatting to use in the "body" property in objects returned from | ||
* `serialize`. The body string for each case: | ||
* * 'capdata' - a JSON string, from an encoding of passables | ||
* into JSON, where some values are represented as objects with a | ||
* `'@qclass` property. | ||
* * 'smallcaps' - a JSON string prefixed with `'#'`, which is | ||
* an unambiguous signal since a valid JSON string cannot begin with | ||
* `'#'`. | ||
*/ | ||
serializeBodyFormat?: "capdata" | "smallcaps" | undefined; | ||
}; | ||
@@ -146,0 +157,0 @@ /** |
@@ -203,3 +203,3 @@ // @ts-nocheck TODO Fix the recursive types to it checks. Will this | ||
* @typedef {Object} MakeMarshalOptions | ||
* @property {'on'|'off'=} errorTagging controls whether serialized errors | ||
* @property {'on'|'off'} [errorTagging] controls whether serialized errors | ||
* also carry tagging information, made from `marshalName` and numbers | ||
@@ -218,2 +218,11 @@ * generated (currently by counting) starting at `errorIdNum`. The | ||
* show that note showing the associated errorId. | ||
* @property {'capdata'|'smallcaps'} [serializeBodyFormat] | ||
* Formatting to use in the "body" property in objects returned from | ||
* `serialize`. The body string for each case: | ||
* * 'capdata' - a JSON string, from an encoding of passables | ||
* into JSON, where some values are represented as objects with a | ||
* `'@qclass` property. | ||
* * 'smallcaps' - a JSON string prefixed with `'#'`, which is | ||
* an unambiguous signal since a valid JSON string cannot begin with | ||
* `'#'`. | ||
*/ | ||
@@ -220,0 +229,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
206032
76
4029
5
Updated@endo/eventual-send@^0.16.5
Updated@endo/nat@^4.1.20
Updated@endo/promise-kit@^0.2.49