This repo is a Windows-first Node/Express app with a vanilla HTML/CSS/JS UI.
The backend shells out to winget and PowerShell and manages long-running operations via a PTY job manager.
Non-Windows environments can run basic Node syntax checks, but most runtime flows (winget, powershell, start) will fail.
server.js: Express server, API routes, static hosting forgui/.utils/winget.js: wrapswingetCLI and parses column output.utils/jobs.js: long-running job manager (node-pty) and log files underjobs/.utils/cache.js: JSON cache persisted underdata/.gui/: frontend (index.html,script.js,style.css).offline-packages/: bundled.tgzdeps and optionalnode-installer.msi.
Install deps (online):
npm installInstall deps (offline, matches run.bat/install.bat):
npm install offline-packages\express-5.2.1.tgz offline-packages\cors-2.8.5.tgz offline-packages\node-pty-1.1.0.tgzRun server:
npm start # or: node server.jsWindows helpers:
run.bat: elevate + install deps if needed + start server.install.bat: install toC:\EasyWinGet+ create shortcuts.
Lint (ESLint 9 flat config + eslint-config-prettier):
npm run lint # report only
npm run lint:fix # auto-fixFormat (Prettier):
npm run format # auto-format all files
npm run format:check # check only (CI-friendly)Quick syntax check (works on any OS):
node --check server.js
node --check utils/winget.jsTests: none currently (npm test intentionally exits 1).
If adding tests, prefer Node's built-in runner:
node --test # all tests
node --test test/winget.test.js # single file
node --test --test-name-pattern="parseUpdates" test/winget.test.js # single test by name- No
.cursorrulesor.cursor/rules/found. - No
.github/copilot-instructions.mdfound.
- CommonJS (
"type": "commonjs"inpackage.json); userequire(...)andmodule.exports. - Target Node 18+ (web globals like
fetch,AbortSignal,TextDecoderare available).
- 4-space indentation, no tabs.
- Semicolons required.
- Single quotes in JS files.
- Trailing commas:
es5(arrays, objects — not function params). - Print width: 100 columns.
- Arrow parens: always (
(x) => ...). - Line endings: LF.
- Bracket spacing enabled:
{ foo }.
- Group in order: Node built-ins, external deps, local modules.
- Prefer
const; destructure only when it improves clarity.
camelCasefor variables and functions.PascalCasefor singleton-ish objects (e.g.,State,DOMin frontend).UPPER_SNAKE_CASEfor true constants (e.g.,PORT,DOWNLOAD_DIR).
no-unused-vars: warn only;args: 'none',caughtErrors: 'none', vars prefixed_are ignored.no-control-regex: off (intentional ANSI/control-char handling).no-empty: error, but emptycatchblocks are allowed (allowEmptyCatch: true).no-useless-escape: off (CLI output cleanup patterns).- Backend files (
server.js,utils/**) usesourceType: 'commonjs'with Node globals. - Frontend files (
gui/**) usesourceType: 'script'with browser globals.
- Log unexpected backend errors with a clear prefix (e.g.,
[Manifest Error],[Details Error]). - Avoid empty
catch (e) {}unless best-effort and tightly scoped; do not silently swallow primary failures. - If you must swallow, do it only around non-critical cleanup (cache delete, reader cancel, optional HEAD checks).
- Response shape:
res.json({ success: true, ... })orres.status(code).json({ success: false, error/message: ... }). - Always
returnafter writing a response to avoid double-sends. - Validate/normalize
req.query/req.body(IDs, filenames, paths); block path traversal (..). - Prefer existing routing style in
server.js(single file) unless doing a larger refactor. - When spawning processes, prefer
utils/jobs.jsorexecFile-style APIs; avoid interpolating user input into shell strings.
- Use
utils/jobs.jsfor install/upgrade/download/uninstall so output is captured and pollable. - Pass args as an array; avoid assembling shell strings.
- Cancel via
jobs.cancelJob(jobId). Job IDs arejob-<uuid>; logs go tojobs/<jobId>.log.
utils/winget.jsparseswingetoutput using column positions; keep parsing tolerant to spacing/header variations.- Preserve
chcp 65001usage for UTF-8 output. - Winget can return non-zero exit codes for non-fatal states; treat
stdoutas the primary signal.
utils/cache.jswrites JSON todata/:installed.json,updates.json,ignored.json,downloads.json.- Cache invalidation is sometimes done by deleting a file (see uninstall flow in
server.js).
- Sanitize user-derived path segments (remove
<>:"/\|?*, trim); reject..and absolute paths. - Keep file operations scoped under the intended base directory (e.g.,
Downloads/,data/). - Use
path.join(base, segment)and validatesegmentbefore joining.
- Keep global state in
Stateand DOM references inDOM(gui/script.js). - Use
apiCall(endpoint)for fetches (consistent errors + cache-busting) andshowToast(...)for user-visible failures. - UI expects
successflags and sometimes coercesapps/updates/filesinto arrays; keep responses consistent.
- Make a small change in
server.jsorutils/*. - Run
node --check <file>to catch syntax errors fast. - Run
npm run lintandnpm run format:checkbefore committing. - On Windows: run
node server.jsand exercise the relevant UI flow. - If you touched WinGet parsing: validate against real
wingetoutput samples (spacing and headers vary).
- No
test/directory exists yet; createtest/with*.test.jsfiles as needed. - Use Node's built-in test runner (
node:test+node:assert/strict) — no Jest/Vitest dependency. - Keep tests deterministic: unit-test parsers (
parseApps,parseUpdates) and helpers. - If you want parser unit tests, export the pure parse functions from
utils/winget.js.
- Runtime deps are vendored as
.tgzunderoffline-packages/. - If adding a runtime dep, consider offline distribution (update
offline-packages/README.md, optionally vendor a.tgz).