@licenseseat/js 0.1.0 → 0.2.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.
- package/README.md +674 -83
- package/dist/index.js +689 -1493
- package/dist/types/LicenseSeat.d.ts +288 -0
- package/dist/types/LicenseSeat.d.ts.map +1 -0
- package/dist/types/cache.d.ts +93 -0
- package/dist/types/cache.d.ts.map +1 -0
- package/dist/types/errors.d.ts +58 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/types.d.ts +321 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/utils.d.ts +54 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/package.json +44 -8
- package/src/LicenseSeat.js +1163 -0
- package/src/cache.js +189 -0
- package/src/errors.js +77 -0
- package/src/index.js +62 -0
- package/src/types.js +144 -0
- package/src/utils.js +156 -0
package/dist/index.js
CHANGED
|
@@ -1,1019 +1,347 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
-
};
|
|
1
|
+
// src/LicenseSeat.js
|
|
2
|
+
import * as ed from "@noble/ed25519";
|
|
3
|
+
import { sha512 } from "@noble/hashes/sha512";
|
|
6
4
|
|
|
7
|
-
//
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
sign: () => sign,
|
|
17
|
-
signAsync: () => signAsync,
|
|
18
|
-
utils: () => utils,
|
|
19
|
-
verify: () => verify,
|
|
20
|
-
verifyAsync: () => verifyAsync
|
|
21
|
-
});
|
|
22
|
-
var ed25519_CURVE = {
|
|
23
|
-
p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,
|
|
24
|
-
n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,
|
|
25
|
-
h: 8n,
|
|
26
|
-
a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,
|
|
27
|
-
d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,
|
|
28
|
-
Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,
|
|
29
|
-
Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n
|
|
30
|
-
};
|
|
31
|
-
var { p: P, n: N, Gx, Gy, a: _a, d: _d } = ed25519_CURVE;
|
|
32
|
-
var h = 8n;
|
|
33
|
-
var L = 32;
|
|
34
|
-
var L2 = 64;
|
|
35
|
-
var err = (m = "") => {
|
|
36
|
-
throw new Error(m);
|
|
37
|
-
};
|
|
38
|
-
var isBig = (n) => typeof n === "bigint";
|
|
39
|
-
var isStr = (s) => typeof s === "string";
|
|
40
|
-
var isBytes = (a) => a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
41
|
-
var abytes = (a, l) => !isBytes(a) || typeof l === "number" && l > 0 && a.length !== l ? err("Uint8Array expected") : a;
|
|
42
|
-
var u8n = (len) => new Uint8Array(len);
|
|
43
|
-
var u8fr = (buf) => Uint8Array.from(buf);
|
|
44
|
-
var padh = (n, pad) => n.toString(16).padStart(pad, "0");
|
|
45
|
-
var bytesToHex = (b) => Array.from(abytes(b)).map((e) => padh(e, 2)).join("");
|
|
46
|
-
var C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
|
|
47
|
-
var _ch = (ch) => {
|
|
48
|
-
if (ch >= C._0 && ch <= C._9)
|
|
49
|
-
return ch - C._0;
|
|
50
|
-
if (ch >= C.A && ch <= C.F)
|
|
51
|
-
return ch - (C.A - 10);
|
|
52
|
-
if (ch >= C.a && ch <= C.f)
|
|
53
|
-
return ch - (C.a - 10);
|
|
54
|
-
return;
|
|
55
|
-
};
|
|
56
|
-
var hexToBytes = (hex) => {
|
|
57
|
-
const e = "hex invalid";
|
|
58
|
-
if (!isStr(hex))
|
|
59
|
-
return err(e);
|
|
60
|
-
const hl = hex.length;
|
|
61
|
-
const al = hl / 2;
|
|
62
|
-
if (hl % 2)
|
|
63
|
-
return err(e);
|
|
64
|
-
const array = u8n(al);
|
|
65
|
-
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|
66
|
-
const n1 = _ch(hex.charCodeAt(hi));
|
|
67
|
-
const n2 = _ch(hex.charCodeAt(hi + 1));
|
|
68
|
-
if (n1 === void 0 || n2 === void 0)
|
|
69
|
-
return err(e);
|
|
70
|
-
array[ai] = n1 * 16 + n2;
|
|
71
|
-
}
|
|
72
|
-
return array;
|
|
73
|
-
};
|
|
74
|
-
var toU8 = (a, len) => abytes(isStr(a) ? hexToBytes(a) : u8fr(abytes(a)), len);
|
|
75
|
-
var cr = () => globalThis?.crypto;
|
|
76
|
-
var subtle = () => cr()?.subtle ?? err("crypto.subtle must be defined");
|
|
77
|
-
var concatBytes = (...arrs) => {
|
|
78
|
-
const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0));
|
|
79
|
-
let pad = 0;
|
|
80
|
-
arrs.forEach((a) => {
|
|
81
|
-
r.set(a, pad);
|
|
82
|
-
pad += a.length;
|
|
83
|
-
});
|
|
84
|
-
return r;
|
|
85
|
-
};
|
|
86
|
-
var randomBytes = (len = L) => {
|
|
87
|
-
const c = cr();
|
|
88
|
-
return c.getRandomValues(u8n(len));
|
|
89
|
-
};
|
|
90
|
-
var big = BigInt;
|
|
91
|
-
var arange = (n, min, max, msg = "bad number: out of range") => isBig(n) && min <= n && n < max ? n : err(msg);
|
|
92
|
-
var M = (a, b = P) => {
|
|
93
|
-
const r = a % b;
|
|
94
|
-
return r >= 0n ? r : b + r;
|
|
95
|
-
};
|
|
96
|
-
var modN = (a) => M(a, N);
|
|
97
|
-
var invert = (num, md) => {
|
|
98
|
-
if (num === 0n || md <= 0n)
|
|
99
|
-
err("no inverse n=" + num + " mod=" + md);
|
|
100
|
-
let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
|
|
101
|
-
while (a !== 0n) {
|
|
102
|
-
const q = b / a, r = b % a;
|
|
103
|
-
const m = x - u * q, n = y - v * q;
|
|
104
|
-
b = a, a = r, x = u, y = v, u = m, v = n;
|
|
105
|
-
}
|
|
106
|
-
return b === 1n ? M(x, md) : err("no inverse");
|
|
107
|
-
};
|
|
108
|
-
var callHash = (name) => {
|
|
109
|
-
const fn = etc[name];
|
|
110
|
-
if (typeof fn !== "function")
|
|
111
|
-
err("hashes." + name + " not set");
|
|
112
|
-
return fn;
|
|
113
|
-
};
|
|
114
|
-
var apoint = (p) => p instanceof Point ? p : err("Point expected");
|
|
115
|
-
var B256 = 2n ** 256n;
|
|
116
|
-
var Point = class _Point {
|
|
117
|
-
static BASE;
|
|
118
|
-
static ZERO;
|
|
119
|
-
ex;
|
|
120
|
-
ey;
|
|
121
|
-
ez;
|
|
122
|
-
et;
|
|
123
|
-
constructor(ex, ey, ez, et) {
|
|
124
|
-
const max = B256;
|
|
125
|
-
this.ex = arange(ex, 0n, max);
|
|
126
|
-
this.ey = arange(ey, 0n, max);
|
|
127
|
-
this.ez = arange(ez, 1n, max);
|
|
128
|
-
this.et = arange(et, 0n, max);
|
|
129
|
-
Object.freeze(this);
|
|
130
|
-
}
|
|
131
|
-
static fromAffine(p) {
|
|
132
|
-
return new _Point(p.x, p.y, 1n, M(p.x * p.y));
|
|
133
|
-
}
|
|
134
|
-
/** RFC8032 5.1.3: Uint8Array to Point. */
|
|
135
|
-
static fromBytes(hex, zip215 = false) {
|
|
136
|
-
const d = _d;
|
|
137
|
-
const normed = u8fr(abytes(hex, L));
|
|
138
|
-
const lastByte = hex[31];
|
|
139
|
-
normed[31] = lastByte & ~128;
|
|
140
|
-
const y = bytesToNumLE(normed);
|
|
141
|
-
const max = zip215 ? B256 : P;
|
|
142
|
-
arange(y, 0n, max);
|
|
143
|
-
const y2 = M(y * y);
|
|
144
|
-
const u = M(y2 - 1n);
|
|
145
|
-
const v = M(d * y2 + 1n);
|
|
146
|
-
let { isValid, value: x } = uvRatio(u, v);
|
|
147
|
-
if (!isValid)
|
|
148
|
-
err("bad point: y not sqrt");
|
|
149
|
-
const isXOdd = (x & 1n) === 1n;
|
|
150
|
-
const isLastByteOdd = (lastByte & 128) !== 0;
|
|
151
|
-
if (!zip215 && x === 0n && isLastByteOdd)
|
|
152
|
-
err("bad point: x==0, isLastByteOdd");
|
|
153
|
-
if (isLastByteOdd !== isXOdd)
|
|
154
|
-
x = M(-x);
|
|
155
|
-
return new _Point(x, y, 1n, M(x * y));
|
|
156
|
-
}
|
|
157
|
-
/** Checks if the point is valid and on-curve. */
|
|
158
|
-
assertValidity() {
|
|
159
|
-
const a = _a;
|
|
160
|
-
const d = _d;
|
|
161
|
-
const p = this;
|
|
162
|
-
if (p.is0())
|
|
163
|
-
throw new Error("bad point: ZERO");
|
|
164
|
-
const { ex: X, ey: Y, ez: Z, et: T } = p;
|
|
165
|
-
const X2 = M(X * X);
|
|
166
|
-
const Y2 = M(Y * Y);
|
|
167
|
-
const Z2 = M(Z * Z);
|
|
168
|
-
const Z4 = M(Z2 * Z2);
|
|
169
|
-
const aX2 = M(X2 * a);
|
|
170
|
-
const left = M(Z2 * M(aX2 + Y2));
|
|
171
|
-
const right = M(Z4 + M(d * M(X2 * Y2)));
|
|
172
|
-
if (left !== right)
|
|
173
|
-
throw new Error("bad point: equation left != right (1)");
|
|
174
|
-
const XY = M(X * Y);
|
|
175
|
-
const ZT = M(Z * T);
|
|
176
|
-
if (XY !== ZT)
|
|
177
|
-
throw new Error("bad point: equation left != right (2)");
|
|
178
|
-
return this;
|
|
179
|
-
}
|
|
180
|
-
/** Equality check: compare points P&Q. */
|
|
181
|
-
equals(other) {
|
|
182
|
-
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
|
183
|
-
const { ex: X2, ey: Y2, ez: Z2 } = apoint(other);
|
|
184
|
-
const X1Z2 = M(X1 * Z2);
|
|
185
|
-
const X2Z1 = M(X2 * Z1);
|
|
186
|
-
const Y1Z2 = M(Y1 * Z2);
|
|
187
|
-
const Y2Z1 = M(Y2 * Z1);
|
|
188
|
-
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
|
189
|
-
}
|
|
190
|
-
is0() {
|
|
191
|
-
return this.equals(I);
|
|
192
|
-
}
|
|
193
|
-
/** Flip point over y coordinate. */
|
|
194
|
-
negate() {
|
|
195
|
-
return new _Point(M(-this.ex), this.ey, this.ez, M(-this.et));
|
|
196
|
-
}
|
|
197
|
-
/** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */
|
|
198
|
-
double() {
|
|
199
|
-
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
|
200
|
-
const a = _a;
|
|
201
|
-
const A = M(X1 * X1);
|
|
202
|
-
const B = M(Y1 * Y1);
|
|
203
|
-
const C2 = M(2n * M(Z1 * Z1));
|
|
204
|
-
const D = M(a * A);
|
|
205
|
-
const x1y1 = X1 + Y1;
|
|
206
|
-
const E = M(M(x1y1 * x1y1) - A - B);
|
|
207
|
-
const G2 = D + B;
|
|
208
|
-
const F = G2 - C2;
|
|
209
|
-
const H = D - B;
|
|
210
|
-
const X3 = M(E * F);
|
|
211
|
-
const Y3 = M(G2 * H);
|
|
212
|
-
const T3 = M(E * H);
|
|
213
|
-
const Z3 = M(F * G2);
|
|
214
|
-
return new _Point(X3, Y3, Z3, T3);
|
|
5
|
+
// src/cache.js
|
|
6
|
+
var LicenseCache = class {
|
|
7
|
+
/**
|
|
8
|
+
* Create a LicenseCache instance
|
|
9
|
+
* @param {string} [prefix="licenseseat_"] - Prefix for all localStorage keys
|
|
10
|
+
*/
|
|
11
|
+
constructor(prefix = "licenseseat_") {
|
|
12
|
+
this.prefix = prefix;
|
|
13
|
+
this.publicKeyCacheKey = this.prefix + "public_keys";
|
|
215
14
|
}
|
|
216
|
-
/**
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const G2 = M(D + C2);
|
|
229
|
-
const H = M(B - a * A);
|
|
230
|
-
const X3 = M(E * F);
|
|
231
|
-
const Y3 = M(G2 * H);
|
|
232
|
-
const T3 = M(E * H);
|
|
233
|
-
const Z3 = M(F * G2);
|
|
234
|
-
return new _Point(X3, Y3, Z3, T3);
|
|
15
|
+
/**
|
|
16
|
+
* Get the cached license data
|
|
17
|
+
* @returns {import('./types.js').CachedLicense|null} Cached license or null if not found
|
|
18
|
+
*/
|
|
19
|
+
getLicense() {
|
|
20
|
+
try {
|
|
21
|
+
const data = localStorage.getItem(this.prefix + "license");
|
|
22
|
+
return data ? JSON.parse(data) : null;
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.error("Failed to read license cache:", e);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
235
27
|
}
|
|
236
28
|
/**
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
* @param n scalar by which point is multiplied
|
|
241
|
-
* @param safe safe mode guards against timing attacks; unsafe mode is faster
|
|
29
|
+
* Store license data in cache
|
|
30
|
+
* @param {import('./types.js').CachedLicense} data - License data to cache
|
|
31
|
+
* @returns {void}
|
|
242
32
|
*/
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return this;
|
|
249
|
-
if (this.equals(G))
|
|
250
|
-
return wNAF(n).p;
|
|
251
|
-
let p = I;
|
|
252
|
-
let f = G;
|
|
253
|
-
for (let d = this; n > 0n; d = d.double(), n >>= 1n) {
|
|
254
|
-
if (n & 1n)
|
|
255
|
-
p = p.add(d);
|
|
256
|
-
else if (safe)
|
|
257
|
-
f = f.add(d);
|
|
33
|
+
setLicense(data) {
|
|
34
|
+
try {
|
|
35
|
+
localStorage.setItem(this.prefix + "license", JSON.stringify(data));
|
|
36
|
+
} catch (e) {
|
|
37
|
+
console.error("Failed to cache license:", e);
|
|
258
38
|
}
|
|
259
|
-
return p;
|
|
260
39
|
}
|
|
261
|
-
/**
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Update the validation data for the cached license
|
|
42
|
+
* @param {import('./types.js').ValidationResult} validationData - Validation result to store
|
|
43
|
+
* @returns {void}
|
|
44
|
+
*/
|
|
45
|
+
updateValidation(validationData) {
|
|
46
|
+
const license = this.getLicense();
|
|
47
|
+
if (license) {
|
|
48
|
+
license.validation = validationData;
|
|
49
|
+
license.last_validated = (/* @__PURE__ */ new Date()).toISOString();
|
|
50
|
+
this.setLicense(license);
|
|
51
|
+
}
|
|
270
52
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Get the device identifier from the cached license
|
|
55
|
+
* @returns {string|null} Device identifier or null if not found
|
|
56
|
+
*/
|
|
57
|
+
getDeviceId() {
|
|
58
|
+
const license = this.getLicense();
|
|
59
|
+
return license ? license.device_identifier : null;
|
|
276
60
|
}
|
|
277
|
-
|
|
278
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Clear the cached license data
|
|
63
|
+
* @returns {void}
|
|
64
|
+
*/
|
|
65
|
+
clearLicense() {
|
|
66
|
+
localStorage.removeItem(this.prefix + "license");
|
|
279
67
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Get the cached offline license
|
|
70
|
+
* @returns {import('./types.js').SignedOfflineLicense|null} Offline license or null if not found
|
|
71
|
+
*/
|
|
72
|
+
getOfflineLicense() {
|
|
73
|
+
try {
|
|
74
|
+
const data = localStorage.getItem(this.prefix + "offline_license");
|
|
75
|
+
return data ? JSON.parse(data) : null;
|
|
76
|
+
} catch (e) {
|
|
77
|
+
console.error("Failed to read offline license cache:", e);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
283
80
|
}
|
|
284
|
-
|
|
285
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Store offline license data in cache
|
|
83
|
+
* @param {import('./types.js').SignedOfflineLicense} data - Signed offline license to cache
|
|
84
|
+
* @returns {void}
|
|
85
|
+
*/
|
|
86
|
+
setOfflineLicense(data) {
|
|
87
|
+
try {
|
|
88
|
+
localStorage.setItem(
|
|
89
|
+
this.prefix + "offline_license",
|
|
90
|
+
JSON.stringify(data)
|
|
91
|
+
);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
console.error("Failed to cache offline license:", e);
|
|
94
|
+
}
|
|
286
95
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Clear the cached offline license
|
|
98
|
+
* @returns {void}
|
|
99
|
+
*/
|
|
100
|
+
clearOfflineLicense() {
|
|
101
|
+
localStorage.removeItem(this.prefix + "offline_license");
|
|
292
102
|
}
|
|
293
|
-
|
|
294
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Get a cached public key by key ID
|
|
105
|
+
* @param {string} keyId - The key ID to look up
|
|
106
|
+
* @returns {string|null} Base64-encoded public key or null if not found
|
|
107
|
+
*/
|
|
108
|
+
getPublicKey(keyId) {
|
|
109
|
+
try {
|
|
110
|
+
const cache = JSON.parse(
|
|
111
|
+
localStorage.getItem(this.publicKeyCacheKey) || "{}"
|
|
112
|
+
);
|
|
113
|
+
return cache[keyId] || null;
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.error("Failed to read public key cache:", e);
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
295
118
|
}
|
|
296
|
-
|
|
297
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Store a public key in cache
|
|
121
|
+
* @param {string} keyId - The key ID
|
|
122
|
+
* @param {string} publicKeyB64 - Base64-encoded public key
|
|
123
|
+
* @returns {void}
|
|
124
|
+
*/
|
|
125
|
+
setPublicKey(keyId, publicKeyB64) {
|
|
126
|
+
try {
|
|
127
|
+
const cache = JSON.parse(
|
|
128
|
+
localStorage.getItem(this.publicKeyCacheKey) || "{}"
|
|
129
|
+
);
|
|
130
|
+
cache[keyId] = publicKeyB64;
|
|
131
|
+
localStorage.setItem(this.publicKeyCacheKey, JSON.stringify(cache));
|
|
132
|
+
} catch (e) {
|
|
133
|
+
console.error("Failed to cache public key:", e);
|
|
134
|
+
}
|
|
298
135
|
}
|
|
299
|
-
|
|
300
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Clear all LicenseSeat SDK data for this prefix
|
|
138
|
+
* @returns {void}
|
|
139
|
+
*/
|
|
140
|
+
clear() {
|
|
141
|
+
Object.keys(localStorage).forEach((key) => {
|
|
142
|
+
if (key.startsWith(this.prefix)) {
|
|
143
|
+
localStorage.removeItem(key);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
this.clearOfflineLicense();
|
|
147
|
+
localStorage.removeItem(this.prefix + "last_seen_ts");
|
|
301
148
|
}
|
|
302
|
-
|
|
303
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Get the last seen timestamp (for clock tamper detection)
|
|
151
|
+
* @returns {number|null} Unix timestamp in milliseconds or null if not set
|
|
152
|
+
*/
|
|
153
|
+
getLastSeenTimestamp() {
|
|
154
|
+
const v = localStorage.getItem(this.prefix + "last_seen_ts");
|
|
155
|
+
return v ? parseInt(v, 10) : null;
|
|
304
156
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
r *= r;
|
|
316
|
-
r %= P;
|
|
157
|
+
/**
|
|
158
|
+
* Store the last seen timestamp
|
|
159
|
+
* @param {number} ts - Unix timestamp in milliseconds
|
|
160
|
+
* @returns {void}
|
|
161
|
+
*/
|
|
162
|
+
setLastSeenTimestamp(ts) {
|
|
163
|
+
try {
|
|
164
|
+
localStorage.setItem(this.prefix + "last_seen_ts", String(ts));
|
|
165
|
+
} catch (e) {
|
|
166
|
+
}
|
|
317
167
|
}
|
|
318
|
-
return r;
|
|
319
|
-
};
|
|
320
|
-
var pow_2_252_3 = (x) => {
|
|
321
|
-
const x2 = x * x % P;
|
|
322
|
-
const b2 = x2 * x % P;
|
|
323
|
-
const b4 = pow2(b2, 2n) * b2 % P;
|
|
324
|
-
const b5 = pow2(b4, 1n) * x % P;
|
|
325
|
-
const b10 = pow2(b5, 5n) * b5 % P;
|
|
326
|
-
const b20 = pow2(b10, 10n) * b10 % P;
|
|
327
|
-
const b40 = pow2(b20, 20n) * b20 % P;
|
|
328
|
-
const b80 = pow2(b40, 40n) * b40 % P;
|
|
329
|
-
const b160 = pow2(b80, 80n) * b80 % P;
|
|
330
|
-
const b240 = pow2(b160, 80n) * b80 % P;
|
|
331
|
-
const b250 = pow2(b240, 10n) * b10 % P;
|
|
332
|
-
const pow_p_5_8 = pow2(b250, 2n) * x % P;
|
|
333
|
-
return { pow_p_5_8, b2 };
|
|
334
|
-
};
|
|
335
|
-
var RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n;
|
|
336
|
-
var uvRatio = (u, v) => {
|
|
337
|
-
const v3 = M(v * v * v);
|
|
338
|
-
const v7 = M(v3 * v3 * v);
|
|
339
|
-
const pow = pow_2_252_3(u * v7).pow_p_5_8;
|
|
340
|
-
let x = M(u * v3 * pow);
|
|
341
|
-
const vx2 = M(v * x * x);
|
|
342
|
-
const root1 = x;
|
|
343
|
-
const root2 = M(x * RM1);
|
|
344
|
-
const useRoot1 = vx2 === u;
|
|
345
|
-
const useRoot2 = vx2 === M(-u);
|
|
346
|
-
const noRoot = vx2 === M(-u * RM1);
|
|
347
|
-
if (useRoot1)
|
|
348
|
-
x = root1;
|
|
349
|
-
if (useRoot2 || noRoot)
|
|
350
|
-
x = root2;
|
|
351
|
-
if ((M(x) & 1n) === 1n)
|
|
352
|
-
x = M(-x);
|
|
353
|
-
return { isValid: useRoot1 || useRoot2, value: x };
|
|
354
|
-
};
|
|
355
|
-
var modL_LE = (hash) => modN(bytesToNumLE(hash));
|
|
356
|
-
var sha512a = (...m) => etc.sha512Async(...m);
|
|
357
|
-
var sha512s = (...m) => callHash("sha512Sync")(...m);
|
|
358
|
-
var hash2extK = (hashed) => {
|
|
359
|
-
const head = hashed.slice(0, L);
|
|
360
|
-
head[0] &= 248;
|
|
361
|
-
head[31] &= 127;
|
|
362
|
-
head[31] |= 64;
|
|
363
|
-
const prefix = hashed.slice(L, L2);
|
|
364
|
-
const scalar = modL_LE(head);
|
|
365
|
-
const point = G.multiply(scalar);
|
|
366
|
-
const pointBytes = point.toBytes();
|
|
367
|
-
return { head, prefix, scalar, point, pointBytes };
|
|
368
|
-
};
|
|
369
|
-
var getExtendedPublicKeyAsync = (priv) => sha512a(toU8(priv, L)).then(hash2extK);
|
|
370
|
-
var getExtendedPublicKey = (priv) => hash2extK(sha512s(toU8(priv, L)));
|
|
371
|
-
var getPublicKeyAsync = (priv) => getExtendedPublicKeyAsync(priv).then((p) => p.pointBytes);
|
|
372
|
-
var getPublicKey = (priv) => getExtendedPublicKey(priv).pointBytes;
|
|
373
|
-
var hashFinishA = (res) => sha512a(res.hashable).then(res.finish);
|
|
374
|
-
var hashFinishS = (res) => res.finish(sha512s(res.hashable));
|
|
375
|
-
var _sign = (e, rBytes, msg) => {
|
|
376
|
-
const { pointBytes: P2, scalar: s } = e;
|
|
377
|
-
const r = modL_LE(rBytes);
|
|
378
|
-
const R = G.multiply(r).toBytes();
|
|
379
|
-
const hashable = concatBytes(R, P2, msg);
|
|
380
|
-
const finish = (hashed) => {
|
|
381
|
-
const S = modN(r + modL_LE(hashed) * s);
|
|
382
|
-
return abytes(concatBytes(R, numTo32bLE(S)), L2);
|
|
383
|
-
};
|
|
384
|
-
return { hashable, finish };
|
|
385
|
-
};
|
|
386
|
-
var signAsync = async (msg, privKey) => {
|
|
387
|
-
const m = toU8(msg);
|
|
388
|
-
const e = await getExtendedPublicKeyAsync(privKey);
|
|
389
|
-
const rBytes = await sha512a(e.prefix, m);
|
|
390
|
-
return hashFinishA(_sign(e, rBytes, m));
|
|
391
|
-
};
|
|
392
|
-
var sign = (msg, privKey) => {
|
|
393
|
-
const m = toU8(msg);
|
|
394
|
-
const e = getExtendedPublicKey(privKey);
|
|
395
|
-
const rBytes = sha512s(e.prefix, m);
|
|
396
|
-
return hashFinishS(_sign(e, rBytes, m));
|
|
397
168
|
};
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
s = bytesToNumLE(sig.slice(L, L2));
|
|
413
|
-
SB = G.multiply(s, false);
|
|
414
|
-
hashable = concatBytes(R.toBytes(), A.toBytes(), msg);
|
|
415
|
-
} catch (error) {
|
|
169
|
+
|
|
170
|
+
// src/errors.js
|
|
171
|
+
var APIError = class extends Error {
|
|
172
|
+
/**
|
|
173
|
+
* Create an APIError
|
|
174
|
+
* @param {string} message - Error message
|
|
175
|
+
* @param {number} status - HTTP status code (0 for network failures)
|
|
176
|
+
* @param {import('./types.js').APIErrorData} [data] - Additional error data from the API response
|
|
177
|
+
*/
|
|
178
|
+
constructor(message, status, data) {
|
|
179
|
+
super(message);
|
|
180
|
+
this.name = "APIError";
|
|
181
|
+
this.status = status;
|
|
182
|
+
this.data = data;
|
|
416
183
|
}
|
|
417
|
-
const finish = (hashed) => {
|
|
418
|
-
if (SB == null)
|
|
419
|
-
return false;
|
|
420
|
-
if (!zip215 && A.isSmallOrder())
|
|
421
|
-
return false;
|
|
422
|
-
const k = modL_LE(hashed);
|
|
423
|
-
const RkA = R.add(A.multiply(k, false));
|
|
424
|
-
return RkA.add(SB.negate()).clearCofactor().is0();
|
|
425
|
-
};
|
|
426
|
-
return { hashable, finish };
|
|
427
184
|
};
|
|
428
|
-
var
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
sha512Sync: void 0,
|
|
437
|
-
bytesToHex,
|
|
438
|
-
hexToBytes,
|
|
439
|
-
concatBytes,
|
|
440
|
-
mod: M,
|
|
441
|
-
invert,
|
|
442
|
-
randomBytes
|
|
443
|
-
};
|
|
444
|
-
var utils = {
|
|
445
|
-
getExtendedPublicKeyAsync,
|
|
446
|
-
getExtendedPublicKey,
|
|
447
|
-
randomPrivateKey: () => randomBytes(L),
|
|
448
|
-
precompute: (w = 8, p = G) => {
|
|
449
|
-
p.multiply(3n);
|
|
450
|
-
w;
|
|
451
|
-
return p;
|
|
185
|
+
var ConfigurationError = class extends Error {
|
|
186
|
+
/**
|
|
187
|
+
* Create a ConfigurationError
|
|
188
|
+
* @param {string} message - Error message
|
|
189
|
+
*/
|
|
190
|
+
constructor(message) {
|
|
191
|
+
super(message);
|
|
192
|
+
this.name = "ConfigurationError";
|
|
452
193
|
}
|
|
453
|
-
// no-op
|
|
454
194
|
};
|
|
455
|
-
var
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
points.push(b);
|
|
466
|
-
for (let i = 1; i < pwindowSize; i++) {
|
|
467
|
-
b = b.add(p);
|
|
468
|
-
points.push(b);
|
|
469
|
-
}
|
|
470
|
-
p = b.double();
|
|
195
|
+
var LicenseError = class extends Error {
|
|
196
|
+
/**
|
|
197
|
+
* Create a LicenseError
|
|
198
|
+
* @param {string} message - Error message
|
|
199
|
+
* @param {string} [code] - Machine-readable error code
|
|
200
|
+
*/
|
|
201
|
+
constructor(message, code) {
|
|
202
|
+
super(message);
|
|
203
|
+
this.name = "LicenseError";
|
|
204
|
+
this.code = code;
|
|
471
205
|
}
|
|
472
|
-
return points;
|
|
473
206
|
};
|
|
474
|
-
var
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
let f = G;
|
|
483
|
-
const pow_2_w = 2 ** W;
|
|
484
|
-
const maxNum = pow_2_w;
|
|
485
|
-
const mask = big(pow_2_w - 1);
|
|
486
|
-
const shiftBy = big(W);
|
|
487
|
-
for (let w = 0; w < pwindows; w++) {
|
|
488
|
-
let wbits = Number(n & mask);
|
|
489
|
-
n >>= shiftBy;
|
|
490
|
-
if (wbits > pwindowSize) {
|
|
491
|
-
wbits -= maxNum;
|
|
492
|
-
n += 1n;
|
|
493
|
-
}
|
|
494
|
-
const off = w * pwindowSize;
|
|
495
|
-
const offF = off;
|
|
496
|
-
const offP = off + Math.abs(wbits) - 1;
|
|
497
|
-
const isEven = w % 2 !== 0;
|
|
498
|
-
const isNeg = wbits < 0;
|
|
499
|
-
if (wbits === 0) {
|
|
500
|
-
f = f.add(ctneg(isEven, comp[offF]));
|
|
501
|
-
} else {
|
|
502
|
-
p = p.add(ctneg(isNeg, comp[offP]));
|
|
503
|
-
}
|
|
207
|
+
var CryptoError = class extends Error {
|
|
208
|
+
/**
|
|
209
|
+
* Create a CryptoError
|
|
210
|
+
* @param {string} message - Error message
|
|
211
|
+
*/
|
|
212
|
+
constructor(message) {
|
|
213
|
+
super(message);
|
|
214
|
+
this.name = "CryptoError";
|
|
504
215
|
}
|
|
505
|
-
return { p, f };
|
|
506
216
|
};
|
|
507
217
|
|
|
508
|
-
//
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
218
|
+
// src/utils.js
|
|
219
|
+
import CJSON from "canonical-json";
|
|
220
|
+
function parseActiveEntitlements(payload = {}) {
|
|
221
|
+
const raw = payload.active_ents || payload.active_entitlements || [];
|
|
222
|
+
return raw.map((e) => ({
|
|
223
|
+
key: e.key,
|
|
224
|
+
name: e.name ?? null,
|
|
225
|
+
description: e.description ?? null,
|
|
226
|
+
expires_at: e.expires_at ?? null,
|
|
227
|
+
metadata: e.metadata ?? null
|
|
228
|
+
}));
|
|
517
229
|
}
|
|
518
|
-
function
|
|
519
|
-
if (
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
function aoutput(out, instance) {
|
|
525
|
-
abytes2(out);
|
|
526
|
-
const min = instance.outputLen;
|
|
527
|
-
if (out.length < min) {
|
|
528
|
-
throw new Error("digestInto() expects output buffer of length at least " + min);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
function clean(...arrays) {
|
|
532
|
-
for (let i = 0; i < arrays.length; i++) {
|
|
533
|
-
arrays[i].fill(0);
|
|
230
|
+
function constantTimeEqual(a = "", b = "") {
|
|
231
|
+
if (a.length !== b.length)
|
|
232
|
+
return false;
|
|
233
|
+
let res = 0;
|
|
234
|
+
for (let i = 0; i < a.length; i++) {
|
|
235
|
+
res |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
534
236
|
}
|
|
237
|
+
return res === 0;
|
|
535
238
|
}
|
|
536
|
-
function
|
|
537
|
-
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
|
|
554
|
-
const tmp = hashCons();
|
|
555
|
-
hashC.outputLen = tmp.outputLen;
|
|
556
|
-
hashC.blockLen = tmp.blockLen;
|
|
557
|
-
hashC.create = () => hashCons();
|
|
558
|
-
return hashC;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// node_modules/@noble/hashes/esm/_md.js
|
|
562
|
-
function setBigUint64(view, byteOffset, value, isLE) {
|
|
563
|
-
if (typeof view.setBigUint64 === "function")
|
|
564
|
-
return view.setBigUint64(byteOffset, value, isLE);
|
|
565
|
-
const _32n2 = BigInt(32);
|
|
566
|
-
const _u32_max = BigInt(4294967295);
|
|
567
|
-
const wh = Number(value >> _32n2 & _u32_max);
|
|
568
|
-
const wl = Number(value & _u32_max);
|
|
569
|
-
const h2 = isLE ? 4 : 0;
|
|
570
|
-
const l = isLE ? 0 : 4;
|
|
571
|
-
view.setUint32(byteOffset + h2, wh, isLE);
|
|
572
|
-
view.setUint32(byteOffset + l, wl, isLE);
|
|
573
|
-
}
|
|
574
|
-
var HashMD = class extends Hash {
|
|
575
|
-
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
576
|
-
super();
|
|
577
|
-
this.finished = false;
|
|
578
|
-
this.length = 0;
|
|
579
|
-
this.pos = 0;
|
|
580
|
-
this.destroyed = false;
|
|
581
|
-
this.blockLen = blockLen;
|
|
582
|
-
this.outputLen = outputLen;
|
|
583
|
-
this.padOffset = padOffset;
|
|
584
|
-
this.isLE = isLE;
|
|
585
|
-
this.buffer = new Uint8Array(blockLen);
|
|
586
|
-
this.view = createView(this.buffer);
|
|
587
|
-
}
|
|
588
|
-
update(data) {
|
|
589
|
-
aexists(this);
|
|
590
|
-
data = toBytes(data);
|
|
591
|
-
abytes2(data);
|
|
592
|
-
const { view, buffer, blockLen } = this;
|
|
593
|
-
const len = data.length;
|
|
594
|
-
for (let pos = 0; pos < len; ) {
|
|
595
|
-
const take = Math.min(blockLen - this.pos, len - pos);
|
|
596
|
-
if (take === blockLen) {
|
|
597
|
-
const dataView = createView(data);
|
|
598
|
-
for (; blockLen <= len - pos; pos += blockLen)
|
|
599
|
-
this.process(dataView, pos);
|
|
600
|
-
continue;
|
|
601
|
-
}
|
|
602
|
-
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
603
|
-
this.pos += take;
|
|
604
|
-
pos += take;
|
|
605
|
-
if (this.pos === blockLen) {
|
|
606
|
-
this.process(view, 0);
|
|
607
|
-
this.pos = 0;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
this.length += data.length;
|
|
611
|
-
this.roundClean();
|
|
612
|
-
return this;
|
|
613
|
-
}
|
|
614
|
-
digestInto(out) {
|
|
615
|
-
aexists(this);
|
|
616
|
-
aoutput(out, this);
|
|
617
|
-
this.finished = true;
|
|
618
|
-
const { buffer, view, blockLen, isLE } = this;
|
|
619
|
-
let { pos } = this;
|
|
620
|
-
buffer[pos++] = 128;
|
|
621
|
-
clean(this.buffer.subarray(pos));
|
|
622
|
-
if (this.padOffset > blockLen - pos) {
|
|
623
|
-
this.process(view, 0);
|
|
624
|
-
pos = 0;
|
|
239
|
+
function canonicalJsonStringify(obj) {
|
|
240
|
+
const stringify = typeof CJSON === "function" ? CJSON : CJSON && typeof CJSON === "object" ? (
|
|
241
|
+
/** @type {any} */
|
|
242
|
+
CJSON.stringify
|
|
243
|
+
) : void 0;
|
|
244
|
+
if (!stringify || typeof stringify !== "function") {
|
|
245
|
+
console.warn(
|
|
246
|
+
"[LicenseSeat SDK] canonical-json library not loaded correctly. Falling back to basic JSON.stringify. Signature verification might be unreliable if server uses different canonicalization."
|
|
247
|
+
);
|
|
248
|
+
try {
|
|
249
|
+
const sortedObj = {};
|
|
250
|
+
Object.keys(obj).sort().forEach((key) => {
|
|
251
|
+
sortedObj[key] = obj[key];
|
|
252
|
+
});
|
|
253
|
+
return JSON.stringify(sortedObj);
|
|
254
|
+
} catch (e) {
|
|
255
|
+
return JSON.stringify(obj);
|
|
625
256
|
}
|
|
626
|
-
for (let i = pos; i < blockLen; i++)
|
|
627
|
-
buffer[i] = 0;
|
|
628
|
-
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
|
|
629
|
-
this.process(view, 0);
|
|
630
|
-
const oview = createView(out);
|
|
631
|
-
const len = this.outputLen;
|
|
632
|
-
if (len % 4)
|
|
633
|
-
throw new Error("_sha2: outputLen should be aligned to 32bit");
|
|
634
|
-
const outLen = len / 4;
|
|
635
|
-
const state = this.get();
|
|
636
|
-
if (outLen > state.length)
|
|
637
|
-
throw new Error("_sha2: outputLen bigger than state");
|
|
638
|
-
for (let i = 0; i < outLen; i++)
|
|
639
|
-
oview.setUint32(4 * i, state[i], isLE);
|
|
640
257
|
}
|
|
641
|
-
|
|
642
|
-
const { buffer, outputLen } = this;
|
|
643
|
-
this.digestInto(buffer);
|
|
644
|
-
const res = buffer.slice(0, outputLen);
|
|
645
|
-
this.destroy();
|
|
646
|
-
return res;
|
|
647
|
-
}
|
|
648
|
-
_cloneInto(to) {
|
|
649
|
-
to || (to = new this.constructor());
|
|
650
|
-
to.set(...this.get());
|
|
651
|
-
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
652
|
-
to.destroyed = destroyed;
|
|
653
|
-
to.finished = finished;
|
|
654
|
-
to.length = length;
|
|
655
|
-
to.pos = pos;
|
|
656
|
-
if (length % blockLen)
|
|
657
|
-
to.buffer.set(buffer);
|
|
658
|
-
return to;
|
|
659
|
-
}
|
|
660
|
-
clone() {
|
|
661
|
-
return this._cloneInto();
|
|
662
|
-
}
|
|
663
|
-
};
|
|
664
|
-
var SHA512_IV = /* @__PURE__ */ Uint32Array.from([
|
|
665
|
-
1779033703,
|
|
666
|
-
4089235720,
|
|
667
|
-
3144134277,
|
|
668
|
-
2227873595,
|
|
669
|
-
1013904242,
|
|
670
|
-
4271175723,
|
|
671
|
-
2773480762,
|
|
672
|
-
1595750129,
|
|
673
|
-
1359893119,
|
|
674
|
-
2917565137,
|
|
675
|
-
2600822924,
|
|
676
|
-
725511199,
|
|
677
|
-
528734635,
|
|
678
|
-
4215389547,
|
|
679
|
-
1541459225,
|
|
680
|
-
327033209
|
|
681
|
-
]);
|
|
682
|
-
|
|
683
|
-
// node_modules/@noble/hashes/esm/_u64.js
|
|
684
|
-
var U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
|
|
685
|
-
var _32n = /* @__PURE__ */ BigInt(32);
|
|
686
|
-
function fromBig(n, le = false) {
|
|
687
|
-
if (le)
|
|
688
|
-
return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
|
|
689
|
-
return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
|
|
258
|
+
return stringify(obj);
|
|
690
259
|
}
|
|
691
|
-
function
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
260
|
+
function base64UrlDecode(base64UrlString) {
|
|
261
|
+
let base64 = base64UrlString.replace(/-/g, "+").replace(/_/g, "/");
|
|
262
|
+
while (base64.length % 4) {
|
|
263
|
+
base64 += "=";
|
|
264
|
+
}
|
|
265
|
+
const raw = window.atob(base64);
|
|
266
|
+
const outputArray = new Uint8Array(raw.length);
|
|
267
|
+
for (let i = 0; i < raw.length; ++i) {
|
|
268
|
+
outputArray[i] = raw.charCodeAt(i);
|
|
698
269
|
}
|
|
699
|
-
return
|
|
270
|
+
return outputArray;
|
|
700
271
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
return { h: Ah + Bh + (l / 2 ** 32 | 0) | 0, l: l | 0 };
|
|
272
|
+
function hashCode(str) {
|
|
273
|
+
let hash = 0;
|
|
274
|
+
for (let i = 0; i < str.length; i++) {
|
|
275
|
+
const char = str.charCodeAt(i);
|
|
276
|
+
hash = (hash << 5) - hash + char;
|
|
277
|
+
hash = hash & hash;
|
|
278
|
+
}
|
|
279
|
+
return Math.abs(hash).toString(36);
|
|
710
280
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
"0x7137449123ef65cd",
|
|
722
|
-
"0xb5c0fbcfec4d3b2f",
|
|
723
|
-
"0xe9b5dba58189dbbc",
|
|
724
|
-
"0x3956c25bf348b538",
|
|
725
|
-
"0x59f111f1b605d019",
|
|
726
|
-
"0x923f82a4af194f9b",
|
|
727
|
-
"0xab1c5ed5da6d8118",
|
|
728
|
-
"0xd807aa98a3030242",
|
|
729
|
-
"0x12835b0145706fbe",
|
|
730
|
-
"0x243185be4ee4b28c",
|
|
731
|
-
"0x550c7dc3d5ffb4e2",
|
|
732
|
-
"0x72be5d74f27b896f",
|
|
733
|
-
"0x80deb1fe3b1696b1",
|
|
734
|
-
"0x9bdc06a725c71235",
|
|
735
|
-
"0xc19bf174cf692694",
|
|
736
|
-
"0xe49b69c19ef14ad2",
|
|
737
|
-
"0xefbe4786384f25e3",
|
|
738
|
-
"0x0fc19dc68b8cd5b5",
|
|
739
|
-
"0x240ca1cc77ac9c65",
|
|
740
|
-
"0x2de92c6f592b0275",
|
|
741
|
-
"0x4a7484aa6ea6e483",
|
|
742
|
-
"0x5cb0a9dcbd41fbd4",
|
|
743
|
-
"0x76f988da831153b5",
|
|
744
|
-
"0x983e5152ee66dfab",
|
|
745
|
-
"0xa831c66d2db43210",
|
|
746
|
-
"0xb00327c898fb213f",
|
|
747
|
-
"0xbf597fc7beef0ee4",
|
|
748
|
-
"0xc6e00bf33da88fc2",
|
|
749
|
-
"0xd5a79147930aa725",
|
|
750
|
-
"0x06ca6351e003826f",
|
|
751
|
-
"0x142929670a0e6e70",
|
|
752
|
-
"0x27b70a8546d22ffc",
|
|
753
|
-
"0x2e1b21385c26c926",
|
|
754
|
-
"0x4d2c6dfc5ac42aed",
|
|
755
|
-
"0x53380d139d95b3df",
|
|
756
|
-
"0x650a73548baf63de",
|
|
757
|
-
"0x766a0abb3c77b2a8",
|
|
758
|
-
"0x81c2c92e47edaee6",
|
|
759
|
-
"0x92722c851482353b",
|
|
760
|
-
"0xa2bfe8a14cf10364",
|
|
761
|
-
"0xa81a664bbc423001",
|
|
762
|
-
"0xc24b8b70d0f89791",
|
|
763
|
-
"0xc76c51a30654be30",
|
|
764
|
-
"0xd192e819d6ef5218",
|
|
765
|
-
"0xd69906245565a910",
|
|
766
|
-
"0xf40e35855771202a",
|
|
767
|
-
"0x106aa07032bbd1b8",
|
|
768
|
-
"0x19a4c116b8d2d0c8",
|
|
769
|
-
"0x1e376c085141ab53",
|
|
770
|
-
"0x2748774cdf8eeb99",
|
|
771
|
-
"0x34b0bcb5e19b48a8",
|
|
772
|
-
"0x391c0cb3c5c95a63",
|
|
773
|
-
"0x4ed8aa4ae3418acb",
|
|
774
|
-
"0x5b9cca4f7763e373",
|
|
775
|
-
"0x682e6ff3d6b2b8a3",
|
|
776
|
-
"0x748f82ee5defb2fc",
|
|
777
|
-
"0x78a5636f43172f60",
|
|
778
|
-
"0x84c87814a1f0ab72",
|
|
779
|
-
"0x8cc702081a6439ec",
|
|
780
|
-
"0x90befffa23631e28",
|
|
781
|
-
"0xa4506cebde82bde9",
|
|
782
|
-
"0xbef9a3f7b2c67915",
|
|
783
|
-
"0xc67178f2e372532b",
|
|
784
|
-
"0xca273eceea26619c",
|
|
785
|
-
"0xd186b8c721c0c207",
|
|
786
|
-
"0xeada7dd6cde0eb1e",
|
|
787
|
-
"0xf57d4f7fee6ed178",
|
|
788
|
-
"0x06f067aa72176fba",
|
|
789
|
-
"0x0a637dc5a2c898a6",
|
|
790
|
-
"0x113f9804bef90dae",
|
|
791
|
-
"0x1b710b35131c471b",
|
|
792
|
-
"0x28db77f523047d84",
|
|
793
|
-
"0x32caab7b40c72493",
|
|
794
|
-
"0x3c9ebe0a15c9bebc",
|
|
795
|
-
"0x431d67c49c100d4c",
|
|
796
|
-
"0x4cc5d4becb3e42b6",
|
|
797
|
-
"0x597f299cfc657e2a",
|
|
798
|
-
"0x5fcb6fab3ad6faec",
|
|
799
|
-
"0x6c44198c4a475817"
|
|
800
|
-
].map((n) => BigInt(n))))();
|
|
801
|
-
var SHA512_Kh = /* @__PURE__ */ (() => K512[0])();
|
|
802
|
-
var SHA512_Kl = /* @__PURE__ */ (() => K512[1])();
|
|
803
|
-
var SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);
|
|
804
|
-
var SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);
|
|
805
|
-
var SHA512 = class extends HashMD {
|
|
806
|
-
constructor(outputLen = 64) {
|
|
807
|
-
super(128, outputLen, 16, false);
|
|
808
|
-
this.Ah = SHA512_IV[0] | 0;
|
|
809
|
-
this.Al = SHA512_IV[1] | 0;
|
|
810
|
-
this.Bh = SHA512_IV[2] | 0;
|
|
811
|
-
this.Bl = SHA512_IV[3] | 0;
|
|
812
|
-
this.Ch = SHA512_IV[4] | 0;
|
|
813
|
-
this.Cl = SHA512_IV[5] | 0;
|
|
814
|
-
this.Dh = SHA512_IV[6] | 0;
|
|
815
|
-
this.Dl = SHA512_IV[7] | 0;
|
|
816
|
-
this.Eh = SHA512_IV[8] | 0;
|
|
817
|
-
this.El = SHA512_IV[9] | 0;
|
|
818
|
-
this.Fh = SHA512_IV[10] | 0;
|
|
819
|
-
this.Fl = SHA512_IV[11] | 0;
|
|
820
|
-
this.Gh = SHA512_IV[12] | 0;
|
|
821
|
-
this.Gl = SHA512_IV[13] | 0;
|
|
822
|
-
this.Hh = SHA512_IV[14] | 0;
|
|
823
|
-
this.Hl = SHA512_IV[15] | 0;
|
|
824
|
-
}
|
|
825
|
-
// prettier-ignore
|
|
826
|
-
get() {
|
|
827
|
-
const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
|
|
828
|
-
return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];
|
|
829
|
-
}
|
|
830
|
-
// prettier-ignore
|
|
831
|
-
set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) {
|
|
832
|
-
this.Ah = Ah | 0;
|
|
833
|
-
this.Al = Al | 0;
|
|
834
|
-
this.Bh = Bh | 0;
|
|
835
|
-
this.Bl = Bl | 0;
|
|
836
|
-
this.Ch = Ch | 0;
|
|
837
|
-
this.Cl = Cl | 0;
|
|
838
|
-
this.Dh = Dh | 0;
|
|
839
|
-
this.Dl = Dl | 0;
|
|
840
|
-
this.Eh = Eh | 0;
|
|
841
|
-
this.El = El | 0;
|
|
842
|
-
this.Fh = Fh | 0;
|
|
843
|
-
this.Fl = Fl | 0;
|
|
844
|
-
this.Gh = Gh | 0;
|
|
845
|
-
this.Gl = Gl | 0;
|
|
846
|
-
this.Hh = Hh | 0;
|
|
847
|
-
this.Hl = Hl | 0;
|
|
848
|
-
}
|
|
849
|
-
process(view, offset) {
|
|
850
|
-
for (let i = 0; i < 16; i++, offset += 4) {
|
|
851
|
-
SHA512_W_H[i] = view.getUint32(offset);
|
|
852
|
-
SHA512_W_L[i] = view.getUint32(offset += 4);
|
|
853
|
-
}
|
|
854
|
-
for (let i = 16; i < 80; i++) {
|
|
855
|
-
const W15h = SHA512_W_H[i - 15] | 0;
|
|
856
|
-
const W15l = SHA512_W_L[i - 15] | 0;
|
|
857
|
-
const s0h = rotrSH(W15h, W15l, 1) ^ rotrSH(W15h, W15l, 8) ^ shrSH(W15h, W15l, 7);
|
|
858
|
-
const s0l = rotrSL(W15h, W15l, 1) ^ rotrSL(W15h, W15l, 8) ^ shrSL(W15h, W15l, 7);
|
|
859
|
-
const W2h = SHA512_W_H[i - 2] | 0;
|
|
860
|
-
const W2l = SHA512_W_L[i - 2] | 0;
|
|
861
|
-
const s1h = rotrSH(W2h, W2l, 19) ^ rotrBH(W2h, W2l, 61) ^ shrSH(W2h, W2l, 6);
|
|
862
|
-
const s1l = rotrSL(W2h, W2l, 19) ^ rotrBL(W2h, W2l, 61) ^ shrSL(W2h, W2l, 6);
|
|
863
|
-
const SUMl = add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);
|
|
864
|
-
const SUMh = add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);
|
|
865
|
-
SHA512_W_H[i] = SUMh | 0;
|
|
866
|
-
SHA512_W_L[i] = SUMl | 0;
|
|
867
|
-
}
|
|
868
|
-
let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
|
|
869
|
-
for (let i = 0; i < 80; i++) {
|
|
870
|
-
const sigma1h = rotrSH(Eh, El, 14) ^ rotrSH(Eh, El, 18) ^ rotrBH(Eh, El, 41);
|
|
871
|
-
const sigma1l = rotrSL(Eh, El, 14) ^ rotrSL(Eh, El, 18) ^ rotrBL(Eh, El, 41);
|
|
872
|
-
const CHIh = Eh & Fh ^ ~Eh & Gh;
|
|
873
|
-
const CHIl = El & Fl ^ ~El & Gl;
|
|
874
|
-
const T1ll = add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);
|
|
875
|
-
const T1h = add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);
|
|
876
|
-
const T1l = T1ll | 0;
|
|
877
|
-
const sigma0h = rotrSH(Ah, Al, 28) ^ rotrBH(Ah, Al, 34) ^ rotrBH(Ah, Al, 39);
|
|
878
|
-
const sigma0l = rotrSL(Ah, Al, 28) ^ rotrBL(Ah, Al, 34) ^ rotrBL(Ah, Al, 39);
|
|
879
|
-
const MAJh = Ah & Bh ^ Ah & Ch ^ Bh & Ch;
|
|
880
|
-
const MAJl = Al & Bl ^ Al & Cl ^ Bl & Cl;
|
|
881
|
-
Hh = Gh | 0;
|
|
882
|
-
Hl = Gl | 0;
|
|
883
|
-
Gh = Fh | 0;
|
|
884
|
-
Gl = Fl | 0;
|
|
885
|
-
Fh = Eh | 0;
|
|
886
|
-
Fl = El | 0;
|
|
887
|
-
({ h: Eh, l: El } = add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));
|
|
888
|
-
Dh = Ch | 0;
|
|
889
|
-
Dl = Cl | 0;
|
|
890
|
-
Ch = Bh | 0;
|
|
891
|
-
Cl = Bl | 0;
|
|
892
|
-
Bh = Ah | 0;
|
|
893
|
-
Bl = Al | 0;
|
|
894
|
-
const All = add3L(T1l, sigma0l, MAJl);
|
|
895
|
-
Ah = add3H(All, T1h, sigma0h, MAJh);
|
|
896
|
-
Al = All | 0;
|
|
897
|
-
}
|
|
898
|
-
({ h: Ah, l: Al } = add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));
|
|
899
|
-
({ h: Bh, l: Bl } = add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));
|
|
900
|
-
({ h: Ch, l: Cl } = add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));
|
|
901
|
-
({ h: Dh, l: Dl } = add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));
|
|
902
|
-
({ h: Eh, l: El } = add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));
|
|
903
|
-
({ h: Fh, l: Fl } = add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));
|
|
904
|
-
({ h: Gh, l: Gl } = add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));
|
|
905
|
-
({ h: Hh, l: Hl } = add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));
|
|
906
|
-
this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);
|
|
907
|
-
}
|
|
908
|
-
roundClean() {
|
|
909
|
-
clean(SHA512_W_H, SHA512_W_L);
|
|
910
|
-
}
|
|
911
|
-
destroy() {
|
|
912
|
-
clean(this.buffer);
|
|
913
|
-
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
281
|
+
function getCanvasFingerprint() {
|
|
282
|
+
try {
|
|
283
|
+
const canvas = document.createElement("canvas");
|
|
284
|
+
const ctx = canvas.getContext("2d");
|
|
285
|
+
ctx.textBaseline = "top";
|
|
286
|
+
ctx.font = "14px Arial";
|
|
287
|
+
ctx.fillText("LicenseSeat SDK", 2, 2);
|
|
288
|
+
return canvas.toDataURL().slice(-50);
|
|
289
|
+
} catch (e) {
|
|
290
|
+
return "no-canvas";
|
|
914
291
|
}
|
|
915
|
-
};
|
|
916
|
-
var sha512 = /* @__PURE__ */ createHasher(() => new SHA512());
|
|
917
|
-
|
|
918
|
-
// node_modules/@noble/hashes/esm/sha512.js
|
|
919
|
-
var sha5122 = sha512;
|
|
920
|
-
|
|
921
|
-
// node_modules/canonical-json/stringify.js
|
|
922
|
-
var gap = "";
|
|
923
|
-
var indent = "";
|
|
924
|
-
var rep;
|
|
925
|
-
var cmp;
|
|
926
|
-
var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
|
|
927
|
-
var meta = { "\b": "\\b", " ": "\\t", "\n": "\\n", "\f": "\\f", "\r": "\\r", '"': '\\"', "\\": "\\\\" };
|
|
928
|
-
function quote(str2) {
|
|
929
|
-
escapable.lastIndex = 0;
|
|
930
|
-
return escapable.test(str2) ? '"' + str2.replace(escapable, (a) => meta[a] || "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4)) + '"' : '"' + str2 + '"';
|
|
931
292
|
}
|
|
932
|
-
function
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
case "boolean":
|
|
946
|
-
case "undefined":
|
|
947
|
-
case "object":
|
|
948
|
-
if (value === null)
|
|
949
|
-
return "null";
|
|
950
|
-
const mind = gap;
|
|
951
|
-
gap += indent;
|
|
952
|
-
const partial = [];
|
|
953
|
-
if (Array.isArray(value)) {
|
|
954
|
-
for (let i = 0; i < value.length; i++)
|
|
955
|
-
partial[i] = str(i, value) || "null";
|
|
956
|
-
} else {
|
|
957
|
-
const keys = Object.keys(value).sort(cmp);
|
|
958
|
-
for (const k of keys) {
|
|
959
|
-
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
|
960
|
-
const v2 = str(k, value);
|
|
961
|
-
if (v2)
|
|
962
|
-
partial.push(quote(k) + (gap ? ": " : ":") + v2);
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
let v;
|
|
967
|
-
if (Array.isArray(value)) {
|
|
968
|
-
v = partial.length === 0 ? "[]" : gap ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : "[" + partial.join(",") + "]";
|
|
969
|
-
} else {
|
|
970
|
-
v = partial.length === 0 ? "{}" : gap ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : "{" + partial.join(",") + "}";
|
|
971
|
-
}
|
|
972
|
-
gap = mind;
|
|
973
|
-
return v;
|
|
974
|
-
default:
|
|
975
|
-
return String(value);
|
|
976
|
-
}
|
|
293
|
+
function generateDeviceId() {
|
|
294
|
+
const nav = window.navigator;
|
|
295
|
+
const screen = window.screen;
|
|
296
|
+
const data = [
|
|
297
|
+
nav.userAgent,
|
|
298
|
+
nav.language,
|
|
299
|
+
screen.colorDepth,
|
|
300
|
+
screen.width + "x" + screen.height,
|
|
301
|
+
(/* @__PURE__ */ new Date()).getTimezoneOffset(),
|
|
302
|
+
nav.hardwareConcurrency,
|
|
303
|
+
getCanvasFingerprint()
|
|
304
|
+
].join("|");
|
|
305
|
+
return `web-${hashCode(data)}-${Date.now().toString(36)}`;
|
|
977
306
|
}
|
|
978
|
-
function
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
indent = " ".repeat(space);
|
|
985
|
-
} else if (typeof space === "string") {
|
|
986
|
-
indent = space;
|
|
987
|
-
}
|
|
988
|
-
return str("", { "": value });
|
|
307
|
+
function sleep(ms) {
|
|
308
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
309
|
+
}
|
|
310
|
+
function getCsrfToken() {
|
|
311
|
+
const token = document.querySelector('meta[name="csrf-token"]');
|
|
312
|
+
return token ? token.content : "";
|
|
989
313
|
}
|
|
990
314
|
|
|
991
|
-
//
|
|
992
|
-
var
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
315
|
+
// src/LicenseSeat.js
|
|
316
|
+
var DEFAULT_CONFIG = {
|
|
317
|
+
apiBaseUrl: "https://api.licenseseat.com",
|
|
318
|
+
storagePrefix: "licenseseat_",
|
|
319
|
+
autoValidateInterval: 36e5,
|
|
320
|
+
// 1 hour
|
|
321
|
+
networkRecheckInterval: 3e4,
|
|
322
|
+
// 30 seconds
|
|
323
|
+
maxRetries: 3,
|
|
324
|
+
retryDelay: 1e3,
|
|
325
|
+
apiKey: null,
|
|
326
|
+
debug: false,
|
|
327
|
+
offlineLicenseRefreshInterval: 1e3 * 60 * 60 * 72,
|
|
328
|
+
// 72 hours
|
|
329
|
+
offlineFallbackEnabled: false,
|
|
330
|
+
// default false (strict mode, matches Swift SDK)
|
|
331
|
+
maxOfflineDays: 0,
|
|
332
|
+
// 0 = disabled
|
|
333
|
+
maxClockSkewMs: 5 * 60 * 1e3,
|
|
334
|
+
// 5 minutes
|
|
335
|
+
autoInitialize: true
|
|
336
|
+
};
|
|
337
|
+
var LicenseSeatSDK = class {
|
|
338
|
+
/**
|
|
339
|
+
* Create a new LicenseSeat SDK instance
|
|
340
|
+
* @param {import('./types.js').LicenseSeatConfig} [config={}] - Configuration options
|
|
341
|
+
*/
|
|
996
342
|
constructor(config = {}) {
|
|
997
343
|
this.config = {
|
|
998
|
-
|
|
999
|
-
storagePrefix: config.storagePrefix || "licenseseat_",
|
|
1000
|
-
autoValidateInterval: config.autoValidateInterval || 36e5,
|
|
1001
|
-
// 1 hour default
|
|
1002
|
-
networkRecheckInterval: config.networkRecheckInterval || 3e4,
|
|
1003
|
-
// 30s while offline
|
|
1004
|
-
maxRetries: config.maxRetries || 3,
|
|
1005
|
-
retryDelay: config.retryDelay || 1e3,
|
|
1006
|
-
apiKey: config.apiKey || null,
|
|
1007
|
-
// Store apiKey
|
|
1008
|
-
debug: config.debug || false,
|
|
1009
|
-
offlineLicenseRefreshInterval: config.offlineLicenseRefreshInterval || 1e3 * 60 * 60 * 72,
|
|
1010
|
-
// 72h
|
|
1011
|
-
offlineFallbackEnabled: config.offlineFallbackEnabled !== false,
|
|
1012
|
-
// default true
|
|
1013
|
-
maxOfflineDays: config.maxOfflineDays || 0,
|
|
1014
|
-
// 0 = disabled
|
|
1015
|
-
maxClockSkewMs: config.maxClockSkewMs || 5 * 60 * 1e3,
|
|
1016
|
-
// 5 minutes
|
|
344
|
+
...DEFAULT_CONFIG,
|
|
1017
345
|
...config
|
|
1018
346
|
};
|
|
1019
347
|
this.eventListeners = {};
|
|
@@ -1024,15 +352,23 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1024
352
|
this.connectivityTimer = null;
|
|
1025
353
|
this.offlineRefreshTimer = null;
|
|
1026
354
|
this.lastOfflineValidation = null;
|
|
1027
|
-
if (
|
|
1028
|
-
etc.sha512Sync = (...m) =>
|
|
355
|
+
if (ed && ed.etc && sha512) {
|
|
356
|
+
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
|
|
1029
357
|
} else {
|
|
1030
358
|
console.error(
|
|
1031
359
|
"[LicenseSeat SDK] Noble-ed25519 or Noble-hashes not loaded correctly. Sync crypto methods may fail."
|
|
1032
360
|
);
|
|
1033
361
|
}
|
|
1034
|
-
this.
|
|
362
|
+
if (this.config.autoInitialize) {
|
|
363
|
+
this.initialize();
|
|
364
|
+
}
|
|
1035
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Initialize the SDK
|
|
368
|
+
* Loads cached license and starts auto-validation if configured.
|
|
369
|
+
* Called automatically unless autoInitialize is set to false.
|
|
370
|
+
* @returns {void}
|
|
371
|
+
*/
|
|
1036
372
|
initialize() {
|
|
1037
373
|
this.log("LicenseSeat SDK initialized", this.config);
|
|
1038
374
|
const cachedLicense = this.cache.getLicense();
|
|
@@ -1054,17 +390,15 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1054
390
|
}
|
|
1055
391
|
if (this.config.apiKey) {
|
|
1056
392
|
this.startAutoValidation(cachedLicense.license_key);
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
this.log("Background validation failed:", err2);
|
|
1061
|
-
if (err2 instanceof APIError && (err2.status === 401 || err2.status === 501)) {
|
|
393
|
+
this.validateLicense(cachedLicense.license_key).catch((err) => {
|
|
394
|
+
this.log("Background validation failed:", err);
|
|
395
|
+
if (err instanceof APIError && (err.status === 401 || err.status === 501)) {
|
|
1062
396
|
this.log(
|
|
1063
397
|
"Authentication issue during validation, using cached license data"
|
|
1064
398
|
);
|
|
1065
399
|
this.emit("validation:auth-failed", {
|
|
1066
400
|
licenseKey: cachedLicense.license_key,
|
|
1067
|
-
error:
|
|
401
|
+
error: err,
|
|
1068
402
|
cached: true
|
|
1069
403
|
});
|
|
1070
404
|
}
|
|
@@ -1075,14 +409,12 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1075
409
|
/**
|
|
1076
410
|
* Activate a license
|
|
1077
411
|
* @param {string} licenseKey - The license key to activate
|
|
1078
|
-
* @param {
|
|
1079
|
-
* @
|
|
1080
|
-
* @
|
|
1081
|
-
* @param {Object} [options.metadata] - Optional metadata.
|
|
1082
|
-
* @returns {Promise<Object>} Activation result
|
|
412
|
+
* @param {import('./types.js').ActivationOptions} [options={}] - Activation options
|
|
413
|
+
* @returns {Promise<import('./types.js').CachedLicense>} Activation result with cached license data
|
|
414
|
+
* @throws {APIError} When the API request fails
|
|
1083
415
|
*/
|
|
1084
416
|
async activate(licenseKey, options = {}) {
|
|
1085
|
-
const deviceId = options.deviceIdentifier ||
|
|
417
|
+
const deviceId = options.deviceIdentifier || generateDeviceId();
|
|
1086
418
|
const payload = {
|
|
1087
419
|
license_key: licenseKey,
|
|
1088
420
|
device_identifier: deviceId,
|
|
@@ -1118,12 +450,14 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1118
450
|
}
|
|
1119
451
|
/**
|
|
1120
452
|
* Deactivate the current license
|
|
1121
|
-
* @returns {Promise<Object>} Deactivation result
|
|
453
|
+
* @returns {Promise<Object>} Deactivation result from the API
|
|
454
|
+
* @throws {LicenseError} When no active license is found
|
|
455
|
+
* @throws {APIError} When the API request fails
|
|
1122
456
|
*/
|
|
1123
457
|
async deactivate() {
|
|
1124
458
|
const cachedLicense = this.cache.getLicense();
|
|
1125
459
|
if (!cachedLicense) {
|
|
1126
|
-
throw new
|
|
460
|
+
throw new LicenseError("No active license found", "no_license");
|
|
1127
461
|
}
|
|
1128
462
|
try {
|
|
1129
463
|
this.emit("deactivation:start", cachedLicense);
|
|
@@ -1147,8 +481,9 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1147
481
|
/**
|
|
1148
482
|
* Validate a license
|
|
1149
483
|
* @param {string} licenseKey - License key to validate
|
|
1150
|
-
* @param {
|
|
1151
|
-
* @returns {Promise<
|
|
484
|
+
* @param {import('./types.js').ValidationOptions} [options={}] - Validation options
|
|
485
|
+
* @returns {Promise<import('./types.js').ValidationResult>} Validation result
|
|
486
|
+
* @throws {APIError} When the API request fails and offline fallback is not available
|
|
1152
487
|
*/
|
|
1153
488
|
async validateLicense(licenseKey, options = {}) {
|
|
1154
489
|
try {
|
|
@@ -1162,6 +497,9 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1162
497
|
}
|
|
1163
498
|
});
|
|
1164
499
|
const cachedLicense = this.cache.getLicense();
|
|
500
|
+
if ((!response.active_entitlements || response.active_entitlements.length === 0) && cachedLicense?.validation?.active_entitlements?.length) {
|
|
501
|
+
response.active_entitlements = cachedLicense.validation.active_entitlements;
|
|
502
|
+
}
|
|
1165
503
|
if (cachedLicense && cachedLicense.license_key === licenseKey) {
|
|
1166
504
|
this.cache.updateValidation(response);
|
|
1167
505
|
}
|
|
@@ -1173,36 +511,6 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1173
511
|
this.stopAutoValidation();
|
|
1174
512
|
this.currentAutoLicenseKey = null;
|
|
1175
513
|
}
|
|
1176
|
-
const isNetworkFailure = response instanceof TypeError && response.message.includes("fetch") || response instanceof APIError && [0, 408].includes(response.status);
|
|
1177
|
-
if (this.config.offlineFallbackEnabled && isNetworkFailure) {
|
|
1178
|
-
const offlineResult = await this.verifyCachedOffline();
|
|
1179
|
-
const duplicateSuccess = offlineResult.valid && this.lastOfflineValidation?.valid === true;
|
|
1180
|
-
const cachedLicense2 = this.cache.getLicense();
|
|
1181
|
-
if (cachedLicense2 && cachedLicense2.license_key === licenseKey) {
|
|
1182
|
-
this.cache.updateValidation(offlineResult);
|
|
1183
|
-
}
|
|
1184
|
-
if (offlineResult.valid) {
|
|
1185
|
-
if (!duplicateSuccess) {
|
|
1186
|
-
this.emit("validation:offline-success", offlineResult);
|
|
1187
|
-
}
|
|
1188
|
-
this.lastOfflineValidation = offlineResult;
|
|
1189
|
-
return offlineResult;
|
|
1190
|
-
} else {
|
|
1191
|
-
this.emit("validation:offline-failed", offlineResult);
|
|
1192
|
-
this.stopAutoValidation();
|
|
1193
|
-
this.currentAutoLicenseKey = null;
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
if (response instanceof APIError && response.data) {
|
|
1197
|
-
const cachedLicense2 = this.cache.getLicense();
|
|
1198
|
-
if (cachedLicense2 && cachedLicense2.license_key === licenseKey) {
|
|
1199
|
-
const invalidValidation = {
|
|
1200
|
-
valid: false,
|
|
1201
|
-
...response.data
|
|
1202
|
-
};
|
|
1203
|
-
this.cache.updateValidation(invalidValidation);
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
514
|
this.cache.setLastSeenTimestamp(Date.now());
|
|
1207
515
|
return response;
|
|
1208
516
|
} catch (error) {
|
|
@@ -1226,11 +534,7 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1226
534
|
if (error instanceof APIError && error.data) {
|
|
1227
535
|
const cachedLicense = this.cache.getLicense();
|
|
1228
536
|
if (cachedLicense && cachedLicense.license_key === licenseKey) {
|
|
1229
|
-
|
|
1230
|
-
valid: false,
|
|
1231
|
-
...error.data
|
|
1232
|
-
};
|
|
1233
|
-
this.cache.updateValidation(invalidValidation);
|
|
537
|
+
this.cache.updateValidation({ valid: false, ...error.data });
|
|
1234
538
|
}
|
|
1235
539
|
if (![0, 408, 429].includes(error.status)) {
|
|
1236
540
|
this.stopAutoValidation();
|
|
@@ -1241,9 +545,9 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1241
545
|
}
|
|
1242
546
|
}
|
|
1243
547
|
/**
|
|
1244
|
-
* Check if a specific entitlement is active
|
|
1245
|
-
* @param {string} entitlementKey - The entitlement to check
|
|
1246
|
-
* @returns {
|
|
548
|
+
* Check if a specific entitlement is active (detailed version)
|
|
549
|
+
* @param {string} entitlementKey - The entitlement key to check
|
|
550
|
+
* @returns {import('./types.js').EntitlementCheckResult} Entitlement status with details
|
|
1247
551
|
*/
|
|
1248
552
|
checkEntitlement(entitlementKey) {
|
|
1249
553
|
const license = this.cache.getLicense();
|
|
@@ -1269,23 +573,32 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1269
573
|
return { active: true, entitlement };
|
|
1270
574
|
}
|
|
1271
575
|
/**
|
|
1272
|
-
*
|
|
1273
|
-
* This
|
|
1274
|
-
*
|
|
576
|
+
* Check if a specific entitlement is active (simple boolean version)
|
|
577
|
+
* This is a convenience method that returns a simple boolean.
|
|
578
|
+
* Use checkEntitlement() for detailed status information.
|
|
579
|
+
* @param {string} entitlementKey - The entitlement key to check
|
|
580
|
+
* @returns {boolean} True if the entitlement is active, false otherwise
|
|
581
|
+
*/
|
|
582
|
+
hasEntitlement(entitlementKey) {
|
|
583
|
+
return this.checkEntitlement(entitlementKey).active;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Get offline license data from the server
|
|
587
|
+
* @returns {Promise<import('./types.js').SignedOfflineLicense>} Signed offline license data
|
|
588
|
+
* @throws {LicenseError} When no active license is found
|
|
589
|
+
* @throws {APIError} When the API request fails
|
|
1275
590
|
*/
|
|
1276
591
|
async getOfflineLicense() {
|
|
1277
592
|
const license = this.cache.getLicense();
|
|
1278
593
|
if (!license || !license.license_key) {
|
|
1279
594
|
const errorMsg = "No active license key found in cache to fetch offline license.";
|
|
1280
595
|
this.emit("sdk:error", { message: errorMsg });
|
|
1281
|
-
throw new
|
|
596
|
+
throw new LicenseError(errorMsg, "no_license");
|
|
1282
597
|
}
|
|
1283
598
|
try {
|
|
1284
599
|
this.emit("offlineLicense:fetching", { licenseKey: license.license_key });
|
|
1285
600
|
const path = `/licenses/${license.license_key}/offline_license`;
|
|
1286
|
-
const response = await this.apiCall(path, {
|
|
1287
|
-
method: "POST"
|
|
1288
|
-
});
|
|
601
|
+
const response = await this.apiCall(path, { method: "POST" });
|
|
1289
602
|
this.emit("offlineLicense:fetched", {
|
|
1290
603
|
licenseKey: license.license_key,
|
|
1291
604
|
data: response
|
|
@@ -1304,10 +617,10 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1304
617
|
}
|
|
1305
618
|
}
|
|
1306
619
|
/**
|
|
1307
|
-
*
|
|
1308
|
-
*
|
|
1309
|
-
* @
|
|
1310
|
-
* @
|
|
620
|
+
* Fetch a public key from the server by key ID
|
|
621
|
+
* @param {string} keyId - The Key ID (kid) for which to fetch the public key
|
|
622
|
+
* @returns {Promise<string>} Base64-encoded public key
|
|
623
|
+
* @throws {Error} When keyId is not provided or the key is not found
|
|
1311
624
|
*/
|
|
1312
625
|
async getPublicKey(keyId) {
|
|
1313
626
|
if (!keyId) {
|
|
@@ -1332,14 +645,12 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1332
645
|
}
|
|
1333
646
|
}
|
|
1334
647
|
/**
|
|
1335
|
-
*
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1339
|
-
* @
|
|
1340
|
-
* @
|
|
1341
|
-
* @returns {Promise<boolean>} True if verification is successful, false otherwise.
|
|
1342
|
-
* @throws {Error} if crypto library is not available or inputs are invalid.
|
|
648
|
+
* Verify a signed offline license client-side using Ed25519
|
|
649
|
+
* @param {import('./types.js').SignedOfflineLicense} signedLicenseData - The signed license data
|
|
650
|
+
* @param {string} publicKeyB64 - Base64-encoded public Ed25519 key
|
|
651
|
+
* @returns {Promise<boolean>} True if verification is successful
|
|
652
|
+
* @throws {CryptoError} When crypto library is not available
|
|
653
|
+
* @throws {Error} When inputs are invalid
|
|
1343
654
|
*/
|
|
1344
655
|
async verifyOfflineLicense(signedLicenseData, publicKeyB64) {
|
|
1345
656
|
this.log("Attempting to verify offline license client-side.");
|
|
@@ -1349,32 +660,19 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1349
660
|
if (!publicKeyB64) {
|
|
1350
661
|
throw new Error("Public key (Base64 encoded) is required.");
|
|
1351
662
|
}
|
|
1352
|
-
if (!
|
|
1353
|
-
|
|
1354
|
-
"Noble-ed25519 not properly initialized or sha512Sync not set. Please check imports and setup."
|
|
1355
|
-
);
|
|
1356
|
-
this.emit("sdk:error", {
|
|
1357
|
-
message: "Client-side verification crypto library (noble-ed25519) not available or configured."
|
|
1358
|
-
});
|
|
1359
|
-
throw new Error(
|
|
663
|
+
if (!ed || !ed.verify || !ed.etc.sha512Sync) {
|
|
664
|
+
const err = new CryptoError(
|
|
1360
665
|
"noble-ed25519 crypto library not available/configured for offline verification."
|
|
1361
666
|
);
|
|
667
|
+
this.emit("sdk:error", { message: err.message });
|
|
668
|
+
throw err;
|
|
1362
669
|
}
|
|
1363
670
|
try {
|
|
1364
|
-
const payloadString =
|
|
1365
|
-
signedLicenseData.payload
|
|
1366
|
-
);
|
|
671
|
+
const payloadString = canonicalJsonStringify(signedLicenseData.payload);
|
|
1367
672
|
const messageBytes = new TextEncoder().encode(payloadString);
|
|
1368
|
-
const publicKeyBytes =
|
|
1369
|
-
const signatureBytes =
|
|
1370
|
-
|
|
1371
|
-
);
|
|
1372
|
-
const isValid = verify(
|
|
1373
|
-
signatureBytes,
|
|
1374
|
-
// signature first for noble-ed25519
|
|
1375
|
-
messageBytes,
|
|
1376
|
-
publicKeyBytes
|
|
1377
|
-
);
|
|
673
|
+
const publicKeyBytes = base64UrlDecode(publicKeyB64);
|
|
674
|
+
const signatureBytes = base64UrlDecode(signedLicenseData.signature_b64u);
|
|
675
|
+
const isValid = ed.verify(signatureBytes, messageBytes, publicKeyBytes);
|
|
1378
676
|
if (isValid) {
|
|
1379
677
|
this.log(
|
|
1380
678
|
"Offline license signature VERIFIED successfully client-side."
|
|
@@ -1399,220 +697,90 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1399
697
|
}
|
|
1400
698
|
}
|
|
1401
699
|
/**
|
|
1402
|
-
*
|
|
1403
|
-
*
|
|
1404
|
-
* @param {Object} obj - The object to stringify.
|
|
1405
|
-
* @returns {string} Canonical JSON string.
|
|
700
|
+
* Get current license status
|
|
701
|
+
* @returns {import('./types.js').LicenseStatus} Current license status
|
|
1406
702
|
*/
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
);
|
|
1412
|
-
try {
|
|
1413
|
-
const sortedObj = {};
|
|
1414
|
-
Object.keys(obj).sort().forEach((key) => {
|
|
1415
|
-
sortedObj[key] = obj[key];
|
|
1416
|
-
});
|
|
1417
|
-
return JSON.stringify(sortedObj);
|
|
1418
|
-
} catch (e) {
|
|
1419
|
-
return JSON.stringify(obj);
|
|
1420
|
-
}
|
|
703
|
+
getStatus() {
|
|
704
|
+
const license = this.cache.getLicense();
|
|
705
|
+
if (!license) {
|
|
706
|
+
return { status: "inactive", message: "No license activated" };
|
|
1421
707
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
* Decodes a Base64URL string to a Uint8Array.
|
|
1426
|
-
* @param {string} base64UrlString - The Base64URL encoded string.
|
|
1427
|
-
* @returns {Uint8Array}
|
|
1428
|
-
*/
|
|
1429
|
-
base64UrlDecode(base64UrlString) {
|
|
1430
|
-
let base64 = base64UrlString.replace(/-/g, "+").replace(/_/g, "/");
|
|
1431
|
-
while (base64.length % 4) {
|
|
1432
|
-
base64 += "=";
|
|
708
|
+
const validation = license.validation;
|
|
709
|
+
if (!validation) {
|
|
710
|
+
return { status: "pending", message: "License pending validation" };
|
|
1433
711
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
712
|
+
if (!validation.valid) {
|
|
713
|
+
if (validation.offline) {
|
|
714
|
+
return {
|
|
715
|
+
status: "offline-invalid",
|
|
716
|
+
message: validation.reason_code || "License invalid (offline)"
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
return {
|
|
720
|
+
status: "invalid",
|
|
721
|
+
message: validation.reason || "License invalid"
|
|
722
|
+
};
|
|
1438
723
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
});
|
|
724
|
+
if (validation.offline) {
|
|
725
|
+
return {
|
|
726
|
+
status: "offline-valid",
|
|
727
|
+
license: license.license_key,
|
|
728
|
+
device: license.device_identifier,
|
|
729
|
+
activated_at: license.activated_at,
|
|
730
|
+
last_validated: license.last_validated,
|
|
731
|
+
entitlements: validation.active_entitlements || []
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
return {
|
|
735
|
+
status: "active",
|
|
736
|
+
license: license.license_key,
|
|
737
|
+
device: license.device_identifier,
|
|
738
|
+
activated_at: license.activated_at,
|
|
739
|
+
last_validated: license.last_validated,
|
|
740
|
+
entitlements: validation.active_entitlements || []
|
|
1457
741
|
};
|
|
1458
|
-
this.validationTimer = setInterval(
|
|
1459
|
-
performAndReschedule,
|
|
1460
|
-
validationInterval
|
|
1461
|
-
);
|
|
1462
|
-
this.emit("autovalidation:cycle", {
|
|
1463
|
-
nextRunAt: new Date(Date.now() + validationInterval)
|
|
1464
|
-
});
|
|
1465
742
|
}
|
|
1466
743
|
/**
|
|
1467
|
-
*
|
|
1468
|
-
*
|
|
744
|
+
* Test server authentication
|
|
745
|
+
* Useful for verifying API key/session is valid.
|
|
746
|
+
* @returns {Promise<Object>} Result from the server
|
|
747
|
+
* @throws {Error} When API key is not configured
|
|
748
|
+
* @throws {APIError} When authentication fails
|
|
1469
749
|
*/
|
|
1470
|
-
|
|
1471
|
-
if (this.
|
|
1472
|
-
|
|
1473
|
-
this.
|
|
1474
|
-
|
|
750
|
+
async testAuth() {
|
|
751
|
+
if (!this.config.apiKey) {
|
|
752
|
+
const err = new Error("API key is required for auth test");
|
|
753
|
+
this.emit("auth_test:error", { error: err });
|
|
754
|
+
throw err;
|
|
1475
755
|
}
|
|
1476
|
-
}
|
|
1477
|
-
/**
|
|
1478
|
-
* Generate a unique device identifier
|
|
1479
|
-
* @private
|
|
1480
|
-
*/
|
|
1481
|
-
generateDeviceId() {
|
|
1482
|
-
const nav = window.navigator;
|
|
1483
|
-
const screen = window.screen;
|
|
1484
|
-
const data = [
|
|
1485
|
-
nav.userAgent,
|
|
1486
|
-
nav.language,
|
|
1487
|
-
screen.colorDepth,
|
|
1488
|
-
screen.width + "x" + screen.height,
|
|
1489
|
-
(/* @__PURE__ */ new Date()).getTimezoneOffset(),
|
|
1490
|
-
nav.hardwareConcurrency,
|
|
1491
|
-
this.getCanvasFingerprint()
|
|
1492
|
-
].join("|");
|
|
1493
|
-
return `web-${this.hashCode(data)}-${Date.now().toString(36)}`;
|
|
1494
|
-
}
|
|
1495
|
-
/**
|
|
1496
|
-
* Get canvas fingerprint for device ID
|
|
1497
|
-
* @private
|
|
1498
|
-
*/
|
|
1499
|
-
getCanvasFingerprint() {
|
|
1500
756
|
try {
|
|
1501
|
-
|
|
1502
|
-
const
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
return "no-canvas";
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
/**
|
|
1512
|
-
* Simple hash function
|
|
1513
|
-
* @private
|
|
1514
|
-
*/
|
|
1515
|
-
hashCode(str2) {
|
|
1516
|
-
let hash = 0;
|
|
1517
|
-
for (let i = 0; i < str2.length; i++) {
|
|
1518
|
-
const char = str2.charCodeAt(i);
|
|
1519
|
-
hash = (hash << 5) - hash + char;
|
|
1520
|
-
hash = hash & hash;
|
|
1521
|
-
}
|
|
1522
|
-
return Math.abs(hash).toString(36);
|
|
1523
|
-
}
|
|
1524
|
-
/**
|
|
1525
|
-
* Make API call with retry logic
|
|
1526
|
-
* @private
|
|
1527
|
-
*/
|
|
1528
|
-
async apiCall(endpoint, options = {}) {
|
|
1529
|
-
const url = `${this.config.apiBaseUrl}${endpoint}`;
|
|
1530
|
-
let lastError;
|
|
1531
|
-
const headers = {
|
|
1532
|
-
"Content-Type": "application/json",
|
|
1533
|
-
Accept: "application/json",
|
|
1534
|
-
...options.headers
|
|
1535
|
-
};
|
|
1536
|
-
if (this.config.apiKey) {
|
|
1537
|
-
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
1538
|
-
} else {
|
|
1539
|
-
this.log(
|
|
1540
|
-
"[Warning] No API key configured for LicenseSeat SDK. Authenticated endpoints will fail."
|
|
1541
|
-
);
|
|
1542
|
-
}
|
|
1543
|
-
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
|
|
1544
|
-
try {
|
|
1545
|
-
const response = await fetch(url, {
|
|
1546
|
-
method: options.method || "GET",
|
|
1547
|
-
headers,
|
|
1548
|
-
// Use prepared headers
|
|
1549
|
-
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
1550
|
-
credentials: "omit"
|
|
1551
|
-
// Do NOT send cookies (session-agnostic)
|
|
1552
|
-
});
|
|
1553
|
-
const data = await response.json();
|
|
1554
|
-
if (!response.ok) {
|
|
1555
|
-
throw new APIError(
|
|
1556
|
-
data.error || "Request failed",
|
|
1557
|
-
response.status,
|
|
1558
|
-
data
|
|
1559
|
-
);
|
|
1560
|
-
}
|
|
1561
|
-
if (!this.online) {
|
|
1562
|
-
this.online = true;
|
|
1563
|
-
this.emit("network:online");
|
|
1564
|
-
}
|
|
1565
|
-
this.stopConnectivityPolling();
|
|
1566
|
-
if (!this.validationTimer && this.currentAutoLicenseKey) {
|
|
1567
|
-
this.startAutoValidation(this.currentAutoLicenseKey);
|
|
1568
|
-
}
|
|
1569
|
-
return data;
|
|
1570
|
-
} catch (error) {
|
|
1571
|
-
const networkFailure = error instanceof TypeError && error.message.includes("fetch") || error instanceof APIError && error.status === 0;
|
|
1572
|
-
if (networkFailure && this.online) {
|
|
1573
|
-
this.online = false;
|
|
1574
|
-
this.emit("network:offline", { error });
|
|
1575
|
-
this.stopAutoValidation();
|
|
1576
|
-
this.startConnectivityPolling();
|
|
1577
|
-
}
|
|
1578
|
-
lastError = error;
|
|
1579
|
-
const shouldRetry = attempt < this.config.maxRetries && this.shouldRetryError(error);
|
|
1580
|
-
if (shouldRetry) {
|
|
1581
|
-
const delay = this.config.retryDelay * Math.pow(2, attempt);
|
|
1582
|
-
this.log(
|
|
1583
|
-
`Retry attempt ${attempt + 1} after ${delay}ms for error:`,
|
|
1584
|
-
error.message
|
|
1585
|
-
);
|
|
1586
|
-
await this.sleep(delay);
|
|
1587
|
-
} else {
|
|
1588
|
-
throw error;
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
757
|
+
this.emit("auth_test:start");
|
|
758
|
+
const response = await this.apiCall("/auth_test", { method: "GET" });
|
|
759
|
+
this.emit("auth_test:success", response);
|
|
760
|
+
return response;
|
|
761
|
+
} catch (error) {
|
|
762
|
+
this.emit("auth_test:error", { error });
|
|
763
|
+
throw error;
|
|
1591
764
|
}
|
|
1592
|
-
throw lastError;
|
|
1593
765
|
}
|
|
1594
766
|
/**
|
|
1595
|
-
*
|
|
1596
|
-
* @
|
|
767
|
+
* Clear all data and reset SDK state
|
|
768
|
+
* @returns {void}
|
|
1597
769
|
*/
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
const status = error.status;
|
|
1604
|
-
if (status >= 502 && status < 600) {
|
|
1605
|
-
return true;
|
|
1606
|
-
}
|
|
1607
|
-
if (status === 0 || status === 408 || status === 429) {
|
|
1608
|
-
return true;
|
|
1609
|
-
}
|
|
1610
|
-
return false;
|
|
1611
|
-
}
|
|
1612
|
-
return false;
|
|
770
|
+
reset() {
|
|
771
|
+
this.stopAutoValidation();
|
|
772
|
+
this.cache.clear();
|
|
773
|
+
this.lastOfflineValidation = null;
|
|
774
|
+
this.emit("sdk:reset");
|
|
1613
775
|
}
|
|
776
|
+
// ============================================================
|
|
777
|
+
// Event Handling
|
|
778
|
+
// ============================================================
|
|
1614
779
|
/**
|
|
1615
|
-
*
|
|
780
|
+
* Subscribe to an event
|
|
781
|
+
* @param {string} event - Event name
|
|
782
|
+
* @param {import('./types.js').EventCallback} callback - Event handler
|
|
783
|
+
* @returns {import('./types.js').EventUnsubscribe} Unsubscribe function
|
|
1616
784
|
*/
|
|
1617
785
|
on(event, callback) {
|
|
1618
786
|
if (!this.eventListeners[event]) {
|
|
@@ -1621,6 +789,12 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1621
789
|
this.eventListeners[event].push(callback);
|
|
1622
790
|
return () => this.off(event, callback);
|
|
1623
791
|
}
|
|
792
|
+
/**
|
|
793
|
+
* Unsubscribe from an event
|
|
794
|
+
* @param {string} event - Event name
|
|
795
|
+
* @param {import('./types.js').EventCallback} callback - Event handler to remove
|
|
796
|
+
* @returns {void}
|
|
797
|
+
*/
|
|
1624
798
|
off(event, callback) {
|
|
1625
799
|
if (this.eventListeners[event]) {
|
|
1626
800
|
this.eventListeners[event] = this.eventListeners[event].filter(
|
|
@@ -1628,6 +802,13 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1628
802
|
);
|
|
1629
803
|
}
|
|
1630
804
|
}
|
|
805
|
+
/**
|
|
806
|
+
* Emit an event
|
|
807
|
+
* @param {string} event - Event name
|
|
808
|
+
* @param {*} data - Event data
|
|
809
|
+
* @returns {void}
|
|
810
|
+
* @private
|
|
811
|
+
*/
|
|
1631
812
|
emit(event, data) {
|
|
1632
813
|
this.log(`Event: ${event}`, data);
|
|
1633
814
|
if (this.eventListeners[event]) {
|
|
@@ -1640,94 +821,50 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1640
821
|
});
|
|
1641
822
|
}
|
|
1642
823
|
}
|
|
824
|
+
// ============================================================
|
|
825
|
+
// Auto-Validation & Connectivity
|
|
826
|
+
// ============================================================
|
|
1643
827
|
/**
|
|
1644
|
-
*
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
return token ? token.content : "";
|
|
1649
|
-
}
|
|
1650
|
-
sleep(ms) {
|
|
1651
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1652
|
-
}
|
|
1653
|
-
log(...args) {
|
|
1654
|
-
if (this.config.debug) {
|
|
1655
|
-
console.log("[LicenseSeat SDK]", ...args);
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
/**
|
|
1659
|
-
* Test server authentication by calling a simple endpoint that requires auth.
|
|
1660
|
-
* Useful for verifying API key/session is valid.
|
|
1661
|
-
* @returns {Promise<Object>} Result from the server
|
|
828
|
+
* Start automatic license validation
|
|
829
|
+
* @param {string} licenseKey - License key to validate
|
|
830
|
+
* @returns {void}
|
|
831
|
+
* @private
|
|
1662
832
|
*/
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
this.emit("
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
833
|
+
startAutoValidation(licenseKey) {
|
|
834
|
+
this.stopAutoValidation();
|
|
835
|
+
this.currentAutoLicenseKey = licenseKey;
|
|
836
|
+
const validationInterval = this.config.autoValidateInterval;
|
|
837
|
+
const performAndReschedule = () => {
|
|
838
|
+
this.validateLicense(licenseKey).catch((err) => {
|
|
839
|
+
this.log("Auto-validation failed:", err);
|
|
840
|
+
this.emit("validation:auto-failed", { licenseKey, error: err });
|
|
841
|
+
});
|
|
842
|
+
this.emit("autovalidation:cycle", {
|
|
843
|
+
nextRunAt: new Date(Date.now() + validationInterval)
|
|
844
|
+
});
|
|
845
|
+
};
|
|
846
|
+
this.validationTimer = setInterval(performAndReschedule, validationInterval);
|
|
847
|
+
this.emit("autovalidation:cycle", {
|
|
848
|
+
nextRunAt: new Date(Date.now() + validationInterval)
|
|
849
|
+
});
|
|
1678
850
|
}
|
|
1679
851
|
/**
|
|
1680
|
-
*
|
|
852
|
+
* Stop automatic validation
|
|
853
|
+
* @returns {void}
|
|
854
|
+
* @private
|
|
1681
855
|
*/
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
const validation = license.validation;
|
|
1688
|
-
if (!validation) {
|
|
1689
|
-
return { status: "pending", message: "License pending validation" };
|
|
1690
|
-
}
|
|
1691
|
-
if (!validation.valid) {
|
|
1692
|
-
if (validation.offline) {
|
|
1693
|
-
return {
|
|
1694
|
-
status: "offline-invalid",
|
|
1695
|
-
message: validation.reason_code || "License invalid (offline)"
|
|
1696
|
-
};
|
|
1697
|
-
}
|
|
1698
|
-
return {
|
|
1699
|
-
status: "invalid",
|
|
1700
|
-
message: validation.reason || "License invalid"
|
|
1701
|
-
};
|
|
1702
|
-
}
|
|
1703
|
-
if (validation.offline) {
|
|
1704
|
-
return {
|
|
1705
|
-
status: "offline-valid",
|
|
1706
|
-
license: license.license_key,
|
|
1707
|
-
device: license.device_identifier,
|
|
1708
|
-
activated_at: license.activated_at,
|
|
1709
|
-
last_validated: license.last_validated,
|
|
1710
|
-
entitlements: validation.active_entitlements || []
|
|
1711
|
-
};
|
|
856
|
+
stopAutoValidation() {
|
|
857
|
+
if (this.validationTimer) {
|
|
858
|
+
clearInterval(this.validationTimer);
|
|
859
|
+
this.validationTimer = null;
|
|
860
|
+
this.emit("autovalidation:stopped");
|
|
1712
861
|
}
|
|
1713
|
-
return {
|
|
1714
|
-
status: "active",
|
|
1715
|
-
license: license.license_key,
|
|
1716
|
-
device: license.device_identifier,
|
|
1717
|
-
activated_at: license.activated_at,
|
|
1718
|
-
last_validated: license.last_validated,
|
|
1719
|
-
entitlements: validation.active_entitlements || []
|
|
1720
|
-
};
|
|
1721
862
|
}
|
|
1722
863
|
/**
|
|
1723
|
-
*
|
|
864
|
+
* Start connectivity polling (when offline)
|
|
865
|
+
* @returns {void}
|
|
866
|
+
* @private
|
|
1724
867
|
*/
|
|
1725
|
-
reset() {
|
|
1726
|
-
this.stopAutoValidation();
|
|
1727
|
-
this.cache.clear();
|
|
1728
|
-
this.lastOfflineValidation = null;
|
|
1729
|
-
this.emit("sdk:reset");
|
|
1730
|
-
}
|
|
1731
868
|
startConnectivityPolling() {
|
|
1732
869
|
if (this.connectivityTimer)
|
|
1733
870
|
return;
|
|
@@ -1746,7 +883,7 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1746
883
|
this.syncOfflineAssets();
|
|
1747
884
|
}
|
|
1748
885
|
this.stopConnectivityPolling();
|
|
1749
|
-
} catch (
|
|
886
|
+
} catch (err) {
|
|
1750
887
|
}
|
|
1751
888
|
};
|
|
1752
889
|
this.connectivityTimer = setInterval(
|
|
@@ -1754,15 +891,23 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1754
891
|
this.config.networkRecheckInterval
|
|
1755
892
|
);
|
|
1756
893
|
}
|
|
894
|
+
/**
|
|
895
|
+
* Stop connectivity polling
|
|
896
|
+
* @returns {void}
|
|
897
|
+
* @private
|
|
898
|
+
*/
|
|
1757
899
|
stopConnectivityPolling() {
|
|
1758
900
|
if (this.connectivityTimer) {
|
|
1759
901
|
clearInterval(this.connectivityTimer);
|
|
1760
902
|
this.connectivityTimer = null;
|
|
1761
903
|
}
|
|
1762
904
|
}
|
|
905
|
+
// ============================================================
|
|
906
|
+
// Offline License Management
|
|
907
|
+
// ============================================================
|
|
1763
908
|
/**
|
|
1764
|
-
* Fetch
|
|
1765
|
-
*
|
|
909
|
+
* Fetch and cache offline license and public key
|
|
910
|
+
* @returns {Promise<void>}
|
|
1766
911
|
* @private
|
|
1767
912
|
*/
|
|
1768
913
|
async syncOfflineAssets() {
|
|
@@ -1781,12 +926,34 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1781
926
|
kid: offline.kid || offline.payload?.kid,
|
|
1782
927
|
exp_at: offline.payload?.exp_at
|
|
1783
928
|
});
|
|
1784
|
-
|
|
1785
|
-
|
|
929
|
+
const res = await this.quickVerifyCachedOfflineLocal();
|
|
930
|
+
if (res) {
|
|
931
|
+
this.cache.updateValidation(res);
|
|
932
|
+
this.emit(
|
|
933
|
+
res.valid ? "validation:offline-success" : "validation:offline-failed",
|
|
934
|
+
res
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
} catch (err) {
|
|
938
|
+
this.log("Failed to sync offline assets:", err);
|
|
1786
939
|
}
|
|
1787
940
|
}
|
|
1788
941
|
/**
|
|
1789
|
-
*
|
|
942
|
+
* Schedule periodic offline license refresh
|
|
943
|
+
* @returns {void}
|
|
944
|
+
* @private
|
|
945
|
+
*/
|
|
946
|
+
scheduleOfflineRefresh() {
|
|
947
|
+
if (this.offlineRefreshTimer)
|
|
948
|
+
clearInterval(this.offlineRefreshTimer);
|
|
949
|
+
this.offlineRefreshTimer = setInterval(
|
|
950
|
+
() => this.syncOfflineAssets(),
|
|
951
|
+
this.config.offlineLicenseRefreshInterval
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Verify cached offline license
|
|
956
|
+
* @returns {Promise<import('./types.js').ValidationResult>}
|
|
1790
957
|
* @private
|
|
1791
958
|
*/
|
|
1792
959
|
async verifyCachedOffline() {
|
|
@@ -1807,18 +974,11 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1807
974
|
try {
|
|
1808
975
|
const ok = await this.verifyOfflineLicense(signed, pub);
|
|
1809
976
|
if (!ok) {
|
|
1810
|
-
return {
|
|
1811
|
-
valid: false,
|
|
1812
|
-
offline: true,
|
|
1813
|
-
reason_code: "signature_invalid"
|
|
1814
|
-
};
|
|
977
|
+
return { valid: false, offline: true, reason_code: "signature_invalid" };
|
|
1815
978
|
}
|
|
1816
979
|
const payload = signed.payload || {};
|
|
1817
980
|
const cached = this.cache.getLicense();
|
|
1818
|
-
if (!cached || !
|
|
1819
|
-
payload.lic_k || "",
|
|
1820
|
-
cached.license_key || ""
|
|
1821
|
-
)) {
|
|
981
|
+
if (!cached || !constantTimeEqual(payload.lic_k || "", cached.license_key || "")) {
|
|
1822
982
|
return { valid: false, offline: true, reason_code: "license_mismatch" };
|
|
1823
983
|
}
|
|
1824
984
|
const now = Date.now();
|
|
@@ -1844,32 +1004,19 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1844
1004
|
return { valid: false, offline: true, reason_code: "clock_tamper" };
|
|
1845
1005
|
}
|
|
1846
1006
|
this.cache.setLastSeenTimestamp(now);
|
|
1847
|
-
|
|
1007
|
+
const active = parseActiveEntitlements(payload);
|
|
1008
|
+
return {
|
|
1009
|
+
valid: true,
|
|
1010
|
+
offline: true,
|
|
1011
|
+
...active.length ? { active_entitlements: active } : {}
|
|
1012
|
+
};
|
|
1848
1013
|
} catch (e) {
|
|
1849
1014
|
return { valid: false, offline: true, reason_code: "verification_error" };
|
|
1850
1015
|
}
|
|
1851
1016
|
}
|
|
1852
|
-
scheduleOfflineRefresh() {
|
|
1853
|
-
if (this.offlineRefreshTimer)
|
|
1854
|
-
clearInterval(this.offlineRefreshTimer);
|
|
1855
|
-
this.offlineRefreshTimer = setInterval(
|
|
1856
|
-
() => this.syncOfflineAssets(),
|
|
1857
|
-
this.config.offlineLicenseRefreshInterval
|
|
1858
|
-
);
|
|
1859
|
-
}
|
|
1860
|
-
// --- Utility: constant-time string comparison to mitigate timing attacks ---
|
|
1861
|
-
static constantTimeEqual(a = "", b = "") {
|
|
1862
|
-
if (a.length !== b.length)
|
|
1863
|
-
return false;
|
|
1864
|
-
let res = 0;
|
|
1865
|
-
for (let i = 0; i < a.length; i++) {
|
|
1866
|
-
res |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
1867
|
-
}
|
|
1868
|
-
return res === 0;
|
|
1869
|
-
}
|
|
1870
1017
|
/**
|
|
1871
|
-
*
|
|
1872
|
-
*
|
|
1018
|
+
* Quick offline verification using only local data (no network)
|
|
1019
|
+
* @returns {Promise<import('./types.js').ValidationResult|null>}
|
|
1873
1020
|
* @private
|
|
1874
1021
|
*/
|
|
1875
1022
|
async quickVerifyCachedOfflineLocal() {
|
|
@@ -1882,133 +1029,182 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1882
1029
|
return null;
|
|
1883
1030
|
try {
|
|
1884
1031
|
const ok = await this.verifyOfflineLicense(signed, pub);
|
|
1885
|
-
|
|
1032
|
+
if (!ok) {
|
|
1033
|
+
return { valid: false, offline: true, reason_code: "signature_invalid" };
|
|
1034
|
+
}
|
|
1035
|
+
const active = parseActiveEntitlements(signed.payload || {});
|
|
1036
|
+
return {
|
|
1037
|
+
valid: true,
|
|
1038
|
+
offline: true,
|
|
1039
|
+
...active.length ? { active_entitlements: active } : {}
|
|
1040
|
+
};
|
|
1886
1041
|
} catch (_) {
|
|
1887
1042
|
return { valid: false, offline: true, reason_code: "verification_error" };
|
|
1888
1043
|
}
|
|
1889
1044
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
this.setLicense(license);
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
getDeviceId() {
|
|
1921
|
-
const license = this.getLicense();
|
|
1922
|
-
return license ? license.device_identifier : null;
|
|
1923
|
-
}
|
|
1924
|
-
clearLicense() {
|
|
1925
|
-
localStorage.removeItem(this.prefix + "license");
|
|
1926
|
-
}
|
|
1927
|
-
// --- Offline license helpers ---
|
|
1928
|
-
getOfflineLicense() {
|
|
1929
|
-
try {
|
|
1930
|
-
const data = localStorage.getItem(this.prefix + "offline_license");
|
|
1931
|
-
return data ? JSON.parse(data) : null;
|
|
1932
|
-
} catch (e) {
|
|
1933
|
-
console.error("Failed to read offline license cache:", e);
|
|
1934
|
-
return null;
|
|
1935
|
-
}
|
|
1936
|
-
}
|
|
1937
|
-
setOfflineLicense(data) {
|
|
1938
|
-
try {
|
|
1939
|
-
localStorage.setItem(
|
|
1940
|
-
this.prefix + "offline_license",
|
|
1941
|
-
JSON.stringify(data)
|
|
1045
|
+
// ============================================================
|
|
1046
|
+
// API Communication
|
|
1047
|
+
// ============================================================
|
|
1048
|
+
/**
|
|
1049
|
+
* Make an API call with retry logic
|
|
1050
|
+
* @param {string} endpoint - API endpoint (will be appended to apiBaseUrl)
|
|
1051
|
+
* @param {Object} [options={}] - Fetch options
|
|
1052
|
+
* @param {string} [options.method="GET"] - HTTP method
|
|
1053
|
+
* @param {Object} [options.body] - Request body (will be JSON-stringified)
|
|
1054
|
+
* @param {Object} [options.headers] - Additional headers
|
|
1055
|
+
* @returns {Promise<Object>} API response data
|
|
1056
|
+
* @throws {APIError} When the request fails after all retries
|
|
1057
|
+
* @private
|
|
1058
|
+
*/
|
|
1059
|
+
async apiCall(endpoint, options = {}) {
|
|
1060
|
+
const url = `${this.config.apiBaseUrl}${endpoint}`;
|
|
1061
|
+
let lastError;
|
|
1062
|
+
const headers = {
|
|
1063
|
+
"Content-Type": "application/json",
|
|
1064
|
+
Accept: "application/json",
|
|
1065
|
+
...options.headers
|
|
1066
|
+
};
|
|
1067
|
+
if (this.config.apiKey) {
|
|
1068
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
1069
|
+
} else {
|
|
1070
|
+
this.log(
|
|
1071
|
+
"[Warning] No API key configured for LicenseSeat SDK. Authenticated endpoints will fail."
|
|
1942
1072
|
);
|
|
1943
|
-
} catch (e) {
|
|
1944
|
-
console.error("Failed to cache offline license:", e);
|
|
1945
1073
|
}
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1074
|
+
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
|
|
1075
|
+
try {
|
|
1076
|
+
const response = await fetch(url, {
|
|
1077
|
+
method: options.method || "GET",
|
|
1078
|
+
headers,
|
|
1079
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
1080
|
+
credentials: "omit"
|
|
1081
|
+
});
|
|
1082
|
+
const data = await response.json();
|
|
1083
|
+
if (!response.ok) {
|
|
1084
|
+
throw new APIError(
|
|
1085
|
+
data.error || "Request failed",
|
|
1086
|
+
response.status,
|
|
1087
|
+
data
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
if (!this.online) {
|
|
1091
|
+
this.online = true;
|
|
1092
|
+
this.emit("network:online");
|
|
1093
|
+
}
|
|
1094
|
+
this.stopConnectivityPolling();
|
|
1095
|
+
if (!this.validationTimer && this.currentAutoLicenseKey) {
|
|
1096
|
+
this.startAutoValidation(this.currentAutoLicenseKey);
|
|
1097
|
+
}
|
|
1098
|
+
return data;
|
|
1099
|
+
} catch (error) {
|
|
1100
|
+
const networkFailure = error instanceof TypeError && error.message.includes("fetch") || error instanceof APIError && error.status === 0;
|
|
1101
|
+
if (networkFailure && this.online) {
|
|
1102
|
+
this.online = false;
|
|
1103
|
+
this.emit("network:offline", { error });
|
|
1104
|
+
this.stopAutoValidation();
|
|
1105
|
+
this.startConnectivityPolling();
|
|
1106
|
+
}
|
|
1107
|
+
lastError = error;
|
|
1108
|
+
const shouldRetry = attempt < this.config.maxRetries && this.shouldRetryError(error);
|
|
1109
|
+
if (shouldRetry) {
|
|
1110
|
+
const delay = this.config.retryDelay * Math.pow(2, attempt);
|
|
1111
|
+
this.log(
|
|
1112
|
+
`Retry attempt ${attempt + 1} after ${delay}ms for error:`,
|
|
1113
|
+
error.message
|
|
1114
|
+
);
|
|
1115
|
+
await sleep(delay);
|
|
1116
|
+
} else {
|
|
1117
|
+
throw error;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1960
1120
|
}
|
|
1121
|
+
throw lastError;
|
|
1961
1122
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1123
|
+
/**
|
|
1124
|
+
* Determine if an error should be retried
|
|
1125
|
+
* @param {Error} error - The error to check
|
|
1126
|
+
* @returns {boolean} True if the error should trigger a retry
|
|
1127
|
+
* @private
|
|
1128
|
+
*/
|
|
1129
|
+
shouldRetryError(error) {
|
|
1130
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
1131
|
+
return true;
|
|
1971
1132
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
if (key.startsWith(this.prefix)) {
|
|
1977
|
-
localStorage.removeItem(key);
|
|
1133
|
+
if (error instanceof APIError) {
|
|
1134
|
+
const status = error.status;
|
|
1135
|
+
if (status >= 502 && status < 600) {
|
|
1136
|
+
return true;
|
|
1978
1137
|
}
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1138
|
+
if (status === 0 || status === 408 || status === 429) {
|
|
1139
|
+
return true;
|
|
1140
|
+
}
|
|
1141
|
+
return false;
|
|
1142
|
+
}
|
|
1143
|
+
return false;
|
|
1982
1144
|
}
|
|
1983
|
-
//
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1145
|
+
// ============================================================
|
|
1146
|
+
// Utilities
|
|
1147
|
+
// ============================================================
|
|
1148
|
+
/**
|
|
1149
|
+
* Get CSRF token from meta tag
|
|
1150
|
+
* @returns {string} CSRF token or empty string
|
|
1151
|
+
*/
|
|
1152
|
+
getCsrfToken() {
|
|
1153
|
+
return getCsrfToken();
|
|
1987
1154
|
}
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1155
|
+
/**
|
|
1156
|
+
* Log a message (if debug mode is enabled)
|
|
1157
|
+
* @param {...*} args - Arguments to log
|
|
1158
|
+
* @returns {void}
|
|
1159
|
+
* @private
|
|
1160
|
+
*/
|
|
1161
|
+
log(...args) {
|
|
1162
|
+
if (this.config.debug) {
|
|
1163
|
+
console.log("[LicenseSeat SDK]", ...args);
|
|
1992
1164
|
}
|
|
1993
1165
|
}
|
|
1994
1166
|
};
|
|
1995
|
-
var
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
this.status = status;
|
|
2000
|
-
this.data = data;
|
|
1167
|
+
var sharedInstance = null;
|
|
1168
|
+
function getSharedInstance(config) {
|
|
1169
|
+
if (!sharedInstance) {
|
|
1170
|
+
sharedInstance = new LicenseSeatSDK(config);
|
|
2001
1171
|
}
|
|
2002
|
-
|
|
1172
|
+
return sharedInstance;
|
|
1173
|
+
}
|
|
1174
|
+
function configure(config, force = false) {
|
|
1175
|
+
if (sharedInstance && !force) {
|
|
1176
|
+
console.warn(
|
|
1177
|
+
"[LicenseSeat SDK] Already configured. Call configure with force=true to reconfigure."
|
|
1178
|
+
);
|
|
1179
|
+
return sharedInstance;
|
|
1180
|
+
}
|
|
1181
|
+
sharedInstance = new LicenseSeatSDK(config);
|
|
1182
|
+
return sharedInstance;
|
|
1183
|
+
}
|
|
1184
|
+
function resetSharedInstance() {
|
|
1185
|
+
if (sharedInstance) {
|
|
1186
|
+
sharedInstance.reset();
|
|
1187
|
+
sharedInstance = null;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
// src/index.js
|
|
2003
1192
|
var src_default = LicenseSeatSDK;
|
|
2004
1193
|
export {
|
|
2005
|
-
|
|
1194
|
+
APIError,
|
|
1195
|
+
ConfigurationError,
|
|
1196
|
+
CryptoError,
|
|
1197
|
+
LicenseCache,
|
|
1198
|
+
LicenseError,
|
|
1199
|
+
LicenseSeatSDK,
|
|
1200
|
+
base64UrlDecode,
|
|
1201
|
+
canonicalJsonStringify,
|
|
1202
|
+
configure,
|
|
1203
|
+
constantTimeEqual,
|
|
1204
|
+
src_default as default,
|
|
1205
|
+
generateDeviceId,
|
|
1206
|
+
getCsrfToken,
|
|
1207
|
+
getSharedInstance,
|
|
1208
|
+
parseActiveEntitlements,
|
|
1209
|
+
resetSharedInstance
|
|
2006
1210
|
};
|
|
2007
|
-
/*! Bundled license information:
|
|
2008
|
-
|
|
2009
|
-
@noble/ed25519/index.js:
|
|
2010
|
-
(*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) *)
|
|
2011
|
-
|
|
2012
|
-
@noble/hashes/esm/utils.js:
|
|
2013
|
-
(*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
2014
|
-
*/
|