Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@agoric/store

Package Overview
Dependencies
Maintainers
5
Versions
2620
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@agoric/store - npm Package Compare versions

Comparing version 0.6.9-dev-a9895c4.0 to 0.6.9-dev-cc99f7e.0

14

package.json
{
"name": "@agoric/store",
"version": "0.6.9-dev-a9895c4.0+a9895c4",
"version": "0.6.9-dev-cc99f7e.0+cc99f7e",
"description": "Wrapper for JavaScript map",

@@ -34,9 +34,9 @@ "type": "module",

"dependencies": {
"@agoric/assert": "0.3.16-dev-a9895c4.0+a9895c4",
"@agoric/eventual-send": "0.14.1-dev-a9895c4.0+a9895c4",
"@agoric/marshal": "0.5.1-dev-a9895c4.0+a9895c4",
"@agoric/promise-kit": "0.2.30-dev-a9895c4.0+a9895c4"
"@agoric/assert": "0.3.16-dev-cc99f7e.0+cc99f7e",
"@agoric/eventual-send": "0.14.1-dev-cc99f7e.0+cc99f7e",
"@agoric/marshal": "0.5.1-dev-cc99f7e.0+cc99f7e",
"@agoric/promise-kit": "0.2.30-dev-cc99f7e.0+cc99f7e"
},
"devDependencies": {
"@agoric/swingset-vat": "0.24.2-dev-a9895c4.0+a9895c4",
"@agoric/swingset-vat": "0.24.2-dev-cc99f7e.0+cc99f7e",
"ava": "^3.12.1"

@@ -70,3 +70,3 @@ },

},
"gitHead": "a9895c41b57d5ab3543a213824e293ee304dbcaa"
"gitHead": "cc99f7ed7f6de1b6ee86b1b813649820e741e1dc"
}

@@ -6,4 +6,10 @@ // @ts-check

export { makePatternKit } from './patterns/patternMatchers.js';
export { compareRank } from './patterns/rankOrder.js';
export {
M,
isPattern,
assertPattern,
matches,
fit,
} from './patterns/patternMatchers.js';
// export { compareRank } from './patterns/rankOrder.js';

@@ -10,0 +16,0 @@ export { makeScalarWeakSetStore } from './stores/scalarWeakSetStore.js';

@@ -9,3 +9,2 @@ // @ts-check

assertPassable,
everyPassableChild,
getTag,

@@ -98,6 +97,9 @@ isObject,

switch (passStyle) {
case 'copyRecord':
case 'copyRecord': {
// A copyRecord is a key iff all its children are keys
return Object.values(val).every(checkIt);
}
case 'copyArray': {
// A copyRecord or copyArray is a key iff all its children are keys
return everyPassableChild(val, checkIt);
// A copyArray is a key iff all its children are keys
return val.every(checkIt);
}

@@ -104,0 +106,0 @@ case 'tagged': {

@@ -9,3 +9,3 @@ // @ts-check

} from '@agoric/marshal';
import { compareRank, sortByRank } from '../patterns/rankOrder.js';
import { compareAntiRank, sortByRank } from '../patterns/rankOrder.js';
import { checkCopySetKeys } from './copySet.js';

@@ -19,3 +19,3 @@

/** @type WeakSet<CopyMap> */
/** @type WeakSet<CopyMap<any,any>> */
const copyMapMemo = new WeakSet();

@@ -68,26 +68,80 @@

/**
* @param {CopyMap} m
* @param {(v: Passable, i: number) => boolean} fn
* @returns {boolean}
* @template K,V
* @param {CopyMap<K,V>} m
* @returns {K[]}
*/
export const everyCopyMapKey = (m, fn) => {
export const getCopyMapKeys = m => {
assertCopyMap(m);
return m.payload.keys.every((v, i) => fn(v, i));
return m.payload.keys;
};
harden(getCopyMapKeys);
/**
* @template K,V
* @param {CopyMap<K,V>} m
* @returns {V[]}
*/
export const getCopyMapValues = m => {
assertCopyMap(m);
return m.payload.values;
};
harden(getCopyMapValues);
/**
* @template K,V
* @param {CopyMap<K,V>} m
* @returns {Iterable<[K,V]>}
*/
export const getCopyMapEntries = m => {
assertCopyMap(m);
const {
payload: { keys, values },
} = m;
const { length } = keys;
return harden({
[Symbol.iterator]: () => {
let i = 0;
return harden({
next: () => {
/** @type {IteratorResult<[K,V],void>} */
let result;
if (i < length) {
result = harden({ done: false, value: [keys[i], values[i]] });
i += 1;
return result;
} else {
result = harden({ done: true, value: undefined });
}
return result;
},
});
},
});
};
harden(getCopyMapEntries);
/**
* @template K,V
* @param {CopyMap<K,V>} m
* @param {(key: K, index: number) => boolean} fn
* @returns {boolean}
*/
export const everyCopyMapKey = (m, fn) =>
getCopyMapKeys(m).every((key, index) => fn(key, index));
harden(everyCopyMapKey);
/**
* @param {CopyMap} m
* @param {(v: Passable, i: number) => boolean} fn
* @template K,V
* @param {CopyMap<K,V>} m
* @param {(value: V, index: number) => boolean} fn
* @returns {boolean}
*/
export const everyCopyMapValue = (m, fn) => {
assertCopyMap(m);
return m.payload.values.every((v, i) => fn(v, i));
};
export const everyCopyMapValue = (m, fn) =>
getCopyMapValues(m).every((value, index) => fn(value, index));
harden(everyCopyMapValue);
/**
* @param {CopyMap} m
* @returns {CopySet}
* @template K,V
* @param {CopyMap<K,V>} m
* @returns {CopySet<K>}
*/

@@ -100,4 +154,5 @@ export const copyMapKeySet = m =>

/**
* @param {Iterable<[Passable, Passable]>} entries
* @returns {CopyMap}
* @template K,V
* @param {Iterable<[K, V]>} entries
* @returns {CopyMap<K,V>}
*/

@@ -108,7 +163,8 @@ export const makeCopyMap = entries => {

// organized by those keys. Also, among values associated with
// keys in the same equivalence class, those are rank sorted. This
// keys in the same equivalence class, those are rank sorted.
// TODO This
// could solve the copyMap cover issue explained in patternMatchers.js.
// But only if we include this criteria in our validation of copyMaps,
// which we currently do not.
const sortedEntries = [...sortByRank(entries, compareRank)].reverse();
const sortedEntries = sortByRank(entries, compareAntiRank);
const keys = sortedEntries.map(([k, _v]) => k);

@@ -115,0 +171,0 @@ const values = sortedEntries.map(([_k, v]) => v);

@@ -10,3 +10,3 @@ // @ts-check

import {
compareRank,
compareAntiRank,
isRankSorted,

@@ -33,4 +33,3 @@ sortByRank,

}
const reverseKeys = harden([...keys].reverse());
if (!isRankSorted(reverseKeys, compareRank)) {
if (!isRankSorted(keys, compareAntiRank)) {
return check(

@@ -45,3 +44,3 @@ false,

/** @type WeakSet<CopySet> */
/** @type WeakSet<CopySet<any>> */
const copySetMemo = new WeakSet();

@@ -77,18 +76,29 @@

/**
* @param {CopySet} s
* @param {(v: Passable, i: number) => boolean} fn
* @returns {boolean}
* @template K
* @param {CopySet<K>} s
* @returns {K[]}
*/
export const everyCopySetKey = (s, fn) => {
export const getCopySetKeys = s => {
assertCopySet(s);
return s.payload.every((v, i) => fn(v, i));
return s.payload;
};
harden(getCopySetKeys);
/**
* @template K
* @param {CopySet<K>} s
* @param {(key: K, index: number) => boolean} fn
* @returns {boolean}
*/
export const everyCopySetKey = (s, fn) =>
getCopySetKeys(s).every((key, index) => fn(key, index));
harden(everyCopySetKey);
/**
* @param {Iterable<Passable>} elements
* @returns {CopySet}
* @template K
* @param {Iterable<K>} elements
* @returns {CopySet<K>}
*/
export const makeCopySet = elements =>
makeTagged('copySet', [...sortByRank(elements, compareRank)].reverse());
makeTagged('copySet', sortByRank(elements, compareAntiRank));
harden(makeCopySet);

@@ -5,3 +5,2 @@ // @ts-check

assertChecker,
everyPassableChild,
Far,

@@ -11,4 +10,6 @@ getTag,

passStyleOf,
hasOwnPropertyOf,
} from '@agoric/marshal';
import {
compareAntiRank,
compareRank,

@@ -41,5 +42,5 @@ getPassStyleCover,

/**
* @returns {Object}
* @returns {PatternKit}
*/
export const makePatternKit = () => {
const makePatternKit = () => {
/**

@@ -88,7 +89,11 @@ * If this is a recognized match tag, return the MatchHelper.

switch (passStyle) {
case 'copyRecord':
case 'copyRecord': {
// A copyRecord is a pattern iff all its children are
// patterns
return Object.values(patt).every(checkIt);
}
case 'copyArray': {
// A copyRecord or copyArray is a pattern iff all its children are
// A copyArray is a pattern iff all its children are
// patterns
return everyPassableChild(patt, checkIt);
return patt.every(checkIt);
}

@@ -250,5 +255,6 @@ case 'tagged': {

keyEQ(specimen, patt),
X`${specimen} - Must be equivalent to the literal pattern: ${patt}`,
X`${specimen} - Must be equivalent to: ${patt}`,
);
}
assertPattern(patt);
const specStyle = passStyleOf(specimen);

@@ -268,8 +274,6 @@ const pattStyle = passStyleOf(patt);

false,
X`Array ${specimen} - must be as long as copyArray pattern: ${patt}`,
X`Array ${specimen} - Must be as long as copyArray pattern: ${patt}`,
);
}
return everyPassableChild(patt, (p, i) =>
checkMatches(specimen[i], p, check),
);
return patt.every((p, i) => checkMatches(specimen[i], p, check));
}

@@ -283,12 +287,4 @@ case 'copyRecord': {

}
const specNames = harden(
ownKeys(specimen)
.sort()
.reverse(),
);
const pattNames = harden(
ownKeys(patt)
.sort()
.reverse(),
);
const specNames = harden(ownKeys(specimen).sort(compareAntiRank));
const pattNames = harden(ownKeys(patt).sort(compareAntiRank));
if (!keyEQ(specNames, pattNames)) {

@@ -379,3 +375,3 @@ return check(

*/
const assertMatches = (specimen, patt) => {
const fit = (specimen, patt) => {
checkMatches(specimen, patt, assertChecker);

@@ -390,3 +386,5 @@ };

const encoded = encodeKey(patt);
return [encoded, `${encoded}~`];
if (encoded !== undefined) {
return [encoded, `${encoded}~`];
}
}

@@ -396,11 +394,18 @@ const passStyle = passStyleOf(patt);

case 'copyArray': {
const rankCovers = patt.map(p => getRankCover(p, encodeKey));
return harden([
rankCovers.map(([left, _right]) => left),
rankCovers.map(([_left, right]) => right),
]);
// XXX this doesn't get along with the world of cover === pair of
// strings. In the meantime, fall through to the default which
// returns a cover that covers all copyArrays.
//
// const rankCovers = patt.map(p => getRankCover(p, encodeKey));
// return harden([
// rankCovers.map(([left, _right]) => left),
// rankCovers.map(([_left, right]) => right),
// ]);
break;
}
case 'copyRecord': {
// XXX this doesn't get along with the world of cover === pair of
// strings
// strings. In the meantime, fall through to the default which
// returns a cover that covers all copyRecords.
//
// const pattKeys = ownKeys(patt);

@@ -414,3 +419,3 @@ // const pattEntries = harden(pattKeys.map(key => [key, patt[key]]));

// ]);
assert.fail('not supporting copyRecord patterns yet'); // XXX TEMP
break;
}

@@ -421,2 +426,4 @@ case 'tagged': {

if (matchHelper) {
// Buried here is the important case, where we process
// the various patternNodes
return matchHelper.getRankCover(patt.payload, encodeKey);

@@ -427,3 +434,5 @@ }

// XXX this doesn't get along with the world of cover === pair of
// strings
// strings. In the meantime, fall through to the default which
// returns a cover that covers all copySets.
//
// // Should already be validated by checkPattern. But because this

@@ -444,27 +453,32 @@ // // is a check that may loosen over time, we also assert

// ]);
assert.fail('not supporting copySet patterns yet'); // XXX TEMP
break;
}
case 'copyMap': {
// A matching copyMap must have the same keys, or at most one
// non-key key pattern. Thus we can assume that value positions
// match 1-to-1.
// XXX this doesn't get along with the world of cover === pair of
// strings. In the meantime, fall through to the default which
// returns a cover that covers all copyMaps.
//
// TODO I may be overlooking that the less precise rankOrder
// equivalence class may cause values to be out of order,
// making this rankCover not actually cover. In that case, for
// all the values for keys at the same rank, we should union their
// rank covers. TODO POSSIBLE SILENT CORRECTNESS BUG
//
// If this is a bug, it probably affects the getRankCover
// cases if matchLTEHelper and matchGTEHelper on copyMap as
// well. See makeCopyMap for an idea on fixing
// this bug.
const [leftPayloadLimit, rightPayloadLimit] = getRankCover(
patt.payload,
encodeKey,
);
return harden([
makeTagged('copyMap', leftPayloadLimit),
makeTagged('copyMap', rightPayloadLimit),
]);
// // A matching copyMap must have the same keys, or at most one
// // non-key key pattern. Thus we can assume that value positions
// // match 1-to-1.
// //
// // TODO I may be overlooking that the less precise rankOrder
// // equivalence class may cause values to be out of order,
// // making this rankCover not actually cover. In that case, for
// // all the values for keys at the same rank, we should union their
// // rank covers. TODO POSSIBLE SILENT CORRECTNESS BUG
// //
// // If this is a bug, it probably affects the getRankCover
// // cases of matchLTEHelper and matchGTEHelper on copyMap as
// // well. See makeCopyMap for an idea on fixing
// // this bug.
// const [leftPayloadLimit, rightPayloadLimit] = getRankCover(
// patt.payload,
// encodeKey,
// );
// return harden([
// makeTagged('copyMap', leftPayloadLimit),
// makeTagged('copyMap', rightPayloadLimit),
// ]);
break;
}

@@ -488,28 +502,12 @@ default: {

const matchAnyHelper = Far('M.any helper', {
checkIsMatcherPayload: (matcherPayload, check = x => x) =>
check(
matcherPayload === undefined,
X`An M.any matcher's .payload must be undefined: ${matcherPayload}`,
),
checkMatches: (_specimen, _matcherPayload, _check = x => x) => true,
getRankCover: (_matchPayload, _encodeKey) => ['', '{'],
checkKeyPattern: (_matcherPayload, _check = x => x) => true,
});
/** @type {MatchHelper} */
const matchScalarHelper = Far('M.scalar helper', {
checkIsMatcherPayload: (matcherPayload, check = x => x) =>
check(
matcherPayload === undefined,
X`An M.scalar matcher's .payload must be undefined: ${matcherPayload}`,
X`Payload must be undefined: ${matcherPayload}`,
),
checkMatches: (specimen, _matcherPayload, check = x => x) =>
checkScalarKey(specimen, check),
getRankCover: (_matchPayload, _encodeKey) => ['', '{'],
getRankCover: (_matchPayload, _encodeKey) => ['a', 'z~'],
checkKeyPattern: (_matcherPayload, _check = x => x) => true,

@@ -519,66 +517,7 @@ });

/** @type {MatchHelper} */
const matchKindHelper = Far('M.kind helper', {
checkIsMatcherPayload: (allegedKeyKind, check = x => x) =>
check(
// We cannot further restrict this to only possible passStyles
// or tags, because we wish to allow matching of tags that we
// don't know ahead of time. Do we need to separate the namespaces?
// TODO are we asking for trouble by lumping passStyles and tags
// together into kinds?
typeof allegedKeyKind === 'string',
X`A kind name must be a string: ${allegedKeyKind}`,
),
checkMatches: (specimen, kind, check = x => x) =>
check(
passStyleOf(specimen) === kind ||
(passStyleOf(specimen) === 'tagged' && getTag(specimen) === kind),
X`${specimen} - Must have passStyle or tag ${q(kind)}`,
),
getRankCover: (kind, _encodeKey) => {
switch (kind) {
case 'copySet': {
// The bounds in the cover are not valid copySets, which is fine.
// They only need to be valid copyTagged that bound all possible
// copySets. Thus, we need to call makeTagged directly, rather
// than using makeCopySet.
return [
makeTagged('copySet', null),
makeTagged('copySet', undefined),
];
}
case 'copyMap': {
// The bounds in the cover are not valid copyMaps, which is fine.
// They only need to be valid copyTagged that bound all possible
// copyMaps.
return [
makeTagged('copyMap', null),
makeTagged('copyMap', undefined),
];
}
default: {
return getPassStyleCover(/** @type {PassStyle} */ (kind));
}
}
const matchAndHelper = Far('match:and helper', {
checkMatches: (specimen, patts, check = x => x) => {
return patts.every(patt => checkMatches(specimen, patt, check));
},
checkKeyPattern: (kind, check = x => x) => {
switch (kind) {
case 'boolean':
case 'number':
case 'bigint':
case 'string':
case 'symbol':
case 'remotable':
case 'undefined':
return true;
default:
return check(false, X`${kind} keys are not supported`);
}
},
});
/** @type {MatchHelper} */
const matchAndHelper = Far('match:and helper', {
checkIsMatcherPayload: (allegedPatts, check = x => x) => {

@@ -590,10 +529,6 @@ const checkIt = patt => checkPattern(patt, check);

X`Needs array of sub-patterns: ${allegedPatts}`,
) && everyPassableChild(allegedPatts, checkIt))
) && allegedPatts.every(checkIt))
);
},
checkMatches: (specimen, patts, check = x => x) => {
return patts.every(patt => checkMatches(specimen, patt, check));
},
getRankCover: (patts, encodeKey) =>

@@ -612,13 +547,18 @@ intersectRankCovers(

const matchOrHelper = Far('match:or helper', {
checkIsMatcherPayload: matchAndHelper.checkIsMatcherPayload,
checkMatches: (specimen, patts, check = x => x) => {
return (
(check(
patts.length >= 1,
const { length } = patts;
if (length === 0) {
return check(
false,
X`${specimen} - no pattern disjuncts to match: ${patts}`,
) && !patts.every(patt => !checkMatches(specimen, patt, check)))
);
);
}
if (patts.some(patt => matches(specimen, patt))) {
return true;
}
return check(false, X`${specimen} - Must match one of ${patts}`);
},
checkIsMatcherPayload: matchAndHelper.checkIsMatcherPayload,
getRankCover: (patts, encodeKey) =>

@@ -637,4 +577,2 @@ unionRankCovers(

const matchNotHelper = Far('match:not helper', {
checkIsMatcherPayload: checkPattern,
checkMatches: (specimen, patt, check = x => x) => {

@@ -651,2 +589,4 @@ if (matches(specimen, patt)) {

checkIsMatcherPayload: checkPattern,
getRankCover: (_patt, _encodeKey) => ['', '{'],

@@ -658,5 +598,91 @@

/** @type {MatchHelper} */
const matchScalarHelper = Far('M.scalar helper', {
checkMatches: (specimen, _matcherPayload, check = x => x) =>
checkScalarKey(specimen, check),
checkIsMatcherPayload: matchAnyHelper.checkIsMatcherPayload,
getRankCover: (_matchPayload, _encodeKey) => ['a', 'z~'],
checkKeyPattern: (_matcherPayload, _check = x => x) => true,
});
/** @type {MatchHelper} */
const matchKeyHelper = Far('M.key helper', {
checkMatches: (specimen, _matcherPayload, check = x => x) =>
checkKey(specimen, check),
checkIsMatcherPayload: matchAnyHelper.checkIsMatcherPayload,
getRankCover: (_matchPayload, _encodeKey) => ['a', 'z~'],
checkKeyPattern: (_matcherPayload, _check = x => x) => true,
});
/** @type {MatchHelper} */
const matchPatternHelper = Far('M.pattern helper', {
checkMatches: (specimen, _matcherPayload, check = x => x) =>
checkPattern(specimen, check),
checkIsMatcherPayload: matchAnyHelper.checkIsMatcherPayload,
getRankCover: (_matchPayload, _encodeKey) => ['a', 'z~'],
checkKeyPattern: (_matcherPayload, _check = x => x) => true,
});
/** @type {MatchHelper} */
const matchKindHelper = Far('M.kind helper', {
checkMatches: (specimen, kind, check = x => x) =>
check(
passStyleOf(specimen) === kind ||
(passStyleOf(specimen) === 'tagged' && getTag(specimen) === kind),
X`${specimen} - Must have passStyle or tag ${q(kind)}`,
),
checkIsMatcherPayload: (allegedKeyKind, check = x => x) =>
check(
// We cannot further restrict this to only possible passStyles
// or tags, because we wish to allow matching of tags that we
// don't know ahead of time. Do we need to separate the namespaces?
// TODO are we asking for trouble by lumping passStyles and tags
// together into kinds?
typeof allegedKeyKind === 'string',
X`A kind name must be a string: ${allegedKeyKind}`,
),
getRankCover: (kind, _encodeKey) => {
let style;
switch (kind) {
case 'copySet':
case 'copyMap': {
style = 'tagged';
break;
}
default: {
style = kind;
break;
}
}
return getPassStyleCover(style);
},
checkKeyPattern: (kind, check = x => x) => {
switch (kind) {
case 'boolean':
case 'number':
case 'bigint':
case 'string':
case 'symbol':
case 'remotable':
case 'undefined':
return true;
default:
return check(false, X`${kind} keys are not supported`);
}
},
});
/** @type {MatchHelper} */
const matchLTEHelper = Far('match:lte helper', {
checkIsMatcherPayload: checkKey,
checkMatches: (specimen, rightOperand, check = x => x) =>

@@ -668,2 +694,4 @@ check(

checkIsMatcherPayload: checkKey,
getRankCover: (rightOperand, encodeKey) => {

@@ -674,50 +702,8 @@ const passStyle = passStyleOf(rightOperand);

// eslint-disable-next-line prefer-const
let [leftBound, _rightBound] = getPassStyleCover(passStyle);
switch (passStyle) {
case 'number': {
if (Number.isNaN(rightOperand)) {
// leftBound = NaN;
leftBound = 'f'; // XXX BOGUS
}
break;
}
case 'copyRecord': {
// XXX this doesn't get along with the world of cover === pair of
// strings
// leftBound = harden(
// fromEntries(entries(rightOperand).map(([k, _v]) => [k, null])),
// );
break;
}
case 'tagged': {
leftBound = makeTagged(getTag(rightOperand), null);
switch (getTag(rightOperand)) {
case 'copyMap': {
const { keys } = rightOperand.payload;
const values = keys.map(_ => null);
// See note in getRankCover for copyMap about why we
// may need to take variable values orders into account
// to be correct.
leftBound = makeTagged('copyMap', harden({ keys, values }));
break;
}
default: {
break;
}
}
break;
}
case 'remotable': {
// This does not make for a tighter rankCover, but if an
// underlying table internally further optimizes, for example with
// an identityHash of a virtual object, then this might
// help it take advantage of that.
leftBound = encodeKey(rightOperand);
break;
}
default: {
break;
}
let [leftBound, rightBound] = getPassStyleCover(passStyle);
const newRightBound = `${encodeKey(rightOperand)}~`;
if (newRightBound !== undefined) {
rightBound = newRightBound;
}
return [leftBound, `${encodeKey(rightOperand)}~`];
return [leftBound, rightBound];
},

@@ -731,4 +717,2 @@

const matchLTHelper = Far('match:lt helper', {
checkIsMatcherPayload: checkKey,
checkMatches: (specimen, rightOperand, check = x => x) =>

@@ -740,2 +724,4 @@ check(

checkIsMatcherPayload: checkKey,
getRankCover: matchLTEHelper.getRankCover,

@@ -749,4 +735,2 @@

const matchGTEHelper = Far('match:gte helper', {
checkIsMatcherPayload: checkKey,
checkMatches: (specimen, rightOperand, check = x => x) =>

@@ -758,2 +742,4 @@ check(

checkIsMatcherPayload: checkKey,
getRankCover: (rightOperand, encodeKey) => {

@@ -764,55 +750,8 @@ const passStyle = passStyleOf(rightOperand);

// eslint-disable-next-line prefer-const
let [_leftBound, rightBound] = getPassStyleCover(passStyle);
switch (passStyle) {
case 'number': {
if (Number.isNaN(rightOperand)) {
// rightBound = NaN;
rightBound = 'f';
} else {
// rightBound = Infinity;
rightBound = 'f~';
}
break;
}
case 'copyRecord': {
// XXX this doesn't get along with the world of cover === pair of
// strings
// rightBound = harden(
// fromEntries(
// entries(rightOperand).map(([k, _v]) => [k, undefined]),
// ),
// );
break;
}
case 'tagged': {
rightBound = makeTagged(getTag(rightOperand), undefined);
switch (getTag(rightOperand)) {
case 'copyMap': {
const { keys } = rightOperand.payload;
const values = keys.map(_ => undefined);
// See note in getRankCover for copyMap about why we
// may need to take variable values orders into account
// to be correct.
rightBound = makeTagged('copyMap', harden({ keys, values }));
break;
}
default: {
break;
}
}
break;
}
case 'remotable': {
// This does not make for a tighter rankCover, but if an
// underlying table internally further optimizes, for example with
// an identityHash of a virtual object, then this might
// help it take advantage of that.
rightBound = encodeKey(rightOperand);
break;
}
default: {
break;
}
let [leftBound, rightBound] = getPassStyleCover(passStyle);
const newLeftBound = encodeKey(rightOperand);
if (newLeftBound !== undefined) {
leftBound = newLeftBound;
}
return [encodeKey(rightOperand), rightBound];
return [leftBound, rightBound];
},

@@ -826,4 +765,2 @@

const matchGTHelper = Far('match:gt helper', {
getMatchTag: () => 'gt',
checkMatches: (specimen, rightOperand, check = x => x) =>

@@ -843,10 +780,212 @@ check(

/** @type {MatchHelper} */
const matchArrayOfHelper = Far('match:arrayOf helper', {
checkMatches: (specimen, subPatt, check = x => x) =>
check(
passStyleOf(specimen) === 'copyArray',
X`${specimen} - Must be an array`,
) && specimen.every(el => checkMatches(el, subPatt, check)),
checkIsMatcherPayload: checkPattern,
getRankCover: () => getPassStyleCover('copyArray'),
checkKeyPattern: (_, check = x => x) =>
check(false, X`Arrays not yet supported as keys`),
});
/** @type {MatchHelper} */
const matchRecordOfHelper = Far('match:recordOf helper', {
checkMatches: (specimen, entryPatt, check = x => x) =>
check(
passStyleOf(specimen) === 'copyRecord',
X`${specimen} - Must be a record`,
) &&
Object.entries(specimen).every(el =>
checkMatches(harden(el), entryPatt, check),
),
checkIsMatcherPayload: (entryPatt, check = x => x) =>
check(
passStyleOf(entryPatt) === 'copyArray' && entryPatt.length === 2,
X`${entryPatt} - Must be an pair of patterns`,
) && checkPattern(entryPatt, check),
getRankCover: _entryPatt => getPassStyleCover('copyRecord'),
checkKeyPattern: (_entryPatt, check = x => x) =>
check(false, X`Records not yet supported as keys`),
});
/** @type {MatchHelper} */
const matchSetOfHelper = Far('match:setOf helper', {
checkMatches: (specimen, keyPatt, check = x => x) =>
check(
passStyleOf(specimen) === 'tagged' && getTag(specimen) === 'copySet',
X`${specimen} - Must be a a CopySet`,
) && specimen.payload.every(el => checkMatches(el, keyPatt)),
checkIsMatcherPayload: checkPattern,
getRankCover: () => getPassStyleCover('tagged'),
checkKeyPattern: (_, check = x => x) =>
check(false, X`CopySets not yet supported as keys`),
});
/** @type {MatchHelper} */
const matchMapOfHelper = Far('match:mapOf helper', {
checkMatches: (specimen, [keyPatt, valuePatt], check = x => x) =>
check(
passStyleOf(specimen) === 'tagged' && getTag(specimen) === 'copyMap',
X`${specimen} - Must be a CopyMap`,
) &&
specimen.payload.keys.every(k => checkMatches(k, keyPatt, check)) &&
specimen.payload.values.every(v => checkMatches(v, valuePatt, check)),
checkIsMatcherPayload: (entryPatt, check = x => x) =>
check(
passStyleOf(entryPatt) === 'copyArray' && entryPatt.length === 2,
X`${entryPatt} - Must be an pair of patterns`,
) && checkPattern(entryPatt, check),
getRankCover: _entryPatt => getPassStyleCover('tagged'),
checkKeyPattern: (_entryPatt, check = x => x) =>
check(false, X`CopyMap not yet supported as keys`),
});
/** @type {MatchHelper} */
const matchSplitHelper = Far('match:split helper', {
checkMatches: (specimen, [base, rest = undefined], check = x => x) => {
const specimenStyle = passStyleOf(specimen);
const baseStyle = passStyleOf(base);
if (specimenStyle !== baseStyle) {
return check(
false,
X`${specimen} - Must have shape of base: ${q(baseStyle)}`,
);
}
let specB;
let specR;
if (baseStyle === 'copyArray') {
const { length: baseLen } = base;
// Frozen below
specB = specimen.slice(0, baseLen);
specR = specimen.slice(baseLen);
} else {
assert(baseStyle === 'copyRecord');
// Not yet frozen! Mutated in place
specB = {};
specR = {};
for (const [name, value] of Object.entries(specimen)) {
if (hasOwnPropertyOf(base, name)) {
specB[name] = value;
} else {
specR[name] = value;
}
}
}
harden(specB);
harden(specR);
return (
(checkMatches(specB, base, check) &&
(rest === undefined || checkMatches(specR, rest, check)))
);
},
checkIsMatcherPayload: (splitArgs, check = x => x) => {
if (
passStyleOf(splitArgs) === 'copyArray' &&
(splitArgs.length === 1 || splitArgs.length === 2)
) {
const [base, rest = undefined] = splitArgs;
const baseStyle = passStyleOf(base);
if (
isPattern(base) &&
(baseStyle === 'copyArray' || baseStyle === 'copyRecord') &&
(rest === undefined || isPattern(rest))
) {
return true;
}
}
return check(
false,
X`Must be an array of a base structure and an optional rest pattern: ${splitArgs}`,
);
},
getRankCover: ([base, _rest = undefined]) =>
getPassStyleCover(passStyleOf(base)),
checkKeyPattern: ([base, _rest = undefined], check = x => x) =>
check(false, X`${q(passStyleOf(base))} not yet supported as keys`),
});
/** @type {MatchHelper} */
const matchPartialHelper = Far('match:partial helper', {
checkMatches: (specimen, [base, rest = undefined], check = x => x) => {
const specimenStyle = passStyleOf(specimen);
const baseStyle = passStyleOf(base);
if (specimenStyle !== baseStyle) {
return check(
false,
X`${specimen} - Must have shape of base: ${q(baseStyle)}`,
);
}
let specB;
let specR;
let newBase = base;
if (baseStyle === 'copyArray') {
const { length: specLen } = specimen;
const { length: baseLen } = base;
if (specLen < baseLen) {
newBase = harden(base.slice(0, specLen));
}
// Frozen below
specB = specimen.slice(0, baseLen);
specR = specimen.slice(baseLen);
} else {
assert(baseStyle === 'copyRecord');
// Not yet frozen! Mutated in place
specB = {};
specR = {};
newBase = {};
for (const [name, value] of Object.entries(specimen)) {
if (hasOwnPropertyOf(base, name)) {
specB[name] = value;
newBase[name] = base[name];
} else {
specR[name] = value;
}
}
}
harden(specB);
harden(specR);
harden(newBase);
return (
(checkMatches(specB, newBase, check) &&
(rest === undefined || checkMatches(specR, rest, check)))
);
},
checkIsMatcherPayload: matchSplitHelper.checkIsMatcherPayload,
getRankCover: matchSplitHelper.getRankCover,
checkKeyPattern: matchSplitHelper.checkKeyPattern,
});
/** @type {Record<string, MatchHelper>} */
const HelpersByMatchTag = harden({
'match:any': matchAnyHelper,
'match:scalar': matchScalarHelper,
'match:kind': matchKindHelper,
'match:and': matchAndHelper,
'match:or': matchOrHelper,
'match:not': matchNotHelper,
'match:scalar': matchScalarHelper,
'match:key': matchKeyHelper,
'match:pattern': matchPatternHelper,
'match:kind': matchKindHelper,
'match:lt': matchLTHelper,

@@ -856,44 +995,67 @@ 'match:lte': matchLTEHelper,

'match:gt': matchGTHelper,
'match:arrayOf': matchArrayOfHelper,
'match:recordOf': matchRecordOfHelper,
'match:setOf': matchSetOfHelper,
'match:mapOf': matchMapOfHelper,
'match:split': matchSplitHelper,
'match:partial': matchPartialHelper,
});
const patt = p => {
assertPattern(p);
return p;
const makeMatcher = (tag, payload) => {
const matcher = makeTagged(tag, payload);
assertPattern(matcher);
return matcher;
};
const makeKindMatcher = kind => makeMatcher('match:kind', kind);
const theAnyPattern = makeMatcher('match:any', undefined);
const theScalarPattern = makeMatcher('match:scalar', undefined);
const theKeyPattern = makeMatcher('match:key', undefined);
const thePatternPattern = makeMatcher('match:pattern', undefined);
const theBooleanPattern = makeKindMatcher('boolean');
const theNumberPattern = makeKindMatcher('number');
const theBigintPattern = makeKindMatcher('bigint');
const theNatPattern = makeMatcher('match:gte', 0n);
const theStringPattern = makeKindMatcher('string');
const theSymbolPattern = makeKindMatcher('symbol');
const theRecordPattern = makeKindMatcher('copyRecord');
const theArrayPattern = makeKindMatcher('copyArray');
const theSetPattern = makeKindMatcher('copySet');
const theMapPattern = makeKindMatcher('copyMap');
const theRemotablePattern = makeKindMatcher('remotable');
const theErrorPattern = makeKindMatcher('error');
const thePromisePattern = makeKindMatcher('promise');
const theUndefinedPattern = makeKindMatcher('undefined');
/** @type {MatcherNamespace} */
const M = harden({
any: () => patt(makeTagged('match:any', undefined)),
scalar: () => patt(makeTagged('match:scalar', undefined)),
and: (...patts) => patt(makeTagged('match:and', patts)),
or: (...patts) => patt(makeTagged('match:or', patts)),
not: subPatt => patt(makeTagged('match:not', subPatt)),
any: () => theAnyPattern,
and: (...patts) => makeMatcher('match:and', patts),
or: (...patts) => makeMatcher('match:or', patts),
not: subPatt => makeMatcher('match:not', subPatt),
kind: kind => patt(makeTagged('match:kind', kind)),
boolean: () => M.kind('boolean'),
number: () => M.kind('number'),
bigint: () => M.kind('bigint'),
string: () => M.kind('string'),
symbol: () => M.kind('symbol'),
record: () => M.kind('copyRecord'),
array: () => M.kind('copyArray'),
set: () => M.kind('copySet'),
map: () => M.kind('copyMap'),
remotable: () => M.kind('remotable'),
error: () => M.kind('error'),
promise: () => M.kind('promise'),
/**
* All keys including `undefined` are already valid patterns and
* so can validly represent themselves. But optional pattern arguments
* `(pattern = undefined) => ...`
* cannot distinguish between `undefined` passed as a pattern vs
* omission of the argument. It will interpret the first as the
* second. Thus, when a passed pattern does not also need to be a key,
* we recommend passing `M.undefined()` instead of `undefined`.
*/
undefined: () => M.kind('undefined'),
scalar: () => theScalarPattern,
key: () => theKeyPattern,
pattern: () => thePatternPattern,
kind: makeKindMatcher,
boolean: () => theBooleanPattern,
number: () => theNumberPattern,
bigint: () => theBigintPattern,
nat: () => theNatPattern,
string: () => theStringPattern,
symbol: () => theSymbolPattern,
record: () => theRecordPattern,
array: () => theArrayPattern,
set: () => theSetPattern,
map: () => theMapPattern,
remotable: () => theRemotablePattern,
error: () => theErrorPattern,
promise: () => thePromisePattern,
undefined: () => theUndefinedPattern,
null: () => null,
lt: rightSide => patt(makeTagged('match:lt', rightSide)),
lte: rightSide => patt(makeTagged('match:lte', rightSide)),
lt: rightOperand => makeMatcher('match:lt', rightOperand),
lte: rightOperand => makeMatcher('match:lte', rightOperand),
eq: key => {

@@ -904,10 +1066,15 @@ assertKey(key);

neq: key => M.not(M.eq(key)),
gte: rightSide => patt(makeTagged('match:gte', rightSide)),
gt: rightSide => patt(makeTagged('match:gt', rightSide)),
gte: rightOperand => makeMatcher('match:gte', rightOperand),
gt: rightOperand => makeMatcher('match:gt', rightOperand),
// TODO make more precise
arrayOf: _elementPatt => M.array(),
recordOf: _entryPatt => M.record(),
setOf: _elementPatt => M.set(),
mapOf: _entryPatt => M.map(),
arrayOf: (subPatt = M.any()) => makeMatcher('match:arrayOf', subPatt),
recordOf: (keyPatt = M.any(), valuePatt = M.any()) =>
makeMatcher('match:recordOf', [keyPatt, valuePatt]),
setOf: (keyPatt = M.any()) => makeMatcher('match:setOf', keyPatt),
mapOf: (keyPatt = M.any(), valuePatt = M.any()) =>
makeMatcher('match:mapOf', [keyPatt, valuePatt]),
split: (base, rest = undefined) =>
makeMatcher('match:split', rest === undefined ? [base] : [base, rest]),
partial: (base, rest = undefined) =>
makeMatcher('match:partial', rest === undefined ? [base] : [base, rest]),
});

@@ -917,3 +1084,3 @@

matches,
assertMatches,
fit,
assertPattern,

@@ -927,1 +1094,17 @@ isPattern,

};
// Only include those whose meaning is independent of an imputed sort order
// of remotables, or of encoding of passable as sortable strings. Thus,
// getRankCover is omitted. To get one, you'd need to instantiate
// `makePatternKit()` yourself. Since there are currently no external
// uses of `getRankCover`, for clarity during development, `makePatternKit`
// is not currently exported.
export const {
matches,
fit,
assertPattern,
isPattern,
assertKeyPattern,
isKeyPattern,
M,
} = makePatternKit();

@@ -49,128 +49,154 @@ // @ts-check

/** @type {CompareRank} */
export const compareRank = (left, right) => {
if (sameValueZero(left, right)) {
return 0;
}
const leftStyle = passStyleOf(left);
const rightStyle = passStyleOf(right);
if (leftStyle !== rightStyle) {
return compareRank(PassStyleRank[leftStyle], PassStyleRank[rightStyle]);
}
switch (leftStyle) {
case 'undefined':
case 'null':
case 'remotable':
case 'error':
case 'promise': {
// For each of these passStyles, all members of that passStyle are tied
// for the same rank.
/**
* @type {WeakMap<CompareRank,WeakSet<Passable[]>>}
*/
const memoOfSorted = new WeakMap();
/**
* @type {WeakMap<CompareRank,CompareRank>}
*/
const comparatorMirrorImages = new WeakMap();
/**
* @param {CompareRank=} compareRemotables
* An option to create a comparator in which an internal order is
* assigned to remotables. This defaults to a comparator that
* always returns `0`, meaning that all remotables are tied
* for the same rank.
* @returns {ComparatorKit}
*/
const makeComparatorKit = (compareRemotables = (_x, _y) => 0) => {
/** @type {CompareRank} */
const comparator = (left, right) => {
if (sameValueZero(left, right)) {
return 0;
}
case 'boolean':
case 'bigint':
case 'string': {
// Within each of these passStyles, the rank ordering agrees with
// JavaScript's relational operators `<` and `>`.
if (left < right) {
return -1;
} else {
assert(left > right);
return 1;
}
const leftStyle = passStyleOf(left);
const rightStyle = passStyleOf(right);
if (leftStyle !== rightStyle) {
return comparator(PassStyleRank[leftStyle], PassStyleRank[rightStyle]);
}
case 'symbol': {
return compareRank(
nameForPassableSymbol(left),
nameForPassableSymbol(right),
);
}
case 'number': {
// `NaN`'s rank is after all other numbers.
if (Number.isNaN(left)) {
assert(!Number.isNaN(right));
return 1;
} else if (Number.isNaN(right)) {
return -1;
switch (leftStyle) {
case 'remotable': {
return compareRemotables(left, right);
}
// The rank ordering of non-NaN numbers agrees with JavaScript's
// relational operators '<' and '>'.
if (left < right) {
return -1;
} else {
assert(left > right);
return 1;
case 'undefined':
case 'null':
case 'error':
case 'promise': {
// For each of these passStyles, all members of that passStyle are tied
// for the same rank.
return 0;
}
}
case 'copyRecord': {
// Lexicographic by inverse sorted order of property names, then
// lexicographic by corresponding values in that same inverse
// order of their property names. Comparing names by themselves first,
// all records with the exact same set of property names sort next to
// each other in a rank-sort of copyRecords.
case 'boolean':
case 'bigint':
case 'string': {
// Within each of these passStyles, the rank ordering agrees with
// JavaScript's relational operators `<` and `>`.
if (left < right) {
return -1;
} else {
assert(left > right);
return 1;
}
}
case 'symbol': {
return comparator(
nameForPassableSymbol(left),
nameForPassableSymbol(right),
);
}
case 'number': {
// `NaN`'s rank is after all other numbers.
if (Number.isNaN(left)) {
assert(!Number.isNaN(right));
return 1;
} else if (Number.isNaN(right)) {
return -1;
}
// The rank ordering of non-NaN numbers agrees with JavaScript's
// relational operators '<' and '>'.
if (left < right) {
return -1;
} else {
assert(left > right);
return 1;
}
}
case 'copyRecord': {
// Lexicographic by inverse sorted order of property names, then
// lexicographic by corresponding values in that same inverse
// order of their property names. Comparing names by themselves first,
// all records with the exact same set of property names sort next to
// each other in a rank-sort of copyRecords.
// The copyRecord invariants enforced by passStyleOf ensure that
// all the property names are strings. We need the reverse sorted order
// 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.
const leftNames = harden(
ownKeys(left)
.sort()
// TODO Measure which is faster: a reverse sort by sorting and
// reversing, or by sorting with an inverse comparison function.
// If it makes a significant difference, use the faster one.
.reverse(),
);
const rightNames = harden(
ownKeys(right)
.sort()
.reverse(),
);
const result = compareRank(leftNames, rightNames);
if (result !== 0) {
return result;
}
const leftValues = harden(leftNames.map(name => left[name]));
const rightValues = harden(rightNames.map(name => right[name]));
return compareRank(leftValues, rightValues);
}
case 'copyArray': {
// Lexicographic
const len = Math.min(left.length, right.length);
for (let i = 0; i < len; i += 1) {
const result = compareRank(left[i], right[i]);
// The copyRecord invariants enforced by passStyleOf ensure that
// all the property names are strings. We need the reverse sorted order
// 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.
const leftNames = harden(
ownKeys(left)
.sort()
// TODO Measure which is faster: a reverse sort by sorting and
// reversing, or by sorting with an inverse comparison function.
// If it makes a significant difference, use the faster one.
.reverse(),
);
const rightNames = harden(
ownKeys(right)
.sort()
.reverse(),
);
const result = comparator(leftNames, rightNames);
if (result !== 0) {
return result;
}
const leftValues = harden(leftNames.map(name => left[name]));
const rightValues = harden(rightNames.map(name => right[name]));
return comparator(leftValues, rightValues);
}
// 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.
return compareRank(left.length, right.length);
}
case 'tagged': {
// Lexicographic by `[Symbol.toStringTag]` then `.payload`.
const labelComp = compareRank(getTag(left), getTag(right));
if (labelComp !== 0) {
return labelComp;
case 'copyArray': {
// Lexicographic
const len = Math.min(left.length, right.length);
for (let i = 0; i < len; i += 1) {
const result = comparator(left[i], right[i]);
if (result !== 0) {
return result;
}
}
// 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.
return comparator(left.length, right.length);
}
return compareRank(left.payload, right.payload);
case 'tagged': {
// Lexicographic by `[Symbol.toStringTag]` then `.payload`.
const labelComp = comparator(getTag(left), getTag(right));
if (labelComp !== 0) {
return labelComp;
}
return comparator(left.payload, right.payload);
}
default: {
assert.fail(X`Unrecognized passStyle: ${q(leftStyle)}`);
}
}
default: {
assert.fail(X`Unrecognized passStyle: ${q(leftStyle)}`);
}
}
};
harden(compareRank);
};
/** @type {CompareRank} */
export const compareAntiRank = (x, y) => compareRank(y, x);
/** @type {CompareRank} */
const antiComparator = (x, y) => comparator(y, x);
memoOfSorted.set(comparator, new WeakSet());
memoOfSorted.set(antiComparator, new WeakSet());
comparatorMirrorImages.set(comparator, antiComparator);
comparatorMirrorImages.set(antiComparator, comparator);
return harden({ comparator, antiComparator });
};
/**
* @type {Map<CompareRank,WeakSet<Passable[]>>}
* @param {CompareRank} comparator
* @returns {CompareRank=}
*/
const memoOfSorted = new Map([
[compareRank, new WeakSet()],
[compareAntiRank, new WeakSet()],
]);
export const comparatorMirrorImage = comparator =>
comparatorMirrorImages.get(comparator);

@@ -351,1 +377,49 @@ /**

};
export const {
comparator: compareRank,
antiComparator: compareAntiRank,
} = makeComparatorKit();
/**
* Create a comparator kit in which remotables are fully ordered
* by the order in which they are first seen by *this* comparator kit.
* BEWARE: This is observable mutable state, so such a comparator kit
* should never be shared among subsystems that should not be able
* to communicate.
*
* Note that this order does not meet the requirements for store
* ordering, since it has no memory of deleted keys.
*
* These full order comparator kit is strictly more precise that the
* rank order comparator kits above. As a result, any array which is
* sorted by such a full order will pass the isRankSorted test with
* a corresponding rank order.
*
* An array which is sorted by a *fresh* full order comparator, i.e.,
* one that has not yet seen any remotables, will of course remain
* sorted by according to *that* full order comparator. An array *of
* scalars* sorted by a fresh full order will remain sorted even
* according to a new fresh full order comparator, since it will see
* the remotables in the same order again. Unfortunately, this is
* not true of arrays of passables in general.
*
* @returns {ComparatorKit}
*/
export const makeFullOrderComparatorKit = () => {
let numSeen = 0;
// Could be a WeakMap but would perform poorly. There are a dynamic
// number of these created, and each typically has a short lifetime.
const seen = new Map();
const tag = r => {
if (seen.has(r)) {
return seen.get(r);
}
numSeen += 1;
seen.set(r, numSeen);
return numSeen;
};
const compareRemotables = (x, y) => compareRank(tag(x), tag(y));
return makeComparatorKit(compareRemotables);
};
harden(makeFullOrderComparatorKit);

@@ -7,3 +7,3 @@ // @ts-check

import { makeCopyMap } from '../keys/copyMap.js';
import { makePatternKit } from '../patterns/patternMatchers.js';
import { fit, assertPattern } from '../patterns/patternMatchers.js';
import { makeWeakMapStoreMethods } from './scalarWeakMapStore.js';

@@ -13,3 +13,2 @@ import { makeCursorKit } from './store-utils.js';

const { details: X, quote: q } = assert;
const { assertMatches, assertPattern } = makePatternKit();

@@ -115,3 +114,3 @@ /**

if (schema) {
assertMatches(harden([key, value]), schema);
fit(harden([key, value]), schema);
}

@@ -118,0 +117,0 @@ };

@@ -7,3 +7,3 @@ // @ts-check

import { makeCopySet } from '../keys/copySet.js';
import { makePatternKit } from '../patterns/patternMatchers.js';
import { fit, assertPattern } from '../patterns/patternMatchers.js';
import { makeWeakSetStoreMethods } from './scalarWeakSetStore.js';

@@ -13,3 +13,2 @@ import { makeCursorKit } from './store-utils.js';

const { details: X, quote: q } = assert;
const { assertMatches, assertPattern } = makePatternKit();

@@ -108,3 +107,3 @@ /**

if (schema) {
assertMatches(key, schema);
fit(key, schema);
}

@@ -111,0 +110,0 @@ };

// @ts-check
import { Far, assertPassable, passStyleOf } from '@agoric/marshal';
import { makePatternKit } from '../patterns/patternMatchers.js';
import { fit, assertPattern } from '../patterns/patternMatchers.js';
const { details: X, quote: q } = assert;
const { assertMatches, assertPattern } = makePatternKit();

@@ -97,3 +96,3 @@ /**

if (schema) {
assertMatches(harden([key, value]), schema);
fit(harden([key, value]), schema);
}

@@ -100,0 +99,0 @@ };

// @ts-check
import { Far, passStyleOf } from '@agoric/marshal';
import { makePatternKit } from '../patterns/patternMatchers.js';
import { fit, assertPattern } from '../patterns/patternMatchers.js';
const { details: X, quote: q } = assert;
const { assertMatches, assertPattern } = makePatternKit();

@@ -81,3 +80,3 @@ /**

if (schema) {
assertMatches(key, schema);
fit(key, schema);
}

@@ -84,0 +83,0 @@ };

// @ts-check
import { filterIterable } from '@agoric/marshal';
import { makePatternKit } from '../patterns/patternMatchers.js';
import { matches } from '../patterns/patternMatchers.js';
import { assertRankSorted } from '../patterns/rankOrder.js';
const { details: X, quote: q } = assert;
const { matches } = makePatternKit();

@@ -10,0 +9,0 @@ export const makeCursorKit = (

@@ -16,2 +16,3 @@ // @ts-check

/**
* @template K
* @typedef {CopyTagged} CopySet

@@ -21,2 +22,3 @@ */

/**
* @template K,V
* @typedef {CopyTagged} CopyMap

@@ -79,4 +81,4 @@ */

* @property {(keyPattern?: Pattern) => K[]} keys
* @property {(keyPattern?: Pattern) => CopySet} snapshot
* @property {(copySet: CopySet) => void} addAll
* @property {(keyPattern?: Pattern) => CopySet<K>} snapshot
* @property {(copySet: CopySet<K>) => void} addAll
* @property {(keyPattern?: Pattern,

@@ -124,4 +126,4 @@ * direction?: Direction

* @property {(entryPattern?: Pattern) => [K,V][]} entries
* @property {(entryPattern?: Pattern) => CopyMap} snapshot
* @property {(copyMap: CopyMap) => void} addAll
* @property {(entryPattern?: Pattern) => CopyMap<K,V>} snapshot
* @property {(copyMap: CopyMap<K,V>) => void} addAll
* @property {(entryPattern?: Pattern,

@@ -220,2 +222,8 @@ * direction?: Direction

/**
* @typedef {Object} ComparatorKit
* @property {CompareRank} comparator
* @property {CompareRank} antiComparator
*/
// ///////////////////// Should be internal only types /////////////////////////

@@ -281,4 +289,7 @@

* @callback KeyToDBKey
* If this key can be encoded as a DBKey string which sorts correctly,
* return that string. Else return `undefined`. For example, a scalar-only
* encodeKey would return `undefined` for all non-scalar keys.
* @param {Passable} key
* @returns {string}
* @returns {string=}
*/

@@ -298,2 +309,98 @@

/**
* @typedef {Object} MatcherNamespace
* @property {() => Matcher} any
* Matches any passable
* @property {(...patts: Pattern[]) => Matcher} and
* Only if it matches all the sub-patterns
* @property {(...patts: Pattern[]) => Matcher} or
* Only if it matches at least one subPattern
* @property {(subPatt: Pattern) => Matcher} not
* Only if it does not match the sub-pattern
*
* @property {() => Matcher} scalar
* The scalars are the primitive values and Remotables.
* All scalars are keys.
* @property {() => Matcher} key
* Can be in a copySet or the key in a CopyMap.
* (Will eventually be able to a key is a MapStore.)
* All keys are patterns that match only themselves.
* @property {() => Matcher} pattern
* If it matches M.pattern(), the it is itself a pattern used
* to match other passables. A pattern cannot contain errors
* or promises, as these are not stable enough to usefully match.
* @property {(kind: string) => Matcher} kind
* @property {() => Matcher} boolean
* @property {() => Matcher} number Only floating point numbers
* @property {() => Matcher} bigint
* @property {() => Matcher} nat
* @property {() => Matcher} string
* @property {() => Matcher} symbol
* Only registered and well-known symbols are passable
* @property {() => Matcher} record A CopyRecord
* @property {() => Matcher} array A CopyArray
* @property {() => Matcher} set A CopySet
* @property {() => Matcher} map A CopyMap
* @property {() => Matcher} remotable A far object or its remote presence
* @property {() => Matcher} error
* Error objects are passable, but are neither keys nor symbols.
* They do not have a useful identity.
* @property {() => Matcher} promise
* Promises are passable, but are neither keys nor symbols.
* They do not have a useful identity.
* @property {() => Matcher} undefined
* All keys including `undefined` are already valid patterns and
* so can validly represent themselves. But optional pattern arguments
* `(pattern = undefined) => ...`
* cannot distinguish between `undefined` passed as a pattern vs
* omission of the argument. It will interpret the first as the
* second. Thus, when a passed pattern does not also need to be a key,
* we recommend passing `M.undefined()` instead of `undefined`.
*
* @property {() => null} null
*
* @property {(rightOperand :Key) => Matcher} lt
* Matches if < the right operand by compareKeys
* @property {(rightOperand :Key) => Matcher} lte
* Matches if <= the right operand by compareKeys
* @property {(key :Key) => Matcher} eq
* @property {(key :Key) => Matcher} neq
* @property {(rightOperand :Key) => Matcher} gte
* Matches if >= the right operand by compareKeys
* @property {(rightOperand :Key) => Matcher} gt
* Matches if > the right operand by compareKeys
*
* @property {(subPatt?: Pattern) => Matcher} arrayOf
* @property {(keyPatt?: Pattern, valuePatt?: Pattern) => Matcher} recordOf
* @property {(keyPatt?: Pattern) => Matcher} setOf
* @property {(keyPatt?: Pattern, valuePatt?: Pattern) => Matcher} mapOf
* @property {(
* base: CopyRecord<*> | CopyArray<*>,
* rest?: Pattern
* ) => Matcher} split
* An array or record is split into the first part that matches the
* base pattern, and the remainder, which matches against the optional
* rest pattern if present.
* @property {(
* base: CopyRecord<*> | CopyArray<*>,
* rest?: Pattern
* ) => Matcher} partial
* An array or record is split into the first part that matches the
* base pattern, and the remainder, which matches against the optional
* rest pattern if present.
* Unlike `M.split`, `M.partial` ignores properties on the base
* pattern that are not present on the specimen.
*/
/**
* @typedef {Object} PatternKit
* @property {(specimen: Passable, patt: Pattern) => boolean} matches
* @property {(specimen: Passable, patt: Pattern) => void} fit
* @property {(patt: Pattern) => void} assertPattern
* @property {(patt: Passable) => boolean} isPattern
* @property {(patt: Pattern) => void} assertKeyPattern
* @property {(patt: Passable) => boolean} isKeyPattern
* @property {MatcherNamespace} M
*/
// /////////////////////////////////////////////////////////////////////////////

@@ -300,0 +407,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc