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,96 @@
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 objs = require("../util/objs");
14
+
15
+ // [use]
16
+
17
+ const extname = path.extname;
18
+ const join = path.join;
19
+ const pathExists = fs.pathExists;
20
+ const pathParse = path.parse;
21
+ const pruneEmpty = objs.pruneEmpty;
22
+ const readdir = fs.promises.readdir;
23
+
24
+ // [declare]
25
+
26
+ const ART = [
27
+ "banner.png",
28
+ "banner_video.mp4",
29
+ "cover.png",
30
+ "cover_video.mp4",
31
+ "icon.png",
32
+ "icon_video.mp4",
33
+ "poster.png",
34
+ "poster_video.mp4",
35
+ "wordmark.svg",
36
+ "wordmark_animated.svg",
37
+ ];
38
+
39
+ // [declare]
40
+
41
+ const MEDIA = ".arcadible";
42
+
43
+ // [function]
44
+
45
+ const handleArt = function (exists) {
46
+ return Object.fromEntries(
47
+ ART.map((f, i) => [pathParse(f).name, exists[i] ? join(MEDIA, f) : null]),
48
+ );
49
+ };
50
+
51
+ // [function]
52
+
53
+ const art = function (root) {
54
+ return (game) =>
55
+ Promise.all(ART.map((value) => pathExists(join(root, MEDIA, value))))
56
+ .then(handleArt)
57
+ .then((data) => ({ ...game, ...data }));
58
+ };
59
+
60
+ // [function]
61
+
62
+ const handleGallery = function (entries) {
63
+ return entries
64
+ .filter((f) => extname(f).toLowerCase() === ".png")
65
+ .map((f) => join(MEDIA, "gallery", f))
66
+ .sort();
67
+ };
68
+
69
+ // [function]
70
+
71
+ const gallery = function (root) {
72
+ const dir = join(root, MEDIA, "gallery");
73
+ return (game) =>
74
+ pathExists(dir)
75
+ .then((exists) => (exists ? readdir(dir) : []))
76
+ .then(handleGallery)
77
+ .then((data) => ({ ...game, gallery: data }));
78
+ };
79
+
80
+ // [function]
81
+
82
+ const parse = function (root) {
83
+ return new Promise((resolve, reject) => {
84
+ Promise.resolve({})
85
+ .then(art(root))
86
+ .then(gallery(root))
87
+ .then(pruneEmpty)
88
+ .then((game) => ({ game }))
89
+ .then(resolve)
90
+ .catch(reject);
91
+ });
92
+ };
93
+
94
+ // [export]
95
+
96
+ module.exports = { parse };
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const cfggame = require("../config/game");
6
+ const cfgworkflow = require("../config/workflow");
7
+ const manifest = require("./manifest");
8
+ const media = require("./media");
9
+ const promise = require("../util/promise");
10
+ const readme = require("./readme");
11
+
12
+ // [function]
13
+
14
+ const configGame = function (readmeGame, mediaGame, manifestGame) {
15
+ return {
16
+ ...cfggame,
17
+ ...(readmeGame || {}),
18
+ ...(mediaGame || {}),
19
+ ...(manifestGame || {}),
20
+ };
21
+ };
22
+
23
+ // [function]
24
+
25
+ const configWorkflow = function (manifestWorkflow) {
26
+ return {
27
+ ...cfgworkflow,
28
+ ...(manifestWorkflow || {}),
29
+ };
30
+ };
31
+
32
+ // [function]
33
+
34
+ const parse = function (root) {
35
+ const parsers = [
36
+ () => manifest.parse(root),
37
+ () => media.parse(root),
38
+ () => readme.parse(root),
39
+ ];
40
+ return promise
41
+ .serial(parsers)
42
+ .then(([manifestData, mediaData, readmeData]) => {
43
+ const game = configGame(
44
+ readmeData.game,
45
+ mediaData.game,
46
+ manifestData.game,
47
+ );
48
+ const workflow = configWorkflow(manifestData.workflow);
49
+ return { game, workflow };
50
+ });
51
+ };
52
+
53
+ // [export]
54
+
55
+ module.exports = { parse };
@@ -0,0 +1,144 @@
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 dom = require("../util/dom");
14
+ const MD = require("../util/md");
15
+ const objs = require("../util/objs");
16
+ const strs = require("../util/strs");
17
+
18
+ // [use]
19
+
20
+ const join = path.join;
21
+ const pruneEmpty = objs.pruneEmpty;
22
+ const readFile = fs.readFile;
23
+ const trimWhitespace = strs.trimWhitespace;
24
+
25
+ // [declare]
26
+
27
+ const README = "README.md";
28
+
29
+ // [function]
30
+
31
+ const splitTitle = function (str) {
32
+ let title = "";
33
+ let shortTitle = "";
34
+ const m = str.match(/^(?<title>\S.*?)(?:\s*\((?<shortTitle>[^)]*)\))?$/u);
35
+ if (m && m.groups.title) {
36
+ title = m.groups.title.trim();
37
+ }
38
+ if (m && m.groups.shortTitle) {
39
+ shortTitle = m.groups.shortTitle.trim();
40
+ }
41
+ return [title, shortTitle];
42
+ };
43
+
44
+ // [function]
45
+
46
+ const extractTitle = function (doc) {
47
+ const elTitle = doc.querySelector("h1");
48
+ let title = "";
49
+ let shortTitle = "";
50
+ if (elTitle) {
51
+ if (elTitle.textContent) {
52
+ [title, shortTitle] = splitTitle(elTitle.textContent);
53
+ }
54
+ }
55
+ return [title, shortTitle].map(trimWhitespace);
56
+ };
57
+
58
+ // [function]
59
+
60
+ const extractTagline = function (doc) {
61
+ const elTagline = doc.querySelector("h2");
62
+ let tagline = "";
63
+ if (elTagline) {
64
+ if (elTagline.textContent) {
65
+ tagline = elTagline.textContent;
66
+ }
67
+ }
68
+ return trimWhitespace(tagline);
69
+ };
70
+
71
+ // [function]
72
+
73
+ const extractBio = function (doc) {
74
+ const elBio = doc.querySelector("h2 + p");
75
+ let bio = "";
76
+ if (elBio) {
77
+ if (elBio.textContent) {
78
+ bio = elBio.textContent;
79
+ }
80
+ }
81
+ return trimWhitespace(bio);
82
+ };
83
+
84
+ // [function]
85
+
86
+ const extractDescription = function (doc) {
87
+ const hrs = doc.querySelectorAll("hr");
88
+ if (hrs.length < 2) {
89
+ return "";
90
+ }
91
+ const first = hrs[0];
92
+ const last = hrs[hrs.length - 1];
93
+ const parts = [];
94
+ let node = first.nextSibling;
95
+ while (node && node !== last) {
96
+ parts.push(node.outerHTML || node.textContent);
97
+ node = node.nextSibling;
98
+ }
99
+ return trimWhitespace(parts.join(""));
100
+ };
101
+
102
+ // [function]
103
+
104
+ const extract = function (html) {
105
+ const doc = new dom.DOM(html).window.document;
106
+ // eslint-disable-next-line camelcase
107
+ const [title, short_title] = extractTitle(doc);
108
+ const tagline = extractTagline(doc);
109
+ const bio = extractBio(doc);
110
+ const description = extractDescription(doc);
111
+ return Object.fromEntries(
112
+ // eslint-disable-next-line camelcase
113
+ Object.entries({ bio, description, short_title, tagline, title }).map(
114
+ ([k, v]) => [k, v || null],
115
+ ),
116
+ );
117
+ };
118
+
119
+ // [function]
120
+
121
+ const parseReadme = function (buf) {
122
+ try {
123
+ return extract(MD.parse(buf));
124
+ } catch (ex) {
125
+ throw new Error(`Unable to parse ${README}: ${ex.message}`, { cause: ex });
126
+ }
127
+ };
128
+
129
+ // [function]
130
+
131
+ const parse = function (root) {
132
+ return new Promise((resolve, reject) => {
133
+ readFile(join(root, README), "utf8")
134
+ .then(parseReadme)
135
+ .then(pruneEmpty)
136
+ .then((game) => ({ game }))
137
+ .then(resolve)
138
+ .catch(reject);
139
+ });
140
+ };
141
+
142
+ // [export]
143
+
144
+ module.exports = { parse };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ // [export]
4
+
5
+ module.exports = {
6
+ input: {
7
+ default: ".",
8
+ message: "Project",
9
+ },
10
+ };
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+
3
+ // [export]
4
+
5
+ module.exports = {
6
+ select: {
7
+ choices: [
8
+ {
9
+ description: "Blank starter template.",
10
+ name: "Blank",
11
+ value: "arcadible/starter",
12
+ },
13
+ {
14
+ description: "Three.js starter template.",
15
+ name: "Three.js",
16
+ value: "arcadible/starter-threejs",
17
+ },
18
+ {
19
+ description: "Phaser starter template.",
20
+ name: "Phaser",
21
+ value: "arcadible/starter-phaser",
22
+ },
23
+ {
24
+ description: "PixiJS starter template.",
25
+ name: "PixiJS",
26
+ value: "arcadible/starter-pixijs",
27
+ },
28
+ {
29
+ description: "Babylon.js starter template.",
30
+ name: "Babylon.js",
31
+ value: "arcadible/starter-babylonjs",
32
+ },
33
+ ],
34
+ default: "arcadible/starter",
35
+ message: "Template",
36
+ },
37
+ };
@@ -0,0 +1,324 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const chalk = require("chalk");
6
+ const z = require("zod");
7
+
8
+ // [import]
9
+
10
+ const cfgaudience = require("../config/audience");
11
+ const cfggenre = require("../config/genre");
12
+ const cfghtml = require("../config/html");
13
+ const cfginput = require("../config/input");
14
+ const cfgorientation = require("../config/orientation");
15
+ const cfgurl = require("../config/url");
16
+ const cfgvisibility = require("../config/visibility");
17
+ const dom = require("../util/dom");
18
+
19
+ // [function]
20
+
21
+ const reference = function (field) {
22
+ return chalk.cyan.underline(`${cfgurl.MANIFEST}#${field}`);
23
+ };
24
+
25
+ // [function]
26
+
27
+ const indent = function (text, spaces = 2) {
28
+ const pad = " ".repeat(spaces);
29
+ return text
30
+ .split("\n")
31
+ .map((line) => pad + line)
32
+ .join("\n");
33
+ };
34
+
35
+ // [function]
36
+
37
+ const message = function (field, msg) {
38
+ return { message: [msg, indent(reference(field))].join("\n") };
39
+ };
40
+
41
+ // [function]
42
+
43
+ const zText = function (field, { max, min }) {
44
+ return z
45
+ .string()
46
+ .min(min)
47
+ .max(max)
48
+ .regex(
49
+ /^[\p{L}0-9 .,!?'"():;-]+$/u,
50
+ message(field, "Contains invalid characters."),
51
+ );
52
+ };
53
+
54
+ // [function]
55
+
56
+ const zPng = function () {
57
+ return z.string();
58
+ };
59
+
60
+ // [function]
61
+
62
+ const zSvg = function () {
63
+ return z.string();
64
+ };
65
+
66
+ // [function]
67
+
68
+ const zMp4 = function () {
69
+ return z.string();
70
+ };
71
+
72
+ // [function]
73
+
74
+ const zVimeo = function () {
75
+ return z
76
+ .string()
77
+ .regex(
78
+ /^https:\/\/(?:www\.)?(?:vimeo\.com|player\.vimeo\.com\/video)\/([0-9]+)(?:[/?#].*)?$/iu,
79
+ );
80
+ };
81
+
82
+ // [function]
83
+
84
+ const zYouTube = function () {
85
+ return z
86
+ .string()
87
+ .regex(
88
+ /^https:\/\/(?:www\.)?(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([A-Za-z0-9_-]{11})(?:[?&#].*)?$/iu,
89
+ );
90
+ };
91
+
92
+ // [function]
93
+
94
+ const zAudience = function () {
95
+ // const field = "game.audience";
96
+ return z.optional(z.enum(cfgaudience));
97
+ };
98
+
99
+ // [function]
100
+
101
+ const zBanner = function () {
102
+ // const field = "game.banner";
103
+ return z.optional(zPng());
104
+ };
105
+
106
+ // [function]
107
+
108
+ const zBannerVideo = function () {
109
+ // const field = "game.banner_video";
110
+ return z.optional(zMp4());
111
+ };
112
+
113
+ // [function]
114
+
115
+ const zBio = function () {
116
+ const field = "game.bio";
117
+ return z.optional(zText(field, { max: 192, min: 1 }));
118
+ };
119
+
120
+ // [function]
121
+
122
+ const zCover = function () {
123
+ // const field = "game.cover";
124
+ return z.optional(zPng());
125
+ };
126
+
127
+ // [function]
128
+
129
+ const zCoverVideo = function () {
130
+ // const field = "game.cover_video";
131
+ return z.optional(zMp4());
132
+ };
133
+
134
+ // [function]
135
+
136
+ const zDescription = function () {
137
+ const field = "game.description";
138
+ return z.optional(
139
+ z
140
+ .string()
141
+ .min(1)
142
+ .max(4000)
143
+ .refine(
144
+ (str) =>
145
+ str ===
146
+ dom.sanitize(str, {
147
+ ALLOWED_ATTR: cfghtml.ALLOWED_ATTR,
148
+ ALLOWED_TAGS: cfghtml.ALLOWED_TAGS,
149
+ }),
150
+ message(field, "Contains invalid elements."),
151
+ ),
152
+ );
153
+ };
154
+
155
+ // [function]
156
+
157
+ const zGallery = function () {
158
+ // const field = "game.gallery";
159
+ return z.optional(z.array(zPng()).min(1).max(12));
160
+ };
161
+
162
+ // [function]
163
+
164
+ const zGenres = function () {
165
+ // const field = "game.genres";
166
+ return z.optional(z.array(z.enum(cfggenre)).max(3));
167
+ };
168
+
169
+ // [function]
170
+
171
+ const zHandle = function () {
172
+ const field = "game.handle";
173
+ return z.optional(
174
+ z
175
+ .string()
176
+ .regex(
177
+ /^[a-z0-9](?:(?![-]{2})[a-z0-9-]){1,22}[a-z0-9]$/u,
178
+ message(field, "Contains invalid characters."),
179
+ ),
180
+ );
181
+ };
182
+
183
+ // [function]
184
+
185
+ const zIcon = function () {
186
+ // const field = "game.icon";
187
+ return z.optional(zPng());
188
+ };
189
+
190
+ // [function]
191
+
192
+ const zIconVideo = function () {
193
+ // const field = "game.icon_video";
194
+ return z.optional(zMp4());
195
+ };
196
+
197
+ // [function]
198
+
199
+ const zInputs = function () {
200
+ // const field = "game.inputs";
201
+ return z.optional(z.array(z.enum(cfginput)).max(3));
202
+ };
203
+
204
+ // [function]
205
+
206
+ const zOrientation = function () {
207
+ // const field = "game.orientation";
208
+ return z.optional(z.enum(cfgorientation));
209
+ };
210
+
211
+ // [function]
212
+
213
+ const zPoster = function () {
214
+ // const field = "game.poster";
215
+ return z.optional(zPng());
216
+ };
217
+
218
+ // [function]
219
+
220
+ const zPosterVideo = function () {
221
+ // const field = "game.poster_video";
222
+ return z.optional(zMp4());
223
+ };
224
+
225
+ // [function]
226
+
227
+ const zPrice = function () {
228
+ // const field = "game.price";
229
+ return z.optional(z.int().min(0).max(25));
230
+ };
231
+
232
+ // [function]
233
+
234
+ const zShortTitle = function () {
235
+ const field = "game.short_title";
236
+ return z.optional(zText(field, { max: 16, min: 1 }));
237
+ };
238
+
239
+ // [function]
240
+
241
+ const zTagline = function () {
242
+ const field = "game.tagline";
243
+ return z.optional(zText(field, { max: 96, min: 1 }));
244
+ };
245
+
246
+ // [function]
247
+
248
+ const zTitle = function () {
249
+ const field = "game.title";
250
+ return z.optional(zText(field, { max: 64, min: 1 }));
251
+ };
252
+
253
+ // [function]
254
+
255
+ const zTrailer = function () {
256
+ const field = "game.trailer";
257
+ return z
258
+ .string()
259
+ .max(255)
260
+ .refine(
261
+ (url) =>
262
+ zVimeo().safeParse(url).success || zYouTube().safeParse(url).success,
263
+ message(field, "Must be a YouTube (or Viemo) URL."),
264
+ )
265
+ .optional();
266
+ };
267
+
268
+ // [function]
269
+
270
+ const zVisibility = function () {
271
+ // const field = "game.visibility";
272
+ return z.optional(z.enum(cfgvisibility));
273
+ };
274
+
275
+ // [function]
276
+
277
+ const zWordmark = function () {
278
+ // const field = "game.wordmark";
279
+ return z.optional(zSvg());
280
+ };
281
+
282
+ // [function]
283
+
284
+ const zWordmarkAnimated = function () {
285
+ // const field = "game.wordmark_animated";
286
+ return z.optional(zSvg());
287
+ };
288
+
289
+ // [function]
290
+
291
+ const main = function (value, { root }) {
292
+ /* eslint-disable camelcase */
293
+ return z.object({
294
+ audience: zAudience(),
295
+ banner: zBanner(root),
296
+ banner_video: zBannerVideo(root),
297
+ bio: zBio(),
298
+ cover: zCover(root),
299
+ cover_video: zCoverVideo(root),
300
+ description: zDescription(),
301
+ gallery: zGallery(root),
302
+ genres: zGenres(),
303
+ handle: zHandle(),
304
+ icon: zIcon(root),
305
+ icon_video: zIconVideo(root),
306
+ inputs: zInputs(),
307
+ orientation: zOrientation(),
308
+ poster: zPoster(root),
309
+ poster_video: zPosterVideo(root),
310
+ price: zPrice(),
311
+ short_title: zShortTitle(),
312
+ tagline: zTagline(),
313
+ title: zTitle(),
314
+ trailer: zTrailer(),
315
+ visibility: zVisibility(),
316
+ wordmark: zWordmark(root),
317
+ wordmark_animated: zWordmarkAnimated(root),
318
+ });
319
+ /* eslint-enable camelcase */
320
+ };
321
+
322
+ // [export]
323
+
324
+ module.exports = main;