arcadible 0.1.26 → 0.1.28
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 +14 -21
- package/src/config/visibility.js +1 -1
- package/src/phrase.js +28 -2
- package/src/schema/check/game.js +16 -1
- package/src/schema/game.js +157 -108
- package/src/util/doc.js +60 -0
- package/src/util/dom.js +24 -5
- 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,18 @@
|
|
|
3
3
|
// [export]
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"code",
|
|
21
|
-
"strong",
|
|
22
|
-
"b",
|
|
23
|
-
"em",
|
|
24
|
-
"i",
|
|
25
|
-
"br",
|
|
26
|
-
],
|
|
6
|
+
allowed: {
|
|
7
|
+
a: ["href", "alt", "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
|
+
},
|
|
27
20
|
};
|
package/src/config/visibility.js
CHANGED
package/src/phrase.js
CHANGED
|
@@ -34,6 +34,30 @@ 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 allowed 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 docUnsupported = function () {
|
|
56
|
+
return "Document has an unsupported file type.";
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// [function]
|
|
60
|
+
|
|
37
61
|
const gameExists = function (idOrHandle) {
|
|
38
62
|
return `Game exists: ${idOrHandle}`;
|
|
39
63
|
};
|
|
@@ -355,8 +379,10 @@ const videoWidthMin = function (width) {
|
|
|
355
379
|
module.exports = {
|
|
356
380
|
cmdInit,
|
|
357
381
|
cmdInspect,
|
|
358
|
-
|
|
359
|
-
|
|
382
|
+
docAttrInvalid,
|
|
383
|
+
docAttrUnallowed,
|
|
384
|
+
docSanitizeFailed,
|
|
385
|
+
docUnsupported,
|
|
360
386
|
gameExists,
|
|
361
387
|
gameVersionExists,
|
|
362
388
|
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,20 +21,24 @@ 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
|
|
|
@@ -41,6 +47,10 @@ const videoCheck = check.video;
|
|
|
41
47
|
const HANDLE = cfgproduct.handle;
|
|
42
48
|
const MEDIA = `.${HANDLE}`;
|
|
43
49
|
|
|
50
|
+
// [declare]
|
|
51
|
+
|
|
52
|
+
const GALLERY = `${MEDIA}/gallery`;
|
|
53
|
+
|
|
44
54
|
// [function]
|
|
45
55
|
|
|
46
56
|
const reference = function (field) {
|
|
@@ -61,15 +71,62 @@ const message = function (field, lines) {
|
|
|
61
71
|
|
|
62
72
|
// [function]
|
|
63
73
|
|
|
64
|
-
const
|
|
74
|
+
const zSet = function (field, schema, { max, min }) {
|
|
75
|
+
return z
|
|
76
|
+
.array(schema)
|
|
77
|
+
.min(min, message(field, [`Must be at least: ${min} element`]))
|
|
78
|
+
.max(max, message(field, [`Must not exceed: ${max} elements`]))
|
|
79
|
+
.refine(
|
|
80
|
+
(items) => new Set(items).size === items.length,
|
|
81
|
+
message(field, ["Must not contain duplicate elements."]),
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// [function]
|
|
86
|
+
|
|
87
|
+
const zSetBasefile = function (field, schema, { max, min }) {
|
|
88
|
+
return z
|
|
89
|
+
.array(schema)
|
|
90
|
+
.min(min, message(field, [`Must be at least: ${min} file`]))
|
|
91
|
+
.max(max, message(field, [`Must not exceed: ${max} files`]))
|
|
92
|
+
.refine(
|
|
93
|
+
(items) => new Set(items.map(basefile)).size === items.length,
|
|
94
|
+
message(field, [
|
|
95
|
+
"Must not contain duplicate filenames (ignoring extensions).",
|
|
96
|
+
]),
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// [function]
|
|
101
|
+
|
|
102
|
+
const zString = function (field, re, { max, min }) {
|
|
65
103
|
return z
|
|
66
104
|
.string()
|
|
67
105
|
.min(min, message(field, [`Must be at least: ${min} character`]))
|
|
68
106
|
.max(max, message(field, [`Must not exceed: ${max} characters`]))
|
|
69
|
-
.regex(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
107
|
+
.regex(re, message(field, ["Contains invalid characters."]));
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// [function]
|
|
111
|
+
|
|
112
|
+
const zText = function (field, opts) {
|
|
113
|
+
const re = /^[\p{L}0-9 .,!?'"():;-]+$/u;
|
|
114
|
+
return zString(field, re, opts);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// [function]
|
|
118
|
+
|
|
119
|
+
const zHtml = function (field, root, { max, min, ...opts }) {
|
|
120
|
+
const re = /^[\p{L}\p{M}\p{N}\p{P}\p{S}\p{Zs}\r\n]*$/u;
|
|
121
|
+
return zString(field, re, { max, min }).superRefine((str, ctx) => {
|
|
122
|
+
const issues = docCheck(root, str, { ...opts, type: cfgdoc[".html"] });
|
|
123
|
+
if (issues.length) {
|
|
124
|
+
ctx.addIssue({
|
|
125
|
+
code: "custom",
|
|
126
|
+
...message(field, ["Invalid Markdown.", ...issues]),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
});
|
|
73
130
|
};
|
|
74
131
|
|
|
75
132
|
// [function]
|
|
@@ -109,20 +166,6 @@ const zPng = function (field, root, opts) {
|
|
|
109
166
|
|
|
110
167
|
// [function]
|
|
111
168
|
|
|
112
|
-
const zSvg = function (field, root, opts) {
|
|
113
|
-
return zMedia(field, root).superRefine((file, ctx) => {
|
|
114
|
-
const issues = imageCheck(root, file, { ...opts, type: cfgimage[".svg"] });
|
|
115
|
-
if (issues.length) {
|
|
116
|
-
ctx.addIssue({
|
|
117
|
-
code: "custom",
|
|
118
|
-
...message(field, ["Invalid SVG.", ...issues]),
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
// [function]
|
|
125
|
-
|
|
126
169
|
const zMp4 = function (field, root, opts) {
|
|
127
170
|
return zMedia(field, root).superRefine((file, ctx) => {
|
|
128
171
|
const issues = videoCheck(root, file, {
|
|
@@ -162,6 +205,13 @@ const zYouTube = function () {
|
|
|
162
205
|
|
|
163
206
|
// [function]
|
|
164
207
|
|
|
208
|
+
const zAi = function () {
|
|
209
|
+
// const field = "ai";
|
|
210
|
+
return z.optional(z.enum(cfgai));
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// [function]
|
|
214
|
+
|
|
165
215
|
const zAudience = function () {
|
|
166
216
|
// const field = "audience";
|
|
167
217
|
return z.optional(z.enum(cfgaudience));
|
|
@@ -199,7 +249,7 @@ const zBannerVideo = function (root) {
|
|
|
199
249
|
|
|
200
250
|
const zBio = function () {
|
|
201
251
|
const field = "bio";
|
|
202
|
-
return z.optional(zText(field, { max: 30 *
|
|
252
|
+
return z.optional(zText(field, { max: 30 * 6, min: 1 }));
|
|
203
253
|
};
|
|
204
254
|
|
|
205
255
|
// [function]
|
|
@@ -232,22 +282,22 @@ const zCoverVideo = function (root) {
|
|
|
232
282
|
|
|
233
283
|
// [function]
|
|
234
284
|
|
|
235
|
-
const zDescription = function () {
|
|
285
|
+
const zDescription = function (root) {
|
|
236
286
|
const field = "description";
|
|
237
287
|
return z.optional(
|
|
238
|
-
|
|
239
|
-
.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
288
|
+
zHtml(field, root, {
|
|
289
|
+
allowed: cfghtml.allowed,
|
|
290
|
+
hooks: {
|
|
291
|
+
href: (uri) => isSafeUri(uri),
|
|
292
|
+
src: (src) =>
|
|
293
|
+
isRelativeCheck(root, src) &&
|
|
294
|
+
isOutCheck(root, src) &&
|
|
295
|
+
isInCheck(root, src, GALLERY) &&
|
|
296
|
+
existsCheck(root, src),
|
|
297
|
+
},
|
|
298
|
+
max: 3000,
|
|
299
|
+
min: 1,
|
|
300
|
+
}),
|
|
251
301
|
);
|
|
252
302
|
};
|
|
253
303
|
|
|
@@ -256,48 +306,48 @@ const zDescription = function () {
|
|
|
256
306
|
const zGallery = function (root) {
|
|
257
307
|
const field = "gallery";
|
|
258
308
|
return z.optional(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
309
|
+
zSetBasefile(
|
|
310
|
+
field,
|
|
311
|
+
zMedia(field, root).superRefine((file, ctx) => {
|
|
312
|
+
if (file.endsWith(".png")) {
|
|
313
|
+
const issues = imageCheck(root, file, {
|
|
314
|
+
maxSize: size.mb(2),
|
|
315
|
+
type: cfgimage[".png"],
|
|
316
|
+
});
|
|
317
|
+
if (issues.length) {
|
|
318
|
+
ctx.addIssue({
|
|
319
|
+
code: "custom",
|
|
320
|
+
...message(field, [`Invalid PNG: ${file}`, ...issues]),
|
|
266
321
|
});
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
322
|
+
}
|
|
323
|
+
} else if (file.endsWith(".svg")) {
|
|
324
|
+
const issues = imageCheck(root, file, {
|
|
325
|
+
maxSize: size.mb(1),
|
|
326
|
+
type: cfgimage[".svg"],
|
|
327
|
+
});
|
|
328
|
+
if (issues.length) {
|
|
329
|
+
ctx.addIssue({
|
|
330
|
+
code: "custom",
|
|
331
|
+
...message(field, [`Invalid SVG: ${file}`, ...issues]),
|
|
277
332
|
});
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
333
|
+
}
|
|
334
|
+
} else if (file.endsWith(".mp4")) {
|
|
335
|
+
const issues = videoCheck(root, file, {
|
|
336
|
+
maxDuration: 30,
|
|
337
|
+
maxSize: size.mb(15),
|
|
338
|
+
minDuration: 3,
|
|
339
|
+
type: cfgvideo[".mp4"],
|
|
340
|
+
});
|
|
341
|
+
if (issues.length) {
|
|
342
|
+
ctx.addIssue({
|
|
343
|
+
code: "custom",
|
|
344
|
+
...message(field, [`Invalid MP4: ${file}`, ...issues]),
|
|
290
345
|
});
|
|
291
|
-
if (issues.length) {
|
|
292
|
-
ctx.addIssue({
|
|
293
|
-
code: "custom",
|
|
294
|
-
...message(field, [`Invalid MP4: ${file}`, ...issues]),
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
346
|
}
|
|
298
|
-
}
|
|
299
|
-
)
|
|
300
|
-
|
|
347
|
+
}
|
|
348
|
+
}),
|
|
349
|
+
{ max: 8 * 3, min: 1 },
|
|
350
|
+
),
|
|
301
351
|
);
|
|
302
352
|
};
|
|
303
353
|
|
|
@@ -305,11 +355,7 @@ const zGallery = function (root) {
|
|
|
305
355
|
|
|
306
356
|
const zGenres = function () {
|
|
307
357
|
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
|
-
);
|
|
358
|
+
return z.optional(zSet(field, z.enum(cfggenre), { max: 3, min: 1 }));
|
|
313
359
|
};
|
|
314
360
|
|
|
315
361
|
// [function]
|
|
@@ -376,11 +422,7 @@ const zId = function () {
|
|
|
376
422
|
|
|
377
423
|
const zInputs = function () {
|
|
378
424
|
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
|
-
);
|
|
425
|
+
return z.optional(zSet(field, z.enum(cfginput), { max: 3, min: 1 }));
|
|
384
426
|
};
|
|
385
427
|
|
|
386
428
|
// [function]
|
|
@@ -439,6 +481,34 @@ const zShortTitle = function () {
|
|
|
439
481
|
|
|
440
482
|
// [function]
|
|
441
483
|
|
|
484
|
+
const zSplash = function (root) {
|
|
485
|
+
const field = "splash";
|
|
486
|
+
return z.optional(
|
|
487
|
+
zPng(field, root, {
|
|
488
|
+
aspect: "16:9",
|
|
489
|
+
maxSize: size.mb(2),
|
|
490
|
+
minHeight: 576,
|
|
491
|
+
minWidth: 1024,
|
|
492
|
+
}),
|
|
493
|
+
);
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
// [function]
|
|
497
|
+
|
|
498
|
+
const zSplashVideo = function (root) {
|
|
499
|
+
const field = "splash_video";
|
|
500
|
+
return z.optional(
|
|
501
|
+
zMp4(field, root, {
|
|
502
|
+
aspect: "16:9",
|
|
503
|
+
maxSize: size.mb(15),
|
|
504
|
+
minHeight: 576,
|
|
505
|
+
minWidth: 1024,
|
|
506
|
+
}),
|
|
507
|
+
);
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
// [function]
|
|
511
|
+
|
|
442
512
|
const zTagline = function () {
|
|
443
513
|
const field = "tagline";
|
|
444
514
|
return z.optional(zText(field, { max: 30 * 1, min: 1 }));
|
|
@@ -496,38 +566,17 @@ const zVisibility = function () {
|
|
|
496
566
|
|
|
497
567
|
// [function]
|
|
498
568
|
|
|
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
569
|
const main = function (value, { root }) {
|
|
522
570
|
/* eslint-disable camelcase */
|
|
523
571
|
return z.object({
|
|
572
|
+
ai: zAi(),
|
|
524
573
|
audience: zAudience(),
|
|
525
574
|
banner: zBanner(root),
|
|
526
575
|
banner_video: zBannerVideo(root),
|
|
527
576
|
bio: zBio(),
|
|
528
577
|
cover: zCover(root),
|
|
529
578
|
cover_video: zCoverVideo(root),
|
|
530
|
-
description: zDescription(),
|
|
579
|
+
description: zDescription(root),
|
|
531
580
|
gallery: zGallery(root),
|
|
532
581
|
genres: zGenres(),
|
|
533
582
|
handle: zHandle(),
|
|
@@ -540,13 +589,13 @@ const main = function (value, { root }) {
|
|
|
540
589
|
poster_video: zPosterVideo(root),
|
|
541
590
|
price: zPrice(),
|
|
542
591
|
short_title: zShortTitle(),
|
|
592
|
+
splash: zSplash(root),
|
|
593
|
+
splash_video: zSplashVideo(root),
|
|
543
594
|
tagline: zTagline(),
|
|
544
595
|
title: zTitle(),
|
|
545
596
|
trailer: zTrailer(),
|
|
546
597
|
version: zVersion(),
|
|
547
598
|
visibility: zVisibility(),
|
|
548
|
-
wordmark: zWordmark(root),
|
|
549
|
-
wordmark_animated: zWordmarkAnimated(root),
|
|
550
599
|
});
|
|
551
600
|
/* eslint-enable camelcase */
|
|
552
601
|
};
|
package/src/util/doc.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
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, nodeName } = issue;
|
|
40
|
+
return isAllowed
|
|
41
|
+
? phrase.docAttrInvalid(nodeName, attrName, attrValue)
|
|
42
|
+
: phrase.docAttrUnallowed(nodeName, attrName);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return [phrase.docSanitizeFailed()];
|
|
46
|
+
}
|
|
47
|
+
return [];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// [function]
|
|
51
|
+
|
|
52
|
+
const check = function (str, { type, ...opts }) {
|
|
53
|
+
const checks = { [cfgdoc[".html"]]: checkHtml };
|
|
54
|
+
const checkDoc = checks[type];
|
|
55
|
+
return checkDoc ? checkDoc(str, opts) : [phrase.docUnsupported()];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// [export]
|
|
59
|
+
|
|
60
|
+
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 };
|
|
@@ -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
|
}
|