arcadible 1.0.2 → 1.0.4
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/bin/arc-build.js +7 -22
- package/bin/arc-default.js +8 -8
- package/bin/arc-deploy.js +20 -22
- package/bin/arc-init.js +4 -3
- package/bin/arc-login.js +2 -2
- package/bin/arc-pack.js +8 -22
- package/bin/arc-play.js +1 -1
- package/bin/arc-serve.js +4 -3
- package/bin/arc-validate.js +2 -1
- package/bin/arc-whoami.js +1 -1
- package/package.json +1 -1
- package/src/account.js +3 -1
- package/src/action/deploy/tool.js +5 -4
- package/src/action/predeploy/ac.js +4 -1
- package/src/action/serve/tool.js +19 -3
- package/src/action/validate/checked.js +37 -0
- package/src/api.js +28 -13
- package/src/config/code.js +2 -2
- package/src/parser/readme.js +1 -1
- package/src/phrase.js +74 -47
- package/src/schema/game.js +32 -20
- package/src/schema/readme.js +2 -1
- package/src/schema/workflow.js +8 -10
- package/src/serve/game.js +7 -1
- package/src/serve/page.js +11 -11
- package/src/serve/reload.js +5 -0
- package/src/util/log.js +9 -2
- package/src/util/md.js +1 -1
- package/src/util/output.js +17 -2
- package/src/util/schema.js +53 -1
package/src/config/code.js
CHANGED
|
@@ -13,6 +13,7 @@ const CLI = {
|
|
|
13
13
|
ARGUMENT_INVALID: "argument_invalid",
|
|
14
14
|
BUILD_FAILED: "build_failed",
|
|
15
15
|
DEPLOY_DIR_MISSING: "deploy_dir_missing",
|
|
16
|
+
DEPLOY_RATE_LIMITED: "deploy_rate_limited",
|
|
16
17
|
GAME_HANDLE_TAKEN: "game_handle_taken",
|
|
17
18
|
GAME_ID_MISSING: "game_id_missing",
|
|
18
19
|
GAME_NOT_DEPLOYED: "game_not_deployed",
|
|
@@ -43,7 +44,6 @@ const CLI = {
|
|
|
43
44
|
TEMPLATE_MISSING: "template_missing",
|
|
44
45
|
TEMPLATE_UNAVAILABLE: "template_unavailable",
|
|
45
46
|
UPLOAD_FAILED: "upload_failed",
|
|
46
|
-
UPLOAD_RATE_LIMITED: "upload_rate_limited",
|
|
47
47
|
VALIDATION_FAILED: "validation_failed",
|
|
48
48
|
WAIT_TIMED_OUT: "wait_timed_out",
|
|
49
49
|
};
|
|
@@ -105,7 +105,7 @@ const WARNING = {
|
|
|
105
105
|
DEPLOY_DIR_NOT_BUILT: "deploy_dir_not_built",
|
|
106
106
|
FIELD_UNKNOWN: "field_unknown",
|
|
107
107
|
GAME_HANDLE_LAPSED: "game_handle_lapsed",
|
|
108
|
-
|
|
108
|
+
GAME_HANDLE_LIMIT_REACHED: "game_handle_limit_reached",
|
|
109
109
|
VERSION_UNCHECKED: "version_unchecked",
|
|
110
110
|
};
|
|
111
111
|
// every error code, for checking one exists.
|
package/src/parser/readme.js
CHANGED
package/src/phrase.js
CHANGED
|
@@ -142,19 +142,19 @@ const didYouMean = function (near) {
|
|
|
142
142
|
// [function]
|
|
143
143
|
|
|
144
144
|
const docAttrInvalid = function (tag, attr, value) {
|
|
145
|
-
return `Value '${value}'
|
|
145
|
+
return `Value '${value}' isn't allowed for ${attr} on <${tag}>.`;
|
|
146
146
|
};
|
|
147
147
|
|
|
148
148
|
// [function]
|
|
149
149
|
|
|
150
150
|
const docAttrUnallowed = function (tag, attr) {
|
|
151
|
-
return `Attribute
|
|
151
|
+
return `Attribute ${attr} isn't allowed on <${tag}>.`;
|
|
152
152
|
};
|
|
153
153
|
|
|
154
154
|
// [function]
|
|
155
155
|
|
|
156
156
|
const docCommentUnallowed = function () {
|
|
157
|
-
return "HTML comments
|
|
157
|
+
return "HTML comments aren't allowed.";
|
|
158
158
|
};
|
|
159
159
|
|
|
160
160
|
// [function]
|
|
@@ -186,7 +186,7 @@ const docMediaType = function (src) {
|
|
|
186
186
|
// [function]
|
|
187
187
|
|
|
188
188
|
const docTagUnallowed = function (tag) {
|
|
189
|
-
return `Tag
|
|
189
|
+
return `Tag <${tag}> isn't allowed.`;
|
|
190
190
|
};
|
|
191
191
|
|
|
192
192
|
// [function]
|
|
@@ -208,13 +208,13 @@ const gameHandleTaken = function (handle) {
|
|
|
208
208
|
// [function]
|
|
209
209
|
|
|
210
210
|
const gameHandleLapsed = function (handle) {
|
|
211
|
-
return `Handle ${handle} lapsed
|
|
211
|
+
return `Handle ${handle} lapsed, so deploying without it.\nA production release takes it back.`;
|
|
212
212
|
};
|
|
213
213
|
|
|
214
214
|
// [function]
|
|
215
215
|
|
|
216
216
|
const gameHandleLimit = function (handle) {
|
|
217
|
-
return `Handle ${handle} not applied
|
|
217
|
+
return `Handle ${handle} not applied, so deploying without it.\nYour unreleased games already hold 5 handles.`;
|
|
218
218
|
};
|
|
219
219
|
|
|
220
220
|
// [function]
|
|
@@ -285,7 +285,7 @@ const packed = function () {
|
|
|
285
285
|
// [function]
|
|
286
286
|
|
|
287
287
|
const portInvalid = function () {
|
|
288
|
-
return "
|
|
288
|
+
return "`--port` must be a whole number from 1 to 65535.";
|
|
289
289
|
};
|
|
290
290
|
|
|
291
291
|
// [function]
|
|
@@ -311,13 +311,13 @@ const remoteName = function (name) {
|
|
|
311
311
|
|
|
312
312
|
// arc serve's copy of one of Arcadible's files, kept from `fetchedAt`.
|
|
313
313
|
const remoteKept = function (name, fetchedAt) {
|
|
314
|
-
return
|
|
314
|
+
return `Unable to reach ${TITLE}.\nServing ${remoteName(name)} saved on ${fetchedAt.slice(0, 10)}.`;
|
|
315
315
|
};
|
|
316
316
|
|
|
317
317
|
// [function]
|
|
318
318
|
|
|
319
319
|
const remoteUnreachable = function (name) {
|
|
320
|
-
return
|
|
320
|
+
return `Unable to reach ${TITLE}, and ${remoteName(name)} isn't saved.\nThe game runs without it until ${TITLE} is back.`;
|
|
321
321
|
};
|
|
322
322
|
|
|
323
323
|
// [function]
|
|
@@ -334,6 +334,20 @@ const serveAlone = function () {
|
|
|
334
334
|
|
|
335
335
|
// [function]
|
|
336
336
|
|
|
337
|
+
// the label before a play link in a result.
|
|
338
|
+
const playLabel = function () {
|
|
339
|
+
return "Play:";
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// [function]
|
|
343
|
+
|
|
344
|
+
// the label before a store page's link in a result.
|
|
345
|
+
const storeLabel = function () {
|
|
346
|
+
return "Store:";
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// [function]
|
|
350
|
+
|
|
337
351
|
const serveNext = function (dir) {
|
|
338
352
|
return `Reloads when ${dir}/ changes. Press Ctrl+C to stop.`;
|
|
339
353
|
};
|
|
@@ -359,7 +373,7 @@ const serveNextUrl = function () {
|
|
|
359
373
|
// [function]
|
|
360
374
|
|
|
361
375
|
const urlInvalid = function () {
|
|
362
|
-
return "
|
|
376
|
+
return "`--url` must start with http:// or https://.";
|
|
363
377
|
};
|
|
364
378
|
|
|
365
379
|
// [function]
|
|
@@ -368,8 +382,12 @@ const urlInvalid = function () {
|
|
|
368
382
|
// says how to add them.
|
|
369
383
|
const urlNotFrameable = function (url, link) {
|
|
370
384
|
return blocks(
|
|
371
|
-
|
|
372
|
-
|
|
385
|
+
`The play page can't frame ${url}.`,
|
|
386
|
+
[
|
|
387
|
+
"Its server doesn't send the headers the play page needs:",
|
|
388
|
+
"Cross-Origin-Embedder-Policy: require-corp",
|
|
389
|
+
"Cross-Origin-Resource-Policy: cross-origin",
|
|
390
|
+
],
|
|
373
391
|
["Add them to your server, as the starters' vite.config.js does:", link],
|
|
374
392
|
);
|
|
375
393
|
};
|
|
@@ -377,7 +395,7 @@ const urlNotFrameable = function (url, link) {
|
|
|
377
395
|
// [function]
|
|
378
396
|
|
|
379
397
|
const urlUnreachable = function (url) {
|
|
380
|
-
return
|
|
398
|
+
return `Nothing answers at ${url}.\nStart your server, then reload the play page.`;
|
|
381
399
|
};
|
|
382
400
|
|
|
383
401
|
// [function]
|
|
@@ -396,10 +414,11 @@ const releaseFailedHeading = function () {
|
|
|
396
414
|
|
|
397
415
|
// origin isn't the game's connected repository: none is, or another is.
|
|
398
416
|
const repoNotConnected = function (game, repo, url) {
|
|
399
|
-
return blocks(
|
|
400
|
-
|
|
401
|
-
url,
|
|
402
|
-
|
|
417
|
+
return blocks(
|
|
418
|
+
`${repo} isn't ${game}'s repository on ${TITLE}.`,
|
|
419
|
+
[`Connect it on the game's Repository tab in ${TITLE} Studio:`, url],
|
|
420
|
+
"If it was renamed on GitHub, connect it again.",
|
|
421
|
+
);
|
|
403
422
|
};
|
|
404
423
|
|
|
405
424
|
// [function]
|
|
@@ -411,10 +430,10 @@ const gameNotFound = function (idOrHandle) {
|
|
|
411
430
|
// [function]
|
|
412
431
|
|
|
413
432
|
const gameUnavailable = function (id) {
|
|
414
|
-
return blocks(
|
|
415
|
-
|
|
433
|
+
return blocks(
|
|
434
|
+
`Game unavailable: ${id}\nIt was deleted, or belongs to another account.`,
|
|
416
435
|
"Give game.id a new UUID to deploy it as a new game.",
|
|
417
|
-
|
|
436
|
+
);
|
|
418
437
|
};
|
|
419
438
|
|
|
420
439
|
// [function]
|
|
@@ -430,7 +449,7 @@ const gitUncommitted = function () {
|
|
|
430
449
|
|
|
431
450
|
const gitRemoteMissing = function () {
|
|
432
451
|
return blocks(
|
|
433
|
-
"No Git remote
|
|
452
|
+
"No Git remote origin.",
|
|
434
453
|
"Add the game's GitHub repository as origin.",
|
|
435
454
|
);
|
|
436
455
|
};
|
|
@@ -439,8 +458,8 @@ const gitRemoteMissing = function () {
|
|
|
439
458
|
|
|
440
459
|
const gitRemoteUnreachable = function () {
|
|
441
460
|
return blocks(
|
|
442
|
-
"Unable to reach Git remote
|
|
443
|
-
"Check your connection, and that you can access it.",
|
|
461
|
+
"Unable to reach Git remote origin.",
|
|
462
|
+
"Check your internet connection, and that you can access it.",
|
|
444
463
|
);
|
|
445
464
|
};
|
|
446
465
|
|
|
@@ -450,8 +469,14 @@ const manifestMissing = function (file, dir, found) {
|
|
|
450
469
|
return blocks(
|
|
451
470
|
dir === "." ? `No ${file} in this directory.` : `No ${file} in ${dir}.`,
|
|
452
471
|
found
|
|
453
|
-
?
|
|
454
|
-
|
|
472
|
+
? [
|
|
473
|
+
`Found a project in ${found}.`,
|
|
474
|
+
`Run \`${TOOL}\` there, or pass \`-C ${found}\`.`,
|
|
475
|
+
]
|
|
476
|
+
: [
|
|
477
|
+
`Run \`${TOOL}\` in a project's directory, or pass \`-C <dir>\`.`,
|
|
478
|
+
`To start a project, run ${cmdInit()}.`,
|
|
479
|
+
],
|
|
455
480
|
);
|
|
456
481
|
};
|
|
457
482
|
|
|
@@ -459,7 +484,7 @@ const manifestMissing = function (file, dir, found) {
|
|
|
459
484
|
|
|
460
485
|
const projectNotRepoRoot = function (prefix) {
|
|
461
486
|
return blocks(
|
|
462
|
-
|
|
487
|
+
`Project is in ${prefix}, not the repository's root.\n${cmdDeployGit()} releases only a project at the root.`,
|
|
463
488
|
`Deploy it with ${cmdDeploy()}, or give it a repository of its own.`,
|
|
464
489
|
);
|
|
465
490
|
};
|
|
@@ -487,7 +512,7 @@ const gitTagCreateFailed = function (version) {
|
|
|
487
512
|
const gitTagPushFailed = function (version) {
|
|
488
513
|
return blocks(
|
|
489
514
|
`Unable to push Git tag for v${version}.`,
|
|
490
|
-
"Check that you can push to
|
|
515
|
+
"Check that you can push to origin.",
|
|
491
516
|
);
|
|
492
517
|
};
|
|
493
518
|
|
|
@@ -564,7 +589,7 @@ const imageWidthMin = function (width) {
|
|
|
564
589
|
// [function]
|
|
565
590
|
|
|
566
591
|
const timeoutInvalid = function () {
|
|
567
|
-
return "
|
|
592
|
+
return "`--timeout` must be a number of seconds above 0.";
|
|
568
593
|
};
|
|
569
594
|
|
|
570
595
|
// [function]
|
|
@@ -573,8 +598,8 @@ const timeoutInvalid = function () {
|
|
|
573
598
|
// parsed the project again.
|
|
574
599
|
const issuesAfterBuild = function (count) {
|
|
575
600
|
return blocks(
|
|
576
|
-
`${count} ${count === 1 ? "issue" : "issues"} found after the build.`,
|
|
577
|
-
`
|
|
601
|
+
`${count} ${count === 1 ? "issue" : "issues"} found after the build.\nThe build writes files the manifest reads.`,
|
|
602
|
+
`Run ${cmdValidate()} for details.`,
|
|
578
603
|
);
|
|
579
604
|
};
|
|
580
605
|
|
|
@@ -590,13 +615,13 @@ const issuesFound = function (count) {
|
|
|
590
615
|
// [function]
|
|
591
616
|
|
|
592
617
|
const loginCodeExpired = function () {
|
|
593
|
-
return "Login code expired.";
|
|
618
|
+
return blocks("Login code expired.", `Run ${cmdLogin()} again.`);
|
|
594
619
|
};
|
|
595
620
|
|
|
596
621
|
// [function]
|
|
597
622
|
|
|
598
623
|
const loginFailed = function () {
|
|
599
|
-
return "Login request failed.";
|
|
624
|
+
return blocks("Login request failed.", `Run ${cmdLogin()} again.`);
|
|
600
625
|
};
|
|
601
626
|
|
|
602
627
|
// [function]
|
|
@@ -611,10 +636,10 @@ const gameIdMissing = function () {
|
|
|
611
636
|
// [function]
|
|
612
637
|
|
|
613
638
|
const lockfileMissing = function () {
|
|
614
|
-
return blocks(
|
|
615
|
-
"
|
|
616
|
-
"
|
|
617
|
-
|
|
639
|
+
return blocks(
|
|
640
|
+
"No lockfile found.",
|
|
641
|
+
"Install your dependencies to create one, and commit it.",
|
|
642
|
+
);
|
|
618
643
|
};
|
|
619
644
|
|
|
620
645
|
// [function]
|
|
@@ -688,7 +713,7 @@ const packagePathReserved = function (folder) {
|
|
|
688
713
|
const packageTooLarge = function (size) {
|
|
689
714
|
return blocks(
|
|
690
715
|
`Package is larger than ${size} MB.`,
|
|
691
|
-
"
|
|
716
|
+
".arcadible/ doesn't count toward it.",
|
|
692
717
|
);
|
|
693
718
|
};
|
|
694
719
|
|
|
@@ -712,7 +737,7 @@ const packageUncompressedTooLarge = function (size) {
|
|
|
712
737
|
// open it.
|
|
713
738
|
const releaseFailed = function ({ message }, studio) {
|
|
714
739
|
return blocks(
|
|
715
|
-
(message ?? "").split(/(?<=\.)\s+/u).join("\n"),
|
|
740
|
+
(message ?? "No reason given.").split(/(?<=\.)\s+/u).join("\n"),
|
|
716
741
|
...(studio ? [[`${TITLE} Studio:`, studio]] : []),
|
|
717
742
|
);
|
|
718
743
|
};
|
|
@@ -742,10 +767,10 @@ const releaseTimedOut = function (
|
|
|
742
767
|
]);
|
|
743
768
|
}
|
|
744
769
|
if (found) {
|
|
745
|
-
return blocks(
|
|
746
|
-
|
|
747
|
-
`
|
|
748
|
-
);
|
|
770
|
+
return blocks(head, [
|
|
771
|
+
"It may still finish.",
|
|
772
|
+
`The ${TITLE} check on the tagged commit says how it went.`,
|
|
773
|
+
]);
|
|
749
774
|
}
|
|
750
775
|
return blocks(
|
|
751
776
|
head,
|
|
@@ -753,7 +778,7 @@ const releaseTimedOut = function (
|
|
|
753
778
|
"GitHub Actions hasn't released it yet, or its workflow failed:",
|
|
754
779
|
actions ?? "See the repository's Actions tab.",
|
|
755
780
|
],
|
|
756
|
-
`If the workflow passed, the ${TITLE} check on the tagged commit
|
|
781
|
+
`If the workflow passed, see the ${TITLE} check on the tagged commit.`,
|
|
757
782
|
);
|
|
758
783
|
};
|
|
759
784
|
|
|
@@ -762,7 +787,7 @@ const releaseTimedOut = function (
|
|
|
762
787
|
// the spinner while `--wait` waits: on GitHub Actions until the release
|
|
763
788
|
// exists, then on Arcadible.
|
|
764
789
|
const releaseWaiting = function (building) {
|
|
765
|
-
return building ? "Waiting for GitHub Actions
|
|
790
|
+
return building ? "Waiting for GitHub Actions… %s" : "Publishing… %s";
|
|
766
791
|
};
|
|
767
792
|
|
|
768
793
|
// [function]
|
|
@@ -887,9 +912,9 @@ const uploadFailed = function (status) {
|
|
|
887
912
|
|
|
888
913
|
// [function]
|
|
889
914
|
|
|
890
|
-
const
|
|
915
|
+
const deployRateLimited = function (version) {
|
|
891
916
|
return blocks(
|
|
892
|
-
`v${version} was
|
|
917
|
+
`v${version} was deployed in the last 5 minutes.`,
|
|
893
918
|
"Wait, then deploy again.",
|
|
894
919
|
);
|
|
895
920
|
};
|
|
@@ -1022,6 +1047,7 @@ module.exports = {
|
|
|
1022
1047
|
cmdInit,
|
|
1023
1048
|
cmdInspect,
|
|
1024
1049
|
deployDirMissing,
|
|
1050
|
+
deployRateLimited,
|
|
1025
1051
|
didYouMean,
|
|
1026
1052
|
docAttrInvalid,
|
|
1027
1053
|
docAttrUnallowed,
|
|
@@ -1083,6 +1109,7 @@ module.exports = {
|
|
|
1083
1109
|
packageTooManyFiles,
|
|
1084
1110
|
packageUncompressedTooLarge,
|
|
1085
1111
|
packed,
|
|
1112
|
+
playLabel,
|
|
1086
1113
|
portInUse,
|
|
1087
1114
|
portInvalid,
|
|
1088
1115
|
pressEnterToOpen,
|
|
@@ -1110,6 +1137,7 @@ module.exports = {
|
|
|
1110
1137
|
starterPhaser,
|
|
1111
1138
|
starterPixiJs,
|
|
1112
1139
|
starterThreeJs,
|
|
1140
|
+
storeLabel,
|
|
1113
1141
|
tagPushed,
|
|
1114
1142
|
templateMissing,
|
|
1115
1143
|
templateUnavailable,
|
|
@@ -1121,7 +1149,6 @@ module.exports = {
|
|
|
1121
1149
|
unknownField,
|
|
1122
1150
|
unknownTable,
|
|
1123
1151
|
uploadFailed,
|
|
1124
|
-
uploadRateLimited,
|
|
1125
1152
|
urlInvalid,
|
|
1126
1153
|
urlNotFrameable,
|
|
1127
1154
|
urlUnreachable,
|
package/src/schema/game.js
CHANGED
|
@@ -123,16 +123,20 @@ const GALLERY_DIMENSIONS = {
|
|
|
123
123
|
// leaving a field out is how to unset it, so an empty one is always a mistake.
|
|
124
124
|
const atLeast = function (field, min, unit) {
|
|
125
125
|
return min === 1
|
|
126
|
-
? message(field, ["Must not be empty. Leave the field out instead."])
|
|
127
|
-
: message(field, [
|
|
126
|
+
? message(field, ["Must not be empty.", "Leave the field out instead."])
|
|
127
|
+
: message(field, [
|
|
128
|
+
unit === "character"
|
|
129
|
+
? `Must be at least ${min} characters.`
|
|
130
|
+
: `Must have at least ${min} ${unit}s.`,
|
|
131
|
+
]);
|
|
128
132
|
};
|
|
129
133
|
|
|
130
134
|
// [function]
|
|
131
135
|
|
|
132
|
-
// a required string's error: "Must be
|
|
136
|
+
// a required string's error: "Must be set." and `lines` when it's missing,
|
|
133
137
|
// and zod's own type message (null) when it's there but isn't a string.
|
|
134
138
|
const required = function (field, lines) {
|
|
135
|
-
const missing = message(field, ["Must be
|
|
139
|
+
const missing = message(field, ["Must be set.", ...lines]).message;
|
|
136
140
|
return {
|
|
137
141
|
error(issue) {
|
|
138
142
|
return typeof issue.input === "undefined" ? missing : null;
|
|
@@ -144,14 +148,14 @@ const required = function (field, lines) {
|
|
|
144
148
|
|
|
145
149
|
// a list of distinct values. Without a max, its length is up to the values.
|
|
146
150
|
const zSet = function (field, schema, { max, min }) {
|
|
147
|
-
const list = z.array(schema).min(min, atLeast(field, min, "
|
|
151
|
+
const list = z.array(schema).min(min, atLeast(field, min, "item"));
|
|
148
152
|
return (
|
|
149
153
|
max
|
|
150
|
-
? list.max(max, message(field, [`Must
|
|
154
|
+
? list.max(max, message(field, [`Must have at most ${max} items.`]))
|
|
151
155
|
: list
|
|
152
156
|
).refine(
|
|
153
157
|
(items) => new Set(items).size === items.length,
|
|
154
|
-
message(field, ["Must not
|
|
158
|
+
message(field, ["Must not repeat an item."], ISSUE.DUPLICATE),
|
|
155
159
|
);
|
|
156
160
|
};
|
|
157
161
|
|
|
@@ -161,12 +165,12 @@ const zSetBasefile = function (field, schema, { max, min }) {
|
|
|
161
165
|
return z
|
|
162
166
|
.array(schema)
|
|
163
167
|
.min(min, atLeast(field, min, "file"))
|
|
164
|
-
.max(max, message(field, [`Must
|
|
168
|
+
.max(max, message(field, [`Must have at most ${max} files.`]))
|
|
165
169
|
.refine(
|
|
166
170
|
(items) => new Set(items.map(basefile)).size === items.length,
|
|
167
171
|
message(
|
|
168
172
|
field,
|
|
169
|
-
["Must not
|
|
173
|
+
["Must not have two files of one name, whatever their extensions."],
|
|
170
174
|
ISSUE.DUPLICATE,
|
|
171
175
|
),
|
|
172
176
|
);
|
|
@@ -183,7 +187,7 @@ const zString = function (field, char, { max, min }) {
|
|
|
183
187
|
.min(min, { ...atLeast(field, min, "character"), abort: min === 1 })
|
|
184
188
|
.refine(
|
|
185
189
|
(str) => [...str].length <= max,
|
|
186
|
-
message(field, [`Must
|
|
190
|
+
message(field, [`Must be at most ${max} characters.`], ISSUE.TOO_LONG),
|
|
187
191
|
)
|
|
188
192
|
.superRefine((str, ctx) => {
|
|
189
193
|
const invalid = invalidChars(str, char);
|
|
@@ -458,7 +462,7 @@ const zHandle = function () {
|
|
|
458
462
|
const field = "handle";
|
|
459
463
|
return z.optional(
|
|
460
464
|
zString(field, /[a-z0-9-]/u, { max: 24, min: 1 })
|
|
461
|
-
.min(3, atLeast(field, 3, "
|
|
465
|
+
.min(3, atLeast(field, 3, "character"))
|
|
462
466
|
.refine(
|
|
463
467
|
(str) => !str.startsWith("-") && !str.endsWith("-"),
|
|
464
468
|
message(
|
|
@@ -487,7 +491,7 @@ const zId = function () {
|
|
|
487
491
|
required(field, [`Run ${phrase.cmdInit()} to generate a new game ID.`]),
|
|
488
492
|
)
|
|
489
493
|
.min(1, { ...atLeast(field, 1), abort: true })
|
|
490
|
-
.uuid(message(field, ["Must be a
|
|
494
|
+
.uuid(message(field, ["Must be a UUID."]))
|
|
491
495
|
.refine(
|
|
492
496
|
(value) => value === value.toLowerCase(),
|
|
493
497
|
message(field, ["Must be lowercase."], ISSUE.UUID_INVALID),
|
|
@@ -514,8 +518,8 @@ const zPrice = function () {
|
|
|
514
518
|
return z.optional(
|
|
515
519
|
z
|
|
516
520
|
.int()
|
|
517
|
-
.min(0, message(field, ["Must
|
|
518
|
-
.max(25, message(field, ["Must
|
|
521
|
+
.min(0, message(field, ["Must be 0 or more."]))
|
|
522
|
+
.max(25, message(field, ["Must be 25 or less."])),
|
|
519
523
|
);
|
|
520
524
|
};
|
|
521
525
|
|
|
@@ -547,7 +551,7 @@ const zTrailer = function () {
|
|
|
547
551
|
return z
|
|
548
552
|
.string()
|
|
549
553
|
.min(1, { ...atLeast(field, 1), abort: true })
|
|
550
|
-
.max(255, message(field, ["Must
|
|
554
|
+
.max(255, message(field, ["Must be at most 255 characters."]))
|
|
551
555
|
.refine(
|
|
552
556
|
(url) =>
|
|
553
557
|
zVimeo().safeParse(url).success || zYouTube().safeParse(url).success,
|
|
@@ -561,18 +565,26 @@ const zTrailer = function () {
|
|
|
561
565
|
const zVersion = function () {
|
|
562
566
|
const field = "version";
|
|
563
567
|
return z
|
|
564
|
-
.string(
|
|
565
|
-
required(field, ["A version is required unless one can be derived."]),
|
|
566
|
-
)
|
|
568
|
+
.string(required(field, ["Set it here, or in package.json."]))
|
|
567
569
|
.min(1, { ...atLeast(field, 1), abort: true })
|
|
568
570
|
.refine((value) => validSemver(value) !== null, {
|
|
569
|
-
...message(
|
|
571
|
+
...message(
|
|
572
|
+
field,
|
|
573
|
+
["Must be a version, such as 1.2.0."],
|
|
574
|
+
ISSUE.VERSION_INVALID,
|
|
575
|
+
),
|
|
570
576
|
abort: true,
|
|
571
577
|
})
|
|
572
578
|
.refine(
|
|
573
579
|
// lowercase too, since the database refuses an uppercase prerelease.
|
|
574
580
|
(value) => cleanSemver(value) === value && value === value.toLowerCase(),
|
|
575
|
-
message(
|
|
581
|
+
message(
|
|
582
|
+
field,
|
|
583
|
+
[
|
|
584
|
+
"Must be written as 1.2.0 or 1.2.0-beta.1: lowercase, with no v, spaces, or build metadata.",
|
|
585
|
+
],
|
|
586
|
+
ISSUE.VERSION_INVALID,
|
|
587
|
+
),
|
|
576
588
|
);
|
|
577
589
|
};
|
|
578
590
|
|
package/src/schema/readme.js
CHANGED
|
@@ -29,7 +29,8 @@ const main = function (value, { file }) {
|
|
|
29
29
|
...message(
|
|
30
30
|
"description",
|
|
31
31
|
[
|
|
32
|
-
|
|
32
|
+
"Needs two --- rules around the description.",
|
|
33
|
+
`${file} has content after its title, tagline, and bio.`,
|
|
33
34
|
],
|
|
34
35
|
cfgcode.ISSUE.DESCRIPTION_RULES_MISSING,
|
|
35
36
|
),
|
package/src/schema/workflow.js
CHANGED
|
@@ -68,7 +68,7 @@ const zDeployDir = function (root, { build }) {
|
|
|
68
68
|
return z
|
|
69
69
|
.string()
|
|
70
70
|
.min(1, {
|
|
71
|
-
...message(field, ["Must not be empty. Leave the field out instead."]),
|
|
71
|
+
...message(field, ["Must not be empty.", "Leave the field out instead."]),
|
|
72
72
|
abort: true,
|
|
73
73
|
})
|
|
74
74
|
.refine((file) => isRelativeCheck(root, file), {
|
|
@@ -82,24 +82,20 @@ const zDeployDir = function (root, { build }) {
|
|
|
82
82
|
.refine((file) => !isRootCheck(root, file), {
|
|
83
83
|
...message(
|
|
84
84
|
field,
|
|
85
|
-
["Must be a
|
|
85
|
+
["Must be a folder in the project, not its root."],
|
|
86
86
|
ISSUE.PATH_INVALID,
|
|
87
87
|
),
|
|
88
88
|
abort: true,
|
|
89
89
|
})
|
|
90
90
|
.refine((file) => avoidsCheck(root, file, MEDIA), {
|
|
91
|
-
...message(
|
|
92
|
-
field,
|
|
93
|
-
[`Must not contain ${MEDIA} in path.`],
|
|
94
|
-
ISSUE.PATH_INVALID,
|
|
95
|
-
),
|
|
91
|
+
...message(field, [`Must not be in ${MEDIA}.`], ISSUE.PATH_INVALID),
|
|
96
92
|
abort: true,
|
|
97
93
|
})
|
|
98
94
|
.refine((file) => hasBuild(build) || existsCheck(root, file), {
|
|
99
95
|
...message(
|
|
100
96
|
field,
|
|
101
97
|
[
|
|
102
|
-
"Must exist
|
|
98
|
+
"Must exist, since there's no build.",
|
|
103
99
|
`Run ${phrase.cmdInspect()} for details.`,
|
|
104
100
|
],
|
|
105
101
|
ISSUE.PATH_INVALID,
|
|
@@ -118,7 +114,8 @@ const zDeployDir = function (root, { build }) {
|
|
|
118
114
|
...message(
|
|
119
115
|
field,
|
|
120
116
|
[
|
|
121
|
-
`Must not have ${RESERVED.join(" or ")} at its root
|
|
117
|
+
`Must not have ${RESERVED.join(" or ")} at its root.`,
|
|
118
|
+
`They're reserved for the files ${cfgproduct.title} adds.`,
|
|
122
119
|
],
|
|
123
120
|
ISSUE.PATH_INVALID,
|
|
124
121
|
),
|
|
@@ -128,7 +125,8 @@ const zDeployDir = function (root, { build }) {
|
|
|
128
125
|
...message(
|
|
129
126
|
field,
|
|
130
127
|
[
|
|
131
|
-
`Must not have an ${RESERVED_FOLDER} folder at its root, in any letter case
|
|
128
|
+
`Must not have an ${RESERVED_FOLDER} folder at its root, in any letter case.`,
|
|
129
|
+
`It's reserved for ${cfgproduct.title}'s own files.`,
|
|
132
130
|
],
|
|
133
131
|
ISSUE.PATH_INVALID,
|
|
134
132
|
),
|
package/src/serve/game.js
CHANGED
|
@@ -98,7 +98,13 @@ const sendFile = function (req, res, file, headers) {
|
|
|
98
98
|
res.end();
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
101
|
-
file
|
|
101
|
+
// the file can go between finding and reading it, as a rebuild empties
|
|
102
|
+
// deploy_dir: the headers are sent, so the response just ends
|
|
103
|
+
const stream = file.open(range);
|
|
104
|
+
stream.on("error", () => {
|
|
105
|
+
res.destroy();
|
|
106
|
+
});
|
|
107
|
+
stream.pipe(res);
|
|
102
108
|
};
|
|
103
109
|
|
|
104
110
|
// [function]
|
package/src/serve/page.js
CHANGED
|
@@ -24,6 +24,17 @@ const SANDBOX =
|
|
|
24
24
|
const HOST = "/_arcadible/host.js";
|
|
25
25
|
const RELOAD = "/_arcadible/reload";
|
|
26
26
|
|
|
27
|
+
// [declare]
|
|
28
|
+
|
|
29
|
+
// the headers of the page's responses, as the play page's: it's
|
|
30
|
+
// cross-origin isolated, and nothing frames it.
|
|
31
|
+
const HEADERS = {
|
|
32
|
+
"cache-control": "no-store",
|
|
33
|
+
"content-security-policy": "frame-ancestors 'none';",
|
|
34
|
+
"cross-origin-embedder-policy": "require-corp",
|
|
35
|
+
"cross-origin-opener-policy": "same-origin",
|
|
36
|
+
};
|
|
37
|
+
|
|
27
38
|
// [function]
|
|
28
39
|
|
|
29
40
|
// `value` as JSON that's safe inside a <script>.
|
|
@@ -105,17 +116,6 @@ const html = function (game) {
|
|
|
105
116
|
|
|
106
117
|
// [function]
|
|
107
118
|
|
|
108
|
-
// the headers of the page's responses, as the play page's: it's
|
|
109
|
-
// cross-origin isolated, and nothing frames it.
|
|
110
|
-
const HEADERS = {
|
|
111
|
-
"cache-control": "no-store",
|
|
112
|
-
"content-security-policy": "frame-ancestors 'none';",
|
|
113
|
-
"cross-origin-embedder-policy": "require-corp",
|
|
114
|
-
"cross-origin-opener-policy": "same-origin",
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
// [function]
|
|
118
|
-
|
|
119
119
|
// the request handler for the page's origin. `reloads` keeps the pages
|
|
120
120
|
// listening for a reload; `remote` has the host.
|
|
121
121
|
const pageHandler = function ({
|
package/src/serve/reload.js
CHANGED
|
@@ -52,6 +52,11 @@ const watchDir = function (dir, onChange) {
|
|
|
52
52
|
clearTimeout(timer);
|
|
53
53
|
timer = setTimeout(onChange, SETTLE);
|
|
54
54
|
});
|
|
55
|
+
// deploy_dir removed while it's watched (EPERM on Windows): stop watching
|
|
56
|
+
// rather than crash the server
|
|
57
|
+
watcher.on("error", () => {
|
|
58
|
+
watcher.close();
|
|
59
|
+
});
|
|
55
60
|
return () => {
|
|
56
61
|
clearTimeout(timer);
|
|
57
62
|
watcher.close();
|
package/src/util/log.js
CHANGED
|
@@ -30,6 +30,13 @@ const log = function (out, message, { bold = false, color, nl = false } = {}) {
|
|
|
30
30
|
|
|
31
31
|
// [function]
|
|
32
32
|
|
|
33
|
+
// a warning, marked with ⚠ (OUTPUT.md), on its first line.
|
|
34
|
+
const warned = function (message) {
|
|
35
|
+
return `⚠ ${[message].flat().join("\n").trim()}`;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// [function]
|
|
39
|
+
|
|
33
40
|
const h = function (message) {
|
|
34
41
|
log(console.log, message, { bold: true });
|
|
35
42
|
};
|
|
@@ -91,13 +98,13 @@ const hwo = function (message) {
|
|
|
91
98
|
// [function]
|
|
92
99
|
|
|
93
100
|
const pw = function (message) {
|
|
94
|
-
log(console.warn, message, { color: "yellow" });
|
|
101
|
+
log(console.warn, warned(message), { color: "yellow" });
|
|
95
102
|
};
|
|
96
103
|
|
|
97
104
|
// [function]
|
|
98
105
|
|
|
99
106
|
const nlpw = function (message) {
|
|
100
|
-
log(console.warn, message, { color: "yellow", nl: true });
|
|
107
|
+
log(console.warn, warned(message), { color: "yellow", nl: true });
|
|
101
108
|
};
|
|
102
109
|
|
|
103
110
|
// [function]
|