arcadible 0.1.27 → 0.1.29
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/package.json +1 -1
- package/src/config/ai.js +5 -0
- package/src/config/art.js +2 -2
- package/src/config/doc.js +7 -0
- package/src/config/html.js +15 -21
- package/src/config/image.js +0 -1
- package/src/config/visibility.js +1 -1
- package/src/phrase.js +35 -2
- package/src/schema/check/game.js +16 -1
- package/src/schema/game.js +177 -118
- package/src/util/doc.js +65 -0
- package/src/util/dom.js +24 -5
- package/src/util/image.js +0 -10
- package/src/util/paths.js +19 -0
- package/src/util/schema.js +5 -1
- package/src/util/uris.js +11 -0
package/package.json
CHANGED
package/src/config/ai.js
ADDED
package/src/config/art.js
CHANGED
package/src/config/html.js
CHANGED
|
@@ -3,25 +3,19 @@
|
|
|
3
3
|
// [export]
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
"strong",
|
|
22
|
-
"b",
|
|
23
|
-
"em",
|
|
24
|
-
"i",
|
|
25
|
-
"br",
|
|
26
|
-
],
|
|
6
|
+
allowed: {
|
|
7
|
+
a: ["href", "title"],
|
|
8
|
+
blockquote: [],
|
|
9
|
+
em: [],
|
|
10
|
+
h3: [],
|
|
11
|
+
h4: [],
|
|
12
|
+
hr: [],
|
|
13
|
+
img: ["src", "alt", "title"],
|
|
14
|
+
li: [],
|
|
15
|
+
ol: [],
|
|
16
|
+
p: [],
|
|
17
|
+
strong: [],
|
|
18
|
+
ul: [],
|
|
19
|
+
video: ["src", "alt", "title"],
|
|
20
|
+
},
|
|
27
21
|
};
|
package/src/config/image.js
CHANGED
package/src/config/visibility.js
CHANGED
package/src/phrase.js
CHANGED
|
@@ -34,6 +34,36 @@ const cmdValidate = function () {
|
|
|
34
34
|
|
|
35
35
|
// [function]
|
|
36
36
|
|
|
37
|
+
const docAttrInvalid = function (tag, attr, value) {
|
|
38
|
+
return `Value '${value}' is not valid for the '${attr}' attribute on the '${tag}' tag.`;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// [function]
|
|
42
|
+
|
|
43
|
+
const docAttrUnallowed = function (tag, attr) {
|
|
44
|
+
return `Attribute '${attr}' is not permitted on the '${tag}' tag.`;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// [function]
|
|
48
|
+
|
|
49
|
+
const docSanitizeFailed = function () {
|
|
50
|
+
return "Document contains invalid elements.";
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// [function]
|
|
54
|
+
|
|
55
|
+
const docTagUnallowed = function (tag) {
|
|
56
|
+
return `Tag '${tag}' is not permitted.`;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// [function]
|
|
60
|
+
|
|
61
|
+
const docUnsupported = function () {
|
|
62
|
+
return "Document has an unsupported file type.";
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// [function]
|
|
66
|
+
|
|
37
67
|
const gameExists = function (idOrHandle) {
|
|
38
68
|
return `Game exists: ${idOrHandle}`;
|
|
39
69
|
};
|
|
@@ -355,8 +385,11 @@ const videoWidthMin = function (width) {
|
|
|
355
385
|
module.exports = {
|
|
356
386
|
cmdInit,
|
|
357
387
|
cmdInspect,
|
|
358
|
-
|
|
359
|
-
|
|
388
|
+
docAttrInvalid,
|
|
389
|
+
docAttrUnallowed,
|
|
390
|
+
docSanitizeFailed,
|
|
391
|
+
docTagUnallowed,
|
|
392
|
+
docUnsupported,
|
|
360
393
|
gameExists,
|
|
361
394
|
gameVersionExists,
|
|
362
395
|
gitChangesPresent,
|
package/src/schema/check/game.js
CHANGED
|
@@ -10,6 +10,7 @@ const fs = require("fs-extra");
|
|
|
10
10
|
|
|
11
11
|
// [import]
|
|
12
12
|
|
|
13
|
+
const docCheck = require("../../util/doc").check;
|
|
13
14
|
const imageCheck = require("../../util/image").check;
|
|
14
15
|
const videoCheck = require("../../util/video").check;
|
|
15
16
|
|
|
@@ -23,6 +24,12 @@ const resolve = path.resolve;
|
|
|
23
24
|
|
|
24
25
|
// [function]
|
|
25
26
|
|
|
27
|
+
const doc = function (root, str, opts) {
|
|
28
|
+
return docCheck(str, opts);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// [function]
|
|
32
|
+
|
|
26
33
|
const exists = function (root, target) {
|
|
27
34
|
return pathExistsSync(join(root, target));
|
|
28
35
|
};
|
|
@@ -64,4 +71,12 @@ const video = function (root, target, opts) {
|
|
|
64
71
|
|
|
65
72
|
// [export]
|
|
66
73
|
|
|
67
|
-
module.exports = {
|
|
74
|
+
module.exports = {
|
|
75
|
+
doc,
|
|
76
|
+
exists,
|
|
77
|
+
image,
|
|
78
|
+
isIn,
|
|
79
|
+
isOut,
|
|
80
|
+
isRelative,
|
|
81
|
+
video,
|
|
82
|
+
};
|
package/src/schema/game.js
CHANGED
|
@@ -8,7 +8,9 @@ const z = require("zod");
|
|
|
8
8
|
|
|
9
9
|
// [import]
|
|
10
10
|
|
|
11
|
+
const cfgai = require("../config/ai");
|
|
11
12
|
const cfgaudience = require("../config/audience");
|
|
13
|
+
const cfgdoc = require("../config/doc");
|
|
12
14
|
const cfggenre = require("../config/genre");
|
|
13
15
|
const cfghtml = require("../config/html");
|
|
14
16
|
const cfgimage = require("../config/image");
|
|
@@ -19,28 +21,41 @@ const cfgurl = require("../config/url");
|
|
|
19
21
|
const cfgvideo = require("../config/video");
|
|
20
22
|
const cfgvisibility = require("../config/visibility");
|
|
21
23
|
const check = require("./check/game");
|
|
22
|
-
const
|
|
24
|
+
const paths = require("../util/paths");
|
|
23
25
|
const phrase = require("../phrase");
|
|
24
26
|
const size = require("../util/size");
|
|
25
27
|
const strs = require("../util/strs");
|
|
28
|
+
const uris = require("../util/uris");
|
|
26
29
|
|
|
27
30
|
// [use]
|
|
28
31
|
|
|
32
|
+
const basefile = paths.basefile;
|
|
29
33
|
const cleanSemver = semver.clean;
|
|
34
|
+
const docCheck = check.doc;
|
|
30
35
|
const existsCheck = check.exists;
|
|
31
36
|
const imageCheck = check.image;
|
|
32
37
|
const indentLines = strs.indentLines;
|
|
33
38
|
const isInCheck = check.isIn;
|
|
34
39
|
const isOutCheck = check.isOut;
|
|
35
40
|
const isRelativeCheck = check.isRelative;
|
|
41
|
+
const isSafeUri = uris.isSafeUri;
|
|
36
42
|
const validSemver = semver.valid;
|
|
37
43
|
const videoCheck = check.video;
|
|
38
44
|
|
|
39
45
|
// [declare]
|
|
40
46
|
|
|
41
47
|
const HANDLE = cfgproduct.handle;
|
|
48
|
+
|
|
49
|
+
// [declare]
|
|
50
|
+
|
|
51
|
+
const GALLERY = `.${HANDLE}/gallery`;
|
|
42
52
|
const MEDIA = `.${HANDLE}`;
|
|
43
53
|
|
|
54
|
+
// [declare]
|
|
55
|
+
|
|
56
|
+
const MAX_SIZE_MP4 = size.mb(15);
|
|
57
|
+
const MAX_SIZE_PNG = size.mb(2);
|
|
58
|
+
|
|
44
59
|
// [function]
|
|
45
60
|
|
|
46
61
|
const reference = function (field) {
|
|
@@ -61,15 +76,62 @@ const message = function (field, lines) {
|
|
|
61
76
|
|
|
62
77
|
// [function]
|
|
63
78
|
|
|
64
|
-
const
|
|
79
|
+
const zSet = function (field, schema, { max, min }) {
|
|
80
|
+
return z
|
|
81
|
+
.array(schema)
|
|
82
|
+
.min(min, message(field, [`Must be at least: ${min} element`]))
|
|
83
|
+
.max(max, message(field, [`Must not exceed: ${max} elements`]))
|
|
84
|
+
.refine(
|
|
85
|
+
(items) => new Set(items).size === items.length,
|
|
86
|
+
message(field, ["Must not contain duplicate elements."]),
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// [function]
|
|
91
|
+
|
|
92
|
+
const zSetBasefile = function (field, schema, { max, min }) {
|
|
93
|
+
return z
|
|
94
|
+
.array(schema)
|
|
95
|
+
.min(min, message(field, [`Must be at least: ${min} file`]))
|
|
96
|
+
.max(max, message(field, [`Must not exceed: ${max} files`]))
|
|
97
|
+
.refine(
|
|
98
|
+
(items) => new Set(items.map(basefile)).size === items.length,
|
|
99
|
+
message(field, [
|
|
100
|
+
"Must not contain duplicate filenames (ignoring extensions).",
|
|
101
|
+
]),
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// [function]
|
|
106
|
+
|
|
107
|
+
const zString = function (field, re, { max, min }) {
|
|
65
108
|
return z
|
|
66
109
|
.string()
|
|
67
110
|
.min(min, message(field, [`Must be at least: ${min} character`]))
|
|
68
111
|
.max(max, message(field, [`Must not exceed: ${max} characters`]))
|
|
69
|
-
.regex(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
112
|
+
.regex(re, message(field, ["Contains invalid characters."]));
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// [function]
|
|
116
|
+
|
|
117
|
+
const zText = function (field, opts) {
|
|
118
|
+
const re = /^[\p{L}0-9 .,!?'"():;-]+$/u;
|
|
119
|
+
return zString(field, re, opts);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// [function]
|
|
123
|
+
|
|
124
|
+
const zHtml = function (field, root, { max, min, ...opts }) {
|
|
125
|
+
const re = /^[\p{L}\p{M}\p{N}\p{P}\p{S}\p{Zs}\r\n]*$/u;
|
|
126
|
+
return zString(field, re, { max, min }).superRefine((str, ctx) => {
|
|
127
|
+
const issues = docCheck(root, str, { ...opts, type: cfgdoc[".html"] });
|
|
128
|
+
if (issues.length) {
|
|
129
|
+
ctx.addIssue({
|
|
130
|
+
code: "custom",
|
|
131
|
+
...message(field, ["Invalid Markdown.", ...issues]),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
});
|
|
73
135
|
};
|
|
74
136
|
|
|
75
137
|
// [function]
|
|
@@ -109,13 +171,18 @@ const zPng = function (field, root, opts) {
|
|
|
109
171
|
|
|
110
172
|
// [function]
|
|
111
173
|
|
|
112
|
-
const
|
|
174
|
+
const zMp4 = function (field, root, opts) {
|
|
113
175
|
return zMedia(field, root).superRefine((file, ctx) => {
|
|
114
|
-
const issues =
|
|
176
|
+
const issues = videoCheck(root, file, {
|
|
177
|
+
maxDuration: 30,
|
|
178
|
+
minDuration: 3,
|
|
179
|
+
...opts,
|
|
180
|
+
type: cfgvideo[".mp4"],
|
|
181
|
+
});
|
|
115
182
|
if (issues.length) {
|
|
116
183
|
ctx.addIssue({
|
|
117
184
|
code: "custom",
|
|
118
|
-
...message(field, ["Invalid
|
|
185
|
+
...message(field, ["Invalid MP4.", ...issues]),
|
|
119
186
|
});
|
|
120
187
|
}
|
|
121
188
|
});
|
|
@@ -123,19 +190,27 @@ const zSvg = function (field, root, opts) {
|
|
|
123
190
|
|
|
124
191
|
// [function]
|
|
125
192
|
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
if (
|
|
193
|
+
const zPngMp4 = function (field, root, optsPng, optsMp4) {
|
|
194
|
+
const pngSchema = zPng(field, root, optsPng);
|
|
195
|
+
const mp4Schema = zMp4(field, root, optsMp4);
|
|
196
|
+
return z.string().superRefine((file, ctx) => {
|
|
197
|
+
let schema = null;
|
|
198
|
+
if (file?.endsWith(".png")) {
|
|
199
|
+
schema = pngSchema;
|
|
200
|
+
}
|
|
201
|
+
if (file?.endsWith(".mp4")) {
|
|
202
|
+
schema = mp4Schema;
|
|
203
|
+
}
|
|
204
|
+
if (!schema) {
|
|
135
205
|
ctx.addIssue({
|
|
136
206
|
code: "custom",
|
|
137
|
-
...message(field, ["
|
|
207
|
+
...message(field, ["Must be a PNG or MP4 file."]),
|
|
138
208
|
});
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const result = schema.safeParse(file);
|
|
212
|
+
if (!result.success) {
|
|
213
|
+
result.error.issues.forEach((issue) => ctx.addIssue(issue));
|
|
139
214
|
}
|
|
140
215
|
});
|
|
141
216
|
};
|
|
@@ -162,6 +237,13 @@ const zYouTube = function () {
|
|
|
162
237
|
|
|
163
238
|
// [function]
|
|
164
239
|
|
|
240
|
+
const zAi = function () {
|
|
241
|
+
// const field = "ai";
|
|
242
|
+
return z.optional(z.enum(cfgai));
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// [function]
|
|
246
|
+
|
|
165
247
|
const zAudience = function () {
|
|
166
248
|
// const field = "audience";
|
|
167
249
|
return z.optional(z.enum(cfgaudience));
|
|
@@ -174,7 +256,7 @@ const zBanner = function (root) {
|
|
|
174
256
|
return z.optional(
|
|
175
257
|
zPng(field, root, {
|
|
176
258
|
aspect: "2:1",
|
|
177
|
-
maxSize:
|
|
259
|
+
maxSize: MAX_SIZE_PNG,
|
|
178
260
|
minHeight: 512,
|
|
179
261
|
minWidth: 1024,
|
|
180
262
|
}),
|
|
@@ -188,7 +270,7 @@ const zBannerVideo = function (root) {
|
|
|
188
270
|
return z.optional(
|
|
189
271
|
zMp4(field, root, {
|
|
190
272
|
aspect: "2:1",
|
|
191
|
-
maxSize:
|
|
273
|
+
maxSize: MAX_SIZE_MP4,
|
|
192
274
|
minHeight: 512,
|
|
193
275
|
minWidth: 1024,
|
|
194
276
|
}),
|
|
@@ -209,7 +291,7 @@ const zCover = function (root) {
|
|
|
209
291
|
return z.optional(
|
|
210
292
|
zPng(field, root, {
|
|
211
293
|
aspect: "3:2",
|
|
212
|
-
maxSize:
|
|
294
|
+
maxSize: MAX_SIZE_PNG,
|
|
213
295
|
minHeight: 512,
|
|
214
296
|
minWidth: 768,
|
|
215
297
|
}),
|
|
@@ -223,7 +305,7 @@ const zCoverVideo = function (root) {
|
|
|
223
305
|
return z.optional(
|
|
224
306
|
zMp4(field, root, {
|
|
225
307
|
aspect: "3:2",
|
|
226
|
-
maxSize:
|
|
308
|
+
maxSize: MAX_SIZE_MP4,
|
|
227
309
|
minHeight: 512,
|
|
228
310
|
minWidth: 768,
|
|
229
311
|
}),
|
|
@@ -232,22 +314,22 @@ const zCoverVideo = function (root) {
|
|
|
232
314
|
|
|
233
315
|
// [function]
|
|
234
316
|
|
|
235
|
-
const zDescription = function () {
|
|
317
|
+
const zDescription = function (root) {
|
|
236
318
|
const field = "description";
|
|
237
319
|
return z.optional(
|
|
238
|
-
|
|
239
|
-
.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
320
|
+
zHtml(field, root, {
|
|
321
|
+
allowed: cfghtml.allowed,
|
|
322
|
+
hooks: {
|
|
323
|
+
href: (uri) => isSafeUri(uri),
|
|
324
|
+
src: (src) =>
|
|
325
|
+
isRelativeCheck(root, src) &&
|
|
326
|
+
isOutCheck(root, src) &&
|
|
327
|
+
isInCheck(root, src, GALLERY) &&
|
|
328
|
+
existsCheck(root, src),
|
|
329
|
+
},
|
|
330
|
+
max: 3000,
|
|
331
|
+
min: 1,
|
|
332
|
+
}),
|
|
251
333
|
);
|
|
252
334
|
};
|
|
253
335
|
|
|
@@ -256,48 +338,26 @@ const zDescription = function () {
|
|
|
256
338
|
const zGallery = function (root) {
|
|
257
339
|
const field = "gallery";
|
|
258
340
|
return z.optional(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
ctx.addIssue({
|
|
280
|
-
code: "custom",
|
|
281
|
-
...message(field, [`Invalid SVG: ${file}`, ...issues]),
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
} else if (file.endsWith(".mp4")) {
|
|
285
|
-
const issues = videoCheck(root, file, {
|
|
286
|
-
maxDuration: 30,
|
|
287
|
-
maxSize: size.mb(15),
|
|
288
|
-
minDuration: 3,
|
|
289
|
-
type: cfgvideo[".mp4"],
|
|
290
|
-
});
|
|
291
|
-
if (issues.length) {
|
|
292
|
-
ctx.addIssue({
|
|
293
|
-
code: "custom",
|
|
294
|
-
...message(field, [`Invalid MP4: ${file}`, ...issues]),
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}),
|
|
299
|
-
)
|
|
300
|
-
.max(12, message(field, ["Must not exceed: 12 elements"])),
|
|
341
|
+
zSetBasefile(
|
|
342
|
+
field,
|
|
343
|
+
zPngMp4(
|
|
344
|
+
field,
|
|
345
|
+
root,
|
|
346
|
+
// png
|
|
347
|
+
{
|
|
348
|
+
maxSize: MAX_SIZE_PNG,
|
|
349
|
+
minHeight: 512,
|
|
350
|
+
minWidth: 512,
|
|
351
|
+
},
|
|
352
|
+
// mp4
|
|
353
|
+
{
|
|
354
|
+
maxSize: MAX_SIZE_MP4,
|
|
355
|
+
minHeight: 512,
|
|
356
|
+
minWidth: 512,
|
|
357
|
+
},
|
|
358
|
+
),
|
|
359
|
+
{ max: 8 * 3, min: 1 },
|
|
360
|
+
),
|
|
301
361
|
);
|
|
302
362
|
};
|
|
303
363
|
|
|
@@ -305,11 +365,7 @@ const zGallery = function (root) {
|
|
|
305
365
|
|
|
306
366
|
const zGenres = function () {
|
|
307
367
|
const field = "genres";
|
|
308
|
-
return z.optional(
|
|
309
|
-
z
|
|
310
|
-
.array(z.enum(cfggenre))
|
|
311
|
-
.max(3, message(field, ["Must not exceed: 3 elements"])),
|
|
312
|
-
);
|
|
368
|
+
return z.optional(zSet(field, z.enum(cfggenre), { max: 3, min: 1 }));
|
|
313
369
|
};
|
|
314
370
|
|
|
315
371
|
// [function]
|
|
@@ -333,7 +389,7 @@ const zIcon = function (root) {
|
|
|
333
389
|
return z.optional(
|
|
334
390
|
zPng(field, root, {
|
|
335
391
|
aspect: "1:1",
|
|
336
|
-
maxSize:
|
|
392
|
+
maxSize: MAX_SIZE_PNG,
|
|
337
393
|
minHeight: 512,
|
|
338
394
|
minWidth: 512,
|
|
339
395
|
}),
|
|
@@ -347,7 +403,7 @@ const zIconVideo = function (root) {
|
|
|
347
403
|
return z.optional(
|
|
348
404
|
zMp4(field, root, {
|
|
349
405
|
aspect: "1:1",
|
|
350
|
-
maxSize:
|
|
406
|
+
maxSize: MAX_SIZE_MP4,
|
|
351
407
|
minHeight: 512,
|
|
352
408
|
minWidth: 512,
|
|
353
409
|
}),
|
|
@@ -376,11 +432,7 @@ const zId = function () {
|
|
|
376
432
|
|
|
377
433
|
const zInputs = function () {
|
|
378
434
|
const field = "inputs";
|
|
379
|
-
return z.optional(
|
|
380
|
-
z
|
|
381
|
-
.array(z.enum(cfginput))
|
|
382
|
-
.max(3, message(field, ["Must not exceed: 3 elements"])),
|
|
383
|
-
);
|
|
435
|
+
return z.optional(zSet(field, z.enum(cfginput), { max: 3, min: 1 }));
|
|
384
436
|
};
|
|
385
437
|
|
|
386
438
|
// [function]
|
|
@@ -397,7 +449,7 @@ const zPoster = function (root) {
|
|
|
397
449
|
return z.optional(
|
|
398
450
|
zPng(field, root, {
|
|
399
451
|
aspect: "2:3",
|
|
400
|
-
maxSize:
|
|
452
|
+
maxSize: MAX_SIZE_PNG,
|
|
401
453
|
minHeight: 768,
|
|
402
454
|
minWidth: 512,
|
|
403
455
|
}),
|
|
@@ -411,7 +463,7 @@ const zPosterVideo = function (root) {
|
|
|
411
463
|
return z.optional(
|
|
412
464
|
zMp4(field, root, {
|
|
413
465
|
aspect: "2:3",
|
|
414
|
-
maxSize:
|
|
466
|
+
maxSize: MAX_SIZE_MP4,
|
|
415
467
|
minHeight: 768,
|
|
416
468
|
minWidth: 512,
|
|
417
469
|
}),
|
|
@@ -439,6 +491,34 @@ const zShortTitle = function () {
|
|
|
439
491
|
|
|
440
492
|
// [function]
|
|
441
493
|
|
|
494
|
+
const zSplash = function (root) {
|
|
495
|
+
const field = "splash";
|
|
496
|
+
return z.optional(
|
|
497
|
+
zPng(field, root, {
|
|
498
|
+
aspect: "16:9",
|
|
499
|
+
maxSize: MAX_SIZE_PNG,
|
|
500
|
+
minHeight: 576,
|
|
501
|
+
minWidth: 1024,
|
|
502
|
+
}),
|
|
503
|
+
);
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// [function]
|
|
507
|
+
|
|
508
|
+
const zSplashVideo = function (root) {
|
|
509
|
+
const field = "splash_video";
|
|
510
|
+
return z.optional(
|
|
511
|
+
zMp4(field, root, {
|
|
512
|
+
aspect: "16:9",
|
|
513
|
+
maxSize: MAX_SIZE_MP4,
|
|
514
|
+
minHeight: 576,
|
|
515
|
+
minWidth: 1024,
|
|
516
|
+
}),
|
|
517
|
+
);
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// [function]
|
|
521
|
+
|
|
442
522
|
const zTagline = function () {
|
|
443
523
|
const field = "tagline";
|
|
444
524
|
return z.optional(zText(field, { max: 30 * 1, min: 1 }));
|
|
@@ -496,38 +576,17 @@ const zVisibility = function () {
|
|
|
496
576
|
|
|
497
577
|
// [function]
|
|
498
578
|
|
|
499
|
-
const zWordmark = function (root) {
|
|
500
|
-
const field = "wordmark";
|
|
501
|
-
return z.optional(
|
|
502
|
-
zSvg(field, root, {
|
|
503
|
-
maxSize: size.kb(512),
|
|
504
|
-
}),
|
|
505
|
-
);
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
// [function]
|
|
509
|
-
|
|
510
|
-
const zWordmarkAnimated = function (root) {
|
|
511
|
-
const field = "wordmark_animated";
|
|
512
|
-
return z.optional(
|
|
513
|
-
zSvg(field, root, {
|
|
514
|
-
maxSize: size.mb(1),
|
|
515
|
-
}),
|
|
516
|
-
);
|
|
517
|
-
};
|
|
518
|
-
|
|
519
|
-
// [function]
|
|
520
|
-
|
|
521
579
|
const main = function (value, { root }) {
|
|
522
580
|
/* eslint-disable camelcase */
|
|
523
581
|
return z.object({
|
|
582
|
+
ai: zAi(),
|
|
524
583
|
audience: zAudience(),
|
|
525
584
|
banner: zBanner(root),
|
|
526
585
|
banner_video: zBannerVideo(root),
|
|
527
586
|
bio: zBio(),
|
|
528
587
|
cover: zCover(root),
|
|
529
588
|
cover_video: zCoverVideo(root),
|
|
530
|
-
description: zDescription(),
|
|
589
|
+
description: zDescription(root),
|
|
531
590
|
gallery: zGallery(root),
|
|
532
591
|
genres: zGenres(),
|
|
533
592
|
handle: zHandle(),
|
|
@@ -540,13 +599,13 @@ const main = function (value, { root }) {
|
|
|
540
599
|
poster_video: zPosterVideo(root),
|
|
541
600
|
price: zPrice(),
|
|
542
601
|
short_title: zShortTitle(),
|
|
602
|
+
splash: zSplash(root),
|
|
603
|
+
splash_video: zSplashVideo(root),
|
|
543
604
|
tagline: zTagline(),
|
|
544
605
|
title: zTitle(),
|
|
545
606
|
trailer: zTrailer(),
|
|
546
607
|
version: zVersion(),
|
|
547
608
|
visibility: zVisibility(),
|
|
548
|
-
wordmark: zWordmark(root),
|
|
549
|
-
wordmark_animated: zWordmarkAnimated(root),
|
|
550
609
|
});
|
|
551
610
|
/* eslint-enable camelcase */
|
|
552
611
|
};
|
package/src/util/doc.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// [import]
|
|
4
|
+
|
|
5
|
+
const cfgdoc = require("../config/doc");
|
|
6
|
+
const dom = require("./dom");
|
|
7
|
+
const phrase = require("../phrase");
|
|
8
|
+
|
|
9
|
+
// [function]
|
|
10
|
+
|
|
11
|
+
const checkAttr = function (allowed, hooks, issues) {
|
|
12
|
+
return (node, event) => {
|
|
13
|
+
const nodeName = node.nodeName.toLowerCase();
|
|
14
|
+
const attrName = event.attrName;
|
|
15
|
+
const attrValue = event.attrValue;
|
|
16
|
+
const isAllowed = allowed[nodeName]?.includes(attrName);
|
|
17
|
+
const isValid = !hooks[attrName] || hooks[attrName](attrValue);
|
|
18
|
+
if (!isAllowed || !isValid) {
|
|
19
|
+
issues.push({ attrName, attrValue, isAllowed, isValid, nodeName });
|
|
20
|
+
event.keepAttr = false;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// [function]
|
|
26
|
+
|
|
27
|
+
const checkAllowed = function (str, allowed, hooks, issues) {
|
|
28
|
+
const attr = checkAttr(allowed, hooks, issues);
|
|
29
|
+
return str === dom.sanitize(str, { allowed, hooks: { attr } });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// [function]
|
|
33
|
+
|
|
34
|
+
const checkHtml = function (str, { allowed, hooks }) {
|
|
35
|
+
const issues = [];
|
|
36
|
+
if (!checkAllowed(str, allowed, hooks, issues)) {
|
|
37
|
+
if (issues) {
|
|
38
|
+
return issues.map((issue) => {
|
|
39
|
+
const { attrName, attrValue, isAllowed, isValid, nodeName } = issue;
|
|
40
|
+
if (!isAllowed) {
|
|
41
|
+
return Object.hasOwn(allowed, nodeName)
|
|
42
|
+
? phrase.docAttrUnallowed(nodeName, attrName)
|
|
43
|
+
: phrase.docTagUnallowed(nodeName);
|
|
44
|
+
}
|
|
45
|
+
return isValid
|
|
46
|
+
? phrase.docSanitizeFailed()
|
|
47
|
+
: phrase.docAttrInvalid(nodeName, attrName, attrValue);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return [phrase.docSanitizeFailed()];
|
|
51
|
+
}
|
|
52
|
+
return [];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// [function]
|
|
56
|
+
|
|
57
|
+
const check = function (str, { type, ...opts }) {
|
|
58
|
+
const checks = { [cfgdoc[".html"]]: checkHtml };
|
|
59
|
+
const checkDoc = checks[type];
|
|
60
|
+
return checkDoc ? checkDoc(str, opts) : [phrase.docUnsupported()];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// [export]
|
|
64
|
+
|
|
65
|
+
module.exports = { check };
|
package/src/util/dom.js
CHANGED
|
@@ -11,17 +11,36 @@ const JSDOM = jsdom.JSDOM;
|
|
|
11
11
|
|
|
12
12
|
// [function]
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
14
|
+
const sanitizeCustom = function (dom, str, cfg) {
|
|
15
|
+
const allowed = cfg.allowed;
|
|
16
|
+
const hooks = cfg.hooks;
|
|
17
|
+
if (hooks?.attr) {
|
|
18
|
+
dom.addHook("uponSanitizeAttribute", hooks.attr);
|
|
19
|
+
}
|
|
20
|
+
if (allowed) {
|
|
21
|
+
const attrs = new Set(Object.values(allowed).flatMap((arr) => arr));
|
|
22
|
+
const tags = Object.keys(allowed);
|
|
17
23
|
return dom.sanitize(str, {
|
|
18
|
-
ALLOWED_ATTR:
|
|
19
|
-
ALLOWED_TAGS:
|
|
24
|
+
ALLOWED_ATTR: attrs,
|
|
25
|
+
ALLOWED_TAGS: tags,
|
|
20
26
|
});
|
|
21
27
|
}
|
|
22
28
|
return dom.sanitize(str);
|
|
23
29
|
};
|
|
24
30
|
|
|
31
|
+
// [function]
|
|
32
|
+
|
|
33
|
+
const sanitizeBasic = function (dom, str) {
|
|
34
|
+
return dom.sanitize(str);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// [function]
|
|
38
|
+
|
|
39
|
+
const sanitize = function (str, cfg) {
|
|
40
|
+
const dom = dompurify(new JSDOM().window);
|
|
41
|
+
return cfg ? sanitizeCustom(dom, str, cfg) : sanitizeBasic(dom, str);
|
|
42
|
+
};
|
|
43
|
+
|
|
25
44
|
// [export]
|
|
26
45
|
|
|
27
46
|
module.exports = { DOM: JSDOM, sanitize };
|
package/src/util/image.js
CHANGED
|
@@ -71,19 +71,9 @@ const checkPng = function (file, { aspect, maxSize, minHeight, minWidth }) {
|
|
|
71
71
|
|
|
72
72
|
// [function]
|
|
73
73
|
|
|
74
|
-
const checkSvg = function (file, { maxSize }) {
|
|
75
|
-
if (!checkSize(file, maxSize)) {
|
|
76
|
-
return [phrase.imageSizeMax(formatBytes(maxSize))];
|
|
77
|
-
}
|
|
78
|
-
return [];
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// [function]
|
|
82
|
-
|
|
83
74
|
const check = function (file, { type, ...opts }) {
|
|
84
75
|
const checks = {
|
|
85
76
|
[cfgimage[".png"]]: checkPng,
|
|
86
|
-
[cfgimage[".svg"]]: checkSvg,
|
|
87
77
|
};
|
|
88
78
|
const checkImage = checks[type];
|
|
89
79
|
return checkImage && cfgimage[extname(file)] === type
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// [import]
|
|
4
|
+
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
// [use]
|
|
8
|
+
|
|
9
|
+
const extname = path.extname;
|
|
10
|
+
|
|
11
|
+
// [function]
|
|
12
|
+
|
|
13
|
+
const basefile = function (file) {
|
|
14
|
+
return file.slice(0, -extname(file).length);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// [export]
|
|
18
|
+
|
|
19
|
+
module.exports = { basefile };
|
package/src/util/schema.js
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
const chalk = require("chalk");
|
|
6
6
|
|
|
7
|
+
// [import]
|
|
8
|
+
|
|
9
|
+
const cfgurl = require("../config/url");
|
|
10
|
+
|
|
7
11
|
// [function]
|
|
8
12
|
|
|
9
13
|
const dereference = function (issues) {
|
|
@@ -12,7 +16,7 @@ const dereference = function (issues) {
|
|
|
12
16
|
issue
|
|
13
17
|
.split("\n")
|
|
14
18
|
.filter((line) => {
|
|
15
|
-
if (
|
|
19
|
+
if (new RegExp(`${cfgurl.MANIFEST}`, "u").test(line)) {
|
|
16
20
|
references.add(line.trim());
|
|
17
21
|
return false;
|
|
18
22
|
}
|