Allow extensions to specify minimum ST client version (#4350) * allow extensions to specify minimum client version * using existing version compare function * Move versionCompare to utils module --------- Co-authored-by: qvink <qvink@users.noreply.github.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
Signed| @@ -1,10 +1,10 @@ | |||
| 1 | import { DOMPurify, Popper } from '../lib.js'; | 1 | import { DOMPurify, Popper } from '../lib.js'; |
| 2 | 2 | ||
| 3 | import { eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders, animation_duration } from '../script.js'; | 3 | import { eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders, animation_duration, CLIENT_VERSION } from '../script.js'; |
| 4 | import { showLoader } from './loader.js'; | 4 | import { showLoader } from './loader.js'; |
| 5 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; | 5 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; |
| 6 | import { renderTemplate, renderTemplateAsync } from './templates.js'; | 6 | import { renderTemplate, renderTemplateAsync } from './templates.js'; |
| 7 | import { delay, isSubsetOf, sanitizeSelector, setValueByPath } from './utils.js'; | 7 | import { delay, isSubsetOf, sanitizeSelector, setValueByPath, versionCompare } from './utils.js'; |
| 8 | import { getContext } from './st-context.js'; | 8 | import { getContext } from './st-context.js'; |
| 9 | import { isAdmin } from './user.js'; | 9 | import { isAdmin } from './user.js'; |
| 10 | import { addLocaleData, getCurrentLocale, t } from './i18n.js'; | 10 | import { addLocaleData, getCurrentLocale, t } from './i18n.js'; |
| @@ -387,6 +387,7 @@ async function getManifests(names) { | |||
| 387 | */ | 387 | */ |
| 388 | async function activateExtensions() { | 388 | async function activateExtensions() { |
| 389 | extensionLoadErrors.clear(); | 389 | extensionLoadErrors.clear(); |
| 390 | const clientVersion = CLIENT_VERSION.split(':')[1]; | ||
| 390 | const extensions = Object.entries(manifests).sort((a, b) => sortManifestsByOrder(a[1], b[1])); | 391 | const extensions = Object.entries(manifests).sort((a, b) => sortManifestsByOrder(a[1], b[1])); |
| 391 | const extensionNames = extensions.map(x => x[0]); | 392 | const extensionNames = extensions.map(x => x[0]); |
| 392 | const promises = []; | 393 | const promises = []; |
| @@ -396,11 +397,17 @@ async function activateExtensions() { | |||
| 396 | const manifest = entry[1]; | 397 | const manifest = entry[1]; |
| 397 | const extrasRequirements = manifest.requires; | 398 | const extrasRequirements = manifest.requires; |
| 398 | const extensionDependencies = manifest.dependencies; | 399 | const extensionDependencies = manifest.dependencies; |
| 400 | const minClientVersion = manifest.minimum_client_version; | ||
| 399 | const displayName = manifest.display_name || name; | 401 | const displayName = manifest.display_name || name; |
| 400 | 402 | ||
| 401 | if (activeExtensions.has(name)) { | 403 | if (activeExtensions.has(name)) { |
| 402 | continue; | 404 | continue; |
| 403 | } | 405 | } |
| 406 | // Client version requirement: pass if 'minimum_client_version' is undefined or null. | ||
| 407 | let meetsClientMinimumVersion = true; | ||
| 408 | if (minClientVersion !== undefined) { | ||
| 409 | meetsClientMinimumVersion = versionCompare(clientVersion, minClientVersion); | ||
| 410 | } | ||
| 404 | 411 | ||
| 405 | // Module requirements: pass if 'requires' is undefined, null, or not an array; check subset if it's an array | 412 | // Module requirements: pass if 'requires' is undefined, null, or not an array; check subset if it's an array |
| 406 | let meetsModuleRequirements = true; | 413 | let meetsModuleRequirements = true; |
| @@ -438,7 +445,7 @@ async function activateExtensions() { | |||
| 438 | 445 | ||
| 439 | const isDisabled = extension_settings.disabledExtensions.includes(name); | 446 | const isDisabled = extension_settings.disabledExtensions.includes(name); |
| 440 | 447 | ||
| 441 | if (meetsModuleRequirements && meetsExtensionDeps && !isDisabled) { | 448 | if (meetsModuleRequirements && meetsExtensionDeps && meetsClientMinimumVersion && !isDisabled) { |
| 442 | try { | 449 | try { |
| 443 | console.debug('Activating extension', name); | 450 | console.debug('Activating extension', name); |
| 444 | const promise = addExtensionLocale(name, manifest).finally(() => | 451 | const promise = addExtensionLocale(name, manifest).finally(() => |
| @@ -465,6 +472,9 @@ async function activateExtensions() { | |||
| 465 | console.warn(t`Extension "${name}" did not load. Missing required extensions: "${missingDependencies.join(', ')}"`); | 472 | console.warn(t`Extension "${name}" did not load. Missing required extensions: "${missingDependencies.join(', ')}"`); |
| 466 | extensionLoadErrors.add(t`Extension "${displayName}" did not load. Missing required extensions: "${missingDependencies.join(', ')}"`); | 473 | extensionLoadErrors.add(t`Extension "${displayName}" did not load. Missing required extensions: "${missingDependencies.join(', ')}"`); |
| 467 | } | 474 | } |
| 475 | } else if (!meetsClientMinimumVersion && !isDisabled) { | ||
| 476 | console.warn(t`Extension "${name}" did not load. Requires ST client version ${minClientVersion}, but current version is ${clientVersion}.`); | ||
| 477 | extensionLoadErrors.add(t`Extension "${displayName}" did not load. Requires ST client version ${minClientVersion}, but current version is ${clientVersion}.`); | ||
| 468 | } | 478 | } |
| 469 | } | 479 | } |
| 470 | 480 | ||
| @@ -20,7 +20,7 @@ import { | |||
| 20 | power_user, | 20 | power_user, |
| 21 | } from './power-user.js'; | 21 | } from './power-user.js'; |
| 22 | import { getEventSourceStream } from './sse-stream.js'; | 22 | import { getEventSourceStream } from './sse-stream.js'; |
| 23 | import { getSortableDelay } from './utils.js'; | 23 | import { getSortableDelay, versionCompare } from './utils.js'; |
| 24 | 24 | ||
| 25 | export let koboldai_settings; | 25 | export let koboldai_settings; |
| 26 | export let koboldai_setting_names; | 26 | export let koboldai_setting_names; |
| @@ -389,16 +389,6 @@ export function setKoboldFlags(koboldUnitedVersion, koboldCppVersion) { | |||
| 389 | } | 389 | } |
| 390 | 390 | ||
| 391 | /** | 391 | /** |
| 392 | * Compares two version numbers, returning true if srcVersion >= minVersion | ||
| 393 | * @param {string} srcVersion The current version. | ||
| 394 | * @param {string} minVersion The target version number to test against | ||
| 395 | * @returns {boolean} True if srcVersion >= minVersion, false if not | ||
| 396 | */ | ||
| 397 | function versionCompare(srcVersion, minVersion) { | ||
| 398 | return (srcVersion || '0.0.0').localeCompare(minVersion, undefined, { numeric: true, sensitivity: 'base' }) > -1; | ||
| 399 | } | ||
| 400 | |||
| 401 | /** | ||
| 402 | * Sorts the sampler items by the given order. | 392 | * Sorts the sampler items by the given order. |
| 403 | * @param {any[]} orderArray Sampler order array. | 393 | * @param {any[]} orderArray Sampler order array. |
| 404 | */ | 394 | */ |
| @@ -2550,3 +2550,13 @@ export function textValueMatcher(params, data) { | |||
| 2550 | // If it doesn't contain the term, don't return anything | 2550 | // If it doesn't contain the term, don't return anything |
| 2551 | return null; | 2551 | return null; |
| 2552 | } | 2552 | } |
| 2553 | |||
| 2554 | /** | ||
| 2555 | * Compares two version numbers, returning true if srcVersion >= minVersion | ||
| 2556 | * @param {string} srcVersion The current version. | ||
| 2557 | * @param {string} minVersion The target version number to test against | ||
| 2558 | * @returns {boolean} True if srcVersion >= minVersion, false if not | ||
| 2559 | */ | ||
| 2560 | export function versionCompare(srcVersion, minVersion) { | ||
| 2561 | return (srcVersion || '0.0.0').localeCompare(minVersion, undefined, { numeric: true, sensitivity: 'base' }) > -1; | ||
| 2562 | } | ||