-
Notifications
You must be signed in to change notification settings - Fork 6
refactor(extension,streams): Abstract envelope handling #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
8085d8f to
20a76b6
Compare
Outdated but with previously requested changes either incorporated or borrowed on in the latest changes.
SMotaal
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| export type CapTpMessage<Type extends `CTP_${string}` = `CTP_${string}`> = { | ||
| type: Type; | ||
| epoch: number; | ||
| [key: string]: unknown; | ||
| }; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we want to upstream to @endo/captp, maybe bring it up in the next Endo Sync.
| makeStreamEnvelopeHandler, | ||
| } from './stream-envelope.js'; | ||
|
|
||
| const defaultCompartment = new Compartment({ URL }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is URL being passed to the Compartment constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was my decision, but there's no greater intention behind it. I think I was of a mind to use it for demonstration purposes via kernel.evaluate(). Everything Compartment-related in the current vat implementation should be considered a placeholder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In other words, this is a placeholder for "some universal endowments go here"?
BTW, IIUC, URL carries no authority, so it shouldn't be needed as an endowment, though it might be desirable to have it in the environment just to avoid having to reimplement it. In this case, it might be sufficient to harden URL, if that is actually possible -- I'm not sure how/if hardening works with exotic objects provided by the host, which I suspect this is, but if harden(URL) doesn't work, putting a hardened wrapper around it should be trivial. As far as I know Agoric hasn't done any work on taming the web APIs, so it's something we might well end up needing to do ourselves (not necessarily a comprehensive taming, but I certainly expect there will be specific things we want to make available to user code).
rekmarks
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a final review, but a first pass.
| /* eslint-disable @typescript-eslint/no-unused-expressions */ | ||
| // eslint-disable-next-line vitest/expect-expect | ||
| it('causes a typescript error when supplying typeguard keys not matching the label type', () => { | ||
| // @ts-expect-error the bar key is missing | ||
| makeStreamEnvelopeKit<typeof labels, ContentMap>({ | ||
| foo: (value: unknown): value is Foo => true, | ||
| }); | ||
| makeStreamEnvelopeKit<typeof labels, ContentMap>({ | ||
| foo: (value: unknown): value is Foo => true, | ||
| bar: (value: unknown): value is Bar => true, | ||
| // @ts-expect-error the qux key is not included in labels | ||
| qux: (value: unknown): value is 'qux' => false, | ||
| }); | ||
| }); | ||
| /* eslint-enable @typescript-eslint/no-unused-expressions */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment goes for all type tests: generally speaking, we haven't bothered with testing our types in this manner. I think the expense is especially difficult to justify for code that is internal to a monorepo, since any bugs or shortcomings of the types will become immediately apparent at development time. Furthermore, in my experience, exhaustive behavioral unit tests are generally sufficient to surface issues with the types.
The other issue with this way of writing type tests is that it's hard on the eyes, and disagreeable to the linter. There are libraries / frameworks that enable it to be done neatly, but adding those would be a bigger decision.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This issue was a challenging typescript exercise for me. The tests helped me validate my assumptions during development. Due to the expense you mentioned I don't advocate them as a required practice, but I do advocate them as an acceptable practice. Given the nuanced type interactions I am more confident in the maintainability of the envelope-kit solution with these type tests in place.
I think the patterns I used here are sufficient and would be helpful if a similar scenario arises, and could serve us well for two more PRs before we consider a dedicated library / framework. The behavioral unit tests cannot check the typing behavior without soothing typescript anyhow, so I collected all the typing assumptions into one test per tested artifact.
To reduce the number of eslint inline configuration comments, I could collect all the type tests into an envelope-kit-types.test.ts and wrap the whole file with the lint exceptions; alternatively we could adopt a convention like envelope-kit.test.types.ts and except these in .eslintrc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proposed different naming convention: envelope-kit.types.test.ts
|
TODO:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is currently being built to /dist due to its inclusion in /src. The file, however, is useful. I prefer to handle this in the following way:
- Create a directory
/testin the package root directory - Add the folder to the included files in
tsconfig.jsononly - Ensure that the folder is covered by the same ESLint rules as other test files in
.eslintrc - Import the fixtures from the new location
| testEnvelopeHandlers, | ||
| ); | ||
| await expect( | ||
| // @ts-expect-error label is intentionally unknown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
info: We call this practice "intentional destructive testing", and that is also an acceptable explanation string.
Co-authored-by: Saleh Abdel Motaal <saleh@smotaal.io>
Co-authored-by: Erik Marks <25517051+rekmarks@users.noreply.github.com>
Co-authored-by: Erik Marks <25517051+rekmarks@users.noreply.github.com>
f914837 to
6f7c0dc
Compare
rekmarks
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Closes: #58
Adds makeStreamEnvelopeHandler, which makes a stream envelope handler for handling unknown values.
If the supplied value is a valid envelope with a defined stream envelope handler, the stream envelope handler will return whatever the defined handler returns.
If the stream envelope handler is passed a well-formed stream envelope without a defined handler, an explanation and the envelope will be passed to the supplied stream envelope handler.
If the stream envelope handler encounters an error while parsing the supplied value, it will pass the reason and value to the supplied stream envelope handler.
If no error handler is supplied, the default error handling behavior is to throw.