make-mp-data 2.0.19 → 2.0.22

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,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import Chance from 'chance';
3
3
  import readline from 'readline';
4
- import { comma, uid} from 'ak-tools';
4
+ import { comma, uid } from 'ak-tools';
5
5
  import { spawn } from 'child_process';
6
6
  import dayjs from 'dayjs';
7
7
  import utc from 'dayjs/plugin/utc.js';
@@ -10,7 +10,8 @@ import { mkdir, parseGCSUri } from 'ak-tools';
10
10
  import { existsSync } from 'fs';
11
11
  dayjs.extend(utc);
12
12
  import 'dotenv/config';
13
- import { domainSuffix, domainPrefix } from '../data/defaults.js';
13
+ import { domainSuffix, domainPrefix } from '../templates/defaults.js';
14
+ const {NODE_ENV = "unknown"} = process.env;
14
15
 
15
16
  /** @typedef {import('../../types').Dungeon} Config */
16
17
  /** @typedef {import('../../types').EventConfig} EventConfig */
@@ -186,6 +187,27 @@ function day(start, end) {
186
187
  function choose(value) {
187
188
  const chance = getChance();
188
189
 
190
+ // most of the time this will receive a list of strings;
191
+ // when that is the case, we need to ensure some 'keywords' like 'variant' or 'test' aren't in the array
192
+ // next we want to see if the array is unweighted ... i.e. no dupe strings and each string only occurs once ['a', 'b', 'c', 'd']
193
+ // if all these are true we will pickAWinner(value)()
194
+ if (Array.isArray(value) && value.length > 2 && value.length < 20 && value.every(item => typeof item === 'string')) {
195
+ // ensure terms 'variant' 'group' 'experiment' or 'population' are NOT in any of the items
196
+ if (!value.some(item => item.includes('variant') || item.includes('group') || item.includes('experiment') || item.includes('population'))) {
197
+ // check to make sure that each element in the array only occurs once...
198
+ const uniqueItems = new Set(value);
199
+ if (uniqueItems.size === value.length) {
200
+ // Array has no duplicates, use pickAWinner
201
+ const quickList = pickAWinner(value, 0)();
202
+ const theChosenOne = chance.pickone(quickList);
203
+ return theChosenOne;
204
+ }
205
+
206
+ }
207
+
208
+ }
209
+
210
+
189
211
  try {
190
212
  // Keep resolving the value if it's a function (with caching)
191
213
  while (typeof value === 'function') {
@@ -218,13 +240,12 @@ function choose(value) {
218
240
  return chance.pickone(value);
219
241
  }
220
242
 
221
- // Now, if the resolved value is an array, use chance.pickone
222
- if (Array.isArray(value) && value.every(item => typeof item === 'string')) {
223
- return chance.pickone(value);
224
- }
225
-
226
- if (Array.isArray(value) && value.every(item => typeof item === 'number')) {
227
- return chance.pickone(value);
243
+ // PERFORMANCE: Optimized array handling - check first item type instead of every()
244
+ if (Array.isArray(value) && value.length > 0) {
245
+ const firstType = typeof value[0];
246
+ if (firstType === 'string' || firstType === 'number') {
247
+ return chance.pickone(value);
248
+ }
228
249
  }
229
250
 
230
251
  if (Array.isArray(value) && value.every(item => typeof item === 'object')) {
@@ -331,7 +352,10 @@ function integer(min = 1, max = 100) {
331
352
  };
332
353
 
333
354
 
334
-
355
+ function decimal(min = 0, max = 1, fixed = 2) {
356
+ const chance = getChance();
357
+ return chance.floating({ min, max, fixed });
358
+ }
335
359
 
336
360
 
337
361
  /*
@@ -522,13 +546,7 @@ WEIGHERS
522
546
  ----
523
547
  */
524
548
 
525
- function weighFunnels(acc, funnel) {
526
- const weight = funnel?.weight || 1;
527
- for (let i = 0; i < weight; i++) {
528
- acc.push(funnel);
529
- }
530
- return acc;
531
- }
549
+
532
550
 
533
551
  /**
534
552
  * a utility function to generate a range of numbers within a given skew
@@ -684,18 +702,18 @@ function pickAWinner(items, mostChosenIndex) {
684
702
  }
685
703
 
686
704
  function quickHash(str, seed = 0) {
687
- let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
688
- for (let i = 0, ch; i < str.length; i++) {
689
- ch = str.charCodeAt(i);
690
- h1 = Math.imul(h1 ^ ch, 2654435761);
691
- h2 = Math.imul(h2 ^ ch, 1597334677);
692
- }
693
- h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
694
- h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
695
- h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
696
- h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
697
-
698
- return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString();
705
+ let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
706
+ for (let i = 0, ch; i < str.length; i++) {
707
+ ch = str.charCodeAt(i);
708
+ h1 = Math.imul(h1 ^ ch, 2654435761);
709
+ h2 = Math.imul(h2 ^ ch, 1597334677);
710
+ }
711
+ h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
712
+ h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
713
+ h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
714
+ h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
715
+
716
+ return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString();
699
717
  };
700
718
 
701
719
  /*
@@ -717,10 +735,10 @@ function shuffleArray(array) {
717
735
  }
718
736
 
719
737
  function pickRandom(array) {
720
- const chance = getChance();
721
- if (!array || array.length === 0) return undefined;
722
- const randomIndex = chance.integer({ min: 0, max: array.length - 1 });
723
- return array[randomIndex];
738
+ if (!array || array.length === 0) return undefined;
739
+ // PERFORMANCE: Use Math.random() instead of chance.integer() for simple cases
740
+ const randomIndex = Math.floor(Math.random() * array.length);
741
+ return array[randomIndex];
724
742
  }
725
743
 
726
744
  function shuffleExceptFirst(array) {
@@ -918,25 +936,25 @@ function buildFileNames(config) {
918
936
  * @param {[string, number][]} arrayOfArrays
919
937
  */
920
938
  function progress(arrayOfArrays) {
921
- const terminalWidth = process.stdout.columns || 120;
922
-
923
- // Clear the entire line
924
- readline.cursorTo(process.stdout, 0);
925
- readline.clearLine(process.stdout, 0);
926
-
927
- // Build message with better formatting
928
- const items = arrayOfArrays.map(([thing, p]) => {
929
- return `${thing}: ${comma(p)}`;
930
- });
931
-
932
- const message = items.join(' │ ');
933
-
934
- // Ensure we don't exceed terminal width
935
- const finalMessage = message.length > terminalWidth
936
- ? message.substring(0, terminalWidth - 3) + '...'
937
- : message.padEnd(terminalWidth, ' ');
938
-
939
- process.stdout.write(finalMessage);
939
+ const terminalWidth = process.stdout.columns || 120;
940
+
941
+ // Clear the entire line
942
+ readline.cursorTo(process.stdout, 0);
943
+ readline.clearLine(process.stdout, 0);
944
+
945
+ // Build message with better formatting
946
+ const items = arrayOfArrays.map(([thing, p]) => {
947
+ return `${thing}: ${comma(p)}`;
948
+ });
949
+
950
+ const message = items.join(' │ ');
951
+
952
+ // Ensure we don't exceed terminal width
953
+ const finalMessage = message.length > terminalWidth
954
+ ? message.substring(0, terminalWidth - 3) + '...'
955
+ : message.padEnd(terminalWidth, ' ');
956
+
957
+ process.stdout.write(finalMessage);
940
958
  }
941
959
 
942
960
  function openFinder(path, callback) {
@@ -997,6 +1015,46 @@ let soupHits = 0;
997
1015
  * @param {number} [peaks=5]
998
1016
  */
999
1017
  function TimeSoup(earliestTime, latestTime, peaks = 5, deviation = 2, mean = 0) {
1018
+ if (!earliestTime) earliestTime = global.FIXED_BEGIN ? global.FIXED_BEGIN : dayjs().subtract(30, 'd').unix(); // 30 days ago
1019
+ if (!latestTime) latestTime = global.FIXED_NOW ? global.FIXED_NOW : dayjs().unix();
1020
+ const chance = getChance();
1021
+ const totalRange = latestTime - earliestTime;
1022
+ const chunkSize = totalRange / peaks;
1023
+
1024
+ // Select a random chunk based on the number of peaks
1025
+ const peakIndex = integer(0, peaks - 1);
1026
+ const chunkStart = earliestTime + peakIndex * chunkSize;
1027
+ const chunkEnd = chunkStart + chunkSize;
1028
+ const chunkMid = (chunkStart + chunkEnd) / 2;
1029
+
1030
+ // Generate a single timestamp within this chunk using a normal distribution centered at chunkMid
1031
+ let offset;
1032
+ let iterations = 0;
1033
+ let isValidTime = false;
1034
+ do {
1035
+ iterations++;
1036
+ soupHits++;
1037
+ offset = chance.normal({ mean: mean, dev: chunkSize / deviation });
1038
+ isValidTime = validTime(chunkMid + offset, earliestTime, latestTime);
1039
+ if (iterations > 25000) {
1040
+ throw `${iterations} iterations... exceeded`;
1041
+ }
1042
+ } while (chunkMid + offset < chunkStart || chunkMid + offset > chunkEnd);
1043
+
1044
+ try {
1045
+ return dayjs.unix(chunkMid + offset).toISOString();
1046
+ }
1047
+
1048
+ catch (e) {
1049
+ //escape hatch
1050
+ // console.log('BAD TIME', e?.message);
1051
+ if (NODE_ENV === 'dev') debugger;
1052
+ return dayjs.unix(integer(earliestTime, latestTime)).toISOString();
1053
+ }
1054
+ }
1055
+
1056
+
1057
+ function NewTimeSoup(earliestTime, latestTime, peaks = 5, deviation = 2, mean = 0) {
1000
1058
  if (!earliestTime) earliestTime = global.FIXED_BEGIN ? global.FIXED_BEGIN : dayjs().subtract(30, 'd').unix(); // 30 days ago
1001
1059
  if (!latestTime) latestTime = global.FIXED_NOW ? global.FIXED_NOW : dayjs().unix();
1002
1060
  const chance = getChance();
@@ -1176,7 +1234,10 @@ function wrapFunc(obj, func, recursion = 0, parentKey = null, grandParentKey = n
1176
1234
 
1177
1235
  // }
1178
1236
 
1179
-
1237
+ const chance = getChance();
1238
+ function odds(num) {
1239
+ return chance.bool({ likelihood: num });
1240
+ }
1180
1241
 
1181
1242
  /**
1182
1243
  * makes a random-sized array of emojis
@@ -1271,7 +1332,8 @@ export {
1271
1332
  deepClone,
1272
1333
  initChance,
1273
1334
  getChance,
1274
-
1335
+ decimal,
1336
+ odds,
1275
1337
  validTime,
1276
1338
  validEvent,
1277
1339
 
@@ -1287,7 +1349,6 @@ export {
1287
1349
  pickAWinner,
1288
1350
  quickHash,
1289
1351
  weighArray,
1290
- weighFunnels,
1291
1352
  validateEventConfig,
1292
1353
  shuffleArray,
1293
1354
  shuffleExceptFirst,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "make-mp-data",
3
- "version": "2.0.19",
3
+ "version": "2.0.22",
4
4
  "description": "builds all mixpanel primitives for a given project",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -16,6 +16,7 @@
16
16
  "README.md"
17
17
  ],
18
18
  "scripts": {
19
+ "new:dungeon": "node ./scripts/create-dungeon.mjs",
19
20
  "start": "node ./index.js",
20
21
  "dev": "nodemon scratch.mjs --ignore ./data/*",
21
22
  "prune": "rm -f ./data/* && rm -f ./tmp/* && rm -f vscode-profile-*",
@@ -24,10 +25,9 @@
24
25
  "test": "NODE_ENV=test vitest run",
25
26
  "coverage": "vitest run --coverage && open ./coverage/index.html",
26
27
  "typecheck": "tsc --noEmit",
27
- "new:dungeon": "./scripts/new-dungeon.sh",
28
- "new:project": "node ./scripts/new-project.mjs",
29
- "exp:benchmark": "node ./tests/benchmark/concurrency.mjs",
30
- "benchmark:phase1": "node ./tests/benchmark/phase1-performance.mjs",
28
+ "typecheck:build": "tsc --noEmit --project tsconfig.build.json",
29
+ "typecheck:strict": "echo '⚠️ Running pedantic strict checks (many false positives expected)...' && tsc --noEmit --project tsconfig.build.json --strict",
30
+ "typecheck:summary": "echo '🔍 Type-checking all shipped files...' && npm run typecheck:build && echo '✅ All TypeScript checks passed!'",
31
31
  "test:quick": "node ./tests/benchmark/quick-test.mjs",
32
32
  "exp:soup": "node ./tests/testSoup.mjs",
33
33
  "func:local": "functions-framework --target=entry",
@@ -61,17 +61,16 @@
61
61
  "dependencies": {
62
62
  "@google-cloud/functions-framework": "^3.4.2",
63
63
  "@google-cloud/storage": "^7.14.0",
64
- "@google/generative-ai": "^0.24.1",
65
- "ak-fetch": "^2.0.1",
66
- "ak-gemini": "^1.0.55",
67
- "ak-tools": "^1.1.0",
64
+ "ak-fetch": "^2.0.12",
65
+ "ak-gemini": "^1.0.59",
66
+ "ak-tools": "^1.1.12",
68
67
  "chance": "^1.1.11",
69
68
  "chart.js": "^3.9.1",
70
69
  "chartjs-node-canvas": "^4.1.6",
71
70
  "dayjs": "^1.11.11",
72
71
  "dotenv": "^16.4.5",
73
72
  "google-auth-library": "^9.15.0",
74
- "mixpanel-import": "^2.8.14",
73
+ "mixpanel-import": "^2.8.162",
75
74
  "p-limit": "^3.1.0",
76
75
  "yargs": "^17.7.2"
77
76
  },