make-mp-data 2.0.18 → 2.0.21

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/lib/utils/ai.js CHANGED
@@ -1,89 +1,67 @@
1
- import { GoogleGenerativeAI } from "@google/generative-ai";
2
- import * as u from "ak-tools";
3
1
 
4
- import 'dotenv/config';
2
+ /** @typedef {import('../../types.js').Dungeon} Dungeon */
3
+ /** @typedef {import('../../types.js').EventConfig} EventConfig */
5
4
 
5
+ import * as u from "ak-tools";
6
+ import 'dotenv/config';
6
7
  const { GEMINI_API_KEY: API_KEY, NODE_ENV = "unknown" } = process.env;
7
8
  if (!API_KEY) throw new Error("Please provide a Gemini API key");
9
+ import AITransformer from 'ak-gemini';
8
10
 
9
- async function generateSchema(userInput) {
10
- const gemini = new GoogleGenerativeAI(API_KEY);
11
- const model = gemini.getGenerativeModel({ model: "gemini-2.0-flash" });
12
- const PROOMPTY = await u.load("./components/prompt.txt");
13
- const prompt = `
14
- Given the following information about a website or app:
15
-
16
- ${userInput}
17
-
18
- ${PROOMPTY}
11
+ let CURRENT_PROMPT = ``;
12
+ CURRENT_PROMPT = `build me a dungeon stream with these events and structure
19
13
 
20
- REMEMBER, YOUR INPUT IS:
14
+ { "event": "impression", "carousel": [{"product": "big mac"}] }
15
+ { "event": "viewed", "product_viewed": "big mac" }
16
+ { "event": "add to basket", "product_added": "big mac" }
17
+ { "event": "customized", "product_customized": "big mac" }
18
+ { "event": "checked out", "cart": [{"item": "big mac"}] }
21
19
 
22
- ${userInput}
23
- `.trim();
24
20
 
25
- let schema;
26
- let schemaIsValid = false;
27
- let attempts = 0;
28
- do {
29
- attempts++;
30
- const result = await model.generateContent(prompt);
31
- const response = await result.response;
32
- const text = response.text();
33
- schema = processResponse(text);
34
- schemaIsValid = validator(schema);
35
- } while (!schemaIsValid);
21
+ but use all the different mcdonalds products as a possible values`;
22
+ CURRENT_PROMPT = ``;
36
23
 
37
- return schema;
38
- }
39
-
40
- function processResponse(text) {
41
- let json;
42
- // check for ```json
43
- const start = text.indexOf("```json");
44
- const end = text.indexOf("```", start + 1);
45
-
46
- if (start === -1 || end === -1) {
47
- const start = text.indexOf("{");
48
- const end = text.lastIndexOf("}");
49
- json = text.slice(start, end + 1).trim();
50
- }
51
24
 
52
- json = text.slice(start + 7, end).trim();
53
25
 
54
- try {
55
- return JSON.parse(json);
56
- }
57
- catch (e) {
58
- return null;
59
- }
60
-
61
-
62
- }
26
+ async function main(params) {
27
+ const { prompt } = params;
28
+ if (!prompt) throw new Error("Please provide a prompt");
29
+ let INSTRUCTIONS = await u.load('./lib/templates/instructions.txt', false);
30
+ const TYPES = await u.load('./lib/templates/abbreviated.d.ts', false);
31
+ const VERBOSE_SCHEMA_FILE = await u.load('./lib/templates/verbose-schema.js', false);
32
+ const VERBOSE_SCHEMA = VERBOSE_SCHEMA_FILE.split(`//SPLIT HERE`).pop()?.trim() || ``;
33
+ INSTRUCTIONS = INSTRUCTIONS
34
+ .replace(/<TYPES>/g, TYPES)
35
+ .replace(/<VERBOSE_SCHEMA>/g, VERBOSE_SCHEMA);
63
36
 
64
- function validator(schema) {
65
- let valid = true;
37
+ const ai = new AITransformer({
38
+ apiKey: API_KEY,
39
+ onlyJSON: false,
40
+ systemInstructions: INSTRUCTIONS?.trim(),
41
+ modelName: "gemini-2.5-pro",
66
42
 
67
- //null schema are always invalid
68
- if (!schema) valid = false;
69
43
 
70
- //must have 3 or more events
71
- if (schema?.events?.length < 3) valid = false;
44
+ });
45
+ await ai.init();
46
+ const response = await ai.message(prompt);
72
47
 
73
- //must have 2 or more superProps
74
- if (Object.keys(schema.superProps).length < 2) valid = false;
48
+ // if (NODE_ENV === "dev") {
49
+ // debugger;
50
+ // }
75
51
 
76
- //must have 2 or more userProps
77
- if (Object.keys(schema.userProps).length < 2) valid = false;
52
+ return response;
78
53
 
79
- return valid;
80
54
  }
81
55
 
82
56
 
83
- export { generateSchema };
57
+ export default main;
84
58
 
85
59
  if (import.meta.url === `file://${process.argv[1]}`) {
86
- generateSchema(`metube, a video streaming company like youutube, where users watch videos, search, like, comment, subscribe, share, create playlists, etc...`)
60
+ main(
61
+ {
62
+ prompt: CURRENT_PROMPT || "Generate a dungeon spec for a simple e-commerce site with checkout and add to cart events."
63
+ }
64
+ )
87
65
  .then((result) => {
88
66
  if (NODE_ENV === "dev") debugger;
89
67
  })
@@ -1,3 +1,7 @@
1
+ /** @typedef {import('../../types.js').EventSchema} EventSchema */
2
+ /** @typedef {import('../../types.js').Result} Result */
3
+ /** @typedef {import('../../types.js').Context} Context */
4
+
1
5
  import { ChartJSNodeCanvas } from 'chartjs-node-canvas';
2
6
  import fs from 'fs';
3
7
  import * as u from 'ak-tools';
@@ -179,6 +183,7 @@ async function generateLineChart(rawData, signupEvents = ["sign up"], fileName)
179
183
  const imageBuffer = await chartJSNodeCanvas.renderToBuffer(configuration);
180
184
  const filePath = path.join(tempDir, `${fileName}.png`);
181
185
  const removed = await u.rm(filePath);
186
+ // @ts-ignore - imageBuffer is a Buffer but touch accepts it
182
187
  const file = await u.touch(filePath, imageBuffer);
183
188
 
184
189
  console.log(`Chart saved as ${fileName}.png`);
@@ -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,7 @@ 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
14
 
15
15
  /** @typedef {import('../../types').Dungeon} Config */
16
16
  /** @typedef {import('../../types').EventConfig} EventConfig */
@@ -186,6 +186,27 @@ function day(start, end) {
186
186
  function choose(value) {
187
187
  const chance = getChance();
188
188
 
189
+ // most of the time this will receive a list of strings;
190
+ // when that is the case, we need to ensure some 'keywords' like 'variant' or 'test' aren't in the array
191
+ // 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']
192
+ // if all these are true we will pickAWinner(value)()
193
+ if (Array.isArray(value) && value.length > 2 && value.length < 20 && value.every(item => typeof item === 'string')) {
194
+ // ensure terms 'variant' 'group' 'experiment' or 'population' are NOT in any of the items
195
+ if (!value.some(item => item.includes('variant') || item.includes('group') || item.includes('experiment') || item.includes('population'))) {
196
+ // check to make sure that each element in the array only occurs once...
197
+ const uniqueItems = new Set(value);
198
+ if (uniqueItems.size === value.length) {
199
+ // Array has no duplicates, use pickAWinner
200
+ const quickList = pickAWinner(value, 0)();
201
+ const theChosenOne = chance.pickone(quickList);
202
+ return theChosenOne;
203
+ }
204
+
205
+ }
206
+
207
+ }
208
+
209
+
189
210
  try {
190
211
  // Keep resolving the value if it's a function (with caching)
191
212
  while (typeof value === 'function') {
@@ -218,13 +239,12 @@ function choose(value) {
218
239
  return chance.pickone(value);
219
240
  }
220
241
 
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);
242
+ // PERFORMANCE: Optimized array handling - check first item type instead of every()
243
+ if (Array.isArray(value) && value.length > 0) {
244
+ const firstType = typeof value[0];
245
+ if (firstType === 'string' || firstType === 'number') {
246
+ return chance.pickone(value);
247
+ }
228
248
  }
229
249
 
230
250
  if (Array.isArray(value) && value.every(item => typeof item === 'object')) {
@@ -331,7 +351,10 @@ function integer(min = 1, max = 100) {
331
351
  };
332
352
 
333
353
 
334
-
354
+ function decimal(min = 0, max = 1, fixed = 2) {
355
+ const chance = getChance();
356
+ return chance.floating({ min, max, fixed });
357
+ }
335
358
 
336
359
 
337
360
  /*
@@ -684,18 +707,18 @@ function pickAWinner(items, mostChosenIndex) {
684
707
  }
685
708
 
686
709
  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();
710
+ let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
711
+ for (let i = 0, ch; i < str.length; i++) {
712
+ ch = str.charCodeAt(i);
713
+ h1 = Math.imul(h1 ^ ch, 2654435761);
714
+ h2 = Math.imul(h2 ^ ch, 1597334677);
715
+ }
716
+ h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
717
+ h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
718
+ h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
719
+ h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
720
+
721
+ return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString();
699
722
  };
700
723
 
701
724
  /*
@@ -717,10 +740,10 @@ function shuffleArray(array) {
717
740
  }
718
741
 
719
742
  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];
743
+ if (!array || array.length === 0) return undefined;
744
+ // PERFORMANCE: Use Math.random() instead of chance.integer() for simple cases
745
+ const randomIndex = Math.floor(Math.random() * array.length);
746
+ return array[randomIndex];
724
747
  }
725
748
 
726
749
  function shuffleExceptFirst(array) {
@@ -918,25 +941,25 @@ function buildFileNames(config) {
918
941
  * @param {[string, number][]} arrayOfArrays
919
942
  */
920
943
  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);
944
+ const terminalWidth = process.stdout.columns || 120;
945
+
946
+ // Clear the entire line
947
+ readline.cursorTo(process.stdout, 0);
948
+ readline.clearLine(process.stdout, 0);
949
+
950
+ // Build message with better formatting
951
+ const items = arrayOfArrays.map(([thing, p]) => {
952
+ return `${thing}: ${comma(p)}`;
953
+ });
954
+
955
+ const message = items.join(' │ ');
956
+
957
+ // Ensure we don't exceed terminal width
958
+ const finalMessage = message.length > terminalWidth
959
+ ? message.substring(0, terminalWidth - 3) + '...'
960
+ : message.padEnd(terminalWidth, ' ');
961
+
962
+ process.stdout.write(finalMessage);
940
963
  }
941
964
 
942
965
  function openFinder(path, callback) {
@@ -1271,6 +1294,7 @@ export {
1271
1294
  deepClone,
1272
1295
  initChance,
1273
1296
  getChance,
1297
+ decimal,
1274
1298
 
1275
1299
  validTime,
1276
1300
  validEvent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "make-mp-data",
3
- "version": "2.0.18",
3
+ "version": "2.0.21",
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.1",
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.15",
75
74
  "p-limit": "^3.1.0",
76
75
  "yargs": "^17.7.2"
77
76
  },
@@ -87,4 +86,4 @@
87
86
  "tmp/"
88
87
  ]
89
88
  }
90
- }
89
+ }