@player-tools/fluent 0.12.1--canary.241.6077

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.
Files changed (134) hide show
  1. package/dist/cjs/index.cjs +2396 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/index.legacy-esm.js +2276 -0
  4. package/dist/index.mjs +2276 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/package.json +38 -0
  7. package/src/core/base-builder/__tests__/fluent-builder-base.test.ts +2423 -0
  8. package/src/core/base-builder/__tests__/fluent-partial.test.ts +179 -0
  9. package/src/core/base-builder/__tests__/id-generator.test.ts +658 -0
  10. package/src/core/base-builder/__tests__/registry.test.ts +534 -0
  11. package/src/core/base-builder/__tests__/resolution-mixed-arrays.test.ts +319 -0
  12. package/src/core/base-builder/__tests__/resolution-pipeline.test.ts +416 -0
  13. package/src/core/base-builder/__tests__/resolution-switches.test.ts +468 -0
  14. package/src/core/base-builder/__tests__/resolution-templates.test.ts +255 -0
  15. package/src/core/base-builder/__tests__/switch.test.ts +815 -0
  16. package/src/core/base-builder/__tests__/template.test.ts +596 -0
  17. package/src/core/base-builder/__tests__/value-extraction.test.ts +200 -0
  18. package/src/core/base-builder/__tests__/value-storage.test.ts +459 -0
  19. package/src/core/base-builder/conditional/index.ts +64 -0
  20. package/src/core/base-builder/context.ts +152 -0
  21. package/src/core/base-builder/errors.ts +69 -0
  22. package/src/core/base-builder/fluent-builder-base.ts +308 -0
  23. package/src/core/base-builder/guards.ts +137 -0
  24. package/src/core/base-builder/id/generator.ts +290 -0
  25. package/src/core/base-builder/id/registry.ts +152 -0
  26. package/src/core/base-builder/index.ts +72 -0
  27. package/src/core/base-builder/resolution/path-resolver.ts +116 -0
  28. package/src/core/base-builder/resolution/pipeline.ts +103 -0
  29. package/src/core/base-builder/resolution/steps/__tests__/nested-asset-wrappers.test.ts +206 -0
  30. package/src/core/base-builder/resolution/steps/asset-id.ts +77 -0
  31. package/src/core/base-builder/resolution/steps/asset-wrappers.ts +64 -0
  32. package/src/core/base-builder/resolution/steps/builders.ts +84 -0
  33. package/src/core/base-builder/resolution/steps/mixed-arrays.ts +95 -0
  34. package/src/core/base-builder/resolution/steps/nested-asset-wrappers.ts +124 -0
  35. package/src/core/base-builder/resolution/steps/static-values.ts +35 -0
  36. package/src/core/base-builder/resolution/steps/switches.ts +71 -0
  37. package/src/core/base-builder/resolution/steps/templates.ts +40 -0
  38. package/src/core/base-builder/resolution/value-resolver.ts +333 -0
  39. package/src/core/base-builder/storage/auxiliary-storage.ts +82 -0
  40. package/src/core/base-builder/storage/value-storage.ts +282 -0
  41. package/src/core/base-builder/types.ts +266 -0
  42. package/src/core/base-builder/utils.ts +10 -0
  43. package/src/core/flow/__tests__/index.test.ts +292 -0
  44. package/src/core/flow/index.ts +118 -0
  45. package/src/core/index.ts +8 -0
  46. package/src/core/mocks/generated/action.builder.ts +92 -0
  47. package/src/core/mocks/generated/choice-item.builder.ts +120 -0
  48. package/src/core/mocks/generated/choice.builder.ts +134 -0
  49. package/src/core/mocks/generated/collection.builder.ts +93 -0
  50. package/src/core/mocks/generated/field-collection.builder.ts +86 -0
  51. package/src/core/mocks/generated/index.ts +10 -0
  52. package/src/core/mocks/generated/info.builder.ts +64 -0
  53. package/src/core/mocks/generated/input.builder.ts +63 -0
  54. package/src/core/mocks/generated/overview-collection.builder.ts +65 -0
  55. package/src/core/mocks/generated/splash-collection.builder.ts +93 -0
  56. package/src/core/mocks/generated/text.builder.ts +47 -0
  57. package/src/core/mocks/index.ts +1 -0
  58. package/src/core/mocks/types/action.ts +92 -0
  59. package/src/core/mocks/types/choice.ts +129 -0
  60. package/src/core/mocks/types/collection.ts +140 -0
  61. package/src/core/mocks/types/info.ts +7 -0
  62. package/src/core/mocks/types/input.ts +7 -0
  63. package/src/core/mocks/types/text.ts +5 -0
  64. package/src/core/schema/__tests__/index.test.ts +127 -0
  65. package/src/core/schema/index.ts +195 -0
  66. package/src/core/schema/types.ts +7 -0
  67. package/src/core/switch/__tests__/index.test.ts +156 -0
  68. package/src/core/switch/index.ts +81 -0
  69. package/src/core/tagged-template/README.md +448 -0
  70. package/src/core/tagged-template/__tests__/extract-bindings-from-schema.test.ts +207 -0
  71. package/src/core/tagged-template/__tests__/index.test.ts +190 -0
  72. package/src/core/tagged-template/__tests__/schema-std-integration.test.ts +580 -0
  73. package/src/core/tagged-template/binding.ts +95 -0
  74. package/src/core/tagged-template/expression.ts +92 -0
  75. package/src/core/tagged-template/extract-bindings-from-schema.ts +120 -0
  76. package/src/core/tagged-template/index.ts +5 -0
  77. package/src/core/tagged-template/std.ts +472 -0
  78. package/src/core/tagged-template/types.ts +123 -0
  79. package/src/core/template/__tests__/index.test.ts +380 -0
  80. package/src/core/template/index.ts +196 -0
  81. package/src/core/utils/index.ts +160 -0
  82. package/src/fp/README.md +411 -0
  83. package/src/fp/__tests__/index.test.ts +1178 -0
  84. package/src/fp/index.ts +386 -0
  85. package/src/gen/common.ts +15 -0
  86. package/src/index.ts +5 -0
  87. package/src/types.ts +203 -0
  88. package/types/core/base-builder/conditional/index.d.ts +21 -0
  89. package/types/core/base-builder/context.d.ts +39 -0
  90. package/types/core/base-builder/errors.d.ts +45 -0
  91. package/types/core/base-builder/fluent-builder-base.d.ts +147 -0
  92. package/types/core/base-builder/guards.d.ts +58 -0
  93. package/types/core/base-builder/id/generator.d.ts +69 -0
  94. package/types/core/base-builder/id/registry.d.ts +93 -0
  95. package/types/core/base-builder/index.d.ts +9 -0
  96. package/types/core/base-builder/resolution/path-resolver.d.ts +15 -0
  97. package/types/core/base-builder/resolution/pipeline.d.ts +27 -0
  98. package/types/core/base-builder/resolution/steps/asset-id.d.ts +14 -0
  99. package/types/core/base-builder/resolution/steps/asset-wrappers.d.ts +14 -0
  100. package/types/core/base-builder/resolution/steps/builders.d.ts +14 -0
  101. package/types/core/base-builder/resolution/steps/mixed-arrays.d.ts +14 -0
  102. package/types/core/base-builder/resolution/steps/nested-asset-wrappers.d.ts +14 -0
  103. package/types/core/base-builder/resolution/steps/static-values.d.ts +14 -0
  104. package/types/core/base-builder/resolution/steps/switches.d.ts +15 -0
  105. package/types/core/base-builder/resolution/steps/templates.d.ts +14 -0
  106. package/types/core/base-builder/resolution/value-resolver.d.ts +62 -0
  107. package/types/core/base-builder/storage/auxiliary-storage.d.ts +50 -0
  108. package/types/core/base-builder/storage/value-storage.d.ts +82 -0
  109. package/types/core/base-builder/types.d.ts +183 -0
  110. package/types/core/base-builder/utils.d.ts +2 -0
  111. package/types/core/flow/index.d.ts +23 -0
  112. package/types/core/index.d.ts +8 -0
  113. package/types/core/mocks/index.d.ts +2 -0
  114. package/types/core/mocks/types/action.d.ts +58 -0
  115. package/types/core/mocks/types/choice.d.ts +95 -0
  116. package/types/core/mocks/types/collection.d.ts +102 -0
  117. package/types/core/mocks/types/info.d.ts +7 -0
  118. package/types/core/mocks/types/input.d.ts +7 -0
  119. package/types/core/mocks/types/text.d.ts +5 -0
  120. package/types/core/schema/index.d.ts +34 -0
  121. package/types/core/schema/types.d.ts +5 -0
  122. package/types/core/switch/index.d.ts +21 -0
  123. package/types/core/tagged-template/binding.d.ts +19 -0
  124. package/types/core/tagged-template/expression.d.ts +11 -0
  125. package/types/core/tagged-template/extract-bindings-from-schema.d.ts +7 -0
  126. package/types/core/tagged-template/index.d.ts +6 -0
  127. package/types/core/tagged-template/std.d.ts +174 -0
  128. package/types/core/tagged-template/types.d.ts +69 -0
  129. package/types/core/template/index.d.ts +97 -0
  130. package/types/core/utils/index.d.ts +47 -0
  131. package/types/fp/index.d.ts +149 -0
  132. package/types/gen/common.d.ts +6 -0
  133. package/types/index.d.ts +3 -0
  134. package/types/types.d.ts +163 -0
@@ -0,0 +1,195 @@
1
+ import type { Schema, Language } from "@player-ui/types";
2
+ import { SyncWaterfallHook } from "tapable-ts";
3
+ import { dequal } from "dequal";
4
+ import { SchemaGeneratorInput } from "./types";
5
+
6
+ /** Symbol to indicate that a schema node should be generated with a different name */
7
+ export const SchemaTypeName = Symbol.for("Schema Rename");
8
+
9
+ export type LoggingInterface = Pick<Console, "warn" | "error" | "log">;
10
+
11
+ interface SchemaChildren {
12
+ /** Object property that will be used to create the intermediate type */
13
+ name: string;
14
+
15
+ /** Object properties children that will be parsed */
16
+ child: Record<string, unknown>;
17
+ }
18
+
19
+ type SchemaNode = (Schema.DataType | Language.DataTypeRef) & {
20
+ /** Overwrite the name of the generated type */
21
+ [SchemaTypeName]?: string;
22
+ };
23
+
24
+ interface GeneratedDataType {
25
+ /** The SchemaNode that was generated */
26
+ node: SchemaNode;
27
+ /** How many times it has been generated */
28
+ count: number;
29
+ }
30
+
31
+ /**
32
+ * Type Guard for the `Schema.DataType` and `Language.DataTypeRef` type
33
+ * A bit hacky but since `Schema.Schema` must have a `Schema.DataType` as
34
+ * the final product we have to call it that even if it is a `Language.DataTypeRef`
35
+ */
36
+ const isTypeDef = (property: SchemaNode): property is Schema.DataType => {
37
+ return (property as Schema.DataType).type !== undefined;
38
+ };
39
+
40
+ /**
41
+ * Generator for `Schema.Schema` Objects
42
+ */
43
+ export class SchemaGenerator {
44
+ private children: SchemaChildren[] = [];
45
+ private generatedDataTypes: Map<string, GeneratedDataType> = new Map();
46
+ private typeNameCache: Map<string, string> = new Map();
47
+ private logger: LoggingInterface;
48
+
49
+ public hooks = {
50
+ createSchemaNode: new SyncWaterfallHook<
51
+ [
52
+ node: Schema.DataType,
53
+ originalProperty: Record<string | symbol, unknown>,
54
+ ]
55
+ >(),
56
+ };
57
+
58
+ constructor(logger?: LoggingInterface) {
59
+ this.logger = logger ?? console;
60
+ }
61
+
62
+ /**
63
+ * Converts an object to a `Schema.Schema` representation
64
+ * Optimized to minimize object operations and memory allocations
65
+ */
66
+ public toSchema = (schema: SchemaGeneratorInput): Schema.Schema => {
67
+ // Clear state efficiently
68
+ this.children.length = 0;
69
+ this.generatedDataTypes.clear();
70
+ this.typeNameCache.clear();
71
+
72
+ const newSchema: Schema.Schema = {
73
+ ROOT: {},
74
+ };
75
+
76
+ // Pre-allocate arrays and use for...in for better performance
77
+ const rootKeys = Object.keys(schema);
78
+ for (let i = 0; i < rootKeys.length; i++) {
79
+ const property = rootKeys[i];
80
+ const subType = schema[property] as SchemaNode;
81
+ newSchema.ROOT[property] = this.hooks.createSchemaNode.call(
82
+ this.processChild(property, subType),
83
+ subType as unknown as Record<string | symbol, unknown>,
84
+ );
85
+ }
86
+
87
+ // Process children using optimized iteration
88
+ while (this.children.length > 0) {
89
+ const { name, child } = this.children.pop()!;
90
+ const typeDef: Record<string, Schema.DataType> = {};
91
+
92
+ const childKeys = Object.keys(child);
93
+ for (let i = 0; i < childKeys.length; i++) {
94
+ const property = childKeys[i];
95
+ const subType = child[property] as SchemaNode;
96
+ typeDef[property] = this.hooks.createSchemaNode.call(
97
+ this.processChild(property, subType),
98
+ subType as unknown as Record<string | symbol, unknown>,
99
+ );
100
+ }
101
+ newSchema[name] = typeDef;
102
+ }
103
+
104
+ return newSchema;
105
+ };
106
+
107
+ private processChild(property: string, subType: SchemaNode): Schema.DataType {
108
+ if (isTypeDef(subType)) {
109
+ return subType;
110
+ }
111
+
112
+ let intermediateType: Schema.DataType;
113
+ let child: Record<string, unknown>;
114
+
115
+ if (Array.isArray(subType)) {
116
+ if (subType.length > 1) {
117
+ this.logger.warn(
118
+ `Type ${property} has multiple types in array, should only contain one top level object type. Only taking first defined type`,
119
+ );
120
+ }
121
+
122
+ const subTypeName = subType[0][SchemaTypeName] ?? property;
123
+ intermediateType = this.makePlaceholderArrayType(subTypeName);
124
+ child = subType[0] as Record<string, unknown>;
125
+ } else {
126
+ const subTypeName = subType[SchemaTypeName] ?? property;
127
+ intermediateType = this.makePlaceholderType(subTypeName);
128
+ child = subType as unknown as Record<string, unknown>;
129
+ }
130
+
131
+ const typeName = intermediateType.type;
132
+
133
+ if (this.generatedDataTypes.has(typeName)) {
134
+ const generatedType = this.generatedDataTypes.get(typeName)!;
135
+
136
+ // Use deep equality check to ensure types are actually different
137
+ if (
138
+ !dequal(child, this.generatedDataTypes.get(typeName)?.node as object)
139
+ ) {
140
+ generatedType.count += 1;
141
+ const newTypeName = `${typeName}${generatedType.count}`;
142
+ intermediateType = {
143
+ ...intermediateType,
144
+ type: newTypeName,
145
+ };
146
+ this.logger.warn(
147
+ `WARNING: Generated two intermediate types with the name: ${typeName} that are of different shapes, using artificial type ${newTypeName}`,
148
+ );
149
+
150
+ // Add new type mapping for the new artificial type
151
+ this.generatedDataTypes.set(newTypeName, {
152
+ node: subType,
153
+ count: 1,
154
+ });
155
+ this.children.push({ name: newTypeName, child });
156
+ return intermediateType;
157
+ }
158
+ } else {
159
+ this.generatedDataTypes.set(typeName, {
160
+ node: subType,
161
+ count: 1,
162
+ });
163
+ }
164
+
165
+ this.children.push({ name: intermediateType.type, child });
166
+ return intermediateType;
167
+ }
168
+
169
+ /**
170
+ * Cached type name generation
171
+ */
172
+ private makePlaceholderType = (typeName: string): Schema.DataType => {
173
+ let cachedName = this.typeNameCache.get(typeName);
174
+ if (!cachedName) {
175
+ cachedName = `${typeName}Type`;
176
+ this.typeNameCache.set(typeName, cachedName);
177
+ }
178
+ return { type: cachedName };
179
+ };
180
+
181
+ /**
182
+ * Cached array type name generation
183
+ */
184
+ private makePlaceholderArrayType(typeName: string): Schema.DataType {
185
+ let cachedName = this.typeNameCache.get(typeName);
186
+ if (!cachedName) {
187
+ cachedName = `${typeName}Type`;
188
+ this.typeNameCache.set(typeName, cachedName);
189
+ }
190
+ return {
191
+ type: cachedName,
192
+ isArray: true,
193
+ };
194
+ }
195
+ }
@@ -0,0 +1,7 @@
1
+ import type { Schema } from "@player-ui/types";
2
+
3
+ type SchemaDataType = Schema.DataType | Schema.RecordType | Schema.ArrayType;
4
+ export type SchemaGeneratorInput = Record<
5
+ string,
6
+ SchemaDataType | Record<string, unknown> | unknown[]
7
+ >;
@@ -0,0 +1,156 @@
1
+ import { describe, test, expect, beforeEach } from "vitest";
2
+ import { switch_ } from "../index";
3
+ import type { AssetWrapperOrSwitch } from "@player-ui/types";
4
+ import { text } from "../../mocks";
5
+ import { expression } from "../../tagged-template";
6
+ import { resetGlobalIdSet } from "../../base-builder";
7
+
8
+ describe("Switch", () => {
9
+ beforeEach(() => {
10
+ resetGlobalIdSet();
11
+ });
12
+
13
+ test("create a static switch by default", () => {
14
+ const cases = [{ case: true, asset: text().withValue("Hello") }];
15
+ const result = switch_({ cases })({ parentId: "parent" });
16
+
17
+ const expected: AssetWrapperOrSwitch = {
18
+ staticSwitch: [
19
+ {
20
+ case: true,
21
+ asset: {
22
+ value: "Hello",
23
+ type: "text",
24
+ id: "parent-staticSwitch-0-text",
25
+ },
26
+ },
27
+ ],
28
+ };
29
+
30
+ expect(result).toEqual(expected);
31
+ });
32
+
33
+ test("create a dynamic switch when isDynamic is true", () => {
34
+ const cases = [{ case: true, asset: text().withValue("Hello") }];
35
+ const result = switch_({ cases, isDynamic: true })({ parentId: "parent" });
36
+
37
+ const expected: AssetWrapperOrSwitch = {
38
+ dynamicSwitch: [
39
+ {
40
+ case: true,
41
+ asset: {
42
+ value: "Hello",
43
+ type: "text",
44
+ id: "parent-dynamicSwitch-0-text",
45
+ },
46
+ },
47
+ ],
48
+ };
49
+
50
+ expect(result).toEqual(expected);
51
+ });
52
+
53
+ test("convert string case expressions to strings", () => {
54
+ const cases = [{ case: "foo == true", asset: text().withValue("Hello") }];
55
+ const result = switch_({ cases })({ parentId: "parent" });
56
+
57
+ const expected: AssetWrapperOrSwitch = {
58
+ staticSwitch: [
59
+ {
60
+ case: "foo == true",
61
+ asset: {
62
+ value: "Hello",
63
+ type: "text",
64
+ id: "parent-staticSwitch-0-text",
65
+ },
66
+ },
67
+ ],
68
+ };
69
+
70
+ expect(result).toEqual(expected);
71
+ });
72
+
73
+ test("handle TaggedTemplateValue case expressions", () => {
74
+ const templateValue = expression`foo == true`;
75
+ const cases = [{ case: templateValue, asset: text().withValue("Hello") }];
76
+ const result = switch_({ cases })({ parentId: "parent" });
77
+
78
+ const expected: AssetWrapperOrSwitch = {
79
+ staticSwitch: [
80
+ {
81
+ case: "@[foo == true]@",
82
+ asset: {
83
+ value: "Hello",
84
+ type: "text",
85
+ id: "parent-staticSwitch-0-text",
86
+ },
87
+ },
88
+ ],
89
+ };
90
+
91
+ expect(result).toEqual(expected);
92
+ });
93
+
94
+ test("preserve ids for assets with ids", () => {
95
+ const assetWithId = text().withId("custom-id").withValue("Hello");
96
+ const result = switch_({
97
+ cases: [{ case: true, asset: assetWithId }],
98
+ })({ parentId: "parent" });
99
+
100
+ const expected: AssetWrapperOrSwitch = {
101
+ staticSwitch: [
102
+ {
103
+ case: true,
104
+ asset: { value: "Hello", type: "text", id: "custom-id" },
105
+ },
106
+ ],
107
+ };
108
+
109
+ expect(result).toEqual(expected);
110
+ });
111
+
112
+ test("handle multiple cases", () => {
113
+ const firstAsset = text().withValue("First");
114
+ const secondAsset = text().withValue("Second");
115
+ const defaultAsset = text().withValue("Default");
116
+
117
+ const result = switch_({
118
+ cases: [
119
+ { case: '{{name.first}} == "John"', asset: firstAsset },
120
+ { case: '{{name.first}} == "Jane"', asset: secondAsset },
121
+ { case: true, asset: defaultAsset },
122
+ ],
123
+ })({ parentId: "parent" });
124
+
125
+ const expected: AssetWrapperOrSwitch = {
126
+ staticSwitch: [
127
+ {
128
+ case: '{{name.first}} == "John"',
129
+ asset: {
130
+ value: "First",
131
+ type: "text",
132
+ id: "parent-staticSwitch-0-text",
133
+ },
134
+ },
135
+ {
136
+ case: '{{name.first}} == "Jane"',
137
+ asset: {
138
+ value: "Second",
139
+ type: "text",
140
+ id: "parent-staticSwitch-1-text",
141
+ },
142
+ },
143
+ {
144
+ case: true,
145
+ asset: {
146
+ value: "Default",
147
+ type: "text",
148
+ id: "parent-staticSwitch-2-text",
149
+ },
150
+ },
151
+ ],
152
+ };
153
+
154
+ expect(result).toEqual(expected);
155
+ });
156
+ });
@@ -0,0 +1,81 @@
1
+ import type { Asset, AssetWrapper } from "@player-ui/types";
2
+ import {
3
+ type BaseBuildContext,
4
+ genId,
5
+ isFluentBuilder,
6
+ BranchTypes,
7
+ } from "../base-builder";
8
+ import {
9
+ isTaggedTemplateValue,
10
+ type TaggedTemplateValue,
11
+ } from "../tagged-template";
12
+
13
+ type CaseExpression = boolean | string | TaggedTemplateValue;
14
+
15
+ interface SwitchCase<
16
+ T extends Asset,
17
+ C extends BaseBuildContext = BaseBuildContext,
18
+ > {
19
+ readonly case: CaseExpression;
20
+ readonly asset: T | { build(context?: C): T };
21
+ }
22
+
23
+ interface SwitchArgs<
24
+ T extends Asset,
25
+ C extends BaseBuildContext = BaseBuildContext,
26
+ > {
27
+ readonly cases: ReadonlyArray<SwitchCase<T, C>>;
28
+ readonly isDynamic?: boolean;
29
+ }
30
+
31
+ function processCaseExpression(exp: CaseExpression): string | boolean {
32
+ if (typeof exp === "boolean") {
33
+ return exp;
34
+ }
35
+
36
+ if (isTaggedTemplateValue(exp)) {
37
+ return exp.toString();
38
+ }
39
+
40
+ return String(exp);
41
+ }
42
+
43
+ /**
44
+ * Creates a switch configuration for conditionally selecting an asset
45
+ * @see https://player-ui.github.io/next/content/assets-views/#switches
46
+ */
47
+ export const switch_ =
48
+ <T extends Asset, C extends BaseBuildContext = BaseBuildContext>({
49
+ cases,
50
+ isDynamic = false,
51
+ }: SwitchArgs<T, C>) =>
52
+ (ctx: C, caseOffset: number = 0): AssetWrapper => {
53
+ const switchType = isDynamic ? "dynamic" : "static";
54
+
55
+ return {
56
+ [`${switchType}Switch`]: cases.map((c, index) => {
57
+ const caseParentCtx: C = {
58
+ ...ctx,
59
+ parentId: ctx.parentId,
60
+ branch: {
61
+ type: BranchTypes.SWITCH,
62
+ kind: switchType,
63
+ index: caseOffset + index,
64
+ },
65
+ } as C;
66
+
67
+ const asset: T =
68
+ isFluentBuilder(c.asset) && "build" in c.asset
69
+ ? (c.asset.build(caseParentCtx) as T)
70
+ : (c.asset as T);
71
+
72
+ return {
73
+ case: processCaseExpression(c.case),
74
+ asset: {
75
+ ...asset,
76
+ id: asset.id ?? genId(caseParentCtx),
77
+ },
78
+ };
79
+ }),
80
+ } as AssetWrapper;
81
+ };