@puzzmo/cli 1.0.29 → 1.0.30

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.
@@ -4,6 +4,7 @@ import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { uploadFiles } from "../util/api.js";
6
6
  import { getAPIURL, getToken } from "../util/config.js";
7
+ import { validatePuzzmoJson } from "../util/validatePuzzmoFile.js";
7
8
  /** Collects all files in a directory recursively */
8
9
  const collectFiles = (dir) => {
9
10
  const entries = fs.readdirSync(dir, { withFileTypes: true });
@@ -59,6 +60,29 @@ export const upload = async (gameSlug, dir) => {
59
60
  console.error(`Directory is empty: ${dir}`);
60
61
  process.exit(1);
61
62
  }
63
+ // Require and validate puzzmo.json
64
+ const puzzmoJsonPath = path.join(distDir, "puzzmo.json");
65
+ if (!fs.existsSync(puzzmoJsonPath)) {
66
+ console.error(`Missing puzzmo.json in ${dir}`);
67
+ console.error("Every game upload must include a puzzmo.json file.");
68
+ process.exit(1);
69
+ }
70
+ let parsed;
71
+ try {
72
+ parsed = JSON.parse(fs.readFileSync(puzzmoJsonPath, "utf-8"));
73
+ }
74
+ catch (e) {
75
+ console.error(`Invalid puzzmo.json: ${e instanceof Error ? e.message : e}`);
76
+ process.exit(1);
77
+ }
78
+ const validation = await validatePuzzmoJson(parsed);
79
+ if (!validation.valid) {
80
+ console.error(`Invalid puzzmo.json:\n`);
81
+ for (const err of validation.errors)
82
+ console.error(` ${err}`);
83
+ process.exit(1);
84
+ }
85
+ const puzzmoFile = validation.data;
62
86
  // Determine SHA
63
87
  const sha = getGitSHA() || hashFiles(files);
64
88
  console.log(`\nUploading ${gameSlug} (${sha.slice(0, 8)})`);
@@ -78,7 +102,7 @@ export const upload = async (gameSlug, dir) => {
78
102
  console.log(`Uploading to ${apiURL}...`);
79
103
  else
80
104
  console.log("Uploading...");
81
- const result = await uploadFiles(token, gameSlug, sha, files, distDir, (batch, totalBatches, uploaded) => {
105
+ const result = await uploadFiles(token, gameSlug, sha, files, distDir, puzzmoFile, (batch, totalBatches, uploaded) => {
82
106
  console.log(` Batch ${batch}/${totalBatches} done (${uploaded} file(s) uploaded)`);
83
107
  });
84
108
  console.log(`\nDone - ${result.versionID}`);
@@ -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;AAEvD,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,QAAgB,EAAE,GAAW,EAAE,EAAE,CAAC;IAC7D,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,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,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,CAAC;QACxG,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;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,QAAgB,EAAE,GAAW,EAAE,EAAE,CAAC;IAC7D,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;IAElC,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"}
@@ -0,0 +1,2 @@
1
+ /** CLI command: puzzmo validate [dir] */
2
+ export declare const validate: (dir: string) => Promise<void>;
@@ -0,0 +1,43 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { validatePuzzmoJson } from "../util/validatePuzzmoFile.js";
4
+ /** CLI command: puzzmo validate [dir] */
5
+ export const validate = async (dir) => {
6
+ const resolvedDir = path.resolve(dir);
7
+ const puzzmoJsonPath = path.join(resolvedDir, "puzzmo.json");
8
+ if (!fs.existsSync(puzzmoJsonPath)) {
9
+ console.error(`No puzzmo.json found in ${dir}`);
10
+ process.exit(1);
11
+ }
12
+ let raw;
13
+ try {
14
+ raw = fs.readFileSync(puzzmoJsonPath, "utf-8");
15
+ }
16
+ catch (e) {
17
+ console.error(`Could not read puzzmo.json: ${e instanceof Error ? e.message : e}`);
18
+ process.exit(1);
19
+ }
20
+ let data;
21
+ try {
22
+ data = JSON.parse(raw);
23
+ }
24
+ catch (e) {
25
+ console.error(`Invalid JSON in puzzmo.json: ${e instanceof Error ? e.message : e}`);
26
+ process.exit(1);
27
+ }
28
+ const result = await validatePuzzmoJson(data);
29
+ if (!result.valid) {
30
+ console.error(`puzzmo.json has ${result.errors.length} error(s):\n`);
31
+ for (const err of result.errors)
32
+ console.error(` ${err}`);
33
+ process.exit(1);
34
+ }
35
+ const { data: puzzmoFile } = result;
36
+ console.log(`Valid puzzmo.json for "${puzzmoFile.game.displayName}" (${puzzmoFile.game.slug})`);
37
+ if (puzzmoFile.integrations) {
38
+ const keys = Object.keys(puzzmoFile.integrations);
39
+ if (keys.length)
40
+ console.log(`Integrations: ${keys.join(", ")}`);
41
+ }
42
+ };
43
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAElE,yCAAyC;AACzC,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,EAAE,GAAW,EAAE,EAAE,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACrC,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAA;IAE5D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QACnC,OAAO,CAAC,KAAK,CAAC,2BAA2B,GAAG,EAAE,CAAC,CAAA;QAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAA;IAChD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QAClF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,IAAI,IAAa,CAAA;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACxB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QACnF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAA;IAC7C,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,mBAAmB,MAAM,CAAC,MAAM,CAAC,MAAM,cAAc,CAAC,CAAA;QACpE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM;YAAE,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAA;QAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,CAAA;IACnC,OAAO,CAAC,GAAG,CAAC,0BAA0B,UAAU,CAAC,IAAI,CAAC,WAAW,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;IAE/F,IAAI,UAAU,CAAC,YAAY,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAA;QACjD,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAClE,CAAC;AAAA,CACF,CAAA"}
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { login } from "./commands/login.js";
3
3
  import { upload } from "./commands/upload.js";
4
+ import { validate } from "./commands/validate.js";
4
5
  import { gameCreate } from "./commands/game/create.js";
5
6
  import { migrate } from "./commands/migrate.js";
6
7
  const [command, ...args] = process.argv.slice(2);
@@ -10,6 +11,7 @@ const printUsage = () => {
10
11
  puzzmo game create [token] Create a new Puzzmo game project
11
12
 
12
13
  puzzmo upload <slug> <dir> Upload game build from <dir>
14
+ puzzmo validate [dir] Validate puzzmo.json in a directory (default: .)
13
15
  puzzmo migrate List and select migration skills from dev.puzzmo.com`);
14
16
  };
15
17
  const run = async () => {
@@ -44,6 +46,10 @@ const run = async () => {
44
46
  }
45
47
  break;
46
48
  }
49
+ case "validate": {
50
+ validate(args[0] || ".");
51
+ break;
52
+ }
47
53
  case "migrate": {
48
54
  await migrate();
49
55
  break;
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,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;;;;;uFAKyE,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,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACxB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACnB,IAAI,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC;gBACtB,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAA;gBACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC;YACD,MAAM,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;YAC3B,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,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":";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,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACxB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACnB,IAAI,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC;gBACtB,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAA;gBACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC;YACD,MAAM,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;YAC3B,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"}
package/lib/util/api.d.ts CHANGED
@@ -1,3 +1,12 @@
1
+ /** The schema for a puzzmo.json file - mirrors PuzzmoFile from @puzzmo-com/shared/hostAPI */
2
+ export type PuzzmoFile = {
3
+ game: {
4
+ displayName: string;
5
+ slug: string;
6
+ teamID: string;
7
+ };
8
+ integrations?: Record<string, unknown>;
9
+ };
1
10
  type CompleteResponse = {
2
11
  assetsBase: string;
3
12
  versionID: string;
@@ -6,5 +15,5 @@ type CompleteResponse = {
6
15
  /** Callback for reporting batch upload progress */
7
16
  export type UploadProgress = (batch: number, totalBatches: number, uploaded: number) => void;
8
17
  /** Multi-step upload: init -> batched file uploads -> complete */
9
- export declare const uploadFiles: (token: string, gameSlug: string, sha: string, filePaths: string[], baseDir: string, onProgress?: UploadProgress | undefined) => Promise<CompleteResponse>;
18
+ export declare const uploadFiles: (token: string, gameSlug: string, sha: string, filePaths: string[], baseDir: string, puzzmoFile: PuzzmoFile, onProgress?: UploadProgress | undefined) => Promise<CompleteResponse>;
10
19
  export {};
package/lib/util/api.js CHANGED
@@ -32,10 +32,10 @@ const uploadFile = async (url, token, filePath, baseDir) => {
32
32
  return json;
33
33
  };
34
34
  /** Multi-step upload: init -> batched file uploads -> complete */
35
- export const uploadFiles = async (token, gameSlug, sha, filePaths, baseDir, onProgress) => {
35
+ export const uploadFiles = async (token, gameSlug, sha, filePaths, baseDir, puzzmoFile, onProgress) => {
36
36
  const apiURL = getAPIURL();
37
- // Step 1: Init session
38
- const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha }));
37
+ // Step 1: Init session (includes puzzmo.json metadata)
38
+ const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile }));
39
39
  // Step 2: Upload files in concurrent batches
40
40
  const fileURL = `${apiURL}/cliUpload/${init.sessionID}/file`;
41
41
  const totalBatches = Math.ceil(filePaths.length / BATCH_SIZE);
@@ -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":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAYvC,MAAM,UAAU,GAAG,EAAE,CAAA;AASrB,gCAAgC;AAChC,MAAM,QAAQ,GAAG,KAAK,EAAE,GAAW,EAAE,KAAa,EAAE,IAAY,EAAE,EAAE,CAAC;IACnE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QACjF,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAA;IACF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;IAC7B,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,GAAG,CAAC,MAAM,MAAO,IAAY,CAAC,KAAK,IAAI,eAAe,EAAE,CAAC,CAAA;IACvG,OAAO,IAAI,CAAA;AAAA,CACZ,CAAA;AAED,uEAAuE;AACvE,MAAM,UAAU,GAAG,KAAK,EAAE,GAAW,EAAE,KAAa,EAAE,QAAgB,EAAE,OAAe,EAAyB,EAAE,CAAC;IACjH,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IACrD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAA;IAEzC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,SAAS,kBAAkB,CAAC,YAAY,CAAC,EAAE,EAAE;QACzE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,KAAK,EAAE;YAChC,cAAc,EAAE,0BAA0B;SAC3C;QACD,IAAI,EAAE,OAAO;KACd,CAAC,CAAA;IACF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;IAC7B,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,GAAG,CAAC,MAAM,MAAO,IAAY,CAAC,KAAK,IAAI,eAAe,EAAE,CAAC,CAAA;IACvG,OAAO,IAAoB,CAAA;AAAA,CAC5B,CAAA;AAED,kEAAkE;AAClE,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAC9B,KAAa,EACb,QAAgB,EAChB,GAAW,EACX,SAAmB,EACnB,OAAe,EACf,UAAsB,EACtB,UAA2B,EACA,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAE1B,uDAAuD;IACvD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,GAAG,MAAM,YAAY,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAiB,CAAA;IAE1G,6CAA6C;IAC7C,MAAM,OAAO,GAAG,GAAG,MAAM,cAAc,IAAI,CAAC,SAAS,OAAO,CAAA;IAC5D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,UAAU,CAAC,CAAA;IAC7D,IAAI,aAAa,GAAG,CAAC,CAAA;IAErB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAA;QAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAA;QAC/C,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;QAC7E,aAAa,IAAI,KAAK,CAAC,MAAM,CAAA;QAC7B,UAAU,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,aAAa,CAAC,CAAA;IACrD,CAAC;IAED,mBAAmB;IACnB,OAAO,CAAC,MAAM,QAAQ,CAAC,GAAG,MAAM,cAAc,IAAI,CAAC,SAAS,WAAW,EAAE,KAAK,EAAE,EAAE,CAAC,CAAqB,CAAA;AAAA,CACzG,CAAA"}
@@ -0,0 +1,11 @@
1
+ import type { PuzzmoFile } from "./api.js";
2
+ type ValidationResult = {
3
+ valid: true;
4
+ data: PuzzmoFile;
5
+ } | {
6
+ valid: false;
7
+ errors: string[];
8
+ };
9
+ /** Validates a parsed object against the puzzmo.json JSON schema */
10
+ export declare const validatePuzzmoJson: (data: unknown) => Promise<ValidationResult>;
11
+ export {};
@@ -0,0 +1,28 @@
1
+ import { Validator } from "@cfworker/json-schema";
2
+ const schemaURL = "https://dev.puzzmo.com/schema/puzzmo-file-schema.json";
3
+ let cachedValidator;
4
+ /** Fetches and caches a JSON schema validator for puzzmo.json files */
5
+ const getValidator = async () => {
6
+ if (cachedValidator)
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();
12
+ cachedValidator = new Validator(puzzmoSchema, "7");
13
+ return cachedValidator;
14
+ };
15
+ /** Validates a parsed object against the puzzmo.json JSON schema */
16
+ export const validatePuzzmoJson = async (data) => {
17
+ const validator = await getValidator();
18
+ const result = validator.validate(data);
19
+ if (result.valid)
20
+ return { valid: true, data: data };
21
+ return {
22
+ valid: false,
23
+ errors: result.errors
24
+ .filter((e) => e.keyword !== "if" && !e.error.startsWith("A subschema had errors"))
25
+ .map((e) => `${e.instanceLocation}: ${e.error}`),
26
+ };
27
+ };
28
+ //# sourceMappingURL=validatePuzzmoFile.js.map
@@ -0,0 +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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puzzmo/cli",
3
- "version": "1.0.29",
3
+ "version": "1.0.30",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "puzzmo": "./lib/index.js"
@@ -21,6 +21,7 @@
21
21
  "type-check": "tsgo --noEmit"
22
22
  },
23
23
  "dependencies": {
24
+ "@cfworker/json-schema": "^4.1.1",
24
25
  "@clack/prompts": "^1.1.0",
25
26
  "@puzzmo/agent-cli-detect": "*"
26
27
  }
@@ -5,6 +5,7 @@ import path from "node:path"
5
5
 
6
6
  import { uploadFiles } from "../util/api.js"
7
7
  import { getAPIURL, getToken } from "../util/config.js"
8
+ import { validatePuzzmoJson } from "../util/validatePuzzmoFile.js"
8
9
 
9
10
  /** Collects all files in a directory recursively */
10
11
  const collectFiles = (dir: string): string[] => {
@@ -63,6 +64,30 @@ export const upload = async (gameSlug: string, dir: string) => {
63
64
  process.exit(1)
64
65
  }
65
66
 
67
+ // Require and validate puzzmo.json
68
+ const puzzmoJsonPath = path.join(distDir, "puzzmo.json")
69
+ if (!fs.existsSync(puzzmoJsonPath)) {
70
+ console.error(`Missing puzzmo.json in ${dir}`)
71
+ console.error("Every game upload must include a puzzmo.json file.")
72
+ process.exit(1)
73
+ }
74
+
75
+ let parsed: unknown
76
+ try {
77
+ parsed = JSON.parse(fs.readFileSync(puzzmoJsonPath, "utf-8"))
78
+ } catch (e) {
79
+ console.error(`Invalid puzzmo.json: ${e instanceof Error ? e.message : e}`)
80
+ process.exit(1)
81
+ }
82
+
83
+ const validation = await validatePuzzmoJson(parsed)
84
+ if (!validation.valid) {
85
+ console.error(`Invalid puzzmo.json:\n`)
86
+ for (const err of validation.errors) console.error(` ${err}`)
87
+ process.exit(1)
88
+ }
89
+ const puzzmoFile = validation.data
90
+
66
91
  // Determine SHA
67
92
  const sha = getGitSHA() || hashFiles(files)
68
93
 
@@ -85,7 +110,7 @@ export const upload = async (gameSlug: string, dir: string) => {
85
110
  if (apiURL !== defaultURL) console.log(`Uploading to ${apiURL}...`)
86
111
  else console.log("Uploading...")
87
112
 
88
- const result = await uploadFiles(token, gameSlug, sha, files, distDir, (batch, totalBatches, uploaded) => {
113
+ const result = await uploadFiles(token, gameSlug, sha, files, distDir, puzzmoFile, (batch, totalBatches, uploaded) => {
89
114
  console.log(` Batch ${batch}/${totalBatches} done (${uploaded} file(s) uploaded)`)
90
115
  })
91
116
 
@@ -0,0 +1,46 @@
1
+ import fs from "node:fs"
2
+ import path from "node:path"
3
+
4
+ import { validatePuzzmoJson } from "../util/validatePuzzmoFile.js"
5
+
6
+ /** CLI command: puzzmo validate [dir] */
7
+ export const validate = async (dir: string) => {
8
+ const resolvedDir = path.resolve(dir)
9
+ const puzzmoJsonPath = path.join(resolvedDir, "puzzmo.json")
10
+
11
+ if (!fs.existsSync(puzzmoJsonPath)) {
12
+ console.error(`No puzzmo.json found in ${dir}`)
13
+ process.exit(1)
14
+ }
15
+
16
+ let raw: string
17
+ try {
18
+ raw = fs.readFileSync(puzzmoJsonPath, "utf-8")
19
+ } catch (e) {
20
+ console.error(`Could not read puzzmo.json: ${e instanceof Error ? e.message : e}`)
21
+ process.exit(1)
22
+ }
23
+
24
+ let data: unknown
25
+ try {
26
+ data = JSON.parse(raw)
27
+ } catch (e) {
28
+ console.error(`Invalid JSON in puzzmo.json: ${e instanceof Error ? e.message : e}`)
29
+ process.exit(1)
30
+ }
31
+
32
+ const result = await validatePuzzmoJson(data)
33
+ if (!result.valid) {
34
+ console.error(`puzzmo.json has ${result.errors.length} error(s):\n`)
35
+ for (const err of result.errors) console.error(` ${err}`)
36
+ process.exit(1)
37
+ }
38
+
39
+ const { data: puzzmoFile } = result
40
+ console.log(`Valid puzzmo.json for "${puzzmoFile.game.displayName}" (${puzzmoFile.game.slug})`)
41
+
42
+ if (puzzmoFile.integrations) {
43
+ const keys = Object.keys(puzzmoFile.integrations)
44
+ if (keys.length) console.log(`Integrations: ${keys.join(", ")}`)
45
+ }
46
+ }
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { login } from "./commands/login.js"
4
4
  import { upload } from "./commands/upload.js"
5
+ import { validate } from "./commands/validate.js"
5
6
  import { gameCreate } from "./commands/game/create.js"
6
7
  import { migrate } from "./commands/migrate.js"
7
8
 
@@ -13,6 +14,7 @@ const printUsage = () => {
13
14
  puzzmo game create [token] Create a new Puzzmo game project
14
15
 
15
16
  puzzmo upload <slug> <dir> Upload game build from <dir>
17
+ puzzmo validate [dir] Validate puzzmo.json in a directory (default: .)
16
18
  puzzmo migrate List and select migration skills from dev.puzzmo.com`)
17
19
  }
18
20
 
@@ -47,6 +49,10 @@ const run = async () => {
47
49
  }
48
50
  break
49
51
  }
52
+ case "validate": {
53
+ validate(args[0] || ".")
54
+ break
55
+ }
50
56
  case "migrate": {
51
57
  await migrate()
52
58
  break
package/src/util/api.ts CHANGED
@@ -3,6 +3,16 @@ import path from "node:path"
3
3
 
4
4
  import { getAPIURL } from "./config.js"
5
5
 
6
+ /** The schema for a puzzmo.json file - mirrors PuzzmoFile from @puzzmo-com/shared/hostAPI */
7
+ export type PuzzmoFile = {
8
+ game: {
9
+ displayName: string
10
+ slug: string
11
+ teamID: string
12
+ }
13
+ integrations?: Record<string, unknown>
14
+ }
15
+
6
16
  const BATCH_SIZE = 10
7
17
 
8
18
  type InitResponse = { sessionID: string; basePath: string; error?: string }
@@ -49,12 +59,13 @@ export const uploadFiles = async (
49
59
  sha: string,
50
60
  filePaths: string[],
51
61
  baseDir: string,
62
+ puzzmoFile: PuzzmoFile,
52
63
  onProgress?: UploadProgress,
53
64
  ): Promise<CompleteResponse> => {
54
65
  const apiURL = getAPIURL()
55
66
 
56
- // Step 1: Init session
57
- const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha })) as InitResponse
67
+ // Step 1: Init session (includes puzzmo.json metadata)
68
+ const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile })) as InitResponse
58
69
 
59
70
  // Step 2: Upload files in concurrent batches
60
71
  const fileURL = `${apiURL}/cliUpload/${init.sessionID}/file`
@@ -0,0 +1,32 @@
1
+ import { Validator } from "@cfworker/json-schema"
2
+
3
+ import type { PuzzmoFile } from "./api.js"
4
+
5
+ const schemaURL = "https://dev.puzzmo.com/schema/puzzmo-file-schema.json"
6
+
7
+ let cachedValidator: Validator | undefined
8
+
9
+ /** Fetches and caches a JSON schema validator for puzzmo.json files */
10
+ const getValidator = async () => {
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")
16
+ return cachedValidator
17
+ }
18
+
19
+ type ValidationResult = { valid: true; data: PuzzmoFile } | { valid: false; errors: string[] }
20
+
21
+ /** Validates a parsed object against the puzzmo.json JSON schema */
22
+ export const validatePuzzmoJson = async (data: unknown): Promise<ValidationResult> => {
23
+ const validator = await getValidator()
24
+ const result = validator.validate(data)
25
+ if (result.valid) return { valid: true, data: data as PuzzmoFile }
26
+ return {
27
+ valid: false,
28
+ errors: result.errors
29
+ .filter((e) => e.keyword !== "if" && !e.error.startsWith("A subschema had errors"))
30
+ .map((e) => `${e.instanceLocation}: ${e.error}`),
31
+ }
32
+ }