Skip to content

Commit d4f10f1

Browse files
grypezclaude
andcommitted
refactor(kernel-utils): move sheaf exports to ./sheaf subpath
Sheaf is a large, self-contained subsystem. Keeping it under its own subpath import reduces coupling on consumers who don't need it, and keeps the main index focused on general utilities. - Add @metamask/kernel-utils/sheaf entry point (src/sheaf/index.ts) - Remove sheaf re-exports from the main index - Add ./sheaf export to package.json alongside the other subpaths - Remove sheaf overview from README (belongs in sheaf/README.md) - Update CHANGELOG: use subpath import, drop internal exports (collectSheafGuard, getStalk, guardCoversPoint), add makeSection and noopLift, fix MetadataSpec capitalisation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b6e5812 commit d4f10f1

6 files changed

Lines changed: 34 additions & 140 deletions

File tree

packages/kernel-utils/CHANGELOG.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12-
- Add sheaf programming module ([#870](https://github.com/MetaMask/ocap-kernel/pull/870))
12+
- Add `@metamask/kernel-utils/sheaf` subpath export ([#870](https://github.com/MetaMask/ocap-kernel/pull/870))
1313
- `sheafify()` for building a `Sheaf` capability authority from a collection of `PresheafSection`s, each an exo with optional invocation-dependent metadata
1414
- `constant()`, `source()`, `callable()` for constructing metadata specs (static value, compartment-evaluated code string, and per-call function respectively)
15-
- `proxyLift()`, `withFilter()`, `withRanking()`, `fallthrough()` for composing lifts to route and rank sections at dispatch time
16-
- `collectSheafGuard()` for deriving a combined `InterfaceGuard` from all sections in a sheaf
17-
- `getStalk()`, `guardCoversPoint()` for section lookup and guard checks
15+
- `noopLift()`, `proxyLift()`, `withFilter()`, `withRanking()`, `fallthrough()` for composing lifts to route and rank sections at dispatch time
16+
- `makeSection()` for constructing a typed exo section from a guard and handler map
1817
- `makeRemoteSection()` for wrapping a remote CapTP reference as a `PresheafSection`, fetching its interface guard once at construction and forwarding method calls via `E()`
19-
- Types: `Sheaf<M>`, `Section<Core>`, `PresheafSection<M>`, `EvaluatedSection<M>`, `MetaDataSpec<M>`, `Lift<M>`, `LiftContext`, `Presheaf`
18+
- Types: `Sheaf<M>`, `Section`, `PresheafSection<M>`, `EvaluatedSection<M>`, `MetadataSpec<M>`, `Lift<M>`, `LiftContext<M>`
2019

2120
## [0.5.0]
2221

packages/kernel-utils/README.md

Lines changed: 0 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -26,110 +26,6 @@ or
2626
npm install --save-dev patch-package
2727
```
2828

29-
## Sheaf Module
30-
31-
The sheaf module provides a dispatch abstraction for routing method calls across multiple capability objects (_sections_) that each cover a region of a shared interface.
32-
33-
### Overview
34-
35-
```
36-
sheafify({ name, sections, compartment? }) → Sheaf
37-
sheaf.getGlobalSection({ lift }) → section proxy
38-
sheaf.getSection({ guard, lift }) → section proxy
39-
```
40-
41-
Each call on the proxy is dispatched to whichever section covers that method. When multiple sections are eligible, a **lift** selects among them. A lift is an `AsyncGenerator` coroutine that yields candidates one at a time and receives the accumulated error history on each resume — enabling retry, fallback, and cost-aware routing without callers needing to know the selection strategy.
42-
43-
### Defining sections
44-
45-
```ts
46-
import { sheafify, constant, callable } from '@metamask/kernel-utils';
47-
48-
const sheaf = sheafify({
49-
name: 'Wallet',
50-
sections: [
51-
{
52-
exo: walletA,
53-
metadata: constant({ cost: 10, push: false }),
54-
},
55-
{
56-
exo: walletB,
57-
// callable metadata is evaluated per-call with the actual arguments
58-
metadata: callable((args) => ({ cost: 1 + 0.1 * (args[0] as number) })),
59-
},
60-
{
61-
exo: walletC,
62-
// source metadata is compiled once at sheafify time via the compartment
63-
metadata: source(`(args) => ({ cost: 5 + 0.01 * args[0] })`),
64-
},
65-
],
66-
compartment, // required only when using source-kind metadata
67-
});
68-
```
69-
70-
**Metadata kinds:**
71-
| Kind | When evaluated | Use case |
72-
|------|---------------|----------|
73-
| `constant(v)` | Never (static) | Fixed priority or capability flags |
74-
| `callable(fn)` | Each call | Arg-dependent cost, remaining spend |
75-
| `source(str)` | Each call (compiled at construction) | Sandboxed cost functions |
76-
77-
### Writing a lift
78-
79-
A lift receives `EvaluatedSection<Partial<M>>[]` (germs) and a context, and yields candidates in preference order. It receives a snapshot of all accumulated errors on each `gen.next(errors)` call.
80-
81-
```ts
82-
import type { Lift } from '@metamask/kernel-utils';
83-
84-
// Yield cheapest section first; fall back in cost order on failure
85-
const cheapest: Lift<{ cost: number }> = async function* (germs) {
86-
yield* [...germs].sort(
87-
(a, b) => (a.metadata?.cost ?? Infinity) - (b.metadata?.cost ?? Infinity),
88-
);
89-
};
90-
91-
const section = sheaf.getGlobalSection({ lift: cheapest });
92-
```
93-
94-
### Composing lifts
95-
96-
```ts
97-
import {
98-
withFilter,
99-
withRanking,
100-
fallthrough,
101-
proxyLift,
102-
} from '@metamask/kernel-utils';
103-
104-
// Filter out sections with insufficient remaining spend
105-
const spendable = withFilter<Cost>(
106-
(germ, { args }) =>
107-
(germ.metadata?.remainingSpend ?? Infinity) >= (args[0] as number),
108-
);
109-
110-
// Sort by cost before passing to the inner lift
111-
const byCost = withRanking<Cost>(
112-
(a, b) => (a.metadata?.cost ?? Infinity) - (b.metadata?.cost ?? Infinity),
113-
);
114-
115-
// Try local sections first, fall through to remote on exhaustion
116-
const withFallback = fallthrough(localLift, remoteLift);
117-
118-
// Compose: filter → rank → select
119-
const lift = spendable(byCost(cheapest));
120-
```
121-
122-
`withFilter` and `withRanking` are pure input transforms that return the inner lift's generator directly. `fallthrough` sequences two lifts via `yield*`, which forwards the error array to each inner lift. `proxyLift` is the primitive for adding logic (logging, circuit-breaking) between yields.
123-
124-
### Error handling
125-
126-
When all candidates are exhausted, `driveLift` throws:
127-
128-
```
129-
Error: No viable section for <method>
130-
cause: [Error: ..., Error: ..., ...] // all accumulated attempt errors
131-
```
132-
13329
## Contributing
13430

13531
This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/ocap-kernel#readme).

packages/kernel-utils/package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@
7979
"default": "./dist/nodejs/index.cjs"
8080
}
8181
},
82+
"./sheaf": {
83+
"import": {
84+
"types": "./dist/sheaf/index.d.mts",
85+
"default": "./dist/sheaf/index.mjs"
86+
},
87+
"require": {
88+
"types": "./dist/sheaf/index.d.cts",
89+
"default": "./dist/sheaf/index.cjs"
90+
}
91+
},
8292
"./vite-plugins": {
8393
"import": {
8494
"types": "./dist/vite-plugins/index.d.mts",

packages/kernel-utils/src/index.test.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ describe('index', () => {
1313
'GET_DESCRIPTION',
1414
'abortableDelay',
1515
'calculateReconnectionBackoff',
16-
'callable',
17-
'constant',
1816
'delay',
19-
'fallthrough',
2017
'fetchValidatedJson',
2118
'fromHex',
2219
'ifDefined',
@@ -33,22 +30,14 @@ describe('index', () => {
3330
'makeDefaultExo',
3431
'makeDefaultInterface',
3532
'makeDiscoverableExo',
36-
'makeRemoteSection',
37-
'makeSection',
3833
'mergeDisjointRecords',
3934
'methodArgsToStruct',
40-
'noopLift',
4135
'prettifySmallcaps',
42-
'proxyLift',
4336
'retry',
4437
'retryWithBackoff',
45-
'sheafify',
46-
'source',
4738
'stringify',
4839
'toHex',
4940
'waitUntilQuiescent',
50-
'withFilter',
51-
'withRanking',
5241
]);
5342
});
5443
});

packages/kernel-utils/src/index.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,3 @@ export {
4444
DEFAULT_MAX_DELAY_MS,
4545
} from './retry.ts';
4646
export type { RetryBackoffOptions, RetryOnRetryInfo } from './retry.ts';
47-
export type {
48-
Section,
49-
PresheafSection,
50-
EvaluatedSection,
51-
MetadataSpec,
52-
Lift,
53-
LiftContext,
54-
Sheaf,
55-
} from './sheaf/types.ts';
56-
export { constant, source, callable } from './sheaf/metadata.ts';
57-
export { sheafify } from './sheaf/sheafify.ts';
58-
export {
59-
noopLift,
60-
proxyLift,
61-
withFilter,
62-
withRanking,
63-
fallthrough,
64-
} from './sheaf/compose.ts';
65-
export { makeRemoteSection } from './sheaf/remote.ts';
66-
export { makeSection } from './sheaf/section.ts';
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export type {
2+
Section,
3+
PresheafSection,
4+
EvaluatedSection,
5+
MetadataSpec,
6+
Lift,
7+
LiftContext,
8+
Sheaf,
9+
} from './types.ts';
10+
export { constant, source, callable } from './metadata.ts';
11+
export { sheafify } from './sheafify.ts';
12+
export {
13+
noopLift,
14+
proxyLift,
15+
withFilter,
16+
withRanking,
17+
fallthrough,
18+
} from './compose.ts';
19+
export { makeRemoteSection } from './remote.ts';
20+
export { makeSection } from './section.ts';

0 commit comments

Comments
 (0)