@ninetailed/experience.js-utils 3.0.0 → 3.0.1-beta.0
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/index.cjs +197 -0
- package/package.json +4 -4
package/index.cjs
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var experience_jsShared = require('@ninetailed/experience.js-shared');
|
|
6
|
+
var zod = require('zod');
|
|
7
|
+
|
|
8
|
+
const Audience = zod.z.object({
|
|
9
|
+
id: zod.z.string()
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const Config = zod.z.object({
|
|
13
|
+
distribution: zod.z.array(zod.z.number()).default([0.5, 0.5]),
|
|
14
|
+
traffic: zod.z.number().default(0),
|
|
15
|
+
components: zod.z.array(zod.z.object({
|
|
16
|
+
baseline: zod.z.object({
|
|
17
|
+
id: zod.z.string().default('')
|
|
18
|
+
}),
|
|
19
|
+
variants: zod.z.array(zod.z.object({
|
|
20
|
+
id: zod.z.string().default(''),
|
|
21
|
+
hidden: zod.z.boolean().default(false)
|
|
22
|
+
}))
|
|
23
|
+
})).default([{
|
|
24
|
+
baseline: {
|
|
25
|
+
id: ''
|
|
26
|
+
},
|
|
27
|
+
variants: [{
|
|
28
|
+
id: '',
|
|
29
|
+
hidden: false
|
|
30
|
+
}]
|
|
31
|
+
}])
|
|
32
|
+
});
|
|
33
|
+
// export interface Config {
|
|
34
|
+
// distribution: number[];
|
|
35
|
+
// traffic: number;
|
|
36
|
+
// components: BaselineWithVariantRefs[];
|
|
37
|
+
// }
|
|
38
|
+
|
|
39
|
+
const Variant = zod.z.object({
|
|
40
|
+
id: zod.z.string()
|
|
41
|
+
}).catchall(zod.z.unknown());
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Zod helper for parsing arrays and ignore items not specified in the schema
|
|
45
|
+
*
|
|
46
|
+
* @param zodUnion - union of known types
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const binaryArraySchema = arrayIgnoreUnknown(z.union([z.literal('0'), z.literal('1')]))
|
|
50
|
+
* type BinaryArray = z.TypeOf<typeof binaryArraySchema>
|
|
51
|
+
*
|
|
52
|
+
* const binaryArray: BinaryArray = binaryArraySchema.parse(['0', '1', '2', '0'])
|
|
53
|
+
* console.log(binaryArray) // ['0', '1', '0']
|
|
54
|
+
*/
|
|
55
|
+
function zodArrayIgnoreUnknown(zodType) {
|
|
56
|
+
const isKnownItem = item => zodType.safeParse(item).success;
|
|
57
|
+
return zod.z.preprocess(val => toSafeArray(val).filter(isKnownItem), zod.z.array(zodType));
|
|
58
|
+
}
|
|
59
|
+
function toSafeArray(item) {
|
|
60
|
+
if (isArray(item)) {
|
|
61
|
+
return item;
|
|
62
|
+
}
|
|
63
|
+
return [item];
|
|
64
|
+
}
|
|
65
|
+
function isArray(item) {
|
|
66
|
+
return Array.isArray(item);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const ExperienceSchema = zod.z.object({
|
|
70
|
+
id: zod.z.string(),
|
|
71
|
+
/**
|
|
72
|
+
* The name of the experience (Short Text)
|
|
73
|
+
*/
|
|
74
|
+
name: zod.z.string(),
|
|
75
|
+
/**
|
|
76
|
+
* The type if the experience (nt_experiment | nt_personalization)
|
|
77
|
+
*/
|
|
78
|
+
type: zod.z.union([zod.z.string().regex(/^nt_experiment$/g), zod.z.string().regex(/^nt_personalization$/g)]),
|
|
79
|
+
/**
|
|
80
|
+
* The config of the experience (JSON)
|
|
81
|
+
*/
|
|
82
|
+
config: Config.default({}),
|
|
83
|
+
/**
|
|
84
|
+
* The audience of the experience (Audience)
|
|
85
|
+
*/
|
|
86
|
+
audience: Audience.optional().nullable(),
|
|
87
|
+
/**
|
|
88
|
+
* All used variants of the experience (References to other Content Types)
|
|
89
|
+
*/
|
|
90
|
+
variants: zodArrayIgnoreUnknown(Variant).default([])
|
|
91
|
+
});
|
|
92
|
+
const parse$1 = input => {
|
|
93
|
+
const output = ExperienceSchema.parse(input);
|
|
94
|
+
return Object.assign(Object.assign({}, output), {
|
|
95
|
+
variants: input.variants
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
const safeParse$1 = input => {
|
|
99
|
+
const output = ExperienceSchema.safeParse(input);
|
|
100
|
+
if (!output.success) {
|
|
101
|
+
return output;
|
|
102
|
+
}
|
|
103
|
+
return Object.assign(Object.assign({}, output), {
|
|
104
|
+
data: Object.assign(Object.assign({}, output.data), {
|
|
105
|
+
variants: input.variants
|
|
106
|
+
})
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
const Experience = Object.assign(Object.assign({}, ExperienceSchema), {
|
|
110
|
+
parse: parse$1,
|
|
111
|
+
safeParse: safeParse$1
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const ExperimentSchema = ExperienceSchema.extend({
|
|
115
|
+
type: zod.z.string().regex(/^nt_experiment$/g)
|
|
116
|
+
});
|
|
117
|
+
const parse = input => {
|
|
118
|
+
const output = ExperimentSchema.parse(input);
|
|
119
|
+
return Object.assign(Object.assign({}, output), {
|
|
120
|
+
variants: input.variants
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
const safeParse = input => {
|
|
124
|
+
const output = ExperimentSchema.safeParse(input);
|
|
125
|
+
if (!output.success) {
|
|
126
|
+
return output;
|
|
127
|
+
}
|
|
128
|
+
return Object.assign(Object.assign({}, output), {
|
|
129
|
+
data: Object.assign(Object.assign({}, output.data), {
|
|
130
|
+
variants: input.variants
|
|
131
|
+
})
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
const Experiment = Object.assign(Object.assign({}, ExperimentSchema), {
|
|
135
|
+
parse,
|
|
136
|
+
safeParse
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
class ExperienceMapper {
|
|
140
|
+
static isExperienceEntry(experience) {
|
|
141
|
+
return Experience.safeParse(experience).success;
|
|
142
|
+
}
|
|
143
|
+
static mapExperience(experience) {
|
|
144
|
+
const parsedExperience = Experience.safeParse(experience);
|
|
145
|
+
if (!parsedExperience.success) {
|
|
146
|
+
experience_jsShared.logger.warn('[Ninetailed ExperienceMapper]', 'Error parsing experience', parsedExperience.error.format());
|
|
147
|
+
throw new Error(`[Ninetailed ExperienceMapper] The Experience Input is not valid. Please filter data first with "ExperienceMapper.isExperienceEntry".\n${JSON.stringify(parsedExperience.error.format(), null, 2)}`);
|
|
148
|
+
}
|
|
149
|
+
const {
|
|
150
|
+
id,
|
|
151
|
+
type,
|
|
152
|
+
audience,
|
|
153
|
+
config,
|
|
154
|
+
variants
|
|
155
|
+
} = parsedExperience.data;
|
|
156
|
+
const {
|
|
157
|
+
components,
|
|
158
|
+
traffic
|
|
159
|
+
} = config;
|
|
160
|
+
return Object.assign(Object.assign({
|
|
161
|
+
id,
|
|
162
|
+
type: type
|
|
163
|
+
}, audience ? {
|
|
164
|
+
audience
|
|
165
|
+
} : {}), {
|
|
166
|
+
trafficAllocation: traffic,
|
|
167
|
+
distribution: config.distribution.map((percentage, index) => ({
|
|
168
|
+
index,
|
|
169
|
+
start: config.distribution.slice(0, index).reduce((a, b) => a + b, 0),
|
|
170
|
+
end: config.distribution.slice(0, index + 1).reduce((a, b) => a + b, 0)
|
|
171
|
+
})),
|
|
172
|
+
components: components.map(component => ({
|
|
173
|
+
baseline: component.baseline,
|
|
174
|
+
variants: component.variants.map(variantRef => {
|
|
175
|
+
if (variantRef.hidden) {
|
|
176
|
+
return variantRef;
|
|
177
|
+
}
|
|
178
|
+
const matchingVariant = variants.find(variant => variant.id === variantRef.id);
|
|
179
|
+
return matchingVariant !== null && matchingVariant !== void 0 ? matchingVariant : null;
|
|
180
|
+
}).filter(variant => variant !== null)
|
|
181
|
+
}))
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
static isExperimentEntry(experiment) {
|
|
185
|
+
return Experiment.safeParse(experiment).success;
|
|
186
|
+
}
|
|
187
|
+
static mapExperiment(experiment) {
|
|
188
|
+
return ExperienceMapper.mapExperience(experiment);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
exports.Audience = Audience;
|
|
193
|
+
exports.Config = Config;
|
|
194
|
+
exports.Experience = Experience;
|
|
195
|
+
exports.ExperienceMapper = ExperienceMapper;
|
|
196
|
+
exports.Experiment = Experiment;
|
|
197
|
+
exports.Variant = Variant;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ninetailed/experience.js-utils",
|
|
3
|
-
"version": "3.0.0",
|
|
3
|
+
"version": "3.0.1-beta.0",
|
|
4
4
|
"module": "./index.js",
|
|
5
|
-
"main": "./index.
|
|
5
|
+
"main": "./index.cjs",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"types": "./index.d.ts",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@ninetailed/experience.js": "3.0.0",
|
|
10
|
-
"@ninetailed/experience.js-shared": "3.0.0",
|
|
9
|
+
"@ninetailed/experience.js": "3.0.1-beta.0",
|
|
10
|
+
"@ninetailed/experience.js-shared": "3.0.1-beta.0",
|
|
11
11
|
"zod": "3.20.2"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {}
|