diff --git a/packages/pass-style/NEWS.md b/packages/pass-style/NEWS.md index 16b020eeee..1609c952a2 100644 --- a/packages/pass-style/NEWS.md +++ b/packages/pass-style/NEWS.md @@ -1,5 +1,9 @@ User-visible changes in `@endo/pass-style`: +# Next release + +- deprecates `assertChecker`. Use `Fail` in the confirm/reject pattern instead, as supported by `@endo/errors/rejector.js`. + # 1.6.3 (2025-07-11) - The exported function name `isObject` is ambiguous. It is unclear whether it diff --git a/packages/pass-style/src/byteArray.js b/packages/pass-style/src/byteArray.js index b9750957e9..fa7582219d 100644 --- a/packages/pass-style/src/byteArray.js +++ b/packages/pass-style/src/byteArray.js @@ -49,9 +49,9 @@ const { immutableArrayBufferPrototype, immutableGetter } = export const ByteArrayHelper = harden({ styleName: 'byteArray', - canBeValid: (candidate, check = undefined) => + confirmCanBeValid: (candidate, reject) => (candidate instanceof ArrayBuffer && candidate.immutable) || - (!!check && check(false, X`Immutable ArrayBuffer expected: ${candidate}`)), + (reject && reject`Immutable ArrayBuffer expected: ${candidate}`), assertRestValid: (candidate, _passStyleOfRecur) => { getPrototypeOf(candidate) === immutableArrayBufferPrototype || diff --git a/packages/pass-style/src/copyArray.js b/packages/pass-style/src/copyArray.js index 6f6041d1f2..60d4221ab2 100644 --- a/packages/pass-style/src/copyArray.js +++ b/packages/pass-style/src/copyArray.js @@ -1,7 +1,7 @@ /// -import { X } from '@endo/errors'; -import { assertChecker, getOwnDataDescriptor } from './passStyle-helpers.js'; +import { Fail, X } from '@endo/errors'; +import { confirmOwnDataDescriptor } from './passStyle-helpers.js'; const { getPrototypeOf } = Object; const { ownKeys } = Reflect; @@ -14,9 +14,8 @@ const { isArray, prototype: arrayPrototype } = Array; export const CopyArrayHelper = harden({ styleName: 'copyArray', - canBeValid: (candidate, check = undefined) => - isArray(candidate) || - (!!check && check(false, X`Array expected: ${candidate}`)), + confirmCanBeValid: (candidate, reject) => + isArray(candidate) || (reject && reject`Array expected: ${candidate}`), assertRestValid: (candidate, passStyleOfRecur) => { getPrototypeOf(candidate) === arrayPrototype || @@ -24,13 +23,13 @@ export const CopyArrayHelper = harden({ // Since we're already ensured candidate is an array, it should not be // possible for the following get to fail. const len = /** @type {number} */ ( - getOwnDataDescriptor(candidate, 'length', false, assertChecker).value + confirmOwnDataDescriptor(candidate, 'length', false, Fail).value ); // Validate that each index property is own/data/enumerable // and its associated value is recursively passable. for (let i = 0; i < len; i += 1) { passStyleOfRecur( - getOwnDataDescriptor(candidate, i, true, assertChecker).value, + confirmOwnDataDescriptor(candidate, i, true, Fail).value, ); } // Expect one key per index plus one for 'length'. diff --git a/packages/pass-style/src/copyRecord.js b/packages/pass-style/src/copyRecord.js index f3ce8a156d..f7f87aff1b 100644 --- a/packages/pass-style/src/copyRecord.js +++ b/packages/pass-style/src/copyRecord.js @@ -1,26 +1,25 @@ /// -/** @import {Checker} from './types.js' */ - -import { - assertChecker, - getOwnDataDescriptor, - CX, -} from './passStyle-helpers.js'; +import { Fail } from '@endo/errors'; +import { confirmOwnDataDescriptor } from './passStyle-helpers.js'; import { canBeMethod } from './remotable.js'; +/** + * @import {Rejector} from '@endo/errors/rejector.js'; + * @import {PassStyleHelper} from './internal-types.js'; + */ + const { ownKeys } = Reflect; const { getPrototypeOf, prototype: objectPrototype } = Object; /** * @param {unknown} candidate - * @param {Checker} [check] + * @param {Rejector} reject */ -const checkObjectPrototype = (candidate, check = undefined) => { +const confirmObjectPrototype = (candidate, reject) => { return ( getPrototypeOf(candidate) === objectPrototype || - (!!check && - CX(check)`Records must inherit from Object.prototype: ${candidate}`) + (reject && reject`Records must inherit from Object.prototype: ${candidate}`) ); }; @@ -28,53 +27,44 @@ const checkObjectPrototype = (candidate, check = undefined) => { * @param {unknown} candidate * @param {PropertyKey} key * @param {unknown} value - * @param {Checker} [check] + * @param {Rejector} reject */ -const checkPropertyCanBeValid = (candidate, key, value, check = undefined) => { +const confirmPropertyCanBeValid = (candidate, key, value, reject) => { return ( (typeof key === 'string' || - (!!check && - CX( - check, - )`Records can only have string-named properties: ${candidate}`)) && + (reject && + reject`Records can only have string-named properties: ${candidate}`)) && (!canBeMethod(value) || - (!!check && + (reject && // TODO: Update message now that there is no such thing as "implicit Remotable". - CX( - check, - )`Records cannot contain non-far functions because they may be methods of an implicit Remotable: ${candidate}`)) + reject`Records cannot contain non-far functions because they may be methods of an implicit Remotable: ${candidate}`)) ); }; /** * - * @type {import('./internal-types.js').PassStyleHelper} + * @type {PassStyleHelper} */ export const CopyRecordHelper = harden({ styleName: 'copyRecord', - canBeValid: (candidate, check = undefined) => { + confirmCanBeValid: (candidate, reject) => { return ( - checkObjectPrototype(candidate, check) && + confirmObjectPrototype(candidate, reject) && // Reject any candidate with a symbol-keyed property or method-like property // (such input is potentially a Remotable). ownKeys(candidate).every(key => - checkPropertyCanBeValid(candidate, key, candidate[key], check), + confirmPropertyCanBeValid(candidate, key, candidate[key], reject), ) ); }, assertRestValid: (candidate, passStyleOfRecur) => { // Validate that each own property has a recursively passable associated - // value (we already know from canBeValid that the other constraints are + // value (we already know from confirmCanBeValid that the other constraints are // satisfied). for (const name of ownKeys(candidate)) { - const { value } = getOwnDataDescriptor( - candidate, - name, - true, - assertChecker, - ); + const { value } = confirmOwnDataDescriptor(candidate, name, true, Fail); passStyleOfRecur(value); } }, diff --git a/packages/pass-style/src/error.js b/packages/pass-style/src/error.js index 87fd8afb62..10768d5088 100644 --- a/packages/pass-style/src/error.js +++ b/packages/pass-style/src/error.js @@ -1,10 +1,12 @@ /// -import { q } from '@endo/errors'; -import { assertChecker, CX } from './passStyle-helpers.js'; +import { Fail, q, hideAndHardenFunction } from '@endo/errors'; -/** @import {PassStyleHelper} from './internal-types.js' */ -/** @import {Checker, PassStyle, PassStyleOf} from './types.js' */ +/** + * @import {Rejector} from '@endo/errors/rejector.js'; + * @import {PassStyleHelper} from './internal-types.js'; + * @import {PassStyle} from './types.js'; + */ const { getPrototypeOf, getOwnPropertyDescriptors, hasOwn, entries } = Object; @@ -50,17 +52,17 @@ harden(getErrorConstructor); /** * @param {unknown} candidate - * @param {Checker} [check] + * @param {Rejector} reject * @returns {boolean} */ -const checkErrorLike = (candidate, check = undefined) => { +const confirmErrorLike = (candidate, reject) => { // TODO: Need a better test than instanceof return ( candidate instanceof Error || - (!!check && CX(check)`Error expected: ${candidate}`) + (reject && reject`Error expected: ${candidate}`) ); }; -harden(checkErrorLike); +harden(confirmErrorLike); /// /** @@ -81,34 +83,34 @@ harden(checkErrorLike); * @param {unknown} candidate * @returns {boolean} */ -export const isErrorLike = candidate => checkErrorLike(candidate); -harden(isErrorLike); +export const isErrorLike = candidate => confirmErrorLike(candidate, false); +hideAndHardenFunction(isErrorLike); /** * @param {string} propName * @param {PropertyDescriptor} desc * @param {(val: any) => PassStyle} passStyleOfRecur - * @param {Checker} [check] + * @param {Rejector} reject * @returns {boolean} */ -export const checkRecursivelyPassableErrorPropertyDesc = ( +export const confirmRecursivelyPassableErrorPropertyDesc = ( propName, desc, passStyleOfRecur, - check = undefined, + reject, ) => { if (desc.enumerable) { return ( - !!check && - CX(check)`Passable Error ${q( + reject && + reject`Passable Error ${q( propName, )} own property must not be enumerable: ${desc}` ); } if (!hasOwn(desc, 'value')) { return ( - !!check && - CX(check)`Passable Error ${q( + reject && + reject`Passable Error ${q( propName, )} own property must be a data property: ${desc}` ); @@ -119,28 +121,28 @@ export const checkRecursivelyPassableErrorPropertyDesc = ( case 'stack': { return ( typeof value === 'string' || - (!!check && - CX(check)`Passable Error ${q( + (reject && + reject`Passable Error ${q( propName, )} own property must be a string: ${value}`) ); } case 'cause': { // eslint-disable-next-line no-use-before-define - return checkRecursivelyPassableError(value, passStyleOfRecur, check); + return confirmRecursivelyPassableError(value, passStyleOfRecur, reject); } case 'errors': { if (!Array.isArray(value) || passStyleOfRecur(value) !== 'copyArray') { return ( - !!check && - CX(check)`Passable Error ${q( + reject && + reject`Passable Error ${q( propName, )} own property must be a copyArray: ${value}` ); } return value.every(err => // eslint-disable-next-line no-use-before-define - checkRecursivelyPassableError(err, passStyleOfRecur, check), + confirmRecursivelyPassableError(err, passStyleOfRecur, reject), ); } default: { @@ -148,24 +150,23 @@ export const checkRecursivelyPassableErrorPropertyDesc = ( } } return ( - !!check && - CX(check)`Passable Error has extra unpassed property ${q(propName)}` + reject && reject`Passable Error has extra unpassed property ${q(propName)}` ); }; -harden(checkRecursivelyPassableErrorPropertyDesc); +harden(confirmRecursivelyPassableErrorPropertyDesc); /** * @param {unknown} candidate * @param {(val: any) => PassStyle} passStyleOfRecur - * @param {Checker} [check] + * @param {Rejector} reject * @returns {boolean} */ -export const checkRecursivelyPassableError = ( +export const confirmRecursivelyPassableError = ( candidate, passStyleOfRecur, - check = undefined, + reject, ) => { - if (!checkErrorLike(candidate, check)) { + if (!confirmErrorLike(candidate, reject)) { return false; } const proto = getPrototypeOf(candidate); @@ -173,32 +174,28 @@ export const checkRecursivelyPassableError = ( const errConstructor = getErrorConstructor(name); if (errConstructor === undefined || errConstructor.prototype !== proto) { return ( - !!check && - CX( - check, - )`Passable Error must inherit from an error class .prototype: ${candidate}` + reject && + reject`Passable Error must inherit from an error class .prototype: ${candidate}` ); } const descs = getOwnPropertyDescriptors(candidate); if (!('message' in descs)) { return ( - !!check && - CX( - check, - )`Passable Error must have an own "message" string property: ${candidate}` + reject && + reject`Passable Error must have an own "message" string property: ${candidate}` ); } return entries(descs).every(([propName, desc]) => - checkRecursivelyPassableErrorPropertyDesc( + confirmRecursivelyPassableErrorPropertyDesc( propName, desc, passStyleOfRecur, - check, + reject, ), ); }; -harden(checkRecursivelyPassableError); +harden(confirmRecursivelyPassableError); /** * @type {PassStyleHelper} @@ -206,8 +203,8 @@ harden(checkRecursivelyPassableError); export const ErrorHelper = harden({ styleName: 'error', - canBeValid: checkErrorLike, + confirmCanBeValid: confirmErrorLike, assertRestValid: (candidate, passStyleOfRecur) => - checkRecursivelyPassableError(candidate, passStyleOfRecur, assertChecker), + confirmRecursivelyPassableError(candidate, passStyleOfRecur, Fail), }); diff --git a/packages/pass-style/src/internal-types.js b/packages/pass-style/src/internal-types.js index 7aaf87e128..224a6b5b33 100644 --- a/packages/pass-style/src/internal-types.js +++ b/packages/pass-style/src/internal-types.js @@ -1,8 +1,9 @@ export {}; -/** @import {Checker} from './types.js' */ -/** @import {PassStyle} from './types.js' */ -/** @import {PassStyleOf} from './types.js' */ +/** + * @import {Rejector} from '@endo/errors/rejector.js'; + * @import {PassStyle} from './types.js'; + */ /** * The PassStyleHelper are only used to make a `passStyleOf` function. @@ -17,11 +18,12 @@ export {}; * * @typedef {object} PassStyleHelper * @property {PassStyle} styleName - * @property {(candidate: any, check?: Checker) => boolean} canBeValid - * If `canBeValid` returns true, then the candidate would + * @property {(candidate: any, reject: Rejector) => boolean} confirmCanBeValid + * If `confirmCanBeValid` returns true, then the candidate would * definitely not be valid for any of the other helpers. * `assertRestValid` still needs to be called to determine if it - * actually is valid, but only after the `canBeValid` check has passed. + * actually is valid, but only after the `confirmCanBeValid` check has passed. + * * @property {(candidate: any, * passStyleOfRecur: (val: any) => PassStyle * ) => void} assertRestValid diff --git a/packages/pass-style/src/make-far.js b/packages/pass-style/src/make-far.js index 0baa8323cf..db74613c44 100644 --- a/packages/pass-style/src/make-far.js +++ b/packages/pass-style/src/make-far.js @@ -2,7 +2,7 @@ import { getMethodNames } from '@endo/eventual-send/utils.js'; import { q, Fail } from '@endo/errors'; -import { assertChecker, PASS_STYLE } from './passStyle-helpers.js'; +import { PASS_STYLE } from './passStyle-helpers.js'; import { assertIface, getInterfaceOf, RemotableHelper } from './remotable.js'; /** @import {RemotableBrand} from '@endo/eventual-send' */ @@ -52,7 +52,7 @@ const makeRemotableProto = (remotable, iface) => { }; const assertCanBeRemotable = candidate => - RemotableHelper.canBeValid(candidate, assertChecker); + RemotableHelper.confirmCanBeValid(candidate, Fail); /** * Create and register a Remotable. After this, getInterfaceOf(remotable) diff --git a/packages/pass-style/src/passStyle-helpers.js b/packages/pass-style/src/passStyle-helpers.js index f64419de86..3a9315f815 100644 --- a/packages/pass-style/src/passStyle-helpers.js +++ b/packages/pass-style/src/passStyle-helpers.js @@ -1,8 +1,9 @@ /// -import { X, q } from '@endo/errors'; +import { q, hideAndHardenFunction } from '@endo/errors'; /** + * @import {Rejector} from '@endo/errors/rejector.js'; * @import {Checker, PassStyle, JSPrimitive} from './types.js'; */ @@ -40,7 +41,7 @@ export const isPrimitive = val => // So instead we use this adhoc set of type tests. But this is not safe in // the face of possible evolution of the language. Beware! !val || (typeof val !== 'object' && typeof val !== 'function'); -harden(isPrimitive); +hideAndHardenFunction(isPrimitive); // NOTE: Do not make this type more precise because it breaks only clients // that rely on it being less precise. @@ -54,7 +55,7 @@ export const isObject = val => // So instead we use this adhoc set of type tests. But this is not safe in // the face of possible evolution of the language. Beware! !!val && (typeof val === 'object' || typeof val === 'function'); -harden(isObject); +hideAndHardenFunction(isObject); /** * Duplicates packages/ses/src/make-hardener.js to avoid a dependency. @@ -66,7 +67,7 @@ export const isTypedArray = object => { const tag = apply(getTypedArrayToStringTag, object, []); return tag !== undefined; }; -harden(isTypedArray); +hideAndHardenFunction(isTypedArray); export const PASS_STYLE = Symbol.for('passStyle'); @@ -76,28 +77,14 @@ export const PASS_STYLE = Symbol.for('passStyle'); * the corresponding predicate function would have returned true. But it * reproduces the internal tests so failures can give a better error message. * + * @deprecated Use `Fail` with confirm/reject pattern instead * @type {Checker} */ export const assertChecker = (cond, details) => { assert(cond, details); return true; }; -harden(assertChecker); - -/** - * Returns a template literal tag function to fail the provided Checker with details. - * The name must be short for ergonomic inline use as in: - * ``` - * return checkCondition(...) || (!!check && CX(check)`...`); - * ``` - * - * @param {Checker} check - */ -export const CX = check => { - const reject = (T, ...subs) => check(false, X(T, ...subs)); - return reject; -}; -harden(CX); +hideAndHardenFunction(assertChecker); /** * Verifies the presence and enumerability of an own data property @@ -106,40 +93,34 @@ harden(CX); * @param {object} candidate * @param {string|number|symbol} propName * @param {boolean} shouldBeEnumerable - * @param {Checker} [check] + * @param {Rejector} reject * @returns {PropertyDescriptor} */ -export const getOwnDataDescriptor = ( +export const confirmOwnDataDescriptor = ( candidate, propName, shouldBeEnumerable, - check, + reject, ) => { const desc = /** @type {PropertyDescriptor} */ ( getOwnPropertyDescriptor(candidate, propName) ); return (desc !== undefined || - (!!check && CX(check)`${q(propName)} property expected: ${candidate}`)) && + (reject && reject`${q(propName)} property expected: ${candidate}`)) && (hasOwn(desc, 'value') || - (!!check && - CX( - check, - )`${q(propName)} must not be an accessor property: ${candidate}`)) && + (reject && + reject`${q(propName)} must not be an accessor property: ${candidate}`)) && (shouldBeEnumerable ? desc.enumerable || - (!!check && - CX( - check, - )`${q(propName)} must be an enumerable property: ${candidate}`) + (reject && + reject`${q(propName)} must be an enumerable property: ${candidate}`) : !desc.enumerable || - (!!check && - CX( - check, - )`${q(propName)} must not be an enumerable property: ${candidate}`)) + (reject && + reject`${q(propName)} must not be an enumerable property: ${candidate}`)) ? desc : /** @type {PropertyDescriptor} */ (/** @type {unknown} */ (undefined)); }; -harden(getOwnDataDescriptor); +harden(confirmOwnDataDescriptor); /** * @template {import('./types.js').InterfaceSpec} T @@ -149,65 +130,68 @@ harden(getOwnDataDescriptor); export const getTag = tagRecord => tagRecord[Symbol.toStringTag]; harden(getTag); -export const checkPassStyle = (obj, passStyle, expectedPassStyle, check) => { +/** + * @param {any} obj + * @param {PassStyle} passStyle + * @param {PassStyle} expectedPassStyle + * @param {Rejector} reject + */ +export const confirmPassStyle = (obj, passStyle, expectedPassStyle, reject) => { return ( passStyle === expectedPassStyle || - (!!check && - CX(check)`Expected ${q(expectedPassStyle)}, not ${q(passStyle)}: ${obj}`) + (reject && + reject`Expected ${q(expectedPassStyle)}, not ${q(passStyle)}: ${obj}`) ); }; -harden(checkPassStyle); +harden(confirmPassStyle); -const makeCheckTagRecord = checkProto => { +const makeConfirmTagRecord = confirmProto => { /** * @param {import('./types.js').PassStyled} tagRecord * @param {PassStyle} expectedPassStyle - * @param {Checker} [check] + * @param {Rejector} reject * @returns {boolean} */ - const checkTagRecord = (tagRecord, expectedPassStyle, check) => { + const confirmTagRecord = (tagRecord, expectedPassStyle, reject) => { return ( (!isPrimitive(tagRecord) || - (!!check && - CX(check)`A non-object cannot be a tagRecord: ${tagRecord}`)) && + (reject && reject`A non-object cannot be a tagRecord: ${tagRecord}`)) && (isFrozen(tagRecord) || - (!!check && CX(check)`A tagRecord must be frozen: ${tagRecord}`)) && + (reject && reject`A tagRecord must be frozen: ${tagRecord}`)) && (!isArray(tagRecord) || - (!!check && CX(check)`An array cannot be a tagRecord: ${tagRecord}`)) && - checkPassStyle( + (reject && reject`An array cannot be a tagRecord: ${tagRecord}`)) && + confirmPassStyle( tagRecord, - getOwnDataDescriptor(tagRecord, PASS_STYLE, false, check).value, + confirmOwnDataDescriptor(tagRecord, PASS_STYLE, false, reject).value, expectedPassStyle, - check, + reject, ) && - (typeof getOwnDataDescriptor(tagRecord, Symbol.toStringTag, false, check) - .value === 'string' || - (!!check && - CX( - check, - )`A [Symbol.toStringTag]-named property must be a string: ${tagRecord}`)) && - checkProto(tagRecord, getPrototypeOf(tagRecord), check) + (typeof confirmOwnDataDescriptor( + tagRecord, + Symbol.toStringTag, + false, + reject, + ).value === 'string' || + (reject && + reject`A [Symbol.toStringTag]-named property must be a string: ${tagRecord}`)) && + confirmProto(tagRecord, getPrototypeOf(tagRecord), reject) ); }; - return harden(checkTagRecord); + return harden(confirmTagRecord); }; -export const checkTagRecord = makeCheckTagRecord( - (val, proto, check) => +export const confirmTagRecord = makeConfirmTagRecord( + (val, proto, reject) => proto === objectPrototype || - (!!check && - check(false, X`A tagRecord must inherit from Object.prototype: ${val}`)), + (reject && reject`A tagRecord must inherit from Object.prototype: ${val}`), ); -harden(checkTagRecord); +harden(confirmTagRecord); -export const checkFunctionTagRecord = makeCheckTagRecord( - (val, proto, check) => +export const confirmFunctionTagRecord = makeConfirmTagRecord( + (val, proto, reject) => proto === functionPrototype || (proto !== null && getPrototypeOf(proto) === functionPrototype) || - (!!check && - check( - false, - X`For functions, a tagRecord must inherit from Function.prototype: ${val}`, - )), + (reject && + reject`For functions, a tagRecord must inherit from Function.prototype: ${val}`), ); -harden(checkFunctionTagRecord); +harden(confirmFunctionTagRecord); diff --git a/packages/pass-style/src/passStyleOf.js b/packages/pass-style/src/passStyleOf.js index 5e9552a22c..bae5e85711 100644 --- a/packages/pass-style/src/passStyleOf.js +++ b/packages/pass-style/src/passStyleOf.js @@ -3,13 +3,15 @@ /// import { isPromise } from '@endo/promise-kit'; -import { X, Fail, q, annotateError, makeError } from '@endo/errors'; import { - isPrimitive, - isTypedArray, - PASS_STYLE, - assertChecker, -} from './passStyle-helpers.js'; + X, + Fail, + q, + annotateError, + makeError, + hideAndHardenFunction, +} from '@endo/errors'; +import { isPrimitive, isTypedArray, PASS_STYLE } from './passStyle-helpers.js'; import { CopyArrayHelper } from './copyArray.js'; import { ByteArrayHelper } from './byteArray.js'; @@ -17,8 +19,8 @@ import { CopyRecordHelper } from './copyRecord.js'; import { TaggedHelper } from './tagged.js'; import { ErrorHelper, - checkRecursivelyPassableErrorPropertyDesc, - checkRecursivelyPassableError, + confirmRecursivelyPassableErrorPropertyDesc, + confirmRecursivelyPassableError, getErrorConstructor, isErrorLike, } from './error.js'; @@ -69,7 +71,7 @@ const makeHelperTable = passStyleHelpers => { }; /** - * The `assertRestValid` assumes that the `canBeValid` check has already passed. + * The `assertRestValid` assumes that the `confirmCanBeValid` check has already passed. * Contexts where we cannot assume that should call `assertValid` instead, * which checks both conditions in the right order. * @@ -79,7 +81,7 @@ const makeHelperTable = passStyleHelpers => { * @returns {void} */ const assertValid = (helper, candidate, passStyleOfRecur) => { - helper.canBeValid(candidate, assertChecker); + helper.confirmCanBeValid(candidate, Fail); helper.assertRestValid(candidate, passStyleOfRecur); }; @@ -188,7 +190,7 @@ const makePassStyleOf = passStyleHelpers => { return /** @type {PassStyle} */ (passStyleTag); } for (const helper of passStyleHelpers) { - if (helper.canBeValid(inner)) { + if (helper.confirmCanBeValid(inner, false)) { helper.assertRestValid(inner, passStyleOfRecur); return helper.styleName; } @@ -246,7 +248,7 @@ export const passStyleOf = export const assertPassable = val => { passStyleOf(val); // throws if val is not a passable }; -harden(assertPassable); +hideAndHardenFunction(assertPassable); /** * Is `specimen` Passable? This returns true iff `passStyleOf(specimen)` @@ -273,7 +275,7 @@ export const isPassable = specimen => { return false; } }; -harden(isPassable); +hideAndHardenFunction(isPassable); /** * @param {string} name @@ -281,7 +283,7 @@ harden(isPassable); * @returns {boolean} */ const isPassableErrorPropertyDesc = (name, desc) => - checkRecursivelyPassableErrorPropertyDesc(name, desc, passStyleOf); + confirmRecursivelyPassableErrorPropertyDesc(name, desc, passStyleOf, false); /** * After hardening, if `err` is a passable error, return it. @@ -298,7 +300,7 @@ const isPassableErrorPropertyDesc = (name, desc) => */ export const toPassableError = err => { harden(err); - if (checkRecursivelyPassableError(err, passStyleOf)) { + if (confirmRecursivelyPassableError(err, passStyleOf, false)) { return err; } const { name, message } = err; diff --git a/packages/pass-style/src/remotable.js b/packages/pass-style/src/remotable.js index 586597930e..8e9cf30d66 100644 --- a/packages/pass-style/src/remotable.js +++ b/packages/pass-style/src/remotable.js @@ -1,22 +1,19 @@ /// -import { Fail, q } from '@endo/errors'; +import { Fail, q, hideAndHardenFunction } from '@endo/errors'; import { getMethodNames } from '@endo/eventual-send/utils.js'; import { - assertChecker, PASS_STYLE, - checkTagRecord, - checkFunctionTagRecord, + confirmTagRecord, + confirmFunctionTagRecord, isPrimitive, getTag, - CX, } from './passStyle-helpers.js'; /** - * @import {Checker, RemotableMethodName} from './types.js' - * @import {InterfaceSpec, PassStyled} from './types.js' - * @import {PassStyleHelper} from './internal-types.js' - * @import {RemotableObject as Remotable} from './types.js' + * @import {Rejector} from '@endo/errors/rejector.js'; + * @import {PassStyleHelper} from './internal-types.js'; + * @import {InterfaceSpec, PassStyled, RemotableObject as Remotable, RemotableMethodName} from './types.js'; */ /** @@ -75,21 +72,19 @@ const { /** * @param {InterfaceSpec} iface - * @param {Checker} [check] + * @param {Rejector} reject */ -const checkIface = (iface, check) => { +const confirmIface = (iface, reject) => { return ( // TODO other possible ifaces, once we have third party veracity (typeof iface === 'string' || - (!!check && - CX( - check, - )`For now, interface ${iface} must be a string; unimplemented`)) && + (reject && + reject`For now, interface ${iface} must be a string; unimplemented`)) && (iface === 'Remotable' || iface.startsWith('Alleged: ') || iface.startsWith('DebugName: ') || - (!!check && - CX(check)`For now, iface ${q( + (reject && + reject`For now, iface ${q( iface, )} must be "Remotable" or begin with "Alleged: " or "DebugName: "; unimplemented`)) ); @@ -105,15 +100,15 @@ const checkIface = (iface, check) => { * * @param {InterfaceSpec} iface */ -export const assertIface = iface => checkIface(iface, assertChecker); -harden(assertIface); +export const assertIface = iface => confirmIface(iface, Fail); +hideAndHardenFunction(assertIface); /** * @param {object | Function} original - * @param {Checker} [check] + * @param {Rejector} reject * @returns {boolean} */ -const checkRemotableProtoOf = (original, check) => { +const confirmRemotableProtoOf = (original, reject) => { !isPrimitive(original) || Fail`Remotables must be objects or functions: ${original}`; @@ -138,8 +133,7 @@ const checkRemotableProtoOf = (original, check) => { proto === Function.prototype ) { return ( - !!check && - CX(check)`Remotables must be explicitly declared: ${q(original)}` + reject && reject`Remotables must be explicitly declared: ${q(original)}` ); } @@ -147,13 +141,13 @@ const checkRemotableProtoOf = (original, check) => { const protoProto = getPrototypeOf(proto); if (protoProto !== objectPrototype && protoProto !== null) { // eslint-disable-next-line no-use-before-define - return checkRemotable(proto, check); + return confirmRemotable(proto, reject); } - if (!checkTagRecord(proto, 'remotable', check)) { + if (!confirmTagRecord(proto, 'remotable', reject)) { return false; } } else if (typeof original === 'function') { - if (!checkFunctionTagRecord(proto, 'remotable', check)) { + if (!confirmFunctionTagRecord(proto, 'remotable', reject)) { return false; } } @@ -162,7 +156,7 @@ const checkRemotableProtoOf = (original, check) => { const passStyleKey = /** @type {unknown} */ (PASS_STYLE); const tagKey = /** @type {unknown} */ (Symbol.toStringTag); const { - // checkTagRecord already verified PASS_STYLE and Symbol.toStringTag own data properties. + // confirmTagRecord already verified PASS_STYLE and Symbol.toStringTag own data properties. [/** @type {string} */ (passStyleKey)]: _passStyleDesc, [/** @type {string} */ (tagKey)]: { value: iface }, ...restDescs @@ -170,11 +164,9 @@ const checkRemotableProtoOf = (original, check) => { return ( (ownKeys(restDescs).length === 0 || - (!!check && - CX( - check, - )`Unexpected properties on Remotable Proto ${ownKeys(restDescs)}`)) && - checkIface(iface, check) + (reject && + reject`Unexpected properties on Remotable Proto ${ownKeys(restDescs)}`)) && + confirmIface(iface, reject) ); }; @@ -191,23 +183,21 @@ const confirmedRemotables = new WeakSet(); /** * @param {any} val - * @param {Checker} [check] + * @param {Rejector} reject * @returns {val is Remotable} */ -const checkRemotable = (val, check) => { +const confirmRemotable = (val, reject) => { if (confirmedRemotables.has(val)) { return true; } if (!isFrozen(val)) { - return ( - !!check && CX(check)`cannot serialize non-frozen objects like ${val}` - ); + return reject && reject`cannot serialize non-frozen objects like ${val}`; } // eslint-disable-next-line no-use-before-define - if (!RemotableHelper.canBeValid(val, check)) { + if (!RemotableHelper.confirmCanBeValid(val, reject)) { return false; } - const result = checkRemotableProtoOf(val, check); + const result = confirmRemotableProtoOf(val, reject); if (result) { confirmedRemotables.add(val); } @@ -227,7 +217,7 @@ export const getInterfaceOf = val => { if ( isPrimitive(val) || val[PASS_STYLE] !== 'remotable' || - !checkRemotable(val) + !confirmRemotable(val, false) ) { // @ts-expect-error narrowed return undefined; @@ -244,14 +234,13 @@ harden(getInterfaceOf); export const RemotableHelper = harden({ styleName: 'remotable', - canBeValid: (candidate, check = undefined) => { + confirmCanBeValid: (candidate, reject) => { const validType = (!isPrimitive(candidate) || - (!!check && - CX(check)`cannot serialize non-objects as Remotable ${candidate}`)) && + (reject && + reject`cannot serialize non-objects as Remotable ${candidate}`)) && (!isArray(candidate) || - (!!check && - CX(check)`cannot serialize arrays as Remotable ${candidate}`)); + (reject && reject`cannot serialize arrays as Remotable ${candidate}`)); if (!validType) { return false; } @@ -264,19 +253,20 @@ export const RemotableHelper = harden({ return ( // Typecast needed due to https://github.com/microsoft/TypeScript/issues/1863 (hasOwn(descs[/** @type {string} */ (key)], 'value') || - (!!check && - CX(check)`cannot serialize Remotables with accessors like ${q( + (reject && + reject`cannot serialize Remotables with accessors like ${q( String(key), )} in ${candidate}`)) && - ((key === Symbol.toStringTag && checkIface(candidate[key], check)) || + ((key === Symbol.toStringTag && + confirmIface(candidate[key], reject)) || ((canBeMethod(candidate[key]) || - (!!check && - CX(check)`cannot serialize Remotables with non-methods like ${q( + (reject && + reject`cannot serialize Remotables with non-methods like ${q( String(key), )} in ${candidate}`)) && (key !== PASS_STYLE || - (!!check && - CX(check)`A pass-by-remote cannot shadow ${q(PASS_STYLE)}`)))) + (reject && + reject`A pass-by-remote cannot shadow ${q(PASS_STYLE)}`)))) ); }); } else if (typeof candidate === 'function') { @@ -292,31 +282,25 @@ export const RemotableHelper = harden({ const restKeys = ownKeys(restDescs); return ( ((nameDesc && typeof nameDesc.value === 'string') || - (!!check && - CX(check)`Far function name must be a string, in ${candidate}`)) && + (reject && + reject`Far function name must be a string, in ${candidate}`)) && ((lengthDesc && typeof lengthDesc.value === 'number') || - (!!check && - CX( - check, - )`Far function length must be a number, in ${candidate}`)) && + (reject && + reject`Far function length must be a number, in ${candidate}`)) && (toStringTagDesc === undefined || ((typeof toStringTagDesc.value === 'string' || - (!!check && - CX( - check, - )`Far function @@toStringTag must be a string, in ${candidate}`)) && - checkIface(toStringTagDesc.value, check))) && + (reject && + reject`Far function @@toStringTag must be a string, in ${candidate}`)) && + confirmIface(toStringTagDesc.value, reject))) && (restKeys.length === 0 || - (!!check && - CX( - check, - )`Far functions unexpected properties besides .name and .length ${restKeys}`)) + (reject && + reject`Far functions unexpected properties besides .name and .length ${restKeys}`)) ); } - return !!check && CX(check)`unrecognized typeof ${candidate}`; + return reject && reject`unrecognized typeof ${candidate}`; }, - assertRestValid: candidate => checkRemotable(candidate, assertChecker), + assertRestValid: candidate => confirmRemotable(candidate, Fail), every: (_passable, _fn) => true, }); diff --git a/packages/pass-style/src/safe-promise.js b/packages/pass-style/src/safe-promise.js index 071d6b6fd4..89dbe64ba2 100644 --- a/packages/pass-style/src/safe-promise.js +++ b/packages/pass-style/src/safe-promise.js @@ -1,10 +1,11 @@ /// import { isPromise } from '@endo/promise-kit'; -import { q } from '@endo/errors'; -import { assertChecker, CX } from './passStyle-helpers.js'; +import { Fail, q, hideAndHardenFunction } from '@endo/errors'; -/** @import {Checker} from './types.js' */ +/** + * @import {Rejector} from '@endo/errors/rejector.js'; + */ const { isFrozen, getPrototypeOf, getOwnPropertyDescriptor, hasOwn } = Object; const { ownKeys } = Reflect; @@ -12,10 +13,10 @@ const { toStringTag } = Symbol; /** * @param {Promise} pr The value to examine - * @param {Checker} check + * @param {Rejector} reject * @returns {pr is Promise} Whether it is a safe promise */ -const checkPromiseOwnKeys = (pr, check) => { +const confirmPromiseOwnKeys = (pr, reject) => { const keys = ownKeys(pr); if (keys.length === 0) { @@ -36,9 +37,10 @@ const checkPromiseOwnKeys = (pr, check) => { ); if (unknownKeys.length !== 0) { - return CX( - check, - )`${pr} - Must not have any own properties: ${q(unknownKeys)}`; + return ( + reject && + reject`${pr} - Must not have any own properties: ${q(unknownKeys)}` + ); } /** @@ -70,15 +72,14 @@ const checkPromiseOwnKeys = (pr, check) => { assert(tagDesc !== undefined); return ( (hasOwn(tagDesc, 'value') || - CX( - check, - )`Own @@toStringTag must be a data property, not an accessor: ${q(tagDesc)}`) && + (reject && + reject`Own @@toStringTag must be a data property, not an accessor: ${q(tagDesc)}`)) && (typeof tagDesc.value === 'string' || - CX( - check, - )`Own @@toStringTag value must be a string: ${q(tagDesc.value)}`) && + (reject && + reject`Own @@toStringTag value must be a string: ${q(tagDesc.value)}`)) && (!tagDesc.enumerable || - CX(check)`Own @@toStringTag must not be enumerable: ${q(tagDesc)}`) + (reject && + reject`Own @@toStringTag must not be enumerable: ${q(tagDesc)}`)) ); } const val = pr[key]; @@ -104,11 +105,12 @@ const checkPromiseOwnKeys = (pr, check) => { return true; } } - return CX( - check, - )`Unexpected Node async_hooks additions to promise: ${pr}.${q( - String(key), - )} is ${val}`; + return ( + reject && + reject`Unexpected Node async_hooks additions to promise: ${pr}.${q( + String(key), + )} is ${val}` + ); }; return keys.every(checkSafeOwnKey); @@ -127,21 +129,22 @@ const checkPromiseOwnKeys = (pr, check) => { * use it here as well. * * @param {unknown} pr The value to examine - * @param {Checker} check + * @param {Rejector} reject * @returns {pr is Promise} Whether it is a safe promise */ -const checkSafePromise = (pr, check) => { +const confirmSafePromise = (pr, reject) => { return ( - (isFrozen(pr) || CX(check)`${pr} - Must be frozen`) && - (isPromise(pr) || CX(check)`${pr} - Must be a promise`) && + (isFrozen(pr) || (reject && reject`${pr} - Must be frozen`)) && + (isPromise(pr) || (reject && reject`${pr} - Must be a promise`)) && (getPrototypeOf(pr) === Promise.prototype || - CX(check)`${pr} - Must inherit from Promise.prototype: ${q( - getPrototypeOf(pr), - )}`) && - checkPromiseOwnKeys(/** @type {Promise} */ (pr), check) + (reject && + reject`${pr} - Must inherit from Promise.prototype: ${q( + getPrototypeOf(pr), + )}`)) && + confirmPromiseOwnKeys(/** @type {Promise} */ (pr), reject) ); }; -harden(checkSafePromise); +harden(confirmSafePromise); /** * Determine if the argument is a Promise. @@ -149,7 +152,8 @@ harden(checkSafePromise); * @param {unknown} pr The value to examine * @returns {pr is Promise} Whether it is a promise */ -export const isSafePromise = pr => checkSafePromise(pr, x => x); -harden(isSafePromise); +export const isSafePromise = pr => confirmSafePromise(pr, false); +hideAndHardenFunction(isSafePromise); -export const assertSafePromise = pr => checkSafePromise(pr, assertChecker); +export const assertSafePromise = pr => confirmSafePromise(pr, Fail); +hideAndHardenFunction(assertSafePromise); diff --git a/packages/pass-style/src/string.js b/packages/pass-style/src/string.js index dad6b1cfef..6d388b1256 100644 --- a/packages/pass-style/src/string.js +++ b/packages/pass-style/src/string.js @@ -1,5 +1,5 @@ import { getEnvironmentOption } from '@endo/env-options'; -import { Fail } from '@endo/errors'; +import { Fail, hideAndHardenFunction } from '@endo/errors'; // know about`isWellFormed` const hasWellFormedStringMethod = !!String.prototype.isWellFormed; @@ -40,7 +40,7 @@ export const isWellFormedString = hasWellFormedStringMethod } return true; }; -harden(isWellFormedString); +hideAndHardenFunction(isWellFormedString); /** * Returns normally when `isWellFormedString(str)` would return true. @@ -52,7 +52,7 @@ harden(isWellFormedString); export const assertWellFormedString = str => { isWellFormedString(str) || Fail`Expected well-formed unicode string: ${str}`; }; -harden(assertWellFormedString); +hideAndHardenFunction(assertWellFormedString); const ONLY_WELL_FORMED_STRINGS_PASSABLE = getEnvironmentOption('ONLY_WELL_FORMED_STRINGS_PASSABLE', 'disabled', [ @@ -80,4 +80,4 @@ export const assertPassableString = str => { typeof str === 'string' || Fail`Expected string ${str}`; !ONLY_WELL_FORMED_STRINGS_PASSABLE || assertWellFormedString(str); }; -harden(assertPassableString); +hideAndHardenFunction(assertPassableString); diff --git a/packages/pass-style/src/symbol.js b/packages/pass-style/src/symbol.js index 9b42c67844..fb5a0945c8 100644 --- a/packages/pass-style/src/symbol.js +++ b/packages/pass-style/src/symbol.js @@ -1,4 +1,4 @@ -import { Fail, q } from '@endo/errors'; +import { Fail, q, hideAndHardenFunction } from '@endo/errors'; const { ownKeys } = Reflect; @@ -38,7 +38,7 @@ harden(isPassableSymbol); export const assertPassableSymbol = sym => isPassableSymbol(sym) || Fail`Only registered symbols or well-known symbols are passable: ${q(sym)}`; -harden(assertPassableSymbol); +hideAndHardenFunction(assertPassableSymbol); /** * If `sym` is a passable symbol, return a string that uniquely identifies this diff --git a/packages/pass-style/src/tagged.js b/packages/pass-style/src/tagged.js index 7abd906385..2bb11b29a1 100644 --- a/packages/pass-style/src/tagged.js +++ b/packages/pass-style/src/tagged.js @@ -2,11 +2,10 @@ import { Fail } from '@endo/errors'; import { - assertChecker, - checkTagRecord, + confirmTagRecord, PASS_STYLE, - getOwnDataDescriptor, - checkPassStyle, + confirmOwnDataDescriptor, + confirmPassStyle, } from './passStyle-helpers.js'; /** @@ -23,17 +22,17 @@ const { getOwnPropertyDescriptors } = Object; export const TaggedHelper = harden({ styleName: 'tagged', - canBeValid: (candidate, check = undefined) => - checkPassStyle(candidate, candidate[PASS_STYLE], 'tagged', check), + confirmCanBeValid: (candidate, reject) => + confirmPassStyle(candidate, candidate[PASS_STYLE], 'tagged', reject), assertRestValid: (candidate, passStyleOfRecur) => { - checkTagRecord(candidate, 'tagged', assertChecker); + confirmTagRecord(candidate, 'tagged', Fail); // Typecasts needed due to https://github.com/microsoft/TypeScript/issues/1863 const passStyleKey = /** @type {unknown} */ (PASS_STYLE); const tagKey = /** @type {unknown} */ (Symbol.toStringTag); const { - // checkTagRecord already verified PASS_STYLE and Symbol.toStringTag own data properties. + // confirmTagRecord already verified PASS_STYLE and Symbol.toStringTag own data properties. [/** @type {string} */ (passStyleKey)]: _passStyleDesc, [/** @type {string} */ (tagKey)]: _labelDesc, payload: _payloadDesc, // value checked by recursive walk at the end @@ -45,7 +44,7 @@ export const TaggedHelper = harden({ // Validate that the 'payload' property is own/data/enumerable // and its associated value is recursively passable. passStyleOfRecur( - getOwnDataDescriptor(candidate, 'payload', true, assertChecker).value, + confirmOwnDataDescriptor(candidate, 'payload', true, Fail).value, ); }, }); diff --git a/packages/pass-style/src/typeGuards.js b/packages/pass-style/src/typeGuards.js index 8a27bbcc78..cdd5ee0872 100644 --- a/packages/pass-style/src/typeGuards.js +++ b/packages/pass-style/src/typeGuards.js @@ -1,10 +1,9 @@ -import { Fail, q, X } from '@endo/errors'; -import { identChecker } from '@endo/common/ident-checker.js'; +import { Fail, q, hideAndHardenFunction } from '@endo/errors'; import { passStyleOf } from './passStyleOf.js'; -import { assertChecker } from './passStyle-helpers.js'; /** - * @import {CopyArray, CopyRecord, Passable, RemotableObject, ByteArray, Checker, Atom} from './types.js' + * @import {Rejector} from '@endo/errors/rejector.js'; + * @import {CopyArray, CopyRecord, Passable, RemotableObject, ByteArray, Atom} from './types.js' */ /** @@ -15,7 +14,7 @@ import { assertChecker } from './passStyle-helpers.js'; * @returns {arr is CopyArray} */ export const isCopyArray = arr => passStyleOf(arr) === 'copyArray'; -harden(isCopyArray); +hideAndHardenFunction(isCopyArray); /** * Check whether the argument is a pass-by-copy binary data, AKA a "byteArray" @@ -25,7 +24,7 @@ harden(isCopyArray); * @returns {arr is ByteArray} */ export const isByteArray = arr => passStyleOf(arr) === 'byteArray'; -harden(isByteArray); +hideAndHardenFunction(isByteArray); /** * Check whether the argument is a pass-by-copy record, AKA a @@ -35,7 +34,7 @@ harden(isByteArray); * @returns {record is CopyRecord} */ export const isRecord = record => passStyleOf(record) === 'copyRecord'; -harden(isRecord); +hideAndHardenFunction(isRecord); /** * Check whether the argument is a remotable. @@ -44,7 +43,7 @@ harden(isRecord); * @returns {remotable is RemotableObject} */ export const isRemotable = remotable => passStyleOf(remotable) === 'remotable'; -harden(isRemotable); +hideAndHardenFunction(isRemotable); /** * @param {any} arr @@ -58,7 +57,7 @@ export const assertCopyArray = (arr, optNameOfArray = 'Alleged array') => { passStyle, )}`; }; -harden(assertCopyArray); +hideAndHardenFunction(assertCopyArray); /** * @param {Passable} arr @@ -72,7 +71,7 @@ export const assertByteArray = (arr, optNameOfArray = 'Alleged byteArray') => { optNameOfArray, )} ${arr} must be a pass-by-copy binary data, not ${q(passStyle)}`; }; -harden(assertByteArray); +hideAndHardenFunction(assertByteArray); /** * @callback AssertRecord @@ -87,7 +86,7 @@ export const assertRecord = (record, optNameOfRecord = 'Alleged record') => { passStyle, )}`; }; -harden(assertRecord); +hideAndHardenFunction(assertRecord); /** * @param {Passable} remotable @@ -104,22 +103,19 @@ export const assertRemotable = ( passStyle, )}`; }; -harden(assertRemotable); +hideAndHardenFunction(assertRemotable); /** * @param {any} val Not necessarily passable - * @param {Checker} check + * @param {Rejector} reject * @returns {val is Atom} */ -const checkAtom = (val, check) => { +const confirmAtom = (val, reject) => { let passStyle; try { passStyle = passStyleOf(val); } catch (err) { - return ( - check !== identChecker && - check(false, X`Not even Passable: ${q(err)}: ${val}`) - ); + return reject && reject`Not even Passable: ${q(err)}: ${val}`; } switch (passStyle) { case 'undefined': @@ -135,10 +131,7 @@ const checkAtom = (val, check) => { } default: { // The other PassStyle cases - return ( - check !== identChecker && - check(false, X`A ${q(passStyle)} cannot be an atom: ${val}`) - ); + return reject && reject`A ${q(passStyle)} cannot be an atom: ${val}`; } } }; @@ -147,14 +140,14 @@ const checkAtom = (val, check) => { * @param {any} val * @returns {val is Atom} */ -export const isAtom = val => checkAtom(val, identChecker); -harden(isAtom); +export const isAtom = val => confirmAtom(val, false); +hideAndHardenFunction(isAtom); /** * @param {Passable} val * @returns {asserts val is Atom} */ export const assertAtom = val => { - checkAtom(val, assertChecker); + confirmAtom(val, Fail); }; -harden(assertAtom); +hideAndHardenFunction(assertAtom); diff --git a/packages/pass-style/src/types.d.ts b/packages/pass-style/src/types.d.ts index cd7b1e967e..e78120e8a3 100644 --- a/packages/pass-style/src/types.d.ts +++ b/packages/pass-style/src/types.d.ts @@ -228,4 +228,8 @@ export type CopyTagged< */ export type InterfaceSpec = string; +/** + * Consider this export deprecated. + * Import `Checker` directly from `'@endo/common/ident-checker.js'` instead. + */ export type { Checker } from '@endo/common/ident-checker.js';