From aada61a659a23f4250d9027e2cd2ee62fc367aed Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Thu, 11 Jan 2024 11:06:03 -0800 Subject: [PATCH] Revert "1488 Passable" --- packages/captp/src/captp.js | 4 +- packages/exo/tsconfig.json | 2 +- packages/far/test/test-marshal-far-obj.js | 2 - packages/marshal/src/deeplyFulfilled.js | 4 - packages/marshal/src/encodePassable.js | 4 +- packages/marshal/src/encodeToCapData.js | 10 +- packages/marshal/src/encodeToSmallcaps.js | 4 +- packages/marshal/src/marshal.js | 24 +-- packages/marshal/src/rankOrder.js | 34 +-- packages/marshal/src/types.js | 4 +- packages/marshal/test/test-marshal-capdata.js | 2 - packages/marshal/test/test-marshal-far-obj.js | 1 - .../marshal/test/test-marshal-smallcaps.js | 1 - .../marshal/test/test-marshal-stringify.js | 2 - packages/pass-style/.eslintignore | 2 - packages/pass-style/package.json | 1 - packages/pass-style/src/make-far.js | 10 +- packages/pass-style/src/makeTagged.js | 7 - packages/pass-style/src/passStyle-helpers.js | 8 +- packages/pass-style/src/passStyleOf.js | 18 +- packages/pass-style/src/remotable.js | 14 +- packages/pass-style/src/typeGuards.js | 17 +- packages/pass-style/src/types.d.ts | 203 ------------------ packages/pass-style/src/types.js | 164 +++++++++++++- packages/pass-style/src/types.test-d.ts | 31 --- packages/pass-style/test/test-passStyleOf.js | 23 -- packages/pass-style/tools/arb-passable.js | 7 +- packages/patterns/src/keys/checkKey.js | 29 ++- packages/patterns/src/keys/compareKeys.js | 13 +- packages/patterns/src/keys/copyBag.js | 11 +- packages/patterns/src/keys/copySet.js | 10 +- .../src/keys/keycollection-operators.js | 3 - .../patterns/src/keys/merge-bag-operators.js | 5 +- .../patterns/src/keys/merge-set-operators.js | 4 +- .../patterns/src/patterns/patternMatchers.js | 2 - packages/patterns/src/types.js | 11 +- packages/patterns/test/test-patterns.js | 1 - 37 files changed, 249 insertions(+), 443 deletions(-) delete mode 100644 packages/pass-style/.eslintignore delete mode 100644 packages/pass-style/src/types.d.ts delete mode 100644 packages/pass-style/src/types.test-d.ts diff --git a/packages/captp/src/captp.js b/packages/captp/src/captp.js index 7d1fc29520..3171aa72ce 100644 --- a/packages/captp/src/captp.js +++ b/packages/captp/src/captp.js @@ -326,9 +326,8 @@ export const makeCapTP = ( const IS_REMOTE_PUMPKIN = harden({}); /** - * @type {import('@endo/marshal').ConvertValToSlot} + * @type {import('@endo/marshal').ConvertSlotToVal} */ - // @ts-expect-error intentional hack const assertValIsLocal = val => { const slot = valToSlot.get(val); if (slot && slot[1] === '-') { @@ -500,7 +499,6 @@ export const makeCapTP = ( } // If we imported this slot, mark it as one our peer exported. - // @ts-expect-error map lacks value type return slotToImported.get(recvSlot.add(slot)); } diff --git a/packages/exo/tsconfig.json b/packages/exo/tsconfig.json index 182dd6244c..7927a61da1 100644 --- a/packages/exo/tsconfig.json +++ b/packages/exo/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.eslint-base.json", "compilerOptions": { - "maxNodeModuleJsDepth": 2 + "maxNodeModuleJsDepth": 1 }, "include": [ "*.js", diff --git a/packages/far/test/test-marshal-far-obj.js b/packages/far/test/test-marshal-far-obj.js index 600b086660..f4ecd7091f 100644 --- a/packages/far/test/test-marshal-far-obj.js +++ b/packages/far/test/test-marshal-far-obj.js @@ -66,7 +66,6 @@ const testRecord = ({ }), ); -/** @type {import('@endo/pass-style').PassStyled<'remotable'>} */ const goodRemotableProto = testRecord(); // @ts-ignore We're testing bad things anyway @@ -130,7 +129,6 @@ test('passStyleOf validation of remotables', t => { t.throws(() => passStyleOf(badRemotableProto3), NON_METHOD); t.throws(() => passStyleOf(badRemotableProto4), NON_METHOD); - // @ts-expect-error UNTIL https://github.com/microsoft/TypeScript/issues/38385 t.is(passStyleOf(sub(goodRemotableProto)), 'remotable'); t.throws(() => passStyleOf(sub(badRemotableProto1)), EXPECTED_PASS_STYLE); diff --git a/packages/marshal/src/deeplyFulfilled.js b/packages/marshal/src/deeplyFulfilled.js index 15873be355..ebedc93a6d 100644 --- a/packages/marshal/src/deeplyFulfilled.js +++ b/packages/marshal/src/deeplyFulfilled.js @@ -51,7 +51,6 @@ export const deeplyFulfilled = async val => { const passStyle = passStyleOf(val); switch (passStyle) { case 'copyRecord': { - // @ts-expect-error FIXME narrowed const names = ownKeys(val); const valPs = names.map(name => deeplyFulfilled(val[name])); return E.when(Promise.all(valPs), vals => @@ -59,14 +58,11 @@ export const deeplyFulfilled = async val => { ); } case 'copyArray': { - // @ts-expect-error FIXME narrowed const valPs = val.map(p => deeplyFulfilled(p)); return E.when(Promise.all(valPs), vals => harden(vals)); } case 'tagged': { - // @ts-expect-error FIXME narrowed const tag = getTag(val); - // @ts-expect-error FIXME narrowed return E.when(deeplyFulfilled(val.payload), payload => makeTagged(tag, payload), ); diff --git a/packages/marshal/src/encodePassable.js b/packages/marshal/src/encodePassable.js index 002d3be0f7..387a88b402 100644 --- a/packages/marshal/src/encodePassable.js +++ b/packages/marshal/src/encodePassable.js @@ -27,7 +27,7 @@ const { ownKeys } = Reflect; * string-named own properties. `recordNames` returns those name *reverse* * sorted, because that's how records are compared, encoded, and sorted. * - * @template {Passable} T + * @template T * @param {CopyRecord} record * @returns {string[]} */ @@ -44,7 +44,7 @@ harden(recordNames); * Assuming that `record` is a CopyRecord and `names` is `recordNames(record)`, * return the corresponding array of property values. * - * @template {Passable} T + * @template T * @param {CopyRecord} record * @param {string[]} names * @returns {T[]} diff --git a/packages/marshal/src/encodeToCapData.js b/packages/marshal/src/encodeToCapData.js index a8303a5667..5546072a71 100644 --- a/packages/marshal/src/encodeToCapData.js +++ b/packages/marshal/src/encodeToCapData.js @@ -20,7 +20,7 @@ import { /** @typedef {import('@endo/pass-style').Passable} Passable */ /** @typedef {import('./types.js').Encoding} Encoding */ -/** @typedef {import('@endo/pass-style').RemotableObject} RemotableObject */ +/** @typedef {import('@endo/pass-style').Remotable} Remotable */ /** @typedef {import('./types.js').EncodingUnion} EncodingUnion */ const { ownKeys } = Reflect; @@ -62,7 +62,7 @@ const qclassMatches = (encoded, qclass) => /** * @typedef {object} EncodeToCapDataOptions * @property {( - * remotable: RemotableObject, + * remotable: Remotable, * encodeRecur: (p: Passable) => Encoding * ) => Encoding} [encodeRemotableToCapData] * @property {( @@ -117,7 +117,7 @@ export const makeEncodeToCapData = (encodeOptions = {}) => { * Readers must not care about this order anyway. We impose this requirement * mainly to reduce non-determinism exposed outside a vat. * - * @param {any} passable + * @param {Passable} passable * @returns {Encoding} except that `encodeToCapData` does not generally * `harden` this result before returning. Rather, `encodeToCapData` is not * directly exposed. @@ -269,11 +269,11 @@ harden(makeEncodeToCapData); * @property {( * encodedRemotable: Encoding, * decodeRecur: (e: Encoding) => Passable - * ) => (Promise|RemotableObject)} [decodeRemotableFromCapData] + * ) => (Promise|Remotable)} [decodeRemotableFromCapData] * @property {( * encodedPromise: Encoding, * decodeRecur: (e: Encoding) => Passable - * ) => (Promise|RemotableObject)} [decodePromiseFromCapData] + * ) => (Promise|Remotable)} [decodePromiseFromCapData] * @property {( * encodedError: Encoding, * decodeRecur: (e: Encoding) => Passable diff --git a/packages/marshal/src/encodeToSmallcaps.js b/packages/marshal/src/encodeToSmallcaps.js index ae6f88c61a..f96c51b841 100644 --- a/packages/marshal/src/encodeToSmallcaps.js +++ b/packages/marshal/src/encodeToSmallcaps.js @@ -18,7 +18,7 @@ import { } from '@endo/pass-style'; /** @typedef {import('@endo/pass-style').Passable} Passable */ -/** @typedef {import('@endo/pass-style').RemotableObject} Remotable */ +/** @typedef {import('@endo/pass-style').Remotable} Remotable */ // @typedef {import('./types.js').SmallcapsEncoding} SmallcapsEncoding */ // @typedef {import('./types.js').SmallcapsEncodingUnion} SmallcapsEncodingUnion */ /** @typedef {any} SmallcapsEncoding */ @@ -161,7 +161,7 @@ export const makeEncodeToSmallcaps = (encodeOptions = {}) => { * Readers must not care about this order anyway. We impose this requirement * mainly to reduce non-determinism exposed outside a vat. * - * @param {any} passable + * @param {Passable} passable * @returns {SmallcapsEncoding} except that `encodeToSmallcaps` does not generally * `harden` this result before returning. Rather, `encodeToSmallcaps` is not * directly exposed. diff --git a/packages/marshal/src/marshal.js b/packages/marshal/src/marshal.js index 4500a5b9e1..181e28887d 100644 --- a/packages/marshal/src/marshal.js +++ b/packages/marshal/src/marshal.js @@ -79,7 +79,7 @@ export const makeMarshal = ( const slotMap = new Map(); /** - * @param {import('@endo/pass-style').PassableCap} passable + * @param {Remotable | Promise} passable * @returns {{index: number, repeat: boolean}} */ const encodeSlotCommon = passable => { @@ -134,7 +134,7 @@ export const makeMarshal = ( if (serializeBodyFormat === 'capdata') { /** - * @param {import('@endo/pass-style').PassableCap} passable + * @param {Passable} passable * @param {InterfaceSpec} [iface] * @returns {Encoding} */ @@ -148,11 +148,9 @@ export const makeMarshal = ( } }; - /** @type {(promise: import('@endo/pass-style').RemotableObject, encodeRecur: (p: Passable) => Encoding) => Encoding} */ const encodeRemotableToCapData = (val, _encodeRecur) => encodeSlotToCapData(val, getInterfaceOf(val)); - /** @type {(promise: Promise, encodeRecur: (p: Passable) => Encoding) => Encoding} */ const encodePromiseToCapData = (promise, _encodeRecur) => encodeSlotToCapData(promise); @@ -186,7 +184,7 @@ export const makeMarshal = ( } else if (serializeBodyFormat === 'smallcaps') { /** * @param {string} prefix - * @param {import('@endo/pass-style').PassableCap} passable + * @param {Passable} passable * @param {InterfaceSpec} [iface] * @returns {string} */ @@ -233,7 +231,7 @@ export const makeMarshal = ( }; const makeFullRevive = slots => { - /** @type {Map} */ + /** @type {Map} */ const valMap = new Map(); /** @@ -244,9 +242,8 @@ export const makeMarshal = ( const { iface = undefined, index, ...rest } = slotData; ownKeys(rest).length === 0 || Fail`unexpected encoded slot properties ${q(ownKeys(rest))}`; - const extant = valMap.get(index); - if (extant) { - return extant; + if (valMap.has(index)) { + return valMap.get(index); } // TODO SECURITY HAZARD: must enfoce that remotable vs promise // is according to the encoded string. @@ -270,12 +267,11 @@ export const makeMarshal = ( // are for reuse by other encodings that do, such as smallcaps. const dName = decodeRecur(name); const dMessage = decodeRecur(message); - const dErrorId = /** @type {string} */ (errorId && decodeRecur(errorId)); + const dErrorId = errorId && decodeRecur(errorId); typeof dName === 'string' || Fail`invalid error name typeof ${q(typeof dName)}`; - if (typeof dMessage !== 'string') { - throw Fail`invalid error message typeof ${q(typeof dMessage)}`; - } + typeof dMessage === 'string' || + Fail`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 = @@ -334,9 +330,7 @@ export const makeMarshal = ( }; const reviveFromSmallcaps = makeDecodeFromSmallcaps({ - // @ts-expect-error FIXME decodeRemotableFromSmallcaps, - // @ts-expect-error FIXME decodePromiseFromSmallcaps, decodeErrorFromSmallcaps, }); diff --git a/packages/marshal/src/rankOrder.js b/packages/marshal/src/rankOrder.js index f8638c9b2c..f8d8d9d95c 100644 --- a/packages/marshal/src/rankOrder.js +++ b/packages/marshal/src/rankOrder.js @@ -144,20 +144,16 @@ export const makeComparatorKit = (compareRemotables = (_x, _y) => 0) => { case 'string': { // Within each of these passStyles, the rank ordering agrees with // JavaScript's relational operators `<` and `>`. - // @ts-expect-error FIXME narrowed if (left < right) { return -1; } else { - // @ts-expect-error FIXME narrowed assert(left > right); return 1; } } case 'symbol': { return comparator( - // @ts-expect-error FIXME narrowed nameForPassableSymbol(left), - // @ts-expect-error FIXME narrowed nameForPassableSymbol(right), ); } @@ -171,11 +167,9 @@ export const makeComparatorKit = (compareRemotables = (_x, _y) => 0) => { } // The rank ordering of non-NaN numbers agrees with JavaScript's // relational operators '<' and '>'. - // @ts-expect-error FIXME narrowed if (left < right) { return -1; } else { - // @ts-expect-error FIXME narrowed assert(left > right); return 1; } @@ -192,9 +186,7 @@ export const makeComparatorKit = (compareRemotables = (_x, _y) => 0) => { // of these names, which we then compare lexicographically. This ensures // that if the names of record X are a subset of the names of record Y, // then record X will have an earlier rank and sort to the left of Y. - // @ts-expect-error FIXME narrowed const leftNames = recordNames(left); - // @ts-expect-error FIXME narrowed const rightNames = recordNames(right); const result = comparator(leftNames, rightNames); @@ -202,18 +194,14 @@ export const makeComparatorKit = (compareRemotables = (_x, _y) => 0) => { return result; } return comparator( - // @ts-expect-error FIXME narrowed recordValues(left, leftNames), - // @ts-expect-error FIXME narrowed recordValues(right, rightNames), ); } case 'copyArray': { // Lexicographic - // @ts-expect-error FIXME narrowed const len = Math.min(left.length, right.length); for (let i = 0; i < len; i += 1) { - // @ts-expect-error FIXME narrowed const result = comparator(left[i], right[i]); if (result !== 0) { return result; @@ -221,17 +209,14 @@ export const makeComparatorKit = (compareRemotables = (_x, _y) => 0) => { } // If all matching elements were tied, then according to their lengths. // If array X is a prefix of array Y, then X has an earlier rank than Y. - // @ts-expect-error FIXME narrowed return comparator(left.length, right.length); } case 'tagged': { // Lexicographic by `[Symbol.toStringTag]` then `.payload`. - // @ts-expect-error FIXME narrowed const labelComp = comparator(getTag(left), getTag(right)); if (labelComp !== 0) { return labelComp; } - // @ts-expect-error FIXME narrowed return comparator(left.payload, right.payload); } default: { @@ -301,10 +286,9 @@ harden(assertRankSorted); * function. This is a genuine bug for us NOW because sometimes we sort * in reverse order by passing a reversed rank comparison function. * - * @template {Passable} T - * @param {Iterable} passables + * @param {Iterable} passables * @param {RankCompare} compare - * @returns {T[]} + * @returns {Passable[]} */ export const sortByRank = (passables, compare) => { if (Array.isArray(passables)) { @@ -398,20 +382,18 @@ export const coveredEntries = (sorted, [leftIndex, rightIndex]) => { harden(coveredEntries); /** - * @template {Passable} T * @param {RankCompare} compare - * @param {T} a - * @param {T} b - * @returns {T} + * @param {Passable} a + * @param {Passable} b + * @returns {Passable} */ const maxRank = (compare, a, b) => (compare(a, b) >= 0 ? a : b); /** - * @template {Passable} T * @param {RankCompare} compare - * @param {T} a - * @param {T} b - * @returns {T} + * @param {Passable} a + * @param {Passable} b + * @returns {Passable} */ const minRank = (compare, a, b) => (compare(a, b) <= 0 ? a : b); diff --git a/packages/marshal/src/types.js b/packages/marshal/src/types.js index 5ac06ce43e..47ea1fc8be 100644 --- a/packages/marshal/src/types.js +++ b/packages/marshal/src/types.js @@ -172,8 +172,8 @@ export {}; * ordering would also compare magnitudes, and so agree with the rank ordering * of all values other than `NaN`. An array sorted by rank would enable range * queries by magnitude. - * @param {import("@endo/pass-style").Passable} left - * @param {import("@endo/pass-style").Passable} right + * @param {any} left a Passable + * @param {any} right a Passable * @returns {RankComparison} */ diff --git a/packages/marshal/test/test-marshal-capdata.js b/packages/marshal/test/test-marshal-capdata.js index 740a997562..a828ac7e2f 100644 --- a/packages/marshal/test/test-marshal-capdata.js +++ b/packages/marshal/test/test-marshal-capdata.js @@ -311,7 +311,6 @@ test('records', t => { const fauxPresence = harden({}); const { serialize: ser, unserialize: unser } = makeMarshal( _val => 'slot', - // @ts-expect-error mock _slot => fauxPresence, { errorTagging: 'off', @@ -405,7 +404,6 @@ test('capdata proto problems', t => { test('capdata slot leniency', t => { const { unserialize: fromCapData } = makeMarshal( undefined, - // @ts-expect-error mock _slot => ({ name: 'I should not be in a slot', }), diff --git a/packages/marshal/test/test-marshal-far-obj.js b/packages/marshal/test/test-marshal-far-obj.js index 4fd45c078a..a194160730 100644 --- a/packages/marshal/test/test-marshal-far-obj.js +++ b/packages/marshal/test/test-marshal-far-obj.js @@ -149,7 +149,6 @@ test('passStyleOf validation of remotables', t => { t.throws(() => passStyleOf(badRemotableProto3), NON_METHOD); t.throws(() => passStyleOf(badRemotableProto4), NON_METHOD); - // @ts-expect-error UNTIL https://github.com/microsoft/TypeScript/issues/38385 t.is(passStyleOf(sub(goodRemotableProto)), 'remotable'); t.throws(() => passStyleOf(sub(badRemotableProto1)), EXPECTED_PASS_STYLE); diff --git a/packages/marshal/test/test-marshal-smallcaps.js b/packages/marshal/test/test-marshal-smallcaps.js index ebc164a00b..d413e87b68 100644 --- a/packages/marshal/test/test-marshal-smallcaps.js +++ b/packages/marshal/test/test-marshal-smallcaps.js @@ -165,7 +165,6 @@ test('smallcaps records', t => { const fauxPresence = harden({}); const { serialize: ser, unserialize: unser } = makeMarshal( _val => 'slot', - // @ts-expect-error mock _slot => fauxPresence, { errorTagging: 'off', diff --git a/packages/marshal/test/test-marshal-stringify.js b/packages/marshal/test/test-marshal-stringify.js index 3a0d2cdd3f..4b33c9b302 100644 --- a/packages/marshal/test/test-marshal-stringify.js +++ b/packages/marshal/test/test-marshal-stringify.js @@ -39,11 +39,9 @@ test('marshal stringify errors', t => { t.throws(() => stringify({}), { message: /Cannot pass non-frozen objects like .*. Use harden()/, }); - // @ts-expect-error intentional error t.throws(() => stringify(harden(new Uint8Array(1))), { message: 'Cannot pass mutable typed arrays like "[Uint8Array]".', }); - // @ts-expect-error intentional error t.throws(() => stringify(harden(new Int16Array(1))), { message: 'Cannot pass mutable typed arrays like "[Int16Array]".', }); diff --git a/packages/pass-style/.eslintignore b/packages/pass-style/.eslintignore deleted file mode 100644 index d76bcff161..0000000000 --- a/packages/pass-style/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -# typescript-eslint errors on this because it has no typecheck information, because tsc produced it for the `types.d.ts` instead -/src/types.js diff --git a/packages/pass-style/package.json b/packages/pass-style/package.json index 0ef01e72f3..ecd9cd9602 100644 --- a/packages/pass-style/package.json +++ b/packages/pass-style/package.json @@ -18,7 +18,6 @@ "module": "./index.js", "exports": { ".": "./index.js", - "./src/types.js": "./src/types.js", "./tools.js": "./tools.js", "./endow.js": "./endow.js", "./package.json": "./package.json" diff --git a/packages/pass-style/src/make-far.js b/packages/pass-style/src/make-far.js index 2bbc912794..1e92acbbc3 100644 --- a/packages/pass-style/src/make-far.js +++ b/packages/pass-style/src/make-far.js @@ -5,7 +5,7 @@ import { assertChecker, PASS_STYLE } from './passStyle-helpers.js'; import { assertIface, getInterfaceOf, RemotableHelper } from './remotable.js'; /** @typedef {import('./types.js').InterfaceSpec} InterfaceSpec */ -/** @template L,R @typedef {import('@endo/eventual-send/src/types').RemotableBrand} RemotableBrand */ +/** @template L,R @typedef {import('@endo/eventual-send').RemotableBrand} RemotableBrand */ const { quote: q, Fail } = assert; @@ -62,8 +62,7 @@ const assertCanBeRemotable = candidate => * // https://github.com/Agoric/agoric-sdk/issues/804 * * @template {{}} T - * @template {InterfaceSpec} I - * @param {I} [iface] The interface specification for + * @param {InterfaceSpec} [iface] The interface specification for * the remotable. For now, a string iface must be "Remotable" or begin with * "Alleged: " or "DebugName: ", to serve as the alleged name. More * general ifaces are not yet implemented. This is temporary. We include the @@ -76,10 +75,9 @@ const assertCanBeRemotable = candidate => * @param {undefined} [props] Currently may only be undefined. * That plan is that own-properties are copied to the remotable * @param {T} [remotable] The object used as the remotable - * @returns {T & import('./types.js').RemotableObject & RemotableBrand<{}, T>}} remotable, modified for debuggability + * @returns {T & RemotableBrand<{}, T>} remotable, modified for debuggability */ export const Remotable = ( - // @ts-expect-error I could have different subtype than string iface = 'Remotable', props = undefined, remotable = /** @type {T} */ ({}), @@ -127,7 +125,7 @@ export const Remotable = ( // COMMITTED! // We're committed, so keep the interface for future reference. assert(iface !== undefined); // To make TypeScript happy - return /** @type {any} */ (remotable); + return /** @type {T & RemotableBrand<{}, T>} */ (remotable); }; harden(Remotable); diff --git a/packages/pass-style/src/makeTagged.js b/packages/pass-style/src/makeTagged.js index 90f5ffad5f..b20ee544b8 100644 --- a/packages/pass-style/src/makeTagged.js +++ b/packages/pass-style/src/makeTagged.js @@ -6,13 +6,6 @@ import { assertPassable } from './passStyleOf.js'; const { create, prototype: objectPrototype } = Object; const { Fail } = assert; -/** - * @template {string} T - * @template {import('./types.js').Passable} P - * @param {T} tag - * @param {P} payload - * @returns {import('./types.js').CopyTagged} - */ export const makeTagged = (tag, payload) => { typeof tag === 'string' || Fail`The tag of a tagged record must be a string: ${tag}`; diff --git a/packages/pass-style/src/passStyle-helpers.js b/packages/pass-style/src/passStyle-helpers.js index 911e3455f9..840156641c 100644 --- a/packages/pass-style/src/passStyle-helpers.js +++ b/packages/pass-style/src/passStyle-helpers.js @@ -29,7 +29,6 @@ export const hasOwnPropertyOf = (obj, prop) => apply(objectHasOwnProperty, obj, [prop]); harden(hasOwnPropertyOf); -/** @type {(val) => val is {}} */ export const isObject = val => Object(val) === val; harden(isObject); @@ -123,11 +122,6 @@ export const checkNormalProperty = ( }; harden(checkNormalProperty); -/** - * @template {import('./types.js').InterfaceSpec} T - * @param {import('./types.js').PassStyled} tagRecord - * @returns {T} - */ export const getTag = tagRecord => tagRecord[Symbol.toStringTag]; harden(getTag); @@ -144,7 +138,7 @@ harden(checkPassStyle); const makeCheckTagRecord = checkProto => { /** - * @param {import('./types.js').PassStyled} tagRecord + * @param {{ [PASS_STYLE]: string }} tagRecord * @param {PassStyle} passStyle * @param {Checker} [check] * @returns {boolean} diff --git a/packages/pass-style/src/passStyleOf.js b/packages/pass-style/src/passStyleOf.js index f635d47a21..0a49fe71a1 100644 --- a/packages/pass-style/src/passStyleOf.js +++ b/packages/pass-style/src/passStyleOf.js @@ -81,26 +81,28 @@ const makePassStyleOf = passStyleHelpers => { * structures, so without this cache, these algorithms could be * O(N**2) or worse. * - * @type {WeakMap} + * @type {WeakMap} */ const passStyleMemo = new WeakMap(); /** * @type {PassStyleOf} */ - // @ts-expect-error cast const passStyleOf = passable => { // Even when a WeakSet is correct, when the set has a shorter lifetime // than its keys, we prefer a Set due to expected implementation // tradeoffs. const inProgress = new Set(); + /** + * @type {PassStyleOf} + */ const passStyleOfRecur = inner => { const innerIsObject = isObject(inner); if (innerIsObject) { - const innerMemo = passStyleMemo.get(inner); - if (innerMemo) { - return innerMemo; + if (passStyleMemo.has(inner)) { + // @ts-ignore TypeScript doesn't know that `get` after `has` is safe + return passStyleMemo.get(inner); } !inProgress.has(inner) || Fail`Pass-by-copy data cannot be cyclic ${inner}`; @@ -115,6 +117,9 @@ const makePassStyleOf = passStyleHelpers => { return passStyle; }; + /** + * @type {PassStyleOf} + */ const passStyleOfInternal = inner => { const typestr = typeof inner; switch (typestr) { @@ -159,12 +164,10 @@ const makePassStyleOf = passStyleHelpers => { } for (const helper of passStyleHelpers) { if (helper.canBeValid(inner)) { - // @ts-expect-error XXX helper.assertValid(inner, passStyleOfRecur); return helper.styleName; } } - // @ts-expect-error XXX remotableHelper.assertValid(inner, passStyleOfRecur); return 'remotable'; } @@ -173,7 +176,6 @@ const makePassStyleOf = passStyleHelpers => { Fail`Cannot pass non-frozen objects like ${inner}. Use harden()`; typeof inner.then !== 'function' || Fail`Cannot pass non-promise thenables`; - // @ts-expect-error XXX remotableHelper.assertValid(inner, passStyleOfRecur); return 'remotable'; } diff --git a/packages/pass-style/src/remotable.js b/packages/pass-style/src/remotable.js index 1f2b91d43a..476dff5387 100644 --- a/packages/pass-style/src/remotable.js +++ b/packages/pass-style/src/remotable.js @@ -13,6 +13,7 @@ import { /** @typedef {import('./types.js').Checker} Checker */ /** @typedef {import('./types.js').InterfaceSpec} InterfaceSpec */ +/** @typedef {import('./types.js').MarshalGetInterfaceOf} MarshalGetInterfaceOf */ /** @typedef {import('./internal-types.js').PassStyleHelper} PassStyleHelper */ /** @typedef {import('./types.js').RemotableObject} Remotable */ @@ -166,26 +167,15 @@ const checkRemotable = (val, check) => { return result; }; -/** - * Simple semantics, just tell what interface (or undefined) a remotable has. - * @type {{ - * (val: import('./types.js').PassStyled): T; - * (val: any): string | undefined; - * }} - * @returns the interface specification, or undefined - * if not a deemed to be a Remotable - */ +/** @type {MarshalGetInterfaceOf} */ export const getInterfaceOf = val => { if ( !isObject(val) || val[PASS_STYLE] !== 'remotable' || - // @ts-expect-error FIXME !checkRemotable(val) ) { - // @ts-expect-error FIXME return undefined; } - // @ts-expect-error FIXME return getTag(val); }; harden(getInterfaceOf); diff --git a/packages/pass-style/src/typeGuards.js b/packages/pass-style/src/typeGuards.js index ffc6e7c398..2d2f13e267 100644 --- a/packages/pass-style/src/typeGuards.js +++ b/packages/pass-style/src/typeGuards.js @@ -17,7 +17,7 @@ const { Fail, quote: q } = assert; * Check whether the argument is a pass-by-copy array, AKA a "copyArray" * in @endo/marshal terms * - * @param {any} arr + * @param {Passable} arr * @returns {arr is CopyArray} */ const isCopyArray = arr => passStyleOf(arr) === 'copyArray'; @@ -27,7 +27,7 @@ harden(isCopyArray); * Check whether the argument is a pass-by-copy record, AKA a * "copyRecord" in @endo/marshal terms * - * @param {any} record + * @param {Passable} record * @returns {record is CopyRecord} */ const isRecord = record => passStyleOf(record) === 'copyRecord'; @@ -43,10 +43,13 @@ const isRemotable = remotable => passStyleOf(remotable) === 'remotable'; harden(isRemotable); /** - * @param {any} array + * @callback AssertArray + * @param {Passable} array * @param {string=} optNameOfArray * @returns {asserts array is CopyArray} */ + +/** @type {AssertArray} */ const assertCopyArray = (array, optNameOfArray = 'Alleged array') => { const passStyle = passStyleOf(array); passStyle === 'copyArray' || @@ -57,10 +60,13 @@ const assertCopyArray = (array, optNameOfArray = 'Alleged array') => { harden(assertCopyArray); /** - * @param {any} record + * @callback AssertRecord + * @param {Passable} record * @param {string=} optNameOfRecord * @returns {asserts record is CopyRecord} */ + +/** @type {AssertRecord} */ const assertRecord = (record, optNameOfRecord = 'Alleged record') => { const passStyle = passStyleOf(record); passStyle === 'copyRecord' || @@ -71,10 +77,13 @@ const assertRecord = (record, optNameOfRecord = 'Alleged record') => { harden(assertRecord); /** + * @callback AssertRemotable * @param {Passable} remotable * @param {string=} optNameOfRemotable * @returns {asserts remotable is Remotable} */ + +/** @type {AssertRemotable} */ const assertRemotable = ( remotable, optNameOfRemotable = 'Alleged remotable', diff --git a/packages/pass-style/src/types.d.ts b/packages/pass-style/src/types.d.ts deleted file mode 100644 index 65080677cc..0000000000 --- a/packages/pass-style/src/types.d.ts +++ /dev/null @@ -1,203 +0,0 @@ -/* eslint-disable no-use-before-define */ -import { PASS_STYLE } from './passStyle-helpers.js'; - -/** - * Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive). - */ -export type Primitive = - | null - | undefined - | string - | number - | boolean - | symbol - | bigint; - -export type PrimitiveStyle = - | 'undefined' - | 'null' - | 'boolean' - | 'number' - | 'bigint' - | 'string' - | 'symbol'; - -export type ContainerStyle = 'copyRecord' | 'copyArray' | 'tagged'; - -export type PassStyle = - | PrimitiveStyle - | ContainerStyle - | 'remotable' - | 'error' - | 'promise'; - -export type TaggedOrRemotable = 'tagged' | 'remotable'; - -/** - * Tagged has own [PASS_STYLE]: "tagged", [Symbol.toStringTag]: $tag. - * - * Remotable has a prototype chain in which the penultimate object has own [PASS_STYLE]: "remotable", [Symbol.toStringTag]: $iface (where both $tag and $iface must be strings, and the latter must either be "Remotable" or start with "Alleged: " or "DebugName: "). - */ -export type PassStyled = { - [PASS_STYLE]: S; - [Symbol.toStringTag]: I; -}; - -export type ExtractStyle

> = P[typeof PASS_STYLE]; - -export type PassByCopy = - | Primitive - | Error - | CopyArray - | CopyRecord - | CopyTagged; - -export type PassByRef = - | RemotableObject - | Promise - | Promise; - -/** - * A Passable is acyclic data that can be marshalled. It must be hardened to - * remain - * stable (even if some components are proxies; see PureData restriction below), - * and is classified by PassStyle: - * * Atomic primitive values have a PrimitiveStyle (PassStyle - * 'undefined' | 'null' | 'boolean' | 'number' | 'bigint' - * | 'string' | 'symbol'). - * * Containers aggregate other Passables into - * * sequences as CopyArrays (PassStyle 'copyArray'), or - * * string-keyed dictionaries as CopyRecords (PassStyle 'copyRecord'), or - * * higher-level types as CopyTaggeds (PassStyle 'tagged'). - * * PassableCaps (PassStyle 'remotable' | 'promise') expose local values to - * remote interaction. - * * As a special case to support system observability, error objects are - * Passable (PassStyle 'error'). - * - * A Passable is essentially a pass-by-copy superstructure with a - * pass-by-reference - * exit point at the site of each PassableCap (which marshalling represents - * using 'slots'). - */ -export type Passable< - PC extends PassableCap = PassableCap, - E extends Error = Error, -> = Primitive | Container | PC | E; - -export type Container = - | CopyArrayI - | CopyRecordI - | CopyTaggedI; -interface CopyArrayI - extends CopyArray> {} -interface CopyRecordI - extends CopyRecord> {} -interface CopyTaggedI - extends CopyTagged> {} - -export type PassStyleOf = { - (p: undefined): 'undefined'; - (p: string): 'string'; - (p: boolean): 'boolean'; - (p: number): 'number'; - (p: bigint): 'bigint'; - (p: symbol): 'symbol'; - (p: null): 'null'; - (p: Promise): 'promise'; - (p: Error): 'error'; - (p: CopyTagged): 'tagged'; - (p: any[]): 'copyArray'; - (p: Iterable): 'remotable'; - (p: Iterator): 'remotable'; - >(p: T): ExtractStyle; - (p: { [key: string]: any }): 'copyRecord'; - (p: any): PassStyle; -}; -/** - * A Passable is PureData when its entire data structure is free of PassableCaps - * (remotables and promises) and error objects. - * PureData is an arbitrary composition of primitive values into CopyArray - * and/or - * CopyRecord and/or CopyTagged containers (or a single primitive value with no - * container), and is fully pass-by-copy. - * - * This restriction assures absence of side effects and interleaving risks *given* - * that none of the containers can be a Proxy instance. - * TODO SECURITY BUG we plan to enforce this, giving PureData the same security - * properties as the proposed - * [Records and Tuples](https://github.com/tc39/proposal-record-tuple). - * - * Given this (currently counter-factual) assumption, a PureData value cannot - * be used as a communications channel, - * and can therefore be safely shared with subgraphs that should not be able - * to communicate with each other. - * Without that assumption, such a guarantee requires a marshal-unmarshal round - * trip (as exists between vats) to produce data structures disconnected from - * any potential proxies. - */ -export type PureData = Passable; -/** - * An object marked as remotely accessible using the `Far` or `Remotable` - * functions, or a local presence representing such a remote object. - * - * A more natural name would be Remotable, but that could be confused with the - * value of the `Remotable` export of this module (a function). - */ -export type RemotableObject = PassStyled< - 'remotable', - I ->; -/** - * The authority-bearing leaves of a Passable's pass-by-copy superstructure. - */ -export type PassableCap = Promise | RemotableObject; -/** - * A Passable sequence of Passable values. - */ -export type CopyArray = Array; - -/** - * A Passable dictionary in which each key is a string and each value is Passable. - */ -export type CopyRecord = Record; -/** - * A Passable "tagged record" with semantics specific to the tag identified in - * the `[Symbol.toStringTag]` property (such as 'copySet', 'copyBag', - * or 'copyMap'). - * It must have a property with key equal to the `PASS_STYLE` export and - * value 'tagged' - * and no other properties except `[Symbol.toStringTag]` and `payload`. - */ -export type CopyTagged< - Tag extends string = string, - Payload extends Passable = any, -> = PassStyled<'tagged', Tag> & { - payload: Payload; -}; -/** - * This is an interface specification. - * For now, it is just a string, but we retain the option to make it `PureData`. - * Either way, it must remain pure, so that it can be safely shared by subgraphs - * that are not supposed to be able to communicate. - */ -export type InterfaceSpec = string; -/** - * Internal to a useful pattern for writing checking logic - * (a "checkFoo" function) that can be used to implement a predicate - * (an "isFoo" function) or a validator (an "assertFoo" function). - * - * * A predicate ideally only returns `true` or `false` and rarely throws. - * * A validator throws an informative diagnostic when the predicate - * would have returned `false`, and simply returns `undefined` normally - * when the predicate would have returned `true`. - * * The internal checking function that they share is parameterized by a - * `Checker` that determines how to proceed with a failure condition. - * Predicates pass in an identity function as checker. Validators - * pass in `assertChecker` which is a trivial wrapper around `assert`. - * - * See the various uses for good examples. - */ -export type Checker = ( - cond: boolean, - details?: import('ses').Details | undefined, -) => boolean; diff --git a/packages/pass-style/src/types.js b/packages/pass-style/src/types.js index 407ac0b20e..438d053605 100644 --- a/packages/pass-style/src/types.js +++ b/packages/pass-style/src/types.js @@ -1,5 +1,161 @@ -/** @file Empty twin for .d.ts */ -/* eslint-disable */ -import { PASS_STYLE } from './passStyle-helpers.js'; - export {}; + +/** + * @typedef { 'undefined' | 'null' | + * 'boolean' | 'number' | 'bigint' | 'string' | 'symbol' + * } PrimitiveStyle + */ + +/** + * @typedef { PrimitiveStyle | + * 'copyRecord' | 'copyArray' | 'tagged' | + * 'remotable' | + * 'error' | 'promise' + * } PassStyle + */ + +// TODO declare more precise types throughout this file, so the type system +// and IDE can be more helpful. + +/** + * @typedef {any} Passable + * + * A Passable is acyclic data that can be marshalled. It must be hardened to + * remain + * stable (even if some components are proxies; see PureData restriction below), + * and is classified by PassStyle: + * * Atomic primitive values have a PrimitiveStyle (PassStyle + * 'undefined' | 'null' | 'boolean' | 'number' | 'bigint' + * | 'string' | 'symbol'). + * * Containers aggregate other Passables into + * * sequences as CopyArrays (PassStyle 'copyArray'), or + * * string-keyed dictionaries as CopyRecords (PassStyle 'copyRecord'), or + * * higher-level types as CopyTaggeds (PassStyle 'tagged'). + * * PassableCaps (PassStyle 'remotable' | 'promise') expose local values to + * remote interaction. + * * As a special case to support system observability, error objects are + * Passable (PassStyle 'error'). + * + * A Passable is essentially a pass-by-copy superstructure with a + * pass-by-reference + * exit point at the site of each PassableCap (which marshalling represents + * using 'slots'). + */ + +/** + * @callback PassStyleOf + * @param {Passable} passable + * @returns {PassStyle} + */ + +/** + * @typedef {Passable} PureData + * + * A Passable is PureData when its entire data structure is free of PassableCaps + * (remotables and promises) and error objects. + * PureData is an arbitrary composition of primitive values into CopyArray + * and/or + * CopyRecord and/or CopyTagged containers (or a single primitive value with no + * container), and is fully pass-by-copy. + * + * This restriction assures absence of side effects and interleaving risks *given* + * that none of the containers can be a Proxy instance. + * TODO SECURITY BUG we plan to enforce this, giving PureData the same security + * properties as the proposed + * [Records and Tuples](https://github.com/tc39/proposal-record-tuple). + * + * Given this (currently counter-factual) assumption, a PureData value cannot + * be used as a communications channel, + * and can therefore be safely shared with subgraphs that should not be able + * to communicate with each other. + * Without that assumption, such a guarantee requires a marshal-unmarshal round + * trip (as exists between vats) to produce data structures disconnected from + * any potential proxies. + */ + +/** + * @typedef {Passable} RemotableObject + * + * An object marked as remotely accessible using the `Far` or `Remotable` + * functions, or a local presence representing such a remote object. + */ + +/** + * @typedef {Promise | RemotableObject} PassableCap + * + * The authority-bearing leaves of a Passable's pass-by-copy superstructure. + */ + +/** + * @template {Passable} [T=Passable] + * @typedef {T[]} CopyArray + * + * A Passable sequence of Passable values. + */ + +/** + * @template {Passable} [T=Passable] + * @typedef {Record} CopyRecord + * + * A Passable dictionary in which each key is a string and each value is Passable. + */ + +/** + * @template {string} [Tag=string] + * @template {Passable} [Payload=Passable] + * @typedef {{ + * [Symbol.toStringTag]: Tag, + * payload: Payload, + * [passStyle: symbol]: 'tagged' | string, + * }} CopyTagged + * + * A Passable "tagged record" with semantics specific to the tag identified in + * the `[Symbol.toStringTag]` property (such as 'copySet', 'copyBag', + * or 'copyMap'). + * It must have a property with key equal to the `PASS_STYLE` export and + * value 'tagged' + * and no other properties except `[Symbol.toStringTag]` and `payload`. + * + * TODO + * But TypeScript complains about a declaration like `[PASS_STYLE]: 'tagged'` + * because importing packages do not know what `PASS_STYLE` is, + * so we appease it with a looser but less accurate definition + * using symbol index properties and `| string`. + */ + +/** + * @typedef {string} InterfaceSpec + * This is an interface specification. + * For now, it is just a string, but will eventually be `PureData`. Either + * way, it must remain pure, so that it can be safely shared by subgraphs that + * are not supposed to be able to communicate. + */ + +/** + * @callback MarshalGetInterfaceOf + * Simple semantics, just tell what interface (or undefined) a remotable has. + * @param {any} maybeRemotable the value to check + * @returns {InterfaceSpec|undefined} the interface specification, or undefined + * if not a deemed to be a Remotable + */ + +/** + * @callback Checker + * Internal to a useful pattern for writing checking logic + * (a "checkFoo" function) that can be used to implement a predicate + * (an "isFoo" function) or a validator (an "assertFoo" function). + * + * * A predicate ideally only returns `true` or `false` and rarely throws. + * * A validator throws an informative diagnostic when the predicate + * would have returned `false`, and simply returns `undefined` normally + * when the predicate would have returned `true`. + * * The internal checking function that they share is parameterized by a + * `Checker` that determines how to proceed with a failure condition. + * Predicates pass in an identity function as checker. Validators + * pass in `assertChecker` which is a trivial wrapper around `assert`. + * + * See the various uses for good examples. + * @param {boolean} cond + * @param {import('ses').Details} [details] + * @returns {boolean} + */ diff --git a/packages/pass-style/src/types.test-d.ts b/packages/pass-style/src/types.test-d.ts deleted file mode 100644 index 957f15ff59..0000000000 --- a/packages/pass-style/src/types.test-d.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-disable */ -import { expectType, expectNotType } from 'tsd'; -import { Far } from './make-far'; -import { passStyleOf } from './passStyleOf'; -import { makeTagged } from './makeTagged'; -import { CopyTagged, PassStyle } from './types'; -import { PASS_STYLE } from './passStyle-helpers'; - -const remotable = Far('foo', {}); - -const copyTagged = makeTagged('someTag', remotable); -expectType>(copyTagged); - -const someUnknown: unknown = null; - -expectType<'undefined'>(passStyleOf(undefined)); -expectType<'string'>(passStyleOf('str')); -expectType<'boolean'>(passStyleOf(true)); -expectType<'number'>(passStyleOf(1)); -expectType<'bigint'>(passStyleOf(1n)); -expectType<'symbol'>(passStyleOf(Symbol.for('foo'))); -expectType<'null'>(passStyleOf(null)); -expectType<'promise'>(passStyleOf(Promise.resolve())); -expectType<'error'>(passStyleOf(new Error())); -expectType<'tagged'>(passStyleOf(copyTagged)); -expectType<'copyArray'>(passStyleOf([])); -expectType<'copyRecord'>(passStyleOf({})); -// though the object is specifying a PASS_STYLE, it doesn't match the case for extracting it -expectType<'copyRecord'>(passStyleOf({ [PASS_STYLE]: 'arbitrary' } as const)); -expectType<'remotable'>(passStyleOf(remotable)); -expectType(passStyleOf(someUnknown)); diff --git a/packages/pass-style/test/test-passStyleOf.js b/packages/pass-style/test/test-passStyleOf.js index 5c86478484..77dd715be1 100644 --- a/packages/pass-style/test/test-passStyleOf.js +++ b/packages/pass-style/test/test-passStyleOf.js @@ -6,11 +6,6 @@ import { Far } from '../src/make-far.js'; import { makeTagged } from '../src/makeTagged.js'; import { PASS_STYLE } from '../src/passStyle-helpers.js'; -const harden = /** @type {import('ses').Harden & { isFake?: boolean }} */ ( - // eslint-disable-next-line no-undef - global.harden -); - const { quote: q } = assert; const { getPrototypeOf, defineProperty } = Object; const { ownKeys } = Reflect; @@ -107,7 +102,6 @@ test('some passStyleOf rejections', t => { * * @param {string} [tag] * @param {object|null} [proto] - * @returns {{ [PASS_STYLE]: 'remotable', [Symbol.toStringTag]: string }} */ const makeTagishRecord = (tag = 'Remotable', proto = undefined) => { return Object.create(proto === undefined ? Object.prototype : proto, { @@ -161,11 +155,6 @@ test('passStyleOf testing tagged records', t => { value: { [PASS_STYLE]: 0 }, message: '0 must be a string', }, - { - label: 'unrecognized', - value: { [PASS_STYLE]: 'arbitrary' }, - message: 'Unrecognized PassStyle: "arbitrary"', - }, ]; for (const testCase of tagRecordBadPayloads) { const { label, message, ...desc } = testCase; @@ -188,14 +177,12 @@ test('passStyleOf testing remotables', t => { t.is(passStyleOf(Far('foo', () => 'far function')), 'remotable'); const tagRecord1 = harden(makeTagishRecord('Alleged: manually constructed')); - /** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */ const farObj1 = harden({ __proto__: tagRecord1, }); t.is(passStyleOf(farObj1), 'remotable'); const tagRecord2 = makeTagishRecord('Alleged: tagRecord not hardened'); - /** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */ const farObj2 = Object.freeze({ __proto__: tagRecord2, }); @@ -211,14 +198,12 @@ test('passStyleOf testing remotables', t => { const tagRecord3 = Object.freeze( makeTagishRecord('Alleged: both manually frozen'), ); - /** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */ const farObj3 = Object.freeze({ __proto__: tagRecord3, }); t.is(passStyleOf(farObj3), 'remotable'); const tagRecord4 = harden(makeTagishRecord('Remotable')); - /** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */ const farObj4 = harden({ __proto__: tagRecord4, }); @@ -237,7 +222,6 @@ test('passStyleOf testing remotables', t => { const farObjProto6 = harden({ __proto__: tagRecord6, }); - /** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */ const farObj6 = harden({ __proto__: farObjProto6, }); @@ -265,7 +249,6 @@ test('passStyleOf testing remotables', t => { const farTagRecord7 = getPrototypeOf(farBaseProto7); t.is(farTagRecord7[PASS_STYLE], 'remotable'); t.is(getPrototypeOf(farTagRecord7), Object.prototype); - /** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */ const farObj7 = new FarBaseClass7(3); t.is(passStyleOf(farObj7), 'remotable'); t.is(farObj7.add(7), 10); @@ -277,7 +260,6 @@ test('passStyleOf testing remotables', t => { } } harden(FarSubclass8); - /** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */ const farObj8 = new FarSubclass8(3); t.is(passStyleOf(farObj8), 'remotable'); t.is(farObj8.twice(), 14); @@ -423,7 +405,6 @@ test('remotables - safety from the gibson042 attack', t => { test('Unexpected stack on errors', t => { let err; try { - // @ts-expect-error purposeful type violation for testing null.error; } catch (e) { err = e; @@ -446,13 +427,11 @@ test('Allow toStringTag overrides', t => { t.is(`${alice}`, '[object DebugName: Allison]'); t.is(`${q(alice)}`, '"[DebugName: Allison]"'); - /** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */ const carol = harden({ __proto__: alice }); t.is(passStyleOf(carol), 'remotable'); t.is(`${carol}`, '[object DebugName: Allison]'); t.is(`${q(carol)}`, '"[DebugName: Allison]"'); - /** @type {any} UNTIL https://github.com/microsoft/TypeScript/issues/38385 */ const bob = harden({ __proto__: carol, [Symbol.toStringTag]: 'DebugName: Robert', @@ -465,9 +444,7 @@ test('Allow toStringTag overrides', t => { t.is(fred.name, 'fred'); defineProperty(fred, Symbol.toStringTag, { value: 'DebugName: Friedrich' }); const f = Far('Fred', fred); - // @ts-expect-error TS doesn't know `fred` has changed t.is(f, fred); - // @ts-expect-error TS doesn't know `fred` has changed t.is(passStyleOf(fred), 'remotable'); t.is(`${fred}`, '() => {}'); t.is(Object.prototype.toString.call(fred), '[object DebugName: Friedrich]'); diff --git a/packages/pass-style/tools/arb-passable.js b/packages/pass-style/tools/arb-passable.js index 22549f5d83..1d6651cdec 100644 --- a/packages/pass-style/tools/arb-passable.js +++ b/packages/pass-style/tools/arb-passable.js @@ -99,12 +99,7 @@ const { arbDag } = fc.letrec(tie => { ), }), ) - .map(({ type, payload }) => - makeTagged( - type, - /** @type {import('../src/types.js').Passable} */ (payload), - ), - ), + .map(({ type, payload }) => makeTagged(type, payload)), ), }; }); diff --git a/packages/patterns/src/keys/checkKey.js b/packages/patterns/src/keys/checkKey.js index 4cdd2334c1..148537f6f7 100644 --- a/packages/patterns/src/keys/checkKey.js +++ b/packages/patterns/src/keys/checkKey.js @@ -100,13 +100,11 @@ harden(assertScalarKey); // ////////////////////////////// Keys ///////////////////////////////////////// -// @ts-expect-error Key does not satisfy WeakKey /** @type {WeakSet} */ -// @ts-expect-error Key does not satisfy WeakKey const keyMemo = new WeakSet(); /** - * @param {Key} val + * @param {Passable} val * @param {Checker} check * @returns {boolean} */ @@ -134,7 +132,7 @@ export const checkKey = (val, check) => { harden(checkKey); /** - * @param {Key} val + * @param {Passable} val * @returns {boolean} */ export const isKey = val => checkKey(val, identChecker); @@ -142,7 +140,6 @@ harden(isKey); /** * @param {Key} val - * @returns {asserts val is Key} */ export const assertKey = val => { checkKey(val, assertChecker); @@ -158,7 +155,7 @@ harden(assertKey); const copySetMemo = new WeakSet(); /** - * @param {any} s + * @param {Passable} s * @param {Checker} check * @returns {boolean} */ @@ -201,7 +198,7 @@ export const assertCopySet = s => { harden(assertCopySet); /** - * @template {Key} K + * @template K * @param {CopySet} s * @returns {K[]} */ @@ -212,7 +209,7 @@ export const getCopySetKeys = s => { harden(getCopySetKeys); /** - * @template {Key} K + * @template K * @param {CopySet} s * @param {(key: K, index: number) => boolean} fn * @returns {boolean} @@ -222,7 +219,7 @@ export const everyCopySetKey = (s, fn) => harden(everyCopySetKey); /** - * @template {Key} K + * @template K * @param {Iterable} elementIter * @returns {CopySet} */ @@ -242,7 +239,7 @@ harden(makeCopySet); const copyBagMemo = new WeakSet(); /** - * @param {any} b + * @param {Passable} b * @param {Checker} check * @returns {boolean} */ @@ -285,7 +282,7 @@ export const assertCopyBag = b => { harden(assertCopyBag); /** - * @template {Key} K + * @template K * @param {CopyBag} b * @returns {CopyBag['payload']} */ @@ -296,7 +293,7 @@ export const getCopyBagEntries = b => { harden(getCopyBagEntries); /** - * @template {Key} K + * @template K * @param {CopyBag} b * @param {(entry: [K, bigint], index: number) => boolean} fn * @returns {boolean} @@ -306,7 +303,7 @@ export const everyCopyBagEntry = (b, fn) => harden(everyCopyBagEntry); /** - * @template {Key} K + * @template K * @param {Iterable<[K,bigint]>} bagEntryIter * @returns {CopyBag} */ @@ -318,7 +315,7 @@ export const makeCopyBag = bagEntryIter => { harden(makeCopyBag); /** - * @template {Key} K + * @template K * @param {Iterable} elementIter * @returns {CopyBag} */ @@ -351,7 +348,7 @@ harden(makeCopyBagFromElements); const copyMapMemo = new WeakSet(); /** - * @param {any} m + * @param {Passable} m * @param {Checker} check * @returns {boolean} */ @@ -546,7 +543,7 @@ harden(makeCopyMap); // //////////////////////// Keys Recur ///////////////////////////////////////// /** - * @param {any} val + * @param {Passable} val * @param {Checker} check * @returns {boolean} */ diff --git a/packages/patterns/src/keys/compareKeys.js b/packages/patterns/src/keys/compareKeys.js index b86325897d..7beee3a65a 100644 --- a/packages/patterns/src/keys/compareKeys.js +++ b/packages/patterns/src/keys/compareKeys.js @@ -28,7 +28,7 @@ const { quote: q, Fail } = assert; * X is equivalent to Y iff the condition 1 holds but condition 2 does not. */ export const setCompare = makeCompareCollection( - /** @type {(s: CopySet) => Array<[K, 1]>} */ ( + /** @type {(s: CopySet) => Array<[K, 1]>} */ ( s => harden(getCopySetKeys(s).map(key => [key, 1])) ), 0, @@ -134,10 +134,8 @@ export const compareKeys = (left, right) => { // rank order. // Because the invariants above apply to the elements of the array, // they apply to the array as a whole. - // @ts-expect-error FIXME narrowed const len = Math.min(left.length, right.length); for (let i = 0; i < len; i += 1) { - // @ts-expect-error FIXME narrowed const result = compareKeys(left[i], right[i]); if (result !== 0) { return result; @@ -145,14 +143,11 @@ export const compareKeys = (left, right) => { } // If all matching elements are keyEQ, then according to their lengths. // Thus, if array X is a prefix of array Y, then X is smaller than Y. - // @ts-expect-error FIXME narrowed return compareRank(left.length, right.length); } case 'copyRecord': { // Pareto partial order comparison. - // @ts-expect-error FIXME narrowed const leftNames = recordNames(left); - // @ts-expect-error FIXME narrowed const rightNames = recordNames(right); // eslint-disable-next-line no-use-before-define @@ -164,9 +159,7 @@ export const compareKeys = (left, right) => { // to avoid more irrelevant ones. return NaN; } - // @ts-expect-error FIXME narrowed const leftValues = recordValues(left, leftNames); - // @ts-expect-error FIXME narrowed const rightValues = recordValues(right, rightNames); // Presume that both copyRecords have the same key order // until encountering a property disproving that hypothesis. @@ -197,9 +190,7 @@ export const compareKeys = (left, right) => { return result; } case 'tagged': { - // @ts-expect-error FIXME narrowed const leftTag = getTag(left); - // @ts-expect-error FIXME narrowed const rightTag = getTag(right); if (leftTag !== rightTag) { // different tags are incommensurate @@ -207,11 +198,9 @@ export const compareKeys = (left, right) => { } switch (leftTag) { case 'copySet': { - // @ts-expect-error FIXME narrowed return setCompare(left, right); } case 'copyBag': { - // @ts-expect-error FIXME narrowed return bagCompare(left, right); } case 'copyMap': { diff --git a/packages/patterns/src/keys/copyBag.js b/packages/patterns/src/keys/copyBag.js index 9c09e7eef4..71c897ac21 100644 --- a/packages/patterns/src/keys/copyBag.js +++ b/packages/patterns/src/keys/copyBag.js @@ -19,7 +19,7 @@ const { details: X } = assert; /** @typedef {import('@endo/pass-style').Passable} Passable */ /** - * @template {Key} T + * @template T * @param {[T,bigint][]} bagEntries * @param {FullCompare | undefined} fullCompare If provided and `bagEntries` is already * known to be sorted by this `fullCompare`, then we should get a memo hit @@ -55,7 +55,7 @@ const checkNoDuplicateKeys = (bagEntries, fullCompare, check) => { }; /** - * @template {Key} T + * @template T * @param {[T,bigint][]} bagEntries * @param {FullCompare} [fullCompare] * @returns {void} @@ -100,7 +100,6 @@ export const checkBagEntries = (bagEntries, check) => { ); } } - // @ts-expect-error FIXME Key types return checkNoDuplicateKeys(bagEntries, undefined, check); }; harden(checkBagEntries); @@ -115,10 +114,6 @@ export const assertBagEntries = bagEntries => { }; harden(assertBagEntries); -/** - * @template {import('../types.js').Key} K - * @param {Iterable<[K, bigint]>} bagEntriesList - */ export const coerceToBagEntries = bagEntriesList => { const bagEntries = sortByRank(bagEntriesList, compareAntiRank); assertBagEntries(bagEntries); @@ -127,7 +122,7 @@ export const coerceToBagEntries = bagEntriesList => { harden(coerceToBagEntries); /** - * @template {import('../types.js').Key} K + * @template K * @param {Iterable<[K, bigint]>} bagEntryIter * @returns {CopyBag} */ diff --git a/packages/patterns/src/keys/copySet.js b/packages/patterns/src/keys/copySet.js index 04f86a7577..e72dd65516 100644 --- a/packages/patterns/src/keys/copySet.js +++ b/packages/patterns/src/keys/copySet.js @@ -19,7 +19,7 @@ const { details: X } = assert; /** @typedef {import('@endo/pass-style').Passable} Passable */ /** - * @template {Passable} T + * @template T * @param {T[]} elements * @param {FullCompare | undefined} fullCompare If provided and `elements` is already known * to be sorted by this `fullCompare`, then we should get a memo hit rather @@ -52,7 +52,7 @@ const checkNoDuplicates = (elements, fullCompare, check) => { }; /** - * @template {Passable} T + * @template T * @param {T[]} elements * @param {FullCompare} [fullCompare] * @returns {void} @@ -88,10 +88,6 @@ export const assertElements = elements => { }; harden(assertElements); -/** - * @template {import('../types.js').Key} K - * @param {Iterable} elementsList - */ export const coerceToElements = elementsList => { const elements = sortByRank(elementsList, compareAntiRank); assertElements(elements); @@ -100,7 +96,7 @@ export const coerceToElements = elementsList => { harden(coerceToElements); /** - * @template {import('../types.js').Key} K + * @template K * @param {Iterable} elementIter * @returns {CopySet} */ diff --git a/packages/patterns/src/keys/keycollection-operators.js b/packages/patterns/src/keys/keycollection-operators.js index 75440a6761..8e2b46f465 100644 --- a/packages/patterns/src/keys/keycollection-operators.js +++ b/packages/patterns/src/keys/keycollection-operators.js @@ -27,7 +27,6 @@ const { quote: q, Fail } = assert; * @returns {IterableIterator<[import('../types.js').Key, V]>} */ const generateFullSortedEntries = (entries, rankCompare, fullCompare) => { - // @ts-expect-error FIXME Key types assertRankSorted(entries, rankCompare); const { length } = entries; let i = 0; @@ -57,10 +56,8 @@ const generateFullSortedEntries = (entries, rankCompare, fullCompare) => { // Sort the ties by `fullCompare`, enforce key uniqueness, and delegate to // a sub-iterator. - // @ts-expect-error FIXME Key types const sortedTies = sortByRank(ties, fullCompare); for (let k = 1; k < sortedTies.length; k += 1) { - // @ts-expect-error FIXME Key types const [key0] = sortedTies[k - 1]; const [key1] = sortedTies[k]; Math.sign(fullCompare(key0, key1)) || diff --git a/packages/patterns/src/keys/merge-bag-operators.js b/packages/patterns/src/keys/merge-bag-operators.js index fdfb4901f5..88a1ce1e50 100644 --- a/packages/patterns/src/keys/merge-bag-operators.js +++ b/packages/patterns/src/keys/merge-bag-operators.js @@ -29,7 +29,7 @@ const { quote: q, Fail } = assert; * to `fullOrder`. However, it optimizes for the case where these contiguous * runs that need to be resorted are either absent or small. * - * @template {import('../types').Passable} T + * @template T * @param {[T,bigint][]} bagEntries * @param {RankCompare} rankCompare * @param {FullCompare} fullCompare @@ -72,7 +72,6 @@ const bagWindowResort = (bagEntries, rankCompare, fullCompare) => { // Providing the same `fullCompare` should cause a memo hit // within `assertNoDuplicates` enabling it to avoid a // redundant resorting. - // @ts-expect-error FIXME Key types assertNoDuplicateKeys(resorted, fullCompare); // This is the raw JS array iterator whose `.next()` method // does not harden the IteratorResult, in violation of our @@ -97,7 +96,7 @@ const bagWindowResort = (bagEntries, rankCompare, fullCompare) => { * For sets, these counts are always 0 or 1, but this representation * generalizes nicely for bags. * - * @template {import('../types').Passable} T + * @template T * @param {[T,bigint][]} xbagEntries * @param {[T,bigint][]} ybagEntries * @returns {Iterable<[T,bigint,bigint]>} diff --git a/packages/patterns/src/keys/merge-set-operators.js b/packages/patterns/src/keys/merge-set-operators.js index 0daedb2264..c18e18d070 100644 --- a/packages/patterns/src/keys/merge-set-operators.js +++ b/packages/patterns/src/keys/merge-set-operators.js @@ -25,7 +25,7 @@ const { quote: q, Fail } = assert; * to `fullOrder`. However, it optimizes for the case where these contiguous * runs that need to be resorted are either absent or small. * - * @template {import('../types').Passable} T + * @template T * @param {T[]} elements * @param {RankCompare} rankCompare * @param {FullCompare} fullCompare @@ -89,7 +89,7 @@ const windowResort = (elements, rankCompare, fullCompare) => { * For sets, these counts are always 0 or 1, but this representation * generalizes nicely for bags. * - * @template {import('../types').Passable} T + * @template T * @param {T[]} xelements * @param {T[]} yelements * @returns {Iterable<[T,bigint,bigint]>} diff --git a/packages/patterns/src/patterns/patternMatchers.js b/packages/patterns/src/patterns/patternMatchers.js index c492b2d97e..054d79679f 100644 --- a/packages/patterns/src/patterns/patternMatchers.js +++ b/packages/patterns/src/patterns/patternMatchers.js @@ -1,5 +1,3 @@ -// @ts-nocheck So many errors that the suppressions hamper readability. -// TODO parameterize MatchHelper which will solve most of them import { assertChecker, Far, diff --git a/packages/patterns/src/types.js b/packages/patterns/src/types.js index 63f22e887b..5b7ff5577e 100644 --- a/packages/patterns/src/types.js +++ b/packages/patterns/src/types.js @@ -4,7 +4,6 @@ export {}; /** @typedef {import('@endo/pass-style').Passable} Passable */ /** @typedef {import('@endo/pass-style').PassStyle} PassStyle */ -/** @typedef {import('@endo/pass-style').RemotableObject} RemotableObject */ /** * @template {string} [Tag=string] * @template {Passable} [Payload=Passable] @@ -22,9 +21,8 @@ export {}; /** @typedef {import('@endo/marshal').RankCompare} RankCompare */ /** @typedef {import('@endo/marshal').RankCover} RankCover */ -// FIXME exclude nested Error and Promise /** - * @typedef {import('@endo/pass-style').Passable} Key + * @typedef {Passable} Key * * Keys are Passable arbitrarily-nested pass-by-copy containers * (CopyArray, CopyRecord, CopySet, CopyBag, CopyMap) in which every @@ -62,9 +60,8 @@ export {}; * @returns {string} */ -// FIXME exclude nested Error and Promise /** - * @typedef {Exclude} Pattern + * @typedef {Passable} Pattern * * Patterns are Passable arbitrarily-nested pass-by-copy containers * (CopyArray, CopyRecord, CopySet, CopyBag, CopyMap) in which every @@ -424,14 +421,14 @@ export {}; * The CopyRecord must have all properties that appear on `required`, * but may omit properties that appear on `optional`. * - * @property {(basePatt: CopyRecord | CopyArray, + * @property {(basePatt: CopyRecord<*> | CopyArray<*>, * rest?: Pattern, * ) => Matcher} split * Deprecated. Use `M.splitArray` or `M.splitRecord` instead. * An array or record is split into the first part that is matched by * `basePatt`, and the remainder, which is matched against `rest` if present. * - * @property {(basePatt: CopyRecord | CopyArray, + * @property {(basePatt: CopyRecord<*> | CopyArray<*>, * rest?: Pattern, * ) => Matcher} partial * Deprecated. Use `M.splitArray` or `M.splitRecord` instead. diff --git a/packages/patterns/test/test-patterns.js b/packages/patterns/test/test-patterns.js index 031213b9f4..60a9891da1 100644 --- a/packages/patterns/test/test-patterns.js +++ b/packages/patterns/test/test-patterns.js @@ -532,7 +532,6 @@ const runTests = (t, successCase, failCase) => { t.throws( () => { copyMapComparison || Fail`No CopyMap comparison support`; - // @ts-expect-error FIXME Key types successCase(specimen, M.gt(makeCopyMap([]))); }, { message: 'No CopyMap comparison support' },