arcadible 0.1.8 → 0.1.10

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.
@@ -30,13 +30,13 @@ const main = function (argv) {
30
30
  yargs(hideBin(argv))
31
31
  .scriptName(CMD)
32
32
  .usage("$0 <command> [options]")
33
+ .command("init", "Initialize a project")
34
+ .command("validate", "Validate project workspace")
33
35
  .command("deploy", "Deploy to Arcadible")
34
- .command("validate", "Validate workspace")
35
36
  .command("inspect", "Inspect project metadata")
36
- .command("init", "Initialize a project")
37
- .command("whoami", "Show current Arcadible account")
38
37
  .command("login", "Log in to Arcadible")
39
38
  .command("logout", "Log out of Arcadible")
39
+ .command("whoami", "Show Arcadible account")
40
40
  .help("help")
41
41
  .alias("help", "h")
42
42
  .version(pkg.version)
package/bin/arc-deploy.js CHANGED
@@ -112,7 +112,7 @@ const main = function (argv) {
112
112
  .usage("$0 [options]")
113
113
  .option("git", {
114
114
  default: conf.git,
115
- describe: "Deploy through Git",
115
+ describe: "Deploy using Git workflow",
116
116
  type: "boolean",
117
117
  })
118
118
  .option("json", {
package/bin/arc-init.js CHANGED
@@ -7,6 +7,7 @@ const path = require("node:path");
7
7
 
8
8
  // [import]
9
9
 
10
+ const fs = require("fs-extra");
10
11
  const hideBin = require("yargs/helpers").hideBin;
11
12
  const prompts = require("@inquirer/prompts");
12
13
  const yargs = require("yargs/yargs");
@@ -18,14 +19,14 @@ const init = require("../src/cli/init");
18
19
  const log = require("../src/util/log");
19
20
  const output = require("../src/util/output");
20
21
  const pkg = require("../package.json");
21
- const project = require("../src/prompt/project");
22
22
  const rc = require("../src/util/rc");
23
23
  const template = require("../src/prompt/template");
24
24
 
25
25
  // [use]
26
26
 
27
27
  const findRc = rc.findRc;
28
- const input = prompts.input;
28
+ const join = path.join;
29
+ const pathExists = fs.pathExists;
29
30
  const print = output.print;
30
31
  const relative = path.relative;
31
32
  const resolve = path.resolve;
@@ -39,8 +40,8 @@ const SUBCMD = "init";
39
40
  // [define]
40
41
 
41
42
  const conf = {
42
- force: false,
43
43
  json: false,
44
+ project: ".",
44
45
  quiet: false,
45
46
  yes: false,
46
47
  ...findRc(CMD, SUBCMD, process.argv, ["--cwd", "-C"]),
@@ -66,8 +67,9 @@ const start = function (argv) {
66
67
  const quiet = argv.quiet;
67
68
  const root = resolve(process.cwd(), argv.cwd);
68
69
  return init({
69
- force: argv.force,
70
+ force: false,
70
71
  handler: handler({ json, quiet }),
72
+ patch: argv.patch,
71
73
  project: argv.project,
72
74
  root,
73
75
  template: argv.template,
@@ -80,14 +82,18 @@ const start = function (argv) {
80
82
  }
81
83
  if (!quiet) {
82
84
  log.hsnl("Initialized.");
85
+ log.h("Getting started:");
86
+ log.pnl(cfgurl.GETTING_STARTED);
83
87
  log.h("Next steps:");
84
- log.pnl(
85
- [data.path === root ? null : `cd ${dir}`, "npm install", "arc deploy"]
88
+ log.p(
89
+ [
90
+ data.path === root ? null : `cd ${dir}`,
91
+ "arc login",
92
+ "arc deploy (or: arc deploy --git)",
93
+ ]
86
94
  .filter(Boolean)
87
- .join(" && "),
95
+ .map((step, i) => `${i + 1}. ${step}`),
88
96
  );
89
- log.h("Getting started:");
90
- log.p(cfgurl.GETTING_STARTED);
91
97
  }
92
98
  // degit sometimes hangs
93
99
  process.exit(0);
@@ -106,17 +112,20 @@ const start = function (argv) {
106
112
 
107
113
  // [function]
108
114
 
115
+ const patch = function (cwd, project) {
116
+ return pathExists(join(resolve(process.cwd(), cwd), project));
117
+ };
118
+
119
+ // [function]
120
+
109
121
  const prompt = async function ({ yes, ...argv }) {
110
122
  const opts = { ...argv };
111
- if (yes) {
112
- opts.project ??= project.input.default;
123
+ opts.patch = await patch(opts.cwd, opts.project);
124
+ if (opts.patch || yes) {
113
125
  opts.template ??= template.select.default;
114
126
  return opts;
115
127
  }
116
- if (!argv.project) {
117
- opts.project = await input(project.input);
118
- }
119
- if (!argv.template) {
128
+ if (!opts.template) {
120
129
  opts.template = await select(template.select);
121
130
  }
122
131
  return opts;
@@ -128,9 +137,10 @@ const main = function (argv) {
128
137
  return prompt(
129
138
  yargs(hideBin(argv))
130
139
  .scriptName(`${CMD} ${SUBCMD}`)
131
- .usage("$0 <project> [options]")
132
- .command("$0 <project>", false, (cli) =>
140
+ .usage("$0 [project] [options]")
141
+ .command("$0 [project]", false, (cli) =>
133
142
  cli.positional("project", {
143
+ default: conf.project,
134
144
  describe: "Project directory",
135
145
  type: "string",
136
146
  }),
@@ -143,13 +153,7 @@ const main = function (argv) {
143
153
  .option("yes", {
144
154
  alias: "y",
145
155
  default: conf.yes,
146
- describe: "Answer prompts using default values",
147
- type: "boolean",
148
- })
149
- .option("force", {
150
- alias: "f",
151
- default: conf.force,
152
- describe: "Overwrite an existing directory",
156
+ describe: "Accept all prompt defaults",
153
157
  type: "boolean",
154
158
  })
155
159
  .option("json", {
package/bin/arc-pack.js CHANGED
@@ -34,7 +34,7 @@ const SUBCMD = "pack";
34
34
 
35
35
  const conf = {
36
36
  json: false,
37
- output: "project.zip",
37
+ output: null,
38
38
  quiet: false,
39
39
  ...findRc(CMD, SUBCMD, process.argv, ["--cwd", "-C"]),
40
40
  };
package/package.json CHANGED
@@ -51,5 +51,5 @@
51
51
  "license": "Apache-2.0",
52
52
  "main": "./src/cli/index.js",
53
53
  "name": "arcadible",
54
- "version": "0.1.8"
54
+ "version": "0.1.10"
55
55
  }
@@ -17,7 +17,7 @@ const os = require("./util/os");
17
17
  // [use]
18
18
 
19
19
  const configDir = os.configDir;
20
- const deepCompact = objs.deepCompact;
20
+ const deepSort = objs.deepSort;
21
21
  const join = path.join;
22
22
  const mkdir = fs.promises.mkdir;
23
23
  const readFile = fs.promises.readFile;
@@ -27,7 +27,7 @@ const writeFile = fs.promises.writeFile;
27
27
  // [declare]
28
28
 
29
29
  const CMD = "arc";
30
- const FILE = "user.json";
30
+ const FILE = "account.json";
31
31
 
32
32
  // [define]
33
33
 
@@ -42,20 +42,22 @@ const expired = function (session) {
42
42
 
43
43
  // [function]
44
44
 
45
- const save = function (user) {
45
+ const save = function (account) {
46
46
  return mkdir(dir, { recursive: true })
47
47
  .then(() =>
48
- writeFile(file, JSON.stringify(deepCompact(user), null, 2), "utf8"),
48
+ writeFile(file, JSON.stringify(deepSort(account), null, 2), "utf8"),
49
49
  )
50
- .then(() => user);
50
+ .then(() => account);
51
51
  };
52
52
 
53
53
  // [function]
54
54
 
55
- const refresh = function ({ session, ...user }) {
55
+ const refresh = function ({ session, ...account }) {
56
56
  const form = new FormData();
57
57
  form.append("refresh_token", session.refresh_token);
58
- return api.authRefresh(form).then((data) => save({ ...user, session: data }));
58
+ return api
59
+ .authRefresh(form)
60
+ .then((data) => save({ ...account, session: data }));
59
61
  };
60
62
 
61
63
  // [function]
@@ -69,13 +71,13 @@ const get = function ({ onRefresh }) {
69
71
  }
70
72
  throw ex;
71
73
  })
72
- .then((user) => {
73
- if (!user || !user.session) {
74
+ .then((account) => {
75
+ if (!account || !account.session) {
74
76
  return null;
75
77
  }
76
- return expired(user.session)
77
- ? onRefresh((renew) => (renew ? refresh(user) : null))
78
- : user;
78
+ return expired(account.session)
79
+ ? onRefresh((renew) => (renew ? refresh(account) : null))
80
+ : account;
79
81
  });
80
82
  };
81
83
 
@@ -44,7 +44,7 @@ const ensureRepo = function (root, cb) {
44
44
  const ensureRemote = function (root, cb) {
45
45
  const message = [
46
46
  "Unable to access Git remote 'origin'.",
47
- "Ensure the remote exists and you have permission to access it.",
47
+ "Ensure the remote 'origin' exists and you have permission to access it.",
48
48
  ];
49
49
  git(root, ["ls-remote", "origin"], (err) => {
50
50
  if (err) {
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const crypto = require("node:crypto");
6
+ const path = require("node:path");
7
+
8
+ // [import]
9
+
10
+ const fs = require("fs-extra");
11
+
12
+ // [import]
13
+
14
+ const TOML = require("../../util/toml");
15
+
16
+ // [use]
17
+
18
+ const join = path.join;
19
+ const pathExists = fs.pathExists;
20
+ const randomUUID = crypto.randomUUID;
21
+ const readFile = fs.promises.readFile;
22
+ const writeFile = fs.promises.writeFile;
23
+
24
+ // [declare]
25
+
26
+ const MANIFEST = "arcadible.toml";
27
+
28
+ // [function]
29
+
30
+ const parseToml = function (buf) {
31
+ try {
32
+ return TOML.parse(buf);
33
+ } catch (ex) {
34
+ throw new Error(`Unable to parse ${MANIFEST}: ${ex.message}`, {
35
+ cause: ex,
36
+ });
37
+ }
38
+ };
39
+
40
+ // [function]
41
+
42
+ const pickVersion = function (version, envVersion) {
43
+ if (version) {
44
+ return { version };
45
+ }
46
+ return envVersion ? {} : { version: "0.1.0" };
47
+ };
48
+
49
+ // [function]
50
+
51
+ const readManifest = function (file) {
52
+ return pathExists(file)
53
+ .then((exists) => (exists ? readFile(file, "utf8") : null))
54
+ .then((buf) => (buf === null ? {} : parseToml(buf)));
55
+ };
56
+
57
+ // [function]
58
+
59
+ const patchGame = function (envVersion, game = {}) {
60
+ const { id, version, ...rest } = game;
61
+ return {
62
+ id: id ?? randomUUID(),
63
+ ...pickVersion(version, envVersion),
64
+ ...rest,
65
+ };
66
+ };
67
+
68
+ // [function]
69
+
70
+ const shouldWrite = function (manifest, next) {
71
+ return (
72
+ manifest.game?.id !== next.game?.id ||
73
+ manifest.game?.version !== next.game?.version
74
+ );
75
+ };
76
+
77
+ // [function]
78
+
79
+ const prepare = function (dest, envVersion) {
80
+ const file = join(dest, MANIFEST);
81
+ return readManifest(file).then((manifest) => {
82
+ const { game, ...rest } = manifest;
83
+ const next = { game: patchGame(envVersion, game), ...rest };
84
+ if (shouldWrite(manifest, next)) {
85
+ return writeFile(file, TOML.stringify(next), "utf8");
86
+ }
87
+ return null;
88
+ });
89
+ };
90
+
91
+ // [export]
92
+
93
+ module.exports = { prepare };
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const path = require("node:path");
6
+
7
+ // [import]
8
+
9
+ const env = require("../../parser/env");
10
+ const manifest = require("./manifest");
11
+
12
+ // [use]
13
+
14
+ const prepare = manifest.prepare;
15
+ const resolve = path.resolve;
16
+
17
+ // [function]
18
+
19
+ const detectVersion = function (root) {
20
+ return env(root).then(({ game }) => game?.version ?? null);
21
+ };
22
+
23
+ // [function]
24
+
25
+ const init = function () {
26
+ return Promise.resolve();
27
+ };
28
+
29
+ // [function]
30
+
31
+ const main = function ({ force, project, root, template }, handler) {
32
+ const dest = resolve(root, project);
33
+ return init(dest, { force, template }, handler)
34
+ .then(() => detectVersion(dest))
35
+ .then((version) => prepare(dest, version))
36
+ .then(() => ({ path: dest }));
37
+ };
38
+
39
+ // [export]
40
+
41
+ module.exports = main;
@@ -9,35 +9,63 @@ const path = require("node:path");
9
9
  const degit = require("degit");
10
10
  const fs = require("fs-extra");
11
11
 
12
+ // [import]
13
+
14
+ const env = require("../../parser/env");
15
+ const manifest = require("./manifest");
16
+
12
17
  // [use]
13
18
 
19
+ const join = path.join;
14
20
  const pathExists = fs.pathExists;
21
+ const prepare = manifest.prepare;
15
22
  const remove = fs.remove;
16
23
  const resolve = path.resolve;
17
24
 
25
+ // [declare]
26
+
27
+ const LICENSE = "LICENSE";
28
+
29
+ // [function]
30
+
31
+ const removeLicense = function (dest) {
32
+ return remove(join(dest, LICENSE));
33
+ };
34
+
18
35
  // [function]
19
36
 
20
- const init = function (src, dest, { onBegin, onInfo }) {
37
+ const detectVersion = function (root) {
38
+ return env(root).then(({ game }) => game?.version ?? null);
39
+ };
40
+
41
+ // [function]
42
+
43
+ const scaffold = function (src, dest, { onBegin, onInfo }) {
21
44
  onBegin({ dest, src });
22
45
  return degit(src, { cache: false, force: true, verbose: false })
23
46
  .on("info", onInfo)
24
- .clone(dest)
25
- .then(() => remove(resolve(dest, "LICENSE")));
47
+ .clone(dest);
48
+ };
49
+
50
+ // [function]
51
+
52
+ const init = function (dest, { force, template }, handler) {
53
+ return pathExists(dest).then((exists) => {
54
+ if (exists && !force) {
55
+ throw new Error("Directory already exists. Use `--force` to overwrite.");
56
+ }
57
+ return scaffold(template, dest, handler);
58
+ });
26
59
  };
27
60
 
28
61
  // [function]
29
62
 
30
63
  const main = function ({ force, project, root, template }, handler) {
31
64
  const dest = resolve(root, project);
32
- return pathExists(dest)
33
- .then((exists) => {
34
- if (exists && !force) {
35
- throw new Error(
36
- "Directory already exists. Use `--force` to overwrite.",
37
- );
38
- }
39
- return init(template, dest, handler);
40
- })
65
+ return init(dest, { force, template }, handler)
66
+ .then(() => removeLicense(dest))
67
+ .then(() => detectVersion(dest))
68
+ .then((version) => prepare(dest, version))
41
69
  .then(() => ({ path: dest }));
42
70
  };
43
71
 
@@ -8,10 +8,12 @@ const crypto = require("node:crypto");
8
8
 
9
9
  const api = require("../../api");
10
10
  const cfgurl = require("../../config/url");
11
+ const delay = require("../../util/delay");
11
12
  const time = require("../../util/time");
12
13
 
13
14
  // [use]
14
15
 
16
+ const poll = delay.poll;
15
17
  const randomUUID = crypto.randomUUID;
16
18
 
17
19
  // [function]
@@ -75,7 +77,7 @@ const main = function ({ onCreated, onPoll }) {
75
77
  onCreated,
76
78
  ).then(({ id }) => {
77
79
  const timeout = Math.max(0, expiresAt.getTime() - Date.now() - time.s(15));
78
- return time.poll(
80
+ return poll(
79
81
  (flow) => pollFlow(flow, onPoll),
80
82
  { id, token },
81
83
  {
@@ -2,13 +2,13 @@
2
2
 
3
3
  // [import]
4
4
 
5
+ const account = require("../../account");
5
6
  const flow = require("./flow");
6
- const user = require("../../user");
7
7
 
8
8
  // [function]
9
9
 
10
10
  const main = function (conf, handler) {
11
- return flow(handler).then(user.save);
11
+ return flow(handler).then(account.save);
12
12
  };
13
13
 
14
14
  // [export]
@@ -2,12 +2,12 @@
2
2
 
3
3
  // [import]
4
4
 
5
- const user = require("../../user");
5
+ const account = require("../../account");
6
6
 
7
7
  // [function]
8
8
 
9
9
  const main = function () {
10
- return user.remove();
10
+ return account.remove();
11
11
  };
12
12
 
13
13
  // [export]
@@ -11,6 +11,7 @@ const fs = require("fs-extra");
11
11
  // [import]
12
12
 
13
13
  const project = require("../../parser/project");
14
+ const size = require("../../util/size");
14
15
  const Zip = require("../../util/zip");
15
16
 
16
17
  // [use]
@@ -21,38 +22,78 @@ const resolve = path.resolve;
21
22
 
22
23
  // [declare]
23
24
 
25
+ const MAIN = "index.html";
24
26
  const MANIFEST = "arcadible.json";
25
27
  const MEDIA = ".arcadible";
28
+ const OUTPUT = ".arc/package";
29
+
30
+ // [declare]
31
+
32
+ const SIZE_PARTIAL = size.mb(64);
33
+ const SIZE_TOTAL = size.mb(256);
34
+
35
+ // [function]
36
+
37
+ const verifyPartial = function (zip) {
38
+ if (!zip.has(MAIN)) {
39
+ throw new Error(`Package is missing "${MAIN}".`);
40
+ }
41
+ if (zip.stat().size > SIZE_PARTIAL) {
42
+ const max = SIZE_PARTIAL / 1024 / 1024;
43
+ throw new Error(`Package size (excluding store assets) exceeds ${max} MB.`);
44
+ }
45
+ return zip;
46
+ };
47
+
48
+ // [function]
49
+
50
+ const verifyTotal = function (zip) {
51
+ if (zip.stat().size > SIZE_TOTAL) {
52
+ const max = SIZE_TOTAL / 1024 / 1024;
53
+ throw new Error(`Package size (including store assets) exceeds ${max} MB.`);
54
+ }
55
+ return zip;
56
+ };
26
57
 
27
58
  // [function]
28
59
 
29
60
  const pack = function (name, game, { deployPath, mediaPath, zipPath }) {
30
61
  const zip = new Zip(name);
31
- return Promise.all([pathExists(deployPath), pathExists(mediaPath)])
32
- .then(([existsDeploy, existsMedia]) => {
62
+ return pathExists(deployPath)
63
+ .then((existsDeploy) => {
33
64
  if (existsDeploy) {
34
65
  zip.addDir(deployPath);
35
66
  }
67
+ return zip;
68
+ })
69
+ .then(verifyPartial)
70
+ .then(() => pathExists(mediaPath))
71
+ .then((existsMedia) => {
36
72
  if (existsMedia) {
37
73
  zip.addDir(mediaPath, MEDIA);
38
74
  }
39
75
  zip.addJson(MANIFEST, { game });
40
- zip.write(zipPath);
41
- return zip.stat();
76
+ return zip;
42
77
  })
43
- .then((stat) => ({ path: zipPath, ...stat }));
78
+ .then(verifyTotal)
79
+ .then((file) => {
80
+ file.write(zipPath);
81
+ return { path: zipPath, ...file.stat() };
82
+ });
44
83
  };
45
84
 
46
85
  // [function]
47
86
 
48
87
  const main = function ({ output, root }) {
49
- return project(root).then(({ game, workflow }) =>
50
- pack(parse(output).name, game, {
88
+ return project(root).then(({ game, workflow }) => {
89
+ const name = game.handle || game.id;
90
+ const file = output || `${OUTPUT}/${name}-v${game.version}.zip`;
91
+ return pack(parse(file).name, game, {
51
92
  deployPath: resolve(root, workflow.deploy_dir),
52
93
  mediaPath: resolve(root, MEDIA),
53
- zipPath: resolve(root, output),
54
- }),
55
- );
94
+ zipPath: resolve(root, file),
95
+ });
96
+ });
56
97
  };
57
98
 
58
99
  // [export]
@@ -2,12 +2,12 @@
2
2
 
3
3
  // [import]
4
4
 
5
- const user = require("../../user");
5
+ const account = require("../../account");
6
6
 
7
7
  // [function]
8
8
 
9
9
  const main = function (conf, handler) {
10
- return user.get(handler);
10
+ return account.get(handler);
11
11
  };
12
12
 
13
13
  // [export]
package/src/cli/deploy.js CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  // [import]
4
4
 
5
- const deployGitTool = require("../action/deploy-git/tool");
6
5
  const deployTool = require("../action/deploy/tool");
6
+ const deployToolGit = require("../action/deploy/tool-git");
7
7
 
8
8
  // [function]
9
9
 
10
10
  const main = function ({ git, handler, ...conf }) {
11
- const tool = git ? deployGitTool : deployTool;
11
+ const tool = git ? deployToolGit : deployTool;
12
12
  return tool(conf, handler);
13
13
  };
14
14
 
package/src/cli/init.js CHANGED
@@ -3,11 +3,13 @@
3
3
  // [import]
4
4
 
5
5
  const initTool = require("../action/init/tool");
6
+ const initToolPatch = require("../action/init/tool-patch");
6
7
 
7
8
  // [function]
8
9
 
9
- const main = function ({ handler, ...conf }) {
10
- return initTool(conf, handler);
10
+ const main = function ({ handler, patch, ...conf }) {
11
+ const tool = patch ? initToolPatch : initTool;
12
+ return tool(conf, handler);
11
13
  };
12
14
 
13
15
  // [export]
@@ -37,10 +37,10 @@ const parseJson = function (buf) {
37
37
 
38
38
  // [function]
39
39
 
40
- const extract = function (pkg) {
40
+ const extract = function (conf) {
41
41
  /* eslint-disable camelcase */
42
- const game = { version: pkg.version };
43
- const workflow = { build: pkg.scripts?.build ?? "" };
42
+ const game = { version: conf.version };
43
+ const workflow = { build: conf.scripts?.build ?? "" };
44
44
  /* eslint-enable camelcase */
45
45
  return deepCompact({ game, workflow });
46
46
  };
@@ -52,7 +52,7 @@ const main = function (root) {
52
52
  return pathExists(file)
53
53
  .then((exists) => (exists ? readFile(file, "utf8") : null))
54
54
  .then((buf) => (buf === null ? {} : parseJson(buf)))
55
- .then((pkg) => (Object.keys(pkg).length ? extract(pkg) : {}));
55
+ .then((conf) => (Object.keys(conf).length ? extract(conf) : {}));
56
56
  };
57
57
 
58
58
  // [export]
@@ -7,10 +7,15 @@ const path = require("node:path");
7
7
  // [import]
8
8
 
9
9
  const fs = require("fs-extra");
10
- const TOML = require("@iarna/toml");
10
+
11
+ // [import]
12
+
13
+ const objs = require("../util/objs");
14
+ const TOML = require("../util/toml");
11
15
 
12
16
  // [use]
13
17
 
18
+ const deepCompact = objs.deepCompact;
14
19
  const join = path.join;
15
20
  const pathExists = fs.pathExists;
16
21
  const readFile = fs.readFile;
@@ -33,11 +38,18 @@ const parseToml = function (buf) {
33
38
 
34
39
  // [function]
35
40
 
41
+ const extract = function (conf) {
42
+ return deepCompact(conf);
43
+ };
44
+
45
+ // [function]
46
+
36
47
  const main = function (root) {
37
48
  const file = join(root, MANIFEST);
38
49
  return pathExists(file)
39
50
  .then((exists) => (exists ? readFile(file, "utf8") : null))
40
- .then((buf) => (buf === null ? {} : parseToml(buf)));
51
+ .then((buf) => (buf === null ? {} : parseToml(buf)))
52
+ .then((conf) => (Object.keys(conf).length ? extract(conf) : {}));
41
53
  };
42
54
 
43
55
  // [export]
@@ -4,31 +4,31 @@
4
4
 
5
5
  const cfggame = require("../config/game");
6
6
  const cfgworkflow = require("../config/workflow");
7
+ const env = require("./env");
7
8
  const manifest = require("./manifest");
8
9
  const media = require("./media");
9
- const pkg = require("./pkg");
10
10
  const promise = require("../util/promise");
11
11
  const readme = require("./readme");
12
12
 
13
13
  // [function]
14
14
 
15
- const configGame = function (readmeGame, mediaGame, pkgGame, manifestGame) {
15
+ const configGame = function (readmeGame, mediaGame, envGame, manifestGame) {
16
16
  return {
17
17
  ...cfggame,
18
- ...(readmeGame || {}),
19
- ...(mediaGame || {}),
20
- ...(pkgGame || {}),
21
- ...(manifestGame || {}),
18
+ ...(readmeGame ?? {}),
19
+ ...(mediaGame ?? {}),
20
+ ...(envGame ?? {}),
21
+ ...(manifestGame ?? {}),
22
22
  };
23
23
  };
24
24
 
25
25
  // [function]
26
26
 
27
- const configWorkflow = function (pkgWorkflow, manifestWorkflow) {
27
+ const configWorkflow = function (envWorkflow, manifestWorkflow) {
28
28
  return {
29
29
  ...cfgworkflow,
30
- ...(pkgWorkflow || {}),
31
- ...(manifestWorkflow || {}),
30
+ ...(envWorkflow ?? {}),
31
+ ...(manifestWorkflow ?? {}),
32
32
  };
33
33
  };
34
34
 
@@ -36,23 +36,22 @@ const configWorkflow = function (pkgWorkflow, manifestWorkflow) {
36
36
 
37
37
  const main = function (root) {
38
38
  const parsers = [
39
- () => manifest(root),
40
- () => media(root),
41
- () => pkg(root),
42
39
  () => readme(root),
40
+ () => media(root),
41
+ () => env(root),
42
+ () => manifest(root),
43
43
  ];
44
44
  return promise
45
45
  .serial(parsers)
46
- .then(([manifestData, mediaData, pkgData, readmeData]) => {
47
- const game = configGame(
46
+ .then(([readmeData, mediaData, envData, manifestData]) => ({
47
+ game: configGame(
48
48
  readmeData.game,
49
49
  mediaData.game,
50
- pkgData.game,
50
+ envData.game,
51
51
  manifestData.game,
52
- );
53
- const workflow = configWorkflow(pkgData.workflow, manifestData.workflow);
54
- return { game, workflow };
55
- });
52
+ ),
53
+ workflow: configWorkflow(envData.workflow, manifestData.workflow),
54
+ }));
56
55
  };
57
56
 
58
57
  // [export]
@@ -10,8 +10,8 @@ module.exports = {
10
10
  select: {
11
11
  choices: [
12
12
  {
13
- description: "Blank starter.",
14
- name: "Blank",
13
+ description: "Zero-build static starter.",
14
+ name: "Default",
15
15
  value: `${REPO}/starter`,
16
16
  },
17
17
  {
@@ -17,6 +17,7 @@ const cfgurl = require("../config/url");
17
17
  const cfgvisibility = require("../config/visibility");
18
18
  const check = require("./check/game");
19
19
  const dom = require("../util/dom");
20
+ const size = require("../util/size");
20
21
  const strs = require("../util/strs");
21
22
 
22
23
  // [use]
@@ -152,7 +153,7 @@ const zBanner = function (root) {
152
153
  return z.optional(
153
154
  zPng(field, root, {
154
155
  aspect: "2:1",
155
- maxSize: 2 * 1024 * 1024,
156
+ maxSize: size.mb(2),
156
157
  minHeight: 512,
157
158
  minWidth: 1024,
158
159
  }),
@@ -166,7 +167,7 @@ const zBannerVideo = function (root) {
166
167
  return z.optional(
167
168
  zMp4(field, root, {
168
169
  aspect: "2:1",
169
- maxSize: 5 * 1024 * 1024,
170
+ maxSize: size.mb(5),
170
171
  minHeight: 512,
171
172
  minWidth: 1024,
172
173
  }),
@@ -187,7 +188,7 @@ const zCover = function (root) {
187
188
  return z.optional(
188
189
  zPng(field, root, {
189
190
  aspect: "3:2",
190
- maxSize: 2 * 1024 * 1024,
191
+ maxSize: size.mb(2),
191
192
  minHeight: 512,
192
193
  minWidth: 768,
193
194
  }),
@@ -201,7 +202,7 @@ const zCoverVideo = function (root) {
201
202
  return z.optional(
202
203
  zMp4(field, root, {
203
204
  aspect: "3:2",
204
- maxSize: 5 * 1024 * 1024,
205
+ maxSize: size.mb(5),
205
206
  minHeight: 512,
206
207
  minWidth: 768,
207
208
  }),
@@ -237,8 +238,8 @@ const zGallery = function (root) {
237
238
  z
238
239
  .array(
239
240
  z.union([
240
- zPng(field, root, { maxSize: 2 * 1024 * 1024 }),
241
- zMp4(field, root, { maxSize: 5 * 1024 * 1024 }),
241
+ zPng(field, root, { maxSize: size.mb(2) }),
242
+ zMp4(field, root, { maxSize: size.mb(5) }),
242
243
  ]),
243
244
  )
244
245
  .min(1)
@@ -274,7 +275,7 @@ const zIcon = function (root) {
274
275
  return z.optional(
275
276
  zPng(field, root, {
276
277
  aspect: "1:1",
277
- maxSize: 2 * 1024 * 1024,
278
+ maxSize: size.mb(2),
278
279
  minHeight: 512,
279
280
  minWidth: 512,
280
281
  }),
@@ -288,7 +289,7 @@ const zIconVideo = function (root) {
288
289
  return z.optional(
289
290
  zMp4(field, root, {
290
291
  aspect: "1:1",
291
- maxSize: 5 * 1024 * 1024,
292
+ maxSize: size.mb(5),
292
293
  minHeight: 512,
293
294
  minWidth: 512,
294
295
  }),
@@ -300,8 +301,12 @@ const zIconVideo = function (root) {
300
301
  const zId = function () {
301
302
  const field = "id";
302
303
  return z
303
- .string()
304
- .uuid(message(field, ["Must be a valid UUID."]))
304
+ .uuid(
305
+ message(field, [
306
+ "Must be a valid UUID.",
307
+ "Run `arc init` to generate a new game ID.",
308
+ ]),
309
+ )
305
310
  .refine(
306
311
  (value) => value === value.toLowerCase(),
307
312
  message(field, ["Must use lowercase characters."]),
@@ -329,7 +334,7 @@ const zPoster = function (root) {
329
334
  return z.optional(
330
335
  zPng(field, root, {
331
336
  aspect: "2:3",
332
- maxSize: 2 * 1024 * 1024,
337
+ maxSize: size.mb(2),
333
338
  minHeight: 768,
334
339
  minWidth: 512,
335
340
  }),
@@ -343,7 +348,7 @@ const zPosterVideo = function (root) {
343
348
  return z.optional(
344
349
  zMp4(field, root, {
345
350
  aspect: "2:3",
346
- maxSize: 5 * 1024 * 1024,
351
+ maxSize: size.mb(5),
347
352
  minHeight: 768,
348
353
  minWidth: 512,
349
354
  }),
@@ -388,7 +393,7 @@ const zTrailer = function () {
388
393
  .refine(
389
394
  (url) =>
390
395
  zVimeo().safeParse(url).success || zYouTube().safeParse(url).success,
391
- message(field, ["Must be a YouTube (or Vimeo) URL."]),
396
+ message(field, ["Must be a YouTube or Vimeo URL."]),
392
397
  )
393
398
  .optional();
394
399
  };
@@ -401,7 +406,7 @@ const zVersion = function () {
401
406
  .string()
402
407
  .refine(
403
408
  (value) => validSemver(value) !== null,
404
- message(field, ["Must be a valid semver."]),
409
+ message(field, ["Must be valid semver."]),
405
410
  );
406
411
  };
407
412
 
@@ -419,7 +424,7 @@ const zWordmark = function (root) {
419
424
  return z.optional(
420
425
  zSvg(field, root, {
421
426
  aspect: "1:1",
422
- maxSize: 512 * 1024,
427
+ maxSize: size.kb(512),
423
428
  }),
424
429
  );
425
430
  };
@@ -431,7 +436,7 @@ const zWordmarkAnimated = function (root) {
431
436
  return z.optional(
432
437
  zSvg(field, root, {
433
438
  aspect: "1:1",
434
- maxSize: 1024 * 1024,
439
+ maxSize: size.mb(1),
435
440
  }),
436
441
  );
437
442
  };
@@ -45,7 +45,7 @@ const message = function (field, lines) {
45
45
 
46
46
  const zBuild = function () {
47
47
  // const field = "build";
48
- return z.string();
48
+ return z.union([z.string(), z.array(z.string())]);
49
49
  };
50
50
 
51
51
  // [function]
@@ -61,7 +61,7 @@ const zDeployDir = function (root, { build }) {
61
61
  )
62
62
  .refine(
63
63
  (file) => isOutCheck(root, file),
64
- message(field, ["Must be within the project."]),
64
+ message(field, ["Must be within the project directory."]),
65
65
  )
66
66
  .refine(
67
67
  (file) => isReservedCheck(root, file, MEDIA),
@@ -70,8 +70,8 @@ const zDeployDir = function (root, { build }) {
70
70
  .refine(
71
71
  (file) => build || existsCheck(root, file),
72
72
  message(field, [
73
- "Must exist if there is no build step.",
74
- "Run `arc inspect workflow.deploy_dir` for its value.",
73
+ "Must exist when there is no build step.",
74
+ "Run `arc inspect workflow.deploy_dir` to check the configured value.",
75
75
  ]),
76
76
  );
77
77
  };
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const time = require("./time");
6
+
7
+ // [function]
8
+
9
+ const backoff = function (elapsed, delay, factor, interval, maxDelay) {
10
+ const step = Math.floor(elapsed / interval);
11
+ return Math.min(delay * factor ** step, maxDelay);
12
+ };
13
+
14
+ // [function]
15
+
16
+ const sleep = function (delay) {
17
+ return new Promise((resolve) => {
18
+ setTimeout(resolve, delay);
19
+ });
20
+ };
21
+
22
+ // [function]
23
+
24
+ const wait = function (delay, next) {
25
+ return sleep(delay).then(next);
26
+ };
27
+
28
+ // [function]
29
+
30
+ const poll = function (
31
+ fn,
32
+ value,
33
+ {
34
+ delay = time.s(1),
35
+ factor = 1,
36
+ interval = time.m(1),
37
+ maxDelay = delay,
38
+ timeout = Infinity,
39
+ } = {},
40
+ ) {
41
+ const started = Date.now();
42
+ const next = function () {
43
+ return fn(value).then((result) => {
44
+ if (result !== null) {
45
+ return result;
46
+ }
47
+ const elapsed = Date.now() - started;
48
+ if (elapsed >= timeout) {
49
+ throw new Error("Operation timed out.");
50
+ }
51
+ return wait(backoff(elapsed, delay, factor, interval, maxDelay), next);
52
+ });
53
+ };
54
+ return next();
55
+ };
56
+
57
+ // [export]
58
+
59
+ module.exports = { poll };
@@ -2,11 +2,8 @@
2
2
 
3
3
  // [import]
4
4
 
5
- const TOML = require("@iarna/toml");
6
-
7
- // [import]
8
-
9
5
  const objs = require("./objs");
6
+ const TOML = require("./toml");
10
7
 
11
8
  // [use]
12
9
 
@@ -35,7 +32,7 @@ const printToml = function (data, key = "value") {
35
32
  try {
36
33
  write(TOML.stringify(deepSort(data)));
37
34
  } catch (ex) {
38
- write(TOML.stringify({ [field]: data }));
35
+ write(TOML.stringify(deepSort({ [field]: data })));
39
36
  }
40
37
  };
41
38
 
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ // [function]
4
+
5
+ const b = function (value) {
6
+ return value;
7
+ };
8
+
9
+ // [function]
10
+
11
+ const kb = function (value) {
12
+ return b(value * 1024);
13
+ };
14
+
15
+ // [function]
16
+
17
+ const mb = function (value) {
18
+ return kb(value * 1024);
19
+ };
20
+
21
+ // [function]
22
+
23
+ const gb = function (value) {
24
+ return mb(value * 1024);
25
+ };
26
+
27
+ // [export]
28
+
29
+ module.exports = { b, gb, kb, mb };
package/src/util/time.js CHANGED
@@ -24,56 +24,6 @@ const h = function (value) {
24
24
  return m(value * 60);
25
25
  };
26
26
 
27
- // [function]
28
-
29
- const backoff = function (elapsed, delay, factor, interval, maxDelay) {
30
- const step = Math.floor(elapsed / interval);
31
- return Math.min(delay * factor ** step, maxDelay);
32
- };
33
-
34
- // [function]
35
-
36
- const sleep = function (delay) {
37
- return new Promise((resolve) => {
38
- setTimeout(resolve, delay);
39
- });
40
- };
41
-
42
- // [function]
43
-
44
- const wait = function (delay, next) {
45
- return sleep(delay).then(next);
46
- };
47
-
48
- // [function]
49
-
50
- const poll = function (
51
- fn,
52
- value,
53
- {
54
- delay = s(1),
55
- factor = 1,
56
- interval = m(1),
57
- maxDelay = delay,
58
- timeout = Infinity,
59
- } = {},
60
- ) {
61
- const started = Date.now();
62
- const next = function () {
63
- return fn(value).then((result) => {
64
- if (result !== null) {
65
- return result;
66
- }
67
- const elapsed = Date.now() - started;
68
- if (elapsed >= timeout) {
69
- throw new Error("Timed out.");
70
- }
71
- return wait(backoff(elapsed, delay, factor, interval, maxDelay), next);
72
- });
73
- };
74
- return next();
75
- };
76
-
77
27
  // [export]
78
28
 
79
- module.exports = { h, m, ms, poll, s };
29
+ module.exports = { h, m, ms, s };
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const TOML = require("@iarna/toml");
6
+
7
+ // [use]
8
+
9
+ const parse = TOML.parse;
10
+ const stringify = TOML.stringify;
11
+
12
+ // [export]
13
+
14
+ module.exports = { parse, stringify };
package/src/util/video.js CHANGED
@@ -57,7 +57,7 @@ const readMp4 = function (file) {
57
57
  };
58
58
  };
59
59
  mp4.onError = function (mod, msg) {
60
- error = new Error(`MP4Box(${mod}): ${msg}`);
60
+ error = new Error(msg);
61
61
  };
62
62
  const buffer = data.buffer.slice(
63
63
  data.byteOffset,
@@ -70,7 +70,7 @@ const readMp4 = function (file) {
70
70
  throw error;
71
71
  }
72
72
  if (!video) {
73
- throw new Error("MP4 metadata not available.");
73
+ throw new Error("No video metadata found.");
74
74
  }
75
75
  return video;
76
76
  };
package/src/util/zip.js CHANGED
@@ -49,6 +49,12 @@ const Main = class {
49
49
 
50
50
  // [method]
51
51
 
52
+ has(name) {
53
+ return this.zip.getEntry(join(this.prefix, name)) !== null;
54
+ }
55
+
56
+ // [method]
57
+
52
58
  stat() {
53
59
  const buffer = this.zip.toBuffer();
54
60
  const hash = createHash("sha256").update(buffer).digest("hex");
@@ -1,10 +0,0 @@
1
- "use strict";
2
-
3
- // [export]
4
-
5
- module.exports = {
6
- input: {
7
- default: ".",
8
- message: "Project",
9
- },
10
- };