arcadible 0.1.32 → 0.1.34

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.
Files changed (58) hide show
  1. package/README.md +4 -4
  2. package/bin/arc-build.js +23 -5
  3. package/bin/arc-deploy.js +78 -13
  4. package/bin/arc-init.js +6 -1
  5. package/bin/arc-inspect.js +1 -0
  6. package/bin/arc-login.js +4 -1
  7. package/bin/arc-open.js +4 -1
  8. package/bin/arc-pack.js +23 -3
  9. package/bin/arc-play.js +4 -1
  10. package/bin/arc-validate.js +1 -0
  11. package/bin/arc-whoami.js +1 -0
  12. package/package.json +3 -3
  13. package/src/account.js +7 -4
  14. package/src/action/build/tool.js +8 -4
  15. package/src/action/deploy/tool-git.js +49 -61
  16. package/src/action/deploy/tool.js +50 -44
  17. package/src/action/deploy/wait.js +37 -22
  18. package/src/action/init/manifest.js +23 -6
  19. package/src/action/init/tool-patch.js +1 -7
  20. package/src/action/init/tool.js +12 -9
  21. package/src/action/login/flow.js +7 -3
  22. package/src/action/pack/limits.js +13 -3
  23. package/src/action/pack/tool.js +19 -6
  24. package/src/action/play/tool.js +7 -1
  25. package/src/action/predeploy/ac.js +39 -22
  26. package/src/action/predeploy/git.js +69 -58
  27. package/src/action/predeploy/pm.js +24 -17
  28. package/src/action/predeploy/published.js +15 -2
  29. package/src/action/predeploy/tool-git.js +74 -86
  30. package/src/action/predeploy/tool.js +7 -33
  31. package/src/action/validate/tool.js +5 -4
  32. package/src/api.js +14 -1
  33. package/src/cli/predeploy.js +1 -1
  34. package/src/config/code.js +112 -0
  35. package/src/config/url.js +2 -1
  36. package/src/parser/env.js +19 -3
  37. package/src/parser/given.js +17 -0
  38. package/src/parser/manifest.js +8 -1
  39. package/src/parser/media.js +5 -9
  40. package/src/parser/project.js +4 -1
  41. package/src/parser/readme-file.js +8 -1
  42. package/src/phrase.js +75 -7
  43. package/src/schema/check/game.js +5 -26
  44. package/src/schema/game.js +117 -210
  45. package/src/schema/message.js +46 -7
  46. package/src/schema/readme.js +8 -3
  47. package/src/schema/workflow.js +35 -15
  48. package/src/util/argv.js +33 -1
  49. package/src/util/delay.js +4 -1
  50. package/src/util/error.js +49 -0
  51. package/src/util/image.js +4 -30
  52. package/src/util/log.js +8 -0
  53. package/src/util/media.js +40 -0
  54. package/src/util/output.js +34 -4
  55. package/src/util/request.js +11 -2
  56. package/src/util/s3.js +37 -20
  57. package/src/util/schema.js +93 -30
  58. package/src/util/video.js +4 -27
@@ -6,95 +6,83 @@ const semver = require("semver");
6
6
 
7
7
  // [import]
8
8
 
9
+ const cfgcode = require("../../config/code");
10
+ const error = require("../../util/error");
11
+ const given = require("../../parser/given");
9
12
  const links = require("../../util/links");
10
13
  const phrase = require("../../phrase");
11
- const project = require("../../parser/project");
12
14
  const repo = require("../predeploy/git");
13
15
 
14
16
  // [use]
15
17
 
18
+ const ArcError = error.ArcError;
19
+ const failWith = repo.failWith;
16
20
  const git = repo.git;
17
21
  const prereleaseSemver = semver.prerelease;
18
22
  const tagOf = repo.tagOf;
19
23
 
20
24
  // [function]
21
25
 
22
- const createTag = function (root, version, cb) {
23
- git(
24
- root,
25
- [
26
- "tag",
27
- "-a",
28
- tagOf(version),
29
- "-m",
30
- prereleaseSemver(version) === null
31
- ? phrase.versionRelease(version)
32
- : phrase.versionPrerelease(version),
33
- ],
34
- (err) => {
35
- if (err) {
36
- cb(new Error(phrase.gitTagCreateFailed(version), { cause: err }));
37
- return;
38
- }
39
- cb(null);
40
- },
26
+ const createTag = function (root, version) {
27
+ const message =
28
+ prereleaseSemver(version) === null
29
+ ? phrase.versionRelease(version)
30
+ : phrase.versionPrerelease(version);
31
+ return git(root, ["tag", "-a", tagOf(version), "-m", message]).then(
32
+ () => null,
33
+ failWith(cfgcode.GIT_TAG_CREATE_FAILED, phrase.gitTagCreateFailed(version)),
41
34
  );
42
35
  };
43
36
 
44
37
  // [function]
45
38
 
46
- // the local tag is deleted when the push fails, so trying again isn't
47
- // blocked by a tag that was never released.
48
- const pushTag = function (root, version, cb) {
49
- git(root, ["push", "origin", tagOf(version)], (err) => {
50
- if (err) {
51
- const failed = new Error(phrase.gitTagPushFailed(version), {
52
- cause: err,
53
- });
54
- git(root, ["tag", "-d", tagOf(version)], () => cb(failed));
55
- return;
56
- }
57
- cb(null);
58
- });
39
+ // deletes the local tag, then fails with `failed` whether or not it could.
40
+ const untag = function (root, version, failed) {
41
+ const fail = () => {
42
+ throw failed;
43
+ };
44
+ return git(root, ["tag", "-d", tagOf(version)]).then(fail, fail);
59
45
  };
60
46
 
61
47
  // [function]
62
48
 
63
- // tags the version and pushes the tag: the Arcadible GitHub App publishes
64
- // the release that the workflow builds from it.
65
- const release = function (root, version, cb) {
66
- createTag(root, version, (err) => {
67
- if (err) {
68
- cb(err);
69
- return;
70
- }
71
- pushTag(root, version, cb);
72
- });
49
+ // the local tag is deleted when the push fails, so trying again isn't
50
+ // blocked by a tag that was never released.
51
+ const pushTag = function (root, version) {
52
+ return git(root, ["push", "origin", tagOf(version)]).then(
53
+ () => null,
54
+ (err) =>
55
+ untag(
56
+ root,
57
+ version,
58
+ new ArcError(
59
+ cfgcode.GIT_TAG_PUSH_FAILED,
60
+ phrase.gitTagPushFailed(version),
61
+ {
62
+ cause: err,
63
+ },
64
+ ),
65
+ ),
66
+ );
73
67
  };
74
68
 
75
69
  // [function]
76
70
 
77
- const main = function ({ root }, { onDeployBegin }) {
71
+ // tags the version and pushes the tag: the Arcadible GitHub App publishes
72
+ // the release that the workflow builds from it.
73
+ const main = function (conf, { onDeployBegin }) {
78
74
  if (onDeployBegin) {
79
75
  onDeployBegin();
80
76
  }
81
- return project(root).then(
82
- ({ game }) =>
83
- new Promise((resolve, reject) => {
84
- release(root, game.version, (err) => {
85
- if (err) {
86
- reject(err);
87
- return;
88
- }
89
- resolve({
90
- ...links.published(game),
91
- game: game.id,
92
- tag: tagOf(game.version),
93
- version: { to: game.version },
94
- });
95
- });
96
- }),
97
- );
77
+ return given(conf)
78
+ .then(({ game }) => Promise.all([game, createTag(conf.root, game.version)]))
79
+ .then(([game]) => Promise.all([game, pushTag(conf.root, game.version)]))
80
+ .then(([game]) => ({
81
+ ...links.published(game),
82
+ game: game.id,
83
+ tag: tagOf(game.version),
84
+ version: { to: game.version },
85
+ }));
98
86
  };
99
87
 
100
88
  // [export]
@@ -3,39 +3,55 @@
3
3
  // [import]
4
4
 
5
5
  const api = require("../../api");
6
+ const cfgcode = require("../../config/code");
7
+ const error = require("../../util/error");
8
+ const given = require("../../parser/given");
6
9
  const links = require("../../util/links");
7
10
  const phrase = require("../../phrase");
8
- const project = require("../../parser/project");
9
11
  const s3 = require("../../util/s3");
10
12
 
13
+ // [use]
14
+
15
+ const ArcError = error.ArcError;
16
+
11
17
  // [function]
12
18
 
13
- const uploadBuild = function (
14
- current,
15
- artifact,
16
- { id, version },
17
- onUploadProgress,
18
- cb,
19
- ) {
19
+ // asks the API for an upload link, then sends the zip to it, and resolves
20
+ // with the upload.
21
+ const uploadBuild = function (current, artifact, { id, version }, onProgress) {
20
22
  const form = new FormData();
21
23
  form.append("artifact", id);
22
24
  form.append("version", version);
23
25
  form.append("sha256", artifact.sha256);
24
26
  form.append("size", artifact.size);
25
- // api
26
27
  return api
27
28
  .postUpload(form, null, api.auth(current?.session?.access_token))
28
29
  .then(
29
- (upload) => {
30
- s3.upload(upload.upload_url, artifact, onUploadProgress, (err) =>
31
- cb(err, upload),
32
- );
33
- },
30
+ (upload) =>
31
+ Promise.all([
32
+ upload,
33
+ s3.upload(upload.upload_url, artifact, onProgress),
34
+ ]),
34
35
  (err) => {
35
- // the API refuses a game that was deleted or is another account's.
36
- cb(err.status === 403 ? new Error(phrase.gameUnavailable(id)) : err);
36
+ // the API refuses a game that was deleted or is another account's,
37
+ // and the same version uploaded again within 5 minutes.
38
+ if (err.status === 403) {
39
+ throw new ArcError(
40
+ cfgcode.GAME_UNAVAILABLE,
41
+ phrase.gameUnavailable(id),
42
+ );
43
+ }
44
+ if (err.status === 429) {
45
+ throw new ArcError(
46
+ cfgcode.UPLOAD_RATE_LIMITED,
47
+ phrase.uploadRateLimited(version),
48
+ { cause: err },
49
+ );
50
+ }
51
+ throw err;
37
52
  },
38
- );
53
+ )
54
+ .then(([upload]) => upload);
39
55
  };
40
56
 
41
57
  // [function]
@@ -43,37 +59,27 @@ const uploadBuild = function (
43
59
  const main = function (
44
60
  current,
45
61
  artifact,
46
- { root },
62
+ conf,
47
63
  { onDeployBegin, onUploadProgress },
48
64
  ) {
49
65
  if (onDeployBegin) {
50
- onDeployBegin({ root });
66
+ onDeployBegin({ root: conf.root });
51
67
  }
52
- return project(root).then(
53
- ({ game }) =>
54
- new Promise((resolve, reject) => {
55
- uploadBuild(
56
- current,
57
- artifact,
58
- game,
59
- onUploadProgress,
60
- (err, upload) => {
61
- if (err) {
62
- reject(err);
63
- return;
64
- }
65
- // a direct deploy's release shares its upload's id, which
66
- // `--wait` follows.
67
- resolve({
68
- ...links.published(game),
69
- game: game.id,
70
- release: upload.id,
71
- version: { to: game.version },
72
- });
73
- },
74
- );
75
- }),
76
- );
68
+ return given(conf)
69
+ .then(({ game }) =>
70
+ Promise.all([
71
+ game,
72
+ uploadBuild(current, artifact, game, onUploadProgress),
73
+ ]),
74
+ )
75
+ .then(([game, upload]) => ({
76
+ ...links.published(game),
77
+ game: game.id,
78
+ // a direct deploy's release shares its upload's id, which `--wait`
79
+ // follows.
80
+ release: upload.id,
81
+ version: { to: game.version },
82
+ }));
77
83
  };
78
84
 
79
85
  // [export]
@@ -19,7 +19,7 @@ const sleep = delay.sleep;
19
19
  const INTERVAL = time.s(2);
20
20
  const FINAL = new Set(["failed", "published"]);
21
21
  const SELECT =
22
- "id,status,reason,message,build,source,version,channel,created_at";
22
+ "id,game,status,reason,message,build,source,version,channel,created_at";
23
23
  // a git release is found by version among the newest few; one from before
24
24
  // this deploy (a tag deleted and pushed again) is left out, allowing for
25
25
  // the machine's clock being off.
@@ -44,16 +44,17 @@ const studioUrl = function (game) {
44
44
  // the repository's GitHub Actions page, from origin's URL, or null when
45
45
  // origin isn't on GitHub.
46
46
  const actionsUrl = function (root) {
47
- return new Promise((resolve) => {
48
- git(root, ["remote", "get-url", "origin"], (err, stdout) => {
49
- const match = err
50
- ? null
51
- : /github\.com[:/]([^/\s]+)\/([^/\s]+?)(?:\.git)?\s*$/u.exec(stdout);
52
- resolve(
53
- match ? `https://github.com/${match[1]}/${match[2]}/actions` : null,
47
+ return git(root, ["remote", "get-url", "origin"]).then(
48
+ (stdout) => {
49
+ const match = /github\.com[:/]([^/\s]+)\/([^/\s]+?)(?:\.git)?\s*$/u.exec(
50
+ stdout,
54
51
  );
55
- });
56
- });
52
+ return match
53
+ ? `https://github.com/${match[1]}/${match[2]}/actions`
54
+ : null;
55
+ },
56
+ () => null,
57
+ );
57
58
  };
58
59
 
59
60
  // [function]
@@ -66,18 +67,26 @@ const findDirect = function (id, headers) {
66
67
  // [function]
67
68
 
68
69
  // a git deploy's release: GitHub's, for this version, made since the tag was
69
- // pushed. Null until GitHub Actions has built it.
70
- const findGit = function (game, version, since, headers) {
70
+ // pushed. Null until GitHub Actions has built it. It's looked for among the
71
+ // account's releases, which include one with no game: a repository that
72
+ // isn't connected fails that way (repo_not_connected). A token's account
73
+ // isn't known, so it looks among the game's.
74
+ const findGit = function ({ account, game, headers, since, version }) {
75
+ const newest = account
76
+ ? () => api.accountReleases(account, { limit: 20, select: SELECT }, headers)
77
+ : () =>
78
+ api.gameReleases(game, { limit: 10, select: SELECT, version }, headers);
71
79
  return () =>
72
- api
73
- .gameReleases(game, { limit: 10, select: SELECT, version }, headers)
74
- .then(
75
- (rows) =>
76
- (rows ?? []).find(
77
- (r) =>
78
- r.source === "github" && Date.parse(r.created_at) >= since - SKEW,
79
- ) ?? null,
80
- );
80
+ newest().then(
81
+ (rows) =>
82
+ (rows ?? []).find(
83
+ (r) =>
84
+ r.source === "github" &&
85
+ r.version === version &&
86
+ (r.game === game || r.game === null) &&
87
+ Date.parse(r.created_at) >= since - SKEW,
88
+ ) ?? null,
89
+ );
81
90
  };
82
91
 
83
92
  // [function]
@@ -127,7 +136,13 @@ const main = function (
127
136
  const headers = api.auth(current?.session?.access_token);
128
137
  const game = deployed.game;
129
138
  const find = isGit
130
- ? findGit(game, deployed.version.to, since, headers)
139
+ ? findGit({
140
+ account: current?.user?.id ?? null,
141
+ game,
142
+ headers,
143
+ since,
144
+ version: deployed.version.to,
145
+ })
131
146
  : findDirect(deployed.release, headers);
132
147
  return Promise.all([
133
148
  poll(find, timeout, onRelease),
@@ -11,12 +11,16 @@ const fs = require("fs-extra");
11
11
 
12
12
  // [import]
13
13
 
14
+ const cfgcode = require("../../config/code");
14
15
  const cfgproduct = require("../../config/product");
16
+ const env = require("../../parser/env");
17
+ const error = require("../../util/error");
15
18
  const phrase = require("../../phrase");
16
19
  const TOML = require("../../util/toml");
17
20
 
18
21
  // [use]
19
22
 
23
+ const ArcError = error.ArcError;
20
24
  const join = path.join;
21
25
  const pathExists = fs.pathExists;
22
26
  const randomUUID = crypto.randomUUID;
@@ -34,12 +38,23 @@ const parseToml = function (buf) {
34
38
  try {
35
39
  return TOML.parse(buf);
36
40
  } catch (ex) {
37
- throw new Error(phrase.unableToParse(MANIFEST, ex.message), { cause: ex });
41
+ throw new ArcError(
42
+ cfgcode.PARSE_FAILED,
43
+ phrase.unableToParse(MANIFEST, ex.message),
44
+ { cause: ex },
45
+ );
38
46
  }
39
47
  };
40
48
 
41
49
  // [function]
42
50
 
51
+ // the version the project's package.json gives, if any.
52
+ const detectVersion = function (dest) {
53
+ return env(dest).then(({ game }) => game?.version ?? null);
54
+ };
55
+
56
+ // [function]
57
+
43
58
  const pickVersion = function (version, envVersion) {
44
59
  if (version) {
45
60
  return { version };
@@ -57,10 +72,12 @@ const readManifest = function (file) {
57
72
 
58
73
  // [function]
59
74
 
60
- const patchGame = function (envVersion, game = {}) {
75
+ // a new project always gets its own id: a template's, such as an existing
76
+ // game's, belongs to that game.
77
+ const patchGame = function (envVersion, game = {}, { fresh = false } = {}) {
61
78
  const { id, version, ...rest } = game;
62
79
  return {
63
- id: id ?? randomUUID(),
80
+ id: fresh ? randomUUID() : (id ?? randomUUID()),
64
81
  ...pickVersion(version, envVersion),
65
82
  ...rest,
66
83
  };
@@ -77,11 +94,11 @@ const shouldWrite = function (manifest, next) {
77
94
 
78
95
  // [function]
79
96
 
80
- const prepare = function (dest, envVersion) {
97
+ const prepare = function (dest, envVersion, opts) {
81
98
  const file = join(dest, MANIFEST);
82
99
  return readManifest(file).then((manifest) => {
83
100
  const { game, ...rest } = manifest;
84
- const next = { game: patchGame(envVersion, game), ...rest };
101
+ const next = { game: patchGame(envVersion, game, opts), ...rest };
85
102
  if (shouldWrite(manifest, next)) {
86
103
  return writeFile(file, TOML.stringify(next), "utf8");
87
104
  }
@@ -91,4 +108,4 @@ const prepare = function (dest, envVersion) {
91
108
 
92
109
  // [export]
93
110
 
94
- module.exports = { prepare };
111
+ module.exports = { detectVersion, prepare };
@@ -6,22 +6,16 @@ const path = require("node:path");
6
6
 
7
7
  // [import]
8
8
 
9
- const env = require("../../parser/env");
10
9
  const manifest = require("./manifest");
11
10
 
12
11
  // [use]
13
12
 
13
+ const detectVersion = manifest.detectVersion;
14
14
  const prepare = manifest.prepare;
15
15
  const resolve = path.resolve;
16
16
 
17
17
  // [function]
18
18
 
19
- const detectVersion = function (dest) {
20
- return env(dest).then(({ game }) => game?.version ?? null);
21
- };
22
-
23
- // [function]
24
-
25
19
  // an existing project: add a manifest, and copy nothing from the template.
26
20
  const main = function ({ project, root }, { onInitBegin }) {
27
21
  const dest = resolve(root, project);
@@ -11,12 +11,15 @@ const fs = require("fs-extra");
11
11
 
12
12
  // [import]
13
13
 
14
- const env = require("../../parser/env");
14
+ const cfgcode = require("../../config/code");
15
+ const error = require("../../util/error");
15
16
  const manifest = require("./manifest");
16
17
  const phrase = require("../../phrase");
17
18
 
18
19
  // [use]
19
20
 
21
+ const ArcError = error.ArcError;
22
+ const detectVersion = manifest.detectVersion;
20
23
  const join = path.join;
21
24
  const prepare = manifest.prepare;
22
25
  const remove = fs.remove;
@@ -34,12 +37,6 @@ const removeLicense = function (dest) {
34
37
 
35
38
  // [function]
36
39
 
37
- const detectVersion = function (dest) {
38
- return env(dest).then(({ game }) => game?.version ?? null);
39
- };
40
-
41
- // [function]
42
-
43
40
  const scaffold = function (src, dest, { onInitBegin }) {
44
41
  if (onInitBegin) {
45
42
  onInitBegin({ dest, src });
@@ -47,7 +44,13 @@ const scaffold = function (src, dest, { onInitBegin }) {
47
44
  return degit(src, { cache: false, force: true, verbose: false })
48
45
  .clone(dest)
49
46
  .catch((ex) => {
50
- throw new Error(phrase.templateUnavailable(src), { cause: ex });
47
+ throw new ArcError(
48
+ cfgcode.TEMPLATE_UNAVAILABLE,
49
+ phrase.templateUnavailable(src),
50
+ {
51
+ cause: ex,
52
+ },
53
+ );
51
54
  });
52
55
  };
53
56
 
@@ -59,7 +62,7 @@ const main = function ({ project, root, template }, handler) {
59
62
  return scaffold(template, dest, handler)
60
63
  .then(() => removeLicense(dest))
61
64
  .then(() => detectVersion(dest))
62
- .then((version) => prepare(dest, version))
65
+ .then((version) => prepare(dest, version, { fresh: true }))
63
66
  .then(() => ({ path: dest, template }));
64
67
  };
65
68
 
@@ -7,13 +7,16 @@ const crypto = require("node:crypto");
7
7
  // [import]
8
8
 
9
9
  const api = require("../../api");
10
+ const cfgcode = require("../../config/code");
10
11
  const cfgurl = require("../../config/url");
11
12
  const delay = require("../../util/delay");
13
+ const error = require("../../util/error");
12
14
  const phrase = require("../../phrase");
13
15
  const time = require("../../util/time");
14
16
 
15
17
  // [use]
16
18
 
19
+ const ArcError = error.ArcError;
17
20
  const poll = delay.poll;
18
21
  const randomUUID = crypto.randomUUID;
19
22
 
@@ -24,7 +27,8 @@ const POLL = {
24
27
  factor: 1.5,
25
28
  interval: time.s(15),
26
29
  maxDelay: time.s(5),
27
- timeoutError: phrase.loginExpired(),
30
+ timeoutError: () =>
31
+ new ArcError(cfgcode.LOGIN_EXPIRED, phrase.loginExpired()),
28
32
  };
29
33
 
30
34
  // [function]
@@ -67,9 +71,9 @@ const pollFlow = function (value, onLoginPoll) {
67
71
  case "completed":
68
72
  return data;
69
73
  case "failed":
70
- throw new Error(phrase.loginFailed());
74
+ throw new ArcError(cfgcode.LOGIN_FAILED, phrase.loginFailed());
71
75
  default:
72
- throw new Error(phrase.loginExpired());
76
+ throw new ArcError(cfgcode.LOGIN_EXPIRED, phrase.loginExpired());
73
77
  }
74
78
  });
75
79
  };
@@ -2,11 +2,14 @@
2
2
 
3
3
  // [import]
4
4
 
5
+ const cfgcode = require("../../config/code");
6
+ const error = require("../../util/error");
5
7
  const phrase = require("../../phrase");
6
8
  const size = require("../../util/size");
7
9
 
8
10
  // [use]
9
11
 
12
+ const ArcError = error.ArcError;
10
13
  const toMb = size.toMb;
11
14
 
12
15
  // [declare]
@@ -25,16 +28,23 @@ const MAX_TOTAL_SIZE = size.mb(256);
25
28
  const check = function (entries) {
26
29
  const large = entries.find((entry) => entry.size > MAX_FILE_SIZE);
27
30
  if (large) {
28
- throw new Error(
31
+ throw new ArcError(
32
+ cfgcode.PACKAGE_FILE_TOO_LARGE,
29
33
  phrase.packageFileTooLarge(large.name, toMb(MAX_FILE_SIZE)),
30
34
  );
31
35
  }
32
36
  if (entries.length > MAX_FILES) {
33
- throw new Error(phrase.packageTooManyFiles(MAX_FILES));
37
+ throw new ArcError(
38
+ cfgcode.PACKAGE_TOO_MANY_FILES,
39
+ phrase.packageTooManyFiles(MAX_FILES),
40
+ );
34
41
  }
35
42
  const total = entries.reduce((sum, entry) => sum + entry.size, 0);
36
43
  if (total > MAX_TOTAL_SIZE) {
37
- throw new Error(phrase.packageUncompressedTooLarge(toMb(MAX_TOTAL_SIZE)));
44
+ throw new ArcError(
45
+ cfgcode.PACKAGE_UNCOMPRESSED_TOO_LARGE,
46
+ phrase.packageUncompressedTooLarge(toMb(MAX_TOTAL_SIZE)),
47
+ );
38
48
  }
39
49
  };
40
50
 
@@ -10,15 +10,18 @@ const fs = require("fs-extra");
10
10
 
11
11
  // [import]
12
12
 
13
+ const cfgcode = require("../../config/code");
13
14
  const cfgproduct = require("../../config/product");
15
+ const error = require("../../util/error");
16
+ const given = require("../../parser/given");
14
17
  const limits = require("./limits");
15
18
  const phrase = require("../../phrase");
16
- const project = require("../../parser/project");
17
19
  const size = require("../../util/size");
18
20
  const Zip = require("../../util/zip");
19
21
 
20
22
  // [use]
21
23
 
24
+ const ArcError = error.ArcError;
22
25
  const parse = path.parse;
23
26
  const pathExists = fs.pathExists;
24
27
  const resolve = path.resolve;
@@ -43,10 +46,16 @@ const SIZE_PARTIAL = size.mb(64);
43
46
 
44
47
  const verifyPartial = function (zip) {
45
48
  if (!zip.has(MAIN)) {
46
- throw new Error(phrase.packageFileMissing(MAIN));
49
+ throw new ArcError(
50
+ cfgcode.PACKAGE_INDEX_MISSING,
51
+ phrase.packageFileMissing(MAIN),
52
+ );
47
53
  }
48
54
  if (zip.stat().size > SIZE_PARTIAL) {
49
- throw new Error(phrase.packageTooLarge(size.toMb(SIZE_PARTIAL)));
55
+ throw new ArcError(
56
+ cfgcode.PACKAGE_TOO_LARGE,
57
+ phrase.packageTooLarge(size.toMb(SIZE_PARTIAL)),
58
+ );
50
59
  }
51
60
  return zip;
52
61
  };
@@ -65,7 +74,10 @@ const pack = function (name, game, { inputPath, mediaPath, outputPath }) {
65
74
  return pathExists(inputPath)
66
75
  .then((existsInput) => {
67
76
  if (!existsInput) {
68
- throw new Error(phrase.unableToFind(inputPath));
77
+ throw new ArcError(
78
+ cfgcode.PATH_NOT_FOUND,
79
+ phrase.unableToFind(inputPath),
80
+ );
69
81
  }
70
82
  zip.addDir(inputPath);
71
83
  return zip;
@@ -88,8 +100,9 @@ const pack = function (name, game, { inputPath, mediaPath, outputPath }) {
88
100
 
89
101
  // [function]
90
102
 
91
- const main = function ({ input, output, root }) {
92
- return project(root).then(({ game, workflow }) => {
103
+ const main = function ({ input, output, ...conf }) {
104
+ const root = conf.root;
105
+ return given(conf).then(({ game, workflow }) => {
93
106
  const name = game.handle ?? game.id;
94
107
  const file = output ?? `${OUTPUT}/${name}-v${game.version}.zip`;
95
108
  return pack(parse(file).name, game, {