@microsoft/dotnet-js-interop
Advanced tools
Comparing version 8.0.0-preview.4.23260.4 to 8.0.0-preview.5.23302.2
export declare module DotNet { | ||
export type JsonReviver = ((key: any, value: any) => any); | ||
/** | ||
* Sets the specified .NET call dispatcher as the current instance so that it will be used | ||
* for future invocations. | ||
* Creates a .NET call dispatcher to use for handling invocations between JavaScript and a .NET runtime. | ||
* | ||
* @param dispatcher An object that can dispatch calls from JavaScript to a .NET runtime. | ||
* @param dotNetCallDispatcher An object that can dispatch calls from JavaScript to a .NET runtime. | ||
*/ | ||
export function attachDispatcher(dispatcher: DotNetCallDispatcher): void; | ||
export function attachDispatcher(dotNetCallDispatcher: DotNetCallDispatcher): ICallDispatcher; | ||
/** | ||
@@ -19,2 +18,3 @@ * Adds a JSON reviver callback that will be used when parsing arguments received from .NET. | ||
* | ||
* @deprecated Use DotNetObject to invoke instance methods instead. | ||
* @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method. | ||
@@ -29,2 +29,3 @@ * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. | ||
* | ||
* @deprecated Use DotNetObject to invoke instance methods instead. | ||
* @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method. | ||
@@ -107,20 +108,6 @@ * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. | ||
/** | ||
* Receives incoming calls from .NET and dispatches them to JavaScript. | ||
* Represents the ability to facilitate function call dispatching between JavaScript and a .NET runtime. | ||
*/ | ||
export const jsCallDispatcher: { | ||
export interface ICallDispatcher { | ||
/** | ||
* Finds the JavaScript function matching the specified identifier. | ||
* | ||
* @param identifier Identifies the globally-reachable function to be returned. | ||
* @param targetInstanceId The instance ID of the target JS object. | ||
* @returns A Function instance. | ||
*/ | ||
findJSFunction: typeof findJSFunction; | ||
/** | ||
* Disposes the JavaScript object reference with the specified object ID. | ||
* | ||
* @param id The ID of the JavaScript object reference. | ||
*/ | ||
disposeJSObjectReferenceById: typeof disposeJSObjectReferenceById; | ||
/** | ||
* Invokes the specified synchronous JavaScript function. | ||
@@ -134,3 +121,3 @@ * | ||
*/ | ||
invokeJSFromDotNet: (identifier: string, argsJson: string, resultType: JSCallResultType, targetInstanceId: number) => string | null; | ||
invokeJSFromDotNet(identifier: string, argsJson: string, resultType: JSCallResultType, targetInstanceId: number): string | null; | ||
/** | ||
@@ -145,3 +132,3 @@ * Invokes the specified synchronous or asynchronous JavaScript function. | ||
*/ | ||
beginInvokeJSFromDotNet: (asyncHandle: number, identifier: string, argsJson: string, resultType: JSCallResultType, targetInstanceId: number) => void; | ||
beginInvokeJSFromDotNet(asyncHandle: number, identifier: string, argsJson: string | null, resultType: JSCallResultType, targetInstanceId: number): void; | ||
/** | ||
@@ -153,4 +140,23 @@ * Receives notification that an async call from JS to .NET has completed. | ||
*/ | ||
endInvokeDotNetFromJS: (asyncCallId: string, success: boolean, resultJsonOrExceptionMessage: string) => void; | ||
endInvokeDotNetFromJS(asyncCallId: string, success: boolean, resultJsonOrExceptionMessage: string): void; | ||
/** | ||
* Invokes the specified .NET public static method synchronously. Not all hosting scenarios support | ||
* synchronous invocation, so if possible use invokeMethodAsync instead. | ||
* | ||
* @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method. | ||
* @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. | ||
* @param args Arguments to pass to the method, each of which must be JSON-serializable. | ||
* @returns The result of the operation. | ||
*/ | ||
invokeDotNetStaticMethod<T>(assemblyName: string, methodIdentifier: string, ...args: any[]): T; | ||
/** | ||
* Invokes the specified .NET public static method asynchronously. | ||
* | ||
* @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method. | ||
* @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. | ||
* @param args Arguments to pass to the method, each of which must be JSON-serializable. | ||
* @returns A promise representing the result of the operation. | ||
*/ | ||
invokeDotNetStaticMethodAsync<T>(assemblyName: string, methodIdentifier: string, ...args: any[]): Promise<T>; | ||
/** | ||
* Receives notification that a byte array is being transferred from .NET to JS. | ||
@@ -160,16 +166,38 @@ * @param id The identifier for the byte array used during revival. | ||
*/ | ||
receiveByteArray: (id: number, data: Uint8Array) => void; | ||
receiveByteArray(id: number, data: Uint8Array): void; | ||
/** | ||
* Supplies a stream of data being sent from .NET. | ||
* | ||
* @param streamId The identifier previously passed to JSRuntime's BeginTransmittingStream in .NET code | ||
* @param streamId The identifier previously passed to JSRuntime's BeginTransmittingStream in .NET code. | ||
* @param stream The stream data. | ||
*/ | ||
supplyDotNetStream: (streamId: number, stream: ReadableStream) => void; | ||
}; | ||
function findJSFunction(identifier: string, targetInstanceId: number): Function; | ||
function disposeJSObjectReferenceById(id: number): void; | ||
supplyDotNetStream(streamId: number, stream: ReadableStream): void; | ||
} | ||
class CallDispatcher implements ICallDispatcher { | ||
private readonly _dotNetCallDispatcher; | ||
private readonly _byteArraysToBeRevived; | ||
private readonly _pendingDotNetToJSStreams; | ||
private readonly _pendingAsyncCalls; | ||
private _nextAsyncCallId; | ||
constructor(_dotNetCallDispatcher: DotNetCallDispatcher); | ||
getDotNetCallDispatcher(): DotNetCallDispatcher; | ||
invokeJSFromDotNet(identifier: string, argsJson: string, resultType: JSCallResultType, targetInstanceId: number): string | null; | ||
beginInvokeJSFromDotNet(asyncHandle: number, identifier: string, argsJson: string | null, resultType: JSCallResultType, targetInstanceId: number): void; | ||
endInvokeDotNetFromJS(asyncCallId: string, success: boolean, resultJsonOrExceptionMessage: string): void; | ||
invokeDotNetStaticMethod<T>(assemblyName: string, methodIdentifier: string, ...args: any[]): T; | ||
invokeDotNetStaticMethodAsync<T>(assemblyName: string, methodIdentifier: string, ...args: any[]): Promise<T>; | ||
invokeDotNetMethod<T>(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, args: any[] | null): T; | ||
invokeDotNetMethodAsync<T>(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, args: any[] | null): Promise<T>; | ||
receiveByteArray(id: number, data: Uint8Array): void; | ||
processByteArray(id: number): Uint8Array | null; | ||
supplyDotNetStream(streamId: number, stream: ReadableStream): void; | ||
getDotNetStreamPromise(streamId: number): Promise<ReadableStream>; | ||
private completePendingCall; | ||
} | ||
export function findJSFunction(identifier: string, targetInstanceId: number): Function; | ||
export function disposeJSObjectReferenceById(id: number): void; | ||
export class DotNetObject { | ||
private _id; | ||
constructor(_id: number); | ||
private readonly _id; | ||
private readonly _callDispatcher; | ||
constructor(_id: number, _callDispatcher: CallDispatcher); | ||
invokeMethod<T>(methodIdentifier: string, ...args: any[]): T; | ||
@@ -176,0 +204,0 @@ invokeMethodAsync<T>(methodIdentifier: string, ...args: any[]): Promise<T>; |
@@ -6,6 +6,3 @@ // Licensed to the .NET Foundation under one or more agreements. | ||
(function (DotNet) { | ||
window.DotNet = DotNet; // Ensure reachable from anywhere | ||
const jsonRevivers = []; | ||
const byteArraysToBeRevived = new Map(); | ||
const pendingDotNetToJSStreams = new Map(); | ||
const jsObjectIdKey = "__jsObjectId"; | ||
@@ -16,2 +13,8 @@ const dotNetObjectRefKey = "__dotNetObject"; | ||
const jsStreamReferenceLengthKey = "__jsStreamReferenceLength"; | ||
// If undefined, no dispatcher has been attached yet. | ||
// If null, this means more than one dispatcher was attached, so no default can be determined. | ||
// Otherwise, there was only one dispatcher registered. We keep track of this instance to keep legacy APIs working. | ||
let defaultCallDispatcher; | ||
// Provides access to the "current" call dispatcher without having to flow it through nested function calls. | ||
let currentCallDispatcher; | ||
class JSObject { | ||
@@ -50,3 +53,2 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
} | ||
const pendingAsyncCalls = {}; | ||
const windowJSObjectId = 0; | ||
@@ -65,13 +67,21 @@ const cachedJSObjectsById = { | ||
}); | ||
let nextAsyncCallId = 1; // Start at 1 because zero signals "no response needed" | ||
let nextJsObjectId = 1; // Start at 1 because zero is reserved for "window" | ||
let dotNetDispatcher = null; | ||
/** | ||
* Sets the specified .NET call dispatcher as the current instance so that it will be used | ||
* for future invocations. | ||
* Creates a .NET call dispatcher to use for handling invocations between JavaScript and a .NET runtime. | ||
* | ||
* @param dispatcher An object that can dispatch calls from JavaScript to a .NET runtime. | ||
* @param dotNetCallDispatcher An object that can dispatch calls from JavaScript to a .NET runtime. | ||
*/ | ||
function attachDispatcher(dispatcher) { | ||
dotNetDispatcher = dispatcher; | ||
function attachDispatcher(dotNetCallDispatcher) { | ||
const result = new CallDispatcher(dotNetCallDispatcher); | ||
if (defaultCallDispatcher === undefined) { | ||
// This was the first dispatcher registered, so it becomes the default. This exists purely for | ||
// backwards compatibility. | ||
defaultCallDispatcher = result; | ||
} | ||
else if (defaultCallDispatcher) { | ||
// There is already a default dispatcher. Now that there are multiple to choose from, there can | ||
// be no acceptable default, so we nullify the default dispatcher. | ||
defaultCallDispatcher = null; | ||
} | ||
return result; | ||
} | ||
@@ -91,2 +101,3 @@ DotNet.attachDispatcher = attachDispatcher; | ||
* | ||
* @deprecated Use DotNetObject to invoke instance methods instead. | ||
* @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method. | ||
@@ -98,3 +109,4 @@ * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. | ||
function invokeMethod(assemblyName, methodIdentifier, ...args) { | ||
return invokePossibleInstanceMethod(assemblyName, methodIdentifier, null, args); | ||
const dispatcher = getDefaultCallDispatcher(); | ||
return dispatcher.invokeDotNetStaticMethod(assemblyName, methodIdentifier, ...args); | ||
} | ||
@@ -105,2 +117,3 @@ DotNet.invokeMethod = invokeMethod; | ||
* | ||
* @deprecated Use DotNetObject to invoke instance methods instead. | ||
* @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method. | ||
@@ -112,3 +125,4 @@ * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. | ||
function invokeMethodAsync(assemblyName, methodIdentifier, ...args) { | ||
return invokePossibleInstanceMethodAsync(assemblyName, methodIdentifier, null, args); | ||
const dispatcher = getDefaultCallDispatcher(); | ||
return dispatcher.invokeDotNetStaticMethodAsync(assemblyName, methodIdentifier, ...args); | ||
} | ||
@@ -186,9 +200,5 @@ DotNet.invokeMethodAsync = invokeMethodAsync; | ||
DotNet.disposeJSObjectReference = disposeJSObjectReference; | ||
/** | ||
* Parses the given JSON string using revivers to restore args passed from .NET to JS. | ||
* | ||
* @param json The JSON stirng to parse. | ||
*/ | ||
function parseJsonWithRevivers(json) { | ||
return json ? JSON.parse(json, (key, initialValue) => { | ||
function parseJsonWithRevivers(callDispatcher, json) { | ||
currentCallDispatcher = callDispatcher; | ||
const result = json ? JSON.parse(json, (key, initialValue) => { | ||
// Invoke each reviver in order, passing the output from the previous reviver, | ||
@@ -198,47 +208,14 @@ // so that each one gets a chance to transform the value | ||
}) : null; | ||
currentCallDispatcher = undefined; | ||
return result; | ||
} | ||
function invokePossibleInstanceMethod(assemblyName, methodIdentifier, dotNetObjectId, args) { | ||
const dispatcher = getRequiredDispatcher(); | ||
if (dispatcher.invokeDotNetFromJS) { | ||
const argsJson = stringifyArgs(args); | ||
const resultJson = dispatcher.invokeDotNetFromJS(assemblyName, methodIdentifier, dotNetObjectId, argsJson); | ||
return resultJson ? parseJsonWithRevivers(resultJson) : null; | ||
function getDefaultCallDispatcher() { | ||
if (defaultCallDispatcher === undefined) { | ||
throw new Error("No call dispatcher has been set."); | ||
} | ||
throw new Error("The current dispatcher does not support synchronous calls from JS to .NET. Use invokeMethodAsync instead."); | ||
} | ||
function invokePossibleInstanceMethodAsync(assemblyName, methodIdentifier, dotNetObjectId, args) { | ||
if (assemblyName && dotNetObjectId) { | ||
throw new Error(`For instance method calls, assemblyName should be null. Received '${assemblyName}'.`); | ||
else if (defaultCallDispatcher === null) { | ||
throw new Error("There are multiple .NET runtimes present, so a default dispatcher could not be resolved. Use DotNetObject to invoke .NET instance methods."); | ||
} | ||
const asyncCallId = nextAsyncCallId++; | ||
const resultPromise = new Promise((resolve, reject) => { | ||
pendingAsyncCalls[asyncCallId] = { resolve, reject }; | ||
}); | ||
try { | ||
const argsJson = stringifyArgs(args); | ||
getRequiredDispatcher().beginInvokeDotNetFromJS(asyncCallId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); | ||
} | ||
catch (ex) { | ||
// Synchronous failure | ||
completePendingCall(asyncCallId, false, ex); | ||
} | ||
return resultPromise; | ||
} | ||
function getRequiredDispatcher() { | ||
if (dotNetDispatcher !== null) { | ||
return dotNetDispatcher; | ||
} | ||
throw new Error("No .NET call dispatcher has been set."); | ||
} | ||
function completePendingCall(asyncCallId, success, resultOrError) { | ||
if (!pendingAsyncCalls.hasOwnProperty(asyncCallId)) { | ||
throw new Error(`There is no pending async call with ID ${asyncCallId}.`); | ||
} | ||
const asyncCall = pendingAsyncCalls[asyncCallId]; | ||
delete pendingAsyncCalls[asyncCallId]; | ||
if (success) { | ||
asyncCall.resolve(resultOrError); | ||
} | ||
else { | ||
asyncCall.reject(resultOrError); | ||
return defaultCallDispatcher; | ||
} | ||
@@ -257,50 +234,30 @@ } | ||
})(JSCallResultType = DotNet.JSCallResultType || (DotNet.JSCallResultType = {})); | ||
/** | ||
* Receives incoming calls from .NET and dispatches them to JavaScript. | ||
*/ | ||
DotNet.jsCallDispatcher = { | ||
/** | ||
* Finds the JavaScript function matching the specified identifier. | ||
* | ||
* @param identifier Identifies the globally-reachable function to be returned. | ||
* @param targetInstanceId The instance ID of the target JS object. | ||
* @returns A Function instance. | ||
*/ | ||
findJSFunction, | ||
/** | ||
* Disposes the JavaScript object reference with the specified object ID. | ||
* | ||
* @param id The ID of the JavaScript object reference. | ||
*/ | ||
disposeJSObjectReferenceById, | ||
/** | ||
* Invokes the specified synchronous JavaScript function. | ||
* | ||
* @param identifier Identifies the globally-reachable function to invoke. | ||
* @param argsJson JSON representation of arguments to be passed to the function. | ||
* @param resultType The type of result expected from the JS interop call. | ||
* @param targetInstanceId The instance ID of the target JS object. | ||
* @returns JSON representation of the invocation result. | ||
*/ | ||
invokeJSFromDotNet: (identifier, argsJson, resultType, targetInstanceId) => { | ||
const returnValue = findJSFunction(identifier, targetInstanceId).apply(null, parseJsonWithRevivers(argsJson)); | ||
class CallDispatcher { | ||
// eslint-disable-next-line no-empty-function | ||
constructor(_dotNetCallDispatcher) { | ||
this._dotNetCallDispatcher = _dotNetCallDispatcher; | ||
this._byteArraysToBeRevived = new Map(); | ||
this._pendingDotNetToJSStreams = new Map(); | ||
this._pendingAsyncCalls = {}; | ||
this._nextAsyncCallId = 1; // Start at 1 because zero signals "no response needed" | ||
} | ||
getDotNetCallDispatcher() { | ||
return this._dotNetCallDispatcher; | ||
} | ||
invokeJSFromDotNet(identifier, argsJson, resultType, targetInstanceId) { | ||
const args = parseJsonWithRevivers(this, argsJson); | ||
const jsFunction = findJSFunction(identifier, targetInstanceId); | ||
const returnValue = jsFunction(...(args || [])); | ||
const result = createJSCallResult(returnValue, resultType); | ||
return result === null || result === undefined | ||
? null | ||
: stringifyArgs(result); | ||
}, | ||
/** | ||
* Invokes the specified synchronous or asynchronous JavaScript function. | ||
* | ||
* @param asyncHandle A value identifying the asynchronous operation. This value will be passed back in a later call to endInvokeJSFromDotNet. | ||
* @param identifier Identifies the globally-reachable function to invoke. | ||
* @param argsJson JSON representation of arguments to be passed to the function. | ||
* @param resultType The type of result expected from the JS interop call. | ||
* @param targetInstanceId The ID of the target JS object instance. | ||
*/ | ||
beginInvokeJSFromDotNet: (asyncHandle, identifier, argsJson, resultType, targetInstanceId) => { | ||
: stringifyArgs(this, result); | ||
} | ||
beginInvokeJSFromDotNet(asyncHandle, identifier, argsJson, resultType, targetInstanceId) { | ||
// Coerce synchronous functions into async ones, plus treat | ||
// synchronous exceptions the same as async ones | ||
const promise = new Promise(resolve => { | ||
const synchronousResultOrPromise = findJSFunction(identifier, targetInstanceId).apply(null, parseJsonWithRevivers(argsJson)); | ||
const args = parseJsonWithRevivers(this, argsJson); | ||
const jsFunction = findJSFunction(identifier, targetInstanceId); | ||
const synchronousResultOrPromise = jsFunction(...(args || [])); | ||
resolve(synchronousResultOrPromise); | ||
@@ -312,6 +269,10 @@ }); | ||
// Not using "await" because it codegens a lot of boilerplate | ||
promise | ||
.then(result => stringifyArgs([asyncHandle, true, createJSCallResult(result, resultType)])) | ||
.then(result => getRequiredDispatcher().endInvokeJSFromDotNet(asyncHandle, true, result), error => getRequiredDispatcher().endInvokeJSFromDotNet(asyncHandle, false, JSON.stringify([ | ||
promise. | ||
then(result => stringifyArgs(this, [ | ||
asyncHandle, | ||
true, | ||
createJSCallResult(result, resultType) | ||
])). | ||
then(result => this._dotNetCallDispatcher.endInvokeJSFromDotNet(asyncHandle, true, result), error => this._dotNetCallDispatcher.endInvokeJSFromDotNet(asyncHandle, false, JSON.stringify([ | ||
asyncHandle, | ||
false, | ||
@@ -321,34 +282,57 @@ formatError(error) | ||
} | ||
}, | ||
/** | ||
* Receives notification that an async call from JS to .NET has completed. | ||
* @param asyncCallId The identifier supplied in an earlier call to beginInvokeDotNetFromJS. | ||
* @param success A flag to indicate whether the operation completed successfully. | ||
* @param resultJsonOrExceptionMessage Either the operation result as JSON, or an error message. | ||
*/ | ||
endInvokeDotNetFromJS: (asyncCallId, success, resultJsonOrExceptionMessage) => { | ||
} | ||
endInvokeDotNetFromJS(asyncCallId, success, resultJsonOrExceptionMessage) { | ||
const resultOrError = success | ||
? parseJsonWithRevivers(resultJsonOrExceptionMessage) | ||
? parseJsonWithRevivers(this, resultJsonOrExceptionMessage) | ||
: new Error(resultJsonOrExceptionMessage); | ||
completePendingCall(parseInt(asyncCallId, 10), success, resultOrError); | ||
}, | ||
/** | ||
* Receives notification that a byte array is being transferred from .NET to JS. | ||
* @param id The identifier for the byte array used during revival. | ||
* @param data The byte array being transferred for eventual revival. | ||
*/ | ||
receiveByteArray: (id, data) => { | ||
byteArraysToBeRevived.set(id, data); | ||
}, | ||
/** | ||
* Supplies a stream of data being sent from .NET. | ||
* | ||
* @param streamId The identifier previously passed to JSRuntime's BeginTransmittingStream in .NET code | ||
* @param stream The stream data. | ||
*/ | ||
supplyDotNetStream: (streamId, stream) => { | ||
if (pendingDotNetToJSStreams.has(streamId)) { | ||
this.completePendingCall(parseInt(asyncCallId, 10), success, resultOrError); | ||
} | ||
invokeDotNetStaticMethod(assemblyName, methodIdentifier, ...args) { | ||
return this.invokeDotNetMethod(assemblyName, methodIdentifier, null, args); | ||
} | ||
invokeDotNetStaticMethodAsync(assemblyName, methodIdentifier, ...args) { | ||
return this.invokeDotNetMethodAsync(assemblyName, methodIdentifier, null, args); | ||
} | ||
invokeDotNetMethod(assemblyName, methodIdentifier, dotNetObjectId, args) { | ||
if (this._dotNetCallDispatcher.invokeDotNetFromJS) { | ||
const argsJson = stringifyArgs(this, args); | ||
const resultJson = this._dotNetCallDispatcher.invokeDotNetFromJS(assemblyName, methodIdentifier, dotNetObjectId, argsJson); | ||
return resultJson ? parseJsonWithRevivers(this, resultJson) : null; | ||
} | ||
throw new Error("The current dispatcher does not support synchronous calls from JS to .NET. Use invokeDotNetMethodAsync instead."); | ||
} | ||
invokeDotNetMethodAsync(assemblyName, methodIdentifier, dotNetObjectId, args) { | ||
if (assemblyName && dotNetObjectId) { | ||
throw new Error(`For instance method calls, assemblyName should be null. Received '${assemblyName}'.`); | ||
} | ||
const asyncCallId = this._nextAsyncCallId++; | ||
const resultPromise = new Promise((resolve, reject) => { | ||
this._pendingAsyncCalls[asyncCallId] = { resolve, reject }; | ||
}); | ||
try { | ||
const argsJson = stringifyArgs(this, args); | ||
this._dotNetCallDispatcher.beginInvokeDotNetFromJS(asyncCallId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); | ||
} | ||
catch (ex) { | ||
// Synchronous failure | ||
this.completePendingCall(asyncCallId, false, ex); | ||
} | ||
return resultPromise; | ||
} | ||
receiveByteArray(id, data) { | ||
this._byteArraysToBeRevived.set(id, data); | ||
} | ||
processByteArray(id) { | ||
const result = this._byteArraysToBeRevived.get(id); | ||
if (!result) { | ||
return null; | ||
} | ||
this._byteArraysToBeRevived.delete(id); | ||
return result; | ||
} | ||
supplyDotNetStream(streamId, stream) { | ||
if (this._pendingDotNetToJSStreams.has(streamId)) { | ||
// The receiver is already waiting, so we can resolve the promise now and stop tracking this | ||
const pendingStream = pendingDotNetToJSStreams.get(streamId); | ||
pendingDotNetToJSStreams.delete(streamId); | ||
const pendingStream = this._pendingDotNetToJSStreams.get(streamId); | ||
this._pendingDotNetToJSStreams.delete(streamId); | ||
pendingStream.resolve(stream); | ||
@@ -360,6 +344,37 @@ } | ||
pendingStream.resolve(stream); | ||
pendingDotNetToJSStreams.set(streamId, pendingStream); | ||
this._pendingDotNetToJSStreams.set(streamId, pendingStream); | ||
} | ||
} | ||
}; | ||
getDotNetStreamPromise(streamId) { | ||
// We might already have started receiving the stream, or maybe it will come later. | ||
// We have to handle both possible orderings, but we can count on it coming eventually because | ||
// it's not something the developer gets to control, and it would be an error if it doesn't. | ||
let result; | ||
if (this._pendingDotNetToJSStreams.has(streamId)) { | ||
// We've already started receiving the stream, so no longer need to track it as pending | ||
result = this._pendingDotNetToJSStreams.get(streamId).streamPromise; | ||
this._pendingDotNetToJSStreams.delete(streamId); | ||
} | ||
else { | ||
// We haven't started receiving it yet, so add an entry to track it as pending | ||
const pendingStream = new PendingStream(); | ||
this._pendingDotNetToJSStreams.set(streamId, pendingStream); | ||
result = pendingStream.streamPromise; | ||
} | ||
return result; | ||
} | ||
completePendingCall(asyncCallId, success, resultOrError) { | ||
if (!this._pendingAsyncCalls.hasOwnProperty(asyncCallId)) { | ||
throw new Error(`There is no pending async call with ID ${asyncCallId}.`); | ||
} | ||
const asyncCall = this._pendingAsyncCalls[asyncCallId]; | ||
delete this._pendingAsyncCalls[asyncCallId]; | ||
if (success) { | ||
asyncCall.resolve(resultOrError); | ||
} | ||
else { | ||
asyncCall.reject(resultOrError); | ||
} | ||
} | ||
} | ||
function formatError(error) { | ||
@@ -378,22 +393,25 @@ if (error instanceof Error) { | ||
} | ||
DotNet.findJSFunction = findJSFunction; | ||
function disposeJSObjectReferenceById(id) { | ||
delete cachedJSObjectsById[id]; | ||
} | ||
DotNet.disposeJSObjectReferenceById = disposeJSObjectReferenceById; | ||
class DotNetObject { | ||
// eslint-disable-next-line no-empty-function | ||
constructor(_id) { | ||
constructor(_id, _callDispatcher) { | ||
this._id = _id; | ||
this._callDispatcher = _callDispatcher; | ||
} | ||
invokeMethod(methodIdentifier, ...args) { | ||
return invokePossibleInstanceMethod(null, methodIdentifier, this._id, args); | ||
return this._callDispatcher.invokeDotNetMethod(null, methodIdentifier, this._id, args); | ||
} | ||
invokeMethodAsync(methodIdentifier, ...args) { | ||
return invokePossibleInstanceMethodAsync(null, methodIdentifier, this._id, args); | ||
return this._callDispatcher.invokeDotNetMethodAsync(null, methodIdentifier, this._id, args); | ||
} | ||
dispose() { | ||
const promise = invokePossibleInstanceMethodAsync(null, "__Dispose", this._id, null); | ||
const promise = this._callDispatcher.invokeDotNetMethodAsync(null, "__Dispose", this._id, null); | ||
promise.catch(error => console.error(error)); | ||
} | ||
serializeAsArg() { | ||
return { __dotNetObject: this._id }; | ||
return { [dotNetObjectRefKey]: this._id }; | ||
} | ||
@@ -405,3 +423,3 @@ } | ||
if (value.hasOwnProperty(dotNetObjectRefKey)) { | ||
return new DotNetObject(value[dotNetObjectRefKey]); | ||
return new DotNetObject(value[dotNetObjectRefKey], currentCallDispatcher); | ||
} | ||
@@ -418,11 +436,12 @@ else if (value.hasOwnProperty(jsObjectIdKey)) { | ||
const index = value[byteArrayRefKey]; | ||
const byteArray = byteArraysToBeRevived.get(index); | ||
const byteArray = currentCallDispatcher.processByteArray(index); | ||
if (byteArray === undefined) { | ||
throw new Error(`Byte array index '${index}' does not exist.`); | ||
} | ||
byteArraysToBeRevived.delete(index); | ||
return byteArray; | ||
} | ||
else if (value.hasOwnProperty(dotNetStreamRefKey)) { | ||
return new DotNetStream(value[dotNetStreamRefKey]); | ||
const streamId = value[dotNetStreamRefKey]; | ||
const streamPromise = currentCallDispatcher.getDotNetStreamPromise(streamId); | ||
return new DotNetStream(streamPromise); | ||
} | ||
@@ -434,18 +453,5 @@ } | ||
class DotNetStream { | ||
constructor(streamId) { | ||
// This constructor runs when we're JSON-deserializing some value from the .NET side. | ||
// At this point we might already have started receiving the stream, or maybe it will come later. | ||
// We have to handle both possible orderings, but we can count on it coming eventually because | ||
// it's not something the developer gets to control, and it would be an error if it doesn't. | ||
if (pendingDotNetToJSStreams.has(streamId)) { | ||
// We've already started receiving the stream, so no longer need to track it as pending | ||
this._streamPromise = pendingDotNetToJSStreams.get(streamId).streamPromise; | ||
pendingDotNetToJSStreams.delete(streamId); | ||
} | ||
else { | ||
// We haven't started receiving it yet, so add an entry to track it as pending | ||
const pendingStream = new PendingStream(); | ||
pendingDotNetToJSStreams.set(streamId, pendingStream); | ||
this._streamPromise = pendingStream.streamPromise; | ||
} | ||
// eslint-disable-next-line no-empty-function | ||
constructor(_streamPromise) { | ||
this._streamPromise = _streamPromise; | ||
} | ||
@@ -489,5 +495,8 @@ /** | ||
let nextByteArrayIndex = 0; | ||
function stringifyArgs(args) { | ||
function stringifyArgs(callDispatcher, args) { | ||
nextByteArrayIndex = 0; | ||
return JSON.stringify(args, argReplacer); | ||
currentCallDispatcher = callDispatcher; | ||
const result = JSON.stringify(args, argReplacer); | ||
currentCallDispatcher = undefined; | ||
return result; | ||
} | ||
@@ -499,3 +508,4 @@ function argReplacer(key, value) { | ||
else if (value instanceof Uint8Array) { | ||
dotNetDispatcher.sendByteArray(nextByteArrayIndex, value); | ||
const dotNetCallDispatcher = currentCallDispatcher.getDotNetCallDispatcher(); | ||
dotNetCallDispatcher.sendByteArray(nextByteArrayIndex, value); | ||
const jsonValue = { [byteArrayRefKey]: nextByteArrayIndex }; | ||
@@ -502,0 +512,0 @@ nextByteArrayIndex++; |
{ | ||
"name": "@microsoft/dotnet-js-interop", | ||
"version": "8.0.0-preview.4.23260.4", | ||
"version": "8.0.0-preview.5.23302.2", | ||
"description": "Provides abstractions and features for interop between .NET and JavaScript code.", | ||
@@ -5,0 +5,0 @@ "main": "dist/Microsoft.JSInterop.js", |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
52309
701