typing.works 0.3.0-beta.8 → 0.3.0-beta.9
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/README.md +2 -2
- package/install.ps1 +97 -0
- package/install.sh +97 -0
- package/package.json +2 -3
- package/vendor/darwin-arm64/typingworks +0 -0
- package/vendor/darwin-x64/typingworks +0 -0
- package/vendor/linux-arm64/typingworks +0 -0
- package/vendor/linux-ia32/typingworks +0 -0
- package/vendor/linux-x64/typingworks +0 -0
- package/vendor/win32-arm64/typingworks.exe +0 -0
- package/vendor/win32-x64/typingworks.exe +0 -0
package/README.md
CHANGED
|
@@ -8,13 +8,13 @@ Typing practice from [typing.works](https://typing.works), in your terminal —
|
|
|
8
8
|
macOS · Linux
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
|
-
curl -fsSL https://
|
|
11
|
+
curl -fsSL https://cdn.jsdelivr.net/npm/typing.works/install.sh | sh
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
Windows 10 · 11 (PowerShell)
|
|
15
15
|
|
|
16
16
|
```powershell
|
|
17
|
-
irm https://
|
|
17
|
+
irm https://cdn.jsdelivr.net/npm/typing.works/install.ps1 | iex
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
아무것도 미리 깔 필요가 없습니다. 실행 파일을 받아 해시를 대조하고 `~/.local/bin`(Windows 는 `%LOCALAPPDATA%\typingworks\bin`)에 둡니다 — 그다음 `typingworks`.
|
package/install.ps1
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# typing.works - terminal installer for Windows
|
|
2
|
+
# irm https://cdn.jsdelivr.net/npm/typing.works/install.ps1 | iex
|
|
3
|
+
#
|
|
4
|
+
# What it does
|
|
5
|
+
# 1. Downloads the typing.works npm package (about 2 MB, holds all seven binaries) and takes the one for this PC
|
|
6
|
+
# (Windows 10 1809+ / 11, x64 or arm64)
|
|
7
|
+
# 2. Verifies it against the sha512 the npm registry publishes - stops on mismatch
|
|
8
|
+
# 3. Puts typingworks.exe and typing.works.exe in %LOCALAPPDATA%\typingworks\bin (override: TW_INSTALL_DIR)
|
|
9
|
+
# 4. Adds that folder to your user PATH (skip: TW_NO_MODIFY_PATH=1)
|
|
10
|
+
# The version follows the latest "typing.works" on npm (pin: TW_VERSION). Ctrl+U in the app re-runs this script.
|
|
11
|
+
# No source code is downloaded - only the binary, README and LICENSE.
|
|
12
|
+
# Uninstall: delete that folder and %USERPROFILE%\.typingworks
|
|
13
|
+
# (ASCII only on purpose: Windows PowerShell 5.1 reads BOM-less script files in the ANSI code page.)
|
|
14
|
+
& {
|
|
15
|
+
$ErrorActionPreference = 'Stop'
|
|
16
|
+
$ProgressPreference = 'SilentlyContinue' # the progress bar slows downloads a lot on PowerShell 5.1
|
|
17
|
+
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
|
|
18
|
+
$reg = if ($env:TW_REGISTRY) { $env:TW_REGISTRY } else { 'https://registry.npmjs.org' } # an npm mirror, if you use one
|
|
19
|
+
$dir = if ($env:TW_INSTALL_DIR) { $env:TW_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA 'typingworks\bin' }
|
|
20
|
+
|
|
21
|
+
# The real CPU - pick arm64 even when x64 PowerShell runs under emulation on ARM
|
|
22
|
+
$arch = $null
|
|
23
|
+
try { $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString() } catch { }
|
|
24
|
+
if (-not $arch) { $arch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } }
|
|
25
|
+
switch -regex ($arch) {
|
|
26
|
+
'^(Arm64|ARM64)$' { $arch = 'arm64' }
|
|
27
|
+
'^(X64|AMD64)$' { $arch = 'x64' }
|
|
28
|
+
default { throw "typing.works: $arch is not supported yet (Windows x64 / arm64)" }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Version and the package's sha512 in one request - latest unless TW_VERSION is set
|
|
32
|
+
$tag = if ($env:TW_VERSION) { $env:TW_VERSION } else { 'latest' }
|
|
33
|
+
$meta = $null
|
|
34
|
+
try { $meta = Invoke-RestMethod -UseBasicParsing "$reg/typing.works/$tag" } catch { }
|
|
35
|
+
if (-not $meta -or -not $meta.version -or -not $meta.dist.integrity) { throw 'typing.works: could not read the npm registry (check your internet connection)' }
|
|
36
|
+
$ver = $meta.version
|
|
37
|
+
$integrity = $meta.dist.integrity
|
|
38
|
+
if ($integrity -notlike 'sha512-*') { throw "typing.works: unexpected integrity format: $integrity" }
|
|
39
|
+
$tar = (Get-Command tar.exe -ErrorAction SilentlyContinue).Source # built in since Windows 10 1803
|
|
40
|
+
if (-not $tar) { throw 'typing.works: tar.exe was not found (Windows 10 1803 or later is needed)' }
|
|
41
|
+
# Download with the built-in curl.exe (Windows 10 1803+). PowerShell 5.1's Invoke-WebRequest follows GitHub's redirect to a
|
|
42
|
+
# signed asset URL but mangles the signature and gets 404 (seen 2026-10-13). Fall back to it only when curl.exe is missing.
|
|
43
|
+
$curl = (Get-Command curl.exe -ErrorAction SilentlyContinue).Source
|
|
44
|
+
function Get-File([string]$from, [string]$to) {
|
|
45
|
+
if ($curl -and $from -notlike 'file:*') {
|
|
46
|
+
& $curl -fsSL --retry 2 -o $to $from
|
|
47
|
+
if ($LASTEXITCODE -ne 0) { throw "typing.works: download failed ($LASTEXITCODE): $from" }
|
|
48
|
+
} else {
|
|
49
|
+
Invoke-WebRequest -UseBasicParsing $from -OutFile $to
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
$tmp = Join-Path ([IO.Path]::GetTempPath()) ('typingworks-' + [guid]::NewGuid().ToString('N'))
|
|
53
|
+
New-Item -ItemType Directory -Path $tmp | Out-Null
|
|
54
|
+
try {
|
|
55
|
+
Write-Host "typing.works $ver (win32-$arch) downloading..."
|
|
56
|
+
$tgz = Join-Path $tmp "typing.works-$ver.tgz"
|
|
57
|
+
Get-File "$reg/typing.works/-/typing.works-$ver.tgz" $tgz
|
|
58
|
+
$want = -join ([Convert]::FromBase64String($integrity.Substring(7)) | ForEach-Object { $_.ToString('x2') })
|
|
59
|
+
$got = (Get-FileHash -Algorithm SHA512 $tgz).Hash.ToLower()
|
|
60
|
+
if ($got -ne $want) { throw 'typing.works: checksum mismatch - not installed' }
|
|
61
|
+
$inner = "package/vendor/win32-$arch/typingworks.exe"
|
|
62
|
+
& $tar -xzf $tgz -C $tmp $inner
|
|
63
|
+
if ($LASTEXITCODE -ne 0) { throw "typing.works: could not extract $inner" }
|
|
64
|
+
|
|
65
|
+
New-Item -ItemType Directory -Force -Path $dir | Out-Null
|
|
66
|
+
# A running .exe cannot be deleted but can be renamed - move the old one aside, then place the new one (Ctrl+U comes this way)
|
|
67
|
+
foreach ($n in @('typingworks.exe', 'typing.works.exe')) {
|
|
68
|
+
$p = Join-Path $dir $n
|
|
69
|
+
Remove-Item ($p + '.old') -Force -ErrorAction SilentlyContinue
|
|
70
|
+
if (Test-Path $p) { Move-Item $p ($p + '.old') -Force }
|
|
71
|
+
}
|
|
72
|
+
$exe = Join-Path $dir 'typingworks.exe'
|
|
73
|
+
Copy-Item (Join-Path $tmp ("package\vendor\win32-$arch\typingworks.exe")) $exe
|
|
74
|
+
Copy-Item $exe (Join-Path $dir 'typing.works.exe') # same program under both names
|
|
75
|
+
foreach ($n in @('typingworks.exe.old', 'typing.works.exe.old')) { Remove-Item (Join-Path $dir $n) -Force -ErrorAction SilentlyContinue }
|
|
76
|
+
|
|
77
|
+
# Tell the app it was installed by this script, so Ctrl+U re-runs it (UTF-8 without BOM - user folders may be non-ASCII)
|
|
78
|
+
$tw = Join-Path $HOME '.typingworks'
|
|
79
|
+
New-Item -ItemType Directory -Force -Path $tw | Out-Null
|
|
80
|
+
[IO.File]::WriteAllText((Join-Path $tw 'install-script'), $exe + "`n", (New-Object Text.UTF8Encoding $false))
|
|
81
|
+
|
|
82
|
+
Write-Host "OK typing.works $ver -> $exe"
|
|
83
|
+
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
84
|
+
$has = $false
|
|
85
|
+
if ($userPath) { foreach ($e in $userPath.Split(';')) { if ($e.TrimEnd('\') -ieq $dir.TrimEnd('\')) { $has = $true } } }
|
|
86
|
+
if (-not $has -and $env:TW_NO_MODIFY_PATH -ne '1') {
|
|
87
|
+
$new = if ($userPath) { $userPath.TrimEnd(';') + ';' + $dir } else { $dir }
|
|
88
|
+
[Environment]::SetEnvironmentVariable('Path', $new, 'User')
|
|
89
|
+
$env:Path = $env:Path.TrimEnd(';') + ';' + $dir
|
|
90
|
+
Write-Host " added $dir to your PATH - open a new terminal, then run: typingworks"
|
|
91
|
+
} else {
|
|
92
|
+
Write-Host ' run: typingworks'
|
|
93
|
+
}
|
|
94
|
+
} finally {
|
|
95
|
+
Remove-Item $tmp -Recurse -Force -ErrorAction SilentlyContinue
|
|
96
|
+
}
|
|
97
|
+
}
|
package/install.sh
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# typing.works — terminal 설치 · install
|
|
3
|
+
# curl -fsSL https://cdn.jsdelivr.net/npm/typing.works/install.sh | sh
|
|
4
|
+
#
|
|
5
|
+
# 하는 일 · what it does
|
|
6
|
+
# 1. npm 의 typing.works 꾸러미(실행 파일 일곱 가지가 든 약 2MB)를 받아 이 기기(macOS · Linux, arm64 · x64 · x86)에 맞는 것만 꺼낸다
|
|
7
|
+
# 2. npm 이 알려 주는 sha512 와 대조한다 — 다르면 멈춘다
|
|
8
|
+
# 3. ~/.local/bin 에 typingworks 와 typing.works 를 둔다(TW_INSTALL_DIR 로 바꿀 수 있다)
|
|
9
|
+
# 4. 그 폴더가 PATH 에 없으면 셸 설정 파일에 한 줄 더하고 알린다
|
|
10
|
+
# 판 번호는 npm 의 typing.works 최신 판을 따른다(TW_VERSION=0.3.0 처럼 고를 수 있다). 앱 안 ^U 가 이 스크립트를 다시 돌린다.
|
|
11
|
+
# 소스 코드는 없다 — npm 꾸러미에는 실행 파일 · 설명서 · 라이선스 · 이 스크립트뿐. 지우기: rm ~/.local/bin/typingworks ~/.local/bin/typing.works ; rm -rf ~/.typingworks
|
|
12
|
+
set -eu
|
|
13
|
+
|
|
14
|
+
REG="${TW_REGISTRY:-https://registry.npmjs.org}" # npm 미러를 쓰는 곳이면 바꾼다
|
|
15
|
+
DIR="${TW_INSTALL_DIR:-$HOME/.local/bin}"
|
|
16
|
+
say() { printf '%s\n' "$*"; }
|
|
17
|
+
die() { printf 'typing.works: %s\n' "$*" >&2; exit 1; }
|
|
18
|
+
|
|
19
|
+
command -v curl >/dev/null 2>&1 || die "curl 이 필요합니다 · curl is required"
|
|
20
|
+
command -v tar >/dev/null 2>&1 || die "tar 가 필요합니다 · tar is required"
|
|
21
|
+
|
|
22
|
+
os="$(uname -s)"; arch="$(uname -m)"
|
|
23
|
+
case "$os" in
|
|
24
|
+
Darwin) os=darwin ;;
|
|
25
|
+
Linux) os=linux ;;
|
|
26
|
+
*) die "$os 은 아직 지원하지 않습니다 — macOS · Linux (Windows 는 PowerShell 설치를 쓰세요) · $os is not supported yet" ;;
|
|
27
|
+
esac
|
|
28
|
+
case "$arch" in
|
|
29
|
+
arm64|aarch64) arch=arm64 ;;
|
|
30
|
+
x86_64|amd64) arch=x64 ;;
|
|
31
|
+
i386|i486|i586|i686) arch=ia32 ;;
|
|
32
|
+
*) die "$arch 은 아직 지원하지 않습니다 · $arch is not supported yet" ;;
|
|
33
|
+
esac
|
|
34
|
+
[ "$os" = darwin ] && [ "$arch" = ia32 ] && die "32비트 macOS 는 지원하지 않습니다 · 32-bit macOS is not supported"
|
|
35
|
+
# Rosetta 로 돌린 셸이어도 진짜 칩에 맞춘다
|
|
36
|
+
if [ "$os" = darwin ] && [ "$arch" = x64 ] && [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)" = 1 ]; then arch=arm64; fi
|
|
37
|
+
|
|
38
|
+
# 판 번호와 꾸러미의 sha512 를 한 번에 — 판을 안 주면 latest
|
|
39
|
+
meta="$(curl -fsSL "$REG/typing.works/${TW_VERSION:-latest}")" || die "판 정보를 못 읽었습니다(인터넷 연결을 확인하세요) · could not reach the npm registry"
|
|
40
|
+
ver="$(printf '%s' "$meta" | grep -o '"version":"[^"]*"' | head -n 1 | cut -d'"' -f4)"
|
|
41
|
+
integ="$(printf '%s' "$meta" | grep -o '"integrity":"sha512-[^"]*"' | head -n 1 | cut -d'"' -f4)"
|
|
42
|
+
[ -n "$ver" ] && [ -n "$integ" ] || die "판 정보가 이상합니다 · unexpected registry response"
|
|
43
|
+
|
|
44
|
+
# base64 → 16진수(base64 -d 는 GNU · 새 macOS, -D 는 옛 macOS, 없으면 openssl)
|
|
45
|
+
b64hex() {
|
|
46
|
+
for d in "base64 -d" "base64 -D" "openssl base64 -d -A"; do
|
|
47
|
+
h="$(printf '%s' "$1" | $d 2>/dev/null | od -An -v -tx1 | tr -d ' \n')"
|
|
48
|
+
[ -n "$h" ] && { printf '%s' "$h"; return 0; }
|
|
49
|
+
done
|
|
50
|
+
return 1
|
|
51
|
+
}
|
|
52
|
+
want="$(b64hex "${integ#sha512-}")" || die "sha512 를 풀 도구가 없습니다 · no base64 tool"
|
|
53
|
+
|
|
54
|
+
tmp="$(mktemp -d 2>/dev/null || mktemp -d -t typingworks)"
|
|
55
|
+
trap 'rm -rf "$tmp"' EXIT INT TERM
|
|
56
|
+
tgz="$tmp/typing.works-$ver.tgz"
|
|
57
|
+
|
|
58
|
+
say "typing.works $ver ($os-$arch) 받는 중 · downloading…"
|
|
59
|
+
curl -fsSL "$REG/typing.works/-/typing.works-$ver.tgz" -o "$tgz" || die "받지 못했습니다 · download failed"
|
|
60
|
+
if command -v sha512sum >/dev/null 2>&1; then got="$(sha512sum "$tgz" | cut -d' ' -f1)"
|
|
61
|
+
elif command -v shasum >/dev/null 2>&1; then got="$(shasum -a 512 "$tgz" | cut -d' ' -f1)"
|
|
62
|
+
elif command -v openssl >/dev/null 2>&1; then got="$(openssl dgst -sha512 -r "$tgz" | cut -d' ' -f1)"
|
|
63
|
+
else die "sha512 도구가 없어 대조할 수 없습니다 · no sha512 tool"; fi
|
|
64
|
+
[ "$got" = "$want" ] || die "해시가 다릅니다 — 설치하지 않았습니다 · checksum mismatch, not installed"
|
|
65
|
+
|
|
66
|
+
tar -xzf "$tgz" -C "$tmp" "package/vendor/$os-$arch/typingworks" || die "풀지 못했습니다 · could not extract"
|
|
67
|
+
mv "$tmp/package/vendor/$os-$arch/typingworks" "$tmp/typingworks"
|
|
68
|
+
mkdir -p "$DIR"
|
|
69
|
+
# 같은 폴더에 새 이름으로 둔 뒤 바꿔 끼운다 — 돌고 있는 typingworks 가 있어도 안전하다(^U 가 이 길로 온다)
|
|
70
|
+
cp "$tmp/typingworks" "$DIR/.typingworks.new"
|
|
71
|
+
chmod 755 "$DIR/.typingworks.new"
|
|
72
|
+
mv -f "$DIR/.typingworks.new" "$DIR/typingworks"
|
|
73
|
+
ln -sf typingworks "$DIR/typing.works"
|
|
74
|
+
|
|
75
|
+
# 앱이 «스크립트로 깔았다»를 알게 — ^U 가 이 스크립트를 다시 돌린다
|
|
76
|
+
mkdir -p "$HOME/.typingworks"
|
|
77
|
+
printf '%s\n' "$DIR/typingworks" > "$HOME/.typingworks/install-script"
|
|
78
|
+
|
|
79
|
+
say "✓ typing.works $ver → $DIR/typingworks"
|
|
80
|
+
|
|
81
|
+
case ":$PATH:" in
|
|
82
|
+
*":$DIR:"*) say " 실행 · run: typingworks" ;;
|
|
83
|
+
*)
|
|
84
|
+
line="export PATH=\"$DIR:\$PATH\""
|
|
85
|
+
case "${SHELL:-}" in
|
|
86
|
+
*/zsh) rc="$HOME/.zshrc" ;;
|
|
87
|
+
*/bash) if [ "$os" = darwin ]; then rc="$HOME/.bash_profile"; else rc="$HOME/.bashrc"; fi ;;
|
|
88
|
+
*) rc="$HOME/.profile" ;;
|
|
89
|
+
esac
|
|
90
|
+
if [ "${TW_NO_MODIFY_PATH:-}" != 1 ] && ! grep -qsF "$line" "$rc"; then
|
|
91
|
+
printf '\n# typing.works\n%s\n' "$line" >> "$rc"
|
|
92
|
+
say " $DIR 를 PATH 에 더했습니다($rc) · added to PATH"
|
|
93
|
+
fi
|
|
94
|
+
say " 새 터미널을 열거나 · open a new terminal, or: $line"
|
|
95
|
+
say " 실행 · run: typingworks"
|
|
96
|
+
;;
|
|
97
|
+
esac
|
package/package.json
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typing.works",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.9",
|
|
4
4
|
"description": "typing.works in your terminal — Korean & English typing practice, records, stats and ranked matches",
|
|
5
5
|
"bin": {
|
|
6
6
|
"typing.works": "bin/typing.works.js",
|
|
7
7
|
"typingworks": "bin/typing.works.js"
|
|
8
8
|
},
|
|
9
|
-
"files": ["bin", "vendor", "README.md", "LICENSE"],
|
|
9
|
+
"files": ["bin", "vendor", "install.sh", "install.ps1", "README.md", "LICENSE"],
|
|
10
10
|
"os": ["darwin", "linux", "win32"],
|
|
11
11
|
"cpu": ["arm64", "x64", "ia32"],
|
|
12
12
|
"engines": { "node": ">=14" },
|
|
13
13
|
"license": "SEE LICENSE IN LICENSE",
|
|
14
14
|
"homepage": "https://typing.works",
|
|
15
|
-
"repository": { "type": "git", "url": "git+https://github.com/pers-dev/typing.works.git" },
|
|
16
15
|
"author": "typing.works",
|
|
17
16
|
"keywords": ["typing", "typing-practice", "typing-test", "korean", "hangul", "terminal", "tui", "cli", "wpm"]
|
|
18
17
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|