capsite/lib: MapStore and patterns work-alikes
Enough of @agoric/store's MapStore and @endo/patterns' M for the
credential-rotation design: makeMapStore(label, { keyShape, valueShape },
kv) keeps entries in a DO's sync KV under store:<label>:, and readOnly()
hands out the queries alone. Facets are workerd RpcTargets in place of
endo's Far.
Unlike endo, error messages never include keys or values: the values
here are credentials, and messages cross RPC into logs.
Generated-by: Claude Opus 5.5
This commit is contained in:
1 parent
1d8a220bbc
commit
9179bdbf38
10 files changed
+2586
No files matched your search
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @file a work-alike for enough of @endo/patterns: `M.string()`, `M.any()`,
|
||||
* and literal patterns (a string, number, boolean, or null matches only
|
||||
* itself, so `"cookie"` as a key shape admits exactly one key).
|
||||
* @see https://github.com/endojs/endo/tree/master/packages/patterns
|
||||
*/
|
||||
|
||||
type MatcherKind = "string" | "any";
|
||||
|
||||
/** a matcher made by {@link M}; recognized by identity, so it is meaningful
|
||||
* only in the realm that made it (it does not survive an RPC copy) */
|
||||
export type Matcher = Readonly<{ toString(): string }>;
|
||||
export type Pattern = Matcher | string | number | boolean | null;
|
||||
|
||||
const kinds = new WeakMap<object, MatcherKind>();
|
||||
|
||||
const makeMatcher = (kind: MatcherKind, label: string): Matcher => {
|
||||
const matcher = Object.freeze({ toString: () => label });
|
||||
kinds.set(matcher, kind);
|
||||
return matcher;
|
||||
};
|
||||
|
||||
const STRING = makeMatcher("string", "M.string()");
|
||||
const ANY = makeMatcher("any", "M.any()");
|
||||
|
||||
export const M = Object.freeze({
|
||||
string: (): Matcher => STRING,
|
||||
any: (): Matcher => ANY,
|
||||
});
|
||||
|
||||
export function matches(specimen: unknown, pattern: Pattern): boolean {
|
||||
if (typeof pattern === "object" && pattern !== null) {
|
||||
const kind = kinds.get(pattern);
|
||||
if (kind === "string") return typeof specimen === "string";
|
||||
if (kind === "any") return true;
|
||||
throw new TypeError("not a pattern");
|
||||
}
|
||||
return Object.is(specimen, pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* throw unless `specimen` matches `pattern`. Unlike endo's, the message
|
||||
* names the pattern and `label` but never the specimen: specimens here
|
||||
* can be credentials, and messages cross RPC and land in logs.
|
||||
*/
|
||||
export function mustMatch(specimen: unknown, pattern: Pattern, label: string): void {
|
||||
if (!matches(specimen, pattern)) {
|
||||
const shape = typeof pattern === "object" && pattern !== null ? String(pattern) : JSON.stringify(pattern);
|
||||
throw new TypeError(`${label}: must match ${shape}`);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user