@puzzmo/cli 1.0.31 → 1.0.33

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,2 +1,6 @@
1
+ type UploadOptions = {
2
+ verbose?: boolean;
3
+ };
1
4
  /** Uploads game build artifacts to Puzzmo */
2
- export declare const upload: (dir: string) => Promise<void>;
5
+ export declare const upload: (dir: string, options?: UploadOptions) => Promise<void>;
6
+ export {};
@@ -5,46 +5,9 @@ import path from "node:path";
5
5
  import { uploadFiles } from "../util/api.js";
6
6
  import { getAPIURL, getToken } from "../util/config.js";
7
7
  import { validatePuzzmoJson } from "../util/validatePuzzmoFile.js";
8
- /** Collects all files in a directory recursively */
9
- const collectFiles = (dir) => {
10
- const entries = fs.readdirSync(dir, { withFileTypes: true });
11
- const files = [];
12
- for (const entry of entries) {
13
- const full = path.join(dir, entry.name);
14
- if (entry.isDirectory())
15
- files.push(...collectFiles(full));
16
- else
17
- files.push(full);
18
- }
19
- return files;
20
- };
21
- /** Tries to get the shortest unique git SHA, returns null if not in a git repo */
22
- const getGitSHA = () => {
23
- try {
24
- return execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
25
- }
26
- catch {
27
- return null;
28
- }
29
- };
30
- /** Hashes all file contents to produce a deterministic SHA */
31
- const hashFiles = (filePaths) => {
32
- const hash = crypto.createHash("sha256");
33
- for (const fp of filePaths.sort()) {
34
- hash.update(fs.readFileSync(fp));
35
- }
36
- return hash.digest("hex").slice(0, 12);
37
- };
38
- /** Formats a byte count as a human-readable string */
39
- const formatBytes = (bytes) => {
40
- if (bytes < 1024)
41
- return `${bytes} B`;
42
- if (bytes < 1024 * 1024)
43
- return `${(bytes / 1024).toFixed(1)} KB`;
44
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
45
- };
46
8
  /** Uploads game build artifacts to Puzzmo */
47
- export const upload = async (dir) => {
9
+ export const upload = async (dir, options = {}) => {
10
+ const { verbose = false } = options;
48
11
  const token = getToken();
49
12
  if (!token) {
50
13
  console.error("Not logged in. Run `puzzmo login <token>` or set PUZZMO_TOKEN.");
@@ -105,8 +68,46 @@ export const upload = async (dir) => {
105
68
  console.log("Uploading...");
106
69
  const result = await uploadFiles(token, gameSlug, sha, files, distDir, puzzmoFile, (batch, totalBatches, uploaded) => {
107
70
  console.log(` Batch ${batch}/${totalBatches} done (${uploaded} file(s) uploaded)`);
108
- });
71
+ }, { verbose });
109
72
  console.log(`\nDone - ${result.versionID}`);
110
73
  console.log(`Assets: ${result.assetsBase}`);
111
74
  };
75
+ /** Collects all files in a directory recursively */
76
+ const collectFiles = (dir) => {
77
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
78
+ const files = [];
79
+ for (const entry of entries) {
80
+ const full = path.join(dir, entry.name);
81
+ if (entry.isDirectory())
82
+ files.push(...collectFiles(full));
83
+ else
84
+ files.push(full);
85
+ }
86
+ return files;
87
+ };
88
+ /** Tries to get the shortest unique git SHA, returns null if not in a git repo */
89
+ const getGitSHA = () => {
90
+ try {
91
+ return execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
92
+ }
93
+ catch {
94
+ return null;
95
+ }
96
+ };
97
+ /** Hashes all file contents to produce a deterministic SHA */
98
+ const hashFiles = (filePaths) => {
99
+ const hash = crypto.createHash("sha256");
100
+ for (const fp of filePaths.sort()) {
101
+ hash.update(fs.readFileSync(fp));
102
+ }
103
+ return hash.digest("hex").slice(0, 12);
104
+ };
105
+ /** Formats a byte count as a human-readable string */
106
+ const formatBytes = (bytes) => {
107
+ if (bytes < 1024)
108
+ return `${bytes} B`;
109
+ if (bytes < 1024 * 1024)
110
+ return `${(bytes / 1024).toFixed(1)} KB`;
111
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
112
+ };
112
113
  //# sourceMappingURL=upload.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/commands/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,MAAM,MAAM,aAAa,CAAA;AAChC,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAElE,oDAAoD;AACpD,MAAM,YAAY,GAAG,CAAC,GAAW,EAAY,EAAE,CAAC;IAC9C,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5D,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACvC,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAA;;YACrD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACvB,CAAC;IACD,OAAO,KAAK,CAAA;AAAA,CACb,CAAA;AAED,kFAAkF;AAClF,MAAM,SAAS,GAAG,GAAkB,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,4BAA4B,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AAAA,CACF,CAAA;AAED,8DAA8D;AAC9D,MAAM,SAAS,GAAG,CAAC,SAAmB,EAAU,EAAE,CAAC;IACjD,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;IACxC,KAAK,MAAM,EAAE,IAAI,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAA;IAClC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAAA,CACvC,CAAA;AAED,sDAAsD;AACtD,MAAM,WAAW,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC;IAC7C,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,IAAI,CAAA;IACrC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAA;IACjE,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAA;AAAA,CAClD,CAAA;AAED,6CAA6C;AAC7C,MAAM,CAAC,MAAM,MAAM,GAAG,KAAK,EAAE,GAAW,EAAE,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAA;IACxB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAA;QAC/E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAA;QAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IACnC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAA;QAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,mCAAmC;IACnC,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;IACxD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QACnC,OAAO,CAAC,KAAK,CAAC,0BAA0B,GAAG,EAAE,CAAC,CAAA;QAC9C,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAA;QACnE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,IAAI,MAAe,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAA;IAC/D,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACnD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAA;QACvC,KAAK,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM;YAAE,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAA;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IACD,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAA;IAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAA;IAErC,gBAAgB;IAChB,MAAM,GAAG,GAAG,SAAS,EAAE,IAAI,SAAS,CAAC,KAAK,CAAC,CAAA;IAE3C,OAAO,CAAC,GAAG,CAAC,eAAe,QAAQ,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;IAC3D,OAAO,CAAC,GAAG,CAAC,cAAc,OAAO,EAAE,CAAC,CAAA;IACpC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAEf,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAA;QACnC,UAAU,IAAI,IAAI,CAAA;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;QACxC,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,KAAK,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAChD,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,UAAU,GAAG,wBAAwB,CAAA;IAE3C,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,aAAa,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;IAC1E,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,KAAK,CAAC,CAAA;;QAC9D,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IAEhC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,CAAC;QACpH,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,IAAI,YAAY,UAAU,QAAQ,oBAAoB,CAAC,CAAA;IAAA,CACpF,CAAC,CAAA;IAEF,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,SAAS,EAAE,CAAC,CAAA;IAC3C,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;AAAA,CAC5C,CAAA"}
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/commands/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,MAAM,MAAM,aAAa,CAAA;AAChC,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAMlE,6CAA6C;AAC7C,MAAM,CAAC,MAAM,MAAM,GAAG,KAAK,EAAE,GAAW,EAAE,OAAO,GAAkB,EAAE,EAAE,EAAE,CAAC;IACxE,MAAM,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,OAAO,CAAA;IACnC,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAA;IACxB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAA;QAC/E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAA;QAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IACnC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAA;QAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,mCAAmC;IACnC,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;IACxD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QACnC,OAAO,CAAC,KAAK,CAAC,0BAA0B,GAAG,EAAE,CAAC,CAAA;QAC9C,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAA;QACnE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,IAAI,MAAe,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAA;IAC/D,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACnD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAA;QACvC,KAAK,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM;YAAE,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAA;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IACD,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAA;IAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAA;IAErC,gBAAgB;IAChB,MAAM,GAAG,GAAG,SAAS,EAAE,IAAI,SAAS,CAAC,KAAK,CAAC,CAAA;IAE3C,OAAO,CAAC,GAAG,CAAC,eAAe,QAAQ,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;IAC3D,OAAO,CAAC,GAAG,CAAC,cAAc,OAAO,EAAE,CAAC,CAAA;IACpC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAEf,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAA;QACnC,UAAU,IAAI,IAAI,CAAA;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;QACxC,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,KAAK,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAChD,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,UAAU,GAAG,wBAAwB,CAAA;IAE3C,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,aAAa,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;IAC1E,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,KAAK,CAAC,CAAA;;QAC9D,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IAEhC,MAAM,MAAM,GAAG,MAAM,WAAW,CAC9B,KAAK,EACL,QAAQ,EACR,GAAG,EACH,KAAK,EACL,OAAO,EACP,UAAU,EACV,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,IAAI,YAAY,UAAU,QAAQ,oBAAoB,CAAC,CAAA;IAAA,CACpF,EACD,EAAE,OAAO,EAAE,CACZ,CAAA;IAED,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,SAAS,EAAE,CAAC,CAAA;IAC3C,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;AAAA,CAC5C,CAAA;AAED,oDAAoD;AACpD,MAAM,YAAY,GAAG,CAAC,GAAW,EAAY,EAAE,CAAC;IAC9C,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5D,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACvC,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAA;;YACrD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACvB,CAAC;IACD,OAAO,KAAK,CAAA;AAAA,CACb,CAAA;AAED,kFAAkF;AAClF,MAAM,SAAS,GAAG,GAAkB,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,4BAA4B,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AAAA,CACF,CAAA;AAED,8DAA8D;AAC9D,MAAM,SAAS,GAAG,CAAC,SAAmB,EAAU,EAAE,CAAC;IACjD,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;IACxC,KAAK,MAAM,EAAE,IAAI,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAA;IAClC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAAA,CACvC,CAAA;AAED,sDAAsD;AACtD,MAAM,WAAW,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC;IAC7C,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,IAAI,CAAA;IACrC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAA;IACjE,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAA;AAAA,CAClD,CAAA"}
package/lib/index.js CHANGED
@@ -10,7 +10,7 @@ const printUsage = () => {
10
10
  puzzmo login <token> Save a CLI auth token
11
11
  puzzmo game create [token] Create a new Puzzmo game project
12
12
 
13
- puzzmo upload <dir> Upload game build from <dir> (slug from puzzmo.json)
13
+ puzzmo upload <dir> [-v] Upload game build from <dir> (slug from puzzmo.json). -v/--verbose prints request URLs and full error bodies.
14
14
  puzzmo validate [dir] Validate puzzmo.json in a directory (default: .)
15
15
  puzzmo migrate List and select migration skills from dev.puzzmo.com`);
16
16
  };
@@ -26,12 +26,13 @@ const run = async () => {
26
26
  break;
27
27
  }
28
28
  case "upload": {
29
- const dir = args[0];
29
+ const verbose = args.includes("--verbose") || args.includes("-v");
30
+ const dir = args.find((a) => !a.startsWith("-"));
30
31
  if (!dir) {
31
- console.error("Usage: puzzmo upload <dir>");
32
+ console.error("Usage: puzzmo upload <dir> [-v|--verbose]");
32
33
  process.exit(1);
33
34
  }
34
- await upload(dir);
35
+ await upload(dir, { verbose });
35
36
  break;
36
37
  }
37
38
  case "game": {
@@ -60,6 +61,10 @@ const run = async () => {
60
61
  };
61
62
  run().catch((err) => {
62
63
  console.error(err.message || err);
64
+ if (err && err.cause) {
65
+ const cause = err.cause;
66
+ console.error(`Caused by: ${cause instanceof Error ? cause.message : cause}`);
67
+ }
63
68
  process.exit(1);
64
69
  });
65
70
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAE/C,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAEhD,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC;IACvB,OAAO,CAAC,GAAG,CAAC;;;;;;uFAMyE,CAAC,CAAA;AAAA,CACvF,CAAA;AAED,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE,CAAC;IACtB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,OAAO,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACrB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAA;gBAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC;YACD,KAAK,CAAC,KAAK,CAAC,CAAA;YACZ,MAAK;QACP,CAAC;QACD,KAAK,QAAQ,EAAE,CAAC;YACd,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACnB,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAA;gBAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC;YACD,MAAM,MAAM,CAAC,GAAG,CAAC,CAAA;YACjB,MAAK;QACP,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACZ,MAAM,CAAC,UAAU,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,CAAA;YACrC,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;gBAC5B,MAAM,UAAU,CAAC,OAAO,CAAC,CAAA;YAC3B,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,0GAA0G,CAAC,CAAA;gBACzH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC;YACD,MAAK;QACP,CAAC;QACD,KAAK,UAAU,EAAE,CAAC;YAChB,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAA;YACxB,MAAK;QACP,CAAC;QACD,KAAK,SAAS,EAAE,CAAC;YACf,MAAM,OAAO,EAAE,CAAA;YACf,MAAK;QACP,CAAC;QACD;YACE,UAAU,EAAE,CAAA;YACZ,MAAK;IACT,CAAC;AAAA,CACF,CAAA;AAED,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAA;IACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA,CAChB,CAAC,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/lib/util/api.d.ts CHANGED
@@ -14,6 +14,10 @@ type CompleteResponse = {
14
14
  };
15
15
  /** Callback for reporting batch upload progress */
16
16
  export type UploadProgress = (batch: number, totalBatches: number, uploaded: number) => void;
17
+ /** Options for uploadFiles */
18
+ export type UploadFilesOptions = {
19
+ verbose?: boolean;
20
+ };
17
21
  /** Multi-step upload: init -> batched file uploads -> complete */
18
- export declare const uploadFiles: (token: string, gameSlug: string, sha: string, filePaths: string[], baseDir: string, puzzmoFile: PuzzmoFile, onProgress?: UploadProgress | undefined) => Promise<CompleteResponse>;
22
+ export declare const uploadFiles: (token: string, gameSlug: string, sha: string, filePaths: string[], baseDir: string, puzzmoFile: PuzzmoFile, onProgress?: UploadProgress | undefined, options?: UploadFilesOptions) => Promise<CompleteResponse>;
19
23
  export {};
package/lib/util/api.js CHANGED
@@ -2,40 +2,80 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { getAPIURL } from "./config.js";
4
4
  const BATCH_SIZE = 10;
5
+ /** Wraps fetch to surface the underlying network cause (DNS, ECONNREFUSED, TLS, etc.) */
6
+ const fetchWithContext = async (url, init, step, verbose) => {
7
+ if (verbose)
8
+ console.log(` → ${init.method ?? "GET"} ${url} [${step}]`);
9
+ try {
10
+ const res = await fetch(url, init);
11
+ if (verbose)
12
+ console.log(` ← ${res.status} ${res.statusText} [${step}]`);
13
+ return res;
14
+ }
15
+ catch (e) {
16
+ const cause = e.cause;
17
+ const causeMsg = cause instanceof Error ? cause.message : cause ? String(cause) : e.message;
18
+ if (verbose && e instanceof Error && e.stack)
19
+ console.error(e.stack);
20
+ throw new Error(`Network error during ${step} (${url}): ${causeMsg}`);
21
+ }
22
+ };
23
+ /** Reads a response body, parsing JSON when possible and including status + body in errors */
24
+ const readResponse = async (res, url, step, verbose) => {
25
+ const text = await res.text();
26
+ let json;
27
+ try {
28
+ json = text ? JSON.parse(text) : {};
29
+ }
30
+ catch {
31
+ if (!res.ok) {
32
+ const snippet = verbose ? text : text.slice(0, 200).replace(/\s+/g, " ").trim();
33
+ throw new Error(`Server error during ${step} (${res.status} ${res.statusText} from ${url}): ${snippet || "no body"}`);
34
+ }
35
+ throw new Error(`Invalid JSON response during ${step} (${url}): ${verbose ? text : text.slice(0, 200)}`);
36
+ }
37
+ if (!res.ok) {
38
+ const serverMsg = json.error || res.statusText || "Unknown error";
39
+ const detail = verbose ? `\n${JSON.stringify(json, null, 2)}` : "";
40
+ throw new Error(`Server error during ${step} (${res.status} from ${url}): ${serverMsg}${detail}`);
41
+ }
42
+ return json;
43
+ };
5
44
  /** Sends a JSON POST request */
6
- const jsonPost = async (url, token, body) => {
7
- const res = await fetch(url, {
45
+ const jsonPost = async (url, token, body, step, verbose) => {
46
+ const res = await fetchWithContext(url, {
8
47
  method: "POST",
9
48
  headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
10
49
  body: JSON.stringify(body),
11
- });
12
- const json = await res.json();
13
- if (!res.ok)
14
- throw new Error(`Server error (${res.status}): ${json.error || "Unknown error"}`);
15
- return json;
50
+ }, step, verbose);
51
+ return readResponse(res, url, step, verbose);
16
52
  };
17
53
  /** Uploads a single file as raw binary with filename in query param */
18
- const uploadFile = async (url, token, filePath, baseDir) => {
54
+ const uploadFile = async (url, token, filePath, baseDir, verbose) => {
19
55
  const relativePath = path.relative(baseDir, filePath);
20
56
  const content = fs.readFileSync(filePath);
21
- const res = await fetch(`${url}?name=${encodeURIComponent(relativePath)}`, {
57
+ const step = `file upload (${relativePath})`;
58
+ const fullURL = `${url}?name=${encodeURIComponent(relativePath)}`;
59
+ const res = await fetchWithContext(fullURL, {
22
60
  method: "POST",
23
61
  headers: {
24
62
  Authorization: `Bearer ${token}`,
25
63
  "Content-Type": "application/octet-stream",
26
64
  },
27
65
  body: content,
28
- });
29
- const json = await res.json();
30
- if (!res.ok)
31
- throw new Error(`Server error (${res.status}): ${json.error || "Unknown error"}`);
32
- return json;
66
+ }, step, verbose);
67
+ return (await readResponse(res, fullURL, step, verbose));
33
68
  };
34
69
  /** Multi-step upload: init -> batched file uploads -> complete */
35
- export const uploadFiles = async (token, gameSlug, sha, filePaths, baseDir, puzzmoFile, onProgress) => {
70
+ export const uploadFiles = async (token, gameSlug, sha, filePaths, baseDir, puzzmoFile, onProgress, options = {}) => {
71
+ const { verbose = false } = options;
36
72
  const apiURL = getAPIURL();
73
+ if (verbose)
74
+ console.log(` API URL: ${apiURL}`);
37
75
  // Step 1: Init session (includes puzzmo.json metadata)
38
- const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile }));
76
+ const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile }, "upload init", verbose));
77
+ if (verbose)
78
+ console.log(` session: ${init.sessionID}`);
39
79
  // Step 2: Upload files in concurrent batches
40
80
  const fileURL = `${apiURL}/cliUpload/${init.sessionID}/file`;
41
81
  const totalBatches = Math.ceil(filePaths.length / BATCH_SIZE);
@@ -43,11 +83,11 @@ export const uploadFiles = async (token, gameSlug, sha, filePaths, baseDir, puzz
43
83
  for (let i = 0; i < filePaths.length; i += BATCH_SIZE) {
44
84
  const batch = filePaths.slice(i, i + BATCH_SIZE);
45
85
  const batchNum = Math.floor(i / BATCH_SIZE) + 1;
46
- await Promise.all(batch.map((fp) => uploadFile(fileURL, token, fp, baseDir)));
86
+ await Promise.all(batch.map((fp) => uploadFile(fileURL, token, fp, baseDir, verbose)));
47
87
  totalUploaded += batch.length;
48
88
  onProgress?.(batchNum, totalBatches, totalUploaded);
49
89
  }
50
90
  // Step 3: Complete
51
- return (await jsonPost(`${apiURL}/cliUpload/${init.sessionID}/complete`, token, {}));
91
+ return (await jsonPost(`${apiURL}/cliUpload/${init.sessionID}/complete`, token, {}, "upload complete", verbose));
52
92
  };
53
93
  //# sourceMappingURL=api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/util/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/util/api.ts"],"names":[],"mappings":"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"}
@@ -1,21 +1,37 @@
1
1
  import { Validator } from "@cfworker/json-schema";
2
- const schemaURL = "https://dev.puzzmo.com/schema/puzzmo-file-schema.json";
2
+ const schemaURL = "https://dev-dj9e.onrender.com/schema/puzzmo-file-schema.json"; //"https://dev.puzzmo.com/schema/puzzmo-file-schema.json"
3
3
  let cachedValidator;
4
4
  /** Fetches and caches a JSON schema validator for puzzmo.json files */
5
5
  const getValidator = async () => {
6
6
  if (cachedValidator)
7
7
  return cachedValidator;
8
- const res = await fetch(schemaURL);
9
- if (!res.ok)
10
- throw new Error(`Failed to fetch puzzmo file schema: ${res.status}`);
11
- const puzzmoSchema = await res.json();
8
+ let res;
9
+ try {
10
+ res = await fetch(schemaURL);
11
+ }
12
+ catch (e) {
13
+ const cause = e.cause;
14
+ const causeMsg = cause instanceof Error ? cause.message : cause ? String(cause) : e.message;
15
+ throw new Error(`Network error fetching puzzmo file schema (${schemaURL}): ${causeMsg}`);
16
+ }
17
+ if (!res.ok) {
18
+ throw new Error(`Failed to fetch puzzmo file schema: ${res.status} ${res.statusText} from ${schemaURL}`);
19
+ }
20
+ let puzzmoSchema;
21
+ try {
22
+ puzzmoSchema = await res.json();
23
+ }
24
+ catch (e) {
25
+ throw new Error(`Invalid JSON in puzzmo file schema (${schemaURL}): ${e instanceof Error ? e.message : e}`);
26
+ }
12
27
  cachedValidator = new Validator(puzzmoSchema, "7");
13
28
  return cachedValidator;
14
29
  };
15
30
  /** Validates a parsed object against the puzzmo.json JSON schema */
16
31
  export const validatePuzzmoJson = async (data) => {
17
32
  const validator = await getValidator();
18
- const result = validator.validate(data);
33
+ const toValidate = stripSchemaProperty(data);
34
+ const result = validator.validate(toValidate);
19
35
  if (result.valid)
20
36
  return { valid: true, data: data };
21
37
  return {
@@ -25,4 +41,13 @@ export const validatePuzzmoJson = async (data) => {
25
41
  .map((e) => `${e.instanceLocation}: ${e.error}`),
26
42
  };
27
43
  };
44
+ /** Removes the `$schema` editor-hint property so it doesn't trip additionalProperties checks */
45
+ const stripSchemaProperty = (data) => {
46
+ if (!data || typeof data !== "object" || Array.isArray(data))
47
+ return data;
48
+ if (!("$schema" in data))
49
+ return data;
50
+ const { $schema: _, ...rest } = data;
51
+ return rest;
52
+ };
28
53
  //# sourceMappingURL=validatePuzzmoFile.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validatePuzzmoFile.js","sourceRoot":"","sources":["../../src/util/validatePuzzmoFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAIjD,MAAM,SAAS,GAAG,uDAAuD,CAAA;AAEzE,IAAI,eAAsC,CAAA;AAE1C,uEAAuE;AACvE,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE,CAAC;IAC/B,IAAI,eAAe;QAAE,OAAO,eAAe,CAAA;IAC3C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,CAAA;IAClC,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;IACjF,MAAM,YAAY,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;IACrC,eAAe,GAAG,IAAI,SAAS,CAAC,YAAY,EAAE,GAAG,CAAC,CAAA;IAClD,OAAO,eAAe,CAAA;AAAA,CACvB,CAAA;AAID,oEAAoE;AACpE,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EAAE,IAAa,EAA6B,EAAE,CAAC;IACpF,MAAM,SAAS,GAAG,MAAM,YAAY,EAAE,CAAA;IACtC,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IACvC,IAAI,MAAM,CAAC,KAAK;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAkB,EAAE,CAAA;IAClE,OAAO;QACL,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,MAAM,CAAC,MAAM;aAClB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,wBAAwB,CAAC,CAAC;aAClF,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,gBAAgB,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;KACnD,CAAA;AAAA,CACF,CAAA"}
1
+ {"version":3,"file":"validatePuzzmoFile.js","sourceRoot":"","sources":["../../src/util/validatePuzzmoFile.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puzzmo/cli",
3
- "version": "1.0.31",
3
+ "version": "1.0.33",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "puzzmo": "./lib/index.js"
@@ -7,45 +7,13 @@ import { uploadFiles } from "../util/api.js"
7
7
  import { getAPIURL, getToken } from "../util/config.js"
8
8
  import { validatePuzzmoJson } from "../util/validatePuzzmoFile.js"
9
9
 
10
- /** Collects all files in a directory recursively */
11
- const collectFiles = (dir: string): string[] => {
12
- const entries = fs.readdirSync(dir, { withFileTypes: true })
13
- const files: string[] = []
14
- for (const entry of entries) {
15
- const full = path.join(dir, entry.name)
16
- if (entry.isDirectory()) files.push(...collectFiles(full))
17
- else files.push(full)
18
- }
19
- return files
20
- }
21
-
22
- /** Tries to get the shortest unique git SHA, returns null if not in a git repo */
23
- const getGitSHA = (): string | null => {
24
- try {
25
- return execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim()
26
- } catch {
27
- return null
28
- }
29
- }
30
-
31
- /** Hashes all file contents to produce a deterministic SHA */
32
- const hashFiles = (filePaths: string[]): string => {
33
- const hash = crypto.createHash("sha256")
34
- for (const fp of filePaths.sort()) {
35
- hash.update(fs.readFileSync(fp))
36
- }
37
- return hash.digest("hex").slice(0, 12)
38
- }
39
-
40
- /** Formats a byte count as a human-readable string */
41
- const formatBytes = (bytes: number): string => {
42
- if (bytes < 1024) return `${bytes} B`
43
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
44
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
10
+ type UploadOptions = {
11
+ verbose?: boolean
45
12
  }
46
13
 
47
14
  /** Uploads game build artifacts to Puzzmo */
48
- export const upload = async (dir: string) => {
15
+ export const upload = async (dir: string, options: UploadOptions = {}) => {
16
+ const { verbose = false } = options
49
17
  const token = getToken()
50
18
  if (!token) {
51
19
  console.error("Not logged in. Run `puzzmo login <token>` or set PUZZMO_TOKEN.")
@@ -111,10 +79,56 @@ export const upload = async (dir: string) => {
111
79
  if (apiURL !== defaultURL) console.log(`Uploading to ${apiURL}...`)
112
80
  else console.log("Uploading...")
113
81
 
114
- const result = await uploadFiles(token, gameSlug, sha, files, distDir, puzzmoFile, (batch, totalBatches, uploaded) => {
115
- console.log(` Batch ${batch}/${totalBatches} done (${uploaded} file(s) uploaded)`)
116
- })
82
+ const result = await uploadFiles(
83
+ token,
84
+ gameSlug,
85
+ sha,
86
+ files,
87
+ distDir,
88
+ puzzmoFile,
89
+ (batch, totalBatches, uploaded) => {
90
+ console.log(` Batch ${batch}/${totalBatches} done (${uploaded} file(s) uploaded)`)
91
+ },
92
+ { verbose },
93
+ )
117
94
 
118
95
  console.log(`\nDone - ${result.versionID}`)
119
96
  console.log(`Assets: ${result.assetsBase}`)
120
97
  }
98
+
99
+ /** Collects all files in a directory recursively */
100
+ const collectFiles = (dir: string): string[] => {
101
+ const entries = fs.readdirSync(dir, { withFileTypes: true })
102
+ const files: string[] = []
103
+ for (const entry of entries) {
104
+ const full = path.join(dir, entry.name)
105
+ if (entry.isDirectory()) files.push(...collectFiles(full))
106
+ else files.push(full)
107
+ }
108
+ return files
109
+ }
110
+
111
+ /** Tries to get the shortest unique git SHA, returns null if not in a git repo */
112
+ const getGitSHA = (): string | null => {
113
+ try {
114
+ return execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim()
115
+ } catch {
116
+ return null
117
+ }
118
+ }
119
+
120
+ /** Hashes all file contents to produce a deterministic SHA */
121
+ const hashFiles = (filePaths: string[]): string => {
122
+ const hash = crypto.createHash("sha256")
123
+ for (const fp of filePaths.sort()) {
124
+ hash.update(fs.readFileSync(fp))
125
+ }
126
+ return hash.digest("hex").slice(0, 12)
127
+ }
128
+
129
+ /** Formats a byte count as a human-readable string */
130
+ const formatBytes = (bytes: number): string => {
131
+ if (bytes < 1024) return `${bytes} B`
132
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
133
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
134
+ }
package/src/index.ts CHANGED
@@ -13,7 +13,7 @@ const printUsage = () => {
13
13
  puzzmo login <token> Save a CLI auth token
14
14
  puzzmo game create [token] Create a new Puzzmo game project
15
15
 
16
- puzzmo upload <dir> Upload game build from <dir> (slug from puzzmo.json)
16
+ puzzmo upload <dir> [-v] Upload game build from <dir> (slug from puzzmo.json). -v/--verbose prints request URLs and full error bodies.
17
17
  puzzmo validate [dir] Validate puzzmo.json in a directory (default: .)
18
18
  puzzmo migrate List and select migration skills from dev.puzzmo.com`)
19
19
  }
@@ -30,12 +30,13 @@ const run = async () => {
30
30
  break
31
31
  }
32
32
  case "upload": {
33
- const dir = args[0]
33
+ const verbose = args.includes("--verbose") || args.includes("-v")
34
+ const dir = args.find((a) => !a.startsWith("-"))
34
35
  if (!dir) {
35
- console.error("Usage: puzzmo upload <dir>")
36
+ console.error("Usage: puzzmo upload <dir> [-v|--verbose]")
36
37
  process.exit(1)
37
38
  }
38
- await upload(dir)
39
+ await upload(dir, { verbose })
39
40
  break
40
41
  }
41
42
  case "game": {
@@ -64,5 +65,9 @@ const run = async () => {
64
65
 
65
66
  run().catch((err) => {
66
67
  console.error(err.message || err)
68
+ if (err && err.cause) {
69
+ const cause = err.cause
70
+ console.error(`Caused by: ${cause instanceof Error ? cause.message : cause}`)
71
+ }
67
72
  process.exit(1)
68
73
  })
package/src/util/api.ts CHANGED
@@ -22,34 +22,83 @@ type CompleteResponse = { assetsBase: string; versionID: string; error?: string
22
22
  /** Callback for reporting batch upload progress */
23
23
  export type UploadProgress = (batch: number, totalBatches: number, uploaded: number) => void
24
24
 
25
- /** Sends a JSON POST request */
26
- const jsonPost = async (url: string, token: string, body: object) => {
27
- const res = await fetch(url, {
28
- method: "POST",
29
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
30
- body: JSON.stringify(body),
31
- })
32
- const json = await res.json()
33
- if (!res.ok) throw new Error(`Server error (${res.status}): ${(json as any).error || "Unknown error"}`)
25
+ /** Options for uploadFiles */
26
+ export type UploadFilesOptions = {
27
+ verbose?: boolean
28
+ }
29
+
30
+ /** Wraps fetch to surface the underlying network cause (DNS, ECONNREFUSED, TLS, etc.) */
31
+ const fetchWithContext = async (url: string, init: RequestInit, step: string, verbose: boolean): Promise<Response> => {
32
+ if (verbose) console.log(` → ${init.method ?? "GET"} ${url} [${step}]`)
33
+ try {
34
+ const res = await fetch(url, init)
35
+ if (verbose) console.log(` ← ${res.status} ${res.statusText} [${step}]`)
36
+ return res
37
+ } catch (e) {
38
+ const cause = (e as { cause?: unknown }).cause
39
+ const causeMsg = cause instanceof Error ? cause.message : cause ? String(cause) : (e as Error).message
40
+ if (verbose && e instanceof Error && e.stack) console.error(e.stack)
41
+ throw new Error(`Network error during ${step} (${url}): ${causeMsg}`)
42
+ }
43
+ }
44
+
45
+ /** Reads a response body, parsing JSON when possible and including status + body in errors */
46
+ const readResponse = async (res: Response, url: string, step: string, verbose: boolean) => {
47
+ const text = await res.text()
48
+ let json: unknown
49
+ try {
50
+ json = text ? JSON.parse(text) : {}
51
+ } catch {
52
+ if (!res.ok) {
53
+ const snippet = verbose ? text : text.slice(0, 200).replace(/\s+/g, " ").trim()
54
+ throw new Error(`Server error during ${step} (${res.status} ${res.statusText} from ${url}): ${snippet || "no body"}`)
55
+ }
56
+ throw new Error(`Invalid JSON response during ${step} (${url}): ${verbose ? text : text.slice(0, 200)}`)
57
+ }
58
+ if (!res.ok) {
59
+ const serverMsg = (json as { error?: string }).error || res.statusText || "Unknown error"
60
+ const detail = verbose ? `\n${JSON.stringify(json, null, 2)}` : ""
61
+ throw new Error(`Server error during ${step} (${res.status} from ${url}): ${serverMsg}${detail}`)
62
+ }
34
63
  return json
35
64
  }
36
65
 
66
+ /** Sends a JSON POST request */
67
+ const jsonPost = async (url: string, token: string, body: object, step: string, verbose: boolean) => {
68
+ const res = await fetchWithContext(
69
+ url,
70
+ {
71
+ method: "POST",
72
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
73
+ body: JSON.stringify(body),
74
+ },
75
+ step,
76
+ verbose,
77
+ )
78
+ return readResponse(res, url, step, verbose)
79
+ }
80
+
37
81
  /** Uploads a single file as raw binary with filename in query param */
38
- const uploadFile = async (url: string, token: string, filePath: string, baseDir: string): Promise<FileResponse> => {
82
+ const uploadFile = async (url: string, token: string, filePath: string, baseDir: string, verbose: boolean): Promise<FileResponse> => {
39
83
  const relativePath = path.relative(baseDir, filePath)
40
84
  const content = fs.readFileSync(filePath)
85
+ const step = `file upload (${relativePath})`
41
86
 
42
- const res = await fetch(`${url}?name=${encodeURIComponent(relativePath)}`, {
43
- method: "POST",
44
- headers: {
45
- Authorization: `Bearer ${token}`,
46
- "Content-Type": "application/octet-stream",
87
+ const fullURL = `${url}?name=${encodeURIComponent(relativePath)}`
88
+ const res = await fetchWithContext(
89
+ fullURL,
90
+ {
91
+ method: "POST",
92
+ headers: {
93
+ Authorization: `Bearer ${token}`,
94
+ "Content-Type": "application/octet-stream",
95
+ },
96
+ body: content,
47
97
  },
48
- body: content,
49
- })
50
- const json = await res.json()
51
- if (!res.ok) throw new Error(`Server error (${res.status}): ${(json as any).error || "Unknown error"}`)
52
- return json as FileResponse
98
+ step,
99
+ verbose,
100
+ )
101
+ return (await readResponse(res, fullURL, step, verbose)) as FileResponse
53
102
  }
54
103
 
55
104
  /** Multi-step upload: init -> batched file uploads -> complete */
@@ -61,11 +110,15 @@ export const uploadFiles = async (
61
110
  baseDir: string,
62
111
  puzzmoFile: PuzzmoFile,
63
112
  onProgress?: UploadProgress,
113
+ options: UploadFilesOptions = {},
64
114
  ): Promise<CompleteResponse> => {
115
+ const { verbose = false } = options
65
116
  const apiURL = getAPIURL()
117
+ if (verbose) console.log(` API URL: ${apiURL}`)
66
118
 
67
119
  // Step 1: Init session (includes puzzmo.json metadata)
68
- const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile })) as InitResponse
120
+ const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile }, "upload init", verbose)) as InitResponse
121
+ if (verbose) console.log(` session: ${init.sessionID}`)
69
122
 
70
123
  // Step 2: Upload files in concurrent batches
71
124
  const fileURL = `${apiURL}/cliUpload/${init.sessionID}/file`
@@ -75,11 +128,11 @@ export const uploadFiles = async (
75
128
  for (let i = 0; i < filePaths.length; i += BATCH_SIZE) {
76
129
  const batch = filePaths.slice(i, i + BATCH_SIZE)
77
130
  const batchNum = Math.floor(i / BATCH_SIZE) + 1
78
- await Promise.all(batch.map((fp) => uploadFile(fileURL, token, fp, baseDir)))
131
+ await Promise.all(batch.map((fp) => uploadFile(fileURL, token, fp, baseDir, verbose)))
79
132
  totalUploaded += batch.length
80
133
  onProgress?.(batchNum, totalBatches, totalUploaded)
81
134
  }
82
135
 
83
136
  // Step 3: Complete
84
- return (await jsonPost(`${apiURL}/cliUpload/${init.sessionID}/complete`, token, {})) as CompleteResponse
137
+ return (await jsonPost(`${apiURL}/cliUpload/${init.sessionID}/complete`, token, {}, "upload complete", verbose)) as CompleteResponse
85
138
  }
@@ -1,18 +1,32 @@
1
- import { Validator } from "@cfworker/json-schema"
1
+ import { Validator, type Schema } from "@cfworker/json-schema"
2
2
 
3
3
  import type { PuzzmoFile } from "./api.js"
4
4
 
5
- const schemaURL = "https://dev.puzzmo.com/schema/puzzmo-file-schema.json"
5
+ const schemaURL = "https://dev-dj9e.onrender.com/schema/puzzmo-file-schema.json" //"https://dev.puzzmo.com/schema/puzzmo-file-schema.json"
6
6
 
7
7
  let cachedValidator: Validator | undefined
8
8
 
9
9
  /** Fetches and caches a JSON schema validator for puzzmo.json files */
10
10
  const getValidator = async () => {
11
11
  if (cachedValidator) return cachedValidator
12
- const res = await fetch(schemaURL)
13
- if (!res.ok) throw new Error(`Failed to fetch puzzmo file schema: ${res.status}`)
14
- const puzzmoSchema = await res.json()
15
- cachedValidator = new Validator(puzzmoSchema, "7")
12
+ let res: Response
13
+ try {
14
+ res = await fetch(schemaURL)
15
+ } catch (e) {
16
+ const cause = (e as { cause?: unknown }).cause
17
+ const causeMsg = cause instanceof Error ? cause.message : cause ? String(cause) : (e as Error).message
18
+ throw new Error(`Network error fetching puzzmo file schema (${schemaURL}): ${causeMsg}`)
19
+ }
20
+ if (!res.ok) {
21
+ throw new Error(`Failed to fetch puzzmo file schema: ${res.status} ${res.statusText} from ${schemaURL}`)
22
+ }
23
+ let puzzmoSchema: unknown
24
+ try {
25
+ puzzmoSchema = await res.json()
26
+ } catch (e) {
27
+ throw new Error(`Invalid JSON in puzzmo file schema (${schemaURL}): ${e instanceof Error ? e.message : e}`)
28
+ }
29
+ cachedValidator = new Validator(puzzmoSchema as Schema, "7")
16
30
  return cachedValidator
17
31
  }
18
32
 
@@ -21,7 +35,8 @@ type ValidationResult = { valid: true; data: PuzzmoFile } | { valid: false; erro
21
35
  /** Validates a parsed object against the puzzmo.json JSON schema */
22
36
  export const validatePuzzmoJson = async (data: unknown): Promise<ValidationResult> => {
23
37
  const validator = await getValidator()
24
- const result = validator.validate(data)
38
+ const toValidate = stripSchemaProperty(data)
39
+ const result = validator.validate(toValidate)
25
40
  if (result.valid) return { valid: true, data: data as PuzzmoFile }
26
41
  return {
27
42
  valid: false,
@@ -30,3 +45,11 @@ export const validatePuzzmoJson = async (data: unknown): Promise<ValidationResul
30
45
  .map((e) => `${e.instanceLocation}: ${e.error}`),
31
46
  }
32
47
  }
48
+
49
+ /** Removes the `$schema` editor-hint property so it doesn't trip additionalProperties checks */
50
+ const stripSchemaProperty = (data: unknown): unknown => {
51
+ if (!data || typeof data !== "object" || Array.isArray(data)) return data
52
+ if (!("$schema" in data)) return data
53
+ const { $schema: _, ...rest } = data as Record<string, unknown>
54
+ return rest
55
+ }