From 418b2dad5976824e0d5639860f2670a826b90a73 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Tue, 9 Jan 2024 09:56:13 -0800 Subject: [PATCH] chore(types): misc accommodations tighten Pattern better RemotableObject WIP punt getInterfaceOf WIP fix sorting restore PassableCap fix valMap type runtime fix decodeErrorCommon acknowledge deferred work fixes to get types building --- 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 | 2 +- packages/marshal/src/encodeToCapData.js | 10 +++---- packages/marshal/src/encodeToSmallcaps.js | 4 +-- packages/marshal/src/marshal.js | 24 +++++++++------ packages/marshal/src/rankOrder.js | 20 +++++++++++-- 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/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 +-- packages/patterns/src/types.js | 8 +++-- 21 files changed, 111 insertions(+), 50 deletions(-) diff --git a/packages/captp/src/captp.js b/packages/captp/src/captp.js index 3171aa72ce..7d1fc29520 100644 --- a/packages/captp/src/captp.js +++ b/packages/captp/src/captp.js @@ -326,8 +326,9 @@ export const makeCapTP = ( const IS_REMOTE_PUMPKIN = harden({}); /** - * @type {import('@endo/marshal').ConvertSlotToVal} + * @type {import('@endo/marshal').ConvertValToSlot} */ + // @ts-expect-error intentional hack const assertValIsLocal = val => { const slot = valToSlot.get(val); if (slot && slot[1] === '-') { @@ -499,6 +500,7 @@ 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 7927a61da1..182dd6244c 100644 --- a/packages/exo/tsconfig.json +++ b/packages/exo/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.eslint-base.json", "compilerOptions": { - "maxNodeModuleJsDepth": 1 + "maxNodeModuleJsDepth": 2 }, "include": [ "*.js", diff --git a/packages/far/test/test-marshal-far-obj.js b/packages/far/test/test-marshal-far-obj.js index f4ecd7091f..600b086660 100644 --- a/packages/far/test/test-marshal-far-obj.js +++ b/packages/far/test/test-marshal-far-obj.js @@ -66,6 +66,7 @@ const testRecord = ({ }), ); +/** @type {import('@endo/pass-style').PassStyled<'remotable'>} */ const goodRemotableProto = testRecord(); // @ts-ignore We're testing bad things anyway @@ -129,6 +130,7 @@ 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 eda563da37..15873be355 100644 --- a/packages/marshal/src/deeplyFulfilled.js +++ b/packages/marshal/src/deeplyFulfilled.js @@ -53,7 +53,6 @@ export const deeplyFulfilled = async val => { case 'copyRecord': { // @ts-expect-error FIXME narrowed const names = ownKeys(val); - // @ts-expect-error FIXME narrowed const valPs = names.map(name => deeplyFulfilled(val[name])); return E.when(Promise.all(valPs), vals => harden(fromEntries(vals.map((c, i) => [names[i], c]))), @@ -65,6 +64,7 @@ export const deeplyFulfilled = async val => { 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 => diff --git a/packages/marshal/src/encodeToCapData.js b/packages/marshal/src/encodeToCapData.js index 5546072a71..a8303a5667 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').Remotable} Remotable */ +/** @typedef {import('@endo/pass-style').RemotableObject} RemotableObject */ /** @typedef {import('./types.js').EncodingUnion} EncodingUnion */ const { ownKeys } = Reflect; @@ -62,7 +62,7 @@ const qclassMatches = (encoded, qclass) => /** * @typedef {object} EncodeToCapDataOptions * @property {( - * remotable: Remotable, + * remotable: RemotableObject, * 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 {Passable} passable + * @param {any} 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|Remotable)} [decodeRemotableFromCapData] + * ) => (Promise|RemotableObject)} [decodeRemotableFromCapData] * @property {( * encodedPromise: Encoding, * decodeRecur: (e: Encoding) => Passable - * ) => (Promise|Remotable)} [decodePromiseFromCapData] + * ) => (Promise|RemotableObject)} [decodePromiseFromCapData] * @property {( * encodedError: Encoding, * decodeRecur: (e: Encoding) => Passable diff --git a/packages/marshal/src/encodeToSmallcaps.js b/packages/marshal/src/encodeToSmallcaps.js index f96c51b841..ae6f88c61a 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').Remotable} Remotable */ +/** @typedef {import('@endo/pass-style').RemotableObject} 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 {Passable} passable + * @param {any} 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 181e28887d..4500a5b9e1 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 {Remotable | Promise} passable + * @param {import('@endo/pass-style').PassableCap} passable * @returns {{index: number, repeat: boolean}} */ const encodeSlotCommon = passable => { @@ -134,7 +134,7 @@ export const makeMarshal = ( if (serializeBodyFormat === 'capdata') { /** - * @param {Passable} passable + * @param {import('@endo/pass-style').PassableCap} passable * @param {InterfaceSpec} [iface] * @returns {Encoding} */ @@ -148,9 +148,11 @@ 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); @@ -184,7 +186,7 @@ export const makeMarshal = ( } else if (serializeBodyFormat === 'smallcaps') { /** * @param {string} prefix - * @param {Passable} passable + * @param {import('@endo/pass-style').PassableCap} passable * @param {InterfaceSpec} [iface] * @returns {string} */ @@ -231,7 +233,7 @@ export const makeMarshal = ( }; const makeFullRevive = slots => { - /** @type {Map} */ + /** @type {Map} */ const valMap = new Map(); /** @@ -242,8 +244,9 @@ export const makeMarshal = ( const { iface = undefined, index, ...rest } = slotData; ownKeys(rest).length === 0 || Fail`unexpected encoded slot properties ${q(ownKeys(rest))}`; - if (valMap.has(index)) { - return valMap.get(index); + const extant = valMap.get(index); + if (extant) { + return extant; } // TODO SECURITY HAZARD: must enfoce that remotable vs promise // is according to the encoded string. @@ -267,11 +270,12 @@ export const makeMarshal = ( // are for reuse by other encodings that do, such as smallcaps. const dName = decodeRecur(name); const dMessage = decodeRecur(message); - const dErrorId = errorId && decodeRecur(errorId); + const dErrorId = /** @type {string} */ (errorId && decodeRecur(errorId)); typeof dName === 'string' || Fail`invalid error name typeof ${q(typeof dName)}`; - typeof dMessage === 'string' || - Fail`invalid error message typeof ${q(typeof dMessage)}`; + if (typeof dMessage !== 'string') { + throw 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 = @@ -330,7 +334,9 @@ 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 cdeea95af4..f8638c9b2c 100644 --- a/packages/marshal/src/rankOrder.js +++ b/packages/marshal/src/rankOrder.js @@ -144,16 +144,20 @@ 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), ); } @@ -167,9 +171,11 @@ 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; } @@ -186,7 +192,9 @@ 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); @@ -194,14 +202,18 @@ 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; @@ -209,14 +221,17 @@ 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: { @@ -286,9 +301,10 @@ 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. * - * @param {Iterable} passables + * @template {Passable} T + * @param {Iterable} passables * @param {RankCompare} compare - * @returns {Passable[]} + * @returns {T[]} */ export const sortByRank = (passables, compare) => { if (Array.isArray(passables)) { diff --git a/packages/marshal/src/types.js b/packages/marshal/src/types.js index 47ea1fc8be..5ac06ce43e 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 {any} left a Passable - * @param {any} right a Passable + * @param {import("@endo/pass-style").Passable} left + * @param {import("@endo/pass-style").Passable} right * @returns {RankComparison} */ diff --git a/packages/marshal/test/test-marshal-capdata.js b/packages/marshal/test/test-marshal-capdata.js index a828ac7e2f..740a997562 100644 --- a/packages/marshal/test/test-marshal-capdata.js +++ b/packages/marshal/test/test-marshal-capdata.js @@ -311,6 +311,7 @@ test('records', t => { const fauxPresence = harden({}); const { serialize: ser, unserialize: unser } = makeMarshal( _val => 'slot', + // @ts-expect-error mock _slot => fauxPresence, { errorTagging: 'off', @@ -404,6 +405,7 @@ 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 a194160730..4fd45c078a 100644 --- a/packages/marshal/test/test-marshal-far-obj.js +++ b/packages/marshal/test/test-marshal-far-obj.js @@ -149,6 +149,7 @@ 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 d413e87b68..ebc164a00b 100644 --- a/packages/marshal/test/test-marshal-smallcaps.js +++ b/packages/marshal/test/test-marshal-smallcaps.js @@ -165,6 +165,7 @@ 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 4b33c9b302..3a0d2cdd3f 100644 --- a/packages/marshal/test/test-marshal-stringify.js +++ b/packages/marshal/test/test-marshal-stringify.js @@ -39,9 +39,11 @@ 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/patterns/src/keys/checkKey.js b/packages/patterns/src/keys/checkKey.js index 148537f6f7..4cdd2334c1 100644 --- a/packages/patterns/src/keys/checkKey.js +++ b/packages/patterns/src/keys/checkKey.js @@ -100,11 +100,13 @@ 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 {Passable} val + * @param {Key} val * @param {Checker} check * @returns {boolean} */ @@ -132,7 +134,7 @@ export const checkKey = (val, check) => { harden(checkKey); /** - * @param {Passable} val + * @param {Key} val * @returns {boolean} */ export const isKey = val => checkKey(val, identChecker); @@ -140,6 +142,7 @@ harden(isKey); /** * @param {Key} val + * @returns {asserts val is Key} */ export const assertKey = val => { checkKey(val, assertChecker); @@ -155,7 +158,7 @@ harden(assertKey); const copySetMemo = new WeakSet(); /** - * @param {Passable} s + * @param {any} s * @param {Checker} check * @returns {boolean} */ @@ -198,7 +201,7 @@ export const assertCopySet = s => { harden(assertCopySet); /** - * @template K + * @template {Key} K * @param {CopySet} s * @returns {K[]} */ @@ -209,7 +212,7 @@ export const getCopySetKeys = s => { harden(getCopySetKeys); /** - * @template K + * @template {Key} K * @param {CopySet} s * @param {(key: K, index: number) => boolean} fn * @returns {boolean} @@ -219,7 +222,7 @@ export const everyCopySetKey = (s, fn) => harden(everyCopySetKey); /** - * @template K + * @template {Key} K * @param {Iterable} elementIter * @returns {CopySet} */ @@ -239,7 +242,7 @@ harden(makeCopySet); const copyBagMemo = new WeakSet(); /** - * @param {Passable} b + * @param {any} b * @param {Checker} check * @returns {boolean} */ @@ -282,7 +285,7 @@ export const assertCopyBag = b => { harden(assertCopyBag); /** - * @template K + * @template {Key} K * @param {CopyBag} b * @returns {CopyBag['payload']} */ @@ -293,7 +296,7 @@ export const getCopyBagEntries = b => { harden(getCopyBagEntries); /** - * @template K + * @template {Key} K * @param {CopyBag} b * @param {(entry: [K, bigint], index: number) => boolean} fn * @returns {boolean} @@ -303,7 +306,7 @@ export const everyCopyBagEntry = (b, fn) => harden(everyCopyBagEntry); /** - * @template K + * @template {Key} K * @param {Iterable<[K,bigint]>} bagEntryIter * @returns {CopyBag} */ @@ -315,7 +318,7 @@ export const makeCopyBag = bagEntryIter => { harden(makeCopyBag); /** - * @template K + * @template {Key} K * @param {Iterable} elementIter * @returns {CopyBag} */ @@ -348,7 +351,7 @@ harden(makeCopyBagFromElements); const copyMapMemo = new WeakSet(); /** - * @param {Passable} m + * @param {any} m * @param {Checker} check * @returns {boolean} */ @@ -543,7 +546,7 @@ harden(makeCopyMap); // //////////////////////// Keys Recur ///////////////////////////////////////// /** - * @param {Passable} val + * @param {any} val * @param {Checker} check * @returns {boolean} */ diff --git a/packages/patterns/src/keys/compareKeys.js b/packages/patterns/src/keys/compareKeys.js index 7beee3a65a..b86325897d 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,8 +134,10 @@ 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; @@ -143,11 +145,14 @@ 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 @@ -159,7 +164,9 @@ 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. @@ -190,7 +197,9 @@ 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 @@ -198,9 +207,11 @@ 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 71c897ac21..9c09e7eef4 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 T + * @template {Key} 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 T + * @template {Key} T * @param {[T,bigint][]} bagEntries * @param {FullCompare} [fullCompare] * @returns {void} @@ -100,6 +100,7 @@ export const checkBagEntries = (bagEntries, check) => { ); } } + // @ts-expect-error FIXME Key types return checkNoDuplicateKeys(bagEntries, undefined, check); }; harden(checkBagEntries); @@ -114,6 +115,10 @@ 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); @@ -122,7 +127,7 @@ export const coerceToBagEntries = bagEntriesList => { harden(coerceToBagEntries); /** - * @template K + * @template {import('../types.js').Key} 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 e72dd65516..04f86a7577 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 T + * @template {Passable} 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 T + * @template {Passable} T * @param {T[]} elements * @param {FullCompare} [fullCompare] * @returns {void} @@ -88,6 +88,10 @@ 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); @@ -96,7 +100,7 @@ export const coerceToElements = elementsList => { harden(coerceToElements); /** - * @template K + * @template {import('../types.js').Key} 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 8e2b46f465..75440a6761 100644 --- a/packages/patterns/src/keys/keycollection-operators.js +++ b/packages/patterns/src/keys/keycollection-operators.js @@ -27,6 +27,7 @@ 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; @@ -56,8 +57,10 @@ 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 88a1ce1e50..fdfb4901f5 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 T + * @template {import('../types').Passable} T * @param {[T,bigint][]} bagEntries * @param {RankCompare} rankCompare * @param {FullCompare} fullCompare @@ -72,6 +72,7 @@ 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 @@ -96,7 +97,7 @@ const bagWindowResort = (bagEntries, rankCompare, fullCompare) => { * For sets, these counts are always 0 or 1, but this representation * generalizes nicely for bags. * - * @template T + * @template {import('../types').Passable} 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 c18e18d070..0daedb2264 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 T + * @template {import('../types').Passable} 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 T + * @template {import('../types').Passable} T * @param {T[]} xelements * @param {T[]} yelements * @returns {Iterable<[T,bigint,bigint]>} diff --git a/packages/patterns/src/types.js b/packages/patterns/src/types.js index 0318c81c59..63f22e887b 100644 --- a/packages/patterns/src/types.js +++ b/packages/patterns/src/types.js @@ -22,6 +22,7 @@ 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 * @@ -61,8 +62,9 @@ export {}; * @returns {string} */ +// FIXME exclude nested Error and Promise /** - * @typedef {Passable} Pattern + * @typedef {Exclude} Pattern * * Patterns are Passable arbitrarily-nested pass-by-copy containers * (CopyArray, CopyRecord, CopySet, CopyBag, CopyMap) in which every @@ -422,14 +424,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.