arcadible 0.1.45 → 0.1.46

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 (53) hide show
  1. package/bin/arc-build.js +9 -17
  2. package/bin/arc-deploy.js +20 -36
  3. package/bin/arc-init.js +6 -16
  4. package/bin/arc-inspect.js +3 -7
  5. package/bin/arc-login.js +5 -5
  6. package/bin/arc-logout.js +4 -8
  7. package/bin/arc-open.js +7 -20
  8. package/bin/arc-pack.js +4 -14
  9. package/bin/arc-play.js +6 -20
  10. package/bin/arc-validate.js +8 -17
  11. package/bin/arc-whoami.js +5 -9
  12. package/bin/arc.js +7 -2
  13. package/package.json +1 -1
  14. package/src/account.js +5 -5
  15. package/src/action/build/tool.js +8 -15
  16. package/src/action/deploy/tool-git.js +1 -6
  17. package/src/action/deploy/wait.js +5 -11
  18. package/src/action/init/manifest.js +9 -37
  19. package/src/action/login/flow.js +3 -10
  20. package/src/action/open/tool.js +2 -1
  21. package/src/action/pack/limits.js +4 -6
  22. package/src/action/pack/tool.js +6 -5
  23. package/src/action/play/tool.js +3 -3
  24. package/src/action/predeploy/ac.js +28 -43
  25. package/src/action/predeploy/git.js +4 -4
  26. package/src/action/predeploy/pm.js +1 -1
  27. package/src/action/predeploy/published.js +4 -4
  28. package/src/action/predeploy/tool-git.js +10 -10
  29. package/src/action/validate/unknown.js +11 -10
  30. package/src/config/file.js +23 -0
  31. package/src/config/limit.js +21 -0
  32. package/src/parser/env.js +0 -2
  33. package/src/parser/manifest.js +15 -13
  34. package/src/parser/media.js +9 -11
  35. package/src/parser/project.js +31 -9
  36. package/src/phrase.js +168 -39
  37. package/src/schema/game.js +65 -42
  38. package/src/schema/workflow.js +7 -8
  39. package/src/util/argv.js +27 -1
  40. package/src/util/command.js +18 -0
  41. package/src/util/delay.js +5 -21
  42. package/src/util/doc.js +4 -13
  43. package/src/util/links.js +17 -2
  44. package/src/util/media.js +2 -2
  45. package/src/util/objs.js +8 -1
  46. package/src/util/output.js +24 -2
  47. package/src/util/rc.js +2 -2
  48. package/src/util/request.js +1 -1
  49. package/src/util/s3.js +1 -1
  50. package/src/util/schema.js +9 -3
  51. package/src/util/uris.js +1 -1
  52. package/src/util/zip.js +2 -2
  53. package/src/config/doc.js +0 -7
@@ -2,10 +2,6 @@
2
2
 
3
3
  // [import]
4
4
 
5
- const semver = require("semver");
6
-
7
- // [import]
8
-
9
5
  const cfgcode = require("../../config/code");
10
6
  const error = require("../../util/error");
11
7
  const given = require("../../parser/given");
@@ -18,14 +14,13 @@ const repo = require("../predeploy/git");
18
14
  const ArcError = error.ArcError;
19
15
  const failWith = repo.failWith;
20
16
  const git = repo.git;
21
- const prereleaseSemver = semver.prerelease;
22
17
  const tagOf = repo.tagOf;
23
18
 
24
19
  // [function]
25
20
 
26
21
  const createTag = function (root, version) {
27
22
  const message =
28
- prereleaseSemver(version) === null
23
+ links.channel(version) === links.PRODUCTION
29
24
  ? phrase.versionRelease(version)
30
25
  : phrase.versionPrerelease(version);
31
26
  return git(root, ["tag", "-a", tagOf(version), "-m", message]).then(
@@ -3,8 +3,8 @@
3
3
  // [import]
4
4
 
5
5
  const api = require("../../api");
6
- const cfguri = require("../../config/uri");
7
6
  const delay = require("../../util/delay");
7
+ const links = require("../../util/links");
8
8
  const repo = require("../predeploy/git");
9
9
  const time = require("../../util/time");
10
10
 
@@ -12,6 +12,7 @@ const time = require("../../util/time");
12
12
 
13
13
  const git = repo.git;
14
14
  const sleep = delay.sleep;
15
+ const studioUrl = links.studioUrl;
15
16
 
16
17
  // [declare]
17
18
 
@@ -34,13 +35,6 @@ const transient = function (err) {
34
35
 
35
36
  // [function]
36
37
 
37
- // where a developer sees the release in Studio.
38
- const studioUrl = function (game) {
39
- return `${cfguri.HOST_HTTP_STUDIO}/games/${game}/builds`;
40
- };
41
-
42
- // [function]
43
-
44
38
  // the repository's GitHub Actions page, from origin's URL, or null when
45
39
  // origin isn't on GitHub.
46
40
  const actionsUrl = function (root) {
@@ -95,7 +89,7 @@ const findGit = function ({ account, game, headers, since, version }) {
95
89
  // checks the release until it's published or failed, or `timeout` seconds
96
90
  // pass. Resolves { release, timedOut }; `release` is null when it was never
97
91
  // found. `onRelease` hears each release it finds.
98
- const poll = function (find, timeout, onRelease) {
92
+ const pollRelease = function (find, timeout, onRelease) {
99
93
  const deadline = Date.now() + timeout * 1000;
100
94
  // one check, then the next, each after the last: `last` is the newest
101
95
  // release found, kept through a check that fails for a moment.
@@ -150,12 +144,12 @@ const main = function (
150
144
  })
151
145
  : findDirect(deployed.release, headers);
152
146
  return Promise.all([
153
- poll(find, timeout, onRelease),
147
+ pollRelease(find, timeout, onRelease),
154
148
  isGit ? actionsUrl(root) : null,
155
149
  ]).then(([{ release, timedOut }, actions]) => ({
156
150
  actions,
157
151
  release,
158
- studio: owns ? studioUrl(game) : null,
152
+ studio: owns ? studioUrl(game, "builds") : null,
159
153
  timedOut,
160
154
  }));
161
155
  };
@@ -11,40 +11,20 @@ const fs = require("fs-extra");
11
11
 
12
12
  // [import]
13
13
 
14
- const cfgcode = require("../../config/code");
15
- const cfgproduct = require("../../config/product");
14
+ const cfgfile = require("../../config/file");
16
15
  const env = require("../../parser/env");
17
- const error = require("../../util/error");
18
- const phrase = require("../../phrase");
16
+ const manifest = require("../../parser/manifest");
19
17
  const TOML = require("../../util/toml");
20
18
 
21
19
  // [use]
22
20
 
23
- const ArcError = error.ArcError;
24
21
  const join = path.join;
25
- const pathExists = fs.pathExists;
26
22
  const randomUUID = crypto.randomUUID;
27
- const readFile = fs.promises.readFile;
28
23
  const writeFile = fs.promises.writeFile;
29
24
 
30
25
  // [declare]
31
26
 
32
- const HANDLE = cfgproduct.handle;
33
- const MANIFEST = `${HANDLE}.toml`;
34
-
35
- // [function]
36
-
37
- const parseToml = function (buf) {
38
- try {
39
- return TOML.parse(buf);
40
- } catch (ex) {
41
- throw new ArcError(
42
- cfgcode.PARSE_FAILED,
43
- phrase.unableToParse(MANIFEST, ex.message),
44
- { cause: ex },
45
- );
46
- }
47
- };
27
+ const MANIFEST = cfgfile.MANIFEST;
48
28
 
49
29
  // [function]
50
30
 
@@ -64,14 +44,6 @@ const pickVersion = function (version, envVersion) {
64
44
 
65
45
  // [function]
66
46
 
67
- const readManifest = function (file) {
68
- return pathExists(file)
69
- .then((exists) => (exists ? readFile(file, "utf8") : null))
70
- .then((buf) => (buf === null ? {} : parseToml(buf)));
71
- };
72
-
73
- // [function]
74
-
75
47
  // a new project always gets its own id: a template's, such as an existing
76
48
  // game's, belongs to that game.
77
49
  const patchGame = function (envVersion, game = {}, { fresh = false } = {}) {
@@ -85,10 +57,10 @@ const patchGame = function (envVersion, game = {}, { fresh = false } = {}) {
85
57
 
86
58
  // [function]
87
59
 
88
- const shouldWrite = function (manifest, next) {
60
+ const shouldWrite = function (written, next) {
89
61
  return (
90
- manifest.game?.id !== next.game?.id ||
91
- manifest.game?.version !== next.game?.version
62
+ written.game?.id !== next.game?.id ||
63
+ written.game?.version !== next.game?.version
92
64
  );
93
65
  };
94
66
 
@@ -96,10 +68,10 @@ const shouldWrite = function (manifest, next) {
96
68
 
97
69
  const prepare = function (dest, envVersion, opts) {
98
70
  const file = join(dest, MANIFEST);
99
- return readManifest(file).then((manifest) => {
100
- const { game, ...rest } = manifest;
71
+ return manifest.read(dest).then((written) => {
72
+ const { game, ...rest } = written;
101
73
  const next = { game: patchGame(envVersion, game, opts), ...rest };
102
- if (shouldWrite(manifest, next)) {
74
+ if (shouldWrite(written, next)) {
103
75
  return writeFile(file, TOML.stringify(next), "utf8");
104
76
  }
105
77
  return null;
@@ -60,11 +60,8 @@ const createFlow = function ({ expiresAt, secret, type }, onLoginCreated) {
60
60
 
61
61
  // [function]
62
62
 
63
- const pollFlow = function (value, onLoginPoll) {
63
+ const pollFlow = function (value) {
64
64
  return flow(value).then(({ data, status }) => {
65
- if (onLoginPoll) {
66
- onLoginPoll({ status });
67
- }
68
65
  switch (status) {
69
66
  case "pending":
70
67
  return null;
@@ -83,7 +80,7 @@ const pollFlow = function (value, onLoginPoll) {
83
80
 
84
81
  // [function]
85
82
 
86
- const main = function ({ onLoginCreated, onLoginPoll }) {
83
+ const main = function ({ onLoginCreated }) {
87
84
  const expiresAt = new Date(Date.now() + time.m(10));
88
85
  const secret = randomUUID();
89
86
  return createFlow(
@@ -95,11 +92,7 @@ const main = function ({ onLoginCreated, onLoginPoll }) {
95
92
  onLoginCreated,
96
93
  ).then(({ id }) => {
97
94
  const timeout = Math.max(0, expiresAt.getTime() - Date.now() - time.s(15));
98
- return poll(
99
- (value) => pollFlow(value, onLoginPoll),
100
- { id, secret },
101
- { ...POLL, timeout },
102
- );
95
+ return poll(pollFlow, { id, secret }, { ...POLL, timeout });
103
96
  });
104
97
  };
105
98
 
@@ -7,8 +7,9 @@ const self = require("../../parser/self");
7
7
 
8
8
  // [function]
9
9
 
10
+ // the store page's path, which the app opens, and its URL on the web.
10
11
  const openUrl = function (id) {
11
- return id ? { page: links.storePage(id) } : null;
12
+ return id ? { page: links.storePage(id), url: links.storeUrl(id) } : null;
12
13
  };
13
14
 
14
15
  // [function]
@@ -3,6 +3,7 @@
3
3
  // [import]
4
4
 
5
5
  const cfgcode = require("../../config/code");
6
+ const cfglimit = require("../../config/limit");
6
7
  const error = require("../../util/error");
7
8
  const phrase = require("../../phrase");
8
9
  const size = require("../../util/size");
@@ -14,12 +15,9 @@ const toMb = size.toMb;
14
15
 
15
16
  // [declare]
16
17
 
17
- // the limits on a packed game, uncompressed, as Arcadible enforces them when
18
- // it publishes a release. They're checked here so a build that would fail
19
- // there fails before it's uploaded.
20
- const MAX_FILES = 10000;
21
- const MAX_FILE_SIZE = size.mb(64);
22
- const MAX_TOTAL_SIZE = size.mb(256);
18
+ const MAX_FILES = cfglimit.FILES;
19
+ const MAX_FILE_SIZE = cfglimit.FILE_SIZE;
20
+ const MAX_TOTAL_SIZE = cfglimit.TOTAL_SIZE;
23
21
 
24
22
  // [function]
25
23
 
@@ -11,6 +11,8 @@ const fs = require("fs-extra");
11
11
  // [import]
12
12
 
13
13
  const cfgcode = require("../../config/code");
14
+ const cfgfile = require("../../config/file");
15
+ const cfglimit = require("../../config/limit");
14
16
  const cfgproduct = require("../../config/product");
15
17
  const error = require("../../util/error");
16
18
  const given = require("../../parser/given");
@@ -28,19 +30,18 @@ const resolve = path.resolve;
28
30
 
29
31
  // [declare]
30
32
 
31
- const HANDLE = cfgproduct.handle;
32
33
  const TOOL = cfgproduct.tool;
33
34
 
34
35
  // [declare]
35
36
 
36
37
  const MAIN = "index.html";
37
- const MANIFEST = `${HANDLE}.json`;
38
- const MEDIA = `.${HANDLE}`;
38
+ const MANIFEST = cfgfile.PACKAGED;
39
+ const MEDIA = cfgfile.MEDIA;
39
40
  const OUTPUT = `.${TOOL}/package`;
40
41
 
41
42
  // [declare]
42
43
 
43
- const SIZE_PARTIAL = size.mb(64);
44
+ const SIZE_PARTIAL = cfglimit.ZIP_SIZE;
44
45
 
45
46
  // [function]
46
47
 
@@ -50,7 +51,7 @@ const verifyPartial = function (zip) {
50
51
  if (!zip.has(MAIN)) {
51
52
  throw new ArcError(
52
53
  cfgcode.PACKAGE_INDEX_MISSING,
53
- phrase.packageFileMissing(MAIN),
54
+ phrase.packageIndexMissing(MAIN),
54
55
  );
55
56
  }
56
57
  limits.check(zip.entries());
@@ -18,7 +18,7 @@ const ArcError = error.ArcError;
18
18
  // the game, `select`ed, or null when the API can't be reached. It rejects
19
19
  // when the game isn't on Arcadible: its builds can't say, since a game that
20
20
  // doesn't exist has none.
21
- const lookup = function (id, select) {
21
+ const findGame = function (id, select) {
22
22
  return api.discoverGame(id, { select }).catch((err) => {
23
23
  if (err?.status === 404) {
24
24
  throw new ArcError(cfgcode.GAME_NOT_FOUND, phrase.gameNotFound(id));
@@ -68,7 +68,7 @@ const play = function (id, release) {
68
68
  if (!id) {
69
69
  return Promise.resolve(null);
70
70
  }
71
- return lookup(id, "orientation").then((game) =>
71
+ return findGame(id, "orientation").then((game) =>
72
72
  target(id, game?.orientation, release),
73
73
  );
74
74
  };
@@ -85,7 +85,7 @@ const playSelf = function (root, release) {
85
85
  return [null, null];
86
86
  }
87
87
  return Promise.all([
88
- release ? null : lookup(id, "id"),
88
+ release ? null : findGame(id, "id"),
89
89
  target(id, game.orientation, release),
90
90
  ]);
91
91
  })
@@ -14,27 +14,26 @@ const ArcError = error.ArcError;
14
14
 
15
15
  // [function]
16
16
 
17
- // fails when the game is on Arcadible on another account: it was deleted
18
- // (the API doesn't find it, and the upload is refused), or it's someone
19
- // else's. A game that isn't on Arcadible yet passes.
17
+ // a lookup's failure, passed over when it's a 404: nothing there to refuse.
18
+ const ignoreMissing = function (err) {
19
+ if (err.status !== 404) {
20
+ throw err;
21
+ }
22
+ };
23
+
24
+ // [function]
25
+
26
+ // fails when the game is another account's. A game the API doesn't find
27
+ // passes: it isn't on Arcadible yet, or it was deleted, which the upload
28
+ // refuses.
20
29
  const ensureOwned = function (current, id) {
21
- return api.discoverGame(id).then(
22
- (game) => {
23
- // a token's account isn't known yet; the server checks ownership when
24
- // it authorizes the upload.
25
- if (!current?.token && game.account !== current?.user?.id) {
26
- throw new ArcError(
27
- cfgcode.GAME_UNAVAILABLE,
28
- phrase.gameUnavailable(id),
29
- );
30
- }
31
- },
32
- (err) => {
33
- if (err.status !== 404) {
34
- throw err;
35
- }
36
- },
37
- );
30
+ return api.discoverGame(id).then((game) => {
31
+ // a token's account isn't known yet; the server checks ownership when
32
+ // it authorizes the upload.
33
+ if (!current?.token && game.account !== current?.user?.id) {
34
+ throw new ArcError(cfgcode.GAME_UNAVAILABLE, phrase.gameUnavailable(id));
35
+ }
36
+ }, ignoreMissing);
38
37
  };
39
38
 
40
39
  // [function]
@@ -45,21 +44,14 @@ const ensureHandle = function ({ handle, id }) {
45
44
  if ((handle ?? null) === null) {
46
45
  return Promise.resolve(null);
47
46
  }
48
- return api.discoverGame(handle).then(
49
- (game) => {
50
- if (game.id !== id) {
51
- throw new ArcError(
52
- cfgcode.GAME_HANDLE_TAKEN,
53
- phrase.gameHandleTaken(handle),
54
- );
55
- }
56
- },
57
- (err) => {
58
- if (err.status !== 404) {
59
- throw err;
60
- }
61
- },
62
- );
47
+ return api.discoverGame(handle).then((game) => {
48
+ if (game.id !== id) {
49
+ throw new ArcError(
50
+ cfgcode.GAME_HANDLE_TAKEN,
51
+ phrase.gameHandleTaken(handle),
52
+ );
53
+ }
54
+ }, ignoreMissing);
63
55
  };
64
56
 
65
57
  // [function]
@@ -79,14 +71,7 @@ const ensureUnpublished = function (current, game) {
79
71
  const { id, version } = game;
80
72
  return api
81
73
  .gameBuild(id, version, null, api.auth(current?.session?.access_token))
82
- .then(
83
- () => published.refuse(id, version),
84
- (err) => {
85
- if (err.status !== 404) {
86
- throw err;
87
- }
88
- },
89
- )
74
+ .then(() => published.refuse(id, version), ignoreMissing)
90
75
  .then(() => game);
91
76
  };
92
77
 
@@ -7,6 +7,7 @@ const cp = require("node:child_process");
7
7
  // [import]
8
8
 
9
9
  const cfgcode = require("../../config/code");
10
+ const cfgfile = require("../../config/file");
10
11
  const cfgproduct = require("../../config/product");
11
12
  const error = require("../../util/error");
12
13
  const phrase = require("../../phrase");
@@ -20,8 +21,7 @@ const execFile = cp.execFile;
20
21
 
21
22
  const HANDLE = cfgproduct.handle;
22
23
  const PREFIX = `${HANDLE}/release/v`;
23
- // the release workflow, which builds the release on GitHub Actions.
24
- const WORKFLOW = ".github/workflows/arcadible-release.yml";
24
+ const WORKFLOW = cfgfile.WORKFLOW;
25
25
  // a remote's URL: `scheme://[user@]host/path`, or scp-like
26
26
  // `[user@]host:path`. A path on this machine is neither.
27
27
  const URL_REMOTE = /^[a-z][a-z0-9+.-]*:\/\/(?:[^@/\s]+@)?[^/\s]+\/(\S+)$/iu;
@@ -70,7 +70,7 @@ const tagOf = function (version) {
70
70
  const ensureClean = function (root) {
71
71
  return git(root, ["status", "--porcelain"]).then((stdout) => {
72
72
  if (stdout.trim()) {
73
- throw new ArcError(cfgcode.GIT_UNCOMMITTED, phrase.gitChangesPresent());
73
+ throw new ArcError(cfgcode.GIT_UNCOMMITTED, phrase.gitUncommitted());
74
74
  }
75
75
  });
76
76
  };
@@ -200,7 +200,7 @@ const ensureTagFree = function (root, version) {
200
200
  if (exists) {
201
201
  throw new ArcError(
202
202
  cfgcode.GIT_TAG_EXISTS,
203
- phrase.gitVersionExists(version),
203
+ phrase.gitTagExists(version),
204
204
  );
205
205
  }
206
206
  });
@@ -37,7 +37,7 @@ const ensureLockFile = function (root) {
37
37
  const has = (file) => existsSync(join(root, file));
38
38
  if (has("package.json") && !LOCK_FILES.some(has)) {
39
39
  return Promise.reject(
40
- new ArcError(cfgcode.LOCKFILE_MISSING, phrase.noLockFile()),
40
+ new ArcError(cfgcode.LOCKFILE_MISSING, phrase.lockfileMissing()),
41
41
  );
42
42
  }
43
43
  return Promise.resolve();
@@ -52,7 +52,7 @@ const next = function (version, { production, staging }) {
52
52
 
53
53
  // the error for a version that's already published, naming what's newest on
54
54
  // each channel and a version to bump to.
55
- const versionPublished = function (id, version) {
55
+ const versionExists = function (id, version) {
56
56
  return Promise.all([
57
57
  latest(id, links.PRODUCTION),
58
58
  latest(id, links.STAGING),
@@ -60,7 +60,7 @@ const versionPublished = function (id, version) {
60
60
  const found = { production, staging };
61
61
  return new ArcError(
62
62
  cfgcode.VERSION_EXISTS,
63
- phrase.versionPublished(version, {
63
+ phrase.versionExists(version, {
64
64
  ...found,
65
65
  next: next(version, found),
66
66
  }),
@@ -70,9 +70,9 @@ const versionPublished = function (id, version) {
70
70
 
71
71
  // [function]
72
72
 
73
- // rejects with versionPublished's error.
73
+ // rejects with versionExists's error.
74
74
  const refuse = function (id, version) {
75
- return versionPublished(id, version).then((err) => {
75
+ return versionExists(id, version).then((err) => {
76
76
  throw err;
77
77
  });
78
78
  };
@@ -4,10 +4,10 @@
4
4
 
5
5
  const api = require("../../api");
6
6
  const cfgcode = require("../../config/code");
7
- const cfguri = require("../../config/uri");
8
7
  const error = require("../../util/error");
9
8
  const git = require("./git");
10
9
  const given = require("../../parser/given");
10
+ const links = require("../../util/links");
11
11
  const phrase = require("../../phrase");
12
12
  const pm = require("./pm");
13
13
  const published = require("./published");
@@ -23,12 +23,13 @@ const ensureRoot = git.ensureRoot;
23
23
  const ensureTagFree = git.ensureTagFree;
24
24
  const ensureWorkflow = git.ensureWorkflow;
25
25
  const originRepo = git.originRepo;
26
+ const studioUrl = links.studioUrl;
26
27
 
27
28
  // [function]
28
29
 
29
30
  // a public lookup's answer: found (200, with its data), missing (404), or
30
31
  // null for any other answer, such as when the API can't be reached.
31
- const lookup = function (request) {
32
+ const answerOf = function (request) {
32
33
  return request.then(
33
34
  (data) => ({ data, status: 200 }),
34
35
  (err) => (err.status === 404 ? { status: 404 } : null),
@@ -45,8 +46,8 @@ const lookup = function (request) {
45
46
  // resolves with the game's owner, or null when Arcadible can't be reached.
46
47
  const ensurePublishable = function ({ handle, id, version }, handler) {
47
48
  return Promise.all([
48
- lookup(api.discoverGame(id)),
49
- lookup(api.discoverGameBuild(id, version)),
49
+ answerOf(api.discoverGame(id)),
50
+ answerOf(api.discoverGameBuild(id, version)),
50
51
  ]).then(([game, build]) => {
51
52
  if (game?.status === 404) {
52
53
  throw new ArcError(
@@ -55,10 +56,9 @@ const ensurePublishable = function ({ handle, id, version }, handler) {
55
56
  );
56
57
  }
57
58
  if (game === null || build === null) {
58
- if (handler.onLogInfo) {
59
- handler.onLogInfo({
59
+ if (handler.onWarning) {
60
+ handler.onWarning({
60
61
  code: cfgcode.WARNING.VERSION_UNCHECKED,
61
- level: "warn",
62
62
  message: phrase.versionUnchecked(version),
63
63
  });
64
64
  }
@@ -96,17 +96,17 @@ const ensureConnected = function (root, { handle, id }) {
96
96
  .then((found) => {
97
97
  repo = found;
98
98
  return repo
99
- ? lookup(api.discoverGameRepo(id, repo.owner, repo.name))
99
+ ? answerOf(api.discoverGameRepo(id, repo.owner, repo.name))
100
100
  : null;
101
101
  })
102
102
  .then((connected) => {
103
103
  if (connected?.status === 404) {
104
104
  throw new ArcError(
105
105
  cfgcode.REPO_NOT_CONNECTED,
106
- phrase.gameRepoMissing(
106
+ phrase.repoNotConnected(
107
107
  handle ?? id,
108
108
  `${repo.owner}/${repo.name}`,
109
- `${cfguri.HOST_HTTP_STUDIO}/games/${id}/repository`,
109
+ studioUrl(id, "repository"),
110
110
  ),
111
111
  );
112
112
  }
@@ -5,12 +5,15 @@
5
5
  const cfgcode = require("../../config/code");
6
6
  const game = require("../../schema/game");
7
7
  const manifest = require("../../parser/manifest");
8
+ const objs = require("../../util/objs");
9
+ const phrase = require("../../phrase");
8
10
  const strs = require("../../util/strs");
9
11
  const workflow = require("../../schema/workflow");
10
12
 
11
13
  // [use]
12
14
 
13
15
  const closest = strs.closest;
16
+ const isTable = objs.isTable;
14
17
  const WARNING = cfgcode.WARNING;
15
18
 
16
19
  // [function]
@@ -30,7 +33,7 @@ const known = function (root) {
30
33
  const warning = function (path, message, near) {
31
34
  return {
32
35
  code: WARNING.FIELD_UNKNOWN,
33
- details: near ? [`Did you mean: ${near}?`] : [],
36
+ details: near ? [phrase.didYouMean(near)] : [],
34
37
  link: null,
35
38
  message,
36
39
  path,
@@ -43,13 +46,11 @@ const warning = function (path, message, near) {
43
46
  // it is, in the table that takes it, if any.
44
47
  const untabled = function (fields, name) {
45
48
  const table = Object.keys(fields).find((each) => fields[each].includes(name));
46
- return warning(name, "Unknown field.", table ? `${table}.${name}` : null);
47
- };
48
-
49
- // [function]
50
-
51
- const isTable = function (value) {
52
- return value !== null && typeof value === "object" && !Array.isArray(value);
49
+ return warning(
50
+ name,
51
+ phrase.unknownField(),
52
+ table ? `${table}.${name}` : null,
53
+ );
53
54
  };
54
55
 
55
56
  // [function]
@@ -65,14 +66,14 @@ const main = function (root) {
65
66
  return Object.hasOwn(fields, table) ? [] : [untabled(fields, table)];
66
67
  }
67
68
  if (!Object.hasOwn(fields, table)) {
68
- return [warning(table, "Unknown table.", closest(table, tables))];
69
+ return [warning(table, phrase.unknownTable(), closest(table, tables))];
69
70
  }
70
71
  return Object.keys(values)
71
72
  .filter((field) => !fields[table].includes(field))
72
73
  .map((field) =>
73
74
  warning(
74
75
  `${table}.${field}`,
75
- "Unknown field.",
76
+ phrase.unknownField(),
76
77
  closest(field, fields[table]),
77
78
  ),
78
79
  );
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const cfgproduct = require("./product");
6
+
7
+ // [declare]
8
+
9
+ const HANDLE = cfgproduct.handle;
10
+ const MEDIA = `.${HANDLE}`;
11
+
12
+ // [export]
13
+
14
+ // the project's files Arcadible reads, relative to its root, and the
15
+ // manifest arc pack writes into the zip.
16
+ module.exports = {
17
+ GALLERY: `${MEDIA}/gallery`,
18
+ MANIFEST: `${HANDLE}.toml`,
19
+ MEDIA,
20
+ PACKAGED: `${HANDLE}.json`,
21
+ // the release workflow, which builds a `git` release on GitHub Actions.
22
+ WORKFLOW: `.github/workflows/${HANDLE}-release.yml`,
23
+ };
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ // [declare]
4
+
5
+ const MB = 1024 * 1024;
6
+
7
+ // [export]
8
+
9
+ // the limits on a packed game, as Arcadible enforces them when it publishes
10
+ // a release (docs: limits.md). arc pack checks them so a build that would
11
+ // fail there fails before it's uploaded.
12
+ module.exports = {
13
+ // the most files in the zip.
14
+ FILES: 10000,
15
+ // the largest file, uncompressed.
16
+ FILE_SIZE: 64 * MB,
17
+ // everything packed, uncompressed.
18
+ TOTAL_SIZE: 256 * MB,
19
+ // the zip of deploy_dir alone, compressed.
20
+ ZIP_SIZE: 64 * MB,
21
+ };
package/src/parser/env.js CHANGED
@@ -58,10 +58,8 @@ const parseJson = function (buf) {
58
58
  // (node_modules/.bin), and its prebuild and postbuild run, as they do
59
59
  // locally. npm runs the scripts of a pnpm or Yarn project too.
60
60
  const extract = function (conf) {
61
- /* eslint-disable camelcase */
62
61
  const game = { version: conf.version };
63
62
  const workflow = conf.scripts?.build ? { build: NPM_RUN_BUILD } : {};
64
- /* eslint-enable camelcase */
65
63
  return deepCompact({ game, workflow }) ?? {};
66
64
  };
67
65