arcadible 1.0.4 → 1.0.6
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/README.md +1 -1
- package/package.json +1 -1
- package/src/action/build/tool.js +7 -3
- package/src/action/deploy/tool-git.js +8 -3
- package/src/action/init/tool.js +11 -3
- package/src/action/pack/tool.js +11 -4
- package/src/action/predeploy/git.js +2 -2
- package/src/phrase.js +49 -15
- package/src/serve/page.js +2 -2
- package/src/util/rc.js +23 -1
- package/src/util/s3.js +22 -3
package/README.md
CHANGED
package/package.json
CHANGED
package/src/action/build/tool.js
CHANGED
|
@@ -35,9 +35,13 @@ const execBuild = function (command, { cwdPath }) {
|
|
|
35
35
|
if (err) {
|
|
36
36
|
const { code, signal } = err;
|
|
37
37
|
reject(
|
|
38
|
-
new ArcError(
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
new ArcError(
|
|
39
|
+
cfgcode.BUILD_FAILED,
|
|
40
|
+
phrase.buildFailed({ code, signal, stderr, stdout }),
|
|
41
|
+
{
|
|
42
|
+
cause: err,
|
|
43
|
+
},
|
|
44
|
+
),
|
|
41
45
|
);
|
|
42
46
|
return;
|
|
43
47
|
}
|
|
@@ -12,7 +12,6 @@ const repo = require("../predeploy/git");
|
|
|
12
12
|
// [use]
|
|
13
13
|
|
|
14
14
|
const ArcError = error.ArcError;
|
|
15
|
-
const failWith = repo.failWith;
|
|
16
15
|
const git = repo.git;
|
|
17
16
|
const tagOf = repo.tagOf;
|
|
18
17
|
|
|
@@ -25,7 +24,13 @@ const createTag = function (root, version) {
|
|
|
25
24
|
: phrase.versionPrerelease(version);
|
|
26
25
|
return git(root, ["tag", "-a", tagOf(version), "-m", message]).then(
|
|
27
26
|
() => null,
|
|
28
|
-
|
|
27
|
+
(err) => {
|
|
28
|
+
throw new ArcError(
|
|
29
|
+
cfgcode.GIT_TAG_CREATE_FAILED,
|
|
30
|
+
phrase.gitTagCreateFailed(version, err.stderr),
|
|
31
|
+
{ cause: err },
|
|
32
|
+
);
|
|
33
|
+
},
|
|
29
34
|
);
|
|
30
35
|
};
|
|
31
36
|
|
|
@@ -52,7 +57,7 @@ const pushTag = function (root, version) {
|
|
|
52
57
|
version,
|
|
53
58
|
new ArcError(
|
|
54
59
|
cfgcode.GIT_TAG_PUSH_FAILED,
|
|
55
|
-
phrase.gitTagPushFailed(version),
|
|
60
|
+
phrase.gitTagPushFailed(version, err.stderr),
|
|
56
61
|
{
|
|
57
62
|
cause: err,
|
|
58
63
|
},
|
package/src/action/init/tool.js
CHANGED
|
@@ -15,6 +15,7 @@ const cfgcode = require("../../config/code");
|
|
|
15
15
|
const error = require("../../util/error");
|
|
16
16
|
const manifest = require("./manifest");
|
|
17
17
|
const phrase = require("../../phrase");
|
|
18
|
+
const templates = require("../../config/template");
|
|
18
19
|
|
|
19
20
|
// [use]
|
|
20
21
|
|
|
@@ -38,10 +39,14 @@ const removeLicense = function (dest) {
|
|
|
38
39
|
|
|
39
40
|
// [function]
|
|
40
41
|
|
|
42
|
+
// a starter's name on its own, such as `blank`, suggests the starter.
|
|
41
43
|
const unavailable = function (src, cause) {
|
|
44
|
+
const starter = templates.choices.find(
|
|
45
|
+
({ value }) => value.split("/").pop() === src,
|
|
46
|
+
);
|
|
42
47
|
return new ArcError(
|
|
43
48
|
cfgcode.TEMPLATE_UNAVAILABLE,
|
|
44
|
-
phrase.templateUnavailable(src),
|
|
49
|
+
phrase.templateUnavailable(src, starter?.value),
|
|
45
50
|
{ cause },
|
|
46
51
|
);
|
|
47
52
|
};
|
|
@@ -64,8 +69,11 @@ const scaffold = function (src, dest, { onInitBegin }) {
|
|
|
64
69
|
if (onInitBegin) {
|
|
65
70
|
onInitBegin({ dest, src });
|
|
66
71
|
}
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
// degit throws as it's made when it can't parse `src`.
|
|
73
|
+
return Promise.resolve()
|
|
74
|
+
.then(() =>
|
|
75
|
+
degit(src, { cache: false, force: true, verbose: false }).clone(dest),
|
|
76
|
+
)
|
|
69
77
|
.catch((ex) => {
|
|
70
78
|
throw unavailable(src, ex);
|
|
71
79
|
})
|
package/src/action/pack/tool.js
CHANGED
|
@@ -48,13 +48,20 @@ const SIZE_PARTIAL = cfglimit.ZIP_SIZE;
|
|
|
48
48
|
|
|
49
49
|
// [function]
|
|
50
50
|
|
|
51
|
-
// the build's
|
|
51
|
+
// the first path at the build's root that's Arcadible's, if it has one: a
|
|
52
|
+
// folder named like the reserved one, or the manifest or media that packing
|
|
53
|
+
// adds, as `arc validate` checks deploy_dir. A folder ends with "/".
|
|
52
54
|
const findReserved = function (zip) {
|
|
53
|
-
|
|
55
|
+
return zip
|
|
54
56
|
.entries()
|
|
55
57
|
.map((entry) => entry.name.split("/"))
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
+
.map((parts) => (parts.length > 1 ? `${parts[0]}/` : parts[0]))
|
|
59
|
+
.find(
|
|
60
|
+
(name) =>
|
|
61
|
+
name.toLowerCase() === `${RESERVED}/` ||
|
|
62
|
+
name === MANIFEST ||
|
|
63
|
+
name === `${MEDIA}/`,
|
|
64
|
+
);
|
|
58
65
|
};
|
|
59
66
|
|
|
60
67
|
// [function]
|
|
@@ -35,9 +35,9 @@ const NAME_REPO = /^[A-Za-z0-9._-]{1,100}$/u;
|
|
|
35
35
|
// runs git in `root`, and resolves with its stdout.
|
|
36
36
|
const git = function (root, args) {
|
|
37
37
|
return new Promise((resolve, reject) => {
|
|
38
|
-
execFile("git", ["-C", root, ...args], (err, stdout) => {
|
|
38
|
+
execFile("git", ["-C", root, ...args], (err, stdout, stderr) => {
|
|
39
39
|
if (err) {
|
|
40
|
-
reject(err);
|
|
40
|
+
reject(Object.assign(err, { stderr }));
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
43
|
resolve(stdout);
|
package/src/phrase.js
CHANGED
|
@@ -6,8 +6,10 @@ const cfgproduct = require("./config/product");
|
|
|
6
6
|
|
|
7
7
|
// [declare]
|
|
8
8
|
|
|
9
|
+
const HANDLE = cfgproduct.handle;
|
|
9
10
|
const TITLE = cfgproduct.title;
|
|
10
11
|
const TOOL = cfgproduct.tool;
|
|
12
|
+
const TAIL_LINES = 20;
|
|
11
13
|
|
|
12
14
|
// [function]
|
|
13
15
|
|
|
@@ -21,6 +23,22 @@ const blocks = function (reason, ...details) {
|
|
|
21
23
|
|
|
22
24
|
// [function]
|
|
23
25
|
|
|
26
|
+
// the last lines a tool printed, stdout's then stderr's, since some, such as
|
|
27
|
+
// tsc, print their errors on stdout, or "" when it printed nothing. Lines
|
|
28
|
+
// left out are marked with "…".
|
|
29
|
+
const tail = function (...outputs) {
|
|
30
|
+
const lines = outputs
|
|
31
|
+
.map((output) => output?.trim())
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.join("\n")
|
|
34
|
+
.split("\n");
|
|
35
|
+
return lines.length > TAIL_LINES
|
|
36
|
+
? ["…", ...lines.slice(-TAIL_LINES)].join("\n")
|
|
37
|
+
: lines.join("\n");
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// [function]
|
|
41
|
+
|
|
24
42
|
// a wait, in words: "5 minutes", "90 seconds".
|
|
25
43
|
const waited = function (seconds) {
|
|
26
44
|
if (seconds % 60 === 0) {
|
|
@@ -39,10 +57,10 @@ const aborted = function (command) {
|
|
|
39
57
|
|
|
40
58
|
// [function]
|
|
41
59
|
|
|
42
|
-
// a build that exited with `code`, or was stopped by `signal`, with
|
|
43
|
-
//
|
|
44
|
-
const buildFailed = function ({ code, signal, stderr }) {
|
|
45
|
-
const output = stderr
|
|
60
|
+
// a build that exited with `code`, or was stopped by `signal`, with the end
|
|
61
|
+
// of what it printed.
|
|
62
|
+
const buildFailed = function ({ code, signal, stderr, stdout }) {
|
|
63
|
+
const output = tail(stdout, stderr);
|
|
46
64
|
return blocks(
|
|
47
65
|
signal
|
|
48
66
|
? `Build stopped by ${signal}.`
|
|
@@ -500,18 +518,27 @@ const gitRepoMissing = function () {
|
|
|
500
518
|
|
|
501
519
|
// [function]
|
|
502
520
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
521
|
+
// with the end of what git printed, such as a lock file it found.
|
|
522
|
+
const gitTagCreateFailed = function (version, stderr) {
|
|
523
|
+
const output = tail(stderr);
|
|
524
|
+
return blocks(
|
|
525
|
+
`Unable to create Git tag for v${version}.`,
|
|
526
|
+
...(output ? [output] : []),
|
|
527
|
+
[
|
|
528
|
+
"Check that the repository has a commit.",
|
|
529
|
+
"Check that your Git name and email are set.",
|
|
530
|
+
],
|
|
531
|
+
);
|
|
508
532
|
};
|
|
509
533
|
|
|
510
534
|
// [function]
|
|
511
535
|
|
|
512
|
-
|
|
536
|
+
// with the end of what git printed, such as a hook's refusal.
|
|
537
|
+
const gitTagPushFailed = function (version, stderr) {
|
|
538
|
+
const output = tail(stderr);
|
|
513
539
|
return blocks(
|
|
514
540
|
`Unable to push Git tag for v${version}.`,
|
|
541
|
+
...(output ? [output] : []),
|
|
515
542
|
"Check that you can push to origin.",
|
|
516
543
|
);
|
|
517
544
|
};
|
|
@@ -701,10 +728,14 @@ const packageFileTooLarge = function (file, size) {
|
|
|
701
728
|
|
|
702
729
|
// [function]
|
|
703
730
|
|
|
704
|
-
|
|
731
|
+
// `name` is a folder when it ends with "/": the reserved folder, which a
|
|
732
|
+
// build may load from, or the media folder. A file is the manifest.
|
|
733
|
+
const packagePathReserved = function (name) {
|
|
705
734
|
return blocks(
|
|
706
|
-
`'${
|
|
707
|
-
|
|
735
|
+
`'${name}' is reserved for ${TITLE}.`,
|
|
736
|
+
name.toLowerCase() === `${HANDLE}/`
|
|
737
|
+
? "Rename the folder, and the paths that load from it."
|
|
738
|
+
: `${TITLE} adds its own, so leave it out of the folder you pack.`,
|
|
708
739
|
);
|
|
709
740
|
};
|
|
710
741
|
|
|
@@ -858,10 +889,13 @@ const tagPushed = function (tag) {
|
|
|
858
889
|
|
|
859
890
|
// [function]
|
|
860
891
|
|
|
861
|
-
|
|
892
|
+
// `starter` is the starter its name suggests.
|
|
893
|
+
const templateUnavailable = function (template, starter) {
|
|
862
894
|
return blocks(
|
|
863
895
|
`Unable to download template ${template}.`,
|
|
864
|
-
|
|
896
|
+
starter
|
|
897
|
+
? `Did you mean: ${starter}?`
|
|
898
|
+
: "Check that it's a public GitHub repository, or a folder in one.",
|
|
865
899
|
);
|
|
866
900
|
};
|
|
867
901
|
|
package/src/serve/page.js
CHANGED
|
@@ -97,10 +97,10 @@ const html = function (game) {
|
|
|
97
97
|
version: config.version,
|
|
98
98
|
});
|
|
99
99
|
} catch (err) {
|
|
100
|
-
console.error("
|
|
100
|
+
console.error("Arcadible SDK: the play page couldn't host the game, so it stands alone.", err);
|
|
101
101
|
}
|
|
102
102
|
} else {
|
|
103
|
-
console.error("
|
|
103
|
+
console.error("Arcadible SDK: the play page's host didn't load, so the game stands alone.");
|
|
104
104
|
}
|
|
105
105
|
frame.src = config.src;
|
|
106
106
|
document.body.prepend(frame);
|
package/src/util/rc.js
CHANGED
|
@@ -75,7 +75,22 @@ const files = function (cmd, argv) {
|
|
|
75
75
|
|
|
76
76
|
// [function]
|
|
77
77
|
|
|
78
|
-
//
|
|
78
|
+
// ini parses anything, so a section header with no closing bracket, such as
|
|
79
|
+
// `[validate`, would become a key, and the options under it would be read
|
|
80
|
+
// outside their section. The first one's problem, or null.
|
|
81
|
+
const unclosedSection = function (str) {
|
|
82
|
+
const lines = str.split(/\r?\n/u).map((line) => line.trim());
|
|
83
|
+
const i = lines.findIndex(
|
|
84
|
+
(text) => text.startsWith("[") && !/^\[[^\]]*\]$/u.test(text),
|
|
85
|
+
);
|
|
86
|
+
return i === -1
|
|
87
|
+
? null
|
|
88
|
+
: `Line ${i + 1}: section header isn't closed: ${lines[i]}`;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// [function]
|
|
92
|
+
|
|
93
|
+
// a file's sections, INI or JSON, or nothing when it can't be read. A file
|
|
79
94
|
// that doesn't parse is `parse_failed`, naming the file.
|
|
80
95
|
const read = function (file) {
|
|
81
96
|
let str = null;
|
|
@@ -85,6 +100,13 @@ const read = function (file) {
|
|
|
85
100
|
return {};
|
|
86
101
|
}
|
|
87
102
|
if (!/^\s*\{/u.test(str)) {
|
|
103
|
+
const unclosed = unclosedSection(str);
|
|
104
|
+
if (unclosed) {
|
|
105
|
+
throw new ArcError(
|
|
106
|
+
cfgcode.PARSE_FAILED,
|
|
107
|
+
phrase.unableToParse(file, unclosed),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
88
110
|
return ini.parse(str);
|
|
89
111
|
}
|
|
90
112
|
try {
|
package/src/util/s3.js
CHANGED
|
@@ -17,13 +17,32 @@ const ArcError = error.ArcError;
|
|
|
17
17
|
const createReadStream = fs.createReadStream;
|
|
18
18
|
const request = https.request;
|
|
19
19
|
|
|
20
|
+
// [declare]
|
|
21
|
+
|
|
22
|
+
const CHECKSUM = "x-amz-checksum-sha256";
|
|
23
|
+
|
|
24
|
+
// [function]
|
|
25
|
+
|
|
26
|
+
// [function]
|
|
27
|
+
|
|
28
|
+
// whether the upload link signs `header`, which must then be sent.
|
|
29
|
+
const signs = function (url, header) {
|
|
30
|
+
const signed = new URL(url).searchParams.get("X-Amz-SignedHeaders");
|
|
31
|
+
return Boolean(signed?.split(";").includes(header));
|
|
32
|
+
};
|
|
33
|
+
|
|
20
34
|
// [function]
|
|
21
35
|
|
|
22
|
-
|
|
36
|
+
// the zip's SHA-256 is sent when the link signs it, so S3 refuses a zip that
|
|
37
|
+
// isn't the one the upload was asked for.
|
|
38
|
+
const requestOpts = function (url, { sha256, size }) {
|
|
23
39
|
return {
|
|
24
40
|
headers: {
|
|
25
41
|
"Content-Length": size,
|
|
26
42
|
"Content-Type": "application/zip",
|
|
43
|
+
...(signs(url, CHECKSUM)
|
|
44
|
+
? { [CHECKSUM]: Buffer.from(sha256, "hex").toString("base64") }
|
|
45
|
+
: {}),
|
|
27
46
|
},
|
|
28
47
|
method: "PUT",
|
|
29
48
|
};
|
|
@@ -34,9 +53,9 @@ const requestOpts = function (size) {
|
|
|
34
53
|
// puts the file to the upload link, and resolves once S3 accepts it. A
|
|
35
54
|
// request that gets no response rejects as api_unreachable; a file that can't
|
|
36
55
|
// be read rejects with its own error.
|
|
37
|
-
const upload = function (url, { path, size }, onProgress) {
|
|
56
|
+
const upload = function (url, { path, sha256, size }, onProgress) {
|
|
38
57
|
return new Promise((resolve, reject) => {
|
|
39
|
-
const req = request(url, requestOpts(size), (resp) => {
|
|
58
|
+
const req = request(url, requestOpts(url, { sha256, size }), (resp) => {
|
|
40
59
|
resp.resume();
|
|
41
60
|
const status = resp.statusCode;
|
|
42
61
|
if (status >= 200 && status < 300) {
|