make-mp-data 1.4.3 → 1.4.5

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.
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "cSpell.words": [
3
+ "adspend",
3
4
  "AVCHD",
4
5
  "chartjs",
5
6
  "chupacabra",
package/core/chart.js CHANGED
@@ -3,8 +3,15 @@ const fs = require('fs');
3
3
  const u = require('ak-tools');
4
4
  const dayjs = require('dayjs');
5
5
  const { openFinder } = require('./utils');
6
+ const { existsSync } = fs;
7
+ const path = require('path');
8
+
9
+
10
+ let tempDir;
11
+ const dataFolder = path.resolve("./data");
12
+ if (existsSync(dataFolder)) tempDir = dataFolder;
13
+ else tempDir = path.resolve("./");
6
14
 
7
- const tempDir = u.mkdir('./tmp');
8
15
 
9
16
  // Function to count events per day
10
17
  function countDailyEvents(eventData) {
@@ -168,9 +175,9 @@ async function generateLineChart(rawData, signupEvents = ["sign up"], fileName)
168
175
  if (typeof fileName !== 'string') fileName = 'chart';
169
176
  // @ts-ignore
170
177
  const imageBuffer = await chartJSNodeCanvas.renderToBuffer(configuration);
171
- const path = `./tmp/${fileName}.png`;
172
- const removed = await u.rm(path);
173
- const file = await u.touch(path, imageBuffer);
178
+ const filePath = path.join(tempDir, `${fileName}.png`);
179
+ const removed = await u.rm(filePath);
180
+ const file = await u.touch(filePath, imageBuffer);
174
181
 
175
182
  console.log(`Chart saved as ${fileName}.png`);
176
183
  openFinder(path)
package/core/cli.js CHANGED
@@ -66,6 +66,18 @@ DATA MODEL: https://github.com/ak--47/make-mp-data/blob/main/default.js
66
66
  describe: 'number of events to model',
67
67
  type: 'number',
68
68
  })
69
+ .option("epochStart", {
70
+ alias: 'start',
71
+ demandOption: false,
72
+ describe: 'start epoch time',
73
+ type: 'number',
74
+ })
75
+ .option("epochEnd", {
76
+ alias: 'end',
77
+ demandOption: false,
78
+ describe: 'end epoch time',
79
+ type: 'number',
80
+ })
69
81
  .option("region", {
70
82
  demandOption: false,
71
83
  default: 'US',
@@ -105,16 +117,82 @@ DATA MODEL: https://github.com/ak--47/make-mp-data/blob/main/default.js
105
117
  type: 'boolean',
106
118
  coerce: boolCoerce
107
119
  })
108
-
120
+ .option("verbose", {
121
+ alias: 'v',
122
+ demandOption: false,
123
+ describe: 'enable verbose logging',
124
+ type: 'boolean',
125
+ coerce: boolCoerce
126
+ })
127
+ .option("makeChart", {
128
+ alias: 'mc',
129
+ demandOption: false,
130
+ describe: 'create a PNG chart from data',
131
+ type: 'boolean',
132
+ coerce: boolCoerce
133
+ })
134
+ .option("hasAdSpend", {
135
+ alias: 'ads',
136
+ demandOption: false,
137
+ describe: 'include ad spend data',
138
+ type: 'boolean',
139
+ coerce: boolCoerce
140
+ })
141
+ .option("hasCampaigns", {
142
+ alias: 'camp',
143
+ demandOption: false,
144
+ describe: 'include campaign data',
145
+ type: 'boolean',
146
+ coerce: boolCoerce
147
+ })
148
+ .option("hasLocation", {
149
+ alias: 'loc',
150
+ demandOption: false,
151
+ describe: 'include location data',
152
+ type: 'boolean',
153
+ coerce: boolCoerce
154
+ })
155
+ .option("isAnonymous", {
156
+ alias: 'anon',
157
+ demandOption: false,
158
+ describe: 'generate anonymous data',
159
+ type: 'boolean',
160
+ coerce: boolCoerce
161
+ })
162
+ .option("hasBrowser", {
163
+ alias: 'browser',
164
+ demandOption: false,
165
+ describe: 'include browser data',
166
+ type: 'boolean',
167
+ coerce: boolCoerce
168
+ })
169
+ .option("hasAndroidDevices", {
170
+ alias: 'android',
171
+ demandOption: false,
172
+ describe: 'include Android device data',
173
+ type: 'boolean',
174
+ coerce: boolCoerce
175
+ })
176
+ .option("hasDesktopDevices", {
177
+ alias: 'desktop',
178
+ demandOption: false,
179
+ describe: 'include desktop device data',
180
+ type: 'boolean',
181
+ coerce: boolCoerce
182
+ })
183
+ .option("hasIOSDevices", {
184
+ alias: 'ios',
185
+ demandOption: false,
186
+ describe: 'include iOS device data',
187
+ type: 'boolean',
188
+ coerce: boolCoerce
189
+ })
109
190
  .help()
110
191
  .wrap(null)
111
192
  .argv;
112
193
 
113
- // if (args._.length === 0 && !args.type?.toLowerCase()?.includes('export')) {
114
- // yargs.showHelp();
115
- // process.exit();
116
- // }
117
194
  return args;
195
+
118
196
  }
119
197
 
120
198
 
package/core/index.js CHANGED
@@ -6,18 +6,23 @@ by AK
6
6
  ak@mixpanel.com
7
7
  */
8
8
 
9
- //todo: churn ... is churnFunnel, possible to return, etc
10
- //todo: fixedTimeFunnel? if set this funnel will occur for all users at the same time ['cards charged', 'charge complete']
11
- //todo: send SCD data to mixpanel
12
- //todo: send and map lookup tables to mixpanel
9
+
10
+ //!feature: mirror strategies
11
+ //!feature: fixedTimeFunnel? if set this funnel will occur for all users at the same time ['cards charged', 'charge complete']
12
+ //!feature: churn ... is churnFunnel, possible to return, etc
13
+ //!bug: not writing adspend CSV
14
+ //!bug: using --mc flag reverts to --complex for some reason
15
+
16
+ //todo: send SCD data to mixpanel (blocked on dev)
17
+ //todo: send and map lookup tables to mixpanel (also blocked on dev)
13
18
 
14
19
  /** @typedef {import('../types').Config} Config */
15
20
  /** @typedef {import('../types').EventConfig} EventConfig */
16
21
  /** @typedef {import('../types').Funnel} Funnel */
17
22
  /** @typedef {import('../types').Person} Person */
18
- /** @typedef {import('../types').SCDTableRow} SCDTableRow */
23
+ /** @typedef {import('../types').SCDSchema} SCDTableRow */
19
24
  /** @typedef {import('../types').UserProfile} UserProfile */
20
- /** @typedef {import('../types').EventSpec} EventSpec */
25
+ /** @typedef {import('../types').EventSchema} EventSpec */
21
26
 
22
27
  const dayjs = require("dayjs");
23
28
  const utc = require("dayjs/plugin/utc");
@@ -177,16 +182,16 @@ async function main(config) {
177
182
  log(`------------------SETUP------------------`, "\n");
178
183
 
179
184
  //setup all the data structures we will push into
180
- const eventData = u.enrichArray([], { hook, type: "event", config });
181
- const userProfilesData = u.enrichArray([], { hook, type: "user", config });
182
- const adSpendData = u.enrichArray([], { hook, type: "ad-spend", config });
185
+ const eventData = u.hookArray([], { hook, type: "event", config });
186
+ const userProfilesData = u.hookArray([], { hook, type: "user", config });
187
+ const adSpendData = u.hookArray([], { hook, type: "ad-spend", config });
183
188
  const scdTableKeys = Object.keys(scdProps);
184
189
  const scdTableData = [];
185
190
  for (const [index, key] of scdTableKeys.entries()) {
186
- scdTableData[index] = u.enrichArray([], { hook, type: "scd", config, scdKey: key });
191
+ scdTableData[index] = u.hookArray([], { hook, type: "scd", config, scdKey: key });
187
192
  }
188
- const groupProfilesData = u.enrichArray([], { hook, type: "group", config });
189
- const lookupTableData = u.enrichArray([], { hook, type: "lookup", config });
193
+ const groupProfilesData = u.hookArray([], { hook, type: "group", config });
194
+ const lookupTableData = u.hookArray([], { hook, type: "lookup", config });
190
195
  const avgEvPerUser = Math.ceil(numEvents / numUsers);
191
196
 
192
197
  // if no funnels, make some out of events...
@@ -331,7 +336,7 @@ async function main(config) {
331
336
  const newTime = dayjs(event.time).add(timeShift, "second");
332
337
  event.time = newTime.toISOString();
333
338
  if (epochStart && newTime.unix() < epochStart) event = {};
334
- if (epochEnd && newTime.unix() > epochEnd) event = {};
339
+ if (epochEnd && newTime.unix() > (epochEnd - 60 * 60)) event = {};
335
340
  }
336
341
  catch (e) {
337
342
  //noop
@@ -453,7 +458,7 @@ async function main(config) {
453
458
  log(`\tsent ${comma(imported.success)} events\n`);
454
459
  importResults.events = imported;
455
460
  }
456
- if (userProfilesData) {
461
+ if (userProfilesData && userProfilesData.length) {
457
462
  log(`importing user profiles to mixpanel...\n`);
458
463
  const imported = await mp(creds, clone(userProfilesData), {
459
464
  recordType: "user",
@@ -462,7 +467,7 @@ async function main(config) {
462
467
  log(`\tsent ${comma(imported.success)} user profiles\n`);
463
468
  importResults.users = imported;
464
469
  }
465
- if (adSpendData) {
470
+ if (adSpendData && adSpendData.length) {
466
471
  log(`importing ad spend data to mixpanel...\n`);
467
472
  const imported = await mp(creds, clone(adSpendData), {
468
473
  recordType: "event",
@@ -616,8 +621,8 @@ function makeEvent(distinct_id, anonymousIds, sessionIds, earliestTime, chosenEv
616
621
  const groupEvents = groupPair[2] || [];
617
622
 
618
623
  // empty array for group events means all events
619
- if (!groupEvents.length) eventTemplate[groupKey] = u.pick(u.weightedRange(1, groupCardinality));
620
- if (groupEvents.includes(eventTemplate.event)) eventTemplate[groupKey] = u.pick(u.weightedRange(1, groupCardinality));
624
+ if (!groupEvents.length) eventTemplate[groupKey] = u.pick(u.weighNumRange(1, groupCardinality));
625
+ if (groupEvents.includes(eventTemplate.event)) eventTemplate[groupKey] = u.pick(u.weighNumRange(1, groupCardinality));
621
626
  }
622
627
 
623
628
  //make $insert_id
@@ -668,7 +673,7 @@ function makeFunnel(funnel, user, profile, scd, firstEventTime, config) {
668
673
  .map((eventName) => {
669
674
  const foundEvent = config.events.find((e) => e.event === eventName);
670
675
  /** @type {EventConfig} */
671
- const eventSpec = foundEvent || { event: eventName, properties: {} };
676
+ const eventSpec = clone(foundEvent) || { event: eventName, properties: {} };
672
677
  for (const key in eventSpec.properties) {
673
678
  try {
674
679
  eventSpec.properties[key] = u.choose(eventSpec.properties[key]);
@@ -877,7 +882,7 @@ function makeAdSpend(day) {
877
882
  const utm_term = u.choose(u.pickAWinner(network.utm_term)());
878
883
  //each of these is a campaign
879
884
  const adSpendEvent = {
880
- event: "Ad Data",
885
+ event: "$ad_spend",
881
886
  time: day,
882
887
  source: 'dm4',
883
888
  utm_campaign: campaign,
package/core/utils.js CHANGED
@@ -15,8 +15,8 @@ const { domainSuffix, domainPrefix } = require('./defaults');
15
15
  /** @typedef {import('../types').Config} Config */
16
16
  /** @typedef {import('../types').EventConfig} EventConfig */
17
17
  /** @typedef {import('../types').ValueValid} ValueValid */
18
- /** @typedef {import('../types').EnrichedArray} EnrichArray */
19
- /** @typedef {import('../types').EnrichArrayOptions} EnrichArrayOptions */
18
+ /** @typedef {import('../types').EnrichedArray} hookArray */
19
+ /** @typedef {import('../types').hookArrayOptions} hookArrayOptions */
20
20
  /** @typedef {import('../types').Person} Person */
21
21
  /** @typedef {import('../types').Funnel} Funnel */
22
22
 
@@ -34,6 +34,7 @@ RNG
34
34
  /**
35
35
  * the random number generator initialization function
36
36
  * @param {string} seed
37
+ * @returns {Chance}
37
38
  */
38
39
  function initChance(seed) {
39
40
  if (process.env.SEED) seed = process.env.SEED; // Override seed with environment variable if available
@@ -42,6 +43,7 @@ function initChance(seed) {
42
43
  if (global.MP_SIMULATION_CONFIG) global.MP_SIMULATION_CONFIG.chance = globalChance;
43
44
  chanceInitialized = true;
44
45
  }
46
+ return globalChance;
45
47
  }
46
48
 
47
49
  /**
@@ -54,8 +56,7 @@ function getChance() {
54
56
  if (!seed) {
55
57
  return new Chance();
56
58
  }
57
- initChance(seed);
58
- return globalChance;
59
+ return initChance(seed);
59
60
  }
60
61
  return globalChance;
61
62
  }
@@ -242,64 +243,6 @@ function integer(min = 1, max = 100) {
242
243
  };
243
244
 
244
245
 
245
- /**
246
- * Creates a function that generates a weighted list of items
247
- * with a higher likelihood of picking a specified index and clear second and third place indices.
248
- *
249
- * @param {Array} items - The list of items to pick from.
250
- * @param {number} [mostChosenIndex] - The index of the item to be most favored.
251
- * @returns {function} - A function that returns a weighted list of items.
252
- */
253
- function pickAWinner(items, mostChosenIndex) {
254
- const chance = getChance();
255
-
256
- // Ensure mostChosenIndex is within the bounds of the items array
257
- if (!items) return () => { return ""; };
258
- if (!items.length) return () => { return ""; };
259
- if (!mostChosenIndex) mostChosenIndex = chance.integer({ min: 0, max: items.length - 1 });
260
- if (mostChosenIndex >= items.length) mostChosenIndex = items.length - 1;
261
-
262
- // Calculate second and third most chosen indices
263
- const secondMostChosenIndex = (mostChosenIndex + 1) % items.length;
264
- const thirdMostChosenIndex = (mostChosenIndex + 2) % items.length;
265
-
266
- // Return a function that generates a weighted list
267
- return function () {
268
- const weighted = [];
269
- for (let i = 0; i < 10; i++) {
270
- const rand = chance.d10(); // Random number between 1 and 10
271
-
272
- // 35% chance to favor the most chosen index
273
- if (chance.bool({ likelihood: 35 })) {
274
- // 50% chance to slightly alter the index
275
- if (chance.bool({ likelihood: 50 })) {
276
- weighted.push(items[mostChosenIndex]);
277
- } else {
278
- const addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;
279
- let newIndex = mostChosenIndex + addOrSubtract;
280
-
281
- // Ensure newIndex is within bounds
282
- if (newIndex < 0) newIndex = 0;
283
- if (newIndex >= items.length) newIndex = items.length - 1;
284
- weighted.push(items[newIndex]);
285
- }
286
- }
287
- // 25% chance to favor the second most chosen index
288
- else if (chance.bool({ likelihood: 25 })) {
289
- weighted.push(items[secondMostChosenIndex]);
290
- }
291
- // 15% chance to favor the third most chosen index
292
- else if (chance.bool({ likelihood: 15 })) {
293
- weighted.push(items[thirdMostChosenIndex]);
294
- }
295
- // Otherwise, pick a random item from the list
296
- else {
297
- weighted.push(chance.pickone(items));
298
- }
299
- }
300
- return weighted;
301
- };
302
- }
303
246
 
304
247
 
305
248
 
@@ -359,12 +302,13 @@ function mapToRange(value, mean, sd) {
359
302
  * @param {number} step=1
360
303
  */
361
304
  function range(a, b, step = 1) {
305
+ const arr = [];
362
306
  step = !step ? 1 : step;
363
307
  b = b / step;
364
308
  for (var i = a; i <= b; i++) {
365
- this.push(i * step);
309
+ arr.push(i * step);
366
310
  }
367
- return this;
311
+ return arr;
368
312
  };
369
313
 
370
314
 
@@ -491,7 +435,7 @@ function weighFunnels(acc, funnel) {
491
435
  * @param {number} skew=1
492
436
  * @param {number} size=100
493
437
  */
494
- function weightedRange(min, max, skew = 1, size = 50) {
438
+ function weighNumRange(min, max, skew = 1, size = 50) {
495
439
  if (size > 2000) size = 2000;
496
440
  const mean = (max + min) / 2;
497
441
  const sd = (max - min) / 4;
@@ -508,13 +452,16 @@ function weightedRange(min, max, skew = 1, size = 50) {
508
452
  return array;
509
453
  }
510
454
 
455
+ /**
456
+ * arbitrarily weigh an array of values to create repeats
457
+ * @param {Array<any>} arr
458
+ */
511
459
  function weighArray(arr) {
512
-
513
460
  // Calculate the upper bound based on the size of the array with added noise
514
461
  const maxCopies = arr.length + integer(1, arr.length);
515
462
 
516
463
  // Create an empty array to store the weighted elements
517
- let weightedArray = [];
464
+ const weightedArray = [];
518
465
 
519
466
  // Iterate over the input array and copy each element a random number of times
520
467
  arr.forEach(element => {
@@ -527,6 +474,106 @@ function weighArray(arr) {
527
474
  return weightedArray;
528
475
  }
529
476
 
477
+ /**
478
+ * Creates a function that generates a weighted array of values.
479
+ *
480
+ * @overload
481
+ * @param {Array<{value: string, weight: number}>} items - An array of weighted objects or an array of strings.
482
+ * @returns {function(): Array<string>} A function that returns a weighted array of values when called.
483
+ *
484
+ * @overload
485
+ * @param {Array<string>} items - An array of strings.
486
+ * @returns {function(): Array<string>} A function that returns a weighted array with automatically assigned random weights to each string.
487
+ */
488
+
489
+ function weighChoices(items) {
490
+ let weightedItems;
491
+
492
+ // If items are strings, assign unique random weights
493
+ if (items.every(item => typeof item === 'string')) {
494
+ const weights = shuffleArray(range(1, items.length));
495
+ weightedItems = items.map((item, index) => ({
496
+ value: item,
497
+ weight: weights[index]
498
+ }));
499
+ } else {
500
+ weightedItems = items;
501
+ }
502
+
503
+ return function generateWeightedArray() {
504
+ const weightedArray = [];
505
+
506
+ // Add each value to the array the number of times specified by its weight
507
+ weightedItems.forEach(({ value, weight }) => {
508
+ if (!weight) weight = 1;
509
+ for (let i = 0; i < weight; i++) {
510
+ weightedArray.push(value);
511
+ }
512
+ });
513
+
514
+ return weightedArray;
515
+ };
516
+ }
517
+
518
+ /**
519
+ * Creates a function that generates a weighted list of items
520
+ * with a higher likelihood of picking a specified index and clear second and third place indices.
521
+ *
522
+ * @param {Array} items - The list of items to pick from.
523
+ * @param {number} [mostChosenIndex] - The index of the item to be most favored.
524
+ * @returns {function} - A function that returns a weighted list of items.
525
+ */
526
+ function pickAWinner(items, mostChosenIndex) {
527
+ const chance = getChance();
528
+
529
+ // Ensure mostChosenIndex is within the bounds of the items array
530
+ if (!items) return () => { return ""; };
531
+ if (!items.length) return () => { return ""; };
532
+ if (!mostChosenIndex) mostChosenIndex = chance.integer({ min: 0, max: items.length - 1 });
533
+ if (mostChosenIndex >= items.length) mostChosenIndex = items.length - 1;
534
+
535
+ // Calculate second and third most chosen indices
536
+ const secondMostChosenIndex = (mostChosenIndex + 1) % items.length;
537
+ const thirdMostChosenIndex = (mostChosenIndex + 2) % items.length;
538
+
539
+ // Return a function that generates a weighted list
540
+ return function () {
541
+ const weighted = [];
542
+ for (let i = 0; i < 10; i++) {
543
+ const rand = chance.d10(); // Random number between 1 and 10
544
+
545
+ // 35% chance to favor the most chosen index
546
+ if (chance.bool({ likelihood: 35 })) {
547
+ // 50% chance to slightly alter the index
548
+ if (chance.bool({ likelihood: 50 })) {
549
+ weighted.push(items[mostChosenIndex]);
550
+ } else {
551
+ const addOrSubtract = chance.bool({ likelihood: 50 }) ? -rand : rand;
552
+ let newIndex = mostChosenIndex + addOrSubtract;
553
+
554
+ // Ensure newIndex is within bounds
555
+ if (newIndex < 0) newIndex = 0;
556
+ if (newIndex >= items.length) newIndex = items.length - 1;
557
+ weighted.push(items[newIndex]);
558
+ }
559
+ }
560
+ // 25% chance to favor the second most chosen index
561
+ else if (chance.bool({ likelihood: 25 })) {
562
+ weighted.push(items[secondMostChosenIndex]);
563
+ }
564
+ // 15% chance to favor the third most chosen index
565
+ else if (chance.bool({ likelihood: 15 })) {
566
+ weighted.push(items[thirdMostChosenIndex]);
567
+ }
568
+ // Otherwise, pick a random item from the list
569
+ else {
570
+ weighted.push(chance.pickone(items));
571
+ }
572
+ }
573
+ return weighted;
574
+ };
575
+ }
576
+
530
577
  /*
531
578
  ----
532
579
  SHUFFLERS
@@ -654,10 +701,10 @@ META
654
701
  /**
655
702
  * our meta programming function which lets you mutate items as they are pushed into an array
656
703
  * @param {any[]} arr
657
- * @param {EnrichArrayOptions} opts
658
- * @returns {EnrichArray}}
704
+ * @param {hookArrayOptions} opts
705
+ * @returns {hookArray}}
659
706
  */
660
- function enrichArray(arr = [], opts = {}) {
707
+ function hookArray(arr = [], opts = {}) {
661
708
  const { hook = a => a, type = "", ...rest } = opts;
662
709
 
663
710
  function transformThenPush(item) {
@@ -703,7 +750,7 @@ function enrichArray(arr = [], opts = {}) {
703
750
 
704
751
  }
705
752
 
706
- /** @type {EnrichArray} */
753
+ /** @type {hookArray} */
707
754
  // @ts-ignore
708
755
  const enrichedArray = arr;
709
756
 
@@ -714,12 +761,15 @@ function enrichArray(arr = [], opts = {}) {
714
761
  return enrichedArray;
715
762
  };
716
763
 
764
+ /**
765
+ * @param {Config} config
766
+ */
717
767
  function buildFileNames(config) {
718
768
  const { format = "csv", groupKeys = [], lookupTables = [] } = config;
719
769
  let extension = "";
720
770
  extension = format === "csv" ? "csv" : "json";
721
771
  // const current = dayjs.utc().format("MM-DD-HH");
722
- const simName = config.simulationName;
772
+ let simName = config.simulationName;
723
773
  let writeDir = "./";
724
774
  if (config.writeToDisk) {
725
775
  const dataFolder = path.resolve("./data");
@@ -732,13 +782,17 @@ function buildFileNames(config) {
732
782
  const writePaths = {
733
783
  eventFiles: [path.join(writeDir, `${simName}-EVENTS.${extension}`)],
734
784
  userFiles: [path.join(writeDir, `${simName}-USERS.${extension}`)],
735
- adSpendFiles: [path.join(writeDir, `${simName}-AD-SPEND.${extension}`)],
785
+ adSpendFiles: [],
736
786
  scdFiles: [],
737
787
  mirrorFiles: [],
738
788
  groupFiles: [],
739
789
  lookupFiles: [],
740
790
  folder: writeDir,
741
791
  };
792
+ //add ad spend files
793
+ if (config?.hasAdSpend) {
794
+ writePaths.adSpendFiles.push(path.join(writeDir, `${simName}-AD-SPEND.${extension}`));
795
+ }
742
796
 
743
797
  //add SCD files
744
798
  const scdKeys = Object.keys(config?.scdProps || {});
@@ -869,7 +923,9 @@ function TimeSoup(earliestTime, latestTime, peaks = 5, deviation = 2, mean = 0)
869
923
  iterations++;
870
924
  offset = chance.normal({ mean: mean, dev: chunkSize / deviation });
871
925
  isValidTime = validateTime(chunkMid + offset, earliestTime, latestTime);
872
- if (iterations > 10000) throw new Error("Too many iterations");
926
+ if (iterations > 25000) {
927
+ throw `${iterations} iterations... exceeded`;
928
+ }
873
929
  } while (chunkMid + offset < chunkStart || chunkMid + offset > chunkEnd);
874
930
 
875
931
  try {
@@ -1026,7 +1082,7 @@ module.exports = {
1026
1082
  boxMullerRandom,
1027
1083
  applySkew,
1028
1084
  mapToRange,
1029
- weightedRange,
1085
+ weighNumRange,
1030
1086
  progress,
1031
1087
  range,
1032
1088
  openFinder,
@@ -1044,11 +1100,12 @@ module.exports = {
1044
1100
  shuffleOutside,
1045
1101
  interruptArray,
1046
1102
  generateUser,
1047
- enrichArray,
1103
+ hookArray,
1048
1104
  optimizedBoxMuller,
1049
1105
  buildFileNames,
1050
1106
  streamJSON,
1051
1107
  streamCSV,
1052
1108
  inferFunnels,
1053
- datesBetween
1109
+ datesBetween,
1110
+ weighChoices
1054
1111
  };
File without changes
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "make-mp-data",
3
- "version": "1.4.03",
3
+ "version": "1.4.05",
4
4
  "description": "builds all mixpanel primitives for a given project",
5
5
  "main": "index.js",
6
6
  "types": "types.d.ts",
7
7
  "scripts": {
8
8
  "start": "node ./core/index.js",
9
- "dev": "nodemon scratch.mjs --ignore /data",
10
- "complex": "nodemon ./core/index.js --complex --e 10000 --u 100",
11
- "simple": "nodemon ./core/index.js --simple --e 10000 --u 100",
9
+ "dev": "nodemon scratch.mjs --ignore /data",
12
10
  "prune": "rm -f ./data/* && rm -f ./tmp/*",
13
11
  "post": "npm publish",
14
12
  "test": "NODE_ENV=test jest --runInBand",
15
- "deps": "sh ./scripts/deps.sh"
13
+ "coverage": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js --coverage && open ./tests/coverage/lcov-report/index.html",
14
+ "deps": "sh ./scripts/deps.sh",
15
+ "new": "sh ./scripts/new.sh"
16
16
  },
17
17
  "repository": {
18
18
  "type": "git",
package/schemas/anon.js CHANGED
@@ -14,7 +14,7 @@ const dayjs = require("dayjs");
14
14
  const utc = require("dayjs/plugin/utc");
15
15
  dayjs.extend(utc);
16
16
  const { uid, comma } = require('ak-tools');
17
- const { pickAWinner, weightedRange, date, integer } = require('../core/utils');
17
+ const { pickAWinner, weighNumRange, date, integer } = require('../core/utils');
18
18
 
19
19
 
20
20
 
@@ -86,7 +86,7 @@ const config = {
86
86
  superProps: {},
87
87
  userProps: {
88
88
  title: chance.profession.bind(chance),
89
- luckyNumber: weightedRange(42, 420),
89
+ luckyNumber: weighNumRange(42, 420),
90
90
  spiritAnimal: ["duck", "dog", "otter", "penguin", "cat", "elephant", "lion", "cheetah", "giraffe", "zebra", "rhino", "hippo", "whale", "dolphin", "shark", "octopus", "squid", "jellyfish", "starfish", "seahorse", "crab", "lobster", "shrimp", "clam", "snail", "slug", "butterfly", "moth", "bee", "wasp", "ant", "beetle", "ladybug", "caterpillar", "centipede", "millipede", "scorpion", "spider", "tarantula", "tick", "mite", "mosquito", "fly", "dragonfly", "damselfly", "grasshopper", "cricket", "locust", "mantis", "cockroach", "termite", "praying mantis", "walking stick", "stick bug", "leaf insect", "lacewing", "aphid", "cicada", "thrips", "psyllid", "scale insect", "whitefly", "mealybug", "planthopper", "leafhopper", "treehopper", "flea", "louse", "bedbug", "flea beetle", "weevil", "longhorn beetle", "leaf beetle", "tiger beetle", "ground beetle", "lady beetle", "firefly", "click beetle", "rove beetle", "scarab beetle", "dung beetle", "stag beetle", "rhinoceros beetle", "hercules beetle", "goliath beetle", "jewel beetle", "tortoise beetle"]
91
91
  },
92
92
  scdProps: {},