arcadible 1.0.13 → 1.0.14
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/bin/arc-deploy.js +8 -1
- package/bin/arc-pack.js +12 -1
- package/package.json +1 -1
- package/src/action/pack/tool.js +9 -2
- package/src/action/pack/unsound.js +69 -0
- package/src/action/validate/tool.js +2 -1
- package/src/action/validate/unsound.js +166 -0
- package/src/config/code.js +6 -0
- package/src/phrase.js +63 -0
package/bin/arc-deploy.js
CHANGED
|
@@ -334,7 +334,14 @@ const start = function (argv) {
|
|
|
334
334
|
// whoami
|
|
335
335
|
current,
|
|
336
336
|
// pack
|
|
337
|
-
git
|
|
337
|
+
git
|
|
338
|
+
? null
|
|
339
|
+
: pack({
|
|
340
|
+
input: artifact?.path,
|
|
341
|
+
onWarning: handler({ json, pb, quiet, sw, warnings }).onWarning,
|
|
342
|
+
project: parsed,
|
|
343
|
+
root,
|
|
344
|
+
}),
|
|
338
345
|
// build: whether workflow.build ran
|
|
339
346
|
Boolean(artifact),
|
|
340
347
|
]);
|
package/bin/arc-pack.js
CHANGED
|
@@ -31,6 +31,7 @@ const print = output.print;
|
|
|
31
31
|
const resolve = path.resolve;
|
|
32
32
|
const SpinnerWheel = spinner.SpinnerWheel;
|
|
33
33
|
const warn = output.warn;
|
|
34
|
+
const warning = output.warning;
|
|
34
35
|
|
|
35
36
|
// [declare]
|
|
36
37
|
|
|
@@ -56,8 +57,9 @@ const start = function (argv) {
|
|
|
56
57
|
const root = resolve(process.cwd(), argv.cwd);
|
|
57
58
|
// the project, parsed once for both steps.
|
|
58
59
|
let parsed = null;
|
|
59
|
-
// what --json lists under `warnings
|
|
60
|
+
// what --json lists under `warnings`: arc validate's, then the package's.
|
|
60
61
|
let warnings = [];
|
|
62
|
+
const packed = [];
|
|
61
63
|
let sw = new SpinnerWheel("Validating… %s");
|
|
62
64
|
sw.start(json || quiet);
|
|
63
65
|
return checked(root, (found) => {
|
|
@@ -71,12 +73,21 @@ const start = function (argv) {
|
|
|
71
73
|
sw.start(json || quiet);
|
|
72
74
|
return pack({
|
|
73
75
|
input: argv.input,
|
|
76
|
+
// the package's own, printed once it's packed.
|
|
77
|
+
onWarning({ code, message }) {
|
|
78
|
+
packed.push(warning(code, message));
|
|
79
|
+
},
|
|
74
80
|
output: argv.output,
|
|
75
81
|
project: parsed,
|
|
76
82
|
root,
|
|
77
83
|
});
|
|
78
84
|
})
|
|
79
85
|
.then((artifact) => {
|
|
86
|
+
warnings = [...warnings, ...packed];
|
|
87
|
+
if (!json && !quiet && packed.length) {
|
|
88
|
+
sw.stop(true);
|
|
89
|
+
packed.forEach((each) => log.pw(each.message));
|
|
90
|
+
}
|
|
80
91
|
if (json) {
|
|
81
92
|
print({ artifact, warnings });
|
|
82
93
|
return;
|
package/package.json
CHANGED
package/src/action/pack/tool.js
CHANGED
|
@@ -19,6 +19,7 @@ const given = require("../../parser/given");
|
|
|
19
19
|
const limits = require("./limits");
|
|
20
20
|
const phrase = require("../../phrase");
|
|
21
21
|
const size = require("../../util/size");
|
|
22
|
+
const unsound = require("./unsound");
|
|
22
23
|
const Zip = require("../../util/zip");
|
|
23
24
|
|
|
24
25
|
// [use]
|
|
@@ -106,7 +107,7 @@ const verifyTotal = function (zip) {
|
|
|
106
107
|
const pack = function (
|
|
107
108
|
name,
|
|
108
109
|
game,
|
|
109
|
-
{ deployDir, inputPath, mediaPath, outputPath },
|
|
110
|
+
{ deployDir, inputPath, mediaPath, onWarning, outputPath },
|
|
110
111
|
) {
|
|
111
112
|
const zip = new Zip(name);
|
|
112
113
|
return pathExists(inputPath)
|
|
@@ -136,6 +137,9 @@ const pack = function (
|
|
|
136
137
|
})
|
|
137
138
|
.then(verifyTotal)
|
|
138
139
|
.then((file) => {
|
|
140
|
+
if (onWarning) {
|
|
141
|
+
unsound(file.entries()).forEach(onWarning);
|
|
142
|
+
}
|
|
139
143
|
file.write(outputPath);
|
|
140
144
|
return { path: outputPath, ...file.stat() };
|
|
141
145
|
});
|
|
@@ -143,7 +147,9 @@ const pack = function (
|
|
|
143
147
|
|
|
144
148
|
// [function]
|
|
145
149
|
|
|
146
|
-
|
|
150
|
+
// `onWarning` gets each of the package's warnings, such as a `.env` file
|
|
151
|
+
// in it, as a command's own.
|
|
152
|
+
const main = function ({ input, onWarning, output, ...conf }) {
|
|
147
153
|
const root = conf.root;
|
|
148
154
|
return given(conf).then(({ game, workflow }) => {
|
|
149
155
|
const name = game.handle ?? game.id;
|
|
@@ -154,6 +160,7 @@ const main = function ({ input, output, ...conf }) {
|
|
|
154
160
|
deployDir: inputPath === deployDir,
|
|
155
161
|
inputPath,
|
|
156
162
|
mediaPath: resolve(root, MEDIA),
|
|
163
|
+
onWarning,
|
|
157
164
|
outputPath: resolve(root, file),
|
|
158
165
|
});
|
|
159
166
|
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// [import]
|
|
4
|
+
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
// [import]
|
|
8
|
+
|
|
9
|
+
const cfgcode = require("../../config/code");
|
|
10
|
+
const phrase = require("../../phrase");
|
|
11
|
+
|
|
12
|
+
// [use]
|
|
13
|
+
|
|
14
|
+
const posixBasename = path.posix.basename;
|
|
15
|
+
const WARNING = cfgcode.WARNING;
|
|
16
|
+
|
|
17
|
+
// [declare]
|
|
18
|
+
|
|
19
|
+
// the files any player could download that are almost never meant to be
|
|
20
|
+
// public, kept short on purpose, to grow later (OUTPUT.md, Codes): a `.env`
|
|
21
|
+
// file other than an example of one, and a git repository.
|
|
22
|
+
const ENV = /^\.env(?:\..+)?$/u;
|
|
23
|
+
const ENV_EXAMPLE = /\.(?:example|sample|template)$/u;
|
|
24
|
+
const GIT = ".git";
|
|
25
|
+
// the folder of a project's installed packages, at the package's root.
|
|
26
|
+
const MODULES = "node_modules/";
|
|
27
|
+
|
|
28
|
+
// [function]
|
|
29
|
+
|
|
30
|
+
// the private files among the package's, each once: a `.env` file by its
|
|
31
|
+
// path, and a git repository by its folder.
|
|
32
|
+
const privates = function (names) {
|
|
33
|
+
const found = names.flatMap((name) => {
|
|
34
|
+
const parts = name.split("/");
|
|
35
|
+
const git = parts.slice(0, -1).indexOf(GIT);
|
|
36
|
+
if (git !== -1) {
|
|
37
|
+
return [`${parts.slice(0, git + 1).join("/")}/`];
|
|
38
|
+
}
|
|
39
|
+
const base = posixBasename(name);
|
|
40
|
+
return ENV.test(base) && !ENV_EXAMPLE.test(base) ? [name] : [];
|
|
41
|
+
});
|
|
42
|
+
return [...new Set(found)].sort();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// [function]
|
|
46
|
+
|
|
47
|
+
// the packed files' warnings, as a command's own (`onWarning`): what the
|
|
48
|
+
// package holds, never a guess at how it's served (OUTPUT.md, Codes).
|
|
49
|
+
const main = function (entries) {
|
|
50
|
+
const names = entries.map((entry) => entry.name);
|
|
51
|
+
return [
|
|
52
|
+
...(names.some((name) => name.startsWith(MODULES))
|
|
53
|
+
? [
|
|
54
|
+
{
|
|
55
|
+
code: WARNING.PACKAGE_FILE_DEV,
|
|
56
|
+
message: phrase.packageFileDev(MODULES),
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
: []),
|
|
60
|
+
...privates(names).map((file) => ({
|
|
61
|
+
code: WARNING.PACKAGE_FILE_PRIVATE,
|
|
62
|
+
message: phrase.packageFilePrivate(file),
|
|
63
|
+
})),
|
|
64
|
+
];
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// [export]
|
|
68
|
+
|
|
69
|
+
module.exports = main;
|
|
@@ -9,6 +9,7 @@ const readmeFile = require("../../parser/readme-file");
|
|
|
9
9
|
const schema = require("../../util/schema");
|
|
10
10
|
const sections = require("../../parser/sections");
|
|
11
11
|
const unknown = require("./unknown");
|
|
12
|
+
const unsound = require("./unsound");
|
|
12
13
|
const workflow = require("../../schema/workflow");
|
|
13
14
|
|
|
14
15
|
// [use]
|
|
@@ -56,7 +57,7 @@ const main = function (conf) {
|
|
|
56
57
|
return Promise.all([given(conf), structure(root), unknown(root)]).then(
|
|
57
58
|
([proj, readmeStructure, warnings]) => ({
|
|
58
59
|
issues: validate(proj, readmeStructure, { root }),
|
|
59
|
-
warnings: warnings.sort(schema.compare),
|
|
60
|
+
warnings: [...warnings, ...unsound(proj)].sort(schema.compare),
|
|
60
61
|
}),
|
|
61
62
|
);
|
|
62
63
|
};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// [import]
|
|
4
|
+
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
// [import]
|
|
8
|
+
|
|
9
|
+
const semver = require("semver");
|
|
10
|
+
|
|
11
|
+
// [import]
|
|
12
|
+
|
|
13
|
+
const cfgart = require("../../config/art");
|
|
14
|
+
const cfgcode = require("../../config/code");
|
|
15
|
+
const cfgfile = require("../../config/file");
|
|
16
|
+
const output = require("../../util/output");
|
|
17
|
+
const phrase = require("../../phrase");
|
|
18
|
+
|
|
19
|
+
// [use]
|
|
20
|
+
|
|
21
|
+
const extname = path.extname;
|
|
22
|
+
const parse = path.parse;
|
|
23
|
+
const posixBasename = path.posix.basename;
|
|
24
|
+
const prerelease = semver.prerelease;
|
|
25
|
+
const validSemver = semver.valid;
|
|
26
|
+
const WARNING = cfgcode.WARNING;
|
|
27
|
+
|
|
28
|
+
// [declare]
|
|
29
|
+
|
|
30
|
+
const MEDIA = cfgfile.MEDIA;
|
|
31
|
+
|
|
32
|
+
// [declare]
|
|
33
|
+
|
|
34
|
+
// the art fields, such as `icon`, each of which may have a `_video`.
|
|
35
|
+
const ART = cfgart
|
|
36
|
+
.map((file) => parse(file).name)
|
|
37
|
+
.filter((name) => !name.endsWith("_video"));
|
|
38
|
+
// a screenshot's name, as the store reads it (ui2's `screenshots()`).
|
|
39
|
+
const SCREENSHOT = /^screenshot_(\d+)(?:_([^.]+))?\.[^.]+$/u;
|
|
40
|
+
// a name one case or separator away from a screenshot's: `Screenshot_01`,
|
|
41
|
+
// `screenshot-01`, `screenshot01`, with any qualifier after it.
|
|
42
|
+
const SCREENSHOT_NEAR = /^screenshot[-_ ]?(\d+)(?:[-_ ](.+))?$/iu;
|
|
43
|
+
|
|
44
|
+
// [function]
|
|
45
|
+
|
|
46
|
+
// a warning about a field, in an issue's shape, with its details.
|
|
47
|
+
const warning = function (code, field, [message, ...details]) {
|
|
48
|
+
return { ...output.warning(code, message, field), details };
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// [function]
|
|
52
|
+
|
|
53
|
+
// the fields a release leaves out whose defaults it may not mean: a release
|
|
54
|
+
// version's `visibility` and `title`, and a public one's `inputs`. A
|
|
55
|
+
// prerelease changes no store page, so it's never warned about.
|
|
56
|
+
const unset = function (game) {
|
|
57
|
+
const version = game.version;
|
|
58
|
+
if (!validSemver(version) || prerelease(version)) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const fields = [
|
|
62
|
+
"visibility",
|
|
63
|
+
"title",
|
|
64
|
+
...(game.visibility === "public" ? ["inputs"] : []),
|
|
65
|
+
];
|
|
66
|
+
return fields
|
|
67
|
+
.filter((field) => typeof game[field] === "undefined")
|
|
68
|
+
.map((field) =>
|
|
69
|
+
warning(WARNING.FIELD_UNSET, `game.${field}`, phrase.fieldUnset(field)),
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// [function]
|
|
74
|
+
|
|
75
|
+
// each art video without its PNG, such as `icon_video` without `icon`.
|
|
76
|
+
const unpaired = function (game) {
|
|
77
|
+
return ART.filter((art) => game[`${art}_video`] && !game[art]).map((art) =>
|
|
78
|
+
warning(
|
|
79
|
+
WARNING.ART_VIDEO_UNPAIRED,
|
|
80
|
+
`game.${art}_video`,
|
|
81
|
+
phrase.artVideoUnpaired(art, `${MEDIA}/${art}.png`),
|
|
82
|
+
),
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// [function]
|
|
87
|
+
|
|
88
|
+
// the gallery's file names, or none when it isn't a list of paths, which
|
|
89
|
+
// its issues report.
|
|
90
|
+
const galleryNames = function (game) {
|
|
91
|
+
const gallery = game.gallery;
|
|
92
|
+
if (!Array.isArray(gallery)) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
return gallery
|
|
96
|
+
.filter((file) => typeof file === "string")
|
|
97
|
+
.map((file) => posixBasename(file));
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// [function]
|
|
101
|
+
|
|
102
|
+
// screenshots with one number and qualifier, such as `screenshot_1.png` and
|
|
103
|
+
// `screenshot_01.png`, which the store can show only one of.
|
|
104
|
+
const repeated = function (names) {
|
|
105
|
+
const groups = {};
|
|
106
|
+
for (const name of names) {
|
|
107
|
+
const match = SCREENSHOT.exec(name);
|
|
108
|
+
if (match) {
|
|
109
|
+
const key = `${Number(match[1])}_${match[2] ?? ""}`;
|
|
110
|
+
groups[key] = [...(groups[key] ?? []), name];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return Object.values(groups)
|
|
114
|
+
.filter((group) => group.length > 1)
|
|
115
|
+
.map((group) =>
|
|
116
|
+
warning(
|
|
117
|
+
WARNING.SCREENSHOT_NUMBER_REPEATED,
|
|
118
|
+
"game.gallery",
|
|
119
|
+
phrase.screenshotNumberRepeated(group),
|
|
120
|
+
),
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// [function]
|
|
125
|
+
|
|
126
|
+
// a gallery file named almost as a screenshot, with the name it might mean.
|
|
127
|
+
const misnamed = function (names) {
|
|
128
|
+
return names.flatMap((name) => {
|
|
129
|
+
if (SCREENSHOT.test(name)) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
const ext = extname(name);
|
|
133
|
+
const match = SCREENSHOT_NEAR.exec(parse(name).name);
|
|
134
|
+
if (!match) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
const qualifier = match[2] ? `_${match[2]}` : "";
|
|
138
|
+
const near = `screenshot_${match[1]}${qualifier}${ext}`;
|
|
139
|
+
return [
|
|
140
|
+
warning(WARNING.SCREENSHOT_NAME_INVALID, "game.gallery", [
|
|
141
|
+
phrase.screenshotNameInvalid(name),
|
|
142
|
+
phrase.didYouMean(near),
|
|
143
|
+
]),
|
|
144
|
+
];
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// [function]
|
|
149
|
+
|
|
150
|
+
// what the resolved manifest has that's sound by its rules but almost never
|
|
151
|
+
// meant, as warnings: facts about the project, never a guess at how the
|
|
152
|
+
// store will show it (OUTPUT.md, Codes).
|
|
153
|
+
const main = function (proj) {
|
|
154
|
+
const game = proj?.game ?? {};
|
|
155
|
+
const names = galleryNames(game);
|
|
156
|
+
return [
|
|
157
|
+
...unset(game),
|
|
158
|
+
...unpaired(game),
|
|
159
|
+
...repeated(names),
|
|
160
|
+
...misnamed(names),
|
|
161
|
+
];
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// [export]
|
|
165
|
+
|
|
166
|
+
module.exports = main;
|
package/src/config/code.js
CHANGED
|
@@ -103,10 +103,16 @@ const ISSUE = {
|
|
|
103
103
|
// they follow the same rules, and share the Errors page, so no warning code
|
|
104
104
|
// is also an error or issue code.
|
|
105
105
|
const WARNING = {
|
|
106
|
+
ART_VIDEO_UNPAIRED: "art_video_unpaired",
|
|
106
107
|
DEPLOY_DIR_NOT_BUILT: "deploy_dir_not_built",
|
|
107
108
|
FIELD_UNKNOWN: "field_unknown",
|
|
109
|
+
FIELD_UNSET: "field_unset",
|
|
108
110
|
GAME_HANDLE_LAPSED: "game_handle_lapsed",
|
|
109
111
|
GAME_HANDLE_LIMIT_REACHED: "game_handle_limit_reached",
|
|
112
|
+
PACKAGE_FILE_DEV: "package_file_dev",
|
|
113
|
+
PACKAGE_FILE_PRIVATE: "package_file_private",
|
|
114
|
+
SCREENSHOT_NAME_INVALID: "screenshot_name_invalid",
|
|
115
|
+
SCREENSHOT_NUMBER_REPEATED: "screenshot_number_repeated",
|
|
110
116
|
VERSION_UNCHECKED: "version_unchecked",
|
|
111
117
|
};
|
|
112
118
|
// every error code, for checking one exists.
|
package/src/phrase.js
CHANGED
|
@@ -13,6 +13,15 @@ const TAIL_LINES = 20;
|
|
|
13
13
|
|
|
14
14
|
// [function]
|
|
15
15
|
|
|
16
|
+
const artVideoUnpaired = function (art, png) {
|
|
17
|
+
return [
|
|
18
|
+
`Has no ${art} beside it.`,
|
|
19
|
+
`Add ${png} too, for places that don't show video.`,
|
|
20
|
+
];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// [function]
|
|
24
|
+
|
|
16
25
|
// a message's reason, then each block of details after a blank line
|
|
17
26
|
// (OUTPUT.md). A block of several lines is an array.
|
|
18
27
|
const blocks = function (reason, ...details) {
|
|
@@ -23,6 +32,54 @@ const blocks = function (reason, ...details) {
|
|
|
23
32
|
|
|
24
33
|
// [function]
|
|
25
34
|
|
|
35
|
+
// a release's field left out, keyed by field: what leaving it out means,
|
|
36
|
+
// then what to set.
|
|
37
|
+
const fieldUnset = function (field) {
|
|
38
|
+
return {
|
|
39
|
+
inputs: [
|
|
40
|
+
"Not set, so this release lists no inputs.",
|
|
41
|
+
"Set inputs to how the game is played.",
|
|
42
|
+
],
|
|
43
|
+
title: [
|
|
44
|
+
"Not set, so this release has no title.",
|
|
45
|
+
"Start the README with a # title, or set title.",
|
|
46
|
+
],
|
|
47
|
+
visibility: [
|
|
48
|
+
"Not set, so this release is unlisted: it plays by link only.",
|
|
49
|
+
'Set visibility = "public" to list it, or "unlisted" to keep it so.',
|
|
50
|
+
],
|
|
51
|
+
}[field];
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// [function]
|
|
55
|
+
|
|
56
|
+
const packageFileDev = function (dir) {
|
|
57
|
+
return `Packing ${dir}, a project's installed packages.\nSet deploy_dir to the folder your build writes.`;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// [function]
|
|
61
|
+
|
|
62
|
+
const packageFilePrivate = function (file) {
|
|
63
|
+
return `Packing ${file}, which anyone can download.\nLeave it out of the package unless it's meant to be public.`;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// [function]
|
|
67
|
+
|
|
68
|
+
const screenshotNameInvalid = function (name) {
|
|
69
|
+
return `Not a screenshot's name: ${name}.`;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// [function]
|
|
73
|
+
|
|
74
|
+
const screenshotNumberRepeated = function (names) {
|
|
75
|
+
return [
|
|
76
|
+
`Screenshots share a number: ${names.join(", ")}.`,
|
|
77
|
+
"Give each screenshot its own number.",
|
|
78
|
+
];
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// [function]
|
|
82
|
+
|
|
26
83
|
// the last lines a tool printed, stdout's then stderr's, since some, such as
|
|
27
84
|
// tsc, print their errors on stdout, or "" when it printed nothing. Lines
|
|
28
85
|
// left out are marked with "…".
|
|
@@ -1077,6 +1134,7 @@ const videoWidthMin = function (width) {
|
|
|
1077
1134
|
|
|
1078
1135
|
module.exports = {
|
|
1079
1136
|
aborted,
|
|
1137
|
+
artVideoUnpaired,
|
|
1080
1138
|
buildFailed,
|
|
1081
1139
|
buildOutputMissing,
|
|
1082
1140
|
buildPublishing,
|
|
@@ -1097,6 +1155,7 @@ module.exports = {
|
|
|
1097
1155
|
docMediaType,
|
|
1098
1156
|
docTagUnallowed,
|
|
1099
1157
|
errorCode,
|
|
1158
|
+
fieldUnset,
|
|
1100
1159
|
gameHandleChanged,
|
|
1101
1160
|
gameHandleLapsed,
|
|
1102
1161
|
gameHandleLimit,
|
|
@@ -1142,6 +1201,8 @@ module.exports = {
|
|
|
1142
1201
|
nothingToDo,
|
|
1143
1202
|
openToContinue,
|
|
1144
1203
|
opened,
|
|
1204
|
+
packageFileDev,
|
|
1205
|
+
packageFilePrivate,
|
|
1145
1206
|
packageFileTooLarge,
|
|
1146
1207
|
packageIndexMissing,
|
|
1147
1208
|
packagePathReserved,
|
|
@@ -1163,6 +1224,8 @@ module.exports = {
|
|
|
1163
1224
|
remoteKept,
|
|
1164
1225
|
remoteUnreachable,
|
|
1165
1226
|
repoNotConnected,
|
|
1227
|
+
screenshotNameInvalid,
|
|
1228
|
+
screenshotNumberRepeated,
|
|
1166
1229
|
serveAlone,
|
|
1167
1230
|
serveNext,
|
|
1168
1231
|
serveNextUrl,
|