track-cli 3.1.0-rc1 → 4.0.0-rc0

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.
@@ -5,7 +5,7 @@ import { fetch, File, FormData, Headers, Request, Response } from "undici";
5
5
  export { fetch, File, FormData, Headers, Request, Response, type BodyInit, type HeadersInit, type RequestInit, type ResponseInit } from "undici";
6
6
  import { alert, confirm, prompt } from "./shim/prompts.js";
7
7
  export { alert, confirm, prompt } from "./shim/prompts.js";
8
- export declare const dntGlobalThis: Omit<typeof globalThis, "Deno" | "crypto" | "fetch" | "File" | "FormData" | "Headers" | "Request" | "Response" | "alert" | "confirm" | "prompt"> & {
8
+ export declare const dntGlobalThis: Omit<typeof globalThis, "fetch" | "Request" | "Response" | "FormData" | "Headers" | "Deno" | "crypto" | "File" | "alert" | "confirm" | "prompt"> & {
9
9
  Deno: typeof Deno;
10
10
  crypto: import("@deno/shim-crypto").Crypto;
11
11
  fetch: typeof fetch;
package/esm/_dnt.shims.js CHANGED
@@ -20,6 +20,7 @@ const dntGlobals = {
20
20
  prompt,
21
21
  };
22
22
  export const dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
23
+ // deno-lint-ignore ban-types
23
24
  function createMergeProxy(baseObj, extObj) {
24
25
  return new Proxy(baseObj, {
25
26
  get(_target, prop, _receiver) {
@@ -1,14 +1,14 @@
1
1
  import * as dntShim from "../../_dnt.shims.js";
2
- import { CONFIG_FILE_PATH } from "../shared/config.js";
2
+ import { CONFIG_FILE_PATH, load as loadConfig } from "../shared/config.js";
3
3
  import { CantReadFile, NewFilesNotAllowed } from "../shared/errors.js";
4
4
  import { pathsToFilenameSet, toNativeStyle } from "../shared/file.js";
5
- import { getCommonChallengeContext, listFileNames, printWorkingFileSet, } from "../shared/mod.js";
6
- import { FileListType } from "../track/types.js";
5
+ import { getCommonChallengeContext, listFileNames, printWorkingFileSet, trackClientFromConfig, } from "../shared/mod.js";
6
+ import { FileListType } from "../shared/types.js";
7
7
  import * as colors from "../../deps/deno.land/std@0.195.0/fmt/colors.js";
8
8
  export async function add(filePaths) {
9
- // console.log("add", filePaths);
10
- const trackContext = await getCommonChallengeContext();
11
- const { config, api, codingContext } = trackContext;
9
+ const config = await loadConfig();
10
+ const api = trackClientFromConfig(config);
11
+ const [codingContext, _challenge] = await getCommonChallengeContext(config, api);
12
12
  if (!codingContext.settings.allowNewFile) {
13
13
  throw new NewFilesNotAllowed();
14
14
  }
@@ -45,8 +45,8 @@ export async function add(filePaths) {
45
45
  console.log(`\t${toNativeStyle(path)}`);
46
46
  }
47
47
  console.log();
48
- await api.saveFiles(config.applicantExamId, config.challengeResultId, saveFilesRequest);
49
- const newCodingContext = await api.getChallengeCodingContext(config.applicantExamId, config.challengeResultId);
48
+ await api.saveFiles(saveFilesRequest);
49
+ const newCodingContext = await api.context();
50
50
  printWorkingFileSet(newCodingContext);
51
51
  }
52
52
  else {
@@ -1,108 +1,78 @@
1
1
  import * as dntShim from "../../_dnt.shims.js";
2
2
  import * as config from "../shared/config.js";
3
- import { ChallengeDirectoryExists, InvalidChallengeId, LocalExamNotAllowed, OtherError, } from "../shared/errors.js";
4
- import { downloadChallengeFilesTo, ensureExamInProgress, getChallengeFromExamSession, printTimeLeft, tryStartingChallenge, } from "../shared/mod.js";
5
- import { TrackClient } from "../track/client.js";
3
+ import { LocalCodingNotAllowed, OtherError } from "../shared/errors.js";
4
+ import { downloadChallengeFilesTo, printTimeLeft, tryStartingChallenge, } from "../shared/mod.js";
5
+ import { CloneUrl, TrackApp } from "../shared/types.js";
6
+ import { TestClient } from "../track/test.js";
7
+ import { TrainingClient } from "../track/training.js";
6
8
  import { exists } from "../../deps/deno.land/std@0.195.0/fs/mod.js";
7
9
  export async function clone(url, options) {
8
10
  // console.log(`url: ${url}`);
9
11
  // console.log(`target_dir: ${targetDir}`);
10
12
  // console.log(`basicAuthUser: ${basicAuthUser}`);
11
13
  // console.log(`basicAuthPassword: ${basicAuthPassword}`);
12
- const cloneData = parseUrl(url);
14
+ const cloneUrl = new CloneUrl(url);
13
15
  const basicAuth = options?.basicAuthUser
14
16
  ? {
15
17
  username: options?.basicAuthUser,
16
18
  password: options?.basicAuthPassword,
17
19
  }
18
20
  : undefined;
19
- const authData = {
20
- examToken: cloneData.examToken,
21
- orgName: cloneData.orgName,
22
- };
23
- const api = new TrackClient(cloneData.scheme, cloneData.host, basicAuth, authData);
24
- await api.authenticate();
25
- const applicantExam = await api.getApplicantExamNoAuth(cloneData.examToken);
26
- const webUrl = `${cloneData.scheme}://${cloneData.host}/${cloneData.orgName}/exams/${cloneData.examToken}`;
27
- ensureExamInProgress(applicantExam, webUrl);
28
- const examSession = await api.getExamSession(applicantExam.id);
29
- // Determine challenge directory
30
- const challengePosition = examSession.challengesSets
31
- .flatMap((set) => set.challenges)
32
- .findIndex((challenge) => challenge.id === cloneData.challengeId) +
33
- 1;
34
- const challengeDir = options?.targetDir ||
35
- `${cloneData.orgName}-challenge-${challengePosition}`;
36
- const configDir = `${challengeDir}/.track`;
37
- const configFile = `${configDir}/config.json`;
38
- const pathExists = await exists(challengeDir);
39
- if (pathExists) {
40
- // オリジナルの Track CLI では空ディレクトリなら OK だったが、そのケースで特に便利にならないので、ファイル・ディレクトリが存在したら無条件でエラーに
41
- throw new ChallengeDirectoryExists(challengeDir);
21
+ let api;
22
+ switch (cloneUrl.app) {
23
+ case TrackApp.Test:
24
+ api = new TestClient(cloneUrl.baseUrl, cloneUrl.orgName, cloneUrl.token, cloneUrl.id, basicAuth);
25
+ break;
26
+ case TrackApp.Training:
27
+ api = new TrainingClient(cloneUrl.baseUrl, cloneUrl.orgName, cloneUrl.token, cloneUrl.id, 0, 0, basicAuth);
28
+ break;
42
29
  }
43
- const challenge = getChallengeFromExamSession(examSession, cloneData.challengeId);
44
- if (!challenge) {
45
- throw new InvalidChallengeId(cloneData.challengeId);
46
- }
47
- if (!challenge.localExamEnabled) {
48
- throw new LocalExamNotAllowed(webUrl);
30
+ await api.authenticate();
31
+ const challenge = await api.startChallengeSession();
32
+ if (!challenge.localCodingAllowed) {
33
+ const webUrl = cloneUrl.toString(); // TODO: Test の場合は clone URL がそのまま受験画面だったけど、Training の場合は?
34
+ throw new LocalCodingNotAllowed(webUrl);
49
35
  }
50
- // Find the challenge result if it exists
51
- const resultOpt = getResultFromExamSession(examSession, cloneData.challengeId);
52
- // Steps:
53
- // - Prepare Challenge
54
- // - Change language
55
- // - Start Challenge
56
- // - TimeLeft
57
- // - Context
58
- // - Presigned
59
- // - Download files
60
- // TODO - Print warnings about challenge time limit, having
61
- // a stable internet connection, and what to do next
62
- // in the challenge solving process.
63
- const result = await tryStartingChallenge(challenge, api, applicantExam, resultOpt);
36
+ // Determine challenge directory
37
+ const challengeDir = await createChallengeDir(options?.targetDir ||
38
+ `${cloneUrl.orgName}-challenge-${challenge.challengeId}`);
39
+ const result = await tryStartingChallenge(api, challenge);
64
40
  if (!result) {
65
41
  throw new OtherError("Expected to have a challenge result after preparing and starting");
66
42
  }
67
- const timeLeft = await api.timeLeft(applicantExam.id, result.id);
43
+ const timeLeft = await api.timeLeft();
68
44
  printTimeLeft(timeLeft);
69
- const codingContext = await api.getChallengeCodingContext(applicantExam.id, result.id);
45
+ const codingContext = await api.context();
70
46
  await dntShim.Deno.mkdir(challengeDir, { recursive: true });
71
47
  const showFileDiff = false;
72
48
  const includeTarball = true;
73
- await downloadChallengeFilesTo(codingContext, challengeDir, api, applicantExam.id, result.id, showFileDiff, includeTarball);
74
- const trackConfig = {
75
- scheme: cloneData.scheme,
76
- host: cloneData.host,
77
- orgName: cloneData.orgName,
49
+ await downloadChallengeFilesTo(codingContext, challengeDir, api, showFileDiff, includeTarball);
50
+ const trackConfig = Object.assign(api.config(), {
51
+ app: cloneUrl.app,
52
+ baseUrl: cloneUrl.baseUrl,
53
+ orgName: cloneUrl.orgName,
78
54
  basicAuth: basicAuth,
79
- examToken: cloneData.examToken,
80
- applicantExamId: applicantExam.id,
81
- challengeId: cloneData.challengeId,
82
- challengeResultId: result.id,
55
+ token: cloneUrl.token,
83
56
  cookies: api.getCookies(),
84
57
  programmingLanguage: result.programmingLanguage,
85
- };
86
- await config.save(trackConfig, { path: configFile });
58
+ });
59
+ await config.save(trackConfig, {
60
+ path: `${challengeDir}/.track/config.json`,
61
+ });
87
62
  console.log();
88
63
  console.log(`Challenge downloaded to ${challengeDir}`);
89
64
  console.log(`Run 'cd ${challengeDir} && track status' to begin!`);
90
65
  }
91
- const PATH_PATTERN = new RegExp("([^/]+)/exams/([^/]+)/challenges/(\\d+)");
92
- function parseUrl(url) {
93
- const urlObj = new URL(url);
94
- const caps = PATH_PATTERN.exec(url);
95
- if (!caps) {
96
- throw new OtherError(`Invalid URL: ${url}`);
66
+ async function createChallengeDir(base) {
67
+ let i = 1;
68
+ while (true) {
69
+ const challengeDir = i == 1 ? base : `${base}-${i}`;
70
+ const pathExists = await exists(challengeDir);
71
+ if (pathExists) {
72
+ i++;
73
+ continue;
74
+ }
75
+ await dntShim.Deno.mkdir(challengeDir, { recursive: true });
76
+ return challengeDir;
97
77
  }
98
- return {
99
- scheme: urlObj.protocol.slice(0, -1),
100
- host: urlObj.host + (urlObj.port ? ":" + urlObj.port : ""),
101
- orgName: caps[1],
102
- examToken: caps[2],
103
- challengeId: parseInt(caps[3]),
104
- };
105
- }
106
- function getResultFromExamSession(examSession, challengeId) {
107
- return examSession.results.find((r) => r.challengeId === challengeId);
108
78
  }
@@ -1,19 +1,15 @@
1
1
  import * as dntShim from "../../_dnt.shims.js";
2
- import { save as saveConfig } from "../shared/config.js";
3
- import { CantChooseLanguage, InvalidChallengeId } from "../shared/errors.js";
4
- import { archiveExistingChallengeFiles, chooseProgrammingLanguage, downloadChallengeFilesTo, getChallengeFromExamSession, getCommonChallengeContext, } from "../shared/mod.js";
2
+ import { load as loadConfig, save as saveConfig, } from "../shared/config.js";
3
+ import { CantChooseLanguage } from "../shared/errors.js";
4
+ import { archiveExistingChallengeFiles, chooseProgrammingLanguage, downloadChallengeFilesTo, getCommonChallengeContext, trackClientFromConfig, } from "../shared/mod.js";
5
5
  export async function lang() {
6
- const trackContext = await getCommonChallengeContext();
7
- const { config, api, codingContext, applicantExam, challengeResult, challengePosition, } = trackContext;
8
- const examSession = await api.getExamSession(applicantExam.id);
9
- const challenge = getChallengeFromExamSession(examSession, codingContext.challengeId) ??
10
- (() => {
11
- throw new InvalidChallengeId(codingContext.challengeId);
12
- })();
6
+ const config = await loadConfig();
7
+ const api = trackClientFromConfig(config);
8
+ const [codingContext, challenge] = await getCommonChallengeContext(config, api);
13
9
  if (challenge.programmingLanguages.length === 0) {
14
10
  throw new CantChooseLanguage();
15
11
  }
16
- const availableLanguages = (await api.getProgrammingLanguages())
12
+ const availableLanguages = (await api.languages())
17
13
  .filter((pl) => codingContext.programmingLanguages.find((n) => n === pl.value));
18
14
  console.log("Your existing code will be archived in this directory and the original challenge code for the new language will be downloaded");
19
15
  const shouldContinue = dntShim.confirm("Are you sure you want to continue?");
@@ -25,14 +21,13 @@ export async function lang() {
25
21
  console.log("You are already using this language");
26
22
  return;
27
23
  }
28
- await api.switchChallengeLanguage(config.applicantExamId, config.challengeResultId, desiredLanguage.value);
24
+ await api.updateLanguage(desiredLanguage.value);
29
25
  const currentDir = dntShim.Deno.cwd();
30
- const selectedLanguage = availableLanguages.find((pl) => pl.value === codingContext.selectedLanguage);
31
- await archiveExistingChallengeFiles(config.orgName, challengePosition, { selectedLanguage, challengeDir: currentDir });
32
- const newCodingContext = await api.getChallengeCodingContext(config.applicantExamId, config.challengeResultId);
26
+ await archiveExistingChallengeFiles(config.orgName);
27
+ const newCodingContext = await api.context();
33
28
  const showFileDiff = false;
34
29
  const includeTarball = true;
35
- await downloadChallengeFilesTo(newCodingContext, currentDir, api, applicantExam.id, challengeResult.id, showFileDiff, includeTarball);
30
+ await downloadChallengeFilesTo(newCodingContext, currentDir, api, showFileDiff, includeTarball);
36
31
  const updatedConfig = Object.assign({}, config, {
37
32
  programmingLanguage: desiredLanguage.value,
38
33
  });
@@ -1,16 +1,16 @@
1
1
  import * as dntShim from "../../_dnt.shims.js";
2
- import { save as saveConfig } from "../shared/config.js";
3
- import { archiveExistingChallengeFiles, downloadChallengeFilesTo, getCommonChallengeContext, } from "../shared/mod.js";
2
+ import { load as loadConfig, save as saveConfig, } from "../shared/config.js";
3
+ import { archiveExistingChallengeFiles, downloadChallengeFilesTo, getCommonChallengeContext, trackClientFromConfig, } from "../shared/mod.js";
4
4
  export async function pull() {
5
- const trackContext = await getCommonChallengeContext();
6
- const { api, codingContext, applicantExam, challengeResult, config, challengePosition, } = trackContext;
5
+ const config = await loadConfig();
6
+ const api = trackClientFromConfig(config);
7
+ const [codingContext, _challenge] = await getCommonChallengeContext(config, api);
7
8
  const configLang = config.programmingLanguage;
8
- const currentLang = challengeResult.programmingLanguage;
9
+ const currentLang = codingContext.selectedLanguage;
9
10
  const currentDir = dntShim.Deno.cwd();
10
11
  if (configLang && currentLang && configLang !== currentLang) {
11
12
  console.log("Your programming language has changed elsewhere since you last ran this challenge");
12
- const selectedLanguage = await api.findProgrammingLanguage(configLang);
13
- await archiveExistingChallengeFiles(config.orgName, challengePosition, { selectedLanguage, challengeDir: currentDir });
13
+ await archiveExistingChallengeFiles(config.orgName);
14
14
  const updatedConfig = Object.assign({}, config, {
15
15
  programmingLanguage: currentLang,
16
16
  });
@@ -18,5 +18,5 @@ export async function pull() {
18
18
  }
19
19
  const showFileDiff = true;
20
20
  const includeTarball = false;
21
- await downloadChallengeFilesTo(codingContext, currentDir, api, applicantExam.id, challengeResult.id, showFileDiff, includeTarball);
21
+ await downloadChallengeFilesTo(codingContext, currentDir, api, showFileDiff, includeTarball);
22
22
  }
@@ -1,12 +1,14 @@
1
1
  import * as dntShim from "../../_dnt.shims.js";
2
+ import { load as loadConfig } from "../shared/config.js";
2
3
  import { DeleteFilesNotAllowed, OtherError } from "../shared/errors.js";
3
4
  import { isEmptyDirectory, pathsToFilenameSet, toNativeStyle, } from "../shared/file.js";
4
- import { getCommonChallengeContext, listFileNames, printWorkingFileSet, } from "../shared/mod.js";
5
- import { FileListType } from "../track/types.js";
5
+ import { getCommonChallengeContext, listFileNames, printWorkingFileSet, trackClientFromConfig, } from "../shared/mod.js";
6
+ import { FileListType } from "../shared/types.js";
6
7
  import * as colors from "../../deps/deno.land/std@0.195.0/fmt/colors.js";
7
8
  export async function remove(filePaths, options) {
8
- const trackContext = await getCommonChallengeContext();
9
- const { config, api, codingContext } = trackContext;
9
+ const config = await loadConfig();
10
+ const api = trackClientFromConfig(config);
11
+ const [codingContext, _challenge] = await getCommonChallengeContext(config, api);
10
12
  if (!codingContext.settings.allowNewFile) {
11
13
  throw new DeleteFilesNotAllowed();
12
14
  }
@@ -35,7 +37,7 @@ export async function remove(filePaths, options) {
35
37
  addedFiles: filesToKeep,
36
38
  updatedFiles: {},
37
39
  };
38
- await api.saveFiles(config.applicantExamId, config.challengeResultId, saveFilesRequest);
40
+ await api.saveFiles(saveFilesRequest);
39
41
  await Promise.all(filesToDelete.map((file) => dntShim.Deno.remove(file)))
40
42
  .catch((e) => Promise.reject(new OtherError(`An error occurred while deleting files: ${e}`)));
41
43
  // For each file to delete, walk the file's parents and delete directories
@@ -55,7 +57,7 @@ export async function remove(filePaths, options) {
55
57
  current = parent;
56
58
  }
57
59
  }
58
- const newCodingContext = await api.getChallengeCodingContext(config.applicantExamId, config.challengeResultId);
60
+ const newCodingContext = await api.context();
59
61
  printWorkingFileSet(newCodingContext);
60
62
  }
61
63
  else {
@@ -1,21 +1,20 @@
1
1
  import * as dntShim from "../../_dnt.shims.js";
2
- import { archiveExistingChallengeFiles, downloadChallengeFilesTo, getCommonChallengeContext, } from "../shared/mod.js";
2
+ import { load as loadConfig } from "../shared/config.js";
3
+ import { archiveExistingChallengeFiles, downloadChallengeFilesTo, getCommonChallengeContext, trackClientFromConfig, } from "../shared/mod.js";
3
4
  export async function reset() {
4
- const trackContext = await getCommonChallengeContext();
5
- const { config, api, codingContext, applicantExam, challengeResult, challengePosition, } = trackContext;
5
+ const config = await loadConfig();
6
+ const api = trackClientFromConfig(config);
7
+ const [_codingContext, _challenge] = await getCommonChallengeContext(config, api);
6
8
  console.log("Your existing code will be archived in this directory and the original challenge code will be downloaded");
7
9
  const shouldContinue = dntShim.confirm("Are you sure you want to reset this challenge?");
8
10
  if (!shouldContinue) {
9
11
  return;
10
12
  }
11
13
  const currentDir = dntShim.Deno.cwd();
12
- const selectedLanguage = codingContext.selectedLanguage
13
- ? await api.findProgrammingLanguage(codingContext.selectedLanguage)
14
- : undefined;
15
- await archiveExistingChallengeFiles(config.orgName, challengePosition, { selectedLanguage, challengeDir: currentDir });
16
- await api.reset(config.applicantExamId, config.challengeResultId);
17
- const newCodingContext = await api.getChallengeCodingContext(config.applicantExamId, config.challengeResultId);
14
+ await archiveExistingChallengeFiles(config.orgName);
15
+ await api.reset();
16
+ const newCodingContext = await api.context();
18
17
  const showFileDiff = false;
19
18
  const includeTarball = true;
20
- await downloadChallengeFilesTo(newCodingContext, currentDir, api, applicantExam.id, challengeResult.id, showFileDiff, includeTarball);
19
+ await downloadChallengeFilesTo(newCodingContext, currentDir, api, showFileDiff, includeTarball);
21
20
  }
@@ -1,21 +1,20 @@
1
1
  import * as dntShim from "../../_dnt.shims.js";
2
- import { webUrl } from "../shared/config.js";
2
+ import { load as loadConfig } from "../shared/config.js";
3
3
  import { CantReadFile } from "../shared/errors.js";
4
- import { getCommonChallengeContext, listFileNames, printTimeLeft, } from "../shared/mod.js";
5
- import { EnvSettings, FileListType, } from "../track/types.js";
4
+ import { getCommonChallengeContext, listFileNames, printTimeLeft, trackClientFromConfig, } from "../shared/mod.js";
5
+ import { EnvSettings, FileListType, } from "../shared/types.js";
6
6
  import { OrcaClient } from "../orca/client.js";
7
7
  import { FileType } from "../orca/types.js";
8
8
  import * as colors from "../../deps/deno.land/std@0.195.0/fmt/colors.js";
9
9
  const MAX_MEMORY_FOR_TEST = 512 * 1024 * 1024; // 512 MB
10
10
  export async function run(options) {
11
- const trackContext = await getCommonChallengeContext();
12
- const api = trackContext.api;
13
- const config = trackContext.config;
14
- const codingContext = trackContext.codingContext;
11
+ const config = await loadConfig();
12
+ const api = trackClientFromConfig(config);
13
+ const [codingContext, challenge] = await getCommonChallengeContext(config, api);
15
14
  // const orcaHost = Deno.env.get("ORCA_HOST") || "track-prod-frontend.orca.run";
16
15
  // Temporary code to retrieve the Orca host. Will be able to get from CodingContext in the future.
17
16
  const orcaHost = codingContext.orcaHost || await api.orcaHost();
18
- const token = await api.orcaToken(codingContext.urlForOrcaToken);
17
+ const token = await api.orcaToken(codingContext);
19
18
  const orca = new OrcaClient(orcaHost, token);
20
19
  await orca.connect();
21
20
  const envConfig = codingContext.answers.envConfig || EnvSettings.default;
@@ -47,7 +46,7 @@ export async function run(options) {
47
46
  updatedFiles: updatedFiles,
48
47
  };
49
48
  console.log("Saving challenge files");
50
- await api.saveFiles(config.applicantExamId, config.challengeResultId, saveFilesRequest);
49
+ await api.saveFiles(saveFilesRequest);
51
50
  console.log(colors.green("Challenge files saved"));
52
51
  }
53
52
  console.log();
@@ -97,13 +96,13 @@ export async function run(options) {
97
96
  console.log("Test Results");
98
97
  console.log("-".repeat(50));
99
98
  console.log();
100
- if (trackContext.challenge.openTestcases) {
99
+ if (challenge.openTestcases) {
101
100
  const totalPassed = scoreCounter.totalPassed();
102
- const scoreStr = `${totalPassed}/${trackContext.challenge.openTestcases}`;
101
+ const scoreStr = `${totalPassed}/${challenge.openTestcases}`;
103
102
  console.log(`Score: ${colors.green(scoreStr)}`);
104
- await api.updateEditorScore(config.applicantExamId, config.challengeResultId, totalPassed);
103
+ await api.updateEditorScore(totalPassed);
105
104
  }
106
- const timeLeft = await api.timeLeft(config.applicantExamId, config.challengeResultId);
105
+ const timeLeft = await api.timeLeft();
107
106
  printTimeLeft(timeLeft);
108
107
  }
109
108
  else {
@@ -121,9 +120,7 @@ export async function run(options) {
121
120
  for (const file of updatedDisplayFiles) {
122
121
  console.log(colors.green(`\t${file}`));
123
122
  }
124
- const url = `${webUrl(config)}/challenges/${config.challengeId}`;
125
- console.log();
126
- console.log(`Files uploaded successfully to ${url}`);
123
+ console.log(`Files uploaded successfully.`);
127
124
  }
128
125
  }
129
126
  async function getAllEditableFileContent(context) {
@@ -1,5 +1,8 @@
1
- import { getCommonChallengeContext, printChallengeInfo } from "../shared/mod.js";
1
+ import { load as loadConfig } from "../shared/config.js";
2
+ import { getCommonChallengeContext, printChallengeInfo, trackClientFromConfig, } from "../shared/mod.js";
2
3
  export async function status() {
3
- const trackContext = await getCommonChallengeContext();
4
- await printChallengeInfo(trackContext);
4
+ const config = await loadConfig();
5
+ const api = trackClientFromConfig(config);
6
+ const [codingContext, challenge] = await getCommonChallengeContext(config, api);
7
+ await printChallengeInfo(api, codingContext, challenge);
5
8
  }
package/esm/src/meta.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export declare const VERSION = "3.1.0-rc1";
1
+ export declare const VERSION = "4.0.0-rc0";
2
2
  export declare const DESCRIPTION = "A CLI for interacting with tracks.run and running code tests on track's servers";
3
3
  export declare function checkUpdates(): Promise<boolean>;
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 = "3.1.0-rc1";
4
+ export const VERSION = "4.0.0-rc0";
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) {
@@ -1,18 +1,26 @@
1
1
  import { BasicAuth } from "../track/client.js";
2
- import { ProgrammingLanguage } from "../track/types.js";
2
+ import { ProgrammingLanguage, TrackApp } from "./types.js";
3
3
  export declare const CONFIG_FILE_PATH = ".track/config.json";
4
- export interface TrackConfig {
5
- scheme: string;
6
- host: string;
4
+ export interface TrackConfigCore {
5
+ app: TrackApp;
6
+ baseUrl: string;
7
7
  orgName: string;
8
8
  basicAuth?: BasicAuth;
9
- examToken: string;
10
- applicantExamId: number;
9
+ token: string;
10
+ programmingLanguage?: ProgrammingLanguage;
11
+ cookies: Record<string, string>;
12
+ }
13
+ export interface TrackTestConfigPart {
11
14
  challengeId: number;
15
+ applicantExamId: number;
12
16
  challengeResultId: number;
13
- cookies: Record<string, string>;
14
- programmingLanguage?: ProgrammingLanguage;
15
17
  }
18
+ export interface TrackTrainingConfigPart {
19
+ courseMaterialId: number;
20
+ klassId: number;
21
+ klassResultId: number;
22
+ }
23
+ export type TrackConfig = TrackConfigCore & (TrackTestConfigPart | TrackTrainingConfigPart);
16
24
  export type SaveConfigOptions = {
17
25
  path?: string;
18
26
  };
@@ -21,4 +29,3 @@ export type LoadConfigOptions = {
21
29
  path?: string;
22
30
  };
23
31
  export declare function load(options?: LoadConfigOptions): Promise<TrackConfig>;
24
- export declare function webUrl(config: TrackConfig): string;
@@ -19,6 +19,3 @@ export async function load(options) {
19
19
  const configStr = new TextDecoder().decode(data);
20
20
  return JSON.parse(configStr);
21
21
  }
22
- export function webUrl(config) {
23
- return `${config.scheme}://${config.host}/${config.orgName}/exams/${config.examToken}`;
24
- }
@@ -72,7 +72,7 @@ export declare class NewFilesNotAllowed extends TrackError {
72
72
  export declare class DeleteFilesNotAllowed extends TrackError {
73
73
  constructor();
74
74
  }
75
- export declare class LocalExamNotAllowed extends TrackError {
75
+ export declare class LocalCodingNotAllowed extends TrackError {
76
76
  url: string;
77
77
  constructor(url: string);
78
78
  }
@@ -11,6 +11,7 @@ export class APIError extends TrackError {
11
11
  this.name = "APIError";
12
12
  }
13
13
  static async fromResponse(res) {
14
+ console.log(res.url);
14
15
  const body = await res.text();
15
16
  if (HttpStatus.isClientError(res)) {
16
17
  return new APIError(`A client error occurred (${res.status}): ${body}`);
@@ -202,16 +203,16 @@ export class DeleteFilesNotAllowed extends TrackError {
202
203
  this.name = "DeleteFilesNotAllowed";
203
204
  }
204
205
  }
205
- export class LocalExamNotAllowed extends TrackError {
206
+ export class LocalCodingNotAllowed extends TrackError {
206
207
  constructor(url) {
207
- super(`You are not allowed to take this challenge in your local CLI.\nPlease continue your exam on tracks.run:\n\n${url}`);
208
+ super(`You are not allowed to take this challenge in your local CLI.\nPlease continue your challenge on web:\n\n${url}`);
208
209
  Object.defineProperty(this, "url", {
209
210
  enumerable: true,
210
211
  configurable: true,
211
212
  writable: true,
212
213
  value: url
213
214
  });
214
- this.name = "LocalExamNotAllowed";
215
+ this.name = "LocalCodingNotAllowed";
215
216
  }
216
217
  }
217
218
  export class OtherError extends TrackError {
@@ -1,8 +1,8 @@
1
+ /// <reference types="node" />
1
2
  import * as dntShim from "../../_dnt.shims.js";
2
- import { TrackCLIContext } from "./types.js";
3
+ import { TrackConfig } from "./config.js";
4
+ import { ChallengeResult, ChallengeSession, ChallengeSettings, CodingAnswers, CodingContext, FileListType, ProgrammingLanguageInfo } from "./types.js";
3
5
  import { TrackClient } from "../track/client.js";
4
- import { ApplicantExamForNoAuth, ChallengeResult, ChallengeSettings, CodingContext, ExamSession, ExamSessionChallenge, FileListType, ProgrammingLanguageInfo } from "../track/types.js";
5
- import { CodingAnswers } from "../track/types.js";
6
6
  export declare const _internals: {
7
7
  prompt: typeof dntShim.prompt;
8
8
  scoreRatio: typeof scoreRatio;
@@ -11,6 +11,7 @@ export declare const HttpStatus: {
11
11
  readonly OK: 200;
12
12
  readonly Unauthorized: 401;
13
13
  readonly isSuccess: (r: number | dntShim.Response) => boolean;
14
+ readonly isRedirect: (r: number | dntShim.Response) => boolean;
14
15
  readonly isClientError: (r: number | dntShim.Response) => boolean;
15
16
  readonly isServerError: (r: number | dntShim.Response) => boolean;
16
17
  };
@@ -18,9 +19,7 @@ export declare const Prompt: {
18
19
  select(message: string, items: string[]): number | undefined;
19
20
  };
20
21
  export declare function printTimeLeft(timeLeftSeconds: number): void;
21
- export declare function ensureExamInProgress(applicantExam: ApplicantExamForNoAuth, webUrlRedirect: string): void;
22
- export declare function getChallengeFromExamSession(examSession: ExamSession, challengeId: number): ExamSessionChallenge | undefined;
23
- export declare function tryStartingChallenge(challenge: ExamSessionChallenge, api: TrackClient, applicantExam: ApplicantExamForNoAuth, resultOpt?: ChallengeResult): Promise<ChallengeResult | undefined>;
22
+ export declare function tryStartingChallenge(api: TrackClient, challenge: ChallengeSession): Promise<ChallengeResult | undefined>;
24
23
  export declare function chooseProgrammingLanguage(languages: ProgrammingLanguageInfo[]): ProgrammingLanguageInfo;
25
24
  export declare function listFileNames(context: {
26
25
  settings: ChallengeSettings;
@@ -30,14 +29,15 @@ export declare const SnakeCase: {
30
29
  to<T>(obj: T): T;
31
30
  from<T_1>(obj: T_1): T_1;
32
31
  };
33
- export declare function getCommonChallengeContext(): Promise<TrackCLIContext>;
32
+ export declare function getCommonChallengeContext(config: TrackConfig, api: TrackClient): Promise<[CodingContext, ChallengeSession]>;
34
33
  declare function scoreRatio(editorScore: number | undefined, openTestcases: number | undefined): string;
35
- export declare function printChallengeInfo(trackContext: TrackCLIContext): Promise<void>;
34
+ export declare function printChallengeInfo(api: TrackClient, codingContext: CodingContext, challenge: ChallengeSession): Promise<void>;
36
35
  export declare function printWorkingFileSet(codingContext: CodingContext): void;
37
- export declare function downloadChallengeFilesTo(codingContext: CodingContext, dest: string, api: TrackClient, applicantExamId: number, challengeResultId: number, showFileDiff: boolean, includeTarball: boolean): Promise<void>;
36
+ export declare function downloadChallengeFilesTo(codingContext: CodingContext, dest: string, api: TrackClient, showFileDiff: boolean, includeTarball: boolean): Promise<void>;
38
37
  export type ArchiveExistingChallengeFilesOptions = {
39
38
  challengeDir?: string;
40
39
  selectedLanguage?: ProgrammingLanguageInfo;
41
40
  };
42
- export declare function archiveExistingChallengeFiles(orgName: string, challengePosition: number, options?: ArchiveExistingChallengeFilesOptions): Promise<void>;
41
+ export declare function archiveExistingChallengeFiles(orgName: string): Promise<void>;
42
+ export declare function trackClientFromConfig(config: TrackConfig): TrackClient;
43
43
  export {};