arcadible 0.1.4 → 0.1.6

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.
@@ -0,0 +1,67 @@
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 imageCheck = require("../../util/image").check;
14
+ const videoCheck = require("../../util/video").check;
15
+
16
+ // [use]
17
+
18
+ const isAbsolute = path.isAbsolute;
19
+ const join = path.join;
20
+ const pathExistsSync = fs.pathExistsSync; // eslint-disable-line no-sync
21
+ const relative = path.relative;
22
+ const resolve = path.resolve;
23
+
24
+ // [function]
25
+
26
+ const exists = function (root, target) {
27
+ return pathExistsSync(join(root, target));
28
+ };
29
+
30
+ // [function]
31
+
32
+ const image = function (root, target, opts) {
33
+ return imageCheck(join(root, target), opts);
34
+ };
35
+
36
+ // [function]
37
+
38
+ const isIn = function (root, target, parent) {
39
+ const parentRoot = resolve(root, parent);
40
+ const targetPath = resolve(root, target);
41
+ const rel = relative(parentRoot, targetPath);
42
+ return rel && !rel.startsWith("..") && !isAbsolute(rel);
43
+ };
44
+
45
+ // [function]
46
+
47
+ const isOut = function (root, target) {
48
+ const rootPath = resolve(root);
49
+ const targetPath = resolve(root, target);
50
+ return targetPath === rootPath || targetPath.startsWith(rootPath + path.sep);
51
+ };
52
+
53
+ // [function]
54
+
55
+ const isRelative = function (root, target) {
56
+ return !isAbsolute(target);
57
+ };
58
+
59
+ // [function]
60
+
61
+ const video = function (root, target, opts) {
62
+ return videoCheck(join(root, target), opts);
63
+ };
64
+
65
+ // [export]
66
+
67
+ module.exports = { exists, image, isIn, isOut, isRelative, video };
@@ -0,0 +1,47 @@
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
+ // [use]
12
+
13
+ const isAbsolute = path.isAbsolute;
14
+ const join = path.join;
15
+ const pathExistsSync = fs.pathExistsSync; // eslint-disable-line no-sync
16
+ const resolve = path.resolve;
17
+
18
+ // [function]
19
+
20
+ const exists = function (root, target) {
21
+ return pathExistsSync(join(root, target));
22
+ };
23
+
24
+ // [function]
25
+
26
+ const isOut = function (root, target) {
27
+ const rootPath = resolve(root);
28
+ const targetPath = resolve(root, target);
29
+ return targetPath === rootPath || targetPath.startsWith(rootPath + path.sep);
30
+ };
31
+
32
+ // [function]
33
+
34
+ const isRelative = function (root, target) {
35
+ return !isAbsolute(target);
36
+ };
37
+
38
+ // [function]
39
+
40
+ const isReserved = function (root, target, parent) {
41
+ const targetPath = resolve(root, target);
42
+ return !targetPath.split(path.sep).includes(parent);
43
+ };
44
+
45
+ // [export]
46
+
47
+ module.exports = { exists, isOut, isRelative, isReserved };
@@ -15,32 +15,35 @@ const cfginput = require("../config/input");
15
15
  const cfgorientation = require("../config/orientation");
16
16
  const cfgurl = require("../config/url");
17
17
  const cfgvisibility = require("../config/visibility");
18
+ const check = require("./check/game");
18
19
  const dom = require("../util/dom");
20
+ const strs = require("../util/strs");
19
21
 
20
22
  // [use]
21
23
 
24
+ const existsCheck = check.exists;
25
+ const imageCheck = check.image;
26
+ const indentLines = strs.indentLines;
27
+ const isInCheck = check.isIn;
28
+ const isOutCheck = check.isOut;
29
+ const isRelativeCheck = check.isRelative;
22
30
  const validSemver = semver.valid;
31
+ const videoCheck = check.video;
23
32
 
24
- // [function]
33
+ // [declare]
25
34
 
26
- const reference = function (field) {
27
- return chalk.cyan.underline(`${cfgurl.MANIFEST}#${field}`);
28
- };
35
+ const MEDIA = ".arcadible";
29
36
 
30
37
  // [function]
31
38
 
32
- const indent = function (text, spaces = 2) {
33
- const pad = " ".repeat(spaces);
34
- return text
35
- .split("\n")
36
- .map((line) => pad + line)
37
- .join("\n");
39
+ const reference = function (field) {
40
+ return chalk.cyan.underline(`${cfgurl.MANIFEST}#game.${field}`);
38
41
  };
39
42
 
40
43
  // [function]
41
44
 
42
45
  const message = function (field, msg) {
43
- return { message: [msg, indent(reference(field))].join("\n") };
46
+ return { message: [msg, indentLines(reference(field))].join("\n") };
44
47
  };
45
48
 
46
49
  // [function]
@@ -58,20 +61,55 @@ const zText = function (field, { max, min }) {
58
61
 
59
62
  // [function]
60
63
 
61
- const zPng = function () {
62
- return z.string();
64
+ const zMedia = function (field, root) {
65
+ return z
66
+ .string()
67
+ .min(1)
68
+ .refine(
69
+ (file) => isRelativeCheck(root, file),
70
+ message(field, "Must be a relative path."),
71
+ )
72
+ .refine(
73
+ (file) => isOutCheck(root, file),
74
+ message(field, "Must be within the project."),
75
+ )
76
+ .refine(
77
+ (file) => isInCheck(root, file, MEDIA),
78
+ message(field, `Must be within ${MEDIA}.`),
79
+ )
80
+ .refine((file) => existsCheck(root, file), message(field, "Must exist."));
81
+ };
82
+
83
+ // [function]
84
+
85
+ const zPng = function (field, root, opts) {
86
+ return zMedia(field, root).refine(
87
+ (file) => imageCheck(root, file, { ...opts, type: "image/png" }),
88
+ message(field, "Invalid PNG."),
89
+ );
63
90
  };
64
91
 
65
92
  // [function]
66
93
 
67
- const zSvg = function () {
68
- return z.string();
94
+ const zSvg = function (field, root, opts) {
95
+ return zMedia(field, root).refine(
96
+ (file) => imageCheck(root, file, { ...opts, type: "image/svg+xml" }),
97
+ message(field, "Invalid SVG."),
98
+ );
69
99
  };
70
100
 
71
101
  // [function]
72
102
 
73
- const zMp4 = function () {
74
- return z.string();
103
+ const zMp4 = function (field, root, opts) {
104
+ return zMedia(field, root).refine(
105
+ (file) =>
106
+ videoCheck(root, file, {
107
+ maxDuration: 10,
108
+ ...opts,
109
+ type: "video/mp4",
110
+ }),
111
+ message(field, "Invalid MP4."),
112
+ );
75
113
  };
76
114
 
77
115
  // [function]
@@ -97,49 +135,77 @@ const zYouTube = function () {
97
135
  // [function]
98
136
 
99
137
  const zAudience = function () {
100
- // const field = "game.audience";
138
+ // const field = "audience";
101
139
  return z.optional(z.enum(cfgaudience));
102
140
  };
103
141
 
104
142
  // [function]
105
143
 
106
- const zBanner = function () {
107
- // const field = "game.banner";
108
- return z.optional(zPng());
144
+ const zBanner = function (root) {
145
+ const field = "banner";
146
+ return z.optional(
147
+ zPng(field, root, {
148
+ aspect: "2:1",
149
+ maxSize: 2 * 1024 * 1024,
150
+ minHeight: 512,
151
+ minWidth: 1024,
152
+ }),
153
+ );
109
154
  };
110
155
 
111
156
  // [function]
112
157
 
113
- const zBannerVideo = function () {
114
- // const field = "game.banner_video";
115
- return z.optional(zMp4());
158
+ const zBannerVideo = function (root) {
159
+ const field = "banner_video";
160
+ return z.optional(
161
+ zMp4(field, root, {
162
+ aspect: "2:1",
163
+ maxSize: 5 * 1024 * 1024,
164
+ minHeight: 512,
165
+ minWidth: 1024,
166
+ }),
167
+ );
116
168
  };
117
169
 
118
170
  // [function]
119
171
 
120
172
  const zBio = function () {
121
- const field = "game.bio";
173
+ const field = "bio";
122
174
  return z.optional(zText(field, { max: 192, min: 1 }));
123
175
  };
124
176
 
125
177
  // [function]
126
178
 
127
- const zCover = function () {
128
- // const field = "game.cover";
129
- return z.optional(zPng());
179
+ const zCover = function (root) {
180
+ const field = "cover";
181
+ return z.optional(
182
+ zPng(field, root, {
183
+ aspect: "3:2",
184
+ maxSize: 2 * 1024 * 1024,
185
+ minHeight: 512,
186
+ minWidth: 768,
187
+ }),
188
+ );
130
189
  };
131
190
 
132
191
  // [function]
133
192
 
134
- const zCoverVideo = function () {
135
- // const field = "game.cover_video";
136
- return z.optional(zMp4());
193
+ const zCoverVideo = function (root) {
194
+ const field = "cover_video";
195
+ return z.optional(
196
+ zMp4(field, root, {
197
+ aspect: "3:2",
198
+ maxSize: 5 * 1024 * 1024,
199
+ minHeight: 512,
200
+ minWidth: 768,
201
+ }),
202
+ );
137
203
  };
138
204
 
139
205
  // [function]
140
206
 
141
207
  const zDescription = function () {
142
- const field = "game.description";
208
+ const field = "description";
143
209
  return z.optional(
144
210
  z
145
211
  .string()
@@ -159,22 +225,32 @@ const zDescription = function () {
159
225
 
160
226
  // [function]
161
227
 
162
- const zGallery = function () {
163
- // const field = "game.gallery";
164
- return z.optional(z.array(zPng()).min(1).max(12));
228
+ const zGallery = function (root) {
229
+ const field = "gallery";
230
+ return z.optional(
231
+ z
232
+ .array(
233
+ z.union([
234
+ zPng(field, root, { maxSize: 2 * 1024 * 1024 }),
235
+ zMp4(field, root, { maxSize: 5 * 1024 * 1024 }),
236
+ ]),
237
+ )
238
+ .min(1)
239
+ .max(12),
240
+ );
165
241
  };
166
242
 
167
243
  // [function]
168
244
 
169
245
  const zGenres = function () {
170
- // const field = "game.genres";
246
+ // const field = "genres";
171
247
  return z.optional(z.array(z.enum(cfggenre)).max(3));
172
248
  };
173
249
 
174
250
  // [function]
175
251
 
176
252
  const zHandle = function () {
177
- const field = "game.handle";
253
+ const field = "handle";
178
254
  return z.optional(
179
255
  z
180
256
  .string()
@@ -187,85 +263,126 @@ const zHandle = function () {
187
263
 
188
264
  // [function]
189
265
 
190
- const zIcon = function () {
191
- // const field = "game.icon";
192
- return z.optional(zPng());
266
+ const zIcon = function (root) {
267
+ const field = "icon";
268
+ return z.optional(
269
+ zPng(field, root, {
270
+ aspect: "1:1",
271
+ maxSize: 2 * 1024 * 1024,
272
+ minHeight: 512,
273
+ minWidth: 512,
274
+ }),
275
+ );
193
276
  };
194
277
 
195
278
  // [function]
196
279
 
197
- const zIconVideo = function () {
198
- // const field = "game.icon_video";
199
- return z.optional(zMp4());
280
+ const zIconVideo = function (root) {
281
+ const field = "icon_video";
282
+ return z.optional(
283
+ zMp4(field, root, {
284
+ aspect: "1:1",
285
+ maxSize: 5 * 1024 * 1024,
286
+ minHeight: 512,
287
+ minWidth: 512,
288
+ }),
289
+ );
290
+ };
291
+
292
+ // [function]
293
+
294
+ const zId = function () {
295
+ const field = "id";
296
+ return z
297
+ .string()
298
+ .uuid(message(field, "Must be a valid UUID."))
299
+ .refine(
300
+ (value) => value === value.toLowerCase(),
301
+ message(field, "Must use lowercase characters."),
302
+ );
200
303
  };
201
304
 
202
305
  // [function]
203
306
 
204
307
  const zInputs = function () {
205
- // const field = "game.inputs";
308
+ // const field = "inputs";
206
309
  return z.optional(z.array(z.enum(cfginput)).max(3));
207
310
  };
208
311
 
209
312
  // [function]
210
313
 
211
314
  const zOrientation = function () {
212
- // const field = "game.orientation";
315
+ // const field = "orientation";
213
316
  return z.optional(z.enum(cfgorientation));
214
317
  };
215
318
 
216
319
  // [function]
217
320
 
218
- const zPoster = function () {
219
- // const field = "game.poster";
220
- return z.optional(zPng());
321
+ const zPoster = function (root) {
322
+ const field = "poster";
323
+ return z.optional(
324
+ zPng(field, root, {
325
+ aspect: "2:3",
326
+ maxSize: 2 * 1024 * 1024,
327
+ minHeight: 768,
328
+ minWidth: 512,
329
+ }),
330
+ );
221
331
  };
222
332
 
223
333
  // [function]
224
334
 
225
- const zPosterVideo = function () {
226
- // const field = "game.poster_video";
227
- return z.optional(zMp4());
335
+ const zPosterVideo = function (root) {
336
+ const field = "poster_video";
337
+ return z.optional(
338
+ zMp4(field, root, {
339
+ aspect: "2:3",
340
+ maxSize: 5 * 1024 * 1024,
341
+ minHeight: 768,
342
+ minWidth: 512,
343
+ }),
344
+ );
228
345
  };
229
346
 
230
347
  // [function]
231
348
 
232
349
  const zPrice = function () {
233
- // const field = "game.price";
350
+ // const field = "price";
234
351
  return z.optional(z.int().min(0).max(25));
235
352
  };
236
353
 
237
354
  // [function]
238
355
 
239
356
  const zShortTitle = function () {
240
- const field = "game.short_title";
357
+ const field = "short_title";
241
358
  return z.optional(zText(field, { max: 16, min: 1 }));
242
359
  };
243
360
 
244
361
  // [function]
245
362
 
246
363
  const zTagline = function () {
247
- const field = "game.tagline";
364
+ const field = "tagline";
248
365
  return z.optional(zText(field, { max: 96, min: 1 }));
249
366
  };
250
367
 
251
368
  // [function]
252
369
 
253
370
  const zTitle = function () {
254
- const field = "game.title";
371
+ const field = "title";
255
372
  return z.optional(zText(field, { max: 64, min: 1 }));
256
373
  };
257
374
 
258
375
  // [function]
259
376
 
260
377
  const zTrailer = function () {
261
- const field = "game.trailer";
378
+ const field = "trailer";
262
379
  return z
263
380
  .string()
264
381
  .max(255)
265
382
  .refine(
266
383
  (url) =>
267
384
  zVimeo().safeParse(url).success || zYouTube().safeParse(url).success,
268
- message(field, "Must be a YouTube (or Viemo) URL."),
385
+ message(field, "Must be a YouTube (or Vimeo) URL."),
269
386
  )
270
387
  .optional();
271
388
  };
@@ -273,7 +390,7 @@ const zTrailer = function () {
273
390
  // [function]
274
391
 
275
392
  const zVersion = function () {
276
- const field = "game.version";
393
+ const field = "version";
277
394
  return z
278
395
  .string()
279
396
  .refine(
@@ -285,22 +402,32 @@ const zVersion = function () {
285
402
  // [function]
286
403
 
287
404
  const zVisibility = function () {
288
- // const field = "game.visibility";
405
+ // const field = "visibility";
289
406
  return z.optional(z.enum(cfgvisibility));
290
407
  };
291
408
 
292
409
  // [function]
293
410
 
294
- const zWordmark = function () {
295
- // const field = "game.wordmark";
296
- return z.optional(zSvg());
411
+ const zWordmark = function (root) {
412
+ const field = "wordmark";
413
+ return z.optional(
414
+ zSvg(field, root, {
415
+ aspect: "1:1",
416
+ maxSize: 512 * 1024,
417
+ }),
418
+ );
297
419
  };
298
420
 
299
421
  // [function]
300
422
 
301
- const zWordmarkAnimated = function () {
302
- // const field = "game.wordmark_animated";
303
- return z.optional(zSvg());
423
+ const zWordmarkAnimated = function (root) {
424
+ const field = "wordmark_animated";
425
+ return z.optional(
426
+ zSvg(field, root, {
427
+ aspect: "1:1",
428
+ maxSize: 1024 * 1024,
429
+ }),
430
+ );
304
431
  };
305
432
 
306
433
  // [function]
@@ -320,6 +447,7 @@ const main = function (value, { root }) {
320
447
  handle: zHandle(),
321
448
  icon: zIcon(root),
322
449
  icon_video: zIconVideo(root),
450
+ id: zId(),
323
451
  inputs: zInputs(),
324
452
  orientation: zOrientation(),
325
453
  poster: zPoster(root),
@@ -2,24 +2,22 @@
2
2
 
3
3
  // [import]
4
4
 
5
- const path = require("node:path");
6
-
7
- // [import]
8
-
9
5
  const chalk = require("chalk");
10
- const fs = require("fs-extra");
11
6
  const z = require("zod");
12
7
 
13
8
  // [import]
14
9
 
15
10
  const cfgurl = require("../config/url");
11
+ const check = require("./check/workflow");
12
+ const strs = require("../util/strs");
16
13
 
17
14
  // [use]
18
15
 
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;
16
+ const existsCheck = check.exists;
17
+ const indentLines = strs.indentLines;
18
+ const isOutCheck = check.isOut;
19
+ const isRelativeCheck = check.isRelative;
20
+ const isReservedCheck = check.isReserved;
23
21
 
24
22
  // [declare]
25
23
 
@@ -28,78 +26,50 @@ const MEDIA = ".arcadible";
28
26
  // [function]
29
27
 
30
28
  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");
29
+ return chalk.cyan.underline(`${cfgurl.MANIFEST}#workflow.${field}`);
42
30
  };
43
31
 
44
32
  // [function]
45
33
 
46
34
  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;
35
+ return { message: [msg, indentLines(reference(field))].join("\n") };
67
36
  };
68
37
 
69
38
  // [function]
70
39
 
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
40
+ const zMedia = function (field, root, { skipBuild }) {
41
+ return z
80
42
  .string()
81
43
  .min(1)
82
- .refine((dir) => !isAbsolute(dir), message(field, "Must be relative."))
83
44
  .refine(
84
- (dir) => !isOutPath(root, dir),
45
+ (file) => isRelativeCheck(root, file),
46
+ message(field, "Must be a relative path."),
47
+ )
48
+ .refine(
49
+ (file) => isOutCheck(root, file),
85
50
  message(field, "Must be within the project."),
86
51
  )
87
52
  .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),
53
+ (file) => isReservedCheck(root, file, MEDIA),
54
+ message(field, `Must not contain ${MEDIA}.`),
55
+ )
56
+ .refine(
57
+ (file) => !skipBuild || existsCheck(root, file),
94
58
  message(field, "Must exist."),
95
59
  );
96
- }
97
- return zz;
60
+ };
61
+
62
+ // [function]
63
+
64
+ const zDeployDir = function (root, { skip_build: skipBuild }) {
65
+ const field = "deploy_dir";
66
+ return zMedia(field, root, { skipBuild });
98
67
  };
99
68
 
100
69
  // [function]
101
70
 
102
71
  const zSkipBuild = function () {
72
+ // const field = "skip_build";
103
73
  return z.boolean();
104
74
  };
105
75
 
package/src/user.js CHANGED
@@ -17,10 +17,10 @@ const os = require("./util/os");
17
17
  // [use]
18
18
 
19
19
  const configDir = os.configDir;
20
+ const deepCompact = objs.deepCompact;
20
21
  const join = path.join;
21
22
  const mkdir = fs.promises.mkdir;
22
23
  const readFile = fs.promises.readFile;
23
- const sortObjs = objs.sort;
24
24
  const unlink = fs.promises.unlink;
25
25
  const writeFile = fs.promises.writeFile;
26
26
 
@@ -45,7 +45,7 @@ const expired = function (session) {
45
45
  const save = function (user) {
46
46
  return mkdir(dir, { recursive: true })
47
47
  .then(() =>
48
- writeFile(file, JSON.stringify(sortObjs(user), null, 2), "utf8"),
48
+ writeFile(file, JSON.stringify(deepCompact(user), null, 2), "utf8"),
49
49
  )
50
50
  .then(() => user);
51
51
  };