diff --git a/packages/js/asyncify/src/AsyncWasmInstance.ts b/packages/js/asyncify/src/AsyncWasmInstance.ts index 7ca2f7a2d1..f43845cf93 100644 --- a/packages/js/asyncify/src/AsyncWasmInstance.ts +++ b/packages/js/asyncify/src/AsyncWasmInstance.ts @@ -44,9 +44,7 @@ export class AsyncWasmInstance { private constructor() {} - public static createMemory(config: { module: ArrayBuffer }): WasmMemory { - const bytecode = new Uint8Array(config.module); - + public static createMemory(config: { module: Uint8Array }): WasmMemory { // extract the initial memory page size, as it will // throw an error if the imported page size differs: // https://chromium.googlesource.com/v8/v8/+/644556e6ed0e6e4fac2dfabb441439820ec59813/src/wasm/module-instantiate.cc#924 @@ -73,7 +71,7 @@ export class AsyncWasmInstance { // 0x__, ]); - const sigIdx = indexOfArray(bytecode, envMemoryImportSignature); + const sigIdx = indexOfArray(config.module, envMemoryImportSignature); if (sigIdx < 0) { throw Error( @@ -86,7 +84,7 @@ export class AsyncWasmInstance { // Extract the initial memory page-range size const memoryInitalLimits = - bytecode[sigIdx + envMemoryImportSignature.length + 1]; + config.module[sigIdx + envMemoryImportSignature.length + 1]; if (memoryInitalLimits === undefined) { throw Error( @@ -98,7 +96,7 @@ export class AsyncWasmInstance { } public static async createInstance(config: { - module: ArrayBuffer; + module: Uint8Array; imports: WasmImports; requiredExports?: readonly string[]; }): Promise { diff --git a/packages/js/client/src/PolywrapClient.ts b/packages/js/client/src/PolywrapClient.ts index 013c957e44..96db5439e1 100644 --- a/packages/js/client/src/PolywrapClient.ts +++ b/packages/js/client/src/PolywrapClient.ts @@ -18,6 +18,7 @@ import { InterfaceImplementations, InvokeOptions, InvokeResult, + InvokerOptions, PluginRegistration, QueryOptions, QueryResult, @@ -47,6 +48,8 @@ import { JobRunner, PluginPackage, RunOptions, + msgpackEncode, + msgpackDecode, } from "@polywrap/core-js"; import { Tracer } from "@polywrap/tracing-js"; @@ -197,7 +200,7 @@ export class PolywrapClient implements Client { public async getFile( uri: TUri, options: GetFileOptions - ): Promise { + ): Promise { const wrapper = await this._loadWrapper(this._toUri(uri), options); const client = contextualizeClient(this, options.contextId); return await wrapper.getFile(options, client); @@ -309,14 +312,14 @@ export class PolywrapClient implements Client { @Tracer.traceMethod("PolywrapClient: invoke") public async invoke( - options: InvokeOptions + options: InvokerOptions ): Promise> { const { contextId, shouldClearContext } = this._setContext( options.contextId, options.config ); - let result: InvokeResult; + let error: Error | undefined; try { const typedOptions: InvokeOptions = { @@ -327,18 +330,39 @@ export class PolywrapClient implements Client { const wrapper = await this._loadWrapper(typedOptions.uri, { contextId }); - result = (await wrapper.invoke( + const invocableResult = await wrapper.invoke( typedOptions, contextualizeClient(this, contextId) - )) as TData; - } catch (error) { - result = { error }; + ); + + if (invocableResult.data !== undefined) { + if (options.encodeResult && !invocableResult.encoded) { + return { + // TODO: if options.encodeResult, fix return type to Uint8Array + data: (msgpackEncode(invocableResult.data) as unknown) as TData, + }; + } else if (invocableResult.encoded && !options.encodeResult) { + return { + // TODO: if result.encoded, fix return type to Uint8Array + data: msgpackDecode(invocableResult.data as Uint8Array) as TData, + }; + } else { + return { + data: invocableResult.data as TData, + }; + } + } else { + error = invocableResult.error; + } + } catch (e) { + error = e; } if (shouldClearContext) { this._clearContext(contextId); } - return result; + + return { error }; } @Tracer.traceMethod("PolywrapClient: run") diff --git a/packages/js/client/src/__tests__/core/interface-impls.spec.ts b/packages/js/client/src/__tests__/core/interface-impls.spec.ts index 266ef84d6c..732026e0f2 100644 --- a/packages/js/client/src/__tests__/core/interface-impls.spec.ts +++ b/packages/js/client/src/__tests__/core/interface-impls.spec.ts @@ -83,7 +83,7 @@ describe("interface-impls", () => { { uri: implementation4Uri, plugin: { - factory: () => ({} as PluginModule), + factory: () => ({} as PluginModule<{}>), manifest: { schema: "", implements: [], @@ -145,7 +145,7 @@ describe("interface-impls", () => { { uri: interface1Uri, plugin: { - factory: () => ({} as PluginModule), + factory: () => ({} as PluginModule<{}>), manifest: { schema: "", implements: [], @@ -155,7 +155,7 @@ describe("interface-impls", () => { { uri: interface2Uri, plugin: { - factory: () => ({} as PluginModule), + factory: () => ({} as PluginModule<{}>), manifest: { schema: "", implements: [], @@ -197,7 +197,7 @@ describe("interface-impls", () => { { uri: interfaceUri, plugin: { - factory: () => ({} as PluginModule), + factory: () => ({} as PluginModule<{}>), manifest: { schema: "", implements: [], @@ -295,7 +295,7 @@ describe("interface-impls", () => { { uri: implementation1Uri, plugin: { - factory: () => ({} as PluginModule), + factory: () => ({} as PluginModule<{}>), manifest: { schema: "", implements: [new Uri(interfaceUri)], @@ -330,7 +330,7 @@ describe("interface-impls", () => { { uri: implementation1Uri, plugin: { - factory: () => ({} as PluginModule), + factory: () => ({} as PluginModule<{}>), manifest: { schema: "", implements: [], diff --git a/packages/js/client/src/__tests__/core/resolveUri.spec.ts b/packages/js/client/src/__tests__/core/resolveUri.spec.ts index 7e7209394e..e4635a2587 100644 --- a/packages/js/client/src/__tests__/core/resolveUri.spec.ts +++ b/packages/js/client/src/__tests__/core/resolveUri.spec.ts @@ -190,7 +190,7 @@ describe("resolveUri", () => { uri: pluginUri.uri, plugin: { factory: () => { - return ({} as unknown) as PluginModule; + return ({} as unknown) as PluginModule<{}>; }, manifest: { schema: "", diff --git a/packages/js/client/src/__tests__/core/wasm-wrapper.spec.ts b/packages/js/client/src/__tests__/core/wasm-wrapper.spec.ts index f58ce78a34..6ea30b2d41 100644 --- a/packages/js/client/src/__tests__/core/wasm-wrapper.spec.ts +++ b/packages/js/client/src/__tests__/core/wasm-wrapper.spec.ts @@ -113,13 +113,13 @@ describe("wasm-wrapper", () => { networkNameOrChainId: "testnet", }, }, - noDecode: true, + encodeResult: true, }); expect(result.error).toBeFalsy(); expect(result.data).toBeTruthy(); - expect(result.data instanceof ArrayBuffer).toBeTruthy(); - expect(msgpackDecode(result.data as ArrayBuffer)).toContain("0x"); + expect(result.data instanceof Uint8Array).toBeTruthy(); + expect(msgpackDecode(result.data as Uint8Array)).toContain("0x"); }); it("should invoke wrapper with custom redirects", async () => { @@ -291,9 +291,9 @@ describe("wasm-wrapper", () => { ): Int! `); - const fileBuffer: ArrayBuffer = (await client.getFile(wrapperUri, { + const fileBuffer: Uint8Array = (await client.getFile(wrapperUri, { path: manifest.schema!, - })) as ArrayBuffer; + })) as Uint8Array; const decoder = new TextDecoder("utf8"); const text = decoder.decode(fileBuffer); expect(text).toContain(`getData( diff --git a/packages/js/client/src/plugin/PluginWrapper.ts b/packages/js/client/src/plugin/PluginWrapper.ts index 38c44e3b53..79116087a5 100644 --- a/packages/js/client/src/plugin/PluginWrapper.ts +++ b/packages/js/client/src/plugin/PluginWrapper.ts @@ -3,7 +3,7 @@ import { Client, GetManifestOptions, InvokeOptions, - InvokeResult, + InvocableResult, PluginModule, PluginPackage, Uri, @@ -11,8 +11,8 @@ import { ManifestArtifactType, GetFileOptions, Env, - msgpackEncode, msgpackDecode, + isBuffer, } from "@polywrap/core-js"; import { Tracer } from "@polywrap/tracing-js"; @@ -51,15 +51,15 @@ export class PluginWrapper extends Wrapper { public async getFile( _options: GetFileOptions, _client: Client - ): Promise { + ): Promise { throw Error("client.getFile(...) is not implemented for Plugins."); } @Tracer.traceMethod("PluginWrapper: invoke") - public async invoke( + public async invoke( options: InvokeOptions, client: Client - ): Promise> { + ): Promise> { try { const { method } = options; const args = options.args || {}; @@ -79,7 +79,7 @@ export class PluginWrapper extends Wrapper { let jsArgs: Record; // If the args are a msgpack buffer, deserialize it - if (args instanceof ArrayBuffer) { + if (isBuffer(args)) { const result = msgpackDecode(args); Tracer.addEvent("msgpack-decoded", result); @@ -97,36 +97,16 @@ export class PluginWrapper extends Wrapper { // Invoke the function try { - const result = (await module._wrap_invoke( - method, - jsArgs, - client - )) as TData; + const result = await module._wrap_invoke(method, jsArgs, client); if (result !== undefined) { const data = result as unknown; - if (process.env.TEST_PLUGIN) { - // try to encode the returned result, - // ensuring it's msgpack compliant - try { - msgpackEncode(data); - } catch (e) { - throw Error( - `TEST_PLUGIN msgpack encode failure.` + - `uri: ${this._uri.uri}\nmodule: ${module}\n` + - `method: ${method}\n` + - `args: ${JSON.stringify(jsArgs, null, 2)}\n` + - `result: ${JSON.stringify(data, null, 2)}\n` + - `exception: ${e}` - ); - } - } - Tracer.addEvent("Result", data); return { - data: data as TData, + data: data, + encoded: false, }; } else { return {}; diff --git a/packages/js/client/src/wasm/WasmWrapper.ts b/packages/js/client/src/wasm/WasmWrapper.ts index 56e82b8d80..29e43bb189 100644 --- a/packages/js/client/src/wasm/WasmWrapper.ts +++ b/packages/js/client/src/wasm/WasmWrapper.ts @@ -5,6 +5,7 @@ import { createImports } from "./imports"; import { InvokeOptions, InvokeResult, + InvocableResult, Wrapper, PolywrapManifest, Uri, @@ -20,13 +21,13 @@ import { UriResolverInterface, GetFileOptions, msgpackEncode, - msgpackDecode, + isBuffer, } from "@polywrap/core-js"; import { Tracer } from "@polywrap/tracing-js"; import { AsyncWasmInstance } from "@polywrap/asyncify-js"; type InvokeResultOrError = - | { type: "InvokeResult"; invokeResult: ArrayBuffer } + | { type: "InvokeResult"; invokeResult: Uint8Array } | { type: "InvokeError"; invokeError: string }; const hasExport = (name: string, exports: Record): boolean => { @@ -39,36 +40,36 @@ const hasExport = (name: string, exports: Record): boolean => { export interface State { method: string; - args: ArrayBuffer; + args: Uint8Array; invoke: { - result?: ArrayBuffer; + result?: Uint8Array; error?: string; }; subinvoke: { - result?: ArrayBuffer; + result?: Uint8Array; error?: string; args: unknown[]; }; subinvokeImplementation: { - result?: ArrayBuffer; + result?: Uint8Array; error?: string; args: unknown[]; }; invokeResult: InvokeResult; - getImplementationsResult?: ArrayBuffer; + getImplementationsResult?: Uint8Array; sanitizeEnv: { - args?: ArrayBuffer; - result?: ArrayBuffer; + args?: Uint8Array; + result?: Uint8Array; }; - env?: ArrayBuffer; + env?: Uint8Array; } export class WasmWrapper extends Wrapper { public static requiredExports: readonly string[] = ["_wrap_invoke"]; private _schema?: string; - private _wasm: ArrayBuffer | undefined = undefined; - private _sanitizedEnv: ArrayBuffer | undefined = undefined; + private _wasm: Uint8Array | undefined = undefined; + private _sanitizedEnv: Uint8Array | undefined = undefined; constructor( private _uri: Uri, @@ -136,12 +137,14 @@ export class WasmWrapper extends Wrapper { public async getFile( options: GetFileOptions, client: Client - ): Promise { + ): Promise { const { path, encoding } = options; const { data, error } = await UriResolverInterface.Query.getFile( - ( - options: InvokeOptions - ): Promise> => client.invoke(options), + { + invoke: ( + options: InvokeOptions + ): Promise> => client.invoke(options), + }, // TODO: support all types of URI resolvers (cache, etc) new Uri(this._uriResolver), combinePaths(this._uri.path, path) @@ -176,9 +179,9 @@ export class WasmWrapper extends Wrapper { public async invoke( options: InvokeOptions, client: Client - ): Promise> { + ): Promise> { try { - const { method, noDecode } = options; + const { method } = options; const args = options.args || {}; const wasm = await this._getWasmModule(client); @@ -193,7 +196,7 @@ export class WasmWrapper extends Wrapper { invokeResult: {} as InvokeResult, method, sanitizeEnv: {}, - args: args instanceof ArrayBuffer ? args : msgpackEncode(args), + args: isBuffer(args) ? args : msgpackEncode(args), }; const abort = (message: string) => { @@ -238,23 +241,10 @@ export class WasmWrapper extends Wrapper { ); } case "InvokeResult": { - if (noDecode) { - return { - data: invokeResult.invokeResult, - } as InvokeResult; - } - - try { - return { - data: msgpackDecode(invokeResult.invokeResult as ArrayBuffer), - } as InvokeResult; - } catch (err) { - throw Error( - `WasmWrapper: Failed to decode query result.\nResult: ${JSON.stringify( - invokeResult.invokeResult - )}\nError: ${err}` - ); - } + return { + data: invokeResult.invokeResult, + encoded: true, + }; } default: { throw Error(`WasmWrapper: Unknown state "${state}"`); @@ -318,7 +308,7 @@ export class WasmWrapper extends Wrapper { ): Promise { if (hasExport("_wrap_load_env", exports)) { if (this._sanitizedEnv !== undefined) { - state.env = this._sanitizedEnv as ArrayBuffer; + state.env = this._sanitizedEnv; } else { const clientEnv = this._getClientEnv(); @@ -326,7 +316,7 @@ export class WasmWrapper extends Wrapper { state.sanitizeEnv.args = msgpackEncode({ env: clientEnv }); await exports._wrap_sanitize_env(state.sanitizeEnv.args.byteLength); - state.env = state.sanitizeEnv.result as ArrayBuffer; + state.env = state.sanitizeEnv.result; this._sanitizedEnv = state.env; } else { state.env = msgpackEncode(clientEnv); @@ -334,7 +324,7 @@ export class WasmWrapper extends Wrapper { } } - await exports._wrap_load_env(state.env.byteLength); + await exports._wrap_load_env(state.env?.byteLength || 0); } } @@ -347,9 +337,9 @@ export class WasmWrapper extends Wrapper { } @Tracer.traceMethod("WasmWrapper: getWasmModule") - private async _getWasmModule(client: Client): Promise { + private async _getWasmModule(client: Client): Promise { if (this._wasm !== undefined) { - return this._wasm as ArrayBuffer; + return this._wasm; } const moduleManifest = this._manifest.module; @@ -361,7 +351,7 @@ export class WasmWrapper extends Wrapper { const data = (await this.getFile( { path: moduleManifest }, client - )) as ArrayBuffer; + )) as Uint8Array; this._wasm = data; return data; diff --git a/packages/js/client/src/wasm/imports.ts b/packages/js/client/src/wasm/imports.ts index e717edf191..08e9ef65ef 100644 --- a/packages/js/client/src/wasm/imports.ts +++ b/packages/js/client/src/wasm/imports.ts @@ -33,22 +33,15 @@ export const createImports = (config: { const method = readString(memory.buffer, methodPtr, methodLen); const args = readBytes(memory.buffer, argsPtr, argsLen); - const { data, error } = await client.invoke({ + const { data, error } = await client.invoke({ uri: uri, method: method, - args: args, - noDecode: true, + args: new Uint8Array(args), + encodeResult: true, }); if (!error) { - let msgpack: ArrayBuffer; - if (data instanceof ArrayBuffer) { - msgpack = data; - } else { - msgpack = msgpackEncode(data); - } - - state.subinvoke.result = msgpack; + state.subinvoke.result = data; } else { state.subinvoke.error = `${error.name}: ${error.message}`; } @@ -106,22 +99,15 @@ export const createImports = (config: { state.subinvokeImplementation.args = [implUri, method, args]; - const { data, error } = await client.invoke({ + const { data, error } = await client.invoke({ uri: implUri, method: method, - args: args, - noDecode: true, + args: new Uint8Array(args), + encodeResult: true, }); if (!error) { - let msgpack: ArrayBuffer; - if (data instanceof ArrayBuffer) { - msgpack = data; - } else { - msgpack = msgpackEncode(data); - } - - state.subinvokeImplementation.result = msgpack; + state.subinvokeImplementation.result = data; } else { state.subinvokeImplementation.error = `${error.name}: ${error.message}`; } @@ -179,7 +165,9 @@ export const createImports = (config: { }, // Store the invocation's result __wrap_invoke_result: (ptr: u32, len: u32): void => { - state.invoke.result = readBytes(memory.buffer, ptr, len); + state.invoke.result = new Uint8Array( + readBytes(memory.buffer, ptr, len) + ); }, // Store the invocation's error __wrap_invoke_error: (ptr: u32, len: u32): void => { @@ -219,7 +207,9 @@ export const createImports = (config: { writeBytes(state.sanitizeEnv.args, memory.buffer, ptr); }, __wrap_sanitize_env_result: (ptr: u32, len: u32): void => { - state.sanitizeEnv.result = readBytes(memory.buffer, ptr, len); + state.sanitizeEnv.result = new Uint8Array( + readBytes(memory.buffer, ptr, len) + ); }, __wrap_abort: ( msgPtr: u32, diff --git a/packages/js/core/src/__tests__/PluginRegistrations.spec.ts b/packages/js/core/src/__tests__/PluginRegistrations.spec.ts index 630c818f2f..18ee20c5eb 100644 --- a/packages/js/core/src/__tests__/PluginRegistrations.spec.ts +++ b/packages/js/core/src/__tests__/PluginRegistrations.spec.ts @@ -12,14 +12,14 @@ describe("sanitizePluginRegistrations", () => { const plugins = sanitizePluginRegistrations([ { uri: "wrap://polywrap/wrapper", - plugin: {} as PluginPackage, + plugin: {} as PluginPackage<{}>, } ]); expect(plugins).toEqual([ { uri: new Uri("wrap://polywrap/wrapper"), - plugin: {} as PluginPackage + plugin: {} as PluginPackage<{}> } ]); }); diff --git a/packages/js/core/src/__tests__/resolveUri.spec.ts b/packages/js/core/src/__tests__/resolveUri.spec.ts index 910a604a22..a61e1e1cd2 100644 --- a/packages/js/core/src/__tests__/resolveUri.spec.ts +++ b/packages/js/core/src/__tests__/resolveUri.spec.ts @@ -31,7 +31,7 @@ import { describe("resolveUri", () => { const client = ( - wrappers: Record, + wrappers: Record>, plugins: PluginRegistration[] = [], interfaces: InterfaceImplementations[] = [], redirects: UriRedirect[] = [] @@ -67,7 +67,6 @@ describe("resolveUri", () => { {} as Client ) as TData }); - }, subscribe: < TData extends Record = Record @@ -111,13 +110,16 @@ describe("resolveUri", () => { }, } as unknown) as Client); - const createPluginWrapper = (uri: Uri, plugin: PluginPackage): Wrapper => { + const createPluginWrapper = (uri: Uri, plugin: PluginPackage<{}>): Wrapper => { return { invoke: () => Promise.resolve({ - uri, - plugin, - } as InvokeResult), + data: { + uri, + plugin, + }, + encoded: false + }), getSchema: (_client: Client): Promise => Promise.resolve(""), getFile: (options: GetFileOptions, client: Client) => Promise.resolve(""), getManifest: ( @@ -142,10 +144,13 @@ describe("resolveUri", () => { return { invoke: () => Promise.resolve({ - uri, - manifest, - uriResolver, - } as InvokeResult), + data: { + uri, + manifest, + uriResolver, + }, + encoded: false + }), getSchema: (_client: Client): Promise => Promise.resolve(""), getFile: (options: GetFileOptions, client: Client) => Promise.resolve(""), getManifest: ( @@ -205,7 +210,7 @@ describe("resolveUri", () => { { uri: new Uri("ens/my-plugin"), plugin: { - factory: () => ({} as Plugin), + factory: () => ({} as PluginModule<{}>), manifest: { schema: "", implements: [coreInterfaceUris.uriResolver], @@ -225,15 +230,15 @@ describe("resolveUri", () => { }, ]; - const wrappers: Record = { - "wrap://ens/ens": ensWrapper as unknown as PluginModule, - "wrap://ens/ipfs": ipfsWrapper as unknown as PluginModule, - "wrap://ens/my-plugin": pluginWrapper as unknown as PluginModule, + const wrappers: Record> = { + "wrap://ens/ens": ensWrapper as unknown as PluginModule<{}>, + "wrap://ens/ipfs": ipfsWrapper as unknown as PluginModule<{}>, + "wrap://ens/my-plugin": pluginWrapper as unknown as PluginModule<{}>, }; const uriResolvers: UriResolver[] = [ new RedirectsResolver(), - new PluginResolver((uri: Uri, plugin: PluginPackage) => + new PluginResolver((uri: Uri, plugin: PluginPackage<{}>) => createPluginWrapper(uri, plugin) ), new ExtendableUriResolver( @@ -257,8 +262,8 @@ describe("resolveUri", () => { const query = UriResolverInterface.Query; const uri = new Uri("wrap/some-uri"); - expect(query.tryResolveUri(client(wrappers).invoke, wrapper, uri)).toBeDefined(); - expect(query.getFile(client(wrappers).invoke, file, path)).toBeDefined(); + expect(query.tryResolveUri(client(wrappers), wrapper, uri)).toBeDefined(); + expect(query.getFile(client(wrappers), file, path)).toBeDefined(); }); it("works in the typical case", async () => { @@ -276,7 +281,7 @@ describe("resolveUri", () => { {} as Client ); - expect(wrapperIdentity).toMatchObject({ + expect(wrapperIdentity.data).toMatchObject({ uri: new Uri("ipfs/QmHash"), manifest: { format: "0.0.1-prealpha.9", @@ -300,7 +305,7 @@ describe("resolveUri", () => { {} as Client ); - expect(wrapperIdentity).toMatchObject({ + expect(wrapperIdentity.data).toMatchObject({ uri: new Uri("my/something-different"), manifest: { format: "0.0.1-prealpha.9", @@ -324,7 +329,7 @@ describe("resolveUri", () => { {} as Client ); - expect(wrapperIdentity).toMatchObject({ + expect(wrapperIdentity.data).toMatchObject({ uri: new Uri("ipfs/QmHash"), manifest: { format: "0.0.1-prealpha.9", @@ -349,7 +354,7 @@ describe("resolveUri", () => { {} as Client ); - expect(wrapperIdentity).toMatchObject({ + expect(wrapperIdentity.data).toMatchObject({ uri: new Uri("my/something-different"), manifest: { format: "0.0.1-prealpha.9", @@ -414,7 +419,7 @@ describe("resolveUri", () => { { uri: new Uri("some/wrapper"), plugin: { - factory: () => ({} as Plugin), + factory: () => ({} as PluginModule<{}>), manifest: { schema: "", implements: [coreInterfaceUris.uriResolver], @@ -460,7 +465,7 @@ describe("resolveUri", () => { client( { ...wrappers, - "wrap://ens/ipfs": faultyIpfsWrapper as unknown as PluginModule + "wrap://ens/ipfs": faultyIpfsWrapper as unknown as PluginModule<{}> }, plugins, interfaces diff --git a/packages/js/core/src/interfaces/uri-resolver.ts b/packages/js/core/src/interfaces/uri-resolver.ts index 46e30c52a6..fbcc1f3e38 100644 --- a/packages/js/core/src/interfaces/uri-resolver.ts +++ b/packages/js/core/src/interfaces/uri-resolver.ts @@ -1,5 +1,5 @@ // TODO: https://github.com/polywrap/monorepo/issues/101 -import { Uri, InvokeHandler, InvokeResult } from "../"; +import { Uri, Invoker, InvokeResult } from "../"; import { Tracer } from "@polywrap/tracing-js"; @@ -13,11 +13,11 @@ export const Query = { tryResolveUri: Tracer.traceFunc( "core: uri-resolver: tryResolveUri", async ( - invoke: InvokeHandler["invoke"], + invoker: Invoker, wrapper: Uri, uri: Uri ): Promise> => { - return invoke({ + return invoker.invoke({ uri: wrapper.uri, method: `tryResolveUri`, args: { @@ -30,11 +30,11 @@ export const Query = { getFile: Tracer.traceFunc( "core: uri-resolver: getFile", async ( - invoke: InvokeHandler["invoke"], + invoker: Invoker, wrapper: Uri, path: string - ): Promise> => { - return invoke({ + ): Promise> => { + return invoker.invoke({ uri: wrapper.uri, method: "getFile", args: { diff --git a/packages/js/core/src/msgpack/index.ts b/packages/js/core/src/msgpack/index.ts index 8ef3c92a4b..e5e28ee00d 100644 --- a/packages/js/core/src/msgpack/index.ts +++ b/packages/js/core/src/msgpack/index.ts @@ -33,7 +33,7 @@ extensionCodec.register({ }, }); -export function msgpackEncode(object: unknown): ArrayBuffer { +export function msgpackEncode(object: unknown): Uint8Array { const encoder = new Encoder( extensionCodec, undefined, // context @@ -45,7 +45,7 @@ export function msgpackEncode(object: unknown): ArrayBuffer { undefined // forceIntegerToFloat ); - return encoder.encode(object).buffer; + return encoder.encode(object); } export function msgpackDecode( @@ -54,3 +54,11 @@ export function msgpackDecode( const decoder = new Decoder(extensionCodec); return decoder.decode(buffer); } + +export function isBuffer(maybeBuf: unknown): maybeBuf is BufferSource { + if (maybeBuf instanceof ArrayBuffer || ArrayBuffer.isView(maybeBuf)) { + return true; + } else { + return false; + } +} diff --git a/packages/js/core/src/types/Client.ts b/packages/js/core/src/types/Client.ts index 23dfa55b8a..4a523bc468 100644 --- a/packages/js/core/src/types/Client.ts +++ b/packages/js/core/src/types/Client.ts @@ -1,6 +1,6 @@ import { QueryHandler, - InvokeHandler, + Invoker, SubscriptionHandler, UriRedirect, Uri, @@ -53,9 +53,9 @@ export interface GetImplementationsOptions extends Contextualized { } export interface Client - extends QueryHandler, + extends Invoker, + QueryHandler, SubscriptionHandler, - InvokeHandler, WorkflowHandler, UriResolverHandler { getRedirects(options: GetRedirectsOptions): readonly UriRedirect[]; @@ -91,7 +91,7 @@ export interface Client getFile( uri: TUri, options: GetFileOptions - ): Promise; + ): Promise; getImplementations( uri: TUri, diff --git a/packages/js/core/src/types/Invoke.ts b/packages/js/core/src/types/Invoke.ts index 057c23bccd..fb26eefbbe 100644 --- a/packages/js/core/src/types/Invoke.ts +++ b/packages/js/core/src/types/Invoke.ts @@ -15,13 +15,7 @@ export interface InvokeOptions< * Arguments for the method, structured as a map, * removing the chance of incorrectly ordering arguments. */ - args?: Record | ArrayBuffer; - - /** - * If set to true, the invoke function will not decode the msgpack results - * into JavaScript objects, and instead return the raw ArrayBuffer. - */ - noDecode?: boolean; + args?: Record | Uint8Array; /** * Override the client's config for all invokes within this invoke. @@ -52,8 +46,26 @@ export interface InvokeResult { error?: Error; } -export interface InvokeHandler { +export interface InvokerOptions< + TUri extends Uri | string = string, + TClientConfig extends ClientConfig = ClientConfig +> extends InvokeOptions { + encodeResult?: boolean; +} + +export interface Invoker { invoke( - options: InvokeOptions + options: InvokerOptions ): Promise>; } + +export interface InvocableResult extends InvokeResult { + encoded?: boolean; +} + +export interface Invocable { + invoke( + options: InvokeOptions, + invoker: Invoker + ): Promise>; +} diff --git a/packages/js/core/src/types/Wrapper.ts b/packages/js/core/src/types/Wrapper.ts index b9308c81b9..d940fe56b2 100644 --- a/packages/js/core/src/types/Wrapper.ts +++ b/packages/js/core/src/types/Wrapper.ts @@ -4,7 +4,9 @@ import { GetFileOptions, GetManifestOptions, InvokeOptions, - InvokeResult, + Invocable, + Invoker, + InvocableResult, } from "."; import { AnyManifestArtifact, ManifestArtifactType } from "../manifest"; @@ -14,7 +16,7 @@ import { AnyManifestArtifact, ManifestArtifactType } from "../manifest"; * this class may do things like caching WASM bytecode, spawning * worker threads, or indexing into resolvers to find the requested method. */ -export abstract class Wrapper { +export abstract class Wrapper implements Invocable { /** * Invoke the Wrapper based on the provided [[InvokeOptions]] * @@ -24,8 +26,8 @@ export abstract class Wrapper { */ public abstract invoke( options: InvokeOptions, - client: Client - ): Promise>; + invoker: Invoker + ): Promise>; /** * Get the Wrapper's schema @@ -57,7 +59,7 @@ export abstract class Wrapper { public abstract getFile( options: GetFileOptions, client: Client - ): Promise; + ): Promise; } /** Cache of Wrapper definitions, mapping the Wrapper's URI to its definition */ diff --git a/packages/js/core/src/uri-resolution/resolvers/extendable/UriResolverWrapper.ts b/packages/js/core/src/uri-resolution/resolvers/extendable/UriResolverWrapper.ts index a8cdef56d0..1c7a4910de 100644 --- a/packages/js/core/src/uri-resolution/resolvers/extendable/UriResolverWrapper.ts +++ b/packages/js/core/src/uri-resolution/resolvers/extendable/UriResolverWrapper.ts @@ -3,7 +3,7 @@ import { DeserializeManifestOptions, deserializePolywrapManifest, } from "../../../manifest"; -import { Uri, WrapperCache, Client, InvokeHandler } from "../../../types"; +import { Uri, WrapperCache, Client, Invoker } from "../../../types"; import { UriResolver, UriResolutionStack, @@ -34,7 +34,7 @@ export class UriResolverWrapper implements UriResolver { const result = await tryResolveUriWithImplementation( uri, this.implementationUri, - client.invoke.bind(client) + client ); if (!result) { @@ -82,10 +82,10 @@ export class UriResolverWrapper implements UriResolver { const tryResolveUriWithImplementation = async ( uri: Uri, implementationUri: Uri, - invoke: InvokeHandler["invoke"] + invoker: Invoker ): Promise => { const { data } = await UriResolverInterface.Query.tryResolveUri( - invoke, + invoker, implementationUri, uri ); diff --git a/packages/js/plugins/file-system/src/index.ts b/packages/js/plugins/file-system/src/index.ts index 96bb84ad92..66b46ef9f7 100644 --- a/packages/js/plugins/file-system/src/index.ts +++ b/packages/js/plugins/file-system/src/index.ts @@ -18,8 +18,12 @@ import { PluginFactory } from "@polywrap/core-js"; type NoConfig = Record; export class FileSystemPlugin extends Module { - async readFile(args: Args_readFile, _client: Client): Promise { - return fs.promises.readFile(args.path); + async readFile(args: Args_readFile, _client: Client): Promise { + return fs.promises + .readFile(args.path) + .then((buffer) => + ArrayBuffer.isView(buffer) ? buffer : new Uint8Array(buffer) + ); } async readFileAsString( diff --git a/packages/js/react/src/invoke.tsx b/packages/js/react/src/invoke.tsx index d01b2f4c00..3b11f1509c 100644 --- a/packages/js/react/src/invoke.tsx +++ b/packages/js/react/src/invoke.tsx @@ -1,7 +1,11 @@ import { usePolywrapClient } from "./client"; import { useStateReducer } from "./state"; -import { InvokeOptions, InvokeResult } from "@polywrap/core-js"; +import { + InvokeOptions, + InvokeResult, + isBuffer +} from "@polywrap/core-js"; export interface UsePolywrapInvokeState< TData = unknown @@ -21,13 +25,13 @@ export interface UsePolywrapInvokeProps extends InvokeOptions { /* Note that the initial values passed into the usePolywrapInvoke hook will be -ignored when an ArrayBuffer is passed into execute(...). +ignored when an Uint8Array is passed into execute(...). */ export interface UsePolywrapInvoke< TData = unknown > extends UsePolywrapInvokeState { execute: ( - args?: Record | ArrayBuffer + args?: Record | Uint8Array ) => Promise>; } @@ -42,11 +46,11 @@ export function usePolywrapInvoke< INITIAL_QUERY_STATE as UsePolywrapInvokeState ); - const execute = async (args?: Record | ArrayBuffer) => { + const execute = async (args?: Record | Uint8Array) => { dispatch({ loading: true }); const { data, error } = await client.invoke({ ...props, - args: args instanceof ArrayBuffer ? args : { + args: isBuffer(args) ? args : { ...props.args, ...args, }, diff --git a/packages/schema/bind/src/bindings/typescript/app-ts/templates/types-ts.mustache b/packages/schema/bind/src/bindings/typescript/app-ts/templates/types-ts.mustache index 78a23f1067..9e7a07ac49 100644 --- a/packages/schema/bind/src/bindings/typescript/app-ts/templates/types-ts.mustache +++ b/packages/schema/bind/src/bindings/typescript/app-ts/templates/types-ts.mustache @@ -15,7 +15,7 @@ export type Int = number; export type Int8 = number; export type Int16 = number; export type Int32 = number; -export type Bytes = ArrayBuffer; +export type Bytes = Uint8Array; export type BigInt = string; export type BigNumber = string; export type Json = string; diff --git a/packages/schema/bind/src/bindings/typescript/plugin-ts/templates/types-ts.mustache b/packages/schema/bind/src/bindings/typescript/plugin-ts/templates/types-ts.mustache index 771baaab3c..e88be3796a 100644 --- a/packages/schema/bind/src/bindings/typescript/plugin-ts/templates/types-ts.mustache +++ b/packages/schema/bind/src/bindings/typescript/plugin-ts/templates/types-ts.mustache @@ -18,7 +18,7 @@ export type Int = number; export type Int8 = number; export type Int16 = number; export type Int32 = number; -export type Bytes = ArrayBuffer; +export type Bytes = Uint8Array; export type BigInt = string; export type BigNumber = string; export type Json = string; diff --git a/packages/test-cases/cases/bind/sanity/output/app-ts/types.ts b/packages/test-cases/cases/bind/sanity/output/app-ts/types.ts index 71e9a4a833..8dcf50fa3f 100644 --- a/packages/test-cases/cases/bind/sanity/output/app-ts/types.ts +++ b/packages/test-cases/cases/bind/sanity/output/app-ts/types.ts @@ -15,7 +15,7 @@ export type Int = number; export type Int8 = number; export type Int16 = number; export type Int32 = number; -export type Bytes = ArrayBuffer; +export type Bytes = Uint8Array; export type BigInt = string; export type BigNumber = string; export type Json = string; diff --git a/packages/test-cases/cases/bind/sanity/output/plugin-ts/types.ts b/packages/test-cases/cases/bind/sanity/output/plugin-ts/types.ts index 8aed41456a..fae3517706 100644 --- a/packages/test-cases/cases/bind/sanity/output/plugin-ts/types.ts +++ b/packages/test-cases/cases/bind/sanity/output/plugin-ts/types.ts @@ -18,7 +18,7 @@ export type Int = number; export type Int8 = number; export type Int16 = number; export type Int32 = number; -export type Bytes = ArrayBuffer; +export type Bytes = Uint8Array; export type BigInt = string; export type BigNumber = string; export type Json = string; diff --git a/packages/test-cases/cases/cli/plugin/codegen/001-sanity/expected/src/wrap/types.ts b/packages/test-cases/cases/cli/plugin/codegen/001-sanity/expected/src/wrap/types.ts index 796131ef49..bb9273518b 100644 --- a/packages/test-cases/cases/cli/plugin/codegen/001-sanity/expected/src/wrap/types.ts +++ b/packages/test-cases/cases/cli/plugin/codegen/001-sanity/expected/src/wrap/types.ts @@ -18,7 +18,7 @@ export type Int = number; export type Int8 = number; export type Int16 = number; export type Int32 = number; -export type Bytes = ArrayBuffer; +export type Bytes = Uint8Array; export type BigInt = string; export type BigNumber = string; export type Json = string; diff --git a/packages/test-cases/cases/cli/plugin/codegen/002-single-module/expected/src/wrap/types.ts b/packages/test-cases/cases/cli/plugin/codegen/002-single-module/expected/src/wrap/types.ts index 796131ef49..bb9273518b 100644 --- a/packages/test-cases/cases/cli/plugin/codegen/002-single-module/expected/src/wrap/types.ts +++ b/packages/test-cases/cases/cli/plugin/codegen/002-single-module/expected/src/wrap/types.ts @@ -18,7 +18,7 @@ export type Int = number; export type Int8 = number; export type Int16 = number; export type Int32 = number; -export type Bytes = ArrayBuffer; +export type Bytes = Uint8Array; export type BigInt = string; export type BigNumber = string; export type Json = string; diff --git a/packages/test-cases/cases/cli/plugin/codegen/003-env/expected/src/wrap/types.ts b/packages/test-cases/cases/cli/plugin/codegen/003-env/expected/src/wrap/types.ts index 450bfe2967..a02c59511e 100644 --- a/packages/test-cases/cases/cli/plugin/codegen/003-env/expected/src/wrap/types.ts +++ b/packages/test-cases/cases/cli/plugin/codegen/003-env/expected/src/wrap/types.ts @@ -18,7 +18,7 @@ export type Int = number; export type Int8 = number; export type Int16 = number; export type Int32 = number; -export type Bytes = ArrayBuffer; +export type Bytes = Uint8Array; export type BigInt = string; export type BigNumber = string; export type Json = string; diff --git a/packages/test-cases/cases/cli/plugin/codegen/004-env-sanitization/expected/src/wrap/types.ts b/packages/test-cases/cases/cli/plugin/codegen/004-env-sanitization/expected/src/wrap/types.ts index 813097c637..d6e697ee03 100644 --- a/packages/test-cases/cases/cli/plugin/codegen/004-env-sanitization/expected/src/wrap/types.ts +++ b/packages/test-cases/cases/cli/plugin/codegen/004-env-sanitization/expected/src/wrap/types.ts @@ -18,7 +18,7 @@ export type Int = number; export type Int8 = number; export type Int16 = number; export type Int32 = number; -export type Bytes = ArrayBuffer; +export type Bytes = Uint8Array; export type BigInt = string; export type BigNumber = string; export type Json = string;