node-pluginsmanager 3.5.1 → 3.6.0

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.
@@ -31,6 +31,7 @@ export default class PluginsManager extends EventEmitter {
31
31
  beforeInitAll(callback: tBeforeAllMethodCallback): Promise<void>;
32
32
  initAll(...data: unknown[]): Promise<void>;
33
33
  releaseAll(...data: unknown[]): Promise<void>;
34
+ getLatestGithubTag(user: string, repo: string): Promise<string>;
34
35
  installViaGithub(user: string, repo: string, ...data: unknown[]): Promise<Orchestrator>;
35
36
  updateViaGithub(plugin: Orchestrator, ...data: unknown[]): Promise<Orchestrator>;
36
37
  uninstall(plugin: Orchestrator, ...data: unknown[]): Promise<string>;
@@ -12,6 +12,7 @@ const promises_1 = require("node:fs/promises");
12
12
  // externals
13
13
  const check_version_modules_1 = __importDefault(require("check-version-modules"));
14
14
  const check_node_engine_1 = __importDefault(require("check-node-engine"));
15
+ const semver_1 = __importDefault(require("semver"));
15
16
  // locals
16
17
  const rmdirp_1 = __importDefault(require("./utils/rmdirp"));
17
18
  const checkAbsoluteDirectory_1 = __importDefault(require("./checkers/checkAbsoluteDirectory"));
@@ -24,7 +25,9 @@ const isFile_1 = __importDefault(require("./utils/isFile"));
24
25
  const createPluginByDirectory_1 = __importDefault(require("./createPluginByDirectory"));
25
26
  const loadSortedPlugins_1 = __importDefault(require("./loadSortedPlugins"));
26
27
  const initSortedPlugins_1 = __importDefault(require("./initSortedPlugins"));
27
- const extractGithub_1 = __importDefault(require("./extractGithub"));
28
+ const extractGithub_1 = __importDefault(require("./utils/extractGithub"));
29
+ const getLatestGithubTag_1 = __importDefault(require("./utils/getLatestGithubTag"));
30
+ const parseGithubUserRepo_1 = __importDefault(require("./utils/parseGithubUserRepo"));
28
31
  // git
29
32
  const gitInstall_1 = __importDefault(require("./cmd/git/gitInstall"));
30
33
  const gitUpdate_1 = __importDefault(require("./cmd/git/gitUpdate"));
@@ -267,6 +270,11 @@ class PluginsManager extends node_events_1.default {
267
270
  this.emit("allreleased", ...data);
268
271
  });
269
272
  }
273
+ getLatestGithubTag(user, repo) {
274
+ return (0, getLatestGithubTag_1.default)(user, repo).then((tag) => {
275
+ return tag.name;
276
+ });
277
+ }
270
278
  // install a plugin via github repo, using "data" in arguments for "install" and "init" plugin's Orchestrator methods
271
279
  installViaGithub(user, repo, ...data) {
272
280
  return (0, checkAbsoluteDirectory_1.default)("installViaGithub/directory", this.directory).then(() => {
@@ -400,24 +408,52 @@ class PluginsManager extends node_events_1.default {
400
408
  updateViaGithub(plugin, ...data) {
401
409
  let directory = "";
402
410
  let key = -1;
411
+ let githubUrl = "";
412
+ let latestTag = "";
403
413
  // check plugin
404
- return Promise.resolve().then(() => {
405
- return (0, checkOrchestrator_1.default)("updateViaGithub/plugin", plugin).then(() => {
406
- return (0, checkNonEmptyString_1.default)("updateViaGithub/github", (0, extractGithub_1.default)(plugin)).catch(() => {
407
- return Promise.reject(new ReferenceError("Plugin \"" + plugin.name + "\" must be linked in the package to a github project to be updated"));
408
- });
414
+ return (0, checkOrchestrator_1.default)("updateViaGithub/plugin", plugin).then(() => {
415
+ const github = (0, extractGithub_1.default)(plugin);
416
+ return (0, checkNonEmptyString_1.default)("updateViaGithub/github", github).catch(() => {
417
+ return Promise.reject(new ReferenceError("Plugin \"" + plugin.name + "\" must be linked in the package to a github project to be updated"));
409
418
  }).then(() => {
410
- key = this.getPluginsNames().findIndex((pluginName) => {
411
- return pluginName === plugin.name;
412
- });
413
- return -1 < key ? Promise.resolve() : Promise.reject(new Error("Plugin \"" + plugin.name + "\" is not registered"));
419
+ githubUrl = github;
420
+ });
421
+ }).then(() => {
422
+ key = this.getPluginsNames().findIndex((pluginName) => {
423
+ return pluginName === plugin.name;
414
424
  });
425
+ if (-1 >= key) {
426
+ throw new Error("Plugin \"" + plugin.name + "\" is not registered");
427
+ }
415
428
  // check plugin directory
416
429
  }).then(() => {
417
430
  return (0, checkAbsoluteDirectory_1.default)("updateViaGithub/directory", this.directory).then(() => {
418
431
  directory = (0, node_path_1.join)(this.directory, plugin.name);
419
432
  return (0, checkAbsoluteDirectory_1.default)("updateViaGithub/plugindirectory", directory);
420
433
  });
434
+ // check remote version
435
+ }).then(() => {
436
+ const githubUserRepo = (0, parseGithubUserRepo_1.default)(githubUrl);
437
+ if (!githubUserRepo) {
438
+ throw new Error("Plugin \"" + plugin.name + "\" has an invalid github project link");
439
+ }
440
+ const currentVersion = semver_1.default.coerce(plugin.version);
441
+ if (!currentVersion) {
442
+ throw new Error("Plugin \"" + plugin.name + "\" has no valid version (\"" + plugin.version + "\")");
443
+ }
444
+ this._logger?.("debug", "Get github latest tag", false, plugin.name);
445
+ return (0, getLatestGithubTag_1.default)(githubUserRepo.user, githubUserRepo.repo).then((tag) => {
446
+ const latestVersion = semver_1.default.coerce(tag.name);
447
+ if (!latestVersion) {
448
+ throw new Error("No valid version found for plugin \"" + plugin.name + "\" on github");
449
+ }
450
+ this._logger?.("debug", "Compare local version with github lastest tag", false, plugin.name);
451
+ if (!semver_1.default.gt(latestVersion, currentVersion)) {
452
+ throw new Error("Plugin \"" + plugin.name + "\" is already up to date (v" + plugin.version + ")");
453
+ }
454
+ latestTag = tag.name;
455
+ this._logger?.("success", "New version available", false, plugin.name);
456
+ });
421
457
  // release plugin
422
458
  }).then(() => {
423
459
  const pluginName = plugin.name;
@@ -428,15 +464,17 @@ class PluginsManager extends node_events_1.default {
428
464
  this.emit("destroyed", pluginName, ...data);
429
465
  this.plugins.splice(key, 1);
430
466
  });
431
- // download plugin
467
+ // update plugin
432
468
  }).then(() => {
433
- return (0, gitUpdate_1.default)(directory).then(() => {
469
+ this._logger?.("debug", "Update with new plugin version", false, plugin.name);
470
+ return (0, gitUpdate_1.default)(directory, latestTag).then(() => {
434
471
  return (0, createPluginByDirectory_1.default)(directory, this.externalResourcesDirectory, this._logger, ...data);
435
472
  });
436
473
  // check plugin modules versions
437
474
  }).then((_plugin) => {
475
+ this._logger?.("debug", "Check modules", false, plugin.name);
438
476
  return this.checkModules(_plugin).then(() => {
439
- return Promise.resolve(_plugin);
477
+ return _plugin;
440
478
  });
441
479
  }).then((_plugin) => {
442
480
  // update dependencies & execute update script
@@ -451,7 +489,7 @@ class PluginsManager extends node_events_1.default {
451
489
  }).then(() => {
452
490
  this.emit("initialized", _plugin, ...data);
453
491
  this.plugins[key] = _plugin;
454
- return Promise.resolve(_plugin);
492
+ return _plugin;
455
493
  });
456
494
  });
457
495
  }
@@ -9,6 +9,7 @@ exports.default = gitInstall;
9
9
  const node_path_1 = require("node:path");
10
10
  // locals
11
11
  const checkDirectory_1 = __importDefault(require("../../checkers/checkDirectory"));
12
+ const getLatestGithubTag_1 = __importDefault(require("../../utils/getLatestGithubTag"));
12
13
  const checkNonEmptyString_1 = __importDefault(require("../../checkers/checkNonEmptyString"));
13
14
  const cmd_1 = __importDefault(require("../cmd"));
14
15
  // module
@@ -26,16 +27,20 @@ function gitInstall(directory, user, repo) {
26
27
  }).then(() => {
27
28
  return (0, checkNonEmptyString_1.default)("cmd/git/install/repo", repo);
28
29
  }).then(() => {
29
- // git clone
30
- return (0, cmd_1.default)((0, node_path_1.dirname)(directory), "git", [
31
- "-c",
32
- "core.quotepath=false",
33
- "clone",
34
- "--recursive",
35
- "--depth",
36
- "1",
37
- "https://github.com/" + user + "/" + repo + "/",
38
- directory
39
- ]);
30
+ return (0, getLatestGithubTag_1.default)(user, repo).then((tag) => {
31
+ // git clone
32
+ return (0, cmd_1.default)((0, node_path_1.dirname)(directory), "git", [
33
+ "-c",
34
+ "core.quotepath=false",
35
+ "clone",
36
+ "--recursive",
37
+ "--depth",
38
+ "1",
39
+ "--branch",
40
+ tag.name,
41
+ "https://github.com/" + user + "/" + repo + "/",
42
+ directory
43
+ ]);
44
+ });
40
45
  });
41
46
  }
@@ -1 +1 @@
1
- export default function gitUpdate(directory: string): Promise<void>;
1
+ export default function gitUpdate(directory: string, tag?: string): Promise<void>;
@@ -7,11 +7,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.default = gitUpdate;
8
8
  // locals
9
9
  const checkDirectory_1 = __importDefault(require("../../checkers/checkDirectory"));
10
+ const checkNonEmptyString_1 = __importDefault(require("../../checkers/checkNonEmptyString"));
10
11
  const cmd_1 = __importDefault(require("../cmd"));
11
12
  // module
12
- function gitUpdate(directory) {
13
+ function gitUpdate(directory, tag) {
13
14
  return (0, checkDirectory_1.default)("cmd/git/update/directory", directory).then(() => {
14
- // git update
15
- return (0, cmd_1.default)(directory, "git", ["pull"]);
15
+ // pull latest changes from remote repository
16
+ if ("string" !== typeof tag) {
17
+ return (0, cmd_1.default)(directory, "git", ["pull"]);
18
+ }
19
+ // OR fetch specific tag from remote repository
20
+ return (0, checkNonEmptyString_1.default)("cmd/git/update/tag", tag).then(() => {
21
+ return (0, cmd_1.default)(directory, "git", [
22
+ "fetch",
23
+ "origin",
24
+ "tag",
25
+ tag,
26
+ "--depth",
27
+ "1"
28
+ ]).then(() => {
29
+ return (0, cmd_1.default)(directory, "git", ["checkout", tag]);
30
+ });
31
+ });
16
32
  });
17
33
  }
@@ -0,0 +1,11 @@
1
+ export interface GithubTag {
2
+ "name": string;
3
+ "commit": {
4
+ "sha": string;
5
+ "url": string;
6
+ };
7
+ "tarball_url": string;
8
+ "zipball_url": string;
9
+ "node_id": string;
10
+ }
11
+ export default function getLatestGithubTag(user: string, repo: string): Promise<GithubTag>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ // deps
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.default = getLatestGithubTag;
8
+ // locals
9
+ const checkNonEmptyString_1 = __importDefault(require("../checkers/checkNonEmptyString"));
10
+ // module
11
+ function getLatestGithubTag(user, repo) {
12
+ return (0, checkNonEmptyString_1.default)("utils/getLatestGithubTag/user", user).then(() => {
13
+ return (0, checkNonEmptyString_1.default)("utils/getLatestGithubTag/repo", repo);
14
+ }).then(() => {
15
+ const url = "https://api.github.com/repos/" + user + "/" + repo + "/tags";
16
+ const method = "GET";
17
+ return fetch(url, {
18
+ "method": method,
19
+ "headers": {
20
+ "Content-Type": "application/json"
21
+ }
22
+ }).then((res) => {
23
+ if (res.ok) {
24
+ return res.json();
25
+ }
26
+ else {
27
+ throw new Error("Failed to fetch '" + url + "': " + res.statusText);
28
+ }
29
+ }).then((data) => {
30
+ if (0 >= data.length) {
31
+ throw new Error("No tags found for '" + url + "'");
32
+ }
33
+ return data[0];
34
+ });
35
+ });
36
+ }
@@ -0,0 +1,5 @@
1
+ export interface GithubUserRepo {
2
+ "user": string;
3
+ "repo": string;
4
+ }
5
+ export default function parseGithubUserRepo(github: string): GithubUserRepo | null;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ // types & interfaces
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.default = parseGithubUserRepo;
5
+ // module
6
+ function parseGithubUserRepo(github) {
7
+ if ("string" !== typeof github) {
8
+ return null;
9
+ }
10
+ const githubUrlPattern = /(?:github\.com[/:]|@github\.com:)([^/]+)\/([^/.]+)/i;
11
+ const userRepoPattern = /^([^/]+)\/([^/]+)$/;
12
+ let match = githubUrlPattern.exec(github);
13
+ match ??= userRepoPattern.exec(github);
14
+ if (!match) {
15
+ return null;
16
+ }
17
+ return {
18
+ "user": match[1],
19
+ "repo": match[2].replace(/\.git$/i, "")
20
+ };
21
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
 
3
3
  "name": "node-pluginsmanager",
4
- "version": "3.5.1",
4
+ "version": "3.6.0",
5
5
  "description": "A plugins manager.",
6
6
 
7
7
  "type": "commonjs",
@@ -43,23 +43,25 @@
43
43
  "/lib/cjs"
44
44
  ],
45
45
  "engines": {
46
- "node": ">=22.22.3"
46
+ "node": ">=22.23.1"
47
47
  },
48
48
 
49
49
  "dependencies": {
50
50
  "check-node-engine": "1.1.0",
51
- "check-version-modules": "2.5.0"
51
+ "check-version-modules": "2.5.0",
52
+ "semver": "7.8.5"
52
53
  },
53
54
  "devDependencies": {
54
55
  "@types/express": "5.0.6",
55
- "@types/node": "25.9.2",
56
+ "@types/node": "26.0.1",
57
+ "@types/semver": "7.7.1",
56
58
  "@types/socket.io": "3.0.2",
57
59
  "@types/ws": "8.18.1",
58
60
  "eslint-plugin-personnallinter": "git+ssh://git@github.com/Psychopoulet/eslint-plugin-personnallinter",
59
61
  "express": "5.2.1",
60
62
  "husky": "9.1.7",
61
63
  "mocha": "11.7.6",
62
- "node-pluginsmanager-plugin": "7.3.2",
64
+ "node-pluginsmanager-plugin": "7.4.0",
63
65
  "nyc": "18.0.0",
64
66
  "proxyquire": "2.1.3",
65
67
  "rimraf": "6.1.3",