diff --git a/packages/common/NEWS.md b/packages/common/NEWS.md index fd9f706c3c..aac7ee8c7b 100644 --- a/packages/common/NEWS.md +++ b/packages/common/NEWS.md @@ -1,5 +1,9 @@ User-visible changes in `@endo/common`: +# Next release + +- TODO explain further generalization of `throwLabeled` + # v1.1.0 (2024-02-22) - `throwLabeled` parameterized error construction diff --git a/packages/errors/NEWS.md b/packages/errors/NEWS.md index 99c636c9da..6a1c99e21a 100644 --- a/packages/errors/NEWS.md +++ b/packages/errors/NEWS.md @@ -1,5 +1,10 @@ User-visible changes in `@endo/errors`: +# Next release + +- TODO explain `SuppressedError` support + + # v1.1.0 (2024-02-22) - `AggegateError` support diff --git a/packages/eslint-plugin/lib/configs/recommended.js b/packages/eslint-plugin/lib/configs/recommended.js index 6e0da94c8a..2c0acd2a1c 100644 --- a/packages/eslint-plugin/lib/configs/recommended.js +++ b/packages/eslint-plugin/lib/configs/recommended.js @@ -63,6 +63,8 @@ module.exports = { HandledPromise: 'readonly', // https://github.com/endojs/endo/issues/550 AggregateError: 'readonly', + // https://github.com/tc39/proposal-explicit-resource-management + SuppressedError: 'readonly', }, rules: { '@endo/assert-fail-as-throw': 'error', diff --git a/packages/marshal/NEWS.md b/packages/marshal/NEWS.md index 7731cd163c..16c0327a8a 100644 --- a/packages/marshal/NEWS.md +++ b/packages/marshal/NEWS.md @@ -1,5 +1,9 @@ User-visible changes in `@endo/marshal`: +# Next release + +TODO explain `SuppressedError` support + # v1.6.0 (2024-10-22) - `compareRank` now short-circuits upon encountering remotables to compare, diff --git a/packages/marshal/src/marshal-justin.js b/packages/marshal/src/marshal-justin.js index 681c14e962..0a6c3da8ab 100644 --- a/packages/marshal/src/marshal-justin.js +++ b/packages/marshal/src/marshal-justin.js @@ -399,8 +399,10 @@ const decodeToJustin = (encoding, shouldIndent = false, slots = []) => { Fail`error cause not yet implemented in marshal-justin`; name !== `AggregateError` || Fail`AggregateError not yet implemented in marshal-justin`; + // TODO SuppressedError errors === undefined || Fail`error errors not yet implemented in marshal-justin`; + // TODO error,suppressed return out.next(`${name}(${quote(message)})`); } diff --git a/packages/marshal/src/marshal.js b/packages/marshal/src/marshal.js index 86ad6891ac..fe67a69f45 100644 --- a/packages/marshal/src/marshal.js +++ b/packages/marshal/src/marshal.js @@ -112,7 +112,7 @@ export const makeMarshal = ( assert.typeof(message, 'string'); const name = encodeRecur(`${err.name}`); assert.typeof(name, 'string'); - // TODO Must encode `cause`, `errors`, but + // TODO Must encode `cause`,`errors`,`error`,`suppressed` but // only once all possible counterparty decoders are tolerant of // receiving them. if (errorTagging === 'on') { @@ -262,8 +262,10 @@ export const makeMarshal = ( * errorId?: string, * message: string, * name: string, - * cause: unknown, - * errors: unknown, + * cause?: unknown, + * errors?: unknown, + * error?: unknown, + * suppressed?: unknown, * }} errData * @param {(e: unknown) => Passable} decodeRecur * @returns {Error} @@ -275,6 +277,8 @@ export const makeMarshal = ( name, cause = undefined, errors = undefined, + error = undefined, + suppressed = undefined, ...rest } = errData; // See https://github.com/endojs/endo/pull/2052 @@ -306,6 +310,12 @@ export const makeMarshal = ( if (errors) { options.errors = decodeRecur(errors); } + if (error) { + options.error = decodeRecur(error); + } + if (suppressed) { + options.suppressed = decodeRecur(suppressed); + } const rawError = makeError(dMessage, errConstructor, options); // Note that this does not decodeRecur rest's property names. // This would be inconsistent with smallcaps' expected handling, diff --git a/packages/marshal/src/types.js b/packages/marshal/src/types.js index 8f73c6c936..58bbb7a0f3 100644 --- a/packages/marshal/src/types.js +++ b/packages/marshal/src/types.js @@ -38,6 +38,8 @@ export {}; * errorId?: string, * cause?: Encoding, * errors?: Encoding[], + * error?: Encoding, + * suppressed?: Encoding, * } | * EncodingClass<'slot'> & { index: number, * iface?: string diff --git a/packages/marshal/test/marshal-capdata.test.js b/packages/marshal/test/marshal-capdata.test.js index 90e7d10819..20dfaa7916 100644 --- a/packages/marshal/test/marshal-capdata.test.js +++ b/packages/marshal/test/marshal-capdata.test.js @@ -219,6 +219,8 @@ testIfAggregateError('unserialize errors w recognized extensions', t => { t.is(getPrototypeOf(unkErr.errors[0]), URIError.prototype); }); +// TODO SuppressedError + test('passStyleOf null is "null"', t => { t.assert(passStyleOf(null), 'null'); }); diff --git a/packages/marshal/test/marshal-smallcaps.test.js b/packages/marshal/test/marshal-smallcaps.test.js index 87ed067ba3..4a778e7bb4 100644 --- a/packages/marshal/test/marshal-smallcaps.test.js +++ b/packages/marshal/test/marshal-smallcaps.test.js @@ -226,6 +226,8 @@ test('smallcaps unserialize errors w recognized extensions', t => { t.is(getPrototypeOf(refErr.errors[0]), URIError.prototype); }); +// TODO SuppressedError + test('smallcaps mal-formed @qclass', t => { const { unserialize } = makeTestMarshal(); const uns = body => unserialize({ body, slots: [] }); diff --git a/packages/pass-style/NEWS.md b/packages/pass-style/NEWS.md index cea0c9aae4..7ff9590bdd 100644 --- a/packages/pass-style/NEWS.md +++ b/packages/pass-style/NEWS.md @@ -1,5 +1,9 @@ User-visible changes in `@endo/pass-style`: +# Next release + +TODO explain `SuppressedError` support + # v1.4.1 (2024-07-30) - `deeplyFulfilled` moved from @endo/marshal to @endo/pass-style. @endo/marshal still reexports it, to avoid breaking old importers. But importers should be upgraded to import `deeplyFulfilled` directly from @endo/pass-style. diff --git a/packages/pass-style/src/error.js b/packages/pass-style/src/error.js index 83fecc90ac..969f256bcf 100644 --- a/packages/pass-style/src/error.js +++ b/packages/pass-style/src/error.js @@ -27,6 +27,8 @@ const errorConstructors = new Map( // To accommodate platforms prior to AggregateError, we comment out the // following line and instead conditionally add it to the map below. // ['AggregateError', AggregateError], + // Likewise https://github.com/tc39/proposal-explicit-resource-management + // ['SuppressedError', SuppressedError], ]), ); @@ -35,9 +37,15 @@ if (typeof AggregateError !== 'undefined') { errorConstructors.set('AggregateError', AggregateError); } +if (typeof SuppressedError !== 'undefined') { + // Conditional, to accommodate platforms prior to SuppressedError + errorConstructors.set('SuppressedError', SuppressedError); +} + /** * Because the error constructor returned by this function might be - * `AggregateError`, which has different construction parameters + * `AggregateError` or `SuppressedError`, + * each of which has different construction parameters * from the other error constructors, do not use it directly to try * to make an error instance. Rather, use `makeError` which encapsulates * this non-uniformity. @@ -125,7 +133,9 @@ export const checkRecursivelyPassableErrorPropertyDesc = ( )} own property must be a string: ${value}`) ); } - case 'cause': { + case 'cause': + case 'error': + case 'suppressed': { // eslint-disable-next-line no-use-before-define return checkRecursivelyPassableError(value, passStyleOfRecur, check); } diff --git a/packages/pass-style/src/passStyleOf.js b/packages/pass-style/src/passStyleOf.js index 7c4dd78b2e..ae9eb40152 100644 --- a/packages/pass-style/src/passStyleOf.js +++ b/packages/pass-style/src/passStyleOf.js @@ -281,22 +281,39 @@ export const toPassableError = err => { return err; } const { name, message } = err; - const { cause: causeDesc, errors: errorsDesc } = - getOwnPropertyDescriptors(err); + const { + cause: causeDesc, + errors: errorsDesc, + error: errorDesc, + suppressed: suppressedDesc, + } = getOwnPropertyDescriptors(err); let cause; let errors; + let error; + let suppressed; if (causeDesc && isPassableErrorPropertyDesc('cause', causeDesc)) { cause = causeDesc.value; } if (errorsDesc && isPassableErrorPropertyDesc('errors', errorsDesc)) { errors = errorsDesc.value; } + if (errorDesc && isPassableErrorPropertyDesc('error', errorDesc)) { + error = errorDesc.value; + } + if ( + suppressedDesc && + isPassableErrorPropertyDesc('suppressed', suppressedDesc) + ) { + suppressed = suppressedDesc.value; + } const errConstructor = getErrorConstructor(`${name}`) || Error; const newError = makeError(`${message}`, errConstructor, { // @ts-ignore Assuming cause is Error | undefined cause, errors, + error, + suppressed, }); // Still needed, because `makeError` only does a shallow freeze. harden(newError); diff --git a/packages/pass-style/test/errors.test.js b/packages/pass-style/test/errors.test.js index 2c0fd0fcce..c94e3a67b6 100644 --- a/packages/pass-style/test/errors.test.js +++ b/packages/pass-style/test/errors.test.js @@ -32,6 +32,11 @@ test('style of extended errors', t => { const a4 = harden(AggregateError([e2, u3], 'a4', { cause: e1 })); t.is(passStyleOf(a4), 'error'); } + if (typeof SuppressedError !== 'undefined') { + // Conditional, to accommodate platforms prior to SuppressedError + const a4 = harden(SuppressedError(e2, u3, 'a4')); + t.is(passStyleOf(a4), 'error'); + } }); test('toPassableError, toThrowable', t => { diff --git a/packages/ses/NEWS.md b/packages/ses/NEWS.md index 4bb7cba98e..6eee9c3d24 100644 --- a/packages/ses/NEWS.md +++ b/packages/ses/NEWS.md @@ -25,6 +25,8 @@ User-visible changes in `ses`: # v1.9.0 (2024-10-10) +- TODO explain `SuppressedError` support + - On platforms without [`Array.prototype.transfer`](https://github.com/tc39/proposal-resizablearraybuffer) but with a global `structuredClone`, the ses-shim's `lockdown` will now diff --git a/packages/ses/src/commons.js b/packages/ses/src/commons.js index b4f8f6f391..1ce95b9dcf 100644 --- a/packages/ses/src/commons.js +++ b/packages/ses/src/commons.js @@ -50,6 +50,7 @@ export const { SyntaxError, TypeError, AggregateError, + SuppressedError, } = globalThis; export const { diff --git a/packages/ses/src/enablements.js b/packages/ses/src/enablements.js index bb352b3cb5..79a0a5cae7 100644 --- a/packages/ses/src/enablements.js +++ b/packages/ses/src/enablements.js @@ -155,6 +155,12 @@ export const moderateEnablements = { name: true, // set by "node 14"? }, + // https://github.com/tc39/proposal-explicit-resource-management + '%SuppressedErrorPrototype%': { + message: true, // to match TypeErrorPrototype.message + name: true, // set by some Node? + }, + '%PromisePrototype%': { constructor: true, // set by "core-js" }, diff --git a/packages/ses/src/error/assert.js b/packages/ses/src/error/assert.js index f4a645c5b0..46a426624c 100644 --- a/packages/ses/src/error/assert.js +++ b/packages/ses/src/error/assert.js @@ -35,6 +35,7 @@ import { weakmapHas, weakmapSet, AggregateError, + SuppressedError, getOwnPropertyDescriptors, ownKeys, create, @@ -269,12 +270,13 @@ const tagError = (err, optErrorName = err.name) => { * such as `stack` on v8 (Chrome, Brave, Edge?) * - `sanitizeError` will freeze the error, preventing any correct engine from * adding or - * altering any of the error's own properties `sanitizeError` is done. + * altering any of the error's own properties after `sanitizeError` is done. * * However, `sanitizeError` will not, for example, `harden` * (i.e., deeply freeze) - * or ensure that the `cause` or `errors` property satisfy the `Passable` - * constraints. The purpose of `sanitizeError` is only to protect against + * or ensure that the `cause`, `errors`, `error`, or `suppressed` properties + * satisfy the `Passable` constraints. + * The purpose of `sanitizeError` is only to protect against * mischief the host may have already added to the error as created, * not to ensure that the error is actually Passable. For that, * see `toPassableError` in `@endo/pass-style`. @@ -286,8 +288,10 @@ export const sanitizeError = error => { const { name: _nameDesc, message: _messageDesc, - errors: _errorsDesc = undefined, cause: _causeDesc = undefined, + errors: _errorsDesc = undefined, + error: _errorDesc = undefined, + suppressed: _suppressedDesc = undefined, stack: _stackDesc = undefined, ...restDescs } = descs; @@ -317,6 +321,8 @@ export const sanitizeError = error => { }; /** + * TODO rewrite to be more general + * * @type {AssertionUtilities['error']} */ const makeError = ( @@ -326,6 +332,8 @@ const makeError = ( errorName = undefined, cause = undefined, errors = undefined, + error = undefined, + suppressed = undefined, sanitize = true, } = {}, ) => { @@ -340,37 +348,41 @@ const makeError = ( } const messageString = getMessageString(hiddenDetails); const opts = cause && { cause }; - let error; + let err; if ( typeof AggregateError !== 'undefined' && errConstructor === AggregateError ) { - error = AggregateError(errors || [], messageString, opts); + err = AggregateError(errors || [], messageString, opts); + } else if ( + typeof SuppressedError !== 'undefined' && + errConstructor === SuppressedError + ) { + err = SuppressedError(error, suppressed, messageString); + // TODO what about errors, cause? } else { - error = /** @type {ErrorConstructor} */ (errConstructor)( - messageString, - opts, - ); + err = /** @type {ErrorConstructor} */ (errConstructor)(messageString, opts); if (errors !== undefined) { // Since we need to tolerate `errors` on an AggregateError, may as // well tolerate it on all errors. - defineProperty(error, 'errors', { + defineProperty(err, 'errors', { value: errors, writable: true, enumerable: false, configurable: true, }); } + // TODO similarly tolerate error,suppressed ? } - weakmapSet(hiddenMessageLogArgs, error, getLogArgs(hiddenDetails)); + weakmapSet(hiddenMessageLogArgs, err, getLogArgs(hiddenDetails)); if (errorName !== undefined) { - tagError(error, errorName); + tagError(err, errorName); } if (sanitize) { - sanitizeError(error); + sanitizeError(err); } // The next line is a particularly fruitful place to put a breakpoint. - return error; + return err; }; freeze(makeError); diff --git a/packages/ses/src/error/console.js b/packages/ses/src/error/console.js index 5151a609a3..bf903799da 100644 --- a/packages/ses/src/error/console.js +++ b/packages/ses/src/error/console.js @@ -210,6 +210,8 @@ const ErrorInfo = { MESSAGE: 'ERROR_MESSAGE:', CAUSE: 'cause:', ERRORS: 'errors:', + ERROR: 'error:', + SUPPRESSED: 'suppressed:', }; freeze(ErrorInfo); @@ -359,6 +361,7 @@ export const makeCausalConsole = (baseConsole, loggedErrorHandler) => { // @ts-expect-error AggregateError has an `errors` property. logErrorInfo(severity, error, ErrorInfo.ERRORS, error.errors, subErrors); } + // TODO SuppressedError for (const noteLogArgs of noteLogArgsArray) { logErrorInfo(severity, error, ErrorInfo.NOTE, noteLogArgs, subErrors); } diff --git a/packages/ses/src/error/internal-types.js b/packages/ses/src/error/internal-types.js index 214ff08ab3..d0fb3c34b2 100644 --- a/packages/ses/src/error/internal-types.js +++ b/packages/ses/src/error/internal-types.js @@ -78,6 +78,8 @@ * MESSAGE: 'ERROR_MESSAGE:', * CAUSE: 'cause:', * ERRORS: 'errors:', + * ERROR: 'error:', + * SUPPRESSED: 'suppressed:', * }} ErrorInfo */ diff --git a/packages/ses/src/permits.js b/packages/ses/src/permits.js index c5745903ee..bd98137582 100644 --- a/packages/ses/src/permits.js +++ b/packages/ses/src/permits.js @@ -89,6 +89,13 @@ export const universalPropertyNames = { // https://github.com/endojs/endo/issues/550 AggregateError: 'AggregateError', + // https://github.com/tc39/proposal-explicit-resource-management + AsyncDisposableStack: 'AsyncDisposableStack', + // https://github.com/tc39/proposal-explicit-resource-management + DisposableStack: 'DisposableStack', + // https://github.com/tc39/proposal-explicit-resource-management + SuppressedError: 'SuppressedError', + // *** Other Properties of the Global Object JSON: 'JSON', @@ -207,6 +214,8 @@ const NativeErrors = [ // Commented out to accommodate platforms prior to AggregateError. // Instead, conditional push below. // AggregateError, + // Likewise https://github.com/tc39/proposal-explicit-resource-management + // SuppressedError, ]; if (typeof AggregateError !== 'undefined') { @@ -214,6 +223,11 @@ if (typeof AggregateError !== 'undefined') { arrayPush(NativeErrors, AggregateError); } +if (typeof SuppressedError !== 'undefined') { + // Conditional, to accommodate platforms prior to SuppressedError + arrayPush(NativeErrors, SuppressedError); +} + export { NativeErrors }; /** @@ -537,8 +551,10 @@ export const permitted = { '%SharedSymbol%': { // Properties of the Symbol Constructor '[[Proto]]': '%FunctionPrototype%', + // https://github.com/tc39/proposal-explicit-resource-management asyncDispose: 'symbol', asyncIterator: 'symbol', + // https://github.com/tc39/proposal-explicit-resource-management dispose: 'symbol', for: fn, hasInstance: 'symbol', @@ -622,6 +638,8 @@ export const permitted = { URIError: NativeError('%URIErrorPrototype%'), // https://github.com/endojs/endo/issues/550 AggregateError: NativeError('%AggregateErrorPrototype%'), + // https://github.com/tc39/proposal-explicit-resource-management + SuppressedError: NativeError('%SuppressedErrorPrototype%'), '%EvalErrorPrototype%': NativeErrorPrototype('EvalError'), '%RangeErrorPrototype%': NativeErrorPrototype('RangeError'), @@ -631,6 +649,8 @@ export const permitted = { '%URIErrorPrototype%': NativeErrorPrototype('URIError'), // https://github.com/endojs/endo/issues/550 '%AggregateErrorPrototype%': NativeErrorPrototype('AggregateError'), + // https://github.com/tc39/proposal-explicit-resource-management + '%SuppressedErrorPrototype%': NativeErrorPrototype('SuppressedError'), // *** Numbers and Dates @@ -1293,6 +1313,44 @@ export const permitted = { SharedArrayBuffer: false, // UNSAFE and purposely suppressed. '%SharedArrayBufferPrototype%': false, // UNSAFE and purposely suppressed. + // https://github.com/tc39/proposal-explicit-resource-management + DisposableStack: { + '[[Proto]]': '%FunctionPrototype%', + prototype: '%DisposableStackPrototype%', + }, + + // https://github.com/tc39/proposal-explicit-resource-management + '%DisposableStackPrototype%': { + constructor: 'DisposableStack', + adopt: fn, + defer: fn, + dispose: fn, + disposed: getter, + move: fn, + use: fn, + '@@dispose': fn, + '@@toStringTag': 'string', + }, + + // https://github.com/tc39/proposal-explicit-resource-management + AsyncDisposableStack: { + '[[Proto]]': '%FunctionPrototype%', + prototype: '%AsyncDisposableStackPrototype%', + }, + + // https://github.com/tc39/proposal-explicit-resource-management + '%AsyncDisposableStackPrototype%': { + constructor: 'AsyncDisposableStack', + adopt: fn, + defer: fn, + disposeAsync: fn, + disposed: getter, + move: fn, + use: fn, + '@@asyncDispose': fn, + '@@toStringTag': 'string', + }, + DataView: { // Properties of the DataView Constructor '[[Proto]]': '%FunctionPrototype%', @@ -1373,8 +1431,8 @@ export const permitted = { '@@toStringTag': 'string', // https://github.com/tc39/proposal-async-iterator-helpers toAsync: fn, - // See https://github.com/Moddable-OpenSource/moddable/issues/523#issuecomment-1942904505 - '@@dispose': false, + // https://github.com/tc39/proposal-explicit-resource-management + '@@dispose': fn, }, // https://github.com/tc39/proposal-iterator-helpers @@ -1417,8 +1475,8 @@ export const permitted = { every: fn, find: fn, '@@toStringTag': 'string', - // See https://github.com/Moddable-OpenSource/moddable/issues/523#issuecomment-1942904505 - '@@asyncDispose': false, + // https://github.com/tc39/proposal-explicit-resource-management + '@@asyncDispose': fn, }, // https://github.com/tc39/proposal-async-iterator-helpers diff --git a/packages/ses/test/error/aggregate-error-console-demo.test.js b/packages/ses/test/error/aggregate-error-console-demo.test.js index ba2da6ae16..05a3dd4468 100644 --- a/packages/ses/test/error/aggregate-error-console-demo.test.js +++ b/packages/ses/test/error/aggregate-error-console-demo.test.js @@ -22,3 +22,5 @@ test('aggregate error console demo', t => { console.log('log1', a1); t.is(a1.cause, e2); }); + +// TODO SuppressedError diff --git a/packages/ses/test/error/aggregate-error-console.test.js b/packages/ses/test/error/aggregate-error-console.test.js index e9981c4fe9..4d44be44ec 100644 --- a/packages/ses/test/error/aggregate-error-console.test.js +++ b/packages/ses/test/error/aggregate-error-console.test.js @@ -46,3 +46,5 @@ test('aggregate error console', t => { { wrapWithCausal: true }, ); }); + +// TODO SuppressedError diff --git a/packages/ses/test/error/aggregate-error.test.js b/packages/ses/test/error/aggregate-error.test.js index 82f397799c..fd02cba3c9 100644 --- a/packages/ses/test/error/aggregate-error.test.js +++ b/packages/ses/test/error/aggregate-error.test.js @@ -53,3 +53,5 @@ test('Promise.any aggregate error', async t => { }); } }); + +// TODO SuppressedError diff --git a/packages/ses/test/error/error-cause.test.js b/packages/ses/test/error/error-cause.test.js index 5cd32091f5..7952d2f035 100644 --- a/packages/ses/test/error/error-cause.test.js +++ b/packages/ses/test/error/error-cause.test.js @@ -41,3 +41,5 @@ test('error cause', t => { configurable: true, }); }); + +// TODO SuppressedError diff --git a/packages/ses/test/get-global-intrinsics.test.js b/packages/ses/test/get-global-intrinsics.test.js new file mode 100644 index 0000000000..9ea264ae15 --- /dev/null +++ b/packages/ses/test/get-global-intrinsics.test.js @@ -0,0 +1,133 @@ +import test from 'ava'; +// import sinon from 'sinon'; +// import { getGlobalIntrinsics } from '../src/intrinsics.js'; + +test.skip('getGlobalIntrinsics', () => { + /* TODO + // We to duplicate this structure here as an + // as an independent refrerence. + const globalNames = [ + // *** 18.1 Value Properties of the Global Object + + // Ignore: those value properties are not intrinsics. + + // *** 18.2 Function Properties of the Global Object + + 'eval', + 'isFinite', + 'isNaN', + 'parseFloat', + 'parseInt', + + 'decodeURI', + 'decodeURIComponent', + 'encodeURI', + 'encodeURIComponent', + + // *** 18.3 Constructor Properties of the Global Object + + 'Array', + 'ArrayBuffer', + 'Boolean', + 'DataView', + 'Date', + 'Error', + 'EvalError', + 'Float32Array', + 'Float64Array', + 'Function', + 'Int8Array', + 'Int16Array', + 'Int32Array', + 'Map', + 'Number', + 'Object', + 'Promise', + 'Proxy', + 'RangeError', + 'ReferenceError', + 'RegExp', + 'Set', + // 'SharedArrayBuffer' // removed on Jan 5, 2018 + 'String', + // 'Symbol', original left in place, but omitted from whitelist + 'SyntaxError', + 'TypeError', + 'Uint8Array', + 'Uint8ClampedArray', + 'Uint16Array', + 'Uint32Array', + 'URIError', + 'WeakMap', + 'WeakSet', + // https://github.com/endojs/endo/issues/550 + 'AggregateError', + // https://github.com/tc39/proposal-explicit-resource-management + 'SuppressedError', + + // *** 18.4 Other Properties of the Global Object + + // 'Atomics', // removed on Jan 5, 2018 + 'JSON', + 'Math', + 'Reflect', + + // *** Annex B + + 'escape', + 'unescape', + + // ESNext + + 'globalThis', + ]; + + const intrinsics = getGlobalIntrinsics(); + + for (const name of globalNames) { + // Assert when both are defined or undefined. + t.is(intrinsics[name], globalThis[name]); + } + + }); + +test('Intrinsics - values', t => { + t.plan(6); + + const intrinsics = getGlobalIntrinsics(); + + t.is(intrinsics.Date, globalThis.Date); + t.is(intrinsics.eval, globalThis.eval); + t.is(intrinsics.Error, globalThis.Error); + t.is(intrinsics.Function, globalThis.Function); + t.is(intrinsics.JSON, globalThis.JSON); + t.is(intrinsics.Math, globalThis.Math); +}); + +test('Intrinsics - shims', t => { + t.plan(2); + + const mockDate = sinon.stub(globalThis, 'Date').callsFake(); + const intrinsics = getGlobalIntrinsics(); + + t.is(intrinsics.Date, mockDate); // Ensure shims are picked up + t.is(intrinsics.Date, globalThis.Date); + + sinon.restore(); +}); + +test('Intrinsics - global accessor throws', t => { + t.plan(1); + + const { JSON } = globalThis; + sinon.stub(globalThis, 'JSON').get(() => JSON); + + t.throws( + () => getGlobalIntrinsics(), + /Unexpected accessor on global property: JSON/, + ); + + sinon.restore(); +}); +*/ +}); diff --git a/packages/ses/types.d.ts b/packages/ses/types.d.ts index cfde6dd017..a1e31d9747 100644 --- a/packages/ses/types.d.ts +++ b/packages/ses/types.d.ts @@ -190,6 +190,24 @@ export interface AssertMakeErrorOptions { */ errors?: Error[]; + /** + * Normally only used when the ErrorConstuctor is `SuppressedError`, to + * represent the error exiting a disposal context where an error also happens + * during disposing. But `makeError` allows it on any error. + * This is represented by a public `error` data property on the error, + * not a hidden annotation. + */ + error?: Error; + + /** + * Normally only used when the ErrorConstuctor is `SuppressedError`, to + * represent an error happens during a disposing that is itself caused + * by an error. But `makeError` allows it on any error. + * This is represented by a public `suppressed` data property on the error, + * not a hidden annotation. + */ + suppressed?: Error; + /** * Defaults to true. If true, `makeError` will apply `sanitizeError` * to the error before returning it. See the comments on @@ -265,7 +283,8 @@ interface StringablePayload { */ export type GenericErrorConstructor = | ErrorConstructor - | AggregateErrorConstructor; + | AggregateErrorConstructor + | SuppressedErrorConstructor; /** * To make an `assert` which terminates some larger unit of computation