@agoric/marshal
Advanced tools
Comparing version 0.5.1-dev-fbc7c64.0 to 0.5.1-dev-fe997f2.0
38
index.js
@@ -1,4 +0,12 @@ | ||
export { PASS_STYLE, isObject } from './src/helpers/passStyleHelpers.js'; | ||
export { getErrorConstructor } from './src/helpers/error.js'; | ||
export { mapIterable, filterIterable } from './src/helpers/iter-helpers.js'; | ||
export { | ||
PASS_STYLE, | ||
isObject, | ||
assertChecker, | ||
getTag, | ||
hasOwnPropertyOf, | ||
} from './src/helpers/passStyle-helpers.js'; | ||
export { getErrorConstructor, toPassableError } from './src/helpers/error.js'; | ||
export { | ||
getInterfaceOf, | ||
@@ -8,15 +16,21 @@ ALLOW_IMPLICIT_REMOTABLES, | ||
export { passStyleOf, everyPassableChild } from './src/passStyleOf.js'; | ||
export { | ||
nameForPassableSymbol, | ||
passableSymbolForName, | ||
} from './src/helpers/symbol.js'; | ||
export { passStyleOf, assertPassable } from './src/passStyleOf.js'; | ||
export { pureCopy, sameValueZero } from './src/pureCopy.js'; | ||
export { deeplyFulfilled } from './src/deeplyFulfilled.js'; | ||
export { makeTagged } from './src/makeTagged.js'; | ||
export { Remotable, Far, ToFarFunction } from './src/make-far.js'; | ||
export { QCLASS, makeMarshal } from './src/marshal.js'; | ||
export { stringify, parse } from './src/marshal-stringify.js'; | ||
// Works, but not yet used | ||
// export { decodeToJustin } from './src/marshal-justin.js'; | ||
export { pureCopy, Remotable, Far, ToFarFunction } from './src/make-far.js'; | ||
export { stringify, parse } from './src/marshal-stringify.js'; | ||
export { | ||
isStructure, | ||
assertStructure, | ||
sameStructure, | ||
fulfillToStructure, | ||
} from './src/structure.js'; | ||
export { | ||
assertRecord, | ||
@@ -28,2 +42,2 @@ assertCopyArray, | ||
isCopyArray, | ||
} from './src/assertPassStyleOf.js'; | ||
} from './src/typeGuards.js'; |
{ | ||
"name": "@agoric/marshal", | ||
"version": "0.5.1-dev-fbc7c64.0+fbc7c64", | ||
"version": "0.5.1-dev-fe997f2.0+fe997f2", | ||
"description": "marshal", | ||
@@ -37,10 +37,10 @@ "type": "module", | ||
"dependencies": { | ||
"@agoric/assert": "0.3.16-dev-fbc7c64.0+fbc7c64", | ||
"@agoric/eventual-send": "0.14.1-dev-fbc7c64.0+fbc7c64", | ||
"@agoric/assert": "0.3.16-dev-fe997f2.0+fe997f2", | ||
"@agoric/eventual-send": "0.14.1-dev-fe997f2.0+fe997f2", | ||
"@agoric/nat": "^4.1.0", | ||
"@agoric/promise-kit": "0.2.30-dev-fbc7c64.0+fbc7c64" | ||
"@agoric/promise-kit": "0.2.30-dev-fe997f2.0+fe997f2" | ||
}, | ||
"devDependencies": { | ||
"@agoric/lockdown": "0.1.2-dev-fbc7c64.0+fbc7c64", | ||
"@endo/ses-ava": "^0.2.8", | ||
"@agoric/lockdown": "0.1.2-dev-fe997f2.0+fe997f2", | ||
"@endo/ses-ava": "^0.2.13", | ||
"ava": "^3.12.1", | ||
@@ -72,3 +72,3 @@ "c8": "^7.7.2" | ||
}, | ||
"gitHead": "fbc7c6455581e1608d252de560af741df32b25ae" | ||
"gitHead": "fe997f28467eb7f61b711e63a581f396f8390e91" | ||
} |
@@ -9,3 +9,3 @@ /* eslint-disable no-use-before-define */ | ||
import { E } from '@agoric/eventual-send'; | ||
import { isObject } from './helpers/passStyleHelpers.js'; | ||
import { isObject } from './helpers/passStyle-helpers.js'; | ||
import { getInterfaceOf } from './helpers/remotable.js'; | ||
@@ -12,0 +12,0 @@ import { Far } from './make-far.js'; |
@@ -9,3 +9,3 @@ // @ts-check | ||
import '@agoric/assert/exported.js'; | ||
import { assertChecker, checkNormalProperty } from './passStyleHelpers.js'; | ||
import { assertChecker, checkNormalProperty } from './passStyle-helpers.js'; | ||
@@ -48,9 +48,4 @@ const { details: X } = assert; | ||
// Recursively validate that each member is passable. | ||
CopyArrayHelper.every(candidate, v => !!passStyleOfRecur(v)); | ||
candidate.every(v => !!passStyleOfRecur(v)); | ||
}, | ||
every: (passable, fn) => | ||
// Note that we explicitly call `fn` with only the arguments we want | ||
// to provide. | ||
passable.every((v, i) => fn(v, i)), | ||
}); |
@@ -10,3 +10,3 @@ // @ts-check | ||
checkNormalProperty, | ||
} from './passStyleHelpers.js'; | ||
} from './passStyle-helpers.js'; | ||
@@ -22,3 +22,2 @@ import '../types.js'; | ||
getOwnPropertyDescriptors, | ||
entries, | ||
prototype: objectPrototype, | ||
@@ -67,9 +66,4 @@ } = Object; | ||
// Recursively validate that each member is passable. | ||
CopyRecordHelper.every(candidate, v => !!passStyleOfRecur(v)); | ||
Object.values(candidate).every(v => !!passStyleOfRecur(v)); | ||
}, | ||
every: (passable, fn) => | ||
// Note that we explicitly call `fn` with only the arguments we want | ||
// to provide. | ||
entries(passable).every(([k, v]) => fn(v, k)), | ||
}); |
@@ -9,3 +9,3 @@ // @ts-check | ||
import '@agoric/assert/exported.js'; | ||
import { assertChecker } from './passStyleHelpers.js'; | ||
import { assertChecker } from './passStyle-helpers.js'; | ||
@@ -99,4 +99,2 @@ const { details: X } = assert; | ||
}, | ||
every: (_passable, _fn) => true, | ||
}); | ||
@@ -103,0 +101,0 @@ |
@@ -30,8 +30,2 @@ // @ts-check | ||
* ) => void} assertValid | ||
* | ||
* @property {(passable: Passable, | ||
* fn: (passable: Passable, index: any) => boolean | ||
* ) => boolean} every | ||
* For recuring through the nested passable structure. Like | ||
* `Array.prototype.every`, return `false` to stop early. | ||
*/ |
@@ -17,3 +17,3 @@ // @ts-check | ||
getTag, | ||
} from './passStyleHelpers.js'; | ||
} from './passStyle-helpers.js'; | ||
import { getEnvironmentOption } from './environment-options.js'; | ||
@@ -20,0 +20,0 @@ |
@@ -7,3 +7,3 @@ // @ts-check | ||
import { assert, details as X, q } from '@agoric/assert'; | ||
import { assertChecker, PASS_STYLE } from './helpers/passStyleHelpers.js'; | ||
import { assertChecker, PASS_STYLE } from './helpers/passStyle-helpers.js'; | ||
import { | ||
@@ -14,3 +14,3 @@ assertIface, | ||
} from './helpers/remotable.js'; | ||
import { passStyleOf } from './passStyleOf.js'; | ||
import { pureCopy } from './pureCopy.js'; | ||
@@ -27,69 +27,2 @@ const { prototype: functionPrototype } = Function; | ||
/** | ||
* Do a deep copy of the object, handling Proxies and recursion. | ||
* The resulting copy is guaranteed to be pure data, as well as hardened. | ||
* Such a hardened, pure copy cannot be used as a communications path. | ||
* | ||
* @template {OnlyData} T | ||
* @param {T} val input value. NOTE: Must be hardened! | ||
* @returns {T} pure, hardened copy | ||
*/ | ||
export const pureCopy = val => { | ||
// passStyleOf now asserts that val has no pass-by-copy cycles. | ||
const passStyle = passStyleOf(val); | ||
switch (passStyle) { | ||
case 'bigint': | ||
case 'boolean': | ||
case 'null': | ||
case 'number': | ||
case 'string': | ||
case 'undefined': | ||
case 'symbol': | ||
return val; | ||
case 'copyArray': | ||
case 'copyRecord': { | ||
const obj = /** @type {Object} */ (val); | ||
// Create a new identity. | ||
const copy = /** @type {T} */ (passStyle === 'copyArray' ? [] : {}); | ||
// Make a deep copy on the new identity. | ||
// Object.entries(obj) takes a snapshot (even if a Proxy). | ||
// Since we already know it is a copyRecord or copyArray, we | ||
// know that Object.entries is safe enough. On a copyRecord it | ||
// will represent all the own properties. On a copyArray it | ||
// will represent all the own properties except for the length. | ||
Object.entries(obj).forEach(([prop, value]) => { | ||
copy[prop] = pureCopy(value); | ||
}); | ||
return harden(copy); | ||
} | ||
case 'error': { | ||
assert.fail(X`Errors cannot be copied: ${val}`, TypeError); | ||
} | ||
case 'remotable': { | ||
assert.fail( | ||
X`Input value ${q( | ||
passStyle, | ||
)} cannot be copied as it must be passed by reference`, | ||
TypeError, | ||
); | ||
} | ||
case 'promise': { | ||
assert.fail(X`Promises cannot be copied`, TypeError); | ||
} | ||
default: | ||
assert.fail( | ||
X`Input value ${q(passStyle)} is not recognized as data`, | ||
TypeError, | ||
); | ||
} | ||
}; | ||
harden(pureCopy); | ||
/** | ||
* Now that the remotableProto does not provide its own `toString` method, | ||
@@ -96,0 +29,0 @@ * ensure it always inherits from something. The original prototype of |
@@ -12,3 +12,4 @@ // @ts-check | ||
import { getErrorConstructor } from './helpers/error.js'; | ||
import { isObject } from './helpers/passStyleHelpers.js'; | ||
import { isObject } from './helpers/passStyle-helpers.js'; | ||
import { AtAtPrefixPattern, passableSymbolForName } from './helpers/symbol.js'; | ||
@@ -169,20 +170,14 @@ const { ownKeys } = Reflect; | ||
} | ||
case 'error': { | ||
const { name, message } = rawTree; | ||
assert.typeof( | ||
name, | ||
'string', | ||
X`invalid error name typeof ${q(typeof name)}`, | ||
); | ||
assert( | ||
getErrorConstructor(name) !== undefined, | ||
X`Must be the name of an Error constructor ${name}`, | ||
); | ||
assert.typeof( | ||
message, | ||
'string', | ||
X`invalid error message typeof ${q(typeof message)}`, | ||
); | ||
case 'symbol': { | ||
const { name } = rawTree; | ||
const sym = passableSymbolForName(name); | ||
assert.typeof(sym, 'symbol'); | ||
return; | ||
} | ||
case 'tagged': { | ||
const { tag, payload } = rawTree; | ||
assert.typeof(tag, 'string'); | ||
prepare(payload); | ||
return; | ||
} | ||
case 'slot': { | ||
@@ -229,2 +224,20 @@ const { index, iface } = rawTree; | ||
} | ||
case 'error': { | ||
const { name, message } = rawTree; | ||
assert.typeof( | ||
name, | ||
'string', | ||
X`invalid error name typeof ${q(typeof name)}`, | ||
); | ||
assert( | ||
getErrorConstructor(name) !== undefined, | ||
X`Must be the name of an Error constructor ${name}`, | ||
); | ||
assert.typeof( | ||
message, | ||
'string', | ||
X`invalid error message typeof ${q(typeof message)}`, | ||
); | ||
return; | ||
} | ||
@@ -321,9 +334,25 @@ default: { | ||
case '@@asyncIterator': { | ||
// TODO deprecated. Eventually remove. | ||
return out.next('Symbol.asyncIterator'); | ||
} | ||
case 'error': { | ||
const { name, message } = rawTree; | ||
return out.next(`${name}(${quote(message)})`); | ||
case 'symbol': { | ||
const { name } = rawTree; | ||
const sym = passableSymbolForName(name); | ||
assert.typeof(sym, 'symbol'); | ||
const registeredName = Symbol.keyFor(sym); | ||
if (registeredName === undefined) { | ||
const match = AtAtPrefixPattern.exec(name); | ||
assert(match !== null); | ||
const suffix = match[1]; | ||
assert(Symbol[suffix] === sym); | ||
return out.next(`Symbol[${quote(suffix)}]`); | ||
} | ||
return out.next(`Symbol.for(${quote(registeredName)})`); | ||
} | ||
case 'tagged': { | ||
const { tag, payload } = rawTree; | ||
out.next(`makeTagged(${quote(tag)},`); | ||
decode(payload); | ||
return out.next(')'); | ||
} | ||
@@ -352,2 +381,7 @@ case 'slot': { | ||
case 'error': { | ||
const { name, message } = rawTree; | ||
return out.next(`${name}(${quote(message)})`); | ||
} | ||
default: { | ||
@@ -354,0 +388,0 @@ assert.fail(X`unrecognized ${q(QCLASS)} ${q(qclass)}`, TypeError); |
@@ -13,3 +13,9 @@ // @ts-check | ||
import { ErrorHelper, getErrorConstructor } from './helpers/error.js'; | ||
import { isObject } from './helpers/passStyleHelpers.js'; | ||
import { makeTagged } from './makeTagged.js'; | ||
import { isObject, getTag } from './helpers/passStyle-helpers.js'; | ||
import { | ||
assertPassableSymbol, | ||
nameForPassableSymbol, | ||
passableSymbolForName, | ||
} from './helpers/symbol.js'; | ||
@@ -138,6 +144,5 @@ const { ownKeys } = Reflect; | ||
* Must encode `val` into plain JSON data *canonically*, such that | ||
* `sameStructure(v1, v2)` implies | ||
* `JSON.stringify(encode(v1)) === JSON.stringify(encode(v2))` | ||
* For each record, we only accept sortable property names | ||
* (no anonymous symbols). On the encoded form the sort | ||
* `JSON.stringify(encode(v1)) === JSON.stringify(encode(v1))` | ||
* For each copyRecord, we only accept string property names, | ||
* not symbols. The encoded form the sort | ||
* order of these names must be the same as their enumeration | ||
@@ -190,12 +195,17 @@ * order, so a `JSON.stringify` of the encoded form agrees with | ||
case 'symbol': { | ||
switch (val) { | ||
case Symbol.asyncIterator: { | ||
return harden({ | ||
[QCLASS]: '@@asyncIterator', | ||
}); | ||
} | ||
default: { | ||
assert.fail(X`Unsupported symbol ${q(String(val))}`); | ||
} | ||
assertPassableSymbol(val); | ||
const name = /** @type {string} */ (nameForPassableSymbol(val)); | ||
if (name === '@@asyncIterator') { | ||
// Deprectated qclass. TODO make conditional | ||
// on environment variable. Eventually remove, but only after | ||
// confident that all supported receivers understand | ||
// `[QCLASS]: 'symbol'`. | ||
return harden({ | ||
[QCLASS]: '@@asyncIterator', | ||
}); | ||
} | ||
return harden({ | ||
[QCLASS]: 'symbol', | ||
name, | ||
}); | ||
} | ||
@@ -232,4 +242,10 @@ case 'copyRecord': { | ||
} | ||
case 'error': { | ||
return encodeError(val); | ||
case 'tagged': { | ||
/** @type {Encoding} */ | ||
const result = harden({ | ||
[QCLASS]: 'tagged', | ||
tag: getTag(val), | ||
payload: encode(val.payload), | ||
}); | ||
return result; | ||
} | ||
@@ -241,2 +257,5 @@ case 'remotable': { | ||
} | ||
case 'error': { | ||
return encodeError(val); | ||
} | ||
case 'promise': { | ||
@@ -352,5 +371,17 @@ // console.log(`serializeSlot: ${val}`); | ||
case '@@asyncIterator': { | ||
// Deprectated qclass. TODO make conditional | ||
// on environment variable. Eventually remove, but after confident | ||
// that there are no more supported senders. | ||
return Symbol.asyncIterator; | ||
} | ||
case 'symbol': { | ||
const { name } = rawTree; | ||
return passableSymbolForName(name); | ||
} | ||
case 'tagged': { | ||
const { tag, payload } = rawTree; | ||
return makeTagged(tag, fullRevive(payload)); | ||
} | ||
case 'error': { | ||
@@ -357,0 +388,0 @@ const { name, message, errorId } = rawTree; |
@@ -7,11 +7,13 @@ // @ts-check | ||
import { isPromise } from '@agoric/promise-kit'; | ||
import { isObject, PASS_STYLE } from './helpers/passStyleHelpers.js'; | ||
import { isObject, PASS_STYLE } from './helpers/passStyle-helpers.js'; | ||
import { CopyArrayHelper } from './helpers/copyArray.js'; | ||
import { CopyRecordHelper } from './helpers/copyRecord.js'; | ||
import { TaggedHelper } from './helpers/tagged.js'; | ||
import { RemotableHelper } from './helpers/remotable.js'; | ||
import { ErrorHelper } from './helpers/error.js'; | ||
import { RemotableHelper } from './helpers/remotable.js'; | ||
import './types.js'; | ||
import './helpers/internal-types.js'; | ||
import { assertPassableSymbol } from './helpers/symbol.js'; | ||
@@ -30,5 +32,5 @@ const { details: X, quote: q } = assert; | ||
* accidents. | ||
* @returns {{passStyleOf: PassStyleOf, HelperTable: any}} | ||
* @returns {PassStyleOf} | ||
*/ | ||
const makePassStyleOfKit = passStyleHelpers => { | ||
const makePassStyleOf = passStyleHelpers => { | ||
const HelperTable = { | ||
@@ -38,4 +40,5 @@ __proto__: null, | ||
copyRecord: undefined, | ||
tagged: undefined, | ||
remotable: undefined, | ||
error: undefined, | ||
remotable: undefined, | ||
}; | ||
@@ -120,6 +123,9 @@ for (const helper of passStyleHelpers) { | ||
case 'number': | ||
case 'bigint': | ||
case 'symbol': { | ||
case 'bigint': { | ||
return typestr; | ||
} | ||
case 'symbol': { | ||
assertPassableSymbol(inner); | ||
return 'symbol'; | ||
} | ||
case 'object': { | ||
@@ -180,23 +186,16 @@ if (inner === null) { | ||
}; | ||
return harden({ passStyleOf, HelperTable }); | ||
return harden(passStyleOf); | ||
}; | ||
const { passStyleOf, HelperTable } = makePassStyleOfKit([ | ||
export const passStyleOf = makePassStyleOf([ | ||
CopyArrayHelper, | ||
CopyRecordHelper, | ||
TaggedHelper, | ||
RemotableHelper, | ||
ErrorHelper, | ||
RemotableHelper, | ||
]); | ||
export { passStyleOf }; | ||
export const everyPassableChild = (passable, fn) => { | ||
const passStyle = passStyleOf(passable); | ||
const helper = HelperTable[passStyle]; | ||
if (helper) { | ||
// everyPassable guards .every so that each helper only gets a | ||
// genuine passable of its own flavor. | ||
return helper.every(passable, fn); | ||
} | ||
return true; | ||
export const assertPassable = val => { | ||
passStyleOf(val); // throws if val is not a passable | ||
}; | ||
harden(everyPassableChild); | ||
harden(assertPassable); |
@@ -9,4 +9,10 @@ // @ts-nocheck TODO Fix the recursive types to it checks. Will this | ||
* @typedef { "undefined" | "null" | | ||
* "boolean" | "number" | "bigint" | "string" | "symbol" | | ||
* "copyArray" | "copyRecord" | "remotable" | | ||
* "boolean" | "number" | "bigint" | "string" | "symbol" | ||
* } PrimitiveStyle | ||
*/ | ||
/** | ||
* @typedef { PrimitiveStyle | | ||
* "copyRecord" | "copyArray" | "tagged" | | ||
* "remotable" | | ||
* "error" | "promise" | ||
@@ -28,6 +34,6 @@ * } PassStyle | ||
* "boolean" | "number" | "bigint" | "string" | "symbol"), | ||
* * the pass-by-copy containers ("copyArray" | "copyRecord") that | ||
* * the pass-by-copy containers | ||
* ("copyRecord" | "copyArray" | "tagged") that | ||
* contain other Passables, | ||
* * and the special cases ("error" | "promise"), which | ||
* also contain other Passables. | ||
* * and the special cases ("error" | "promise"). | ||
* | ||
@@ -48,14 +54,3 @@ * A Passable's pass-by-copy superstructure ends in | ||
/** | ||
* @typedef {Passable} Structure | ||
* | ||
* A Passable is a Structure when it contains only | ||
* * pass-by-copy primitives, | ||
* * pass-by-copy containers, | ||
* * remotables. | ||
* | ||
* Two Structures may be compared by for equivalence according to | ||
* `sameStructure`, which is the strongest equivalence class supported by | ||
* marshal's distributed object semantics. | ||
* | ||
* Two Structures can also be compared for full ordering, | ||
* Two Passables can also be compared for total rank ordering, | ||
* * where their passStyles are ordered according to the | ||
@@ -79,10 +74,5 @@ * PassStyle typedef above. | ||
/** | ||
* @deprecated Renamed to `Structure` | ||
* @typedef {Structure} Comparable | ||
*/ | ||
/** | ||
* @typedef {Structure} OnlyData | ||
* @typedef {Passable} OnlyData | ||
* | ||
* A Structure is OnlyData when its pass-by-copy superstructure has no | ||
* A Passable is OnlyData when its pass-by-copy superstructure has no | ||
* remotables, i.e., when all the leaves of the data structure tree are | ||
@@ -115,11 +105,19 @@ * primitive data types or empty composites. | ||
/** | ||
* @typedef {Passable} CopySet | ||
* @template T | ||
* @typedef {T[]} CopyArray | ||
*/ | ||
/** | ||
* @typedef {Passable} CopyMap | ||
* @template T | ||
* @typedef {Record<string, T>} CopyRecord | ||
*/ | ||
/** | ||
* @typedef {Passable} PatternNode | ||
* @typedef {{ | ||
* [PASS_STYLE]: 'tagged', | ||
* [Symbol.toStringTag]: string, | ||
* payload: Passable | ||
* }} CopyTagged | ||
* | ||
* The tag is the value of the `[String.toStringTag]` property. | ||
*/ | ||
@@ -156,2 +154,3 @@ | ||
* EncodingClass<'@@asyncIterator'> | | ||
* EncodingClass<'symbol'> & { name: string } | | ||
* EncodingClass<'error'> & { name: string, | ||
@@ -163,10 +162,12 @@ * message: string, | ||
* EncodingClass<'hilbert'> & { original: Encoding, | ||
* rest?: Encoding } | ||
* rest?: Encoding | ||
* } | | ||
* EncodingClass<'tagged'> & { tag: string, | ||
* payload: Encoding | ||
* } | ||
* } EncodingUnion | ||
* | ||
* @typedef {{ [index: string]: Encoding, | ||
* '@qclass'?: undefined } | ||
* } EncodingRecord | ||
* '@qclass'?: undefined | ||
* }} EncodingRecord | ||
* We exclude '@qclass' as a property in encoding records. | ||
* | ||
* @typedef {EncodingUnion | null | string | | ||
@@ -256,3 +257,2 @@ * boolean | number | EncodingRecord | ||
* Simple semantics, just tell what interface (or undefined) a remotable has. | ||
* | ||
* @param {*} maybeRemotable the value to check | ||
@@ -279,3 +279,2 @@ * @returns {InterfaceSpec|undefined} the interface specification, or undefined | ||
* See the various uses for good examples. | ||
* | ||
* @param {boolean} cond | ||
@@ -282,0 +281,0 @@ * @param {Details=} details |
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
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
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
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
135247
29
2888