@usertour/helpers 0.0.2 → 0.0.3

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.
@@ -0,0 +1,112 @@
1
+ import {
2
+ __publicField
3
+ } from "./chunk-XEO3YXBM.js";
4
+
5
+ // src/jwt-license-signer.ts
6
+ import * as fs from "fs";
7
+ import * as path from "path";
8
+ import * as jwt from "jsonwebtoken";
9
+ var JWTLicenseSigner = class {
10
+ constructor(options) {
11
+ __publicField(this, "privateKey");
12
+ __publicField(this, "issuer");
13
+ __publicField(this, "algorithm");
14
+ this.privateKey = this.loadPrivateKey(options.privateKeyPath);
15
+ this.issuer = options.issuer || "https://www.usertour.io";
16
+ this.algorithm = options.algorithm || "RS256";
17
+ }
18
+ /**
19
+ * Load RSA private key from file
20
+ */
21
+ loadPrivateKey(keyPath) {
22
+ try {
23
+ const fullPath = path.resolve(keyPath);
24
+ return fs.readFileSync(fullPath, "utf8");
25
+ } catch (error) {
26
+ throw new Error(`Failed to load private key from ${keyPath}: ${error}`);
27
+ }
28
+ }
29
+ /**
30
+ * Generate a JWT license token
31
+ */
32
+ generateLicense(options) {
33
+ const now = Math.floor(Date.now() / 1e3);
34
+ const expiresAt = now + options.expiresInDays * 24 * 60 * 60;
35
+ const payload = {
36
+ plan: options.plan,
37
+ sub: options.subject,
38
+ projectId: options.projectId,
39
+ iat: now,
40
+ exp: expiresAt,
41
+ issuer: options.issuer || this.issuer,
42
+ features: options.features
43
+ };
44
+ try {
45
+ return jwt.sign(payload, this.privateKey, {
46
+ algorithm: this.algorithm,
47
+ issuer: this.issuer
48
+ });
49
+ } catch (error) {
50
+ throw new Error(`Failed to generate JWT license: ${error}`);
51
+ }
52
+ }
53
+ /**
54
+ * Generate a license and return both token and payload info
55
+ */
56
+ generateLicenseWithInfo(options) {
57
+ const now = Math.floor(Date.now() / 1e3);
58
+ const expiresAt = now + options.expiresInDays * 24 * 60 * 60;
59
+ const payload = {
60
+ plan: options.plan,
61
+ sub: options.subject,
62
+ projectId: options.projectId,
63
+ iat: now,
64
+ exp: expiresAt,
65
+ issuer: options.issuer || this.issuer,
66
+ features: options.features
67
+ };
68
+ const token = jwt.sign(payload, this.privateKey, {
69
+ algorithm: this.algorithm,
70
+ issuer: this.issuer
71
+ });
72
+ return {
73
+ token,
74
+ payload,
75
+ expiresAt: new Date(expiresAt * 1e3)
76
+ };
77
+ }
78
+ /**
79
+ * Decode a JWT token without verification (for debugging)
80
+ */
81
+ decodeToken(token) {
82
+ try {
83
+ return jwt.decode(token);
84
+ } catch (error) {
85
+ console.error("Failed to decode JWT token:", error);
86
+ return null;
87
+ }
88
+ }
89
+ /**
90
+ * Get token information without verification
91
+ */
92
+ getTokenInfo(token) {
93
+ try {
94
+ const decoded = jwt.decode(token, { complete: true });
95
+ if (!decoded || typeof decoded === "string") {
96
+ return null;
97
+ }
98
+ return {
99
+ header: decoded.header,
100
+ payload: decoded.payload,
101
+ signature: decoded.signature
102
+ };
103
+ } catch (error) {
104
+ console.error("Failed to get token info:", error);
105
+ return null;
106
+ }
107
+ }
108
+ };
109
+
110
+ export {
111
+ JWTLicenseSigner
112
+ };
@@ -0,0 +1,183 @@
1
+ // src/jwt-license-validator.ts
2
+ import * as jwt from "jsonwebtoken";
3
+ var JWTLicenseValidator = {
4
+ /**
5
+ * Validate a JWT license
6
+ * @param license - JWT license string
7
+ * @param publicKey - RSA public key in PEM format
8
+ * @param options - Validation options
9
+ * @returns Validation result
10
+ */
11
+ validateLicense(license, publicKey, options = {}) {
12
+ try {
13
+ const { checkExpiration = true, currentTime = /* @__PURE__ */ new Date() } = options;
14
+ const decoded = jwt.verify(license, publicKey, {
15
+ algorithms: ["RS256"],
16
+ ignoreExpiration: !checkExpiration
17
+ });
18
+ const fieldValidation = this.validateRequiredFields(decoded);
19
+ if (!fieldValidation.isValid) {
20
+ return fieldValidation;
21
+ }
22
+ if (checkExpiration) {
23
+ const expirationValidation = this.checkExpiration(decoded, currentTime);
24
+ if (!expirationValidation.isValid) {
25
+ return expirationValidation;
26
+ }
27
+ }
28
+ const hasFeature = (feature) => {
29
+ return decoded.features.includes("*") || decoded.features.includes(feature);
30
+ };
31
+ return {
32
+ isValid: true,
33
+ payload: decoded,
34
+ isExpired: false,
35
+ hasFeature
36
+ };
37
+ } catch (error) {
38
+ if (error instanceof jwt.JsonWebTokenError) {
39
+ return {
40
+ isValid: false,
41
+ error: `JWT validation failed: ${error.message}`
42
+ };
43
+ }
44
+ if (error instanceof jwt.TokenExpiredError) {
45
+ return {
46
+ isValid: false,
47
+ error: `License expired: ${error.message}`,
48
+ isExpired: true
49
+ };
50
+ }
51
+ return {
52
+ isValid: false,
53
+ error: `License validation failed: ${error instanceof Error ? error.message : "Unknown error"}`
54
+ };
55
+ }
56
+ },
57
+ /**
58
+ * Validate that all required fields are present in license payload
59
+ * @param payload - License payload to validate
60
+ * @returns Validation result
61
+ */
62
+ validateRequiredFields(payload) {
63
+ const requiredFields = ["plan", "sub", "projectId", "iat", "exp", "issuer", "features"];
64
+ for (const field of requiredFields) {
65
+ if (!(field in payload)) {
66
+ return {
67
+ isValid: false,
68
+ error: `Missing required field: ${field}`
69
+ };
70
+ }
71
+ }
72
+ if (typeof payload.plan !== "string" || !payload.plan.trim()) {
73
+ return {
74
+ isValid: false,
75
+ error: "Invalid plan: must be a non-empty string"
76
+ };
77
+ }
78
+ if (typeof payload.sub !== "string" || !payload.sub.trim()) {
79
+ return {
80
+ isValid: false,
81
+ error: "Invalid sub: must be a non-empty string"
82
+ };
83
+ }
84
+ if (typeof payload.projectId !== "string" || !payload.projectId.trim()) {
85
+ return {
86
+ isValid: false,
87
+ error: "Invalid projectId: must be a non-empty string"
88
+ };
89
+ }
90
+ if (typeof payload.issuer !== "string" || !payload.issuer.trim()) {
91
+ return {
92
+ isValid: false,
93
+ error: "Invalid issuer: must be a non-empty string"
94
+ };
95
+ }
96
+ if (!Array.isArray(payload.features)) {
97
+ return {
98
+ isValid: false,
99
+ error: "Invalid features: must be an array"
100
+ };
101
+ }
102
+ if (typeof payload.iat !== "number" || payload.iat <= 0) {
103
+ return {
104
+ isValid: false,
105
+ error: "Invalid iat: must be a positive number"
106
+ };
107
+ }
108
+ if (typeof payload.exp !== "number" || payload.exp <= 0) {
109
+ return {
110
+ isValid: false,
111
+ error: "Invalid exp: must be a positive number"
112
+ };
113
+ }
114
+ if (payload.iat >= payload.exp) {
115
+ return {
116
+ isValid: false,
117
+ error: "Invalid timestamps: iat must be before exp"
118
+ };
119
+ }
120
+ return { isValid: true };
121
+ },
122
+ /**
123
+ * Check if license has expired
124
+ * @param payload - License payload
125
+ * @param currentTime - Current time to check against (defaults to now)
126
+ * @returns Validation result
127
+ */
128
+ checkExpiration(payload, currentTime = /* @__PURE__ */ new Date()) {
129
+ const now = Math.floor(currentTime.getTime() / 1e3);
130
+ const expiresAt = payload.exp;
131
+ if (now > expiresAt) {
132
+ return {
133
+ isValid: false,
134
+ error: `License expired on ${new Date(expiresAt * 1e3).toISOString()}`,
135
+ isExpired: true
136
+ };
137
+ }
138
+ return { isValid: true, isExpired: false };
139
+ },
140
+ /**
141
+ * Check if license has a specific feature
142
+ * @param payload - License payload
143
+ * @param feature - Feature to check
144
+ * @returns Whether the feature is available
145
+ */
146
+ hasFeature(payload, feature) {
147
+ return payload.features.includes("*") || payload.features.includes(feature);
148
+ },
149
+ /**
150
+ * Get license expiration status
151
+ * @param payload - License payload
152
+ * @param currentTime - Current time to check against (defaults to now)
153
+ * @returns Expiration information
154
+ */
155
+ getExpirationInfo(payload, currentTime = /* @__PURE__ */ new Date()) {
156
+ const now = Math.floor(currentTime.getTime() / 1e3);
157
+ const expiresAt = payload.exp;
158
+ const isExpired = now > expiresAt;
159
+ const daysUntilExpiration = Math.ceil((expiresAt - now) / (24 * 60 * 60));
160
+ return {
161
+ isExpired,
162
+ expiresAt: new Date(expiresAt * 1e3),
163
+ daysUntilExpiration: isExpired ? 0 : daysUntilExpiration
164
+ };
165
+ },
166
+ /**
167
+ * Decode JWT license without verification (for debugging)
168
+ * @param license - JWT license string
169
+ * @returns Decoded payload or null if invalid
170
+ */
171
+ decodeLicense(license) {
172
+ try {
173
+ const decoded = jwt.decode(license);
174
+ return decoded;
175
+ } catch {
176
+ return null;
177
+ }
178
+ }
179
+ };
180
+
181
+ export {
182
+ JWTLicenseValidator
183
+ };
package/dist/index.cjs CHANGED
@@ -37,6 +37,8 @@ var src_exports = {};
37
37
  __export(src_exports, {
38
38
  AbortController: () => AbortController,
39
39
  ArrayProto: () => ArrayProto,
40
+ JWTLicenseSigner: () => JWTLicenseSigner,
41
+ JWTLicenseValidator: () => JWTLicenseValidator,
40
42
  XMLHttpRequest: () => XMLHttpRequest,
41
43
  absoluteUrl: () => absoluteUrl,
42
44
  assignableWindow: () => assignableWindow,
@@ -448,8 +450,8 @@ var parseUrl = (url) => {
448
450
  if (!urlPatterns) {
449
451
  return null;
450
452
  }
451
- const [, , scheme = "", domain = "", path = "", , query = "", fragment = ""] = urlPatterns;
452
- return { scheme, domain, path, query, fragment };
453
+ const [, , scheme = "", domain = "", path2 = "", , query = "", fragment = ""] = urlPatterns;
454
+ return { scheme, domain, path: path2, query, fragment };
453
455
  };
454
456
  var replaceWildcard = (input, s1, s2) => {
455
457
  const withSpecialWords = replaceSpecialWords(input);
@@ -471,11 +473,11 @@ var parsePattern = (pattern) => {
471
473
  console.error("Invalid URL pattern:", pattern);
472
474
  return null;
473
475
  }
474
- const { scheme, domain, path, query, fragment } = _pattern;
476
+ const { scheme, domain, path: path2, query, fragment } = _pattern;
475
477
  const _scheme = scheme ? replaceSpecialWords(scheme) : "[a-z\\d]+";
476
478
  const _domain = domain ? replaceWildcard(domain, "[^/]", ".") : "[^/]*";
477
479
  const _fragment = fragment ? replaceWildcard(fragment, ".", "/") : "(#.*)?";
478
- const _path = path ? replaceWildcard(path, "[^?#]", "/") : "/[^?#]*";
480
+ const _path = path2 ? replaceWildcard(path2, "[^?#]", "/") : "/[^?#]*";
479
481
  let _query = "(\\?[^#]*)?";
480
482
  if (query) {
481
483
  new URLSearchParams(query).forEach((value, key) => {
@@ -1038,8 +1040,8 @@ function formatDate(input) {
1038
1040
  year: "numeric"
1039
1041
  });
1040
1042
  }
1041
- function absoluteUrl(path) {
1042
- return `${path}`;
1043
+ function absoluteUrl(path2) {
1044
+ return `${path2}`;
1043
1045
  }
1044
1046
  var uuidV4 = () => {
1045
1047
  return (0, import_uuid.v4)();
@@ -1086,10 +1088,297 @@ var getRandomColor = () => {
1086
1088
  ];
1087
1089
  return colors[Math.floor(Math.random() * colors.length)];
1088
1090
  };
1091
+
1092
+ // src/jwt-license-signer.ts
1093
+ var fs = __toESM(require("fs"), 1);
1094
+ var path = __toESM(require("path"), 1);
1095
+ var jwt = __toESM(require("jsonwebtoken"), 1);
1096
+ var JWTLicenseSigner = class {
1097
+ constructor(options) {
1098
+ __publicField(this, "privateKey");
1099
+ __publicField(this, "issuer");
1100
+ __publicField(this, "algorithm");
1101
+ this.privateKey = this.loadPrivateKey(options.privateKeyPath);
1102
+ this.issuer = options.issuer || "https://www.usertour.io";
1103
+ this.algorithm = options.algorithm || "RS256";
1104
+ }
1105
+ /**
1106
+ * Load RSA private key from file
1107
+ */
1108
+ loadPrivateKey(keyPath) {
1109
+ try {
1110
+ const fullPath = path.resolve(keyPath);
1111
+ return fs.readFileSync(fullPath, "utf8");
1112
+ } catch (error) {
1113
+ throw new Error(`Failed to load private key from ${keyPath}: ${error}`);
1114
+ }
1115
+ }
1116
+ /**
1117
+ * Generate a JWT license token
1118
+ */
1119
+ generateLicense(options) {
1120
+ const now = Math.floor(Date.now() / 1e3);
1121
+ const expiresAt = now + options.expiresInDays * 24 * 60 * 60;
1122
+ const payload = {
1123
+ plan: options.plan,
1124
+ sub: options.subject,
1125
+ projectId: options.projectId,
1126
+ iat: now,
1127
+ exp: expiresAt,
1128
+ issuer: options.issuer || this.issuer,
1129
+ features: options.features
1130
+ };
1131
+ try {
1132
+ return jwt.sign(payload, this.privateKey, {
1133
+ algorithm: this.algorithm,
1134
+ issuer: this.issuer
1135
+ });
1136
+ } catch (error) {
1137
+ throw new Error(`Failed to generate JWT license: ${error}`);
1138
+ }
1139
+ }
1140
+ /**
1141
+ * Generate a license and return both token and payload info
1142
+ */
1143
+ generateLicenseWithInfo(options) {
1144
+ const now = Math.floor(Date.now() / 1e3);
1145
+ const expiresAt = now + options.expiresInDays * 24 * 60 * 60;
1146
+ const payload = {
1147
+ plan: options.plan,
1148
+ sub: options.subject,
1149
+ projectId: options.projectId,
1150
+ iat: now,
1151
+ exp: expiresAt,
1152
+ issuer: options.issuer || this.issuer,
1153
+ features: options.features
1154
+ };
1155
+ const token = jwt.sign(payload, this.privateKey, {
1156
+ algorithm: this.algorithm,
1157
+ issuer: this.issuer
1158
+ });
1159
+ return {
1160
+ token,
1161
+ payload,
1162
+ expiresAt: new Date(expiresAt * 1e3)
1163
+ };
1164
+ }
1165
+ /**
1166
+ * Decode a JWT token without verification (for debugging)
1167
+ */
1168
+ decodeToken(token) {
1169
+ try {
1170
+ return jwt.decode(token);
1171
+ } catch (error) {
1172
+ console.error("Failed to decode JWT token:", error);
1173
+ return null;
1174
+ }
1175
+ }
1176
+ /**
1177
+ * Get token information without verification
1178
+ */
1179
+ getTokenInfo(token) {
1180
+ try {
1181
+ const decoded = jwt.decode(token, { complete: true });
1182
+ if (!decoded || typeof decoded === "string") {
1183
+ return null;
1184
+ }
1185
+ return {
1186
+ header: decoded.header,
1187
+ payload: decoded.payload,
1188
+ signature: decoded.signature
1189
+ };
1190
+ } catch (error) {
1191
+ console.error("Failed to get token info:", error);
1192
+ return null;
1193
+ }
1194
+ }
1195
+ };
1196
+
1197
+ // src/jwt-license-validator.ts
1198
+ var jwt2 = __toESM(require("jsonwebtoken"), 1);
1199
+ var JWTLicenseValidator = {
1200
+ /**
1201
+ * Validate a JWT license
1202
+ * @param license - JWT license string
1203
+ * @param publicKey - RSA public key in PEM format
1204
+ * @param options - Validation options
1205
+ * @returns Validation result
1206
+ */
1207
+ validateLicense(license, publicKey, options = {}) {
1208
+ try {
1209
+ const { checkExpiration = true, currentTime = /* @__PURE__ */ new Date() } = options;
1210
+ const decoded = jwt2.verify(license, publicKey, {
1211
+ algorithms: ["RS256"],
1212
+ ignoreExpiration: !checkExpiration
1213
+ });
1214
+ const fieldValidation = this.validateRequiredFields(decoded);
1215
+ if (!fieldValidation.isValid) {
1216
+ return fieldValidation;
1217
+ }
1218
+ if (checkExpiration) {
1219
+ const expirationValidation = this.checkExpiration(decoded, currentTime);
1220
+ if (!expirationValidation.isValid) {
1221
+ return expirationValidation;
1222
+ }
1223
+ }
1224
+ const hasFeature = (feature) => {
1225
+ return decoded.features.includes("*") || decoded.features.includes(feature);
1226
+ };
1227
+ return {
1228
+ isValid: true,
1229
+ payload: decoded,
1230
+ isExpired: false,
1231
+ hasFeature
1232
+ };
1233
+ } catch (error) {
1234
+ if (error instanceof jwt2.JsonWebTokenError) {
1235
+ return {
1236
+ isValid: false,
1237
+ error: `JWT validation failed: ${error.message}`
1238
+ };
1239
+ }
1240
+ if (error instanceof jwt2.TokenExpiredError) {
1241
+ return {
1242
+ isValid: false,
1243
+ error: `License expired: ${error.message}`,
1244
+ isExpired: true
1245
+ };
1246
+ }
1247
+ return {
1248
+ isValid: false,
1249
+ error: `License validation failed: ${error instanceof Error ? error.message : "Unknown error"}`
1250
+ };
1251
+ }
1252
+ },
1253
+ /**
1254
+ * Validate that all required fields are present in license payload
1255
+ * @param payload - License payload to validate
1256
+ * @returns Validation result
1257
+ */
1258
+ validateRequiredFields(payload) {
1259
+ const requiredFields = ["plan", "sub", "projectId", "iat", "exp", "issuer", "features"];
1260
+ for (const field of requiredFields) {
1261
+ if (!(field in payload)) {
1262
+ return {
1263
+ isValid: false,
1264
+ error: `Missing required field: ${field}`
1265
+ };
1266
+ }
1267
+ }
1268
+ if (typeof payload.plan !== "string" || !payload.plan.trim()) {
1269
+ return {
1270
+ isValid: false,
1271
+ error: "Invalid plan: must be a non-empty string"
1272
+ };
1273
+ }
1274
+ if (typeof payload.sub !== "string" || !payload.sub.trim()) {
1275
+ return {
1276
+ isValid: false,
1277
+ error: "Invalid sub: must be a non-empty string"
1278
+ };
1279
+ }
1280
+ if (typeof payload.projectId !== "string" || !payload.projectId.trim()) {
1281
+ return {
1282
+ isValid: false,
1283
+ error: "Invalid projectId: must be a non-empty string"
1284
+ };
1285
+ }
1286
+ if (typeof payload.issuer !== "string" || !payload.issuer.trim()) {
1287
+ return {
1288
+ isValid: false,
1289
+ error: "Invalid issuer: must be a non-empty string"
1290
+ };
1291
+ }
1292
+ if (!Array.isArray(payload.features)) {
1293
+ return {
1294
+ isValid: false,
1295
+ error: "Invalid features: must be an array"
1296
+ };
1297
+ }
1298
+ if (typeof payload.iat !== "number" || payload.iat <= 0) {
1299
+ return {
1300
+ isValid: false,
1301
+ error: "Invalid iat: must be a positive number"
1302
+ };
1303
+ }
1304
+ if (typeof payload.exp !== "number" || payload.exp <= 0) {
1305
+ return {
1306
+ isValid: false,
1307
+ error: "Invalid exp: must be a positive number"
1308
+ };
1309
+ }
1310
+ if (payload.iat >= payload.exp) {
1311
+ return {
1312
+ isValid: false,
1313
+ error: "Invalid timestamps: iat must be before exp"
1314
+ };
1315
+ }
1316
+ return { isValid: true };
1317
+ },
1318
+ /**
1319
+ * Check if license has expired
1320
+ * @param payload - License payload
1321
+ * @param currentTime - Current time to check against (defaults to now)
1322
+ * @returns Validation result
1323
+ */
1324
+ checkExpiration(payload, currentTime = /* @__PURE__ */ new Date()) {
1325
+ const now = Math.floor(currentTime.getTime() / 1e3);
1326
+ const expiresAt = payload.exp;
1327
+ if (now > expiresAt) {
1328
+ return {
1329
+ isValid: false,
1330
+ error: `License expired on ${new Date(expiresAt * 1e3).toISOString()}`,
1331
+ isExpired: true
1332
+ };
1333
+ }
1334
+ return { isValid: true, isExpired: false };
1335
+ },
1336
+ /**
1337
+ * Check if license has a specific feature
1338
+ * @param payload - License payload
1339
+ * @param feature - Feature to check
1340
+ * @returns Whether the feature is available
1341
+ */
1342
+ hasFeature(payload, feature) {
1343
+ return payload.features.includes("*") || payload.features.includes(feature);
1344
+ },
1345
+ /**
1346
+ * Get license expiration status
1347
+ * @param payload - License payload
1348
+ * @param currentTime - Current time to check against (defaults to now)
1349
+ * @returns Expiration information
1350
+ */
1351
+ getExpirationInfo(payload, currentTime = /* @__PURE__ */ new Date()) {
1352
+ const now = Math.floor(currentTime.getTime() / 1e3);
1353
+ const expiresAt = payload.exp;
1354
+ const isExpired = now > expiresAt;
1355
+ const daysUntilExpiration = Math.ceil((expiresAt - now) / (24 * 60 * 60));
1356
+ return {
1357
+ isExpired,
1358
+ expiresAt: new Date(expiresAt * 1e3),
1359
+ daysUntilExpiration: isExpired ? 0 : daysUntilExpiration
1360
+ };
1361
+ },
1362
+ /**
1363
+ * Decode JWT license without verification (for debugging)
1364
+ * @param license - JWT license string
1365
+ * @returns Decoded payload or null if invalid
1366
+ */
1367
+ decodeLicense(license) {
1368
+ try {
1369
+ const decoded = jwt2.decode(license);
1370
+ return decoded;
1371
+ } catch {
1372
+ return null;
1373
+ }
1374
+ }
1375
+ };
1089
1376
  // Annotate the CommonJS export names for ESM import in node:
1090
1377
  0 && (module.exports = {
1091
1378
  AbortController,
1092
1379
  ArrayProto,
1380
+ JWTLicenseSigner,
1381
+ JWTLicenseValidator,
1093
1382
  XMLHttpRequest,
1094
1383
  absoluteUrl,
1095
1384
  assignableWindow,
package/dist/index.d.cts CHANGED
@@ -11,8 +11,11 @@ export { isPublishedAtLeastOneEnvironment, isPublishedInAllEnvironments } from '
11
11
  export { deepClone } from './utils.cjs';
12
12
  export { generateAutoStateColors, hexToHSLString, hexToRGBStr } from './color.cjs';
13
13
  export { absoluteUrl, cn, cuid, evalCode, formatDate, getRandomColor, hexToRgb, isDark, uuidV4 } from './helper.cjs';
14
+ export { GenerateLicenseOptions, JWTLicenseSigner, JWTLicenseSignerOptions } from './jwt-license-signer.cjs';
15
+ export { JWTLicenseValidator } from './jwt-license-validator.cjs';
14
16
  export { default as isEqual } from 'fast-deep-equal';
15
17
  import '@usertour/types';
16
18
  import './storage.cjs';
17
19
  import '@usertour/types/';
18
20
  import 'clsx';
21
+ import 'jsonwebtoken';
package/dist/index.d.ts CHANGED
@@ -11,8 +11,11 @@ export { isPublishedAtLeastOneEnvironment, isPublishedInAllEnvironments } from '
11
11
  export { deepClone } from './utils.js';
12
12
  export { generateAutoStateColors, hexToHSLString, hexToRGBStr } from './color.js';
13
13
  export { absoluteUrl, cn, cuid, evalCode, formatDate, getRandomColor, hexToRgb, isDark, uuidV4 } from './helper.js';
14
+ export { GenerateLicenseOptions, JWTLicenseSigner, JWTLicenseSignerOptions } from './jwt-license-signer.js';
15
+ export { JWTLicenseValidator } from './jwt-license-validator.js';
14
16
  export { default as isEqual } from 'fast-deep-equal';
15
17
  import '@usertour/types';
16
18
  import './storage.js';
17
19
  import '@usertour/types/';
18
20
  import 'clsx';
21
+ import 'jsonwebtoken';
package/dist/index.js CHANGED
@@ -15,6 +15,12 @@ import {
15
15
  import {
16
16
  isUrl
17
17
  } from "./chunk-ZNFXGN3M.js";
18
+ import {
19
+ JWTLicenseSigner
20
+ } from "./chunk-JQ4E3IDB.js";
21
+ import {
22
+ JWTLicenseValidator
23
+ } from "./chunk-KJJ6UD5L.js";
18
24
  import {
19
25
  defaultStep
20
26
  } from "./chunk-FW54TSA3.js";
@@ -103,6 +109,8 @@ import "./chunk-XEO3YXBM.js";
103
109
  export {
104
110
  AbortController,
105
111
  ArrayProto,
112
+ JWTLicenseSigner,
113
+ JWTLicenseValidator,
106
114
  XMLHttpRequest,
107
115
  absoluteUrl,
108
116
  assignableWindow,
@@ -1,111 +1,7 @@
1
1
  import {
2
- __publicField
3
- } from "./chunk-XEO3YXBM.js";
4
-
5
- // src/jwt-license-signer.ts
6
- import * as fs from "fs";
7
- import * as path from "path";
8
- import * as jwt from "jsonwebtoken";
9
- var JWTLicenseSigner = class {
10
- constructor(options) {
11
- __publicField(this, "privateKey");
12
- __publicField(this, "issuer");
13
- __publicField(this, "algorithm");
14
- this.privateKey = this.loadPrivateKey(options.privateKeyPath);
15
- this.issuer = options.issuer || "https://www.usertour.io";
16
- this.algorithm = options.algorithm || "RS256";
17
- }
18
- /**
19
- * Load RSA private key from file
20
- */
21
- loadPrivateKey(keyPath) {
22
- try {
23
- const fullPath = path.resolve(keyPath);
24
- return fs.readFileSync(fullPath, "utf8");
25
- } catch (error) {
26
- throw new Error(`Failed to load private key from ${keyPath}: ${error}`);
27
- }
28
- }
29
- /**
30
- * Generate a JWT license token
31
- */
32
- generateLicense(options) {
33
- const now = Math.floor(Date.now() / 1e3);
34
- const expiresAt = now + options.expiresInDays * 24 * 60 * 60;
35
- const payload = {
36
- plan: options.plan,
37
- sub: options.subject,
38
- projectId: options.projectId,
39
- iat: now,
40
- exp: expiresAt,
41
- issuer: options.issuer || this.issuer,
42
- features: options.features
43
- };
44
- try {
45
- return jwt.sign(payload, this.privateKey, {
46
- algorithm: this.algorithm,
47
- issuer: this.issuer
48
- });
49
- } catch (error) {
50
- throw new Error(`Failed to generate JWT license: ${error}`);
51
- }
52
- }
53
- /**
54
- * Generate a license and return both token and payload info
55
- */
56
- generateLicenseWithInfo(options) {
57
- const now = Math.floor(Date.now() / 1e3);
58
- const expiresAt = now + options.expiresInDays * 24 * 60 * 60;
59
- const payload = {
60
- plan: options.plan,
61
- sub: options.subject,
62
- projectId: options.projectId,
63
- iat: now,
64
- exp: expiresAt,
65
- issuer: options.issuer || this.issuer,
66
- features: options.features
67
- };
68
- const token = jwt.sign(payload, this.privateKey, {
69
- algorithm: this.algorithm,
70
- issuer: this.issuer
71
- });
72
- return {
73
- token,
74
- payload,
75
- expiresAt: new Date(expiresAt * 1e3)
76
- };
77
- }
78
- /**
79
- * Decode a JWT token without verification (for debugging)
80
- */
81
- decodeToken(token) {
82
- try {
83
- return jwt.decode(token);
84
- } catch (error) {
85
- console.error("Failed to decode JWT token:", error);
86
- return null;
87
- }
88
- }
89
- /**
90
- * Get token information without verification
91
- */
92
- getTokenInfo(token) {
93
- try {
94
- const decoded = jwt.decode(token, { complete: true });
95
- if (!decoded || typeof decoded === "string") {
96
- return null;
97
- }
98
- return {
99
- header: decoded.header,
100
- payload: decoded.payload,
101
- signature: decoded.signature
102
- };
103
- } catch (error) {
104
- console.error("Failed to get token info:", error);
105
- return null;
106
- }
107
- }
108
- };
2
+ JWTLicenseSigner
3
+ } from "./chunk-JQ4E3IDB.js";
4
+ import "./chunk-XEO3YXBM.js";
109
5
  export {
110
6
  JWTLicenseSigner
111
7
  };
@@ -1,184 +1,7 @@
1
+ import {
2
+ JWTLicenseValidator
3
+ } from "./chunk-KJJ6UD5L.js";
1
4
  import "./chunk-XEO3YXBM.js";
2
-
3
- // src/jwt-license-validator.ts
4
- import * as jwt from "jsonwebtoken";
5
- var JWTLicenseValidator = {
6
- /**
7
- * Validate a JWT license
8
- * @param license - JWT license string
9
- * @param publicKey - RSA public key in PEM format
10
- * @param options - Validation options
11
- * @returns Validation result
12
- */
13
- validateLicense(license, publicKey, options = {}) {
14
- try {
15
- const { checkExpiration = true, currentTime = /* @__PURE__ */ new Date() } = options;
16
- const decoded = jwt.verify(license, publicKey, {
17
- algorithms: ["RS256"],
18
- ignoreExpiration: !checkExpiration
19
- });
20
- const fieldValidation = this.validateRequiredFields(decoded);
21
- if (!fieldValidation.isValid) {
22
- return fieldValidation;
23
- }
24
- if (checkExpiration) {
25
- const expirationValidation = this.checkExpiration(decoded, currentTime);
26
- if (!expirationValidation.isValid) {
27
- return expirationValidation;
28
- }
29
- }
30
- const hasFeature = (feature) => {
31
- return decoded.features.includes("*") || decoded.features.includes(feature);
32
- };
33
- return {
34
- isValid: true,
35
- payload: decoded,
36
- isExpired: false,
37
- hasFeature
38
- };
39
- } catch (error) {
40
- if (error instanceof jwt.JsonWebTokenError) {
41
- return {
42
- isValid: false,
43
- error: `JWT validation failed: ${error.message}`
44
- };
45
- }
46
- if (error instanceof jwt.TokenExpiredError) {
47
- return {
48
- isValid: false,
49
- error: `License expired: ${error.message}`,
50
- isExpired: true
51
- };
52
- }
53
- return {
54
- isValid: false,
55
- error: `License validation failed: ${error instanceof Error ? error.message : "Unknown error"}`
56
- };
57
- }
58
- },
59
- /**
60
- * Validate that all required fields are present in license payload
61
- * @param payload - License payload to validate
62
- * @returns Validation result
63
- */
64
- validateRequiredFields(payload) {
65
- const requiredFields = ["plan", "sub", "projectId", "iat", "exp", "issuer", "features"];
66
- for (const field of requiredFields) {
67
- if (!(field in payload)) {
68
- return {
69
- isValid: false,
70
- error: `Missing required field: ${field}`
71
- };
72
- }
73
- }
74
- if (typeof payload.plan !== "string" || !payload.plan.trim()) {
75
- return {
76
- isValid: false,
77
- error: "Invalid plan: must be a non-empty string"
78
- };
79
- }
80
- if (typeof payload.sub !== "string" || !payload.sub.trim()) {
81
- return {
82
- isValid: false,
83
- error: "Invalid sub: must be a non-empty string"
84
- };
85
- }
86
- if (typeof payload.projectId !== "string" || !payload.projectId.trim()) {
87
- return {
88
- isValid: false,
89
- error: "Invalid projectId: must be a non-empty string"
90
- };
91
- }
92
- if (typeof payload.issuer !== "string" || !payload.issuer.trim()) {
93
- return {
94
- isValid: false,
95
- error: "Invalid issuer: must be a non-empty string"
96
- };
97
- }
98
- if (!Array.isArray(payload.features)) {
99
- return {
100
- isValid: false,
101
- error: "Invalid features: must be an array"
102
- };
103
- }
104
- if (typeof payload.iat !== "number" || payload.iat <= 0) {
105
- return {
106
- isValid: false,
107
- error: "Invalid iat: must be a positive number"
108
- };
109
- }
110
- if (typeof payload.exp !== "number" || payload.exp <= 0) {
111
- return {
112
- isValid: false,
113
- error: "Invalid exp: must be a positive number"
114
- };
115
- }
116
- if (payload.iat >= payload.exp) {
117
- return {
118
- isValid: false,
119
- error: "Invalid timestamps: iat must be before exp"
120
- };
121
- }
122
- return { isValid: true };
123
- },
124
- /**
125
- * Check if license has expired
126
- * @param payload - License payload
127
- * @param currentTime - Current time to check against (defaults to now)
128
- * @returns Validation result
129
- */
130
- checkExpiration(payload, currentTime = /* @__PURE__ */ new Date()) {
131
- const now = Math.floor(currentTime.getTime() / 1e3);
132
- const expiresAt = payload.exp;
133
- if (now > expiresAt) {
134
- return {
135
- isValid: false,
136
- error: `License expired on ${new Date(expiresAt * 1e3).toISOString()}`,
137
- isExpired: true
138
- };
139
- }
140
- return { isValid: true, isExpired: false };
141
- },
142
- /**
143
- * Check if license has a specific feature
144
- * @param payload - License payload
145
- * @param feature - Feature to check
146
- * @returns Whether the feature is available
147
- */
148
- hasFeature(payload, feature) {
149
- return payload.features.includes("*") || payload.features.includes(feature);
150
- },
151
- /**
152
- * Get license expiration status
153
- * @param payload - License payload
154
- * @param currentTime - Current time to check against (defaults to now)
155
- * @returns Expiration information
156
- */
157
- getExpirationInfo(payload, currentTime = /* @__PURE__ */ new Date()) {
158
- const now = Math.floor(currentTime.getTime() / 1e3);
159
- const expiresAt = payload.exp;
160
- const isExpired = now > expiresAt;
161
- const daysUntilExpiration = Math.ceil((expiresAt - now) / (24 * 60 * 60));
162
- return {
163
- isExpired,
164
- expiresAt: new Date(expiresAt * 1e3),
165
- daysUntilExpiration: isExpired ? 0 : daysUntilExpiration
166
- };
167
- },
168
- /**
169
- * Decode JWT license without verification (for debugging)
170
- * @param license - JWT license string
171
- * @returns Decoded payload or null if invalid
172
- */
173
- decodeLicense(license) {
174
- try {
175
- const decoded = jwt.decode(license);
176
- return decoded;
177
- } catch {
178
- return null;
179
- }
180
- }
181
- };
182
5
  export {
183
6
  JWTLicenseValidator
184
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usertour/helpers",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "description": "Utility functions and helpers shared across the UserTour project",
6
6
  "homepage": "https://www.usertour.io",