@player-ui/async-node-plugin 0.8.0-next.2 → 0.8.0-next.4

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.
@@ -67,6 +67,56 @@ var AsyncNodePluginPlugin = class {
67
67
  this.name = "AsyncNode";
68
68
  this.resolvedMapping = /* @__PURE__ */ new Map();
69
69
  }
70
+ /**
71
+ * Updates the node asynchronously based on the result provided.
72
+ * This method is responsible for handling the update logic of asynchronous nodes.
73
+ * It checks if the node needs to be updated based on the new result and updates the mapping accordingly.
74
+ * If an update is necessary, it triggers an asynchronous update on the view.
75
+ * @param node The asynchronous node that might be updated.
76
+ * @param result The result obtained from resolving the async node. This could be any data structure or value.
77
+ * @param options Options provided for node resolution, including a potential parseNode function to process the result.
78
+ * @param view The view instance where the node resides. This can be undefined if the view is not currently active.
79
+ */
80
+ handleAsyncUpdate(node, result, options, view) {
81
+ const parsedNode = options.parseNode && result ? options.parseNode(result) : void 0;
82
+ if (this.resolvedMapping.get(node.id) !== parsedNode) {
83
+ this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);
84
+ view?.updateAsync();
85
+ }
86
+ }
87
+ /**
88
+ * Handles the asynchronous API integration for resolving nodes.
89
+ * This method sets up a hook on the resolver's `beforeResolve` event to process async nodes.
90
+ * @param resolver The resolver instance to attach the hook to.
91
+ * @param view
92
+ */
93
+ applyResolver(resolver) {
94
+ resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
95
+ let resolvedNode;
96
+ if (this.isAsync(node)) {
97
+ const mappedValue = this.resolvedMapping.get(node.id);
98
+ if (mappedValue) {
99
+ resolvedNode = mappedValue;
100
+ }
101
+ } else {
102
+ resolvedNode = null;
103
+ }
104
+ const newNode = resolvedNode || node;
105
+ if (!resolvedNode && node?.type === import_player.NodeType.Async) {
106
+ (0, import_queue_microtask.default)(async () => {
107
+ const result = await this.basePlugin?.hooks.onAsyncNode.call(
108
+ node,
109
+ (result2) => {
110
+ this.handleAsyncUpdate(node, result2, options, this.currentView);
111
+ }
112
+ );
113
+ this.handleAsyncUpdate(node, result, options, this.currentView);
114
+ });
115
+ return node;
116
+ }
117
+ return newNode;
118
+ });
119
+ }
70
120
  isAsync(node) {
71
121
  return node?.type === import_player.NodeType.Async;
72
122
  }
@@ -101,58 +151,10 @@ var AsyncNodePluginPlugin = class {
101
151
  }
102
152
  );
103
153
  }
104
- applyResolverHooks(resolver) {
105
- resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
106
- let resolvedNode;
107
- if (this.isAsync(node)) {
108
- const mappedValue = this.resolvedMapping.get(node.id);
109
- if (mappedValue) {
110
- resolvedNode = mappedValue;
111
- }
112
- } else {
113
- resolvedNode = null;
114
- }
115
- const newNode = resolvedNode || node;
116
- if (!resolvedNode && node?.type === import_player.NodeType.Async) {
117
- (0, import_queue_microtask.default)(async () => {
118
- const result = await this.basePlugin?.hooks.onAsyncNode.call(node);
119
- const parsedNode = options.parseNode && result ? options.parseNode(result) : void 0;
120
- if (parsedNode) {
121
- this.resolvedMapping.set(node.id, parsedNode);
122
- this.currentView?.updateAsync();
123
- }
124
- });
125
- return node;
126
- }
127
- return newNode;
128
- });
129
- }
130
154
  apply(view) {
131
- view.hooks.parser.tap("template", this.applyParser.bind(this));
132
- view.hooks.resolver.tap("template", (resolver) => {
133
- resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
134
- let resolvedNode;
135
- if (this.isAsync(node)) {
136
- const mappedValue = this.resolvedMapping.get(node.id);
137
- if (mappedValue) {
138
- resolvedNode = mappedValue;
139
- }
140
- } else {
141
- resolvedNode = null;
142
- }
143
- const newNode = resolvedNode || node;
144
- if (!resolvedNode && node?.type === import_player.NodeType.Async) {
145
- (0, import_queue_microtask.default)(async () => {
146
- const result = await this.basePlugin?.hooks.onAsyncNode.call(node);
147
- const parsedNode = options.parseNode && result ? options.parseNode(result) : void 0;
148
- this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);
149
- view.updateAsync();
150
- });
151
- return node;
152
- }
153
- return newNode;
154
- });
155
- });
155
+ this.currentView = view;
156
+ view.hooks.parser.tap("async", this.applyParser.bind(this));
157
+ view.hooks.resolver.tap("async", this.applyResolver.bind(this));
156
158
  }
157
159
  applyPlugin(asyncNodePlugin) {
158
160
  this.basePlugin = asyncNodePlugin;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts"],"sourcesContent":["import { NodeType, getNodeID } from \"@player-ui/player\";\nimport type {\n Player,\n PlayerPlugin,\n Node,\n ParseObjectOptions,\n ViewInstance,\n Parser,\n ViewPlugin,\n Resolver,\n} from \"@player-ui/player\";\nimport { AsyncParallelBailHook } from \"tapable-ts\";\nimport queueMicrotask from \"queue-microtask\";\nimport { omit } from \"timm\";\n\nexport * from \"./types\";\n\nexport interface AsyncNodePluginOptions {\n /** A set of plugins to load */\n plugins?: AsyncNodeViewPlugin[];\n}\n\nexport interface AsyncNodeViewPlugin extends ViewPlugin {\n /** Use this to tap into the async node plugin hooks */\n applyPlugin: (asyncNodePlugin: AsyncNodePlugin) => void;\n\n asyncNode: AsyncParallelBailHook<[Node.Async], any>;\n}\n\n/**\n * Async node plugin used to resolve async nodes in the content\n * If an async node is present, allow users to provide a replacement node to be rendered when ready\n */\nexport class AsyncNodePlugin implements PlayerPlugin {\n private plugins: AsyncNodeViewPlugin[] | undefined;\n\n constructor(options: AsyncNodePluginOptions) {\n if (options?.plugins) {\n this.plugins = options.plugins;\n options.plugins.forEach((plugin) => {\n plugin.applyPlugin(this);\n });\n }\n }\n\n public readonly hooks = {\n onAsyncNode: new AsyncParallelBailHook<[Node.Async], any>(),\n };\n\n name = \"AsyncNode\";\n\n apply(player: Player) {\n player.hooks.viewController.tap(this.name, (viewController) => {\n viewController.hooks.view.tap(this.name, (view) => {\n this.plugins?.forEach((plugin) => {\n plugin.apply(view);\n });\n });\n });\n }\n}\n\nexport class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {\n public asyncNode = new AsyncParallelBailHook<[Node.Async], any>();\n private basePlugin: AsyncNodePlugin | undefined;\n\n name = \"AsyncNode\";\n\n private resolvedMapping = new Map<string, any>();\n\n private currentView: ViewInstance | undefined;\n\n private isAsync(node: Node.Node | null): node is Node.Async {\n return node?.type === NodeType.Async;\n }\n\n applyParser(parser: Parser) {\n parser.hooks.determineNodeType.tap(this.name, (obj) => {\n if (Object.prototype.hasOwnProperty.call(obj, \"async\")) {\n return NodeType.Async;\n }\n });\n parser.hooks.parseNode.tap(\n this.name,\n (\n obj: any,\n nodeType: Node.ChildrenTypes,\n options: ParseObjectOptions,\n determinedNodeType: null | NodeType,\n ) => {\n if (determinedNodeType === NodeType.Async) {\n const parsedAsync = parser.parseObject(\n omit(obj, \"async\"),\n nodeType,\n options,\n );\n const parsedNodeId = getNodeID(parsedAsync);\n if (parsedAsync !== null && parsedNodeId) {\n return parser.createASTNode(\n {\n id: parsedNodeId,\n type: NodeType.Async,\n value: parsedAsync,\n },\n obj,\n );\n }\n\n return null;\n }\n },\n );\n }\n\n applyResolverHooks(resolver: Resolver) {\n resolver.hooks.beforeResolve.tap(this.name, (node, options) => {\n let resolvedNode;\n if (this.isAsync(node)) {\n const mappedValue = this.resolvedMapping.get(node.id);\n if (mappedValue) {\n resolvedNode = mappedValue;\n }\n } else {\n resolvedNode = null;\n }\n\n const newNode = resolvedNode || node;\n if (!resolvedNode && node?.type === NodeType.Async) {\n queueMicrotask(async () => {\n const result = await this.basePlugin?.hooks.onAsyncNode.call(node);\n const parsedNode =\n options.parseNode && result ? options.parseNode(result) : undefined;\n\n if (parsedNode) {\n this.resolvedMapping.set(node.id, parsedNode);\n this.currentView?.updateAsync();\n }\n });\n\n return node;\n }\n\n return newNode;\n });\n }\n\n apply(view: ViewInstance): void {\n view.hooks.parser.tap(\"template\", this.applyParser.bind(this));\n view.hooks.resolver.tap(\"template\", (resolver) => {\n resolver.hooks.beforeResolve.tap(this.name, (node, options) => {\n let resolvedNode;\n if (this.isAsync(node)) {\n const mappedValue = this.resolvedMapping.get(node.id);\n if (mappedValue) {\n resolvedNode = mappedValue;\n }\n } else {\n resolvedNode = null;\n }\n\n const newNode = resolvedNode || node;\n if (!resolvedNode && node?.type === NodeType.Async) {\n queueMicrotask(async () => {\n const result = await this.basePlugin?.hooks.onAsyncNode.call(node);\n const parsedNode =\n options.parseNode && result\n ? options.parseNode(result)\n : undefined;\n\n this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);\n view.updateAsync();\n });\n\n return node;\n }\n\n return newNode;\n });\n });\n }\n\n applyPlugin(asyncNodePlugin: AsyncNodePlugin): void {\n this.basePlugin = asyncNodePlugin;\n }\n}\n"],"mappings":"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","names":["queueMicrotask"]}
1
+ {"version":3,"sources":["../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts"],"sourcesContent":["import { NodeType, getNodeID } from \"@player-ui/player\";\nimport type {\n Player,\n PlayerPlugin,\n Node,\n ParseObjectOptions,\n ViewInstance,\n Parser,\n ViewPlugin,\n Resolver,\n Resolve,\n} from \"@player-ui/player\";\nimport { AsyncParallelBailHook } from \"tapable-ts\";\nimport queueMicrotask from \"queue-microtask\";\nimport { omit } from \"timm\";\n\nexport * from \"./types\";\n\nexport interface AsyncNodePluginOptions {\n /** A set of plugins to load */\n plugins?: AsyncNodeViewPlugin[];\n}\n\nexport interface AsyncNodeViewPlugin extends ViewPlugin {\n /** Use this to tap into the async node plugin hooks */\n applyPlugin: (asyncNodePlugin: AsyncNodePlugin) => void;\n\n asyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any>;\n}\n\n/**\n * Async node plugin used to resolve async nodes in the content\n * If an async node is present, allow users to provide a replacement node to be rendered when ready\n */\nexport class AsyncNodePlugin implements PlayerPlugin {\n private plugins: AsyncNodeViewPlugin[] | undefined;\n\n constructor(options: AsyncNodePluginOptions) {\n if (options?.plugins) {\n this.plugins = options.plugins;\n options.plugins.forEach((plugin) => {\n plugin.applyPlugin(this);\n });\n }\n }\n\n public readonly hooks = {\n onAsyncNode: new AsyncParallelBailHook<\n [Node.Async, (result: any) => void],\n any\n >(),\n };\n\n name = \"AsyncNode\";\n\n apply(player: Player) {\n player.hooks.viewController.tap(this.name, (viewController) => {\n viewController.hooks.view.tap(this.name, (view) => {\n this.plugins?.forEach((plugin) => {\n plugin.apply(view);\n });\n });\n });\n }\n}\n\nexport class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {\n public asyncNode = new AsyncParallelBailHook<\n [Node.Async, (result: any) => void],\n any\n >();\n private basePlugin: AsyncNodePlugin | undefined;\n\n name = \"AsyncNode\";\n\n private resolvedMapping = new Map<string, any>();\n\n private currentView: ViewInstance | undefined;\n\n /**\n * Updates the node asynchronously based on the result provided.\n * This method is responsible for handling the update logic of asynchronous nodes.\n * It checks if the node needs to be updated based on the new result and updates the mapping accordingly.\n * If an update is necessary, it triggers an asynchronous update on the view.\n * @param node The asynchronous node that might be updated.\n * @param result The result obtained from resolving the async node. This could be any data structure or value.\n * @param options Options provided for node resolution, including a potential parseNode function to process the result.\n * @param view The view instance where the node resides. This can be undefined if the view is not currently active.\n */\n private handleAsyncUpdate(\n node: Node.Async,\n result: any,\n options: Resolve.NodeResolveOptions,\n view: ViewInstance | undefined,\n ) {\n const parsedNode =\n options.parseNode && result ? options.parseNode(result) : undefined;\n\n if (this.resolvedMapping.get(node.id) !== parsedNode) {\n this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);\n view?.updateAsync();\n }\n }\n\n /**\n * Handles the asynchronous API integration for resolving nodes.\n * This method sets up a hook on the resolver's `beforeResolve` event to process async nodes.\n * @param resolver The resolver instance to attach the hook to.\n * @param view\n */\n applyResolver(resolver: Resolver) {\n resolver.hooks.beforeResolve.tap(this.name, (node, options) => {\n let resolvedNode;\n if (this.isAsync(node)) {\n const mappedValue = this.resolvedMapping.get(node.id);\n if (mappedValue) {\n resolvedNode = mappedValue;\n }\n } else {\n resolvedNode = null;\n }\n\n const newNode = resolvedNode || node;\n if (!resolvedNode && node?.type === NodeType.Async) {\n queueMicrotask(async () => {\n const result = await this.basePlugin?.hooks.onAsyncNode.call(\n node,\n (result) => {\n this.handleAsyncUpdate(node, result, options, this.currentView);\n },\n );\n this.handleAsyncUpdate(node, result, options, this.currentView);\n });\n\n return node;\n }\n return newNode;\n });\n }\n\n private isAsync(node: Node.Node | null): node is Node.Async {\n return node?.type === NodeType.Async;\n }\n\n applyParser(parser: Parser) {\n parser.hooks.determineNodeType.tap(this.name, (obj) => {\n if (Object.prototype.hasOwnProperty.call(obj, \"async\")) {\n return NodeType.Async;\n }\n });\n parser.hooks.parseNode.tap(\n this.name,\n (\n obj: any,\n nodeType: Node.ChildrenTypes,\n options: ParseObjectOptions,\n determinedNodeType: null | NodeType,\n ) => {\n if (determinedNodeType === NodeType.Async) {\n const parsedAsync = parser.parseObject(\n omit(obj, \"async\"),\n nodeType,\n options,\n );\n const parsedNodeId = getNodeID(parsedAsync);\n if (parsedAsync !== null && parsedNodeId) {\n return parser.createASTNode(\n {\n id: parsedNodeId,\n type: NodeType.Async,\n value: parsedAsync,\n },\n obj,\n );\n }\n\n return null;\n }\n },\n );\n }\n\n apply(view: ViewInstance): void {\n this.currentView = view;\n view.hooks.parser.tap(\"async\", this.applyParser.bind(this));\n view.hooks.resolver.tap(\"async\", this.applyResolver.bind(this));\n }\n\n applyPlugin(asyncNodePlugin: AsyncNodePlugin): void {\n this.basePlugin = asyncNodePlugin;\n }\n}\n"],"mappings":"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","names":["queueMicrotask","result"]}
@@ -32,6 +32,56 @@ var AsyncNodePluginPlugin = class {
32
32
  this.name = "AsyncNode";
33
33
  this.resolvedMapping = /* @__PURE__ */ new Map();
34
34
  }
35
+ /**
36
+ * Updates the node asynchronously based on the result provided.
37
+ * This method is responsible for handling the update logic of asynchronous nodes.
38
+ * It checks if the node needs to be updated based on the new result and updates the mapping accordingly.
39
+ * If an update is necessary, it triggers an asynchronous update on the view.
40
+ * @param node The asynchronous node that might be updated.
41
+ * @param result The result obtained from resolving the async node. This could be any data structure or value.
42
+ * @param options Options provided for node resolution, including a potential parseNode function to process the result.
43
+ * @param view The view instance where the node resides. This can be undefined if the view is not currently active.
44
+ */
45
+ handleAsyncUpdate(node, result, options, view) {
46
+ const parsedNode = options.parseNode && result ? options.parseNode(result) : void 0;
47
+ if (this.resolvedMapping.get(node.id) !== parsedNode) {
48
+ this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);
49
+ view?.updateAsync();
50
+ }
51
+ }
52
+ /**
53
+ * Handles the asynchronous API integration for resolving nodes.
54
+ * This method sets up a hook on the resolver's `beforeResolve` event to process async nodes.
55
+ * @param resolver The resolver instance to attach the hook to.
56
+ * @param view
57
+ */
58
+ applyResolver(resolver) {
59
+ resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
60
+ let resolvedNode;
61
+ if (this.isAsync(node)) {
62
+ const mappedValue = this.resolvedMapping.get(node.id);
63
+ if (mappedValue) {
64
+ resolvedNode = mappedValue;
65
+ }
66
+ } else {
67
+ resolvedNode = null;
68
+ }
69
+ const newNode = resolvedNode || node;
70
+ if (!resolvedNode && node?.type === NodeType.Async) {
71
+ queueMicrotask(async () => {
72
+ const result = await this.basePlugin?.hooks.onAsyncNode.call(
73
+ node,
74
+ (result2) => {
75
+ this.handleAsyncUpdate(node, result2, options, this.currentView);
76
+ }
77
+ );
78
+ this.handleAsyncUpdate(node, result, options, this.currentView);
79
+ });
80
+ return node;
81
+ }
82
+ return newNode;
83
+ });
84
+ }
35
85
  isAsync(node) {
36
86
  return node?.type === NodeType.Async;
37
87
  }
@@ -66,58 +116,10 @@ var AsyncNodePluginPlugin = class {
66
116
  }
67
117
  );
68
118
  }
69
- applyResolverHooks(resolver) {
70
- resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
71
- let resolvedNode;
72
- if (this.isAsync(node)) {
73
- const mappedValue = this.resolvedMapping.get(node.id);
74
- if (mappedValue) {
75
- resolvedNode = mappedValue;
76
- }
77
- } else {
78
- resolvedNode = null;
79
- }
80
- const newNode = resolvedNode || node;
81
- if (!resolvedNode && node?.type === NodeType.Async) {
82
- queueMicrotask(async () => {
83
- const result = await this.basePlugin?.hooks.onAsyncNode.call(node);
84
- const parsedNode = options.parseNode && result ? options.parseNode(result) : void 0;
85
- if (parsedNode) {
86
- this.resolvedMapping.set(node.id, parsedNode);
87
- this.currentView?.updateAsync();
88
- }
89
- });
90
- return node;
91
- }
92
- return newNode;
93
- });
94
- }
95
119
  apply(view) {
96
- view.hooks.parser.tap("template", this.applyParser.bind(this));
97
- view.hooks.resolver.tap("template", (resolver) => {
98
- resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
99
- let resolvedNode;
100
- if (this.isAsync(node)) {
101
- const mappedValue = this.resolvedMapping.get(node.id);
102
- if (mappedValue) {
103
- resolvedNode = mappedValue;
104
- }
105
- } else {
106
- resolvedNode = null;
107
- }
108
- const newNode = resolvedNode || node;
109
- if (!resolvedNode && node?.type === NodeType.Async) {
110
- queueMicrotask(async () => {
111
- const result = await this.basePlugin?.hooks.onAsyncNode.call(node);
112
- const parsedNode = options.parseNode && result ? options.parseNode(result) : void 0;
113
- this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);
114
- view.updateAsync();
115
- });
116
- return node;
117
- }
118
- return newNode;
119
- });
120
- });
120
+ this.currentView = view;
121
+ view.hooks.parser.tap("async", this.applyParser.bind(this));
122
+ view.hooks.resolver.tap("async", this.applyResolver.bind(this));
121
123
  }
122
124
  applyPlugin(asyncNodePlugin) {
123
125
  this.basePlugin = asyncNodePlugin;
package/dist/index.mjs CHANGED
@@ -32,6 +32,56 @@ var AsyncNodePluginPlugin = class {
32
32
  this.name = "AsyncNode";
33
33
  this.resolvedMapping = /* @__PURE__ */ new Map();
34
34
  }
35
+ /**
36
+ * Updates the node asynchronously based on the result provided.
37
+ * This method is responsible for handling the update logic of asynchronous nodes.
38
+ * It checks if the node needs to be updated based on the new result and updates the mapping accordingly.
39
+ * If an update is necessary, it triggers an asynchronous update on the view.
40
+ * @param node The asynchronous node that might be updated.
41
+ * @param result The result obtained from resolving the async node. This could be any data structure or value.
42
+ * @param options Options provided for node resolution, including a potential parseNode function to process the result.
43
+ * @param view The view instance where the node resides. This can be undefined if the view is not currently active.
44
+ */
45
+ handleAsyncUpdate(node, result, options, view) {
46
+ const parsedNode = options.parseNode && result ? options.parseNode(result) : void 0;
47
+ if (this.resolvedMapping.get(node.id) !== parsedNode) {
48
+ this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);
49
+ view?.updateAsync();
50
+ }
51
+ }
52
+ /**
53
+ * Handles the asynchronous API integration for resolving nodes.
54
+ * This method sets up a hook on the resolver's `beforeResolve` event to process async nodes.
55
+ * @param resolver The resolver instance to attach the hook to.
56
+ * @param view
57
+ */
58
+ applyResolver(resolver) {
59
+ resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
60
+ let resolvedNode;
61
+ if (this.isAsync(node)) {
62
+ const mappedValue = this.resolvedMapping.get(node.id);
63
+ if (mappedValue) {
64
+ resolvedNode = mappedValue;
65
+ }
66
+ } else {
67
+ resolvedNode = null;
68
+ }
69
+ const newNode = resolvedNode || node;
70
+ if (!resolvedNode && node?.type === NodeType.Async) {
71
+ queueMicrotask(async () => {
72
+ const result = await this.basePlugin?.hooks.onAsyncNode.call(
73
+ node,
74
+ (result2) => {
75
+ this.handleAsyncUpdate(node, result2, options, this.currentView);
76
+ }
77
+ );
78
+ this.handleAsyncUpdate(node, result, options, this.currentView);
79
+ });
80
+ return node;
81
+ }
82
+ return newNode;
83
+ });
84
+ }
35
85
  isAsync(node) {
36
86
  return node?.type === NodeType.Async;
37
87
  }
@@ -66,58 +116,10 @@ var AsyncNodePluginPlugin = class {
66
116
  }
67
117
  );
68
118
  }
69
- applyResolverHooks(resolver) {
70
- resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
71
- let resolvedNode;
72
- if (this.isAsync(node)) {
73
- const mappedValue = this.resolvedMapping.get(node.id);
74
- if (mappedValue) {
75
- resolvedNode = mappedValue;
76
- }
77
- } else {
78
- resolvedNode = null;
79
- }
80
- const newNode = resolvedNode || node;
81
- if (!resolvedNode && node?.type === NodeType.Async) {
82
- queueMicrotask(async () => {
83
- const result = await this.basePlugin?.hooks.onAsyncNode.call(node);
84
- const parsedNode = options.parseNode && result ? options.parseNode(result) : void 0;
85
- if (parsedNode) {
86
- this.resolvedMapping.set(node.id, parsedNode);
87
- this.currentView?.updateAsync();
88
- }
89
- });
90
- return node;
91
- }
92
- return newNode;
93
- });
94
- }
95
119
  apply(view) {
96
- view.hooks.parser.tap("template", this.applyParser.bind(this));
97
- view.hooks.resolver.tap("template", (resolver) => {
98
- resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
99
- let resolvedNode;
100
- if (this.isAsync(node)) {
101
- const mappedValue = this.resolvedMapping.get(node.id);
102
- if (mappedValue) {
103
- resolvedNode = mappedValue;
104
- }
105
- } else {
106
- resolvedNode = null;
107
- }
108
- const newNode = resolvedNode || node;
109
- if (!resolvedNode && node?.type === NodeType.Async) {
110
- queueMicrotask(async () => {
111
- const result = await this.basePlugin?.hooks.onAsyncNode.call(node);
112
- const parsedNode = options.parseNode && result ? options.parseNode(result) : void 0;
113
- this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);
114
- view.updateAsync();
115
- });
116
- return node;
117
- }
118
- return newNode;
119
- });
120
- });
120
+ this.currentView = view;
121
+ view.hooks.parser.tap("async", this.applyParser.bind(this));
122
+ view.hooks.resolver.tap("async", this.applyResolver.bind(this));
121
123
  }
122
124
  applyPlugin(asyncNodePlugin) {
123
125
  this.basePlugin = asyncNodePlugin;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts"],"sourcesContent":["import { NodeType, getNodeID } from \"@player-ui/player\";\nimport type {\n Player,\n PlayerPlugin,\n Node,\n ParseObjectOptions,\n ViewInstance,\n Parser,\n ViewPlugin,\n Resolver,\n} from \"@player-ui/player\";\nimport { AsyncParallelBailHook } from \"tapable-ts\";\nimport queueMicrotask from \"queue-microtask\";\nimport { omit } from \"timm\";\n\nexport * from \"./types\";\n\nexport interface AsyncNodePluginOptions {\n /** A set of plugins to load */\n plugins?: AsyncNodeViewPlugin[];\n}\n\nexport interface AsyncNodeViewPlugin extends ViewPlugin {\n /** Use this to tap into the async node plugin hooks */\n applyPlugin: (asyncNodePlugin: AsyncNodePlugin) => void;\n\n asyncNode: AsyncParallelBailHook<[Node.Async], any>;\n}\n\n/**\n * Async node plugin used to resolve async nodes in the content\n * If an async node is present, allow users to provide a replacement node to be rendered when ready\n */\nexport class AsyncNodePlugin implements PlayerPlugin {\n private plugins: AsyncNodeViewPlugin[] | undefined;\n\n constructor(options: AsyncNodePluginOptions) {\n if (options?.plugins) {\n this.plugins = options.plugins;\n options.plugins.forEach((plugin) => {\n plugin.applyPlugin(this);\n });\n }\n }\n\n public readonly hooks = {\n onAsyncNode: new AsyncParallelBailHook<[Node.Async], any>(),\n };\n\n name = \"AsyncNode\";\n\n apply(player: Player) {\n player.hooks.viewController.tap(this.name, (viewController) => {\n viewController.hooks.view.tap(this.name, (view) => {\n this.plugins?.forEach((plugin) => {\n plugin.apply(view);\n });\n });\n });\n }\n}\n\nexport class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {\n public asyncNode = new AsyncParallelBailHook<[Node.Async], any>();\n private basePlugin: AsyncNodePlugin | undefined;\n\n name = \"AsyncNode\";\n\n private resolvedMapping = new Map<string, any>();\n\n private currentView: ViewInstance | undefined;\n\n private isAsync(node: Node.Node | null): node is Node.Async {\n return node?.type === NodeType.Async;\n }\n\n applyParser(parser: Parser) {\n parser.hooks.determineNodeType.tap(this.name, (obj) => {\n if (Object.prototype.hasOwnProperty.call(obj, \"async\")) {\n return NodeType.Async;\n }\n });\n parser.hooks.parseNode.tap(\n this.name,\n (\n obj: any,\n nodeType: Node.ChildrenTypes,\n options: ParseObjectOptions,\n determinedNodeType: null | NodeType,\n ) => {\n if (determinedNodeType === NodeType.Async) {\n const parsedAsync = parser.parseObject(\n omit(obj, \"async\"),\n nodeType,\n options,\n );\n const parsedNodeId = getNodeID(parsedAsync);\n if (parsedAsync !== null && parsedNodeId) {\n return parser.createASTNode(\n {\n id: parsedNodeId,\n type: NodeType.Async,\n value: parsedAsync,\n },\n obj,\n );\n }\n\n return null;\n }\n },\n );\n }\n\n applyResolverHooks(resolver: Resolver) {\n resolver.hooks.beforeResolve.tap(this.name, (node, options) => {\n let resolvedNode;\n if (this.isAsync(node)) {\n const mappedValue = this.resolvedMapping.get(node.id);\n if (mappedValue) {\n resolvedNode = mappedValue;\n }\n } else {\n resolvedNode = null;\n }\n\n const newNode = resolvedNode || node;\n if (!resolvedNode && node?.type === NodeType.Async) {\n queueMicrotask(async () => {\n const result = await this.basePlugin?.hooks.onAsyncNode.call(node);\n const parsedNode =\n options.parseNode && result ? options.parseNode(result) : undefined;\n\n if (parsedNode) {\n this.resolvedMapping.set(node.id, parsedNode);\n this.currentView?.updateAsync();\n }\n });\n\n return node;\n }\n\n return newNode;\n });\n }\n\n apply(view: ViewInstance): void {\n view.hooks.parser.tap(\"template\", this.applyParser.bind(this));\n view.hooks.resolver.tap(\"template\", (resolver) => {\n resolver.hooks.beforeResolve.tap(this.name, (node, options) => {\n let resolvedNode;\n if (this.isAsync(node)) {\n const mappedValue = this.resolvedMapping.get(node.id);\n if (mappedValue) {\n resolvedNode = mappedValue;\n }\n } else {\n resolvedNode = null;\n }\n\n const newNode = resolvedNode || node;\n if (!resolvedNode && node?.type === NodeType.Async) {\n queueMicrotask(async () => {\n const result = await this.basePlugin?.hooks.onAsyncNode.call(node);\n const parsedNode =\n options.parseNode && result\n ? options.parseNode(result)\n : undefined;\n\n this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);\n view.updateAsync();\n });\n\n return node;\n }\n\n return newNode;\n });\n });\n }\n\n applyPlugin(asyncNodePlugin: AsyncNodePlugin): void {\n this.basePlugin = asyncNodePlugin;\n }\n}\n"],"mappings":";AAAA,SAAS,UAAU,iBAAiB;AAWpC,SAAS,6BAA6B;AACtC,OAAO,oBAAoB;AAC3B,SAAS,YAAY;AAoBd,IAAM,kBAAN,MAA8C;AAAA,EAGnD,YAAY,SAAiC;AAS7C,SAAgB,QAAQ;AAAA,MACtB,aAAa,IAAI,sBAAyC;AAAA,IAC5D;AAEA,gBAAO;AAZL,QAAI,SAAS,SAAS;AACpB,WAAK,UAAU,QAAQ;AACvB,cAAQ,QAAQ,QAAQ,CAAC,WAAW;AAClC,eAAO,YAAY,IAAI;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAQA,MAAM,QAAgB;AACpB,WAAO,MAAM,eAAe,IAAI,KAAK,MAAM,CAAC,mBAAmB;AAC7D,qBAAe,MAAM,KAAK,IAAI,KAAK,MAAM,CAAC,SAAS;AACjD,aAAK,SAAS,QAAQ,CAAC,WAAW;AAChC,iBAAO,MAAM,IAAI;AAAA,QACnB,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;AAEO,IAAM,wBAAN,MAA2D;AAAA,EAA3D;AACL,SAAO,YAAY,IAAI,sBAAyC;AAGhE,gBAAO;AAEP,SAAQ,kBAAkB,oBAAI,IAAiB;AAAA;AAAA,EAIvC,QAAQ,MAA4C;AAC1D,WAAO,MAAM,SAAS,SAAS;AAAA,EACjC;AAAA,EAEA,YAAY,QAAgB;AAC1B,WAAO,MAAM,kBAAkB,IAAI,KAAK,MAAM,CAAC,QAAQ;AACrD,UAAI,OAAO,UAAU,eAAe,KAAK,KAAK,OAAO,GAAG;AACtD,eAAO,SAAS;AAAA,MAClB;AAAA,IACF,CAAC;AACD,WAAO,MAAM,UAAU;AAAA,MACrB,KAAK;AAAA,MACL,CACE,KACA,UACA,SACA,uBACG;AACH,YAAI,uBAAuB,SAAS,OAAO;AACzC,gBAAM,cAAc,OAAO;AAAA,YACzB,KAAK,KAAK,OAAO;AAAA,YACjB;AAAA,YACA;AAAA,UACF;AACA,gBAAM,eAAe,UAAU,WAAW;AAC1C,cAAI,gBAAgB,QAAQ,cAAc;AACxC,mBAAO,OAAO;AAAA,cACZ;AAAA,gBACE,IAAI;AAAA,gBACJ,MAAM,SAAS;AAAA,gBACf,OAAO;AAAA,cACT;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAEA,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,mBAAmB,UAAoB;AACrC,aAAS,MAAM,cAAc,IAAI,KAAK,MAAM,CAAC,MAAM,YAAY;AAC7D,UAAI;AACJ,UAAI,KAAK,QAAQ,IAAI,GAAG;AACtB,cAAM,cAAc,KAAK,gBAAgB,IAAI,KAAK,EAAE;AACpD,YAAI,aAAa;AACf,yBAAe;AAAA,QACjB;AAAA,MACF,OAAO;AACL,uBAAe;AAAA,MACjB;AAEA,YAAM,UAAU,gBAAgB;AAChC,UAAI,CAAC,gBAAgB,MAAM,SAAS,SAAS,OAAO;AAClD,uBAAe,YAAY;AACzB,gBAAM,SAAS,MAAM,KAAK,YAAY,MAAM,YAAY,KAAK,IAAI;AACjE,gBAAM,aACJ,QAAQ,aAAa,SAAS,QAAQ,UAAU,MAAM,IAAI;AAE5D,cAAI,YAAY;AACd,iBAAK,gBAAgB,IAAI,KAAK,IAAI,UAAU;AAC5C,iBAAK,aAAa,YAAY;AAAA,UAChC;AAAA,QACF,CAAC;AAED,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAA0B;AAC9B,SAAK,MAAM,OAAO,IAAI,YAAY,KAAK,YAAY,KAAK,IAAI,CAAC;AAC7D,SAAK,MAAM,SAAS,IAAI,YAAY,CAAC,aAAa;AAChD,eAAS,MAAM,cAAc,IAAI,KAAK,MAAM,CAAC,MAAM,YAAY;AAC7D,YAAI;AACJ,YAAI,KAAK,QAAQ,IAAI,GAAG;AACtB,gBAAM,cAAc,KAAK,gBAAgB,IAAI,KAAK,EAAE;AACpD,cAAI,aAAa;AACf,2BAAe;AAAA,UACjB;AAAA,QACF,OAAO;AACL,yBAAe;AAAA,QACjB;AAEA,cAAM,UAAU,gBAAgB;AAChC,YAAI,CAAC,gBAAgB,MAAM,SAAS,SAAS,OAAO;AAClD,yBAAe,YAAY;AACzB,kBAAM,SAAS,MAAM,KAAK,YAAY,MAAM,YAAY,KAAK,IAAI;AACjE,kBAAM,aACJ,QAAQ,aAAa,SACjB,QAAQ,UAAU,MAAM,IACxB;AAEN,iBAAK,gBAAgB,IAAI,KAAK,IAAI,aAAa,aAAa,IAAI;AAChE,iBAAK,YAAY;AAAA,UACnB,CAAC;AAED,iBAAO;AAAA,QACT;AAEA,eAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,YAAY,iBAAwC;AAClD,SAAK,aAAa;AAAA,EACpB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts"],"sourcesContent":["import { NodeType, getNodeID } from \"@player-ui/player\";\nimport type {\n Player,\n PlayerPlugin,\n Node,\n ParseObjectOptions,\n ViewInstance,\n Parser,\n ViewPlugin,\n Resolver,\n Resolve,\n} from \"@player-ui/player\";\nimport { AsyncParallelBailHook } from \"tapable-ts\";\nimport queueMicrotask from \"queue-microtask\";\nimport { omit } from \"timm\";\n\nexport * from \"./types\";\n\nexport interface AsyncNodePluginOptions {\n /** A set of plugins to load */\n plugins?: AsyncNodeViewPlugin[];\n}\n\nexport interface AsyncNodeViewPlugin extends ViewPlugin {\n /** Use this to tap into the async node plugin hooks */\n applyPlugin: (asyncNodePlugin: AsyncNodePlugin) => void;\n\n asyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any>;\n}\n\n/**\n * Async node plugin used to resolve async nodes in the content\n * If an async node is present, allow users to provide a replacement node to be rendered when ready\n */\nexport class AsyncNodePlugin implements PlayerPlugin {\n private plugins: AsyncNodeViewPlugin[] | undefined;\n\n constructor(options: AsyncNodePluginOptions) {\n if (options?.plugins) {\n this.plugins = options.plugins;\n options.plugins.forEach((plugin) => {\n plugin.applyPlugin(this);\n });\n }\n }\n\n public readonly hooks = {\n onAsyncNode: new AsyncParallelBailHook<\n [Node.Async, (result: any) => void],\n any\n >(),\n };\n\n name = \"AsyncNode\";\n\n apply(player: Player) {\n player.hooks.viewController.tap(this.name, (viewController) => {\n viewController.hooks.view.tap(this.name, (view) => {\n this.plugins?.forEach((plugin) => {\n plugin.apply(view);\n });\n });\n });\n }\n}\n\nexport class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {\n public asyncNode = new AsyncParallelBailHook<\n [Node.Async, (result: any) => void],\n any\n >();\n private basePlugin: AsyncNodePlugin | undefined;\n\n name = \"AsyncNode\";\n\n private resolvedMapping = new Map<string, any>();\n\n private currentView: ViewInstance | undefined;\n\n /**\n * Updates the node asynchronously based on the result provided.\n * This method is responsible for handling the update logic of asynchronous nodes.\n * It checks if the node needs to be updated based on the new result and updates the mapping accordingly.\n * If an update is necessary, it triggers an asynchronous update on the view.\n * @param node The asynchronous node that might be updated.\n * @param result The result obtained from resolving the async node. This could be any data structure or value.\n * @param options Options provided for node resolution, including a potential parseNode function to process the result.\n * @param view The view instance where the node resides. This can be undefined if the view is not currently active.\n */\n private handleAsyncUpdate(\n node: Node.Async,\n result: any,\n options: Resolve.NodeResolveOptions,\n view: ViewInstance | undefined,\n ) {\n const parsedNode =\n options.parseNode && result ? options.parseNode(result) : undefined;\n\n if (this.resolvedMapping.get(node.id) !== parsedNode) {\n this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);\n view?.updateAsync();\n }\n }\n\n /**\n * Handles the asynchronous API integration for resolving nodes.\n * This method sets up a hook on the resolver's `beforeResolve` event to process async nodes.\n * @param resolver The resolver instance to attach the hook to.\n * @param view\n */\n applyResolver(resolver: Resolver) {\n resolver.hooks.beforeResolve.tap(this.name, (node, options) => {\n let resolvedNode;\n if (this.isAsync(node)) {\n const mappedValue = this.resolvedMapping.get(node.id);\n if (mappedValue) {\n resolvedNode = mappedValue;\n }\n } else {\n resolvedNode = null;\n }\n\n const newNode = resolvedNode || node;\n if (!resolvedNode && node?.type === NodeType.Async) {\n queueMicrotask(async () => {\n const result = await this.basePlugin?.hooks.onAsyncNode.call(\n node,\n (result) => {\n this.handleAsyncUpdate(node, result, options, this.currentView);\n },\n );\n this.handleAsyncUpdate(node, result, options, this.currentView);\n });\n\n return node;\n }\n return newNode;\n });\n }\n\n private isAsync(node: Node.Node | null): node is Node.Async {\n return node?.type === NodeType.Async;\n }\n\n applyParser(parser: Parser) {\n parser.hooks.determineNodeType.tap(this.name, (obj) => {\n if (Object.prototype.hasOwnProperty.call(obj, \"async\")) {\n return NodeType.Async;\n }\n });\n parser.hooks.parseNode.tap(\n this.name,\n (\n obj: any,\n nodeType: Node.ChildrenTypes,\n options: ParseObjectOptions,\n determinedNodeType: null | NodeType,\n ) => {\n if (determinedNodeType === NodeType.Async) {\n const parsedAsync = parser.parseObject(\n omit(obj, \"async\"),\n nodeType,\n options,\n );\n const parsedNodeId = getNodeID(parsedAsync);\n if (parsedAsync !== null && parsedNodeId) {\n return parser.createASTNode(\n {\n id: parsedNodeId,\n type: NodeType.Async,\n value: parsedAsync,\n },\n obj,\n );\n }\n\n return null;\n }\n },\n );\n }\n\n apply(view: ViewInstance): void {\n this.currentView = view;\n view.hooks.parser.tap(\"async\", this.applyParser.bind(this));\n view.hooks.resolver.tap(\"async\", this.applyResolver.bind(this));\n }\n\n applyPlugin(asyncNodePlugin: AsyncNodePlugin): void {\n this.basePlugin = asyncNodePlugin;\n }\n}\n"],"mappings":"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","names":["result"]}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@player-ui/async-node-plugin",
3
- "version": "0.8.0-next.2",
3
+ "version": "0.8.0-next.4",
4
4
  "main": "dist/cjs/index.cjs",
5
5
  "peerDependencies": {
6
- "@player-ui/player": "0.8.0-next.2"
6
+ "@player-ui/player": "0.8.0-next.4"
7
7
  },
8
8
  "module": "dist/index.legacy-esm.js",
9
9
  "types": "types/index.d.ts",
package/src/index.test.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { expect, test } from "vitest";
1
+ import { expect, test, vi } from "vitest";
2
2
  import { Node, InProgressState, ViewInstance } from "@player-ui/player";
3
3
  import { Player } from "@player-ui/player";
4
4
  import { waitFor } from "@testing-library/react";
@@ -44,11 +44,20 @@ const asyncNodeTest = async (resolvedValue: any) => {
44
44
 
45
45
  let deferredResolve: ((value: any) => void) | undefined;
46
46
 
47
- plugin.hooks.onAsyncNode.tap("test", async (node: Node.Async) => {
48
- return new Promise((resolve) => {
49
- deferredResolve = resolve; // Promise would be resolved only once
50
- });
51
- });
47
+ let updateContent: any;
48
+
49
+ plugin.hooks.onAsyncNode.tap(
50
+ "test",
51
+ async (node: Node.Async, update: (content: any) => void) => {
52
+ const result = new Promise((resolve) => {
53
+ deferredResolve = resolve; // Promise would be resolved only once
54
+ });
55
+
56
+ updateContent = update;
57
+ // Return the result to follow the same mechanism as before
58
+ return result;
59
+ },
60
+ );
52
61
 
53
62
  let updateNumber = 0;
54
63
 
@@ -84,7 +93,7 @@ const asyncNodeTest = async (resolvedValue: any) => {
84
93
  }
85
94
 
86
95
  await waitFor(() => {
87
- expect(updateNumber).toBe(2);
96
+ expect(updateNumber).toBe(1);
88
97
  });
89
98
 
90
99
  view = (player.getState() as InProgressState).controllers.view.currentView
@@ -93,10 +102,14 @@ const asyncNodeTest = async (resolvedValue: any) => {
93
102
  expect(view?.actions[0].asset.type).toBe("action");
94
103
  expect(view?.actions.length).toBe(1);
95
104
 
96
- viewInstance?.update();
105
+ // Consumer responds with null/undefined
106
+ if (deferredResolve) {
107
+ updateContent(resolvedValue);
108
+ }
97
109
 
110
+ //Even after an update, the view should not change as we are deleting the resolved node if there is no view update
98
111
  await waitFor(() => {
99
- expect(updateNumber).toBe(3);
112
+ expect(updateNumber).toBe(1);
100
113
  });
101
114
 
102
115
  view = (player.getState() as InProgressState).controllers.view.currentView
@@ -114,6 +127,89 @@ test("should return current node view when the resolved node is undefined", asyn
114
127
  await asyncNodeTest(undefined);
115
128
  });
116
129
 
130
+ test("can handle multiple updates through callback mechanism", async () => {
131
+ const plugin = new AsyncNodePlugin({
132
+ plugins: [new AsyncNodePluginPlugin()],
133
+ });
134
+
135
+ let deferredResolve: ((value: any) => void) | undefined;
136
+
137
+ let updateContent: any;
138
+
139
+ plugin.hooks.onAsyncNode.tap(
140
+ "test",
141
+ async (node: Node.Async, update: (content: any) => void) => {
142
+ const result = new Promise((resolve) => {
143
+ deferredResolve = resolve; // Promise would be resolved only once
144
+ });
145
+
146
+ updateContent = update;
147
+ // Return the result to follow the same mechanism as before
148
+ return result;
149
+ },
150
+ );
151
+
152
+ let updateNumber = 0;
153
+
154
+ const player = new Player({ plugins: [plugin] });
155
+
156
+ player.hooks.viewController.tap("async-node-test", (vc) => {
157
+ vc.hooks.view.tap("async-node-test", (view) => {
158
+ view.hooks.onUpdate.tap("async-node-test", (update) => {
159
+ updateNumber++;
160
+ });
161
+ });
162
+ });
163
+
164
+ player.start(basicFRFWithActions as any);
165
+
166
+ let view = (player.getState() as InProgressState).controllers.view.currentView
167
+ ?.lastUpdate;
168
+
169
+ expect(view).toBeDefined();
170
+ expect(view?.actions[1]).toBeUndefined();
171
+
172
+ await waitFor(() => {
173
+ expect(updateNumber).toBe(1);
174
+ expect(deferredResolve).toBeDefined();
175
+ });
176
+
177
+ if (deferredResolve) {
178
+ deferredResolve({
179
+ asset: {
180
+ id: "next-label-action",
181
+ type: "action",
182
+ value: "dummy value",
183
+ },
184
+ });
185
+ }
186
+
187
+ await waitFor(() => {
188
+ expect(updateNumber).toBe(2);
189
+ });
190
+
191
+ view = (player.getState() as InProgressState).controllers.view.currentView
192
+ ?.lastUpdate;
193
+
194
+ expect(view?.actions[0].asset.type).toBe("action");
195
+ expect(view?.actions[1].asset.type).toBe("action");
196
+ expect(updateNumber).toBe(2);
197
+
198
+ if (deferredResolve) {
199
+ updateContent(null);
200
+ }
201
+
202
+ await waitFor(() => {
203
+ expect(updateNumber).toBe(3);
204
+ });
205
+
206
+ view = (player.getState() as InProgressState).controllers.view.currentView
207
+ ?.lastUpdate;
208
+
209
+ expect(view?.actions[0].asset.type).toBe("action");
210
+ expect(view?.actions[1]).toBeUndefined();
211
+ });
212
+
117
213
  test("replaces async nodes with provided node", async () => {
118
214
  const plugin = new AsyncNodePlugin({
119
215
  plugins: [new AsyncNodePluginPlugin()],