Skip to content

Commit

Permalink
chore: Added JSDocs to improve maintainability and tooling support
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-coster committed Mar 27, 2023
1 parent ff2d7c0 commit 231cd03
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 9 deletions.
1 change: 1 addition & 0 deletions bin/update-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const redlock = new Redlock([client], {
retryDelay: ms("10s"),
});

/** @type {import("../src/types.js").ServerCache} */
const cache = {
async get(key) {
const json = await get(key);
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"semver": "^7.3.2"
},
"devDependencies": {
"@types/node": "^18.15.10",
"@types/request-ip": "^0.0.37",
"cross-env": "^7.0.2",
"nock": "^13.0.5",
"nodemon": "^2.0.6",
Expand Down
1 change: 1 addition & 0 deletions src/asset-platform.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { PLATFORM_ARCH } = require("./constants");

/** @param {string} fileName */
const assetPlatform = (fileName) => {
if (/.*(mac|darwin|osx).*(-arm).*\.zip/i.test(fileName)) {
return PLATFORM_ARCH.DARWIN_ARM64;
Expand Down
8 changes: 4 additions & 4 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
const PLATFORM = {
const PLATFORM = /** @type {const} */ ({
WIN32: "win32",
DARWIN: "darwin",
};
});

const PLATFORM_ARCH = {
const PLATFORM_ARCH = /** @type {const} */ ({
DARWIN_X64: "darwin-x64",
DARWIN_ARM64: "darwin-arm64",
WIN_X64: "win32-x64",
WIN_IA32: "win32-ia32",
WIN_ARM64: "win32-arm64",
};
});

const PLATFORM_ARCHS = Object.values(PLATFORM_ARCH);

Expand Down
23 changes: 23 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { PLATFORM_ARCH } from "./constants.js";

export type Platform = typeof PLATFORM_ARCH[keyof typeof PLATFORM_ARCH];

export interface Lock {
unlock(): Promise<void>;
}

export interface ServerCache {
get<T = unknown>(key: string): Promise<T | undefined>;
set(key: string, value: any): Promise<void>;
lock(resource: unknown): Promise<Lock>;
}

export type Latest = {
[P in Platform | "darwin" | "win32"]?: {
name: string;
version: string;
url: string;
notes: string;
RELEASES?: string;
};
};
57 changes: 52 additions & 5 deletions src/updates.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@ const { NODE_ENV: env } = process.env;
if (env === "test") log.level = "error";

class Updates {
/** @type {string} */ token;
/** @type {import("./types.js").ServerCache} */ cache;

/** @param {{token: string, cache: import("./types.js").ServerCache}} options */
constructor({ token, cache }) {
assert(cache, ".cache required");
this.token = token;
this.cache = cache;
}

/**
* @param {string|number|undefined} port
* @param {()=>void} [cb]
*/
listen(port, cb) {
if (typeof port === "function") {
[port, cb] = [undefined, port];
Expand Down Expand Up @@ -113,8 +121,14 @@ class Updates {
}
}

/**
* @param {string} account
* @param {string} repository
* @param {import("./types.js").Platform} platform
*/
async cachedGetLatest(account, repository, platform) {
const key = `${account}/${repository}`;
/** @type {import("./types.js").Latest|undefined|null} */
let latest = await this.cache.get(key);

if (latest) {
Expand All @@ -126,6 +140,7 @@ class Updates {
return latest[platform] || null;
}

/** @type {import("./types.js").Lock|undefined} */
let lock;
if (this.cache.lock) {
log.debug({ key }, "lock acquiring");
Expand Down Expand Up @@ -155,6 +170,11 @@ class Updates {
return latest && latest[platform];
}

/**
* @param {string} account
* @param {string} repository
* @returns {Promise<import("./types.js").Latest|undefined|null>}
*/
async getLatest(account, repository) {
account = encodeURIComponent(account);
repository = encodeURIComponent(repository);
Expand All @@ -176,6 +196,7 @@ class Updates {
return;
}

/** @type {import("./types.js").Latest} */
const latest = {};

const releases = await res.json();
Expand Down Expand Up @@ -213,28 +234,31 @@ class Updates {
PLATFORM_ARCH.WIN_IA32,
PLATFORM_ARCH.WIN_ARM64,
]) {
if (latest[key]) {
const rurl = `https://github.com/${account}/${repository}/releases/download/${latest[key].version}/RELEASES`;
const the_latest = latest[key];
if (the_latest) {
const rurl = `https://github.com/${account}/${repository}/releases/download/${the_latest.version}/RELEASES`;
const rres = await fetch(rurl);
if (rres.status < 400) {
const body = await rres.text();
const matches = body.match(/[^ ]*\.nupkg/gim);
assert(matches);
const nuPKG = rurl.replace("RELEASES", matches[0]);
latest[key].RELEASES = body.replace(matches[0], nuPKG);
the_latest.RELEASES = body.replace(matches[0], nuPKG);
}
}
}

return hasAnyAsset(latest) ? latest : null;
}

/** @param {string|null} ip */
hashIp(ip) {
if (!ip) return;
return crypto.createHash("sha256").update(ip).digest("hex");
}
}

/** @param {import("./types.js").Latest} latest */
const hasAllAssets = (latest) => {
return !!(
latest[PLATFORM_ARCH.DARWIN_X64] &&
Expand All @@ -245,6 +269,7 @@ const hasAllAssets = (latest) => {
);
};

/** @param {import("./types.js").Latest} latest */
const hasAnyAsset = (latest) => {
return !!(
latest[PLATFORM_ARCH.DARWIN_X64] ||
Expand All @@ -255,32 +280,54 @@ const hasAnyAsset = (latest) => {
);
};

/**
* @param {Res} res
* @param {string} message
*/
const notFound = (res, message = "Not found") => {
res.statusCode = 404;
res.end(message);
};

/**
* @param {Res} res
* @param {string} message
*/
const badRequest = (res, message) => {
res.statusCode = 400;
res.end(message);
};

/** @param {Res} res */
const noContent = (res) => {
res.statusCode = 204;
res.end();
};

/**
* @param {Res} res
* @param {any} obj
*/
const json = (res, obj) => {
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(obj));
};

// DO NOT PASS USER-SUPPLIED CONTENT TO THIS FUNCTION
// AS IT WILL REDIRECT A USER ANYWHERE
/**
* DO NOT PASS USER-SUPPLIED CONTENT TO THIS FUNCTION
* AS IT WILL REDIRECT A USER ANYWHERE
* @param {Res} res
* @param {string} url
*/
const redirect = (res, url) => {
res.statusCode = 302;
res.setHeader("Location", url);
res.end(url);
};

module.exports = Updates;

/**
* @typedef {http.IncomingMessage} Req
* @typedef {http.ServerResponse} Res
*/

0 comments on commit 231cd03

Please sign in to comment.