Skip to content

Commit 7db7c02

Browse files
committed
fix: better version checking for plugins
1 parent f8f724c commit 7db7c02

File tree

3 files changed

+173
-7
lines changed

3 files changed

+173
-7
lines changed

lib/ts/superTokens.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ import {
4242
redirectWithNavigate,
4343
} from "./utils";
4444
import { package_version } from "./version";
45+
import { isVersionCompatible } from "./versionChecker";
46+
import { package_version as webjs_package_version } from "supertokens-web-js/lib/ts/version";
4547

4648
import type RecipeModule from "./recipe/recipeModule";
4749
import type { BaseRecipeModule } from "./recipe/recipeModule/baseRecipeModule";
@@ -218,14 +220,22 @@ export default class SuperTokens {
218220
if (config.experimental?.plugins) {
219221
for (const plugin of config.experimental.plugins) {
220222
if (plugin.compatibleAuthReactSDKVersions) {
221-
const versionContraints = Array.isArray(plugin.compatibleAuthReactSDKVersions)
222-
? plugin.compatibleAuthReactSDKVersions
223-
: [plugin.compatibleAuthReactSDKVersions];
224-
if (!versionContraints.includes(package_version)) {
225-
// TODO: better checks
223+
const versionCheck = isVersionCompatible(package_version, plugin.compatibleAuthReactSDKVersions);
224+
if (!versionCheck) {
226225
throw new Error(
227-
`Plugin version mismatch. Version ${package_version} not found in compatible versions: ${versionContraints.join(
228-
", "
226+
`Plugin AuthReact SDK version mismatch. Version ${package_version} not found in compatible versions: ${JSON.stringify(
227+
plugin.compatibleAuthReactSDKVersions
228+
)}`
229+
);
230+
}
231+
}
232+
233+
if (plugin.compatibleWebJSSDKVersions) {
234+
const versionCheck = isVersionCompatible(webjs_package_version, plugin.compatibleWebJSSDKVersions);
235+
if (!versionCheck) {
236+
throw new Error(
237+
`Plugin WebJS SDK version mismatch. Version ${webjs_package_version} not found in compatible versions: ${JSON.stringify(
238+
plugin.compatibleWebJSSDKVersions
229239
)}`
230240
);
231241
}

lib/ts/versionChecker.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
const parseVersion = (version: string): { major: number; minor: number; patch: number; prerelease?: string } => {
2+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
3+
if (!match) {
4+
throw new Error(`Invalid version format: ${version}`);
5+
}
6+
7+
return {
8+
major: parseInt(match[1]),
9+
minor: parseInt(match[2]),
10+
patch: parseInt(match[3]),
11+
prerelease: match[4],
12+
};
13+
};
14+
15+
const compareVersions = (a: ReturnType<typeof parseVersion>, b: ReturnType<typeof parseVersion>): number => {
16+
if (a.major !== b.major) return a.major - b.major;
17+
if (a.minor !== b.minor) return a.minor - b.minor;
18+
if (a.patch !== b.patch) return a.patch - b.patch;
19+
20+
// canary versions
21+
if (a.prerelease && !b.prerelease) return -1;
22+
if (!a.prerelease && b.prerelease) return 1;
23+
if (a.prerelease && b.prerelease) {
24+
return a.prerelease.localeCompare(b.prerelease);
25+
}
26+
27+
return 0;
28+
};
29+
30+
// checks if a version satisfies a range constraint. supports: ">=0.49.0", "0.49.x", "~0.49.0", "^0.49.0", "0.49.1"
31+
const satisfiesRange = (version: string, range: string): boolean => {
32+
const parsedVersion = parseVersion(version);
33+
34+
if (range === version) return true;
35+
36+
const rangeMatch = range.match(/^([<>=~^]+)\s*(.+)$/);
37+
if (rangeMatch) {
38+
const operator = rangeMatch[1];
39+
const rangeVersion = rangeMatch[2];
40+
const parsedRangeVersion = parseVersion(rangeVersion);
41+
const comparison = compareVersions(parsedVersion, parsedRangeVersion);
42+
43+
switch (operator) {
44+
case ">=":
45+
return comparison >= 0;
46+
case ">":
47+
return comparison > 0;
48+
case "<=":
49+
return comparison <= 0;
50+
case "<":
51+
return comparison < 0;
52+
case "=":
53+
case "==":
54+
return comparison === 0;
55+
case "~":
56+
return (
57+
parsedVersion.major === parsedRangeVersion.major &&
58+
parsedVersion.minor === parsedRangeVersion.minor &&
59+
parsedVersion.patch >= parsedRangeVersion.patch
60+
);
61+
case "^":
62+
if (parsedRangeVersion.major === 0) {
63+
return (
64+
parsedVersion.major === 0 &&
65+
parsedVersion.minor === parsedRangeVersion.minor &&
66+
parsedVersion.patch >= parsedRangeVersion.patch
67+
);
68+
} else {
69+
return (
70+
parsedVersion.major === parsedRangeVersion.major &&
71+
parsedVersion.minor >= parsedRangeVersion.minor
72+
);
73+
}
74+
default:
75+
return false;
76+
}
77+
}
78+
79+
// x-ranges like "0.49.x"
80+
const xRangeMatch = range.match(/^(\d+)\.(\d+)\.x$/);
81+
if (xRangeMatch) {
82+
return (
83+
parsedVersion.major === parseInt(xRangeMatch[1], 10) && parsedVersion.minor === parseInt(xRangeMatch[2], 10)
84+
);
85+
}
86+
87+
// wildcard ranges like "0.x"
88+
const wildcardMatch = range.match(/^(\d+)\.x$/);
89+
if (wildcardMatch) {
90+
return parsedVersion.major === parseInt(wildcardMatch[1], 10);
91+
}
92+
93+
const exactRangeVersion = parseVersion(range);
94+
return compareVersions(parsedVersion, exactRangeVersion) === 0;
95+
};
96+
97+
export const isVersionCompatible = (currentVersion: string, constraints: string | string[]): boolean => {
98+
const constraintArray = Array.isArray(constraints) ? constraints : [constraints];
99+
100+
for (const constraint of constraintArray) {
101+
if (satisfiesRange(currentVersion, constraint)) {
102+
return true;
103+
}
104+
}
105+
106+
return false;
107+
};

test/unit/versionChecker.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/* Copyright (c) 2025, VRAI Labs and/or its affiliates. All rights reserved.
2+
*
3+
* This software is licensed under the Apache License, Version 2.0 (the
4+
* "License") as published by the Apache Software Foundation.
5+
*
6+
* You may not use this file except in compliance with the License. You may
7+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
* License for the specific language governing permissions and limitations
13+
* under the License.
14+
*/
15+
16+
const assert = require("assert");
17+
import { isVersionCompatible } from "../../lib/ts/versionChecker";
18+
19+
// Test cases
20+
const testCases = [
21+
{ version: "0.49.1", constraint: "0.49.1", expected: true },
22+
{ version: "0.49.1", constraint: ">=0.49.0", expected: true },
23+
{ version: "0.49.1", constraint: ">0.49.0", expected: true },
24+
{ version: "0.49.1", constraint: "<=0.49.1", expected: true },
25+
{ version: "0.49.1", constraint: "<0.50.0", expected: true },
26+
{ version: "0.49.1", constraint: "=0.49.1", expected: true },
27+
{ version: "0.49.2", constraint: "~0.49.0", expected: true },
28+
{ version: "0.49.5", constraint: "~0.49.0", expected: true },
29+
{ version: "0.50.0", constraint: "~0.49.0", expected: false },
30+
{ version: "0.49.5", constraint: "^0.49.0", expected: true },
31+
{ version: "0.50.0", constraint: "^0.49.0", expected: false },
32+
{ version: "0.49.1", constraint: "0.49.x", expected: true },
33+
{ version: "0.49.5", constraint: "0.49.x", expected: true },
34+
{ version: "0.50.0", constraint: "0.49.x", expected: false },
35+
{ version: "0.49.1", constraint: "0.x", expected: true },
36+
{ version: "0.50.0", constraint: "0.x", expected: true },
37+
{ version: "1.0.0", constraint: "0.x", expected: false },
38+
{ version: "0.49.1", constraint: ["0.49.0", "0.49.1", "0.49.2"], expected: true },
39+
{ version: "0.49.1", constraint: ["0.48.0", "0.50.0"], expected: false },
40+
];
41+
42+
describe("versionChecker tests", function () {
43+
it("check version compatibility", function () {
44+
for (const testCase of testCases) {
45+
const result = isVersionCompatible(testCase.version, testCase.constraint);
46+
assert.strictEqual(result, testCase.expected);
47+
}
48+
});
49+
});

0 commit comments

Comments
 (0)