@puzzmo/cli 1.0.31 → 1.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -1
- package/lib/util/api.js +42 -15
- package/lib/util/api.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +4 -0
- package/src/util/api.ts +58 -21
package/lib/index.js
CHANGED
|
@@ -60,6 +60,10 @@ const run = async () => {
|
|
|
60
60
|
};
|
|
61
61
|
run().catch((err) => {
|
|
62
62
|
console.error(err.message || err);
|
|
63
|
+
if (err && err.cause) {
|
|
64
|
+
const cause = err.cause;
|
|
65
|
+
console.error(`Caused by: ${cause instanceof Error ? cause.message : cause}`);
|
|
66
|
+
}
|
|
63
67
|
process.exit(1);
|
|
64
68
|
});
|
|
65
69
|
//# 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":";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,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAA;QACvB,OAAO,CAAC,KAAK,CAAC,cAAc,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAA;IAC/E,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA,CAChB,CAAC,CAAA"}
|
package/lib/util/api.js
CHANGED
|
@@ -2,40 +2,67 @@ 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) => {
|
|
7
|
+
try {
|
|
8
|
+
return await fetch(url, init);
|
|
9
|
+
}
|
|
10
|
+
catch (e) {
|
|
11
|
+
const cause = e.cause;
|
|
12
|
+
const causeMsg = cause instanceof Error ? cause.message : cause ? String(cause) : e.message;
|
|
13
|
+
throw new Error(`Network error during ${step} (${url}): ${causeMsg}`);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/** Reads a response body, parsing JSON when possible and including status + body in errors */
|
|
17
|
+
const readResponse = async (res, url, step) => {
|
|
18
|
+
const text = await res.text();
|
|
19
|
+
let json;
|
|
20
|
+
try {
|
|
21
|
+
json = text ? JSON.parse(text) : {};
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
const snippet = text.slice(0, 200).replace(/\s+/g, " ").trim();
|
|
26
|
+
throw new Error(`Server error during ${step} (${res.status} ${res.statusText} from ${url}): ${snippet || "no body"}`);
|
|
27
|
+
}
|
|
28
|
+
throw new Error(`Invalid JSON response during ${step} (${url}): ${text.slice(0, 200)}`);
|
|
29
|
+
}
|
|
30
|
+
if (!res.ok) {
|
|
31
|
+
const serverMsg = json.error || res.statusText || "Unknown error";
|
|
32
|
+
throw new Error(`Server error during ${step} (${res.status} from ${url}): ${serverMsg}`);
|
|
33
|
+
}
|
|
34
|
+
return json;
|
|
35
|
+
};
|
|
5
36
|
/** Sends a JSON POST request */
|
|
6
|
-
const jsonPost = async (url, token, body) => {
|
|
7
|
-
const res = await
|
|
37
|
+
const jsonPost = async (url, token, body, step) => {
|
|
38
|
+
const res = await fetchWithContext(url, {
|
|
8
39
|
method: "POST",
|
|
9
40
|
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
10
41
|
body: JSON.stringify(body),
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
if (!res.ok)
|
|
14
|
-
throw new Error(`Server error (${res.status}): ${json.error || "Unknown error"}`);
|
|
15
|
-
return json;
|
|
42
|
+
}, step);
|
|
43
|
+
return readResponse(res, url, step);
|
|
16
44
|
};
|
|
17
45
|
/** Uploads a single file as raw binary with filename in query param */
|
|
18
46
|
const uploadFile = async (url, token, filePath, baseDir) => {
|
|
19
47
|
const relativePath = path.relative(baseDir, filePath);
|
|
20
48
|
const content = fs.readFileSync(filePath);
|
|
21
|
-
const
|
|
49
|
+
const step = `file upload (${relativePath})`;
|
|
50
|
+
const fullURL = `${url}?name=${encodeURIComponent(relativePath)}`;
|
|
51
|
+
const res = await fetchWithContext(fullURL, {
|
|
22
52
|
method: "POST",
|
|
23
53
|
headers: {
|
|
24
54
|
Authorization: `Bearer ${token}`,
|
|
25
55
|
"Content-Type": "application/octet-stream",
|
|
26
56
|
},
|
|
27
57
|
body: content,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
if (!res.ok)
|
|
31
|
-
throw new Error(`Server error (${res.status}): ${json.error || "Unknown error"}`);
|
|
32
|
-
return json;
|
|
58
|
+
}, step);
|
|
59
|
+
return (await readResponse(res, fullURL, step));
|
|
33
60
|
};
|
|
34
61
|
/** Multi-step upload: init -> batched file uploads -> complete */
|
|
35
62
|
export const uploadFiles = async (token, gameSlug, sha, filePaths, baseDir, puzzmoFile, onProgress) => {
|
|
36
63
|
const apiURL = getAPIURL();
|
|
37
64
|
// Step 1: Init session (includes puzzmo.json metadata)
|
|
38
|
-
const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile }));
|
|
65
|
+
const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile }, "upload init"));
|
|
39
66
|
// Step 2: Upload files in concurrent batches
|
|
40
67
|
const fileURL = `${apiURL}/cliUpload/${init.sessionID}/file`;
|
|
41
68
|
const totalBatches = Math.ceil(filePaths.length / BATCH_SIZE);
|
|
@@ -48,6 +75,6 @@ export const uploadFiles = async (token, gameSlug, sha, filePaths, baseDir, puzz
|
|
|
48
75
|
onProgress?.(batchNum, totalBatches, totalUploaded);
|
|
49
76
|
}
|
|
50
77
|
// Step 3: Complete
|
|
51
|
-
return (await jsonPost(`${apiURL}/cliUpload/${init.sessionID}/complete`, token, {}));
|
|
78
|
+
return (await jsonPost(`${apiURL}/cliUpload/${init.sessionID}/complete`, token, {}, "upload complete"));
|
|
52
79
|
};
|
|
53
80
|
//# sourceMappingURL=api.js.map
|
package/lib/util/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/util/api.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
package/src/util/api.ts
CHANGED
|
@@ -22,34 +22,71 @@ 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
|
-
/**
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
/** Wraps fetch to surface the underlying network cause (DNS, ECONNREFUSED, TLS, etc.) */
|
|
26
|
+
const fetchWithContext = async (url: string, init: RequestInit, step: string): Promise<Response> => {
|
|
27
|
+
try {
|
|
28
|
+
return await fetch(url, init)
|
|
29
|
+
} catch (e) {
|
|
30
|
+
const cause = (e as { cause?: unknown }).cause
|
|
31
|
+
const causeMsg = cause instanceof Error ? cause.message : cause ? String(cause) : (e as Error).message
|
|
32
|
+
throw new Error(`Network error during ${step} (${url}): ${causeMsg}`)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Reads a response body, parsing JSON when possible and including status + body in errors */
|
|
37
|
+
const readResponse = async (res: Response, url: string, step: string) => {
|
|
38
|
+
const text = await res.text()
|
|
39
|
+
let json: unknown
|
|
40
|
+
try {
|
|
41
|
+
json = text ? JSON.parse(text) : {}
|
|
42
|
+
} catch {
|
|
43
|
+
if (!res.ok) {
|
|
44
|
+
const snippet = text.slice(0, 200).replace(/\s+/g, " ").trim()
|
|
45
|
+
throw new Error(`Server error during ${step} (${res.status} ${res.statusText} from ${url}): ${snippet || "no body"}`)
|
|
46
|
+
}
|
|
47
|
+
throw new Error(`Invalid JSON response during ${step} (${url}): ${text.slice(0, 200)}`)
|
|
48
|
+
}
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
const serverMsg = (json as { error?: string }).error || res.statusText || "Unknown error"
|
|
51
|
+
throw new Error(`Server error during ${step} (${res.status} from ${url}): ${serverMsg}`)
|
|
52
|
+
}
|
|
34
53
|
return json
|
|
35
54
|
}
|
|
36
55
|
|
|
56
|
+
/** Sends a JSON POST request */
|
|
57
|
+
const jsonPost = async (url: string, token: string, body: object, step: string) => {
|
|
58
|
+
const res = await fetchWithContext(
|
|
59
|
+
url,
|
|
60
|
+
{
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
63
|
+
body: JSON.stringify(body),
|
|
64
|
+
},
|
|
65
|
+
step,
|
|
66
|
+
)
|
|
67
|
+
return readResponse(res, url, step)
|
|
68
|
+
}
|
|
69
|
+
|
|
37
70
|
/** Uploads a single file as raw binary with filename in query param */
|
|
38
71
|
const uploadFile = async (url: string, token: string, filePath: string, baseDir: string): Promise<FileResponse> => {
|
|
39
72
|
const relativePath = path.relative(baseDir, filePath)
|
|
40
73
|
const content = fs.readFileSync(filePath)
|
|
74
|
+
const step = `file upload (${relativePath})`
|
|
41
75
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
76
|
+
const fullURL = `${url}?name=${encodeURIComponent(relativePath)}`
|
|
77
|
+
const res = await fetchWithContext(
|
|
78
|
+
fullURL,
|
|
79
|
+
{
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: {
|
|
82
|
+
Authorization: `Bearer ${token}`,
|
|
83
|
+
"Content-Type": "application/octet-stream",
|
|
84
|
+
},
|
|
85
|
+
body: content,
|
|
47
86
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (!res.ok) throw new Error(`Server error (${res.status}): ${(json as any).error || "Unknown error"}`)
|
|
52
|
-
return json as FileResponse
|
|
87
|
+
step,
|
|
88
|
+
)
|
|
89
|
+
return (await readResponse(res, fullURL, step)) as FileResponse
|
|
53
90
|
}
|
|
54
91
|
|
|
55
92
|
/** Multi-step upload: init -> batched file uploads -> complete */
|
|
@@ -65,7 +102,7 @@ export const uploadFiles = async (
|
|
|
65
102
|
const apiURL = getAPIURL()
|
|
66
103
|
|
|
67
104
|
// Step 1: Init session (includes puzzmo.json metadata)
|
|
68
|
-
const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile })) as InitResponse
|
|
105
|
+
const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile }, "upload init")) as InitResponse
|
|
69
106
|
|
|
70
107
|
// Step 2: Upload files in concurrent batches
|
|
71
108
|
const fileURL = `${apiURL}/cliUpload/${init.sessionID}/file`
|
|
@@ -81,5 +118,5 @@ export const uploadFiles = async (
|
|
|
81
118
|
}
|
|
82
119
|
|
|
83
120
|
// Step 3: Complete
|
|
84
|
-
return (await jsonPost(`${apiURL}/cliUpload/${init.sessionID}/complete`, token, {})) as CompleteResponse
|
|
121
|
+
return (await jsonPost(`${apiURL}/cliUpload/${init.sessionID}/complete`, token, {}, "upload complete")) as CompleteResponse
|
|
85
122
|
}
|