arcadible 0.0.1 → 0.1.1

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 (66) hide show
  1. package/README.md +3 -0
  2. package/bin/arc-default.js +57 -0
  3. package/bin/arc-deploy.js +117 -0
  4. package/bin/arc-init.js +196 -0
  5. package/bin/arc-inspect.js +107 -0
  6. package/bin/arc-login.js +127 -0
  7. package/bin/arc-logout.js +58 -0
  8. package/bin/arc-pack.js +126 -0
  9. package/bin/arc-validate.js +114 -0
  10. package/bin/arc-whoami.js +111 -0
  11. package/bin/arc.js +64 -0
  12. package/package.json +49 -1
  13. package/src/action/deploy/tool.js +11 -0
  14. package/src/action/deploy-git/git.js +23 -0
  15. package/src/action/deploy-git/release.js +74 -0
  16. package/src/action/deploy-git/tool.js +11 -0
  17. package/src/action/init/tool.js +44 -0
  18. package/src/action/inspect/tool.js +25 -0
  19. package/src/action/login/flow.js +94 -0
  20. package/src/action/login/tool.js +16 -0
  21. package/src/action/logout/tool.js +15 -0
  22. package/src/action/pack/tool.js +60 -0
  23. package/src/action/validate/tool.js +39 -0
  24. package/src/action/whoami/tool.js +15 -0
  25. package/src/api.js +39 -0
  26. package/src/cli/deploy.js +17 -0
  27. package/src/cli/index.js +25 -0
  28. package/src/cli/init.js +15 -0
  29. package/src/cli/inspect.js +15 -0
  30. package/src/cli/login.js +15 -0
  31. package/src/cli/logout.js +15 -0
  32. package/src/cli/pack.js +15 -0
  33. package/src/cli/validate.js +15 -0
  34. package/src/cli/whoami.js +15 -0
  35. package/src/config/audience.js +5 -0
  36. package/src/config/game.js +5 -0
  37. package/src/config/genre.js +22 -0
  38. package/src/config/html.js +27 -0
  39. package/src/config/input.js +14 -0
  40. package/src/config/orientation.js +14 -0
  41. package/src/config/uri.js +14 -0
  42. package/src/config/url.js +17 -0
  43. package/src/config/visibility.js +5 -0
  44. package/src/config/workflow.js +5 -0
  45. package/src/parser/manifest.js +46 -0
  46. package/src/parser/media.js +96 -0
  47. package/src/parser/project.js +55 -0
  48. package/src/parser/readme.js +144 -0
  49. package/src/prompt/project.js +10 -0
  50. package/src/prompt/template.js +37 -0
  51. package/src/schema/game.js +324 -0
  52. package/src/schema/workflow.js +119 -0
  53. package/src/user.js +91 -0
  54. package/src/util/dom.js +27 -0
  55. package/src/util/log.js +99 -0
  56. package/src/util/md.js +24 -0
  57. package/src/util/objs.js +28 -0
  58. package/src/util/os.js +24 -0
  59. package/src/util/output.js +46 -0
  60. package/src/util/promise.js +29 -0
  61. package/src/util/rc.js +41 -0
  62. package/src/util/request.js +62 -0
  63. package/src/util/schema.js +25 -0
  64. package/src/util/strs.js +21 -0
  65. package/src/util/time.js +79 -0
  66. package/src/util/zip.js +59 -0
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const path = require("node:path");
6
+
7
+ // [import]
8
+
9
+ const chalk = require("chalk");
10
+ const fs = require("fs-extra");
11
+ const z = require("zod");
12
+
13
+ // [import]
14
+
15
+ const cfgurl = require("../config/url");
16
+
17
+ // [use]
18
+
19
+ const isAbsolute = path.isAbsolute;
20
+ const join = path.join;
21
+ const pathExistsSync = fs.pathExistsSync; // eslint-disable-line no-sync
22
+ const resolve = path.resolve;
23
+
24
+ // [declare]
25
+
26
+ const MEDIA = ".arcadible";
27
+
28
+ // [function]
29
+
30
+ const reference = function (field) {
31
+ return chalk.cyan.underline(`${cfgurl.MANIFEST}#${field}`);
32
+ };
33
+
34
+ // [function]
35
+
36
+ const indent = function (text, spaces = 2) {
37
+ const pad = " ".repeat(spaces);
38
+ return text
39
+ .split("\n")
40
+ .map((line) => pad + line)
41
+ .join("\n");
42
+ };
43
+
44
+ // [function]
45
+
46
+ const message = function (field, msg) {
47
+ return { message: [msg, indent(reference(field))].join("\n") };
48
+ };
49
+
50
+ // [function]
51
+
52
+ const isOutPath = function (root, dir) {
53
+ const resolvedRoot = resolve(root);
54
+ const resolvedTarget = resolve(root, dir);
55
+ const outside =
56
+ resolvedTarget !== resolvedRoot &&
57
+ !resolvedTarget.startsWith(resolvedRoot + path.sep);
58
+ return outside;
59
+ };
60
+
61
+ // [function]
62
+
63
+ const isMediaPath = function (root, dir) {
64
+ const resolvedTarget = resolve(root, dir);
65
+ const containsMedia = resolvedTarget.split(path.sep).includes(MEDIA);
66
+ return containsMedia;
67
+ };
68
+
69
+ // [function]
70
+
71
+ const isRealPath = function (root, dir) {
72
+ return pathExistsSync(join(root, dir));
73
+ };
74
+
75
+ // [function]
76
+
77
+ const zDeployDir = function (root, { skip_build: skipBuild }) {
78
+ const field = "workflow.deploy_dir";
79
+ let zz = z
80
+ .string()
81
+ .min(1)
82
+ .refine((dir) => !isAbsolute(dir), message(field, "Must be relative."))
83
+ .refine(
84
+ (dir) => !isOutPath(root, dir),
85
+ message(field, "Must be within the project."),
86
+ )
87
+ .refine(
88
+ (dir) => !isMediaPath(root, dir),
89
+ message(field, "Must not contain reserved names."),
90
+ );
91
+ if (skipBuild) {
92
+ zz = zz.refine(
93
+ (dir) => isRealPath(root, dir),
94
+ message(field, "Must exist."),
95
+ );
96
+ }
97
+ return zz;
98
+ };
99
+
100
+ // [function]
101
+
102
+ const zSkipBuild = function () {
103
+ return z.boolean();
104
+ };
105
+
106
+ // [function]
107
+
108
+ const main = function (value, { root }) {
109
+ /* eslint-disable camelcase */
110
+ return z.object({
111
+ deploy_dir: zDeployDir(root, value),
112
+ skip_build: zSkipBuild(),
113
+ });
114
+ /* eslint-enable camelcase */
115
+ };
116
+
117
+ // [export]
118
+
119
+ module.exports = main;
package/src/user.js ADDED
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const path = require("node:path");
6
+
7
+ // [import]
8
+
9
+ const fs = require("fs-extra");
10
+
11
+ // [import]
12
+
13
+ const api = require("./api");
14
+ const os = require("./util/os");
15
+
16
+ // [use]
17
+
18
+ const configDir = os.configDir;
19
+ const join = path.join;
20
+ const mkdir = fs.promises.mkdir;
21
+ const readFile = fs.promises.readFile;
22
+ const unlink = fs.promises.unlink;
23
+ const writeFile = fs.promises.writeFile;
24
+
25
+ // [declare]
26
+
27
+ const CMD = "arc";
28
+ const FILE = "user.json";
29
+
30
+ // [define]
31
+
32
+ const dir = join(configDir(), CMD);
33
+ const file = join(dir, FILE);
34
+
35
+ // [function]
36
+
37
+ const expired = function (session) {
38
+ return session.expires_at <= Math.floor(Date.now() / 1000) + 60;
39
+ };
40
+
41
+ // [function]
42
+
43
+ const save = function (user) {
44
+ return mkdir(dir, { recursive: true })
45
+ .then(() => writeFile(file, JSON.stringify(user, null, 2), "utf8"))
46
+ .then(() => user);
47
+ };
48
+
49
+ // [function]
50
+
51
+ const refresh = function ({ session, ...user }) {
52
+ const form = new FormData();
53
+ form.append("refresh_token", session.refresh_token);
54
+ return api.authRefresh(form).then((data) => save({ ...user, session: data }));
55
+ };
56
+
57
+ // [function]
58
+
59
+ const get = function ({ onRefresh }) {
60
+ return readFile(file, "utf8")
61
+ .then(JSON.parse)
62
+ .catch((ex) => {
63
+ if (ex.code === "ENOENT") {
64
+ return null;
65
+ }
66
+ throw ex;
67
+ })
68
+ .then((user) => {
69
+ if (!user || !user.session) {
70
+ return null;
71
+ }
72
+ return expired(user.session)
73
+ ? onRefresh((renew) => (renew ? refresh(user) : null))
74
+ : user;
75
+ });
76
+ };
77
+
78
+ // [function]
79
+
80
+ const remove = function () {
81
+ return unlink(file).catch((ex) => {
82
+ if (ex.code === "ENOENT") {
83
+ return;
84
+ }
85
+ throw ex;
86
+ });
87
+ };
88
+
89
+ // [export]
90
+
91
+ module.exports = { get, remove, save };
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const dompurify = require("dompurify");
6
+ const jsdom = require("jsdom");
7
+
8
+ // [use]
9
+
10
+ const DOM = jsdom.JSDOM;
11
+
12
+ // [function]
13
+
14
+ const sanitize = function (str, cfg) {
15
+ const dom = dompurify(new DOM().window);
16
+ if (cfg) {
17
+ return dom.sanitize(str, {
18
+ ALLOWED_ATTR: cfg.ALLOWED_ATTR,
19
+ ALLOWED_TAGS: cfg.ALLOWED_TAGS,
20
+ });
21
+ }
22
+ return dom.sanitize(str);
23
+ };
24
+
25
+ // [export]
26
+
27
+ module.exports = { DOM, sanitize };
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const chalk = require("chalk");
6
+
7
+ // [function]
8
+
9
+ const log = function (
10
+ out,
11
+ message,
12
+ { bold = false, color = "white", nl = false } = {},
13
+ ) {
14
+ let text = message;
15
+ if (Array.isArray(text)) {
16
+ text = text.join("\n");
17
+ }
18
+ let marker = chalk;
19
+ if (bold) {
20
+ marker = chalk.bold;
21
+ }
22
+ out(marker[color](text.trim().concat(nl ? "\n" : "")));
23
+ };
24
+
25
+ // [function]
26
+
27
+ const h = function (message) {
28
+ log(console.log, message, { bold: true });
29
+ };
30
+
31
+ // [function]
32
+
33
+ const hnl = function (message) {
34
+ log(console.log, message, { bold: true, nl: true });
35
+ };
36
+
37
+ // [function]
38
+
39
+ const p = function (message) {
40
+ log(console.log, message);
41
+ };
42
+
43
+ // [function]
44
+
45
+ const pnl = function (message) {
46
+ log(console.log, message, { nl: true });
47
+ };
48
+
49
+ // [function]
50
+
51
+ const hd = function (message) {
52
+ log(console.error, message, { bold: true, color: "red" });
53
+ };
54
+
55
+ // [function]
56
+
57
+ const hdnl = function (message) {
58
+ log(console.error, message, { bold: true, color: "red", nl: true });
59
+ };
60
+
61
+ // [function]
62
+
63
+ const pd = function (message) {
64
+ log(console.error, message, { color: "red" });
65
+ };
66
+
67
+ // [function]
68
+
69
+ const pdnl = function (message) {
70
+ log(console.error, message, { color: "red", nl: true });
71
+ };
72
+
73
+ // [function]
74
+
75
+ const hs = function (message) {
76
+ log(console.log, message, { bold: true, color: "green" });
77
+ };
78
+
79
+ // [function]
80
+
81
+ const hsnl = function (message) {
82
+ log(console.log, message, { bold: true, color: "green", nl: true });
83
+ };
84
+
85
+ // [function]
86
+
87
+ const ps = function (message) {
88
+ log(console.log, message, { color: "green" });
89
+ };
90
+
91
+ // [function]
92
+
93
+ const psnl = function (message) {
94
+ log(console.log, message, { color: "green", nl: true });
95
+ };
96
+
97
+ // [export]
98
+
99
+ module.exports = { h, hd, hdnl, hnl, hs, hsnl, p, pd, pdnl, pnl, ps, psnl };
package/src/util/md.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const marked = require("marked");
6
+
7
+ // [import]
8
+
9
+ const dom = require("./dom");
10
+ const strs = require("./strs");
11
+
12
+ // [use]
13
+
14
+ const stripLeadingInvisible = strs.stripLeadingInvisible;
15
+
16
+ // [function]
17
+
18
+ const parse = function (str) {
19
+ return dom.sanitize(marked.parse(stripLeadingInvisible(str)));
20
+ };
21
+
22
+ // [export]
23
+
24
+ module.exports = { parse };
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+
3
+ // [function]
4
+
5
+ const pruneEmpty = function (value) {
6
+ /* eslint-disable no-undefined */
7
+ if (value === null || value === undefined) {
8
+ return undefined;
9
+ }
10
+ if (Array.isArray(value)) {
11
+ const cleaned = value.map(pruneEmpty).filter((v) => v !== undefined);
12
+ return cleaned.length ? cleaned : undefined;
13
+ }
14
+ if (typeof value === "object") {
15
+ const cleaned = Object.fromEntries(
16
+ Object.entries(value)
17
+ .map(([k, v]) => [k, pruneEmpty(v)])
18
+ .filter(([, v]) => v !== undefined),
19
+ );
20
+ return Object.keys(cleaned).length ? cleaned : undefined;
21
+ }
22
+ return value;
23
+ /* eslint-enable no-undefined */
24
+ };
25
+
26
+ // [export]
27
+
28
+ module.exports = { pruneEmpty };
package/src/util/os.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const os = require("node:os");
6
+ const path = require("node:path");
7
+
8
+ // [use]
9
+
10
+ const homedir = os.homedir;
11
+ const join = path.join;
12
+
13
+ // [function]
14
+
15
+ const configDir = function () {
16
+ if (process.platform === "win32") {
17
+ return process.env.APPDATA;
18
+ }
19
+ return join(homedir(), ".config");
20
+ };
21
+
22
+ // [export]
23
+
24
+ module.exports = { configDir };
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const TOML = require("@iarna/toml");
6
+
7
+ // [function]
8
+
9
+ const write = function (data) {
10
+ process.stdout.write(data.trim().concat("\n"));
11
+ };
12
+
13
+ // [function]
14
+
15
+ const printJson = function (data) {
16
+ write(JSON.stringify(data, null, 2));
17
+ };
18
+
19
+ // [function]
20
+
21
+ const printToml = function (data, key = "value") {
22
+ const field = key.split(".").at(-1);
23
+ if (data === null) {
24
+ write(`# ${field} = null`);
25
+ return;
26
+ }
27
+ try {
28
+ write(TOML.stringify(data));
29
+ } catch (ex) {
30
+ write(TOML.stringify({ [field]: data }));
31
+ }
32
+ };
33
+
34
+ // [function]
35
+
36
+ const print = function (data, { key, toml } = {}) {
37
+ if (toml) {
38
+ printToml(data, key);
39
+ } else {
40
+ printJson(data);
41
+ }
42
+ };
43
+
44
+ // [export]
45
+
46
+ module.exports = { print, printJson, printToml };
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ // [function]
4
+
5
+ const aConcat = function (ls) {
6
+ return Array.prototype.concat.bind(ls);
7
+ };
8
+
9
+ // [function]
10
+
11
+ const pConcat = function (fn) {
12
+ return (ls) => fn(ls).then(aConcat(ls));
13
+ };
14
+
15
+ // [function]
16
+
17
+ const pReduce = function (p, fn) {
18
+ return p.then(pConcat(fn));
19
+ };
20
+
21
+ // [function]
22
+
23
+ const serial = function (fns) {
24
+ return fns.reduce(pReduce, Promise.resolve([]));
25
+ };
26
+
27
+ // [export]
28
+
29
+ module.exports = { serial };
package/src/util/rc.js ADDED
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const path = require("node:path");
6
+
7
+ // [import]
8
+
9
+ const rc = require("rc");
10
+
11
+ // [use]
12
+
13
+ const join = path.join;
14
+ const resolve = path.resolve;
15
+
16
+ // [function]
17
+
18
+ const cwd = function (argv, option) {
19
+ let dir = ".";
20
+ const idx = argv.findIndex((arg) => option.includes(arg));
21
+ if (idx !== -1 && argv[idx + 1] && !argv[idx + 1].startsWith("-")) {
22
+ dir = argv[idx + 1];
23
+ }
24
+ return dir;
25
+ };
26
+
27
+ // [function]
28
+
29
+ const config = function (cmd, argv, option) {
30
+ return join(resolve(process.cwd(), cwd(argv, option)), `.${cmd}rc`);
31
+ };
32
+
33
+ // [function]
34
+
35
+ const findRc = function (cmd, subcmd, argv, option) {
36
+ return rc(cmd, {}, { config: config(cmd, argv, option) })[subcmd] || {};
37
+ };
38
+
39
+ // [export]
40
+
41
+ module.exports = { findRc };
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const pkg = require("../../package.json");
6
+
7
+ // [declare]
8
+
9
+ const CLIENT_ID = "cli";
10
+ const CLIENT_VERSION = pkg.version;
11
+
12
+ // [function]
13
+
14
+ const parse = function (resp) {
15
+ return resp.json().then((json) => {
16
+ if (!resp.ok) {
17
+ throw new Error(json.data.message);
18
+ }
19
+ return json.data;
20
+ });
21
+ };
22
+
23
+ // [function]
24
+
25
+ const send = function (method, url, body, { token } = {}) {
26
+ const headers = {
27
+ "X-Arc-Id": CLIENT_ID,
28
+ "X-Arc-Version": CLIENT_VERSION,
29
+ };
30
+ if (token) {
31
+ headers.authorization = `Bearer ${token}`;
32
+ }
33
+ return fetch(url, { body, headers, method }).then(parse);
34
+ };
35
+
36
+ // [function]
37
+
38
+ const get = function (url, options) {
39
+ return send("GET", url, null, options);
40
+ };
41
+
42
+ // [function]
43
+
44
+ const post = function (url, body, options) {
45
+ return send("POST", url, body, options);
46
+ };
47
+
48
+ // [function]
49
+
50
+ const patch = function (url, body, options) {
51
+ return send("PATCH", url, body, options);
52
+ };
53
+
54
+ // [function]
55
+
56
+ const del = function (url, options) {
57
+ return send("DELETE", url, null, options);
58
+ };
59
+
60
+ // [export]
61
+
62
+ module.exports = { del, get, patch, post };
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ // [function]
4
+
5
+ const format = function (name, error) {
6
+ return error.issues.map(
7
+ (issue) => `${name}.${issue.path.join(".") || "value"}: ${issue.message}`,
8
+ );
9
+ };
10
+
11
+ // [function]
12
+
13
+ const validator = function (name, schema) {
14
+ return function (value, conf) {
15
+ const result = schema(value, conf).safeParse(value);
16
+ if (!result.success) {
17
+ return { issues: format(name, result.error) };
18
+ }
19
+ return { issues: [] };
20
+ };
21
+ };
22
+
23
+ // [export]
24
+
25
+ module.exports = { validator };
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ // [function]
4
+
5
+ const stripLeadingInvisible = function (str) {
6
+ return String(str).replace(
7
+ // eslint-disable-next-line no-misleading-character-class
8
+ /^[\u200B\u200C\u200D\u2060\u2061\u2062\u2063\uFEFF]+/u,
9
+ "",
10
+ );
11
+ };
12
+
13
+ // [function]
14
+
15
+ const trimWhitespace = function (str) {
16
+ return String(str).trim();
17
+ };
18
+
19
+ // [export]
20
+
21
+ module.exports = { stripLeadingInvisible, trimWhitespace };