From 270612faf4fadc5bc3cb58820124858c5285eb14 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Mon, 14 Nov 2022 19:33:35 +0530 Subject: [PATCH 01/40] added tests for invoke error structure --- .../__tests__/core/error-structure.spec.ts | 130 ++++++++++++++++++ .../subinvoke-error/0-subinvoke/package.json | 8 ++ .../subinvoke-error/0-subinvoke/polywrap.yaml | 10 ++ .../0-subinvoke/schema.graphql | 5 + .../subinvoke-error/0-subinvoke/src/index.ts | 6 + .../subinvoke-error/1-subinvoke/package.json | 8 ++ .../subinvoke-error/1-subinvoke/polywrap.yaml | 7 + .../1-subinvoke/schema.graphql | 3 + .../subinvoke-error/1-subinvoke/src/index.ts | 8 ++ .../subinvoke-error/invoke/package.json | 8 ++ .../subinvoke-error/invoke/polywrap.yaml | 12 ++ .../subinvoke-error/invoke/schema.graphql | 7 + .../subinvoke-error/invoke/src/index.ts | 19 +++ 13 files changed, 231 insertions(+) create mode 100644 packages/js/client/src/__tests__/core/error-structure.spec.ts create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/package.json create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/polywrap.yaml create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/schema.graphql create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/src/index.ts create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/package.json create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/polywrap.yaml create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/schema.graphql create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/src/index.ts create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/package.json create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/polywrap.yaml create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/schema.graphql create mode 100644 packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/src/index.ts diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts new file mode 100644 index 0000000000..ff9864d1e1 --- /dev/null +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -0,0 +1,130 @@ +import { buildWrapper } from "@polywrap/test-env-js"; +import { GetPathToTestWrappers } from "@polywrap/test-cases"; +import { Uri, PolywrapClient } from "../.."; + +jest.setTimeout(360000); + +const simpleWrapperPath = `${GetPathToTestWrappers()}/wasm-as/simple`; +const simpleWrapperUri = new Uri(`fs/${simpleWrapperPath}/build`); + +const subinvokeErrorWrapperPath = `${GetPathToTestWrappers()}/wasm-as/subinvoke-error/invoke`; +const subinvokeErrorWrapperUri = new Uri(`fs/${subinvokeErrorWrapperPath}/build`); + +const badMathWrapperPath = `${GetPathToTestWrappers()}/wasm-as/subinvoke-error/0-subinvoke`; +const badMathWrapperUri = new Uri(`fs/${badMathWrapperPath}/build`); + +const badUtilWrapperPath = `${GetPathToTestWrappers()}/wasm-as/subinvoke-error/1-subinvoke`; +const badUtilWrapperUri = new Uri(`fs/${badUtilWrapperPath}/build`); + +// const incompatibleVersionWrapperUri = new Uri(""); + +describe("error structure", () => { + + let client: PolywrapClient; + + beforeAll(async () => { + await buildWrapper(simpleWrapperPath); + await buildWrapper(badUtilWrapperPath); + await buildWrapper(badMathWrapperPath); + await buildWrapper(subinvokeErrorWrapperPath); + + client = new PolywrapClient({ + redirects: [ + { + from: "ens/bad-math.eth", + to: badMathWrapperUri, + }, + { + from: "ens/bad-util.eth", + to: badUtilWrapperUri, + } + ] + }) + }); + + test("Invoke a wrapper that is not found", async () => { + const result = await client.invoke({ + uri: simpleWrapperUri.uri + "-not-found", + method: "simpleMethod", + args: { + arg: "test", + }, + }); + + if (!result.ok) fail(result.error); + expect(result.value).toEqual("test"); + }); + + test("Invoke a wrapper with malformed arguments", async () => { + const result = await client.invoke({ + uri: simpleWrapperUri.uri, + method: "simpleMethod", + args: { + arg: 3, + }, + }); + + console.log(result); + + if (!result.ok) fail(result.error); + expect(result.value).toEqual("test"); + }); + + test("Invoke a wrapper method that doesn't exist", async () => { + const result = await client.invoke({ + uri: simpleWrapperUri.uri, + method: "complexMethod", + args: { + arg: "test", + }, + }); + + console.log(result); + + if (!result.ok) fail(result.error); + expect(result.value).toEqual("test"); + }); + + test("Subinvoke a wrapper that is not found", async () => { + const result = await client.invoke({ + uri: subinvokeErrorWrapperUri.uri, + method: "subWrapperNotFound", + args: { + a: 1, + b: 1, + }, + }); + + if (!result.ok) fail(result.error); + expect(result.value).toEqual("test"); + }); + + test("Subinvoke error two layers deep", async () => { + const result = await client.invoke({ + uri: subinvokeErrorWrapperUri.uri, + method: "throwsInTwoSubinvokeLayers", + args: { + a: 1, + b: 1, + }, + }); + + if (!result.ok) fail(result.error); + expect(result.value).toEqual(3); + }); + + // test("Invoke a wrapper of incompatible version", async () => { + // const result = await client.invoke({ + // uri: incompatibleVersionWrapperUri.uri, + // method: "simpleMethod", + // args: { + // arg: "test", + // }, + // }); + // + // console.log(result) + // + // if (!result.ok) fail(result.error); + // expect(result.value).toEqual("test"); + // }); +}); diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/package.json b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/package.json new file mode 100644 index 0000000000..897ac01244 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/package.json @@ -0,0 +1,8 @@ +{ + "name": "test-case-simple-subinvoke", + "private": true, + "dependencies": { + "@polywrap/wasm-as": "0.9.1", + "assemblyscript": "0.19.1" + } +} diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/polywrap.yaml b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/polywrap.yaml new file mode 100644 index 0000000000..42cd93bbd9 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/polywrap.yaml @@ -0,0 +1,10 @@ +format: 0.2.0 +project: + name: Simple + type: wasm/assemblyscript +source: + schema: ./schema.graphql + module: ./src/index.ts + import_abis: + - uri: ens/bad-util.eth + abi: ../1-subinvoke/build/wrap.info diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/schema.graphql b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/schema.graphql new file mode 100644 index 0000000000..214779b400 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/schema.graphql @@ -0,0 +1,5 @@ +#import * into BadUtil from "ens/bad-util.eth" + +type Module { + subInvokeWillThrow(a: Int!, b: Int!): Int! +} diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/src/index.ts b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/src/index.ts new file mode 100644 index 0000000000..a73b226f89 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/0-subinvoke/src/index.ts @@ -0,0 +1,6 @@ +import { Args_subInvokeWillThrow, BadUtil_Module } from "./wrap"; + +export function subInvokeWillThrow(args: Args_subInvokeWillThrow): i32 { + const subInvokeResult = BadUtil_Module.iThrow({ a: 0 }).unwrap(); + return args.a + args.b + subInvokeResult; +} diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/package.json b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/package.json new file mode 100644 index 0000000000..897ac01244 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/package.json @@ -0,0 +1,8 @@ +{ + "name": "test-case-simple-subinvoke", + "private": true, + "dependencies": { + "@polywrap/wasm-as": "0.9.1", + "assemblyscript": "0.19.1" + } +} diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/polywrap.yaml b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/polywrap.yaml new file mode 100644 index 0000000000..2d21ce24e6 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/polywrap.yaml @@ -0,0 +1,7 @@ +format: 0.2.0 +project: + name: Simple + type: wasm/assemblyscript +source: + schema: ./schema.graphql + module: ./src/index.ts diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/schema.graphql b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/schema.graphql new file mode 100644 index 0000000000..af3a9c4bdc --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/schema.graphql @@ -0,0 +1,3 @@ +type Module { + iThrow(a: Int!): Int! +} diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/src/index.ts b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/src/index.ts new file mode 100644 index 0000000000..af7b1e2633 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/1-subinvoke/src/index.ts @@ -0,0 +1,8 @@ +import { Args_iThrow } from "./wrap"; + +export function iThrow(args: Args_iThrow): i32 { + if (2 == 2) { + throw new Error("I threw an error!"); + } + return args.a + 1; +} diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/package.json b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/package.json new file mode 100644 index 0000000000..68ecff0a4e --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/package.json @@ -0,0 +1,8 @@ +{ + "name": "test-case-simple-invoke", + "private": true, + "dependencies": { + "@polywrap/wasm-as": "0.9.1", + "assemblyscript": "0.19.1" + } +} diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/polywrap.yaml b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/polywrap.yaml new file mode 100644 index 0000000000..96144bcfce --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/polywrap.yaml @@ -0,0 +1,12 @@ +format: 0.2.0 +project: + name: Simple + type: wasm/assemblyscript +source: + schema: ./schema.graphql + module: ./src/index.ts + import_abis: + - uri: ens/bad-math.eth + abi: ../0-subinvoke/build/wrap.info + - uri: ens/not-found.eth + abi: ../0-subinvoke/build/wrap.info diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/schema.graphql b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/schema.graphql new file mode 100644 index 0000000000..0b784f3ed9 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/schema.graphql @@ -0,0 +1,7 @@ +#import * into BadMath from "ens/bad-math.eth" +#import * into NotFound from "ens/not-found.eth" + +type Module { + throwsInTwoSubinvokeLayers(a: Int!, b: Int!): Int! + subWrapperNotFound(a: Int!, b: Int!): Int! +} diff --git a/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/src/index.ts b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/src/index.ts new file mode 100644 index 0000000000..d4cbe5ecbb --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/src/index.ts @@ -0,0 +1,19 @@ +import { Args_throwsInTwoSubinvokeLayers, Args_subWrapperNotFound, BadMath_Module, NotFound_Module } from "./wrap"; +import { Args_subInvokeWillThrow as BadMathArgs_subInvokeWillThrow } from "./wrap/imported/BadMath_Module/serialization"; +import { Args_subInvokeWillThrow as NotFoundArgs_subInvokeWillThrow } from "./wrap/imported/NotFound_Module/serialization"; + +export function throwsInTwoSubinvokeLayers(args: Args_throwsInTwoSubinvokeLayers): i32 { + let importedArgs: BadMathArgs_subInvokeWillThrow = { + a: args.a, + b: args.b + } + return BadMath_Module.subInvokeWillThrow(importedArgs).unwrap() +} + +export function subWrapperNotFound(args: Args_subWrapperNotFound): i32 { + let importedArgs: NotFoundArgs_subInvokeWillThrow = { + a: args.a, + b: args.b + } + return NotFound_Module.subInvokeWillThrow(importedArgs).unwrap() +} \ No newline at end of file From be51143b4e427e93993634f0dbac6a1ba9ae72a1 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Tue, 15 Nov 2022 19:34:29 +0530 Subject: [PATCH 02/40] added InvokeError and error structure tests --- .../__tests__/core/error-structure.spec.ts | 12 +- packages/js/wasm/src/InvokeError.ts | 114 ++++++++++++++++++ packages/js/wasm/src/WasmWrapper.ts | 39 +++--- packages/js/wasm/src/imports.ts | 7 +- packages/js/wasm/src/index.ts | 1 + 5 files changed, 144 insertions(+), 29 deletions(-) create mode 100644 packages/js/wasm/src/InvokeError.ts diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index ff9864d1e1..7446e288df 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -24,9 +24,9 @@ describe("error structure", () => { beforeAll(async () => { await buildWrapper(simpleWrapperPath); - await buildWrapper(badUtilWrapperPath); - await buildWrapper(badMathWrapperPath); - await buildWrapper(subinvokeErrorWrapperPath); + // await buildWrapper(badUtilWrapperPath); + // await buildWrapper(badMathWrapperPath); + // await buildWrapper(subinvokeErrorWrapperPath); client = new PolywrapClient({ redirects: [ @@ -51,6 +51,8 @@ describe("error structure", () => { }, }); + console.log(result) + if (!result.ok) fail(result.error); expect(result.value).toEqual("test"); }); @@ -64,8 +66,6 @@ describe("error structure", () => { }, }); - console.log(result); - if (!result.ok) fail(result.error); expect(result.value).toEqual("test"); }); @@ -79,8 +79,6 @@ describe("error structure", () => { }, }); - console.log(result); - if (!result.ok) fail(result.error); expect(result.value).toEqual("test"); }); diff --git a/packages/js/wasm/src/InvokeError.ts b/packages/js/wasm/src/InvokeError.ts new file mode 100644 index 0000000000..99c2487670 --- /dev/null +++ b/packages/js/wasm/src/InvokeError.ts @@ -0,0 +1,114 @@ +export type WasmErrorSource = Readonly<{ + file: string; + line: number; + col: number; +}>; + +export enum InvokeErrorCode { + ABORTED, + RETURNED, +} + +export interface InvokeErrorOptions { + code: InvokeErrorCode; + source?: WasmErrorSource; + uri: string; + method: string; + args?: string; + cause?: Error; +} + +export class InvokeError extends Error { + readonly name: "InvokeError"; + readonly code: InvokeErrorCode; + readonly text: string; + readonly source?: WasmErrorSource; + readonly uri: string; + readonly method: string; + readonly args?: string; + readonly cause?: Error; + + private static re = new RegExp( + [ + /^InvokeError: ([\w ]+\.)/.source, + /(?:\r\n|\r|\n)/.source, + /message: ((?:.|\r\n|\r|\n)*)/.source, + /(?:\r\n|\r|\n)/.source, + /source: (.*)/.source, + /(?:\r\n|\r|\n)/.source, + /uri: (.*)/.source, + /(?:\r\n|\r|\n)/.source, + /method: (.*)/.source, + /(?:\r\n|\r|\n)/.source, + /args: ((?:.|\r\n|\r|\n)*)$/.source, + ].join("") + ); + + constructor(message: string, options: InvokeErrorOptions) { + super(InvokeError.stringify(message, options)); + this.text = message; + this.code = options.code; + this.source = options.source; + this.uri = options.uri; + this.method = options.method; + this.args = options.args; + this.cause = options.cause; + Object.setPrototypeOf(this, InvokeError.prototype); + } + + static parse(error: string): InvokeError { + const result = this.re.exec(error); + if (!result) { + throw Error("Failed to parse error into InvokeError: " + error); + } + const [, metaMessage, text, sourceStr, uri, method, args] = result; + + const code: InvokeErrorCode = error.startsWith(metaMessage) + ? InvokeErrorCode.ABORTED + : InvokeErrorCode.RETURNED; + const source = JSON.parse(sourceStr) as WasmErrorSource; + + // message may be a stringified InvokeError + let cause: Error | undefined; + if (this.re.test(text)) { + cause = this.parse(text); + } + + return new InvokeError(text, { code, source, uri, method, args, cause }); + } + + toString(): string { + return InvokeError.stringify(this.text, { + code: this.code, + source: this.source, + uri: this.uri, + method: this.method, + args: this.args, + cause: this.cause, + }); + } + + private static metaMessage(code: InvokeErrorCode): string { + switch (code) { + case InvokeErrorCode.ABORTED: + return "Wasm module aborted execution."; + case InvokeErrorCode.RETURNED: + return "Invocation exception encountered."; + default: + return "Unknown invocation exception."; + } + } + + private static stringify(text: string, options: InvokeErrorOptions) { + const { code, source, uri, method, args } = options; + const sourceStr = source + ? `{ file: ${source?.file}, row: ${source?.line}, col: ${source?.col} }` + : "undefined"; + return `${this.name}: ${InvokeError.metaMessage(code)} +message: ${text} +source: ${sourceStr} +uri: ${uri} +method: ${method} +args: ${args ?? "undefined"}`; + } +} diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index 1cb83f16d0..1901becda0 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -4,20 +4,21 @@ import { createImports } from "./imports"; import { IFileReader } from "./IFileReader"; import { WRAP_MODULE_PATH } from "./constants"; import { createWasmWrapper } from "./helpers/createWasmWrapper"; +import { InvokeError, InvokeErrorCode, WasmErrorSource } from "./InvokeError"; import { WrapManifest } from "@polywrap/wrap-manifest-types-js"; import { msgpackEncode } from "@polywrap/msgpack-js"; import { Tracer, TracingLevel } from "@polywrap/tracing-js"; import { AsyncWasmInstance } from "@polywrap/asyncify-js"; import { - Wrapper, - Uri, - InvokeOptions, CoreClient, - InvocableResult, - isBuffer, GetFileOptions, GetManifestOptions, + InvocableResult, + InvokeOptions, + isBuffer, + Uri, + Wrapper, } from "@polywrap/core-js"; import { Result, ResultErr, ResultOk } from "@polywrap/result"; @@ -172,12 +173,14 @@ export class WasmWrapper implements Wrapper { env: options.env ? msgpackEncode(options.env) : EMPTY_ENCODED_OBJECT, }; - const abort = (message: string) => { - throw Error( - `WasmWrapper: Wasm module aborted execution.\nURI: ${options.uri.uri}\n` + - `Method: ${method}\n` + - `Args: ${JSON.stringify(args, null, 2)}\nMessage: ${message}.\n` - ); + const abort = (message: string, source?: WasmErrorSource) => { + throw new InvokeError(message, { + code: InvokeErrorCode.ABORTED, + uri: options.uri.uri, + method, + args: JSON.stringify(args, null, 2), + source, + }); }; const memory = AsyncWasmInstance.createMemory({ module: wasm }); @@ -208,13 +211,13 @@ export class WasmWrapper implements Wrapper { encoded: true, }; } else { - const error = Error( - `WasmWrapper: invocation exception encountered.\n` + - `uri: ${options.uri.uri}\n` + - `method: ${method}\n` + - `args: ${JSON.stringify(args, null, 2)}\n` + - `exception: ${invokeResult.error?.message}` - ); + const error = new InvokeError(invokeResult.error?.message ?? "", { + code: InvokeErrorCode.RETURNED, + uri: options.uri.uri, + method, + args: JSON.stringify(args, null, 2), + cause: invokeResult.error, + }); return ResultErr(error); } } catch (error) { diff --git a/packages/js/wasm/src/imports.ts b/packages/js/wasm/src/imports.ts index 4cecf0ae82..9cc19a2139 100644 --- a/packages/js/wasm/src/imports.ts +++ b/packages/js/wasm/src/imports.ts @@ -3,6 +3,7 @@ import { u32, WrapImports } from "./types"; import { readBytes, readString, writeBytes, writeString } from "./buffer"; import { State } from "./WasmWrapper"; +import { WasmErrorSource } from "./InvokeError"; import { msgpackEncode } from "@polywrap/msgpack-js"; import { CoreClient } from "@polywrap/core-js"; @@ -11,7 +12,7 @@ export const createImports = (config: { client: CoreClient; memory: WebAssembly.Memory; state: State; - abort: (message: string) => never; + abort: (message: string, source?: WasmErrorSource) => never; }): WrapImports => { const { memory, state, client, abort } = config; @@ -212,9 +213,7 @@ export const createImports = (config: { const msg = readString(memory.buffer, msgPtr, msgLen); const file = readString(memory.buffer, filePtr, fileLen); - abort( - `__wrap_abort: ${msg}\nFile: ${file}\nLocation: [${line},${column}]` - ); + abort(`__wrap_abort: ${msg}`, { file, line, col: column }); }, __wrap_debug_log: (ptr: u32, len: u32): void => { const msg = readString(memory.buffer, ptr, len); diff --git a/packages/js/wasm/src/index.ts b/packages/js/wasm/src/index.ts index 1309eab0b6..6a5d72ca29 100644 --- a/packages/js/wasm/src/index.ts +++ b/packages/js/wasm/src/index.ts @@ -5,3 +5,4 @@ export { IFileReader } from "./IFileReader"; export { InMemoryFileReader } from "./InMemoryFileReader"; export { WrapImports } from "./types"; export * from "./constants"; +export { InvokeError } from "./InvokeError"; From 3dd3d17d2691533774e8a497f7cda0d1bd35e510 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Tue, 15 Nov 2022 19:39:37 +0530 Subject: [PATCH 03/40] uncommented some lines in error-structure.spec.ts --- .../js/client/src/__tests__/core/error-structure.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 7446e288df..075e182b6e 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -24,9 +24,9 @@ describe("error structure", () => { beforeAll(async () => { await buildWrapper(simpleWrapperPath); - // await buildWrapper(badUtilWrapperPath); - // await buildWrapper(badMathWrapperPath); - // await buildWrapper(subinvokeErrorWrapperPath); + await buildWrapper(badUtilWrapperPath); + await buildWrapper(badMathWrapperPath); + await buildWrapper(subinvokeErrorWrapperPath); client = new PolywrapClient({ redirects: [ From d5f8b8553d87813e93885e3c264ef9590e5f4944 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Tue, 15 Nov 2022 22:10:46 +0530 Subject: [PATCH 04/40] moved InvokeError to core-js --- packages/js/{wasm/src => core/src/types}/InvokeError.ts | 0 packages/js/core/src/types/index.ts | 1 + packages/js/wasm/src/WasmWrapper.ts | 4 +++- packages/js/wasm/src/imports.ts | 3 +-- packages/js/wasm/src/index.ts | 1 - 5 files changed, 5 insertions(+), 4 deletions(-) rename packages/js/{wasm/src => core/src/types}/InvokeError.ts (100%) diff --git a/packages/js/wasm/src/InvokeError.ts b/packages/js/core/src/types/InvokeError.ts similarity index 100% rename from packages/js/wasm/src/InvokeError.ts rename to packages/js/core/src/types/InvokeError.ts diff --git a/packages/js/core/src/types/index.ts b/packages/js/core/src/types/index.ts index 462080bc83..acced58c8d 100644 --- a/packages/js/core/src/types/index.ts +++ b/packages/js/core/src/types/index.ts @@ -12,3 +12,4 @@ export * from "./IWrapPackage"; export * from "./IUriRedirect"; export * from "./IUriWrapper"; export * from "./IUriPackage"; +export * from "./InvokeError"; diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index 1901becda0..546b930dd2 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -4,7 +4,6 @@ import { createImports } from "./imports"; import { IFileReader } from "./IFileReader"; import { WRAP_MODULE_PATH } from "./constants"; import { createWasmWrapper } from "./helpers/createWasmWrapper"; -import { InvokeError, InvokeErrorCode, WasmErrorSource } from "./InvokeError"; import { WrapManifest } from "@polywrap/wrap-manifest-types-js"; import { msgpackEncode } from "@polywrap/msgpack-js"; @@ -19,6 +18,9 @@ import { isBuffer, Uri, Wrapper, + InvokeError, + InvokeErrorCode, + WasmErrorSource, } from "@polywrap/core-js"; import { Result, ResultErr, ResultOk } from "@polywrap/result"; diff --git a/packages/js/wasm/src/imports.ts b/packages/js/wasm/src/imports.ts index 9cc19a2139..9724e92918 100644 --- a/packages/js/wasm/src/imports.ts +++ b/packages/js/wasm/src/imports.ts @@ -3,10 +3,9 @@ import { u32, WrapImports } from "./types"; import { readBytes, readString, writeBytes, writeString } from "./buffer"; import { State } from "./WasmWrapper"; -import { WasmErrorSource } from "./InvokeError"; import { msgpackEncode } from "@polywrap/msgpack-js"; -import { CoreClient } from "@polywrap/core-js"; +import { CoreClient, WasmErrorSource } from "@polywrap/core-js"; export const createImports = (config: { client: CoreClient; diff --git a/packages/js/wasm/src/index.ts b/packages/js/wasm/src/index.ts index 6a5d72ca29..1309eab0b6 100644 --- a/packages/js/wasm/src/index.ts +++ b/packages/js/wasm/src/index.ts @@ -5,4 +5,3 @@ export { IFileReader } from "./IFileReader"; export { InMemoryFileReader } from "./InMemoryFileReader"; export { WrapImports } from "./types"; export * from "./constants"; -export { InvokeError } from "./InvokeError"; From f4993e4ca66a89810d526a09fe6002ba7927f076 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Fri, 18 Nov 2022 18:07:34 +0530 Subject: [PATCH 05/40] custom error structure prints correct output --- packages/js/client/package.json | 3 +- packages/js/client/src/PolywrapClient.ts | 83 ++++-- .../__tests__/core/error-structure.spec.ts | 20 +- packages/js/core/src/types/Invoke.ts | 4 +- packages/js/core/src/types/InvokeError.ts | 114 -------- packages/js/core/src/types/Query.ts | 4 +- packages/js/core/src/types/WrapError.ts | 259 ++++++++++++++++++ packages/js/core/src/types/index.ts | 2 +- packages/js/plugin/src/PluginWrapper.ts | 37 ++- packages/js/wasm/src/WasmWrapper.ts | 28 +- packages/js/wasm/src/imports.ts | 4 +- 11 files changed, 377 insertions(+), 181 deletions(-) delete mode 100644 packages/js/core/src/types/InvokeError.ts create mode 100644 packages/js/core/src/types/WrapError.ts diff --git a/packages/js/client/package.json b/packages/js/client/package.json index 82dfb7b44b..bc8b0ef2f1 100644 --- a/packages/js/client/package.json +++ b/packages/js/client/package.json @@ -17,7 +17,8 @@ "test": "jest --passWithNoTests --runInBand --verbose=true --detectOpenHandles --forceExit", "test:ci": "jest --passWithNoTests --runInBand --verbose --detectOpenHandles --forceExit", "test:rust": "jest --passWithNoTests --runInBand --verbose --detectOpenHandles --forceExit --config ./jest.rs.config.js", - "test:watch": "jest --watch --passWithNoTests --verbose --detectOpenHandles" + "test:watch": "jest --watch --passWithNoTests --verbose --detectOpenHandles", + "test:errors": "yarn test src/__tests__/core/error-structure.spec.ts" }, "dependencies": { "@polywrap/client-config-builder-js": "0.10.0-pre.3", diff --git a/packages/js/client/src/PolywrapClient.ts b/packages/js/client/src/PolywrapClient.ts index ed597cad91..178a88986e 100644 --- a/packages/js/client/src/PolywrapClient.ts +++ b/packages/js/client/src/PolywrapClient.ts @@ -1,4 +1,3 @@ -import { UriResolverError } from "./UriResolverError"; import { PolywrapCoreClientConfig } from "./PolywrapCoreClientConfig"; import { PolywrapClientConfig } from "./PolywrapClientConfig"; @@ -27,6 +26,8 @@ import { getEnvFromUriHistory, QueryResult, InvokeResult, + WrapError, + WrapErrorCode, } from "@polywrap/core-js"; import { buildCleanUriHistory } from "@polywrap/uri-resolvers-js"; import { msgpackEncode, msgpackDecode } from "@polywrap/msgpack-js"; @@ -182,7 +183,6 @@ export class PolywrapClient implements CoreClient { * returns a package's wrap manifest * * @param uri - a wrap URI - * @param options - { noValidate?: boolean } * @returns a Result containing the WrapManifest if the request was successful */ @Tracer.traceMethod("PolywrapClient: getManifest") @@ -298,7 +298,12 @@ export class PolywrapClient implements CoreClient { // Parse the query to understand what's being invoked const parseResult = parseQuery(uri, queryDocument, variables); if (!parseResult.ok) { - result = { errors: [parseResult.error as Error] }; + const error = new WrapError(parseResult.error?.message, { + code: WrapErrorCode.QUERY_MALFORMED, + uri: uri.uri, + cause: parseResult.error, + }); + result = { errors: [error] }; break err; } const queryInvocations = parseResult.value; @@ -328,13 +333,13 @@ export class PolywrapClient implements CoreClient { // Aggregate all invocation results const data: Record = {}; - const errors: Error[] = []; + const errors: WrapError[] = []; for (const invocation of invocationResults) { if (invocation.result.ok) { data[invocation.name] = invocation.result.value; } else { - errors.push(invocation.result.error as Error); + errors.push(invocation.result.error as WrapError); } } @@ -343,10 +348,16 @@ export class PolywrapClient implements CoreClient { errors: errors.length === 0 ? undefined : errors, }; } catch (error: unknown) { + const unknownQueryErrorToWrapError = (e: Error): WrapError => + new WrapError((e as Error)?.message, { + code: WrapErrorCode.QUERY_FAIL, + uri: options.uri.toString(), + cause: e as Error, + }); if (Array.isArray(error)) { - result = { errors: error }; + result = { errors: error.map(unknownQueryErrorToWrapError) }; } else { - result = { errors: [error as Error] }; + result = { errors: [unknownQueryErrorToWrapError(error as Error)] }; } } @@ -646,7 +657,7 @@ export class PolywrapClient implements CoreClient { uri: Uri, resolutionContext?: IUriResolutionContext, options?: DeserializeManifestOptions - ): Promise> { + ): Promise> { Tracer.setAttribute("label", `Wrapper loaded: ${uri}`, TracingLevel.High); if (!resolutionContext) { @@ -659,34 +670,43 @@ export class PolywrapClient implements CoreClient { }); if (!result.ok) { + const history = buildCleanUriHistory(resolutionContext.getHistory()); + const resolutionStack = `${JSON.stringify(history, null, 2)}`; + + let error: WrapError; if (result.error) { - return ResultErr(new UriResolverError(result.error, resolutionContext)); - } else { - return ResultErr( - Error( - `Error resolving URI "${ - uri.uri - }"\nResolution Stack: ${JSON.stringify( - buildCleanUriHistory(resolutionContext.getHistory()), - null, - 2 - )}` - ) + error = new WrapError( + "An internal resolver error occurred while resolving a URI", + { + code: WrapErrorCode.URI_RESOLVER_ERROR, + uri: uri.uri, + resolutionStack, + cause: result.error, + } ); + } else { + error = new WrapError("Error resolving URI", { + code: WrapErrorCode.URI_RESOLUTION_ERROR, + uri: uri.uri, + resolutionStack, + }); } + + return ResultErr(error); } const uriPackageOrWrapper = result.value; if (uriPackageOrWrapper.type === "uri") { - const error = Error( - `Error resolving URI "${uri.uri}"\nURI not found ${ - uriPackageOrWrapper.uri.uri - }\nResolution Stack: ${JSON.stringify( - buildCleanUriHistory(resolutionContext.getHistory()), - null, - 2 - )}` + const history = buildCleanUriHistory(resolutionContext.getHistory()); + const resolutionStack = `${JSON.stringify(history, null, 2)}`; + const error = new WrapError( + `Unable to find URI ${uriPackageOrWrapper.uri.uri}.`, + { + code: WrapErrorCode.URI_NOT_FOUND, + uri: uri.uri, + resolutionStack, + } ); return ResultErr(error); } @@ -695,7 +715,12 @@ export class PolywrapClient implements CoreClient { const result = await uriPackageOrWrapper.package.createWrapper(options); if (!result.ok) { - return result; + const error = new WrapError(result.error?.message, { + code: WrapErrorCode.LOAD_WRAPPER_FAIL, + uri: uri.uri, + cause: result.error, + }); + return ResultErr(error); } return ResultOk(result.value); diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 075e182b6e..f12fef1b01 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,4 +1,3 @@ -import { buildWrapper } from "@polywrap/test-env-js"; import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; @@ -23,10 +22,10 @@ describe("error structure", () => { let client: PolywrapClient; beforeAll(async () => { - await buildWrapper(simpleWrapperPath); - await buildWrapper(badUtilWrapperPath); - await buildWrapper(badMathWrapperPath); - await buildWrapper(subinvokeErrorWrapperPath); + // await buildWrapper(simpleWrapperPath); + // await buildWrapper(badUtilWrapperPath); + // await buildWrapper(badMathWrapperPath); + // await buildWrapper(subinvokeErrorWrapperPath); client = new PolywrapClient({ redirects: [ @@ -51,12 +50,11 @@ describe("error structure", () => { }, }); - console.log(result) - if (!result.ok) fail(result.error); expect(result.value).toEqual("test"); }); + // TODO: edit wasm "Context" and wasm bindings test("Invoke a wrapper with malformed arguments", async () => { const result = await client.invoke({ uri: simpleWrapperUri.uri, @@ -70,6 +68,7 @@ describe("error structure", () => { expect(result.value).toEqual("test"); }); + // TODO: edit packages/wasm/as/assembly/invoke.ts test("Invoke a wrapper method that doesn't exist", async () => { const result = await client.invoke({ uri: simpleWrapperUri.uri, @@ -97,7 +96,7 @@ describe("error structure", () => { expect(result.value).toEqual("test"); }); - test("Subinvoke error two layers deep", async () => { + test.only("Subinvoke error two layers deep", async () => { const result = await client.invoke({ uri: subinvokeErrorWrapperUri.uri, method: "throwsInTwoSubinvokeLayers", @@ -107,7 +106,10 @@ describe("error structure", () => { }, }); - if (!result.ok) fail(result.error); + if (!result.ok) { + console.log(result.error); + fail(result.error); + } expect(result.value).toEqual(3); }); diff --git a/packages/js/core/src/types/Invoke.ts b/packages/js/core/src/types/Invoke.ts index 3a0214f5fb..38bce7d87d 100644 --- a/packages/js/core/src/types/Invoke.ts +++ b/packages/js/core/src/types/Invoke.ts @@ -1,4 +1,4 @@ -import { Uri, Wrapper } from "."; +import { WrapError, Uri, Wrapper } from "."; import { IUriResolutionContext } from "../uri-resolution"; import { Result } from "@polywrap/result"; @@ -30,7 +30,7 @@ export interface InvokeOptions { * * @template TData Type of the invoke result data. */ -export type InvokeResult = Result; +export type InvokeResult = Result; export interface InvokerOptions extends InvokeOptions { diff --git a/packages/js/core/src/types/InvokeError.ts b/packages/js/core/src/types/InvokeError.ts deleted file mode 100644 index 99c2487670..0000000000 --- a/packages/js/core/src/types/InvokeError.ts +++ /dev/null @@ -1,114 +0,0 @@ -export type WasmErrorSource = Readonly<{ - file: string; - line: number; - col: number; -}>; - -export enum InvokeErrorCode { - ABORTED, - RETURNED, -} - -export interface InvokeErrorOptions { - code: InvokeErrorCode; - source?: WasmErrorSource; - uri: string; - method: string; - args?: string; - cause?: Error; -} - -export class InvokeError extends Error { - readonly name: "InvokeError"; - readonly code: InvokeErrorCode; - readonly text: string; - readonly source?: WasmErrorSource; - readonly uri: string; - readonly method: string; - readonly args?: string; - readonly cause?: Error; - - private static re = new RegExp( - [ - /^InvokeError: ([\w ]+\.)/.source, - /(?:\r\n|\r|\n)/.source, - /message: ((?:.|\r\n|\r|\n)*)/.source, - /(?:\r\n|\r|\n)/.source, - /source: (.*)/.source, - /(?:\r\n|\r|\n)/.source, - /uri: (.*)/.source, - /(?:\r\n|\r|\n)/.source, - /method: (.*)/.source, - /(?:\r\n|\r|\n)/.source, - /args: ((?:.|\r\n|\r|\n)*)$/.source, - ].join("") - ); - - constructor(message: string, options: InvokeErrorOptions) { - super(InvokeError.stringify(message, options)); - this.text = message; - this.code = options.code; - this.source = options.source; - this.uri = options.uri; - this.method = options.method; - this.args = options.args; - this.cause = options.cause; - Object.setPrototypeOf(this, InvokeError.prototype); - } - - static parse(error: string): InvokeError { - const result = this.re.exec(error); - if (!result) { - throw Error("Failed to parse error into InvokeError: " + error); - } - const [, metaMessage, text, sourceStr, uri, method, args] = result; - - const code: InvokeErrorCode = error.startsWith(metaMessage) - ? InvokeErrorCode.ABORTED - : InvokeErrorCode.RETURNED; - const source = JSON.parse(sourceStr) as WasmErrorSource; - - // message may be a stringified InvokeError - let cause: Error | undefined; - if (this.re.test(text)) { - cause = this.parse(text); - } - - return new InvokeError(text, { code, source, uri, method, args, cause }); - } - - toString(): string { - return InvokeError.stringify(this.text, { - code: this.code, - source: this.source, - uri: this.uri, - method: this.method, - args: this.args, - cause: this.cause, - }); - } - - private static metaMessage(code: InvokeErrorCode): string { - switch (code) { - case InvokeErrorCode.ABORTED: - return "Wasm module aborted execution."; - case InvokeErrorCode.RETURNED: - return "Invocation exception encountered."; - default: - return "Unknown invocation exception."; - } - } - - private static stringify(text: string, options: InvokeErrorOptions) { - const { code, source, uri, method, args } = options; - const sourceStr = source - ? `{ file: ${source?.file}, row: ${source?.line}, col: ${source?.col} }` - : "undefined"; - return `${this.name}: ${InvokeError.metaMessage(code)} -message: ${text} -source: ${sourceStr} -uri: ${uri} -method: ${method} -args: ${args ?? "undefined"}`; - } -} diff --git a/packages/js/core/src/types/Query.ts b/packages/js/core/src/types/Query.ts index 5bcd019e82..c39e5e4e6f 100644 --- a/packages/js/core/src/types/Query.ts +++ b/packages/js/core/src/types/Query.ts @@ -1,4 +1,4 @@ -import { Uri, InvokeOptions } from "./"; +import { Uri, InvokeOptions, WrapError } from "./"; import { Tracer } from "@polywrap/tracing-js"; import { DocumentNode } from "graphql"; @@ -57,7 +57,7 @@ export interface QueryResult< data?: TData; /** Errors encountered during the query. */ - errors?: Error[]; + errors?: WrapError[]; } export interface QueryInvocations { diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts new file mode 100644 index 0000000000..37e51b5d8e --- /dev/null +++ b/packages/js/core/src/types/WrapError.ts @@ -0,0 +1,259 @@ +export type WrapErrorSource = Readonly<{ + file?: string; + line?: number; + col?: number; +}>; + +// 0-49 -> Internal +// 50-99 -> URI Resolution +// 100-150 -> Wasm wrapper invocation +// 150-199 -> Wasm wrapper sub-invocation +// 200-255 -> Plugin invocation & sub-invocation +export enum WrapErrorCode { + UNKNOWN, + LOAD_WRAPPER_FAIL, + QUERY_MALFORMED = 48, + QUERY_FAIL, + URI_RESOLUTION_ERROR = 50, + URI_RESOLVER_ERROR, + URI_NOT_FOUND, + WASM_INVOKE_ABORTED = 100, + WASM_INVOKE_FAIL, + WASM_NO_MODULE, + WASM_METHOD_NOT_FOUND, + PLUGIN_INVOKE_ABORTED = 200, + PLUGIN_INVOKE_FAIL, + PLUGIN_METHOD_NOT_FOUND, + PLUGIN_ARGS_MALFORMED, +} + +export interface WrapErrorOptions { + code: WrapErrorCode; + uri: string; + method?: string; + args?: string; + source?: WrapErrorSource; + resolutionStack?: string; + cause?: unknown; +} + +type RegExpGroups = + | (RegExpExecArray & { + groups?: { [name in T]: string | undefined } | { [key: string]: string }; + }) + | null; + +export class WrapError extends Error { + readonly name: string; + readonly code: WrapErrorCode; + readonly text: string; + readonly uri: string; + readonly method?: string; + readonly args?: string; + readonly source?: WrapErrorSource; + readonly resolutionStack?: string; + readonly cause?: unknown; + + // TODO: KRIS - elements can be string literals for prod, but they are easier to read as regex literals + static re = new RegExp( + [ + // [A-z]+Error can be replaced with specific error names when finalized + /^(?:[A-z_: ]*; )?[A-z]+Error: [\w ]+\./.source, + // there is some padding added to the number of words expected in an error code + /(?:\r\n|\r|\n)code: (?1?[0-9]{1,2}|2[0-4][0-9]|25[0-5]) (?:[A-Z]+ ?){1,5}/ + .source, + /(?:\r\n|\r|\n)message: (?(?:.|\r\n|\r|\n)*)/.source, + /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-z0-9_-]+\/.+)/.source, + /(?:(?:\r\n|\r|\n)method: (?([A-z_]{1}[A-z0-9_]*)))?/.source, + /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r\n|\r|\n)+} ))?/.source, + /(?:(?:\r\n|\r|\n)source: \{ file: "(?.+)", row: (?[0-9]+), col: (?[0-9]+) })?/ + .source, + /(?:(?:\r\n|\r|\n)uriResolutionStack: (?\[(?:.|\r\n|\r|\n)+]))?/ + .source, + /(?:(?:\r\n|\r|\n){2}Another exception was encountered during execution:(?:\r\n|\r|\n)(?(?:.|\r\n|\r|\n)+))?$/ + .source, + ].join("") + ); + + constructor(text = "Encountered an exception.", options: WrapErrorOptions) { + super(WrapError.stringify(text, options)); + this.name = WrapError.codeToName(options.code); + this.code = options.code; + this.text = text; + this.uri = options.uri; + this.method = options.method; + this.args = options.args; + this.source = options.source; + this.resolutionStack = options.resolutionStack; + this.cause = options.cause; + Object.setPrototypeOf(this, WrapError.prototype); + } + + static parse(error: string): WrapError | undefined { + const result: RegExpGroups< + | "code" + | "message" + | "uri" + | "method" + | "args" + | "file" + | "line" + | "col" + | "resolutionStack" + | "cause" + > = this.re.exec(error); + if (!result) { + return undefined; + } + const { + code: codeStr, + message, + uri, + method, + args, + file, + line, + col, + resolutionStack, + cause: causeStr, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + } = result.groups!; + + const code = parseInt(codeStr as string); + + // replace parens () with brackets {} + const source: WrapErrorSource | undefined = file + ? { + file, + line: line ? parseInt(line) : undefined, + col: col ? parseInt(col) : undefined, + } + : undefined; + + // message may be a stringified WrapError + let cause: WrapError | undefined; + if (this.re.test(message as string)) { + cause = this.parse(message as string); + } + + return new WrapError(message, { + code, + uri: uri as string, + method, + args, + source, + resolutionStack, + cause: cause ?? causeStr, + }); + } + + toString(): string { + return `${this.name}: ${this.message}`; + } + + private static stringify(text: string, options: WrapErrorOptions) { + const { code, uri, method, args, source, resolutionStack, cause } = options; + const formattedCode = `${code} ${WrapErrorCode[code].replace(/_/g, " ")}`; + + // Some items are not always present + const maybeMethod = method ? `method: ${method}` : ""; + const maybeArgs = args ? `args: ${args} ` : ""; + // source is uses () instead of {} to facilitate regex + const maybeSource = source + ? `source: { file: "${source?.file}", row: ${source?.line}, col: ${source?.col} }` + : ""; + const maybeResolutionStack = resolutionStack + ? `uriResolutionStack: ${resolutionStack}` + : ""; + + const errorCause = WrapError.stringifyCause(cause); + const maybeCause = errorCause + ? `\nAnother exception was encountered during execution:\n${errorCause}` + : ""; + + return [ + WrapError.metaMessage(code), + `code: ${formattedCode}`, + `message: ${text}`, + `uri: ${uri}`, + maybeMethod, + maybeArgs, + maybeSource, + maybeResolutionStack, + maybeCause, + ] + .filter((it) => !!it) + .join("\n"); + } + + private static stringifyCause(cause: unknown): string | undefined { + if (cause === undefined || cause === null) { + return undefined; + } else if (cause instanceof Error) { + return cause.toString(); + } else if (typeof cause === "object" && cause) { + if ( + cause.toString !== Object.prototype.toString && + typeof cause.toString === "function" + ) { + return cause.toString(); + } + return JSON.stringify(cause); + } else if ( + typeof cause === "function" && + cause.toString !== Object.prototype.toString && + typeof cause.toString === "function" + ) { + return cause.toString(); + } else { + return `${cause}`; + } + } + + private static codeToName(code: WrapErrorCode): string { + if (code < 50) { + return "WrapError"; + } else if (code < 100) { + return "UriResolutionError"; + } else if (code < 150) { + return "InvokeError"; + } else if (code < 200) { + return "SubInvokeError"; + } else { + return "PluginError"; + } + } + + private static metaMessage(code: WrapErrorCode): string { + switch (code) { + case WrapErrorCode.LOAD_WRAPPER_FAIL: + return "Failed to create Wrapper from WrapPackage."; + case WrapErrorCode.QUERY_MALFORMED: + return "Failed to parse GraphQL query."; + case WrapErrorCode.QUERY_FAIL: + return "Unknown query exception encountered."; + case WrapErrorCode.URI_RESOLUTION_ERROR: + return "Unable to resolve URI."; + case WrapErrorCode.URI_RESOLVER_ERROR: + return "An internal resolver error occurred while resolving a URI."; + case WrapErrorCode.URI_NOT_FOUND: + return "URI not found."; + case WrapErrorCode.WASM_INVOKE_ABORTED: + return "Wasm module aborted execution."; + case WrapErrorCode.WASM_INVOKE_FAIL: + return "Invocation exception encountered."; + case WrapErrorCode.WASM_NO_MODULE: + return "Wrapper does not contain a wasm module."; + case WrapErrorCode.WASM_METHOD_NOT_FOUND: + return "Could not find invoke function."; + case WrapErrorCode.PLUGIN_METHOD_NOT_FOUND: + return "Method not found."; + case WrapErrorCode.PLUGIN_ARGS_MALFORMED: + return "Malformed args."; + case WrapErrorCode.PLUGIN_INVOKE_FAIL: + return "Invocation exception encountered."; + default: + return "Unknown exception."; + } + } +} diff --git a/packages/js/core/src/types/index.ts b/packages/js/core/src/types/index.ts index acced58c8d..fd2648df9e 100644 --- a/packages/js/core/src/types/index.ts +++ b/packages/js/core/src/types/index.ts @@ -12,4 +12,4 @@ export * from "./IWrapPackage"; export * from "./IUriRedirect"; export * from "./IUriWrapper"; export * from "./IUriPackage"; -export * from "./InvokeError"; +export * from "./WrapError"; diff --git a/packages/js/plugin/src/PluginWrapper.ts b/packages/js/plugin/src/PluginWrapper.ts index b1540953e4..5e33f30ae9 100644 --- a/packages/js/plugin/src/PluginWrapper.ts +++ b/packages/js/plugin/src/PluginWrapper.ts @@ -8,6 +8,8 @@ import { Uri, GetFileOptions, isBuffer, + WrapError, + WrapErrorCode, } from "@polywrap/core-js"; import { WrapManifest } from "@polywrap/wrap-manifest-types-js"; import { msgpackDecode } from "@polywrap/msgpack-js"; @@ -53,7 +55,12 @@ export class PluginWrapper implements Wrapper { const args = options.args || {}; if (!this.module.getMethod(method)) { - return ResultErr(Error(`PluginWrapper: method "${method}" not found.`)); + const error = new WrapError(`Method "${method}" not found.`, { + code: WrapErrorCode.PLUGIN_METHOD_NOT_FOUND, + uri: options.uri.uri, + method, + }); + return ResultErr(error); } // Set the module's environment @@ -68,10 +75,16 @@ export class PluginWrapper implements Wrapper { Tracer.addEvent("msgpack-decoded", result); if (typeof result !== "object") { - const msgPackException = Error( - `PluginWrapper: decoded MsgPack args did not result in an object.\nResult: ${result}` + const error = new WrapError( + `Decoded MsgPack args did not result in an object.\nResult: ${result}`, + { + code: WrapErrorCode.PLUGIN_ARGS_MALFORMED, + uri: options.uri.uri, + method, + args: JSON.stringify(args), + } ); - return ResultErr(msgPackException); + return ResultErr(error); } jsArgs = result as Record; @@ -92,14 +105,14 @@ export class PluginWrapper implements Wrapper { encoded: false, }; } else { - const invocationException = Error( - `PluginWrapper: invocation exception encountered.\n` + - `uri: ${options.uri}\nmodule: ${module}\n` + - `method: ${method}\n` + - `args: ${JSON.stringify(jsArgs, null, 2)}\n` + - `exception: ${result.error?.message}` - ); - return ResultErr(invocationException); + const error = new WrapError(result.error?.message, { + code: WrapErrorCode.PLUGIN_INVOKE_FAIL, + uri: `${options.uri}; module: ${module}`, + method, + args: JSON.stringify(jsArgs, null, 2), + cause: result.error, + }); + return ResultErr(error); } } } diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index 546b930dd2..d524ce3504 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -18,9 +18,9 @@ import { isBuffer, Uri, Wrapper, - InvokeError, - InvokeErrorCode, - WasmErrorSource, + WrapError, + WrapErrorCode, + WrapErrorSource, } from "@polywrap/core-js"; import { Result, ResultErr, ResultOk } from "@polywrap/result"; @@ -154,7 +154,14 @@ export class WasmWrapper implements Wrapper { const args = options.args || {}; const wasmResult = await this._getWasmModule(); if (!wasmResult.ok) { - return wasmResult; + const error = new WrapError(wasmResult.error?.message, { + code: WrapErrorCode.WASM_NO_MODULE, + uri: options.uri.uri, + method, + args: JSON.stringify(args, null, 2), + cause: wasmResult.error, + }); + return ResultErr(error); } const wasm = wasmResult.value; @@ -175,13 +182,16 @@ export class WasmWrapper implements Wrapper { env: options.env ? msgpackEncode(options.env) : EMPTY_ENCODED_OBJECT, }; - const abort = (message: string, source?: WasmErrorSource) => { - throw new InvokeError(message, { - code: InvokeErrorCode.ABORTED, + const abort = (message: string, source?: WrapErrorSource) => { + const cause = WrapError.parse(message); + const text = cause ? "SubInvocation exception encountered" : message; + throw new WrapError(text, { + code: WrapErrorCode.WASM_INVOKE_ABORTED, uri: options.uri.uri, method, args: JSON.stringify(args, null, 2), source, + cause, }); }; @@ -213,8 +223,8 @@ export class WasmWrapper implements Wrapper { encoded: true, }; } else { - const error = new InvokeError(invokeResult.error?.message ?? "", { - code: InvokeErrorCode.RETURNED, + const error = new WrapError(invokeResult.error?.message ?? "", { + code: WrapErrorCode.WASM_INVOKE_FAIL, uri: options.uri.uri, method, args: JSON.stringify(args, null, 2), diff --git a/packages/js/wasm/src/imports.ts b/packages/js/wasm/src/imports.ts index 9724e92918..8469e6ed09 100644 --- a/packages/js/wasm/src/imports.ts +++ b/packages/js/wasm/src/imports.ts @@ -5,13 +5,13 @@ import { readBytes, readString, writeBytes, writeString } from "./buffer"; import { State } from "./WasmWrapper"; import { msgpackEncode } from "@polywrap/msgpack-js"; -import { CoreClient, WasmErrorSource } from "@polywrap/core-js"; +import { CoreClient, WrapErrorSource } from "@polywrap/core-js"; export const createImports = (config: { client: CoreClient; memory: WebAssembly.Memory; state: State; - abort: (message: string, source?: WasmErrorSource) => never; + abort: (message: string, source?: WrapErrorSource) => never; }): WrapImports => { const { memory, state, client, abort } = config; From b8399cf3b57fbe2ef986f11b05658fcb71f710ce Mon Sep 17 00:00:00 2001 From: krisbitney Date: Mon, 21 Nov 2022 19:22:03 +0530 Subject: [PATCH 06/40] added test for incompatible wasm wrapper version --- .../__tests__/core/error-structure.spec.ts | 37 +++++++++---------- packages/js/core/src/types/WrapError.ts | 1 - .../wasm-as/simple-deprecated/.gitignore | 18 +++++++++ .../wasm-as/simple-deprecated/package.json | 17 +++++++++ .../wasm-as/simple-deprecated/polywrap.yaml | 5 +++ .../wasm-as/simple-deprecated/src/index.ts | 5 +++ .../simple-deprecated/src/schema.graphql | 3 ++ 7 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/.gitignore create mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/package.json create mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/polywrap.yaml create mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/index.ts create mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/schema.graphql diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index f12fef1b01..79d84c6e4a 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,5 +1,6 @@ import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; +// import { buildWrapper } from "@polywrap/test-env-js"; jest.setTimeout(360000); @@ -15,7 +16,8 @@ const badMathWrapperUri = new Uri(`fs/${badMathWrapperPath}/build`); const badUtilWrapperPath = `${GetPathToTestWrappers()}/wasm-as/subinvoke-error/1-subinvoke`; const badUtilWrapperUri = new Uri(`fs/${badUtilWrapperPath}/build`); -// const incompatibleVersionWrapperUri = new Uri(""); +const incompatibleWrapperPath = `${GetPathToTestWrappers()}/wasm-as/simple-deprecated`; +const incompatibleWrapperUri = new Uri(`fs/${incompatibleWrapperPath}/build`); describe("error structure", () => { @@ -54,7 +56,6 @@ describe("error structure", () => { expect(result.value).toEqual("test"); }); - // TODO: edit wasm "Context" and wasm bindings test("Invoke a wrapper with malformed arguments", async () => { const result = await client.invoke({ uri: simpleWrapperUri.uri, @@ -68,7 +69,6 @@ describe("error structure", () => { expect(result.value).toEqual("test"); }); - // TODO: edit packages/wasm/as/assembly/invoke.ts test("Invoke a wrapper method that doesn't exist", async () => { const result = await client.invoke({ uri: simpleWrapperUri.uri, @@ -96,7 +96,7 @@ describe("error structure", () => { expect(result.value).toEqual("test"); }); - test.only("Subinvoke error two layers deep", async () => { + test("Subinvoke error two layers deep", async () => { const result = await client.invoke({ uri: subinvokeErrorWrapperUri.uri, method: "throwsInTwoSubinvokeLayers", @@ -107,24 +107,23 @@ describe("error structure", () => { }); if (!result.ok) { - console.log(result.error); fail(result.error); } expect(result.value).toEqual(3); }); - // test("Invoke a wrapper of incompatible version", async () => { - // const result = await client.invoke({ - // uri: incompatibleVersionWrapperUri.uri, - // method: "simpleMethod", - // args: { - // arg: "test", - // }, - // }); - // - // console.log(result) - // - // if (!result.ok) fail(result.error); - // expect(result.value).toEqual("test"); - // }); + test("Invoke a wrapper of incompatible version", async () => { + const result = await client.invoke({ + uri: incompatibleWrapperUri.uri, + method: "simpleMethod", + args: { + arg: "test", + }, + }); + + console.log(result) + + if (!result.ok) fail(result.error); + expect(result.value).toEqual("test"); + }); }); diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 37e51b5d8e..8a1309dd34 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -54,7 +54,6 @@ export class WrapError extends Error { readonly resolutionStack?: string; readonly cause?: unknown; - // TODO: KRIS - elements can be string literals for prod, but they are easier to read as regex literals static re = new RegExp( [ // [A-z]+Error can be replaced with specific error names when finalized diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/.gitignore b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/.gitignore new file mode 100644 index 0000000000..bbe46fe4cc --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/.gitignore @@ -0,0 +1,18 @@ +node_modules +.idea +yarn-error.log +coverage +.vscode +*.log +wrap +.polywrap +.DS_Store +report.* +target +**/*.rs.bk +Cargo.lock +bin +!packages/cli/bin +pkg +wasm-pack.log +.env diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/package.json b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/package.json new file mode 100644 index 0000000000..600be3a08c --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/package.json @@ -0,0 +1,17 @@ +{ + "name": "test-case-simple", + "private": true, + "workspaces": { + "nohoist": [ + "@polywrap/**", + "polywrap" + ] + }, + "dependencies": { + "@polywrap/wasm-as": "0.0.1-prealpha.93", + "assemblyscript": "0.19.23" + }, + "devDependencies": { + "polywrap": "0.0.1-prealpha.93" + } +} diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/polywrap.yaml b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/polywrap.yaml new file mode 100644 index 0000000000..18270b0310 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/polywrap.yaml @@ -0,0 +1,5 @@ +format: 0.0.1-prealpha.9 +name: SimpleDeprecated +language: wasm/assemblyscript +schema: ./src/schema.graphql +module: ./src/index.ts \ No newline at end of file diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/index.ts b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/index.ts new file mode 100644 index 0000000000..65c1f47ee4 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/index.ts @@ -0,0 +1,5 @@ +import { Args_simpleMethod } from "./wrap"; + +export function simpleMethod(args: Args_simpleMethod): string { + return args.arg; +} diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/schema.graphql b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/schema.graphql new file mode 100644 index 0000000000..1fa8e36696 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/schema.graphql @@ -0,0 +1,3 @@ +type Module { + simpleMethod(arg: String!): String! +} From add143667cb59dcf0e91ab6d17cbdef16a02adba Mon Sep 17 00:00:00 2001 From: krisbitney Date: Mon, 21 Nov 2022 19:25:35 +0530 Subject: [PATCH 07/40] removed unnecessary files from deprecated wrapper test case --- .../__tests__/core/error-structure.spec.ts | 12 ++-- .../wasm-as/simple-deprecated/.gitignore | 18 ------ .../wasm-as/simple-deprecated/package.json | 17 ------ .../wasm-as/simple-deprecated/polywrap.yaml | 5 -- .../wasm-as/simple-deprecated/schema.graphql | 56 ++++++++++++++++++ .../wasm-as/simple-deprecated/src/index.ts | 5 -- .../simple-deprecated/src/schema.graphql | 3 - .../wasm-as/simple-deprecated/wrap.info | 1 + .../wasm-as/simple-deprecated/wrap.wasm | Bin 0 -> 31315 bytes 9 files changed, 63 insertions(+), 54 deletions(-) delete mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/.gitignore delete mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/package.json delete mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/polywrap.yaml create mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/schema.graphql delete mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/index.ts delete mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/schema.graphql create mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/wrap.info create mode 100644 packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/wrap.wasm diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 79d84c6e4a..fa33404fbb 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,6 +1,6 @@ import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; -// import { buildWrapper } from "@polywrap/test-env-js"; +import { buildWrapper } from "@polywrap/test-env-js"; jest.setTimeout(360000); @@ -17,17 +17,17 @@ const badUtilWrapperPath = `${GetPathToTestWrappers()}/wasm-as/subinvoke-error/1 const badUtilWrapperUri = new Uri(`fs/${badUtilWrapperPath}/build`); const incompatibleWrapperPath = `${GetPathToTestWrappers()}/wasm-as/simple-deprecated`; -const incompatibleWrapperUri = new Uri(`fs/${incompatibleWrapperPath}/build`); +const incompatibleWrapperUri = new Uri(`fs/${incompatibleWrapperPath}`); describe("error structure", () => { let client: PolywrapClient; beforeAll(async () => { - // await buildWrapper(simpleWrapperPath); - // await buildWrapper(badUtilWrapperPath); - // await buildWrapper(badMathWrapperPath); - // await buildWrapper(subinvokeErrorWrapperPath); + await buildWrapper(simpleWrapperPath); + await buildWrapper(badUtilWrapperPath); + await buildWrapper(badMathWrapperPath); + await buildWrapper(subinvokeErrorWrapperPath); client = new PolywrapClient({ redirects: [ diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/.gitignore b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/.gitignore deleted file mode 100644 index bbe46fe4cc..0000000000 --- a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -node_modules -.idea -yarn-error.log -coverage -.vscode -*.log -wrap -.polywrap -.DS_Store -report.* -target -**/*.rs.bk -Cargo.lock -bin -!packages/cli/bin -pkg -wasm-pack.log -.env diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/package.json b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/package.json deleted file mode 100644 index 600be3a08c..0000000000 --- a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "test-case-simple", - "private": true, - "workspaces": { - "nohoist": [ - "@polywrap/**", - "polywrap" - ] - }, - "dependencies": { - "@polywrap/wasm-as": "0.0.1-prealpha.93", - "assemblyscript": "0.19.23" - }, - "devDependencies": { - "polywrap": "0.0.1-prealpha.93" - } -} diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/polywrap.yaml b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/polywrap.yaml deleted file mode 100644 index 18270b0310..0000000000 --- a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/polywrap.yaml +++ /dev/null @@ -1,5 +0,0 @@ -format: 0.0.1-prealpha.9 -name: SimpleDeprecated -language: wasm/assemblyscript -schema: ./src/schema.graphql -module: ./src/index.ts \ No newline at end of file diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/schema.graphql b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/schema.graphql new file mode 100644 index 0000000000..8ca05a8d80 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/schema.graphql @@ -0,0 +1,56 @@ +### Polywrap Header START ### +scalar UInt +scalar UInt8 +scalar UInt16 +scalar UInt32 +scalar Int +scalar Int8 +scalar Int16 +scalar Int32 +scalar Bytes +scalar BigInt +scalar BigNumber +scalar JSON +scalar Map + +directive @imported( + uri: String! + namespace: String! + nativeType: String! +) on OBJECT | ENUM + +directive @imports( + types: [String!]! +) on OBJECT + +directive @capability( + type: String! + uri: String! + namespace: String! +) repeatable on OBJECT + +directive @enabled_interface on OBJECT + +directive @annotate(type: String!) on FIELD + +directive @env(required: Boolean!) on FIELD_DEFINITION + +### Polywrap Header END ### + +type Module { + simpleMethod( + arg: String! + ): String! +} + +### Imported Modules START ### + +### Imported Modules END ### + +### Imported Objects START ### + +### Imported Objects END ### + +### Imported Envs START ### + +### Imported Envs END ### diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/index.ts b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/index.ts deleted file mode 100644 index 65c1f47ee4..0000000000 --- a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Args_simpleMethod } from "./wrap"; - -export function simpleMethod(args: Args_simpleMethod): string { - return args.arg; -} diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/schema.graphql b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/schema.graphql deleted file mode 100644 index 1fa8e36696..0000000000 --- a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/src/schema.graphql +++ /dev/null @@ -1,3 +0,0 @@ -type Module { - simpleMethod(arg: String!): String! -} diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/wrap.info b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/wrap.info new file mode 100644 index 0000000000..3ea79091d6 --- /dev/null +++ b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/wrap.info @@ -0,0 +1 @@ +��abi��objectTypes��enumTypes��interfaceTypes��importedObjectTypes��importedModuleTypes��importedEnumTypes��importedEnvTypes��moduleType��type�Module�name��required��kind̀�methods���type�Method�name�simpleMethod�requiredäkind@�arguments���type�String�name�arg�requiredäkind"�array��map��scalar��type�String�name�arg�requiredäkind�object��enum��unresolvedObjectOrEnum��return��type�String�name�simpleMethod�requiredäkind"�array��map��scalar��type�String�name�simpleMethod�requiredäkind�object��enum��unresolvedObjectOrEnum��imports��interfaces��name�SimpleDeprecated�type�wasm�version�0.0.1 \ No newline at end of file diff --git a/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/wrap.wasm b/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated/wrap.wasm new file mode 100644 index 0000000000000000000000000000000000000000..6d97abc359da9792f8359fe835028334d8afdfe8 GIT binary patch literal 31315 zcmd6vYm6M%mEUhwb@x1aNH)olM3a$b(3X1lUCqCSei>Ss+}1 zFpwZ0m?*#hx%XC8Pxp{RQXkU8>Z-1~=RVFokN-LM7Tw$DKFpkR+0p6mXBV@J-_I|` zZ}C;MoBJ((@GIkR{NqkXces}?w_6`Ay?tipt+#Y~>+K($URrijc5h3(_ra+P zrx#DW_4d;H=Ui@2W{00Foj7;?-16H?%Ws`O_0b2X-gSFt8~0D2Nw@c{F1~ca zitk@lJl;OArK;uq6U%C0`Gh+-nY}PIkvX4rvoBBo`0r=?e$vj`7k3vvf2DP7cWZZR zPwxE8;(YGxa!~NQ?@CUsex}o8zv$+x30f6_is#+k#qrj$oWI?)w79VlPrI&LxVy4~ga;I46cI{rSds&y8xp%vZ9rTgMt~c4Y-#&X~L$9{gWj>oY>!+9f%1R4H=+_d3 zV)fgWxka1tXK#6l-!s*3TIctc3;s^Fw8Sja5965A$pwzl0tC0L({)X$-#iUN>0q5PBVR^3CUubIL-DabV6$HC$60^-9DQ`LOYdYxL&K@(yu5Nt-c#Bn7!gN zq)0+!Mfp(`&7ufpL4@c_6sCS#ZR8axqEu$(gjmpnhpCG0rfxqdW7!(d!B*g4D{v4V z!ojj7M$T|*&*wc(6Z83OV(T`swfi4X#nnvh)l40u?)1!fdp|a|sv%#QEVCVGobz0{ z9qYaeN_X?Q&w-tXmEXZ7%gjg!0IIApz;AiC-N-OoPztazPCUSgIj zqv5Yj`8!j-@1N{dqsX_1;k#})za-1hTlSk4{F$=@)wNYuzO^`TyED|awJ>Ne3<}v1 zI9z6no~9Pi=^vf-y$gfvwSI>OS=rff3}yJHq%2<01WGoSF~6onZPT|b=o|WkT9+u~OgJOHowzq>8B518@sFKud(`za2BStByd3?V2BigUp?GHZ{Z<~6B;Y3le{MD zzCbbIaqcIpg4?QsGofHFoMyvm8)Q2xvTfDXY<1O(SNO}bejB|IE(ksAX9b+`<(ufL zd)XZ>=#oy@K%Hnwr)?^-*cWTc=rOz9E5$uQn3EVmVdzVzRzr*`)HWnW{Z_;1`YGQ+ zj^IqCHEwM=deKc{U-FCSP^EQ##<#78sCW7Hk{*Gsj$`h242q!NOj;}XHvA$)xe--v z&4>cH0(gbFvq`gGG~4n;QglCS(9C&N9-x`4pcx>tH;p_aTSD zt*-xh$6t42EoB6&NChm&mlq6keIDBW-^Pr2vc?KoFm9aI0kY&(8Ei3ruuRs_VE_BN z2TLCw)S@SoHW_i|)iF4;VZ-ZTVu?!|ZEeHXnA)cD^$0#1ikH=jC;nk`<@nDAkW}fL zroVK(ncC06KgBD-qexL27!pOlsQkyR zY;rYf*g0M(fG@#fahk!4bD9O!aM}hS<^)u)yx|(0#3oE8;TXfo;#eu0W-1c~Tmb)# zNgd#VG=>Es_)}dqi#tH?iJHYlL~xMpyg(?E1G5qjF*B_$ZMEd=<1~OUUem0J z(>rQ}(|DdOGQDZSQ1EEkE@Ekl=7bg7%M88fQd^jBx_~7;9tKA3J1kVijiPEe+JQ`_ zLan+j>yueLg4_G!!Lc)q9R=ykaI)wD=t=azFM^0|NoRIOo%x5;U`H52{CAeV=8A#{ z?!{%SspeXjNKn~&-We{)>3Bc401=aZmJUM>L^f|JVKC8ym@x)3%+;g}y4{1C1{zMx7N}vk9%w?Qp8xY(}-Q7}k zH;1IB?)Dnp?a*Cr@m%K-J1xF&Uspo!@Xgu9ob+Ukj)Crtv zbaH}DauaShI@t{+;dY~wo9JX+a_vr9?x{BIE(f!vsfn$yZ6apxL zw|o)tl!}fJxNLdx4VhGK7QG%;Qb=u z!ni)ECv}O?y=G4|%KCX-1AYB;43L|xp-G+FQo_(O8J5sc!jNA9#9G=RU&iT@e-p5% zv*N??Ri~LNY+0yMG+-Vu1o!NTSj~5XNy=7lvuG*+;(H_x72Vgli~?Z^PTicarXOF7 zHx*d8VOYj1*s}z>1$5q4Nf_Qf{z*nT<&C3t8wj~T6jn$J2WF? zlyy=tz-b2I;nV}ya*9dkZGBR)d-9$ADV@sxv`%w`l!Rg5J;t4!weq?y`D3>%ruaysJO-I`63Yf_;Bzi+95xoi{BMg*o*AfzPg9#!Zi=#Z~`*bYEHLcMU z>qMPm6~dZa7<6PLCrYsWs)TC$HD+UiMyEnTbudA}_Aw-j+^VHiJ9C0EKu&&D=vylg z0@st$+~q;>I`cg#4Kk*Z&8lRyu)JhURsiWkUXqyCVP5E5r?;8ZUc&jVoUZY`cjbg3 zXhu+XBF$uLQTYiasvatPN$jHRC3Zx4K`?!flNJUzF<4Sw<lhgEkUUI@l%q8Qn zCmM&_t5?gIXwBxxcB1t1mE!G$P!2)iPI==v3=564jL{|C=2@G2UKe z2xMl4o5-bMJGhIL#eGp{TH#A;74)x=m(_A!&IRrS*WiNx%>7El`fU~l?I9~)wv=Q3 zHD`(JENgqV?x-akzw4~;*yOtu;V{1!Y07f}rUJdg`YyXt8vO(cpr(ttCU28kbO*xa?=h zL=L;mA;n-DR`d#y#`wdP6{7`-mVU629k-X1f{4Icpna;Om}%`&HI+5Wz` zsM?TWv$uH@>P4(XEmF>17Y3-aM0Up@^H=^V2ta#Ce&)h?`b`Y4BtdLtw*psxI`QU$ zMJg+Q_rG90Rr>mmzy7B0Sh5vTLr>pqs5&Qk@{cOf+bK*N#t(GvZ4M73Kk#H`^VYd~plrF=8$wovoU4WRUrdh%TXS`=)r6Cl zLD%X5Hx*@ZQk2C>Q5GjfS)3GQaasrBWCu>oLM+Cv-nM?;aG-gAI>DO$q|fn`mnZ8P z>iE{{gNap>2FfUKD}qdQoJB;Hf&-siO!T=dpXL>KVlPP90kgeoAqh~zCJ1bCQdpDO z$(bD@1Q{XZvnVGP2NRkh)Xb(>$stW7Jy4X9)&biA(N~I6CktTnPXRQM0BP&rWnh+Z!6BMrXYd>*f>$D_<)7hnKy@{;kKVht!kz9 zr=|b7^IsywAY|15+N+{Ypm}5zaq5}lsQ1E zTYyn z!Ayg|%4|qw0I8QYS(yz(WvWVBeKKxszp*Aj)>Y@mq4E^j)F=`pc~hf^345<9l7zk2 zD3XMI!eq-LA~n6aA|jt-O>YiFrXi0K3dIoJl5_VD(UUQqi?R71O^oi79E+}3r#(9X z9^5yZ*EE>y%Gf*td&DqcREJbp*wtaDdJ~pzvI$Ekr7;YXlg`E&g$&`61SuJk+GzfC zV~`1iEHE0gSp?atEE=_5n`SbgUtOlQXq1s`70IO4^;A|1x4)eN(P~)%fbg)@%B+rP zwdx|Z)yn)&pR_X3YSm?G8)GBxq07m@)Sap9hM`CGH=2yPo}}f+;N7LCtgWc!b(u<$ zNt$X*QIYF*YDE@GRXb6U>pCjkrJd`cov6F9Of;==d#hQXT9-q+QIUurnE@%1 z?$$+W-32VlF&926Z(XKVUNC>}DT+kpO+^R}WLez~j3yYkskyk!f!}T^q^P`&bY~b7 z#!dLePV&Cw-pJ-g5f-RnW38c~hlcXFDy89)9;XQK&)N^F$1yx!uO276CNEgB#7H6+ z_wkyxBul7G0pq+1#{w|qU9413P6j5xQ6Q^?FDR4{08YXVKt zPNlg*v{pw12KAU5dh4%;c2+5Ft8&lB@LE#-rq)_JmDX16H1*ba($L$a9IF(Uo&ME$ z941Z5(bQWeag}nkd_!&hojJeN)&eTsr2!_K=>DO0k0R-ocOE0~-M^(3P!qd)iLJ5T z)C{cd>!DJ{3xQPtKx|%1>b2e2!BRIQshQxMLVi*LQUf)kbx7rL#mr$P(4Kz<`ZZJi ztuGREm9<9-3=&(Z4lsqMQ_3>jR=SdXJ$cx6i|uep>DPJ}B0W!=)BYeE<9G1vX132Tn6z9|!5G-;!q22l1~y??K#on$5WP9s~o4gAgkxn0xX!F|O z)O2@d!^Ta$&0Dt4&dxse*yE2sLAd!W$;_26@pVmKxAb-C%Y0qe*UEFc($^h*U3*?v z`nvWNzHaI3(hGcD*Vi3=UE8ZGeO=q9D}7ztqbq$~p|#LK*|nm!3b>>y^VVTEVt8Dc zJ%4S#0k$ley;KIP*F;)kuZq}OroVK+{{y1NdI7gnhti^ZmV#gmTN2e7DazxVgegj< z_5PYG%T@1OO?D{R(0?-K700nDJgqxe3eTLwIyTvBUA@7@e$7QTW)`BSpP(_S1Ol9hX*khixwrD#IqfFbaP2m}3gP*cgNEQTA3_4H#}61W2youh0|T<27LzbYd(Bes1N&!Do!gVX_z4GqtF)VG*9+|C9vmD#_W|S>*RL5 z4puFIG1A5sB1Ru}$aC6Bj+>J}Z9;i#tR`sBO$0-x)Kh5+c?)L)41!ISNVobEu{0Ac zyJ;tO>FC!a$x?eCh9tWJVK&d}rnslYM=cm|JvksXe&Kb2Z86?pGjhvD13vC`40vPD z*Hk@SWNVZX-Rk$EE^*4z&U>PaFa~z%FC$NuDtQ$_iI>`R(_w25+Nb079m>SE>4UcW z=#^6oRe?_IKx%j*9hx}?zNy7?+hOiT=~Z=@9iO`iJGYV6vgE`s{u#SY>n zrH?R0?Ub08#B-Cxyj(tFx?>l$mYm4o=GGNP>nts9Nk&?+<@g^rTE230rC;;9<{tf*U8Zfb1nwTR}RN?MO#Ya#;s zM(Y*o!dl*X$-?%Cx~js0C_*M|X0caStQSPK7nbaeIGI;_<%zdQ!3Q$4*!8O8f#)@6 zY6zEubm?0}a8+}4(Hgtu{?c`4;do5gRi%eIS4H1+F-6~v?g_qF_mY>0;*w+mlhJ7; z01JQpI>9iQDkcHb##R%4ea&dAQLF^$!oGsIW@6813K0$xp9Hy)Jv2d%xkFZ8CCGtS zO(90-09f$eQanyr4Ed|?DtQv_A{h1UBHWIoZSzXZ%1{TL?rPD@T1`GZ>3&pD`XXPO zReEp6Tx8L5R(fA2JzekFv`_)S#wNByT21acS-D194a=%I0vHIz;0a|D!Jn{?6BE|s zWcy$N0dl!)4IXj1zqjR{_Hr$kd#{ZO>#&!=j#e^w{?>-x6F4aBUyN%6Z+TwXF7^O! z8bQGIq=&#*$c}2Kgw|M8j%tL9tGuUP*k+5d)fAzUEo@db0mRJnf_fs<$3!-Lsd?t! zu2tSN#eg;+3`>T!_KL^YUDt;qS|@8!dkccdI`-X~i!o1Hn;%D%TJ0YI(AAqMwCBB# z1GVJOO7_a2pb86J*&OyCiILd_bxeQJewYycCo8ADq{PO9Y(p^vtLk=ZTc!w?|IsH* z1<`S{U3-f+gEFdHO<$WP1-WeL4fM9MZrt4v69r;Ur8(+AWL9mk95HU*SQb%GXPciYP7}^4AZ6DB@~k3Q6>29@Ga#%pS(PV3-|ae!U(^G$3g5 z(|IbYHWX+$_11wf__+2M_f`Z^GH#5(mdHKQ!*%umQHK)yn zI$JCnSZr+WiWFMQZ?CGj{#7&7+Z#2QsoPPNm?72ySbG+xD_g$05i`_i6ipI?u&)gF zN^$U*8S9;)vS6&YT5UVW)n?1q>fe)9>pkDG@=rF(SH?YZCbUj2SgqS>MwL8ORZ_-m z%TtX?lCVtp(!ywqjP1Q~?KdpzstA|mj8)^VR3rD=y)XG)slI4Ejlr<5WB(6HS(VOu zTVY@sN}J$_K@chWZ~1;I=TFKR?s1idoIy&I*?)I3m%5`Y1pgg@yysklHoYE05<@V?M7KFJU##fyl<@J3 zi8k`_5i(uis(ZdMlrWbiCp`b3R41EQy`bBOErmAwSf>zM3dsh19ACfG{|v!R#g?2D zTXI?lR$@{KVPtt$g)K-Bi7=nf5k{zKpe6Y&3R;#7PUf34*Dn&VLY4{)Rm2@Nz?rCl zmc+N0dwICv08vwZ*ne=A5LI*!{&!xo`~(Qp(=>jSobo3x32^5ZNQ#osAf){!`%B?T z1$QRFDjnrnE~(@oJ3eqqx`Y%3(^xKVz z!F&Nts;RO1tVlH#gfi3Eh9!*J0Lt2eg z()P~Rd(TmIJ9RdUCM^RSMMV0E!Wlt?`*HZuKs>TYDCe6jXxahxPqWegoUySque~aL zgmu_1uC?nUZ|?fI_R?CrK2WTXa0vgGn|@eMz>?YzkabLBu|?PJA9Y6Mc#V;)W0QgL z+~2(J<`9>{lv7qR>utoU*5aEg2CT65kS1IYE71i@in+7(h-t2mn)~OgSGUH5}+B4wM$;n zF^b`(%i$)#9F4V0-rO;YnEXP*N814Zv0E*93cdAXBr$b0mZT?=D%{j7M7>S0I_9Lt zlFW*vO7-aVAtQl7(KL;AY@0}`?QZt&kH*e(hxOYn^1)tQoOZogk z%D2#-ZuhT!{VnG|K{j@}`jgd{^_9Om***5P`nOZ5IgVd(r`#F$w)?;>xfAYPtjale zp6exd+P%;HW$soVe|xgK_qpn)vs%>lUvVd?;Ro&osF$JrfLh;oU*YVw_Ln)I=6sU7OVkqE_czDX^3+hi>7lmO*CpsbrsebQJ^oJcd#DfolkR9Ongg}{F6Dnn zyQiUEw)1;@t48&rIR`h^sHL8RWqNT2ns<$Zp;tpaVML$I4EIHB(wt-*(;V+|{AuAx zV>ox^w=;L_};{_ZL&wYiPs z9K8RKo{RTyaESM(ttUM6S2qmH`B0tdhrh9*d*)>N#aVTKo8y~o0cAux!z`YmY|2=zDx8gtDILuA8s2{K0+Sep{-NYD=j(CFG>4S zsL9{mI6Okt(&A80s*k_CX_)6KB^~@VB=2MDJK^3Ml3t_sH#T)wvL98o1jcBR)q(F? zcY@OHanI76gyvm3Es+drcpf%MB_y05(sd1?q{$QUdzs;#uo9))Z&VWM(j9Q~b*uhH zZfC0QfTWV7+j3^TSA&-WfJ{cD|$KG^9XI`-GUMlu-bFbUSv7h4r$3c!m z9P=E9IgW4~wG#XI-bcB8l-ozSeU#frxqXz|N4b5JQ!Og7-~JBpeURf2$2`Yjjw2jL zttHi>5(n(>Am4{L<~a^?9N{=>y-`c5MI{c}-yy!|ISz9i;W%nNR&UgjYEg+p_BYS> zVU8mlN1@Wl)NA!ft*NF%9Qr%VafIV2R~o%Wre3Q@YE3n%l>Uxz9OYXa(&#lZ^;$hr zYpO}5^mmlMz(sLLqu0pPYxPL2sV0?j{?pm+mC2ow>-f>^NzT_hReO=hy~xF0)Mqa; zwigN6i^0_-uV)JPLB9|BebDcNejoJvpkMFQ?uULq^!uUT5B+}V_d_q?TF;apfc^mV z2cSOy{Q>9?Krg{uk0>03{vh-Rp+5-yLFf-cFQHzKh#Z3c5cG$jKLq_D=np}^UeTF{ zejfUH=;xuIhkhRV^@`MC=nq4G82ZD|ABO%g^y?M1BhVj#{s{C(pg#ir5$M+|f=8i0 z3jI;&k3xSG`lHZGsw5ejUCofVEKX^(>akiC<=gi61HJ`ZCg;omc(gsOCwgV)v~Dd_b$f?j`z5d zHb|l+m69w;hh|u_Bu=THMlz)oZmZ zYW)fF1TB;%NgE{5l1fRIq(d{TSrVtkEsa>cR?DIu`uLkWyWjuy#w}TiZ*oY-Wm0AS zWU6FV#7~Vy^}G*g_`of=MSRGI_>j{8inDkS`K$9lypQl{AG_ZOHTzGW9Q8*f&-8zO zf6Kk;e$l;-JADJF`aQhltL|6b@nLtYdi>od$Gc?t$K;Yv+ZY8wmO~Bx>Qkd_%NGs< z$W+6(piF?Y0SNrnb9|p*9?x+1V>yp6hBaXo|_2gqjUU2RVkm@7O=V0gac7B1Yb101BKQa7; zF?iJ?xT@95yGPltsaLddf*O7Wt_UI?NfbK!8*5Pgn(=9jP#e|VF;trBTB+V#tx~jg zb#AnW!&GAw$={qC<))&BN8@IKvgd~5K#fixf8&dzeci|d0q=9nh-6C;edy!wj-gV_ zr6_TE{0xXMPmi{xnH#1OZftz~&2>=;<2FA2lV?Uv_c025hw;c93(!l`Mzu^vQW<~{ zftBtVBvg|73>+*QO>pUNKQlTaMUk)2#(AVr!N)UE9z|~-e|gWS8B!Kum`X+{Wicw+ zP=mhx%{`-SDks&8lM8y?J-o+gW(?j0A9&P z#4-BIUmES?l7t1St6v_Szj9<;?=hy+NX{}q zAyOJ!;+MbufVC)!ileRT@mHT4<<{u9&sR1vjNs;TqazrVz(iH?t8Q0U{_^t=Qp;E_ zNRA((JzT4;YLx!w3!^+P zVg9fVy@ED;4N#w>!YA2w!C`?%c}ro4k}}Grm61B+z3%QG z_3Xb&E05;c|2(a$7s@JzJ`5$P{OtpyV|#^GAI1Z&_C+Q2*vH>EI4adO5_lOeDp*Yh zzWMQY$507%$Y(BcoCYPHYEtOhUp>_Qj%Fu)GA@J_6wt`ey<=h5@H}YdhW&l=?6DSq z_t08cSDF7*>hk<(sTpg#_R@p~^P@c!4oz(a`3T5BawMtJ946^m-CJwje{#5cExQ_f zqLT)GiR0TbASi(+r>zn(TjHnqN0G5&mawQHQ4C!TODxK{2D&qxmEu?MAo>rvde7LV ztMH_bf67%cRx?^s->Q%Q^vI~#f1Ae-CiaS`6^*`6XfPbzRk2moBp3eb(NTevne~iF zA&4N~XiRtW=xATR1JxT=Rw?)(R+_N0^rl{_lHk41ofN={&(F|~?&|uxk?W#o9i#d2 zH(nf7(PNZ22lYD^Dy5+54Q4AX4`_Kwvs^DC2xNp=9>WuV^{b;}`*m9R?y6eS823>B z2V^J0Z)M^iVsXM>{@QpmuRu_8mRcJt@`+3S=GVqV&ELOg=U3b0ha5oag3kWt*N3(L zF>Weq&h#{W{N1mQpH7LbB6oQV**3+{@+BcY(D?#)7nu&-mvzvZOj?VSS8IIy&0iSq zi~692hoN%*?y6FcQ$qHtw~H@6XLR Date: Mon, 21 Nov 2022 19:30:30 +0530 Subject: [PATCH 08/40] added more info to comment in WrapError.ts --- packages/js/core/src/types/WrapError.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 8a1309dd34..7f9359e13d 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -129,7 +129,7 @@ export class WrapError extends Error { } : undefined; - // message may be a stringified WrapError + // message may be a stringified WrapError due to current string-based error passing between client and wasm let cause: WrapError | undefined; if (this.re.test(message as string)) { cause = this.parse(message as string); From 908fa7b7e45d9cd8aed5d83baee6b73ec04a57c8 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Tue, 6 Dec 2022 22:21:42 +0530 Subject: [PATCH 09/40] moved wrap error structure updates to core client --- .../js/core-client/src/PolywrapCoreClient.ts | 82 ++++++++++++------- .../js/core-client/src/UriResolverError.ts | 18 ---- packages/js/wasm/src/WasmWrapper.ts | 7 +- 3 files changed, 57 insertions(+), 50 deletions(-) delete mode 100644 packages/js/core-client/src/UriResolverError.ts diff --git a/packages/js/core-client/src/PolywrapCoreClient.ts b/packages/js/core-client/src/PolywrapCoreClient.ts index b08e57d4fe..9a3b60d443 100644 --- a/packages/js/core-client/src/PolywrapCoreClient.ts +++ b/packages/js/core-client/src/PolywrapCoreClient.ts @@ -1,4 +1,3 @@ -import { UriResolverError } from "./UriResolverError"; import { PolywrapCoreClientConfig } from "./PolywrapCoreClientConfig"; import { @@ -27,6 +26,8 @@ import { InvokeResult, ValidateOptions, buildCleanUriHistory, + WrapError, + WrapErrorCode, } from "@polywrap/core-js"; import { msgpackEncode, msgpackDecode } from "@polywrap/msgpack-js"; import { @@ -271,7 +272,12 @@ export class PolywrapCoreClient implements CoreClient { // Parse the query to understand what's being invoked const parseResult = parseQuery(uri, queryDocument, variables); if (!parseResult.ok) { - result = { errors: [parseResult.error as Error] }; + const error = new WrapError(parseResult.error?.message, { + code: WrapErrorCode.QUERY_MALFORMED, + uri: uri.uri, + cause: parseResult.error, + }); + result = { errors: [error] }; break err; } const queryInvocations = parseResult.value; @@ -301,13 +307,13 @@ export class PolywrapCoreClient implements CoreClient { // Aggregate all invocation results const data: Record = {}; - const errors: Error[] = []; + const errors: WrapError[] = []; for (const invocation of invocationResults) { if (invocation.result.ok) { data[invocation.name] = invocation.result.value; } else { - errors.push(invocation.result.error as Error); + errors.push(invocation.result.error as WrapError); } } @@ -316,10 +322,16 @@ export class PolywrapCoreClient implements CoreClient { errors: errors.length === 0 ? undefined : errors, }; } catch (error: unknown) { + const unknownQueryErrorToWrapError = (e: Error): WrapError => + new WrapError((e as Error)?.message, { + code: WrapErrorCode.QUERY_FAIL, + uri: options.uri.toString(), + cause: e as Error, + }); if (Array.isArray(error)) { - result = { errors: error }; + result = { errors: error.map(unknownQueryErrorToWrapError) }; } else { - result = { errors: [error as Error] }; + result = { errors: [unknownQueryErrorToWrapError(error as Error)] }; } } @@ -619,7 +631,7 @@ export class PolywrapCoreClient implements CoreClient { uri: Uri, resolutionContext?: IUriResolutionContext, options?: DeserializeManifestOptions - ): Promise> { + ): Promise> { Tracer.setAttribute("label", `Wrapper loaded: ${uri}`, TracingLevel.High); if (!resolutionContext) { @@ -632,34 +644,43 @@ export class PolywrapCoreClient implements CoreClient { }); if (!result.ok) { + const history = buildCleanUriHistory(resolutionContext.getHistory()); + const resolutionStack = `${JSON.stringify(history, null, 2)}`; + + let error: WrapError; if (result.error) { - return ResultErr(new UriResolverError(result.error, resolutionContext)); - } else { - return ResultErr( - Error( - `Error resolving URI "${ - uri.uri - }"\nResolution Stack: ${JSON.stringify( - buildCleanUriHistory(resolutionContext.getHistory()), - null, - 2 - )}` - ) + error = new WrapError( + "An internal resolver error occurred while resolving a URI", + { + code: WrapErrorCode.URI_RESOLVER_ERROR, + uri: uri.uri, + resolutionStack, + cause: result.error, + } ); + } else { + error = new WrapError("Error resolving URI", { + code: WrapErrorCode.URI_RESOLUTION_ERROR, + uri: uri.uri, + resolutionStack, + }); } + + return ResultErr(error); } const uriPackageOrWrapper = result.value; if (uriPackageOrWrapper.type === "uri") { - const error = Error( - `Error resolving URI "${uri.uri}"\nURI not found ${ - uriPackageOrWrapper.uri.uri - }\nResolution Stack: ${JSON.stringify( - buildCleanUriHistory(resolutionContext.getHistory()), - null, - 2 - )}` + const history = buildCleanUriHistory(resolutionContext.getHistory()); + const resolutionStack = `${JSON.stringify(history, null, 2)}`; + const error = new WrapError( + `Unable to find URI ${uriPackageOrWrapper.uri.uri}.`, + { + code: WrapErrorCode.URI_NOT_FOUND, + uri: uri.uri, + resolutionStack, + } ); return ResultErr(error); } @@ -668,7 +689,12 @@ export class PolywrapCoreClient implements CoreClient { const result = await uriPackageOrWrapper.package.createWrapper(options); if (!result.ok) { - return result; + const error = new WrapError(result.error?.message, { + code: WrapErrorCode.LOAD_WRAPPER_FAIL, + uri: uri.uri, + cause: result.error, + }); + return ResultErr(error); } return ResultOk(result.value); diff --git a/packages/js/core-client/src/UriResolverError.ts b/packages/js/core-client/src/UriResolverError.ts deleted file mode 100644 index c58f024303..0000000000 --- a/packages/js/core-client/src/UriResolverError.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IUriResolutionContext, buildCleanUriHistory } from "@polywrap/core-js"; - -export class UriResolverError< - TError extends unknown = undefined -> extends Error { - constructor( - public readonly resolverError: TError, - resolutionContext: IUriResolutionContext - ) { - super( - `An internal resolver error occurred while resolving a URI.\nResolution Stack: ${JSON.stringify( - buildCleanUriHistory(resolutionContext.getHistory()), - null, - 2 - )}` - ); - } -} diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index d524ce3504..20d516d248 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -223,12 +223,11 @@ export class WasmWrapper implements Wrapper { encoded: true, }; } else { - const error = new WrapError(invokeResult.error?.message ?? "", { + const error = new WrapError(invokeResult.error, { code: WrapErrorCode.WASM_INVOKE_FAIL, uri: options.uri.uri, method, args: JSON.stringify(args, null, 2), - cause: invokeResult.error, }); return ResultErr(error); } @@ -242,7 +241,7 @@ export class WasmWrapper implements Wrapper { state: State, result: boolean, abort: (message: string) => never - ): Result { + ): Result { if (result) { if (!state.invoke.result) { abort("Invoke result is missing."); @@ -254,7 +253,7 @@ export class WasmWrapper implements Wrapper { abort("Invoke error is missing."); } - return ResultErr(Error(state.invoke.error)); + return ResultErr(state.invoke.error); } } From 97d4ceb448f9ba52d9c1a0d7a8300c832681bf5f Mon Sep 17 00:00:00 2001 From: krisbitney Date: Wed, 7 Dec 2022 22:32:38 +0530 Subject: [PATCH 10/40] improved tests for wrap error structure --- .../__tests__/core/error-structure.spec.ts | 105 ++++++++++++--- .../js/core-client/src/PolywrapCoreClient.ts | 112 +++++++++------ .../src/algorithms/GetImplementationsError.ts | 5 - .../src/algorithms/get-implementations.ts | 31 +++-- packages/js/core/src/algorithms/index.ts | 1 - .../js/core/src/interfaces/uri-resolver.ts | 6 +- packages/js/core/src/types/CoreClient.ts | 9 +- packages/js/core/src/types/WrapError.ts | 127 +++++++++++------- packages/js/core/src/types/Wrapper.ts | 1 - packages/js/plugin/src/PluginWrapper.ts | 17 ++- packages/js/wasm/src/WasmWrapper.ts | 18 ++- packages/js/wasm/src/imports.ts | 2 +- 12 files changed, 285 insertions(+), 149 deletions(-) delete mode 100644 packages/js/core/src/algorithms/GetImplementationsError.ts diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index fa33404fbb..9893801cfa 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,6 +1,7 @@ import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; -import { buildWrapper } from "@polywrap/test-env-js"; +// import { buildWrapper } from "@polywrap/test-env-js"; +import { WrapError, WrapErrorCode } from "@polywrap/core-js"; jest.setTimeout(360000); @@ -24,10 +25,10 @@ describe("error structure", () => { let client: PolywrapClient; beforeAll(async () => { - await buildWrapper(simpleWrapperPath); - await buildWrapper(badUtilWrapperPath); - await buildWrapper(badMathWrapperPath); - await buildWrapper(subinvokeErrorWrapperPath); + // await buildWrapper(simpleWrapperPath); + // await buildWrapper(badUtilWrapperPath); + // await buildWrapper(badMathWrapperPath); + // await buildWrapper(subinvokeErrorWrapperPath); client = new PolywrapClient({ redirects: [ @@ -52,8 +53,14 @@ describe("error structure", () => { }, }); - if (!result.ok) fail(result.error); - expect(result.value).toEqual("test"); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("UriResolutionError"); + expect(result.error?.code).toEqual(WrapErrorCode.URI_NOT_FOUND); + expect(result.error?.text.startsWith("Unable to find URI ")).toBeTruthy(); + expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build-not-found")).toBeTruthy(); + expect(result.error?.resolutionStack).toBeTruthy(); }); test("Invoke a wrapper with malformed arguments", async () => { @@ -65,8 +72,16 @@ describe("error structure", () => { }, }); - if (!result.ok) fail(result.error); - expect(result.value).toEqual("test"); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(result.error?.text.startsWith("__wrap_abort:")).toBeTruthy(); + expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); + expect(result.error?.method).toEqual("simpleMethod"); + expect(result.error?.args).toEqual("{\n \"arg\": 3\n}"); + expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/msgpack/ReadDecoder.ts", row: 167, col: 5 }); }); test("Invoke a wrapper method that doesn't exist", async () => { @@ -78,8 +93,17 @@ describe("error structure", () => { }, }); - if (!result.ok) fail(result.error); - expect(result.value).toEqual("test"); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_FAIL); + expect(result.error?.text.startsWith("Could not find invoke function")).toBeTruthy(); + expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); + expect(result.error?.method).toEqual("complexMethod"); + expect(result.error?.args).toEqual("{\n \"arg\": \"test\"\n}"); + expect(result.error?.toString().split("51").length).toEqual(2); + expect(result.error?.cause).toBeUndefined(); }); test("Subinvoke a wrapper that is not found", async () => { @@ -92,8 +116,24 @@ describe("error structure", () => { }, }); - if (!result.ok) fail(result.error); - expect(result.value).toEqual("test"); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(result.error?.text.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); + expect(result.error?.method).toEqual("subWrapperNotFound"); + expect(result.error?.args).toEqual("{\n \"a\": 1,\n \"b\": 1\n}"); + expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); + + expect(result.error?.cause instanceof WrapError).toBeTruthy(); + const cause = result.error?.cause as WrapError; + expect(cause.name).toEqual("UriResolutionError"); + expect(cause.code).toEqual(WrapErrorCode.URI_NOT_FOUND); + expect(cause.text).toEqual("Unable to find URI wrap://ens/not-found.eth."); + expect(cause.uri).toEqual("wrap://ens/not-found.eth"); + expect(cause.resolutionStack).toBeTruthy(); }); test("Subinvoke error two layers deep", async () => { @@ -106,10 +146,39 @@ describe("error structure", () => { }, }); - if (!result.ok) { - fail(result.error); - } - expect(result.value).toEqual(3); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(result.error?.text.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); + expect(result.error?.method).toEqual("throwsInTwoSubinvokeLayers"); + expect(result.error?.args).toEqual(`{ + "a": 1, + "b": 1 +}`); + expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); + + expect(result.error?.cause instanceof WrapError).toBeTruthy(); + const cause = result.error?.cause as WrapError; + expect(cause.name).toEqual("InvokeError"); + expect(cause.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(cause.text.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(cause.uri).toEqual("wrap://ens/bad-math.eth"); + expect(cause.method).toEqual("subInvokeWillThrow"); + expect(cause.args).toEqual("{\n \"0\": 130,\n \"1\": 161,\n \"2\": 97,\n \"3\": 1,\n \"4\": 161,\n \"5\": 98,\n \"6\": 1\n}"); + expect(cause.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); + + expect(cause.cause instanceof WrapError).toBeTruthy(); + const causeOfCause = cause.cause as WrapError; + expect(causeOfCause.name).toEqual("InvokeError"); + expect(causeOfCause.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(causeOfCause.text).toEqual("__wrap_abort: I threw an error!"); + expect(causeOfCause.uri.endsWith("wrap://ens/bad-util.eth")).toBeTruthy(); + expect(causeOfCause.method).toEqual("iThrow"); + expect(causeOfCause.args).toEqual("{\n \"0\": 129,\n \"1\": 161,\n \"2\": 97,\n \"3\": 0\n}"); + expect(causeOfCause.source).toEqual({ file: "src/index.ts", row: 5, col: 5 }); }); test("Invoke a wrapper of incompatible version", async () => { @@ -121,8 +190,6 @@ describe("error structure", () => { }, }); - console.log(result) - if (!result.ok) fail(result.error); expect(result.value).toEqual("test"); }); diff --git a/packages/js/core-client/src/PolywrapCoreClient.ts b/packages/js/core-client/src/PolywrapCoreClient.ts index 9a3b60d443..9085be3894 100644 --- a/packages/js/core-client/src/PolywrapCoreClient.ts +++ b/packages/js/core-client/src/PolywrapCoreClient.ts @@ -155,13 +155,12 @@ export class PolywrapCoreClient implements CoreClient { * returns a package's wrap manifest * * @param uri - a wrap URI - * @param options - { noValidate?: boolean } * @returns a Result containing the WrapManifest if the request was successful */ @Tracer.traceMethod("PolywrapClient: getManifest") public async getManifest( uri: TUri - ): Promise> { + ): Promise> { const load = await this.loadWrapper(Uri.from(uri), undefined); if (!load.ok) { return load; @@ -183,14 +182,23 @@ export class PolywrapCoreClient implements CoreClient { public async getFile( uri: TUri, options: GetFileOptions - ): Promise> { + ): Promise> { const load = await this.loadWrapper(Uri.from(uri), undefined); if (!load.ok) { return load; } const wrapper = load.value; - return await wrapper.getFile(options); + const result = await wrapper.getFile(options); + if (!result.ok) { + const error = new WrapError(result.error?.message, { + code: WrapErrorCode.CLIENT_GET_FILE, + uri: uri.toString(), + stack: result.error?.stack, + }); + return ResultErr(error); + } + return ResultOk(result.value); } /** @@ -205,7 +213,7 @@ export class PolywrapCoreClient implements CoreClient { public async getImplementations( uri: TUri, options: GetImplementationsOptions = {} - ): Promise> { + ): Promise> { const isUriTypeString = typeof uri === "string"; const applyResolution = !!options.applyResolution; @@ -273,7 +281,7 @@ export class PolywrapCoreClient implements CoreClient { const parseResult = parseQuery(uri, queryDocument, variables); if (!parseResult.ok) { const error = new WrapError(parseResult.error?.message, { - code: WrapErrorCode.QUERY_MALFORMED, + code: WrapErrorCode.CLIENT_QUERY_MALFORMED, uri: uri.uri, cause: parseResult.error, }); @@ -324,7 +332,7 @@ export class PolywrapCoreClient implements CoreClient { } catch (error: unknown) { const unknownQueryErrorToWrapError = (e: Error): WrapError => new WrapError((e as Error)?.message, { - code: WrapErrorCode.QUERY_FAIL, + code: WrapErrorCode.CLIENT_QUERY_FAIL, uri: options.uri.toString(), cause: e as Error, }); @@ -645,24 +653,23 @@ export class PolywrapCoreClient implements CoreClient { if (!result.ok) { const history = buildCleanUriHistory(resolutionContext.getHistory()); - const resolutionStack = `${JSON.stringify(history, null, 2)}`; let error: WrapError; if (result.error) { error = new WrapError( "An internal resolver error occurred while resolving a URI", { - code: WrapErrorCode.URI_RESOLVER_ERROR, + code: WrapErrorCode.URI_RESOLVER, uri: uri.uri, - resolutionStack, + resolutionStack: history, cause: result.error, } ); } else { error = new WrapError("Error resolving URI", { - code: WrapErrorCode.URI_RESOLUTION_ERROR, + code: WrapErrorCode.URI_RESOLUTION, uri: uri.uri, - resolutionStack, + resolutionStack: history, }); } @@ -672,16 +679,13 @@ export class PolywrapCoreClient implements CoreClient { const uriPackageOrWrapper = result.value; if (uriPackageOrWrapper.type === "uri") { + const message = `Unable to find URI ${uriPackageOrWrapper.uri.uri}.`; const history = buildCleanUriHistory(resolutionContext.getHistory()); - const resolutionStack = `${JSON.stringify(history, null, 2)}`; - const error = new WrapError( - `Unable to find URI ${uriPackageOrWrapper.uri.uri}.`, - { - code: WrapErrorCode.URI_NOT_FOUND, - uri: uri.uri, - resolutionStack, - } - ); + const error = new WrapError(message, { + code: WrapErrorCode.URI_NOT_FOUND, + uri: uri.uri, + resolutionStack: history, + }); return ResultErr(error); } @@ -690,7 +694,7 @@ export class PolywrapCoreClient implements CoreClient { if (!result.ok) { const error = new WrapError(result.error?.message, { - code: WrapErrorCode.LOAD_WRAPPER_FAIL, + code: WrapErrorCode.CLIENT_LOAD_WRAPPER, uri: uri.uri, cause: result.error, }); @@ -703,34 +707,43 @@ export class PolywrapCoreClient implements CoreClient { } } - @Tracer.traceMethod("PolywrapClient: validateConfig") + @Tracer.traceMethod("PolywrapClient: validate") public async validate( uri: TUri, options: ValidateOptions - ): Promise> { + ): Promise> { const wrapper = await this.loadWrapper(Uri.from(uri)); if (!wrapper.ok) { - return ResultErr(new Error(wrapper.error?.message)); + return wrapper; } const { abi } = await wrapper.value.getManifest(); const importedModules: ImportedModuleDefinition[] = abi.importedModuleTypes || []; - const importUri = (importedModuleType: ImportedModuleDefinition) => { - return this.tryResolveUri({ uri: importedModuleType.uri }); + const importUri = async ( + importedModuleType: ImportedModuleDefinition + ): Promise<{ + uri: string; + result: Result; + }> => { + const uri = importedModuleType.uri; + return { + uri, + result: await this.tryResolveUri({ uri }), + }; }; const resolvedModules = await Promise.all(importedModules.map(importUri)); - const modulesNotFound = resolvedModules.filter(({ ok }) => !ok) as { - error: Error; - }[]; + const modulesNotFound = resolvedModules + .filter(({ result }) => !result.ok) + .map(({ uri }) => uri); if (modulesNotFound.length) { - const missingModules = modulesNotFound.map(({ error }) => { - const uriIndex = error?.message.indexOf("\n"); - return error?.message.substring(0, uriIndex); + const message = `The following URIs could not be resolved: ${modulesNotFound}`; + const error = new WrapError(message, { + code: WrapErrorCode.CLIENT_VALIDATE_RESOLUTION, + uri: uri.toString(), }); - const error = new Error(JSON.stringify(missingModules)); return ResultErr(error); } @@ -749,7 +762,6 @@ export class PolywrapCoreClient implements CoreClient { ({ uri }) => importedModule.uri === uri ); - const errorMessage = `ABI from Uri: ${importedModule.uri} is not compatible with Uri: ${uri}`; for (const [i, _] of Object.keys(importedMethods).entries()) { const importedMethod = importedMethods[i]; @@ -757,9 +769,21 @@ export class PolywrapCoreClient implements CoreClient { const expectedMethod = expectedMethods?.methods[i]; const areEqual = compareSignature(importedMethod, expectedMethod); - if (!areEqual) return ResultErr(new Error(errorMessage)); + if (!areEqual) { + const message = `ABI from Uri: ${importedModule.uri} is not compatible with Uri: ${uri}`; + const error = new WrapError(message, { + code: WrapErrorCode.CLIENT_VALIDATE_ABI, + uri: uri.toString(), + }); + return ResultErr(error); + } } else { - return ResultErr(new Error(errorMessage)); + const message = `ABI from Uri: ${importedModule.uri} is not compatible with Uri: ${uri}`; + const error = new WrapError(message, { + code: WrapErrorCode.CLIENT_VALIDATE_ABI, + uri: uri.toString(), + }); + return ResultErr(error); } } } @@ -771,14 +795,18 @@ export class PolywrapCoreClient implements CoreClient { ); const resolverUris = await Promise.all(validateImportedModules); const invalidUris = resolverUris.filter(({ ok }) => !ok) as { - error: Error; + error: WrapError; }[]; + if (invalidUris.length) { - const missingUris = invalidUris.map(({ error }) => { - const uriIndex = error?.message.indexOf("\n"); - return error?.message.substring(0, uriIndex); + let message = "The following URIs failed validation:"; + for (const { error } of invalidUris) { + message += `\n${error.uri} -> ${error.text}`; + } + const error = new WrapError(message, { + code: WrapErrorCode.CLIENT_VALIDATE_RECURSIVE, + uri: uri.toString(), }); - const error = new Error(JSON.stringify(missingUris)); return ResultErr(error); } } diff --git a/packages/js/core/src/algorithms/GetImplementationsError.ts b/packages/js/core/src/algorithms/GetImplementationsError.ts deleted file mode 100644 index ef49d53cda..0000000000 --- a/packages/js/core/src/algorithms/GetImplementationsError.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class GetImplementationsError extends Error { - constructor(public readonly internalError: TInternalError) { - super("Error occurred while getting implementations"); - } -} diff --git a/packages/js/core/src/algorithms/get-implementations.ts b/packages/js/core/src/algorithms/get-implementations.ts index 2865dffbe6..9b64333a30 100644 --- a/packages/js/core/src/algorithms/get-implementations.ts +++ b/packages/js/core/src/algorithms/get-implementations.ts @@ -1,6 +1,11 @@ -import { Uri, InterfaceImplementations, CoreClient } from "../types"; +import { + Uri, + InterfaceImplementations, + CoreClient, + WrapError, + WrapErrorCode, +} from "../types"; import { IUriResolutionContext } from "../uri-resolution"; -import { GetImplementationsError } from "./GetImplementationsError"; import { applyResolution } from "./applyResolution"; import { Tracer } from "@polywrap/tracing-js"; @@ -13,7 +18,7 @@ export const getImplementations = Tracer.traceFunc( interfaces: readonly InterfaceImplementations[], client?: CoreClient, resolutionContext?: IUriResolutionContext - ): Promise> => { + ): Promise> => { const result: Uri[] = []; const addUniqueResult = (uri: Uri) => { @@ -26,7 +31,7 @@ export const getImplementations = Tracer.traceFunc( const addAllImplementationsFromImplementationsArray = async ( implementationsArray: readonly InterfaceImplementations[], wrapperInterfaceUri: Uri - ): Promise> => { + ): Promise> => { for (const interfaceImplementations of implementationsArray) { let fullyResolvedUri: Uri; if (client) { @@ -36,7 +41,12 @@ export const getImplementations = Tracer.traceFunc( resolutionContext ); if (!redirectsResult.ok) { - return redirectsResult; + const error = new WrapError("Failed to resolve redirects", { + uri: interfaceImplementations.interface.uri, + code: WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS, + cause: redirectsResult.error, + }); + return ResultErr(error); } fullyResolvedUri = redirectsResult.value; } else { @@ -61,7 +71,12 @@ export const getImplementations = Tracer.traceFunc( resolutionContext ); if (!redirectsResult.ok) { - return ResultErr(new GetImplementationsError(redirectsResult.error)); + const error = new WrapError("Failed to resolve redirects", { + uri: wrapperInterfaceUri.uri, + code: WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS, + cause: redirectsResult.error, + }); + return ResultErr(error); } finalUri = redirectsResult.value; } @@ -71,8 +86,6 @@ export const getImplementations = Tracer.traceFunc( finalUri ); - return addAllImp.ok - ? ResultOk(result) - : ResultErr(new GetImplementationsError(addAllImp.error)); + return addAllImp.ok ? ResultOk(result) : addAllImp; } ); diff --git a/packages/js/core/src/algorithms/index.ts b/packages/js/core/src/algorithms/index.ts index bcf5cd1317..b73729b0da 100644 --- a/packages/js/core/src/algorithms/index.ts +++ b/packages/js/core/src/algorithms/index.ts @@ -1,4 +1,3 @@ -export * from "./GetImplementationsError"; export * from "./applyResolution"; export * from "./get-implementations"; export * from "./parse-query"; diff --git a/packages/js/core/src/interfaces/uri-resolver.ts b/packages/js/core/src/interfaces/uri-resolver.ts index 9b886a3da5..2be4064cbf 100644 --- a/packages/js/core/src/interfaces/uri-resolver.ts +++ b/packages/js/core/src/interfaces/uri-resolver.ts @@ -1,4 +1,4 @@ -import { Uri, Invoker } from "../"; +import { Uri, Invoker, WrapError } from "../"; import { Tracer } from "@polywrap/tracing-js"; import { Result } from "@polywrap/result"; @@ -15,7 +15,7 @@ export const module = { invoker: Invoker, wrapper: Uri, uri: Uri - ): Promise> => { + ): Promise> => { return invoker.invoke({ uri: wrapper.uri, method: `tryResolveUri`, @@ -32,7 +32,7 @@ export const module = { invoker: Invoker, wrapper: Uri, path: string - ): Promise> => { + ): Promise> => { return invoker.invoke({ uri: wrapper.uri, method: "getFile", diff --git a/packages/js/core/src/types/CoreClient.ts b/packages/js/core/src/types/CoreClient.ts index 1b6904eaed..509953c246 100644 --- a/packages/js/core/src/types/CoreClient.ts +++ b/packages/js/core/src/types/CoreClient.ts @@ -5,6 +5,7 @@ import { Uri, InterfaceImplementations, Env, + WrapError, } from "."; import { IUriResolutionContext, IUriResolver } from "../uri-resolution"; import { UriResolverHandler } from "./UriResolver"; @@ -54,20 +55,20 @@ export interface CoreClient getManifest( uri: TUri - ): Promise>; + ): Promise>; getFile( uri: TUri, options: GetFileOptions - ): Promise>; + ): Promise>; getImplementations( uri: TUri, options: GetImplementationsOptions - ): Promise>; + ): Promise>; validate( uri: TUri, options?: ValidateOptions - ): Promise>; + ): Promise>; } diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 7f9359e13d..6b75f27b58 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -1,40 +1,58 @@ -export type WrapErrorSource = Readonly<{ - file?: string; - line?: number; - col?: number; -}>; +import { CleanResolutionStep } from "../algorithms"; + +/** +Wrap error codes provide additional context to WrapErrors. -// 0-49 -> Internal -// 50-99 -> URI Resolution -// 100-150 -> Wasm wrapper invocation -// 150-199 -> Wasm wrapper sub-invocation -// 200-255 -> Plugin invocation & sub-invocation +Error code naming convention (approximate): + type of handler + type of functionality + piece of functionality + ==> handler_typeFn_pieceFn + +Error code map: + 0-24 -> Client + 25-49 -> URI resolution + 50-74 -> Wasm wrapper invocation & sub-invocation + 75-99 -> Plugin invocation & sub-invocation + 100-255 -> Unallocated + */ export enum WrapErrorCode { UNKNOWN, - LOAD_WRAPPER_FAIL, - QUERY_MALFORMED = 48, - QUERY_FAIL, - URI_RESOLUTION_ERROR = 50, - URI_RESOLVER_ERROR, + CLIENT_LOAD_WRAPPER, + CLIENT_GET_FILE, + CLIENT_GET_IMPLEMENTATIONS, + CLIENT_VALIDATE_RESOLUTION, + CLIENT_VALIDATE_ABI, + CLIENT_VALIDATE_RECURSIVE, + CLIENT_QUERY_MALFORMED, + CLIENT_QUERY_FAIL, + URI_RESOLUTION = 25, + URI_RESOLVER, URI_NOT_FOUND, - WASM_INVOKE_ABORTED = 100, + WASM_INVOKE_ABORTED = 50, WASM_INVOKE_FAIL, - WASM_NO_MODULE, - WASM_METHOD_NOT_FOUND, - PLUGIN_INVOKE_ABORTED = 200, - PLUGIN_INVOKE_FAIL, + WASM_MODULE_NOT_FOUND, + WASM_METHOD_NOT_FOUND, // not yet used + PLUGIN_INVOKE_FAIL = 75, PLUGIN_METHOD_NOT_FOUND, PLUGIN_ARGS_MALFORMED, } +export type WrapErrorSource = Readonly<{ + file?: string; + row?: number; + col?: number; +}>; + export interface WrapErrorOptions { code: WrapErrorCode; uri: string; method?: string; args?: string; source?: WrapErrorSource; - resolutionStack?: string; + resolutionStack?: CleanResolutionStep; cause?: unknown; + stack?: string; } type RegExpGroups = @@ -51,7 +69,7 @@ export class WrapError extends Error { readonly method?: string; readonly args?: string; readonly source?: WrapErrorSource; - readonly resolutionStack?: string; + readonly resolutionStack?: CleanResolutionStep; readonly cause?: unknown; static re = new RegExp( @@ -65,7 +83,7 @@ export class WrapError extends Error { /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-z0-9_-]+\/.+)/.source, /(?:(?:\r\n|\r|\n)method: (?([A-z_]{1}[A-z0-9_]*)))?/.source, /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r\n|\r|\n)+} ))?/.source, - /(?:(?:\r\n|\r|\n)source: \{ file: "(?.+)", row: (?[0-9]+), col: (?[0-9]+) })?/ + /(?:(?:\r\n|\r|\n)source: \{ file: "(?.+)", row: (?[0-9]+), col: (?[0-9]+) })?/ .source, /(?:(?:\r\n|\r|\n)uriResolutionStack: (?\[(?:.|\r\n|\r|\n)+]))?/ .source, @@ -85,6 +103,7 @@ export class WrapError extends Error { this.source = options.source; this.resolutionStack = options.resolutionStack; this.cause = options.cause; + this.stack = options.stack; Object.setPrototypeOf(this, WrapError.prototype); } @@ -96,7 +115,7 @@ export class WrapError extends Error { | "method" | "args" | "file" - | "line" + | "row" | "col" | "resolutionStack" | "cause" @@ -111,9 +130,9 @@ export class WrapError extends Error { method, args, file, - line, + row, col, - resolutionStack, + resolutionStack: resolutionStackStr, cause: causeStr, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion } = result.groups!; @@ -124,11 +143,15 @@ export class WrapError extends Error { const source: WrapErrorSource | undefined = file ? { file, - line: line ? parseInt(line) : undefined, + row: row ? parseInt(row) : undefined, col: col ? parseInt(col) : undefined, } : undefined; + const resolutionStack = resolutionStackStr + ? JSON.parse(resolutionStackStr) + : undefined; + // message may be a stringified WrapError due to current string-based error passing between client and wasm let cause: WrapError | undefined; if (this.re.test(message as string)) { @@ -139,10 +162,10 @@ export class WrapError extends Error { code, uri: uri as string, method, - args, + args: args?.trim(), source, resolutionStack, - cause: cause ?? causeStr, + cause: cause ?? causeStr, // TODO: is it possible that 'cause' information is lost here? }); } @@ -159,10 +182,10 @@ export class WrapError extends Error { const maybeArgs = args ? `args: ${args} ` : ""; // source is uses () instead of {} to facilitate regex const maybeSource = source - ? `source: { file: "${source?.file}", row: ${source?.line}, col: ${source?.col} }` + ? `source: { file: "${source?.file}", row: ${source?.row}, col: ${source?.col} }` : ""; const maybeResolutionStack = resolutionStack - ? `uriResolutionStack: ${resolutionStack}` + ? `uriResolutionStack: ${JSON.stringify(resolutionStack, null, 2)}` : ""; const errorCause = WrapError.stringifyCause(cause); @@ -210,30 +233,40 @@ export class WrapError extends Error { } private static codeToName(code: WrapErrorCode): string { - if (code < 50) { + if (code < 25) { return "WrapError"; - } else if (code < 100) { + } else if (code < 50) { return "UriResolutionError"; - } else if (code < 150) { + } else if (code < 75) { return "InvokeError"; - } else if (code < 200) { - return "SubInvokeError"; - } else { + } else if (code < 100) { return "PluginError"; + } else { + return "WrapError"; } } private static metaMessage(code: WrapErrorCode): string { switch (code) { - case WrapErrorCode.LOAD_WRAPPER_FAIL: + case WrapErrorCode.CLIENT_LOAD_WRAPPER: return "Failed to create Wrapper from WrapPackage."; - case WrapErrorCode.QUERY_MALFORMED: + case WrapErrorCode.CLIENT_GET_FILE: + return "An error occurred while retrieving a file."; + case WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS: + return "An error occurred while retrieving interface implementations."; + case WrapErrorCode.CLIENT_VALIDATE_RESOLUTION: + return "An URI resolution error occurred while validating a WRAP URI."; + case WrapErrorCode.CLIENT_VALIDATE_ABI: + return "An error occurred while validating a WRAP URI against its ABI."; + case WrapErrorCode.CLIENT_VALIDATE_RECURSIVE: + return "An error occurred while recursively validating a WRAP URI."; + case WrapErrorCode.CLIENT_QUERY_MALFORMED: return "Failed to parse GraphQL query."; - case WrapErrorCode.QUERY_FAIL: + case WrapErrorCode.CLIENT_QUERY_FAIL: return "Unknown query exception encountered."; - case WrapErrorCode.URI_RESOLUTION_ERROR: + case WrapErrorCode.URI_RESOLUTION: return "Unable to resolve URI."; - case WrapErrorCode.URI_RESOLVER_ERROR: + case WrapErrorCode.URI_RESOLVER: return "An internal resolver error occurred while resolving a URI."; case WrapErrorCode.URI_NOT_FOUND: return "URI not found."; @@ -241,14 +274,14 @@ export class WrapError extends Error { return "Wasm module aborted execution."; case WrapErrorCode.WASM_INVOKE_FAIL: return "Invocation exception encountered."; - case WrapErrorCode.WASM_NO_MODULE: - return "Wrapper does not contain a wasm module."; + case WrapErrorCode.WASM_MODULE_NOT_FOUND: + return "Wrapper does not contain a Wasm module."; case WrapErrorCode.WASM_METHOD_NOT_FOUND: - return "Could not find invoke function."; + return "Could not find method in Wasm module."; case WrapErrorCode.PLUGIN_METHOD_NOT_FOUND: - return "Method not found."; + return "Method not found in plugin module."; case WrapErrorCode.PLUGIN_ARGS_MALFORMED: - return "Malformed args."; + return "Malformed arguments passed to plugin."; case WrapErrorCode.PLUGIN_INVOKE_FAIL: return "Invocation exception encountered."; default: diff --git a/packages/js/core/src/types/Wrapper.ts b/packages/js/core/src/types/Wrapper.ts index 5d5655b4b3..d2051d8b4f 100644 --- a/packages/js/core/src/types/Wrapper.ts +++ b/packages/js/core/src/types/Wrapper.ts @@ -33,7 +33,6 @@ export interface Wrapper extends Invocable { * Get a file from the Wrapper package. * * @param options Configuration options for file retrieval - * @param client The client instance requesting the file. */ getFile(options: GetFileOptions): Promise>; diff --git a/packages/js/plugin/src/PluginWrapper.ts b/packages/js/plugin/src/PluginWrapper.ts index 5e33f30ae9..fb54c3d080 100644 --- a/packages/js/plugin/src/PluginWrapper.ts +++ b/packages/js/plugin/src/PluginWrapper.ts @@ -105,13 +105,16 @@ export class PluginWrapper implements Wrapper { encoded: false, }; } else { - const error = new WrapError(result.error?.message, { - code: WrapErrorCode.PLUGIN_INVOKE_FAIL, - uri: `${options.uri}; module: ${module}`, - method, - args: JSON.stringify(jsArgs, null, 2), - cause: result.error, - }); + const error = new WrapError( + `Failed to invoke method "${method}" in module: ${this.module}`, + { + code: WrapErrorCode.PLUGIN_INVOKE_FAIL, + uri: options.uri.toString(), + method, + args: JSON.stringify(jsArgs, null, 2), + cause: result.error, + } + ); return ResultErr(error); } } diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index 20d516d248..ef1d3c9fc4 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -154,12 +154,11 @@ export class WasmWrapper implements Wrapper { const args = options.args || {}; const wasmResult = await this._getWasmModule(); if (!wasmResult.ok) { - const error = new WrapError(wasmResult.error?.message, { - code: WrapErrorCode.WASM_NO_MODULE, + const error = new WrapError(wasmResult.error, { + code: WrapErrorCode.WASM_MODULE_NOT_FOUND, uri: options.uri.uri, method, args: JSON.stringify(args, null, 2), - cause: wasmResult.error, }); return ResultErr(error); } @@ -215,7 +214,7 @@ export class WasmWrapper implements Wrapper { state.env.byteLength ); - const invokeResult = this._processInvokeResult(state, result, abort); + const invokeResult = this._processInvokeResult(state, result); if (invokeResult.ok) { return { @@ -239,18 +238,17 @@ export class WasmWrapper implements Wrapper { @Tracer.traceMethod("WasmWrapper: _processInvokeResult") private _processInvokeResult( state: State, - result: boolean, - abort: (message: string) => never + result: boolean ): Result { if (result) { if (!state.invoke.result) { - abort("Invoke result is missing."); + return ResultErr("Invoke result is missing."); } return ResultOk(state.invoke.result); } else { if (!state.invoke.error) { - abort("Invoke error is missing."); + return ResultErr("Invoke error is missing."); } return ResultErr(state.invoke.error); @@ -258,12 +256,12 @@ export class WasmWrapper implements Wrapper { } @Tracer.traceMethod("WasmWrapper: getWasmModule") - private async _getWasmModule(): Promise> { + private async _getWasmModule(): Promise> { if (this._wasmModule === undefined) { const result = await this._fileReader.readFile(WRAP_MODULE_PATH); if (!result.ok) { - return ResultErr(Error(`Wrapper does not contain a wasm module`)); + return ResultErr("Wrapper does not contain a wasm module"); } this._wasmModule = result.value; diff --git a/packages/js/wasm/src/imports.ts b/packages/js/wasm/src/imports.ts index 4d9441f848..be87e5ad81 100644 --- a/packages/js/wasm/src/imports.ts +++ b/packages/js/wasm/src/imports.ts @@ -215,7 +215,7 @@ export const createImports = (config: { const msg = readString(memory.buffer, msgPtr, msgLen); const file = readString(memory.buffer, filePtr, fileLen); - abort(`__wrap_abort: ${msg}`, { file, line, col: column }); + abort(`__wrap_abort: ${msg}`, { file, row: line, col: column }); }, __wrap_debug_log: (ptr: u32, len: u32): void => { const msg = readString(memory.buffer, ptr, len); From 4fa295d47e97b87ea2a4ed10970eeb9728fa0402 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Wed, 7 Dec 2022 22:32:38 +0530 Subject: [PATCH 11/40] added WrapError to more modules, added more error codes, improved tests for wrap error structure --- .../__tests__/core/error-structure.spec.ts | 105 ++++++++++++--- .../js/core-client/src/PolywrapCoreClient.ts | 112 +++++++++------ .../src/algorithms/GetImplementationsError.ts | 5 - .../src/algorithms/get-implementations.ts | 31 +++-- packages/js/core/src/algorithms/index.ts | 1 - .../js/core/src/interfaces/uri-resolver.ts | 6 +- packages/js/core/src/types/CoreClient.ts | 9 +- packages/js/core/src/types/WrapError.ts | 127 +++++++++++------- packages/js/core/src/types/Wrapper.ts | 1 - packages/js/plugin/src/PluginWrapper.ts | 17 ++- packages/js/wasm/src/WasmWrapper.ts | 18 ++- packages/js/wasm/src/imports.ts | 2 +- 12 files changed, 285 insertions(+), 149 deletions(-) delete mode 100644 packages/js/core/src/algorithms/GetImplementationsError.ts diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index fa33404fbb..9893801cfa 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,6 +1,7 @@ import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; -import { buildWrapper } from "@polywrap/test-env-js"; +// import { buildWrapper } from "@polywrap/test-env-js"; +import { WrapError, WrapErrorCode } from "@polywrap/core-js"; jest.setTimeout(360000); @@ -24,10 +25,10 @@ describe("error structure", () => { let client: PolywrapClient; beforeAll(async () => { - await buildWrapper(simpleWrapperPath); - await buildWrapper(badUtilWrapperPath); - await buildWrapper(badMathWrapperPath); - await buildWrapper(subinvokeErrorWrapperPath); + // await buildWrapper(simpleWrapperPath); + // await buildWrapper(badUtilWrapperPath); + // await buildWrapper(badMathWrapperPath); + // await buildWrapper(subinvokeErrorWrapperPath); client = new PolywrapClient({ redirects: [ @@ -52,8 +53,14 @@ describe("error structure", () => { }, }); - if (!result.ok) fail(result.error); - expect(result.value).toEqual("test"); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("UriResolutionError"); + expect(result.error?.code).toEqual(WrapErrorCode.URI_NOT_FOUND); + expect(result.error?.text.startsWith("Unable to find URI ")).toBeTruthy(); + expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build-not-found")).toBeTruthy(); + expect(result.error?.resolutionStack).toBeTruthy(); }); test("Invoke a wrapper with malformed arguments", async () => { @@ -65,8 +72,16 @@ describe("error structure", () => { }, }); - if (!result.ok) fail(result.error); - expect(result.value).toEqual("test"); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(result.error?.text.startsWith("__wrap_abort:")).toBeTruthy(); + expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); + expect(result.error?.method).toEqual("simpleMethod"); + expect(result.error?.args).toEqual("{\n \"arg\": 3\n}"); + expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/msgpack/ReadDecoder.ts", row: 167, col: 5 }); }); test("Invoke a wrapper method that doesn't exist", async () => { @@ -78,8 +93,17 @@ describe("error structure", () => { }, }); - if (!result.ok) fail(result.error); - expect(result.value).toEqual("test"); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_FAIL); + expect(result.error?.text.startsWith("Could not find invoke function")).toBeTruthy(); + expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); + expect(result.error?.method).toEqual("complexMethod"); + expect(result.error?.args).toEqual("{\n \"arg\": \"test\"\n}"); + expect(result.error?.toString().split("51").length).toEqual(2); + expect(result.error?.cause).toBeUndefined(); }); test("Subinvoke a wrapper that is not found", async () => { @@ -92,8 +116,24 @@ describe("error structure", () => { }, }); - if (!result.ok) fail(result.error); - expect(result.value).toEqual("test"); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(result.error?.text.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); + expect(result.error?.method).toEqual("subWrapperNotFound"); + expect(result.error?.args).toEqual("{\n \"a\": 1,\n \"b\": 1\n}"); + expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); + + expect(result.error?.cause instanceof WrapError).toBeTruthy(); + const cause = result.error?.cause as WrapError; + expect(cause.name).toEqual("UriResolutionError"); + expect(cause.code).toEqual(WrapErrorCode.URI_NOT_FOUND); + expect(cause.text).toEqual("Unable to find URI wrap://ens/not-found.eth."); + expect(cause.uri).toEqual("wrap://ens/not-found.eth"); + expect(cause.resolutionStack).toBeTruthy(); }); test("Subinvoke error two layers deep", async () => { @@ -106,10 +146,39 @@ describe("error structure", () => { }, }); - if (!result.ok) { - fail(result.error); - } - expect(result.value).toEqual(3); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(result.error?.text.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); + expect(result.error?.method).toEqual("throwsInTwoSubinvokeLayers"); + expect(result.error?.args).toEqual(`{ + "a": 1, + "b": 1 +}`); + expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); + + expect(result.error?.cause instanceof WrapError).toBeTruthy(); + const cause = result.error?.cause as WrapError; + expect(cause.name).toEqual("InvokeError"); + expect(cause.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(cause.text.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(cause.uri).toEqual("wrap://ens/bad-math.eth"); + expect(cause.method).toEqual("subInvokeWillThrow"); + expect(cause.args).toEqual("{\n \"0\": 130,\n \"1\": 161,\n \"2\": 97,\n \"3\": 1,\n \"4\": 161,\n \"5\": 98,\n \"6\": 1\n}"); + expect(cause.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); + + expect(cause.cause instanceof WrapError).toBeTruthy(); + const causeOfCause = cause.cause as WrapError; + expect(causeOfCause.name).toEqual("InvokeError"); + expect(causeOfCause.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(causeOfCause.text).toEqual("__wrap_abort: I threw an error!"); + expect(causeOfCause.uri.endsWith("wrap://ens/bad-util.eth")).toBeTruthy(); + expect(causeOfCause.method).toEqual("iThrow"); + expect(causeOfCause.args).toEqual("{\n \"0\": 129,\n \"1\": 161,\n \"2\": 97,\n \"3\": 0\n}"); + expect(causeOfCause.source).toEqual({ file: "src/index.ts", row: 5, col: 5 }); }); test("Invoke a wrapper of incompatible version", async () => { @@ -121,8 +190,6 @@ describe("error structure", () => { }, }); - console.log(result) - if (!result.ok) fail(result.error); expect(result.value).toEqual("test"); }); diff --git a/packages/js/core-client/src/PolywrapCoreClient.ts b/packages/js/core-client/src/PolywrapCoreClient.ts index 9a3b60d443..9085be3894 100644 --- a/packages/js/core-client/src/PolywrapCoreClient.ts +++ b/packages/js/core-client/src/PolywrapCoreClient.ts @@ -155,13 +155,12 @@ export class PolywrapCoreClient implements CoreClient { * returns a package's wrap manifest * * @param uri - a wrap URI - * @param options - { noValidate?: boolean } * @returns a Result containing the WrapManifest if the request was successful */ @Tracer.traceMethod("PolywrapClient: getManifest") public async getManifest( uri: TUri - ): Promise> { + ): Promise> { const load = await this.loadWrapper(Uri.from(uri), undefined); if (!load.ok) { return load; @@ -183,14 +182,23 @@ export class PolywrapCoreClient implements CoreClient { public async getFile( uri: TUri, options: GetFileOptions - ): Promise> { + ): Promise> { const load = await this.loadWrapper(Uri.from(uri), undefined); if (!load.ok) { return load; } const wrapper = load.value; - return await wrapper.getFile(options); + const result = await wrapper.getFile(options); + if (!result.ok) { + const error = new WrapError(result.error?.message, { + code: WrapErrorCode.CLIENT_GET_FILE, + uri: uri.toString(), + stack: result.error?.stack, + }); + return ResultErr(error); + } + return ResultOk(result.value); } /** @@ -205,7 +213,7 @@ export class PolywrapCoreClient implements CoreClient { public async getImplementations( uri: TUri, options: GetImplementationsOptions = {} - ): Promise> { + ): Promise> { const isUriTypeString = typeof uri === "string"; const applyResolution = !!options.applyResolution; @@ -273,7 +281,7 @@ export class PolywrapCoreClient implements CoreClient { const parseResult = parseQuery(uri, queryDocument, variables); if (!parseResult.ok) { const error = new WrapError(parseResult.error?.message, { - code: WrapErrorCode.QUERY_MALFORMED, + code: WrapErrorCode.CLIENT_QUERY_MALFORMED, uri: uri.uri, cause: parseResult.error, }); @@ -324,7 +332,7 @@ export class PolywrapCoreClient implements CoreClient { } catch (error: unknown) { const unknownQueryErrorToWrapError = (e: Error): WrapError => new WrapError((e as Error)?.message, { - code: WrapErrorCode.QUERY_FAIL, + code: WrapErrorCode.CLIENT_QUERY_FAIL, uri: options.uri.toString(), cause: e as Error, }); @@ -645,24 +653,23 @@ export class PolywrapCoreClient implements CoreClient { if (!result.ok) { const history = buildCleanUriHistory(resolutionContext.getHistory()); - const resolutionStack = `${JSON.stringify(history, null, 2)}`; let error: WrapError; if (result.error) { error = new WrapError( "An internal resolver error occurred while resolving a URI", { - code: WrapErrorCode.URI_RESOLVER_ERROR, + code: WrapErrorCode.URI_RESOLVER, uri: uri.uri, - resolutionStack, + resolutionStack: history, cause: result.error, } ); } else { error = new WrapError("Error resolving URI", { - code: WrapErrorCode.URI_RESOLUTION_ERROR, + code: WrapErrorCode.URI_RESOLUTION, uri: uri.uri, - resolutionStack, + resolutionStack: history, }); } @@ -672,16 +679,13 @@ export class PolywrapCoreClient implements CoreClient { const uriPackageOrWrapper = result.value; if (uriPackageOrWrapper.type === "uri") { + const message = `Unable to find URI ${uriPackageOrWrapper.uri.uri}.`; const history = buildCleanUriHistory(resolutionContext.getHistory()); - const resolutionStack = `${JSON.stringify(history, null, 2)}`; - const error = new WrapError( - `Unable to find URI ${uriPackageOrWrapper.uri.uri}.`, - { - code: WrapErrorCode.URI_NOT_FOUND, - uri: uri.uri, - resolutionStack, - } - ); + const error = new WrapError(message, { + code: WrapErrorCode.URI_NOT_FOUND, + uri: uri.uri, + resolutionStack: history, + }); return ResultErr(error); } @@ -690,7 +694,7 @@ export class PolywrapCoreClient implements CoreClient { if (!result.ok) { const error = new WrapError(result.error?.message, { - code: WrapErrorCode.LOAD_WRAPPER_FAIL, + code: WrapErrorCode.CLIENT_LOAD_WRAPPER, uri: uri.uri, cause: result.error, }); @@ -703,34 +707,43 @@ export class PolywrapCoreClient implements CoreClient { } } - @Tracer.traceMethod("PolywrapClient: validateConfig") + @Tracer.traceMethod("PolywrapClient: validate") public async validate( uri: TUri, options: ValidateOptions - ): Promise> { + ): Promise> { const wrapper = await this.loadWrapper(Uri.from(uri)); if (!wrapper.ok) { - return ResultErr(new Error(wrapper.error?.message)); + return wrapper; } const { abi } = await wrapper.value.getManifest(); const importedModules: ImportedModuleDefinition[] = abi.importedModuleTypes || []; - const importUri = (importedModuleType: ImportedModuleDefinition) => { - return this.tryResolveUri({ uri: importedModuleType.uri }); + const importUri = async ( + importedModuleType: ImportedModuleDefinition + ): Promise<{ + uri: string; + result: Result; + }> => { + const uri = importedModuleType.uri; + return { + uri, + result: await this.tryResolveUri({ uri }), + }; }; const resolvedModules = await Promise.all(importedModules.map(importUri)); - const modulesNotFound = resolvedModules.filter(({ ok }) => !ok) as { - error: Error; - }[]; + const modulesNotFound = resolvedModules + .filter(({ result }) => !result.ok) + .map(({ uri }) => uri); if (modulesNotFound.length) { - const missingModules = modulesNotFound.map(({ error }) => { - const uriIndex = error?.message.indexOf("\n"); - return error?.message.substring(0, uriIndex); + const message = `The following URIs could not be resolved: ${modulesNotFound}`; + const error = new WrapError(message, { + code: WrapErrorCode.CLIENT_VALIDATE_RESOLUTION, + uri: uri.toString(), }); - const error = new Error(JSON.stringify(missingModules)); return ResultErr(error); } @@ -749,7 +762,6 @@ export class PolywrapCoreClient implements CoreClient { ({ uri }) => importedModule.uri === uri ); - const errorMessage = `ABI from Uri: ${importedModule.uri} is not compatible with Uri: ${uri}`; for (const [i, _] of Object.keys(importedMethods).entries()) { const importedMethod = importedMethods[i]; @@ -757,9 +769,21 @@ export class PolywrapCoreClient implements CoreClient { const expectedMethod = expectedMethods?.methods[i]; const areEqual = compareSignature(importedMethod, expectedMethod); - if (!areEqual) return ResultErr(new Error(errorMessage)); + if (!areEqual) { + const message = `ABI from Uri: ${importedModule.uri} is not compatible with Uri: ${uri}`; + const error = new WrapError(message, { + code: WrapErrorCode.CLIENT_VALIDATE_ABI, + uri: uri.toString(), + }); + return ResultErr(error); + } } else { - return ResultErr(new Error(errorMessage)); + const message = `ABI from Uri: ${importedModule.uri} is not compatible with Uri: ${uri}`; + const error = new WrapError(message, { + code: WrapErrorCode.CLIENT_VALIDATE_ABI, + uri: uri.toString(), + }); + return ResultErr(error); } } } @@ -771,14 +795,18 @@ export class PolywrapCoreClient implements CoreClient { ); const resolverUris = await Promise.all(validateImportedModules); const invalidUris = resolverUris.filter(({ ok }) => !ok) as { - error: Error; + error: WrapError; }[]; + if (invalidUris.length) { - const missingUris = invalidUris.map(({ error }) => { - const uriIndex = error?.message.indexOf("\n"); - return error?.message.substring(0, uriIndex); + let message = "The following URIs failed validation:"; + for (const { error } of invalidUris) { + message += `\n${error.uri} -> ${error.text}`; + } + const error = new WrapError(message, { + code: WrapErrorCode.CLIENT_VALIDATE_RECURSIVE, + uri: uri.toString(), }); - const error = new Error(JSON.stringify(missingUris)); return ResultErr(error); } } diff --git a/packages/js/core/src/algorithms/GetImplementationsError.ts b/packages/js/core/src/algorithms/GetImplementationsError.ts deleted file mode 100644 index ef49d53cda..0000000000 --- a/packages/js/core/src/algorithms/GetImplementationsError.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class GetImplementationsError extends Error { - constructor(public readonly internalError: TInternalError) { - super("Error occurred while getting implementations"); - } -} diff --git a/packages/js/core/src/algorithms/get-implementations.ts b/packages/js/core/src/algorithms/get-implementations.ts index 2865dffbe6..9b64333a30 100644 --- a/packages/js/core/src/algorithms/get-implementations.ts +++ b/packages/js/core/src/algorithms/get-implementations.ts @@ -1,6 +1,11 @@ -import { Uri, InterfaceImplementations, CoreClient } from "../types"; +import { + Uri, + InterfaceImplementations, + CoreClient, + WrapError, + WrapErrorCode, +} from "../types"; import { IUriResolutionContext } from "../uri-resolution"; -import { GetImplementationsError } from "./GetImplementationsError"; import { applyResolution } from "./applyResolution"; import { Tracer } from "@polywrap/tracing-js"; @@ -13,7 +18,7 @@ export const getImplementations = Tracer.traceFunc( interfaces: readonly InterfaceImplementations[], client?: CoreClient, resolutionContext?: IUriResolutionContext - ): Promise> => { + ): Promise> => { const result: Uri[] = []; const addUniqueResult = (uri: Uri) => { @@ -26,7 +31,7 @@ export const getImplementations = Tracer.traceFunc( const addAllImplementationsFromImplementationsArray = async ( implementationsArray: readonly InterfaceImplementations[], wrapperInterfaceUri: Uri - ): Promise> => { + ): Promise> => { for (const interfaceImplementations of implementationsArray) { let fullyResolvedUri: Uri; if (client) { @@ -36,7 +41,12 @@ export const getImplementations = Tracer.traceFunc( resolutionContext ); if (!redirectsResult.ok) { - return redirectsResult; + const error = new WrapError("Failed to resolve redirects", { + uri: interfaceImplementations.interface.uri, + code: WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS, + cause: redirectsResult.error, + }); + return ResultErr(error); } fullyResolvedUri = redirectsResult.value; } else { @@ -61,7 +71,12 @@ export const getImplementations = Tracer.traceFunc( resolutionContext ); if (!redirectsResult.ok) { - return ResultErr(new GetImplementationsError(redirectsResult.error)); + const error = new WrapError("Failed to resolve redirects", { + uri: wrapperInterfaceUri.uri, + code: WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS, + cause: redirectsResult.error, + }); + return ResultErr(error); } finalUri = redirectsResult.value; } @@ -71,8 +86,6 @@ export const getImplementations = Tracer.traceFunc( finalUri ); - return addAllImp.ok - ? ResultOk(result) - : ResultErr(new GetImplementationsError(addAllImp.error)); + return addAllImp.ok ? ResultOk(result) : addAllImp; } ); diff --git a/packages/js/core/src/algorithms/index.ts b/packages/js/core/src/algorithms/index.ts index bcf5cd1317..b73729b0da 100644 --- a/packages/js/core/src/algorithms/index.ts +++ b/packages/js/core/src/algorithms/index.ts @@ -1,4 +1,3 @@ -export * from "./GetImplementationsError"; export * from "./applyResolution"; export * from "./get-implementations"; export * from "./parse-query"; diff --git a/packages/js/core/src/interfaces/uri-resolver.ts b/packages/js/core/src/interfaces/uri-resolver.ts index 9b886a3da5..2be4064cbf 100644 --- a/packages/js/core/src/interfaces/uri-resolver.ts +++ b/packages/js/core/src/interfaces/uri-resolver.ts @@ -1,4 +1,4 @@ -import { Uri, Invoker } from "../"; +import { Uri, Invoker, WrapError } from "../"; import { Tracer } from "@polywrap/tracing-js"; import { Result } from "@polywrap/result"; @@ -15,7 +15,7 @@ export const module = { invoker: Invoker, wrapper: Uri, uri: Uri - ): Promise> => { + ): Promise> => { return invoker.invoke({ uri: wrapper.uri, method: `tryResolveUri`, @@ -32,7 +32,7 @@ export const module = { invoker: Invoker, wrapper: Uri, path: string - ): Promise> => { + ): Promise> => { return invoker.invoke({ uri: wrapper.uri, method: "getFile", diff --git a/packages/js/core/src/types/CoreClient.ts b/packages/js/core/src/types/CoreClient.ts index 1b6904eaed..509953c246 100644 --- a/packages/js/core/src/types/CoreClient.ts +++ b/packages/js/core/src/types/CoreClient.ts @@ -5,6 +5,7 @@ import { Uri, InterfaceImplementations, Env, + WrapError, } from "."; import { IUriResolutionContext, IUriResolver } from "../uri-resolution"; import { UriResolverHandler } from "./UriResolver"; @@ -54,20 +55,20 @@ export interface CoreClient getManifest( uri: TUri - ): Promise>; + ): Promise>; getFile( uri: TUri, options: GetFileOptions - ): Promise>; + ): Promise>; getImplementations( uri: TUri, options: GetImplementationsOptions - ): Promise>; + ): Promise>; validate( uri: TUri, options?: ValidateOptions - ): Promise>; + ): Promise>; } diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 7f9359e13d..6b75f27b58 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -1,40 +1,58 @@ -export type WrapErrorSource = Readonly<{ - file?: string; - line?: number; - col?: number; -}>; +import { CleanResolutionStep } from "../algorithms"; + +/** +Wrap error codes provide additional context to WrapErrors. -// 0-49 -> Internal -// 50-99 -> URI Resolution -// 100-150 -> Wasm wrapper invocation -// 150-199 -> Wasm wrapper sub-invocation -// 200-255 -> Plugin invocation & sub-invocation +Error code naming convention (approximate): + type of handler + type of functionality + piece of functionality + ==> handler_typeFn_pieceFn + +Error code map: + 0-24 -> Client + 25-49 -> URI resolution + 50-74 -> Wasm wrapper invocation & sub-invocation + 75-99 -> Plugin invocation & sub-invocation + 100-255 -> Unallocated + */ export enum WrapErrorCode { UNKNOWN, - LOAD_WRAPPER_FAIL, - QUERY_MALFORMED = 48, - QUERY_FAIL, - URI_RESOLUTION_ERROR = 50, - URI_RESOLVER_ERROR, + CLIENT_LOAD_WRAPPER, + CLIENT_GET_FILE, + CLIENT_GET_IMPLEMENTATIONS, + CLIENT_VALIDATE_RESOLUTION, + CLIENT_VALIDATE_ABI, + CLIENT_VALIDATE_RECURSIVE, + CLIENT_QUERY_MALFORMED, + CLIENT_QUERY_FAIL, + URI_RESOLUTION = 25, + URI_RESOLVER, URI_NOT_FOUND, - WASM_INVOKE_ABORTED = 100, + WASM_INVOKE_ABORTED = 50, WASM_INVOKE_FAIL, - WASM_NO_MODULE, - WASM_METHOD_NOT_FOUND, - PLUGIN_INVOKE_ABORTED = 200, - PLUGIN_INVOKE_FAIL, + WASM_MODULE_NOT_FOUND, + WASM_METHOD_NOT_FOUND, // not yet used + PLUGIN_INVOKE_FAIL = 75, PLUGIN_METHOD_NOT_FOUND, PLUGIN_ARGS_MALFORMED, } +export type WrapErrorSource = Readonly<{ + file?: string; + row?: number; + col?: number; +}>; + export interface WrapErrorOptions { code: WrapErrorCode; uri: string; method?: string; args?: string; source?: WrapErrorSource; - resolutionStack?: string; + resolutionStack?: CleanResolutionStep; cause?: unknown; + stack?: string; } type RegExpGroups = @@ -51,7 +69,7 @@ export class WrapError extends Error { readonly method?: string; readonly args?: string; readonly source?: WrapErrorSource; - readonly resolutionStack?: string; + readonly resolutionStack?: CleanResolutionStep; readonly cause?: unknown; static re = new RegExp( @@ -65,7 +83,7 @@ export class WrapError extends Error { /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-z0-9_-]+\/.+)/.source, /(?:(?:\r\n|\r|\n)method: (?([A-z_]{1}[A-z0-9_]*)))?/.source, /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r\n|\r|\n)+} ))?/.source, - /(?:(?:\r\n|\r|\n)source: \{ file: "(?.+)", row: (?[0-9]+), col: (?[0-9]+) })?/ + /(?:(?:\r\n|\r|\n)source: \{ file: "(?.+)", row: (?[0-9]+), col: (?[0-9]+) })?/ .source, /(?:(?:\r\n|\r|\n)uriResolutionStack: (?\[(?:.|\r\n|\r|\n)+]))?/ .source, @@ -85,6 +103,7 @@ export class WrapError extends Error { this.source = options.source; this.resolutionStack = options.resolutionStack; this.cause = options.cause; + this.stack = options.stack; Object.setPrototypeOf(this, WrapError.prototype); } @@ -96,7 +115,7 @@ export class WrapError extends Error { | "method" | "args" | "file" - | "line" + | "row" | "col" | "resolutionStack" | "cause" @@ -111,9 +130,9 @@ export class WrapError extends Error { method, args, file, - line, + row, col, - resolutionStack, + resolutionStack: resolutionStackStr, cause: causeStr, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion } = result.groups!; @@ -124,11 +143,15 @@ export class WrapError extends Error { const source: WrapErrorSource | undefined = file ? { file, - line: line ? parseInt(line) : undefined, + row: row ? parseInt(row) : undefined, col: col ? parseInt(col) : undefined, } : undefined; + const resolutionStack = resolutionStackStr + ? JSON.parse(resolutionStackStr) + : undefined; + // message may be a stringified WrapError due to current string-based error passing between client and wasm let cause: WrapError | undefined; if (this.re.test(message as string)) { @@ -139,10 +162,10 @@ export class WrapError extends Error { code, uri: uri as string, method, - args, + args: args?.trim(), source, resolutionStack, - cause: cause ?? causeStr, + cause: cause ?? causeStr, // TODO: is it possible that 'cause' information is lost here? }); } @@ -159,10 +182,10 @@ export class WrapError extends Error { const maybeArgs = args ? `args: ${args} ` : ""; // source is uses () instead of {} to facilitate regex const maybeSource = source - ? `source: { file: "${source?.file}", row: ${source?.line}, col: ${source?.col} }` + ? `source: { file: "${source?.file}", row: ${source?.row}, col: ${source?.col} }` : ""; const maybeResolutionStack = resolutionStack - ? `uriResolutionStack: ${resolutionStack}` + ? `uriResolutionStack: ${JSON.stringify(resolutionStack, null, 2)}` : ""; const errorCause = WrapError.stringifyCause(cause); @@ -210,30 +233,40 @@ export class WrapError extends Error { } private static codeToName(code: WrapErrorCode): string { - if (code < 50) { + if (code < 25) { return "WrapError"; - } else if (code < 100) { + } else if (code < 50) { return "UriResolutionError"; - } else if (code < 150) { + } else if (code < 75) { return "InvokeError"; - } else if (code < 200) { - return "SubInvokeError"; - } else { + } else if (code < 100) { return "PluginError"; + } else { + return "WrapError"; } } private static metaMessage(code: WrapErrorCode): string { switch (code) { - case WrapErrorCode.LOAD_WRAPPER_FAIL: + case WrapErrorCode.CLIENT_LOAD_WRAPPER: return "Failed to create Wrapper from WrapPackage."; - case WrapErrorCode.QUERY_MALFORMED: + case WrapErrorCode.CLIENT_GET_FILE: + return "An error occurred while retrieving a file."; + case WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS: + return "An error occurred while retrieving interface implementations."; + case WrapErrorCode.CLIENT_VALIDATE_RESOLUTION: + return "An URI resolution error occurred while validating a WRAP URI."; + case WrapErrorCode.CLIENT_VALIDATE_ABI: + return "An error occurred while validating a WRAP URI against its ABI."; + case WrapErrorCode.CLIENT_VALIDATE_RECURSIVE: + return "An error occurred while recursively validating a WRAP URI."; + case WrapErrorCode.CLIENT_QUERY_MALFORMED: return "Failed to parse GraphQL query."; - case WrapErrorCode.QUERY_FAIL: + case WrapErrorCode.CLIENT_QUERY_FAIL: return "Unknown query exception encountered."; - case WrapErrorCode.URI_RESOLUTION_ERROR: + case WrapErrorCode.URI_RESOLUTION: return "Unable to resolve URI."; - case WrapErrorCode.URI_RESOLVER_ERROR: + case WrapErrorCode.URI_RESOLVER: return "An internal resolver error occurred while resolving a URI."; case WrapErrorCode.URI_NOT_FOUND: return "URI not found."; @@ -241,14 +274,14 @@ export class WrapError extends Error { return "Wasm module aborted execution."; case WrapErrorCode.WASM_INVOKE_FAIL: return "Invocation exception encountered."; - case WrapErrorCode.WASM_NO_MODULE: - return "Wrapper does not contain a wasm module."; + case WrapErrorCode.WASM_MODULE_NOT_FOUND: + return "Wrapper does not contain a Wasm module."; case WrapErrorCode.WASM_METHOD_NOT_FOUND: - return "Could not find invoke function."; + return "Could not find method in Wasm module."; case WrapErrorCode.PLUGIN_METHOD_NOT_FOUND: - return "Method not found."; + return "Method not found in plugin module."; case WrapErrorCode.PLUGIN_ARGS_MALFORMED: - return "Malformed args."; + return "Malformed arguments passed to plugin."; case WrapErrorCode.PLUGIN_INVOKE_FAIL: return "Invocation exception encountered."; default: diff --git a/packages/js/core/src/types/Wrapper.ts b/packages/js/core/src/types/Wrapper.ts index 5d5655b4b3..d2051d8b4f 100644 --- a/packages/js/core/src/types/Wrapper.ts +++ b/packages/js/core/src/types/Wrapper.ts @@ -33,7 +33,6 @@ export interface Wrapper extends Invocable { * Get a file from the Wrapper package. * * @param options Configuration options for file retrieval - * @param client The client instance requesting the file. */ getFile(options: GetFileOptions): Promise>; diff --git a/packages/js/plugin/src/PluginWrapper.ts b/packages/js/plugin/src/PluginWrapper.ts index 5e33f30ae9..fb54c3d080 100644 --- a/packages/js/plugin/src/PluginWrapper.ts +++ b/packages/js/plugin/src/PluginWrapper.ts @@ -105,13 +105,16 @@ export class PluginWrapper implements Wrapper { encoded: false, }; } else { - const error = new WrapError(result.error?.message, { - code: WrapErrorCode.PLUGIN_INVOKE_FAIL, - uri: `${options.uri}; module: ${module}`, - method, - args: JSON.stringify(jsArgs, null, 2), - cause: result.error, - }); + const error = new WrapError( + `Failed to invoke method "${method}" in module: ${this.module}`, + { + code: WrapErrorCode.PLUGIN_INVOKE_FAIL, + uri: options.uri.toString(), + method, + args: JSON.stringify(jsArgs, null, 2), + cause: result.error, + } + ); return ResultErr(error); } } diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index 20d516d248..ef1d3c9fc4 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -154,12 +154,11 @@ export class WasmWrapper implements Wrapper { const args = options.args || {}; const wasmResult = await this._getWasmModule(); if (!wasmResult.ok) { - const error = new WrapError(wasmResult.error?.message, { - code: WrapErrorCode.WASM_NO_MODULE, + const error = new WrapError(wasmResult.error, { + code: WrapErrorCode.WASM_MODULE_NOT_FOUND, uri: options.uri.uri, method, args: JSON.stringify(args, null, 2), - cause: wasmResult.error, }); return ResultErr(error); } @@ -215,7 +214,7 @@ export class WasmWrapper implements Wrapper { state.env.byteLength ); - const invokeResult = this._processInvokeResult(state, result, abort); + const invokeResult = this._processInvokeResult(state, result); if (invokeResult.ok) { return { @@ -239,18 +238,17 @@ export class WasmWrapper implements Wrapper { @Tracer.traceMethod("WasmWrapper: _processInvokeResult") private _processInvokeResult( state: State, - result: boolean, - abort: (message: string) => never + result: boolean ): Result { if (result) { if (!state.invoke.result) { - abort("Invoke result is missing."); + return ResultErr("Invoke result is missing."); } return ResultOk(state.invoke.result); } else { if (!state.invoke.error) { - abort("Invoke error is missing."); + return ResultErr("Invoke error is missing."); } return ResultErr(state.invoke.error); @@ -258,12 +256,12 @@ export class WasmWrapper implements Wrapper { } @Tracer.traceMethod("WasmWrapper: getWasmModule") - private async _getWasmModule(): Promise> { + private async _getWasmModule(): Promise> { if (this._wasmModule === undefined) { const result = await this._fileReader.readFile(WRAP_MODULE_PATH); if (!result.ok) { - return ResultErr(Error(`Wrapper does not contain a wasm module`)); + return ResultErr("Wrapper does not contain a wasm module"); } this._wasmModule = result.value; diff --git a/packages/js/wasm/src/imports.ts b/packages/js/wasm/src/imports.ts index 4d9441f848..be87e5ad81 100644 --- a/packages/js/wasm/src/imports.ts +++ b/packages/js/wasm/src/imports.ts @@ -215,7 +215,7 @@ export const createImports = (config: { const msg = readString(memory.buffer, msgPtr, msgLen); const file = readString(memory.buffer, filePtr, fileLen); - abort(`__wrap_abort: ${msg}`, { file, line, col: column }); + abort(`__wrap_abort: ${msg}`, { file, row: line, col: column }); }, __wrap_debug_log: (ptr: u32, len: u32): void => { const msg = readString(memory.buffer, ptr, len); From beeb967e587e43afaf2b35ea594469cb78648f09 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 8 Dec 2022 15:11:00 +0530 Subject: [PATCH 12/40] fixed WrapError parsing for multi-error stacks --- .../__tests__/core/error-structure.spec.ts | 64 +++++------ .../js/core-client/src/PolywrapCoreClient.ts | 2 +- packages/js/core/src/types/WrapError.ts | 108 +++++++++++++----- packages/js/wasm/src/WasmWrapper.ts | 6 +- 4 files changed, 113 insertions(+), 67 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 9893801cfa..c731b435ad 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -58,7 +58,7 @@ describe("error structure", () => { expect(result.error?.name).toEqual("UriResolutionError"); expect(result.error?.code).toEqual(WrapErrorCode.URI_NOT_FOUND); - expect(result.error?.text.startsWith("Unable to find URI ")).toBeTruthy(); + expect(result.error?.reason.startsWith("Unable to find URI ")).toBeTruthy(); expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build-not-found")).toBeTruthy(); expect(result.error?.resolutionStack).toBeTruthy(); }); @@ -77,7 +77,7 @@ describe("error structure", () => { expect(result.error?.name).toEqual("InvokeError"); expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); - expect(result.error?.text.startsWith("__wrap_abort:")).toBeTruthy(); + expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); expect(result.error?.method).toEqual("simpleMethod"); expect(result.error?.args).toEqual("{\n \"arg\": 3\n}"); @@ -98,12 +98,12 @@ describe("error structure", () => { expect(result.error?.name).toEqual("InvokeError"); expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_FAIL); - expect(result.error?.text.startsWith("Could not find invoke function")).toBeTruthy(); + expect(result.error?.reason.startsWith("Could not find invoke function")).toBeTruthy(); expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); expect(result.error?.method).toEqual("complexMethod"); expect(result.error?.args).toEqual("{\n \"arg\": \"test\"\n}"); expect(result.error?.toString().split("51").length).toEqual(2); - expect(result.error?.cause).toBeUndefined(); + expect(result.error?.prev).toBeUndefined(); }); test("Subinvoke a wrapper that is not found", async () => { @@ -121,19 +121,19 @@ describe("error structure", () => { expect(result.error?.name).toEqual("InvokeError"); expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); - expect(result.error?.text.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); expect(result.error?.method).toEqual("subWrapperNotFound"); expect(result.error?.args).toEqual("{\n \"a\": 1,\n \"b\": 1\n}"); expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); - expect(result.error?.cause instanceof WrapError).toBeTruthy(); - const cause = result.error?.cause as WrapError; - expect(cause.name).toEqual("UriResolutionError"); - expect(cause.code).toEqual(WrapErrorCode.URI_NOT_FOUND); - expect(cause.text).toEqual("Unable to find URI wrap://ens/not-found.eth."); - expect(cause.uri).toEqual("wrap://ens/not-found.eth"); - expect(cause.resolutionStack).toBeTruthy(); + expect(result.error?.prev instanceof WrapError).toBeTruthy(); + const prev = result.error?.prev as WrapError; + expect(prev.name).toEqual("UriResolutionError"); + expect(prev.code).toEqual(WrapErrorCode.URI_NOT_FOUND); + expect(prev.reason).toEqual("Unable to find URI wrap://ens/not-found.eth."); + expect(prev.uri).toEqual("wrap://ens/not-found.eth"); + expect(prev.resolutionStack).toBeTruthy(); }); test("Subinvoke error two layers deep", async () => { @@ -151,7 +151,7 @@ describe("error structure", () => { expect(result.error?.name).toEqual("InvokeError"); expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); - expect(result.error?.text.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); expect(result.error?.method).toEqual("throwsInTwoSubinvokeLayers"); expect(result.error?.args).toEqual(`{ @@ -160,25 +160,25 @@ describe("error structure", () => { }`); expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); - expect(result.error?.cause instanceof WrapError).toBeTruthy(); - const cause = result.error?.cause as WrapError; - expect(cause.name).toEqual("InvokeError"); - expect(cause.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); - expect(cause.text.startsWith("SubInvocation exception encountered")).toBeTruthy(); - expect(cause.uri).toEqual("wrap://ens/bad-math.eth"); - expect(cause.method).toEqual("subInvokeWillThrow"); - expect(cause.args).toEqual("{\n \"0\": 130,\n \"1\": 161,\n \"2\": 97,\n \"3\": 1,\n \"4\": 161,\n \"5\": 98,\n \"6\": 1\n}"); - expect(cause.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); - - expect(cause.cause instanceof WrapError).toBeTruthy(); - const causeOfCause = cause.cause as WrapError; - expect(causeOfCause.name).toEqual("InvokeError"); - expect(causeOfCause.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); - expect(causeOfCause.text).toEqual("__wrap_abort: I threw an error!"); - expect(causeOfCause.uri.endsWith("wrap://ens/bad-util.eth")).toBeTruthy(); - expect(causeOfCause.method).toEqual("iThrow"); - expect(causeOfCause.args).toEqual("{\n \"0\": 129,\n \"1\": 161,\n \"2\": 97,\n \"3\": 0\n}"); - expect(causeOfCause.source).toEqual({ file: "src/index.ts", row: 5, col: 5 }); + expect(result.error?.prev instanceof WrapError).toBeTruthy(); + const prev = result.error?.prev as WrapError; + expect(prev.name).toEqual("InvokeError"); + expect(prev.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(prev.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(prev.uri).toEqual("wrap://ens/bad-math.eth"); + expect(prev.method).toEqual("subInvokeWillThrow"); + expect(prev.args).toEqual("{\n \"0\": 130,\n \"1\": 161,\n \"2\": 97,\n \"3\": 1,\n \"4\": 161,\n \"5\": 98,\n \"6\": 1\n}"); + expect(prev.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); + + expect(prev.prev instanceof WrapError).toBeTruthy(); + const prevOfPrev = prev.prev as WrapError; + expect(prevOfPrev.name).toEqual("InvokeError"); + expect(prevOfPrev.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(prevOfPrev.reason).toEqual("__wrap_abort: I threw an error!"); + expect(prevOfPrev.uri.endsWith("wrap://ens/bad-util.eth")).toBeTruthy(); + expect(prevOfPrev.method).toEqual("iThrow"); + expect(prevOfPrev.args).toEqual("{\n \"0\": 129,\n \"1\": 161,\n \"2\": 97,\n \"3\": 0\n}"); + expect(prevOfPrev.source).toEqual({ file: "src/index.ts", row: 5, col: 5 }); }); test("Invoke a wrapper of incompatible version", async () => { diff --git a/packages/js/core-client/src/PolywrapCoreClient.ts b/packages/js/core-client/src/PolywrapCoreClient.ts index 9085be3894..f554a63c86 100644 --- a/packages/js/core-client/src/PolywrapCoreClient.ts +++ b/packages/js/core-client/src/PolywrapCoreClient.ts @@ -801,7 +801,7 @@ export class PolywrapCoreClient implements CoreClient { if (invalidUris.length) { let message = "The following URIs failed validation:"; for (const { error } of invalidUris) { - message += `\n${error.uri} -> ${error.text}`; + message += `\n${error.uri} -> ${error.reason}`; } const error = new WrapError(message, { code: WrapErrorCode.CLIENT_VALIDATE_RECURSIVE, diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 6b75f27b58..9cbd14c0ce 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -52,6 +52,7 @@ export interface WrapErrorOptions { source?: WrapErrorSource; resolutionStack?: CleanResolutionStep; cause?: unknown; + prev?: Error; stack?: string; } @@ -64,22 +65,23 @@ type RegExpGroups = export class WrapError extends Error { readonly name: string; readonly code: WrapErrorCode; - readonly text: string; + readonly reason: string; readonly uri: string; readonly method?: string; readonly args?: string; readonly source?: WrapErrorSource; readonly resolutionStack?: CleanResolutionStep; readonly cause?: unknown; + readonly prev?: Error; - static re = new RegExp( + private static re = new RegExp( [ // [A-z]+Error can be replaced with specific error names when finalized /^(?:[A-z_: ]*; )?[A-z]+Error: [\w ]+\./.source, // there is some padding added to the number of words expected in an error code /(?:\r\n|\r|\n)code: (?1?[0-9]{1,2}|2[0-4][0-9]|25[0-5]) (?:[A-Z]+ ?){1,5}/ .source, - /(?:\r\n|\r|\n)message: (?(?:.|\r\n|\r|\n)*)/.source, + /(?:\r\n|\r|\n)reason: (?(?:.|\r\n|\r|\n)*)/.source, /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-z0-9_-]+\/.+)/.source, /(?:(?:\r\n|\r|\n)method: (?([A-z_]{1}[A-z0-9_]*)))?/.source, /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r\n|\r|\n)+} ))?/.source, @@ -87,16 +89,16 @@ export class WrapError extends Error { .source, /(?:(?:\r\n|\r|\n)uriResolutionStack: (?\[(?:.|\r\n|\r|\n)+]))?/ .source, - /(?:(?:\r\n|\r|\n){2}Another exception was encountered during execution:(?:\r\n|\r|\n)(?(?:.|\r\n|\r|\n)+))?$/ + /(?:(?:\r\n|\r|\n){2}This exception was caused by the following exception:(?:\r\n|\r|\n)(?(?:.|\r\n|\r|\n)+))?$/ .source, ].join("") ); - constructor(text = "Encountered an exception.", options: WrapErrorOptions) { - super(WrapError.stringify(text, options)); + constructor(reason = "Encountered an exception.", options: WrapErrorOptions) { + super(WrapError.stringify(reason, options)); this.name = WrapError.codeToName(options.code); this.code = options.code; - this.text = text; + this.reason = reason; this.uri = options.uri; this.method = options.method; this.args = options.args; @@ -104,13 +106,50 @@ export class WrapError extends Error { this.resolutionStack = options.resolutionStack; this.cause = options.cause; this.stack = options.stack; + this.prev = options.prev; Object.setPrototypeOf(this, WrapError.prototype); } static parse(error: string): WrapError | undefined { + const errorStrings = error.split( + "\n\nAnother exception was encountered during execution:\n" + ); + + // case: single WrapError + if (errorStrings.length === 1) { + const args = WrapError._parse(error); + return args ? new WrapError(args.reason, args.options) : undefined; + } + + // case: stack of WrapErrors stringified + const errArgs = errorStrings.map(WrapError._parse); + + // iterate through args to assign `cause` and `prev` + let curr: WrapError | undefined = undefined; + for (let i = errArgs.length - 1; i >= 0; i--) { + const currArgs = errArgs[i]; + if (!currArgs) { + throw new Error("Failed to parse WrapError"); + } + curr = new WrapError(currArgs.reason, { + ...currArgs.options, + prev: curr, + }); + } + return curr; + } + + toString(): string { + return `${this.name}: ${this.message}`; + } + + // parse a single WrapError, where the 'prev' property is undefined + private static _parse( + error: string + ): { reason: string; options: WrapErrorOptions } | undefined { const result: RegExpGroups< | "code" - | "message" + | "reason" | "uri" | "method" | "args" @@ -119,13 +158,13 @@ export class WrapError extends Error { | "col" | "resolutionStack" | "cause" - > = this.re.exec(error); + > = WrapError.re.exec(error); if (!result) { return undefined; } const { code: codeStr, - message, + reason, uri, method, args, @@ -133,7 +172,7 @@ export class WrapError extends Error { row, col, resolutionStack: resolutionStackStr, - cause: causeStr, + cause, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion } = result.groups!; @@ -152,29 +191,31 @@ export class WrapError extends Error { ? JSON.parse(resolutionStackStr) : undefined; - // message may be a stringified WrapError due to current string-based error passing between client and wasm - let cause: WrapError | undefined; - if (this.re.test(message as string)) { - cause = this.parse(message as string); - } + return { + reason: reason as string, + options: { + code, + uri: uri as string, + method, + args: args?.trim(), + source, + resolutionStack, + cause, + }, + }; + } - return new WrapError(message, { + private static stringify(reason: string, options: WrapErrorOptions) { + const { code, - uri: uri as string, + uri, method, - args: args?.trim(), + args, source, resolutionStack, - cause: cause ?? causeStr, // TODO: is it possible that 'cause' information is lost here? - }); - } - - toString(): string { - return `${this.name}: ${this.message}`; - } - - private static stringify(text: string, options: WrapErrorOptions) { - const { code, uri, method, args, source, resolutionStack, cause } = options; + cause, + prev, + } = options; const formattedCode = `${code} ${WrapErrorCode[code].replace(/_/g, " ")}`; // Some items are not always present @@ -190,19 +231,24 @@ export class WrapError extends Error { const errorCause = WrapError.stringifyCause(cause); const maybeCause = errorCause - ? `\nAnother exception was encountered during execution:\n${errorCause}` + ? `\nThis exception was caused by the following exception:\n${errorCause}` + : ""; + + const maybeDelim = prev + ? `\nAnother exception was encountered during execution:\n${prev}` : ""; return [ WrapError.metaMessage(code), `code: ${formattedCode}`, - `message: ${text}`, + `reason: ${reason}`, `uri: ${uri}`, maybeMethod, maybeArgs, maybeSource, maybeResolutionStack, maybeCause, + maybeDelim, ] .filter((it) => !!it) .join("\n"); diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index ef1d3c9fc4..67f409387d 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -182,15 +182,15 @@ export class WasmWrapper implements Wrapper { }; const abort = (message: string, source?: WrapErrorSource) => { - const cause = WrapError.parse(message); - const text = cause ? "SubInvocation exception encountered" : message; + const prev = WrapError.parse(message); + const text = prev ? "SubInvocation exception encountered" : message; throw new WrapError(text, { code: WrapErrorCode.WASM_INVOKE_ABORTED, uri: options.uri.uri, method, args: JSON.stringify(args, null, 2), source, - cause, + prev, }); }; From 9db1636de58974e4d8107433a9a56cb5190cbb91 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 8 Dec 2022 20:26:38 +0530 Subject: [PATCH 13/40] initial wrap error structure tests passing --- .../src/__tests__/core/error-structure.spec.ts | 10 ++++++++-- packages/js/core-client/src/PolywrapCoreClient.ts | 15 ++++++--------- packages/js/wasm/src/WasmPackage.ts | 8 +++++++- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index c731b435ad..a5bf307ba7 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -190,7 +190,13 @@ describe("error structure", () => { }, }); - if (!result.ok) fail(result.error); - expect(result.value).toEqual("test"); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("UriResolutionError"); + expect(result.error?.code).toEqual(WrapErrorCode.URI_RESOLVER); + expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated")).toBeTruthy(); + expect(result.error?.resolutionStack).toBeDefined(); + expect(`${result.error?.cause}`).toContain(`Unrecognized WrapManifest schema version "0.0.1"`); }); }); diff --git a/packages/js/core-client/src/PolywrapCoreClient.ts b/packages/js/core-client/src/PolywrapCoreClient.ts index f554a63c86..02550c18d5 100644 --- a/packages/js/core-client/src/PolywrapCoreClient.ts +++ b/packages/js/core-client/src/PolywrapCoreClient.ts @@ -656,15 +656,12 @@ export class PolywrapCoreClient implements CoreClient { let error: WrapError; if (result.error) { - error = new WrapError( - "An internal resolver error occurred while resolving a URI", - { - code: WrapErrorCode.URI_RESOLVER, - uri: uri.uri, - resolutionStack: history, - cause: result.error, - } - ); + error = new WrapError("A URI Resolver returned an error.", { + code: WrapErrorCode.URI_RESOLVER, + uri: uri.uri, + resolutionStack: history, + cause: result.error, + }); } else { error = new WrapError("Error resolving URI", { code: WrapErrorCode.URI_RESOLUTION, diff --git a/packages/js/wasm/src/WasmPackage.ts b/packages/js/wasm/src/WasmPackage.ts index 3ab37e17a5..bad1ee044d 100644 --- a/packages/js/wasm/src/WasmPackage.ts +++ b/packages/js/wasm/src/WasmPackage.ts @@ -44,7 +44,13 @@ export class WasmPackage implements IWasmPackage { } const wrapManifest = result.value; - return ResultOk(await deserializeWrapManifest(wrapManifest, options)); + + try { + const deserialized = await deserializeWrapManifest(wrapManifest, options); + return ResultOk(deserialized); + } catch (e) { + return ResultErr(e); + } } async getWasmModule(): Promise> { From a201bd628fd2fe605629f067e8f75a594850fc49 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Fri, 9 Dec 2022 12:36:11 +0530 Subject: [PATCH 14/40] added try-catch block to PluginModule method invocation --- packages/js/plugin/src/PluginModule.ts | 9 ++++++--- packages/js/plugin/src/PluginWrapper.ts | 20 +++++++++---------- .../src/utils/PluginModuleWithMethods.ts | 17 ++++++++++------ packages/js/wasm/src/WasmPackage.ts | 3 +-- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/packages/js/plugin/src/PluginModule.ts b/packages/js/plugin/src/PluginModule.ts index 739f0ff7f3..8298820e08 100644 --- a/packages/js/plugin/src/PluginModule.ts +++ b/packages/js/plugin/src/PluginModule.ts @@ -47,9 +47,12 @@ export abstract class PluginModule< ); } - const data = await fn(args, client); - - return ResultOk(data); + try { + const data = await fn(args, client); + return ResultOk(data); + } catch (e) { + return ResultErr(e); + } } public getMethod< diff --git a/packages/js/plugin/src/PluginWrapper.ts b/packages/js/plugin/src/PluginWrapper.ts index fb54c3d080..3a666b7624 100644 --- a/packages/js/plugin/src/PluginWrapper.ts +++ b/packages/js/plugin/src/PluginWrapper.ts @@ -55,7 +55,7 @@ export class PluginWrapper implements Wrapper { const args = options.args || {}; if (!this.module.getMethod(method)) { - const error = new WrapError(`Method "${method}" not found.`, { + const error = new WrapError(`Plugin missing method "${method}"`, { code: WrapErrorCode.PLUGIN_METHOD_NOT_FOUND, uri: options.uri.uri, method, @@ -105,16 +105,14 @@ export class PluginWrapper implements Wrapper { encoded: false, }; } else { - const error = new WrapError( - `Failed to invoke method "${method}" in module: ${this.module}`, - { - code: WrapErrorCode.PLUGIN_INVOKE_FAIL, - uri: options.uri.toString(), - method, - args: JSON.stringify(jsArgs, null, 2), - cause: result.error, - } - ); + const reason = `Failed to invoke method "${method}" in module: ${this.module}`; + const error = new WrapError(reason, { + code: WrapErrorCode.PLUGIN_INVOKE_FAIL, + uri: options.uri.toString(), + method, + args: JSON.stringify(jsArgs, null, 2), + cause: result.error, + }); return ResultErr(error); } } diff --git a/packages/js/plugin/src/utils/PluginModuleWithMethods.ts b/packages/js/plugin/src/utils/PluginModuleWithMethods.ts index 665e264fff..ba5d0af22b 100644 --- a/packages/js/plugin/src/utils/PluginModuleWithMethods.ts +++ b/packages/js/plugin/src/utils/PluginModuleWithMethods.ts @@ -5,7 +5,7 @@ import { PluginModule } from "../PluginModule"; import { GetPluginMethodsFunc } from "./GetPluginMethodsFunc"; import { CoreClient } from "@polywrap/core-js"; -import { Result, ResultOk } from "@polywrap/result"; +import { Result, ResultErr, ResultOk } from "@polywrap/result"; export class PluginModuleWithMethods< TEnv extends Record = Record @@ -25,16 +25,21 @@ export class PluginModuleWithMethods< const fn = this.getMethod(method); if (!fn) { - throw Error(`Plugin missing method "${method}"`); + return ResultErr(Error(`Plugin missing method "${method}"`)); } if (typeof fn !== "function") { - throw Error(`Plugin method "${method}" must be of type 'function'`); + return ResultErr( + Error(`Plugin method "${method}" must be of type 'function'`) + ); } - const data = await fn(args, client); - - return ResultOk(data); + try { + const data = await fn(args, client); + return ResultOk(data); + } catch (e) { + return ResultErr(e); + } } getMethod< diff --git a/packages/js/wasm/src/WasmPackage.ts b/packages/js/wasm/src/WasmPackage.ts index bad1ee044d..8e32997745 100644 --- a/packages/js/wasm/src/WasmPackage.ts +++ b/packages/js/wasm/src/WasmPackage.ts @@ -46,8 +46,7 @@ export class WasmPackage implements IWasmPackage { const wrapManifest = result.value; try { - const deserialized = await deserializeWrapManifest(wrapManifest, options); - return ResultOk(deserialized); + return ResultOk(await deserializeWrapManifest(wrapManifest, options)); } catch (e) { return ResultErr(e); } From ba5c3dc8cb0d3ad54b43da9905f469cc7025cd90 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Fri, 9 Dec 2022 13:20:02 +0530 Subject: [PATCH 15/40] fixed some plugin and client tests for WrapError --- .../client/src/__tests__/core/error-structure.spec.ts | 10 +++++----- packages/js/client/src/__tests__/core/sanity.spec.ts | 4 ++-- packages/js/core/src/__tests__/MaybeAsync.spec.ts | 2 +- packages/js/plugins/ipfs/src/__tests__/e2e.spec.ts | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index a5bf307ba7..607ad52368 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,6 +1,6 @@ import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; -// import { buildWrapper } from "@polywrap/test-env-js"; +import { buildWrapper } from "@polywrap/test-env-js"; import { WrapError, WrapErrorCode } from "@polywrap/core-js"; jest.setTimeout(360000); @@ -25,10 +25,10 @@ describe("error structure", () => { let client: PolywrapClient; beforeAll(async () => { - // await buildWrapper(simpleWrapperPath); - // await buildWrapper(badUtilWrapperPath); - // await buildWrapper(badMathWrapperPath); - // await buildWrapper(subinvokeErrorWrapperPath); + await buildWrapper(simpleWrapperPath); + await buildWrapper(badUtilWrapperPath); + await buildWrapper(badMathWrapperPath); + await buildWrapper(subinvokeErrorWrapperPath); client = new PolywrapClient({ redirects: [ diff --git a/packages/js/client/src/__tests__/core/sanity.spec.ts b/packages/js/client/src/__tests__/core/sanity.spec.ts index 236d552eab..0c57c285c2 100644 --- a/packages/js/client/src/__tests__/core/sanity.spec.ts +++ b/packages/js/client/src/__tests__/core/sanity.spec.ts @@ -70,7 +70,7 @@ describe("sanity", () => { expect(result.ok).toBeFalsy(); let resultError = (result as { error: Error }).error; expect(resultError).toBeTruthy(); - expect(resultError.message).toContain("Error resolving URI"); + expect(resultError.message).toContain("URI not found"); let fooPackage: IUriPackage = { uri: fooUri, @@ -95,7 +95,7 @@ describe("sanity", () => { resultError = (result as { error: Error }).error; expect(result.ok).toBeFalsy(); expect(resultError).toBeTruthy(); - expect(resultError.message).toContain("Error resolving URI"); + expect(resultError.message).toContain("URI not found"); await buildWrapper(greetingPath); diff --git a/packages/js/core/src/__tests__/MaybeAsync.spec.ts b/packages/js/core/src/__tests__/MaybeAsync.spec.ts index f23280de65..f9a297991d 100644 --- a/packages/js/core/src/__tests__/MaybeAsync.spec.ts +++ b/packages/js/core/src/__tests__/MaybeAsync.spec.ts @@ -1,4 +1,4 @@ -import { MaybeAsync, isPromise } from ".."; +import { MaybeAsync } from ".."; interface IClassInterface { normalMethod(arg: string): MaybeAsync; diff --git a/packages/js/plugins/ipfs/src/__tests__/e2e.spec.ts b/packages/js/plugins/ipfs/src/__tests__/e2e.spec.ts index b38febc227..09a29a2d66 100644 --- a/packages/js/plugins/ipfs/src/__tests__/e2e.spec.ts +++ b/packages/js/plugins/ipfs/src/__tests__/e2e.spec.ts @@ -140,8 +140,8 @@ describe("IPFS Plugin", () => { expect(result.ok).toBeFalsy(); result = result as { ok: false; error: Error | undefined }; expect(result.error).toBeTruthy(); - expect(result.error?.stack).toMatch("Timeout has been reached"); - expect(result.error?.stack).toMatch("Timeout: 1000"); + expect(result.error?.message).toMatch("Timeout has been reached"); + expect(result.error?.message).toMatch("Timeout: 1000"); const catPromiseWithTimeoutOverride = Ipfs_Module.cat( { @@ -164,8 +164,8 @@ describe("IPFS Plugin", () => { error: Error | undefined; }; expect(resultForOverride.error).toBeTruthy(); - expect(resultForOverride.error?.stack).toMatch("Timeout has been reached"); - expect(resultForOverride.error?.stack).toMatch("Timeout: 500"); + expect(resultForOverride.error?.message).toMatch("Timeout has been reached"); + expect(resultForOverride.error?.message).toMatch("Timeout: 500"); }); it("Should use provider from method options", async () => { From bf7fb2a958d43880e4751e71fb386cca7aa47dd4 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Fri, 9 Dec 2022 13:54:06 +0530 Subject: [PATCH 16/40] fixed more plugin and client tests for WrapError --- .../__tests__/core/error-structure.spec.ts | 22 +++++++++---------- .../client/src/__tests__/core/sanity.spec.ts | 2 +- .../js/client/src/__tests__/e2e/test-cases.ts | 2 +- .../ethereum/src/__tests__/e2e.spec.ts | 3 ++- .../file-system/src/__tests__/e2e.spec.ts | 3 ++- .../http/src/__tests__/e2e/e2e.spec.ts | 5 +++-- .../js/plugins/ipfs/src/__tests__/e2e.spec.ts | 4 ++-- 7 files changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 607ad52368..981d274cf8 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,6 +1,6 @@ import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; -import { buildWrapper } from "@polywrap/test-env-js"; +// import { buildWrapper } from "@polywrap/test-env-js"; import { WrapError, WrapErrorCode } from "@polywrap/core-js"; jest.setTimeout(360000); @@ -25,10 +25,10 @@ describe("error structure", () => { let client: PolywrapClient; beforeAll(async () => { - await buildWrapper(simpleWrapperPath); - await buildWrapper(badUtilWrapperPath); - await buildWrapper(badMathWrapperPath); - await buildWrapper(subinvokeErrorWrapperPath); + // await buildWrapper(simpleWrapperPath); + // await buildWrapper(badUtilWrapperPath); + // await buildWrapper(badMathWrapperPath); + // await buildWrapper(subinvokeErrorWrapperPath); client = new PolywrapClient({ redirects: [ @@ -59,7 +59,7 @@ describe("error structure", () => { expect(result.error?.name).toEqual("UriResolutionError"); expect(result.error?.code).toEqual(WrapErrorCode.URI_NOT_FOUND); expect(result.error?.reason.startsWith("Unable to find URI ")).toBeTruthy(); - expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build-not-found")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build-not-found")).toBeTruthy(); expect(result.error?.resolutionStack).toBeTruthy(); }); @@ -78,7 +78,7 @@ describe("error structure", () => { expect(result.error?.name).toEqual("InvokeError"); expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); - expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); expect(result.error?.method).toEqual("simpleMethod"); expect(result.error?.args).toEqual("{\n \"arg\": 3\n}"); expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/msgpack/ReadDecoder.ts", row: 167, col: 5 }); @@ -99,7 +99,7 @@ describe("error structure", () => { expect(result.error?.name).toEqual("InvokeError"); expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_FAIL); expect(result.error?.reason.startsWith("Could not find invoke function")).toBeTruthy(); - expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); expect(result.error?.method).toEqual("complexMethod"); expect(result.error?.args).toEqual("{\n \"arg\": \"test\"\n}"); expect(result.error?.toString().split("51").length).toEqual(2); @@ -122,7 +122,7 @@ describe("error structure", () => { expect(result.error?.name).toEqual("InvokeError"); expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); - expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); expect(result.error?.method).toEqual("subWrapperNotFound"); expect(result.error?.args).toEqual("{\n \"a\": 1,\n \"b\": 1\n}"); expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); @@ -152,7 +152,7 @@ describe("error structure", () => { expect(result.error?.name).toEqual("InvokeError"); expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); - expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); expect(result.error?.method).toEqual("throwsInTwoSubinvokeLayers"); expect(result.error?.args).toEqual(`{ "a": 1, @@ -195,7 +195,7 @@ describe("error structure", () => { expect(result.error?.name).toEqual("UriResolutionError"); expect(result.error?.code).toEqual(WrapErrorCode.URI_RESOLVER); - expect(result.error?.uri.endsWith("monorepo/packages/test-cases/cases/wrappers/wasm-as/simple-deprecated")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple-deprecated")).toBeTruthy(); expect(result.error?.resolutionStack).toBeDefined(); expect(`${result.error?.cause}`).toContain(`Unrecognized WrapManifest schema version "0.0.1"`); }); diff --git a/packages/js/client/src/__tests__/core/sanity.spec.ts b/packages/js/client/src/__tests__/core/sanity.spec.ts index 0c57c285c2..cac600faad 100644 --- a/packages/js/client/src/__tests__/core/sanity.spec.ts +++ b/packages/js/client/src/__tests__/core/sanity.spec.ts @@ -70,7 +70,7 @@ describe("sanity", () => { expect(result.ok).toBeFalsy(); let resultError = (result as { error: Error }).error; expect(resultError).toBeTruthy(); - expect(resultError.message).toContain("URI not found"); + expect(resultError.message).toContain("Unable to resolve URI"); let fooPackage: IUriPackage = { uri: fooUri, diff --git a/packages/js/client/src/__tests__/e2e/test-cases.ts b/packages/js/client/src/__tests__/e2e/test-cases.ts index 9a0c3176ad..af7f276571 100644 --- a/packages/js/client/src/__tests__/e2e/test-cases.ts +++ b/packages/js/client/src/__tests__/e2e/test-cases.ts @@ -374,7 +374,7 @@ export const runInvalidTypesTest = async (client: CoreClient, uri: string) => { arg: 10, }, }); - invalidBoolIntSent = invalidBoolIntSent as { ok: false; error: Error }; + invalidBoolIntSent = invalidBoolIntSent as ErrResult; expect(invalidBoolIntSent.error).toBeTruthy(); expect(invalidBoolIntSent.error?.message).toMatch( /Property must be of type 'bool'. Found 'int'./ diff --git a/packages/js/plugins/ethereum/src/__tests__/e2e.spec.ts b/packages/js/plugins/ethereum/src/__tests__/e2e.spec.ts index cea0bb531a..35d241b8db 100644 --- a/packages/js/plugins/ethereum/src/__tests__/e2e.spec.ts +++ b/packages/js/plugins/ethereum/src/__tests__/e2e.spec.ts @@ -22,6 +22,7 @@ import { keccak256 } from "js-sha3"; import { Connections } from "../Connections"; import { Connection } from "../Connection"; import { getDefaultConfig } from "./helpers/getDefaultConfig"; +import { WrapError } from "@polywrap/core-js"; const { hash: namehash } = require("eth-ens-namehash"); const contracts = { @@ -935,7 +936,7 @@ describe("Ethereum Plugin", () => { uri, method: "requestAccounts", }); - result = result as { ok: false; error: Error | undefined }; + result = result as { ok: false; error: WrapError | undefined }; // eth_requestAccounts is not supported by Ganache // this RPC error indicates that the method call was attempted expect( diff --git a/packages/js/plugins/file-system/src/__tests__/e2e.spec.ts b/packages/js/plugins/file-system/src/__tests__/e2e.spec.ts index 4e9b2103dd..f0df413665 100644 --- a/packages/js/plugins/file-system/src/__tests__/e2e.spec.ts +++ b/packages/js/plugins/file-system/src/__tests__/e2e.spec.ts @@ -5,6 +5,7 @@ import { FileSystem_Module, FileSystem_EncodingEnum } from "../wrap"; import fs from "fs"; import path from "path"; import fileSystemEncodingToBufferEncoding from "../utils/fileSystemEncodingToBufferEncoding"; +import { WrapError } from "@polywrap/core-js"; jest.setTimeout(360000); @@ -61,7 +62,7 @@ describe("FileSystem plugin", () => { client ); - result = result as { ok: false; error: Error | undefined }; + result = result as { ok: false; error: WrapError | undefined }; expect(result.error).toBeTruthy(); expect(result.ok).toBeFalsy(); }); diff --git a/packages/js/plugins/http/src/__tests__/e2e/e2e.spec.ts b/packages/js/plugins/http/src/__tests__/e2e/e2e.spec.ts index 8c93d0c937..4b29dec15a 100644 --- a/packages/js/plugins/http/src/__tests__/e2e/e2e.spec.ts +++ b/packages/js/plugins/http/src/__tests__/e2e/e2e.spec.ts @@ -5,6 +5,7 @@ import { PolywrapClient } from "@polywrap/client-js"; import { UriResolver } from "@polywrap/uri-resolvers-js"; import nock from "nock"; +import { WrapError } from "@polywrap/core-js"; jest.setTimeout(360000); @@ -133,7 +134,7 @@ describe("e2e tests for HttpPlugin", () => { }, }); - response = response as { ok: false; error: Error | undefined }; + response = response as { ok: false; error: WrapError | undefined }; expect(response.error).toBeDefined(); expect(response.ok).toBeFalsy(); }); @@ -283,7 +284,7 @@ describe("e2e tests for HttpPlugin", () => { }, }); - response = response as { ok: false; error: Error | undefined }; + response = response as { ok: false; error: WrapError | undefined }; expect(response.error).toBeDefined(); expect(response.ok).toBeFalsy(); }); diff --git a/packages/js/plugins/ipfs/src/__tests__/e2e.spec.ts b/packages/js/plugins/ipfs/src/__tests__/e2e.spec.ts index 09a29a2d66..ad27f00599 100644 --- a/packages/js/plugins/ipfs/src/__tests__/e2e.spec.ts +++ b/packages/js/plugins/ipfs/src/__tests__/e2e.spec.ts @@ -1,4 +1,4 @@ -import { Result } from "@polywrap/core-js"; +import { Result, WrapError } from "@polywrap/core-js"; import { initTestEnvironment, providers, @@ -138,7 +138,7 @@ describe("IPFS Plugin", () => { expect(result).toBeTruthy(); expect(result.ok).toBeFalsy(); - result = result as { ok: false; error: Error | undefined }; + result = result as { ok: false; error: WrapError | undefined }; expect(result.error).toBeTruthy(); expect(result.error?.message).toMatch("Timeout has been reached"); expect(result.error?.message).toMatch("Timeout: 1000"); From 743f239eb6432149ef8e43b0ec5265c8943f2447 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Sat, 10 Dec 2022 10:38:26 +0530 Subject: [PATCH 17/40] added WASM_STATE_ERROR and WASM_SERIALIZATION_ERROR error codes to WrapError --- .../__tests__/core/error-structure.spec.ts | 47 +++++++++--- packages/js/core/src/types/WrapError.ts | 20 +++-- packages/js/wasm/src/WasmWrapper.ts | 8 +- packages/js/wasm/src/imports.ts | 73 +++++++++++++++---- 4 files changed, 116 insertions(+), 32 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 981d274cf8..8bd09755c0 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,10 +1,12 @@ import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; -// import { buildWrapper } from "@polywrap/test-env-js"; +import { buildWrapper } from "@polywrap/test-env-js"; import { WrapError, WrapErrorCode } from "@polywrap/core-js"; -jest.setTimeout(360000); +jest.setTimeout(660000); + +// AS const simpleWrapperPath = `${GetPathToTestWrappers()}/wasm-as/simple`; const simpleWrapperUri = new Uri(`fs/${simpleWrapperPath}/build`); @@ -20,15 +22,20 @@ const badUtilWrapperUri = new Uri(`fs/${badUtilWrapperPath}/build`); const incompatibleWrapperPath = `${GetPathToTestWrappers()}/wasm-as/simple-deprecated`; const incompatibleWrapperUri = new Uri(`fs/${incompatibleWrapperPath}`); +// RS +const invalidTypesWrapperPath = `${GetPathToTestWrappers()}/wasm-rs/invalid-types`; +const invalidTypesWrapperUri = new Uri(`fs/${invalidTypesWrapperPath}/build`); + describe("error structure", () => { let client: PolywrapClient; beforeAll(async () => { - // await buildWrapper(simpleWrapperPath); - // await buildWrapper(badUtilWrapperPath); - // await buildWrapper(badMathWrapperPath); - // await buildWrapper(subinvokeErrorWrapperPath); + await buildWrapper(simpleWrapperPath); + await buildWrapper(badUtilWrapperPath); + await buildWrapper(badMathWrapperPath); + await buildWrapper(subinvokeErrorWrapperPath); + await buildWrapper(invalidTypesWrapperPath); client = new PolywrapClient({ redirects: [ @@ -63,7 +70,7 @@ describe("error structure", () => { expect(result.error?.resolutionStack).toBeTruthy(); }); - test("Invoke a wrapper with malformed arguments", async () => { + test("Invoke a wrapper with malformed arguments - as", async () => { const result = await client.invoke({ uri: simpleWrapperUri.uri, method: "simpleMethod", @@ -76,7 +83,7 @@ describe("error structure", () => { if (result.ok) throw Error("should never happen"); expect(result.error?.name).toEqual("InvokeError"); - expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_SERIALIZATION_ERROR); expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); expect(result.error?.method).toEqual("simpleMethod"); @@ -84,6 +91,28 @@ describe("error structure", () => { expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/msgpack/ReadDecoder.ts", row: 167, col: 5 }); }); + test("Invoke a wrapper with malformed arguments - rs", async () => { + const result = await client.invoke({ + uri: invalidTypesWrapperUri.uri, + method: "boolMethod", + args: { + arg: 3, + }, + }); + + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_SERIALIZATION_ERROR); + expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-rs/invalid-types/build")).toBeTruthy(); + expect(result.error?.method).toEqual("boolMethod"); + expect(result.error?.args).toEqual("{\n \"arg\": 3\n}"); + expect(result.error?.source).toEqual({ file: "src/wrap/module/wrapped.rs", row: 38, col: 13 }); + }); + + test("Invoke a wrapper method that doesn't exist", async () => { const result = await client.invoke({ uri: simpleWrapperUri.uri, @@ -102,7 +131,7 @@ describe("error structure", () => { expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); expect(result.error?.method).toEqual("complexMethod"); expect(result.error?.args).toEqual("{\n \"arg\": \"test\"\n}"); - expect(result.error?.toString().split("51").length).toEqual(2); + expect(result.error?.toString().split("52").length).toEqual(2); expect(result.error?.prev).toBeUndefined(); }); diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 9cbd14c0ce..f51603deca 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -30,9 +30,11 @@ export enum WrapErrorCode { URI_RESOLVER, URI_NOT_FOUND, WASM_INVOKE_ABORTED = 50, + WASM_SUBINVOKE_ABORTED = 51, WASM_INVOKE_FAIL, WASM_MODULE_NOT_FOUND, - WASM_METHOD_NOT_FOUND, // not yet used + WASM_STATE_ERROR, + WASM_SERIALIZATION_ERROR, PLUGIN_INVOKE_FAIL = 75, PLUGIN_METHOD_NOT_FOUND, PLUGIN_ARGS_MALFORMED, @@ -111,11 +113,10 @@ export class WrapError extends Error { } static parse(error: string): WrapError | undefined { - const errorStrings = error.split( - "\n\nAnother exception was encountered during execution:\n" - ); + const delim = "\n\nAnother exception was encountered during execution:\n"; + const errorStrings = error.split(delim); - // case: single WrapError + // case: single WrapError or not a WrapError if (errorStrings.length === 1) { const args = WrapError._parse(error); return args ? new WrapError(args.reason, args.options) : undefined; @@ -129,6 +130,7 @@ export class WrapError extends Error { for (let i = errArgs.length - 1; i >= 0; i--) { const currArgs = errArgs[i]; if (!currArgs) { + // should only happen if a user includes the delimiter in their error message throw new Error("Failed to parse WrapError"); } curr = new WrapError(currArgs.reason, { @@ -318,12 +320,16 @@ export class WrapError extends Error { return "URI not found."; case WrapErrorCode.WASM_INVOKE_ABORTED: return "Wasm module aborted execution."; + case WrapErrorCode.WASM_SUBINVOKE_ABORTED: + return "Wasm module aborted execution during a subinvocation."; case WrapErrorCode.WASM_INVOKE_FAIL: return "Invocation exception encountered."; case WrapErrorCode.WASM_MODULE_NOT_FOUND: return "Wrapper does not contain a Wasm module."; - case WrapErrorCode.WASM_METHOD_NOT_FOUND: - return "Could not find method in Wasm module."; + case WrapErrorCode.WASM_STATE_ERROR: + return "Invocation state is missing."; + case WrapErrorCode.WASM_SERIALIZATION_ERROR: + return "An exception was encountered while deserializing arguments or serializing results."; case WrapErrorCode.PLUGIN_METHOD_NOT_FOUND: return "Method not found in plugin module."; case WrapErrorCode.PLUGIN_ARGS_MALFORMED: diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index 67f409387d..5e7cf1351c 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -181,11 +181,15 @@ export class WasmWrapper implements Wrapper { env: options.env ? msgpackEncode(options.env) : EMPTY_ENCODED_OBJECT, }; - const abort = (message: string, source?: WrapErrorSource) => { + const abort = ( + message: string, + code: WrapErrorCode = WrapErrorCode.WASM_INVOKE_ABORTED, + source?: WrapErrorSource + ) => { const prev = WrapError.parse(message); const text = prev ? "SubInvocation exception encountered" : message; throw new WrapError(text, { - code: WrapErrorCode.WASM_INVOKE_ABORTED, + code, uri: options.uri.uri, method, args: JSON.stringify(args, null, 2), diff --git a/packages/js/wasm/src/imports.ts b/packages/js/wasm/src/imports.ts index be87e5ad81..a054355ef4 100644 --- a/packages/js/wasm/src/imports.ts +++ b/packages/js/wasm/src/imports.ts @@ -5,13 +5,17 @@ import { readBytes, readString, writeBytes, writeString } from "./buffer"; import { State } from "./WasmWrapper"; import { msgpackEncode } from "@polywrap/msgpack-js"; -import { CoreClient, WrapErrorSource } from "@polywrap/core-js"; +import { CoreClient, WrapErrorCode, WrapErrorSource } from "@polywrap/core-js"; export const createImports = (config: { client: CoreClient; memory: WebAssembly.Memory; state: State; - abort: (message: string, source?: WrapErrorSource) => never; + abort: ( + message: string, + code?: WrapErrorCode, + source?: WrapErrorSource + ) => never; }): WrapImports => { const { memory, state, client, abort } = config; @@ -51,7 +55,10 @@ export const createImports = (config: { // Give WASM the size of the result __wrap_subinvoke_result_len: (): u32 => { if (!state.subinvoke.result) { - abort("__wrap_subinvoke_result_len: subinvoke.result is not set"); + abort( + "__wrap_subinvoke_result_len: subinvoke.result is not set", + WrapErrorCode.WASM_STATE_ERROR + ); return 0; } return state.subinvoke.result.byteLength; @@ -59,7 +66,10 @@ export const createImports = (config: { // Copy the subinvoke result into WASM __wrap_subinvoke_result: (ptr: u32): void => { if (!state.subinvoke.result) { - abort("__wrap_subinvoke_result: subinvoke.result is not set"); + abort( + "__wrap_subinvoke_result: subinvoke.result is not set", + WrapErrorCode.WASM_STATE_ERROR + ); return; } writeBytes(state.subinvoke.result, memory.buffer, ptr); @@ -67,7 +77,10 @@ export const createImports = (config: { // Give WASM the size of the error __wrap_subinvoke_error_len: (): u32 => { if (!state.subinvoke.error) { - abort("__wrap_subinvoke_error_len: subinvoke.error is not set"); + abort( + "__wrap_subinvoke_error_len: subinvoke.error is not set", + WrapErrorCode.WASM_STATE_ERROR + ); return 0; } return state.subinvoke.error.length; @@ -75,7 +88,10 @@ export const createImports = (config: { // Copy the subinvoke error into WASM __wrap_subinvoke_error: (ptr: u32): void => { if (!state.subinvoke.error) { - abort("__wrap_subinvoke_error: subinvoke.error is not set"); + abort( + "__wrap_subinvoke_error: subinvoke.error is not set", + WrapErrorCode.WASM_STATE_ERROR + ); return; } writeString(state.subinvoke.error, memory.buffer, ptr); @@ -117,7 +133,8 @@ export const createImports = (config: { __wrap_subinvokeImplementation_result_len: (): u32 => { if (!state.subinvokeImplementation.result) { abort( - "__wrap_subinvokeImplementation_result_len: subinvokeImplementation.result is not set" + "__wrap_subinvokeImplementation_result_len: subinvokeImplementation.result is not set", + WrapErrorCode.WASM_STATE_ERROR ); return 0; } @@ -135,7 +152,8 @@ export const createImports = (config: { __wrap_subinvokeImplementation_error_len: (): u32 => { if (!state.subinvokeImplementation.error) { abort( - "__wrap_subinvokeImplementation_error_len: subinvokeImplementation.error is not set" + "__wrap_subinvokeImplementation_error_len: subinvokeImplementation.error is not set", + WrapErrorCode.WASM_STATE_ERROR ); return 0; } @@ -144,7 +162,8 @@ export const createImports = (config: { __wrap_subinvokeImplementation_error: (ptr: u32): void => { if (!state.subinvokeImplementation.error) { abort( - "__wrap_subinvokeImplementation_error: subinvokeImplementation.error is not set" + "__wrap_subinvokeImplementation_error: subinvokeImplementation.error is not set", + WrapErrorCode.WASM_STATE_ERROR ); return; } @@ -153,11 +172,17 @@ export const createImports = (config: { // Copy the invocation's method & args into WASM __wrap_invoke_args: (methodPtr: u32, argsPtr: u32): void => { if (!state.method) { - abort("__wrap_invoke_args: method is not set"); + abort( + "__wrap_invoke_args: method is not set", + WrapErrorCode.WASM_STATE_ERROR + ); return; } if (!state.args) { - abort("__wrap_invoke_args: args is not set"); + abort( + "__wrap_invoke_args: args is not set", + WrapErrorCode.WASM_STATE_ERROR + ); return; } writeString(state.method, memory.buffer, methodPtr); @@ -189,14 +214,20 @@ export const createImports = (config: { }, __wrap_getImplementations_result_len: (): u32 => { if (!state.getImplementationsResult) { - abort("__wrap_getImplementations_result_len: result is not set"); + abort( + "__wrap_getImplementations_result_len: result is not set", + WrapErrorCode.WASM_STATE_ERROR + ); return 0; } return state.getImplementationsResult.byteLength; }, __wrap_getImplementations_result: (ptr: u32): void => { if (!state.getImplementationsResult) { - abort("__wrap_getImplementations_result: result is not set"); + abort( + "__wrap_getImplementations_result: result is not set", + WrapErrorCode.WASM_STATE_ERROR + ); return; } writeBytes(state.getImplementationsResult, memory.buffer, ptr); @@ -215,7 +246,21 @@ export const createImports = (config: { const msg = readString(memory.buffer, msgPtr, msgLen); const file = readString(memory.buffer, filePtr, fileLen); - abort(`__wrap_abort: ${msg}`, { file, row: line, col: column }); + let code = WrapErrorCode.WASM_INVOKE_ABORTED; + + // check if error was thrown in bindings + if ( + file.startsWith("~lib/@polywrap/wasm-as/msgpack/") || + file.startsWith("src/wrap/") + ) { + code = WrapErrorCode.WASM_SERIALIZATION_ERROR; + } + + abort(`__wrap_abort: ${msg}`, code, { + file, + row: line, + col: column, + }); }, __wrap_debug_log: (ptr: u32, len: u32): void => { const msg = readString(memory.buffer, ptr, len); From 5566b1398e06033248ca51e7f344353cd6b3ceb4 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Sat, 10 Dec 2022 10:47:20 +0530 Subject: [PATCH 18/40] removed unnecessary number specified on WASM_SUBINVOKE_ABORTED --- packages/js/core/src/types/WrapError.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index f51603deca..4c563ad4e6 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -30,7 +30,7 @@ export enum WrapErrorCode { URI_RESOLVER, URI_NOT_FOUND, WASM_INVOKE_ABORTED = 50, - WASM_SUBINVOKE_ABORTED = 51, + WASM_SUBINVOKE_ABORTED, WASM_INVOKE_FAIL, WASM_MODULE_NOT_FOUND, WASM_STATE_ERROR, From 06d416469c1248bd6dc84ad6d98ac7baf0ab58bd Mon Sep 17 00:00:00 2001 From: krisbitney Date: Sat, 17 Dec 2022 10:09:00 +0530 Subject: [PATCH 19/40] removed CLIENT_QUERY... error codes; removed test:errors script from client --- packages/js/client/package.json | 3 +-- packages/js/core-client/src/PolywrapCoreClient.ts | 4 ++-- packages/js/core/src/types/WrapError.ts | 6 ------ 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/js/client/package.json b/packages/js/client/package.json index e1999454b5..897e7ebc6e 100644 --- a/packages/js/client/package.json +++ b/packages/js/client/package.json @@ -17,8 +17,7 @@ "test": "jest --passWithNoTests --runInBand --verbose=true --detectOpenHandles --forceExit", "test:ci": "jest --passWithNoTests --runInBand --verbose --detectOpenHandles --forceExit", "test:rust": "jest --passWithNoTests --runInBand --verbose --detectOpenHandles --forceExit --config ./jest.rs.config.js", - "test:watch": "jest --watch --passWithNoTests --verbose --detectOpenHandles", - "test:errors": "yarn test src/__tests__/core/error-structure.spec.ts" + "test:watch": "jest --watch --passWithNoTests --verbose --detectOpenHandles" }, "dependencies": { "@polywrap/wrap-manifest-types-js": "0.10.0-pre.5", diff --git a/packages/js/core-client/src/PolywrapCoreClient.ts b/packages/js/core-client/src/PolywrapCoreClient.ts index 02550c18d5..f93a4cb320 100644 --- a/packages/js/core-client/src/PolywrapCoreClient.ts +++ b/packages/js/core-client/src/PolywrapCoreClient.ts @@ -281,7 +281,7 @@ export class PolywrapCoreClient implements CoreClient { const parseResult = parseQuery(uri, queryDocument, variables); if (!parseResult.ok) { const error = new WrapError(parseResult.error?.message, { - code: WrapErrorCode.CLIENT_QUERY_MALFORMED, + code: WrapErrorCode.WASM_INVOKE_FAIL, uri: uri.uri, cause: parseResult.error, }); @@ -332,7 +332,7 @@ export class PolywrapCoreClient implements CoreClient { } catch (error: unknown) { const unknownQueryErrorToWrapError = (e: Error): WrapError => new WrapError((e as Error)?.message, { - code: WrapErrorCode.CLIENT_QUERY_FAIL, + code: WrapErrorCode.WASM_INVOKE_FAIL, uri: options.uri.toString(), cause: e as Error, }); diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 4c563ad4e6..b4b7de04f3 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -24,8 +24,6 @@ export enum WrapErrorCode { CLIENT_VALIDATE_RESOLUTION, CLIENT_VALIDATE_ABI, CLIENT_VALIDATE_RECURSIVE, - CLIENT_QUERY_MALFORMED, - CLIENT_QUERY_FAIL, URI_RESOLUTION = 25, URI_RESOLVER, URI_NOT_FOUND, @@ -308,10 +306,6 @@ export class WrapError extends Error { return "An error occurred while validating a WRAP URI against its ABI."; case WrapErrorCode.CLIENT_VALIDATE_RECURSIVE: return "An error occurred while recursively validating a WRAP URI."; - case WrapErrorCode.CLIENT_QUERY_MALFORMED: - return "Failed to parse GraphQL query."; - case WrapErrorCode.CLIENT_QUERY_FAIL: - return "Unknown query exception encountered."; case WrapErrorCode.URI_RESOLUTION: return "Unable to resolve URI."; case WrapErrorCode.URI_RESOLVER: From b87ec3f22760aca5ade46ab4a21d5a769724b1cb Mon Sep 17 00:00:00 2001 From: krisbitney Date: Mon, 19 Dec 2022 16:28:20 +0530 Subject: [PATCH 20/40] removed WASM_SERIALIZATION_ERROR code --- .../__tests__/core/error-structure.spec.ts | 24 +++++++++++-------- packages/js/core/src/types/WrapError.ts | 3 --- packages/js/wasm/src/imports.ts | 12 +--------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 8bd09755c0..789b6e7a73 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,6 +1,6 @@ import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; -import { buildWrapper } from "@polywrap/test-env-js"; +// import { buildWrapper } from "@polywrap/test-env-js"; import { WrapError, WrapErrorCode } from "@polywrap/core-js"; jest.setTimeout(660000); @@ -31,11 +31,11 @@ describe("error structure", () => { let client: PolywrapClient; beforeAll(async () => { - await buildWrapper(simpleWrapperPath); - await buildWrapper(badUtilWrapperPath); - await buildWrapper(badMathWrapperPath); - await buildWrapper(subinvokeErrorWrapperPath); - await buildWrapper(invalidTypesWrapperPath); + // await buildWrapper(simpleWrapperPath); + // await buildWrapper(badUtilWrapperPath); + // await buildWrapper(badMathWrapperPath); + // await buildWrapper(subinvokeErrorWrapperPath); + // await buildWrapper(invalidTypesWrapperPath); client = new PolywrapClient({ redirects: [ @@ -70,7 +70,7 @@ describe("error structure", () => { expect(result.error?.resolutionStack).toBeTruthy(); }); - test("Invoke a wrapper with malformed arguments - as", async () => { + test.only("Invoke a wrapper with malformed arguments - as", async () => { const result = await client.invoke({ uri: simpleWrapperUri.uri, method: "simpleMethod", @@ -82,8 +82,10 @@ describe("error structure", () => { expect(result.ok).toBeFalsy(); if (result.ok) throw Error("should never happen"); + console.log(result.error?.toString()); + expect(result.error?.name).toEqual("InvokeError"); - expect(result.error?.code).toEqual(WrapErrorCode.WASM_SERIALIZATION_ERROR); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); expect(result.error?.method).toEqual("simpleMethod"); @@ -91,7 +93,7 @@ describe("error structure", () => { expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/msgpack/ReadDecoder.ts", row: 167, col: 5 }); }); - test("Invoke a wrapper with malformed arguments - rs", async () => { + test.only("Invoke a wrapper with malformed arguments - rs", async () => { const result = await client.invoke({ uri: invalidTypesWrapperUri.uri, method: "boolMethod", @@ -103,8 +105,10 @@ describe("error structure", () => { expect(result.ok).toBeFalsy(); if (result.ok) throw Error("should never happen"); + console.log(result.error?.toString()); + expect(result.error?.name).toEqual("InvokeError"); - expect(result.error?.code).toEqual(WrapErrorCode.WASM_SERIALIZATION_ERROR); + expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-rs/invalid-types/build")).toBeTruthy(); expect(result.error?.method).toEqual("boolMethod"); diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index b4b7de04f3..9e6330da79 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -32,7 +32,6 @@ export enum WrapErrorCode { WASM_INVOKE_FAIL, WASM_MODULE_NOT_FOUND, WASM_STATE_ERROR, - WASM_SERIALIZATION_ERROR, PLUGIN_INVOKE_FAIL = 75, PLUGIN_METHOD_NOT_FOUND, PLUGIN_ARGS_MALFORMED, @@ -322,8 +321,6 @@ export class WrapError extends Error { return "Wrapper does not contain a Wasm module."; case WrapErrorCode.WASM_STATE_ERROR: return "Invocation state is missing."; - case WrapErrorCode.WASM_SERIALIZATION_ERROR: - return "An exception was encountered while deserializing arguments or serializing results."; case WrapErrorCode.PLUGIN_METHOD_NOT_FOUND: return "Method not found in plugin module."; case WrapErrorCode.PLUGIN_ARGS_MALFORMED: diff --git a/packages/js/wasm/src/imports.ts b/packages/js/wasm/src/imports.ts index a054355ef4..4a1b55f6ea 100644 --- a/packages/js/wasm/src/imports.ts +++ b/packages/js/wasm/src/imports.ts @@ -246,17 +246,7 @@ export const createImports = (config: { const msg = readString(memory.buffer, msgPtr, msgLen); const file = readString(memory.buffer, filePtr, fileLen); - let code = WrapErrorCode.WASM_INVOKE_ABORTED; - - // check if error was thrown in bindings - if ( - file.startsWith("~lib/@polywrap/wasm-as/msgpack/") || - file.startsWith("src/wrap/") - ) { - code = WrapErrorCode.WASM_SERIALIZATION_ERROR; - } - - abort(`__wrap_abort: ${msg}`, code, { + abort(`__wrap_abort: ${msg}`, WrapErrorCode.WASM_INVOKE_ABORTED, { file, row: line, col: column, From e12e539a97d442485f40e8cdf704b9b154251d17 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Mon, 19 Dec 2022 16:30:25 +0530 Subject: [PATCH 21/40] removed leftover adjustments to error-structure.spec.ts made for testing --- .../__tests__/core/error-structure.spec.ts | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 789b6e7a73..a39924f5b2 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,6 +1,6 @@ import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; -// import { buildWrapper } from "@polywrap/test-env-js"; +import { buildWrapper } from "@polywrap/test-env-js"; import { WrapError, WrapErrorCode } from "@polywrap/core-js"; jest.setTimeout(660000); @@ -31,11 +31,11 @@ describe("error structure", () => { let client: PolywrapClient; beforeAll(async () => { - // await buildWrapper(simpleWrapperPath); - // await buildWrapper(badUtilWrapperPath); - // await buildWrapper(badMathWrapperPath); - // await buildWrapper(subinvokeErrorWrapperPath); - // await buildWrapper(invalidTypesWrapperPath); + await buildWrapper(simpleWrapperPath); + await buildWrapper(badUtilWrapperPath); + await buildWrapper(badMathWrapperPath); + await buildWrapper(subinvokeErrorWrapperPath); + await buildWrapper(invalidTypesWrapperPath); client = new PolywrapClient({ redirects: [ @@ -70,7 +70,7 @@ describe("error structure", () => { expect(result.error?.resolutionStack).toBeTruthy(); }); - test.only("Invoke a wrapper with malformed arguments - as", async () => { + test("Invoke a wrapper with malformed arguments - as", async () => { const result = await client.invoke({ uri: simpleWrapperUri.uri, method: "simpleMethod", @@ -82,8 +82,6 @@ describe("error structure", () => { expect(result.ok).toBeFalsy(); if (result.ok) throw Error("should never happen"); - console.log(result.error?.toString()); - expect(result.error?.name).toEqual("InvokeError"); expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); @@ -93,7 +91,7 @@ describe("error structure", () => { expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/msgpack/ReadDecoder.ts", row: 167, col: 5 }); }); - test.only("Invoke a wrapper with malformed arguments - rs", async () => { + test("Invoke a wrapper with malformed arguments - rs", async () => { const result = await client.invoke({ uri: invalidTypesWrapperUri.uri, method: "boolMethod", @@ -105,8 +103,6 @@ describe("error structure", () => { expect(result.ok).toBeFalsy(); if (result.ok) throw Error("should never happen"); - console.log(result.error?.toString()); - expect(result.error?.name).toEqual("InvokeError"); expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); From 5aeb31fbca3cbeeca1091e94dadef32db148d692 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Tue, 20 Dec 2022 23:56:23 +0530 Subject: [PATCH 22/40] moved WrapError parsing to wasm-js package; combined wasm and plugin wrap error codes into one category; removed UNKNOWN error code; renamed MODULE_NOT_FOUND error to READ_FAIL --- .../__tests__/core/error-structure.spec.ts | 15 +- .../js/core-client/src/PolywrapCoreClient.ts | 4 +- packages/js/core/src/types/WrapError.ts | 186 +++--------------- packages/js/plugin/src/PluginWrapper.ts | 6 +- packages/js/wasm/src/WasmWrapper.ts | 12 +- .../js/wasm/src/helpers/wrapErrorUtils.ts | 126 ++++++++++++ packages/js/wasm/src/imports.ts | 33 ++-- 7 files changed, 191 insertions(+), 191 deletions(-) create mode 100644 packages/js/wasm/src/helpers/wrapErrorUtils.ts diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index a39924f5b2..04272e0a5f 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -5,7 +5,6 @@ import { WrapError, WrapErrorCode } from "@polywrap/core-js"; jest.setTimeout(660000); - // AS const simpleWrapperPath = `${GetPathToTestWrappers()}/wasm-as/simple`; const simpleWrapperUri = new Uri(`fs/${simpleWrapperPath}/build`); @@ -83,7 +82,7 @@ describe("error structure", () => { if (result.ok) throw Error("should never happen"); expect(result.error?.name).toEqual("InvokeError"); - expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); expect(result.error?.method).toEqual("simpleMethod"); @@ -104,7 +103,7 @@ describe("error structure", () => { if (result.ok) throw Error("should never happen"); expect(result.error?.name).toEqual("InvokeError"); - expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-rs/invalid-types/build")).toBeTruthy(); expect(result.error?.method).toEqual("boolMethod"); @@ -126,7 +125,7 @@ describe("error structure", () => { if (result.ok) throw Error("should never happen"); expect(result.error?.name).toEqual("InvokeError"); - expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_FAIL); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_FAIL); expect(result.error?.reason.startsWith("Could not find invoke function")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); expect(result.error?.method).toEqual("complexMethod"); @@ -149,7 +148,7 @@ describe("error structure", () => { if (result.ok) throw Error("should never happen"); expect(result.error?.name).toEqual("InvokeError"); - expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); expect(result.error?.method).toEqual("subWrapperNotFound"); @@ -179,7 +178,7 @@ describe("error structure", () => { if (result.ok) throw Error("should never happen"); expect(result.error?.name).toEqual("InvokeError"); - expect(result.error?.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); expect(result.error?.method).toEqual("throwsInTwoSubinvokeLayers"); @@ -192,7 +191,7 @@ describe("error structure", () => { expect(result.error?.prev instanceof WrapError).toBeTruthy(); const prev = result.error?.prev as WrapError; expect(prev.name).toEqual("InvokeError"); - expect(prev.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(prev.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(prev.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); expect(prev.uri).toEqual("wrap://ens/bad-math.eth"); expect(prev.method).toEqual("subInvokeWillThrow"); @@ -202,7 +201,7 @@ describe("error structure", () => { expect(prev.prev instanceof WrapError).toBeTruthy(); const prevOfPrev = prev.prev as WrapError; expect(prevOfPrev.name).toEqual("InvokeError"); - expect(prevOfPrev.code).toEqual(WrapErrorCode.WASM_INVOKE_ABORTED); + expect(prevOfPrev.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(prevOfPrev.reason).toEqual("__wrap_abort: I threw an error!"); expect(prevOfPrev.uri.endsWith("wrap://ens/bad-util.eth")).toBeTruthy(); expect(prevOfPrev.method).toEqual("iThrow"); diff --git a/packages/js/core-client/src/PolywrapCoreClient.ts b/packages/js/core-client/src/PolywrapCoreClient.ts index f93a4cb320..20804d177c 100644 --- a/packages/js/core-client/src/PolywrapCoreClient.ts +++ b/packages/js/core-client/src/PolywrapCoreClient.ts @@ -281,7 +281,7 @@ export class PolywrapCoreClient implements CoreClient { const parseResult = parseQuery(uri, queryDocument, variables); if (!parseResult.ok) { const error = new WrapError(parseResult.error?.message, { - code: WrapErrorCode.WASM_INVOKE_FAIL, + code: WrapErrorCode.WRAPPER_INVOKE_FAIL, uri: uri.uri, cause: parseResult.error, }); @@ -332,7 +332,7 @@ export class PolywrapCoreClient implements CoreClient { } catch (error: unknown) { const unknownQueryErrorToWrapError = (e: Error): WrapError => new WrapError((e as Error)?.message, { - code: WrapErrorCode.WASM_INVOKE_FAIL, + code: WrapErrorCode.WRAPPER_INVOKE_FAIL, uri: options.uri.toString(), cause: e as Error, }); diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 9e6330da79..e82a64363f 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -12,12 +12,10 @@ Error code naming convention (approximate): Error code map: 0-24 -> Client 25-49 -> URI resolution - 50-74 -> Wasm wrapper invocation & sub-invocation - 75-99 -> Plugin invocation & sub-invocation - 100-255 -> Unallocated + 50-74 -> Wrapper invocation & sub-invocation + 75-255 -> Unallocated */ export enum WrapErrorCode { - UNKNOWN, CLIENT_LOAD_WRAPPER, CLIENT_GET_FILE, CLIENT_GET_IMPLEMENTATIONS, @@ -27,40 +25,31 @@ export enum WrapErrorCode { URI_RESOLUTION = 25, URI_RESOLVER, URI_NOT_FOUND, - WASM_INVOKE_ABORTED = 50, - WASM_SUBINVOKE_ABORTED, - WASM_INVOKE_FAIL, - WASM_MODULE_NOT_FOUND, - WASM_STATE_ERROR, - PLUGIN_INVOKE_FAIL = 75, - PLUGIN_METHOD_NOT_FOUND, - PLUGIN_ARGS_MALFORMED, + WRAPPER_INVOKE_ABORTED = 50, + WRAPPER_SUBINVOKE_ABORTED, + WRAPPER_INVOKE_FAIL, + WRAPPER_READ_FAIL, + WRAPPER_STATE_ERROR, + WRAPPER_METHOD_NOT_FOUND, + WRAPPER_ARGS_MALFORMED, } -export type WrapErrorSource = Readonly<{ - file?: string; - row?: number; - col?: number; -}>; - export interface WrapErrorOptions { code: WrapErrorCode; uri: string; method?: string; args?: string; - source?: WrapErrorSource; + source?: { + file?: string; + row?: number; + col?: number; + }; resolutionStack?: CleanResolutionStep; cause?: unknown; prev?: Error; stack?: string; } -type RegExpGroups = - | (RegExpExecArray & { - groups?: { [name in T]: string | undefined } | { [key: string]: string }; - }) - | null; - export class WrapError extends Error { readonly name: string; readonly code: WrapErrorCode; @@ -68,31 +57,15 @@ export class WrapError extends Error { readonly uri: string; readonly method?: string; readonly args?: string; - readonly source?: WrapErrorSource; + readonly source?: Readonly<{ + file?: string; + row?: number; + col?: number; + }>; readonly resolutionStack?: CleanResolutionStep; readonly cause?: unknown; readonly prev?: Error; - private static re = new RegExp( - [ - // [A-z]+Error can be replaced with specific error names when finalized - /^(?:[A-z_: ]*; )?[A-z]+Error: [\w ]+\./.source, - // there is some padding added to the number of words expected in an error code - /(?:\r\n|\r|\n)code: (?1?[0-9]{1,2}|2[0-4][0-9]|25[0-5]) (?:[A-Z]+ ?){1,5}/ - .source, - /(?:\r\n|\r|\n)reason: (?(?:.|\r\n|\r|\n)*)/.source, - /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-z0-9_-]+\/.+)/.source, - /(?:(?:\r\n|\r|\n)method: (?([A-z_]{1}[A-z0-9_]*)))?/.source, - /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r\n|\r|\n)+} ))?/.source, - /(?:(?:\r\n|\r|\n)source: \{ file: "(?.+)", row: (?[0-9]+), col: (?[0-9]+) })?/ - .source, - /(?:(?:\r\n|\r|\n)uriResolutionStack: (?\[(?:.|\r\n|\r|\n)+]))?/ - .source, - /(?:(?:\r\n|\r|\n){2}This exception was caused by the following exception:(?:\r\n|\r|\n)(?(?:.|\r\n|\r|\n)+))?$/ - .source, - ].join("") - ); - constructor(reason = "Encountered an exception.", options: WrapErrorOptions) { super(WrapError.stringify(reason, options)); this.name = WrapError.codeToName(options.code); @@ -109,101 +82,10 @@ export class WrapError extends Error { Object.setPrototypeOf(this, WrapError.prototype); } - static parse(error: string): WrapError | undefined { - const delim = "\n\nAnother exception was encountered during execution:\n"; - const errorStrings = error.split(delim); - - // case: single WrapError or not a WrapError - if (errorStrings.length === 1) { - const args = WrapError._parse(error); - return args ? new WrapError(args.reason, args.options) : undefined; - } - - // case: stack of WrapErrors stringified - const errArgs = errorStrings.map(WrapError._parse); - - // iterate through args to assign `cause` and `prev` - let curr: WrapError | undefined = undefined; - for (let i = errArgs.length - 1; i >= 0; i--) { - const currArgs = errArgs[i]; - if (!currArgs) { - // should only happen if a user includes the delimiter in their error message - throw new Error("Failed to parse WrapError"); - } - curr = new WrapError(currArgs.reason, { - ...currArgs.options, - prev: curr, - }); - } - return curr; - } - toString(): string { return `${this.name}: ${this.message}`; } - // parse a single WrapError, where the 'prev' property is undefined - private static _parse( - error: string - ): { reason: string; options: WrapErrorOptions } | undefined { - const result: RegExpGroups< - | "code" - | "reason" - | "uri" - | "method" - | "args" - | "file" - | "row" - | "col" - | "resolutionStack" - | "cause" - > = WrapError.re.exec(error); - if (!result) { - return undefined; - } - const { - code: codeStr, - reason, - uri, - method, - args, - file, - row, - col, - resolutionStack: resolutionStackStr, - cause, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - } = result.groups!; - - const code = parseInt(codeStr as string); - - // replace parens () with brackets {} - const source: WrapErrorSource | undefined = file - ? { - file, - row: row ? parseInt(row) : undefined, - col: col ? parseInt(col) : undefined, - } - : undefined; - - const resolutionStack = resolutionStackStr - ? JSON.parse(resolutionStackStr) - : undefined; - - return { - reason: reason as string, - options: { - code, - uri: uri as string, - method, - args: args?.trim(), - source, - resolutionStack, - cause, - }, - }; - } - private static stringify(reason: string, options: WrapErrorOptions) { const { code, @@ -279,13 +161,11 @@ export class WrapError extends Error { private static codeToName(code: WrapErrorCode): string { if (code < 25) { - return "WrapError"; + return "ClientError"; } else if (code < 50) { return "UriResolutionError"; } else if (code < 75) { return "InvokeError"; - } else if (code < 100) { - return "PluginError"; } else { return "WrapError"; } @@ -311,22 +191,20 @@ export class WrapError extends Error { return "An internal resolver error occurred while resolving a URI."; case WrapErrorCode.URI_NOT_FOUND: return "URI not found."; - case WrapErrorCode.WASM_INVOKE_ABORTED: - return "Wasm module aborted execution."; - case WrapErrorCode.WASM_SUBINVOKE_ABORTED: - return "Wasm module aborted execution during a subinvocation."; - case WrapErrorCode.WASM_INVOKE_FAIL: + case WrapErrorCode.WRAPPER_INVOKE_ABORTED: + return "Wrapper aborted execution."; + case WrapErrorCode.WRAPPER_SUBINVOKE_ABORTED: + return "Wrapper aborted execution during a subinvocation."; + case WrapErrorCode.WRAPPER_INVOKE_FAIL: return "Invocation exception encountered."; - case WrapErrorCode.WASM_MODULE_NOT_FOUND: - return "Wrapper does not contain a Wasm module."; - case WrapErrorCode.WASM_STATE_ERROR: + case WrapErrorCode.WRAPPER_READ_FAIL: + return "Wrapper does not contain a module, or module could not be read."; + case WrapErrorCode.WRAPPER_STATE_ERROR: return "Invocation state is missing."; - case WrapErrorCode.PLUGIN_METHOD_NOT_FOUND: - return "Method not found in plugin module."; - case WrapErrorCode.PLUGIN_ARGS_MALFORMED: - return "Malformed arguments passed to plugin."; - case WrapErrorCode.PLUGIN_INVOKE_FAIL: - return "Invocation exception encountered."; + case WrapErrorCode.WRAPPER_METHOD_NOT_FOUND: + return "Method not found in wrapper module."; + case WrapErrorCode.WRAPPER_ARGS_MALFORMED: + return "Malformed arguments passed to wrapper."; default: return "Unknown exception."; } diff --git a/packages/js/plugin/src/PluginWrapper.ts b/packages/js/plugin/src/PluginWrapper.ts index 3a666b7624..be11501475 100644 --- a/packages/js/plugin/src/PluginWrapper.ts +++ b/packages/js/plugin/src/PluginWrapper.ts @@ -56,7 +56,7 @@ export class PluginWrapper implements Wrapper { if (!this.module.getMethod(method)) { const error = new WrapError(`Plugin missing method "${method}"`, { - code: WrapErrorCode.PLUGIN_METHOD_NOT_FOUND, + code: WrapErrorCode.WRAPPER_METHOD_NOT_FOUND, uri: options.uri.uri, method, }); @@ -78,7 +78,7 @@ export class PluginWrapper implements Wrapper { const error = new WrapError( `Decoded MsgPack args did not result in an object.\nResult: ${result}`, { - code: WrapErrorCode.PLUGIN_ARGS_MALFORMED, + code: WrapErrorCode.WRAPPER_ARGS_MALFORMED, uri: options.uri.uri, method, args: JSON.stringify(args), @@ -107,7 +107,7 @@ export class PluginWrapper implements Wrapper { } else { const reason = `Failed to invoke method "${method}" in module: ${this.module}`; const error = new WrapError(reason, { - code: WrapErrorCode.PLUGIN_INVOKE_FAIL, + code: WrapErrorCode.WRAPPER_INVOKE_FAIL, uri: options.uri.toString(), method, args: JSON.stringify(jsArgs, null, 2), diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index 5e7cf1351c..ad8442d974 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -4,6 +4,7 @@ import { createImports } from "./imports"; import { IFileReader } from "./IFileReader"; import { WRAP_MODULE_PATH } from "./constants"; import { createWasmWrapper } from "./helpers/createWasmWrapper"; +import { parseWrapError, ErrorSource } from "./helpers/wrapErrorUtils"; import { WrapManifest } from "@polywrap/wrap-manifest-types-js"; import { msgpackEncode } from "@polywrap/msgpack-js"; @@ -20,7 +21,6 @@ import { Wrapper, WrapError, WrapErrorCode, - WrapErrorSource, } from "@polywrap/core-js"; import { Result, ResultErr, ResultOk } from "@polywrap/result"; @@ -155,7 +155,7 @@ export class WasmWrapper implements Wrapper { const wasmResult = await this._getWasmModule(); if (!wasmResult.ok) { const error = new WrapError(wasmResult.error, { - code: WrapErrorCode.WASM_MODULE_NOT_FOUND, + code: WrapErrorCode.WRAPPER_READ_FAIL, uri: options.uri.uri, method, args: JSON.stringify(args, null, 2), @@ -183,10 +183,10 @@ export class WasmWrapper implements Wrapper { const abort = ( message: string, - code: WrapErrorCode = WrapErrorCode.WASM_INVOKE_ABORTED, - source?: WrapErrorSource + code: WrapErrorCode = WrapErrorCode.WRAPPER_INVOKE_ABORTED, + source?: ErrorSource ) => { - const prev = WrapError.parse(message); + const prev = parseWrapError(message); const text = prev ? "SubInvocation exception encountered" : message; throw new WrapError(text, { code, @@ -227,7 +227,7 @@ export class WasmWrapper implements Wrapper { }; } else { const error = new WrapError(invokeResult.error, { - code: WrapErrorCode.WASM_INVOKE_FAIL, + code: WrapErrorCode.WRAPPER_INVOKE_FAIL, uri: options.uri.uri, method, args: JSON.stringify(args, null, 2), diff --git a/packages/js/wasm/src/helpers/wrapErrorUtils.ts b/packages/js/wasm/src/helpers/wrapErrorUtils.ts new file mode 100644 index 0000000000..a14b603394 --- /dev/null +++ b/packages/js/wasm/src/helpers/wrapErrorUtils.ts @@ -0,0 +1,126 @@ +import { WrapError, WrapErrorOptions } from "@polywrap/core-js"; + +export type ErrorSource = Readonly<{ + file?: string; + row?: number; + col?: number; +}>; + +type RegExpGroups = + | (RegExpExecArray & { + groups?: { [name in T]: string | undefined } | { [key: string]: string }; + }) + | null; + +const wrapErrorRegEx = new RegExp( + [ + // [A-z]+Error can be replaced with specific error names when finalized + /^(?:[A-z_: ]*; )?[A-z]+Error: [\w ]+\./.source, + // there is some padding added to the number of words expected in an error code + /(?:\r\n|\r|\n)code: (?1?[0-9]{1,2}|2[0-4][0-9]|25[0-5]) (?:[A-Z]+ ?){1,5}/ + .source, + /(?:\r\n|\r|\n)reason: (?(?:.|\r\n|\r|\n)*)/.source, + /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-z0-9_-]+\/.+)/.source, + /(?:(?:\r\n|\r|\n)method: (?([A-z_]{1}[A-z0-9_]*)))?/.source, + /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r\n|\r|\n)+} ))?/.source, + /(?:(?:\r\n|\r|\n)source: \{ file: "(?.+)", row: (?[0-9]+), col: (?[0-9]+) })?/ + .source, + /(?:(?:\r\n|\r|\n)uriResolutionStack: (?\[(?:.|\r\n|\r|\n)+]))?/ + .source, + /(?:(?:\r\n|\r|\n){2}This exception was caused by the following exception:(?:\r\n|\r|\n)(?(?:.|\r\n|\r|\n)+))?$/ + .source, + ].join("") +); + +// parse a stringified WrapError +export function parseWrapError(error: string): WrapError | undefined { + const delim = "\n\nAnother exception was encountered during execution:\n"; + const errorStrings = error.split(delim); + + // case: single WrapError or not a WrapError + if (errorStrings.length === 1) { + const args = parseSimpleWrapError(error); + return args ? new WrapError(args.reason, args.options) : undefined; + } + + // case: stack of WrapErrors stringified + const errArgs = errorStrings.map(parseSimpleWrapError); + + // iterate through args to assign `cause` and `prev` + let curr: WrapError | undefined = undefined; + for (let i = errArgs.length - 1; i >= 0; i--) { + const currArgs = errArgs[i]; + if (!currArgs) { + // should only happen if a user includes the delimiter in their error message + throw new Error("Failed to parse WrapError"); + } + curr = new WrapError(currArgs.reason, { + ...currArgs.options, + prev: curr, + }); + } + return curr; +} + +// parse a single WrapError, where the 'prev' property is undefined +// eslint-disable-next-line @typescript-eslint/naming-convention +function parseSimpleWrapError( + error: string +): { reason: string; options: WrapErrorOptions } | undefined { + const result: RegExpGroups< + | "code" + | "reason" + | "uri" + | "method" + | "args" + | "file" + | "row" + | "col" + | "resolutionStack" + | "cause" + > = wrapErrorRegEx.exec(error); + if (!result) { + return undefined; + } + const { + code: codeStr, + reason, + uri, + method, + args, + file, + row, + col, + resolutionStack: resolutionStackStr, + cause, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + } = result.groups!; + + const code = parseInt(codeStr as string); + + // replace parens () with brackets {} + const source: ErrorSource | undefined = file + ? { + file, + row: row ? parseInt(row) : undefined, + col: col ? parseInt(col) : undefined, + } + : undefined; + + const resolutionStack = resolutionStackStr + ? JSON.parse(resolutionStackStr) + : undefined; + + return { + reason: reason as string, + options: { + code, + uri: uri as string, + method, + args: args?.trim(), + source, + resolutionStack, + cause, + }, + }; +} diff --git a/packages/js/wasm/src/imports.ts b/packages/js/wasm/src/imports.ts index 4a1b55f6ea..eba0af7eae 100644 --- a/packages/js/wasm/src/imports.ts +++ b/packages/js/wasm/src/imports.ts @@ -3,19 +3,16 @@ import { u32, WrapImports } from "./types"; import { readBytes, readString, writeBytes, writeString } from "./buffer"; import { State } from "./WasmWrapper"; +import { ErrorSource } from "./helpers/wrapErrorUtils"; import { msgpackEncode } from "@polywrap/msgpack-js"; -import { CoreClient, WrapErrorCode, WrapErrorSource } from "@polywrap/core-js"; +import { CoreClient, WrapErrorCode } from "@polywrap/core-js"; export const createImports = (config: { client: CoreClient; memory: WebAssembly.Memory; state: State; - abort: ( - message: string, - code?: WrapErrorCode, - source?: WrapErrorSource - ) => never; + abort: (message: string, code?: WrapErrorCode, source?: ErrorSource) => never; }): WrapImports => { const { memory, state, client, abort } = config; @@ -57,7 +54,7 @@ export const createImports = (config: { if (!state.subinvoke.result) { abort( "__wrap_subinvoke_result_len: subinvoke.result is not set", - WrapErrorCode.WASM_STATE_ERROR + WrapErrorCode.WRAPPER_STATE_ERROR ); return 0; } @@ -68,7 +65,7 @@ export const createImports = (config: { if (!state.subinvoke.result) { abort( "__wrap_subinvoke_result: subinvoke.result is not set", - WrapErrorCode.WASM_STATE_ERROR + WrapErrorCode.WRAPPER_STATE_ERROR ); return; } @@ -79,7 +76,7 @@ export const createImports = (config: { if (!state.subinvoke.error) { abort( "__wrap_subinvoke_error_len: subinvoke.error is not set", - WrapErrorCode.WASM_STATE_ERROR + WrapErrorCode.WRAPPER_STATE_ERROR ); return 0; } @@ -90,7 +87,7 @@ export const createImports = (config: { if (!state.subinvoke.error) { abort( "__wrap_subinvoke_error: subinvoke.error is not set", - WrapErrorCode.WASM_STATE_ERROR + WrapErrorCode.WRAPPER_STATE_ERROR ); return; } @@ -134,7 +131,7 @@ export const createImports = (config: { if (!state.subinvokeImplementation.result) { abort( "__wrap_subinvokeImplementation_result_len: subinvokeImplementation.result is not set", - WrapErrorCode.WASM_STATE_ERROR + WrapErrorCode.WRAPPER_STATE_ERROR ); return 0; } @@ -153,7 +150,7 @@ export const createImports = (config: { if (!state.subinvokeImplementation.error) { abort( "__wrap_subinvokeImplementation_error_len: subinvokeImplementation.error is not set", - WrapErrorCode.WASM_STATE_ERROR + WrapErrorCode.WRAPPER_STATE_ERROR ); return 0; } @@ -163,7 +160,7 @@ export const createImports = (config: { if (!state.subinvokeImplementation.error) { abort( "__wrap_subinvokeImplementation_error: subinvokeImplementation.error is not set", - WrapErrorCode.WASM_STATE_ERROR + WrapErrorCode.WRAPPER_STATE_ERROR ); return; } @@ -174,14 +171,14 @@ export const createImports = (config: { if (!state.method) { abort( "__wrap_invoke_args: method is not set", - WrapErrorCode.WASM_STATE_ERROR + WrapErrorCode.WRAPPER_STATE_ERROR ); return; } if (!state.args) { abort( "__wrap_invoke_args: args is not set", - WrapErrorCode.WASM_STATE_ERROR + WrapErrorCode.WRAPPER_STATE_ERROR ); return; } @@ -216,7 +213,7 @@ export const createImports = (config: { if (!state.getImplementationsResult) { abort( "__wrap_getImplementations_result_len: result is not set", - WrapErrorCode.WASM_STATE_ERROR + WrapErrorCode.WRAPPER_STATE_ERROR ); return 0; } @@ -226,7 +223,7 @@ export const createImports = (config: { if (!state.getImplementationsResult) { abort( "__wrap_getImplementations_result: result is not set", - WrapErrorCode.WASM_STATE_ERROR + WrapErrorCode.WRAPPER_STATE_ERROR ); return; } @@ -246,7 +243,7 @@ export const createImports = (config: { const msg = readString(memory.buffer, msgPtr, msgLen); const file = readString(memory.buffer, filePtr, fileLen); - abort(`__wrap_abort: ${msg}`, WrapErrorCode.WASM_INVOKE_ABORTED, { + abort(`__wrap_abort: ${msg}`, WrapErrorCode.WRAPPER_INVOKE_ABORTED, { file, row: line, col: column, From 7998b2a77784203df0ca0fc5c55fba6d927c9900 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Wed, 21 Dec 2022 15:26:09 +0530 Subject: [PATCH 23/40] moved WrapError parsing back to WrapError; re-added UNKNOWN error code; renamed WRAPPER_STATE_ERROR to WRAPPER_INTERNAL_ERROR; added "ERROR" or "FAIL" suffixes to error code names to clarify they are errors --- .../js/core-client/src/PolywrapCoreClient.ts | 14 +- .../src/algorithms/get-implementations.ts | 4 +- packages/js/core/src/types/WrapError.ts | 166 +++++++++++++++--- packages/js/wasm/src/WasmWrapper.ts | 9 +- .../js/wasm/src/helpers/wrapErrorUtils.ts | 126 ------------- packages/js/wasm/src/imports.ts | 33 ++-- 6 files changed, 176 insertions(+), 176 deletions(-) delete mode 100644 packages/js/wasm/src/helpers/wrapErrorUtils.ts diff --git a/packages/js/core-client/src/PolywrapCoreClient.ts b/packages/js/core-client/src/PolywrapCoreClient.ts index 20804d177c..184ac30399 100644 --- a/packages/js/core-client/src/PolywrapCoreClient.ts +++ b/packages/js/core-client/src/PolywrapCoreClient.ts @@ -192,7 +192,7 @@ export class PolywrapCoreClient implements CoreClient { const result = await wrapper.getFile(options); if (!result.ok) { const error = new WrapError(result.error?.message, { - code: WrapErrorCode.CLIENT_GET_FILE, + code: WrapErrorCode.CLIENT_GET_FILE_ERROR, uri: uri.toString(), stack: result.error?.stack, }); @@ -331,7 +331,7 @@ export class PolywrapCoreClient implements CoreClient { }; } catch (error: unknown) { const unknownQueryErrorToWrapError = (e: Error): WrapError => - new WrapError((e as Error)?.message, { + new WrapError(e?.message ?? "client.query: no error message found", { code: WrapErrorCode.WRAPPER_INVOKE_FAIL, uri: options.uri.toString(), cause: e as Error, @@ -691,7 +691,7 @@ export class PolywrapCoreClient implements CoreClient { if (!result.ok) { const error = new WrapError(result.error?.message, { - code: WrapErrorCode.CLIENT_LOAD_WRAPPER, + code: WrapErrorCode.CLIENT_LOAD_WRAPPER_ERROR, uri: uri.uri, cause: result.error, }); @@ -738,7 +738,7 @@ export class PolywrapCoreClient implements CoreClient { if (modulesNotFound.length) { const message = `The following URIs could not be resolved: ${modulesNotFound}`; const error = new WrapError(message, { - code: WrapErrorCode.CLIENT_VALIDATE_RESOLUTION, + code: WrapErrorCode.CLIENT_VALIDATE_RESOLUTION_FAIL, uri: uri.toString(), }); return ResultErr(error); @@ -769,7 +769,7 @@ export class PolywrapCoreClient implements CoreClient { if (!areEqual) { const message = `ABI from Uri: ${importedModule.uri} is not compatible with Uri: ${uri}`; const error = new WrapError(message, { - code: WrapErrorCode.CLIENT_VALIDATE_ABI, + code: WrapErrorCode.CLIENT_VALIDATE_ABI_FAIL, uri: uri.toString(), }); return ResultErr(error); @@ -777,7 +777,7 @@ export class PolywrapCoreClient implements CoreClient { } else { const message = `ABI from Uri: ${importedModule.uri} is not compatible with Uri: ${uri}`; const error = new WrapError(message, { - code: WrapErrorCode.CLIENT_VALIDATE_ABI, + code: WrapErrorCode.CLIENT_VALIDATE_ABI_FAIL, uri: uri.toString(), }); return ResultErr(error); @@ -801,7 +801,7 @@ export class PolywrapCoreClient implements CoreClient { message += `\n${error.uri} -> ${error.reason}`; } const error = new WrapError(message, { - code: WrapErrorCode.CLIENT_VALIDATE_RECURSIVE, + code: WrapErrorCode.CLIENT_VALIDATE_RECURSIVE_FAIL, uri: uri.toString(), }); return ResultErr(error); diff --git a/packages/js/core/src/algorithms/get-implementations.ts b/packages/js/core/src/algorithms/get-implementations.ts index 9b64333a30..5083a9afc0 100644 --- a/packages/js/core/src/algorithms/get-implementations.ts +++ b/packages/js/core/src/algorithms/get-implementations.ts @@ -43,7 +43,7 @@ export const getImplementations = Tracer.traceFunc( if (!redirectsResult.ok) { const error = new WrapError("Failed to resolve redirects", { uri: interfaceImplementations.interface.uri, - code: WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS, + code: WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS_ERROR, cause: redirectsResult.error, }); return ResultErr(error); @@ -73,7 +73,7 @@ export const getImplementations = Tracer.traceFunc( if (!redirectsResult.ok) { const error = new WrapError("Failed to resolve redirects", { uri: wrapperInterfaceUri.uri, - code: WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS, + code: WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS_ERROR, cause: redirectsResult.error, }); return ResultErr(error); diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index e82a64363f..3fbb2f43b1 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -16,12 +16,13 @@ Error code map: 75-255 -> Unallocated */ export enum WrapErrorCode { - CLIENT_LOAD_WRAPPER, - CLIENT_GET_FILE, - CLIENT_GET_IMPLEMENTATIONS, - CLIENT_VALIDATE_RESOLUTION, - CLIENT_VALIDATE_ABI, - CLIENT_VALIDATE_RECURSIVE, + UNKNOWN, + CLIENT_LOAD_WRAPPER_ERROR, + CLIENT_GET_FILE_ERROR, + CLIENT_GET_IMPLEMENTATIONS_ERROR, + CLIENT_VALIDATE_RESOLUTION_FAIL, + CLIENT_VALIDATE_ABI_FAIL, + CLIENT_VALIDATE_RECURSIVE_FAIL, URI_RESOLUTION = 25, URI_RESOLVER, URI_NOT_FOUND, @@ -29,7 +30,7 @@ export enum WrapErrorCode { WRAPPER_SUBINVOKE_ABORTED, WRAPPER_INVOKE_FAIL, WRAPPER_READ_FAIL, - WRAPPER_STATE_ERROR, + WRAPPER_INTERNAL_ERROR, WRAPPER_METHOD_NOT_FOUND, WRAPPER_ARGS_MALFORMED, } @@ -39,17 +40,25 @@ export interface WrapErrorOptions { uri: string; method?: string; args?: string; - source?: { - file?: string; - row?: number; - col?: number; - }; + source?: ErrorSource; resolutionStack?: CleanResolutionStep; cause?: unknown; prev?: Error; stack?: string; } +type ErrorSource = Readonly<{ + file?: string; + row?: number; + col?: number; +}>; + +type RegExpGroups = + | (RegExpExecArray & { + groups?: { [name in T]: string | undefined } | { [key: string]: string }; + }) + | null; + export class WrapError extends Error { readonly name: string; readonly code: WrapErrorCode; @@ -57,11 +66,7 @@ export class WrapError extends Error { readonly uri: string; readonly method?: string; readonly args?: string; - readonly source?: Readonly<{ - file?: string; - row?: number; - col?: number; - }>; + readonly source?: ErrorSource; readonly resolutionStack?: CleanResolutionStep; readonly cause?: unknown; readonly prev?: Error; @@ -82,10 +87,121 @@ export class WrapError extends Error { Object.setPrototypeOf(this, WrapError.prototype); } + private static re = new RegExp( + [ + // [A-z]+Error can be replaced with specific error names when finalized + /^(?:[A-z_: ]*; )?[A-z]+Error: [\w ]+\./.source, + // there is some padding added to the number of words expected in an error code + /(?:\r\n|\r|\n)code: (?1?[0-9]{1,2}|2[0-4][0-9]|25[0-5]) (?:[A-Z]+ ?){1,5}/ + .source, + /(?:\r\n|\r|\n)reason: (?(?:.|\r\n|\r|\n)*)/.source, + /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-z0-9_-]+\/.+)/.source, + /(?:(?:\r\n|\r|\n)method: (?([A-z_]{1}[A-z0-9_]*)))?/.source, + /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r\n|\r|\n)+} ))?/.source, + /(?:(?:\r\n|\r|\n)source: \{ file: "(?.+)", row: (?[0-9]+), col: (?[0-9]+) })?/ + .source, + /(?:(?:\r\n|\r|\n)uriResolutionStack: (?\[(?:.|\r\n|\r|\n)+]))?/ + .source, + /(?:(?:\r\n|\r|\n){2}This exception was caused by the following exception:(?:\r\n|\r|\n)(?(?:.|\r\n|\r|\n)+))?$/ + .source, + ].join("") + ); + + static parse(error: string): WrapError | undefined { + const delim = "\n\nAnother exception was encountered during execution:\n"; + const errorStrings = error.split(delim); + + // case: single WrapError or not a WrapError + if (errorStrings.length === 1) { + const args = WrapError._parse(error); + return args ? new WrapError(args.reason, args.options) : undefined; + } + + // case: stack of WrapErrors stringified + const errArgs = errorStrings.map(WrapError._parse); + + // iterate through args to assign `cause` and `prev` + let curr: WrapError | undefined = undefined; + for (let i = errArgs.length - 1; i >= 0; i--) { + const currArgs = errArgs[i]; + if (!currArgs) { + // should only happen if a user includes the delimiter in their error message + throw new Error("Failed to parse WrapError"); + } + curr = new WrapError(currArgs.reason, { + ...currArgs.options, + prev: curr, + }); + } + return curr; + } + toString(): string { return `${this.name}: ${this.message}`; } + // parse a single WrapError, where the 'prev' property is undefined + private static _parse( + error: string + ): { reason: string; options: WrapErrorOptions } | undefined { + const result: RegExpGroups< + | "code" + | "reason" + | "uri" + | "method" + | "args" + | "file" + | "row" + | "col" + | "resolutionStack" + | "cause" + > = WrapError.re.exec(error); + if (!result) { + return undefined; + } + const { + code: codeStr, + reason, + uri, + method, + args, + file, + row, + col, + resolutionStack: resolutionStackStr, + cause, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + } = result.groups!; + + const code = parseInt(codeStr as string); + + // replace parens () with brackets {} + const source: ErrorSource | undefined = file + ? { + file, + row: row ? parseInt(row) : undefined, + col: col ? parseInt(col) : undefined, + } + : undefined; + + const resolutionStack = resolutionStackStr + ? JSON.parse(resolutionStackStr) + : undefined; + + return { + reason: reason as string, + options: { + code, + uri: uri as string, + method, + args: args?.trim(), + source, + resolutionStack, + cause, + }, + }; + } + private static stringify(reason: string, options: WrapErrorOptions) { const { code, @@ -173,17 +289,17 @@ export class WrapError extends Error { private static metaMessage(code: WrapErrorCode): string { switch (code) { - case WrapErrorCode.CLIENT_LOAD_WRAPPER: + case WrapErrorCode.CLIENT_LOAD_WRAPPER_ERROR: return "Failed to create Wrapper from WrapPackage."; - case WrapErrorCode.CLIENT_GET_FILE: + case WrapErrorCode.CLIENT_GET_FILE_ERROR: return "An error occurred while retrieving a file."; - case WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS: + case WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS_ERROR: return "An error occurred while retrieving interface implementations."; - case WrapErrorCode.CLIENT_VALIDATE_RESOLUTION: + case WrapErrorCode.CLIENT_VALIDATE_RESOLUTION_FAIL: return "An URI resolution error occurred while validating a WRAP URI."; - case WrapErrorCode.CLIENT_VALIDATE_ABI: + case WrapErrorCode.CLIENT_VALIDATE_ABI_FAIL: return "An error occurred while validating a WRAP URI against its ABI."; - case WrapErrorCode.CLIENT_VALIDATE_RECURSIVE: + case WrapErrorCode.CLIENT_VALIDATE_RECURSIVE_FAIL: return "An error occurred while recursively validating a WRAP URI."; case WrapErrorCode.URI_RESOLUTION: return "Unable to resolve URI."; @@ -199,8 +315,8 @@ export class WrapError extends Error { return "Invocation exception encountered."; case WrapErrorCode.WRAPPER_READ_FAIL: return "Wrapper does not contain a module, or module could not be read."; - case WrapErrorCode.WRAPPER_STATE_ERROR: - return "Invocation state is missing."; + case WrapErrorCode.WRAPPER_INTERNAL_ERROR: + return "An internal error occurred."; case WrapErrorCode.WRAPPER_METHOD_NOT_FOUND: return "Method not found in wrapper module."; case WrapErrorCode.WRAPPER_ARGS_MALFORMED: diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index ad8442d974..3f8cc69551 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -4,7 +4,6 @@ import { createImports } from "./imports"; import { IFileReader } from "./IFileReader"; import { WRAP_MODULE_PATH } from "./constants"; import { createWasmWrapper } from "./helpers/createWasmWrapper"; -import { parseWrapError, ErrorSource } from "./helpers/wrapErrorUtils"; import { WrapManifest } from "@polywrap/wrap-manifest-types-js"; import { msgpackEncode } from "@polywrap/msgpack-js"; @@ -184,9 +183,13 @@ export class WasmWrapper implements Wrapper { const abort = ( message: string, code: WrapErrorCode = WrapErrorCode.WRAPPER_INVOKE_ABORTED, - source?: ErrorSource + source?: { + file?: string; + row?: number; + col?: number; + } ) => { - const prev = parseWrapError(message); + const prev = WrapError.parse(message); const text = prev ? "SubInvocation exception encountered" : message; throw new WrapError(text, { code, diff --git a/packages/js/wasm/src/helpers/wrapErrorUtils.ts b/packages/js/wasm/src/helpers/wrapErrorUtils.ts deleted file mode 100644 index a14b603394..0000000000 --- a/packages/js/wasm/src/helpers/wrapErrorUtils.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { WrapError, WrapErrorOptions } from "@polywrap/core-js"; - -export type ErrorSource = Readonly<{ - file?: string; - row?: number; - col?: number; -}>; - -type RegExpGroups = - | (RegExpExecArray & { - groups?: { [name in T]: string | undefined } | { [key: string]: string }; - }) - | null; - -const wrapErrorRegEx = new RegExp( - [ - // [A-z]+Error can be replaced with specific error names when finalized - /^(?:[A-z_: ]*; )?[A-z]+Error: [\w ]+\./.source, - // there is some padding added to the number of words expected in an error code - /(?:\r\n|\r|\n)code: (?1?[0-9]{1,2}|2[0-4][0-9]|25[0-5]) (?:[A-Z]+ ?){1,5}/ - .source, - /(?:\r\n|\r|\n)reason: (?(?:.|\r\n|\r|\n)*)/.source, - /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-z0-9_-]+\/.+)/.source, - /(?:(?:\r\n|\r|\n)method: (?([A-z_]{1}[A-z0-9_]*)))?/.source, - /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r\n|\r|\n)+} ))?/.source, - /(?:(?:\r\n|\r|\n)source: \{ file: "(?.+)", row: (?[0-9]+), col: (?[0-9]+) })?/ - .source, - /(?:(?:\r\n|\r|\n)uriResolutionStack: (?\[(?:.|\r\n|\r|\n)+]))?/ - .source, - /(?:(?:\r\n|\r|\n){2}This exception was caused by the following exception:(?:\r\n|\r|\n)(?(?:.|\r\n|\r|\n)+))?$/ - .source, - ].join("") -); - -// parse a stringified WrapError -export function parseWrapError(error: string): WrapError | undefined { - const delim = "\n\nAnother exception was encountered during execution:\n"; - const errorStrings = error.split(delim); - - // case: single WrapError or not a WrapError - if (errorStrings.length === 1) { - const args = parseSimpleWrapError(error); - return args ? new WrapError(args.reason, args.options) : undefined; - } - - // case: stack of WrapErrors stringified - const errArgs = errorStrings.map(parseSimpleWrapError); - - // iterate through args to assign `cause` and `prev` - let curr: WrapError | undefined = undefined; - for (let i = errArgs.length - 1; i >= 0; i--) { - const currArgs = errArgs[i]; - if (!currArgs) { - // should only happen if a user includes the delimiter in their error message - throw new Error("Failed to parse WrapError"); - } - curr = new WrapError(currArgs.reason, { - ...currArgs.options, - prev: curr, - }); - } - return curr; -} - -// parse a single WrapError, where the 'prev' property is undefined -// eslint-disable-next-line @typescript-eslint/naming-convention -function parseSimpleWrapError( - error: string -): { reason: string; options: WrapErrorOptions } | undefined { - const result: RegExpGroups< - | "code" - | "reason" - | "uri" - | "method" - | "args" - | "file" - | "row" - | "col" - | "resolutionStack" - | "cause" - > = wrapErrorRegEx.exec(error); - if (!result) { - return undefined; - } - const { - code: codeStr, - reason, - uri, - method, - args, - file, - row, - col, - resolutionStack: resolutionStackStr, - cause, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - } = result.groups!; - - const code = parseInt(codeStr as string); - - // replace parens () with brackets {} - const source: ErrorSource | undefined = file - ? { - file, - row: row ? parseInt(row) : undefined, - col: col ? parseInt(col) : undefined, - } - : undefined; - - const resolutionStack = resolutionStackStr - ? JSON.parse(resolutionStackStr) - : undefined; - - return { - reason: reason as string, - options: { - code, - uri: uri as string, - method, - args: args?.trim(), - source, - resolutionStack, - cause, - }, - }; -} diff --git a/packages/js/wasm/src/imports.ts b/packages/js/wasm/src/imports.ts index eba0af7eae..6830f0c320 100644 --- a/packages/js/wasm/src/imports.ts +++ b/packages/js/wasm/src/imports.ts @@ -3,7 +3,6 @@ import { u32, WrapImports } from "./types"; import { readBytes, readString, writeBytes, writeString } from "./buffer"; import { State } from "./WasmWrapper"; -import { ErrorSource } from "./helpers/wrapErrorUtils"; import { msgpackEncode } from "@polywrap/msgpack-js"; import { CoreClient, WrapErrorCode } from "@polywrap/core-js"; @@ -12,7 +11,15 @@ export const createImports = (config: { client: CoreClient; memory: WebAssembly.Memory; state: State; - abort: (message: string, code?: WrapErrorCode, source?: ErrorSource) => never; + abort: ( + message: string, + code?: WrapErrorCode, + source?: { + file?: string; + row?: number; + col?: number; + } + ) => never; }): WrapImports => { const { memory, state, client, abort } = config; @@ -54,7 +61,7 @@ export const createImports = (config: { if (!state.subinvoke.result) { abort( "__wrap_subinvoke_result_len: subinvoke.result is not set", - WrapErrorCode.WRAPPER_STATE_ERROR + WrapErrorCode.WRAPPER_INTERNAL_ERROR ); return 0; } @@ -65,7 +72,7 @@ export const createImports = (config: { if (!state.subinvoke.result) { abort( "__wrap_subinvoke_result: subinvoke.result is not set", - WrapErrorCode.WRAPPER_STATE_ERROR + WrapErrorCode.WRAPPER_INTERNAL_ERROR ); return; } @@ -76,7 +83,7 @@ export const createImports = (config: { if (!state.subinvoke.error) { abort( "__wrap_subinvoke_error_len: subinvoke.error is not set", - WrapErrorCode.WRAPPER_STATE_ERROR + WrapErrorCode.WRAPPER_INTERNAL_ERROR ); return 0; } @@ -87,7 +94,7 @@ export const createImports = (config: { if (!state.subinvoke.error) { abort( "__wrap_subinvoke_error: subinvoke.error is not set", - WrapErrorCode.WRAPPER_STATE_ERROR + WrapErrorCode.WRAPPER_INTERNAL_ERROR ); return; } @@ -131,7 +138,7 @@ export const createImports = (config: { if (!state.subinvokeImplementation.result) { abort( "__wrap_subinvokeImplementation_result_len: subinvokeImplementation.result is not set", - WrapErrorCode.WRAPPER_STATE_ERROR + WrapErrorCode.WRAPPER_INTERNAL_ERROR ); return 0; } @@ -150,7 +157,7 @@ export const createImports = (config: { if (!state.subinvokeImplementation.error) { abort( "__wrap_subinvokeImplementation_error_len: subinvokeImplementation.error is not set", - WrapErrorCode.WRAPPER_STATE_ERROR + WrapErrorCode.WRAPPER_INTERNAL_ERROR ); return 0; } @@ -160,7 +167,7 @@ export const createImports = (config: { if (!state.subinvokeImplementation.error) { abort( "__wrap_subinvokeImplementation_error: subinvokeImplementation.error is not set", - WrapErrorCode.WRAPPER_STATE_ERROR + WrapErrorCode.WRAPPER_INTERNAL_ERROR ); return; } @@ -171,14 +178,14 @@ export const createImports = (config: { if (!state.method) { abort( "__wrap_invoke_args: method is not set", - WrapErrorCode.WRAPPER_STATE_ERROR + WrapErrorCode.WRAPPER_INTERNAL_ERROR ); return; } if (!state.args) { abort( "__wrap_invoke_args: args is not set", - WrapErrorCode.WRAPPER_STATE_ERROR + WrapErrorCode.WRAPPER_INTERNAL_ERROR ); return; } @@ -213,7 +220,7 @@ export const createImports = (config: { if (!state.getImplementationsResult) { abort( "__wrap_getImplementations_result_len: result is not set", - WrapErrorCode.WRAPPER_STATE_ERROR + WrapErrorCode.WRAPPER_INTERNAL_ERROR ); return 0; } @@ -223,7 +230,7 @@ export const createImports = (config: { if (!state.getImplementationsResult) { abort( "__wrap_getImplementations_result: result is not set", - WrapErrorCode.WRAPPER_STATE_ERROR + WrapErrorCode.WRAPPER_INTERNAL_ERROR ); return; } From a3588187916ae92dd9c772911cd07a892043c374 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 22 Dec 2022 14:20:55 +0530 Subject: [PATCH 24/40] changed URI_RESOLVER and URI_RESOLUTION error code names to URI_RESOLVER_ERROR and URI_RESOLUTION_ERROR --- packages/js/client/src/__tests__/core/error-structure.spec.ts | 2 +- packages/js/core-client/src/PolywrapCoreClient.ts | 4 ++-- packages/js/core/src/types/WrapError.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 04272e0a5f..f594895b39 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -222,7 +222,7 @@ describe("error structure", () => { if (result.ok) throw Error("should never happen"); expect(result.error?.name).toEqual("UriResolutionError"); - expect(result.error?.code).toEqual(WrapErrorCode.URI_RESOLVER); + expect(result.error?.code).toEqual(WrapErrorCode.URI_RESOLVER_ERROR); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple-deprecated")).toBeTruthy(); expect(result.error?.resolutionStack).toBeDefined(); expect(`${result.error?.cause}`).toContain(`Unrecognized WrapManifest schema version "0.0.1"`); diff --git a/packages/js/core-client/src/PolywrapCoreClient.ts b/packages/js/core-client/src/PolywrapCoreClient.ts index f422471fc0..6c0888b0fe 100644 --- a/packages/js/core-client/src/PolywrapCoreClient.ts +++ b/packages/js/core-client/src/PolywrapCoreClient.ts @@ -429,14 +429,14 @@ export class PolywrapCoreClient implements CoreClient { let error: WrapError; if (result.error) { error = new WrapError("A URI Resolver returned an error.", { - code: WrapErrorCode.URI_RESOLVER, + code: WrapErrorCode.URI_RESOLVER_ERROR, uri: uri.uri, resolutionStack: history, cause: result.error, }); } else { error = new WrapError("Error resolving URI", { - code: WrapErrorCode.URI_RESOLUTION, + code: WrapErrorCode.URI_RESOLUTION_ERROR, uri: uri.uri, resolutionStack: history, }); diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 3fbb2f43b1..3d7780b63c 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -23,8 +23,8 @@ export enum WrapErrorCode { CLIENT_VALIDATE_RESOLUTION_FAIL, CLIENT_VALIDATE_ABI_FAIL, CLIENT_VALIDATE_RECURSIVE_FAIL, - URI_RESOLUTION = 25, - URI_RESOLVER, + URI_RESOLUTION_ERROR = 25, + URI_RESOLVER_ERROR, URI_NOT_FOUND, WRAPPER_INVOKE_ABORTED = 50, WRAPPER_SUBINVOKE_ABORTED, From 9eb9c684119a1b3df2515f96746d98e6925e6f7a Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 22 Dec 2022 14:58:21 +0530 Subject: [PATCH 25/40] fixed error code name in WrapError --- packages/js/core/src/types/WrapError.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 3d7780b63c..3269b61c2d 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -301,9 +301,9 @@ export class WrapError extends Error { return "An error occurred while validating a WRAP URI against its ABI."; case WrapErrorCode.CLIENT_VALIDATE_RECURSIVE_FAIL: return "An error occurred while recursively validating a WRAP URI."; - case WrapErrorCode.URI_RESOLUTION: + case WrapErrorCode.URI_RESOLUTION_ERROR: return "Unable to resolve URI."; - case WrapErrorCode.URI_RESOLVER: + case WrapErrorCode.URI_RESOLVER_ERROR: return "An internal resolver error occurred while resolving a URI."; case WrapErrorCode.URI_NOT_FOUND: return "URI not found."; From 03f6e5976b84c46315d9633412bac1443fdc1ac3 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Wed, 28 Dec 2022 17:16:52 +0530 Subject: [PATCH 26/40] lint --- packages/js/core/src/types/CoreClient.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/js/core/src/types/CoreClient.ts b/packages/js/core/src/types/CoreClient.ts index 2ba4d06f69..eb40e1364a 100644 --- a/packages/js/core/src/types/CoreClient.ts +++ b/packages/js/core/src/types/CoreClient.ts @@ -1,10 +1,4 @@ -import { - Invoker, - Uri, - InterfaceImplementations, - Env, - WrapError, -} from "."; +import { Invoker, Uri, InterfaceImplementations, Env, WrapError } from "."; import { IUriResolutionContext, IUriResolver } from "../uri-resolution"; import { UriResolverHandler } from "./UriResolver"; From c5c0d9159717fbd9168b9260491e86c7b4253baa Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 29 Dec 2022 12:10:25 +0530 Subject: [PATCH 27/40] removed "metaMessage" concept from WrapError --- .../__tests__/core/error-structure.spec.ts | 20 +++--- packages/js/core/src/types/WrapError.ts | 61 +------------------ 2 files changed, 13 insertions(+), 68 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index f594895b39..49ca5fe932 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -62,7 +62,7 @@ describe("error structure", () => { expect(result.ok).toBeFalsy(); if (result.ok) throw Error("should never happen"); - expect(result.error?.name).toEqual("UriResolutionError"); + expect(result.error?.name).toEqual("WrapError"); expect(result.error?.code).toEqual(WrapErrorCode.URI_NOT_FOUND); expect(result.error?.reason.startsWith("Unable to find URI ")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build-not-found")).toBeTruthy(); @@ -81,7 +81,7 @@ describe("error structure", () => { expect(result.ok).toBeFalsy(); if (result.ok) throw Error("should never happen"); - expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.name).toEqual("WrapError"); expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); @@ -102,7 +102,7 @@ describe("error structure", () => { expect(result.ok).toBeFalsy(); if (result.ok) throw Error("should never happen"); - expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.name).toEqual("WrapError"); expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-rs/invalid-types/build")).toBeTruthy(); @@ -124,7 +124,7 @@ describe("error structure", () => { expect(result.ok).toBeFalsy(); if (result.ok) throw Error("should never happen"); - expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.name).toEqual("WrapError"); expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_FAIL); expect(result.error?.reason.startsWith("Could not find invoke function")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); @@ -147,7 +147,7 @@ describe("error structure", () => { expect(result.ok).toBeFalsy(); if (result.ok) throw Error("should never happen"); - expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.name).toEqual("WrapError"); expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); @@ -157,7 +157,7 @@ describe("error structure", () => { expect(result.error?.prev instanceof WrapError).toBeTruthy(); const prev = result.error?.prev as WrapError; - expect(prev.name).toEqual("UriResolutionError"); + expect(prev.name).toEqual("WrapError"); expect(prev.code).toEqual(WrapErrorCode.URI_NOT_FOUND); expect(prev.reason).toEqual("Unable to find URI wrap://ens/not-found.eth."); expect(prev.uri).toEqual("wrap://ens/not-found.eth"); @@ -177,7 +177,7 @@ describe("error structure", () => { expect(result.ok).toBeFalsy(); if (result.ok) throw Error("should never happen"); - expect(result.error?.name).toEqual("InvokeError"); + expect(result.error?.name).toEqual("WrapError"); expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); @@ -190,7 +190,7 @@ describe("error structure", () => { expect(result.error?.prev instanceof WrapError).toBeTruthy(); const prev = result.error?.prev as WrapError; - expect(prev.name).toEqual("InvokeError"); + expect(prev.name).toEqual("WrapError"); expect(prev.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(prev.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); expect(prev.uri).toEqual("wrap://ens/bad-math.eth"); @@ -200,7 +200,7 @@ describe("error structure", () => { expect(prev.prev instanceof WrapError).toBeTruthy(); const prevOfPrev = prev.prev as WrapError; - expect(prevOfPrev.name).toEqual("InvokeError"); + expect(prevOfPrev.name).toEqual("WrapError"); expect(prevOfPrev.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(prevOfPrev.reason).toEqual("__wrap_abort: I threw an error!"); expect(prevOfPrev.uri.endsWith("wrap://ens/bad-util.eth")).toBeTruthy(); @@ -221,7 +221,7 @@ describe("error structure", () => { expect(result.ok).toBeFalsy(); if (result.ok) throw Error("should never happen"); - expect(result.error?.name).toEqual("UriResolutionError"); + expect(result.error?.name).toEqual("WrapError"); expect(result.error?.code).toEqual(WrapErrorCode.URI_RESOLVER_ERROR); expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple-deprecated")).toBeTruthy(); expect(result.error?.resolutionStack).toBeDefined(); diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 3269b61c2d..b5e538a07d 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -60,7 +60,7 @@ type RegExpGroups = | null; export class WrapError extends Error { - readonly name: string; + readonly name: string = "WrapError"; readonly code: WrapErrorCode; readonly reason: string; readonly uri: string; @@ -73,7 +73,6 @@ export class WrapError extends Error { constructor(reason = "Encountered an exception.", options: WrapErrorOptions) { super(WrapError.stringify(reason, options)); - this.name = WrapError.codeToName(options.code); this.code = options.code; this.reason = reason; this.uri = options.uri; @@ -89,12 +88,10 @@ export class WrapError extends Error { private static re = new RegExp( [ - // [A-z]+Error can be replaced with specific error names when finalized - /^(?:[A-z_: ]*; )?[A-z]+Error: [\w ]+\./.source, + /^(?:[A-z_: ]*; )?WrapError: (?(?:.|\r\n|\r|\n)*)/.source, // there is some padding added to the number of words expected in an error code /(?:\r\n|\r|\n)code: (?1?[0-9]{1,2}|2[0-4][0-9]|25[0-5]) (?:[A-Z]+ ?){1,5}/ .source, - /(?:\r\n|\r|\n)reason: (?(?:.|\r\n|\r|\n)*)/.source, /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-z0-9_-]+\/.+)/.source, /(?:(?:\r\n|\r|\n)method: (?([A-z_]{1}[A-z0-9_]*)))?/.source, /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r\n|\r|\n)+} ))?/.source, @@ -236,9 +233,8 @@ export class WrapError extends Error { : ""; return [ - WrapError.metaMessage(code), + `${reason}`, `code: ${formattedCode}`, - `reason: ${reason}`, `uri: ${uri}`, maybeMethod, maybeArgs, @@ -274,55 +270,4 @@ export class WrapError extends Error { return `${cause}`; } } - - private static codeToName(code: WrapErrorCode): string { - if (code < 25) { - return "ClientError"; - } else if (code < 50) { - return "UriResolutionError"; - } else if (code < 75) { - return "InvokeError"; - } else { - return "WrapError"; - } - } - - private static metaMessage(code: WrapErrorCode): string { - switch (code) { - case WrapErrorCode.CLIENT_LOAD_WRAPPER_ERROR: - return "Failed to create Wrapper from WrapPackage."; - case WrapErrorCode.CLIENT_GET_FILE_ERROR: - return "An error occurred while retrieving a file."; - case WrapErrorCode.CLIENT_GET_IMPLEMENTATIONS_ERROR: - return "An error occurred while retrieving interface implementations."; - case WrapErrorCode.CLIENT_VALIDATE_RESOLUTION_FAIL: - return "An URI resolution error occurred while validating a WRAP URI."; - case WrapErrorCode.CLIENT_VALIDATE_ABI_FAIL: - return "An error occurred while validating a WRAP URI against its ABI."; - case WrapErrorCode.CLIENT_VALIDATE_RECURSIVE_FAIL: - return "An error occurred while recursively validating a WRAP URI."; - case WrapErrorCode.URI_RESOLUTION_ERROR: - return "Unable to resolve URI."; - case WrapErrorCode.URI_RESOLVER_ERROR: - return "An internal resolver error occurred while resolving a URI."; - case WrapErrorCode.URI_NOT_FOUND: - return "URI not found."; - case WrapErrorCode.WRAPPER_INVOKE_ABORTED: - return "Wrapper aborted execution."; - case WrapErrorCode.WRAPPER_SUBINVOKE_ABORTED: - return "Wrapper aborted execution during a subinvocation."; - case WrapErrorCode.WRAPPER_INVOKE_FAIL: - return "Invocation exception encountered."; - case WrapErrorCode.WRAPPER_READ_FAIL: - return "Wrapper does not contain a module, or module could not be read."; - case WrapErrorCode.WRAPPER_INTERNAL_ERROR: - return "An internal error occurred."; - case WrapErrorCode.WRAPPER_METHOD_NOT_FOUND: - return "Method not found in wrapper module."; - case WrapErrorCode.WRAPPER_ARGS_MALFORMED: - return "Malformed arguments passed to wrapper."; - default: - return "Unknown exception."; - } - } } From b551edb95359b5ee276752af8f9d4922c6e7325d Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 29 Dec 2022 12:29:38 +0530 Subject: [PATCH 28/40] added call to captureStackTrace in WrapError constructor --- packages/js/core/src/types/WrapError.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index b5e538a07d..69423188b8 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -84,6 +84,7 @@ export class WrapError extends Error { this.stack = options.stack; this.prev = options.prev; Object.setPrototypeOf(this, WrapError.prototype); + Error.captureStackTrace(this, this.constructor); } private static re = new RegExp( From 4981ddebfe28f382c4a55b0e846f1a37d6863679 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 29 Dec 2022 17:56:03 +0530 Subject: [PATCH 29/40] added error source to WrapErrors that are thrown from plugin wrappers; added more WrapError tests; removed option to include custom stack trace when creating a WrapError --- .../__tests__/core/error-structure.spec.ts | 397 +++++++++++------- .../helpers/mockPluginRegistration.ts | 3 + .../js/core-client/src/PolywrapCoreClient.ts | 1 - packages/js/core/src/types/WrapError.ts | 17 +- packages/js/plugin/src/PluginModule.ts | 5 +- packages/js/plugin/src/PluginWrapper.ts | 11 +- .../src/utils/PluginModuleWithMethods.ts | 5 +- .../js/plugin/src/utils/getErrorSource.ts | 31 ++ packages/js/wasm/src/WasmWrapper.ts | 7 +- packages/js/wasm/src/imports.ts | 12 +- 10 files changed, 299 insertions(+), 190 deletions(-) create mode 100644 packages/js/plugin/src/utils/getErrorSource.ts diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 49ca5fe932..fdda67d7b2 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,7 +1,8 @@ import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; -import { buildWrapper } from "@polywrap/test-env-js"; +// import { buildWrapper } from "@polywrap/test-env-js"; import { WrapError, WrapErrorCode } from "@polywrap/core-js"; +import { mockPluginRegistration } from "../helpers/mockPluginRegistration"; jest.setTimeout(660000); @@ -25,18 +26,19 @@ const incompatibleWrapperUri = new Uri(`fs/${incompatibleWrapperPath}`); const invalidTypesWrapperPath = `${GetPathToTestWrappers()}/wasm-rs/invalid-types`; const invalidTypesWrapperUri = new Uri(`fs/${invalidTypesWrapperPath}/build`); -describe("error structure", () => { +describe("Error structure", () => { let client: PolywrapClient; beforeAll(async () => { - await buildWrapper(simpleWrapperPath); - await buildWrapper(badUtilWrapperPath); - await buildWrapper(badMathWrapperPath); - await buildWrapper(subinvokeErrorWrapperPath); - await buildWrapper(invalidTypesWrapperPath); + // await buildWrapper(simpleWrapperPath); + // await buildWrapper(badUtilWrapperPath); + // await buildWrapper(badMathWrapperPath); + // await buildWrapper(subinvokeErrorWrapperPath); + // await buildWrapper(invalidTypesWrapperPath); client = new PolywrapClient({ + packages: [mockPluginRegistration("plugin/mock")], redirects: [ { from: "ens/bad-math.eth", @@ -50,181 +52,260 @@ describe("error structure", () => { }) }); - test("Invoke a wrapper that is not found", async () => { - const result = await client.invoke({ - uri: simpleWrapperUri.uri + "-not-found", - method: "simpleMethod", - args: { - arg: "test", - }, - }); + describe("URI resolution", () => { + test("Invoke a wrapper that is not found", async () => { + const result = await client.invoke({ + uri: simpleWrapperUri.uri + "-not-found", + method: "simpleMethod", + args: { + arg: "test", + }, + }); - expect(result.ok).toBeFalsy(); - if (result.ok) throw Error("should never happen"); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("WrapError"); + expect(result.error?.code).toEqual(WrapErrorCode.URI_NOT_FOUND); + expect(result.error?.reason.startsWith("Unable to find URI ")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build-not-found")).toBeTruthy(); + expect(result.error?.resolutionStack).toBeTruthy(); + }); - expect(result.error?.name).toEqual("WrapError"); - expect(result.error?.code).toEqual(WrapErrorCode.URI_NOT_FOUND); - expect(result.error?.reason.startsWith("Unable to find URI ")).toBeTruthy(); - expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build-not-found")).toBeTruthy(); - expect(result.error?.resolutionStack).toBeTruthy(); + test("Subinvoke a wrapper that is not found", async () => { + const result = await client.invoke({ + uri: subinvokeErrorWrapperUri.uri, + method: "subWrapperNotFound", + args: { + a: 1, + b: 1, + }, + }); + + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("WrapError"); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); + expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); + expect(result.error?.method).toEqual("subWrapperNotFound"); + expect(result.error?.args).toEqual("{\n \"a\": 1,\n \"b\": 1\n}"); + expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); + + expect(result.error?.prev instanceof WrapError).toBeTruthy(); + const prev = result.error?.prev as WrapError; + expect(prev.name).toEqual("WrapError"); + expect(prev.code).toEqual(WrapErrorCode.URI_NOT_FOUND); + expect(prev.reason).toEqual("Unable to find URI wrap://ens/not-found.eth."); + expect(prev.uri).toEqual("wrap://ens/not-found.eth"); + expect(prev.resolutionStack).toBeTruthy(); + }); }); - test("Invoke a wrapper with malformed arguments - as", async () => { - const result = await client.invoke({ - uri: simpleWrapperUri.uri, - method: "simpleMethod", - args: { - arg: 3, - }, + describe("Wasm wrapper", () => { + test("Invoke a wrapper with malformed arguments - as", async () => { + const result = await client.invoke({ + uri: simpleWrapperUri.uri, + method: "simpleMethod", + args: { + arg: 3, + }, + }); + + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("WrapError"); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); + expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); + expect(result.error?.method).toEqual("simpleMethod"); + expect(result.error?.args).toEqual("{\n \"arg\": 3\n}"); + expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/msgpack/ReadDecoder.ts", row: 167, col: 5 }); }); - expect(result.ok).toBeFalsy(); - if (result.ok) throw Error("should never happen"); + test("Invoke a wrapper with malformed arguments - rs", async () => { + const result = await client.invoke({ + uri: invalidTypesWrapperUri.uri, + method: "boolMethod", + args: { + arg: 3, + }, + }); + + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("WrapError"); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); + expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-rs/invalid-types/build")).toBeTruthy(); + expect(result.error?.method).toEqual("boolMethod"); + expect(result.error?.args).toEqual("{\n \"arg\": 3\n}"); + expect(result.error?.source).toEqual({ file: "src/wrap/module/wrapped.rs", row: 38, col: 13 }); + }); - expect(result.error?.name).toEqual("WrapError"); - expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); - expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); - expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); - expect(result.error?.method).toEqual("simpleMethod"); - expect(result.error?.args).toEqual("{\n \"arg\": 3\n}"); - expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/msgpack/ReadDecoder.ts", row: 167, col: 5 }); - }); - test("Invoke a wrapper with malformed arguments - rs", async () => { - const result = await client.invoke({ - uri: invalidTypesWrapperUri.uri, - method: "boolMethod", - args: { - arg: 3, - }, + test("Invoke a wrapper method that doesn't exist", async () => { + const result = await client.invoke({ + uri: simpleWrapperUri.uri, + method: "complexMethod", + args: { + arg: "test", + }, + }); + + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("WrapError"); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_FAIL); + expect(result.error?.reason.startsWith("Could not find invoke function")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); + expect(result.error?.method).toEqual("complexMethod"); + expect(result.error?.args).toEqual("{\n \"arg\": \"test\"\n}"); + expect(result.error?.toString().split("52").length).toEqual(2); + expect(result.error?.prev).toBeUndefined(); }); - expect(result.ok).toBeFalsy(); - if (result.ok) throw Error("should never happen"); + test("Subinvoke error two layers deep", async () => { + const result = await client.invoke({ + uri: subinvokeErrorWrapperUri.uri, + method: "throwsInTwoSubinvokeLayers", + args: { + a: 1, + b: 1, + }, + }); + + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("WrapError"); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); + expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); + expect(result.error?.method).toEqual("throwsInTwoSubinvokeLayers"); + expect(result.error?.args).toEqual("{\n \"a\": 1,\n \"b\": 1\n}"); + expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); + + expect(result.error?.prev instanceof WrapError).toBeTruthy(); + const prev = result.error?.prev as WrapError; + expect(prev.name).toEqual("WrapError"); + expect(prev.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); + expect(prev.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); + expect(prev.uri).toEqual("wrap://ens/bad-math.eth"); + expect(prev.method).toEqual("subInvokeWillThrow"); + expect(prev.args).toEqual("{\n \"0\": 130,\n \"1\": 161,\n \"2\": 97,\n \"3\": 1,\n \"4\": 161,\n \"5\": 98,\n \"6\": 1\n}"); + expect(prev.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); + + expect(prev.prev instanceof WrapError).toBeTruthy(); + const prevOfPrev = prev.prev as WrapError; + expect(prevOfPrev.name).toEqual("WrapError"); + expect(prevOfPrev.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); + expect(prevOfPrev.reason).toEqual("__wrap_abort: I threw an error!"); + expect(prevOfPrev.uri.endsWith("wrap://ens/bad-util.eth")).toBeTruthy(); + expect(prevOfPrev.method).toEqual("iThrow"); + expect(prevOfPrev.args).toEqual("{\n \"0\": 129,\n \"1\": 161,\n \"2\": 97,\n \"3\": 0\n}"); + expect(prevOfPrev.source).toEqual({ file: "src/index.ts", row: 5, col: 5 }); + }); - expect(result.error?.name).toEqual("WrapError"); - expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); - expect(result.error?.reason.startsWith("__wrap_abort:")).toBeTruthy(); - expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-rs/invalid-types/build")).toBeTruthy(); - expect(result.error?.method).toEqual("boolMethod"); - expect(result.error?.args).toEqual("{\n \"arg\": 3\n}"); - expect(result.error?.source).toEqual({ file: "src/wrap/module/wrapped.rs", row: 38, col: 13 }); - }); + test("Invoke a wrapper of incompatible version", async () => { + const result = await client.invoke({ + uri: incompatibleWrapperUri.uri, + method: "simpleMethod", + args: { + arg: "test", + }, + }); + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); - test("Invoke a wrapper method that doesn't exist", async () => { - const result = await client.invoke({ - uri: simpleWrapperUri.uri, - method: "complexMethod", - args: { - arg: "test", - }, + expect(result.error?.name).toEqual("WrapError"); + expect(result.error?.code).toEqual(WrapErrorCode.URI_RESOLVER_ERROR); + expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple-deprecated")).toBeTruthy(); + expect(result.error?.resolutionStack).toBeDefined(); + expect(`${result.error?.cause}`).toContain(`Unrecognized WrapManifest schema version "0.0.1"`); }); - - expect(result.ok).toBeFalsy(); - if (result.ok) throw Error("should never happen"); - - expect(result.error?.name).toEqual("WrapError"); - expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_FAIL); - expect(result.error?.reason.startsWith("Could not find invoke function")).toBeTruthy(); - expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); - expect(result.error?.method).toEqual("complexMethod"); - expect(result.error?.args).toEqual("{\n \"arg\": \"test\"\n}"); - expect(result.error?.toString().split("52").length).toEqual(2); - expect(result.error?.prev).toBeUndefined(); }); - test("Subinvoke a wrapper that is not found", async () => { - const result = await client.invoke({ - uri: subinvokeErrorWrapperUri.uri, - method: "subWrapperNotFound", - args: { - a: 1, - b: 1, - }, + describe("Plugin wrapper", () => { + test("Invoke a plugin wrapper with malformed args", async () => { + const result = await client.invoke({ + uri: "wrap://ens/fs.polywrap.eth", + method: "readFile", + args: { + pathh: __dirname + "/index.ts", + }, + }); + + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("WrapError"); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); + expect(result.error?.reason).toEqual("The \"path\" argument must be of type string or an instance of Buffer or URL. Received undefined"); + expect(result.error?.uri).toEqual("wrap://ens/fs.polywrap.eth"); + expect(result.error?.method).toEqual("readFile"); + expect(result.error?.args).toEqual("{\n \"pathh\": \"/Users/kris/WebstormProjects/monorepo/packages/js/client/src/__tests__/core/index.ts\"\n}"); + expect(result.error?.source).toEqual({ file: "node:internal/fs/promises", row: 450, col: 10 }); }); - expect(result.ok).toBeFalsy(); - if (result.ok) throw Error("should never happen"); - - expect(result.error?.name).toEqual("WrapError"); - expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); - expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); - expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); - expect(result.error?.method).toEqual("subWrapperNotFound"); - expect(result.error?.args).toEqual("{\n \"a\": 1,\n \"b\": 1\n}"); - expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); - - expect(result.error?.prev instanceof WrapError).toBeTruthy(); - const prev = result.error?.prev as WrapError; - expect(prev.name).toEqual("WrapError"); - expect(prev.code).toEqual(WrapErrorCode.URI_NOT_FOUND); - expect(prev.reason).toEqual("Unable to find URI wrap://ens/not-found.eth."); - expect(prev.uri).toEqual("wrap://ens/not-found.eth"); - expect(prev.resolutionStack).toBeTruthy(); - }); + test("Invoke a plugin wrapper with a method that doesn't exist", async () => { + const result = await client.invoke({ + uri: "wrap://ens/fs.polywrap.eth", + method: "readFileNotFound", + args: { + path: __dirname + "/index.ts", + }, + }); - test("Subinvoke error two layers deep", async () => { - const result = await client.invoke({ - uri: subinvokeErrorWrapperUri.uri, - method: "throwsInTwoSubinvokeLayers", - args: { - a: 1, - b: 1, - }, + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); + + expect(result.error?.name).toEqual("WrapError"); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_METHOD_NOT_FOUND); + expect(result.error?.reason.startsWith("Plugin missing method ")).toBeTruthy(); + expect(result.error?.uri).toEqual("wrap://ens/fs.polywrap.eth"); + expect(result.error?.method).toEqual("readFileNotFound"); }); - expect(result.ok).toBeFalsy(); - if (result.ok) throw Error("should never happen"); - - expect(result.error?.name).toEqual("WrapError"); - expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); - expect(result.error?.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); - expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/subinvoke-error/invoke/build")).toBeTruthy(); - expect(result.error?.method).toEqual("throwsInTwoSubinvokeLayers"); - expect(result.error?.args).toEqual(`{ - "a": 1, - "b": 1 -}`); - expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); - - expect(result.error?.prev instanceof WrapError).toBeTruthy(); - const prev = result.error?.prev as WrapError; - expect(prev.name).toEqual("WrapError"); - expect(prev.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); - expect(prev.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); - expect(prev.uri).toEqual("wrap://ens/bad-math.eth"); - expect(prev.method).toEqual("subInvokeWillThrow"); - expect(prev.args).toEqual("{\n \"0\": 130,\n \"1\": 161,\n \"2\": 97,\n \"3\": 1,\n \"4\": 161,\n \"5\": 98,\n \"6\": 1\n}"); - expect(prev.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); - - expect(prev.prev instanceof WrapError).toBeTruthy(); - const prevOfPrev = prev.prev as WrapError; - expect(prevOfPrev.name).toEqual("WrapError"); - expect(prevOfPrev.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); - expect(prevOfPrev.reason).toEqual("__wrap_abort: I threw an error!"); - expect(prevOfPrev.uri.endsWith("wrap://ens/bad-util.eth")).toBeTruthy(); - expect(prevOfPrev.method).toEqual("iThrow"); - expect(prevOfPrev.args).toEqual("{\n \"0\": 129,\n \"1\": 161,\n \"2\": 97,\n \"3\": 0\n}"); - expect(prevOfPrev.source).toEqual({ file: "src/index.ts", row: 5, col: 5 }); - }); + test("Invoke a plugin wrapper that throws explicitly", async () => { + const result = await client.invoke({ + uri: "wrap://plugin/mock", + method: "methodThatThrows", + }); + + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); - test("Invoke a wrapper of incompatible version", async () => { - const result = await client.invoke({ - uri: incompatibleWrapperUri.uri, - method: "simpleMethod", - args: { - arg: "test", - }, + expect(result.error?.name).toEqual("WrapError"); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); + expect(result.error?.reason).toEqual("I'm throwing!"); + expect(result.error?.uri).toEqual("wrap://plugin/mock"); + expect(result.error?.source).toEqual({ file: "/Users/kris/WebstormProjects/monorepo/packages/js/client/src/__tests__/helpers/mockPluginRegistration.ts", row: 13, col: 17 }); }); - expect(result.ok).toBeFalsy(); - if (result.ok) throw Error("should never happen"); + test("Invoke a plugin wrapper that throws unexpectedly", async () => { + const result = await client.invoke({ + uri: "wrap://ens/fs.polywrap.eth", + method: "readFile", + args: { + path: "./this/path/does/not/exist.ts", + }, + }); + + expect(result.ok).toBeFalsy(); + if (result.ok) throw Error("should never happen"); - expect(result.error?.name).toEqual("WrapError"); - expect(result.error?.code).toEqual(WrapErrorCode.URI_RESOLVER_ERROR); - expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple-deprecated")).toBeTruthy(); - expect(result.error?.resolutionStack).toBeDefined(); - expect(`${result.error?.cause}`).toContain(`Unrecognized WrapManifest schema version "0.0.1"`); + expect(result.error?.name).toEqual("WrapError"); + expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); + expect(result.error?.reason.startsWith("ENOENT: no such file or directory")).toBeTruthy(); + expect(result.error?.uri).toEqual("wrap://ens/fs.polywrap.eth"); + expect(result.error?.method).toEqual("readFile"); + expect(result.error?.args).toEqual("{\n \"path\": \"./this/path/does/not/exist.ts\"\n}"); + }); }); }); diff --git a/packages/js/client/src/__tests__/helpers/mockPluginRegistration.ts b/packages/js/client/src/__tests__/helpers/mockPluginRegistration.ts index 9b1b72fa90..7dc2411b3d 100644 --- a/packages/js/client/src/__tests__/helpers/mockPluginRegistration.ts +++ b/packages/js/client/src/__tests__/helpers/mockPluginRegistration.ts @@ -8,6 +8,9 @@ export const mockPluginRegistration = (uri: string | Uri) => { () => ({ simpleMethod: (_: unknown): string => { return "plugin response"; + }, + methodThatThrows: (_: unknown): string => { + throw Error("I'm throwing!"); } }) ), diff --git a/packages/js/core-client/src/PolywrapCoreClient.ts b/packages/js/core-client/src/PolywrapCoreClient.ts index 6c0888b0fe..27b58cf32f 100644 --- a/packages/js/core-client/src/PolywrapCoreClient.ts +++ b/packages/js/core-client/src/PolywrapCoreClient.ts @@ -188,7 +188,6 @@ export class PolywrapCoreClient implements CoreClient { const error = new WrapError(result.error?.message, { code: WrapErrorCode.CLIENT_GET_FILE_ERROR, uri: uri.toString(), - stack: result.error?.stack, }); return ResultErr(error); } diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 69423188b8..08807f7221 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -1,5 +1,11 @@ import { CleanResolutionStep } from "../algorithms"; +export type ErrorSource = Readonly<{ + file?: string; + row?: number; + col?: number; +}>; + /** Wrap error codes provide additional context to WrapErrors. @@ -44,15 +50,8 @@ export interface WrapErrorOptions { resolutionStack?: CleanResolutionStep; cause?: unknown; prev?: Error; - stack?: string; } -type ErrorSource = Readonly<{ - file?: string; - row?: number; - col?: number; -}>; - type RegExpGroups = | (RegExpExecArray & { groups?: { [name in T]: string | undefined } | { [key: string]: string }; @@ -73,6 +72,7 @@ export class WrapError extends Error { constructor(reason = "Encountered an exception.", options: WrapErrorOptions) { super(WrapError.stringify(reason, options)); + this.code = options.code; this.reason = reason; this.uri = options.uri; @@ -81,8 +81,8 @@ export class WrapError extends Error { this.source = options.source; this.resolutionStack = options.resolutionStack; this.cause = options.cause; - this.stack = options.stack; this.prev = options.prev; + Object.setPrototypeOf(this, WrapError.prototype); Error.captureStackTrace(this, this.constructor); } @@ -173,7 +173,6 @@ export class WrapError extends Error { const code = parseInt(codeStr as string); - // replace parens () with brackets {} const source: ErrorSource | undefined = file ? { file, diff --git a/packages/js/plugin/src/PluginModule.ts b/packages/js/plugin/src/PluginModule.ts index 8298820e08..38c2f5730f 100644 --- a/packages/js/plugin/src/PluginModule.ts +++ b/packages/js/plugin/src/PluginModule.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { PluginMethod } from "./PluginMethod"; -import { CoreClient } from "@polywrap/core-js"; +import { CoreClient, WrapErrorCode } from "@polywrap/core-js"; import { Result, ResultErr, ResultOk } from "@polywrap/result"; export abstract class PluginModule< @@ -51,6 +51,7 @@ export abstract class PluginModule< const data = await fn(args, client); return ResultOk(data); } catch (e) { + e.code = WrapErrorCode.WRAPPER_INVOKE_ABORTED; return ResultErr(e); } } @@ -66,6 +67,6 @@ export abstract class PluginModule< PluginMethod >)[method]; - return fn.bind(this); + return fn?.bind(this); } } diff --git a/packages/js/plugin/src/PluginWrapper.ts b/packages/js/plugin/src/PluginWrapper.ts index be11501475..bdca73c016 100644 --- a/packages/js/plugin/src/PluginWrapper.ts +++ b/packages/js/plugin/src/PluginWrapper.ts @@ -1,4 +1,5 @@ import { PluginModule } from "./PluginModule"; +import { getErrorSource } from "./utils/getErrorSource"; import { Wrapper, @@ -105,13 +106,17 @@ export class PluginWrapper implements Wrapper { encoded: false, }; } else { - const reason = `Failed to invoke method "${method}" in module: ${this.module}`; + const code = + (result.error as { code?: WrapErrorCode })?.code ?? + WrapErrorCode.WRAPPER_INVOKE_FAIL; + const reason = + result.error?.message ?? `Failed to invoke method "${method}"`; const error = new WrapError(reason, { - code: WrapErrorCode.WRAPPER_INVOKE_FAIL, + code, uri: options.uri.toString(), method, args: JSON.stringify(jsArgs, null, 2), - cause: result.error, + source: getErrorSource(result.error), }); return ResultErr(error); } diff --git a/packages/js/plugin/src/utils/PluginModuleWithMethods.ts b/packages/js/plugin/src/utils/PluginModuleWithMethods.ts index ba5d0af22b..6fa40f415b 100644 --- a/packages/js/plugin/src/utils/PluginModuleWithMethods.ts +++ b/packages/js/plugin/src/utils/PluginModuleWithMethods.ts @@ -4,7 +4,7 @@ import { PluginMethod } from "../PluginMethod"; import { PluginModule } from "../PluginModule"; import { GetPluginMethodsFunc } from "./GetPluginMethodsFunc"; -import { CoreClient } from "@polywrap/core-js"; +import { CoreClient, WrapErrorCode } from "@polywrap/core-js"; import { Result, ResultErr, ResultOk } from "@polywrap/result"; export class PluginModuleWithMethods< @@ -38,6 +38,7 @@ export class PluginModuleWithMethods< const data = await fn(args, client); return ResultOk(data); } catch (e) { + e.code = WrapErrorCode.WRAPPER_INVOKE_ABORTED; return ResultErr(e); } } @@ -50,6 +51,6 @@ export class PluginModuleWithMethods< this )[method] as PluginMethod; - return fn.bind(this); + return fn?.bind(this); } } diff --git a/packages/js/plugin/src/utils/getErrorSource.ts b/packages/js/plugin/src/utils/getErrorSource.ts new file mode 100644 index 0000000000..8b737f432a --- /dev/null +++ b/packages/js/plugin/src/utils/getErrorSource.ts @@ -0,0 +1,31 @@ +import { ErrorSource } from "@polywrap/core-js"; + +type RegExpGroups = + | (RegExpExecArray & { + groups?: { [name in T]: string | undefined } | { [key: string]: string }; + }) + | null; + +const re = /\((?.*):(?\d+):(?\d+)\)$/; + +// retrieve the most recent line of source information for an error +export function getErrorSource(error?: Error): ErrorSource | undefined { + if (!error || !error.stack) return undefined; + + // find first source line in stack + const stack = error.stack?.split("\n"); + let i = 0; + for (i; i < stack.length && !stack[i].startsWith(` at`); i++) {} // eslint-disable-line no-empty + + const result: RegExpGroups<"file" | "row" | "col"> = re.exec(stack[i]); + if (!result) return undefined; + + const { file, row, col } = result.groups!; // eslint-disable-line @typescript-eslint/no-non-null-assertion + return file + ? { + file, + row: row ? parseInt(row) : undefined, + col: col ? parseInt(col) : undefined, + } + : undefined; +} diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index 3f8cc69551..ab0020d389 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -20,6 +20,7 @@ import { Wrapper, WrapError, WrapErrorCode, + ErrorSource, } from "@polywrap/core-js"; import { Result, ResultErr, ResultOk } from "@polywrap/result"; @@ -183,11 +184,7 @@ export class WasmWrapper implements Wrapper { const abort = ( message: string, code: WrapErrorCode = WrapErrorCode.WRAPPER_INVOKE_ABORTED, - source?: { - file?: string; - row?: number; - col?: number; - } + source?: ErrorSource ) => { const prev = WrapError.parse(message); const text = prev ? "SubInvocation exception encountered" : message; diff --git a/packages/js/wasm/src/imports.ts b/packages/js/wasm/src/imports.ts index 6830f0c320..80d3c637b0 100644 --- a/packages/js/wasm/src/imports.ts +++ b/packages/js/wasm/src/imports.ts @@ -5,21 +5,13 @@ import { readBytes, readString, writeBytes, writeString } from "./buffer"; import { State } from "./WasmWrapper"; import { msgpackEncode } from "@polywrap/msgpack-js"; -import { CoreClient, WrapErrorCode } from "@polywrap/core-js"; +import { CoreClient, ErrorSource, WrapErrorCode } from "@polywrap/core-js"; export const createImports = (config: { client: CoreClient; memory: WebAssembly.Memory; state: State; - abort: ( - message: string, - code?: WrapErrorCode, - source?: { - file?: string; - row?: number; - col?: number; - } - ) => never; + abort: (message: string, code?: WrapErrorCode, source?: ErrorSource) => never; }): WrapImports => { const { memory, state, client, abort } = config; From 65e08b4f35160e19a9b8828ff71057d2dd3904ab Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 29 Dec 2022 18:18:25 +0530 Subject: [PATCH 30/40] removed comments from testing --- .../src/__tests__/core/error-structure.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index fdda67d7b2..4bdf78bb1f 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -1,6 +1,6 @@ import { GetPathToTestWrappers } from "@polywrap/test-cases"; import { Uri, PolywrapClient } from "../.."; -// import { buildWrapper } from "@polywrap/test-env-js"; +import { buildWrapper } from "@polywrap/test-env-js"; import { WrapError, WrapErrorCode } from "@polywrap/core-js"; import { mockPluginRegistration } from "../helpers/mockPluginRegistration"; @@ -31,11 +31,11 @@ describe("Error structure", () => { let client: PolywrapClient; beforeAll(async () => { - // await buildWrapper(simpleWrapperPath); - // await buildWrapper(badUtilWrapperPath); - // await buildWrapper(badMathWrapperPath); - // await buildWrapper(subinvokeErrorWrapperPath); - // await buildWrapper(invalidTypesWrapperPath); + await buildWrapper(simpleWrapperPath); + await buildWrapper(badUtilWrapperPath); + await buildWrapper(badMathWrapperPath); + await buildWrapper(subinvokeErrorWrapperPath); + await buildWrapper(invalidTypesWrapperPath); client = new PolywrapClient({ packages: [mockPluginRegistration("plugin/mock")], From 84a33c197ea67a39b8798cdf0a9c064a92270610 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 29 Dec 2022 20:30:00 +0530 Subject: [PATCH 31/40] fixed error strings in sanity.spec.ts in client --- packages/js/client/src/__tests__/core/sanity.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/js/client/src/__tests__/core/sanity.spec.ts b/packages/js/client/src/__tests__/core/sanity.spec.ts index 5bffe6e182..cd4a30df23 100644 --- a/packages/js/client/src/__tests__/core/sanity.spec.ts +++ b/packages/js/client/src/__tests__/core/sanity.spec.ts @@ -77,7 +77,7 @@ describe("sanity", () => { expect(result.ok).toBeFalsy(); let resultError = (result as { error: Error }).error; expect(resultError).toBeTruthy(); - expect(resultError.message).toContain("Unable to resolve URI"); + expect(resultError.message).toContain("Error resolving URI"); let fooPackage: IUriPackage = { uri: fooUri, @@ -102,7 +102,7 @@ describe("sanity", () => { resultError = (result as { error: Error }).error; expect(result.ok).toBeFalsy(); expect(resultError).toBeTruthy(); - expect(resultError.message).toContain("URI not found"); + expect(resultError.message).toContain("Unable to find URI"); await buildWrapper(greetingPath); From d99fa44d3723ab91d38e35b6e876b04c1fdf19c1 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 29 Dec 2022 20:30:24 +0530 Subject: [PATCH 32/40] added ".polywrap" to modulePathIgnorePatterns in jest config in client --- packages/js/client/jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/client/jest.config.js b/packages/js/client/jest.config.js index 55919b7e0d..5a41a18e7d 100644 --- a/packages/js/client/jest.config.js +++ b/packages/js/client/jest.config.js @@ -3,7 +3,7 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], - modulePathIgnorePatterns: ["./src/__tests__/e2e/wasm-rs.spec.ts"], + modulePathIgnorePatterns: ["./src/__tests__/e2e/wasm-rs.spec.ts", ".polywrap"], globals: { 'ts-jest': { diagnostics: false From a309411bed5fa65f57d0380f8790bea63a0edff5 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 29 Dec 2022 21:03:05 +0530 Subject: [PATCH 33/40] fixed some paths in wrap error tests --- .../js/client/src/__tests__/core/error-structure.spec.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 4bdf78bb1f..2b7b29fe4d 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -237,7 +237,7 @@ describe("Error structure", () => { uri: "wrap://ens/fs.polywrap.eth", method: "readFile", args: { - pathh: __dirname + "/index.ts", + pathh: "packages/js/client/src/__tests__/core/index.ts", }, }); @@ -249,7 +249,7 @@ describe("Error structure", () => { expect(result.error?.reason).toEqual("The \"path\" argument must be of type string or an instance of Buffer or URL. Received undefined"); expect(result.error?.uri).toEqual("wrap://ens/fs.polywrap.eth"); expect(result.error?.method).toEqual("readFile"); - expect(result.error?.args).toEqual("{\n \"pathh\": \"/Users/kris/WebstormProjects/monorepo/packages/js/client/src/__tests__/core/index.ts\"\n}"); + expect(result.error?.args).toContain("{\n \"pathh\": \"packages/js/client/src/__tests__/core/index.ts\"\n}"); expect(result.error?.source).toEqual({ file: "node:internal/fs/promises", row: 450, col: 10 }); }); @@ -285,7 +285,9 @@ describe("Error structure", () => { expect(result.error?.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(result.error?.reason).toEqual("I'm throwing!"); expect(result.error?.uri).toEqual("wrap://plugin/mock"); - expect(result.error?.source).toEqual({ file: "/Users/kris/WebstormProjects/monorepo/packages/js/client/src/__tests__/helpers/mockPluginRegistration.ts", row: 13, col: 17 }); + expect(result.error?.source?.file?.endsWith("packages/js/client/src/__tests__/helpers/mockPluginRegistration.ts")).toBeTruthy(); + expect(result.error?.source?.row).toEqual(13); + expect(result.error?.source?.col).toEqual(17); }); test("Invoke a plugin wrapper that throws unexpectedly", async () => { From 4f0c6814c852e5589d06e2bf2a2ef37a5cae6d34 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 29 Dec 2022 21:35:56 +0530 Subject: [PATCH 34/40] removed unnecessary \r\n from WrapError regex --- packages/js/core/src/types/WrapError.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index 08807f7221..d7da401917 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -89,18 +89,18 @@ export class WrapError extends Error { private static re = new RegExp( [ - /^(?:[A-z_: ]*; )?WrapError: (?(?:.|\r\n|\r|\n)*)/.source, + /^(?:[A-z_: ]*; )?WrapError: (?(?:.|\r|\n)*)/.source, // there is some padding added to the number of words expected in an error code /(?:\r\n|\r|\n)code: (?1?[0-9]{1,2}|2[0-4][0-9]|25[0-5]) (?:[A-Z]+ ?){1,5}/ .source, /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-z0-9_-]+\/.+)/.source, /(?:(?:\r\n|\r|\n)method: (?([A-z_]{1}[A-z0-9_]*)))?/.source, - /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r\n|\r|\n)+} ))?/.source, + /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r|\n)+} ))?/.source, /(?:(?:\r\n|\r|\n)source: \{ file: "(?.+)", row: (?[0-9]+), col: (?[0-9]+) })?/ .source, - /(?:(?:\r\n|\r|\n)uriResolutionStack: (?\[(?:.|\r\n|\r|\n)+]))?/ + /(?:(?:\r\n|\r|\n)uriResolutionStack: (?\[(?:.|\r|\n)+]))?/ .source, - /(?:(?:\r\n|\r|\n){2}This exception was caused by the following exception:(?:\r\n|\r|\n)(?(?:.|\r\n|\r|\n)+))?$/ + /(?:(?:\r\n|\r|\n){2}This exception was caused by the following exception:(?:\r\n|\r|\n)(?(?:.|\r|\n)+))?$/ .source, ].join("") ); From 1cde74f77156e22ed55372b221a6955fbe2f3d22 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 29 Dec 2022 21:47:57 +0530 Subject: [PATCH 35/40] adjusted patterns in WrapError regex that were marked "suspicious" by code analysis --- packages/js/core/src/types/WrapError.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index d7da401917..d5b262ec8b 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -89,12 +89,13 @@ export class WrapError extends Error { private static re = new RegExp( [ - /^(?:[A-z_: ]*; )?WrapError: (?(?:.|\r|\n)*)/.source, + /^(?:[A-Za-z_: ]*; )?WrapError: (?(?:.|\r|\n)*)/.source, // there is some padding added to the number of words expected in an error code /(?:\r\n|\r|\n)code: (?1?[0-9]{1,2}|2[0-4][0-9]|25[0-5]) (?:[A-Z]+ ?){1,5}/ .source, - /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-z0-9_-]+\/.+)/.source, - /(?:(?:\r\n|\r|\n)method: (?([A-z_]{1}[A-z0-9_]*)))?/.source, + /(?:\r\n|\r|\n)uri: (?wrap:\/\/[A-Za-z0-9_-]+\/.+)/.source, + /(?:(?:\r\n|\r|\n)method: (?([A-Za-z_]{1}[A-Za-z0-9_]*)))?/ + .source, /(?:(?:\r\n|\r|\n)args: (?\{(?:.|\r|\n)+} ))?/.source, /(?:(?:\r\n|\r|\n)source: \{ file: "(?.+)", row: (?[0-9]+), col: (?[0-9]+) })?/ .source, From bfab74fd663a30dadab97c7a2337c680b733220a Mon Sep 17 00:00:00 2001 From: krisbitney Date: Wed, 4 Jan 2023 11:28:34 +0530 Subject: [PATCH 36/40] wrap error codes now start at 1; abort in wasm-js no longer has a default code value; the `prev` property in WrapError was renamed to `innerError`; changed type of innerError to WrapError --- packages/js/core/src/types/WrapError.ts | 30 ++++++++++++------------- packages/js/wasm/src/WasmWrapper.ts | 4 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/js/core/src/types/WrapError.ts b/packages/js/core/src/types/WrapError.ts index d5b262ec8b..3fcee3c6cb 100644 --- a/packages/js/core/src/types/WrapError.ts +++ b/packages/js/core/src/types/WrapError.ts @@ -16,23 +16,23 @@ Error code naming convention (approximate): ==> handler_typeFn_pieceFn Error code map: - 0-24 -> Client - 25-49 -> URI resolution - 50-74 -> Wrapper invocation & sub-invocation - 75-255 -> Unallocated + 0 -> Invalid + 1-25 -> Client + 26-50 -> URI resolution + 51-75 -> Wrapper invocation & sub-invocation + 76-255 -> Unallocated */ export enum WrapErrorCode { - UNKNOWN, - CLIENT_LOAD_WRAPPER_ERROR, + CLIENT_LOAD_WRAPPER_ERROR = 1, CLIENT_GET_FILE_ERROR, CLIENT_GET_IMPLEMENTATIONS_ERROR, CLIENT_VALIDATE_RESOLUTION_FAIL, CLIENT_VALIDATE_ABI_FAIL, CLIENT_VALIDATE_RECURSIVE_FAIL, - URI_RESOLUTION_ERROR = 25, + URI_RESOLUTION_ERROR = 26, URI_RESOLVER_ERROR, URI_NOT_FOUND, - WRAPPER_INVOKE_ABORTED = 50, + WRAPPER_INVOKE_ABORTED = 51, WRAPPER_SUBINVOKE_ABORTED, WRAPPER_INVOKE_FAIL, WRAPPER_READ_FAIL, @@ -49,7 +49,7 @@ export interface WrapErrorOptions { source?: ErrorSource; resolutionStack?: CleanResolutionStep; cause?: unknown; - prev?: Error; + innerError?: WrapError; } type RegExpGroups = @@ -68,7 +68,7 @@ export class WrapError extends Error { readonly source?: ErrorSource; readonly resolutionStack?: CleanResolutionStep; readonly cause?: unknown; - readonly prev?: Error; + readonly innerError?: WrapError; constructor(reason = "Encountered an exception.", options: WrapErrorOptions) { super(WrapError.stringify(reason, options)); @@ -81,7 +81,7 @@ export class WrapError extends Error { this.source = options.source; this.resolutionStack = options.resolutionStack; this.cause = options.cause; - this.prev = options.prev; + this.innerError = options.innerError; Object.setPrototypeOf(this, WrapError.prototype); Error.captureStackTrace(this, this.constructor); @@ -129,7 +129,7 @@ export class WrapError extends Error { } curr = new WrapError(currArgs.reason, { ...currArgs.options, - prev: curr, + innerError: curr, }); } return curr; @@ -209,7 +209,7 @@ export class WrapError extends Error { source, resolutionStack, cause, - prev, + innerError, } = options; const formattedCode = `${code} ${WrapErrorCode[code].replace(/_/g, " ")}`; @@ -229,8 +229,8 @@ export class WrapError extends Error { ? `\nThis exception was caused by the following exception:\n${errorCause}` : ""; - const maybeDelim = prev - ? `\nAnother exception was encountered during execution:\n${prev}` + const maybeDelim = innerError + ? `\nAnother exception was encountered during execution:\n${innerError}` : ""; return [ diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index ab0020d389..13425c2cd2 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -183,7 +183,7 @@ export class WasmWrapper implements Wrapper { const abort = ( message: string, - code: WrapErrorCode = WrapErrorCode.WRAPPER_INVOKE_ABORTED, + code: WrapErrorCode, source?: ErrorSource ) => { const prev = WrapError.parse(message); @@ -194,7 +194,7 @@ export class WasmWrapper implements Wrapper { method, args: JSON.stringify(args, null, 2), source, - prev, + innerError: prev, }); }; From ad890701870940f7554f70e1e7de42f300d09b73 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 5 Jan 2023 11:27:50 +0530 Subject: [PATCH 37/40] split `abort` into two methods, one for aborted invocations and one for internal errors --- packages/js/wasm/src/WasmWrapper.ts | 17 +++++-- packages/js/wasm/src/imports.ts | 74 +++++++++++++---------------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/packages/js/wasm/src/WasmWrapper.ts b/packages/js/wasm/src/WasmWrapper.ts index 13425c2cd2..344c7274e4 100644 --- a/packages/js/wasm/src/WasmWrapper.ts +++ b/packages/js/wasm/src/WasmWrapper.ts @@ -181,15 +181,14 @@ export class WasmWrapper implements Wrapper { env: options.env ? msgpackEncode(options.env) : EMPTY_ENCODED_OBJECT, }; - const abort = ( + const abortWithInvokeAborted = ( message: string, - code: WrapErrorCode, source?: ErrorSource ) => { const prev = WrapError.parse(message); const text = prev ? "SubInvocation exception encountered" : message; throw new WrapError(text, { - code, + code: WrapErrorCode.WRAPPER_INVOKE_ABORTED, uri: options.uri.uri, method, args: JSON.stringify(args, null, 2), @@ -198,6 +197,15 @@ export class WasmWrapper implements Wrapper { }); }; + const abortWithInternalError = (message: string) => { + throw new WrapError(message, { + code: WrapErrorCode.WRAPPER_INTERNAL_ERROR, + uri: options.uri.uri, + method, + args: JSON.stringify(args, null, 2), + }); + }; + const memory = AsyncWasmInstance.createMemory({ module: wasm }); const instance = await AsyncWasmInstance.createInstance({ module: wasm, @@ -205,7 +213,8 @@ export class WasmWrapper implements Wrapper { state, client, memory, - abort, + abortWithInvokeAborted, + abortWithInternalError, }), requiredExports: WasmWrapper.requiredExports, }); diff --git a/packages/js/wasm/src/imports.ts b/packages/js/wasm/src/imports.ts index 80d3c637b0..155a0ead77 100644 --- a/packages/js/wasm/src/imports.ts +++ b/packages/js/wasm/src/imports.ts @@ -5,15 +5,22 @@ import { readBytes, readString, writeBytes, writeString } from "./buffer"; import { State } from "./WasmWrapper"; import { msgpackEncode } from "@polywrap/msgpack-js"; -import { CoreClient, ErrorSource, WrapErrorCode } from "@polywrap/core-js"; +import { CoreClient, ErrorSource } from "@polywrap/core-js"; export const createImports = (config: { client: CoreClient; memory: WebAssembly.Memory; state: State; - abort: (message: string, code?: WrapErrorCode, source?: ErrorSource) => never; + abortWithInvokeAborted: (message: string, source: ErrorSource) => never; + abortWithInternalError: (message: string) => never; }): WrapImports => { - const { memory, state, client, abort } = config; + const { + memory, + state, + client, + abortWithInvokeAborted, + abortWithInternalError, + } = config; return { wrap: { @@ -51,9 +58,8 @@ export const createImports = (config: { // Give WASM the size of the result __wrap_subinvoke_result_len: (): u32 => { if (!state.subinvoke.result) { - abort( - "__wrap_subinvoke_result_len: subinvoke.result is not set", - WrapErrorCode.WRAPPER_INTERNAL_ERROR + abortWithInternalError( + "__wrap_subinvoke_result_len: subinvoke.result is not set" ); return 0; } @@ -62,9 +68,8 @@ export const createImports = (config: { // Copy the subinvoke result into WASM __wrap_subinvoke_result: (ptr: u32): void => { if (!state.subinvoke.result) { - abort( - "__wrap_subinvoke_result: subinvoke.result is not set", - WrapErrorCode.WRAPPER_INTERNAL_ERROR + abortWithInternalError( + "__wrap_subinvoke_result: subinvoke.result is not set" ); return; } @@ -73,9 +78,8 @@ export const createImports = (config: { // Give WASM the size of the error __wrap_subinvoke_error_len: (): u32 => { if (!state.subinvoke.error) { - abort( - "__wrap_subinvoke_error_len: subinvoke.error is not set", - WrapErrorCode.WRAPPER_INTERNAL_ERROR + abortWithInternalError( + "__wrap_subinvoke_error_len: subinvoke.error is not set" ); return 0; } @@ -84,9 +88,8 @@ export const createImports = (config: { // Copy the subinvoke error into WASM __wrap_subinvoke_error: (ptr: u32): void => { if (!state.subinvoke.error) { - abort( - "__wrap_subinvoke_error: subinvoke.error is not set", - WrapErrorCode.WRAPPER_INTERNAL_ERROR + abortWithInternalError( + "__wrap_subinvoke_error: subinvoke.error is not set" ); return; } @@ -128,9 +131,8 @@ export const createImports = (config: { }, __wrap_subinvokeImplementation_result_len: (): u32 => { if (!state.subinvokeImplementation.result) { - abort( - "__wrap_subinvokeImplementation_result_len: subinvokeImplementation.result is not set", - WrapErrorCode.WRAPPER_INTERNAL_ERROR + abortWithInternalError( + "__wrap_subinvokeImplementation_result_len: subinvokeImplementation.result is not set" ); return 0; } @@ -138,7 +140,7 @@ export const createImports = (config: { }, __wrap_subinvokeImplementation_result: (ptr: u32): void => { if (!state.subinvokeImplementation.result) { - abort( + abortWithInternalError( "__wrap_subinvokeImplementation_result: subinvokeImplementation.result is not set" ); return; @@ -147,9 +149,8 @@ export const createImports = (config: { }, __wrap_subinvokeImplementation_error_len: (): u32 => { if (!state.subinvokeImplementation.error) { - abort( - "__wrap_subinvokeImplementation_error_len: subinvokeImplementation.error is not set", - WrapErrorCode.WRAPPER_INTERNAL_ERROR + abortWithInternalError( + "__wrap_subinvokeImplementation_error_len: subinvokeImplementation.error is not set" ); return 0; } @@ -157,9 +158,8 @@ export const createImports = (config: { }, __wrap_subinvokeImplementation_error: (ptr: u32): void => { if (!state.subinvokeImplementation.error) { - abort( - "__wrap_subinvokeImplementation_error: subinvokeImplementation.error is not set", - WrapErrorCode.WRAPPER_INTERNAL_ERROR + abortWithInternalError( + "__wrap_subinvokeImplementation_error: subinvokeImplementation.error is not set" ); return; } @@ -168,17 +168,11 @@ export const createImports = (config: { // Copy the invocation's method & args into WASM __wrap_invoke_args: (methodPtr: u32, argsPtr: u32): void => { if (!state.method) { - abort( - "__wrap_invoke_args: method is not set", - WrapErrorCode.WRAPPER_INTERNAL_ERROR - ); + abortWithInternalError("__wrap_invoke_args: method is not set"); return; } if (!state.args) { - abort( - "__wrap_invoke_args: args is not set", - WrapErrorCode.WRAPPER_INTERNAL_ERROR - ); + abortWithInternalError("__wrap_invoke_args: args is not set"); return; } writeString(state.method, memory.buffer, methodPtr); @@ -201,7 +195,7 @@ export const createImports = (config: { const uri = readString(memory.buffer, uriPtr, uriLen); const result = await client.getImplementations(uri, {}); if (!result.ok) { - abort(result.error?.message as string); + abortWithInternalError(result.error?.message as string); return false; } const implementations = result.value; @@ -210,9 +204,8 @@ export const createImports = (config: { }, __wrap_getImplementations_result_len: (): u32 => { if (!state.getImplementationsResult) { - abort( - "__wrap_getImplementations_result_len: result is not set", - WrapErrorCode.WRAPPER_INTERNAL_ERROR + abortWithInternalError( + "__wrap_getImplementations_result_len: result is not set" ); return 0; } @@ -220,9 +213,8 @@ export const createImports = (config: { }, __wrap_getImplementations_result: (ptr: u32): void => { if (!state.getImplementationsResult) { - abort( - "__wrap_getImplementations_result: result is not set", - WrapErrorCode.WRAPPER_INTERNAL_ERROR + abortWithInternalError( + "__wrap_getImplementations_result: result is not set" ); return; } @@ -242,7 +234,7 @@ export const createImports = (config: { const msg = readString(memory.buffer, msgPtr, msgLen); const file = readString(memory.buffer, filePtr, fileLen); - abort(`__wrap_abort: ${msg}`, WrapErrorCode.WRAPPER_INVOKE_ABORTED, { + abortWithInvokeAborted(`__wrap_abort: ${msg}`, { file, row: line, col: column, From 1a1b1cf0e901736568eaf0bcc19245f7f41b2b71 Mon Sep 17 00:00:00 2001 From: krisbitney Date: Thu, 5 Jan 2023 11:56:21 +0530 Subject: [PATCH 38/40] fixed wrap error tests for name change of prop from "prev" to "innerError" --- .../src/__tests__/core/error-structure.spec.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 2b7b29fe4d..01ec621e43 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -93,8 +93,8 @@ describe("Error structure", () => { expect(result.error?.args).toEqual("{\n \"a\": 1,\n \"b\": 1\n}"); expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); - expect(result.error?.prev instanceof WrapError).toBeTruthy(); - const prev = result.error?.prev as WrapError; + expect(result.error?.innerError instanceof WrapError).toBeTruthy(); + const prev = result.error?.innerError as WrapError; expect(prev.name).toEqual("WrapError"); expect(prev.code).toEqual(WrapErrorCode.URI_NOT_FOUND); expect(prev.reason).toEqual("Unable to find URI wrap://ens/not-found.eth."); @@ -166,7 +166,7 @@ describe("Error structure", () => { expect(result.error?.method).toEqual("complexMethod"); expect(result.error?.args).toEqual("{\n \"arg\": \"test\"\n}"); expect(result.error?.toString().split("52").length).toEqual(2); - expect(result.error?.prev).toBeUndefined(); + expect(result.error?.innerError).toBeUndefined(); }); test("Subinvoke error two layers deep", async () => { @@ -190,8 +190,8 @@ describe("Error structure", () => { expect(result.error?.args).toEqual("{\n \"a\": 1,\n \"b\": 1\n}"); expect(result.error?.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); - expect(result.error?.prev instanceof WrapError).toBeTruthy(); - const prev = result.error?.prev as WrapError; + expect(result.error?.innerError instanceof WrapError).toBeTruthy(); + const prev = result.error?.innerError as WrapError; expect(prev.name).toEqual("WrapError"); expect(prev.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(prev.reason.startsWith("SubInvocation exception encountered")).toBeTruthy(); @@ -200,8 +200,8 @@ describe("Error structure", () => { expect(prev.args).toEqual("{\n \"0\": 130,\n \"1\": 161,\n \"2\": 97,\n \"3\": 1,\n \"4\": 161,\n \"5\": 98,\n \"6\": 1\n}"); expect(prev.source).toEqual({ file: "~lib/@polywrap/wasm-as/containers/Result.ts", row: 171, col: 13 }); - expect(prev.prev instanceof WrapError).toBeTruthy(); - const prevOfPrev = prev.prev as WrapError; + expect(prev.innerError instanceof WrapError).toBeTruthy(); + const prevOfPrev = prev.innerError as WrapError; expect(prevOfPrev.name).toEqual("WrapError"); expect(prevOfPrev.code).toEqual(WrapErrorCode.WRAPPER_INVOKE_ABORTED); expect(prevOfPrev.reason).toEqual("__wrap_abort: I threw an error!"); From 4923540564f7ce15823861b6dcaf2f96dfa269c2 Mon Sep 17 00:00:00 2001 From: dOrgJelli Date: Thu, 5 Jan 2023 14:17:32 -0500 Subject: [PATCH 39/40] fix: error test --- .../client/src/__tests__/core/error-structure.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 01ec621e43..8918e44a2b 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -31,11 +31,11 @@ describe("Error structure", () => { let client: PolywrapClient; beforeAll(async () => { - await buildWrapper(simpleWrapperPath); - await buildWrapper(badUtilWrapperPath); - await buildWrapper(badMathWrapperPath); - await buildWrapper(subinvokeErrorWrapperPath); - await buildWrapper(invalidTypesWrapperPath); + await buildWrapper(simpleWrapperPath, undefined, true); + await buildWrapper(badUtilWrapperPath, undefined, true); + await buildWrapper(badMathWrapperPath, undefined, true); + await buildWrapper(subinvokeErrorWrapperPath, undefined, true); + await buildWrapper(invalidTypesWrapperPath, undefined, true); client = new PolywrapClient({ packages: [mockPluginRegistration("plugin/mock")], From 5995310b9056e00f30aacd04b011cac4f027b2f6 Mon Sep 17 00:00:00 2001 From: dOrgJelli Date: Thu, 5 Jan 2023 15:11:03 -0500 Subject: [PATCH 40/40] fix: error-structure tests --- .../js/client/src/__tests__/core/error-structure.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/js/client/src/__tests__/core/error-structure.spec.ts b/packages/js/client/src/__tests__/core/error-structure.spec.ts index 8918e44a2b..1dbf76bc3f 100644 --- a/packages/js/client/src/__tests__/core/error-structure.spec.ts +++ b/packages/js/client/src/__tests__/core/error-structure.spec.ts @@ -146,7 +146,6 @@ describe("Error structure", () => { expect(result.error?.source).toEqual({ file: "src/wrap/module/wrapped.rs", row: 38, col: 13 }); }); - test("Invoke a wrapper method that doesn't exist", async () => { const result = await client.invoke({ uri: simpleWrapperUri.uri, @@ -165,7 +164,9 @@ describe("Error structure", () => { expect(result.error?.uri.endsWith("packages/test-cases/cases/wrappers/wasm-as/simple/build")).toBeTruthy(); expect(result.error?.method).toEqual("complexMethod"); expect(result.error?.args).toEqual("{\n \"arg\": \"test\"\n}"); - expect(result.error?.toString().split("52").length).toEqual(2); + expect(result.error?.toString().split( + WrapErrorCode.WRAPPER_INVOKE_FAIL.valueOf().toString() + ).length).toEqual(2); expect(result.error?.innerError).toBeUndefined(); });