diff --git a/packages/pass-style/.eslintignore b/packages/pass-style/.eslintignore new file mode 100644 index 0000000000..d76bcff161 --- /dev/null +++ b/packages/pass-style/.eslintignore @@ -0,0 +1,2 @@ +# typescript-eslint errors on this because it has no typecheck information, because tsc produced it for the `types.d.ts` instead +/src/types.js diff --git a/packages/pass-style/src/types.d.ts b/packages/pass-style/src/types.d.ts new file mode 100644 index 0000000000..ac5efb95c9 --- /dev/null +++ b/packages/pass-style/src/types.d.ts @@ -0,0 +1,163 @@ +/* eslint-disable no-use-before-define */ +import { PASS_STYLE } from './passStyle-helpers.js'; + +export type PrimitiveStyle = + | 'undefined' + | 'null' + | 'boolean' + | 'number' + | 'bigint' + | 'string' + | 'symbol'; +export type ContainerStyle = 'copyRecord' | 'copyArray' | 'tagged'; +export type PassStyle = + | PrimitiveStyle + | ContainerStyle + | 'remotable' + | 'error' + | 'promise'; +export type PassStyled = { + [PASS_STYLE]: S; +}; +export type ExtractStyle

> = P[typeof PASS_STYLE]; +export type PassByCopy = + | import('type-fest').Primitive + | Error + | any[] + | CopyRecord + | CopyTagged; +export type PassByRef = + | RemotableObject + | Promise + | Promise; +/** + * A Passable is acyclic data that can be marshalled. It must be hardened to + * remain + * stable (even if some components are proxies; see PureData restriction below), + * and is classified by PassStyle: + * * Atomic primitive values have a PrimitiveStyle (PassStyle + * 'undefined' | 'null' | 'boolean' | 'number' | 'bigint' + * | 'string' | 'symbol'). + * * Containers aggregate other Passables into + * * sequences as CopyArrays (PassStyle 'copyArray'), or + * * string-keyed dictionaries as CopyRecords (PassStyle 'copyRecord'), or + * * higher-level types as CopyTaggeds (PassStyle 'tagged'). + * * PassableCaps (PassStyle 'remotable' | 'promise') expose local values to + * remote interaction. + * * As a special case to support system observability, error objects are + * Passable (PassStyle 'error'). + * + * A Passable is essentially a pass-by-copy superstructure with a + * pass-by-reference + * exit point at the site of each PassableCap (which marshalling represents + * using 'slots'). + */ +export type Passable = PassByCopy | PassByRef; +export type PassStyleOf = { + (p: undefined): 'undefined'; + (p: string): 'string'; + (p: boolean): 'boolean'; + (p: number): 'number'; + (p: bigint): 'bigint'; + (p: symbol): 'symbol'; + (p: null): 'null'; + (p: Promise): 'promise'; + (p: Error): 'error'; + (p: CopyTagged): 'tagged'; + (p: any[]): 'copyArray'; + (p: Iterable): 'remotable'; + (p: Iterator): 'remotable'; + >(p: T): ExtractStyle; + (p: { [key: string]: any }): 'copyRecord'; + (p: any): PassStyle; +}; +/** + * A Passable is PureData when its entire data structure is free of PassableCaps + * (remotables and promises) and error objects. + * PureData is an arbitrary composition of primitive values into CopyArray + * and/or + * CopyRecord and/or CopyTagged containers (or a single primitive value with no + * container), and is fully pass-by-copy. + * + * This restriction assures absence of side effects and interleaving risks *given* + * that none of the containers can be a Proxy instance. + * TODO SECURITY BUG we plan to enforce this, giving PureData the same security + * properties as the proposed + * [Records and Tuples](https://github.com/tc39/proposal-record-tuple). + * + * Given this (currently counter-factual) assumption, a PureData value cannot + * be used as a communications channel, + * and can therefore be safely shared with subgraphs that should not be able + * to communicate with each other. + * Without that assumption, such a guarantee requires a marshal-unmarshal round + * trip (as exists between vats) to produce data structures disconnected from + * any potential proxies. + */ +export type PureData = Passable; +export type TaggedRecord = PassStyled & { + [Symbol.toStringTag]: I; +}; +/** + * An object marked as remotely accessible using the `Far` or `Remotable` + * functions, or a local presence representing such a remote object. + * + * A more natural name would be Remotable, but that could be confused with the + * value of the `Remotable` export of this module (a function). + */ +export type RemotableObject = TaggedRecord< + 'remotable', + I +>; +/** + * The authority-bearing leaves of a Passable's pass-by-copy superstructure. + */ +export type PassableCap = Promise | RemotableObject; +/** + * A Passable sequence of Passable values. + */ +export type CopyArray = T[]; +/** + * A Passable dictionary in which each key is a string and each value is Passable. + */ +export type CopyRecord = Record; +/** + * A Passable "tagged record" with semantics specific to the tag identified in + * the `[Symbol.toStringTag]` property (such as 'copySet', 'copyBag', + * or 'copyMap'). + * It must have a property with key equal to the `PASS_STYLE` export and + * value 'tagged' + * and no other properties except `[Symbol.toStringTag]` and `payload`. + */ +export type CopyTagged< + Tag extends string = string, + Payload extends Passable = any, +> = TaggedRecord<'tagged', Tag> & { + payload: Payload; +}; +/** + * This is an interface specification. + * For now, it is just a string, but will eventually be `PureData`. Either + * way, it must remain pure, so that it can be safely shared by subgraphs that + * are not supposed to be able to communicate. + */ +export type InterfaceSpec = string; +/** + * Internal to a useful pattern for writing checking logic + * (a "checkFoo" function) that can be used to implement a predicate + * (an "isFoo" function) or a validator (an "assertFoo" function). + * + * * A predicate ideally only returns `true` or `false` and rarely throws. + * * A validator throws an informative diagnostic when the predicate + * would have returned `false`, and simply returns `undefined` normally + * when the predicate would have returned `true`. + * * The internal checking function that they share is parameterized by a + * `Checker` that determines how to proceed with a failure condition. + * Predicates pass in an identity function as checker. Validators + * pass in `assertChecker` which is a trivial wrapper around `assert`. + * + * See the various uses for good examples. + */ +export type Checker = ( + cond: boolean, + details?: import('ses').Details | undefined, +) => boolean; diff --git a/packages/pass-style/src/types.js b/packages/pass-style/src/types.js index e77c3dd67d..407ac0b20e 100644 --- a/packages/pass-style/src/types.js +++ b/packages/pass-style/src/types.js @@ -1,189 +1,5 @@ -/* eslint-disable no-unused-vars */ +/** @file Empty twin for .d.ts */ +/* eslint-disable */ import { PASS_STYLE } from './passStyle-helpers.js'; export {}; - -/** - * @typedef { 'undefined' | 'null' | - * 'boolean' | 'number' | 'bigint' | 'string' | 'symbol' - * } PrimitiveStyle - */ - -/** @typedef {'copyRecord' | 'copyArray' | 'tagged'} ContainerStyle */ - -/** - * @typedef { PrimitiveStyle | - * ContainerStyle | - * 'remotable' | - * 'error' | 'promise' - * } PassStyle - */ - -/** - * @template {string} S style - * @typedef {{ [PASS_STYLE]: S }} PassStyled - */ - -/** - * @template {PassStyled} P - * @typedef {P[typeof PASS_STYLE]} ExtractStyle - */ - -/** @typedef {import('type-fest').Primitive | Error | CopyArray | CopyRecord | CopyTagged } PassByCopy */ -/** @typedef {RemotableObject | Promise | Promise } PassByRef */ - -/** - * @typedef {PassByCopy | PassByRef} Passable - * - * A Passable is acyclic data that can be marshalled. It must be hardened to - * remain - * stable (even if some components are proxies; see PureData restriction below), - * and is classified by PassStyle: - * * Atomic primitive values have a PrimitiveStyle (PassStyle - * 'undefined' | 'null' | 'boolean' | 'number' | 'bigint' - * | 'string' | 'symbol'). - * * Containers aggregate other Passables into - * * sequences as CopyArrays (PassStyle 'copyArray'), or - * * string-keyed dictionaries as CopyRecords (PassStyle 'copyRecord'), or - * * higher-level types as CopyTaggeds (PassStyle 'tagged'). - * * PassableCaps (PassStyle 'remotable' | 'promise') expose local values to - * remote interaction. - * * As a special case to support system observability, error objects are - * Passable (PassStyle 'error'). - * - * A Passable is essentially a pass-by-copy superstructure with a - * pass-by-reference - * exit point at the site of each PassableCap (which marshalling represents - * using 'slots'). - */ - -// Cases match in sequence. The final case 'remotable' is for a Passable that isn't one of the others. -/** - * @typedef {{ - * (p: undefined): 'undefined'; - * (p: string): 'string'; - * (p: boolean): 'boolean'; - * (p: number): 'number'; - * (p: bigint): 'bigint'; - * (p: symbol): 'symbol'; - * (p: null): 'null'; - * (p: Promise): 'promise'; - * (p: Error): 'error'; - * (p: CopyTagged): 'tagged'; - * (p: Array): 'copyArray'; - * (p: Iterable): 'remotable'; - * (p: Iterator): 'remotable'; - * >(p: T): ExtractStyle - * (p: {[key: string]: any}): 'copyRecord'; - * (p: RemotableObject): 'remotable'; - * (p: any): PassStyle; - * }} PassStyleOf - */ - -/** - * @typedef {Passable} PureData - * - * A Passable is PureData when its entire data structure is free of PassableCaps - * (remotables and promises) and error objects. - * PureData is an arbitrary composition of primitive values into CopyArray - * and/or - * CopyRecord and/or CopyTagged containers (or a single primitive value with no - * container), and is fully pass-by-copy. - * - * This restriction assures absence of side effects and interleaving risks *given* - * that none of the containers can be a Proxy instance. - * TODO SECURITY BUG we plan to enforce this, giving PureData the same security - * properties as the proposed - * [Records and Tuples](https://github.com/tc39/proposal-record-tuple). - * - * Given this (currently counter-factual) assumption, a PureData value cannot - * be used as a communications channel, - * and can therefore be safely shared with subgraphs that should not be able - * to communicate with each other. - * Without that assumption, such a guarantee requires a marshal-unmarshal round - * trip (as exists between vats) to produce data structures disconnected from - * any potential proxies. - */ - -/** - * @template {string} S pass style - * @template {InterfaceSpec} I interface tag - * @typedef {PassStyled & {[Symbol.toStringTag]: I}} TaggedRecord - */ - -/** - * @template {InterfaceSpec} [I=string] - * @typedef {TaggedRecord<'remotable', I>} RemotableObject - * - * An object marked as remotely accessible using the `Far` or `Remotable` - * functions, or a local presence representing such a remote object. - * - * A more natural name would be Remotable, but that could be confused with the - * value of the `Remotable` export of this module (a function). - */ - -/** - * @typedef {Promise | RemotableObject} PassableCap - * - * The authority-bearing leaves of a Passable's pass-by-copy superstructure. - */ - -// TODO a better default type. Ideally it's Passable, but that causes a circular definition. -/** - * @template {Passable} [T=object] - * @typedef {T[]} CopyArray - * - * A Passable sequence of Passable values. - */ - -// TODO a better default type. Ideally it's Passable, but that causes a circular definition. -/** - * @template {Passable} [T=object] - * @typedef {Record} CopyRecord - * - * A Passable dictionary in which each key is a string and each value is Passable. - */ - -/** - * @template {InterfaceSpec} [Tag=string] - * @template {Passable} [Payload=any] - * @typedef {TaggedRecord<'tagged', Tag> & { - * payload: Payload, - * }} CopyTagged - * - * A Passable "tagged record" with semantics specific to the tag identified in - * the `[Symbol.toStringTag]` property (such as 'copySet', 'copyBag', - * or 'copyMap'). - * It must have a property with key equal to the `PASS_STYLE` export and - * value 'tagged' - * and no other properties except `[Symbol.toStringTag]` and `payload`. - */ - -/** - * @typedef {string} InterfaceSpec - * This is an interface specification. - * For now, it is just a string, but will eventually be `PureData`. Either - * way, it must remain pure, so that it can be safely shared by subgraphs that - * are not supposed to be able to communicate. - */ - -/** - * @callback Checker - * Internal to a useful pattern for writing checking logic - * (a "checkFoo" function) that can be used to implement a predicate - * (an "isFoo" function) or a validator (an "assertFoo" function). - * - * * A predicate ideally only returns `true` or `false` and rarely throws. - * * A validator throws an informative diagnostic when the predicate - * would have returned `false`, and simply returns `undefined` normally - * when the predicate would have returned `true`. - * * The internal checking function that they share is parameterized by a - * `Checker` that determines how to proceed with a failure condition. - * Predicates pass in an identity function as checker. Validators - * pass in `assertChecker` which is a trivial wrapper around `assert`. - * - * See the various uses for good examples. - * @param {boolean} cond - * @param {import('ses').Details} [details] - * @returns {boolean} - */