@ninetailed/experience.js-utils 3.0.1-beta.4 → 3.0.2-beta.1

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 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/index.d.ts CHANGED
@@ -1,2 +1,11 @@
1
- export { ExperienceMapper } from './lib';
2
- export { Experience, Experiment, Audience, Config, Variant } from './types';
1
+ export { ExperienceMapper } from './lib/ExperienceMapper';
2
+ export { Audience } from './types/Audience';
3
+ export { Config } from './types/Config';
4
+ export { Experience } from './types/Experience';
5
+ export { Experiment } from './types/Experiment';
6
+ export { Variant } from './types/Variant';
7
+ export type { AudienceLike } from './types/Audience';
8
+ export type { ConfigLike } from './types/Config';
9
+ export type { ExperienceLike } from './types/Experience';
10
+ export type { ExperimentLike } from './types/Experiment';
11
+ export type { VariantLike } from './types/Variant';
@@ -26,10 +26,15 @@ const Config = z.object({
26
26
  }]
27
27
  }])
28
28
  });
29
+ // export interface Config {
30
+ // distribution: number[];
31
+ // traffic: number;
32
+ // components: BaselineWithVariantRefs[];
33
+ // }
29
34
 
30
35
  const Variant = z.object({
31
36
  id: z.string()
32
- }).passthrough();
37
+ }).catchall(z.unknown());
33
38
 
34
39
  /**
35
40
  * Zod helper for parsing arrays and ignore items not specified in the schema
@@ -43,71 +48,100 @@ const Variant = z.object({
43
48
  * const binaryArray: BinaryArray = binaryArraySchema.parse(['0', '1', '2', '0'])
44
49
  * console.log(binaryArray) // ['0', '1', '0']
45
50
  */
46
-
47
51
  function zodArrayIgnoreUnknown(zodType) {
48
52
  const isKnownItem = item => zodType.safeParse(item).success;
49
-
50
53
  return z.preprocess(val => toSafeArray(val).filter(isKnownItem), z.array(zodType));
51
54
  }
52
-
53
55
  function toSafeArray(item) {
54
56
  if (isArray(item)) {
55
57
  return item;
56
58
  }
57
-
58
59
  return [item];
59
60
  }
60
-
61
61
  function isArray(item) {
62
62
  return Array.isArray(item);
63
63
  }
64
64
 
65
- const Experience = z.object({
65
+ const ExperienceSchema = z.object({
66
66
  id: z.string(),
67
-
68
67
  /**
69
68
  * The name of the experience (Short Text)
70
69
  */
71
70
  name: z.string(),
72
-
73
71
  /**
74
72
  * The type if the experience (nt_experiment | nt_personalization)
75
73
  */
76
- type: z.union([z.literal('nt_experiment'), z.literal('nt_personalization')]),
77
-
74
+ type: z.union([z.string().regex(/^nt_experiment$/g), z.string().regex(/^nt_personalization$/g)]),
78
75
  /**
79
76
  * The config of the experience (JSON)
80
77
  */
81
78
  config: Config.default({}),
82
-
83
79
  /**
84
80
  * The audience of the experience (Audience)
85
81
  */
86
82
  audience: Audience.optional().nullable(),
87
-
88
83
  /**
89
84
  * All used variants of the experience (References to other Content Types)
90
85
  */
91
86
  variants: zodArrayIgnoreUnknown(Variant).default([])
92
87
  });
88
+ const parse$1 = input => {
89
+ const output = ExperienceSchema.parse(input);
90
+ return Object.assign(Object.assign({}, output), {
91
+ variants: input.variants
92
+ });
93
+ };
94
+ const safeParse$1 = input => {
95
+ const output = ExperienceSchema.safeParse(input);
96
+ if (!output.success) {
97
+ return output;
98
+ }
99
+ return Object.assign(Object.assign({}, output), {
100
+ data: Object.assign(Object.assign({}, output.data), {
101
+ variants: input.variants
102
+ })
103
+ });
104
+ };
105
+ const Experience = Object.assign(Object.assign({}, ExperienceSchema), {
106
+ parse: parse$1,
107
+ safeParse: safeParse$1
108
+ });
93
109
 
94
- const Experiment = Experience.extend({
95
- type: z.literal('nt_experiment')
110
+ const ExperimentSchema = ExperienceSchema.extend({
111
+ type: z.string().regex(/^nt_experiment$/g)
112
+ });
113
+ const parse = input => {
114
+ const output = ExperimentSchema.parse(input);
115
+ return Object.assign(Object.assign({}, output), {
116
+ variants: input.variants
117
+ });
118
+ };
119
+ const safeParse = input => {
120
+ const output = ExperimentSchema.safeParse(input);
121
+ if (!output.success) {
122
+ return output;
123
+ }
124
+ return Object.assign(Object.assign({}, output), {
125
+ data: Object.assign(Object.assign({}, output.data), {
126
+ variants: input.variants
127
+ })
128
+ });
129
+ };
130
+ const Experiment = Object.assign(Object.assign({}, ExperimentSchema), {
131
+ parse,
132
+ safeParse
96
133
  });
97
134
 
98
135
  class ExperienceMapper {
99
136
  static isExperienceEntry(experience) {
100
137
  return Experience.safeParse(experience).success;
101
138
  }
102
-
103
139
  static mapExperience(experience) {
104
140
  const parsedExperience = Experience.safeParse(experience);
105
-
106
141
  if (!parsedExperience.success) {
107
142
  logger.warn('[Ninetailed ExperienceMapper]', 'Error parsing experience', parsedExperience.error.format());
108
143
  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)}`);
109
144
  }
110
-
111
145
  const {
112
146
  id,
113
147
  type,
@@ -115,47 +149,40 @@ class ExperienceMapper {
115
149
  config,
116
150
  variants
117
151
  } = parsedExperience.data;
152
+ const {
153
+ components,
154
+ traffic
155
+ } = config;
118
156
  return Object.assign(Object.assign({
119
157
  id,
120
- type
158
+ type: type
121
159
  }, audience ? {
122
160
  audience
123
161
  } : {}), {
124
- trafficAllocation: config.traffic,
162
+ trafficAllocation: traffic,
125
163
  distribution: config.distribution.map((percentage, index) => ({
126
164
  index,
127
165
  start: config.distribution.slice(0, index).reduce((a, b) => a + b, 0),
128
166
  end: config.distribution.slice(0, index + 1).reduce((a, b) => a + b, 0)
129
167
  })),
130
- components: config.components.map(component => {
131
- return {
132
- baseline: component.baseline,
133
- variants: component.variants.map(variant => {
134
- if (variant.hidden) {
135
- return variant;
136
- }
137
-
138
- const matchingVariant = variants.find(variantReference => variantReference.id === variant.id);
139
-
140
- if (!matchingVariant) {
141
- return null;
142
- }
143
-
144
- return matchingVariant;
145
- }).filter(variant => variant !== null)
146
- };
147
- })
168
+ components: components.map(component => ({
169
+ baseline: component.baseline,
170
+ variants: component.variants.map(variantRef => {
171
+ if (variantRef.hidden) {
172
+ return variantRef;
173
+ }
174
+ const matchingVariant = variants.find(variant => variant.id === variantRef.id);
175
+ return matchingVariant !== null && matchingVariant !== void 0 ? matchingVariant : null;
176
+ }).filter(variant => variant !== null)
177
+ }))
148
178
  });
149
179
  }
150
-
151
180
  static isExperimentEntry(experiment) {
152
181
  return Experiment.safeParse(experiment).success;
153
182
  }
154
-
155
183
  static mapExperiment(experiment) {
156
184
  return ExperienceMapper.mapExperience(experiment);
157
185
  }
158
-
159
186
  }
160
187
 
161
188
  export { Audience, Config, Experience, ExperienceMapper, Experiment, Variant };
@@ -1,12 +1,9 @@
1
- import { ExperienceConfiguration } from '@ninetailed/experience.js';
2
- import { z } from 'zod';
3
- import { Experience, Experiment } from '../types';
4
- declare type ExperienceEntry = z.input<typeof Experience>;
5
- declare type ExperimentEntry = z.input<typeof Experiment>;
1
+ import { ExperienceConfiguration, Reference } from '@ninetailed/experience.js';
2
+ import { Experience, ExperienceLike } from '../types/Experience';
3
+ import { ExperimentLike } from '../types/Experiment';
6
4
  export declare class ExperienceMapper {
7
- static isExperienceEntry(experience: ExperienceEntry): experience is Experience;
8
- static mapExperience(experience: ExperienceEntry): ExperienceConfiguration;
9
- static isExperimentEntry(experiment: ExperimentEntry): experiment is Experiment;
10
- static mapExperiment(experiment: ExperimentEntry): ExperienceConfiguration;
5
+ static isExperienceEntry<Variant extends Reference>(experience: ExperienceLike<Variant>): experience is Experience<Variant>;
6
+ static mapExperience<Variant extends Reference>(experience: ExperienceLike<Variant>): ExperienceConfiguration<Variant>;
7
+ static isExperimentEntry<Variant extends Reference>(experiment: ExperimentLike<Variant>): experiment is ExperimentLike<Variant>;
8
+ static mapExperiment<Variant extends Reference>(experiment: ExperimentLike<Variant>): ExperienceConfiguration<Variant>;
11
9
  }
12
- export {};
package/package.json CHANGED
@@ -1,20 +1,14 @@
1
1
  {
2
2
  "name": "@ninetailed/experience.js-utils",
3
- "version": "3.0.1-beta.4",
4
- "main": "./index.umd.js",
5
- "module": "./index.esm.js",
6
- "typings": "./index.d.ts",
3
+ "version": "3.0.2-beta.1",
4
+ "module": "./index.js",
5
+ "main": "./index.cjs",
6
+ "type": "module",
7
+ "types": "./index.d.ts",
7
8
  "dependencies": {
8
- "@ninetailed/experience.js": "3.0.1-beta.4",
9
- "@ninetailed/experience.js-shared": "3.0.1-beta.4",
10
- "ts-toolbelt": "^9.6.0",
11
- "diary": "^0.3.1",
12
- "zod": "^3.18.0",
13
- "locale-enum": "^1.1.1",
14
- "i18n-iso-countries": "^7.3.0",
15
- "analytics": "^0.8.0",
16
- "lodash": "^4.17.21",
17
- "murmurhash-js": "^1.0.0"
9
+ "@ninetailed/experience.js": "3.0.2-beta.1",
10
+ "@ninetailed/experience.js-shared": "3.0.2-beta.1",
11
+ "zod": "3.20.2"
18
12
  },
19
13
  "peerDependencies": {}
20
14
  }
@@ -6,4 +6,5 @@ export declare const Audience: z.ZodObject<{
6
6
  }, {
7
7
  id: string;
8
8
  }>;
9
+ export declare type AudienceLike = z.input<typeof Audience>;
9
10
  export declare type Audience = z.infer<typeof Audience>;
package/types/Config.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Baseline, VariantRef } from '@ninetailed/experience.js';
1
2
  import { z } from 'zod';
2
3
  export declare const Config: z.ZodObject<{
3
4
  distribution: z.ZodDefault<z.ZodArray<z.ZodNumber, "many">>;
@@ -62,4 +63,9 @@ export declare const Config: z.ZodObject<{
62
63
  }[];
63
64
  }[] | undefined;
64
65
  }>;
66
+ export declare type ConfigLike = z.input<typeof Config>;
65
67
  export declare type Config = z.infer<typeof Config>;
68
+ export interface BaselineWithVariantRefs {
69
+ baseline: Baseline;
70
+ variants: VariantRef[];
71
+ }