@player-ui/async-node-plugin 0.11.0-next.0 → 0.11.0-next.2
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 +36 -14
- package/dist/AsyncNodePlugin.native.js.map +1 -1
- package/dist/cjs/index.cjs +9 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.legacy-esm.js +13 -5
- package/dist/index.mjs +13 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/index.test.ts +150 -4
- package/src/index.ts +14 -1
- package/src/transform.ts +1 -1
- package/types/index.d.ts +2 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -63,7 +63,7 @@ var asyncTransform = (assetId, wrapperAssetType, asset, flatten) => {
|
|
|
63
63
|
|
|
64
64
|
// ../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts
|
|
65
65
|
var AsyncNodePlugin = class {
|
|
66
|
-
constructor(options) {
|
|
66
|
+
constructor(options, asyncHandler) {
|
|
67
67
|
this.hooks = {
|
|
68
68
|
onAsyncNode: new import_tapable_ts.AsyncParallelBailHook()
|
|
69
69
|
};
|
|
@@ -74,6 +74,14 @@ var AsyncNodePlugin = class {
|
|
|
74
74
|
plugin.applyPlugin(this);
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
+
if (asyncHandler) {
|
|
78
|
+
this.hooks.onAsyncNode.tap(
|
|
79
|
+
"async",
|
|
80
|
+
async (node, callback) => {
|
|
81
|
+
return await asyncHandler(node, callback);
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
}
|
|
77
85
|
}
|
|
78
86
|
apply(player) {
|
|
79
87
|
player.hooks.viewController.tap(this.name, (viewController) => {
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts","../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/transform.ts"],"sourcesContent":["import { NodeType, getNodeID } from \"@player-ui/player\";\nimport type {\n Player,\n PlayerPlugin,\n Node,\n ParseObjectOptions,\n ParseObjectChildOptions,\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\";\nexport * from \"./transform\";\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 private isDeterminedAsync(obj: any) {\n return obj && Object.prototype.hasOwnProperty.call(obj, \"async\");\n }\n\n applyParser(parser: Parser) {\n parser.hooks.parseNode.tap(\n this.name,\n (\n obj: any,\n nodeType: Node.ChildrenTypes,\n options: ParseObjectOptions,\n childOptions?: ParseObjectChildOptions,\n ) => {\n if (this.isDeterminedAsync(obj)) {\n const parsedAsync = parser.parseObject(\n omit(obj, \"async\"),\n nodeType,\n options,\n );\n const parsedNodeId = getNodeID(parsedAsync);\n\n if (parsedAsync === null || !parsedNodeId) {\n return childOptions ? [] : null;\n }\n\n const asyncAST = parser.createASTNode(\n {\n id: parsedNodeId,\n type: NodeType.Async,\n value: parsedAsync,\n },\n obj,\n );\n\n if (childOptions) {\n return asyncAST\n ? [\n {\n path: [...childOptions.path, childOptions.key],\n value: asyncAST,\n },\n ]\n : [];\n }\n\n return asyncAST;\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","import { Builder, NodeType } from \"@player-ui/player\";\nimport type { AsyncTransformFunc } from \"./types\";\n\n/**\n * Util function to generate transform function for async asset\n * @param asset - async asset to apply beforeResolve transform\n * @param transformedAssetType: transformed asset type for rendering\n * @param wrapperAssetType: container asset type\n * @param flatten: flatten the streamed in content\n * @returns - wrapper asset with children of transformed asset and async node\n */\n\nexport const asyncTransform: AsyncTransformFunc = (\n assetId,\n wrapperAssetType,\n asset,\n flatten,\n) => {\n const id = \"async-\" + assetId;\n\n const asyncNode = Builder.asyncNode(id, flatten);\n let multiNode;\n let assetNode;\n\n if (asset) {\n assetNode = Builder.assetWrapper(asset);\n multiNode = Builder.multiNode(assetNode, asyncNode);\n } else {\n multiNode = Builder.multiNode(asyncNode);\n }\n\n const wrapperAsset = Builder.asset({\n id: wrapperAssetType + \"-\" + id,\n type: wrapperAssetType,\n });\n\n Builder.addChild(wrapperAsset, [\"values\"], multiNode);\n\n return wrapperAsset;\n};\n"],"mappings":"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","names":["import_player","queueMicrotask","result"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts","../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/transform.ts"],"sourcesContent":["import { NodeType, getNodeID } from \"@player-ui/player\";\nimport type {\n Player,\n PlayerPlugin,\n Node,\n ParseObjectOptions,\n ParseObjectChildOptions,\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\";\nexport * from \"./transform\";\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}\nexport type AsyncHandler = (\n node: Node.Async,\n callback?: (result: any) => void,\n) => Promise<any>;\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, asyncHandler?: AsyncHandler) {\n if (options?.plugins) {\n this.plugins = options.plugins;\n options.plugins.forEach((plugin) => {\n plugin.applyPlugin(this);\n });\n }\n\n if (asyncHandler) {\n this.hooks.onAsyncNode.tap(\n \"async\",\n async (node: Node.Async, callback) => {\n return await asyncHandler(node, callback);\n },\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 private isDeterminedAsync(obj: any) {\n return obj && Object.prototype.hasOwnProperty.call(obj, \"async\");\n }\n\n applyParser(parser: Parser) {\n parser.hooks.parseNode.tap(\n this.name,\n (\n obj: any,\n nodeType: Node.ChildrenTypes,\n options: ParseObjectOptions,\n childOptions?: ParseObjectChildOptions,\n ) => {\n if (this.isDeterminedAsync(obj)) {\n const parsedAsync = parser.parseObject(\n omit(obj, \"async\"),\n nodeType,\n options,\n );\n const parsedNodeId = getNodeID(parsedAsync);\n\n if (parsedAsync === null || !parsedNodeId) {\n return childOptions ? [] : null;\n }\n\n const asyncAST = parser.createASTNode(\n {\n id: parsedNodeId,\n type: NodeType.Async,\n value: parsedAsync,\n },\n obj,\n );\n\n if (childOptions) {\n return asyncAST\n ? [\n {\n path: [...childOptions.path, childOptions.key],\n value: asyncAST,\n },\n ]\n : [];\n }\n\n return asyncAST;\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","import { Builder } from \"@player-ui/player\";\nimport type { AsyncTransformFunc } from \"./types\";\n\n/**\n * Util function to generate transform function for async asset\n * @param asset - async asset to apply beforeResolve transform\n * @param transformedAssetType: transformed asset type for rendering\n * @param wrapperAssetType: container asset type\n * @param flatten: flatten the streamed in content\n * @returns - wrapper asset with children of transformed asset and async node\n */\n\nexport const asyncTransform: AsyncTransformFunc = (\n assetId,\n wrapperAssetType,\n asset,\n flatten,\n) => {\n const id = \"async-\" + assetId;\n\n const asyncNode = Builder.asyncNode(id, flatten);\n let multiNode;\n let assetNode;\n\n if (asset) {\n assetNode = Builder.assetWrapper(asset);\n multiNode = Builder.multiNode(assetNode, asyncNode);\n } else {\n multiNode = Builder.multiNode(asyncNode);\n }\n\n const wrapperAsset = Builder.asset({\n id: wrapperAssetType + \"-\" + id,\n type: wrapperAssetType,\n });\n\n Builder.addChild(wrapperAsset, [\"values\"], multiNode);\n\n return wrapperAsset;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,iBAAoC;AAapC,wBAAsC;AACtC,6BAA2B;AAC3B,kBAAqB;;;ACfrB,oBAAwB;AAYjB,IAAM,iBAAqC,CAChD,SACA,kBACA,OACA,YACG;AACH,QAAM,KAAK,WAAW;AAEtB,QAAM,YAAY,sBAAQ,UAAU,IAAI,OAAO;AAC/C,MAAI;AACJ,MAAI;AAEJ,MAAI,OAAO;AACT,gBAAY,sBAAQ,aAAa,KAAK;AACtC,gBAAY,sBAAQ,UAAU,WAAW,SAAS;AAAA,EACpD,OAAO;AACL,gBAAY,sBAAQ,UAAU,SAAS;AAAA,EACzC;AAEA,QAAM,eAAe,sBAAQ,MAAM;AAAA,IACjC,IAAI,mBAAmB,MAAM;AAAA,IAC7B,MAAM;AAAA,EACR,CAAC;AAED,wBAAQ,SAAS,cAAc,CAAC,QAAQ,GAAG,SAAS;AAEpD,SAAO;AACT;;;ADCO,IAAM,kBAAN,MAA8C;AAAA,EAGnD,YAAY,SAAiC,cAA6B;AAkB1E,SAAgB,QAAQ;AAAA,MACtB,aAAa,IAAI,wCAGf;AAAA,IACJ;AAEA,gBAAO;AAxBL,QAAI,SAAS,SAAS;AACpB,WAAK,UAAU,QAAQ;AACvB,cAAQ,QAAQ,QAAQ,CAAC,WAAW;AAClC,eAAO,YAAY,IAAI;AAAA,MACzB,CAAC;AAAA,IACH;AAEA,QAAI,cAAc;AAChB,WAAK,MAAM,YAAY;AAAA,QACrB;AAAA,QACA,OAAO,MAAkB,aAAa;AACpC,iBAAO,MAAM,aAAa,MAAM,QAAQ;AAAA,QAC1C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAWA,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,wCAGrB;AAGF,gBAAO;AAEP,SAAQ,kBAAkB,oBAAI,IAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcvC,kBACN,MACA,QACA,SACA,MACA;AACA,UAAM,aACJ,QAAQ,aAAa,SAAS,QAAQ,UAAU,MAAM,IAAI;AAE5D,QAAI,KAAK,gBAAgB,IAAI,KAAK,EAAE,MAAM,YAAY;AACpD,WAAK,gBAAgB,IAAI,KAAK,IAAI,aAAa,aAAa,IAAI;AAChE,YAAM,YAAY;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,cAAc,UAAoB;AAChC,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,wBAAS,OAAO;AAClD,mCAAAC,SAAe,YAAY;AACzB,gBAAM,SAAS,MAAM,KAAK,YAAY,MAAM,YAAY;AAAA,YACtD;AAAA,YACA,CAACC,YAAW;AACV,mBAAK,kBAAkB,MAAMA,SAAQ,SAAS,KAAK,WAAW;AAAA,YAChE;AAAA,UACF;AACA,eAAK,kBAAkB,MAAM,QAAQ,SAAS,KAAK,WAAW;AAAA,QAChE,CAAC;AAED,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEQ,QAAQ,MAA4C;AAC1D,WAAO,MAAM,SAAS,wBAAS;AAAA,EACjC;AAAA,EAEQ,kBAAkB,KAAU;AAClC,WAAO,OAAO,OAAO,UAAU,eAAe,KAAK,KAAK,OAAO;AAAA,EACjE;AAAA,EAEA,YAAY,QAAgB;AAC1B,WAAO,MAAM,UAAU;AAAA,MACrB,KAAK;AAAA,MACL,CACE,KACA,UACA,SACA,iBACG;AACH,YAAI,KAAK,kBAAkB,GAAG,GAAG;AAC/B,gBAAM,cAAc,OAAO;AAAA,gBACzB,kBAAK,KAAK,OAAO;AAAA,YACjB;AAAA,YACA;AAAA,UACF;AACA,gBAAM,mBAAe,0BAAU,WAAW;AAE1C,cAAI,gBAAgB,QAAQ,CAAC,cAAc;AACzC,mBAAO,eAAe,CAAC,IAAI;AAAA,UAC7B;AAEA,gBAAM,WAAW,OAAO;AAAA,YACtB;AAAA,cACE,IAAI;AAAA,cACJ,MAAM,wBAAS;AAAA,cACf,OAAO;AAAA,YACT;AAAA,YACA;AAAA,UACF;AAEA,cAAI,cAAc;AAChB,mBAAO,WACH;AAAA,cACE;AAAA,gBACE,MAAM,CAAC,GAAG,aAAa,MAAM,aAAa,GAAG;AAAA,gBAC7C,OAAO;AAAA,cACT;AAAA,YACF,IACA,CAAC;AAAA,UACP;AAEA,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,MAA0B;AAC9B,SAAK,cAAc;AACnB,SAAK,MAAM,OAAO,IAAI,SAAS,KAAK,YAAY,KAAK,IAAI,CAAC;AAC1D,SAAK,MAAM,SAAS,IAAI,SAAS,KAAK,cAAc,KAAK,IAAI,CAAC;AAAA,EAChE;AAAA,EAEA,YAAY,iBAAwC;AAClD,SAAK,aAAa;AAAA,EACpB;AACF;","names":["import_player","queueMicrotask","result"]}
|
package/dist/index.legacy-esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts
|
|
2
|
-
import { NodeType
|
|
2
|
+
import { NodeType, getNodeID } from "@player-ui/player";
|
|
3
3
|
import { AsyncParallelBailHook } from "tapable-ts";
|
|
4
4
|
import queueMicrotask from "queue-microtask";
|
|
5
5
|
import { omit } from "timm";
|
|
@@ -27,7 +27,7 @@ var asyncTransform = (assetId, wrapperAssetType, asset, flatten) => {
|
|
|
27
27
|
|
|
28
28
|
// ../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts
|
|
29
29
|
var AsyncNodePlugin = class {
|
|
30
|
-
constructor(options) {
|
|
30
|
+
constructor(options, asyncHandler) {
|
|
31
31
|
this.hooks = {
|
|
32
32
|
onAsyncNode: new AsyncParallelBailHook()
|
|
33
33
|
};
|
|
@@ -38,6 +38,14 @@ var AsyncNodePlugin = class {
|
|
|
38
38
|
plugin.applyPlugin(this);
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
+
if (asyncHandler) {
|
|
42
|
+
this.hooks.onAsyncNode.tap(
|
|
43
|
+
"async",
|
|
44
|
+
async (node, callback) => {
|
|
45
|
+
return await asyncHandler(node, callback);
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
}
|
|
41
49
|
}
|
|
42
50
|
apply(player) {
|
|
43
51
|
player.hooks.viewController.tap(this.name, (viewController) => {
|
|
@@ -90,7 +98,7 @@ var AsyncNodePluginPlugin = class {
|
|
|
90
98
|
resolvedNode = null;
|
|
91
99
|
}
|
|
92
100
|
const newNode = resolvedNode || node;
|
|
93
|
-
if (!resolvedNode && node?.type ===
|
|
101
|
+
if (!resolvedNode && node?.type === NodeType.Async) {
|
|
94
102
|
queueMicrotask(async () => {
|
|
95
103
|
const result = await this.basePlugin?.hooks.onAsyncNode.call(
|
|
96
104
|
node,
|
|
@@ -106,7 +114,7 @@ var AsyncNodePluginPlugin = class {
|
|
|
106
114
|
});
|
|
107
115
|
}
|
|
108
116
|
isAsync(node) {
|
|
109
|
-
return node?.type ===
|
|
117
|
+
return node?.type === NodeType.Async;
|
|
110
118
|
}
|
|
111
119
|
isDeterminedAsync(obj) {
|
|
112
120
|
return obj && Object.prototype.hasOwnProperty.call(obj, "async");
|
|
@@ -128,7 +136,7 @@ var AsyncNodePluginPlugin = class {
|
|
|
128
136
|
const asyncAST = parser.createASTNode(
|
|
129
137
|
{
|
|
130
138
|
id: parsedNodeId,
|
|
131
|
-
type:
|
|
139
|
+
type: NodeType.Async,
|
|
132
140
|
value: parsedAsync
|
|
133
141
|
},
|
|
134
142
|
obj
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts
|
|
2
|
-
import { NodeType
|
|
2
|
+
import { NodeType, getNodeID } from "@player-ui/player";
|
|
3
3
|
import { AsyncParallelBailHook } from "tapable-ts";
|
|
4
4
|
import queueMicrotask from "queue-microtask";
|
|
5
5
|
import { omit } from "timm";
|
|
@@ -27,7 +27,7 @@ var asyncTransform = (assetId, wrapperAssetType, asset, flatten) => {
|
|
|
27
27
|
|
|
28
28
|
// ../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts
|
|
29
29
|
var AsyncNodePlugin = class {
|
|
30
|
-
constructor(options) {
|
|
30
|
+
constructor(options, asyncHandler) {
|
|
31
31
|
this.hooks = {
|
|
32
32
|
onAsyncNode: new AsyncParallelBailHook()
|
|
33
33
|
};
|
|
@@ -38,6 +38,14 @@ var AsyncNodePlugin = class {
|
|
|
38
38
|
plugin.applyPlugin(this);
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
+
if (asyncHandler) {
|
|
42
|
+
this.hooks.onAsyncNode.tap(
|
|
43
|
+
"async",
|
|
44
|
+
async (node, callback) => {
|
|
45
|
+
return await asyncHandler(node, callback);
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
}
|
|
41
49
|
}
|
|
42
50
|
apply(player) {
|
|
43
51
|
player.hooks.viewController.tap(this.name, (viewController) => {
|
|
@@ -90,7 +98,7 @@ var AsyncNodePluginPlugin = class {
|
|
|
90
98
|
resolvedNode = null;
|
|
91
99
|
}
|
|
92
100
|
const newNode = resolvedNode || node;
|
|
93
|
-
if (!resolvedNode && node?.type ===
|
|
101
|
+
if (!resolvedNode && node?.type === NodeType.Async) {
|
|
94
102
|
queueMicrotask(async () => {
|
|
95
103
|
const result = await this.basePlugin?.hooks.onAsyncNode.call(
|
|
96
104
|
node,
|
|
@@ -106,7 +114,7 @@ var AsyncNodePluginPlugin = class {
|
|
|
106
114
|
});
|
|
107
115
|
}
|
|
108
116
|
isAsync(node) {
|
|
109
|
-
return node?.type ===
|
|
117
|
+
return node?.type === NodeType.Async;
|
|
110
118
|
}
|
|
111
119
|
isDeterminedAsync(obj) {
|
|
112
120
|
return obj && Object.prototype.hasOwnProperty.call(obj, "async");
|
|
@@ -128,7 +136,7 @@ var AsyncNodePluginPlugin = class {
|
|
|
128
136
|
const asyncAST = parser.createASTNode(
|
|
129
137
|
{
|
|
130
138
|
id: parsedNodeId,
|
|
131
|
-
type:
|
|
139
|
+
type: NodeType.Async,
|
|
132
140
|
value: parsedAsync
|
|
133
141
|
},
|
|
134
142
|
obj
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts","../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/transform.ts"],"sourcesContent":["import { NodeType, getNodeID } from \"@player-ui/player\";\nimport type {\n Player,\n PlayerPlugin,\n Node,\n ParseObjectOptions,\n ParseObjectChildOptions,\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\";\nexport * from \"./transform\";\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 private isDeterminedAsync(obj: any) {\n return obj && Object.prototype.hasOwnProperty.call(obj, \"async\");\n }\n\n applyParser(parser: Parser) {\n parser.hooks.parseNode.tap(\n this.name,\n (\n obj: any,\n nodeType: Node.ChildrenTypes,\n options: ParseObjectOptions,\n childOptions?: ParseObjectChildOptions,\n ) => {\n if (this.isDeterminedAsync(obj)) {\n const parsedAsync = parser.parseObject(\n omit(obj, \"async\"),\n nodeType,\n options,\n );\n const parsedNodeId = getNodeID(parsedAsync);\n\n if (parsedAsync === null || !parsedNodeId) {\n return childOptions ? [] : null;\n }\n\n const asyncAST = parser.createASTNode(\n {\n id: parsedNodeId,\n type: NodeType.Async,\n value: parsedAsync,\n },\n obj,\n );\n\n if (childOptions) {\n return asyncAST\n ? [\n {\n path: [...childOptions.path, childOptions.key],\n value: asyncAST,\n },\n ]\n : [];\n }\n\n return asyncAST;\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","import { Builder, NodeType } from \"@player-ui/player\";\nimport type { AsyncTransformFunc } from \"./types\";\n\n/**\n * Util function to generate transform function for async asset\n * @param asset - async asset to apply beforeResolve transform\n * @param transformedAssetType: transformed asset type for rendering\n * @param wrapperAssetType: container asset type\n * @param flatten: flatten the streamed in content\n * @returns - wrapper asset with children of transformed asset and async node\n */\n\nexport const asyncTransform: AsyncTransformFunc = (\n assetId,\n wrapperAssetType,\n asset,\n flatten,\n) => {\n const id = \"async-\" + assetId;\n\n const asyncNode = Builder.asyncNode(id, flatten);\n let multiNode;\n let assetNode;\n\n if (asset) {\n assetNode = Builder.assetWrapper(asset);\n multiNode = Builder.multiNode(assetNode, asyncNode);\n } else {\n multiNode = Builder.multiNode(asyncNode);\n }\n\n const wrapperAsset = Builder.asset({\n id: wrapperAssetType + \"-\" + id,\n type: wrapperAssetType,\n });\n\n Builder.addChild(wrapperAsset, [\"values\"], multiNode);\n\n return wrapperAsset;\n};\n"],"mappings":"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","names":["NodeType","NodeType","result"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/index.ts","../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/async-node/core/src/transform.ts"],"sourcesContent":["import { NodeType, getNodeID } from \"@player-ui/player\";\nimport type {\n Player,\n PlayerPlugin,\n Node,\n ParseObjectOptions,\n ParseObjectChildOptions,\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\";\nexport * from \"./transform\";\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}\nexport type AsyncHandler = (\n node: Node.Async,\n callback?: (result: any) => void,\n) => Promise<any>;\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, asyncHandler?: AsyncHandler) {\n if (options?.plugins) {\n this.plugins = options.plugins;\n options.plugins.forEach((plugin) => {\n plugin.applyPlugin(this);\n });\n }\n\n if (asyncHandler) {\n this.hooks.onAsyncNode.tap(\n \"async\",\n async (node: Node.Async, callback) => {\n return await asyncHandler(node, callback);\n },\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 private isDeterminedAsync(obj: any) {\n return obj && Object.prototype.hasOwnProperty.call(obj, \"async\");\n }\n\n applyParser(parser: Parser) {\n parser.hooks.parseNode.tap(\n this.name,\n (\n obj: any,\n nodeType: Node.ChildrenTypes,\n options: ParseObjectOptions,\n childOptions?: ParseObjectChildOptions,\n ) => {\n if (this.isDeterminedAsync(obj)) {\n const parsedAsync = parser.parseObject(\n omit(obj, \"async\"),\n nodeType,\n options,\n );\n const parsedNodeId = getNodeID(parsedAsync);\n\n if (parsedAsync === null || !parsedNodeId) {\n return childOptions ? [] : null;\n }\n\n const asyncAST = parser.createASTNode(\n {\n id: parsedNodeId,\n type: NodeType.Async,\n value: parsedAsync,\n },\n obj,\n );\n\n if (childOptions) {\n return asyncAST\n ? [\n {\n path: [...childOptions.path, childOptions.key],\n value: asyncAST,\n },\n ]\n : [];\n }\n\n return asyncAST;\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","import { Builder } from \"@player-ui/player\";\nimport type { AsyncTransformFunc } from \"./types\";\n\n/**\n * Util function to generate transform function for async asset\n * @param asset - async asset to apply beforeResolve transform\n * @param transformedAssetType: transformed asset type for rendering\n * @param wrapperAssetType: container asset type\n * @param flatten: flatten the streamed in content\n * @returns - wrapper asset with children of transformed asset and async node\n */\n\nexport const asyncTransform: AsyncTransformFunc = (\n assetId,\n wrapperAssetType,\n asset,\n flatten,\n) => {\n const id = \"async-\" + assetId;\n\n const asyncNode = Builder.asyncNode(id, flatten);\n let multiNode;\n let assetNode;\n\n if (asset) {\n assetNode = Builder.assetWrapper(asset);\n multiNode = Builder.multiNode(assetNode, asyncNode);\n } else {\n multiNode = Builder.multiNode(asyncNode);\n }\n\n const wrapperAsset = Builder.asset({\n id: wrapperAssetType + \"-\" + id,\n type: wrapperAssetType,\n });\n\n Builder.addChild(wrapperAsset, [\"values\"], multiNode);\n\n return wrapperAsset;\n};\n"],"mappings":"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","names":["result"]}
|
package/package.json
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"types"
|
|
7
7
|
],
|
|
8
8
|
"name": "@player-ui/async-node-plugin",
|
|
9
|
-
"version": "0.11.0-next.
|
|
9
|
+
"version": "0.11.0-next.2",
|
|
10
10
|
"main": "dist/cjs/index.cjs",
|
|
11
11
|
"peerDependencies": {
|
|
12
|
-
"@player-ui/player": "0.11.0-next.
|
|
12
|
+
"@player-ui/player": "0.11.0-next.2"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@player-ui/reference-assets-plugin": "workspace:*"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test, describe } from "vitest";
|
|
2
|
-
import { Node, InProgressState
|
|
2
|
+
import { Node, InProgressState } from "@player-ui/player";
|
|
3
3
|
import { Player, Parser } from "@player-ui/player";
|
|
4
4
|
import { waitFor } from "@testing-library/react";
|
|
5
5
|
import { AsyncNodePlugin, AsyncNodePluginPlugin } from "../index";
|
|
@@ -99,11 +99,8 @@ describe("view", () => {
|
|
|
99
99
|
|
|
100
100
|
const player = new Player({ plugins: [plugin] });
|
|
101
101
|
|
|
102
|
-
let viewInstance: ViewInstance | undefined;
|
|
103
|
-
|
|
104
102
|
player.hooks.viewController.tap("async-node-test", (vc) => {
|
|
105
103
|
vc.hooks.view.tap("async-node-test", (view) => {
|
|
106
|
-
viewInstance = view;
|
|
107
104
|
view.hooks.onUpdate.tap("async-node-test", () => {
|
|
108
105
|
updateNumber++;
|
|
109
106
|
});
|
|
@@ -246,6 +243,92 @@ describe("view", () => {
|
|
|
246
243
|
expect(view?.actions[1]).toBeUndefined();
|
|
247
244
|
});
|
|
248
245
|
|
|
246
|
+
test("can handle multiple updates through callback mechanism - init with handler", async () => {
|
|
247
|
+
let deferredResolve: ((value: any) => void) | undefined;
|
|
248
|
+
|
|
249
|
+
let updateContent: any;
|
|
250
|
+
|
|
251
|
+
const asyncHandler = (
|
|
252
|
+
node: Node.Async,
|
|
253
|
+
callback: (content: any) => void,
|
|
254
|
+
) => {
|
|
255
|
+
const result = new Promise((resolve) => {
|
|
256
|
+
deferredResolve = resolve; // Promise would be resolved only once
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
updateContent = callback;
|
|
260
|
+
// Return the result to follow the same mechanism as before
|
|
261
|
+
return result;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const plugin = new AsyncNodePlugin(
|
|
265
|
+
{
|
|
266
|
+
plugins: [new AsyncNodePluginPlugin()],
|
|
267
|
+
},
|
|
268
|
+
asyncHandler,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
let updateNumber = 0;
|
|
272
|
+
|
|
273
|
+
const player = new Player({ plugins: [plugin] });
|
|
274
|
+
|
|
275
|
+
player.hooks.viewController.tap("async-node-test", (vc) => {
|
|
276
|
+
vc.hooks.view.tap("async-node-test", (view) => {
|
|
277
|
+
view.hooks.onUpdate.tap("async-node-test", (update) => {
|
|
278
|
+
updateNumber++;
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
player.start(basicFRFWithActions as any);
|
|
284
|
+
|
|
285
|
+
let view = (player.getState() as InProgressState).controllers.view
|
|
286
|
+
.currentView?.lastUpdate;
|
|
287
|
+
|
|
288
|
+
expect(view).toBeDefined();
|
|
289
|
+
expect(view?.actions[1]).toBeUndefined();
|
|
290
|
+
|
|
291
|
+
await waitFor(() => {
|
|
292
|
+
expect(updateNumber).toBe(1);
|
|
293
|
+
expect(deferredResolve).toBeDefined();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
if (deferredResolve) {
|
|
297
|
+
deferredResolve({
|
|
298
|
+
asset: {
|
|
299
|
+
id: "next-label-action",
|
|
300
|
+
type: "action",
|
|
301
|
+
value: "dummy value",
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
await waitFor(() => {
|
|
307
|
+
expect(updateNumber).toBe(2);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
view = (player.getState() as InProgressState).controllers.view.currentView
|
|
311
|
+
?.lastUpdate;
|
|
312
|
+
|
|
313
|
+
expect(view?.actions[0].asset.type).toBe("action");
|
|
314
|
+
expect(view?.actions[1].asset.type).toBe("action");
|
|
315
|
+
expect(updateNumber).toBe(2);
|
|
316
|
+
|
|
317
|
+
if (deferredResolve) {
|
|
318
|
+
updateContent(null);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
await waitFor(() => {
|
|
322
|
+
expect(updateNumber).toBe(3);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
view = (player.getState() as InProgressState).controllers.view.currentView
|
|
326
|
+
?.lastUpdate;
|
|
327
|
+
|
|
328
|
+
expect(view?.actions[0].asset.type).toBe("action");
|
|
329
|
+
expect(view?.actions[1]).toBeUndefined();
|
|
330
|
+
});
|
|
331
|
+
|
|
249
332
|
test("replaces async nodes with provided node", async () => {
|
|
250
333
|
const plugin = new AsyncNodePlugin({
|
|
251
334
|
plugins: [new AsyncNodePluginPlugin()],
|
|
@@ -305,6 +388,69 @@ describe("view", () => {
|
|
|
305
388
|
expect(view?.actions[1].asset.type).toBe("action");
|
|
306
389
|
});
|
|
307
390
|
|
|
391
|
+
test("init with handler", async () => {
|
|
392
|
+
let deferredResolve: ((value: any) => void) | undefined;
|
|
393
|
+
|
|
394
|
+
const asyncHandler = (node: Node.Async) => {
|
|
395
|
+
return new Promise((resolve) => {
|
|
396
|
+
deferredResolve = resolve;
|
|
397
|
+
});
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const plugin = new AsyncNodePlugin(
|
|
401
|
+
{
|
|
402
|
+
plugins: [new AsyncNodePluginPlugin()],
|
|
403
|
+
},
|
|
404
|
+
asyncHandler,
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
let updateNumber = 0;
|
|
408
|
+
|
|
409
|
+
const player = new Player({ plugins: [plugin] });
|
|
410
|
+
|
|
411
|
+
player.hooks.viewController.tap("async-node-test", (vc) => {
|
|
412
|
+
vc.hooks.view.tap("async-node-test", (view) => {
|
|
413
|
+
view.hooks.onUpdate.tap("async-node-test", (update) => {
|
|
414
|
+
updateNumber++;
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
player.start(basicFRFWithActions as any);
|
|
420
|
+
|
|
421
|
+
let view = (player.getState() as InProgressState).controllers.view
|
|
422
|
+
.currentView?.lastUpdate;
|
|
423
|
+
|
|
424
|
+
expect(view).toBeDefined();
|
|
425
|
+
expect(view?.actions[0].asset.type).toBe("action");
|
|
426
|
+
expect(view?.actions[1]).toBeUndefined();
|
|
427
|
+
expect(updateNumber).toBe(1);
|
|
428
|
+
|
|
429
|
+
await waitFor(() => {
|
|
430
|
+
expect(deferredResolve).toBeDefined();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
if (deferredResolve) {
|
|
434
|
+
deferredResolve({
|
|
435
|
+
asset: {
|
|
436
|
+
id: "next-label-action",
|
|
437
|
+
type: "action",
|
|
438
|
+
value: "dummy value",
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
await waitFor(() => {
|
|
444
|
+
expect(updateNumber).toBe(2);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
view = (player.getState() as InProgressState).controllers.view.currentView
|
|
448
|
+
?.lastUpdate;
|
|
449
|
+
|
|
450
|
+
expect(view?.actions[0].asset.type).toBe("action");
|
|
451
|
+
expect(view?.actions[1].asset.type).toBe("action");
|
|
452
|
+
});
|
|
453
|
+
|
|
308
454
|
test("replaces async nodes with multi node", async () => {
|
|
309
455
|
const plugin = new AsyncNodePlugin({
|
|
310
456
|
plugins: [new AsyncNodePluginPlugin()],
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,10 @@ export interface AsyncNodeViewPlugin extends ViewPlugin {
|
|
|
29
29
|
|
|
30
30
|
asyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any>;
|
|
31
31
|
}
|
|
32
|
+
export type AsyncHandler = (
|
|
33
|
+
node: Node.Async,
|
|
34
|
+
callback?: (result: any) => void,
|
|
35
|
+
) => Promise<any>;
|
|
32
36
|
|
|
33
37
|
/**
|
|
34
38
|
* Async node plugin used to resolve async nodes in the content
|
|
@@ -37,13 +41,22 @@ export interface AsyncNodeViewPlugin extends ViewPlugin {
|
|
|
37
41
|
export class AsyncNodePlugin implements PlayerPlugin {
|
|
38
42
|
private plugins: AsyncNodeViewPlugin[] | undefined;
|
|
39
43
|
|
|
40
|
-
constructor(options: AsyncNodePluginOptions) {
|
|
44
|
+
constructor(options: AsyncNodePluginOptions, asyncHandler?: AsyncHandler) {
|
|
41
45
|
if (options?.plugins) {
|
|
42
46
|
this.plugins = options.plugins;
|
|
43
47
|
options.plugins.forEach((plugin) => {
|
|
44
48
|
plugin.applyPlugin(this);
|
|
45
49
|
});
|
|
46
50
|
}
|
|
51
|
+
|
|
52
|
+
if (asyncHandler) {
|
|
53
|
+
this.hooks.onAsyncNode.tap(
|
|
54
|
+
"async",
|
|
55
|
+
async (node: Node.Async, callback) => {
|
|
56
|
+
return await asyncHandler(node, callback);
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
}
|
|
47
60
|
}
|
|
48
61
|
|
|
49
62
|
public readonly hooks = {
|
package/src/transform.ts
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -11,13 +11,14 @@ export interface AsyncNodeViewPlugin extends ViewPlugin {
|
|
|
11
11
|
applyPlugin: (asyncNodePlugin: AsyncNodePlugin) => void;
|
|
12
12
|
asyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any>;
|
|
13
13
|
}
|
|
14
|
+
export type AsyncHandler = (node: Node.Async, callback?: (result: any) => void) => Promise<any>;
|
|
14
15
|
/**
|
|
15
16
|
* Async node plugin used to resolve async nodes in the content
|
|
16
17
|
* If an async node is present, allow users to provide a replacement node to be rendered when ready
|
|
17
18
|
*/
|
|
18
19
|
export declare class AsyncNodePlugin implements PlayerPlugin {
|
|
19
20
|
private plugins;
|
|
20
|
-
constructor(options: AsyncNodePluginOptions);
|
|
21
|
+
constructor(options: AsyncNodePluginOptions, asyncHandler?: AsyncHandler);
|
|
21
22
|
readonly hooks: {
|
|
22
23
|
onAsyncNode: AsyncParallelBailHook<[Node.Async, (result: any) => void], any, Record<string, any>>;
|
|
23
24
|
};
|