track-cli 3.0.0-rc
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/LICENSE +7 -0
- package/README.md +3 -0
- package/esm/_dnt.polyfills.d.ts +6 -0
- package/esm/_dnt.polyfills.js +1 -0
- package/esm/_dnt.shims.d.ts +19 -0
- package/esm/_dnt.shims.js +78 -0
- package/esm/deps/deno.land/std@0.170.0/fmt/colors.d.ts +270 -0
- package/esm/deps/deno.land/std@0.170.0/fmt/colors.js +473 -0
- package/esm/deps/deno.land/std@0.195.0/_util/os.d.ts +4 -0
- package/esm/deps/deno.land/std@0.195.0/_util/os.js +18 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert.d.ts +2 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assert.js +8 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assertion_error.d.ts +4 -0
- package/esm/deps/deno.land/std@0.195.0/assert/assertion_error.js +12 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/_common.d.ts +50 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/_common.js +545 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/constants.d.ts +55 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/constants.js +57 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/day_of_year.d.ts +26 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/day_of_year.js +45 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/difference.d.ts +38 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/difference.js +93 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/format.d.ts +22 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/format.js +28 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/is_leap.d.ts +49 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/is_leap.js +60 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/mod.d.ts +43 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/mod.js +45 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/parse.d.ts +21 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/parse.js +29 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/to_imf.d.ts +17 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/to_imf.js +44 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/week_of_year.d.ts +13 -0
- package/esm/deps/deno.land/std@0.195.0/datetime/week_of_year.js +38 -0
- package/esm/deps/deno.land/std@0.195.0/fmt/colors.d.ts +270 -0
- package/esm/deps/deno.land/std@0.195.0/fmt/colors.js +474 -0
- package/esm/deps/deno.land/std@0.195.0/fmt/duration.d.ts +15 -0
- package/esm/deps/deno.land/std@0.195.0/fmt/duration.js +96 -0
- package/esm/deps/deno.land/std@0.195.0/fs/_util.d.ts +35 -0
- package/esm/deps/deno.land/std@0.195.0/fs/_util.js +80 -0
- package/esm/deps/deno.land/std@0.195.0/fs/copy.d.ts +51 -0
- package/esm/deps/deno.land/std@0.195.0/fs/copy.js +244 -0
- package/esm/deps/deno.land/std@0.195.0/fs/empty_dir.d.ts +31 -0
- package/esm/deps/deno.land/std@0.195.0/fs/empty_dir.js +74 -0
- package/esm/deps/deno.land/std@0.195.0/fs/ensure_dir.d.ts +27 -0
- package/esm/deps/deno.land/std@0.195.0/fs/ensure_dir.js +55 -0
- package/esm/deps/deno.land/std@0.195.0/fs/ensure_file.d.ts +33 -0
- package/esm/deps/deno.land/std@0.195.0/fs/ensure_file.js +75 -0
- package/esm/deps/deno.land/std@0.195.0/fs/ensure_link.d.ts +31 -0
- package/esm/deps/deno.land/std@0.195.0/fs/ensure_link.js +43 -0
- package/esm/deps/deno.land/std@0.195.0/fs/ensure_symlink.d.ts +17 -0
- package/esm/deps/deno.land/std@0.195.0/fs/ensure_symlink.js +68 -0
- package/esm/deps/deno.land/std@0.195.0/fs/eol.d.ts +40 -0
- package/esm/deps/deno.land/std@0.195.0/fs/eol.js +53 -0
- package/esm/deps/deno.land/std@0.195.0/fs/exists.d.ts +102 -0
- package/esm/deps/deno.land/std@0.195.0/fs/exists.js +163 -0
- package/esm/deps/deno.land/std@0.195.0/fs/expand_glob.d.ts +37 -0
- package/esm/deps/deno.land/std@0.195.0/fs/expand_glob.js +216 -0
- package/esm/deps/deno.land/std@0.195.0/fs/mod.d.ts +16 -0
- package/esm/deps/deno.land/std@0.195.0/fs/mod.js +17 -0
- package/esm/deps/deno.land/std@0.195.0/fs/move.d.ts +29 -0
- package/esm/deps/deno.land/std@0.195.0/fs/move.js +88 -0
- package/esm/deps/deno.land/std@0.195.0/fs/walk.d.ts +40 -0
- package/esm/deps/deno.land/std@0.195.0/fs/walk.js +158 -0
- package/esm/deps/deno.land/std@0.195.0/path/_constants.d.ts +39 -0
- package/esm/deps/deno.land/std@0.195.0/path/_constants.js +46 -0
- package/esm/deps/deno.land/std@0.195.0/path/_interface.d.ts +26 -0
- package/esm/deps/deno.land/std@0.195.0/path/_interface.js +3 -0
- package/esm/deps/deno.land/std@0.195.0/path/_util.d.ts +11 -0
- package/esm/deps/deno.land/std@0.195.0/path/_util.js +161 -0
- package/esm/deps/deno.land/std@0.195.0/path/common.d.ts +13 -0
- package/esm/deps/deno.land/std@0.195.0/path/common.js +36 -0
- package/esm/deps/deno.land/std@0.195.0/path/glob.d.ts +83 -0
- package/esm/deps/deno.land/std@0.195.0/path/glob.js +361 -0
- package/esm/deps/deno.land/std@0.195.0/path/mod.d.ts +9 -0
- package/esm/deps/deno.land/std@0.195.0/path/mod.js +33 -0
- package/esm/deps/deno.land/std@0.195.0/path/posix.d.ts +86 -0
- package/esm/deps/deno.land/std@0.195.0/path/posix.js +442 -0
- package/esm/deps/deno.land/std@0.195.0/path/separator.d.ts +2 -0
- package/esm/deps/deno.land/std@0.195.0/path/separator.js +5 -0
- package/esm/deps/deno.land/std@0.195.0/path/win32.d.ts +91 -0
- package/esm/deps/deno.land/std@0.195.0/path/win32.js +909 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/_utils/distance.d.ts +1 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/_utils/distance.js +26 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/_errors.d.ts +76 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/_errors.js +154 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/_utils.d.ts +32 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/_utils.js +105 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/command.d.ts +831 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/command.js +1701 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/_bash_completions_generator.d.ts +17 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/_bash_completions_generator.js +201 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/_fish_completions_generator.d.ts +14 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/_fish_completions_generator.js +141 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/_zsh_completions_generator.d.ts +21 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/_zsh_completions_generator.js +285 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/bash.d.ts +6 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/bash.js +38 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/complete.d.ts +8 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/complete.js +32 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/fish.d.ts +6 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/fish.js +38 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/mod.d.ts +6 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/mod.js +46 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/zsh.d.ts +6 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/completions/zsh.js +38 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/deprecated.d.ts +45 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/deprecated.js +2 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/deps.d.ts +1 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/deps.js +1 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/help/_help_generator.d.ts +27 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/help/_help_generator.js +323 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/help/mod.d.ts +6 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/help/mod.js +34 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/mod.d.ts +18 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/mod.js +17 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/type.d.ts +41 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/type.js +24 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/action_list.d.ts +9 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/action_list.js +20 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/boolean.d.ts +9 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/boolean.js +13 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/child_command.d.ts +9 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/child_command.js +27 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/command.d.ts +7 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/command.js +9 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/enum.d.ts +10 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/enum.js +29 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/file.d.ts +5 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/file.js +12 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/integer.d.ts +7 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/integer.js +9 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/number.d.ts +7 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/number.js +9 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/string.d.ts +7 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types/string.js +9 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types.d.ts +118 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/types.js +2 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/mod.d.ts +5 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/mod.js +5 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/provider/deno_land.d.ts +14 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/provider/deno_land.js +46 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/provider/github.d.ts +26 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/provider/github.js +118 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/provider/nest_land.d.ts +14 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/provider/nest_land.js +55 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/provider.d.ts +27 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/provider.js +102 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/upgrade_command.d.ts +17 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/command/upgrade/upgrade_command.js +75 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/_errors.d.ts +60 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/_errors.js +117 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/_utils.d.ts +18 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/_utils.js +106 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/_validate_flags.d.ts +10 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/_validate_flags.js +157 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/deprecated.d.ts +24 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/deprecated.js +9 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/flags.d.ts +33 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/flags.js +402 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/types/boolean.d.ts +3 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/types/boolean.js +11 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/types/integer.d.ts +3 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/types/integer.js +9 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/types/number.d.ts +3 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/types/number.js +9 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/types/string.d.ts +3 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/types/string.js +4 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/types.d.ts +59 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/flags/types.js +1 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/border.d.ts +20 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/border.js +18 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/cell.d.ts +78 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/cell.js +128 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/deps.d.ts +1 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/deps.js +1 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/layout.d.ts +99 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/layout.js +529 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/row.d.ts +49 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/row.js +74 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/table.d.ts +135 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/table.js +237 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/utils.d.ts +13 -0
- package/esm/deps/deno.land/x/cliffy@v0.25.7/table/utils.js +60 -0
- package/esm/package.json +3 -0
- package/esm/src/action/add.d.ts +1 -0
- package/esm/src/action/add.js +57 -0
- package/esm/src/action/clone.d.ts +6 -0
- package/esm/src/action/clone.js +108 -0
- package/esm/src/action/lang.d.ts +1 -0
- package/esm/src/action/lang.js +40 -0
- package/esm/src/action/pull.d.ts +1 -0
- package/esm/src/action/pull.js +22 -0
- package/esm/src/action/remove.d.ts +4 -0
- package/esm/src/action/remove.js +67 -0
- package/esm/src/action/reset.d.ts +1 -0
- package/esm/src/action/reset.js +21 -0
- package/esm/src/action/run.d.ts +4 -0
- package/esm/src/action/run.js +163 -0
- package/esm/src/action/status.d.ts +1 -0
- package/esm/src/action/status.js +5 -0
- package/esm/src/main.d.ts +2 -0
- package/esm/src/main.js +96 -0
- package/esm/src/meta.d.ts +3 -0
- package/esm/src/meta.js +51 -0
- package/esm/src/orca/client.d.ts +20 -0
- package/esm/src/orca/client.js +152 -0
- package/esm/src/orca/types.d.ts +54 -0
- package/esm/src/orca/types.js +12 -0
- package/esm/src/shared/config.d.ts +24 -0
- package/esm/src/shared/config.js +24 -0
- package/esm/src/shared/errors.d.ts +82 -0
- package/esm/src/shared/errors.js +228 -0
- package/esm/src/shared/file.d.ts +30 -0
- package/esm/src/shared/file.js +110 -0
- package/esm/src/shared/mod.d.ts +40 -0
- package/esm/src/shared/mod.js +383 -0
- package/esm/src/shared/types.d.ts +12 -0
- package/esm/src/shared/types.js +1 -0
- package/esm/src/track/client.d.ts +60 -0
- package/esm/src/track/client.js +268 -0
- package/esm/src/track/types.d.ts +210 -0
- package/esm/src/track/types.js +40 -0
- package/package.json +35 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { load as loadConfig, save as saveConfig, webUrl, } from "./config.js";
|
|
3
|
+
import { BadTarballURL, ChallengeAlreadyFinished, ExamCanceled, ExamExpired, ExamSubmitted, ExamUnread, OtherError, } from "./errors.js";
|
|
4
|
+
import { getUniqueDirName, toNativeStyle, untgz } from "./file.js";
|
|
5
|
+
import { TrackClient } from "../track/client.js";
|
|
6
|
+
import { ApplicantExamStatus, ChallengeResultStatus, ChallengeStyle, FileListType, } from "../track/types.js";
|
|
7
|
+
import * as datetime from "../../deps/deno.land/std@0.195.0/datetime/mod.js";
|
|
8
|
+
import * as colors from "../../deps/deno.land/std@0.195.0/fmt/colors.js";
|
|
9
|
+
import { format as duration } from "../../deps/deno.land/std@0.195.0/fmt/duration.js";
|
|
10
|
+
import * as fs from "../../deps/deno.land/std@0.195.0/fs/mod.js";
|
|
11
|
+
export const _internals = {
|
|
12
|
+
prompt: dntShim.prompt,
|
|
13
|
+
};
|
|
14
|
+
export const HttpStatus = {
|
|
15
|
+
OK: 200,
|
|
16
|
+
Unauthorized: 401,
|
|
17
|
+
isSuccess(r) {
|
|
18
|
+
const status = typeof r === "number" ? r : r.status;
|
|
19
|
+
return 300 > status && status >= 200;
|
|
20
|
+
},
|
|
21
|
+
isClientError(r) {
|
|
22
|
+
const status = typeof r === "number" ? r : r.status;
|
|
23
|
+
return 500 > status && status >= 400;
|
|
24
|
+
},
|
|
25
|
+
isServerError(r) {
|
|
26
|
+
const status = typeof r === "number" ? r : r.status;
|
|
27
|
+
return 600 > status && status >= 500;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export const Prompt = {
|
|
31
|
+
select(message, items) {
|
|
32
|
+
const maxDigits = items.length.toString().length;
|
|
33
|
+
// Writes lines like this, with padding:
|
|
34
|
+
// [ 1] C++
|
|
35
|
+
// [..] ...
|
|
36
|
+
// [45] Golang
|
|
37
|
+
const list = items
|
|
38
|
+
.map((item, i) => `[${((i + 1) + "").padStart(maxDigits, " ")}] ${item}`)
|
|
39
|
+
.join("\n");
|
|
40
|
+
const answerStr = _internals.prompt(`${message}\n${list}\n> `);
|
|
41
|
+
if (!answerStr) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const answer = parseInt(answerStr);
|
|
45
|
+
if (answer > 0 && answer <= items.length) {
|
|
46
|
+
return answer - 1;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
export function printTimeLeft(timeLeftSeconds) {
|
|
54
|
+
const durationMessage = duration(timeLeftSeconds * 1000, { ignoreZero: true }) || "0s";
|
|
55
|
+
if (timeLeftSeconds > 300) {
|
|
56
|
+
console.log(`Time left: ${colors.yellow(durationMessage)}`);
|
|
57
|
+
}
|
|
58
|
+
else if (timeLeftSeconds >= 0) {
|
|
59
|
+
const isWindows = dntShim.Deno.build.os === "windows";
|
|
60
|
+
const warningSymbol = isWindows ? "!" : "⚠️";
|
|
61
|
+
console.log(`Time left: ${warningSymbol} ${colors.red(durationMessage)} ${warningSymbol}`);
|
|
62
|
+
}
|
|
63
|
+
else if (timeLeftSeconds === -1) {
|
|
64
|
+
console.log("The exam deadline has not yet been started");
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.log("The exam deadline has passed");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function ensureExamInProgress(applicantExam, webUrlRedirect) {
|
|
71
|
+
switch (applicantExam.status) {
|
|
72
|
+
case ApplicantExamStatus.Submitted:
|
|
73
|
+
case ApplicantExamStatus.Reviewed:
|
|
74
|
+
throw new ExamSubmitted();
|
|
75
|
+
case ApplicantExamStatus.Expired:
|
|
76
|
+
throw new ExamExpired();
|
|
77
|
+
case ApplicantExamStatus.Canceled:
|
|
78
|
+
throw new ExamCanceled();
|
|
79
|
+
case ApplicantExamStatus.Unread:
|
|
80
|
+
throw new ExamUnread(webUrlRedirect);
|
|
81
|
+
case ApplicantExamStatus.InProgress:
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function getChallengeFromExamSession(examSession, challengeId) {
|
|
86
|
+
return examSession.challengesSets
|
|
87
|
+
.flatMap((s) => s.challenges)
|
|
88
|
+
.find((c) => c.id === challengeId &&
|
|
89
|
+
(c.style === ChallengeStyle.Development ||
|
|
90
|
+
c.style === ChallengeStyle.Algorithm));
|
|
91
|
+
}
|
|
92
|
+
export async function tryStartingChallenge(challenge, api, applicantExam, resultOpt) {
|
|
93
|
+
let result = resultOpt;
|
|
94
|
+
while (true) {
|
|
95
|
+
if (result) {
|
|
96
|
+
switch (result.status) {
|
|
97
|
+
case ChallengeResultStatus.Prepared:
|
|
98
|
+
case ChallengeResultStatus.CanRestart: {
|
|
99
|
+
const timeMsg = typeof challenge.timeLimitMinutes === "number"
|
|
100
|
+
? `You have ${challenge.timeLimitMinutes} minutes to complete this challenge.`
|
|
101
|
+
: `This challenge has no time limit.`;
|
|
102
|
+
const wariningMsg = challenge.programmingLanguages.length === 0
|
|
103
|
+
? `The challenge will start after this point.\n${timeMsg}`
|
|
104
|
+
: `The challenge will start after you select a programming language in the next step.\n${timeMsg}`;
|
|
105
|
+
console.log(wariningMsg);
|
|
106
|
+
const shouldContinue = dntShim.confirm("Do you want to continue? (y/n): ");
|
|
107
|
+
if (!shouldContinue) {
|
|
108
|
+
throw new OtherError("Aborted. Challenge was not started.");
|
|
109
|
+
}
|
|
110
|
+
if (challenge.programmingLanguages.length > 0) {
|
|
111
|
+
const programmingLanguages = (await api.getProgrammingLanguages())
|
|
112
|
+
.filter((pl) => challenge.programmingLanguages.includes(pl.value));
|
|
113
|
+
const desiredLanguage = chooseProgrammingLanguage(programmingLanguages);
|
|
114
|
+
if (desiredLanguage.value !== result.programmingLanguage) {
|
|
115
|
+
await api.switchChallengeLanguage(applicantExam.id, result.id, desiredLanguage.value);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
result = await api.startChallenge(applicantExam.id, result.id);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case ChallengeResultStatus.Started:
|
|
122
|
+
console.log("Challenge is started.");
|
|
123
|
+
return result;
|
|
124
|
+
case ChallengeResultStatus.InProgress:
|
|
125
|
+
console.log("Challenge is in progress!");
|
|
126
|
+
return result;
|
|
127
|
+
case ChallengeResultStatus.Finished:
|
|
128
|
+
case ChallengeResultStatus.NotModified:
|
|
129
|
+
case ChallengeResultStatus.ScoringWaiting:
|
|
130
|
+
throw new ChallengeAlreadyFinished();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Challenge hasn't been prepared yet
|
|
135
|
+
result = await api.prepareChallenge(applicantExam.id, challenge.id);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export function chooseProgrammingLanguage(languages) {
|
|
140
|
+
while (true) {
|
|
141
|
+
const desiredLanguageIndex = Prompt.select("Please select a programming language:", languages.map((l) => l.displayString));
|
|
142
|
+
if (desiredLanguageIndex !== undefined) {
|
|
143
|
+
return languages[desiredLanguageIndex];
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
console.log("An invalid programming language was specified");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function filePaths(settings, fileListType) {
|
|
151
|
+
const files = [];
|
|
152
|
+
function addFiles(path, fileTree) {
|
|
153
|
+
for (const [name, entry] of Object.entries(fileTree)) {
|
|
154
|
+
const relativePath = path.length === 0 ? name : `${path}/${name}`;
|
|
155
|
+
if (typeof entry === "boolean") { // file
|
|
156
|
+
const editable = entry;
|
|
157
|
+
switch (fileListType) {
|
|
158
|
+
case FileListType.All:
|
|
159
|
+
files.push(relativePath);
|
|
160
|
+
break;
|
|
161
|
+
case FileListType.ReadOnlyChallengeFiles:
|
|
162
|
+
if (!editable) {
|
|
163
|
+
files.push(relativePath);
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
case FileListType.EditableChallengeFiles:
|
|
167
|
+
case FileListType.Editable:
|
|
168
|
+
if (editable) {
|
|
169
|
+
files.push(relativePath);
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
case FileListType.UserAddedFiles:
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else { // directory
|
|
177
|
+
addFiles(relativePath, entry);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
addFiles("", settings.files);
|
|
182
|
+
return files;
|
|
183
|
+
}
|
|
184
|
+
export function listFileNames(context, fileListType) {
|
|
185
|
+
const allFiles = new Set(filePaths(context.settings, fileListType));
|
|
186
|
+
const addedFiles = context.answers.addedFiles;
|
|
187
|
+
switch (fileListType) {
|
|
188
|
+
case FileListType.All:
|
|
189
|
+
case FileListType.Editable:
|
|
190
|
+
case FileListType.UserAddedFiles:
|
|
191
|
+
addedFiles?.forEach((f) => allFiles.add(f));
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
return Array.from(allFiles);
|
|
195
|
+
}
|
|
196
|
+
export const SnakeCase = {
|
|
197
|
+
to(obj) {
|
|
198
|
+
const type = typeof obj;
|
|
199
|
+
switch (type) {
|
|
200
|
+
case "string":
|
|
201
|
+
return obj.replace(/([A-Z])/g, "_$1").toLowerCase();
|
|
202
|
+
case "object": {
|
|
203
|
+
if (obj instanceof Array) {
|
|
204
|
+
return obj;
|
|
205
|
+
}
|
|
206
|
+
else if (obj === null) {
|
|
207
|
+
return obj;
|
|
208
|
+
}
|
|
209
|
+
const newObj = {};
|
|
210
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
211
|
+
newObj[this.to(key)] = typeof value === "string"
|
|
212
|
+
? value
|
|
213
|
+
: this.to(value);
|
|
214
|
+
}
|
|
215
|
+
return newObj;
|
|
216
|
+
}
|
|
217
|
+
default:
|
|
218
|
+
return obj;
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
from(obj) {
|
|
222
|
+
const type = typeof obj;
|
|
223
|
+
switch (type) {
|
|
224
|
+
case "string":
|
|
225
|
+
return obj.replace(/(_\w)/g, (m) => m[1].toUpperCase());
|
|
226
|
+
case "object": {
|
|
227
|
+
if (obj instanceof Array) {
|
|
228
|
+
return obj;
|
|
229
|
+
}
|
|
230
|
+
else if (obj === null) {
|
|
231
|
+
return obj;
|
|
232
|
+
}
|
|
233
|
+
const newObj = {};
|
|
234
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
235
|
+
newObj[this.from(key)] = typeof value === "string"
|
|
236
|
+
? value
|
|
237
|
+
: this.from(value);
|
|
238
|
+
}
|
|
239
|
+
return newObj;
|
|
240
|
+
}
|
|
241
|
+
default:
|
|
242
|
+
return obj;
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
export async function getCommonChallengeContext() {
|
|
247
|
+
const config = await loadConfig();
|
|
248
|
+
const api = TrackClient.fromTrackConfig(config);
|
|
249
|
+
const applicantExam = await api.getApplicantExamNoAuth(config.examToken);
|
|
250
|
+
ensureExamInProgress(applicantExam, webUrl(config));
|
|
251
|
+
const codingContext = await api.getChallengeCodingContext(config.applicantExamId, config.challengeResultId);
|
|
252
|
+
const examSession = await api.getExamSession(config.applicantExamId);
|
|
253
|
+
let result = getResultFromExamSession(examSession, config.challengeId);
|
|
254
|
+
if (!result) {
|
|
255
|
+
throw new OtherError(`Cchallenge result for challenge id ${config.challengeId} is missing - has the challenge been started yet?`);
|
|
256
|
+
}
|
|
257
|
+
const challenge = getChallengeFromExamSession(examSession, config.challengeId);
|
|
258
|
+
if (!challenge) {
|
|
259
|
+
throw new OtherError(`Challenge for challenge id ${config.challengeId} is missing - has the challenge been started yet?`);
|
|
260
|
+
}
|
|
261
|
+
switch (result.status) {
|
|
262
|
+
case ChallengeResultStatus.Finished:
|
|
263
|
+
case ChallengeResultStatus.NotModified:
|
|
264
|
+
throw new ChallengeAlreadyFinished();
|
|
265
|
+
case ChallengeResultStatus.CanRestart:
|
|
266
|
+
console.log("This challenge can be restarted");
|
|
267
|
+
console.log();
|
|
268
|
+
result = await tryStartingChallenge(challenge, api, applicantExam, result);
|
|
269
|
+
if (!result) {
|
|
270
|
+
throw new OtherError("Expected to have a challenge result after preparing and starting");
|
|
271
|
+
}
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
const challengePosition = examSession
|
|
275
|
+
.challengesSets
|
|
276
|
+
.flatMap((s) => s.challenges)
|
|
277
|
+
.findIndex((c) => c.id === config.challengeId) +
|
|
278
|
+
1;
|
|
279
|
+
if (api.authCookieExpired()) {
|
|
280
|
+
config.cookies = api.getCookies();
|
|
281
|
+
await saveConfig(config);
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
config,
|
|
285
|
+
api,
|
|
286
|
+
codingContext,
|
|
287
|
+
applicantExam,
|
|
288
|
+
challengeResult: result,
|
|
289
|
+
challenge,
|
|
290
|
+
challengePosition,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function getResultFromExamSession(examSession, challengeId) {
|
|
294
|
+
return examSession
|
|
295
|
+
.results
|
|
296
|
+
.find((cr) => cr.challengeId === challengeId);
|
|
297
|
+
}
|
|
298
|
+
export async function printChallengeInfo(trackContext) {
|
|
299
|
+
const { config, api, challengeResult: result } = trackContext;
|
|
300
|
+
const timeLeft = await api.timeLeft(config.applicantExamId, config.challengeResultId);
|
|
301
|
+
printWorkingFileSet(trackContext.codingContext);
|
|
302
|
+
console.log();
|
|
303
|
+
const lastScoredAt = result.lastScoredAt
|
|
304
|
+
? ` (Ran at ${datetime.format(new Date(result.lastScoredAt), "yyyy-MM-dd HH:mm:ss")})`
|
|
305
|
+
: "";
|
|
306
|
+
const scoreRatio = (result.editorScore && trackContext.challenge.openTestcases)
|
|
307
|
+
? `\t${result.editorScore}/${trackContext.challenge.openTestcases}`
|
|
308
|
+
: "\tN/A - Use 'track run' to save and run your submission";
|
|
309
|
+
console.log(`Latest score${lastScoredAt}`);
|
|
310
|
+
console.log(colors.green(scoreRatio));
|
|
311
|
+
console.log();
|
|
312
|
+
printTimeLeft(timeLeft);
|
|
313
|
+
}
|
|
314
|
+
export function printWorkingFileSet(codingContext) {
|
|
315
|
+
console.log("Extra files included in your submission:");
|
|
316
|
+
const userAddedFiles = listFileNames(codingContext, FileListType.UserAddedFiles);
|
|
317
|
+
if (userAddedFiles.length > 0) {
|
|
318
|
+
console.log(" (use 'track rm <file>' to remove files from your submission)");
|
|
319
|
+
console.log();
|
|
320
|
+
userAddedFiles.sort();
|
|
321
|
+
userAddedFiles.forEach((f) => console.log(`\t${colors.green(toNativeStyle(f))}`));
|
|
322
|
+
}
|
|
323
|
+
else if (codingContext.settings.allowNewFile) {
|
|
324
|
+
console.log();
|
|
325
|
+
console.log("\tNo files currently added, use 'track add <file>' to add your own files to your submission");
|
|
326
|
+
console.log();
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
console.log();
|
|
330
|
+
console.log("\tThis challenge does not support adding new files");
|
|
331
|
+
console.log();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
export async function downloadChallengeFilesTo(codingContext, dest, api, applicantExamId, challengeResultId, showFileDiff, includeTarball) {
|
|
335
|
+
if (includeTarball) {
|
|
336
|
+
console.log("Fetching challenge tarball...");
|
|
337
|
+
const tarballResp = await dntShim.fetch(codingContext.tarballUrl);
|
|
338
|
+
if (!HttpStatus.isSuccess(tarballResp.status)) {
|
|
339
|
+
const statusReason = `${tarballResp.status} - ${tarballResp.statusText}`;
|
|
340
|
+
throw new BadTarballURL(codingContext.tarballUrl, statusReason);
|
|
341
|
+
}
|
|
342
|
+
untgz(new Uint8Array(await (await tarballResp.blob()).arrayBuffer()), dest);
|
|
343
|
+
}
|
|
344
|
+
console.log("Downloading editable files");
|
|
345
|
+
const filesToDownload = listFileNames(codingContext, FileListType.Editable);
|
|
346
|
+
const presignedUrls = Object.entries(await api.getPresignedUrls(applicantExamId, challengeResultId, filesToDownload))
|
|
347
|
+
.map(([file, url]) => ({ file, url }));
|
|
348
|
+
presignedUrls.sort((a, b) => a.file.localeCompare(b.file));
|
|
349
|
+
const promises = presignedUrls.map(async ({ file, url }) => {
|
|
350
|
+
const fileResp = await dntShim.fetch(url);
|
|
351
|
+
const fileBody = new Uint8Array(await fileResp.arrayBuffer());
|
|
352
|
+
const filePath = `${dest}/${file}`;
|
|
353
|
+
if (!showFileDiff || !await fs.exists(file)) {
|
|
354
|
+
await fs.ensureFile(filePath);
|
|
355
|
+
await dntShim.Deno.writeFile(filePath, fileBody);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
// TODO
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
await Promise.all(promises);
|
|
362
|
+
}
|
|
363
|
+
export /**
|
|
364
|
+
* Archives all files in a challenge directory into a subdirectory of
|
|
365
|
+
* the challenge directory. Ignores .track/ and other existing archival directories
|
|
366
|
+
*/ async function archiveExistingChallengeFiles(orgName, challengePosition, options) {
|
|
367
|
+
const challengeDir = options?.challengeDir ?? dntShim.Deno.cwd();
|
|
368
|
+
const langString = options?.selectedLanguage?.displayString ?? "";
|
|
369
|
+
const archivePath = await getUniqueDirName(`${orgName}_challenge_${challengePosition}_old_${langString}`, { root: challengeDir });
|
|
370
|
+
console.log(`Your old challenge contents will be archived in ${archivePath}`);
|
|
371
|
+
await fs.ensureDir(archivePath);
|
|
372
|
+
// Match directories with names like 'ORGNAME_challenge_12_Go_1'
|
|
373
|
+
const oldArchivePattern = /.*_challenge_\d+_.+_\d+/;
|
|
374
|
+
// Move all files and directories in the challenge dir into the archive path
|
|
375
|
+
// Do not copy the track config folder, the archive path, or old archive paths
|
|
376
|
+
for await (const dirEntry of dntShim.Deno.readDir(challengeDir)) {
|
|
377
|
+
if (dirEntry.name === archivePath || dirEntry.name === ".track" ||
|
|
378
|
+
(dirEntry.isDirectory && oldArchivePattern.test(dirEntry.name))) {
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
await fs.move(`${challengeDir}/${dirEntry.name}`, `${archivePath}/${dirEntry.name}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TrackConfig } from "./config.js";
|
|
2
|
+
import { TrackClient } from "../track/client.js";
|
|
3
|
+
import { ApplicantExamForNoAuth, ChallengeResult, CodingContext, ExamSessionChallenge } from "../track/types.js";
|
|
4
|
+
export interface TrackCLIContext {
|
|
5
|
+
config: TrackConfig;
|
|
6
|
+
api: TrackClient;
|
|
7
|
+
codingContext: CodingContext;
|
|
8
|
+
applicantExam: ApplicantExamForNoAuth;
|
|
9
|
+
challengeResult: ChallengeResult;
|
|
10
|
+
challenge: ExamSessionChallenge;
|
|
11
|
+
challengePosition: number;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { TrackConfig } from "../shared/config.js";
|
|
2
|
+
import { ApplicantExamForNoAuth, ChallengeResult, CodingContext, ExamSession, ProgrammingLanguage, ProgrammingLanguageInfo, SaveFilesRequest } from "./types.js";
|
|
3
|
+
export interface BasicAuth {
|
|
4
|
+
username: string;
|
|
5
|
+
password?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface AuthData {
|
|
8
|
+
examToken: string;
|
|
9
|
+
orgName: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ApiResponse<T> {
|
|
12
|
+
code: number;
|
|
13
|
+
message?: string[];
|
|
14
|
+
result?: T;
|
|
15
|
+
}
|
|
16
|
+
export declare const FormType: {
|
|
17
|
+
URLEncoded: string;
|
|
18
|
+
JSON: string;
|
|
19
|
+
};
|
|
20
|
+
export type FormType = typeof FormType[keyof typeof FormType];
|
|
21
|
+
export declare class TrackClient {
|
|
22
|
+
private baseUrl;
|
|
23
|
+
private headers;
|
|
24
|
+
private cookies;
|
|
25
|
+
private authData?;
|
|
26
|
+
private cookieExpired;
|
|
27
|
+
private agent;
|
|
28
|
+
constructor(scheme: string, hostname: string, basic?: BasicAuth, authData?: AuthData);
|
|
29
|
+
getCookies(): {
|
|
30
|
+
[name: string]: string;
|
|
31
|
+
};
|
|
32
|
+
authCookieExpired(): boolean;
|
|
33
|
+
static fromTrackConfig(config: TrackConfig): TrackClient;
|
|
34
|
+
authenticate(): Promise<void>;
|
|
35
|
+
private _get;
|
|
36
|
+
private _post;
|
|
37
|
+
private _put;
|
|
38
|
+
private _result;
|
|
39
|
+
getApplicantExamNoAuth(token: string): Promise<ApplicantExamForNoAuth>;
|
|
40
|
+
getExamSession(applicantExamId: number): Promise<ExamSession>;
|
|
41
|
+
getProgrammingLanguages(): Promise<ProgrammingLanguageInfo[]>;
|
|
42
|
+
findProgrammingLanguage(programmingLanguage: ProgrammingLanguage): Promise<ProgrammingLanguageInfo | undefined>;
|
|
43
|
+
switchChallengeLanguage(applicantExamId: number, resultId: number, language: ProgrammingLanguage): Promise<ChallengeResult>;
|
|
44
|
+
startChallenge(applicantExamId: number, resultId: number): Promise<ChallengeResult>;
|
|
45
|
+
prepareChallenge(applicantExamId: number, challengeId: number): Promise<ChallengeResult>;
|
|
46
|
+
timeLeft(applicantExamId: number, resultId: number): Promise<number>;
|
|
47
|
+
getChallengeCodingContext(applicantExamId: number, resultId: number): Promise<CodingContext>;
|
|
48
|
+
getPresignedUrls(applicantExamId: number, resultId: number, files: string[]): Promise<{
|
|
49
|
+
[name: string]: string;
|
|
50
|
+
}>;
|
|
51
|
+
orcaToken(url: string): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Temporary code to retrieve the Orca host.
|
|
54
|
+
* Will be able to get from CodingContext in the future.
|
|
55
|
+
*/
|
|
56
|
+
orcaHost(): Promise<string>;
|
|
57
|
+
saveFiles(applicantExamId: number, resultId: number, saveFilesRequest: SaveFilesRequest): Promise<ChallengeResult>;
|
|
58
|
+
updateEditorScore(applicantExamId: number, resultId: number, editorScore: number): Promise<ChallengeResult>;
|
|
59
|
+
reset(applicantExamId: number, resultId: number): Promise<ChallengeResult>;
|
|
60
|
+
}
|