make-mp-data 1.4.0 → 1.4.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.
@@ -28,7 +28,7 @@ const config = {
28
28
  "event": "checkout",
29
29
  "weight": 2,
30
30
  "properties": {
31
- amount: weightedRange(5, 500, .25, 1000),
31
+ amount: weightedRange(5, 500, .25),
32
32
  currency: ["USD", "USD", "USD", "CAD", "EUR", "EUR", "BTC", "BTC", "ETH", "JPY"],
33
33
  cart: makeProducts(12),
34
34
  }
@@ -37,9 +37,9 @@ const config = {
37
37
  "event": "add to cart",
38
38
  "weight": 4,
39
39
  "properties": {
40
- amount: weightedRange(5, 500, .25, 1000),
40
+ amount: weightedRange(5, 500, .25),
41
41
  qty: integer(1, 5),
42
- product_id: weightedRange(1, 1000, 1.4, 42)
42
+ product_id: weightedRange(1, 1000, 1.4)
43
43
  }
44
44
  },
45
45
  {
@@ -56,10 +56,10 @@ const config = {
56
56
  "properties": {
57
57
  category: ["funny", "educational", "inspirational", "music", "news", "sports", "cooking", "DIY", "travel", "gaming"],
58
58
  hashTags: makeHashTags,
59
- watchTimeSec: weightedRange(10, 600,.25, 1000),
59
+ watchTimeSec: weightedRange(10, 600, .25,),
60
60
  quality: ["2160p", "1440p", "1080p", "720p", "480p", "360p", "240p"],
61
61
  format: ["mp4", "avi", "mov", "mpg"],
62
- video_id: weightedRange(1, 50000, 1.4, 420000),
62
+ video_id: weightedRange(1, 50000, 1.4),
63
63
 
64
64
  }
65
65
  },
@@ -67,8 +67,8 @@ const config = {
67
67
  "event": "comment",
68
68
  "weight": 2,
69
69
  "properties": {
70
- length: weightedRange(1, 500, .25, 1000),
71
- video_id: weightedRange(1, 50000, 1.4, 420000),
70
+ length: weightedRange(1, 500, .25),
71
+ video_id: weightedRange(1, 50000, 1.4),
72
72
  has_replies: [true, false, false, false, false],
73
73
  has_photo: [true, false, false, false, false],
74
74
 
@@ -78,7 +78,7 @@ const config = {
78
78
  "event": "save video",
79
79
  "weight": 4,
80
80
  "properties": {
81
- video_id: weightedRange(1, 50000, 1.4, 420000),
81
+ video_id: weightedRange(1, 50000, 1.4),
82
82
  ui_control: ["toolbar", "menu", "keyboard"]
83
83
 
84
84
 
@@ -88,7 +88,7 @@ const config = {
88
88
  "event": "view item",
89
89
  "weight": 8,
90
90
  "properties": {
91
- product_id: weightedRange(1, 24, 3, 1000),
91
+ product_id: weightedRange(1, 24, 3),
92
92
  colors: ["light", "dark", "custom", "dark"]
93
93
  }
94
94
  },
@@ -96,7 +96,7 @@ const config = {
96
96
  "event": "save item",
97
97
  "weight": 5,
98
98
  "properties": {
99
- product_id: weightedRange(1, 1000, 12, 8 ),
99
+ product_id: weightedRange(1, 1000, 12),
100
100
  colors: ["light", "dark", "custom", "dark"]
101
101
  }
102
102
  },
@@ -104,7 +104,7 @@ const config = {
104
104
  "event": "support ticket",
105
105
  "weight": 2,
106
106
  "properties": {
107
- product_id: weightedRange(1, 1000, .6, 420),
107
+ product_id: weightedRange(1, 1000, .6),
108
108
  description: chance.sentence.bind(chance),
109
109
  severity: ["low", "medium", "high"],
110
110
  ticket_id: chance.guid.bind(chance)
@@ -144,8 +144,8 @@ const config = {
144
144
  /** each generates it's own table */
145
145
  scdProps: {
146
146
  plan: ["free", "free", "free", "free", "basic", "basic", "basic", "premium", "premium", "enterprise"],
147
- MRR: weightedRange(0, 10000, .15, 1000),
148
- NPS: weightedRange(0, 10, 2, 150),
147
+ MRR: weightedRange(0, 10000, .15),
148
+ NPS: weightedRange(0, 10, 2, 150),
149
149
  subscribed: [true, true, true, true, true, true, false, false, false, false, "it's complicated"],
150
150
  renewalDate: date(100, false),
151
151
  },
@@ -155,7 +155,7 @@ const config = {
155
155
  profit: { events: ["checkout"], values: [4, 2, 42, 420] },
156
156
  watchTimeSec: {
157
157
  events: ["watch video"],
158
- values: weightedRange(50, 1200, 6 ,247)
158
+ values: weightedRange(50, 1200, 6, 247)
159
159
  }
160
160
  },
161
161
 
@@ -37,7 +37,7 @@ const config = {
37
37
  event: "checkout",
38
38
  weight: 2,
39
39
  properties: {
40
- amount: weightedRange(5, 500, .25, 1000),
40
+ amount: weightedRange(5, 500, .25),
41
41
  currency: ["USD", "CAD", "EUR", "BTC", "ETH", "JPY"],
42
42
  coupon: ["none", "none", "none", "none", "10%OFF", "20%OFF", "10%OFF", "20%OFF", "30%OFF", "40%OFF", "50%OFF"],
43
43
  numItems: weightedRange(1, 10),
@@ -48,7 +48,7 @@ const config = {
48
48
  event: "add to cart",
49
49
  weight: 4,
50
50
  properties: {
51
- amount: weightedRange(5, 500, .25, 1000),
51
+ amount: weightedRange(5, 500, .25),
52
52
  rating: weightedRange(1, 5),
53
53
  reviews: weightedRange(0, 35),
54
54
  isFeaturedItem: [true, false, false],
@@ -71,7 +71,7 @@ const config = {
71
71
  properties: {
72
72
  videoCategory: pickAWinner(videoCategories, integer(0, 9)),
73
73
  isFeaturedItem: [true, false, false],
74
- watchTimeSec: weightedRange(10, 600, .25, 1000),
74
+ watchTimeSec: weightedRange(10, 600, .25),
75
75
  quality: ["2160p", "1440p", "1080p", "720p", "480p", "360p", "240p"],
76
76
  format: ["mp4", "avi", "mov", "mpg"],
77
77
  uploader_id: chance.guid.bind(chance)
@@ -107,69 +107,68 @@ const config = {
107
107
  }
108
108
  }
109
109
  ],
110
- funnels: [{
111
- sequence: ["page view", "view item", "page view", "sign up"],
112
- weight: 1,
113
- isFirstFunnel: true,
114
- order: "sequential",
115
- conversionRate: 50,
116
- timeToConvert: 2,
117
- props: {
118
- variants: ["A", "B", "C", "Control"],
119
- flows: ["new", "existing", "loyal", "churned"],
120
- flags: ["on", "off"],
121
- experiment_ids: ["1234", "5678", "9012", "3456", "7890"],
122
- multiVariate: [true, false]
110
+ funnels: [
111
+ // {
112
+ // sequence: ["page view", "view item", "page view", "sign up"],
113
+ // weight: 1,
114
+ // isFirstFunnel: true,
115
+ // order: "sequential",
116
+ // conversionRate: 50,
117
+ // timeToConvert: 2,
118
+ // props: {
119
+ // variants: ["A", "B", "C", "Control"],
120
+ // flows: ["new", "existing", "loyal", "churned"],
121
+ // flags: ["on", "off"],
122
+ // experiment_ids: ["1234", "5678", "9012", "3456", "7890"],
123
+ // multiVariate: [true, false]
124
+
125
+ // },
123
126
 
124
- },
125
-
126
- // isInterruptedFunnel: false, // an interrupted funnel will have random events interspersed with the sequence
127
- // fixedTimeFunnel: 30, // if set this funnel will occur for all users at the same time ['cart charged', 'charge complete']
128
- // churn: {
129
- // isChurnFunnel: true, //if the user completes this funnel, they churn
130
- // probabilityToReturn: 0.1, //if the user churns, this is the probability they will return
131
127
  // },
132
- },
133
- {
134
- sequence: ["app install", "app open", "tutorial", "sign up"],
135
- weight: 1,
136
- isFirstFunnel: true,
137
- order: "sequential",
138
- conversionRate: 50,
139
- timeToConvert: 2,
140
- props: {
141
- variants: ["A", "B", "C", "Control"],
142
- flows: ["new", "existing", "loyal", "churned"],
143
- flags: ["on", "off"],
144
- experiment_ids: ["1234", "5678", "9012", "3456", "7890"],
145
- multiVariate: [true, false]
146
-
147
- }
148
- },
149
- {
150
- sequence: ["page view", "view item", "add to cart", "add to cart", "checkout"],
151
- weight: 3,
152
- isFirstFunnel: false,
153
- order: "sequential",
154
- conversionRate: 70,
155
- timeToConvert: 7 * 24,
156
- props: {
157
- variants: ["A", "B", "C", "Control"],
158
- flows: ["new", "existing", "loyal", "churned"],
159
- flags: ["on", "off"],
160
- experiment_ids: ["1234", "5678", "9012", "3456", "7890"],
161
- multiVariate: [true, false]
162
-
163
- }
164
- },
165
- {
166
- timeToConvert: 2,
167
- conversionRate: 66,
168
- sequence: ["foo", "bar", "baz", "qux"],
169
- }, {
170
- weight: 4,
171
- sequence: ["video", "video", "attack", "defend", "click"],
172
- }
128
+ // {
129
+ // sequence: ["app install", "app open", "tutorial", "sign up"],
130
+ // weight: 1,
131
+ // isFirstFunnel: true,
132
+ // order: "sequential",
133
+ // conversionRate: 50,
134
+ // timeToConvert: 2,
135
+ // props: {
136
+ // variants: ["A", "B", "C", "Control"],
137
+ // flows: ["new", "existing", "loyal", "churned"],
138
+ // flags: ["on", "off"],
139
+ // experiment_ids: ["1234", "5678", "9012", "3456", "7890"],
140
+ // multiVariate: [true, false]
141
+
142
+ // }
143
+ // },
144
+ // {
145
+ // sequence: ["view item", "add to cart", "checkout", "rage", "cage", "mage"],
146
+ // order: "interrupted"
147
+ // },
148
+ // {
149
+ // sequence: ["page view", "view item", "add to cart", "add to cart", "checkout"],
150
+ // weight: 3,
151
+ // isFirstFunnel: false,
152
+ // order: "sequential",
153
+ // conversionRate: 70,
154
+ // timeToConvert: 7 * 24,
155
+ // props: {
156
+ // variants: ["A", "B", "C", "Control"],
157
+ // flows: ["new", "existing", "loyal", "churned"],
158
+ // flags: ["on", "off"],
159
+ // experiment_ids: ["1234", "5678", "9012", "3456", "7890"],
160
+ // multiVariate: [true, false]
161
+
162
+ // }
163
+ // },
164
+ // {
165
+ // timeToConvert: 2,
166
+ // conversionRate: 66,
167
+ // sequence: ["foo", "bar", "baz", "qux"],
168
+ // }, {
169
+ // weight: 4,
170
+ // sequence: ["video", "video", "attack", "defend", "click"],
171
+ // }
173
172
  ],
174
173
  superProps: {
175
174
  platform: ["web", "mobile", "web", "mobile", "web", "web", "kiosk", "smartTV"],
@@ -189,14 +188,14 @@ const config = {
189
188
 
190
189
  scdProps: {
191
190
  nps: [1, 1, 1, 4, 4, 4, 5, 5, 6, 7, 8, 9],
192
- mrr: () => { weightedRange(10, 1000,.25, 1000); },
191
+ mrr: () => { weightedRange(10, 1000, .25); },
193
192
  },
194
193
  mirrorProps: {
195
194
  isBot: { events: "*", values: [false, false, false, false, true] },
196
- profit: { events: ["checkout"], values: [4, 2, 42, 420] },
195
+ profit: { events: ["checkout"], values: [4, 2, 42] },
197
196
  watchTimeSec: {
198
197
  events: ["watch video"],
199
- values: weightedRange(50, 1200, 6, 247)
198
+ values: weightedRange(50, 1200, 6)
200
199
  }
201
200
 
202
201
  },
package/schemas/simple.js CHANGED
@@ -31,13 +31,23 @@ const config = {
31
31
  region: "US",
32
32
  anonIds: false, //if true, anonymousIds are created for each user
33
33
  sessionIds: false, //if true, sessionIds are created for each user
34
+ hasAdSpend: false,
35
+
36
+ hasLocation: true,
37
+ hasAndroidDevices: true,
38
+ hasIOSDevices: true,
39
+ hasDesktopDevices: true,
40
+ hasBrowser: true,
41
+ hasCampaigns: true,
42
+ isAnonymous: false,
43
+
34
44
 
35
45
  events: [
36
46
  {
37
47
  event: "checkout",
38
48
  weight: 2,
39
49
  properties: {
40
- amount: weightedRange(5, 500, .25, 1000),
50
+ amount: weightedRange(5, 500, .25),
41
51
  currency: ["USD", "CAD", "EUR", "BTC", "ETH", "JPY"],
42
52
  coupon: ["none", "none", "none", "none", "10%OFF", "20%OFF", "10%OFF", "20%OFF", "30%OFF", "40%OFF", "50%OFF"],
43
53
  numItems: weightedRange(1, 10),
@@ -48,7 +58,7 @@ const config = {
48
58
  event: "add to cart",
49
59
  weight: 4,
50
60
  properties: {
51
- amount: weightedRange(5, 500, .25, 1000),
61
+ amount: weightedRange(5, 500, .25),
52
62
  rating: weightedRange(1, 5),
53
63
  reviews: weightedRange(0, 35),
54
64
  isFeaturedItem: [true, false, false],
@@ -71,7 +81,7 @@ const config = {
71
81
  properties: {
72
82
  videoCategory: pickAWinner(videoCategories, integer(0, 9)),
73
83
  isFeaturedItem: [true, false, false],
74
- watchTimeSec: weightedRange(10, 600, .25, 1000),
84
+ watchTimeSec: weightedRange(10, 600, .25),
75
85
  quality: ["2160p", "1440p", "1080p", "720p", "480p", "360p", "240p"],
76
86
  format: ["mp4", "avi", "mov", "mpg"],
77
87
  uploader_id: chance.guid.bind(chance)
@@ -133,7 +143,7 @@ const config = {
133
143
  profit: { events: ["checkout"], values: [4, 2, 42, 420] },
134
144
  watchTimeSec: {
135
145
  events: ["watch video"],
136
- values: weightedRange(50, 1200, 6, 247)
146
+ values: weightedRange(50, 1200, 6)
137
147
  }
138
148
 
139
149
  },
package/scratch.mjs CHANGED
@@ -1,17 +1,20 @@
1
1
  import main from "./index.js";
2
2
  import amir from './customers/amir.js';
3
3
  import simple from './schemas/simple.js';
4
+ import funnels from './schemas/funnels.js';
5
+ import foobar from './schemas/foobar.js';
6
+ import complex from './schemas/complex.js';
7
+ import deepNest from './schemas/deepNest.js';
4
8
  import execSync from 'child_process';
5
9
 
6
10
 
7
11
  /** @type {main.Config} */
8
12
  const spec = {
9
- ...amir,
10
- numUsers: 1000,
11
- numEvents: 100000,
13
+ ...simple,
12
14
  writeToDisk: false,
13
15
  verbose: true,
14
- makeChart: true,
16
+ makeChart: false,
17
+ token: "e98e6af94f6ddfb5e967fa265484539a"
15
18
  };
16
19
 
17
20
 
@@ -39,7 +39,12 @@ const { applySkew,
39
39
  buildFileNames,
40
40
  TimeSoup,
41
41
  getChance,
42
- initChance
42
+ initChance,
43
+ validateEventConfig,
44
+ validateTime,
45
+ interruptArray,
46
+ optimizedBoxMuller,
47
+ inferFunnels
43
48
  } = require('../utils');
44
49
 
45
50
 
@@ -59,7 +64,7 @@ describe('timesoup', () => {
59
64
  });
60
65
 
61
66
 
62
- describe('naming things', () => {
67
+ describe('names', () => {
63
68
 
64
69
  test('default config', () => {
65
70
  const config = { simulationName: 'testSim' };
@@ -182,7 +187,7 @@ describe('naming things', () => {
182
187
  });
183
188
 
184
189
 
185
- describe('determined random', () => {
190
+ describe('determinism', () => {
186
191
  test('initializes RNG with seed from environment variable', () => {
187
192
  process.env.SEED = 'test-seed';
188
193
  // @ts-ignore
@@ -206,8 +211,8 @@ describe('determined random', () => {
206
211
  });
207
212
 
208
213
 
209
- describe('generateUser', () => {
210
- test('creates a user with valid fields', () => {
214
+ describe('generation', () => {
215
+ test('users: can make', () => {
211
216
  const numDays = 30;
212
217
  const user = generateUser('uuid-123', numDays);
213
218
  expect(user).toHaveProperty('distinct_id');
@@ -219,7 +224,7 @@ describe('generateUser', () => {
219
224
  expect(user).toHaveProperty('sessionIds');
220
225
  });
221
226
 
222
- test('creates a user with a created date within the specified range', () => {
227
+ test('user: in time range', () => {
223
228
  const numDays = 30;
224
229
  const user = generateUser('uuid-123', numDays);
225
230
  const createdDate = dayjs(user.created, 'YYYY-MM-DD');
@@ -228,9 +233,86 @@ describe('generateUser', () => {
228
233
  });
229
234
  });
230
235
 
236
+ describe('validation', () => {
231
237
 
238
+ beforeAll(() => {
239
+ global.NOW = 1672531200; // fixed point in time for testing
240
+ });
241
+
242
+ test('events: throws non array', () => {
243
+ // @ts-ignore
244
+ expect(() => validateEventConfig("not an array")).toThrow("events must be an array");
245
+ });
246
+
247
+ test('events: strings', () => {
248
+ const events = ["event1", "event2"];
249
+ const result = validateEventConfig(events);
250
+
251
+ expect(result).toEqual([
252
+ { event: "event1", isFirstEvent: false, properties: {}, weight: expect.any(Number) },
253
+ { event: "event2", isFirstEvent: false, properties: {}, weight: expect.any(Number) },
254
+ ]);
255
+
256
+ result.forEach(event => {
257
+ expect(event.weight).toBeGreaterThanOrEqual(1);
258
+ expect(event.weight).toBeLessThanOrEqual(5);
259
+ });
260
+ });
261
+
262
+ test('events: objects', () => {
263
+ const events = [{ event: "event1", properties: { a: 1 } }, { event: "event2", properties: { b: 2 } }];
264
+ const result = validateEventConfig(events);
265
+
266
+ expect(result).toEqual(events);
267
+ });
268
+
269
+ test('events: mix', () => {
270
+ const events = ["event1", { event: "event2", properties: { b: 2 } }];
271
+ // @ts-ignore
272
+ const result = validateEventConfig(events);
273
+
274
+ expect(result).toEqual([
275
+ { event: "event1", isFirstEvent: false, properties: {}, weight: expect.any(Number) },
276
+ { event: "event2", properties: { b: 2 } }
277
+ ]);
278
+
279
+ expect(result[0].weight).toBeGreaterThanOrEqual(1);
280
+ expect(result[0].weight).toBeLessThanOrEqual(5);
281
+ });
282
+
283
+ test('dates: between', () => {
284
+ const chosenTime = global.NOW - (60 * 60 * 24 * 15); // 15 days ago
285
+ const earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
286
+ const latestTime = global.NOW;
287
+ expect(validateTime(chosenTime, earliestTime, latestTime)).toBe(true);
288
+ });
232
289
 
233
- describe('enrich array', () => {
290
+ test('dates: outside earliest', () => {
291
+ const chosenTime = global.NOW - (60 * 60 * 24 * 31); // 31 days ago
292
+ const earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
293
+ const latestTime = global.NOW;
294
+ expect(validateTime(chosenTime, earliestTime, latestTime)).toBe(false);
295
+ });
296
+
297
+ test('dates: outside latest', () => {
298
+ const chosenTime = -1;
299
+ const earliestTime = global.NOW - (60 * 60 * 24 * 30); // 30 days ago
300
+ const latestTime = global.NOW;
301
+ expect(validateTime(chosenTime, earliestTime, latestTime)).toBe(false);
302
+ });
303
+
304
+ test('dates: inference in', () => {
305
+ const chosenTime = global.NOW - (60 * 60 * 24 * 15); // 15 days ago
306
+ expect(validateTime(chosenTime)).toBe(true);
307
+ });
308
+
309
+ test('dates: inference out', () => {
310
+ const chosenTime = global.NOW - (60 * 60 * 24 * 31); // 31 days ago
311
+ expect(validateTime(chosenTime)).toBe(false);
312
+ });
313
+ });
314
+
315
+ describe('enrichment', () => {
234
316
  test('hook works', () => {
235
317
  const arr = [];
236
318
  const hook = (item) => item * 2;
@@ -238,7 +320,7 @@ describe('enrich array', () => {
238
320
  enrichedArray.hookPush(1);
239
321
  enrichedArray.hookPush(2);
240
322
  expect(enrichedArray.includes(2)).toBeTruthy();
241
- expect(enrichedArray.includes(4)).toBeTruthy();
323
+ expect(enrichedArray.includes(4)).toBeTruthy();
242
324
  });
243
325
 
244
326
  test('filter empties', () => {
@@ -247,7 +329,7 @@ describe('enrich array', () => {
247
329
  const enrichedArray = enrichArray(arr, { hook });
248
330
  enrichedArray.hookPush(null);
249
331
  enrichedArray.hookPush(undefined);
250
- enrichedArray.hookPush({});
332
+ enrichedArray.hookPush({});
251
333
  enrichedArray.hookPush({ a: 1 });
252
334
  enrichedArray.hookPush([1, 2]);
253
335
  expect(enrichedArray).toHaveLength(3);
@@ -256,12 +338,14 @@ describe('enrich array', () => {
256
338
  expect(enrichedArray.includes('[object Object]')).toBeTruthy();
257
339
  expect(enrichedArray.includes('1')).toBeTruthy();
258
340
  expect(enrichedArray.includes('2')).toBeTruthy();
259
-
341
+
260
342
  });
343
+
344
+
261
345
  });
262
346
 
263
347
 
264
- describe('utils', () => {
348
+ describe('utilities', () => {
265
349
 
266
350
  test('pick: works', () => {
267
351
  const array = [1, 2, 3];
@@ -291,7 +375,7 @@ describe('utils', () => {
291
375
 
292
376
 
293
377
  test('person: fields', () => {
294
- const generatedPerson = person();
378
+ const generatedPerson = person('myId');
295
379
  expect(generatedPerson).toHaveProperty('name');
296
380
  expect(generatedPerson).toHaveProperty('email');
297
381
  expect(generatedPerson).toHaveProperty('avatar');
@@ -343,13 +427,13 @@ describe('utils', () => {
343
427
  test('weightedRange: within range', () => {
344
428
  const values = weightedRange(5, 15);
345
429
  expect(values.every(v => v >= 5 && v <= 15)).toBe(true);
346
- expect(values.length).toBe(100);
430
+ expect(values.length).toBe(50);
347
431
  });
348
432
 
349
433
  test('applySkew: skews', () => {
350
434
  const value = boxMullerRandom();
351
435
  const skewedValue = applySkew(value, .25);
352
- expect(Math.abs(skewedValue)).toBeGreaterThanOrEqual(Math.abs(value));
436
+ expect(Math.abs(skewedValue)).toBeLessThanOrEqual(Math.abs(value));
353
437
  });
354
438
 
355
439
  test('mapToRange: works', () => {
@@ -537,5 +621,17 @@ describe('utils', () => {
537
621
  expect(stdDev).toBeCloseTo(1, 1);
538
622
  });
539
623
 
624
+ test('optimized box normal distribution', () => {
625
+ const values = [];
626
+ for (let i = 0; i < 10000; i++) {
627
+ values.push(optimizedBoxMuller());
628
+ }
629
+ const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
630
+ const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length;
631
+ const stdDev = Math.sqrt(variance);
632
+ expect(mean).toBeLessThan(1);
633
+ expect(stdDev).toBeLessThan(1);
634
+ });
635
+
540
636
 
541
637
  });
package/types.d.ts CHANGED
@@ -12,10 +12,21 @@ declare namespace main {
12
12
  epochStart?: number;
13
13
  epochEnd?: number;
14
14
  numEvents?: number;
15
- numUsers?: number;
15
+ numUsers?: number;
16
+
17
+ //switches
18
+ isAnonymous?: boolean;
19
+ hasLocation?: boolean;
20
+ hasCampaigns?: boolean;
21
+ hasAdSpend?: boolean;
22
+ hasIOSDevices?: boolean;
23
+ hasAndroidDevices?: boolean;
24
+ hasDesktopDevices?: boolean;
25
+ hasBrowser?: boolean;
26
+
27
+
16
28
  format?: "csv" | "json";
17
29
  region?: "US" | "EU";
18
- chance?: any;
19
30
  events?: EventConfig[]; //can also be a array of strings
20
31
  superProps?: Record<string, ValueValid>;
21
32
  funnels?: Funnel[];
@@ -50,6 +61,8 @@ declare namespace main {
50
61
  | "mirror"
51
62
  | "funnel-pre"
52
63
  | "funnel-post"
64
+ | "ad-spend"
65
+ | "churn"
53
66
  | "";
54
67
  export type Hook<T> = (record: any, type: hookTypes, meta: any) => T;
55
68
 
@@ -60,7 +73,7 @@ declare namespace main {
60
73
  }
61
74
 
62
75
  export interface EnrichedArray<T> extends Array<T> {
63
- hookPush: (item: T) => number;
76
+ hookPush: (item: T) => boolean;
64
77
  }
65
78
 
66
79
  export interface EventConfig {
@@ -85,13 +98,20 @@ declare namespace main {
85
98
  sequence: string[];
86
99
  weight?: number;
87
100
  isFirstFunnel?: boolean;
101
+ /**
102
+ * If true, the funnel will require the user to repeat the sequence of events in order to convert
103
+ * If false, the user does not need to repeat the sequence of events in order to convert
104
+ * ^ when false, users who repeat the repetitive steps are more likely to convert
105
+ */
106
+ requireRepeats?: boolean;
88
107
  order?:
89
108
  | "sequential"
90
109
  | "first-fixed"
91
110
  | "last-fixed"
92
111
  | "random"
93
112
  | "first-and-last-fixed"
94
- | "middle-fixed";
113
+ | "middle-fixed"
114
+ | "interrupted";
95
115
  conversionRate?: number;
96
116
  timeToConvert?: number;
97
117
  props?: Record<string, ValueValid>;