@player-ui/async-node-plugin 0.10.4-next.2 → 0.10.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.
@@ -3,6 +3,7 @@ import { Node, InProgressState, ViewInstance } 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";
6
+ import { ReferenceAssetsPlugin } from "@player-ui/reference-assets-plugin";
6
7
 
7
8
  describe("view", () => {
8
9
  const basicFRFWithActions = {
@@ -38,6 +39,40 @@ describe("view", () => {
38
39
  },
39
40
  };
40
41
 
42
+ const chatMessageContent = {
43
+ id: "chat",
44
+ views: [
45
+ {
46
+ id: "1",
47
+ type: "chat-message",
48
+ value: {
49
+ asset: {
50
+ id: "2",
51
+ type: "text",
52
+ value: "chat message",
53
+ },
54
+ },
55
+ },
56
+ ],
57
+ navigation: {
58
+ BEGIN: "FLOW_1",
59
+ FLOW_1: {
60
+ startState: "VIEW_1",
61
+ VIEW_1: {
62
+ state_type: "VIEW",
63
+ ref: "1",
64
+ transitions: {
65
+ "*": "END_Done",
66
+ },
67
+ },
68
+ END_Done: {
69
+ state_type: "END",
70
+ outcome: "DONE",
71
+ },
72
+ },
73
+ },
74
+ };
75
+
41
76
  const asyncNodeTest = async (resolvedValue: any) => {
42
77
  const plugin = new AsyncNodePlugin({
43
78
  plugins: [new AsyncNodePluginPlugin()],
@@ -548,6 +583,300 @@ describe("view", () => {
548
583
  expect(localNode.type).toStrictEqual("async");
549
584
  });
550
585
  });
586
+
587
+ test("chat-message asset - replaces async nodes with multi node flattened", async () => {
588
+ const plugin = new AsyncNodePlugin({
589
+ plugins: [new AsyncNodePluginPlugin()],
590
+ });
591
+
592
+ let deferredResolve: ((value: any) => void) | undefined;
593
+
594
+ plugin.hooks.onAsyncNode.tap("test", async (node) => {
595
+ return new Promise((resolve) => {
596
+ deferredResolve = resolve;
597
+ });
598
+ });
599
+
600
+ let updateNumber = 0;
601
+
602
+ const plugins = [plugin, new ReferenceAssetsPlugin()];
603
+
604
+ const player = new Player({
605
+ plugins: plugins,
606
+ });
607
+
608
+ player.hooks.viewController.tap("async-node-test", (vc) => {
609
+ vc.hooks.view.tap("async-node-test", (view) => {
610
+ view.hooks.onUpdate.tap("async-node-test", (update) => {
611
+ updateNumber++;
612
+ });
613
+ });
614
+ });
615
+
616
+ player.start(chatMessageContent as any);
617
+
618
+ let view = (player.getState() as InProgressState).controllers.view
619
+ .currentView?.lastUpdate;
620
+
621
+ expect(view).toBeDefined();
622
+ expect(view?.values[0].asset.type).toBe("text");
623
+ expect(updateNumber).toBe(1);
624
+
625
+ await waitFor(() => {
626
+ expect(deferredResolve).toBeDefined();
627
+ });
628
+
629
+ if (deferredResolve) {
630
+ deferredResolve([
631
+ {
632
+ asset: {
633
+ id: "2",
634
+ type: "text",
635
+ value: "Hello World!",
636
+ },
637
+ },
638
+ {
639
+ asset: {
640
+ id: "3",
641
+ type: "text",
642
+ value: "Hello World!",
643
+ },
644
+ },
645
+ ]);
646
+ }
647
+
648
+ await waitFor(() => {
649
+ expect(updateNumber).toBe(2);
650
+ });
651
+
652
+ view = (player.getState() as InProgressState).controllers.view.currentView
653
+ ?.lastUpdate;
654
+
655
+ expect(view?.values[1][0].asset.type).toBe("text");
656
+ expect(view?.values[1][1].asset.type).toBe("text");
657
+ });
658
+
659
+ test("chat-message asset - replaces async nodes with provided node", async () => {
660
+ const plugin = new AsyncNodePlugin({
661
+ plugins: [new AsyncNodePluginPlugin()],
662
+ });
663
+
664
+ let deferredResolve: ((value: any) => void) | undefined;
665
+
666
+ plugin.hooks.onAsyncNode.tap("test", async (node) => {
667
+ return new Promise((resolve) => {
668
+ deferredResolve = resolve;
669
+ });
670
+ });
671
+
672
+ let updateNumber = 0;
673
+
674
+ const plugins = [plugin, new ReferenceAssetsPlugin()];
675
+
676
+ const player = new Player({
677
+ plugins: plugins,
678
+ });
679
+
680
+ player.hooks.viewController.tap("async-node-test", (vc) => {
681
+ vc.hooks.view.tap("async-node-test", (view) => {
682
+ view.hooks.onUpdate.tap("async-node-test", (update) => {
683
+ updateNumber++;
684
+ });
685
+ });
686
+ });
687
+
688
+ player.start(chatMessageContent as any);
689
+
690
+ let view = (player.getState() as InProgressState).controllers.view
691
+ .currentView?.lastUpdate;
692
+
693
+ expect(view).toBeDefined();
694
+ expect(view?.values[0].asset.type).toBe("text");
695
+ expect(view?.values[0].asset.value).toBe("chat message");
696
+ expect(updateNumber).toBe(1);
697
+
698
+ await waitFor(() => {
699
+ expect(deferredResolve).toBeDefined();
700
+ });
701
+
702
+ if (deferredResolve) {
703
+ deferredResolve({
704
+ asset: {
705
+ id: "2",
706
+ type: "text",
707
+ value: "async content",
708
+ },
709
+ });
710
+ }
711
+
712
+ await waitFor(() => {
713
+ expect(updateNumber).toBe(2);
714
+ });
715
+
716
+ view = (player.getState() as InProgressState).controllers.view.currentView
717
+ ?.lastUpdate;
718
+
719
+ expect(view?.values[1].asset.type).toBe("text");
720
+ expect(view?.values[1].asset.value).toBe("async content");
721
+ });
722
+
723
+ test("chat-message asset - replaces async nodes with chat-message asset", async () => {
724
+ const plugin = new AsyncNodePlugin({
725
+ plugins: [new AsyncNodePluginPlugin()],
726
+ });
727
+
728
+ let deferredResolve: ((value: any) => void) | undefined;
729
+
730
+ plugin.hooks.onAsyncNode.tap("test", async (node) => {
731
+ return new Promise((resolve) => {
732
+ deferredResolve = resolve;
733
+ });
734
+ });
735
+
736
+ let updateNumber = 0;
737
+
738
+ const player = new Player({
739
+ plugins: [plugin, new ReferenceAssetsPlugin()],
740
+ });
741
+
742
+ player.hooks.viewController.tap("async-node-test", (vc) => {
743
+ vc.hooks.view.tap("async-node-test", (view) => {
744
+ view.hooks.onUpdate.tap("async-node-test", (update) => {
745
+ updateNumber++;
746
+ });
747
+ });
748
+ });
749
+
750
+ player.start(chatMessageContent as any);
751
+
752
+ let view = (player.getState() as InProgressState).controllers.view
753
+ .currentView?.lastUpdate;
754
+
755
+ expect(view).toBeDefined();
756
+ expect(view?.values[0].asset.type).toBe("text");
757
+ expect(view?.values[0].asset.value).toBe("chat message");
758
+ expect(updateNumber).toBe(1);
759
+
760
+ await waitFor(() => {
761
+ expect(deferredResolve).toBeDefined();
762
+ });
763
+
764
+ if (deferredResolve) {
765
+ deferredResolve({
766
+ asset: {
767
+ id: "3",
768
+ type: "chat-message",
769
+ value: {
770
+ asset: {
771
+ id: "4",
772
+ type: "text",
773
+ value: "async content",
774
+ },
775
+ },
776
+ },
777
+ });
778
+ }
779
+
780
+ await waitFor(() => {
781
+ expect(updateNumber).toBe(2);
782
+ });
783
+
784
+ view = (player.getState() as InProgressState).controllers.view.currentView
785
+ ?.lastUpdate;
786
+
787
+ expect(view?.values[1].asset.type).toBe("text");
788
+ expect(view?.values[1].asset.value).toBe("async content");
789
+ });
790
+
791
+ test("chat-message asset - resolve chained chat-message", async () => {
792
+ const plugin = new AsyncNodePlugin({
793
+ plugins: [new AsyncNodePluginPlugin()],
794
+ });
795
+
796
+ let deferredResolve: ((value: any) => void) | undefined;
797
+
798
+ plugin.hooks.onAsyncNode.tap("test", async (node) => {
799
+ return new Promise((resolve) => {
800
+ deferredResolve = resolve;
801
+ });
802
+ });
803
+
804
+ let updateNumber = 0;
805
+
806
+ const plugins = [plugin, new ReferenceAssetsPlugin()];
807
+
808
+ const player = new Player({
809
+ plugins: plugins,
810
+ });
811
+
812
+ player.hooks.viewController.tap("async-node-test", (vc) => {
813
+ vc.hooks.view.tap("async-node-test", (view) => {
814
+ view.hooks.onUpdate.tap("async-node-test", (update) => {
815
+ updateNumber++;
816
+ });
817
+ });
818
+ });
819
+
820
+ player.start(chatMessageContent as any);
821
+
822
+ let view = (player.getState() as InProgressState).controllers.view
823
+ .currentView?.lastUpdate;
824
+
825
+ expect(view).toBeDefined();
826
+ expect(view?.values[0].asset.type).toBe("text");
827
+ expect(view?.values[0].asset.value).toBe("chat message");
828
+ expect(updateNumber).toBe(1);
829
+
830
+ await waitFor(() => {
831
+ expect(deferredResolve).toBeDefined();
832
+ });
833
+
834
+ if (deferredResolve) {
835
+ deferredResolve({
836
+ asset: {
837
+ id: "3",
838
+ type: "chat-message",
839
+ value: {
840
+ asset: {
841
+ id: "4",
842
+ type: "text",
843
+ value: "async content",
844
+ },
845
+ },
846
+ },
847
+ });
848
+ }
849
+
850
+ await waitFor(() => {
851
+ expect(updateNumber).toBe(2);
852
+ });
853
+
854
+ view = (player.getState() as InProgressState).controllers.view.currentView
855
+ ?.lastUpdate;
856
+
857
+ expect(view?.values[1].asset.type).toBe("text");
858
+ expect(view?.values[1].asset.value).toBe("async content");
859
+
860
+ if (deferredResolve) {
861
+ deferredResolve({
862
+ asset: {
863
+ id: "5",
864
+ type: "text",
865
+ value: "chained async content",
866
+ },
867
+ });
868
+ }
869
+
870
+ await waitFor(() => {
871
+ expect(updateNumber).toBe(3);
872
+ });
873
+
874
+ view = (player.getState() as InProgressState).controllers.view.currentView
875
+ ?.lastUpdate;
876
+
877
+ expect(view?.values[2].asset.type).toBe("text");
878
+ expect(view?.values[2].asset.value).toBe("chained async content");
879
+ });
551
880
  });
552
881
 
553
882
  describe("parser", () => {
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { asyncTransform } from "..";
3
+ import { Builder } from "@player-ui/player";
4
+
5
+ describe("asyncTransform", () => {
6
+ const asset = Builder.asset({
7
+ id: "2",
8
+ type: "text",
9
+ value: "chat message",
10
+ });
11
+
12
+ it("generates wrapper asset with asset", async () => {
13
+ const transformedAsset = asyncTransform("1", "collection", asset);
14
+
15
+ expect(transformedAsset).toMatchSnapshot();
16
+ });
17
+
18
+ it("generates wrapper asset with flatten is false", async () => {
19
+ const transformedAsset = asyncTransform("1", "collection", asset, false);
20
+
21
+ expect(transformedAsset?.children?.[0]?.value.values[1].flatten).toBe(
22
+ false,
23
+ );
24
+ });
25
+ });
package/src/index.ts CHANGED
@@ -16,6 +16,7 @@ import queueMicrotask from "queue-microtask";
16
16
  import { omit } from "timm";
17
17
 
18
18
  export * from "./types";
19
+ export * from "./transform";
19
20
 
20
21
  export interface AsyncNodePluginOptions {
21
22
  /** A set of plugins to load */
@@ -0,0 +1,40 @@
1
+ import { Builder, NodeType } from "@player-ui/player";
2
+ import type { AsyncTransformFunc } from "./types";
3
+
4
+ /**
5
+ * Util function to generate transform function for async asset
6
+ * @param asset - async asset to apply beforeResolve transform
7
+ * @param transformedAssetType: transformed asset type for rendering
8
+ * @param wrapperAssetType: container asset type
9
+ * @param flatten: flatten the streamed in content
10
+ * @returns - wrapper asset with children of transformed asset and async node
11
+ */
12
+
13
+ export const asyncTransform: AsyncTransformFunc = (
14
+ assetId,
15
+ wrapperAssetType,
16
+ asset,
17
+ flatten,
18
+ ) => {
19
+ const id = "async-" + assetId;
20
+
21
+ const asyncNode = Builder.asyncNode(id, flatten);
22
+ let multiNode;
23
+ let assetNode;
24
+
25
+ if (asset) {
26
+ assetNode = Builder.assetWrapper(asset);
27
+ multiNode = Builder.multiNode(assetNode, asyncNode);
28
+ } else {
29
+ multiNode = Builder.multiNode(asyncNode);
30
+ }
31
+
32
+ const wrapperAsset = Builder.asset({
33
+ id: wrapperAssetType + "-" + id,
34
+ type: wrapperAssetType,
35
+ });
36
+
37
+ Builder.addChild(wrapperAsset, ["values"], multiNode);
38
+
39
+ return wrapperAsset;
40
+ };
package/src/types.ts CHANGED
@@ -4,3 +4,10 @@ export type AsyncNodeHandler = (
4
4
  node: Node.Node,
5
5
  update: (object: any) => void,
6
6
  ) => void;
7
+
8
+ export type AsyncTransformFunc = (
9
+ id: string,
10
+ wrapperAssetType: string,
11
+ asset?: Node.Node,
12
+ flatten?: boolean,
13
+ ) => Node.Asset;
package/types/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Player, PlayerPlugin, Node, ViewInstance, Parser, ViewPlugin, Resolver } from "@player-ui/player";
2
2
  import { AsyncParallelBailHook } from "tapable-ts";
3
3
  export * from "./types";
4
+ export * from "./transform";
4
5
  export interface AsyncNodePluginOptions {
5
6
  /** A set of plugins to load */
6
7
  plugins?: AsyncNodeViewPlugin[];
@@ -0,0 +1,11 @@
1
+ import type { AsyncTransformFunc } from "./types";
2
+ /**
3
+ * Util function to generate transform function for async asset
4
+ * @param asset - async asset to apply beforeResolve transform
5
+ * @param transformedAssetType: transformed asset type for rendering
6
+ * @param wrapperAssetType: container asset type
7
+ * @param flatten: flatten the streamed in content
8
+ * @returns - wrapper asset with children of transformed asset and async node
9
+ */
10
+ export declare const asyncTransform: AsyncTransformFunc;
11
+ //# sourceMappingURL=transform.d.ts.map
package/types/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import type { Node } from "@player-ui/player";
2
2
  export type AsyncNodeHandler = (node: Node.Node, update: (object: any) => void) => void;
3
+ export type AsyncTransformFunc = (id: string, wrapperAssetType: string, asset?: Node.Node, flatten?: boolean) => Node.Asset;
3
4
  //# sourceMappingURL=types.d.ts.map