track-cli 4.0.3 → 4.1.0-rc2
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/esm/_dnt.shims.d.ts +1 -1
- package/esm/_dnt.test_shims.d.ts +20 -0
- package/esm/_dnt.test_shims.js +77 -0
- package/esm/deps/deno.land/std@0.195.0/_util/diff.d.ts +26 -0
- package/esm/deps/deno.land/std@0.195.0/_util/diff.js +311 -0
- package/esm/deps/deno.land/std@0.195.0/assert/_constants.d.ts +1 -0
- package/esm/deps/deno.land/std@0.195.0/assert/_constants.js +2 -0
- package/esm/deps/deno.land/std@0.195.0/assert/_format.d.ts +1 -0
- package/esm/deps/deno.land/std@0.195.0/assert/_format.js +23 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_almost_equals.d.ts +18 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_almost_equals.js +32 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_array_includes.d.ts +14 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_array_includes.js +38 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_equals.d.ts +17 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_equals.js +45 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_exists.d.ts +5 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_exists.js +14 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_false.d.ts +4 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_false.js +7 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_instance_of.d.ts +8 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_instance_of.js +38 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_is_error.d.ts +7 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_is_error.js +26 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_match.d.ts +5 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_match.js +13 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_not_equals.d.ts +14 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_not_equals.js +37 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_not_instance_of.d.ts +5 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_not_instance_of.js +14 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_not_match.d.ts +5 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_not_match.js +14 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_not_strict_equals.d.ts +11 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_not_strict_equals.js +20 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_object_match.d.ts +5 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_object_match.js +78 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_rejects.d.ts +64 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_rejects.js +50 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_strict_equals.d.ts +23 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_strict_equals.js +60 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_string_includes.d.ts +5 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_string_includes.js +13 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_throws.d.ts +54 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert_throws.js +44 -0
- package/esm/deps/deno.land/std@0.195.0/assert/equal.d.ts +6 -0
- package/esm/deps/deno.land/std@0.195.0/assert/equal.js +102 -0
- package/esm/deps/deno.land/std@0.195.0/assert/fail.d.ts +4 -0
- package/esm/deps/deno.land/std@0.195.0/assert/fail.js +9 -0
- package/esm/deps/deno.land/std@0.195.0/assert/mod.d.ts +32 -0
- package/esm/deps/deno.land/std@0.195.0/assert/mod.js +33 -0
- package/esm/deps/deno.land/std@0.195.0/assert/unimplemented.d.ts +2 -0
- package/esm/deps/deno.land/std@0.195.0/assert/unimplemented.js +7 -0
- package/esm/deps/deno.land/std@0.195.0/assert/unreachable.d.ts +2 -0
- package/esm/deps/deno.land/std@0.195.0/assert/unreachable.js +6 -0
- package/esm/deps/deno.land/std@0.195.0/testing/_test_suite.d.ts +70 -0
- package/esm/deps/deno.land/std@0.195.0/testing/_test_suite.js +321 -0
- package/esm/deps/deno.land/std@0.195.0/testing/asserts.d.ts +329 -0
- package/esm/deps/deno.land/std@0.195.0/testing/asserts.js +330 -0
- package/esm/deps/deno.land/std@0.195.0/testing/bdd.d.ts +440 -0
- package/esm/deps/deno.land/std@0.195.0/testing/bdd.js +215 -0
- package/esm/deps/deno.land/std@0.195.0/testing/mock.d.ts +110 -0
- package/esm/deps/deno.land/std@0.195.0/testing/mock.js +746 -0
- package/esm/src/action/clone.js +4 -3
- package/esm/src/action/frontend-template-switch.d.ts +1 -0
- package/esm/src/action/frontend-template-switch.js +53 -0
- package/esm/src/action/frontend-template.d.ts +2 -0
- package/esm/src/action/frontend-template.js +12 -0
- package/esm/src/action/lang.js +11 -2
- package/esm/src/action/run.js +6 -7
- package/esm/src/main.js +9 -2
- package/esm/src/meta.d.ts +1 -1
- package/esm/src/meta.js +108 -25
- package/esm/src/orca/client.js +1 -1
- package/esm/src/shared/config.d.ts +1 -0
- package/esm/src/shared/errors.d.ts +7 -1
- package/esm/src/shared/errors.js +16 -3
- package/esm/src/shared/file.js +1 -1
- package/esm/src/shared/mod.d.ts +1 -1
- package/esm/src/shared/mod.js +10 -2
- package/esm/src/shared/types.d.ts +16 -0
- package/esm/src/shared/types.js +2 -0
- package/esm/src/track/client.d.ts +4 -1
- package/esm/src/track/test.d.ts +6 -2
- package/esm/src/track/test.js +28 -5
- package/esm/src/track/training.d.ts +4 -1
- package/esm/src/track/training.js +9 -0
- package/esm/test/shared/config_test.d.ts +1 -0
- package/esm/test/shared/config_test.js +57 -0
- package/esm/test/shared/file_test.d.ts +1 -0
- package/esm/test/shared/file_test.js +265 -0
- package/esm/test/shared/mod_test.d.ts +1 -0
- package/esm/test/shared/mod_test.js +353 -0
- package/package.json +1 -1
- package/test_runner.js +186 -0
package/esm/src/action/clone.js
CHANGED
|
@@ -8,9 +8,9 @@ import { TrainingClient } from "../track/training.js";
|
|
|
8
8
|
import { exists } from "../../deps/deno.land/std@0.195.0/fs/mod.js";
|
|
9
9
|
export async function clone(url, options) {
|
|
10
10
|
// console.log(`url: ${url}`);
|
|
11
|
-
// console.log(`target_dir: ${targetDir}`);
|
|
12
|
-
// console.log(`basicAuthUser: ${basicAuthUser}`);
|
|
13
|
-
// console.log(`basicAuthPassword: ${basicAuthPassword}`);
|
|
11
|
+
// console.log(`target_dir: ${options.targetDir}`);
|
|
12
|
+
// console.log(`basicAuthUser: ${options.basicAuthUser}`);
|
|
13
|
+
// console.log(`basicAuthPassword: ${options.basicAuthPassword}`);
|
|
14
14
|
const cloneUrl = new CloneUrl(url);
|
|
15
15
|
const basicAuth = options?.basicAuthUser
|
|
16
16
|
? {
|
|
@@ -55,6 +55,7 @@ export async function clone(url, options) {
|
|
|
55
55
|
token: cloneUrl.token,
|
|
56
56
|
cookies: api.getCookies(),
|
|
57
57
|
programmingLanguage: result.programmingLanguage,
|
|
58
|
+
frontendTemplate: codingContext.selectedFrontendTemplate?.seriesCode,
|
|
58
59
|
});
|
|
59
60
|
await config.save(trackConfig, {
|
|
60
61
|
path: `${challengeDir}/.track/config.json`,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function switchFrontendTemplate(): Promise<void>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { load as loadConfig, save as saveConfig, } from "../shared/config.js";
|
|
3
|
+
import { OtherError } from "../shared/errors.js";
|
|
4
|
+
import { archiveExistingChallengeFiles, chooseFrontendTemplate, downloadChallengeFilesTo, getCommonChallengeContext, trackClientFromConfig, } from "../shared/mod.js";
|
|
5
|
+
import { ChallengeStyle } from "../shared/types.js";
|
|
6
|
+
export async function switchFrontendTemplate() {
|
|
7
|
+
const config = await loadConfig();
|
|
8
|
+
const api = trackClientFromConfig(config);
|
|
9
|
+
const [codingContext, challenge] = await getCommonChallengeContext(config, api);
|
|
10
|
+
if (challenge.style !== ChallengeStyle.Frontend) {
|
|
11
|
+
throw new OtherError("This challenge is not a Frontend Framework (VSCode) challenge." +
|
|
12
|
+
"\n\thint: Use 'track lang' to change programming language instead.");
|
|
13
|
+
}
|
|
14
|
+
// Get available frontend templates from Track
|
|
15
|
+
const allFrontendTemplates = await api.frontendTemplates();
|
|
16
|
+
// Filter by allowed templates for this challenge
|
|
17
|
+
const allowedCodes = challenge.allowedTemplateSeriesCodes;
|
|
18
|
+
const availableTemplates = allowedCodes && allowedCodes.length > 0
|
|
19
|
+
? allFrontendTemplates.filter((t) => allowedCodes.includes(t.seriesCode))
|
|
20
|
+
: allFrontendTemplates;
|
|
21
|
+
if (availableTemplates.length === 0) {
|
|
22
|
+
throw new OtherError("This challenge does not support changing frontend templates");
|
|
23
|
+
}
|
|
24
|
+
console.log("Your existing code will be archived in this directory and the original challenge code for the new template will be downloaded");
|
|
25
|
+
const shouldContinue = dntShim.confirm("Are you sure you want to continue?");
|
|
26
|
+
if (!shouldContinue) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const desiredTemplate = chooseFrontendTemplate(availableTemplates);
|
|
30
|
+
if (desiredTemplate.seriesCode ===
|
|
31
|
+
codingContext.selectedFrontendTemplate?.seriesCode) {
|
|
32
|
+
const shouldForceReset = dntShim.confirm(`You are already using the template '${desiredTemplate.seriesCode}'. Do you want to start-over from the template code anyway?`);
|
|
33
|
+
if (!shouldForceReset) {
|
|
34
|
+
// already using this template
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
await archiveExistingChallengeFiles(config.orgName);
|
|
39
|
+
// Ask the backend to reset the user's solution to the starter code for this template.
|
|
40
|
+
await api.updateFrontendTemplate(desiredTemplate.seriesCode);
|
|
41
|
+
// Re-download the challenge files to reflect the reset solution in the backend.
|
|
42
|
+
const currentDir = dntShim.Deno.cwd();
|
|
43
|
+
const newCodingContext = await api.context();
|
|
44
|
+
const showFileDiff = false;
|
|
45
|
+
const includeTarball = false;
|
|
46
|
+
await downloadChallengeFilesTo(newCodingContext, currentDir, api, showFileDiff, includeTarball);
|
|
47
|
+
const updatedConfig = {
|
|
48
|
+
...config,
|
|
49
|
+
frontendTemplate: desiredTemplate.seriesCode,
|
|
50
|
+
};
|
|
51
|
+
await saveConfig(updatedConfig);
|
|
52
|
+
console.debug("Frontend template switch completed.");
|
|
53
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Prompt } from "../shared/mod.js";
|
|
2
|
+
export function chooseFrontendTemplate(availableTemplates) {
|
|
3
|
+
while (true) {
|
|
4
|
+
const desiredTemplateIndex = Prompt.select("Please select a frontend template:", availableTemplates.map((t) => t.seriesName));
|
|
5
|
+
if (desiredTemplateIndex !== undefined) {
|
|
6
|
+
return availableTemplates[desiredTemplateIndex];
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
console.log("An invalid frontend template was specified");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
package/esm/src/action/lang.js
CHANGED
|
@@ -2,15 +2,24 @@ import * as dntShim from "../../_dnt.shims.js";
|
|
|
2
2
|
import { load as loadConfig, save as saveConfig, } from "../shared/config.js";
|
|
3
3
|
import { CantChooseLanguage } from "../shared/errors.js";
|
|
4
4
|
import { archiveExistingChallengeFiles, chooseProgrammingLanguage, downloadChallengeFilesTo, getCommonChallengeContext, trackClientFromConfig, } from "../shared/mod.js";
|
|
5
|
+
import { ChallengeStyle, } from "../shared/types.js";
|
|
5
6
|
export async function lang() {
|
|
6
7
|
const config = await loadConfig();
|
|
7
8
|
const api = trackClientFromConfig(config);
|
|
8
9
|
const [codingContext, challenge] = await getCommonChallengeContext(config, api);
|
|
9
10
|
if (challenge.programmingLanguages.length === 0) {
|
|
10
|
-
throw new CantChooseLanguage(
|
|
11
|
+
throw new CantChooseLanguage({
|
|
12
|
+
hint: challenge.style === ChallengeStyle.Frontend
|
|
13
|
+
? "Use 'track frontend-template' to change the frontend template for a Frontend Framework (VSCode) challenge."
|
|
14
|
+
: "",
|
|
15
|
+
});
|
|
11
16
|
}
|
|
12
17
|
const availableLanguages = (await api.languages())
|
|
13
|
-
.filter((pl) =>
|
|
18
|
+
.filter((pl) => {
|
|
19
|
+
return codingContext.programmingLanguages.find((n) => {
|
|
20
|
+
return n === pl.value;
|
|
21
|
+
});
|
|
22
|
+
});
|
|
14
23
|
console.log("Your existing code will be archived in this directory and the original challenge code for the new language will be downloaded");
|
|
15
24
|
const shouldContinue = dntShim.confirm("Are you sure you want to continue?");
|
|
16
25
|
if (!shouldContinue) {
|
package/esm/src/action/run.js
CHANGED
|
@@ -59,17 +59,16 @@ export async function run(options) {
|
|
|
59
59
|
const runId = dntShim.crypto.randomUUID();
|
|
60
60
|
// Run the initialize/build commands if they exist
|
|
61
61
|
let buildDoneData;
|
|
62
|
-
|
|
63
|
-
codingContext.settings.initialize
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
.filter((s) => s && s.length > 0);
|
|
62
|
+
const buildCommands = []
|
|
63
|
+
.concat(codingContext.settings.initialize || [])
|
|
64
|
+
.concat(codingContext.answers.build || [])
|
|
65
|
+
.filter((s) => s && s.length > 0);
|
|
66
|
+
if (buildCommands.length > 0) {
|
|
68
67
|
const runCmd = {
|
|
69
68
|
id: `${runId}-build`,
|
|
70
69
|
imageName: envConfig.imageName,
|
|
71
70
|
workingDir: envConfig.workingDir ?? "",
|
|
72
|
-
shellCmd:
|
|
71
|
+
shellCmd: buildCommands.join(" && "),
|
|
73
72
|
attachVolume: true,
|
|
74
73
|
files,
|
|
75
74
|
envVars: {},
|
package/esm/src/main.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as dntShim from "../_dnt.shims.js";
|
|
|
4
4
|
import { clone as cloneAction } from "./action/clone.js";
|
|
5
5
|
import { run as runAction } from "./action/run.js";
|
|
6
6
|
import { lang as langAction } from "./action/lang.js";
|
|
7
|
+
import { switchFrontendTemplate as frontendTemplateAction } from "./action/frontend-template-switch.js";
|
|
7
8
|
import { reset as resetAction } from "./action/reset.js";
|
|
8
9
|
import { pull as pullAction } from "./action/pull.js";
|
|
9
10
|
import { add as addAction } from "./action/add.js";
|
|
@@ -37,6 +38,11 @@ import { Command } from "../deps/deno.land/x/cliffy@v0.25.7/command/mod.js";
|
|
|
37
38
|
.action(async () => {
|
|
38
39
|
await langAction();
|
|
39
40
|
});
|
|
41
|
+
const frontendTemplateCommand = new Command()
|
|
42
|
+
.description("Change the frontend template used for this challenge.")
|
|
43
|
+
.action(async () => {
|
|
44
|
+
await frontendTemplateAction();
|
|
45
|
+
});
|
|
40
46
|
const resetCommand = new Command()
|
|
41
47
|
.description("Reset the challenge files back to their starting point and archive your current challenge files.")
|
|
42
48
|
.action(async () => {
|
|
@@ -65,7 +71,7 @@ import { Command } from "../deps/deno.land/x/cliffy@v0.25.7/command/mod.js";
|
|
|
65
71
|
.action(async () => {
|
|
66
72
|
await statusAction();
|
|
67
73
|
});
|
|
68
|
-
if (!await checkUpdates()) {
|
|
74
|
+
if (!(await checkUpdates())) {
|
|
69
75
|
dntShim.Deno.exit(1);
|
|
70
76
|
}
|
|
71
77
|
await new Command()
|
|
@@ -78,6 +84,7 @@ import { Command } from "../deps/deno.land/x/cliffy@v0.25.7/command/mod.js";
|
|
|
78
84
|
.command("clone", cloneCommand)
|
|
79
85
|
.command("run", runCommand)
|
|
80
86
|
.command("lang", langCommand)
|
|
87
|
+
.command("frontend-template", frontendTemplateCommand)
|
|
81
88
|
.command("reset", resetCommand)
|
|
82
89
|
.command("pull", pullCommand)
|
|
83
90
|
.command("add", addCommand)
|
|
@@ -87,7 +94,7 @@ import { Command } from "../deps/deno.land/x/cliffy@v0.25.7/command/mod.js";
|
|
|
87
94
|
dntShim.Deno.exit(0);
|
|
88
95
|
})().catch((err) => {
|
|
89
96
|
if (err instanceof TrackError) {
|
|
90
|
-
console.error(`${red("[ERROR]")} ${err.message}`);
|
|
97
|
+
console.error(`${red("[ERROR]")} ${"message" in err ? String(err.message) : String(err)}`);
|
|
91
98
|
dntShim.Deno.exit(1);
|
|
92
99
|
}
|
|
93
100
|
else {
|
package/esm/src/meta.d.ts
CHANGED
package/esm/src/meta.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as colors from "../deps/deno.land/std@0.195.0/fmt/colors.js";
|
|
2
2
|
// @ts-ignore: has no exported member
|
|
3
3
|
import { CookieJar, fetch } from "node-fetch-cookies";
|
|
4
|
-
export const VERSION = "4.0
|
|
4
|
+
export const VERSION = "4.1.0-rc2";
|
|
5
5
|
export const DESCRIPTION = "A CLI for interacting with tracks.run and running code tests on track's servers";
|
|
6
6
|
const VERSION_RE = /^(\d+)\.(\d+)\.(\d+)(?:-?(.*))$/;
|
|
7
7
|
function parseSemver(s) {
|
|
@@ -9,43 +9,126 @@ function parseSemver(s) {
|
|
|
9
9
|
if (!match) {
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
|
-
return [
|
|
13
|
-
parseInt(match[1]),
|
|
14
|
-
parseInt(match[2]),
|
|
15
|
-
parseInt(match[3]),
|
|
16
|
-
match[4],
|
|
17
|
-
];
|
|
12
|
+
return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3]), match[4]];
|
|
18
13
|
}
|
|
19
14
|
export async function checkUpdates() {
|
|
20
15
|
const res = await fetch(new CookieJar(), "https://track-cli.s3-ap-northeast-1.amazonaws.com/version.json");
|
|
21
16
|
const publishedVersion = await res.json();
|
|
22
|
-
const supportedFrom = parseSemver(publishedVersion.supported_from) ??
|
|
23
|
-
|
|
17
|
+
const supportedFrom = parseSemver(publishedVersion.supported_from) ?? [
|
|
18
|
+
1,
|
|
19
|
+
0,
|
|
20
|
+
0,
|
|
21
|
+
"",
|
|
22
|
+
];
|
|
24
23
|
const latest = parseSemver(publishedVersion.latest) ?? [1, 0, 0, ""];
|
|
25
24
|
const current = parseSemver(VERSION);
|
|
26
|
-
// deno-fmt-ignore
|
|
27
25
|
if (parseSemver(VERSION) < supportedFrom) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
console.log("| |");
|
|
35
|
-
console.log("╰─────────────────────────────────────────────────────────────────────────────────────────────╯");
|
|
26
|
+
new Box({ width: 95 }).printBox([
|
|
27
|
+
{ center: true },
|
|
28
|
+
[
|
|
29
|
+
applyFormatting("!!! OUTDATED !!!", (s) => colors.bold(colors.red(s))),
|
|
30
|
+
],
|
|
31
|
+
], [{ left: 22 }, ["This version of Track CLI is no longer supported."]], [{ left: 22 + 11 }, [`Installed version: ${VERSION}`]], [{ left: 22 + 11 }, [` Latest version: ${publishedVersion.latest}`]]);
|
|
36
32
|
return false;
|
|
37
33
|
}
|
|
38
34
|
else if (current < latest) {
|
|
39
|
-
|
|
40
|
-
console.log("| |");
|
|
41
|
-
console.log("| New version of Track CLI is available! |");
|
|
42
|
-
console.log(`| Current version: ${VERSION.padEnd(20)} |`);
|
|
43
|
-
console.log(`| Latest version: ${publishedVersion.latest.padEnd(20)} |`);
|
|
44
|
-
console.log("| |");
|
|
45
|
-
console.log("╰─────────────────────────────────────────────────────────────────────────────────────────────╯");
|
|
35
|
+
new Box({ width: 95 }).printBox([{ left: 22 }, ["A new version of Track CLI is available!"]], [{ left: 22 + 11 }, [`Installed version: ${VERSION}`]], [{ left: 22 + 11 }, [` Latest version: ${publishedVersion.latest}`]]);
|
|
46
36
|
return true;
|
|
47
37
|
}
|
|
48
38
|
else {
|
|
49
39
|
return true;
|
|
50
40
|
}
|
|
51
41
|
}
|
|
42
|
+
function applyFormatting(s, formatting) {
|
|
43
|
+
return { textWidth: s.length, formatted: formatting(s) };
|
|
44
|
+
}
|
|
45
|
+
class Box {
|
|
46
|
+
/**
|
|
47
|
+
* Inclusive of the pipes and the 1 space padding on each side:
|
|
48
|
+
* `"| content |"` has width 11.
|
|
49
|
+
*/
|
|
50
|
+
constructor(p) {
|
|
51
|
+
Object.defineProperty(this, "p", {
|
|
52
|
+
enumerable: true,
|
|
53
|
+
configurable: true,
|
|
54
|
+
writable: true,
|
|
55
|
+
value: p
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
print(message) {
|
|
59
|
+
console.log(message);
|
|
60
|
+
}
|
|
61
|
+
printBox(...lines) {
|
|
62
|
+
this.printTop();
|
|
63
|
+
this.printLine({ left: 0 }, [""]);
|
|
64
|
+
for (const [align, content] of lines) {
|
|
65
|
+
this.printLine(align, content);
|
|
66
|
+
}
|
|
67
|
+
this.printLine({ left: 0 }, [""]);
|
|
68
|
+
this.printBottom();
|
|
69
|
+
}
|
|
70
|
+
printTop() {
|
|
71
|
+
const message = Box.CORNERS[0][0] +
|
|
72
|
+
Box.HORIZONTAL.repeat(this.p.width - 2) +
|
|
73
|
+
Box.CORNERS[0][1];
|
|
74
|
+
this.print(message);
|
|
75
|
+
}
|
|
76
|
+
printBottom() {
|
|
77
|
+
const message = Box.CORNERS[1][0] +
|
|
78
|
+
Box.HORIZONTAL.repeat(this.p.width - 2) +
|
|
79
|
+
Box.CORNERS[1][1];
|
|
80
|
+
this.print(message);
|
|
81
|
+
}
|
|
82
|
+
printLine(align, content) {
|
|
83
|
+
let contentWidth = 0;
|
|
84
|
+
const parts = [];
|
|
85
|
+
for (const part of content) {
|
|
86
|
+
if (typeof part === "string") {
|
|
87
|
+
contentWidth += part.length;
|
|
88
|
+
parts.push(part);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
contentWidth += part.textWidth;
|
|
92
|
+
parts.push(part.formatted);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const innerWidth = this.p.width - 4;
|
|
96
|
+
let leftPadding = 0;
|
|
97
|
+
if (align.center) {
|
|
98
|
+
leftPadding = Math.max(0, Math.floor((innerWidth - contentWidth) / 2));
|
|
99
|
+
}
|
|
100
|
+
else if (align.left !== undefined) {
|
|
101
|
+
leftPadding = align.left;
|
|
102
|
+
}
|
|
103
|
+
const rightPadding = innerWidth - leftPadding - contentWidth;
|
|
104
|
+
const message = [
|
|
105
|
+
Box.PIPE,
|
|
106
|
+
" ".repeat(1 + leftPadding),
|
|
107
|
+
...parts,
|
|
108
|
+
rightPadding > 0 ? " ".repeat(rightPadding + 1) : "",
|
|
109
|
+
rightPadding >= 0 ? Box.PIPE : "",
|
|
110
|
+
].join("");
|
|
111
|
+
this.print(message);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
Object.defineProperty(Box, "PIPE", {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
configurable: true,
|
|
117
|
+
writable: true,
|
|
118
|
+
value: "|"
|
|
119
|
+
});
|
|
120
|
+
Object.defineProperty(Box, "HORIZONTAL", {
|
|
121
|
+
enumerable: true,
|
|
122
|
+
configurable: true,
|
|
123
|
+
writable: true,
|
|
124
|
+
value: "─"
|
|
125
|
+
});
|
|
126
|
+
Object.defineProperty(Box, "CORNERS", {
|
|
127
|
+
enumerable: true,
|
|
128
|
+
configurable: true,
|
|
129
|
+
writable: true,
|
|
130
|
+
value: [
|
|
131
|
+
["╭", "╮"],
|
|
132
|
+
["╰", "╯"],
|
|
133
|
+
]
|
|
134
|
+
});
|
package/esm/src/orca/client.js
CHANGED
|
@@ -47,8 +47,14 @@ export declare class InvalidChallengeId extends TrackError {
|
|
|
47
47
|
challengeId: number;
|
|
48
48
|
constructor(challengeId: number);
|
|
49
49
|
}
|
|
50
|
+
export declare class UnsupportedChallenge extends TrackError {
|
|
51
|
+
challengeId: number;
|
|
52
|
+
constructor(challengeId: number);
|
|
53
|
+
}
|
|
50
54
|
export declare class CantChooseLanguage extends TrackError {
|
|
51
|
-
constructor(
|
|
55
|
+
constructor(p?: {
|
|
56
|
+
hint?: string;
|
|
57
|
+
});
|
|
52
58
|
}
|
|
53
59
|
export declare class NotASingleResponse extends TrackError {
|
|
54
60
|
constructor();
|
package/esm/src/shared/errors.js
CHANGED
|
@@ -119,7 +119,7 @@ export class ChallengeAlreadyFinished extends TrackError {
|
|
|
119
119
|
}
|
|
120
120
|
export class InvalidChallengeId extends TrackError {
|
|
121
121
|
constructor(challengeId) {
|
|
122
|
-
super(`The challenge id specified in your URL (${challengeId})
|
|
122
|
+
super(`The challenge id specified in your URL (${challengeId}) is invalid`);
|
|
123
123
|
Object.defineProperty(this, "challengeId", {
|
|
124
124
|
enumerable: true,
|
|
125
125
|
configurable: true,
|
|
@@ -129,9 +129,22 @@ export class InvalidChallengeId extends TrackError {
|
|
|
129
129
|
this.name = "InvalidChallengeId";
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
+
export class UnsupportedChallenge extends TrackError {
|
|
133
|
+
constructor(challengeId) {
|
|
134
|
+
super(`The challenge id specified in your URL (${challengeId}) is not supported by the 'track' CLI`);
|
|
135
|
+
Object.defineProperty(this, "challengeId", {
|
|
136
|
+
enumerable: true,
|
|
137
|
+
configurable: true,
|
|
138
|
+
writable: true,
|
|
139
|
+
value: challengeId
|
|
140
|
+
});
|
|
141
|
+
this.name = "UnsupportedChallenge";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
132
144
|
export class CantChooseLanguage extends TrackError {
|
|
133
|
-
constructor() {
|
|
134
|
-
super("This challenge does not support changing programming languages"
|
|
145
|
+
constructor(p = {}) {
|
|
146
|
+
super("This challenge does not support changing programming languages." +
|
|
147
|
+
(p.hint ? `\n\thint: ${p.hint}` : ""));
|
|
135
148
|
this.name = "CantChooseLanguage";
|
|
136
149
|
}
|
|
137
150
|
}
|
package/esm/src/shared/file.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
-
import * as path from "../../deps/deno.land/std@0.195.0/path/mod.js";
|
|
3
2
|
import { Foras, gunzip } from "@hazae41/foras";
|
|
4
3
|
import tar from "tar";
|
|
5
4
|
import * as fs from "../../deps/deno.land/std@0.195.0/fs/mod.js";
|
|
5
|
+
import * as path from "../../deps/deno.land/std@0.195.0/path/mod.js";
|
|
6
6
|
export const _internals = {
|
|
7
7
|
SEP: path.SEP,
|
|
8
8
|
};
|
package/esm/src/shared/mod.d.ts
CHANGED
|
@@ -39,5 +39,5 @@ export type ArchiveExistingChallengeFilesOptions = {
|
|
|
39
39
|
selectedLanguage?: ProgrammingLanguageInfo;
|
|
40
40
|
};
|
|
41
41
|
export declare function archiveExistingChallengeFiles(orgName: string): Promise<void>;
|
|
42
|
+
export { chooseFrontendTemplate } from "../action/frontend-template.js";
|
|
42
43
|
export declare function trackClientFromConfig(config: TrackConfig): TrackClient;
|
|
43
|
-
export {};
|
package/esm/src/shared/mod.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as dntShim from "../../_dnt.shims.js";
|
|
|
2
2
|
import { save as saveConfig, } from "./config.js";
|
|
3
3
|
import { BadTarballURL, ChallengeAlreadyFinished, OtherError, } from "./errors.js";
|
|
4
4
|
import { toNativeStyle, untgz } from "./file.js";
|
|
5
|
-
import { ChallengeResultStatus, FileListType, TrackApp, } from "./types.js";
|
|
5
|
+
import { ChallengeResultStatus, ChallengeStyle, FileListType, TrackApp, } from "./types.js";
|
|
6
6
|
import { TestClient } from "../track/test.js";
|
|
7
7
|
import { TrainingClient } from "../track/training.js";
|
|
8
8
|
import * as datetime from "../../deps/deno.land/std@0.195.0/datetime/mod.js";
|
|
@@ -100,6 +100,13 @@ export async function tryStartingChallenge(api, challenge) {
|
|
|
100
100
|
await api.updateLanguage(desiredLanguage.value);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
+
// Handle template selection for frontend challenges
|
|
104
|
+
if (challenge.style === ChallengeStyle.Frontend) {
|
|
105
|
+
const frontendTemplates = await api.frontendTemplates();
|
|
106
|
+
if (frontendTemplates.length > 0) {
|
|
107
|
+
console.log("Frontend challenge detected. Use 'track frontend-template' to switch templates after cloning.");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
103
110
|
result = await api.start();
|
|
104
111
|
break;
|
|
105
112
|
}
|
|
@@ -354,11 +361,12 @@ export /**
|
|
|
354
361
|
await fs.move(`${challengeDir}/${dirEntry.name}`, `${archiveDir}/${dirEntry.name}`);
|
|
355
362
|
}
|
|
356
363
|
}
|
|
364
|
+
export { chooseFrontendTemplate } from "../action/frontend-template.js";
|
|
357
365
|
export function trackClientFromConfig(config) {
|
|
358
366
|
let api;
|
|
359
367
|
switch (config.app) {
|
|
360
368
|
case TrackApp.Test:
|
|
361
|
-
api = new TestClient(config.baseUrl, config.orgName, config.token, config.challengeId, config.basicAuth, config.cookies);
|
|
369
|
+
api = new TestClient(config.baseUrl, config.orgName, config.token, config.challengeId, config.basicAuth, config.cookies, config.applicantExamId, config.challengeResultId);
|
|
362
370
|
break;
|
|
363
371
|
case TrackApp.Training:
|
|
364
372
|
api = new TrainingClient(config.baseUrl, config.orgName, config.token, config.courseMaterialId, config.klassId, config.klassResultId, config.basicAuth, config.cookies);
|
|
@@ -25,6 +25,7 @@ export interface ChallengeSession {
|
|
|
25
25
|
timeLimitMinutes?: number;
|
|
26
26
|
displayOrder: number;
|
|
27
27
|
programmingLanguages: ProgrammingLanguage[];
|
|
28
|
+
allowedTemplateSeriesCodes?: string[];
|
|
28
29
|
localCodingAllowed: boolean;
|
|
29
30
|
openTestcases?: number;
|
|
30
31
|
result?: ChallengeResult;
|
|
@@ -59,6 +60,8 @@ export interface CodingContext {
|
|
|
59
60
|
allowEdit?: boolean;
|
|
60
61
|
programmingLanguages: ProgrammingLanguage[];
|
|
61
62
|
selectedLanguage?: ProgrammingLanguage;
|
|
63
|
+
selectedFrontendTemplate?: FrontendTemplateInfo;
|
|
64
|
+
availableFrontendTemplates?: FrontendTemplateInfo[];
|
|
62
65
|
challengeLanguage: SpokenLanguage;
|
|
63
66
|
settings: ChallengeSettings;
|
|
64
67
|
answers: CodingAnswers;
|
|
@@ -121,7 +124,9 @@ export declare const ChallengeStyle: {
|
|
|
121
124
|
readonly Quiz: 1;
|
|
122
125
|
readonly Development: 2;
|
|
123
126
|
readonly Algorithm: 3;
|
|
127
|
+
readonly Function: 7;
|
|
124
128
|
readonly Ai: 4;
|
|
129
|
+
readonly Frontend: 8;
|
|
125
130
|
};
|
|
126
131
|
export type ChallengeStyle = (typeof ChallengeStyle)[keyof typeof ChallengeStyle];
|
|
127
132
|
export declare const ChallengeResultStatus: {
|
|
@@ -140,3 +145,14 @@ export declare const SpokenLanguage: {
|
|
|
140
145
|
};
|
|
141
146
|
export type SpokenLanguage = (typeof SpokenLanguage)[keyof typeof SpokenLanguage];
|
|
142
147
|
export type ProgrammingLanguage = number;
|
|
148
|
+
export interface FrontendTemplateInfo {
|
|
149
|
+
seriesCode: string;
|
|
150
|
+
seriesName: string;
|
|
151
|
+
versionId: number;
|
|
152
|
+
versionName: string;
|
|
153
|
+
downloadUrl?: string;
|
|
154
|
+
readmeUrl?: {
|
|
155
|
+
en?: string;
|
|
156
|
+
ja?: string;
|
|
157
|
+
};
|
|
158
|
+
}
|
package/esm/src/shared/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as dntShim from "../../_dnt.shims.js";
|
|
2
2
|
import { TrackTestConfigPart, TrackTrainingConfigPart } from "../shared/config.js";
|
|
3
|
-
import { ChallengeResult, ChallengeSession, CodingContext, ProgrammingLanguage, ProgrammingLanguageInfo, SaveFilesRequest } from "../shared/types.js";
|
|
3
|
+
import { ChallengeResult, ChallengeSession, CodingContext, FrontendTemplateInfo, ProgrammingLanguage, ProgrammingLanguageInfo, SaveFilesRequest } from "../shared/types.js";
|
|
4
4
|
import { CookieJar } from "node-fetch-cookies";
|
|
5
5
|
import { ProxyAgent } from "proxy-agent";
|
|
6
6
|
export interface BasicAuth {
|
|
@@ -41,9 +41,12 @@ export interface TrackClient {
|
|
|
41
41
|
};
|
|
42
42
|
startChallengeSession(): Promise<ChallengeSession>;
|
|
43
43
|
languages(): Promise<ProgrammingLanguageInfo[]>;
|
|
44
|
+
frontendTemplates(): Promise<FrontendTemplateInfo[]>;
|
|
45
|
+
frontendTemplate(code: string): Promise<FrontendTemplateInfo>;
|
|
44
46
|
start(): Promise<ChallengeResult>;
|
|
45
47
|
prepare(): Promise<ChallengeResult>;
|
|
46
48
|
updateLanguage(language: ProgrammingLanguage): Promise<void>;
|
|
49
|
+
updateFrontendTemplate(templateCode: string): Promise<void>;
|
|
47
50
|
timeLeft(): Promise<number>;
|
|
48
51
|
context(): Promise<CodingContext>;
|
|
49
52
|
presigned(files: string[]): Promise<{
|
package/esm/src/track/test.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TrackTestConfigPart } from "../shared/config.js";
|
|
2
|
-
import { ChallengeResult, ChallengeSession, CodingContext, ProgrammingLanguage, ProgrammingLanguageInfo, SaveFilesRequest } from "../shared/types.js";
|
|
2
|
+
import { ChallengeResult, ChallengeSession, CodingContext, FrontendTemplateInfo, ProgrammingLanguage, ProgrammingLanguageInfo, SaveFilesRequest } from "../shared/types.js";
|
|
3
3
|
import { BasicAuth, TrackClient, TrackClientBase } from "./client.js";
|
|
4
4
|
export declare class TestClient extends TrackClientBase implements TrackClient {
|
|
5
5
|
private orgName;
|
|
@@ -7,7 +7,7 @@ export declare class TestClient extends TrackClientBase implements TrackClient {
|
|
|
7
7
|
private challengeId;
|
|
8
8
|
private applicantExamId;
|
|
9
9
|
private resultId;
|
|
10
|
-
constructor(baseUrl: string, orgName: string, token: string, challengeId: number, basic?: BasicAuth, cookies?: Record<string, string
|
|
10
|
+
constructor(baseUrl: string, orgName: string, token: string, challengeId: number, basic?: BasicAuth, cookies?: Record<string, string>, applicantExamId?: number, challengeResultId?: number);
|
|
11
11
|
config(): TrackTestConfigPart;
|
|
12
12
|
authenticate(): Promise<void>;
|
|
13
13
|
startChallengeSession(): Promise<ChallengeSession>;
|
|
@@ -15,10 +15,14 @@ export declare class TestClient extends TrackClientBase implements TrackClient {
|
|
|
15
15
|
private ensureExamInProgress;
|
|
16
16
|
private getExamSession;
|
|
17
17
|
private getChallengeFromExamSession;
|
|
18
|
+
isChallengeSupported(c: ChallengeSession): boolean;
|
|
18
19
|
languages(): Promise<ProgrammingLanguageInfo[]>;
|
|
20
|
+
frontendTemplates(): Promise<FrontendTemplateInfo[]>;
|
|
21
|
+
frontendTemplate(code: string): Promise<FrontendTemplateInfo>;
|
|
19
22
|
start(): Promise<ChallengeResult>;
|
|
20
23
|
prepare(): Promise<ChallengeResult>;
|
|
21
24
|
updateLanguage(language: ProgrammingLanguage): Promise<void>;
|
|
25
|
+
updateFrontendTemplate(seriesCode: string): Promise<void>;
|
|
22
26
|
timeLeft(): Promise<number>;
|
|
23
27
|
context(): Promise<CodingContext>;
|
|
24
28
|
presigned(files: string[]): Promise<{
|