testeranto 0.110.0 → 0.112.0

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,4 +1,10 @@
1
1
  import { IRunTime, IBuiltConfig } from "./lib";
2
+ export type ISummary = Record<string, {
3
+ runTimeError?: number | "?";
4
+ typeErrors?: number | "?";
5
+ staticErrors?: number | "?";
6
+ prompt?: string | "?";
7
+ }>;
2
8
  export declare const destinationOfRuntime: (f: string, r: IRunTime, configs: IBuiltConfig) => string;
3
9
  export declare const tscPather: (entryPoint: string, platform: "web" | "node") => string;
4
10
  export declare const tscExitCodePather: (entryPoint: string, platform: "web" | "node") => string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "testeranto",
3
3
  "description": "the AI powered BDD test framework for typescript projects",
4
- "version": "0.110.0",
4
+ "version": "0.112.0",
5
5
  "engines": {
6
6
  "node": "18.18.0"
7
7
  },
package/src/PM/main.ts CHANGED
@@ -1,30 +1,43 @@
1
+ import ts from "typescript";
2
+
1
3
  import { CdpPage, Page } from "puppeteer-core/lib/esm/puppeteer";
2
- import fs from "fs";
4
+ import fs, { watch } from "fs";
3
5
  import path from "path";
4
6
  import puppeteer, {
5
7
  Browser,
6
8
  ConsoleMessage,
7
- LaunchOptions,
8
9
  ScreenRecorder,
9
10
  ScreenshotOptions,
10
11
  } from "puppeteer-core";
11
12
  import { PassThrough } from "stream";
12
13
  import ansiC from "ansi-colors";
13
-
14
+ import crypto from "node:crypto";
15
+ import { ESLint } from "eslint";
16
+ import tsc from "tsc-prog";
14
17
  import {
15
18
  IBuiltConfig,
16
19
  IFinalResults,
20
+ IRunnables,
17
21
  ITestTypes,
18
22
  ITLog,
19
23
  } from "../lib/index.js";
20
- import {
21
- bddExitCodePather,
22
- lintExitCodePather,
23
- tscExitCodePather,
24
- } from "../utils";
24
+ import { ISummary, lintPather, tscPather } from "../utils";
25
25
 
26
26
  import { PM } from "./index.js";
27
27
 
28
+ type IOutputs = Record<
29
+ string,
30
+ {
31
+ entryPoint: string;
32
+ inputs: Record<string, string>;
33
+ }
34
+ >;
35
+ const eslint = new ESLint();
36
+ const formatter = await eslint.loadFormatter(
37
+ "./node_modules/testeranto/dist/prebuild/esbuildConfigs/eslint-formatter-testeranto.mjs"
38
+ );
39
+ const changes: Record<string, string> = {};
40
+ const fileHashes = {};
28
41
  const fileStreams3: fs.WriteStream[] = [];
29
42
  type IFPaths = string[];
30
43
  const fPaths: IFPaths = [];
@@ -32,6 +45,52 @@ const files: Record<string, Set<string>> = {};
32
45
  const recorders: Record<string, ScreenRecorder> = {};
33
46
  const screenshots: Record<string, Promise<Uint8Array>[]> = {};
34
47
 
48
+ async function fileHash(filePath, algorithm = "md5") {
49
+ return new Promise<string>((resolve, reject) => {
50
+ const hash = crypto.createHash(algorithm);
51
+ const fileStream = fs.createReadStream(filePath);
52
+
53
+ fileStream.on("data", (data) => {
54
+ hash.update(data);
55
+ });
56
+
57
+ fileStream.on("end", () => {
58
+ const fileHash = hash.digest("hex");
59
+ resolve(fileHash);
60
+ });
61
+
62
+ fileStream.on("error", (error) => {
63
+ reject(`Error reading file: ${error.message}`);
64
+ });
65
+ });
66
+ }
67
+
68
+ const getRunnables = (
69
+ tests: ITestTypes[],
70
+ payload = {
71
+ nodeEntryPoints: {},
72
+ webEntryPoints: {},
73
+ }
74
+ ): IRunnables => {
75
+ return tests.reduce((pt, cv, cndx, cry) => {
76
+ if (cv[1] === "node") {
77
+ pt.nodeEntryPoints[cv[0]] = path.resolve(
78
+ `./docs/node/${cv[0].split(".").slice(0, -1).concat("mjs").join(".")}`
79
+ );
80
+ } else if (cv[1] === "web") {
81
+ pt.webEntryPoints[cv[0]] = path.resolve(
82
+ `./docs/web/${cv[0].split(".").slice(0, -1).concat("mjs").join(".")}`
83
+ );
84
+ }
85
+
86
+ if (cv[3].length) {
87
+ getRunnables(cv[3], payload);
88
+ }
89
+
90
+ return pt;
91
+ }, payload as IRunnables);
92
+ };
93
+
35
94
  const statusMessagePretty = (failures: number, test: string) => {
36
95
  if (failures === 0) {
37
96
  console.log(ansiC.green(ansiC.inverse(`> ${test} completed successfully`)));
@@ -51,6 +110,16 @@ async function writeFileAndCreateDir(filePath, data) {
51
110
  }
52
111
  }
53
112
 
113
+ const filesHash = async (files: string[], algorithm = "md5") => {
114
+ return new Promise<string>((resolve, reject) => {
115
+ resolve(
116
+ files.reduce(async (mm: Promise<string>, f) => {
117
+ return (await mm) + (await fileHash(f));
118
+ }, Promise.resolve(""))
119
+ );
120
+ });
121
+ };
122
+
54
123
  function isValidUrl(string) {
55
124
  try {
56
125
  new URL(string);
@@ -68,25 +137,27 @@ export class PM_Main extends PM {
68
137
  ports: Record<number, boolean>;
69
138
  queue: any[];
70
139
 
71
- bigBoard: Record<
72
- string,
73
- {
74
- status: "?" | "running" | "waiting";
75
- runTimeError?: number;
76
- typeErrors?: string;
77
- staticErrors?: string;
78
- }
79
- > = {};
140
+ mode: "DEV" | "PROD";
141
+
142
+ bigBoard: ISummary = {};
143
+ webMetafileWatcher: fs.FSWatcher;
144
+ nodeMetafileWatcher: fs.FSWatcher;
80
145
 
81
146
  constructor(configs: IBuiltConfig) {
82
147
  super();
148
+
149
+ this.mode = configs.devMode ? "DEV" : "PROD";
150
+
83
151
  this.server = {};
84
152
  this.configs = configs;
85
153
  this.ports = {};
86
154
 
87
155
  this.configs.tests.forEach(([t]) => {
88
156
  this.bigBoard[t] = {
89
- status: "?",
157
+ runTimeError: "?",
158
+ typeErrors: "?",
159
+ staticErrors: "?",
160
+ prompt: "?",
90
161
  };
91
162
  });
92
163
 
@@ -178,32 +249,6 @@ export class PM_Main extends PM {
178
249
  fileStreams3[uid].end();
179
250
  };
180
251
 
181
- // async (ssOpts: ScreenshotOptions, testName: string) => {
182
- // const p = ssOpts.path as string;
183
- // const dir = path.dirname(p);
184
- // fs.mkdirSync(dir, {
185
- // recursive: true,
186
- // });
187
- // if (!files[testName]) {
188
- // files[testName] = new Set();
189
- // }
190
- // files[testName].add(ssOpts.path as string);
191
-
192
- // const sPromise = page.screenshot({
193
- // ...ssOpts,
194
- // path: p,
195
- // });
196
-
197
- // if (!screenshots[testName]) {
198
- // screenshots[testName] = [];
199
- // }
200
- // screenshots[testName].push(sPromise);
201
- // // sPromise.then(())
202
- // await sPromise;
203
- // return sPromise;
204
- // // page.evaluate(`window["screenshot done"]`);
205
- // };
206
-
207
252
  globalThis["customScreenShot"] = async (
208
253
  opts: { path: string },
209
254
  pageKey: string,
@@ -262,20 +307,16 @@ export class PM_Main extends PM {
262
307
 
263
308
  return opts.path;
264
309
  };
265
-
266
- // globalThis["customclose"] = (p: string, testName: string) => {
267
- // if (!files[testName]) {
268
- // files[testName] = new Set();
269
- // }
270
-
271
- // fs.writeFileSync(
272
- // p + "/manifest.json",
273
- // JSON.stringify(Array.from(files[testName]))
274
- // );
275
- // delete files[testName];
276
- // };
277
310
  }
278
311
 
312
+ stop = () => {
313
+ console.log(ansiC.inverse("Testeranto-Run is shutting down gracefully..."));
314
+ this.mode = "PROD";
315
+ this.nodeMetafileWatcher.close();
316
+ this.webMetafileWatcher.close();
317
+ this.checkForShutdown();
318
+ };
319
+
279
320
  customclose() {
280
321
  throw new Error("Method not implemented.");
281
322
  }
@@ -404,56 +445,323 @@ export class PM_Main extends PM {
404
445
  }
405
446
  ////////////////////////////////////////////////////////////////////////////////
406
447
 
407
- async startPuppeteer(
408
- options: LaunchOptions,
409
- destfolder: string
410
- ): Promise<any> {
411
- this.browser = (await puppeteer.launch(options)) as any;
448
+ async metafileOutputs(platform: "web" | "node") {
449
+ const metafile = JSON.parse(
450
+ fs.readFileSync(`docs/${platform}/metafile.json`).toString()
451
+ ).metafile;
452
+
453
+ if (!metafile) return;
454
+
455
+ const outputs: IOutputs = metafile.outputs;
456
+
457
+ Object.keys(outputs).forEach(async (k) => {
458
+ const addableFiles = Object.keys(outputs[k].inputs).filter((i) => {
459
+ if (!fs.existsSync(i)) return false;
460
+ if (i.startsWith("node_modules")) return false;
461
+ return true;
462
+ });
463
+
464
+ const f = `${k.split(".").slice(0, -1).join(".")}/`;
465
+ if (!fs.existsSync(f)) {
466
+ fs.mkdirSync(f);
467
+ }
468
+
469
+ const entrypoint = outputs[k].entryPoint;
470
+
471
+ if (entrypoint) {
472
+ const changeDigest = await filesHash(addableFiles);
473
+
474
+ if (changeDigest === changes[entrypoint]) {
475
+ // skip
476
+ } else {
477
+ changes[entrypoint] = changeDigest;
478
+ this.tscCheck({
479
+ platform,
480
+ addableFiles,
481
+ entrypoint: "./" + entrypoint,
482
+ });
483
+ this.eslintCheck("./" + entrypoint, platform, addableFiles);
484
+ this.makePrompt("./" + entrypoint, addableFiles, platform);
485
+ }
486
+ }
487
+ });
412
488
  }
413
489
 
414
- // goodbye = () => {
415
- // this.browser.disconnect().then(() => {
490
+ async start(): Promise<any> {
491
+ this.browser = (await puppeteer.launch({
492
+ slowMo: 1,
493
+ // timeout: 1,
494
+ waitForInitialPage: false,
495
+ executablePath:
496
+ // process.env.CHROMIUM_PATH || "/opt/homebrew/bin/chromium",
497
+ "/opt/homebrew/bin/chromium",
498
+ headless: true,
499
+ dumpio: true,
500
+ // timeout: 0,
501
+ devtools: true,
502
+
503
+ args: [
504
+ "--auto-open-devtools-for-tabs",
505
+ `--remote-debugging-port=3234`,
506
+
507
+ // "--disable-features=IsolateOrigins,site-per-process",
508
+ "--disable-site-isolation-trials",
509
+ "--allow-insecure-localhost",
510
+ "--allow-file-access-from-files",
511
+ "--allow-running-insecure-content",
512
+
513
+ "--disable-dev-shm-usage",
514
+ "--disable-extensions",
515
+ "--disable-gpu",
516
+ "--disable-setuid-sandbox",
517
+ "--disable-site-isolation-trials",
518
+ "--disable-web-security",
519
+ "--no-first-run",
520
+ "--no-sandbox",
521
+ "--no-startup-window",
522
+ // "--no-zygote",
523
+ "--reduce-security-for-testing",
524
+ "--remote-allow-origins=*",
525
+ "--unsafely-treat-insecure-origin-as-secure=*",
526
+ // "--disable-features=IsolateOrigins",
527
+ // "--remote-allow-origins=ws://localhost:3234",
528
+ // "--single-process",
529
+ // "--unsafely-treat-insecure-origin-as-secure",
530
+ // "--unsafely-treat-insecure-origin-as-secure=ws://192.168.0.101:3234",
531
+
532
+ // "--disk-cache-dir=/dev/null",
533
+ // "--disk-cache-size=1",
534
+ // "--start-maximized",
535
+ ],
536
+ })) as any;
537
+
538
+ const { nodeEntryPoints, webEntryPoints } = getRunnables(
539
+ this.configs.tests
540
+ );
416
541
 
417
- // console.log("Goodbye");
418
- // process.exit();
419
- // });
420
- // };
542
+ Object.entries(nodeEntryPoints).forEach(
543
+ ([k, outputFile]: [string, string]) => {
544
+ this.launchNode(k, outputFile);
545
+ try {
546
+ watch(outputFile, async (e, filename) => {
547
+ const hash = await fileHash(outputFile);
548
+ if (fileHashes[k] !== hash) {
549
+ fileHashes[k] = hash;
550
+ console.log(ansiC.green(ansiC.inverse(`< ${e} ${filename}`)));
551
+ this.launchNode(k, outputFile);
552
+ }
553
+ });
554
+ } catch (e) {
555
+ console.error(e);
556
+ }
557
+ }
558
+ );
421
559
 
422
- shutDown() {
423
- this.shutdownMode = true;
424
- this.checkForShutdown();
560
+ Object.entries(webEntryPoints).forEach(
561
+ ([k, outputFile]: [string, string]) => {
562
+ this.launchWeb(k, outputFile);
563
+ watch(outputFile, async (e, filename) => {
564
+ const hash = await fileHash(outputFile);
565
+ if (fileHashes[k] !== hash) {
566
+ fileHashes[k] = hash;
567
+ console.log(ansiC.green(ansiC.inverse(`< ${e} ${filename}`)));
568
+ this.launchWeb(k, outputFile);
569
+ }
570
+ });
571
+ }
572
+ );
573
+
574
+ this.metafileOutputs("node");
575
+ this.nodeMetafileWatcher = watch(
576
+ "docs/node/metafile.json",
577
+ async (e, filename) => {
578
+ console.log(ansiC.green(ansiC.inverse(`< ${e} ${filename} (node)`)));
579
+ this.metafileOutputs("node");
580
+ }
581
+ );
582
+
583
+ this.metafileOutputs("web");
584
+ this.webMetafileWatcher = watch(
585
+ "docs/web/metafile.json",
586
+ async (e, filename) => {
587
+ console.log(ansiC.green(ansiC.inverse(`< ${e} ${filename} (web)`)));
588
+ this.metafileOutputs("web");
589
+ }
590
+ );
425
591
  }
426
592
 
593
+ tscCheck = async ({
594
+ entrypoint,
595
+ addableFiles,
596
+ platform,
597
+ }: {
598
+ platform: "web" | "node";
599
+ entrypoint: string;
600
+ addableFiles: string[];
601
+ }) => {
602
+ console.log(ansiC.green(ansiC.inverse(`tsc < ${entrypoint}`)));
603
+ this.bigBoard[entrypoint].typeErrors = "?";
604
+
605
+ const program = tsc.createProgramFromConfig({
606
+ basePath: process.cwd(), // always required, used for relative paths
607
+ configFilePath: "tsconfig.json", // config to inherit from (optional)
608
+ compilerOptions: {
609
+ rootDir: "src",
610
+ outDir: tscPather(entrypoint, platform),
611
+ // declaration: true,
612
+ // skipLibCheck: true,
613
+ noEmit: true,
614
+ },
615
+ include: addableFiles, //["src/**/*"],
616
+ // exclude: ["**/*.test.ts", "**/*.spec.ts"],
617
+ });
618
+ const tscPath = tscPather(entrypoint, platform);
619
+
620
+ let allDiagnostics = program.getSemanticDiagnostics();
621
+
622
+ const d: string[] = [];
623
+ allDiagnostics.forEach((diagnostic) => {
624
+ if (diagnostic.file) {
625
+ let { line, character } = ts.getLineAndCharacterOfPosition(
626
+ diagnostic.file,
627
+ diagnostic.start!
628
+ );
629
+ let message = ts.flattenDiagnosticMessageText(
630
+ diagnostic.messageText,
631
+ "\n"
632
+ );
633
+ d.push(
634
+ `${diagnostic.file.fileName} (${line + 1},${
635
+ character + 1
636
+ }): ${message}`
637
+ );
638
+ } else {
639
+ d.push(ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
640
+ }
641
+ });
642
+
643
+ fs.writeFileSync(tscPath, d.join("\n"));
644
+ this.bigBoard[entrypoint].typeErrors = d.length;
645
+ if (this.shutdownMode) {
646
+ this.checkForShutdown();
647
+ }
648
+ // fs.writeFileSync(
649
+ // tscExitCodePather(entrypoint, platform),
650
+ // d.length.toString()
651
+ // );
652
+ };
653
+
654
+ eslintCheck = async (
655
+ entrypoint: string,
656
+ platform: "web" | "node",
657
+ addableFiles: string[]
658
+ ) => {
659
+ this.bigBoard[entrypoint].staticErrors = "?";
660
+ console.log(ansiC.green(ansiC.inverse(`eslint < ${entrypoint}`)));
661
+ const results = (await eslint.lintFiles(addableFiles))
662
+ .filter((r) => r.messages.length)
663
+ .filter((r) => {
664
+ return r.messages[0].ruleId !== null;
665
+ })
666
+ .map((r) => {
667
+ delete r.source;
668
+ return r;
669
+ });
670
+
671
+ fs.writeFileSync(
672
+ lintPather(entrypoint, platform),
673
+ await formatter.format(results)
674
+ );
675
+ this.bigBoard[entrypoint].staticErrors = results.length;
676
+ if (this.shutdownMode) {
677
+ this.checkForShutdown();
678
+ }
679
+ // fs.writeFileSync(
680
+ // lintExitCodePather(entrypoint, platform),
681
+ // results.length.toString()
682
+ // );
683
+ };
684
+
685
+ makePrompt = async (
686
+ entryPoint: string,
687
+ addableFiles: string[],
688
+ platform: "web" | "node"
689
+ ) => {
690
+ this.bigBoard[entryPoint].prompt = "?";
691
+ const promptPath = path.join(
692
+ "./docs/",
693
+ platform,
694
+ entryPoint.split(".").slice(0, -1).join("."),
695
+ `prompt.txt`
696
+ );
697
+
698
+ const testPaths = path.join(
699
+ "./docs/",
700
+ platform,
701
+ entryPoint.split(".").slice(0, -1).join("."),
702
+ `tests.json`
703
+ );
704
+
705
+ const featuresPath = path.join(
706
+ "./docs/",
707
+ platform,
708
+ entryPoint.split(".").slice(0, -1).join("."),
709
+ `featurePrompt.txt`
710
+ );
711
+
712
+ fs.writeFileSync(
713
+ promptPath,
714
+ `
715
+ ${addableFiles
716
+ .map((x) => {
717
+ return `/add ${x}`;
718
+ })
719
+ .join("\n")}
720
+
721
+ /read ${lintPather(entryPoint, platform)}
722
+ /read ${tscPather(entryPoint, platform)}
723
+ /read ${testPaths}
724
+
725
+ /load ${featuresPath}
726
+
727
+ /code Fix the failing tests described in ${testPaths}. Correct any type signature errors described in the files ${tscPather(
728
+ entryPoint,
729
+ platform
730
+ )}. Implement any method which throws "Function not implemented. Resolve the lint errors described in ${lintPather(
731
+ entryPoint,
732
+ platform
733
+ )}"
734
+ `
735
+ );
736
+ this.bigBoard[
737
+ entryPoint
738
+ ].prompt = `aider --model deepseek/deepseek-chat --load docs/${platform}/${entryPoint
739
+ .split(".")
740
+ .slice(0, -1)
741
+ .join(".")}/prompt.txt`;
742
+ if (this.shutdownMode) {
743
+ this.checkForShutdown();
744
+ }
745
+ };
746
+
427
747
  checkForShutdown = () => {
428
748
  const anyRunning: boolean =
429
- Object.values(this.bigBoard).filter((x) => x.status === "running")
430
- .length > 0;
749
+ Object.values(this.bigBoard).filter((x) => x.prompt === "?").length +
750
+ Object.values(this.bigBoard).filter((x) => x.runTimeError === "?")
751
+ .length +
752
+ Object.values(this.bigBoard).filter((x) => x.staticErrors === "?")
753
+ .length +
754
+ Object.values(this.bigBoard).filter((x) => x.typeErrors === "?")
755
+ .length >
756
+ 0;
431
757
  if (anyRunning) {
758
+ console.log(ansiC.inverse("Shutting down. Please wait"));
432
759
  } else {
433
760
  this.browser.disconnect().then(() => {
434
- const final = {
435
- timestamp: Date.now(),
436
- tests: this.configs.tests.reduce((mm, t) => {
437
- const bddErrors = fs
438
- .readFileSync(bddExitCodePather(t[0], t[1]))
439
- .toString();
440
- const lintErrors = fs
441
- .readFileSync(lintExitCodePather(t[0], t[1]))
442
- .toString();
443
- const typeErrors = fs
444
- .readFileSync(tscExitCodePather(t[0], t[1]))
445
- .toString();
446
- mm[t[0]] = {
447
- bddErrors,
448
- lintErrors,
449
- typeErrors,
450
- };
451
- return mm;
452
- }, {}),
453
- };
454
-
455
- const s = JSON.stringify(final, null, 2);
456
- fs.writeFileSync("docs/summary.json", s);
761
+ fs.writeFileSync(
762
+ "docs/summary.json",
763
+ JSON.stringify(this.bigBoard, null, 2)
764
+ );
457
765
  console.log(ansiC.inverse("Goodbye"));
458
766
  process.exit();
459
767
  });
@@ -591,16 +899,8 @@ export class PM_Main extends PM {
591
899
  testConfig: ITestTypes
592
900
  ): Promise<Page> => {
593
901
  const d = dest + ".mjs";
594
- // console.log(green, "launchWebSideCar", src, dest, d);
595
- console.log(ansiC.green(ansiC.inverse(`launchWebSideCar ${src}`)));
596
902
 
597
- const destFolder = dest.replace(".mjs", "");
598
- // const webArgz = JSON.stringify({
599
- // name: dest,
600
- // ports: [].toString(),
601
- // fs: destFolder,
602
- // browserWSEndpoint: this.browser.wsEndpoint(),
603
- // });
903
+ console.log(ansiC.green(ansiC.inverse(`launchWebSideCar ${src}`)));
604
904
 
605
905
  const fileStreams2: fs.WriteStream[] = [];
606
906
  const doneFileStream2: Promise<any>[] = [];
@@ -743,7 +1043,6 @@ export class PM_Main extends PM {
743
1043
  testConfig: ITestTypes
744
1044
  ) => {
745
1045
  const d = dest + ".mjs";
746
- // console.log(green, "launchNodeSideCar", src, dest, d);
747
1046
  console.log(ansiC.green(ansiC.inverse(`launchNodeSideCar ${src}`)));
748
1047
 
749
1048
  const destFolder = dest.replace(".mjs", "");
@@ -792,10 +1091,6 @@ export class PM_Main extends PM {
792
1091
 
793
1092
  const builtfile = dest + ".mjs";
794
1093
 
795
- // console.log(
796
- // "node builtfile",
797
- // (await import(`${builtfile}?cacheBust=${Date.now()}`)).default
798
- // );
799
1094
  this.server[builtfile] = await import(
800
1095
  `${builtfile}?cacheBust=${Date.now()}`
801
1096
  ).then((module) => {
@@ -817,7 +1112,6 @@ export class PM_Main extends PM {
817
1112
  });
818
1113
  });
819
1114
 
820
- // console.log("portsToUse", portsToUse);
821
1115
  for (let i = 0; i <= portsToUse.length; i++) {
822
1116
  if (portsToUse[i]) {
823
1117
  this.ports[portsToUse[i]] = "true"; //port is open again
@@ -1245,7 +1539,7 @@ export class PM_Main extends PM {
1245
1539
  );
1246
1540
  });
1247
1541
 
1248
- this.writeBigBoard();
1542
+ // this.writeBigBoard();
1249
1543
  };
1250
1544
 
1251
1545
  receiveExitCode = (srcTest: string, failures: number) => {
@@ -1255,7 +1549,7 @@ export class PM_Main extends PM {
1255
1549
 
1256
1550
  writeBigBoard = () => {
1257
1551
  fs.writeFileSync(
1258
- "./docs/bigBoard.json",
1552
+ "./docs/summary.json",
1259
1553
  JSON.stringify(this.bigBoard, null, 2)
1260
1554
  );
1261
1555
  };