diff --git a/packages/exo/src/exo-tools.js b/packages/exo/src/exo-tools.js
index ba0d9cfb63..8b7f3578be 100644
--- a/packages/exo/src/exo-tools.js
+++ b/packages/exo/src/exo-tools.js
@@ -1,5 +1,11 @@
import { E, Far } from '@endo/far';
-import { listDifference, objectMap, mustMatch, M } from '@endo/patterns';
+import {
+ listDifference,
+ objectMap,
+ mustMatch,
+ M,
+ matches,
+} from '@endo/patterns';
/** @typedef {import('@endo/patterns').Method} Method */
/** @typedef {import('@endo/patterns').MethodGuard} MethodGuard */
@@ -17,8 +23,8 @@ const { defineProperties } = Object;
*/
const MinMethodGuard = M.call().rest(M.any()).returns(M.any());
-const defendSyncArgs = (args, methodGuard, label) => {
- const { argGuards, optionalArgGuards, restArgGuard } = methodGuard;
+const defendSyncArgs = (args, methodGuardPayload, label) => {
+ const { argGuards, optionalArgGuards, restArgGuard } = methodGuardPayload;
const paramsPattern = M.splitArray(
argGuards,
optionalArgGuards,
@@ -29,16 +35,16 @@ const defendSyncArgs = (args, methodGuard, label) => {
/**
* @param {Method} method
- * @param {MethodGuard} methodGuard
+ * @param {MethodGuard['payload']} methodGuardPayload
* @param {string} label
* @returns {Method}
*/
-const defendSyncMethod = (method, methodGuard, label) => {
- const { returnGuard } = methodGuard;
+const defendSyncMethod = (method, methodGuardPayload, label) => {
+ const { returnGuard } = methodGuardPayload;
const { syncMethod } = {
// Note purposeful use of `this` and concise method syntax
syncMethod(...args) {
- defendSyncArgs(harden(args), methodGuard, label);
+ defendSyncArgs(harden(args), methodGuardPayload, label);
const result = apply(method, this, args);
mustMatch(harden(result), returnGuard, `${label}: result`);
return result;
@@ -48,10 +54,14 @@ const defendSyncMethod = (method, methodGuard, label) => {
};
const isAwaitArgGuard = argGuard =>
- argGuard && typeof argGuard === 'object' && argGuard.klass === 'awaitArg';
+ matches(argGuard, M.kind('guard:awaitArgGuard'));
-const desync = methodGuard => {
- const { argGuards, optionalArgGuards = [], restArgGuard } = methodGuard;
+const desync = methodGuardPayload => {
+ const {
+ argGuards,
+ optionalArgGuards = [],
+ restArgGuard,
+ } = methodGuardPayload;
!isAwaitArgGuard(restArgGuard) ||
Fail`Rest args may not be awaited: ${restArgGuard}`;
const rawArgGuards = [...argGuards, ...optionalArgGuards];
@@ -66,7 +76,7 @@ const desync = methodGuard => {
}
return {
awaitIndexes,
- rawMethodGuard: {
+ rawMethodGuardPayload: {
argGuards: rawArgGuards.slice(0, argGuards.length),
optionalArgGuards: rawArgGuards.slice(argGuards.length),
restArgGuard,
@@ -74,9 +84,9 @@ const desync = methodGuard => {
};
};
-const defendAsyncMethod = (method, methodGuard, label) => {
- const { returnGuard } = methodGuard;
- const { awaitIndexes, rawMethodGuard } = desync(methodGuard);
+const defendAsyncMethod = (method, methodGuardPayload, label) => {
+ const { returnGuard } = methodGuardPayload;
+ const { awaitIndexes, rawMethodGuardPayload } = desync(methodGuardPayload);
const { asyncMethod } = {
// Note purposeful use of `this` and concise method syntax
asyncMethod(...args) {
@@ -87,7 +97,7 @@ const defendAsyncMethod = (method, methodGuard, label) => {
for (let j = 0; j < awaitIndexes.length; j += 1) {
rawArgs[awaitIndexes[j]] = awaitedArgs[j];
}
- defendSyncArgs(rawArgs, rawMethodGuard, label);
+ defendSyncArgs(rawArgs, rawMethodGuardPayload, label);
return apply(method, this, rawArgs);
});
return E.when(resultP, result => {
@@ -106,13 +116,14 @@ const defendAsyncMethod = (method, methodGuard, label) => {
* @param {string} label
*/
const defendMethod = (method, methodGuard, label) => {
- const { klass, callKind } = methodGuard;
- assert(klass === 'methodGuard');
+ mustMatch(methodGuard, M.kind('guard:methodGuard'), 'internal');
+ const { payload } = methodGuard;
+ const { callKind } = payload;
if (callKind === 'sync') {
- return defendSyncMethod(method, methodGuard, label);
+ return defendSyncMethod(method, payload, label);
} else {
assert(callKind === 'async');
- return defendAsyncMethod(method, methodGuard, label);
+ return defendAsyncMethod(method, payload, label);
}
};
@@ -228,15 +239,11 @@ export const defendPrototype = (
);
let methodGuards;
if (interfaceGuard) {
+ mustMatch(interfaceGuard, M.kind('guard:interfaceGuard'), 'internal');
const {
- klass,
- interfaceName,
- methodGuards: mg,
- sloppy = false,
+ payload: { interfaceName, methodGuards: mg, sloppy = false },
} = interfaceGuard;
methodGuards = mg;
- assert.equal(klass, 'Interface');
- assert.typeof(interfaceName, 'string');
{
const methodGuardNames = ownKeys(methodGuards);
const unimplemented = listDifference(methodGuardNames, methodNames);
diff --git a/packages/patterns/src/patterns/internal-types.js b/packages/patterns/src/patterns/internal-types.js
index c79f167136..c354f879b1 100644
--- a/packages/patterns/src/patterns/internal-types.js
+++ b/packages/patterns/src/patterns/internal-types.js
@@ -1 +1,67 @@
///
+
+/** @typedef {import('@endo/marshal').Passable} Passable */
+/** @typedef {import('@endo/marshal').PassStyle} PassStyle */
+/** @typedef {import('@endo/marshal').CopyTagged} CopyTagged */
+/** @template T @typedef {import('@endo/marshal').CopyRecord} CopyRecord */
+/** @template T @typedef {import('@endo/marshal').CopyArray} CopyArray */
+/** @typedef {import('@endo/marshal').Checker} Checker */
+/** @typedef {import('@endo/marshal').RankCompare} RankCompare */
+/** @typedef {import('@endo/marshal').RankCover} RankCover */
+
+/** @typedef {import('../types.js').AwaitArgGuard} AwaitArgGuard */
+/** @typedef {import('../types.js').ArgGuard} ArgGuard */
+/** @typedef {import('../types.js').MethodGuard} MethodGuard */
+/** @typedef {import('../types.js').InterfaceGuard} InterfaceGuard */
+/** @typedef {import('../types.js').MethodGuardMaker0} MethodGuardMaker0 */
+
+/** @typedef {import('../types').MatcherNamespace} MatcherNamespace */
+/** @typedef {import('../types').Key} Key */
+/** @typedef {import('../types').Pattern} Pattern */
+/** @typedef {import('../types').CheckPattern} CheckPattern */
+/** @typedef {import('../types').Limits} Limits */
+/** @typedef {import('../types').AllLimits} AllLimits */
+/** @typedef {import('../types').GetRankCover} GetRankCover */
+
+/**
+ * @typedef {object} MatchHelper
+ * This factors out only the parts specific to each kind of Matcher. It is
+ * encapsulated, and its methods can make the stated unchecked assumptions
+ * enforced by the common calling logic.
+ *
+ * @property {(allegedPayload: Passable,
+ * check: Checker
+ * ) => boolean} checkIsWellFormed
+ * Reports whether `allegedPayload` is valid as the payload of a CopyTagged
+ * whose tag corresponds with this MatchHelper's Matchers.
+ *
+ * @property {(specimen: Passable,
+ * matcherPayload: Passable,
+ * check: Checker,
+ * ) => boolean} checkMatches
+ * Assuming validity of `matcherPayload` as the payload of a Matcher corresponding
+ * with this MatchHelper, reports whether `specimen` is matched by that Matcher.
+ *
+ * @property {import('../types').GetRankCover} getRankCover
+ * Assumes this is the payload of a CopyTagged with the corresponding
+ * matchTag. Return a RankCover to bound from below and above,
+ * in rank order, all possible Passables that would match this Matcher.
+ * The left element must be before or the same rank as any possible
+ * matching specimen. The right element must be after or the same
+ * rank as any possible matching specimen.
+ */
+
+/**
+ * @typedef {object} PatternKit
+ * @property {(specimen: Passable,
+ * patt: Passable,
+ * check: Checker,
+ * label?: string|number
+ * ) => boolean} checkMatches
+ * @property {(specimen: Passable, patt: Pattern) => boolean} matches
+ * @property {(specimen: Passable, patt: Pattern, label?: string|number) => void} mustMatch
+ * @property {(patt: Pattern) => void} assertPattern
+ * @property {(patt: Passable) => boolean} isPattern
+ * @property {GetRankCover} getRankCover
+ * @property {MatcherNamespace} M
+ */
diff --git a/packages/patterns/src/patterns/patternMatchers.js b/packages/patterns/src/patterns/patternMatchers.js
index f091451b6b..0d3886c28a 100644
--- a/packages/patterns/src/patterns/patternMatchers.js
+++ b/packages/patterns/src/patterns/patternMatchers.js
@@ -32,6 +32,8 @@ import {
checkCopyBag,
} from '../keys/checkKey.js';
+import './internal-types.js';
+
///
const { quote: q, bare: b, details: X, Fail } = assert;
@@ -156,6 +158,18 @@ const makePatternKit = () => {
// eslint-disable-next-line no-use-before-define
HelpersByMatchTag[tag];
+ /**
+ * Note that this function indicates absence by returning `undefined`,
+ * even though `undefined` is a valid pattern. To evade this confusion,
+ * to register a payload shape with that meaning, use `MM.undefined()`.
+ *
+ * @param {string} tag
+ * @returns {Pattern | undefined}
+ */
+ const maybePayloadShape = tag =>
+ // eslint-disable-next-line no-use-before-define
+ GuardPayloadShapes[tag];
+
/**
* @typedef {string} Kind
* It is either a PassStyle other than 'tagged', or, if the underlying
@@ -187,6 +201,12 @@ const makePatternKit = () => {
// Buried here is the important case, where we process
// the various patternNodes
return matchHelper.checkIsWellFormed(tagged.payload, check);
+ } else {
+ const payloadShape = maybePayloadShape(tag);
+ if (payloadShape !== undefined) {
+ // eslint-disable-next-line no-use-before-define
+ return checkMatches(tagged.payload, payloadShape, check, tag);
+ }
}
switch (tag) {
case 'copySet': {
@@ -261,6 +281,16 @@ const makePatternKit = () => {
return false;
};
+ /**
+ * Checks only recognized kinds, and only if the specimen
+ * passes the invariants associated with that recognition.
+ *
+ * @param {Passable} specimen
+ * @param {Kind} kind
+ * @returns {boolean}
+ */
+ const isKind = (specimen, kind) => checkKind(specimen, kind, identChecker);
+
/**
* @param {Passable} specimen
* @param {Key} keyAsPattern
@@ -493,8 +523,8 @@ const makePatternKit = () => {
}
const { payload: pattPayload } = patt;
const { payload: specimenPayload } = specimen;
- const pattKeySet = copyMapKeySet(pattPayload);
- const specimenKeySet = copyMapKeySet(specimenPayload);
+ const pattKeySet = copyMapKeySet(patt);
+ const specimenKeySet = copyMapKeySet(specimen);
// Compare keys as copySets
if (!checkMatches(specimenKeySet, pattKeySet, check)) {
return false;
@@ -502,6 +532,11 @@ const makePatternKit = () => {
const pattValues = pattPayload.values;
const specimenValues = specimenPayload.values;
// compare values as copyArrays
+ // TODO BUG: this assumes that the keys appear in the
+ // same order, so we can compare values in that order.
+ // However, we're only guaranteed that they appear in
+ // the same rankOrder. Thus we must search one of these
+ // in the other's rankOrder.
return checkMatches(specimenValues, pattValues, check);
}
default: {
@@ -651,8 +686,15 @@ const makePatternKit = () => {
return getPassStyleCover(passStyle);
};
+ /**
+ * @param {Passable[]} array
+ * @param {Pattern} patt
+ * @param {Checker} check
+ * @param {string} [labelPrefix]
+ * @returns {boolean}
+ */
const arrayEveryMatchPattern = (array, patt, check, labelPrefix = '') => {
- if (checkKind(patt, 'match:any', identChecker)) {
+ if (isKind(patt, 'match:any')) {
// if the pattern is M.any(), we know its true
return true;
}
@@ -709,7 +751,7 @@ const makePatternKit = () => {
if (
patts.length === 2 &&
!matches(specimen, patts[0]) &&
- checkKind(patts[0], 'match:kind', identChecker) &&
+ isKind(patts[0], 'match:kind') &&
patts[0].payload === 'undefined'
) {
// Worth special casing the optional pattern for
@@ -920,7 +962,7 @@ const makePatternKit = () => {
const matchRemotableHelper = Far('match:remotable helper', {
checkMatches: (specimen, remotableDesc, check) => {
// Unfortunate duplication of checkKind logic, but no better choices.
- if (checkKind(specimen, 'remotable', identChecker)) {
+ if (isKind(specimen, 'remotable')) {
return true;
}
if (check === identChecker) {
@@ -1530,53 +1572,6 @@ const makePatternKit = () => {
return [base];
};
- /**
- * @param {'sync'|'async'} callKind
- * @param {ArgGuard[]} argGuards
- * @param {ArgGuard[]} [optionalArgGuards]
- * @param {ArgGuard} [restArgGuard]
- * @returns {MethodGuardMaker}
- */
- const makeMethodGuardMaker = (
- callKind,
- argGuards,
- optionalArgGuards = undefined,
- restArgGuard = undefined,
- ) =>
- harden({
- optional: (...optArgGuards) => {
- optionalArgGuards === undefined ||
- Fail`Can only have one set of optional guards`;
- restArgGuard === undefined ||
- Fail`optional arg guards must come before rest arg`;
- return makeMethodGuardMaker(callKind, argGuards, optArgGuards);
- },
- rest: rArgGuard => {
- restArgGuard === undefined || Fail`Can only have one rest arg`;
- return makeMethodGuardMaker(
- callKind,
- argGuards,
- optionalArgGuards,
- rArgGuard,
- );
- },
- returns: (returnGuard = UndefinedShape) =>
- harden({
- klass: 'methodGuard',
- callKind,
- argGuards,
- optionalArgGuards,
- restArgGuard,
- returnGuard,
- }),
- });
-
- const makeAwaitArgGuard = argGuard =>
- harden({
- klass: 'awaitArg',
- argGuard,
- });
-
// //////////////////
/** @type {MatcherNamespace} */
@@ -1665,22 +1660,18 @@ const makePatternKit = () => {
eref: t => M.or(t, M.promise()),
opt: t => M.or(M.undefined(), t),
- interface: (interfaceName, methodGuards, { sloppy = false } = {}) => {
- for (const [_, methodGuard] of entries(methodGuards)) {
- methodGuard.klass === 'methodGuard' ||
- Fail`unrecognize method guard ${methodGuard}`;
- }
- return harden({
- klass: 'Interface',
- interfaceName,
- methodGuards,
- sloppy,
- });
- },
- call: (...argGuards) => makeMethodGuardMaker('sync', argGuards),
- callWhen: (...argGuards) => makeMethodGuardMaker('async', argGuards),
-
- await: argGuard => makeAwaitArgGuard(argGuard),
+ interface: (interfaceName, methodGuards, options) =>
+ // eslint-disable-next-line no-use-before-define
+ makeInterfaceGuard(interfaceName, methodGuards, options),
+ call: (...argGuards) =>
+ // eslint-disable-next-line no-use-before-define
+ makeMethodGuardMaker('sync', argGuards),
+ callWhen: (...argGuards) =>
+ // eslint-disable-next-line no-use-before-define
+ makeMethodGuardMaker('async', argGuards),
+ await: argGuard =>
+ // eslint-disable-next-line no-use-before-define
+ makeAwaitArgGuard(argGuard),
});
return harden({
@@ -1712,50 +1703,128 @@ export const {
MM = M;
-/** @typedef {import('@endo/marshal').Passable} Passable */
-/** @typedef {import('@endo/marshal').PassStyle} PassStyle */
-/** @typedef {import('@endo/marshal').CopyTagged} CopyTagged */
-/** @template T @typedef {import('@endo/marshal').CopyRecord} CopyRecord */
-/** @template T @typedef {import('@endo/marshal').CopyArray} CopyArray */
-/** @typedef {import('@endo/marshal').Checker} Checker */
-/** @typedef {import('@endo/marshal').RankCompare} RankCompare */
-/** @typedef {import('@endo/marshal').RankCover} RankCover */
-
-/** @typedef {import('../types').ArgGuard} ArgGuard */
-/** @typedef {import('../types').MethodGuardMaker} MethodGuardMaker */
-/** @typedef {import('../types').MatcherNamespace} MatcherNamespace */
-/** @typedef {import('../types').Key} Key */
-/** @typedef {import('../types').Pattern} Pattern */
-/** @typedef {import('../types').PatternKit} PatternKit */
-/** @typedef {import('../types').CheckPattern} CheckPattern */
-/** @typedef {import('../types').Limits} Limits */
-/** @typedef {import('../types').AllLimits} AllLimits */
-/** @typedef {import('../types').GetRankCover} GetRankCover */
+// //////////////////////////// Guards ///////////////////////////////////////
+
+const AwaitArgGuardPayloadShape = harden({
+ argGuard: M.pattern(),
+});
+
+const AwaitArgGuardShape = M.kind('guard:awaitArgGuard');
/**
- * @typedef {object} MatchHelper
- * This factors out only the parts specific to each kind of Matcher. It is
- * encapsulated, and its methods can make the stated unchecker assumptions
- * enforced by the common calling logic.
- *
- * @property {(allegedPayload: Passable,
- * check: Checker
- * ) => boolean} checkIsWellFormed
- * Reports whether `allegedPayload` is valid as the payload of a CopyTagged
- * whose tag corresponds with this MatchHelper's Matchers.
- *
- * @property {(specimen: Passable,
- * matcherPayload: Passable,
- * check: Checker,
- * ) => boolean} checkMatches
- * Assuming validity of `matcherPayload` as the payload of a Matcher corresponding
- * with this MatchHelper, reports whether `specimen` is matched by that Matcher.
- *
- * @property {import('../types').GetRankCover} getRankCover
- * Assumes this is the payload of a CopyTagged with the corresponding
- * matchTag. Return a RankCover to bound from below and above,
- * in rank order, all possible Passables that would match this Matcher.
- * The left element must be before or the same rank as any possible
- * matching specimen. The right element must be after or the same
- * rank as any possible matching specimen.
+ * @param {Pattern} argGuard
+ * @returns {AwaitArgGuard}
+ */
+const makeAwaitArgGuard = argGuard => {
+ const result = makeTagged('guard:awaitArgGuard', {
+ argGuard,
+ });
+ mustMatch(result, AwaitArgGuardShape, 'argGuard');
+ return result;
+};
+
+const PatternListShape = M.arrayOf(M.pattern());
+
+const ArgGuardShape = M.or(M.pattern(), AwaitArgGuardShape);
+const ArgGuardListShape = M.arrayOf(ArgGuardShape);
+
+const SyncMethodGuardPayloadShape = harden({
+ callKind: 'sync',
+ argGuards: PatternListShape,
+ optionalArgGuards: M.opt(PatternListShape),
+ restArgGuard: M.pattern(),
+ returnGuard: M.pattern(),
+});
+
+const AsyncMethodGuardPayloadShape = harden({
+ callKind: 'async',
+ argGuards: ArgGuardListShape,
+ optionalArgGuards: M.opt(ArgGuardListShape),
+ restArgGuard: M.pattern(),
+ returnGuard: M.pattern(),
+});
+
+const MethodGuardPayloadShape = M.or(
+ SyncMethodGuardPayloadShape,
+ AsyncMethodGuardPayloadShape,
+);
+
+const MethodGuardShape = M.kind('guard:methodGuard');
+
+/**
+ * @param {'sync'|'async'} callKind
+ * @param {ArgGuard[]} argGuards
+ * @param {ArgGuard[]} [optionalArgGuards]
+ * @param {ArgGuard} [restArgGuard]
+ * @returns {MethodGuardMaker0}
*/
+const makeMethodGuardMaker = (
+ callKind,
+ argGuards,
+ optionalArgGuards = undefined,
+ restArgGuard = undefined,
+) =>
+ harden({
+ optional: (...optArgGuards) => {
+ optionalArgGuards === undefined ||
+ Fail`Can only have one set of optional guards`;
+ restArgGuard === undefined ||
+ Fail`optional arg guards must come before rest arg`;
+ return makeMethodGuardMaker(callKind, argGuards, optArgGuards);
+ },
+ rest: rArgGuard => {
+ restArgGuard === undefined || Fail`Can only have one rest arg`;
+ return makeMethodGuardMaker(
+ callKind,
+ argGuards,
+ optionalArgGuards,
+ rArgGuard,
+ );
+ },
+ returns: (returnGuard = M.undefined()) => {
+ const result = makeTagged('guard:methodGuard', {
+ callKind,
+ argGuards,
+ optionalArgGuards,
+ restArgGuard,
+ returnGuard,
+ });
+ mustMatch(result, MethodGuardShape, 'methodGuard');
+ return result;
+ },
+ });
+
+const InterfaceGuardPayloadShape = harden({
+ interfaceName: M.string(),
+ methodGuards: M.recordOf(M.string(), MethodGuardShape),
+ sloppy: M.boolean(),
+});
+
+const InterfaceGuardShape = M.kind('guard:interfaceGuard');
+
+/**
+ * @param {string} interfaceName
+ * @param {Record} methodGuards
+ * @param {{sloppy?: boolean}} [options]
+ * @returns {InterfaceGuard}
+ */
+const makeInterfaceGuard = (interfaceName, methodGuards, options = {}) => {
+ const { sloppy = false } = options;
+ for (const [_, methodGuard] of entries(methodGuards)) {
+ getTag(methodGuard) === 'guard:methodGuard' ||
+ Fail`unrecognize method guard ${methodGuard}`;
+ }
+ const result = makeTagged('guard:interfaceGuard', {
+ interfaceName,
+ methodGuards,
+ sloppy,
+ });
+ mustMatch(result, InterfaceGuardShape, 'interfaceGuard');
+ return result;
+};
+
+const GuardPayloadShapes = harden({
+ 'guard:awaitArgGuard': AwaitArgGuardPayloadShape,
+ 'guard:methodGuard': MethodGuardPayloadShape,
+ 'guard:interfaceGuard': InterfaceGuardPayloadShape,
+});
diff --git a/packages/patterns/src/types.js b/packages/patterns/src/types.js
index 7a4704a870..e2a0512eb3 100644
--- a/packages/patterns/src/types.js
+++ b/packages/patterns/src/types.js
@@ -483,13 +483,15 @@ export {};
* @property {>(interfaceName: string,
* methodGuards: M,
* options?: {sloppy?: boolean}
- * ) => InterfaceGuard} interface Guard an interface to a far object or facet
+ * ) => InterfaceGuard} interface Guard the interface of an exo object
*
- * @property {(...argGuards: ArgGuard[]) => MethodGuardMaker} call Guard a synchronous call
+ * @property {(...argGuards: ArgGuard[]) => MethodGuardMaker0} call
+ * Guard a synchronous call
*
- * @property {(...argGuards: ArgGuard[]) => MethodGuardMaker} callWhen Guard an async call
+ * @property {(...argGuards: ArgGuard[]) => MethodGuardMaker0} callWhen
+ * Guard an async call
*
- * @property {(argGuard: ArgGuard) => ArgGuard} await Guard an await
+ * @property {(argGuard: ArgGuard) => AwaitArgGuard} await Guard an await
*/
/**
@@ -500,16 +502,53 @@ export {};
// TODO parameterize this to match the behavior object it guards
/**
- * @typedef {{
- * klass: 'Interface',
- * interfaceName: string,
- * methodGuards: Record
- * sloppy?: boolean
+ * @typedef {CopyTagged & {
+ * [Symbol.toStringTag]: 'guard:interfaceGuard',
+ * payload: {
+ * interfaceName: string,
+ * methodGuards: Record
+ * sloppy?: boolean
+ * }
* }} InterfaceGuard
*/
/**
- * @typedef {any} MethodGuardMaker
+ * @typedef {object} MethodGuardMaker0
+ * A method name and parameter/return signature like:
+ * ```js
+ * foo(a, b, c = d, ...e) => f
+ * ```
+ * should be guarded by something like:
+ * ```js
+ * {
+ * ...otherMethodGuards,
+ * foo: M.call(AShape, BShape).optional(CShape).rest(EShape).returns(FShape),
+ * }
+ * ```
+ * @property {(...optArgGuards: ArgGuard[]) => MethodGuardMaker1} optional
+ * @property {(rArgGuard: Pattern) => MethodGuardMaker2} rest
+ * @property {(returnGuard?: Pattern) => MethodGuard} returns
+ */
+
+/**
+ * @typedef {object} MethodGuardMaker1
+ * A method name and parameter/return signature like:
+ * ```js
+ * foo(a, b, c = d, ...e) => f
+ * ```
+ * should be guarded by something like:
+ * ```js
+ * {
+ * ...otherMethodGuards,
+ * foo: M.call(AShape, BShape).optional(CShape).rest(EShape).returns(FShape),
+ * }
+ * ```
+ * @property {(rArgGuard: Pattern) => MethodGuardMaker2} rest
+ * @property {(returnGuard?: Pattern) => MethodGuard} returns
+ */
+
+/**
+ * @typedef {object} MethodGuardMaker2
* A method name and parameter/return signature like:
* ```js
* foo(a, b, c = d, ...e) => f
@@ -521,22 +560,29 @@ export {};
* foo: M.call(AShape, BShape).optional(CShape).rest(EShape).returns(FShape),
* }
* ```
+ * @property {(returnGuard?: Pattern) => MethodGuard} returns
*/
-/** @typedef {{ klass: 'methodGuard', callKind: 'sync' | 'async', returnGuard: unknown }} MethodGuard */
-/** @typedef {any} ArgGuard */
+/**
+ * @typedef {CopyTagged & {
+ * [Symbol.toStringTag]: 'guard:methodGuard',
+ * payload: {
+ * callKind: 'sync' | 'async',
+ * argGuards: ArgGuard[]
+ * optionalArgGuards?: ArgGuard[]
+ * restArgGuard?: Pattern
+ * returnGuard?: Pattern
+ * }
+ * }} MethodGuard
+ */
/**
- * @typedef {object} PatternKit
- * @property {(specimen: Passable,
- * patt: Passable,
- * check: Checker,
- * label?: string|number
- * ) => boolean} checkMatches
- * @property {(specimen: Passable, patt: Pattern) => boolean} matches
- * @property {(specimen: Passable, patt: Pattern, label?: string|number) => void} mustMatch
- * @property {(patt: Pattern) => void} assertPattern
- * @property {(patt: Passable) => boolean} isPattern
- * @property {GetRankCover} getRankCover
- * @property {MatcherNamespace} M
+ * @typedef {CopyTagged & {
+ * [Symbol.toStringTag]: 'guard:awaitArgGuard'
+ * payload: {
+ * argGuard: Pattern
+ * }
+ * }} AwaitArgGuard
*/
+
+/** @typedef {AwaitArgGuard | Pattern} ArgGuard */