arcadible 0.1.43 → 0.1.44

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-build.js CHANGED
@@ -36,6 +36,7 @@ const relative = path.relative;
36
36
  const resolve = path.resolve;
37
37
  const SpinnerWheel = spinner.SpinnerWheel;
38
38
  const warn = output.warn;
39
+ const warning = output.warning;
39
40
 
40
41
  // [declare]
41
42
 
@@ -59,6 +60,8 @@ const start = function (argv) {
59
60
  const root = resolve(process.cwd(), argv.cwd);
60
61
  // the project, parsed once for both steps.
61
62
  let parsed = null;
63
+ // what --json lists under `warnings`.
64
+ let warnings = [];
62
65
  let sw = new SpinnerWheel("Validating... %s");
63
66
  sw.start(json || quiet);
64
67
  return project(root)
@@ -66,13 +69,14 @@ const start = function (argv) {
66
69
  parsed = proj;
67
70
  return validate({ project: parsed, root });
68
71
  })
69
- .then(({ issues, warnings }) => {
70
- const count = issues.length;
72
+ .then((validated) => {
73
+ const count = validated.issues.length;
74
+ warnings = validated.warnings;
71
75
  if (count) {
72
76
  throw new ArcError(
73
77
  cfgcode.VALIDATION_FAILED,
74
78
  phrase.issuesFound(count),
75
- { data: { issues } },
79
+ { data: { issues: validated.issues } },
76
80
  );
77
81
  }
78
82
  sw.stop(true);
@@ -83,7 +87,16 @@ const start = function (argv) {
83
87
  })
84
88
  .then((artifact) => {
85
89
  if (json) {
86
- print({ artifact });
90
+ const missing = artifact?.missing
91
+ ? [
92
+ warning(
93
+ cfgcode.WARNING.DEPLOY_DIR_NOT_BUILT,
94
+ phrase.buildOutputMissing(relative(root, artifact.missing)),
95
+ "workflow.deploy_dir",
96
+ ),
97
+ ]
98
+ : [];
99
+ print({ artifact, warnings: [...warnings, ...missing] });
87
100
  return;
88
101
  }
89
102
  if (!quiet) {
@@ -103,7 +116,9 @@ const start = function (argv) {
103
116
  log.hs("Nothing to do.");
104
117
  }
105
118
  })
106
- .catch((ex) => abort("Build aborted.", ex, { json, sw }));
119
+ .catch((ex) =>
120
+ abort("Build aborted.", ex, { data: { warnings }, json, sw }),
121
+ );
107
122
  };
108
123
 
109
124
  // [function]
package/bin/arc-deploy.js CHANGED
@@ -42,6 +42,7 @@ const ProgressBar = progress.ProgressBar;
42
42
  const resolve = path.resolve;
43
43
  const SpinnerWheel = spinner.SpinnerWheel;
44
44
  const warn = output.warn;
45
+ const warning = output.warning;
45
46
 
46
47
  // [declare]
47
48
 
@@ -65,7 +66,9 @@ const conf = {
65
66
 
66
67
  // [function]
67
68
 
68
- const handler = function ({ json, pb, quiet, sw }) {
69
+ // `warnings` collects what --json lists, such as a version that couldn't be
70
+ // checked.
71
+ const handler = function ({ json, pb, quiet, sw, warnings }) {
69
72
  return {
70
73
  onAccountRefresh(refresh) {
71
74
  sw.start(json || quiet);
@@ -76,7 +79,10 @@ const handler = function ({ json, pb, quiet, sw }) {
76
79
  pb.start();
77
80
  }
78
81
  },
79
- onLogInfo({ level, message }) {
82
+ onLogInfo({ code, level, message }) {
83
+ if (code) {
84
+ warnings.push(warning(code, message));
85
+ }
80
86
  if (!json && !quiet) {
81
87
  switch (level) {
82
88
  case "warn":
@@ -253,6 +259,8 @@ const start = function (argv) {
253
259
  // the project, parsed once and given to each step. A build can write files
254
260
  // the manifest reads, such as art, so it's parsed again after one runs.
255
261
  let parsed = null;
262
+ // what --json lists under `warnings`: arc validate's, then the deploy's.
263
+ const warnings = [];
256
264
  let sw = new SpinnerWheel("Validating... %s");
257
265
  sw.start(json || quiet);
258
266
  return checkTimeout(timeout)
@@ -261,26 +269,29 @@ const start = function (argv) {
261
269
  parsed = proj;
262
270
  return validate({ project: parsed, root });
263
271
  })
264
- .then(({ issues, warnings }) => {
265
- const count = issues.length;
272
+ .then((validated) => {
273
+ const count = validated.issues.length;
274
+ warnings.push(...validated.warnings);
266
275
  if (count) {
267
276
  throw new ArcError(
268
277
  cfgcode.VALIDATION_FAILED,
269
278
  phrase.issuesFound(count),
270
- { data: { issues } },
279
+ { data: { issues: validated.issues } },
271
280
  );
272
281
  }
273
282
  sw.stop(true);
274
- warn(warnings, { json, quiet });
283
+ warn(validated.warnings, { json, quiet });
275
284
  sw = new SpinnerWheel("Checking... %s");
276
285
  // git deploys need no login: anyone who can push to the repository can
277
286
  // release, and the Arcadible GitHub App publishes the release. Anyone
278
287
  // can wait for it too; the game's owner, logged in, waits among their
279
288
  // own releases, which also say when the repository isn't connected.
280
289
  if (git) {
281
- return whoamiIfAny({ handler: handler({ json, pb, quiet, sw }) });
290
+ return whoamiIfAny({
291
+ handler: handler({ json, pb, quiet, sw, warnings }),
292
+ });
282
293
  }
283
- return whoami({ handler: handler({ json, pb, quiet, sw }) });
294
+ return whoami({ handler: handler({ json, pb, quiet, sw, warnings }) });
284
295
  })
285
296
  .then((current) => {
286
297
  if (!current && !git) {
@@ -295,7 +306,7 @@ const start = function (argv) {
295
306
  predeploy({
296
307
  current,
297
308
  git,
298
- handler: handler({ json, pb, quiet, sw }),
309
+ handler: handler({ json, pb, quiet, sw, warnings }),
299
310
  project: parsed,
300
311
  root,
301
312
  }),
@@ -359,7 +370,7 @@ const start = function (argv) {
359
370
  artifact,
360
371
  current,
361
372
  git,
362
- handler: handler({ json, pb, quiet, sw }),
373
+ handler: handler({ json, pb, quiet, sw, warnings }),
363
374
  project: parsed,
364
375
  root,
365
376
  }),
@@ -376,7 +387,7 @@ const start = function (argv) {
376
387
  const shown = { ...deployed };
377
388
  delete shown.game;
378
389
  delete shown.release;
379
- const result = git ? shown : { built, ...shown };
390
+ const result = { ...(git ? shown : { built, ...shown }), warnings };
380
391
  sw.stop(true);
381
392
  if (argv.wait) {
382
393
  return waitAndReport(current, deployed, result, {
@@ -396,7 +407,9 @@ const start = function (argv) {
396
407
  }
397
408
  return null;
398
409
  })
399
- .catch((ex) => abort("Deploy aborted.", ex, { json, pb, sw }));
410
+ .catch((ex) =>
411
+ abort("Deploy aborted.", ex, { data: { warnings }, json, pb, sw }),
412
+ );
400
413
  };
401
414
 
402
415
  // [function]
package/bin/arc-pack.js CHANGED
@@ -60,6 +60,8 @@ const start = function (argv) {
60
60
  const root = resolve(process.cwd(), argv.cwd);
61
61
  // the project, parsed once for both steps.
62
62
  let parsed = null;
63
+ // what --json lists under `warnings`.
64
+ let warnings = [];
63
65
  let sw = new SpinnerWheel("Validating... %s");
64
66
  sw.start(json || quiet);
65
67
  return project(root)
@@ -67,13 +69,14 @@ const start = function (argv) {
67
69
  parsed = proj;
68
70
  return validate({ project: parsed, root });
69
71
  })
70
- .then(({ issues, warnings }) => {
71
- const count = issues.length;
72
+ .then((validated) => {
73
+ const count = validated.issues.length;
74
+ warnings = validated.warnings;
72
75
  if (count) {
73
76
  throw new ArcError(
74
77
  cfgcode.VALIDATION_FAILED,
75
78
  phrase.issuesFound(count),
76
- { data: { issues } },
79
+ { data: { issues: validated.issues } },
77
80
  );
78
81
  }
79
82
  sw.stop(true);
@@ -89,7 +92,7 @@ const start = function (argv) {
89
92
  })
90
93
  .then((artifact) => {
91
94
  if (json) {
92
- print({ artifact });
95
+ print({ artifact, warnings });
93
96
  return;
94
97
  }
95
98
  if (!quiet) {
@@ -98,7 +101,9 @@ const start = function (argv) {
98
101
  print(artifact, { nl: true, toml: true });
99
102
  }
100
103
  })
101
- .catch((ex) => abort("Pack aborted.", ex, { json, sw }));
104
+ .catch((ex) =>
105
+ abort("Pack aborted.", ex, { data: { warnings }, json, sw }),
106
+ );
102
107
  };
103
108
 
104
109
  // [function]
package/package.json CHANGED
@@ -75,5 +75,5 @@
75
75
  "scripts": {
76
76
  "test": "node --test \"test/**/*.test.js\""
77
77
  },
78
- "version": "0.1.43"
78
+ "version": "0.1.44"
79
79
  }
@@ -28,7 +28,7 @@ const POLL = {
28
28
  interval: time.s(15),
29
29
  maxDelay: time.s(5),
30
30
  timeoutError: () =>
31
- new ArcError(cfgcode.LOGIN_EXPIRED, phrase.loginExpired()),
31
+ new ArcError(cfgcode.LOGIN_CODE_EXPIRED, phrase.loginCodeExpired()),
32
32
  };
33
33
 
34
34
  // [function]
@@ -73,7 +73,10 @@ const pollFlow = function (value, onLoginPoll) {
73
73
  case "failed":
74
74
  throw new ArcError(cfgcode.LOGIN_FAILED, phrase.loginFailed());
75
75
  default:
76
- throw new ArcError(cfgcode.LOGIN_EXPIRED, phrase.loginExpired());
76
+ throw new ArcError(
77
+ cfgcode.LOGIN_CODE_EXPIRED,
78
+ phrase.loginCodeExpired(),
79
+ );
77
80
  }
78
81
  });
79
82
  };
@@ -69,15 +69,26 @@ const verifyTotal = function (zip) {
69
69
 
70
70
  // [function]
71
71
 
72
- const pack = function (name, game, { inputPath, mediaPath, outputPath }) {
72
+ // `deployDir` when the input is deploy_dir, rather than a folder --input
73
+ // named: a missing deploy_dir means the build hasn't written it.
74
+ const pack = function (
75
+ name,
76
+ game,
77
+ { deployDir, inputPath, mediaPath, outputPath },
78
+ ) {
73
79
  const zip = new Zip(name);
74
80
  return pathExists(inputPath)
75
81
  .then((existsInput) => {
76
82
  if (!existsInput) {
77
- throw new ArcError(
78
- cfgcode.PATH_NOT_FOUND,
79
- phrase.unableToFind(inputPath),
80
- );
83
+ throw deployDir
84
+ ? new ArcError(
85
+ cfgcode.DEPLOY_DIR_MISSING,
86
+ phrase.deployDirMissing(inputPath),
87
+ )
88
+ : new ArcError(
89
+ cfgcode.PATH_NOT_FOUND,
90
+ phrase.unableToFind(inputPath),
91
+ );
81
92
  }
82
93
  zip.addDir(inputPath);
83
94
  return zip;
@@ -105,8 +116,11 @@ const main = function ({ input, output, ...conf }) {
105
116
  return given(conf).then(({ game, workflow }) => {
106
117
  const name = game.handle ?? game.id;
107
118
  const file = output ?? `${OUTPUT}/${name}-v${game.version}.zip`;
119
+ const deployDir = resolve(root, workflow.deploy_dir);
120
+ const inputPath = resolve(root, input ?? workflow.deploy_dir);
108
121
  return pack(parse(file).name, game, {
109
- inputPath: resolve(root, input ?? workflow.deploy_dir),
122
+ deployDir: inputPath === deployDir,
123
+ inputPath,
110
124
  mediaPath: resolve(root, MEDIA),
111
125
  outputPath: resolve(root, file),
112
126
  });
@@ -77,11 +77,28 @@ const ensureClean = function (root) {
77
77
 
78
78
  // [function]
79
79
 
80
+ // fails with git_remote_missing when there's no origin, and with
81
+ // git_remote_unreachable when it's there but can't be reached.
80
82
  const ensureRemote = function (root) {
81
- return git(root, ["ls-remote", "origin"]).then(
82
- () => null,
83
- failWith(cfgcode.GIT_REMOTE_UNAVAILABLE, phrase.gitRemoteMissing()),
84
- );
83
+ return git(root, ["remote", "get-url", "origin"])
84
+ .then(
85
+ () => null,
86
+ failWith(cfgcode.GIT_REMOTE_MISSING, phrase.gitRemoteMissing()),
87
+ )
88
+ .then(() => git(root, ["ls-remote", "origin"]))
89
+ .then(
90
+ () => null,
91
+ // git_remote_missing passes through.
92
+ (err) => {
93
+ if (err instanceof ArcError) {
94
+ throw err;
95
+ }
96
+ failWith(
97
+ cfgcode.GIT_REMOTE_UNREACHABLE,
98
+ phrase.gitRemoteUnreachable(),
99
+ )(err);
100
+ },
101
+ );
85
102
  };
86
103
 
87
104
  // [function]
@@ -168,7 +185,7 @@ const hasTagLocal = function (root, tag) {
168
185
  const hasTagRemote = function (root, tag) {
169
186
  return git(root, ["ls-remote", "--tags", "origin", `refs/tags/${tag}`]).then(
170
187
  (stdout) => Boolean(stdout.trim()),
171
- failWith(cfgcode.GIT_REMOTE_UNAVAILABLE, phrase.gitRemoteMissing()),
188
+ failWith(cfgcode.GIT_REMOTE_UNREACHABLE, phrase.gitRemoteUnreachable()),
172
189
  );
173
190
  };
174
191
 
@@ -57,6 +57,7 @@ const ensurePublishable = function ({ handle, id, version }, handler) {
57
57
  if (game === null || build === null) {
58
58
  if (handler.onLogInfo) {
59
59
  handler.onLogInfo({
60
+ code: cfgcode.WARNING.VERSION_UNCHECKED,
60
61
  level: "warn",
61
62
  message: phrase.versionUnchecked(version),
62
63
  });
@@ -11,7 +11,7 @@ const workflow = require("../../schema/workflow");
11
11
  // [use]
12
12
 
13
13
  const closest = strs.closest;
14
- const ISSUE = cfgcode.ISSUE;
14
+ const WARNING = cfgcode.WARNING;
15
15
 
16
16
  // [function]
17
17
 
@@ -29,7 +29,7 @@ const known = function (root) {
29
29
  // it's reported without failing, with the name it might have meant.
30
30
  const warning = function (path, message, near) {
31
31
  return {
32
- code: ISSUE.FIELD_UNKNOWN,
32
+ code: WARNING.FIELD_UNKNOWN,
33
33
  details: near ? [`Did you mean: ${near}?`] : [],
34
34
  link: null,
35
35
  message,
@@ -8,24 +8,26 @@
8
8
 
9
9
  // the CLI's own codes.
10
10
  const CLI = {
11
- API_ERROR: "api_error",
11
+ API_REFUSED: "api_refused",
12
+ API_UNREACHABLE: "api_unreachable",
12
13
  ARGUMENT_INVALID: "argument_invalid",
13
14
  BUILD_FAILED: "build_failed",
15
+ DEPLOY_DIR_MISSING: "deploy_dir_missing",
14
16
  GAME_EXISTS: "game_exists",
15
17
  GAME_ID_MISSING: "game_id_missing",
16
18
  GAME_NOT_DEPLOYED: "game_not_deployed",
17
19
  GAME_NOT_FOUND: "game_not_found",
18
- GIT_REMOTE_UNAVAILABLE: "git_remote_unavailable",
20
+ GIT_REMOTE_MISSING: "git_remote_missing",
21
+ GIT_REMOTE_UNREACHABLE: "git_remote_unreachable",
19
22
  GIT_REPO_MISSING: "git_repo_missing",
20
23
  GIT_TAG_CREATE_FAILED: "git_tag_create_failed",
21
24
  GIT_TAG_EXISTS: "git_tag_exists",
22
25
  GIT_TAG_PUSH_FAILED: "git_tag_push_failed",
23
26
  GIT_UNCOMMITTED: "git_uncommitted",
24
27
  LOCKFILE_MISSING: "lockfile_missing",
25
- LOGIN_EXPIRED: "login_expired",
28
+ LOGIN_CODE_EXPIRED: "login_code_expired",
26
29
  LOGIN_FAILED: "login_failed",
27
30
  MANIFEST_MISSING: "manifest_missing",
28
- NETWORK_ERROR: "network_error",
29
31
  NOT_LOGGED_IN: "not_logged_in",
30
32
  PACKAGE_FILE_TOO_LARGE: "package_file_too_large",
31
33
  PACKAGE_INDEX_MISSING: "package_index_missing",
@@ -48,24 +50,26 @@ const CLI = {
48
50
  // thing wherever it appears: GAME_UNAVAILABLE, INTERNAL, REPO_NOT_CONNECTED,
49
51
  // and VERSION_EXISTS are the CLI's too.
50
52
  const RELEASE = {
51
- APP_NOT_INSTALLED: "app_not_installed",
52
- EXPIRED: "expired",
53
+ BUILD_RATE_LIMITED: "build_rate_limited",
53
54
  GAME_LIMIT_REACHED: "game_limit_reached",
54
55
  GAME_MISMATCH: "game_mismatch",
55
56
  GAME_UNAVAILABLE: "game_unavailable",
57
+ GITHUB_APP_NOT_INSTALLED: "github_app_not_installed",
56
58
  INTERNAL: "internal",
57
59
  MANIFEST_INVALID: "manifest_invalid",
58
- RATE_LIMITED: "rate_limited",
60
+ RELEASE_EXPIRED: "release_expired",
59
61
  REPO_NOT_CONNECTED: "repo_not_connected",
60
62
  VERSION_EXISTS: "version_exists",
63
+ VERSION_MISMATCH: "version_mismatch",
61
64
  ZIP_INVALID: "zip_invalid",
65
+ ZIP_MISSING: "zip_missing",
62
66
  ZIP_TOO_LARGE: "zip_too_large",
63
67
  };
64
68
  // the Arcadible check's own codes on GitHub, for a `git` release that fails
65
- // before there's a release (the queue publisher Lambda). `arc deploy git`
69
+ // before there's a release (the queue publisher and subscriber Lambdas). `arc deploy git`
66
70
  // checks for workflow_missing before it pushes.
67
71
  const GITHUB = {
68
- TAG_INVALID: "tag_invalid",
72
+ GIT_TAG_INVALID: "git_tag_invalid",
69
73
  WORKFLOW_MISSING: "workflow_missing",
70
74
  };
71
75
  // an `arc validate` issue's codes, named for the rule it breaks; the field
@@ -76,7 +80,6 @@ const ISSUE = {
76
80
  DESCRIPTION_RULES_MISSING: "description_rules_missing",
77
81
  DUPLICATE: "duplicate",
78
82
  EMPTY: "empty",
79
- FIELD_UNKNOWN: "field_unknown",
80
83
  INDEX_MISSING: "index_missing",
81
84
  INVALID: "invalid",
82
85
  MARKDOWN_INVALID: "markdown_invalid",
@@ -93,6 +96,14 @@ const ISSUE = {
93
96
  UUID_INVALID: "uuid_invalid",
94
97
  VERSION_INVALID: "version_invalid",
95
98
  };
99
+ // a warning's codes: what's reported without failing, in an issue's shape.
100
+ // they follow the same rules, and share the Errors page, so no warning code
101
+ // is also an error or issue code.
102
+ const WARNING = {
103
+ DEPLOY_DIR_NOT_BUILT: "deploy_dir_not_built",
104
+ FIELD_UNKNOWN: "field_unknown",
105
+ VERSION_UNCHECKED: "version_unchecked",
106
+ };
96
107
  // every error code, for checking one exists.
97
108
  const ALL = new Set([
98
109
  ...Object.values(CLI),
@@ -101,6 +112,8 @@ const ALL = new Set([
101
112
  ]);
102
113
  // every issue code.
103
114
  const ISSUES = new Set(Object.values(ISSUE));
115
+ // every warning code.
116
+ const WARNINGS = new Set(Object.values(WARNING));
104
117
 
105
118
  // [export]
106
119
 
@@ -113,4 +126,6 @@ module.exports = {
113
126
  ISSUE,
114
127
  ISSUES,
115
128
  RELEASE,
129
+ WARNING,
130
+ WARNINGS,
116
131
  };
package/src/phrase.js CHANGED
@@ -101,6 +101,15 @@ const cmdValidate = function () {
101
101
 
102
102
  // [function]
103
103
 
104
+ const deployDirMissing = function (dir) {
105
+ return blocks(
106
+ `Unable to find deploy_dir: ${dir}`,
107
+ "Run `arc build`, or set deploy_dir to the folder your build writes.",
108
+ );
109
+ };
110
+
111
+ // [function]
112
+
104
113
  const docAttrInvalid = function (tag, attr, value) {
105
114
  return `Value '${value}' is not valid for the '${attr}' attribute on the '${tag}' tag.`;
106
115
  };
@@ -207,8 +216,17 @@ const gitChangesPresent = function () {
207
216
 
208
217
  const gitRemoteMissing = function () {
209
218
  return blocks(
210
- "Unable to access Git remote 'origin'.",
211
- "Check that it exists and that you can access it.",
219
+ "No Git remote 'origin'.",
220
+ "Add the game's GitHub repository as origin.",
221
+ );
222
+ };
223
+
224
+ // [function]
225
+
226
+ const gitRemoteUnreachable = function () {
227
+ return blocks(
228
+ "Unable to reach Git remote 'origin'.",
229
+ "Check your connection, and that you can access it.",
212
230
  );
213
231
  };
214
232
 
@@ -357,8 +375,8 @@ const issuesFound = function (count) {
357
375
 
358
376
  // [function]
359
377
 
360
- const loginExpired = function () {
361
- return "Login request expired.";
378
+ const loginCodeExpired = function () {
379
+ return "Login code expired.";
362
380
  };
363
381
 
364
382
  // [function]
@@ -469,10 +487,9 @@ const packageUncompressedTooLarge = function (size) {
469
487
  // and where to see it.
470
488
  // `studio` is the game's builds in Studio, for its owner; anyone else can't
471
489
  // open it.
472
- const releaseFailed = function ({ message, reason }, studio) {
490
+ const releaseFailed = function ({ message }, studio) {
473
491
  return blocks(
474
492
  (message ?? "").split(/(?<=\.)\s+/u).join("\n"),
475
- `Reason: ${reason}.`,
476
493
  ...(studio ? [[`${TITLE} Studio:`, studio]] : []),
477
494
  );
478
495
  };
@@ -767,6 +784,7 @@ module.exports = {
767
784
  buildSubmitted,
768
785
  cmdInit,
769
786
  cmdInspect,
787
+ deployDirMissing,
770
788
  docAttrInvalid,
771
789
  docAttrUnallowed,
772
790
  docCommentUnallowed,
@@ -783,6 +801,7 @@ module.exports = {
783
801
  gameUnavailable,
784
802
  gitChangesPresent,
785
803
  gitRemoteMissing,
804
+ gitRemoteUnreachable,
786
805
  gitRepoMissing,
787
806
  gitTagCreateFailed,
788
807
  gitTagPushFailed,
@@ -799,7 +818,7 @@ module.exports = {
799
818
  imageWidthMin,
800
819
  issuesAfterBuild,
801
820
  issuesFound,
802
- loginExpired,
821
+ loginCodeExpired,
803
822
  loginFailed,
804
823
  noGameId,
805
824
  noLockFile,
package/src/util/error.js CHANGED
@@ -32,14 +32,15 @@ const ArcError = class extends Error {
32
32
 
33
33
  // [function]
34
34
 
35
- // the code a failure reports: its own, api_error for a request the API
36
- // refused, or internal for anything else, which is a bug in the CLI.
35
+ // the code a failure reports: its own, api_refused for a request the API
36
+ // refused, or internal for a failure on Arcadible's side (a 5xx) and for
37
+ // anything else, which is a bug in the CLI.
37
38
  const codeOf = function (ex) {
38
39
  if (ex instanceof ArcError) {
39
40
  return ex.code;
40
41
  }
41
- if (ex instanceof HttpError) {
42
- return cfgcode.API_ERROR;
42
+ if (ex instanceof HttpError && !(ex.status >= 500)) {
43
+ return cfgcode.API_REFUSED;
43
44
  }
44
45
  return cfgcode.INTERNAL;
45
46
  };
@@ -66,7 +66,7 @@ const print = function (data, { key, nl, toml } = {}) {
66
66
  // release that failed. `nl` puts a blank line before the heading, after
67
67
  // output the command has already printed, such as arc login's link.
68
68
  // `code` is a failed release's reason, which the API may add to, so it isn't
69
- // checked, and its message already says it.
69
+ // checked.
70
70
  const abort = function (
71
71
  heading,
72
72
  ex,
@@ -74,12 +74,15 @@ const abort = function (
74
74
  ) {
75
75
  const message = ex.message;
76
76
  const status = ex.status;
77
+ const shown = code ?? codeOf(ex);
78
+ const link = `${cfgurl.ERRORS}#${shown}`;
77
79
  if (json) {
78
80
  print({
79
81
  ...ex.data,
80
82
  ...data,
81
83
  error: {
82
- code: code ?? codeOf(ex),
84
+ code: shown,
85
+ link,
83
86
  message,
84
87
  ...(status ? { status } : {}),
85
88
  },
@@ -97,12 +100,7 @@ const abort = function (
97
100
  log.hd(heading);
98
101
  }
99
102
  log.pd(message);
100
- if (!code) {
101
- log.nlpd([
102
- phrase.errorCode(codeOf(ex)),
103
- chalk.cyan.underline(`${cfgurl.ERRORS}#${codeOf(ex)}`),
104
- ]);
105
- }
103
+ log.nlpd([phrase.errorCode(shown), chalk.cyan.underline(link)]);
106
104
  }
107
105
  process.exit(1);
108
106
  };
@@ -119,6 +117,21 @@ const warn = function (warnings, { json, quiet } = {}) {
119
117
  warnings.forEach((each) => log.pw(`⚠ ${schema.print(each)}`));
120
118
  };
121
119
 
120
+ // [function]
121
+
122
+ // a warning of the command's own, in an issue's shape, as --json lists it
123
+ // under `warnings` beside arc validate's: its code, and the link to it on
124
+ // the Errors page. `path` is the field it's about, if any.
125
+ const warning = function (code, message, path = null) {
126
+ return {
127
+ code,
128
+ details: [],
129
+ link: `${cfgurl.ERRORS}#${code}`,
130
+ message,
131
+ path,
132
+ };
133
+ };
134
+
122
135
  // [export]
123
136
 
124
- module.exports = { abort, print, printJson, printToml, warn };
137
+ module.exports = { abort, print, printJson, printToml, warn, warning };
@@ -53,7 +53,7 @@ const parse = function (resp) {
53
53
 
54
54
  // sends a request to a full URL. `headers` are added to the client's own, and
55
55
  // a form is sent as JSON. A request that gets no response, such as when
56
- // offline, rejects as network_error.
56
+ // offline, rejects as api_unreachable.
57
57
  const send = function (method, url, form, headers = {}) {
58
58
  const all = {
59
59
  [`X-${CLIENT}-Client-Id`]: ID,
@@ -66,7 +66,7 @@ const send = function (method, url, form, headers = {}) {
66
66
  headers: all,
67
67
  method,
68
68
  }).then(parse, (ex) => {
69
- throw new ArcError(cfgcode.NETWORK_ERROR, phrase.unableToReach(), {
69
+ throw new ArcError(cfgcode.API_UNREACHABLE, phrase.unableToReach(), {
70
70
  cause: ex,
71
71
  });
72
72
  });
package/src/util/s3.js CHANGED
@@ -32,7 +32,7 @@ const requestOpts = function (size) {
32
32
  // [function]
33
33
 
34
34
  // puts the file to the upload link, and resolves once S3 accepts it. A
35
- // request that gets no response rejects as network_error; a file that can't
35
+ // request that gets no response rejects as api_unreachable; a file that can't
36
36
  // be read rejects with its own error.
37
37
  const upload = function (url, { path, size }, onProgress) {
38
38
  return new Promise((resolve, reject) => {
@@ -49,7 +49,7 @@ const upload = function (url, { path, size }, onProgress) {
49
49
  req.on("error", (ex) => {
50
50
  reject(
51
51
  readError ??
52
- new ArcError(cfgcode.NETWORK_ERROR, phrase.unableToReach(), {
52
+ new ArcError(cfgcode.API_UNREACHABLE, phrase.unableToReach(), {
53
53
  cause: ex,
54
54
  }),
55
55
  );