arcadible 0.1.47 → 0.1.49

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 CHANGED
@@ -46,6 +46,7 @@ Your repository is the store page: `arcadible.toml` holds the game's settings, `
46
46
  | `arc inspect` | Prints the manifest Arcadible will see, or one value from it |
47
47
  | `arc build` | Runs your project's build command |
48
48
  | `arc pack` | Packages your project into the zip that `arc deploy` uploads |
49
+ | `arc serve` | Serves your game locally, framed in a play page, as Arcadible does |
49
50
  | `arc deploy` | Validates, builds, packages, and publishes, directly or through GitHub Actions |
50
51
  | `arc play` | Plays a deployed game from any channel or version |
51
52
  | `arc open` | Opens a game's store page |
@@ -39,6 +39,7 @@ const main = function (argv) {
39
39
  .command("validate", "Validate project")
40
40
  .command("build", "Build project")
41
41
  .command("pack", "Package project")
42
+ .command("serve", "Serve a game locally")
42
43
  .command("deploy", "Deploy project")
43
44
  .command("open", "Open a game's store page")
44
45
  .command("play", "Play a game")
package/bin/arc-init.js CHANGED
@@ -149,7 +149,7 @@ const main = function (argv) {
149
149
  default: conf.template,
150
150
  describe: [
151
151
  "Project template",
152
- "Public GitHub repository (format: user/repo).",
152
+ "Public GitHub repository, or a folder in one (user/repo[/folder]).",
153
153
  ].join("\n"),
154
154
  type: "string",
155
155
  })
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // [import]
5
+
6
+ const path = require("node:path");
7
+
8
+ // [import]
9
+
10
+ const hideBin = require("yargs/helpers").hideBin;
11
+ const yargs = require("yargs/yargs");
12
+
13
+ // [import]
14
+
15
+ const args = require("../src/util/argv");
16
+ const cfgcode = require("../src/config/code");
17
+ const cfgpackage = require("../src/config/package");
18
+ const cfgproduct = require("../src/config/product");
19
+ const cfgurl = require("../src/config/url");
20
+ const error = require("../src/util/error");
21
+ const log = require("../src/util/log");
22
+ const output = require("../src/util/output");
23
+ const phrase = require("../src/phrase");
24
+ const rc = require("../src/util/rc");
25
+ const serve = require("../src/cli/serve");
26
+ const shell = require("../src/util/shell");
27
+ const spinner = require("../src/util/spinner");
28
+
29
+ // [use]
30
+
31
+ const abort = output.abort;
32
+ const ArcError = error.ArcError;
33
+ const findRc = rc.findRc;
34
+ const launch = shell.launch;
35
+ const launched = output.launched;
36
+ const relative = path.relative;
37
+ const resolve = path.resolve;
38
+ const SpinnerWheel = spinner.SpinnerWheel;
39
+
40
+ // [declare]
41
+
42
+ const COMMAND = "serve";
43
+ const PORT = 4100;
44
+ const TOOL = cfgproduct.tool;
45
+ const VERSION = cfgpackage.version;
46
+
47
+ // [define]
48
+
49
+ const conf = {
50
+ build: true,
51
+ open: false,
52
+ port: PORT,
53
+ quiet: false,
54
+ ...findRc(TOOL, COMMAND, process.argv),
55
+ };
56
+
57
+ // [function]
58
+
59
+ // fails unless the port is one a server can listen on.
60
+ const checkPort = function (port) {
61
+ if (Number.isInteger(port) && port > 0 && port < 65536) {
62
+ return Promise.resolve();
63
+ }
64
+ return Promise.reject(
65
+ new ArcError(cfgcode.ARGUMENT_INVALID, phrase.portInvalid()),
66
+ );
67
+ };
68
+
69
+ // [function]
70
+
71
+ // the page --url names, as a URL, or a failure when it isn't an http one.
72
+ const checkUrl = function (url) {
73
+ if (url === null || typeof url === "undefined") {
74
+ return Promise.resolve(null);
75
+ }
76
+ try {
77
+ const parsed = new URL(String(url));
78
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") {
79
+ return Promise.resolve(parsed.href);
80
+ }
81
+ } catch (ex) {
82
+ // not a URL at all: refused below, as one that isn't http.
83
+ }
84
+ return Promise.reject(
85
+ new ArcError(cfgcode.ARGUMENT_INVALID, phrase.urlInvalid()),
86
+ );
87
+ };
88
+
89
+ // [function]
90
+
91
+ // warns once for each of Arcadible's files that's a kept copy, or can't be
92
+ // had, rather than on every request.
93
+ const remoteWarnings = function ({ quiet }) {
94
+ const warned = new Set();
95
+ return function (name, file, ex) {
96
+ const message = ex
97
+ ? phrase.remoteUnreachable(name)
98
+ : file?.kept && phrase.remoteKept(name, file.fetchedAt);
99
+ if (!message || quiet || warned.has(message)) {
100
+ return;
101
+ }
102
+ warned.add(message);
103
+ log.pw(message);
104
+ };
105
+ };
106
+
107
+ // [function]
108
+
109
+ const start = function (argv) {
110
+ const quiet = argv.quiet;
111
+ // `.arcrc` values are strings, and a flag that isn't a number is NaN.
112
+ const port = Number(argv.port);
113
+ const root = resolve(process.cwd(), argv.cwd);
114
+ const sw = new SpinnerWheel("Building... %s");
115
+ return checkPort(port)
116
+ .then(() => checkUrl(argv.url))
117
+ .then((url) =>
118
+ serve({
119
+ build: argv.build,
120
+ handler: {
121
+ onBuild: () => sw.start(quiet),
122
+ onReload() {
123
+ if (!quiet) {
124
+ log.p(phrase.reloaded());
125
+ }
126
+ },
127
+ onRemote: remoteWarnings({ quiet }),
128
+ },
129
+ port,
130
+ root,
131
+ url,
132
+ }),
133
+ )
134
+ .then((served) => {
135
+ sw.stop(true);
136
+ const stop = () => {
137
+ served.close();
138
+ process.exit(0);
139
+ };
140
+ process.once("SIGINT", stop);
141
+ process.once("SIGTERM", stop);
142
+ if (!quiet) {
143
+ log.result(phrase.served(served), [
144
+ ["Play:", `${served.page}/`],
145
+ [phrase.serveAlone(), served.game],
146
+ served.url
147
+ ? phrase.serveNextUrl()
148
+ : phrase.serveNext(relative(root, served.deployDir) || "."),
149
+ ]);
150
+ if (served.probed && !served.probed.reachable) {
151
+ log.nlpw(phrase.urlUnreachable(served.url));
152
+ } else if (served.probed && !served.probed.frameable) {
153
+ log.nlpw(phrase.urlNotFrameable(served.url, cfgurl.SERVE_URL));
154
+ }
155
+ }
156
+ return argv.open ? launch(`${served.page}/`) : false;
157
+ })
158
+ .then((where) => {
159
+ if (where !== false && !quiet) {
160
+ launched(where, `http://localhost:${port}/`);
161
+ }
162
+ })
163
+ .catch((ex) => abort(phrase.aborted(COMMAND), ex, { sw }));
164
+ };
165
+
166
+ // [function]
167
+
168
+ const main = function (argv) {
169
+ return start(
170
+ yargs(args.normalize(hideBin(argv)))
171
+ .scriptName(`${TOOL} ${COMMAND}`)
172
+ .usage("$0 [options]")
173
+ .option("port", {
174
+ alias: "p",
175
+ default: conf.port,
176
+ describe: "Port to serve on",
177
+ type: "number",
178
+ })
179
+ .option("build", {
180
+ default: conf.build,
181
+ describe: "Run the build command first",
182
+ type: "boolean",
183
+ })
184
+ .option("url", {
185
+ describe: "Frame another server's page instead of deploy_dir",
186
+ type: "string",
187
+ })
188
+ .option("open", {
189
+ alias: "o",
190
+ default: conf.open,
191
+ describe: "Open the play page in a web browser",
192
+ type: "boolean",
193
+ })
194
+ .option("quiet", { ...args.QUIET, default: conf.quiet })
195
+ .option("cwd", args.CWD)
196
+ .help("help")
197
+ .alias("help", "h")
198
+ .version(VERSION)
199
+ .alias("version", "v")
200
+ .strict()
201
+ .parse(),
202
+ );
203
+ };
204
+
205
+ // [start]
206
+
207
+ if (require.main === module) {
208
+ main(process.argv);
209
+ }
210
+
211
+ // [export]
212
+
213
+ module.exports = { main };
package/package.json CHANGED
@@ -11,6 +11,7 @@
11
11
  "arc-open": "bin/arc-open.js",
12
12
  "arc-pack": "bin/arc-pack.js",
13
13
  "arc-play": "bin/arc-play.js",
14
+ "arc-serve": "bin/arc-serve.js",
14
15
  "arc-validate": "bin/arc-validate.js",
15
16
  "arc-whoami": "bin/arc-whoami.js"
16
17
  },
@@ -28,10 +29,12 @@
28
29
  "ini": "^6.0.0",
29
30
  "jsdom": "^27.3.0",
30
31
  "marked": "^17.0.1",
32
+ "mime-types": "^3.0.2",
31
33
  "mp4box": "^2.3.0",
32
34
  "open": "^8.4.2",
33
35
  "pretty-toml": "^1.0.0",
34
36
  "qrcode-terminal": "^0.12.0",
37
+ "range-parser": "^1.3.0",
35
38
  "semver": "^7.7.3",
36
39
  "yargs": "^17.7.2",
37
40
  "zod": "^4.1.13"
@@ -52,6 +55,7 @@
52
55
  "./pack": "./src/cli/pack.js",
53
56
  "./play": "./src/cli/play.js",
54
57
  "./predeploy": "./src/cli/predeploy.js",
58
+ "./serve": "./src/cli/serve.js",
55
59
  "./validate": "./src/cli/validate.js",
56
60
  "./whoami": "./src/cli/whoami.js"
57
61
  },
@@ -75,5 +79,5 @@
75
79
  "scripts": {
76
80
  "test": "node --test \"test/**/*.test.js\""
77
81
  },
78
- "version": "0.1.47"
82
+ "version": "0.1.49"
79
83
  }
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ // arc serve: builds the project unless told not to, then serves deploy_dir
3
+ // framed in a local play page (src/serve), reloading the game when deploy_dir
4
+ // changes.
5
+
6
+ // [import]
7
+
8
+ const path = require("node:path");
9
+
10
+ // [import]
11
+
12
+ const fs = require("fs-extra");
13
+
14
+ // [import]
15
+
16
+ const buildTool = require("../build/tool");
17
+ const cfgcode = require("../../config/code");
18
+ const cfgproduct = require("../../config/product");
19
+ const cfguri = require("../../config/uri");
20
+ const commands = require("../../util/command");
21
+ const error = require("../../util/error");
22
+ const files = require("../../serve/files");
23
+ const given = require("../../parser/given");
24
+ const os = require("../../util/os");
25
+ const phrase = require("../../phrase");
26
+ const reload = require("../../serve/reload");
27
+ const remote = require("../../serve/remote");
28
+ const server = require("../../serve/server");
29
+
30
+ // [use]
31
+
32
+ const ArcError = error.ArcError;
33
+ const buildCommand = commands.buildCommand;
34
+ const configDir = os.configDir;
35
+ const dirFiles = files.dirFiles;
36
+ const join = path.join;
37
+ const pathExists = fs.pathExists;
38
+ const relative = path.relative;
39
+ const remoteFiles = remote.remoteFiles;
40
+ const resolvePath = path.resolve;
41
+ const serve = server.serve;
42
+ const watchDir = reload.watchDir;
43
+
44
+ // [declare]
45
+
46
+ // what build.channel says while served locally (docs: arc-serve.md).
47
+ const CHANNEL = "local";
48
+ // the saves of a game with no id yet, kept apart from any other's.
49
+ const UNNAMED = "local";
50
+
51
+ // [function]
52
+
53
+ // the headers a page needs for the play page to frame it: the play page is
54
+ // cross-origin isolated, so a cross-origin frame must be too, and allow it.
55
+ const FRAMEABLE = {
56
+ "cross-origin-embedder-policy": ["credentialless", "require-corp"],
57
+ "cross-origin-resource-policy": ["cross-origin"],
58
+ };
59
+
60
+ // [function]
61
+
62
+ // what `url` says when asked: `{ reachable, frameable }`, where `frameable`
63
+ // says whether it sends the headers the play page needs to frame it.
64
+ const probe = async function (url) {
65
+ try {
66
+ const res = await fetch(url, { method: "GET" });
67
+ await res.body?.cancel();
68
+ const frameable = Object.entries(FRAMEABLE).every(([name, values]) =>
69
+ values.includes(res.headers.get(name)?.trim().toLowerCase()),
70
+ );
71
+ return { frameable, reachable: true };
72
+ } catch (ex) {
73
+ return { frameable: false, reachable: false };
74
+ }
75
+ };
76
+
77
+ // [function]
78
+
79
+ // listens, turning a port in use into its code.
80
+ const listen = async function (options) {
81
+ try {
82
+ return await serve(options);
83
+ } catch (ex) {
84
+ if (ex.code === "EADDRINUSE") {
85
+ throw new ArcError(cfgcode.PORT_IN_USE, phrase.portInUse(options.port), {
86
+ cause: ex,
87
+ });
88
+ }
89
+ throw ex;
90
+ }
91
+ };
92
+
93
+ // [function]
94
+
95
+ // serves the project at `conf.root`. `conf.build` false skips the build, and
96
+ // `conf.url` frames that server's page instead of serving deploy_dir.
97
+ // `conf.handler` hears `onBuild()` as a build starts, `onRemote(name, file,
98
+ // error)` for each of Arcadible's files, and `onReload()` for each reload.
99
+ // resolves with `{ page, game, built, deployDir, url, probed, version,
100
+ // close() }`, where `probed` is what `conf.url` answered.
101
+ const main = async function (conf) {
102
+ const { port, root, url } = conf;
103
+ const handler = {
104
+ onBuild() {},
105
+ onReload() {},
106
+ onRemote() {},
107
+ ...conf.handler,
108
+ };
109
+ const project = await given(conf);
110
+ const game = project.game;
111
+ const options = {
112
+ info: {
113
+ channel: CHANNEL,
114
+ id: game.id ?? UNNAMED,
115
+ title: game.title ?? game.handle ?? cfgproduct.title,
116
+ version: game.version ?? null,
117
+ },
118
+ onRemote: handler.onRemote,
119
+ port,
120
+ remote: remoteFiles({
121
+ asset: cfguri.ARCADIBLE_ASSET,
122
+ dir: join(configDir(), cfgproduct.tool, "sdk"),
123
+ sdk: cfguri.ARCADIBLE_SDK,
124
+ }),
125
+ };
126
+ const version = game.version ?? null;
127
+ if (url) {
128
+ const served = await listen({ ...options, src: url });
129
+ const probed = await probe(url);
130
+ return { ...served, built: false, deployDir: null, probed, url, version };
131
+ }
132
+ const built =
133
+ conf.build !== false && Boolean(buildCommand(project.workflow.build));
134
+ if (built) {
135
+ handler.onBuild();
136
+ await buildTool({ project, root });
137
+ }
138
+ const deployDir = resolvePath(root, project.workflow.deploy_dir);
139
+ if (!(await pathExists(deployDir))) {
140
+ throw new ArcError(
141
+ cfgcode.DEPLOY_DIR_MISSING,
142
+ phrase.deployDirMissing(relative(root, deployDir) || "."),
143
+ );
144
+ }
145
+ const served = await listen({ ...options, files: dirFiles(deployDir) });
146
+ const stop = watchDir(deployDir, () => {
147
+ served.reload();
148
+ handler.onReload();
149
+ });
150
+ return {
151
+ ...served,
152
+ built,
153
+ close() {
154
+ stop();
155
+ served.close();
156
+ },
157
+ deployDir,
158
+ probed: null,
159
+ url: null,
160
+ version,
161
+ };
162
+ };
163
+
164
+ // [export]
165
+
166
+ module.exports = main;
package/src/cli/index.js CHANGED
@@ -12,6 +12,7 @@ const open = require("./open");
12
12
  const pack = require("./pack");
13
13
  const play = require("./play");
14
14
  const predeploy = require("./predeploy");
15
+ const serve = require("./serve");
15
16
  const validate = require("./validate");
16
17
  const whoami = require("./whoami");
17
18
 
@@ -28,6 +29,7 @@ module.exports = {
28
29
  pack,
29
30
  play,
30
31
  predeploy,
32
+ serve,
31
33
  validate,
32
34
  whoami,
33
35
  };
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const serveTool = require("../action/serve/tool");
6
+
7
+ // [function]
8
+
9
+ const main = function (conf) {
10
+ return serveTool(conf);
11
+ };
12
+
13
+ // [export]
14
+
15
+ module.exports = main;
@@ -37,6 +37,7 @@ const CLI = {
37
37
  PACKAGE_UNCOMPRESSED_TOO_LARGE: "package_uncompressed_too_large",
38
38
  PARSE_FAILED: "parse_failed",
39
39
  PATH_NOT_FOUND: "path_not_found",
40
+ PORT_IN_USE: "port_in_use",
40
41
  PROJECT_NOT_REPO_ROOT: "project_not_repo_root",
41
42
  SESSION_EXPIRED: "session_expired",
42
43
  TEMPLATE_MISSING: "template_missing",
@@ -8,7 +8,7 @@ const phrase = require("../phrase");
8
8
  // [declare]
9
9
 
10
10
  const HANDLE = cfgproduct.handle;
11
- const STARTER = `${HANDLE}/starter`;
11
+ const STARTERS = `${HANDLE}/starters`;
12
12
 
13
13
  // [export]
14
14
 
@@ -17,29 +17,29 @@ module.exports = {
17
17
  {
18
18
  description: phrase.starterBlank(),
19
19
  name: "Blank",
20
- value: STARTER,
20
+ value: `${STARTERS}/blank`,
21
21
  },
22
22
  {
23
23
  description: phrase.starterThreeJs(),
24
24
  name: "Three.js",
25
- value: `${STARTER}-threejs`,
25
+ value: `${STARTERS}/threejs`,
26
26
  },
27
27
  {
28
28
  description: phrase.starterPhaser(),
29
29
  name: "Phaser",
30
- value: `${STARTER}-phaser`,
30
+ value: `${STARTERS}/phaser`,
31
31
  },
32
32
  {
33
33
  description: phrase.starterPixiJs(),
34
34
  name: "PixiJS",
35
- value: `${STARTER}-pixijs`,
35
+ value: `${STARTERS}/pixijs`,
36
36
  },
37
37
  {
38
38
  description: phrase.starterBabylonJs(),
39
39
  name: "Babylon.js",
40
- value: `${STARTER}-babylonjs`,
40
+ value: `${STARTERS}/babylonjs`,
41
41
  },
42
42
  ],
43
- default: STARTER,
43
+ default: `${STARTERS}/blank`,
44
44
  message: "Template",
45
45
  };
package/src/config/uri.js CHANGED
@@ -12,6 +12,11 @@ const PROTOCOL = cfgproduct.protocol;
12
12
  // [export]
13
13
 
14
14
  module.exports = {
15
+ // where arc serve fetches Arcadible's own files (src/serve/remote.js): the
16
+ // asset site, which has the play page's host, and a build origin, any of
17
+ // which serves the SDK's hosted script at /arcadible/sdk/.
18
+ ARCADIBLE_ASSET: "https://asset.offst.net",
19
+ ARCADIBLE_SDK: `https://sdk.${cfgproduct.handle}.io`,
15
20
  HOST_APP_PLAY: `${PROTOCOL}://play`,
16
21
  HOST_APP_STORE: `${PROTOCOL}://store`,
17
22
  HOST_HTTP_ACCOUNT: `https://account.${DOMAIN}`,
package/src/config/url.js CHANGED
@@ -15,4 +15,6 @@ module.exports = {
15
15
  ERRORS: `${cfguri.HOST_HTTP_DEVELOPER}/references/errors`,
16
16
  LINK: `${cfguri.HOST_HTTP_ACCOUNT}/link`,
17
17
  MANIFEST: `${cfguri.HOST_HTTP_DEVELOPER}/references/${HANDLE}-manifest`,
18
+ // how to let arc serve's play page frame another server.
19
+ SERVE_URL: `${cfguri.HOST_HTTP_DEVELOPER}/references/${HANDLE}-cli/arc-serve#url`,
18
20
  };
package/src/phrase.js CHANGED
@@ -272,6 +272,103 @@ const packed = function () {
272
272
 
273
273
  // [function]
274
274
 
275
+ const portInvalid = function () {
276
+ return "The port must be a whole number from 1 to 65535.";
277
+ };
278
+
279
+ // [function]
280
+
281
+ const portInUse = function (port) {
282
+ return blocks(
283
+ `Port ${port} is in use.`,
284
+ "Stop what's using it, or pass another with --port.",
285
+ );
286
+ };
287
+
288
+ // [function]
289
+
290
+ // what one of Arcadible's files is, for arc serve's warnings: `host`, the play
291
+ // page's, or a path under /arcadible/, such as sdk/v1.js.
292
+ const remoteName = function (name) {
293
+ return name === "host"
294
+ ? "the play page's host"
295
+ : `/${cfgproduct.handle}/${name}`;
296
+ };
297
+
298
+ // [function]
299
+
300
+ // arc serve's copy of one of Arcadible's files, kept from `fetchedAt`.
301
+ const remoteKept = function (name, fetchedAt) {
302
+ return `${TITLE} can't be reached: serving ${remoteName(name)} from ${fetchedAt.slice(0, 10)}.`;
303
+ };
304
+
305
+ // [function]
306
+
307
+ const remoteUnreachable = function (name) {
308
+ return `${TITLE} can't be reached, and there's no copy of ${remoteName(name)} yet: the game runs without it until ${TITLE} can be.`;
309
+ };
310
+
311
+ // [function]
312
+
313
+ const reloaded = function () {
314
+ return `Reloaded at ${new Date().toTimeString().slice(0, 8)}.`;
315
+ };
316
+
317
+ // [function]
318
+
319
+ const serveAlone = function () {
320
+ return "The game alone, without the play page:";
321
+ };
322
+
323
+ // [function]
324
+
325
+ const serveNext = function (dir) {
326
+ return `Reloads when ${dir}/ changes. Press Ctrl+C to stop.`;
327
+ };
328
+
329
+ // [function]
330
+
331
+ const served = function ({ built, url, version }) {
332
+ const what = version ? `v${version}` : "the game";
333
+ if (url) {
334
+ return `Serving ${what} locally, from ${url}`;
335
+ }
336
+ return built
337
+ ? `Built and serving ${what} locally.`
338
+ : `Serving ${what} locally.`;
339
+ };
340
+
341
+ // [function]
342
+
343
+ const serveNextUrl = function () {
344
+ return "Your server reloads the game. Press Ctrl+C to stop.";
345
+ };
346
+
347
+ // [function]
348
+
349
+ const urlInvalid = function () {
350
+ return "The URL must be an http or https one, such as http://localhost:5173.";
351
+ };
352
+
353
+ // [function]
354
+
355
+ // --url's server sent no headers that let the play page frame it. `link`
356
+ // says how to add them.
357
+ const urlNotFrameable = function (url, link) {
358
+ return blocks(
359
+ `⚠ ${url} can't be framed by the play page: it needs to send Cross-Origin-Embedder-Policy: require-corp and Cross-Origin-Resource-Policy: cross-origin.`,
360
+ ["Add them to your server, as the starters' vite.config.js does:", link],
361
+ );
362
+ };
363
+
364
+ // [function]
365
+
366
+ const urlUnreachable = function (url) {
367
+ return `⚠ Nothing answers at ${url} yet. Start your server, then reload the play page.`;
368
+ };
369
+
370
+ // [function]
371
+
275
372
  const pressEnterToOpen = function () {
276
373
  return "Press Enter to open the URL.";
277
374
  };
@@ -726,7 +823,7 @@ const tagPushed = function (tag) {
726
823
  const templateUnavailable = function (template) {
727
824
  return blocks(
728
825
  `Unable to download template ${template}.`,
729
- "Check that it's a public GitHub repository.",
826
+ "Check that it's a public GitHub repository, or a folder in one.",
730
827
  );
731
828
  };
732
829
 
@@ -971,6 +1068,8 @@ module.exports = {
971
1068
  packageTooManyFiles,
972
1069
  packageUncompressedTooLarge,
973
1070
  packed,
1071
+ portInUse,
1072
+ portInvalid,
974
1073
  pressEnterToOpen,
975
1074
  projectNotRepoRoot,
976
1075
  releaseFailed,
@@ -978,7 +1077,14 @@ module.exports = {
978
1077
  releasePublished,
979
1078
  releaseTimedOut,
980
1079
  releaseWaiting,
1080
+ reloaded,
1081
+ remoteKept,
1082
+ remoteUnreachable,
981
1083
  repoNotConnected,
1084
+ serveAlone,
1085
+ serveNext,
1086
+ serveNextUrl,
1087
+ served,
982
1088
  sessionExpired,
983
1089
  sizeB,
984
1090
  sizeGb,
@@ -1001,6 +1107,9 @@ module.exports = {
1001
1107
  unknownTable,
1002
1108
  uploadFailed,
1003
1109
  uploadRateLimited,
1110
+ urlInvalid,
1111
+ urlNotFrameable,
1112
+ urlUnreachable,
1004
1113
  versionExists,
1005
1114
  versionPrerelease,
1006
1115
  versionRelease,