node-pluginsmanager 3.5.1 → 3.6.1

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(plugin: Orchestrator): 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,17 @@ class PluginsManager extends node_events_1.default {
267
270
  this.emit("allreleased", ...data);
268
271
  });
269
272
  }
273
+ getLatestGithubTag(plugin) {
274
+ return (0, checkOrchestrator_1.default)("getLatestGithubTag/plugin", plugin).then(() => {
275
+ const githubUserRepo = (0, parseGithubUserRepo_1.default)((0, extractGithub_1.default)(plugin));
276
+ if (!githubUserRepo) {
277
+ throw new Error("Plugin \"" + plugin.name + "\" has an invalid github project link");
278
+ }
279
+ return (0, getLatestGithubTag_1.default)(githubUserRepo.user, githubUserRepo.repo).then((tag) => {
280
+ return tag.name;
281
+ });
282
+ });
283
+ }
270
284
  // install a plugin via github repo, using "data" in arguments for "install" and "init" plugin's Orchestrator methods
271
285
  installViaGithub(user, repo, ...data) {
272
286
  return (0, checkAbsoluteDirectory_1.default)("installViaGithub/directory", this.directory).then(() => {
@@ -401,25 +415,40 @@ class PluginsManager extends node_events_1.default {
401
415
  let directory = "";
402
416
  let key = -1;
403
417
  // 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
- });
409
- }).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"));
418
+ return (0, checkOrchestrator_1.default)("updateViaGithub/plugin", plugin).then(() => {
419
+ key = this.getPluginsNames().findIndex((pluginName) => {
420
+ return pluginName === plugin.name;
414
421
  });
422
+ if (-1 >= key) {
423
+ throw new Error("Plugin \"" + plugin.name + "\" is not registered");
424
+ }
415
425
  // check plugin directory
416
426
  }).then(() => {
417
427
  return (0, checkAbsoluteDirectory_1.default)("updateViaGithub/directory", this.directory).then(() => {
418
428
  directory = (0, node_path_1.join)(this.directory, plugin.name);
419
429
  return (0, checkAbsoluteDirectory_1.default)("updateViaGithub/plugindirectory", directory);
420
430
  });
421
- // release plugin
431
+ // check remote version
422
432
  }).then(() => {
433
+ const currentVersion = semver_1.default.coerce(plugin.version);
434
+ if (!currentVersion) {
435
+ throw new Error("Plugin \"" + plugin.name + "\" has no valid version (\"" + plugin.version + "\")");
436
+ }
437
+ this._logger?.("debug", "Get github latest tag", false, plugin.name);
438
+ return this.getLatestGithubTag(plugin).then((tag) => {
439
+ const latestVersion = semver_1.default.coerce(tag);
440
+ if (!latestVersion) {
441
+ throw new Error("No valid version found for plugin \"" + plugin.name + "\" on github");
442
+ }
443
+ this._logger?.("debug", "Compare local version with github lastest tag", false, plugin.name);
444
+ if (!semver_1.default.gt(latestVersion, currentVersion)) {
445
+ throw new Error("Plugin \"" + plugin.name + "\" is already up to date (v" + plugin.version + ")");
446
+ }
447
+ this._logger?.("success", "New version available", false, plugin.name);
448
+ return tag;
449
+ });
450
+ // release plugin
451
+ }).then((latestTag) => {
423
452
  const pluginName = plugin.name;
424
453
  return plugin.release(...data).then(() => {
425
454
  this.emit("released", plugin, ...data);
@@ -427,16 +456,19 @@ class PluginsManager extends node_events_1.default {
427
456
  }).then(() => {
428
457
  this.emit("destroyed", pluginName, ...data);
429
458
  this.plugins.splice(key, 1);
459
+ return latestTag;
430
460
  });
431
- // download plugin
432
- }).then(() => {
433
- return (0, gitUpdate_1.default)(directory).then(() => {
461
+ // update plugin
462
+ }).then((latestTag) => {
463
+ this._logger?.("debug", "Update with new plugin version", false, plugin.name);
464
+ return (0, gitUpdate_1.default)(directory, latestTag).then(() => {
434
465
  return (0, createPluginByDirectory_1.default)(directory, this.externalResourcesDirectory, this._logger, ...data);
435
466
  });
436
467
  // check plugin modules versions
437
468
  }).then((_plugin) => {
469
+ this._logger?.("debug", "Check modules", false, plugin.name);
438
470
  return this.checkModules(_plugin).then(() => {
439
- return Promise.resolve(_plugin);
471
+ return _plugin;
440
472
  });
441
473
  }).then((_plugin) => {
442
474
  // update dependencies & execute update script
@@ -451,7 +483,7 @@ class PluginsManager extends node_events_1.default {
451
483
  }).then(() => {
452
484
  this.emit("initialized", _plugin, ...data);
453
485
  this.plugins[key] = _plugin;
454
- return Promise.resolve(_plugin);
486
+ return _plugin;
455
487
  });
456
488
  });
457
489
  }
@@ -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.1",
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",