@player-ui/async-node-plugin 0.13.0-next.2 → 0.13.0-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,43 @@
1
- import { expect, test, describe } from "vitest";
2
- import { Node, InProgressState } from "@player-ui/player";
1
+ import { expect, test, describe, vi, beforeEach } from "vitest";
2
+ import {
3
+ Node,
4
+ InProgressState,
5
+ ErrorState,
6
+ PlayerPlugin,
7
+ AssetTransformCorePlugin,
8
+ BeforeTransformFunction,
9
+ } from "@player-ui/player";
3
10
  import { Player, Parser } from "@player-ui/player";
4
11
  import { waitFor } from "@testing-library/react";
5
- import { AsyncNodePlugin, AsyncNodePluginPlugin } from "../index";
6
- import { ReferenceAssetsPlugin } from "@player-ui/reference-assets-plugin";
12
+ import {
13
+ AsyncNodePlugin,
14
+ AsyncNodePluginPlugin,
15
+ asyncTransform,
16
+ } from "../index";
7
17
  import { CheckPathPlugin } from "@player-ui/check-path-plugin";
18
+ import { Registry } from "@player-ui/partial-match-registry";
19
+
20
+ const transform: BeforeTransformFunction = (asset) => {
21
+ const newAsset = asset.children?.[0]?.value;
22
+
23
+ if (!newAsset) {
24
+ return asyncTransform(asset.value.id, "collection");
25
+ }
26
+ return asyncTransform(asset.value.id, "collection", newAsset);
27
+ };
28
+
29
+ const transformPlugin = new AssetTransformCorePlugin(
30
+ new Registry([[{ type: "chat-message" }, { beforeResolve: transform }]]),
31
+ );
32
+
33
+ class TestAsyncPlugin implements PlayerPlugin {
34
+ name = "test-async";
35
+ apply(player: Player) {
36
+ player.hooks.view.tap("test-async", (view) => {
37
+ transformPlugin.apply(view);
38
+ });
39
+ }
40
+ }
8
41
 
9
42
  describe("view", () => {
10
43
  const basicFRFWithActions = {
@@ -734,6 +767,84 @@ describe("view", () => {
734
767
  });
735
768
  });
736
769
 
770
+ describe("Async Node Error Handling", () => {
771
+ let failingAsyncNodePlugin: AsyncNodePlugin = new AsyncNodePlugin({});
772
+ const onAsyncNodeErrorCallback = vi.fn();
773
+
774
+ beforeEach(() => {
775
+ onAsyncNodeErrorCallback.mockReset();
776
+ const failingHandler = vi.fn();
777
+ failingHandler.mockRejectedValue("Promise Rejected");
778
+
779
+ failingAsyncNodePlugin = new AsyncNodePlugin(
780
+ {
781
+ plugins: [new AsyncNodePluginPlugin()],
782
+ },
783
+ failingHandler,
784
+ );
785
+
786
+ failingAsyncNodePlugin.hooks.onAsyncNodeError.tap(
787
+ "test",
788
+ onAsyncNodeErrorCallback,
789
+ );
790
+ });
791
+
792
+ test("should replace the async node with the result from the onAsyncNodeError hook when there is an error handling the async node", async () => {
793
+ onAsyncNodeErrorCallback.mockReturnValue({
794
+ asset: {
795
+ id: "async-text",
796
+ type: "text",
797
+ value: "Fallback Text",
798
+ },
799
+ });
800
+
801
+ const player = new Player({ plugins: [failingAsyncNodePlugin] });
802
+ player.start(basicFRFWithActions as any);
803
+
804
+ await waitFor(() => {
805
+ expect(onAsyncNodeErrorCallback).toHaveBeenCalledWith(
806
+ new Error("Promise Rejected"),
807
+ expect.anything(),
808
+ );
809
+
810
+ const playerState = player.getState();
811
+ expect(playerState.status).toBe("in-progress");
812
+ const inProgressState = playerState as InProgressState;
813
+ const lastViewUpdate =
814
+ inProgressState.controllers.view.currentView?.lastUpdate;
815
+
816
+ expect(lastViewUpdate?.actions[1]).toStrictEqual({
817
+ asset: {
818
+ id: "async-text",
819
+ type: "text",
820
+ value: "Fallback Text",
821
+ },
822
+ });
823
+ });
824
+ });
825
+
826
+ test("should bubble up the error and cause player to fail when there is an error handling the async node and the onAsyncNodeError hook does not produce a fallback", async () => {
827
+ onAsyncNodeErrorCallback.mockReturnValue(undefined);
828
+
829
+ const player = new Player({ plugins: [failingAsyncNodePlugin] });
830
+ player.start(basicFRFWithActions as any).catch(() => {
831
+ /** Purposefully failing player in this test so catching the unresolved exception suppresses warnings from vitest */
832
+ });
833
+
834
+ await waitFor(() => {
835
+ expect(onAsyncNodeErrorCallback).toHaveBeenCalledWith(
836
+ new Error("Promise Rejected"),
837
+ expect.anything(),
838
+ );
839
+
840
+ const playerState = player.getState();
841
+ expect(playerState.status).toBe("error");
842
+ const errorState = playerState as ErrorState;
843
+ expect(errorState.error.message).toBe("Promise Rejected");
844
+ });
845
+ });
846
+ });
847
+
737
848
  test("chat-message asset - replaces async nodes with multi node flattened", async () => {
738
849
  const plugin = new AsyncNodePlugin({
739
850
  plugins: [new AsyncNodePluginPlugin()],
@@ -749,7 +860,7 @@ describe("view", () => {
749
860
 
750
861
  let updateNumber = 0;
751
862
 
752
- const plugins = [plugin, new ReferenceAssetsPlugin()];
863
+ const plugins = [plugin, new TestAsyncPlugin()];
753
864
 
754
865
  const player = new Player({
755
866
  plugins: plugins,
@@ -821,7 +932,7 @@ describe("view", () => {
821
932
 
822
933
  let updateNumber = 0;
823
934
 
824
- const plugins = [plugin, new ReferenceAssetsPlugin()];
935
+ const plugins = [plugin, new TestAsyncPlugin()];
825
936
 
826
937
  const player = new Player({
827
938
  plugins: plugins,
@@ -886,7 +997,7 @@ describe("view", () => {
886
997
  let updateNumber = 0;
887
998
 
888
999
  const player = new Player({
889
- plugins: [plugin, new ReferenceAssetsPlugin()],
1000
+ plugins: [plugin, new TestAsyncPlugin()],
890
1001
  });
891
1002
 
892
1003
  player.hooks.viewController.tap("async-node-test", (vc) => {
@@ -953,7 +1064,7 @@ describe("view", () => {
953
1064
 
954
1065
  let updateNumber = 0;
955
1066
 
956
- const plugins = [plugin, new ReferenceAssetsPlugin()];
1067
+ const plugins = [plugin, new TestAsyncPlugin()];
957
1068
 
958
1069
  const player = new Player({
959
1070
  plugins: plugins,
@@ -1051,7 +1162,7 @@ describe("view", () => {
1051
1162
 
1052
1163
  const checkPathPlugin = new CheckPathPlugin();
1053
1164
 
1054
- const plugins = [plugin, new ReferenceAssetsPlugin(), checkPathPlugin];
1165
+ const plugins = [plugin, new TestAsyncPlugin(), checkPathPlugin];
1055
1166
 
1056
1167
  const player = new Player({
1057
1168
  plugins: plugins,
@@ -1240,6 +1351,93 @@ describe("view", () => {
1240
1351
  ],
1241
1352
  });
1242
1353
  });
1354
+
1355
+ describe("multi-view cache", () => {
1356
+ let asyncNodePlugin: AsyncNodePlugin = new AsyncNodePlugin({});
1357
+ const deferHandler = vi.fn();
1358
+ let deferredResolve = (value: any) => {};
1359
+
1360
+ beforeEach(() => {
1361
+ deferHandler.mockReset();
1362
+ deferHandler.mockImplementation(
1363
+ () =>
1364
+ new Promise((res) => {
1365
+ deferredResolve = res;
1366
+ }),
1367
+ );
1368
+
1369
+ asyncNodePlugin = new AsyncNodePlugin(
1370
+ {
1371
+ plugins: [new AsyncNodePluginPlugin()],
1372
+ },
1373
+ deferHandler,
1374
+ );
1375
+ });
1376
+
1377
+ test("clear cache between views", async () => {
1378
+ const player = new Player({ plugins: [asyncNodePlugin] });
1379
+ player.start(basicFRFWithActions as any);
1380
+
1381
+ await waitFor(() => {
1382
+ expect(deferHandler).toHaveBeenCalledOnce();
1383
+ });
1384
+
1385
+ deferredResolve({
1386
+ asset: {
1387
+ id: "async-text",
1388
+ type: "text",
1389
+ value: "Fallback Text",
1390
+ },
1391
+ });
1392
+
1393
+ await waitFor(() => {
1394
+ const playerState = player.getState();
1395
+ expect(playerState.status).toBe("in-progress");
1396
+ const inProgressState = playerState as InProgressState;
1397
+ const lastViewUpdate =
1398
+ inProgressState.controllers.view.currentView?.lastUpdate;
1399
+
1400
+ expect(lastViewUpdate?.actions[1]).toStrictEqual({
1401
+ asset: {
1402
+ id: "async-text",
1403
+ type: "text",
1404
+ value: "Fallback Text",
1405
+ },
1406
+ });
1407
+ });
1408
+
1409
+ deferHandler.mockClear();
1410
+ player.start(basicFRFWithActions as any);
1411
+
1412
+ await waitFor(() => {
1413
+ expect(deferHandler).toHaveBeenCalledOnce();
1414
+ });
1415
+
1416
+ deferredResolve({
1417
+ asset: {
1418
+ id: "async-text",
1419
+ type: "text",
1420
+ value: "New Fallback Text",
1421
+ },
1422
+ });
1423
+
1424
+ await waitFor(() => {
1425
+ const playerState = player.getState();
1426
+ expect(playerState.status).toBe("in-progress");
1427
+ const inProgressState = playerState as InProgressState;
1428
+ const lastViewUpdate =
1429
+ inProgressState.controllers.view.currentView?.lastUpdate;
1430
+
1431
+ expect(lastViewUpdate?.actions[1]).toStrictEqual({
1432
+ asset: {
1433
+ id: "async-text",
1434
+ type: "text",
1435
+ value: "New Fallback Text",
1436
+ },
1437
+ });
1438
+ });
1439
+ });
1440
+ });
1243
1441
  });
1244
1442
 
1245
1443
  describe("parser", () => {
package/src/index.ts CHANGED
@@ -11,13 +11,23 @@ import type {
11
11
  Resolver,
12
12
  Resolve,
13
13
  } from "@player-ui/player";
14
- import { AsyncParallelBailHook } from "tapable-ts";
14
+ import { AsyncParallelBailHook, SyncBailHook } from "tapable-ts";
15
15
  import queueMicrotask from "queue-microtask";
16
16
  import { omit } from "timm";
17
17
 
18
18
  export * from "./types";
19
19
  export * from "./transform";
20
20
 
21
+ /** Object type for storing data related to a single `apply` of the `AsyncNodePluginPlugin`
22
+ * This object should be setup once per ViewInstance to keep any cached info just for that view to avoid conflicts of shared async node ids across different view states.
23
+ */
24
+ type AsyncPluginContext = {
25
+ /** Map of async node id to resolved content */
26
+ nodeResolveCache: Map<string, any>;
27
+ /** The view instance this context is attached to. */
28
+ view: ViewInstance;
29
+ };
30
+
21
31
  export interface AsyncNodePluginOptions {
22
32
  /** A set of plugins to load */
23
33
  plugins?: AsyncNodeViewPlugin[];
@@ -34,12 +44,21 @@ export type AsyncHandler = (
34
44
  callback?: (result: any) => void,
35
45
  ) => Promise<any>;
36
46
 
47
+ /** Hook declaration for the AsyncNodePlugin */
48
+ type AsyncNodeHooks = {
49
+ /** Async hook to get content for an async node */
50
+ onAsyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any>;
51
+ /** Sync hook to manage errors coming from the onAsyncNode hook. Return a fallback node or null to render a fallback. The first argument of passed in the call is the error thrown. */
52
+ onAsyncNodeError: SyncBailHook<[Error, Node.Async], any>;
53
+ };
54
+
37
55
  /**
38
56
  * Async node plugin used to resolve async nodes in the content
39
57
  * If an async node is present, allow users to provide a replacement node to be rendered when ready
40
58
  */
41
59
  export class AsyncNodePlugin implements PlayerPlugin {
42
60
  private plugins: AsyncNodeViewPlugin[] | undefined;
61
+ private playerInstance: Player | undefined;
43
62
 
44
63
  constructor(options: AsyncNodePluginOptions, asyncHandler?: AsyncHandler) {
45
64
  if (options?.plugins) {
@@ -59,16 +78,20 @@ export class AsyncNodePlugin implements PlayerPlugin {
59
78
  }
60
79
  }
61
80
 
62
- public readonly hooks = {
63
- onAsyncNode: new AsyncParallelBailHook<
64
- [Node.Async, (result: any) => void],
65
- any
66
- >(),
81
+ public readonly hooks: AsyncNodeHooks = {
82
+ onAsyncNode: new AsyncParallelBailHook(),
83
+ onAsyncNodeError: new SyncBailHook(),
67
84
  };
68
85
 
86
+ getPlayerInstance(): Player | undefined {
87
+ return this.playerInstance;
88
+ }
89
+
69
90
  name = "AsyncNode";
70
91
 
71
- apply(player: Player) {
92
+ apply(player: Player): void {
93
+ this.playerInstance = player;
94
+
72
95
  player.hooks.viewController.tap(this.name, (viewController) => {
73
96
  viewController.hooks.view.tap(this.name, (view) => {
74
97
  this.plugins?.forEach((plugin) => {
@@ -80,40 +103,51 @@ export class AsyncNodePlugin implements PlayerPlugin {
80
103
  }
81
104
 
82
105
  export class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
83
- public asyncNode = new AsyncParallelBailHook<
106
+ public asyncNode: AsyncParallelBailHook<
84
107
  [Node.Async, (result: any) => void],
85
108
  any
86
- >();
109
+ > = new AsyncParallelBailHook();
87
110
  private basePlugin: AsyncNodePlugin | undefined;
88
111
 
89
112
  name = "AsyncNode";
90
113
 
91
- private resolvedMapping = new Map<string, any>();
92
-
93
- private currentView: ViewInstance | undefined;
94
-
95
114
  /**
96
- * Updates the node asynchronously based on the result provided.
97
- * This method is responsible for handling the update logic of asynchronous nodes.
98
- * It checks if the node needs to be updated based on the new result and updates the mapping accordingly.
99
- * If an update is necessary, it triggers an asynchronous update on the view.
115
+ * Parses the node from the result and triggers an asynchronous view update if necessary.
100
116
  * @param node The asynchronous node that might be updated.
101
117
  * @param result The result obtained from resolving the async node. This could be any data structure or value.
102
118
  * @param options Options provided for node resolution, including a potential parseNode function to process the result.
103
119
  * @param view The view instance where the node resides. This can be undefined if the view is not currently active.
104
120
  */
105
- private handleAsyncUpdate(
121
+ private parseNodeAndUpdate(
106
122
  node: Node.Async,
123
+ context: AsyncPluginContext,
107
124
  result: any,
108
125
  options: Resolve.NodeResolveOptions,
109
- view: ViewInstance | undefined,
110
126
  ) {
111
127
  const parsedNode =
112
128
  options.parseNode && result ? options.parseNode(result) : undefined;
113
129
 
114
- if (this.resolvedMapping.get(node.id) !== parsedNode) {
115
- this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);
116
- view?.updateAsync();
130
+ this.handleAsyncUpdate(node, context, parsedNode);
131
+ }
132
+
133
+ /**
134
+ * Updates the node asynchronously based on the result provided.
135
+ * This method is responsible for handling the update logic of asynchronous nodes.
136
+ * It checks if the node needs to be updated based on the new result and updates the mapping accordingly.
137
+ * If an update is necessary, it triggers an asynchronous update on the view.
138
+ * @param node The asynchronous node that might be updated.
139
+ * @param newNode The new node to replace the async node.
140
+ * @param view The view instance where the node resides. This can be undefined if the view is not currently active.
141
+ */
142
+ private handleAsyncUpdate(
143
+ node: Node.Async,
144
+ context: AsyncPluginContext,
145
+ newNode?: Node.Node | null,
146
+ ) {
147
+ const { nodeResolveCache, view } = context;
148
+ if (nodeResolveCache.get(node.id) !== newNode) {
149
+ nodeResolveCache.set(node.id, newNode ? newNode : node);
150
+ view.updateAsync();
117
151
  }
118
152
  }
119
153
 
@@ -123,36 +157,59 @@ export class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
123
157
  * @param resolver The resolver instance to attach the hook to.
124
158
  * @param view
125
159
  */
126
- applyResolver(resolver: Resolver) {
160
+ applyResolver(resolver: Resolver, context: AsyncPluginContext): void {
127
161
  resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
128
- let resolvedNode;
129
- if (this.isAsync(node)) {
130
- const mappedValue = this.resolvedMapping.get(node.id);
131
- if (mappedValue) {
132
- resolvedNode = mappedValue;
133
- }
134
- } else {
135
- resolvedNode = null;
162
+ if (!this.isAsync(node)) {
163
+ return node;
136
164
  }
137
165
 
138
- const newNode = resolvedNode || node;
139
- if (!resolvedNode && node?.type === NodeType.Async) {
140
- queueMicrotask(async () => {
141
- const result = await this.basePlugin?.hooks.onAsyncNode.call(
142
- node,
143
- (result) => {
144
- this.handleAsyncUpdate(node, result, options, this.currentView);
145
- },
146
- );
147
- this.handleAsyncUpdate(node, result, options, this.currentView);
148
- });
149
-
150
- return node;
166
+ const resolvedNode = context.nodeResolveCache.get(node.id);
167
+ if (resolvedNode !== undefined) {
168
+ return resolvedNode;
151
169
  }
152
- return newNode;
170
+
171
+ queueMicrotask(() => this.runAsyncNode(node, context, options));
172
+
173
+ return node;
153
174
  });
154
175
  }
155
176
 
177
+ private async runAsyncNode(
178
+ node: Node.Async,
179
+ context: AsyncPluginContext,
180
+ options: Resolve.NodeResolveOptions,
181
+ ) {
182
+ try {
183
+ const result = await this.basePlugin?.hooks.onAsyncNode.call(
184
+ node,
185
+ (result) => {
186
+ this.parseNodeAndUpdate(node, context, result, options);
187
+ },
188
+ );
189
+ this.parseNodeAndUpdate(node, context, result, options);
190
+ } catch (e: unknown) {
191
+ const error = e instanceof Error ? e : new Error(String(e));
192
+ const result = this.basePlugin?.hooks.onAsyncNodeError.call(error, node);
193
+
194
+ if (result === undefined) {
195
+ const playerState = this.basePlugin?.getPlayerInstance()?.getState();
196
+
197
+ if (playerState?.status === "in-progress") {
198
+ playerState.fail(error);
199
+ }
200
+
201
+ return;
202
+ }
203
+
204
+ options.logger?.error(
205
+ "Async node handling failed and resolved with a fallback. Error:",
206
+ error,
207
+ );
208
+
209
+ this.parseNodeAndUpdate(node, context, result, options);
210
+ }
211
+ }
212
+
156
213
  private isAsync(node: Node.Node | null): node is Node.Async {
157
214
  return node?.type === NodeType.Async;
158
215
  }
@@ -161,7 +218,7 @@ export class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
161
218
  return obj && Object.prototype.hasOwnProperty.call(obj, "async");
162
219
  }
163
220
 
164
- applyParser(parser: Parser) {
221
+ applyParser(parser: Parser): void {
165
222
  parser.hooks.parseNode.tap(
166
223
  this.name,
167
224
  (
@@ -209,9 +266,15 @@ export class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
209
266
  }
210
267
 
211
268
  apply(view: ViewInstance): void {
212
- this.currentView = view;
269
+ const context: AsyncPluginContext = {
270
+ nodeResolveCache: new Map<string, any>(),
271
+ view,
272
+ };
273
+
213
274
  view.hooks.parser.tap("async", this.applyParser.bind(this));
214
- view.hooks.resolver.tap("async", this.applyResolver.bind(this));
275
+ view.hooks.resolver.tap("async", (resolver) => {
276
+ this.applyResolver(resolver, context);
277
+ });
215
278
  }
216
279
 
217
280
  applyPlugin(asyncNodePlugin: AsyncNodePlugin): void {
package/types/index.d.ts CHANGED
@@ -1,7 +1,16 @@
1
1
  import type { Player, PlayerPlugin, Node, ViewInstance, Parser, ViewPlugin, Resolver } from "@player-ui/player";
2
- import { AsyncParallelBailHook } from "tapable-ts";
2
+ import { AsyncParallelBailHook, SyncBailHook } from "tapable-ts";
3
3
  export * from "./types";
4
4
  export * from "./transform";
5
+ /** Object type for storing data related to a single `apply` of the `AsyncNodePluginPlugin`
6
+ * This object should be setup once per ViewInstance to keep any cached info just for that view to avoid conflicts of shared async node ids across different view states.
7
+ */
8
+ type AsyncPluginContext = {
9
+ /** Map of async node id to resolved content */
10
+ nodeResolveCache: Map<string, any>;
11
+ /** The view instance this context is attached to. */
12
+ view: ViewInstance;
13
+ };
5
14
  export interface AsyncNodePluginOptions {
6
15
  /** A set of plugins to load */
7
16
  plugins?: AsyncNodeViewPlugin[];
@@ -12,33 +21,48 @@ export interface AsyncNodeViewPlugin extends ViewPlugin {
12
21
  asyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any>;
13
22
  }
14
23
  export type AsyncHandler = (node: Node.Async, callback?: (result: any) => void) => Promise<any>;
24
+ /** Hook declaration for the AsyncNodePlugin */
25
+ type AsyncNodeHooks = {
26
+ /** Async hook to get content for an async node */
27
+ onAsyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any>;
28
+ /** Sync hook to manage errors coming from the onAsyncNode hook. Return a fallback node or null to render a fallback. The first argument of passed in the call is the error thrown. */
29
+ onAsyncNodeError: SyncBailHook<[Error, Node.Async], any>;
30
+ };
15
31
  /**
16
32
  * Async node plugin used to resolve async nodes in the content
17
33
  * If an async node is present, allow users to provide a replacement node to be rendered when ready
18
34
  */
19
35
  export declare class AsyncNodePlugin implements PlayerPlugin {
20
36
  private plugins;
37
+ private playerInstance;
21
38
  constructor(options: AsyncNodePluginOptions, asyncHandler?: AsyncHandler);
22
- readonly hooks: {
23
- onAsyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any, Record<string, any>>;
24
- };
39
+ readonly hooks: AsyncNodeHooks;
40
+ getPlayerInstance(): Player | undefined;
25
41
  name: string;
26
42
  apply(player: Player): void;
27
43
  }
28
44
  export declare class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
29
- asyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any, Record<string, any>>;
45
+ asyncNode: AsyncParallelBailHook<[
46
+ Node.Async,
47
+ (result: any) => void
48
+ ], any>;
30
49
  private basePlugin;
31
50
  name: string;
32
- private resolvedMapping;
33
- private currentView;
51
+ /**
52
+ * Parses the node from the result and triggers an asynchronous view update if necessary.
53
+ * @param node The asynchronous node that might be updated.
54
+ * @param result The result obtained from resolving the async node. This could be any data structure or value.
55
+ * @param options Options provided for node resolution, including a potential parseNode function to process the result.
56
+ * @param view The view instance where the node resides. This can be undefined if the view is not currently active.
57
+ */
58
+ private parseNodeAndUpdate;
34
59
  /**
35
60
  * Updates the node asynchronously based on the result provided.
36
61
  * This method is responsible for handling the update logic of asynchronous nodes.
37
62
  * It checks if the node needs to be updated based on the new result and updates the mapping accordingly.
38
63
  * If an update is necessary, it triggers an asynchronous update on the view.
39
64
  * @param node The asynchronous node that might be updated.
40
- * @param result The result obtained from resolving the async node. This could be any data structure or value.
41
- * @param options Options provided for node resolution, including a potential parseNode function to process the result.
65
+ * @param newNode The new node to replace the async node.
42
66
  * @param view The view instance where the node resides. This can be undefined if the view is not currently active.
43
67
  */
44
68
  private handleAsyncUpdate;
@@ -48,7 +72,8 @@ export declare class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
48
72
  * @param resolver The resolver instance to attach the hook to.
49
73
  * @param view
50
74
  */
51
- applyResolver(resolver: Resolver): void;
75
+ applyResolver(resolver: Resolver, context: AsyncPluginContext): void;
76
+ private runAsyncNode;
52
77
  private isAsync;
53
78
  private isDeterminedAsync;
54
79
  applyParser(parser: Parser): void;