@licenseseat/js 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +684 -83
- package/dist/index.js +775 -1526
- package/dist/types/LicenseSeat.d.ts +309 -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 +342 -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 +1247 -0
- package/src/cache.js +189 -0
- package/src/errors.js +77 -0
- package/src/index.js +62 -0
- package/src/types.js +148 -0
- package/src/utils.js +154 -0
package/dist/index.js
CHANGED
|
@@ -1,1019 +1,345 @@
|
|
|
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
|
+
expires_at: e.expires_at ?? null,
|
|
225
|
+
metadata: e.metadata ?? null
|
|
226
|
+
}));
|
|
517
227
|
}
|
|
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);
|
|
228
|
+
function constantTimeEqual(a = "", b = "") {
|
|
229
|
+
if (a.length !== b.length)
|
|
230
|
+
return false;
|
|
231
|
+
let res = 0;
|
|
232
|
+
for (let i = 0; i < a.length; i++) {
|
|
233
|
+
res |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
534
234
|
}
|
|
235
|
+
return res === 0;
|
|
535
236
|
}
|
|
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;
|
|
237
|
+
function canonicalJsonStringify(obj) {
|
|
238
|
+
const stringify = typeof CJSON === "function" ? CJSON : CJSON && typeof CJSON === "object" ? (
|
|
239
|
+
/** @type {any} */
|
|
240
|
+
CJSON.stringify
|
|
241
|
+
) : void 0;
|
|
242
|
+
if (!stringify || typeof stringify !== "function") {
|
|
243
|
+
console.warn(
|
|
244
|
+
"[LicenseSeat SDK] canonical-json library not loaded correctly. Falling back to basic JSON.stringify. Signature verification might be unreliable if server uses different canonicalization."
|
|
245
|
+
);
|
|
246
|
+
try {
|
|
247
|
+
const sortedObj = {};
|
|
248
|
+
Object.keys(obj).sort().forEach((key) => {
|
|
249
|
+
sortedObj[key] = obj[key];
|
|
250
|
+
});
|
|
251
|
+
return JSON.stringify(sortedObj);
|
|
252
|
+
} catch (e) {
|
|
253
|
+
return JSON.stringify(obj);
|
|
625
254
|
}
|
|
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
|
-
}
|
|
641
|
-
digest() {
|
|
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
255
|
}
|
|
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 };
|
|
256
|
+
return stringify(obj);
|
|
690
257
|
}
|
|
691
|
-
function
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
for (let i = 0; i < len; i++) {
|
|
696
|
-
const { h: h2, l } = fromBig(lst[i], le);
|
|
697
|
-
[Ah[i], Al[i]] = [h2, l];
|
|
258
|
+
function base64UrlDecode(base64UrlString) {
|
|
259
|
+
let base64 = base64UrlString.replace(/-/g, "+").replace(/_/g, "/");
|
|
260
|
+
while (base64.length % 4) {
|
|
261
|
+
base64 += "=";
|
|
698
262
|
}
|
|
699
|
-
|
|
263
|
+
const raw = window.atob(base64);
|
|
264
|
+
const outputArray = new Uint8Array(raw.length);
|
|
265
|
+
for (let i = 0; i < raw.length; ++i) {
|
|
266
|
+
outputArray[i] = raw.charCodeAt(i);
|
|
267
|
+
}
|
|
268
|
+
return outputArray;
|
|
700
269
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
return { h: Ah + Bh + (l / 2 ** 32 | 0) | 0, l: l | 0 };
|
|
270
|
+
function hashCode(str) {
|
|
271
|
+
let hash = 0;
|
|
272
|
+
for (let i = 0; i < str.length; i++) {
|
|
273
|
+
const char = str.charCodeAt(i);
|
|
274
|
+
hash = (hash << 5) - hash + char;
|
|
275
|
+
hash = hash & hash;
|
|
276
|
+
}
|
|
277
|
+
return Math.abs(hash).toString(36);
|
|
710
278
|
}
|
|
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);
|
|
279
|
+
function getCanvasFingerprint() {
|
|
280
|
+
try {
|
|
281
|
+
const canvas = document.createElement("canvas");
|
|
282
|
+
const ctx = canvas.getContext("2d");
|
|
283
|
+
ctx.textBaseline = "top";
|
|
284
|
+
ctx.font = "14px Arial";
|
|
285
|
+
ctx.fillText("LicenseSeat SDK", 2, 2);
|
|
286
|
+
return canvas.toDataURL().slice(-50);
|
|
287
|
+
} catch (e) {
|
|
288
|
+
return "no-canvas";
|
|
914
289
|
}
|
|
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
290
|
}
|
|
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
|
-
}
|
|
291
|
+
function generateDeviceId() {
|
|
292
|
+
const nav = window.navigator;
|
|
293
|
+
const screen = window.screen;
|
|
294
|
+
const data = [
|
|
295
|
+
nav.userAgent,
|
|
296
|
+
nav.language,
|
|
297
|
+
screen.colorDepth,
|
|
298
|
+
screen.width + "x" + screen.height,
|
|
299
|
+
(/* @__PURE__ */ new Date()).getTimezoneOffset(),
|
|
300
|
+
nav.hardwareConcurrency,
|
|
301
|
+
getCanvasFingerprint()
|
|
302
|
+
].join("|");
|
|
303
|
+
return `web-${hashCode(data)}-${Date.now().toString(36)}`;
|
|
977
304
|
}
|
|
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 });
|
|
305
|
+
function sleep(ms) {
|
|
306
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
307
|
+
}
|
|
308
|
+
function getCsrfToken() {
|
|
309
|
+
const token = document.querySelector('meta[name="csrf-token"]');
|
|
310
|
+
return token ? token.content : "";
|
|
989
311
|
}
|
|
990
312
|
|
|
991
|
-
//
|
|
992
|
-
var
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
313
|
+
// src/LicenseSeat.js
|
|
314
|
+
var DEFAULT_CONFIG = {
|
|
315
|
+
apiBaseUrl: "https://licenseseat.com/api",
|
|
316
|
+
storagePrefix: "licenseseat_",
|
|
317
|
+
autoValidateInterval: 36e5,
|
|
318
|
+
// 1 hour
|
|
319
|
+
networkRecheckInterval: 3e4,
|
|
320
|
+
// 30 seconds
|
|
321
|
+
maxRetries: 3,
|
|
322
|
+
retryDelay: 1e3,
|
|
323
|
+
apiKey: null,
|
|
324
|
+
debug: false,
|
|
325
|
+
offlineLicenseRefreshInterval: 1e3 * 60 * 60 * 72,
|
|
326
|
+
// 72 hours
|
|
327
|
+
offlineFallbackEnabled: false,
|
|
328
|
+
// default false (strict mode, matches Swift SDK)
|
|
329
|
+
maxOfflineDays: 0,
|
|
330
|
+
// 0 = disabled
|
|
331
|
+
maxClockSkewMs: 5 * 60 * 1e3,
|
|
332
|
+
// 5 minutes
|
|
333
|
+
autoInitialize: true
|
|
334
|
+
};
|
|
335
|
+
var LicenseSeatSDK = class {
|
|
336
|
+
/**
|
|
337
|
+
* Create a new LicenseSeat SDK instance
|
|
338
|
+
* @param {import('./types.js').LicenseSeatConfig} [config={}] - Configuration options
|
|
339
|
+
*/
|
|
996
340
|
constructor(config = {}) {
|
|
997
341
|
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
|
|
342
|
+
...DEFAULT_CONFIG,
|
|
1017
343
|
...config
|
|
1018
344
|
};
|
|
1019
345
|
this.eventListeners = {};
|
|
@@ -1024,15 +350,25 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1024
350
|
this.connectivityTimer = null;
|
|
1025
351
|
this.offlineRefreshTimer = null;
|
|
1026
352
|
this.lastOfflineValidation = null;
|
|
1027
|
-
|
|
1028
|
-
|
|
353
|
+
this.syncingOfflineAssets = false;
|
|
354
|
+
this.destroyed = false;
|
|
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,13 +481,14 @@ 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 {
|
|
1155
490
|
this.emit("validation:start", { licenseKey });
|
|
1156
|
-
const
|
|
491
|
+
const rawResponse = await this.apiCall("/licenses/validate", {
|
|
1157
492
|
method: "POST",
|
|
1158
493
|
body: {
|
|
1159
494
|
license_key: licenseKey,
|
|
@@ -1161,7 +496,14 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1161
496
|
product_slug: options.productSlug
|
|
1162
497
|
}
|
|
1163
498
|
});
|
|
499
|
+
const response = {
|
|
500
|
+
valid: rawResponse.valid,
|
|
501
|
+
...rawResponse.license || {}
|
|
502
|
+
};
|
|
1164
503
|
const cachedLicense = this.cache.getLicense();
|
|
504
|
+
if ((!response.active_entitlements || response.active_entitlements.length === 0) && cachedLicense?.validation?.active_entitlements?.length) {
|
|
505
|
+
response.active_entitlements = cachedLicense.validation.active_entitlements;
|
|
506
|
+
}
|
|
1165
507
|
if (cachedLicense && cachedLicense.license_key === licenseKey) {
|
|
1166
508
|
this.cache.updateValidation(response);
|
|
1167
509
|
}
|
|
@@ -1173,36 +515,6 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1173
515
|
this.stopAutoValidation();
|
|
1174
516
|
this.currentAutoLicenseKey = null;
|
|
1175
517
|
}
|
|
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
518
|
this.cache.setLastSeenTimestamp(Date.now());
|
|
1207
519
|
return response;
|
|
1208
520
|
} catch (error) {
|
|
@@ -1226,11 +538,7 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1226
538
|
if (error instanceof APIError && error.data) {
|
|
1227
539
|
const cachedLicense = this.cache.getLicense();
|
|
1228
540
|
if (cachedLicense && cachedLicense.license_key === licenseKey) {
|
|
1229
|
-
|
|
1230
|
-
valid: false,
|
|
1231
|
-
...error.data
|
|
1232
|
-
};
|
|
1233
|
-
this.cache.updateValidation(invalidValidation);
|
|
541
|
+
this.cache.updateValidation({ valid: false, ...error.data });
|
|
1234
542
|
}
|
|
1235
543
|
if (![0, 408, 429].includes(error.status)) {
|
|
1236
544
|
this.stopAutoValidation();
|
|
@@ -1241,9 +549,9 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1241
549
|
}
|
|
1242
550
|
}
|
|
1243
551
|
/**
|
|
1244
|
-
* Check if a specific entitlement is active
|
|
1245
|
-
* @param {string} entitlementKey - The entitlement to check
|
|
1246
|
-
* @returns {
|
|
552
|
+
* Check if a specific entitlement is active (detailed version)
|
|
553
|
+
* @param {string} entitlementKey - The entitlement key to check
|
|
554
|
+
* @returns {import('./types.js').EntitlementCheckResult} Entitlement status with details
|
|
1247
555
|
*/
|
|
1248
556
|
checkEntitlement(entitlementKey) {
|
|
1249
557
|
const license = this.cache.getLicense();
|
|
@@ -1269,23 +577,32 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1269
577
|
return { active: true, entitlement };
|
|
1270
578
|
}
|
|
1271
579
|
/**
|
|
1272
|
-
*
|
|
1273
|
-
* This
|
|
1274
|
-
*
|
|
580
|
+
* Check if a specific entitlement is active (simple boolean version)
|
|
581
|
+
* This is a convenience method that returns a simple boolean.
|
|
582
|
+
* Use checkEntitlement() for detailed status information.
|
|
583
|
+
* @param {string} entitlementKey - The entitlement key to check
|
|
584
|
+
* @returns {boolean} True if the entitlement is active, false otherwise
|
|
585
|
+
*/
|
|
586
|
+
hasEntitlement(entitlementKey) {
|
|
587
|
+
return this.checkEntitlement(entitlementKey).active;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Get offline license data from the server
|
|
591
|
+
* @returns {Promise<import('./types.js').SignedOfflineLicense>} Signed offline license data
|
|
592
|
+
* @throws {LicenseError} When no active license is found
|
|
593
|
+
* @throws {APIError} When the API request fails
|
|
1275
594
|
*/
|
|
1276
595
|
async getOfflineLicense() {
|
|
1277
596
|
const license = this.cache.getLicense();
|
|
1278
597
|
if (!license || !license.license_key) {
|
|
1279
598
|
const errorMsg = "No active license key found in cache to fetch offline license.";
|
|
1280
599
|
this.emit("sdk:error", { message: errorMsg });
|
|
1281
|
-
throw new
|
|
600
|
+
throw new LicenseError(errorMsg, "no_license");
|
|
1282
601
|
}
|
|
1283
602
|
try {
|
|
1284
603
|
this.emit("offlineLicense:fetching", { licenseKey: license.license_key });
|
|
1285
604
|
const path = `/licenses/${license.license_key}/offline_license`;
|
|
1286
|
-
const response = await this.apiCall(path, {
|
|
1287
|
-
method: "POST"
|
|
1288
|
-
});
|
|
605
|
+
const response = await this.apiCall(path, { method: "POST" });
|
|
1289
606
|
this.emit("offlineLicense:fetched", {
|
|
1290
607
|
licenseKey: license.license_key,
|
|
1291
608
|
data: response
|
|
@@ -1304,10 +621,10 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1304
621
|
}
|
|
1305
622
|
}
|
|
1306
623
|
/**
|
|
1307
|
-
*
|
|
1308
|
-
*
|
|
1309
|
-
* @
|
|
1310
|
-
* @
|
|
624
|
+
* Fetch a public key from the server by key ID
|
|
625
|
+
* @param {string} keyId - The Key ID (kid) for which to fetch the public key
|
|
626
|
+
* @returns {Promise<string>} Base64-encoded public key
|
|
627
|
+
* @throws {Error} When keyId is not provided or the key is not found
|
|
1311
628
|
*/
|
|
1312
629
|
async getPublicKey(keyId) {
|
|
1313
630
|
if (!keyId) {
|
|
@@ -1332,14 +649,12 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1332
649
|
}
|
|
1333
650
|
}
|
|
1334
651
|
/**
|
|
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.
|
|
652
|
+
* Verify a signed offline license client-side using Ed25519
|
|
653
|
+
* @param {import('./types.js').SignedOfflineLicense} signedLicenseData - The signed license data
|
|
654
|
+
* @param {string} publicKeyB64 - Base64-encoded public Ed25519 key
|
|
655
|
+
* @returns {Promise<boolean>} True if verification is successful
|
|
656
|
+
* @throws {CryptoError} When crypto library is not available
|
|
657
|
+
* @throws {Error} When inputs are invalid
|
|
1343
658
|
*/
|
|
1344
659
|
async verifyOfflineLicense(signedLicenseData, publicKeyB64) {
|
|
1345
660
|
this.log("Attempting to verify offline license client-side.");
|
|
@@ -1349,32 +664,19 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1349
664
|
if (!publicKeyB64) {
|
|
1350
665
|
throw new Error("Public key (Base64 encoded) is required.");
|
|
1351
666
|
}
|
|
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(
|
|
667
|
+
if (!ed || !ed.verify || !ed.etc.sha512Sync) {
|
|
668
|
+
const err = new CryptoError(
|
|
1360
669
|
"noble-ed25519 crypto library not available/configured for offline verification."
|
|
1361
670
|
);
|
|
671
|
+
this.emit("sdk:error", { message: err.message });
|
|
672
|
+
throw err;
|
|
1362
673
|
}
|
|
1363
674
|
try {
|
|
1364
|
-
const payloadString =
|
|
1365
|
-
signedLicenseData.payload
|
|
1366
|
-
);
|
|
675
|
+
const payloadString = canonicalJsonStringify(signedLicenseData.payload);
|
|
1367
676
|
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
|
-
);
|
|
677
|
+
const publicKeyBytes = base64UrlDecode(publicKeyB64);
|
|
678
|
+
const signatureBytes = base64UrlDecode(signedLicenseData.signature_b64u);
|
|
679
|
+
const isValid = ed.verify(signatureBytes, messageBytes, publicKeyBytes);
|
|
1378
680
|
if (isValid) {
|
|
1379
681
|
this.log(
|
|
1380
682
|
"Offline license signature VERIFIED successfully client-side."
|
|
@@ -1399,47 +701,163 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1399
701
|
}
|
|
1400
702
|
}
|
|
1401
703
|
/**
|
|
1402
|
-
*
|
|
1403
|
-
*
|
|
1404
|
-
* @param {Object} obj - The object to stringify.
|
|
1405
|
-
* @returns {string} Canonical JSON string.
|
|
704
|
+
* Get current license status
|
|
705
|
+
* @returns {import('./types.js').LicenseStatus} Current license status
|
|
1406
706
|
*/
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
707
|
+
getStatus() {
|
|
708
|
+
const license = this.cache.getLicense();
|
|
709
|
+
if (!license) {
|
|
710
|
+
return { status: "inactive", message: "No license activated" };
|
|
711
|
+
}
|
|
712
|
+
const validation = license.validation;
|
|
713
|
+
if (!validation) {
|
|
714
|
+
return { status: "pending", message: "License pending validation" };
|
|
715
|
+
}
|
|
716
|
+
if (!validation.valid) {
|
|
717
|
+
if (validation.offline) {
|
|
718
|
+
return {
|
|
719
|
+
status: "offline-invalid",
|
|
720
|
+
message: validation.reason_code || "License invalid (offline)"
|
|
721
|
+
};
|
|
1420
722
|
}
|
|
723
|
+
return {
|
|
724
|
+
status: "invalid",
|
|
725
|
+
message: validation.reason || "License invalid"
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
if (validation.offline) {
|
|
729
|
+
return {
|
|
730
|
+
status: "offline-valid",
|
|
731
|
+
license: license.license_key,
|
|
732
|
+
device: license.device_identifier,
|
|
733
|
+
activated_at: license.activated_at,
|
|
734
|
+
last_validated: license.last_validated,
|
|
735
|
+
entitlements: validation.active_entitlements || []
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
return {
|
|
739
|
+
status: "active",
|
|
740
|
+
license: license.license_key,
|
|
741
|
+
device: license.device_identifier,
|
|
742
|
+
activated_at: license.activated_at,
|
|
743
|
+
last_validated: license.last_validated,
|
|
744
|
+
entitlements: validation.active_entitlements || []
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Test server authentication
|
|
749
|
+
* Useful for verifying API key/session is valid.
|
|
750
|
+
* @returns {Promise<Object>} Result from the server
|
|
751
|
+
* @throws {Error} When API key is not configured
|
|
752
|
+
* @throws {APIError} When authentication fails
|
|
753
|
+
*/
|
|
754
|
+
async testAuth() {
|
|
755
|
+
if (!this.config.apiKey) {
|
|
756
|
+
const err = new Error("API key is required for auth test");
|
|
757
|
+
this.emit("auth_test:error", { error: err });
|
|
758
|
+
throw err;
|
|
759
|
+
}
|
|
760
|
+
try {
|
|
761
|
+
this.emit("auth_test:start");
|
|
762
|
+
const response = await this.apiCall("/auth_test", { method: "GET" });
|
|
763
|
+
this.emit("auth_test:success", response);
|
|
764
|
+
return response;
|
|
765
|
+
} catch (error) {
|
|
766
|
+
this.emit("auth_test:error", { error });
|
|
767
|
+
throw error;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Clear all data and reset SDK state
|
|
772
|
+
* @returns {void}
|
|
773
|
+
*/
|
|
774
|
+
reset() {
|
|
775
|
+
this.stopAutoValidation();
|
|
776
|
+
this.stopConnectivityPolling();
|
|
777
|
+
if (this.offlineRefreshTimer) {
|
|
778
|
+
clearInterval(this.offlineRefreshTimer);
|
|
779
|
+
this.offlineRefreshTimer = null;
|
|
780
|
+
}
|
|
781
|
+
this.cache.clear();
|
|
782
|
+
this.lastOfflineValidation = null;
|
|
783
|
+
this.currentAutoLicenseKey = null;
|
|
784
|
+
this.emit("sdk:reset");
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Destroy the SDK instance and release all resources
|
|
788
|
+
* Call this when you no longer need the SDK to prevent memory leaks.
|
|
789
|
+
* After calling destroy(), the SDK instance should not be used.
|
|
790
|
+
* @returns {void}
|
|
791
|
+
*/
|
|
792
|
+
destroy() {
|
|
793
|
+
this.destroyed = true;
|
|
794
|
+
this.stopAutoValidation();
|
|
795
|
+
this.stopConnectivityPolling();
|
|
796
|
+
if (this.offlineRefreshTimer) {
|
|
797
|
+
clearInterval(this.offlineRefreshTimer);
|
|
798
|
+
this.offlineRefreshTimer = null;
|
|
799
|
+
}
|
|
800
|
+
this.eventListeners = {};
|
|
801
|
+
this.cache.clear();
|
|
802
|
+
this.lastOfflineValidation = null;
|
|
803
|
+
this.currentAutoLicenseKey = null;
|
|
804
|
+
this.emit("sdk:destroyed");
|
|
805
|
+
}
|
|
806
|
+
// ============================================================
|
|
807
|
+
// Event Handling
|
|
808
|
+
// ============================================================
|
|
809
|
+
/**
|
|
810
|
+
* Subscribe to an event
|
|
811
|
+
* @param {string} event - Event name
|
|
812
|
+
* @param {import('./types.js').EventCallback} callback - Event handler
|
|
813
|
+
* @returns {import('./types.js').EventUnsubscribe} Unsubscribe function
|
|
814
|
+
*/
|
|
815
|
+
on(event, callback) {
|
|
816
|
+
if (!this.eventListeners[event]) {
|
|
817
|
+
this.eventListeners[event] = [];
|
|
1421
818
|
}
|
|
1422
|
-
|
|
819
|
+
this.eventListeners[event].push(callback);
|
|
820
|
+
return () => this.off(event, callback);
|
|
1423
821
|
}
|
|
1424
822
|
/**
|
|
1425
|
-
*
|
|
1426
|
-
* @param {string}
|
|
1427
|
-
* @
|
|
823
|
+
* Unsubscribe from an event
|
|
824
|
+
* @param {string} event - Event name
|
|
825
|
+
* @param {import('./types.js').EventCallback} callback - Event handler to remove
|
|
826
|
+
* @returns {void}
|
|
1428
827
|
*/
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
828
|
+
off(event, callback) {
|
|
829
|
+
if (this.eventListeners[event]) {
|
|
830
|
+
this.eventListeners[event] = this.eventListeners[event].filter(
|
|
831
|
+
(cb) => cb !== callback
|
|
832
|
+
);
|
|
1433
833
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Emit an event
|
|
837
|
+
* @param {string} event - Event name
|
|
838
|
+
* @param {*} data - Event data
|
|
839
|
+
* @returns {void}
|
|
840
|
+
* @private
|
|
841
|
+
*/
|
|
842
|
+
emit(event, data) {
|
|
843
|
+
this.log(`Event: ${event}`, data);
|
|
844
|
+
if (this.eventListeners[event]) {
|
|
845
|
+
this.eventListeners[event].forEach((callback) => {
|
|
846
|
+
try {
|
|
847
|
+
callback(data);
|
|
848
|
+
} catch (error) {
|
|
849
|
+
console.error(`Error in event listener for ${event}:`, error);
|
|
850
|
+
}
|
|
851
|
+
});
|
|
1438
852
|
}
|
|
1439
|
-
return outputArray;
|
|
1440
853
|
}
|
|
854
|
+
// ============================================================
|
|
855
|
+
// Auto-Validation & Connectivity
|
|
856
|
+
// ============================================================
|
|
1441
857
|
/**
|
|
1442
858
|
* Start automatic license validation
|
|
859
|
+
* @param {string} licenseKey - License key to validate
|
|
860
|
+
* @returns {void}
|
|
1443
861
|
* @private
|
|
1444
862
|
*/
|
|
1445
863
|
startAutoValidation(licenseKey) {
|
|
@@ -1447,24 +865,22 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1447
865
|
this.currentAutoLicenseKey = licenseKey;
|
|
1448
866
|
const validationInterval = this.config.autoValidateInterval;
|
|
1449
867
|
const performAndReschedule = () => {
|
|
1450
|
-
this.validateLicense(licenseKey).catch((
|
|
1451
|
-
this.log("Auto-validation failed:",
|
|
1452
|
-
this.emit("validation:auto-failed", { licenseKey, error:
|
|
868
|
+
this.validateLicense(licenseKey).catch((err) => {
|
|
869
|
+
this.log("Auto-validation failed:", err);
|
|
870
|
+
this.emit("validation:auto-failed", { licenseKey, error: err });
|
|
1453
871
|
});
|
|
1454
872
|
this.emit("autovalidation:cycle", {
|
|
1455
873
|
nextRunAt: new Date(Date.now() + validationInterval)
|
|
1456
874
|
});
|
|
1457
875
|
};
|
|
1458
|
-
this.validationTimer = setInterval(
|
|
1459
|
-
performAndReschedule,
|
|
1460
|
-
validationInterval
|
|
1461
|
-
);
|
|
876
|
+
this.validationTimer = setInterval(performAndReschedule, validationInterval);
|
|
1462
877
|
this.emit("autovalidation:cycle", {
|
|
1463
878
|
nextRunAt: new Date(Date.now() + validationInterval)
|
|
1464
879
|
});
|
|
1465
880
|
}
|
|
1466
881
|
/**
|
|
1467
882
|
* Stop automatic validation
|
|
883
|
+
* @returns {void}
|
|
1468
884
|
* @private
|
|
1469
885
|
*/
|
|
1470
886
|
stopAutoValidation() {
|
|
@@ -1475,54 +891,222 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1475
891
|
}
|
|
1476
892
|
}
|
|
1477
893
|
/**
|
|
1478
|
-
*
|
|
894
|
+
* Start connectivity polling (when offline)
|
|
895
|
+
* @returns {void}
|
|
896
|
+
* @private
|
|
897
|
+
*/
|
|
898
|
+
startConnectivityPolling() {
|
|
899
|
+
if (this.connectivityTimer)
|
|
900
|
+
return;
|
|
901
|
+
const heartbeat = async () => {
|
|
902
|
+
try {
|
|
903
|
+
await fetch(`${this.config.apiBaseUrl}/heartbeat`, {
|
|
904
|
+
method: "GET",
|
|
905
|
+
credentials: "omit"
|
|
906
|
+
});
|
|
907
|
+
if (!this.online) {
|
|
908
|
+
this.online = true;
|
|
909
|
+
this.emit("network:online");
|
|
910
|
+
if (this.currentAutoLicenseKey && !this.validationTimer) {
|
|
911
|
+
this.startAutoValidation(this.currentAutoLicenseKey);
|
|
912
|
+
}
|
|
913
|
+
this.syncOfflineAssets();
|
|
914
|
+
}
|
|
915
|
+
this.stopConnectivityPolling();
|
|
916
|
+
} catch (err) {
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
this.connectivityTimer = setInterval(
|
|
920
|
+
heartbeat,
|
|
921
|
+
this.config.networkRecheckInterval
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Stop connectivity polling
|
|
926
|
+
* @returns {void}
|
|
927
|
+
* @private
|
|
928
|
+
*/
|
|
929
|
+
stopConnectivityPolling() {
|
|
930
|
+
if (this.connectivityTimer) {
|
|
931
|
+
clearInterval(this.connectivityTimer);
|
|
932
|
+
this.connectivityTimer = null;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
// ============================================================
|
|
936
|
+
// Offline License Management
|
|
937
|
+
// ============================================================
|
|
938
|
+
/**
|
|
939
|
+
* Fetch and cache offline license and public key
|
|
940
|
+
* Uses a lock to prevent concurrent calls from causing race conditions
|
|
941
|
+
* @returns {Promise<void>}
|
|
942
|
+
* @private
|
|
943
|
+
*/
|
|
944
|
+
async syncOfflineAssets() {
|
|
945
|
+
if (this.syncingOfflineAssets || this.destroyed) {
|
|
946
|
+
this.log("Skipping syncOfflineAssets: already syncing or destroyed");
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
this.syncingOfflineAssets = true;
|
|
950
|
+
try {
|
|
951
|
+
const offline = await this.getOfflineLicense();
|
|
952
|
+
this.cache.setOfflineLicense(offline);
|
|
953
|
+
const kid = offline.kid || offline.payload?.kid;
|
|
954
|
+
if (kid) {
|
|
955
|
+
const existingKey = this.cache.getPublicKey(kid);
|
|
956
|
+
if (!existingKey) {
|
|
957
|
+
const pub = await this.getPublicKey(kid);
|
|
958
|
+
this.cache.setPublicKey(kid, pub);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
this.emit("offlineLicense:ready", {
|
|
962
|
+
kid: offline.kid || offline.payload?.kid,
|
|
963
|
+
exp_at: offline.payload?.exp_at
|
|
964
|
+
});
|
|
965
|
+
const res = await this.quickVerifyCachedOfflineLocal();
|
|
966
|
+
if (res) {
|
|
967
|
+
this.cache.updateValidation(res);
|
|
968
|
+
this.emit(
|
|
969
|
+
res.valid ? "validation:offline-success" : "validation:offline-failed",
|
|
970
|
+
res
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
} catch (err) {
|
|
974
|
+
this.log("Failed to sync offline assets:", err);
|
|
975
|
+
} finally {
|
|
976
|
+
this.syncingOfflineAssets = false;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Schedule periodic offline license refresh
|
|
981
|
+
* @returns {void}
|
|
1479
982
|
* @private
|
|
1480
983
|
*/
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
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)}`;
|
|
984
|
+
scheduleOfflineRefresh() {
|
|
985
|
+
if (this.offlineRefreshTimer)
|
|
986
|
+
clearInterval(this.offlineRefreshTimer);
|
|
987
|
+
this.offlineRefreshTimer = setInterval(
|
|
988
|
+
() => this.syncOfflineAssets(),
|
|
989
|
+
this.config.offlineLicenseRefreshInterval
|
|
990
|
+
);
|
|
1494
991
|
}
|
|
1495
992
|
/**
|
|
1496
|
-
*
|
|
993
|
+
* Verify cached offline license
|
|
994
|
+
* @returns {Promise<import('./types.js').ValidationResult>}
|
|
1497
995
|
* @private
|
|
1498
996
|
*/
|
|
1499
|
-
|
|
997
|
+
async verifyCachedOffline() {
|
|
998
|
+
const signed = this.cache.getOfflineLicense();
|
|
999
|
+
if (!signed) {
|
|
1000
|
+
return { valid: false, offline: true, reason_code: "no_offline_license" };
|
|
1001
|
+
}
|
|
1002
|
+
const kid = signed.kid || signed.payload?.kid;
|
|
1003
|
+
let pub = kid ? this.cache.getPublicKey(kid) : null;
|
|
1004
|
+
if (!pub) {
|
|
1005
|
+
try {
|
|
1006
|
+
pub = await this.getPublicKey(kid);
|
|
1007
|
+
this.cache.setPublicKey(kid, pub);
|
|
1008
|
+
} catch (e) {
|
|
1009
|
+
return { valid: false, offline: true, reason_code: "no_public_key" };
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1500
1012
|
try {
|
|
1501
|
-
const
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1013
|
+
const ok = await this.verifyOfflineLicense(signed, pub);
|
|
1014
|
+
if (!ok) {
|
|
1015
|
+
return { valid: false, offline: true, reason_code: "signature_invalid" };
|
|
1016
|
+
}
|
|
1017
|
+
const payload = signed.payload || {};
|
|
1018
|
+
const cached = this.cache.getLicense();
|
|
1019
|
+
if (!cached || !constantTimeEqual(payload.lic_k || "", cached.license_key || "")) {
|
|
1020
|
+
return { valid: false, offline: true, reason_code: "license_mismatch" };
|
|
1021
|
+
}
|
|
1022
|
+
const now = Date.now();
|
|
1023
|
+
const expAt = payload.exp_at ? Date.parse(payload.exp_at) : null;
|
|
1024
|
+
if (expAt && expAt < now) {
|
|
1025
|
+
return { valid: false, offline: true, reason_code: "expired" };
|
|
1026
|
+
}
|
|
1027
|
+
if (!expAt && this.config.maxOfflineDays > 0) {
|
|
1028
|
+
const pivot = cached.last_validated || cached.activated_at;
|
|
1029
|
+
if (pivot) {
|
|
1030
|
+
const ageMs = now - new Date(pivot).getTime();
|
|
1031
|
+
if (ageMs > this.config.maxOfflineDays * 24 * 60 * 60 * 1e3) {
|
|
1032
|
+
return {
|
|
1033
|
+
valid: false,
|
|
1034
|
+
offline: true,
|
|
1035
|
+
reason_code: "grace_period_expired"
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
const lastSeen = this.cache.getLastSeenTimestamp();
|
|
1041
|
+
if (lastSeen && now + this.config.maxClockSkewMs < lastSeen) {
|
|
1042
|
+
return { valid: false, offline: true, reason_code: "clock_tamper" };
|
|
1043
|
+
}
|
|
1044
|
+
this.cache.setLastSeenTimestamp(now);
|
|
1045
|
+
const active = parseActiveEntitlements(payload);
|
|
1046
|
+
return {
|
|
1047
|
+
valid: true,
|
|
1048
|
+
offline: true,
|
|
1049
|
+
...active.length ? { active_entitlements: active } : {}
|
|
1050
|
+
};
|
|
1507
1051
|
} catch (e) {
|
|
1508
|
-
return "
|
|
1052
|
+
return { valid: false, offline: true, reason_code: "verification_error" };
|
|
1509
1053
|
}
|
|
1510
1054
|
}
|
|
1511
1055
|
/**
|
|
1512
|
-
*
|
|
1056
|
+
* Quick offline verification using only local data (no network)
|
|
1057
|
+
* Performs signature verification plus basic validity checks (expiry, license key match)
|
|
1058
|
+
* @returns {Promise<import('./types.js').ValidationResult|null>}
|
|
1513
1059
|
* @private
|
|
1514
1060
|
*/
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1061
|
+
async quickVerifyCachedOfflineLocal() {
|
|
1062
|
+
const signed = this.cache.getOfflineLicense();
|
|
1063
|
+
if (!signed)
|
|
1064
|
+
return null;
|
|
1065
|
+
const kid = signed.kid || signed.payload?.kid;
|
|
1066
|
+
const pub = kid ? this.cache.getPublicKey(kid) : null;
|
|
1067
|
+
if (!pub)
|
|
1068
|
+
return null;
|
|
1069
|
+
try {
|
|
1070
|
+
const ok = await this.verifyOfflineLicense(signed, pub);
|
|
1071
|
+
if (!ok) {
|
|
1072
|
+
return { valid: false, offline: true, reason_code: "signature_invalid" };
|
|
1073
|
+
}
|
|
1074
|
+
const payload = signed.payload || {};
|
|
1075
|
+
const cached = this.cache.getLicense();
|
|
1076
|
+
if (!cached || !constantTimeEqual(payload.lic_k || "", cached.license_key || "")) {
|
|
1077
|
+
return { valid: false, offline: true, reason_code: "license_mismatch" };
|
|
1078
|
+
}
|
|
1079
|
+
const now = Date.now();
|
|
1080
|
+
const expAt = payload.exp_at ? Date.parse(payload.exp_at) : null;
|
|
1081
|
+
if (expAt && expAt < now) {
|
|
1082
|
+
return { valid: false, offline: true, reason_code: "expired" };
|
|
1083
|
+
}
|
|
1084
|
+
const lastSeen = this.cache.getLastSeenTimestamp();
|
|
1085
|
+
if (lastSeen && now + this.config.maxClockSkewMs < lastSeen) {
|
|
1086
|
+
return { valid: false, offline: true, reason_code: "clock_tamper" };
|
|
1087
|
+
}
|
|
1088
|
+
const active = parseActiveEntitlements(payload);
|
|
1089
|
+
return {
|
|
1090
|
+
valid: true,
|
|
1091
|
+
offline: true,
|
|
1092
|
+
...active.length ? { active_entitlements: active } : {}
|
|
1093
|
+
};
|
|
1094
|
+
} catch (_) {
|
|
1095
|
+
return { valid: false, offline: true, reason_code: "verification_error" };
|
|
1521
1096
|
}
|
|
1522
|
-
return Math.abs(hash).toString(36);
|
|
1523
1097
|
}
|
|
1098
|
+
// ============================================================
|
|
1099
|
+
// API Communication
|
|
1100
|
+
// ============================================================
|
|
1524
1101
|
/**
|
|
1525
|
-
* Make API call with retry logic
|
|
1102
|
+
* Make an API call with retry logic
|
|
1103
|
+
* @param {string} endpoint - API endpoint (will be appended to apiBaseUrl)
|
|
1104
|
+
* @param {Object} [options={}] - Fetch options
|
|
1105
|
+
* @param {string} [options.method="GET"] - HTTP method
|
|
1106
|
+
* @param {Object} [options.body] - Request body (will be JSON-stringified)
|
|
1107
|
+
* @param {Object} [options.headers] - Additional headers
|
|
1108
|
+
* @returns {Promise<Object>} API response data
|
|
1109
|
+
* @throws {APIError} When the request fails after all retries
|
|
1526
1110
|
* @private
|
|
1527
1111
|
*/
|
|
1528
1112
|
async apiCall(endpoint, options = {}) {
|
|
@@ -1545,10 +1129,8 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1545
1129
|
const response = await fetch(url, {
|
|
1546
1130
|
method: options.method || "GET",
|
|
1547
1131
|
headers,
|
|
1548
|
-
// Use prepared headers
|
|
1549
1132
|
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
1550
1133
|
credentials: "omit"
|
|
1551
|
-
// Do NOT send cookies (session-agnostic)
|
|
1552
1134
|
});
|
|
1553
1135
|
const data = await response.json();
|
|
1554
1136
|
if (!response.ok) {
|
|
@@ -1583,7 +1165,7 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1583
1165
|
`Retry attempt ${attempt + 1} after ${delay}ms for error:`,
|
|
1584
1166
|
error.message
|
|
1585
1167
|
);
|
|
1586
|
-
await
|
|
1168
|
+
await sleep(delay);
|
|
1587
1169
|
} else {
|
|
1588
1170
|
throw error;
|
|
1589
1171
|
}
|
|
@@ -1593,6 +1175,8 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1593
1175
|
}
|
|
1594
1176
|
/**
|
|
1595
1177
|
* Determine if an error should be retried
|
|
1178
|
+
* @param {Error} error - The error to check
|
|
1179
|
+
* @returns {boolean} True if the error should trigger a retry
|
|
1596
1180
|
* @private
|
|
1597
1181
|
*/
|
|
1598
1182
|
shouldRetryError(error) {
|
|
@@ -1611,404 +1195,69 @@ var LicenseSeatSDK = class _LicenseSeatSDK {
|
|
|
1611
1195
|
}
|
|
1612
1196
|
return false;
|
|
1613
1197
|
}
|
|
1198
|
+
// ============================================================
|
|
1199
|
+
// Utilities
|
|
1200
|
+
// ============================================================
|
|
1614
1201
|
/**
|
|
1615
|
-
*
|
|
1202
|
+
* Get CSRF token from meta tag
|
|
1203
|
+
* @returns {string} CSRF token or empty string
|
|
1616
1204
|
*/
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
this.eventListeners[event] = [];
|
|
1620
|
-
}
|
|
1621
|
-
this.eventListeners[event].push(callback);
|
|
1622
|
-
return () => this.off(event, callback);
|
|
1623
|
-
}
|
|
1624
|
-
off(event, callback) {
|
|
1625
|
-
if (this.eventListeners[event]) {
|
|
1626
|
-
this.eventListeners[event] = this.eventListeners[event].filter(
|
|
1627
|
-
(cb) => cb !== callback
|
|
1628
|
-
);
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
emit(event, data) {
|
|
1632
|
-
this.log(`Event: ${event}`, data);
|
|
1633
|
-
if (this.eventListeners[event]) {
|
|
1634
|
-
this.eventListeners[event].forEach((callback) => {
|
|
1635
|
-
try {
|
|
1636
|
-
callback(data);
|
|
1637
|
-
} catch (error) {
|
|
1638
|
-
console.error(`Error in event listener for ${event}:`, error);
|
|
1639
|
-
}
|
|
1640
|
-
});
|
|
1641
|
-
}
|
|
1205
|
+
getCsrfToken() {
|
|
1206
|
+
return getCsrfToken();
|
|
1642
1207
|
}
|
|
1643
1208
|
/**
|
|
1644
|
-
*
|
|
1209
|
+
* Log a message (if debug mode is enabled)
|
|
1210
|
+
* @param {...*} args - Arguments to log
|
|
1211
|
+
* @returns {void}
|
|
1212
|
+
* @private
|
|
1645
1213
|
*/
|
|
1646
|
-
getCsrfToken() {
|
|
1647
|
-
const token = document.querySelector('meta[name="csrf-token"]');
|
|
1648
|
-
return token ? token.content : "";
|
|
1649
|
-
}
|
|
1650
|
-
sleep(ms) {
|
|
1651
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1652
|
-
}
|
|
1653
1214
|
log(...args) {
|
|
1654
1215
|
if (this.config.debug) {
|
|
1655
1216
|
console.log("[LicenseSeat SDK]", ...args);
|
|
1656
1217
|
}
|
|
1657
1218
|
}
|
|
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
|
|
1662
|
-
*/
|
|
1663
|
-
async testAuth() {
|
|
1664
|
-
if (!this.config.apiKey) {
|
|
1665
|
-
const err2 = new Error("API key is required for auth test");
|
|
1666
|
-
this.emit("auth_test:error", { error: err2 });
|
|
1667
|
-
throw err2;
|
|
1668
|
-
}
|
|
1669
|
-
try {
|
|
1670
|
-
this.emit("auth_test:start");
|
|
1671
|
-
const response = await this.apiCall("/auth_test", { method: "GET" });
|
|
1672
|
-
this.emit("auth_test:success", response);
|
|
1673
|
-
return response;
|
|
1674
|
-
} catch (error) {
|
|
1675
|
-
this.emit("auth_test:error", { error });
|
|
1676
|
-
throw error;
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1679
|
-
/**
|
|
1680
|
-
* Get current license status
|
|
1681
|
-
*/
|
|
1682
|
-
getStatus() {
|
|
1683
|
-
const license = this.cache.getLicense();
|
|
1684
|
-
if (!license) {
|
|
1685
|
-
return { status: "inactive", message: "No license activated" };
|
|
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
|
-
};
|
|
1712
|
-
}
|
|
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
|
-
}
|
|
1722
|
-
/**
|
|
1723
|
-
* Clear all data and reset
|
|
1724
|
-
*/
|
|
1725
|
-
reset() {
|
|
1726
|
-
this.stopAutoValidation();
|
|
1727
|
-
this.cache.clear();
|
|
1728
|
-
this.lastOfflineValidation = null;
|
|
1729
|
-
this.emit("sdk:reset");
|
|
1730
|
-
}
|
|
1731
|
-
startConnectivityPolling() {
|
|
1732
|
-
if (this.connectivityTimer)
|
|
1733
|
-
return;
|
|
1734
|
-
const heartbeat = async () => {
|
|
1735
|
-
try {
|
|
1736
|
-
await fetch(`${this.config.apiBaseUrl}/heartbeat`, {
|
|
1737
|
-
method: "GET",
|
|
1738
|
-
credentials: "omit"
|
|
1739
|
-
});
|
|
1740
|
-
if (!this.online) {
|
|
1741
|
-
this.online = true;
|
|
1742
|
-
this.emit("network:online");
|
|
1743
|
-
if (this.currentAutoLicenseKey && !this.validationTimer) {
|
|
1744
|
-
this.startAutoValidation(this.currentAutoLicenseKey);
|
|
1745
|
-
}
|
|
1746
|
-
this.syncOfflineAssets();
|
|
1747
|
-
}
|
|
1748
|
-
this.stopConnectivityPolling();
|
|
1749
|
-
} catch (err2) {
|
|
1750
|
-
}
|
|
1751
|
-
};
|
|
1752
|
-
this.connectivityTimer = setInterval(
|
|
1753
|
-
heartbeat,
|
|
1754
|
-
this.config.networkRecheckInterval
|
|
1755
|
-
);
|
|
1756
|
-
}
|
|
1757
|
-
stopConnectivityPolling() {
|
|
1758
|
-
if (this.connectivityTimer) {
|
|
1759
|
-
clearInterval(this.connectivityTimer);
|
|
1760
|
-
this.connectivityTimer = null;
|
|
1761
|
-
}
|
|
1762
|
-
}
|
|
1763
|
-
/**
|
|
1764
|
-
* Fetch & cache offline license + public key so we can verify while offline.
|
|
1765
|
-
* Runs in background; errors are logged but not thrown.
|
|
1766
|
-
* @private
|
|
1767
|
-
*/
|
|
1768
|
-
async syncOfflineAssets() {
|
|
1769
|
-
try {
|
|
1770
|
-
const offline = await this.getOfflineLicense();
|
|
1771
|
-
this.cache.setOfflineLicense(offline);
|
|
1772
|
-
const kid = offline.kid || offline.payload?.kid;
|
|
1773
|
-
if (kid) {
|
|
1774
|
-
const existingKey = this.cache.getPublicKey(kid);
|
|
1775
|
-
if (!existingKey) {
|
|
1776
|
-
const pub = await this.getPublicKey(kid);
|
|
1777
|
-
this.cache.setPublicKey(kid, pub);
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1780
|
-
this.emit("offlineLicense:ready", {
|
|
1781
|
-
kid: offline.kid || offline.payload?.kid,
|
|
1782
|
-
exp_at: offline.payload?.exp_at
|
|
1783
|
-
});
|
|
1784
|
-
} catch (err2) {
|
|
1785
|
-
this.log("Failed to sync offline assets:", err2);
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
|
-
/**
|
|
1789
|
-
* Verify cached offline license & return synthetic validation object.
|
|
1790
|
-
* @private
|
|
1791
|
-
*/
|
|
1792
|
-
async verifyCachedOffline() {
|
|
1793
|
-
const signed = this.cache.getOfflineLicense();
|
|
1794
|
-
if (!signed) {
|
|
1795
|
-
return { valid: false, offline: true, reason_code: "no_offline_license" };
|
|
1796
|
-
}
|
|
1797
|
-
const kid = signed.kid || signed.payload?.kid;
|
|
1798
|
-
let pub = kid ? this.cache.getPublicKey(kid) : null;
|
|
1799
|
-
if (!pub) {
|
|
1800
|
-
try {
|
|
1801
|
-
pub = await this.getPublicKey(kid);
|
|
1802
|
-
this.cache.setPublicKey(kid, pub);
|
|
1803
|
-
} catch (e) {
|
|
1804
|
-
return { valid: false, offline: true, reason_code: "no_public_key" };
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
try {
|
|
1808
|
-
const ok = await this.verifyOfflineLicense(signed, pub);
|
|
1809
|
-
if (!ok) {
|
|
1810
|
-
return {
|
|
1811
|
-
valid: false,
|
|
1812
|
-
offline: true,
|
|
1813
|
-
reason_code: "signature_invalid"
|
|
1814
|
-
};
|
|
1815
|
-
}
|
|
1816
|
-
const payload = signed.payload || {};
|
|
1817
|
-
const cached = this.cache.getLicense();
|
|
1818
|
-
if (!cached || !_LicenseSeatSDK.constantTimeEqual(
|
|
1819
|
-
payload.lic_k || "",
|
|
1820
|
-
cached.license_key || ""
|
|
1821
|
-
)) {
|
|
1822
|
-
return { valid: false, offline: true, reason_code: "license_mismatch" };
|
|
1823
|
-
}
|
|
1824
|
-
const now = Date.now();
|
|
1825
|
-
const expAt = payload.exp_at ? Date.parse(payload.exp_at) : null;
|
|
1826
|
-
if (expAt && expAt < now) {
|
|
1827
|
-
return { valid: false, offline: true, reason_code: "expired" };
|
|
1828
|
-
}
|
|
1829
|
-
if (!expAt && this.config.maxOfflineDays > 0) {
|
|
1830
|
-
const pivot = cached.last_validated || cached.activated_at;
|
|
1831
|
-
if (pivot) {
|
|
1832
|
-
const ageMs = now - new Date(pivot).getTime();
|
|
1833
|
-
if (ageMs > this.config.maxOfflineDays * 24 * 60 * 60 * 1e3) {
|
|
1834
|
-
return {
|
|
1835
|
-
valid: false,
|
|
1836
|
-
offline: true,
|
|
1837
|
-
reason_code: "grace_period_expired"
|
|
1838
|
-
};
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
const lastSeen = this.cache.getLastSeenTimestamp();
|
|
1843
|
-
if (lastSeen && now + this.config.maxClockSkewMs < lastSeen) {
|
|
1844
|
-
return { valid: false, offline: true, reason_code: "clock_tamper" };
|
|
1845
|
-
}
|
|
1846
|
-
this.cache.setLastSeenTimestamp(now);
|
|
1847
|
-
return { valid: true, offline: true };
|
|
1848
|
-
} catch (e) {
|
|
1849
|
-
return { valid: false, offline: true, reason_code: "verification_error" };
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
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
|
-
/**
|
|
1871
|
-
* Attempt to verify cached offline license using only local data (no network).
|
|
1872
|
-
* Returns validation-like object or null if not possible.
|
|
1873
|
-
* @private
|
|
1874
|
-
*/
|
|
1875
|
-
async quickVerifyCachedOfflineLocal() {
|
|
1876
|
-
const signed = this.cache.getOfflineLicense();
|
|
1877
|
-
if (!signed)
|
|
1878
|
-
return null;
|
|
1879
|
-
const kid = signed.kid || signed.payload?.kid;
|
|
1880
|
-
const pub = kid ? this.cache.getPublicKey(kid) : null;
|
|
1881
|
-
if (!pub)
|
|
1882
|
-
return null;
|
|
1883
|
-
try {
|
|
1884
|
-
const ok = await this.verifyOfflineLicense(signed, pub);
|
|
1885
|
-
return ok ? { valid: true, offline: true } : { valid: false, offline: true, reason_code: "signature_invalid" };
|
|
1886
|
-
} catch (_) {
|
|
1887
|
-
return { valid: false, offline: true, reason_code: "verification_error" };
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
1219
|
};
|
|
1891
|
-
var
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1220
|
+
var sharedInstance = null;
|
|
1221
|
+
function getSharedInstance(config) {
|
|
1222
|
+
if (!sharedInstance) {
|
|
1223
|
+
sharedInstance = new LicenseSeatSDK(config);
|
|
1895
1224
|
}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
}
|
|
1905
|
-
setLicense(data) {
|
|
1906
|
-
try {
|
|
1907
|
-
localStorage.setItem(this.prefix + "license", JSON.stringify(data));
|
|
1908
|
-
} catch (e) {
|
|
1909
|
-
console.error("Failed to cache license:", e);
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
updateValidation(validationData) {
|
|
1913
|
-
const license = this.getLicense();
|
|
1914
|
-
if (license) {
|
|
1915
|
-
license.validation = validationData;
|
|
1916
|
-
license.last_validated = (/* @__PURE__ */ new Date()).toISOString();
|
|
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)
|
|
1942
|
-
);
|
|
1943
|
-
} catch (e) {
|
|
1944
|
-
console.error("Failed to cache offline license:", e);
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
clearOfflineLicense() {
|
|
1948
|
-
localStorage.removeItem(this.prefix + "offline_license");
|
|
1949
|
-
}
|
|
1950
|
-
// Methods for caching public keys
|
|
1951
|
-
getPublicKey(keyId) {
|
|
1952
|
-
try {
|
|
1953
|
-
const cache = JSON.parse(
|
|
1954
|
-
localStorage.getItem(this.publicKeyCacheKey) || "{}"
|
|
1955
|
-
);
|
|
1956
|
-
return cache[keyId] || null;
|
|
1957
|
-
} catch (e) {
|
|
1958
|
-
console.error("Failed to read public key cache:", e);
|
|
1959
|
-
return null;
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
setPublicKey(keyId, publicKeyB64) {
|
|
1963
|
-
try {
|
|
1964
|
-
const cache = JSON.parse(
|
|
1965
|
-
localStorage.getItem(this.publicKeyCacheKey) || "{}"
|
|
1966
|
-
);
|
|
1967
|
-
cache[keyId] = publicKeyB64;
|
|
1968
|
-
localStorage.setItem(this.publicKeyCacheKey, JSON.stringify(cache));
|
|
1969
|
-
} catch (e) {
|
|
1970
|
-
console.error("Failed to cache public key:", e);
|
|
1971
|
-
}
|
|
1972
|
-
}
|
|
1973
|
-
// Clears *all* LicenseSeat SDK data for this prefix.
|
|
1974
|
-
clear() {
|
|
1975
|
-
Object.keys(localStorage).forEach((key) => {
|
|
1976
|
-
if (key.startsWith(this.prefix)) {
|
|
1977
|
-
localStorage.removeItem(key);
|
|
1978
|
-
}
|
|
1979
|
-
});
|
|
1980
|
-
this.clearOfflineLicense();
|
|
1981
|
-
localStorage.removeItem(this.prefix + "last_seen_ts");
|
|
1982
|
-
}
|
|
1983
|
-
// --- Time baseline helpers ---
|
|
1984
|
-
getLastSeenTimestamp() {
|
|
1985
|
-
const v = localStorage.getItem(this.prefix + "last_seen_ts");
|
|
1986
|
-
return v ? parseInt(v, 10) : null;
|
|
1987
|
-
}
|
|
1988
|
-
setLastSeenTimestamp(ts) {
|
|
1989
|
-
try {
|
|
1990
|
-
localStorage.setItem(this.prefix + "last_seen_ts", String(ts));
|
|
1991
|
-
} catch (e) {
|
|
1992
|
-
}
|
|
1225
|
+
return sharedInstance;
|
|
1226
|
+
}
|
|
1227
|
+
function configure(config, force = false) {
|
|
1228
|
+
if (sharedInstance && !force) {
|
|
1229
|
+
console.warn(
|
|
1230
|
+
"[LicenseSeat SDK] Already configured. Call configure with force=true to reconfigure."
|
|
1231
|
+
);
|
|
1232
|
+
return sharedInstance;
|
|
1993
1233
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
1234
|
+
sharedInstance = new LicenseSeatSDK(config);
|
|
1235
|
+
return sharedInstance;
|
|
1236
|
+
}
|
|
1237
|
+
function resetSharedInstance() {
|
|
1238
|
+
if (sharedInstance) {
|
|
1239
|
+
sharedInstance.reset();
|
|
1240
|
+
sharedInstance = null;
|
|
2001
1241
|
}
|
|
2002
|
-
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// src/index.js
|
|
2003
1245
|
var src_default = LicenseSeatSDK;
|
|
2004
1246
|
export {
|
|
2005
|
-
|
|
1247
|
+
APIError,
|
|
1248
|
+
ConfigurationError,
|
|
1249
|
+
CryptoError,
|
|
1250
|
+
LicenseCache,
|
|
1251
|
+
LicenseError,
|
|
1252
|
+
LicenseSeatSDK,
|
|
1253
|
+
base64UrlDecode,
|
|
1254
|
+
canonicalJsonStringify,
|
|
1255
|
+
configure,
|
|
1256
|
+
constantTimeEqual,
|
|
1257
|
+
src_default as default,
|
|
1258
|
+
generateDeviceId,
|
|
1259
|
+
getCsrfToken,
|
|
1260
|
+
getSharedInstance,
|
|
1261
|
+
parseActiveEntitlements,
|
|
1262
|
+
resetSharedInstance
|
|
2006
1263
|
};
|
|
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
|
-
*/
|