@player-ui/async-node-plugin 0.11.3--canary.666.23588 → 0.11.3--canary.660.23595
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.
- package/dist/AsyncNodePlugin.native.js +40 -92
- package/dist/AsyncNodePlugin.native.js.map +1 -1
- package/dist/cjs/index.cjs +28 -54
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.legacy-esm.js +29 -55
- package/dist/index.mjs +29 -55
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/index.test.ts +2 -79
- package/src/index.ts +40 -88
- package/types/index.d.ts +7 -25
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type {
|
|
|
11
11
|
Resolver,
|
|
12
12
|
Resolve,
|
|
13
13
|
} from "@player-ui/player";
|
|
14
|
-
import { AsyncParallelBailHook
|
|
14
|
+
import { AsyncParallelBailHook } from "tapable-ts";
|
|
15
15
|
import queueMicrotask from "queue-microtask";
|
|
16
16
|
import { omit } from "timm";
|
|
17
17
|
|
|
@@ -34,21 +34,12 @@ export type AsyncHandler = (
|
|
|
34
34
|
callback?: (result: any) => void,
|
|
35
35
|
) => Promise<any>;
|
|
36
36
|
|
|
37
|
-
/** Hook declaration for the AsyncNodePlugin */
|
|
38
|
-
type AsyncNodeHooks = {
|
|
39
|
-
/** Async hook to get content for an async node */
|
|
40
|
-
onAsyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any>;
|
|
41
|
-
/** 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. */
|
|
42
|
-
onAsyncNodeError: SyncBailHook<[Error, Node.Async], Node.Node | null>;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
37
|
/**
|
|
46
38
|
* Async node plugin used to resolve async nodes in the content
|
|
47
39
|
* If an async node is present, allow users to provide a replacement node to be rendered when ready
|
|
48
40
|
*/
|
|
49
41
|
export class AsyncNodePlugin implements PlayerPlugin {
|
|
50
42
|
private plugins: AsyncNodeViewPlugin[] | undefined;
|
|
51
|
-
private playerInstance: Player | undefined;
|
|
52
43
|
|
|
53
44
|
constructor(options: AsyncNodePluginOptions, asyncHandler?: AsyncHandler) {
|
|
54
45
|
if (options?.plugins) {
|
|
@@ -68,20 +59,16 @@ export class AsyncNodePlugin implements PlayerPlugin {
|
|
|
68
59
|
}
|
|
69
60
|
}
|
|
70
61
|
|
|
71
|
-
public readonly hooks
|
|
72
|
-
onAsyncNode: new AsyncParallelBailHook
|
|
73
|
-
|
|
62
|
+
public readonly hooks = {
|
|
63
|
+
onAsyncNode: new AsyncParallelBailHook<
|
|
64
|
+
[Node.Async, (result: any) => void],
|
|
65
|
+
any
|
|
66
|
+
>(),
|
|
74
67
|
};
|
|
75
68
|
|
|
76
|
-
getPlayerInstance(): Player | undefined {
|
|
77
|
-
return this.playerInstance;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
69
|
name = "AsyncNode";
|
|
81
70
|
|
|
82
|
-
apply(player: Player)
|
|
83
|
-
this.playerInstance = player;
|
|
84
|
-
|
|
71
|
+
apply(player: Player) {
|
|
85
72
|
player.hooks.viewController.tap(this.name, (viewController) => {
|
|
86
73
|
viewController.hooks.view.tap(this.name, (view) => {
|
|
87
74
|
this.plugins?.forEach((plugin) => {
|
|
@@ -93,10 +80,10 @@ export class AsyncNodePlugin implements PlayerPlugin {
|
|
|
93
80
|
}
|
|
94
81
|
|
|
95
82
|
export class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
|
|
96
|
-
public asyncNode
|
|
83
|
+
public asyncNode = new AsyncParallelBailHook<
|
|
97
84
|
[Node.Async, (result: any) => void],
|
|
98
85
|
any
|
|
99
|
-
>
|
|
86
|
+
>();
|
|
100
87
|
private basePlugin: AsyncNodePlugin | undefined;
|
|
101
88
|
|
|
102
89
|
name = "AsyncNode";
|
|
@@ -106,13 +93,16 @@ export class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
|
|
|
106
93
|
private currentView: ViewInstance | undefined;
|
|
107
94
|
|
|
108
95
|
/**
|
|
109
|
-
*
|
|
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.
|
|
110
100
|
* @param node The asynchronous node that might be updated.
|
|
111
101
|
* @param result The result obtained from resolving the async node. This could be any data structure or value.
|
|
112
102
|
* @param options Options provided for node resolution, including a potential parseNode function to process the result.
|
|
113
103
|
* @param view The view instance where the node resides. This can be undefined if the view is not currently active.
|
|
114
104
|
*/
|
|
115
|
-
private
|
|
105
|
+
private handleAsyncUpdate(
|
|
116
106
|
node: Node.Async,
|
|
117
107
|
result: any,
|
|
118
108
|
options: Resolve.NodeResolveOptions,
|
|
@@ -121,25 +111,8 @@ export class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
|
|
|
121
111
|
const parsedNode =
|
|
122
112
|
options.parseNode && result ? options.parseNode(result) : undefined;
|
|
123
113
|
|
|
124
|
-
this.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Updates the node asynchronously based on the result provided.
|
|
129
|
-
* This method is responsible for handling the update logic of asynchronous nodes.
|
|
130
|
-
* It checks if the node needs to be updated based on the new result and updates the mapping accordingly.
|
|
131
|
-
* If an update is necessary, it triggers an asynchronous update on the view.
|
|
132
|
-
* @param node The asynchronous node that might be updated.
|
|
133
|
-
* @param newNode The new node to replace the async node.
|
|
134
|
-
* @param view The view instance where the node resides. This can be undefined if the view is not currently active.
|
|
135
|
-
*/
|
|
136
|
-
private handleAsyncUpdate(
|
|
137
|
-
node: Node.Async,
|
|
138
|
-
newNode?: Node.Node | null,
|
|
139
|
-
view?: ViewInstance,
|
|
140
|
-
) {
|
|
141
|
-
if (this.resolvedMapping.get(node.id) !== newNode) {
|
|
142
|
-
this.resolvedMapping.set(node.id, newNode ? newNode : node);
|
|
114
|
+
if (this.resolvedMapping.get(node.id) !== parsedNode) {
|
|
115
|
+
this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);
|
|
143
116
|
view?.updateAsync();
|
|
144
117
|
}
|
|
145
118
|
}
|
|
@@ -150,55 +123,34 @@ export class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
|
|
|
150
123
|
* @param resolver The resolver instance to attach the hook to.
|
|
151
124
|
* @param view
|
|
152
125
|
*/
|
|
153
|
-
applyResolver(resolver: Resolver)
|
|
126
|
+
applyResolver(resolver: Resolver) {
|
|
154
127
|
resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
136
|
}
|
|
163
137
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const result = await this.basePlugin?.hooks.onAsyncNode.call(
|
|
176
|
-
node,
|
|
177
|
-
(result) => {
|
|
178
|
-
this.parseNodeAndUpdate(node, result, options, this.currentView);
|
|
179
|
-
},
|
|
180
|
-
);
|
|
181
|
-
this.parseNodeAndUpdate(node, result, options, this.currentView);
|
|
182
|
-
} catch (e: unknown) {
|
|
183
|
-
const error = e instanceof Error ? e : new Error(String(e));
|
|
184
|
-
const result = this.basePlugin?.hooks.onAsyncNodeError.call(error, node);
|
|
185
|
-
|
|
186
|
-
if (result === undefined) {
|
|
187
|
-
const playerState = this.basePlugin?.getPlayerInstance()?.getState();
|
|
188
|
-
|
|
189
|
-
if (playerState?.status === "in-progress") {
|
|
190
|
-
playerState.fail(error);
|
|
191
|
-
}
|
|
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
|
+
});
|
|
192
149
|
|
|
193
|
-
return;
|
|
150
|
+
return node;
|
|
194
151
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
"Async node handling failed and resolved with a fallback. Error:",
|
|
198
|
-
error,
|
|
199
|
-
);
|
|
200
|
-
this.handleAsyncUpdate(node, result, this.currentView);
|
|
201
|
-
}
|
|
152
|
+
return newNode;
|
|
153
|
+
});
|
|
202
154
|
}
|
|
203
155
|
|
|
204
156
|
private isAsync(node: Node.Node | null): node is Node.Async {
|
|
@@ -209,7 +161,7 @@ export class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
|
|
|
209
161
|
return obj && Object.prototype.hasOwnProperty.call(obj, "async");
|
|
210
162
|
}
|
|
211
163
|
|
|
212
|
-
applyParser(parser: Parser)
|
|
164
|
+
applyParser(parser: Parser) {
|
|
213
165
|
parser.hooks.parseNode.tap(
|
|
214
166
|
this.name,
|
|
215
167
|
(
|
package/types/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Player, PlayerPlugin, Node, ViewInstance, Parser, ViewPlugin, Resolver } from "@player-ui/player";
|
|
2
|
-
import { AsyncParallelBailHook
|
|
2
|
+
import { AsyncParallelBailHook } from "tapable-ts";
|
|
3
3
|
export * from "./types";
|
|
4
4
|
export * from "./transform";
|
|
5
5
|
export interface AsyncNodePluginOptions {
|
|
@@ -12,50 +12,33 @@ export interface AsyncNodeViewPlugin extends ViewPlugin {
|
|
|
12
12
|
asyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any>;
|
|
13
13
|
}
|
|
14
14
|
export type AsyncHandler = (node: Node.Async, callback?: (result: any) => void) => Promise<any>;
|
|
15
|
-
/** Hook declaration for the AsyncNodePlugin */
|
|
16
|
-
type AsyncNodeHooks = {
|
|
17
|
-
/** Async hook to get content for an async node */
|
|
18
|
-
onAsyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any>;
|
|
19
|
-
/** 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. */
|
|
20
|
-
onAsyncNodeError: SyncBailHook<[Error, Node.Async], Node.Node | null>;
|
|
21
|
-
};
|
|
22
15
|
/**
|
|
23
16
|
* Async node plugin used to resolve async nodes in the content
|
|
24
17
|
* If an async node is present, allow users to provide a replacement node to be rendered when ready
|
|
25
18
|
*/
|
|
26
19
|
export declare class AsyncNodePlugin implements PlayerPlugin {
|
|
27
20
|
private plugins;
|
|
28
|
-
private playerInstance;
|
|
29
21
|
constructor(options: AsyncNodePluginOptions, asyncHandler?: AsyncHandler);
|
|
30
|
-
readonly hooks:
|
|
31
|
-
|
|
22
|
+
readonly hooks: {
|
|
23
|
+
onAsyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any, Record<string, any>>;
|
|
24
|
+
};
|
|
32
25
|
name: string;
|
|
33
26
|
apply(player: Player): void;
|
|
34
27
|
}
|
|
35
28
|
export declare class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
|
|
36
|
-
asyncNode: AsyncParallelBailHook<[
|
|
37
|
-
Node.Async,
|
|
38
|
-
(result: any) => void
|
|
39
|
-
], any>;
|
|
29
|
+
asyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any, Record<string, any>>;
|
|
40
30
|
private basePlugin;
|
|
41
31
|
name: string;
|
|
42
32
|
private resolvedMapping;
|
|
43
33
|
private currentView;
|
|
44
|
-
/**
|
|
45
|
-
* Parses the node from the result and triggers an asynchronous view update if necessary.
|
|
46
|
-
* @param node The asynchronous node that might be updated.
|
|
47
|
-
* @param result The result obtained from resolving the async node. This could be any data structure or value.
|
|
48
|
-
* @param options Options provided for node resolution, including a potential parseNode function to process the result.
|
|
49
|
-
* @param view The view instance where the node resides. This can be undefined if the view is not currently active.
|
|
50
|
-
*/
|
|
51
|
-
private parseNodeAndUpdate;
|
|
52
34
|
/**
|
|
53
35
|
* Updates the node asynchronously based on the result provided.
|
|
54
36
|
* This method is responsible for handling the update logic of asynchronous nodes.
|
|
55
37
|
* It checks if the node needs to be updated based on the new result and updates the mapping accordingly.
|
|
56
38
|
* If an update is necessary, it triggers an asynchronous update on the view.
|
|
57
39
|
* @param node The asynchronous node that might be updated.
|
|
58
|
-
* @param
|
|
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.
|
|
59
42
|
* @param view The view instance where the node resides. This can be undefined if the view is not currently active.
|
|
60
43
|
*/
|
|
61
44
|
private handleAsyncUpdate;
|
|
@@ -66,7 +49,6 @@ export declare class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
|
|
|
66
49
|
* @param view
|
|
67
50
|
*/
|
|
68
51
|
applyResolver(resolver: Resolver): void;
|
|
69
|
-
private runAsyncNode;
|
|
70
52
|
private isAsync;
|
|
71
53
|
private isDeterminedAsync;
|
|
72
54
|
applyParser(parser: Parser): void;
|