diff --git a/packages/captp/src/captp.js b/packages/captp/src/captp.js
index 81be418cd3..419f7a5715 100644
--- a/packages/captp/src/captp.js
+++ b/packages/captp/src/captp.js
@@ -1,6 +1,9 @@
// @ts-check
///
+/** @template Slot @typedef {import('@endo/marshal').ConvertValToSlot} ConvertValToSlot */
+/** @template Slot @typedef {import('@endo/marshal').ConvertSlotToVal} ConvertSlotToVal */
+
// Your app may need to `import '@endo/eventual-send/shim.js'` to get HandledPromise
// This logic was mostly lifted from @agoric/swingset-vat liveSlots.js
diff --git a/packages/captp/src/types.js b/packages/captp/src/types.js
index e07cbe6221..65ee214ab5 100644
--- a/packages/captp/src/types.js
+++ b/packages/captp/src/types.js
@@ -17,7 +17,7 @@
*/
/**
- * @typedef {[boolean, CapData]} TrapCompletion The head of the pair
+ * @typedef {[boolean, import('@endo/marshal').CapData]} TrapCompletion The head of the pair
* is the `isRejected` value indicating whether the sync call was an exception,
* and tail of the pair is the serialized fulfillment value or rejection reason.
* (The fulfillment value is a non-thenable. The rejection reason is normally
diff --git a/packages/marshal/NEWS.md b/packages/marshal/NEWS.md
index 158f12a165..a8c8972ea6 100644
--- a/packages/marshal/NEWS.md
+++ b/packages/marshal/NEWS.md
@@ -1,5 +1,17 @@
User-visible changes in `@endo/marshal`:
+# Next release
+
+Switch from ambient to exported types.
+Include type declarations (`.d.ts`) generated from JSDoc to avoid requiring
+dependents to parse `.js` files in their `node_modules`.
+
+In order to use the types from `@endo/marshal` you now need to import them
+explicitly. For example, to make them available in scope, use the following:
+- JSDoc: `/** @typedef {import('@endo/marshal').PassStyle} PassStyle */`
+- TypeScript: `import type { PassStyle } from '@endo/marshal'`
+
+
# v0.5.3 (2021-01-27)
Includes TypeScript definitions in published artifact.
diff --git a/packages/marshal/exported.js b/packages/marshal/exported.js
deleted file mode 100644
index f4cba017ea..0000000000
--- a/packages/marshal/exported.js
+++ /dev/null
@@ -1 +0,0 @@
-import './src/types.js';
diff --git a/packages/marshal/index.js b/packages/marshal/index.js
index fcb824728a..8144bdad60 100644
--- a/packages/marshal/index.js
+++ b/packages/marshal/index.js
@@ -39,3 +39,6 @@ export {
isRecord,
isCopyArray,
} from './src/typeGuards.js';
+
+// eslint-disable-next-line import/export
+export * from './src/types.js';
diff --git a/packages/marshal/jsconfig.build.json b/packages/marshal/jsconfig.build.json
new file mode 100644
index 0000000000..13018509fd
--- /dev/null
+++ b/packages/marshal/jsconfig.build.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./jsconfig.json",
+ "compilerOptions": {
+ "noEmit": false,
+ "declaration": true,
+ "emitDeclarationOnly": true,
+ "declarationMap": true
+ },
+ "exclude": ["test/"]
+}
diff --git a/packages/marshal/package.json b/packages/marshal/package.json
index 016bda94f0..5d76329268 100644
--- a/packages/marshal/package.json
+++ b/packages/marshal/package.json
@@ -9,6 +9,9 @@
},
"scripts": {
"build": "exit 0",
+ "clean": "tsc --build jsconfig.build.json --clean",
+ "prepack": "tsc --build jsconfig.build.json",
+ "postpack": "yarn clean",
"test": "ava",
"test:c8": "c8 $C8_OPTIONS ava --config=ava-nesm.config.js",
"test:xs": "exit 0",
@@ -47,9 +50,11 @@
"files": [
"LICENSE*",
"SECURITY*",
+ "NEWS*",
"src",
"*.js",
- "*.ts"
+ "*.ts",
+ "*.map"
],
"eslintConfig": {
"extends": [
diff --git a/packages/marshal/src/deeplyFulfilled.js b/packages/marshal/src/deeplyFulfilled.js
index 3084cf31e8..dc2104e9aa 100644
--- a/packages/marshal/src/deeplyFulfilled.js
+++ b/packages/marshal/src/deeplyFulfilled.js
@@ -3,15 +3,14 @@
///
import { E } from '@endo/eventual-send';
-/**
- * @template T
- * @typedef {import('@endo/eventual-send').ERef} ERef
- */
import { isPromise } from '@endo/promise-kit';
import { getTag, isObject } from './helpers/passStyle-helpers.js';
import { makeTagged } from './makeTagged.js';
import { passStyleOf } from './passStyleOf.js';
+/** @typedef {import('./types.js').Passable} Passable */
+/** @template T @typedef {import('@endo/eventual-send').ERef} ERef */
+
const { details: X, quote: q } = assert;
const { ownKeys } = Reflect;
const { fromEntries } = Object;
diff --git a/packages/marshal/src/helpers/copyArray.js b/packages/marshal/src/helpers/copyArray.js
index f6ec450546..c492c292f4 100644
--- a/packages/marshal/src/helpers/copyArray.js
+++ b/packages/marshal/src/helpers/copyArray.js
@@ -2,8 +2,6 @@
///
-import '../types.js';
-import './internal-types.js';
import { assertChecker, checkNormalProperty } from './passStyle-helpers.js';
const { details: X } = assert;
@@ -13,7 +11,7 @@ const { isArray, prototype: arrayPrototype } = Array;
/**
*
- * @type {PassStyleHelper}
+ * @type {import('./internal-types.js').PassStyleHelper}
*/
export const CopyArrayHelper = harden({
styleName: 'copyArray',
diff --git a/packages/marshal/src/helpers/copyRecord.js b/packages/marshal/src/helpers/copyRecord.js
index 92dcf4b1bc..05c16e23e1 100644
--- a/packages/marshal/src/helpers/copyRecord.js
+++ b/packages/marshal/src/helpers/copyRecord.js
@@ -8,9 +8,6 @@ import {
checkNormalProperty,
} from './passStyle-helpers.js';
-import '../types.js';
-import './internal-types.js';
-
const { details: X } = assert;
const { ownKeys } = Reflect;
const {
@@ -21,7 +18,7 @@ const {
/**
*
- * @type {PassStyleHelper}
+ * @type {import('./internal-types.js').PassStyleHelper}
*/
export const CopyRecordHelper = harden({
styleName: 'copyRecord',
diff --git a/packages/marshal/src/helpers/error.js b/packages/marshal/src/helpers/error.js
index b6fa37315e..2f2d745a49 100644
--- a/packages/marshal/src/helpers/error.js
+++ b/packages/marshal/src/helpers/error.js
@@ -2,10 +2,10 @@
///
-import '../types.js';
-import './internal-types.js';
import { assertChecker } from './passStyle-helpers.js';
+/** @typedef {import('./internal-types.js').PassStyleHelper} PassStyleHelper */
+
const { details: X } = assert;
const { getPrototypeOf, getOwnPropertyDescriptors } = Object;
const { ownKeys } = Reflect;
diff --git a/packages/marshal/src/helpers/internal-types.js b/packages/marshal/src/helpers/internal-types.js
index c813fe8345..5338fa00f6 100644
--- a/packages/marshal/src/helpers/internal-types.js
+++ b/packages/marshal/src/helpers/internal-types.js
@@ -2,6 +2,12 @@
///
+export {};
+
+/** @typedef {import('../types.js').Checker} Checker */
+/** @typedef {import('../types.js').PassStyle} PassStyle */
+/** @typedef {import('../types.js').PassStyleOf} PassStyleOf */
+
/**
* The PassStyleHelper are only used to make a `passStyleOf` function.
* Thus, it should not depend on an ambient one. Rather, each helper should be
diff --git a/packages/marshal/src/helpers/passStyle-helpers.js b/packages/marshal/src/helpers/passStyle-helpers.js
index 5eaab77de4..37b3dbfcf3 100644
--- a/packages/marshal/src/helpers/passStyle-helpers.js
+++ b/packages/marshal/src/helpers/passStyle-helpers.js
@@ -2,8 +2,8 @@
///
-import '../types.js';
-import './internal-types.js';
+/** @typedef {import('../types.js').Checker} Checker */
+/** @typedef {import('../types.js').PassStyle} PassStyle */
const { details: X, quote: q } = assert;
const {
diff --git a/packages/marshal/src/helpers/remotable.js b/packages/marshal/src/helpers/remotable.js
index e563566999..a87a47340b 100644
--- a/packages/marshal/src/helpers/remotable.js
+++ b/packages/marshal/src/helpers/remotable.js
@@ -2,8 +2,6 @@
///
-import '../types.js';
-import './internal-types.js';
import {
assertChecker,
canBeMethod,
@@ -15,6 +13,12 @@ import {
} from './passStyle-helpers.js';
import { getEnvironmentOption } from './environment-options.js';
+/** @typedef {import('../types.js').Checker} Checker */
+/** @typedef {import('../types.js').InterfaceSpec} InterfaceSpec */
+/** @typedef {import('../types.js').MarshalGetInterfaceOf} MarshalGetInterfaceOf */
+/** @typedef {import('./internal-types.js').PassStyleHelper} PassStyleHelper */
+/** @typedef {import('../types.js').Remotable} Remotable */
+
const { details: X, quote: q } = assert;
const { ownKeys } = Reflect;
const { prototype: functionPrototype } = Function;
diff --git a/packages/marshal/src/helpers/tagged.js b/packages/marshal/src/helpers/tagged.js
index 6bfdf56c36..500db88fd3 100644
--- a/packages/marshal/src/helpers/tagged.js
+++ b/packages/marshal/src/helpers/tagged.js
@@ -9,16 +9,13 @@ import {
checkNormalProperty,
} from './passStyle-helpers.js';
-import '../types.js';
-import './internal-types.js';
-
const { details: X } = assert;
const { ownKeys } = Reflect;
const { getPrototypeOf, prototype: objectPrototype } = Object;
/**
*
- * @type {PassStyleHelper}
+ * @type {import('./internal-types.js').PassStyleHelper}
*/
export const TaggedHelper = harden({
styleName: 'tagged',
diff --git a/packages/marshal/src/make-far.js b/packages/marshal/src/make-far.js
index ab431a0a1a..697ac76b23 100644
--- a/packages/marshal/src/make-far.js
+++ b/packages/marshal/src/make-far.js
@@ -10,6 +10,8 @@ import {
} from './helpers/remotable.js';
import { pureCopy } from './pureCopy.js';
+/** @typedef {import('./types.js').InterfaceSpec} InterfaceSpec */
+
const { quote: q, details: X } = assert;
const { prototype: functionPrototype } = Function;
@@ -81,8 +83,8 @@ const assertCanBeRemotable = candidate =>
* Carol's `iface` as misrepresented by VatA.
* @param {undefined} [props=undefined] Currently may only be undefined.
* That plan is that own-properties are copied to the remotable
- * @param {object} [remotable={}] The object used as the remotable
- * @returns {object} remotable, modified for debuggability
+ * @param {any} [remotable={}] The object used as the remotable
+ * @returns {any} remotable, modified for debuggability
*/
export const Remotable = (
iface = 'Remotable',
diff --git a/packages/marshal/src/marshal-justin.js b/packages/marshal/src/marshal-justin.js
index c9f47c0092..f0006518a3 100644
--- a/packages/marshal/src/marshal-justin.js
+++ b/packages/marshal/src/marshal-justin.js
@@ -5,11 +5,12 @@
import { Nat } from '@endo/nat';
import { QCLASS } from './marshal.js';
-import './types.js';
import { getErrorConstructor } from './helpers/error.js';
import { isObject } from './helpers/passStyle-helpers.js';
import { AtAtPrefixPattern, passableSymbolForName } from './helpers/symbol.js';
+/** @typedef {import('./types.js').Encoding} Encoding */
+
const { ownKeys } = Reflect;
const { isArray } = Array;
const { stringify: quote } = JSON;
diff --git a/packages/marshal/src/marshal-stringify.js b/packages/marshal/src/marshal-stringify.js
index d54c655629..d657c14126 100644
--- a/packages/marshal/src/marshal-stringify.js
+++ b/packages/marshal/src/marshal-stringify.js
@@ -3,15 +3,15 @@
import { makeMarshal } from './marshal.js';
-import './types.js';
+/** @typedef {import('./types.js').OnlyData} OnlyData */
const { details: X } = assert;
-/** @type {ConvertValToSlot} */
+/** @type {import('./types.js').ConvertValToSlot} */
const doNotConvertValToSlot = val =>
assert.fail(X`Marshal's stringify rejects presences and promises ${val}`);
-/** @type {ConvertSlotToVal} */
+/** @type {import('./types.js').ConvertSlotToVal} */
const doNotConvertSlotToVal = (slot, _iface) =>
assert.fail(X`Marshal's parse must not encode any slots ${slot}`);
diff --git a/packages/marshal/src/marshal.js b/packages/marshal/src/marshal.js
index de832b840b..53d80019b3 100644
--- a/packages/marshal/src/marshal.js
+++ b/packages/marshal/src/marshal.js
@@ -5,7 +5,6 @@
import { Nat } from '@endo/nat';
import { assertPassable, passStyleOf } from './passStyleOf.js';
-import './types.js';
import { getInterfaceOf } from './helpers/remotable.js';
import { ErrorHelper, getErrorConstructor } from './helpers/error.js';
import { makeTagged } from './makeTagged.js';
@@ -16,6 +15,15 @@ import {
passableSymbolForName,
} from './helpers/symbol.js';
+/** @typedef {import('./types.js').MakeMarshalOptions} MakeMarshalOptions */
+/** @template Slot @typedef {import('./types.js').ConvertSlotToVal} ConvertSlotToVal */
+/** @template Slot @typedef {import('./types.js').ConvertValToSlot} ConvertValToSlot */
+/** @template Slot @typedef {import('./types.js').Serialize} Serialize */
+/** @template Slot @typedef {import('./types.js').Unserialize} Unserialize */
+/** @typedef {import('./types.js').Passable} Passable */
+/** @typedef {import('./types.js').InterfaceSpec} InterfaceSpec */
+/** @typedef {import('./types.js').Encoding} Encoding */
+
const { ownKeys } = Reflect;
const { isArray } = Array;
const {
@@ -34,14 +42,18 @@ const { details: X, quote: q } = assert;
const QCLASS = '@qclass';
export { QCLASS };
+/** @type {ConvertValToSlot} */
const defaultValToSlotFn = x => x;
+/** @type {ConvertSlotToVal} */
const defaultSlotToValFn = (x, _) => x;
/**
* @template Slot
- * @type {MakeMarshal}
+ * @param {ConvertValToSlot} [convertValToSlot]
+ * @param {ConvertSlotToVal} [convertSlotToVal]
+ * @param {MakeMarshalOptions} [options]
*/
-export function makeMarshal(
+export const makeMarshal = (
convertValToSlot = defaultValToSlotFn,
convertSlotToVal = defaultSlotToValFn,
{
@@ -55,7 +67,7 @@ export function makeMarshal(
marshalSaveError = err =>
console.log('Temporary logging of sent error', err),
} = {},
-) {
+) => {
assert.typeof(marshalName, 'string');
assert(
errorTagging === 'on' || errorTagging === 'off',
@@ -67,7 +79,6 @@ export function makeMarshal(
};
/**
- * @template Slot
* @type {Serialize}
*/
const serialize = root => {
@@ -482,7 +493,6 @@ export function makeMarshal(
};
/**
- * @template Slot
* @type {Unserialize}
*/
const unserialize = data => {
@@ -507,4 +517,4 @@ export function makeMarshal(
serialize,
unserialize,
});
-}
+};
diff --git a/packages/marshal/src/passStyleOf.js b/packages/marshal/src/passStyleOf.js
index 6d9d7917c8..ec5cbea1fb 100644
--- a/packages/marshal/src/passStyleOf.js
+++ b/packages/marshal/src/passStyleOf.js
@@ -11,10 +11,14 @@ import { TaggedHelper } from './helpers/tagged.js';
import { RemotableHelper } from './helpers/remotable.js';
import { ErrorHelper } from './helpers/error.js';
-import './types.js';
-import './helpers/internal-types.js';
import { assertPassableSymbol } from './helpers/symbol.js';
+/** @typedef {import('./helpers/internal-types.js').PassStyleHelper} PassStyleHelper */
+/** @typedef {import('./types.js').Passable} Passable */
+/** @typedef {import('./types.js').PassStyle} PassStyle */
+/** @typedef {import('./types.js').PassStyleOf} PassStyleOf */
+/** @typedef {import('./types.js').PrimitiveStyle} PrimitiveStyle */
+
/** @typedef {Exclude} HelperPassStyle */
const { details: X, quote: q } = assert;
diff --git a/packages/marshal/src/pureCopy.js b/packages/marshal/src/pureCopy.js
index 323d7861cb..0579ef6ce3 100644
--- a/packages/marshal/src/pureCopy.js
+++ b/packages/marshal/src/pureCopy.js
@@ -4,6 +4,9 @@ import { getTag } from './helpers/passStyle-helpers.js';
import { makeTagged } from './makeTagged.js';
import { passStyleOf } from './passStyleOf.js';
+/** @typedef {import('./types.js').OnlyData} OnlyData */
+/** @typedef {import('./types.js').CopyTagged} CopyTagged */
+
const { is } = Object;
const { details: X, quote: q } = assert;
diff --git a/packages/marshal/src/typeGuards.js b/packages/marshal/src/typeGuards.js
index c789689b15..a41b079187 100644
--- a/packages/marshal/src/typeGuards.js
+++ b/packages/marshal/src/typeGuards.js
@@ -2,6 +2,11 @@
import { passStyleOf } from './passStyleOf.js';
+/** @typedef {import('./types.js').Passable} Passable */
+/** @template T @typedef {import('./types.js').CopyArray} CopyArray */
+/** @template T @typedef {import('./types.js').CopyRecord} CopyRecord */
+/** @typedef {import('./types.js').Remotable} Remotable */
+
const { details: X, quote: q } = assert;
/**
diff --git a/packages/marshal/src/types.js b/packages/marshal/src/types.js
index 084887df73..b777892c67 100644
--- a/packages/marshal/src/types.js
+++ b/packages/marshal/src/types.js
@@ -3,6 +3,8 @@
///
+export {};
+
/**
* @typedef { "undefined" | "null" |
* "boolean" | "number" | "bigint" | "string" | "symbol"
@@ -180,7 +182,7 @@
/**
* @template Slot
- * @typedef CapData
+ * @typedef {Object} CapData
* @property {string} body A JSON.stringify of an Encoding
* @property {Slot[]} slots
*/
@@ -201,22 +203,13 @@
/**
* @template Slot
- * @typedef Marshal
+ * @typedef {Object} Marshal
* @property {Serialize} serialize
* @property {Unserialize} unserialize
*/
/**
- * @template Slot
- * @callback MakeMarshal
- * @param {ConvertValToSlot=} convertValToSlot
- * @param {ConvertSlotToVal=} convertSlotToVal
- * @param {MakeMarshalOptions=} options
- * @returns {Marshal}
- */
-
-/**
- * @typedef MakeMarshalOptions
+ * @typedef {Object} MakeMarshalOptions
* @property {'on'|'off'=} errorTagging controls whether serialized errors
* also carry tagging information, made from `marshalName` and numbers
* generated (currently by counting) starting at `errorIdNum`. The
diff --git a/packages/marshal/test/test-marshal-far-obj.js b/packages/marshal/test/test-marshal-far-obj.js
index 0a9f9fe87f..6d7ecefaef 100644
--- a/packages/marshal/test/test-marshal-far-obj.js
+++ b/packages/marshal/test/test-marshal-far-obj.js
@@ -59,9 +59,10 @@ test('Remotable/getInterfaceOf', t => {
t.is(p2.birthYear(2020), 1956, `birthYear() works`);
// Remotables and Fars can be serialized, of course
- function convertValToSlot(_val) {
+ /** @type {import('../src/types.js').ConvertValToSlot} */
+ const convertValToSlot = _val => {
return 'slot';
- }
+ };
const m = makeMarshal(convertValToSlot);
t.deepEqual(m.serialize(p2), {
body: JSON.stringify({