-
Notifications
You must be signed in to change notification settings - Fork 212
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
export types #6500
export types #6500
Conversation
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.
Nice start. Not an approval until I can get some clarifications on the various ts-expect-error
.
I'd like to at least double check the need for some of these ts-expect-error
s. As I've expressed before, I believe they are worse than casts as they can hide multiple errors unexpectedly. If possible narrow application of casts are better (while slightly harder to read unfortunately).
Also I see there was an attempt to switch to exported types, but it was reverted. If attempted again, let's avoid deep imports cross package.
I have some type fixes for swingset that I may now try to brush off :)
// eslint-disable-next-line import/no-extraneous-dependencies | ||
import '@agoric/swingset-vat/src/types-ambient.js'; |
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 don't suppose we can avoid relying on ambient types for swingset-vat
. I know it does re-export most types explicitly at the top level.
@@ -158,7 +158,7 @@ const FIRST_METER_ID = 1n; | |||
|
|||
/** | |||
* @param {HostStore} hostStorage | |||
* @param {KernelSlog} kernelSlog | |||
* @param {KernelSlog | null} kernelSlog |
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.
Oh I have a stash with this fix and other swingset ones somewhere
/** @type {<T>(store: ERef<MapStore>, key: string, make: () => T) => Promise<T>} */ | ||
/** @type {<T>(store: ERef<Map<string, T>>, key: string, make: () => T) => Promise<T>} */ |
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'm surprised we have a Map
based store around
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.
provideWhen
seem to be used once with a ERef<MapStore>
and once with a Map
. The semantics of these Maps are different enough, which I believe does reflect on their signature. If provideWhen
is in fact using the common subset of these 2, can we be explicit about this somehow? Since TS is structural typing only, maybe by defining an interface of the subset used? That would avoid a cast / expect-error in the place where provideWhen
is used with the ERef<MapStore>
.
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.
Good idea. Done.
fd324c8
to
8980a2d
Compare
Nit on reviewing process: While I understand this Another point specific to this PR: please include the main package name affected in the conventional commit prefix, e.g. I really want type improvements, but right now this PR is extremely difficult to review. |
I agree with the amendment policy for PRs marked R4R. I don't for PRs that aren't. This is something our eng org should agree on so let's discuss in standup.
I've opted against that because IIUC it creates redundant info in READMEs. e.g. the
I'm confused on why omission of package name makes it impossible to understand what's going on. If I had not separated the commits and instead, for example, squashed all the
All the changes since your review of the draft were addressing your comments. So maybe it suffices to look over your comments and see whether they were addressed to your satisfaction. |
8f791aa
to
8980a2d
Compare
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'd like to do another pass, but here is my review so far.
I still have some concerns regarding casts and type compatibility.
I believe the conversion of the vats
package to exported type introduces some mistaken re-exports, and could use a better layering for the main entrypoint.
/** @type {<T>(store: ERef<MapStore>, key: string, make: () => T) => Promise<T>} */ | ||
/** @type {<T>(store: ERef<Map<string, T>>, key: string, make: () => T) => Promise<T>} */ |
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.
provideWhen
seem to be used once with a ERef<MapStore>
and once with a Map
. The semantics of these Maps are different enough, which I believe does reflect on their signature. If provideWhen
is in fact using the common subset of these 2, can we be explicit about this somehow? Since TS is structural typing only, maybe by defining an interface of the subset used? That would avoid a cast / expect-error in the place where provideWhen
is used with the ERef<MapStore>
.
packages/vats/src/core/boot.js
Outdated
export * from '../nameHub.js'; | ||
export * from '../bridge.js'; |
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.
These see, to be re-exporting non-type exports as well, is that intended?
If so please visually separate them. In general I prefer having runtime imports before type based ones.
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 suspect the ./bridge.js
import is for the BridgeManager
type? In that case I believe the better approach would be to have a JSDoc @typedef
in ../types.js
and leverage the existing re-export from there. Same the nameHub.js
's NameHub
and NameAdmin
.
// @ts-expect-error cast | ||
E.get(homeP).scratch, |
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.
While I mentioned above I'd prefer to figure out the provideWhen
's param type to the compatible subtype, is there a reason to use // @ts-expect-error
for casts instead of doing an actual cast ?
Do we consider casting to be too visually onerous?
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.
Moot with 570cc99, but I do find casting in JSdoc to be hard to read. No strong feeling one way or the other.
@@ -50,7 +51,7 @@ export function buildRootObject(_vatPowers) { | |||
// Update the notifier when the chainBundle resolves. | |||
const { notifier, updater } = makeNotifierKit(); | |||
chainBundle.then(clientHome => { | |||
updater(harden({ clientHome, clientAddress: address })); | |||
updater.updateState(harden({ clientHome, clientAddress: address })); |
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.
Fix LGTM, but I should probably defer to someone more familiar with this code and notifiers. I believe this is the only behavioral difference in this PR.
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.
Yep, the only intended behavior change. updater
comes out of makeNotifierKit
which just has updateState
, fail
and finish
. The comment says "Update the notifier" so I'm sure it's not meant to be one of the latter two.
FYI @michaelfig who I think wrote this.
@@ -68,7 +68,7 @@ export const reserveThenDeposit = async ( | |||
console.info('confirmed deposit for', debugName); | |||
}; | |||
|
|||
/** @type {<T>(store: ERef<Map<string, T>>, key: string, make: () => T) => Promise<T>} */ | |||
/** @type {<T>(store: ERef<Pick<MapStore<string, T>, 'get' | 'set'>>, key: string, make: () => T) => Promise<T>} */ | |||
const provideWhen = async (store, key, make) => { | |||
const found = await E(store).get(key); |
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 probably should have looked at the actual implementation before.
I was under the impression that MapStore
threw when doing a get
of an uninitialized value. That's what I meant earlier by different semantics, unfortunately the signature of get
methods only varies on the return type between Map
and MapStore
, making them "compatible". Thinking more about this, it's probably a footgun that these objects have such a similar shape, yet such different semantics.
Anyway, I think provideWhen
is faulty because of this, and I don't understand how the fallback path would have every worked with the MapStore
variant.
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.
Agree with your assessment. Is there a requested change in there? An issue to make or a thread to start?
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 don't know. I don't think it'd be easy enough to fix now, but at least we should create an issue?
I think reverting the Pick
and adding a ts-expect-error
pointing to the issue?
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 made an issue but then realized a simpler fix: bf9b05f
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 fail to see how it's a fix?
@@ -351,7 +351,7 @@ export {}; | |||
* | |||
* @typedef { string } BundleID | |||
* @typedef {*} BundleCap | |||
* @typedef { { moduleFormat: 'endoZipBase64', endoZipBase64: string, endoZipBase64Sha512 } } EndoZipBase64Bundle | |||
* @typedef { { moduleFormat: 'endoZipBase64', endoZipBase64: string, endoZipBase64Sha512: string } } EndoZipBase64Bundle |
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.
Oh yeah that would do it!
export * from './src/nameHub.js'; | ||
export * from './src/bridge.js'; |
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 still believe this will cause formerly internal runtime helpers to now be visible on the root of this package, not only types.
See #6500 (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.
Thanks for the thorough review!
I suspect the ./bridge.js import is for the BridgeManager type? In that case I believe the better approach would be to have a JSDoc @typedef in ../types.js and leverage the existing re-export from there. Same the nameHub.js's NameHub and NameAdmin.
makeNameHubAdmin
is imported in three other modules, so arguably this top level is an improvement, but I'll err towards conservative and leave runtime exports refactoring for another day.
@@ -108,6 +110,7 @@ export const makeInstallCache = async ( | |||
}; | |||
|
|||
const wrapInstall = install => async (mPath, bPath, opts) => { | |||
// @ts-expect-error xxx bundle types |
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 believe runtime changes are worth when they directly address issues found by typing. improvements
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'm not sure I understand how the provideWhen
type change is actually a fix.
@@ -81,7 +81,7 @@ const provideWhen = async (store, key, make) => { | |||
|
|||
/** | |||
* | |||
* @param {{ scratch: ERef<MapStore<string, CopyTagged>> }} homeP | |||
* @param {{ scratch: ERef<Map<string, CopyTagged>> }} homeP |
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.
Does scratch
actually have Map
semantics? If not that'd be a very unobvious cast that hide the runtime differences.
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.
Yes, it does
agoric-sdk/packages/solo/src/scratch.js
Lines 17 to 20 in bf9b05f
get: async keyP => { | |
const key = await keyP; | |
return map.get(key); | |
}, |
@@ -68,7 +68,7 @@ export const reserveThenDeposit = async ( | |||
console.info('confirmed deposit for', debugName); | |||
}; | |||
|
|||
/** @type {<T>(store: ERef<Map<string, T>>, key: string, make: () => T) => Promise<T>} */ | |||
/** @type {<T>(store: ERef<Pick<MapStore<string, T>, 'get' | 'set'>>, key: string, make: () => T) => Promise<T>} */ | |||
const provideWhen = async (store, key, make) => { | |||
const found = await E(store).get(key); |
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 fail to see how it's a fix?
@@ -21,9 +21,11 @@ const DEFAULT_WALKER = Far('walker', { walk: pluginRootP => pluginRootP }); | |||
|
|||
/** | |||
* @template T | |||
* @typedef {T} Device | |||
* @typedef {'Device' & { __deviceType__: T }} Device |
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.
🎉
import '@agoric/swingset-vat/exported.js'; | ||
import '@agoric/swingset-vat/src/vats/network/types.js'; |
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.
Seeing this now, would it make sense to add import './src/vats/network/types.js';
to @agoric/swingset-vat/exported.js
instead ?
Edit: maybe not since it's used directly in other places?
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.
yeah, I considered that and opted for lesser change to what's exported. I figure it will be addressed in further ambients work
in the absence of a type signature, TypeScript infers an expected argument
bf9b05f
to
078263a
Compare
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
I would have like to see more coordination around getting rid of |
@dckc it's an easy change to reverse. You want every JS file in a package to have |
That would have been my preference, yes. At this point, I don't feel strongly that it's worth more churn in the git history. But it seems like a change to house style that's worth fairly widespread coordination. |
refs: #6343
Description
Emit types for agoric-sdk package builds like endojs/endo#1307
Security Considerations
n/a
Documentation Considerations
Export types are a documentation resource.
Testing Considerations
CI. And dry release?