@licenseseat/js 0.4.1 → 0.4.2
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/dist/index.bundled.js +2775 -0
- package/dist/index.js +6 -1
- package/dist/types/LicenseSeat.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/LicenseSeat.js +9 -1
|
@@ -0,0 +1,2775 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// node_modules/@noble/ed25519/index.js
|
|
8
|
+
var ed25519_exports = {};
|
|
9
|
+
__export(ed25519_exports, {
|
|
10
|
+
CURVE: () => ed25519_CURVE,
|
|
11
|
+
ExtendedPoint: () => Point,
|
|
12
|
+
Point: () => Point,
|
|
13
|
+
etc: () => etc,
|
|
14
|
+
getPublicKey: () => getPublicKey,
|
|
15
|
+
getPublicKeyAsync: () => getPublicKeyAsync,
|
|
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);
|
|
215
|
+
}
|
|
216
|
+
/** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */
|
|
217
|
+
add(other) {
|
|
218
|
+
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
|
|
219
|
+
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = apoint(other);
|
|
220
|
+
const a = _a;
|
|
221
|
+
const d = _d;
|
|
222
|
+
const A = M(X1 * X2);
|
|
223
|
+
const B = M(Y1 * Y2);
|
|
224
|
+
const C2 = M(T1 * d * T2);
|
|
225
|
+
const D = M(Z1 * Z2);
|
|
226
|
+
const E = M((X1 + Y1) * (X2 + Y2) - A - B);
|
|
227
|
+
const F = M(D - C2);
|
|
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);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.
|
|
238
|
+
* Uses {@link wNAF} for base point.
|
|
239
|
+
* Uses fake point to mitigate side-channel leakage.
|
|
240
|
+
* @param n scalar by which point is multiplied
|
|
241
|
+
* @param safe safe mode guards against timing attacks; unsafe mode is faster
|
|
242
|
+
*/
|
|
243
|
+
multiply(n, safe = true) {
|
|
244
|
+
if (!safe && (n === 0n || this.is0()))
|
|
245
|
+
return I;
|
|
246
|
+
arange(n, 1n, N);
|
|
247
|
+
if (n === 1n)
|
|
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);
|
|
258
|
+
}
|
|
259
|
+
return p;
|
|
260
|
+
}
|
|
261
|
+
/** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
|
|
262
|
+
toAffine() {
|
|
263
|
+
const { ex: x, ey: y, ez: z } = this;
|
|
264
|
+
if (this.equals(I))
|
|
265
|
+
return { x: 0n, y: 1n };
|
|
266
|
+
const iz = invert(z, P);
|
|
267
|
+
if (M(z * iz) !== 1n)
|
|
268
|
+
err("invalid inverse");
|
|
269
|
+
return { x: M(x * iz), y: M(y * iz) };
|
|
270
|
+
}
|
|
271
|
+
toBytes() {
|
|
272
|
+
const { x, y } = this.assertValidity().toAffine();
|
|
273
|
+
const b = numTo32bLE(y);
|
|
274
|
+
b[31] |= x & 1n ? 128 : 0;
|
|
275
|
+
return b;
|
|
276
|
+
}
|
|
277
|
+
toHex() {
|
|
278
|
+
return bytesToHex(this.toBytes());
|
|
279
|
+
}
|
|
280
|
+
// encode to hex string
|
|
281
|
+
clearCofactor() {
|
|
282
|
+
return this.multiply(big(h), false);
|
|
283
|
+
}
|
|
284
|
+
isSmallOrder() {
|
|
285
|
+
return this.clearCofactor().is0();
|
|
286
|
+
}
|
|
287
|
+
isTorsionFree() {
|
|
288
|
+
let p = this.multiply(N / 2n, false).double();
|
|
289
|
+
if (N % 2n)
|
|
290
|
+
p = p.add(this);
|
|
291
|
+
return p.is0();
|
|
292
|
+
}
|
|
293
|
+
static fromHex(hex, zip215) {
|
|
294
|
+
return _Point.fromBytes(toU8(hex), zip215);
|
|
295
|
+
}
|
|
296
|
+
get x() {
|
|
297
|
+
return this.toAffine().x;
|
|
298
|
+
}
|
|
299
|
+
get y() {
|
|
300
|
+
return this.toAffine().y;
|
|
301
|
+
}
|
|
302
|
+
toRawBytes() {
|
|
303
|
+
return this.toBytes();
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
var G = new Point(Gx, Gy, 1n, M(Gx * Gy));
|
|
307
|
+
var I = new Point(0n, 1n, 1n, 0n);
|
|
308
|
+
Point.BASE = G;
|
|
309
|
+
Point.ZERO = I;
|
|
310
|
+
var numTo32bLE = (num) => hexToBytes(padh(arange(num, 0n, B256), L2)).reverse();
|
|
311
|
+
var bytesToNumLE = (b) => big("0x" + bytesToHex(u8fr(abytes(b)).reverse()));
|
|
312
|
+
var pow2 = (x, power) => {
|
|
313
|
+
let r = x;
|
|
314
|
+
while (power-- > 0n) {
|
|
315
|
+
r *= r;
|
|
316
|
+
r %= P;
|
|
317
|
+
}
|
|
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
|
+
};
|
|
398
|
+
var veriOpts = { zip215: true };
|
|
399
|
+
var _verify = (sig, msg, pub, opts = veriOpts) => {
|
|
400
|
+
sig = toU8(sig, L2);
|
|
401
|
+
msg = toU8(msg);
|
|
402
|
+
pub = toU8(pub, L);
|
|
403
|
+
const { zip215 } = opts;
|
|
404
|
+
let A;
|
|
405
|
+
let R;
|
|
406
|
+
let s;
|
|
407
|
+
let SB;
|
|
408
|
+
let hashable = Uint8Array.of();
|
|
409
|
+
try {
|
|
410
|
+
A = Point.fromHex(pub, zip215);
|
|
411
|
+
R = Point.fromHex(sig.slice(0, L), zip215);
|
|
412
|
+
s = bytesToNumLE(sig.slice(L, L2));
|
|
413
|
+
SB = G.multiply(s, false);
|
|
414
|
+
hashable = concatBytes(R.toBytes(), A.toBytes(), msg);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
}
|
|
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
|
+
};
|
|
428
|
+
var verifyAsync = async (s, m, p, opts = veriOpts) => hashFinishA(_verify(s, m, p, opts));
|
|
429
|
+
var verify = (s, m, p, opts = veriOpts) => hashFinishS(_verify(s, m, p, opts));
|
|
430
|
+
var etc = {
|
|
431
|
+
sha512Async: async (...messages) => {
|
|
432
|
+
const s = subtle();
|
|
433
|
+
const m = concatBytes(...messages);
|
|
434
|
+
return u8n(await s.digest("SHA-512", m.buffer));
|
|
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;
|
|
452
|
+
}
|
|
453
|
+
// no-op
|
|
454
|
+
};
|
|
455
|
+
var W = 8;
|
|
456
|
+
var scalarBits = 256;
|
|
457
|
+
var pwindows = Math.ceil(scalarBits / W) + 1;
|
|
458
|
+
var pwindowSize = 2 ** (W - 1);
|
|
459
|
+
var precompute = () => {
|
|
460
|
+
const points = [];
|
|
461
|
+
let p = G;
|
|
462
|
+
let b = p;
|
|
463
|
+
for (let w = 0; w < pwindows; w++) {
|
|
464
|
+
b = p;
|
|
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();
|
|
471
|
+
}
|
|
472
|
+
return points;
|
|
473
|
+
};
|
|
474
|
+
var Gpows = void 0;
|
|
475
|
+
var ctneg = (cnd, p) => {
|
|
476
|
+
const n = p.negate();
|
|
477
|
+
return cnd ? n : p;
|
|
478
|
+
};
|
|
479
|
+
var wNAF = (n) => {
|
|
480
|
+
const comp = Gpows || (Gpows = precompute());
|
|
481
|
+
let p = I;
|
|
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
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return { p, f };
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// node_modules/@noble/hashes/esm/utils.js
|
|
509
|
+
function isBytes2(a) {
|
|
510
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
511
|
+
}
|
|
512
|
+
function abytes2(b, ...lengths) {
|
|
513
|
+
if (!isBytes2(b))
|
|
514
|
+
throw new Error("Uint8Array expected");
|
|
515
|
+
if (lengths.length > 0 && !lengths.includes(b.length))
|
|
516
|
+
throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
|
|
517
|
+
}
|
|
518
|
+
function aexists(instance, checkFinished = true) {
|
|
519
|
+
if (instance.destroyed)
|
|
520
|
+
throw new Error("Hash instance has been destroyed");
|
|
521
|
+
if (checkFinished && instance.finished)
|
|
522
|
+
throw new Error("Hash#digest() has already been called");
|
|
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);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function createView(arr) {
|
|
537
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
538
|
+
}
|
|
539
|
+
function utf8ToBytes(str2) {
|
|
540
|
+
if (typeof str2 !== "string")
|
|
541
|
+
throw new Error("string expected");
|
|
542
|
+
return new Uint8Array(new TextEncoder().encode(str2));
|
|
543
|
+
}
|
|
544
|
+
function toBytes(data) {
|
|
545
|
+
if (typeof data === "string")
|
|
546
|
+
data = utf8ToBytes(data);
|
|
547
|
+
abytes2(data);
|
|
548
|
+
return data;
|
|
549
|
+
}
|
|
550
|
+
var Hash = class {
|
|
551
|
+
};
|
|
552
|
+
function createHasher(hashCons) {
|
|
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;
|
|
625
|
+
}
|
|
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
|
+
}
|
|
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 };
|
|
690
|
+
}
|
|
691
|
+
function split(lst, le = false) {
|
|
692
|
+
const len = lst.length;
|
|
693
|
+
let Ah = new Uint32Array(len);
|
|
694
|
+
let Al = new Uint32Array(len);
|
|
695
|
+
for (let i = 0; i < len; i++) {
|
|
696
|
+
const { h: h2, l } = fromBig(lst[i], le);
|
|
697
|
+
[Ah[i], Al[i]] = [h2, l];
|
|
698
|
+
}
|
|
699
|
+
return [Ah, Al];
|
|
700
|
+
}
|
|
701
|
+
var shrSH = (h2, _l, s) => h2 >>> s;
|
|
702
|
+
var shrSL = (h2, l, s) => h2 << 32 - s | l >>> s;
|
|
703
|
+
var rotrSH = (h2, l, s) => h2 >>> s | l << 32 - s;
|
|
704
|
+
var rotrSL = (h2, l, s) => h2 << 32 - s | l >>> s;
|
|
705
|
+
var rotrBH = (h2, l, s) => h2 << 64 - s | l >>> s - 32;
|
|
706
|
+
var rotrBL = (h2, l, s) => h2 >>> s - 32 | l << 64 - s;
|
|
707
|
+
function add(Ah, Al, Bh, Bl) {
|
|
708
|
+
const l = (Al >>> 0) + (Bl >>> 0);
|
|
709
|
+
return { h: Ah + Bh + (l / 2 ** 32 | 0) | 0, l: l | 0 };
|
|
710
|
+
}
|
|
711
|
+
var add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);
|
|
712
|
+
var add3H = (low, Ah, Bh, Ch) => Ah + Bh + Ch + (low / 2 ** 32 | 0) | 0;
|
|
713
|
+
var add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);
|
|
714
|
+
var add4H = (low, Ah, Bh, Ch, Dh) => Ah + Bh + Ch + Dh + (low / 2 ** 32 | 0) | 0;
|
|
715
|
+
var add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);
|
|
716
|
+
var add5H = (low, Ah, Bh, Ch, Dh, Eh) => Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0;
|
|
717
|
+
|
|
718
|
+
// node_modules/@noble/hashes/esm/sha2.js
|
|
719
|
+
var K512 = /* @__PURE__ */ (() => split([
|
|
720
|
+
"0x428a2f98d728ae22",
|
|
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);
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
var sha512 = /* @__PURE__ */ createHasher(() => new SHA512());
|
|
917
|
+
|
|
918
|
+
// node_modules/@noble/hashes/esm/sha512.js
|
|
919
|
+
var sha5122 = sha512;
|
|
920
|
+
|
|
921
|
+
// src/cache.js
|
|
922
|
+
var LicenseCache = class {
|
|
923
|
+
/**
|
|
924
|
+
* Create a LicenseCache instance
|
|
925
|
+
* @param {string} [prefix="licenseseat_"] - Prefix for all localStorage keys
|
|
926
|
+
*/
|
|
927
|
+
constructor(prefix = "licenseseat_") {
|
|
928
|
+
this.prefix = prefix;
|
|
929
|
+
this.publicKeyCacheKey = this.prefix + "public_keys";
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Get the cached license data
|
|
933
|
+
* @returns {import('./types.js').CachedLicense|null} Cached license or null if not found
|
|
934
|
+
*/
|
|
935
|
+
getLicense() {
|
|
936
|
+
try {
|
|
937
|
+
const data = localStorage.getItem(this.prefix + "license");
|
|
938
|
+
return data ? JSON.parse(data) : null;
|
|
939
|
+
} catch (e) {
|
|
940
|
+
console.error("Failed to read license cache:", e);
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Store license data in cache
|
|
946
|
+
* @param {import('./types.js').CachedLicense} data - License data to cache
|
|
947
|
+
* @returns {void}
|
|
948
|
+
*/
|
|
949
|
+
setLicense(data) {
|
|
950
|
+
try {
|
|
951
|
+
localStorage.setItem(this.prefix + "license", JSON.stringify(data));
|
|
952
|
+
} catch (e) {
|
|
953
|
+
console.error("Failed to cache license:", e);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Update the validation data for the cached license
|
|
958
|
+
* @param {import('./types.js').ValidationResult} validationData - Validation result to store
|
|
959
|
+
* @returns {void}
|
|
960
|
+
*/
|
|
961
|
+
updateValidation(validationData) {
|
|
962
|
+
const license = this.getLicense();
|
|
963
|
+
if (license) {
|
|
964
|
+
license.validation = validationData;
|
|
965
|
+
license.last_validated = (/* @__PURE__ */ new Date()).toISOString();
|
|
966
|
+
this.setLicense(license);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Get the device ID from the cached license
|
|
971
|
+
* @returns {string|null} Device ID or null if not found
|
|
972
|
+
*/
|
|
973
|
+
getDeviceId() {
|
|
974
|
+
const license = this.getLicense();
|
|
975
|
+
return license ? license.device_id : null;
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Clear the cached license data
|
|
979
|
+
* @returns {void}
|
|
980
|
+
*/
|
|
981
|
+
clearLicense() {
|
|
982
|
+
localStorage.removeItem(this.prefix + "license");
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* Get the cached offline token
|
|
986
|
+
* @returns {import('./types.js').OfflineToken|null} Offline token or null if not found
|
|
987
|
+
*/
|
|
988
|
+
getOfflineToken() {
|
|
989
|
+
try {
|
|
990
|
+
const data = localStorage.getItem(this.prefix + "offline_token");
|
|
991
|
+
return data ? JSON.parse(data) : null;
|
|
992
|
+
} catch (e) {
|
|
993
|
+
console.error("Failed to read offline token cache:", e);
|
|
994
|
+
return null;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Store offline token data in cache
|
|
999
|
+
* @param {import('./types.js').OfflineToken} data - Offline token to cache
|
|
1000
|
+
* @returns {void}
|
|
1001
|
+
*/
|
|
1002
|
+
setOfflineToken(data) {
|
|
1003
|
+
try {
|
|
1004
|
+
localStorage.setItem(
|
|
1005
|
+
this.prefix + "offline_token",
|
|
1006
|
+
JSON.stringify(data)
|
|
1007
|
+
);
|
|
1008
|
+
} catch (e) {
|
|
1009
|
+
console.error("Failed to cache offline token:", e);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Clear the cached offline token
|
|
1014
|
+
* @returns {void}
|
|
1015
|
+
*/
|
|
1016
|
+
clearOfflineToken() {
|
|
1017
|
+
localStorage.removeItem(this.prefix + "offline_token");
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Get a cached public key by key ID
|
|
1021
|
+
* @param {string} keyId - The key ID to look up
|
|
1022
|
+
* @returns {string|null} Base64-encoded public key or null if not found
|
|
1023
|
+
*/
|
|
1024
|
+
getPublicKey(keyId) {
|
|
1025
|
+
try {
|
|
1026
|
+
const cache = JSON.parse(
|
|
1027
|
+
localStorage.getItem(this.publicKeyCacheKey) || "{}"
|
|
1028
|
+
);
|
|
1029
|
+
return cache[keyId] || null;
|
|
1030
|
+
} catch (e) {
|
|
1031
|
+
console.error("Failed to read public key cache:", e);
|
|
1032
|
+
return null;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Store a public key in cache
|
|
1037
|
+
* @param {string} keyId - The key ID
|
|
1038
|
+
* @param {string} publicKeyB64 - Base64-encoded public key
|
|
1039
|
+
* @returns {void}
|
|
1040
|
+
*/
|
|
1041
|
+
setPublicKey(keyId, publicKeyB64) {
|
|
1042
|
+
try {
|
|
1043
|
+
const cache = JSON.parse(
|
|
1044
|
+
localStorage.getItem(this.publicKeyCacheKey) || "{}"
|
|
1045
|
+
);
|
|
1046
|
+
cache[keyId] = publicKeyB64;
|
|
1047
|
+
localStorage.setItem(this.publicKeyCacheKey, JSON.stringify(cache));
|
|
1048
|
+
} catch (e) {
|
|
1049
|
+
console.error("Failed to cache public key:", e);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Clear all LicenseSeat SDK data for this prefix
|
|
1054
|
+
* @returns {void}
|
|
1055
|
+
*/
|
|
1056
|
+
clear() {
|
|
1057
|
+
Object.keys(localStorage).forEach((key) => {
|
|
1058
|
+
if (key.startsWith(this.prefix)) {
|
|
1059
|
+
localStorage.removeItem(key);
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Get the last seen timestamp (for clock tamper detection)
|
|
1065
|
+
* @returns {number|null} Unix timestamp in milliseconds or null if not set
|
|
1066
|
+
*/
|
|
1067
|
+
getLastSeenTimestamp() {
|
|
1068
|
+
const v = localStorage.getItem(this.prefix + "last_seen_ts");
|
|
1069
|
+
return v ? parseInt(v, 10) : null;
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Store the last seen timestamp
|
|
1073
|
+
* @param {number} ts - Unix timestamp in milliseconds
|
|
1074
|
+
* @returns {void}
|
|
1075
|
+
*/
|
|
1076
|
+
setLastSeenTimestamp(ts) {
|
|
1077
|
+
try {
|
|
1078
|
+
localStorage.setItem(this.prefix + "last_seen_ts", String(ts));
|
|
1079
|
+
} catch (e) {
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
// src/errors.js
|
|
1085
|
+
var APIError = class extends Error {
|
|
1086
|
+
/**
|
|
1087
|
+
* Create an APIError
|
|
1088
|
+
* @param {string} message - Error message
|
|
1089
|
+
* @param {number} status - HTTP status code (0 for network failures)
|
|
1090
|
+
* @param {import('./types.js').APIErrorData} [data] - Additional error data from the API response
|
|
1091
|
+
*/
|
|
1092
|
+
constructor(message, status, data) {
|
|
1093
|
+
super(message);
|
|
1094
|
+
this.name = "APIError";
|
|
1095
|
+
this.status = status;
|
|
1096
|
+
this.data = data;
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
var ConfigurationError = class extends Error {
|
|
1100
|
+
/**
|
|
1101
|
+
* Create a ConfigurationError
|
|
1102
|
+
* @param {string} message - Error message
|
|
1103
|
+
*/
|
|
1104
|
+
constructor(message) {
|
|
1105
|
+
super(message);
|
|
1106
|
+
this.name = "ConfigurationError";
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
var LicenseError = class extends Error {
|
|
1110
|
+
/**
|
|
1111
|
+
* Create a LicenseError
|
|
1112
|
+
* @param {string} message - Error message
|
|
1113
|
+
* @param {string} [code] - Machine-readable error code
|
|
1114
|
+
*/
|
|
1115
|
+
constructor(message, code) {
|
|
1116
|
+
super(message);
|
|
1117
|
+
this.name = "LicenseError";
|
|
1118
|
+
this.code = code;
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
var CryptoError = class extends Error {
|
|
1122
|
+
/**
|
|
1123
|
+
* Create a CryptoError
|
|
1124
|
+
* @param {string} message - Error message
|
|
1125
|
+
*/
|
|
1126
|
+
constructor(message) {
|
|
1127
|
+
super(message);
|
|
1128
|
+
this.name = "CryptoError";
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
|
|
1132
|
+
// node_modules/canonical-json/stringify.js
|
|
1133
|
+
var gap = "";
|
|
1134
|
+
var indent = "";
|
|
1135
|
+
var rep;
|
|
1136
|
+
var cmp;
|
|
1137
|
+
var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
|
|
1138
|
+
var meta = { "\b": "\\b", " ": "\\t", "\n": "\\n", "\f": "\\f", "\r": "\\r", '"': '\\"', "\\": "\\\\" };
|
|
1139
|
+
function quote(str2) {
|
|
1140
|
+
escapable.lastIndex = 0;
|
|
1141
|
+
return escapable.test(str2) ? '"' + str2.replace(escapable, (a) => meta[a] || "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4)) + '"' : '"' + str2 + '"';
|
|
1142
|
+
}
|
|
1143
|
+
function str(key, holder) {
|
|
1144
|
+
let value = holder[key];
|
|
1145
|
+
if (value && typeof value === "object" && typeof value.toJSON === "function") {
|
|
1146
|
+
value = value.toJSON(key);
|
|
1147
|
+
}
|
|
1148
|
+
if (typeof rep === "function") {
|
|
1149
|
+
value = rep.call(holder, key, value);
|
|
1150
|
+
}
|
|
1151
|
+
switch (typeof value) {
|
|
1152
|
+
case "string":
|
|
1153
|
+
return quote(value);
|
|
1154
|
+
case "number":
|
|
1155
|
+
return isFinite(value) ? String(value) : "null";
|
|
1156
|
+
case "boolean":
|
|
1157
|
+
case "undefined":
|
|
1158
|
+
case "object":
|
|
1159
|
+
if (value === null)
|
|
1160
|
+
return "null";
|
|
1161
|
+
const mind = gap;
|
|
1162
|
+
gap += indent;
|
|
1163
|
+
const partial = [];
|
|
1164
|
+
if (Array.isArray(value)) {
|
|
1165
|
+
for (let i = 0; i < value.length; i++)
|
|
1166
|
+
partial[i] = str(i, value) || "null";
|
|
1167
|
+
} else {
|
|
1168
|
+
const keys = Object.keys(value).sort(cmp);
|
|
1169
|
+
for (const k of keys) {
|
|
1170
|
+
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
|
1171
|
+
const v2 = str(k, value);
|
|
1172
|
+
if (v2)
|
|
1173
|
+
partial.push(quote(k) + (gap ? ": " : ":") + v2);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
let v;
|
|
1178
|
+
if (Array.isArray(value)) {
|
|
1179
|
+
v = partial.length === 0 ? "[]" : gap ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : "[" + partial.join(",") + "]";
|
|
1180
|
+
} else {
|
|
1181
|
+
v = partial.length === 0 ? "{}" : gap ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : "{" + partial.join(",") + "}";
|
|
1182
|
+
}
|
|
1183
|
+
gap = mind;
|
|
1184
|
+
return v;
|
|
1185
|
+
default:
|
|
1186
|
+
return String(value);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
function stringify(value, replacer, space, keyCompare) {
|
|
1190
|
+
gap = "";
|
|
1191
|
+
indent = "";
|
|
1192
|
+
rep = replacer;
|
|
1193
|
+
cmp = typeof keyCompare === "function" ? keyCompare : void 0;
|
|
1194
|
+
if (typeof space === "number") {
|
|
1195
|
+
indent = " ".repeat(space);
|
|
1196
|
+
} else if (typeof space === "string") {
|
|
1197
|
+
indent = space;
|
|
1198
|
+
}
|
|
1199
|
+
return str("", { "": value });
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// node_modules/canonical-json/index.js
|
|
1203
|
+
var canonical_json_default = stringify;
|
|
1204
|
+
|
|
1205
|
+
// src/utils.js
|
|
1206
|
+
function parseActiveEntitlements(payload = {}) {
|
|
1207
|
+
const raw = payload.entitlements || payload.active_entitlements || [];
|
|
1208
|
+
return raw.map((e) => ({
|
|
1209
|
+
key: e.key,
|
|
1210
|
+
expires_at: e.expires_at ?? null,
|
|
1211
|
+
metadata: e.metadata ?? null
|
|
1212
|
+
}));
|
|
1213
|
+
}
|
|
1214
|
+
function constantTimeEqual(a = "", b = "") {
|
|
1215
|
+
if (a.length !== b.length)
|
|
1216
|
+
return false;
|
|
1217
|
+
let res = 0;
|
|
1218
|
+
for (let i = 0; i < a.length; i++) {
|
|
1219
|
+
res |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
1220
|
+
}
|
|
1221
|
+
return res === 0;
|
|
1222
|
+
}
|
|
1223
|
+
function canonicalJsonStringify(obj) {
|
|
1224
|
+
const stringify2 = typeof canonical_json_default === "function" ? canonical_json_default : canonical_json_default && typeof canonical_json_default === "object" ? (
|
|
1225
|
+
/** @type {any} */
|
|
1226
|
+
canonical_json_default.stringify
|
|
1227
|
+
) : void 0;
|
|
1228
|
+
if (!stringify2 || typeof stringify2 !== "function") {
|
|
1229
|
+
console.warn(
|
|
1230
|
+
"[LicenseSeat SDK] canonical-json library not loaded correctly. Falling back to basic JSON.stringify. Signature verification might be unreliable if server uses different canonicalization."
|
|
1231
|
+
);
|
|
1232
|
+
try {
|
|
1233
|
+
const sortedObj = {};
|
|
1234
|
+
Object.keys(obj).sort().forEach((key) => {
|
|
1235
|
+
sortedObj[key] = obj[key];
|
|
1236
|
+
});
|
|
1237
|
+
return JSON.stringify(sortedObj);
|
|
1238
|
+
} catch (e) {
|
|
1239
|
+
return JSON.stringify(obj);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
return stringify2(obj);
|
|
1243
|
+
}
|
|
1244
|
+
function base64UrlDecode(base64UrlString) {
|
|
1245
|
+
let base64 = base64UrlString.replace(/-/g, "+").replace(/_/g, "/");
|
|
1246
|
+
while (base64.length % 4) {
|
|
1247
|
+
base64 += "=";
|
|
1248
|
+
}
|
|
1249
|
+
let raw;
|
|
1250
|
+
if (typeof atob === "function") {
|
|
1251
|
+
raw = atob(base64);
|
|
1252
|
+
} else if (typeof Buffer !== "undefined") {
|
|
1253
|
+
raw = Buffer.from(base64, "base64").toString("binary");
|
|
1254
|
+
} else {
|
|
1255
|
+
throw new Error("No base64 decoder available (neither atob nor Buffer found)");
|
|
1256
|
+
}
|
|
1257
|
+
const outputArray = new Uint8Array(raw.length);
|
|
1258
|
+
for (let i = 0; i < raw.length; ++i) {
|
|
1259
|
+
outputArray[i] = raw.charCodeAt(i);
|
|
1260
|
+
}
|
|
1261
|
+
return outputArray;
|
|
1262
|
+
}
|
|
1263
|
+
function hashCode(str2) {
|
|
1264
|
+
let hash = 0;
|
|
1265
|
+
for (let i = 0; i < str2.length; i++) {
|
|
1266
|
+
const char = str2.charCodeAt(i);
|
|
1267
|
+
hash = (hash << 5) - hash + char;
|
|
1268
|
+
hash = hash & hash;
|
|
1269
|
+
}
|
|
1270
|
+
return Math.abs(hash).toString(36);
|
|
1271
|
+
}
|
|
1272
|
+
function getCanvasFingerprint() {
|
|
1273
|
+
try {
|
|
1274
|
+
const canvas = document.createElement("canvas");
|
|
1275
|
+
const ctx = canvas.getContext("2d");
|
|
1276
|
+
ctx.textBaseline = "top";
|
|
1277
|
+
ctx.font = "14px Arial";
|
|
1278
|
+
ctx.fillText("LicenseSeat SDK", 2, 2);
|
|
1279
|
+
return canvas.toDataURL().slice(-50);
|
|
1280
|
+
} catch (e) {
|
|
1281
|
+
return "no-canvas";
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
function generateDeviceId() {
|
|
1285
|
+
if (typeof window === "undefined" || typeof navigator === "undefined") {
|
|
1286
|
+
const os = typeof process !== "undefined" ? process.platform : "unknown";
|
|
1287
|
+
const arch = typeof process !== "undefined" ? process.arch : "unknown";
|
|
1288
|
+
return `node-${hashCode(os + "|" + arch)}`;
|
|
1289
|
+
}
|
|
1290
|
+
const nav = window.navigator;
|
|
1291
|
+
const screen2 = window.screen;
|
|
1292
|
+
const data = [
|
|
1293
|
+
nav.userAgent,
|
|
1294
|
+
nav.language,
|
|
1295
|
+
screen2.colorDepth,
|
|
1296
|
+
screen2.width + "x" + screen2.height,
|
|
1297
|
+
(/* @__PURE__ */ new Date()).getTimezoneOffset(),
|
|
1298
|
+
nav.hardwareConcurrency,
|
|
1299
|
+
getCanvasFingerprint()
|
|
1300
|
+
].join("|");
|
|
1301
|
+
return `web-${hashCode(data)}`;
|
|
1302
|
+
}
|
|
1303
|
+
function sleep(ms) {
|
|
1304
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1305
|
+
}
|
|
1306
|
+
function getCsrfToken() {
|
|
1307
|
+
const token = document.querySelector('meta[name="csrf-token"]');
|
|
1308
|
+
return token ? token.content : "";
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// src/telemetry.js
|
|
1312
|
+
function detectOSName() {
|
|
1313
|
+
if (typeof process !== "undefined" && process.platform) {
|
|
1314
|
+
const map = {
|
|
1315
|
+
darwin: "macOS",
|
|
1316
|
+
win32: "Windows",
|
|
1317
|
+
linux: "Linux",
|
|
1318
|
+
freebsd: "FreeBSD",
|
|
1319
|
+
sunos: "SunOS"
|
|
1320
|
+
};
|
|
1321
|
+
return map[process.platform] || process.platform;
|
|
1322
|
+
}
|
|
1323
|
+
if (typeof navigator !== "undefined") {
|
|
1324
|
+
if (navigator.userAgentData && navigator.userAgentData.platform) {
|
|
1325
|
+
return navigator.userAgentData.platform;
|
|
1326
|
+
}
|
|
1327
|
+
const ua = navigator.userAgent || "";
|
|
1328
|
+
if (/Android/i.test(ua))
|
|
1329
|
+
return "Android";
|
|
1330
|
+
if (/iPhone|iPad|iPod/i.test(ua))
|
|
1331
|
+
return "iOS";
|
|
1332
|
+
if (/Mac/i.test(ua))
|
|
1333
|
+
return "macOS";
|
|
1334
|
+
if (/Win/i.test(ua))
|
|
1335
|
+
return "Windows";
|
|
1336
|
+
if (/Linux/i.test(ua))
|
|
1337
|
+
return "Linux";
|
|
1338
|
+
}
|
|
1339
|
+
return "Unknown";
|
|
1340
|
+
}
|
|
1341
|
+
function detectOSVersion() {
|
|
1342
|
+
if (typeof process !== "undefined" && process.version) {
|
|
1343
|
+
try {
|
|
1344
|
+
const os = await_free_os_release();
|
|
1345
|
+
if (os)
|
|
1346
|
+
return os;
|
|
1347
|
+
} catch (_) {
|
|
1348
|
+
}
|
|
1349
|
+
return process.version;
|
|
1350
|
+
}
|
|
1351
|
+
if (typeof navigator !== "undefined") {
|
|
1352
|
+
const ua = navigator.userAgent || "";
|
|
1353
|
+
const macMatch = ua.match(/Mac OS X\s+([\d._]+)/);
|
|
1354
|
+
if (macMatch)
|
|
1355
|
+
return macMatch[1].replace(/_/g, ".");
|
|
1356
|
+
const winMatch = ua.match(/Windows NT\s+([\d.]+)/);
|
|
1357
|
+
if (winMatch)
|
|
1358
|
+
return winMatch[1];
|
|
1359
|
+
const androidMatch = ua.match(/Android\s+([\d.]+)/);
|
|
1360
|
+
if (androidMatch)
|
|
1361
|
+
return androidMatch[1];
|
|
1362
|
+
const iosMatch = ua.match(/OS\s+([\d._]+)/);
|
|
1363
|
+
if (iosMatch)
|
|
1364
|
+
return iosMatch[1].replace(/_/g, ".");
|
|
1365
|
+
}
|
|
1366
|
+
return null;
|
|
1367
|
+
}
|
|
1368
|
+
function await_free_os_release() {
|
|
1369
|
+
try {
|
|
1370
|
+
const os = new Function("try { return require('os') } catch(e) { return null }")();
|
|
1371
|
+
if (os && os.release)
|
|
1372
|
+
return os.release();
|
|
1373
|
+
} catch (_) {
|
|
1374
|
+
}
|
|
1375
|
+
return null;
|
|
1376
|
+
}
|
|
1377
|
+
function dynamicRequire(moduleName) {
|
|
1378
|
+
try {
|
|
1379
|
+
return new Function("m", "try { return require(m) } catch(e) { return null }")(moduleName);
|
|
1380
|
+
} catch (_) {
|
|
1381
|
+
return null;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
function detectPlatform() {
|
|
1385
|
+
if (typeof process !== "undefined") {
|
|
1386
|
+
if (process.versions && process.versions.electron)
|
|
1387
|
+
return "electron";
|
|
1388
|
+
if (process.versions && process.versions.bun)
|
|
1389
|
+
return "bun";
|
|
1390
|
+
if (process.versions && process.versions.node)
|
|
1391
|
+
return "node";
|
|
1392
|
+
}
|
|
1393
|
+
if (typeof Deno !== "undefined")
|
|
1394
|
+
return "deno";
|
|
1395
|
+
if (typeof navigator !== "undefined" && navigator.product === "ReactNative")
|
|
1396
|
+
return "react-native";
|
|
1397
|
+
if (typeof window !== "undefined")
|
|
1398
|
+
return "browser";
|
|
1399
|
+
return "unknown";
|
|
1400
|
+
}
|
|
1401
|
+
function detectDeviceModel() {
|
|
1402
|
+
try {
|
|
1403
|
+
if (typeof navigator !== "undefined" && navigator.userAgentData) {
|
|
1404
|
+
return navigator.userAgentData.model || null;
|
|
1405
|
+
}
|
|
1406
|
+
} catch (_) {
|
|
1407
|
+
}
|
|
1408
|
+
return null;
|
|
1409
|
+
}
|
|
1410
|
+
function detectLocale() {
|
|
1411
|
+
if (typeof navigator !== "undefined" && navigator.language) {
|
|
1412
|
+
return navigator.language;
|
|
1413
|
+
}
|
|
1414
|
+
if (typeof Intl !== "undefined") {
|
|
1415
|
+
try {
|
|
1416
|
+
return Intl.DateTimeFormat().resolvedOptions().locale || null;
|
|
1417
|
+
} catch (_) {
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
if (typeof process !== "undefined" && process.env) {
|
|
1421
|
+
return process.env.LANG || process.env.LC_ALL || null;
|
|
1422
|
+
}
|
|
1423
|
+
return null;
|
|
1424
|
+
}
|
|
1425
|
+
function detectTimezone() {
|
|
1426
|
+
if (typeof Intl !== "undefined") {
|
|
1427
|
+
try {
|
|
1428
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || null;
|
|
1429
|
+
} catch (_) {
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return null;
|
|
1433
|
+
}
|
|
1434
|
+
function detectDeviceType() {
|
|
1435
|
+
try {
|
|
1436
|
+
const platform = detectPlatform();
|
|
1437
|
+
if (platform === "node" || platform === "bun" || platform === "deno")
|
|
1438
|
+
return "server";
|
|
1439
|
+
if (platform === "electron")
|
|
1440
|
+
return "desktop";
|
|
1441
|
+
if (platform === "react-native") {
|
|
1442
|
+
if (typeof screen !== "undefined" && screen.width) {
|
|
1443
|
+
return screen.width < 768 ? "phone" : "tablet";
|
|
1444
|
+
}
|
|
1445
|
+
return "phone";
|
|
1446
|
+
}
|
|
1447
|
+
if (typeof navigator !== "undefined") {
|
|
1448
|
+
if (navigator.userAgentData && typeof navigator.userAgentData.mobile === "boolean") {
|
|
1449
|
+
if (navigator.userAgentData.mobile) {
|
|
1450
|
+
if (typeof screen !== "undefined" && screen.width >= 768)
|
|
1451
|
+
return "tablet";
|
|
1452
|
+
return "phone";
|
|
1453
|
+
}
|
|
1454
|
+
return "desktop";
|
|
1455
|
+
}
|
|
1456
|
+
if (navigator.maxTouchPoints > 0) {
|
|
1457
|
+
if (typeof screen !== "undefined" && screen.width >= 768)
|
|
1458
|
+
return "tablet";
|
|
1459
|
+
return "phone";
|
|
1460
|
+
}
|
|
1461
|
+
return "desktop";
|
|
1462
|
+
}
|
|
1463
|
+
} catch (_) {
|
|
1464
|
+
}
|
|
1465
|
+
return "unknown";
|
|
1466
|
+
}
|
|
1467
|
+
function detectArchitecture() {
|
|
1468
|
+
try {
|
|
1469
|
+
if (typeof process !== "undefined" && process.arch) {
|
|
1470
|
+
const map = { ia32: "x86", x64: "x64", arm: "arm", arm64: "arm64" };
|
|
1471
|
+
return map[process.arch] || process.arch;
|
|
1472
|
+
}
|
|
1473
|
+
if (typeof navigator !== "undefined" && navigator.userAgentData) {
|
|
1474
|
+
if (navigator.userAgentData.architecture) {
|
|
1475
|
+
return navigator.userAgentData.architecture;
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
} catch (_) {
|
|
1479
|
+
}
|
|
1480
|
+
return null;
|
|
1481
|
+
}
|
|
1482
|
+
function detectCpuCores() {
|
|
1483
|
+
try {
|
|
1484
|
+
if (typeof navigator !== "undefined" && navigator.hardwareConcurrency) {
|
|
1485
|
+
return navigator.hardwareConcurrency;
|
|
1486
|
+
}
|
|
1487
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
1488
|
+
const os = dynamicRequire("os");
|
|
1489
|
+
if (os && os.cpus) {
|
|
1490
|
+
const cpus = os.cpus();
|
|
1491
|
+
if (cpus && cpus.length)
|
|
1492
|
+
return cpus.length;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
} catch (_) {
|
|
1496
|
+
}
|
|
1497
|
+
return null;
|
|
1498
|
+
}
|
|
1499
|
+
function detectMemoryGb() {
|
|
1500
|
+
try {
|
|
1501
|
+
if (typeof navigator !== "undefined" && navigator.deviceMemory) {
|
|
1502
|
+
return navigator.deviceMemory;
|
|
1503
|
+
}
|
|
1504
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
1505
|
+
const os = dynamicRequire("os");
|
|
1506
|
+
if (os && os.totalmem) {
|
|
1507
|
+
return Math.round(os.totalmem() / (1024 * 1024 * 1024));
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
} catch (_) {
|
|
1511
|
+
}
|
|
1512
|
+
return null;
|
|
1513
|
+
}
|
|
1514
|
+
function detectLanguage() {
|
|
1515
|
+
try {
|
|
1516
|
+
const locale = detectLocale();
|
|
1517
|
+
if (locale) {
|
|
1518
|
+
const lang = locale.split(/[-_]/)[0];
|
|
1519
|
+
if (lang && lang.length >= 2)
|
|
1520
|
+
return lang.toLowerCase();
|
|
1521
|
+
}
|
|
1522
|
+
} catch (_) {
|
|
1523
|
+
}
|
|
1524
|
+
return null;
|
|
1525
|
+
}
|
|
1526
|
+
function detectScreenResolution() {
|
|
1527
|
+
try {
|
|
1528
|
+
if (typeof screen !== "undefined" && screen.width && screen.height) {
|
|
1529
|
+
return `${screen.width}x${screen.height}`;
|
|
1530
|
+
}
|
|
1531
|
+
} catch (_) {
|
|
1532
|
+
}
|
|
1533
|
+
return null;
|
|
1534
|
+
}
|
|
1535
|
+
function detectDisplayScale() {
|
|
1536
|
+
try {
|
|
1537
|
+
if (typeof window !== "undefined" && window.devicePixelRatio) {
|
|
1538
|
+
return window.devicePixelRatio;
|
|
1539
|
+
}
|
|
1540
|
+
} catch (_) {
|
|
1541
|
+
}
|
|
1542
|
+
return null;
|
|
1543
|
+
}
|
|
1544
|
+
function detectBrowserName() {
|
|
1545
|
+
try {
|
|
1546
|
+
if (typeof navigator === "undefined")
|
|
1547
|
+
return null;
|
|
1548
|
+
if (navigator.userAgentData && navigator.userAgentData.brands) {
|
|
1549
|
+
const brands = navigator.userAgentData.brands;
|
|
1550
|
+
for (const b of brands) {
|
|
1551
|
+
const name = b.brand || "";
|
|
1552
|
+
if (/^(Google Chrome|Microsoft Edge|Opera|Brave|Vivaldi|Samsung Internet)$/i.test(name)) {
|
|
1553
|
+
return name;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
for (const b of brands) {
|
|
1557
|
+
if ((b.brand || "").toLowerCase() === "chromium")
|
|
1558
|
+
return "Chrome";
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
const ua = navigator.userAgent || "";
|
|
1562
|
+
if (/Edg\//i.test(ua))
|
|
1563
|
+
return "Edge";
|
|
1564
|
+
if (/OPR\//i.test(ua) || /Opera/i.test(ua))
|
|
1565
|
+
return "Opera";
|
|
1566
|
+
if (/Brave/i.test(ua))
|
|
1567
|
+
return "Brave";
|
|
1568
|
+
if (/Vivaldi/i.test(ua))
|
|
1569
|
+
return "Vivaldi";
|
|
1570
|
+
if (/Firefox/i.test(ua))
|
|
1571
|
+
return "Firefox";
|
|
1572
|
+
if (/SamsungBrowser/i.test(ua))
|
|
1573
|
+
return "Samsung Internet";
|
|
1574
|
+
if (/CriOS/i.test(ua))
|
|
1575
|
+
return "Chrome";
|
|
1576
|
+
if (/Chrome/i.test(ua))
|
|
1577
|
+
return "Chrome";
|
|
1578
|
+
if (/Safari/i.test(ua))
|
|
1579
|
+
return "Safari";
|
|
1580
|
+
} catch (_) {
|
|
1581
|
+
}
|
|
1582
|
+
return null;
|
|
1583
|
+
}
|
|
1584
|
+
function detectBrowserVersion() {
|
|
1585
|
+
try {
|
|
1586
|
+
if (typeof navigator === "undefined")
|
|
1587
|
+
return null;
|
|
1588
|
+
if (navigator.userAgentData && navigator.userAgentData.brands) {
|
|
1589
|
+
const brands = navigator.userAgentData.brands;
|
|
1590
|
+
for (const b of brands) {
|
|
1591
|
+
const name = b.brand || "";
|
|
1592
|
+
if (/^(Google Chrome|Microsoft Edge|Opera|Brave|Vivaldi|Samsung Internet)$/i.test(name)) {
|
|
1593
|
+
return b.version || null;
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
for (const b of brands) {
|
|
1597
|
+
if ((b.brand || "").toLowerCase() === "chromium")
|
|
1598
|
+
return b.version || null;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
const ua = navigator.userAgent || "";
|
|
1602
|
+
const patterns = [
|
|
1603
|
+
/Edg\/([\d.]+)/,
|
|
1604
|
+
/OPR\/([\d.]+)/,
|
|
1605
|
+
/Firefox\/([\d.]+)/,
|
|
1606
|
+
/SamsungBrowser\/([\d.]+)/,
|
|
1607
|
+
/CriOS\/([\d.]+)/,
|
|
1608
|
+
/Chrome\/([\d.]+)/,
|
|
1609
|
+
/Version\/([\d.]+).*Safari/
|
|
1610
|
+
];
|
|
1611
|
+
for (const re of patterns) {
|
|
1612
|
+
const m = ua.match(re);
|
|
1613
|
+
if (m)
|
|
1614
|
+
return m[1];
|
|
1615
|
+
}
|
|
1616
|
+
} catch (_) {
|
|
1617
|
+
}
|
|
1618
|
+
return null;
|
|
1619
|
+
}
|
|
1620
|
+
function detectRuntimeVersion() {
|
|
1621
|
+
try {
|
|
1622
|
+
if (typeof process !== "undefined" && process.versions) {
|
|
1623
|
+
if (process.versions.bun)
|
|
1624
|
+
return process.versions.bun;
|
|
1625
|
+
if (process.versions.electron)
|
|
1626
|
+
return process.versions.electron;
|
|
1627
|
+
if (process.versions.node)
|
|
1628
|
+
return process.versions.node;
|
|
1629
|
+
}
|
|
1630
|
+
if (typeof Deno !== "undefined" && Deno.version)
|
|
1631
|
+
return Deno.version.deno;
|
|
1632
|
+
} catch (_) {
|
|
1633
|
+
}
|
|
1634
|
+
return null;
|
|
1635
|
+
}
|
|
1636
|
+
function collectTelemetry(sdkVersion, options) {
|
|
1637
|
+
const locale = detectLocale();
|
|
1638
|
+
const raw = {
|
|
1639
|
+
sdk_version: sdkVersion,
|
|
1640
|
+
sdk_name: "js",
|
|
1641
|
+
os_name: detectOSName(),
|
|
1642
|
+
os_version: detectOSVersion(),
|
|
1643
|
+
platform: detectPlatform(),
|
|
1644
|
+
device_model: detectDeviceModel(),
|
|
1645
|
+
device_type: detectDeviceType(),
|
|
1646
|
+
locale,
|
|
1647
|
+
timezone: detectTimezone(),
|
|
1648
|
+
language: detectLanguage(),
|
|
1649
|
+
architecture: detectArchitecture(),
|
|
1650
|
+
cpu_cores: detectCpuCores(),
|
|
1651
|
+
memory_gb: detectMemoryGb(),
|
|
1652
|
+
screen_resolution: detectScreenResolution(),
|
|
1653
|
+
display_scale: detectDisplayScale(),
|
|
1654
|
+
browser_name: detectBrowserName(),
|
|
1655
|
+
browser_version: detectBrowserVersion(),
|
|
1656
|
+
runtime_version: detectRuntimeVersion(),
|
|
1657
|
+
app_version: options && options.appVersion || null,
|
|
1658
|
+
app_build: options && options.appBuild || null
|
|
1659
|
+
};
|
|
1660
|
+
const result = {};
|
|
1661
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
1662
|
+
if (value != null) {
|
|
1663
|
+
result[key] = value;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
return result;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
// src/LicenseSeat.js
|
|
1670
|
+
var SDK_VERSION = "0.4.2";
|
|
1671
|
+
var DEFAULT_CONFIG = {
|
|
1672
|
+
apiBaseUrl: "https://licenseseat.com/api/v1",
|
|
1673
|
+
productSlug: null,
|
|
1674
|
+
// Required: Product slug for API calls (e.g., "my-app")
|
|
1675
|
+
storagePrefix: "licenseseat_",
|
|
1676
|
+
autoValidateInterval: 36e5,
|
|
1677
|
+
// 1 hour
|
|
1678
|
+
heartbeatInterval: 3e5,
|
|
1679
|
+
// 5 minutes
|
|
1680
|
+
networkRecheckInterval: 3e4,
|
|
1681
|
+
// 30 seconds
|
|
1682
|
+
maxRetries: 3,
|
|
1683
|
+
retryDelay: 1e3,
|
|
1684
|
+
apiKey: null,
|
|
1685
|
+
debug: false,
|
|
1686
|
+
offlineLicenseRefreshInterval: 1e3 * 60 * 60 * 72,
|
|
1687
|
+
// 72 hours
|
|
1688
|
+
offlineFallbackEnabled: false,
|
|
1689
|
+
// default false (strict mode, matches Swift SDK)
|
|
1690
|
+
maxOfflineDays: 0,
|
|
1691
|
+
// 0 = disabled
|
|
1692
|
+
maxClockSkewMs: 5 * 60 * 1e3,
|
|
1693
|
+
// 5 minutes
|
|
1694
|
+
autoInitialize: true,
|
|
1695
|
+
telemetryEnabled: true,
|
|
1696
|
+
// Set false to disable telemetry (e.g. for GDPR compliance)
|
|
1697
|
+
appVersion: null,
|
|
1698
|
+
// User-provided app version, sent as app_version in telemetry
|
|
1699
|
+
appBuild: null
|
|
1700
|
+
// User-provided app build, sent as app_build in telemetry
|
|
1701
|
+
};
|
|
1702
|
+
var LicenseSeatSDK = class {
|
|
1703
|
+
/**
|
|
1704
|
+
* Create a new LicenseSeat SDK instance
|
|
1705
|
+
* @param {import('./types.js').LicenseSeatConfig} [config={}] - Configuration options
|
|
1706
|
+
*/
|
|
1707
|
+
constructor(config = {}) {
|
|
1708
|
+
this.config = {
|
|
1709
|
+
...DEFAULT_CONFIG,
|
|
1710
|
+
...config
|
|
1711
|
+
};
|
|
1712
|
+
this.eventListeners = {};
|
|
1713
|
+
this.validationTimer = null;
|
|
1714
|
+
this.heartbeatTimer = null;
|
|
1715
|
+
this.cache = new LicenseCache(this.config.storagePrefix);
|
|
1716
|
+
this.online = true;
|
|
1717
|
+
this.currentAutoLicenseKey = null;
|
|
1718
|
+
this.connectivityTimer = null;
|
|
1719
|
+
this.offlineRefreshTimer = null;
|
|
1720
|
+
this.lastOfflineValidation = null;
|
|
1721
|
+
this.syncingOfflineAssets = false;
|
|
1722
|
+
this.destroyed = false;
|
|
1723
|
+
if (ed25519_exports && etc && sha5122) {
|
|
1724
|
+
etc.sha512Sync = (...m) => sha5122(etc.concatBytes(...m));
|
|
1725
|
+
} else {
|
|
1726
|
+
console.error(
|
|
1727
|
+
"[LicenseSeat SDK] Noble-ed25519 or Noble-hashes not loaded correctly. Sync crypto methods may fail."
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
if (this.config.autoInitialize) {
|
|
1731
|
+
this.initialize();
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
/**
|
|
1735
|
+
* Initialize the SDK
|
|
1736
|
+
* Loads cached license and starts auto-validation if configured.
|
|
1737
|
+
* Called automatically unless autoInitialize is set to false.
|
|
1738
|
+
* @returns {void}
|
|
1739
|
+
*/
|
|
1740
|
+
initialize() {
|
|
1741
|
+
this.log("LicenseSeat SDK initialized", this.config);
|
|
1742
|
+
const cachedLicense = this.cache.getLicense();
|
|
1743
|
+
if (cachedLicense) {
|
|
1744
|
+
this.emit("license:loaded", cachedLicense);
|
|
1745
|
+
if (this.config.offlineFallbackEnabled) {
|
|
1746
|
+
this.quickVerifyCachedOfflineLocal().then((offlineResult) => {
|
|
1747
|
+
if (offlineResult) {
|
|
1748
|
+
this.cache.updateValidation(offlineResult);
|
|
1749
|
+
if (offlineResult.valid) {
|
|
1750
|
+
this.emit("validation:offline-success", offlineResult);
|
|
1751
|
+
} else {
|
|
1752
|
+
this.emit("validation:offline-failed", offlineResult);
|
|
1753
|
+
}
|
|
1754
|
+
this.lastOfflineValidation = offlineResult;
|
|
1755
|
+
}
|
|
1756
|
+
}).catch(() => {
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
if (this.config.apiKey) {
|
|
1760
|
+
this.startAutoValidation(cachedLicense.license_key);
|
|
1761
|
+
this.startHeartbeat();
|
|
1762
|
+
this.validateLicense(cachedLicense.license_key).catch((err2) => {
|
|
1763
|
+
this.log("Background validation failed:", err2);
|
|
1764
|
+
if (err2 instanceof APIError && (err2.status === 401 || err2.status === 501)) {
|
|
1765
|
+
this.log(
|
|
1766
|
+
"Authentication issue during validation, using cached license data"
|
|
1767
|
+
);
|
|
1768
|
+
this.emit("validation:auth-failed", {
|
|
1769
|
+
licenseKey: cachedLicense.license_key,
|
|
1770
|
+
error: err2,
|
|
1771
|
+
cached: true
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
/**
|
|
1779
|
+
* Activate a license
|
|
1780
|
+
* @param {string} licenseKey - The license key to activate
|
|
1781
|
+
* @param {import('./types.js').ActivationOptions} [options={}] - Activation options
|
|
1782
|
+
* @returns {Promise<import('./types.js').CachedLicense>} Activation result with cached license data
|
|
1783
|
+
* @throws {ConfigurationError} When productSlug is not configured
|
|
1784
|
+
* @throws {APIError} When the API request fails
|
|
1785
|
+
*/
|
|
1786
|
+
async activate(licenseKey, options = {}) {
|
|
1787
|
+
if (!this.config.productSlug) {
|
|
1788
|
+
throw new ConfigurationError("productSlug is required for activation");
|
|
1789
|
+
}
|
|
1790
|
+
const deviceId = options.deviceId || generateDeviceId();
|
|
1791
|
+
const payload = {
|
|
1792
|
+
device_id: deviceId,
|
|
1793
|
+
metadata: options.metadata || {}
|
|
1794
|
+
};
|
|
1795
|
+
if (options.deviceName) {
|
|
1796
|
+
payload.device_name = options.deviceName;
|
|
1797
|
+
}
|
|
1798
|
+
try {
|
|
1799
|
+
this.emit("activation:start", { licenseKey, deviceId });
|
|
1800
|
+
const response = await this.apiCall(
|
|
1801
|
+
`/products/${this.config.productSlug}/licenses/${encodeURIComponent(licenseKey)}/activate`,
|
|
1802
|
+
{
|
|
1803
|
+
method: "POST",
|
|
1804
|
+
body: payload
|
|
1805
|
+
}
|
|
1806
|
+
);
|
|
1807
|
+
const licenseData = {
|
|
1808
|
+
license_key: licenseKey,
|
|
1809
|
+
device_id: deviceId,
|
|
1810
|
+
activation: response,
|
|
1811
|
+
activated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1812
|
+
last_validated: (/* @__PURE__ */ new Date()).toISOString()
|
|
1813
|
+
};
|
|
1814
|
+
this.cache.setLicense(licenseData);
|
|
1815
|
+
this.cache.updateValidation({ valid: true, optimistic: true });
|
|
1816
|
+
this.startAutoValidation(licenseKey);
|
|
1817
|
+
this.startHeartbeat();
|
|
1818
|
+
this.syncOfflineAssets();
|
|
1819
|
+
this.scheduleOfflineRefresh();
|
|
1820
|
+
this.emit("activation:success", licenseData);
|
|
1821
|
+
return licenseData;
|
|
1822
|
+
} catch (error) {
|
|
1823
|
+
this.emit("activation:error", { licenseKey, error });
|
|
1824
|
+
throw error;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Deactivate the current license
|
|
1829
|
+
* @returns {Promise<Object>} Deactivation result from the API
|
|
1830
|
+
* @throws {ConfigurationError} When productSlug is not configured
|
|
1831
|
+
* @throws {LicenseError} When no active license is found
|
|
1832
|
+
* @throws {APIError} When the API request fails
|
|
1833
|
+
*/
|
|
1834
|
+
async deactivate() {
|
|
1835
|
+
if (!this.config.productSlug) {
|
|
1836
|
+
throw new ConfigurationError("productSlug is required for deactivation");
|
|
1837
|
+
}
|
|
1838
|
+
const cachedLicense = this.cache.getLicense();
|
|
1839
|
+
if (!cachedLicense) {
|
|
1840
|
+
throw new LicenseError("No active license found", "no_license");
|
|
1841
|
+
}
|
|
1842
|
+
try {
|
|
1843
|
+
this.emit("deactivation:start", cachedLicense);
|
|
1844
|
+
const response = await this.apiCall(
|
|
1845
|
+
`/products/${this.config.productSlug}/licenses/${encodeURIComponent(cachedLicense.license_key)}/deactivate`,
|
|
1846
|
+
{
|
|
1847
|
+
method: "POST",
|
|
1848
|
+
body: {
|
|
1849
|
+
device_id: cachedLicense.device_id
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
);
|
|
1853
|
+
this.cache.clearLicense();
|
|
1854
|
+
this.cache.clearOfflineToken();
|
|
1855
|
+
this.stopAutoValidation();
|
|
1856
|
+
this.stopHeartbeat();
|
|
1857
|
+
this.emit("deactivation:success", response);
|
|
1858
|
+
return response;
|
|
1859
|
+
} catch (error) {
|
|
1860
|
+
this.emit("deactivation:error", { error, license: cachedLicense });
|
|
1861
|
+
throw error;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
/**
|
|
1865
|
+
* Validate a license
|
|
1866
|
+
* @param {string} licenseKey - License key to validate
|
|
1867
|
+
* @param {import('./types.js').ValidationOptions} [options={}] - Validation options
|
|
1868
|
+
* @returns {Promise<import('./types.js').ValidationResult>} Validation result
|
|
1869
|
+
* @throws {ConfigurationError} When productSlug is not configured
|
|
1870
|
+
* @throws {APIError} When the API request fails and offline fallback is not available
|
|
1871
|
+
*/
|
|
1872
|
+
async validateLicense(licenseKey, options = {}) {
|
|
1873
|
+
if (!this.config.productSlug) {
|
|
1874
|
+
throw new ConfigurationError("productSlug is required for validation");
|
|
1875
|
+
}
|
|
1876
|
+
try {
|
|
1877
|
+
this.emit("validation:start", { licenseKey });
|
|
1878
|
+
const rawResponse = await this.apiCall(
|
|
1879
|
+
`/products/${this.config.productSlug}/licenses/${encodeURIComponent(licenseKey)}/validate`,
|
|
1880
|
+
{
|
|
1881
|
+
method: "POST",
|
|
1882
|
+
body: {
|
|
1883
|
+
device_id: options.deviceId || this.cache.getDeviceId()
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
);
|
|
1887
|
+
const response = {
|
|
1888
|
+
valid: rawResponse.valid,
|
|
1889
|
+
code: rawResponse.code,
|
|
1890
|
+
message: rawResponse.message,
|
|
1891
|
+
warnings: rawResponse.warnings,
|
|
1892
|
+
license: rawResponse.license,
|
|
1893
|
+
activation: rawResponse.activation,
|
|
1894
|
+
// Extract entitlements from license for easy access
|
|
1895
|
+
active_entitlements: rawResponse.license?.active_entitlements || []
|
|
1896
|
+
};
|
|
1897
|
+
const cachedLicense = this.cache.getLicense();
|
|
1898
|
+
if ((!response.active_entitlements || response.active_entitlements.length === 0) && cachedLicense?.validation?.active_entitlements?.length) {
|
|
1899
|
+
response.active_entitlements = cachedLicense.validation.active_entitlements;
|
|
1900
|
+
}
|
|
1901
|
+
if (cachedLicense && cachedLicense.license_key === licenseKey) {
|
|
1902
|
+
this.cache.updateValidation(response);
|
|
1903
|
+
}
|
|
1904
|
+
if (response.valid) {
|
|
1905
|
+
this.emit("validation:success", response);
|
|
1906
|
+
this.cache.setLastSeenTimestamp(Date.now());
|
|
1907
|
+
} else {
|
|
1908
|
+
this.emit("validation:failed", response);
|
|
1909
|
+
this.stopAutoValidation();
|
|
1910
|
+
this.currentAutoLicenseKey = null;
|
|
1911
|
+
}
|
|
1912
|
+
this.cache.setLastSeenTimestamp(Date.now());
|
|
1913
|
+
return response;
|
|
1914
|
+
} catch (error) {
|
|
1915
|
+
this.emit("validation:error", { licenseKey, error });
|
|
1916
|
+
const isNetworkFailure = error instanceof TypeError && error.message.includes("fetch") || error instanceof APIError && [0, 408].includes(error.status);
|
|
1917
|
+
if (this.config.offlineFallbackEnabled && isNetworkFailure) {
|
|
1918
|
+
const offlineResult = await this.verifyCachedOffline();
|
|
1919
|
+
const cachedLicense = this.cache.getLicense();
|
|
1920
|
+
if (cachedLicense && cachedLicense.license_key === licenseKey) {
|
|
1921
|
+
this.cache.updateValidation(offlineResult);
|
|
1922
|
+
}
|
|
1923
|
+
if (offlineResult.valid) {
|
|
1924
|
+
this.emit("validation:offline-success", offlineResult);
|
|
1925
|
+
return offlineResult;
|
|
1926
|
+
} else {
|
|
1927
|
+
this.emit("validation:offline-failed", offlineResult);
|
|
1928
|
+
this.stopAutoValidation();
|
|
1929
|
+
this.currentAutoLicenseKey = null;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
if (error instanceof APIError && error.data) {
|
|
1933
|
+
const cachedLicense = this.cache.getLicense();
|
|
1934
|
+
if (cachedLicense && cachedLicense.license_key === licenseKey) {
|
|
1935
|
+
const errorCode = error.data.error?.code || error.data.code;
|
|
1936
|
+
const errorMessage = error.data.error?.message || error.data.message;
|
|
1937
|
+
this.cache.updateValidation({
|
|
1938
|
+
valid: false,
|
|
1939
|
+
code: errorCode,
|
|
1940
|
+
message: errorMessage
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
if (![0, 408, 429].includes(error.status)) {
|
|
1944
|
+
this.stopAutoValidation();
|
|
1945
|
+
this.currentAutoLicenseKey = null;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
throw error;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
/**
|
|
1952
|
+
* Check if a specific entitlement is active (detailed version)
|
|
1953
|
+
* @param {string} entitlementKey - The entitlement key to check
|
|
1954
|
+
* @returns {import('./types.js').EntitlementCheckResult} Entitlement status with details
|
|
1955
|
+
*/
|
|
1956
|
+
checkEntitlement(entitlementKey) {
|
|
1957
|
+
const license = this.cache.getLicense();
|
|
1958
|
+
if (!license || !license.validation) {
|
|
1959
|
+
return { active: false, reason: "no_license" };
|
|
1960
|
+
}
|
|
1961
|
+
const entitlements = license.validation.active_entitlements || [];
|
|
1962
|
+
const entitlement = entitlements.find((e) => e.key === entitlementKey);
|
|
1963
|
+
if (!entitlement) {
|
|
1964
|
+
return { active: false, reason: "not_found" };
|
|
1965
|
+
}
|
|
1966
|
+
if (entitlement.expires_at) {
|
|
1967
|
+
const expiresAt = new Date(entitlement.expires_at);
|
|
1968
|
+
const now = /* @__PURE__ */ new Date();
|
|
1969
|
+
if (expiresAt < now) {
|
|
1970
|
+
return {
|
|
1971
|
+
active: false,
|
|
1972
|
+
reason: "expired",
|
|
1973
|
+
expires_at: entitlement.expires_at
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
return { active: true, entitlement };
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Check if a specific entitlement is active (simple boolean version)
|
|
1981
|
+
* This is a convenience method that returns a simple boolean.
|
|
1982
|
+
* Use checkEntitlement() for detailed status information.
|
|
1983
|
+
* @param {string} entitlementKey - The entitlement key to check
|
|
1984
|
+
* @returns {boolean} True if the entitlement is active, false otherwise
|
|
1985
|
+
*/
|
|
1986
|
+
hasEntitlement(entitlementKey) {
|
|
1987
|
+
return this.checkEntitlement(entitlementKey).active;
|
|
1988
|
+
}
|
|
1989
|
+
/**
|
|
1990
|
+
* Get offline token data from the server
|
|
1991
|
+
* @param {Object} [options={}] - Options for offline token generation
|
|
1992
|
+
* @param {string} [options.deviceId] - Device ID to bind the token to (required for hardware_locked mode)
|
|
1993
|
+
* @param {number} [options.ttlDays] - Token lifetime in days (default: 30, max: 90)
|
|
1994
|
+
* @returns {Promise<import('./types.js').OfflineToken>} Offline token data
|
|
1995
|
+
* @throws {ConfigurationError} When productSlug is not configured
|
|
1996
|
+
* @throws {LicenseError} When no active license is found
|
|
1997
|
+
* @throws {APIError} When the API request fails
|
|
1998
|
+
*/
|
|
1999
|
+
async getOfflineToken(options = {}) {
|
|
2000
|
+
if (!this.config.productSlug) {
|
|
2001
|
+
throw new ConfigurationError("productSlug is required for offline token");
|
|
2002
|
+
}
|
|
2003
|
+
const license = this.cache.getLicense();
|
|
2004
|
+
if (!license || !license.license_key) {
|
|
2005
|
+
const errorMsg = "No active license key found in cache to fetch offline token.";
|
|
2006
|
+
this.emit("sdk:error", { message: errorMsg });
|
|
2007
|
+
throw new LicenseError(errorMsg, "no_license");
|
|
2008
|
+
}
|
|
2009
|
+
try {
|
|
2010
|
+
this.emit("offlineToken:fetching", { licenseKey: license.license_key });
|
|
2011
|
+
const body = {};
|
|
2012
|
+
if (options.deviceId) {
|
|
2013
|
+
body.device_id = options.deviceId;
|
|
2014
|
+
}
|
|
2015
|
+
if (options.ttlDays) {
|
|
2016
|
+
body.ttl_days = options.ttlDays;
|
|
2017
|
+
}
|
|
2018
|
+
const path = `/products/${this.config.productSlug}/licenses/${encodeURIComponent(license.license_key)}/offline_token`;
|
|
2019
|
+
const response = await this.apiCall(path, {
|
|
2020
|
+
method: "POST",
|
|
2021
|
+
body: Object.keys(body).length > 0 ? body : void 0
|
|
2022
|
+
});
|
|
2023
|
+
this.emit("offlineToken:fetched", {
|
|
2024
|
+
licenseKey: license.license_key,
|
|
2025
|
+
data: response
|
|
2026
|
+
});
|
|
2027
|
+
return response;
|
|
2028
|
+
} catch (error) {
|
|
2029
|
+
this.log(
|
|
2030
|
+
`Failed to get offline token for ${license.license_key}:`,
|
|
2031
|
+
error
|
|
2032
|
+
);
|
|
2033
|
+
this.emit("offlineToken:fetchError", {
|
|
2034
|
+
licenseKey: license.license_key,
|
|
2035
|
+
error
|
|
2036
|
+
});
|
|
2037
|
+
throw error;
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* Fetch a signing key from the server by key ID
|
|
2042
|
+
* @param {string} keyId - The Key ID (kid) for which to fetch the signing key
|
|
2043
|
+
* @returns {Promise<import('./types.js').SigningKey>} Signing key data
|
|
2044
|
+
* @throws {Error} When keyId is not provided or the key is not found
|
|
2045
|
+
*/
|
|
2046
|
+
async getSigningKey(keyId) {
|
|
2047
|
+
if (!keyId) {
|
|
2048
|
+
throw new Error("Key ID is required to fetch a signing key.");
|
|
2049
|
+
}
|
|
2050
|
+
try {
|
|
2051
|
+
this.log(`Fetching signing key for kid: ${keyId}`);
|
|
2052
|
+
const response = await this.apiCall(`/signing_keys/${encodeURIComponent(keyId)}`, {
|
|
2053
|
+
method: "GET"
|
|
2054
|
+
});
|
|
2055
|
+
if (response && response.public_key) {
|
|
2056
|
+
this.log(`Successfully fetched signing key for kid: ${keyId}`);
|
|
2057
|
+
return response;
|
|
2058
|
+
} else {
|
|
2059
|
+
throw new Error(
|
|
2060
|
+
`Signing key not found or invalid response for kid: ${keyId}`
|
|
2061
|
+
);
|
|
2062
|
+
}
|
|
2063
|
+
} catch (error) {
|
|
2064
|
+
this.log(`Failed to fetch signing key for kid ${keyId}:`, error);
|
|
2065
|
+
throw error;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
/**
|
|
2069
|
+
* Verify a signed offline token client-side using Ed25519
|
|
2070
|
+
* @param {import('./types.js').OfflineToken} offlineTokenData - The offline token data
|
|
2071
|
+
* @param {string} publicKeyB64 - Base64-encoded public Ed25519 key
|
|
2072
|
+
* @returns {Promise<boolean>} True if verification is successful
|
|
2073
|
+
* @throws {CryptoError} When crypto library is not available
|
|
2074
|
+
* @throws {Error} When inputs are invalid
|
|
2075
|
+
*/
|
|
2076
|
+
async verifyOfflineToken(offlineTokenData, publicKeyB64) {
|
|
2077
|
+
this.log("Attempting to verify offline token client-side.");
|
|
2078
|
+
if (!offlineTokenData || !offlineTokenData.canonical || !offlineTokenData.signature) {
|
|
2079
|
+
throw new Error("Invalid offline token object provided. Expected format: { token, signature, canonical }");
|
|
2080
|
+
}
|
|
2081
|
+
if (!publicKeyB64) {
|
|
2082
|
+
throw new Error("Public key (Base64 encoded) is required.");
|
|
2083
|
+
}
|
|
2084
|
+
if (!ed25519_exports || !verify || !etc.sha512Sync) {
|
|
2085
|
+
const err2 = new CryptoError(
|
|
2086
|
+
"noble-ed25519 crypto library not available/configured for offline verification."
|
|
2087
|
+
);
|
|
2088
|
+
this.emit("sdk:error", { message: err2.message });
|
|
2089
|
+
throw err2;
|
|
2090
|
+
}
|
|
2091
|
+
try {
|
|
2092
|
+
const messageBytes = new TextEncoder().encode(offlineTokenData.canonical);
|
|
2093
|
+
const signatureBytes = base64UrlDecode(offlineTokenData.signature.value);
|
|
2094
|
+
const publicKeyBytes = base64UrlDecode(publicKeyB64);
|
|
2095
|
+
const isValid = verify(signatureBytes, messageBytes, publicKeyBytes);
|
|
2096
|
+
if (isValid) {
|
|
2097
|
+
this.log("Offline token signature VERIFIED successfully client-side.");
|
|
2098
|
+
this.emit("offlineToken:verified", { token: offlineTokenData.token });
|
|
2099
|
+
} else {
|
|
2100
|
+
this.log("Offline token signature INVALID client-side.");
|
|
2101
|
+
this.emit("offlineToken:verificationFailed", { token: offlineTokenData.token });
|
|
2102
|
+
}
|
|
2103
|
+
return isValid;
|
|
2104
|
+
} catch (error) {
|
|
2105
|
+
this.log("Client-side offline token verification error:", error);
|
|
2106
|
+
this.emit("sdk:error", {
|
|
2107
|
+
message: "Client-side verification failed.",
|
|
2108
|
+
error
|
|
2109
|
+
});
|
|
2110
|
+
throw error;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
/**
|
|
2114
|
+
* Get current license status
|
|
2115
|
+
* @returns {import('./types.js').LicenseStatus} Current license status
|
|
2116
|
+
*/
|
|
2117
|
+
getStatus() {
|
|
2118
|
+
const license = this.cache.getLicense();
|
|
2119
|
+
if (!license) {
|
|
2120
|
+
return { status: "inactive", message: "No license activated" };
|
|
2121
|
+
}
|
|
2122
|
+
const validation = license.validation;
|
|
2123
|
+
if (!validation) {
|
|
2124
|
+
return { status: "pending", message: "License pending validation" };
|
|
2125
|
+
}
|
|
2126
|
+
if (!validation.valid) {
|
|
2127
|
+
if (validation.offline) {
|
|
2128
|
+
return {
|
|
2129
|
+
status: "offline-invalid",
|
|
2130
|
+
message: validation.code || "License invalid (offline)"
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
return {
|
|
2134
|
+
status: "invalid",
|
|
2135
|
+
message: validation.message || validation.code || "License invalid"
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
if (validation.offline) {
|
|
2139
|
+
return {
|
|
2140
|
+
status: "offline-valid",
|
|
2141
|
+
license: license.license_key,
|
|
2142
|
+
device: license.device_id,
|
|
2143
|
+
activated_at: license.activated_at,
|
|
2144
|
+
last_validated: license.last_validated,
|
|
2145
|
+
entitlements: validation.active_entitlements || []
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
return {
|
|
2149
|
+
status: "active",
|
|
2150
|
+
license: license.license_key,
|
|
2151
|
+
device: license.device_id,
|
|
2152
|
+
activated_at: license.activated_at,
|
|
2153
|
+
last_validated: license.last_validated,
|
|
2154
|
+
entitlements: validation.active_entitlements || []
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
/**
|
|
2158
|
+
* Test API connectivity
|
|
2159
|
+
* Makes a request to the health endpoint to verify connectivity.
|
|
2160
|
+
* Note: To fully verify API key validity, attempt an actual operation like activate() or validateLicense().
|
|
2161
|
+
* @returns {Promise<{authenticated: boolean, healthy: boolean, api_version: string}>}
|
|
2162
|
+
* @throws {ConfigurationError} If API key is not configured
|
|
2163
|
+
* @throws {APIError} If the health check fails
|
|
2164
|
+
*/
|
|
2165
|
+
async testAuth() {
|
|
2166
|
+
if (!this.config.apiKey) {
|
|
2167
|
+
const err2 = new ConfigurationError("API key is required for auth test");
|
|
2168
|
+
this.emit("auth_test:error", { error: err2 });
|
|
2169
|
+
throw err2;
|
|
2170
|
+
}
|
|
2171
|
+
try {
|
|
2172
|
+
this.emit("auth_test:start");
|
|
2173
|
+
const response = await this.apiCall("/health", { method: "GET" });
|
|
2174
|
+
const result = {
|
|
2175
|
+
authenticated: true,
|
|
2176
|
+
// API key was included in request
|
|
2177
|
+
healthy: response.status === "healthy",
|
|
2178
|
+
api_version: response.api_version
|
|
2179
|
+
};
|
|
2180
|
+
this.emit("auth_test:success", result);
|
|
2181
|
+
return result;
|
|
2182
|
+
} catch (error) {
|
|
2183
|
+
this.emit("auth_test:error", { error });
|
|
2184
|
+
throw error;
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
/**
|
|
2188
|
+
* Send a heartbeat for the current license.
|
|
2189
|
+
* Heartbeats let the server know the device is still active.
|
|
2190
|
+
* @returns {Promise<Object|undefined>} Heartbeat response, or undefined if no active license
|
|
2191
|
+
* @throws {ConfigurationError} When productSlug is not configured
|
|
2192
|
+
* @throws {APIError} When the API request fails
|
|
2193
|
+
*/
|
|
2194
|
+
async heartbeat() {
|
|
2195
|
+
if (!this.config.productSlug) {
|
|
2196
|
+
throw new ConfigurationError("productSlug is required for heartbeat");
|
|
2197
|
+
}
|
|
2198
|
+
const cached = this.cache.getLicense();
|
|
2199
|
+
if (!cached) {
|
|
2200
|
+
this.log("No active license for heartbeat");
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2203
|
+
const body = { device_id: cached.device_id };
|
|
2204
|
+
const response = await this.apiCall(
|
|
2205
|
+
`/products/${this.config.productSlug}/licenses/${encodeURIComponent(cached.license_key)}/heartbeat`,
|
|
2206
|
+
{
|
|
2207
|
+
method: "POST",
|
|
2208
|
+
body
|
|
2209
|
+
}
|
|
2210
|
+
);
|
|
2211
|
+
this.emit("heartbeat:success", response);
|
|
2212
|
+
this.log("Heartbeat sent successfully");
|
|
2213
|
+
return response;
|
|
2214
|
+
}
|
|
2215
|
+
/**
|
|
2216
|
+
* Clear all data and reset SDK state
|
|
2217
|
+
* @returns {void}
|
|
2218
|
+
*/
|
|
2219
|
+
reset() {
|
|
2220
|
+
this.stopAutoValidation();
|
|
2221
|
+
this.stopHeartbeat();
|
|
2222
|
+
this.stopConnectivityPolling();
|
|
2223
|
+
if (this.offlineRefreshTimer) {
|
|
2224
|
+
clearInterval(this.offlineRefreshTimer);
|
|
2225
|
+
this.offlineRefreshTimer = null;
|
|
2226
|
+
}
|
|
2227
|
+
this.cache.clear();
|
|
2228
|
+
this.lastOfflineValidation = null;
|
|
2229
|
+
this.currentAutoLicenseKey = null;
|
|
2230
|
+
this.emit("sdk:reset");
|
|
2231
|
+
}
|
|
2232
|
+
/**
|
|
2233
|
+
* Destroy the SDK instance and release all resources
|
|
2234
|
+
* Call this when you no longer need the SDK to prevent memory leaks.
|
|
2235
|
+
* After calling destroy(), the SDK instance should not be used.
|
|
2236
|
+
* @returns {void}
|
|
2237
|
+
*/
|
|
2238
|
+
destroy() {
|
|
2239
|
+
this.destroyed = true;
|
|
2240
|
+
this.stopAutoValidation();
|
|
2241
|
+
this.stopHeartbeat();
|
|
2242
|
+
this.stopConnectivityPolling();
|
|
2243
|
+
if (this.offlineRefreshTimer) {
|
|
2244
|
+
clearInterval(this.offlineRefreshTimer);
|
|
2245
|
+
this.offlineRefreshTimer = null;
|
|
2246
|
+
}
|
|
2247
|
+
this.eventListeners = {};
|
|
2248
|
+
this.cache.clear();
|
|
2249
|
+
this.lastOfflineValidation = null;
|
|
2250
|
+
this.currentAutoLicenseKey = null;
|
|
2251
|
+
this.emit("sdk:destroyed");
|
|
2252
|
+
}
|
|
2253
|
+
// ============================================================
|
|
2254
|
+
// Event Handling
|
|
2255
|
+
// ============================================================
|
|
2256
|
+
/**
|
|
2257
|
+
* Subscribe to an event
|
|
2258
|
+
* @param {string} event - Event name
|
|
2259
|
+
* @param {import('./types.js').EventCallback} callback - Event handler
|
|
2260
|
+
* @returns {import('./types.js').EventUnsubscribe} Unsubscribe function
|
|
2261
|
+
*/
|
|
2262
|
+
on(event, callback) {
|
|
2263
|
+
if (!this.eventListeners[event]) {
|
|
2264
|
+
this.eventListeners[event] = [];
|
|
2265
|
+
}
|
|
2266
|
+
this.eventListeners[event].push(callback);
|
|
2267
|
+
return () => this.off(event, callback);
|
|
2268
|
+
}
|
|
2269
|
+
/**
|
|
2270
|
+
* Unsubscribe from an event
|
|
2271
|
+
* @param {string} event - Event name
|
|
2272
|
+
* @param {import('./types.js').EventCallback} callback - Event handler to remove
|
|
2273
|
+
* @returns {void}
|
|
2274
|
+
*/
|
|
2275
|
+
off(event, callback) {
|
|
2276
|
+
if (this.eventListeners[event]) {
|
|
2277
|
+
this.eventListeners[event] = this.eventListeners[event].filter(
|
|
2278
|
+
(cb) => cb !== callback
|
|
2279
|
+
);
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
/**
|
|
2283
|
+
* Emit an event
|
|
2284
|
+
* @param {string} event - Event name
|
|
2285
|
+
* @param {*} data - Event data
|
|
2286
|
+
* @returns {void}
|
|
2287
|
+
* @private
|
|
2288
|
+
*/
|
|
2289
|
+
emit(event, data) {
|
|
2290
|
+
this.log(`Event: ${event}`, data);
|
|
2291
|
+
if (this.eventListeners[event]) {
|
|
2292
|
+
this.eventListeners[event].forEach((callback) => {
|
|
2293
|
+
try {
|
|
2294
|
+
callback(data);
|
|
2295
|
+
} catch (error) {
|
|
2296
|
+
console.error(`Error in event listener for ${event}:`, error);
|
|
2297
|
+
}
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
// ============================================================
|
|
2302
|
+
// Auto-Validation & Connectivity
|
|
2303
|
+
// ============================================================
|
|
2304
|
+
/**
|
|
2305
|
+
* Start automatic license validation
|
|
2306
|
+
* @param {string} licenseKey - License key to validate
|
|
2307
|
+
* @returns {void}
|
|
2308
|
+
* @private
|
|
2309
|
+
*/
|
|
2310
|
+
startAutoValidation(licenseKey) {
|
|
2311
|
+
this.stopAutoValidation();
|
|
2312
|
+
this.currentAutoLicenseKey = licenseKey;
|
|
2313
|
+
const validationInterval = this.config.autoValidateInterval;
|
|
2314
|
+
if (!validationInterval || validationInterval <= 0) {
|
|
2315
|
+
this.log("Auto-validation disabled (interval:", validationInterval, ")");
|
|
2316
|
+
return;
|
|
2317
|
+
}
|
|
2318
|
+
const performAndReschedule = () => {
|
|
2319
|
+
this.validateLicense(licenseKey).then(() => {
|
|
2320
|
+
this.heartbeat().catch((err2) => this.log("Heartbeat failed:", err2));
|
|
2321
|
+
}).catch((err2) => {
|
|
2322
|
+
this.log("Auto-validation failed:", err2);
|
|
2323
|
+
this.emit("validation:auto-failed", { licenseKey, error: err2 });
|
|
2324
|
+
});
|
|
2325
|
+
this.emit("autovalidation:cycle", {
|
|
2326
|
+
nextRunAt: new Date(Date.now() + validationInterval)
|
|
2327
|
+
});
|
|
2328
|
+
};
|
|
2329
|
+
this.validationTimer = setInterval(performAndReschedule, validationInterval);
|
|
2330
|
+
this.emit("autovalidation:cycle", {
|
|
2331
|
+
nextRunAt: new Date(Date.now() + validationInterval)
|
|
2332
|
+
});
|
|
2333
|
+
}
|
|
2334
|
+
/**
|
|
2335
|
+
* Stop automatic validation
|
|
2336
|
+
* @returns {void}
|
|
2337
|
+
* @private
|
|
2338
|
+
*/
|
|
2339
|
+
stopAutoValidation() {
|
|
2340
|
+
if (this.validationTimer) {
|
|
2341
|
+
clearInterval(this.validationTimer);
|
|
2342
|
+
this.validationTimer = null;
|
|
2343
|
+
this.emit("autovalidation:stopped");
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
/**
|
|
2347
|
+
* Start separate heartbeat timer
|
|
2348
|
+
* Sends periodic heartbeats between auto-validation cycles.
|
|
2349
|
+
* @returns {void}
|
|
2350
|
+
* @private
|
|
2351
|
+
*/
|
|
2352
|
+
startHeartbeat() {
|
|
2353
|
+
this.stopHeartbeat();
|
|
2354
|
+
const interval = this.config.heartbeatInterval;
|
|
2355
|
+
if (!interval || interval <= 0) {
|
|
2356
|
+
this.log("Heartbeat timer disabled (interval:", interval, ")");
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2359
|
+
if (!this.config.productSlug) {
|
|
2360
|
+
this.log("Heartbeat timer disabled (productSlug not configured)");
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2363
|
+
this.heartbeatTimer = setInterval(() => {
|
|
2364
|
+
this.heartbeat().then(() => this.emit("heartbeat:cycle", { nextRunAt: new Date(Date.now() + interval) })).catch((err2) => this.log("Heartbeat timer failed:", err2));
|
|
2365
|
+
}, interval);
|
|
2366
|
+
this.emit("heartbeat:cycle", { nextRunAt: new Date(Date.now() + interval) });
|
|
2367
|
+
this.log("Heartbeat timer started (interval:", interval, "ms)");
|
|
2368
|
+
}
|
|
2369
|
+
/**
|
|
2370
|
+
* Stop the separate heartbeat timer
|
|
2371
|
+
* @returns {void}
|
|
2372
|
+
* @private
|
|
2373
|
+
*/
|
|
2374
|
+
stopHeartbeat() {
|
|
2375
|
+
if (this.heartbeatTimer) {
|
|
2376
|
+
clearInterval(this.heartbeatTimer);
|
|
2377
|
+
this.heartbeatTimer = null;
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
/**
|
|
2381
|
+
* Start connectivity polling (when offline)
|
|
2382
|
+
* @returns {void}
|
|
2383
|
+
* @private
|
|
2384
|
+
*/
|
|
2385
|
+
startConnectivityPolling() {
|
|
2386
|
+
if (this.connectivityTimer)
|
|
2387
|
+
return;
|
|
2388
|
+
const healthCheck = async () => {
|
|
2389
|
+
try {
|
|
2390
|
+
const res = await fetch(`${this.config.apiBaseUrl}/health`, {
|
|
2391
|
+
method: "GET",
|
|
2392
|
+
credentials: "omit"
|
|
2393
|
+
});
|
|
2394
|
+
await res.text().catch(() => {
|
|
2395
|
+
});
|
|
2396
|
+
if (!this.online) {
|
|
2397
|
+
this.online = true;
|
|
2398
|
+
this.emit("network:online");
|
|
2399
|
+
if (this.currentAutoLicenseKey && !this.validationTimer) {
|
|
2400
|
+
this.startAutoValidation(this.currentAutoLicenseKey);
|
|
2401
|
+
}
|
|
2402
|
+
this.syncOfflineAssets();
|
|
2403
|
+
}
|
|
2404
|
+
this.stopConnectivityPolling();
|
|
2405
|
+
} catch (err2) {
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2408
|
+
this.connectivityTimer = setInterval(
|
|
2409
|
+
healthCheck,
|
|
2410
|
+
this.config.networkRecheckInterval
|
|
2411
|
+
);
|
|
2412
|
+
}
|
|
2413
|
+
/**
|
|
2414
|
+
* Stop connectivity polling
|
|
2415
|
+
* @returns {void}
|
|
2416
|
+
* @private
|
|
2417
|
+
*/
|
|
2418
|
+
stopConnectivityPolling() {
|
|
2419
|
+
if (this.connectivityTimer) {
|
|
2420
|
+
clearInterval(this.connectivityTimer);
|
|
2421
|
+
this.connectivityTimer = null;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
// ============================================================
|
|
2425
|
+
// Offline License Management
|
|
2426
|
+
// ============================================================
|
|
2427
|
+
/**
|
|
2428
|
+
* Download and cache the offline token and its corresponding public signing key.
|
|
2429
|
+
* Emits `offlineToken:ready` on success. Safe to call multiple times — concurrent
|
|
2430
|
+
* calls are deduplicated automatically.
|
|
2431
|
+
* @returns {Promise<void>}
|
|
2432
|
+
*/
|
|
2433
|
+
async syncOfflineAssets() {
|
|
2434
|
+
if (this.syncingOfflineAssets || this.destroyed) {
|
|
2435
|
+
this.log("Skipping syncOfflineAssets: already syncing or destroyed");
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
this.syncingOfflineAssets = true;
|
|
2439
|
+
try {
|
|
2440
|
+
const offline = await this.getOfflineToken();
|
|
2441
|
+
this.cache.setOfflineToken(offline);
|
|
2442
|
+
const kid = offline.signature?.key_id || offline.token?.kid;
|
|
2443
|
+
if (kid) {
|
|
2444
|
+
const existingKey = this.cache.getPublicKey(kid);
|
|
2445
|
+
if (!existingKey) {
|
|
2446
|
+
const signingKey = await this.getSigningKey(kid);
|
|
2447
|
+
this.cache.setPublicKey(kid, signingKey.public_key);
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
this.emit("offlineToken:ready", {
|
|
2451
|
+
kid,
|
|
2452
|
+
exp: offline.token?.exp
|
|
2453
|
+
});
|
|
2454
|
+
const res = await this.quickVerifyCachedOfflineLocal();
|
|
2455
|
+
if (res) {
|
|
2456
|
+
this.cache.updateValidation(res);
|
|
2457
|
+
this.emit(
|
|
2458
|
+
res.valid ? "validation:offline-success" : "validation:offline-failed",
|
|
2459
|
+
res
|
|
2460
|
+
);
|
|
2461
|
+
}
|
|
2462
|
+
} catch (err2) {
|
|
2463
|
+
this.log("Failed to sync offline assets:", err2);
|
|
2464
|
+
} finally {
|
|
2465
|
+
this.syncingOfflineAssets = false;
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
/**
|
|
2469
|
+
* Schedule periodic offline license refresh
|
|
2470
|
+
* @returns {void}
|
|
2471
|
+
* @private
|
|
2472
|
+
*/
|
|
2473
|
+
scheduleOfflineRefresh() {
|
|
2474
|
+
if (this.offlineRefreshTimer)
|
|
2475
|
+
clearInterval(this.offlineRefreshTimer);
|
|
2476
|
+
this.offlineRefreshTimer = setInterval(
|
|
2477
|
+
() => this.syncOfflineAssets(),
|
|
2478
|
+
this.config.offlineLicenseRefreshInterval
|
|
2479
|
+
);
|
|
2480
|
+
}
|
|
2481
|
+
/**
|
|
2482
|
+
* Verify the cached offline token and return a validation result.
|
|
2483
|
+
* Use this to validate the license when the device is offline.
|
|
2484
|
+
* The offline token must have been previously downloaded via {@link syncOfflineAssets}.
|
|
2485
|
+
* @returns {Promise<import('./types.js').ValidationResult>}
|
|
2486
|
+
*/
|
|
2487
|
+
async verifyCachedOffline() {
|
|
2488
|
+
const signed = this.cache.getOfflineToken();
|
|
2489
|
+
if (!signed) {
|
|
2490
|
+
return { valid: false, offline: true, code: "no_offline_token" };
|
|
2491
|
+
}
|
|
2492
|
+
const kid = signed.signature?.key_id || signed.token?.kid;
|
|
2493
|
+
let pub = kid ? this.cache.getPublicKey(kid) : null;
|
|
2494
|
+
if (!pub) {
|
|
2495
|
+
try {
|
|
2496
|
+
const signingKey = await this.getSigningKey(kid);
|
|
2497
|
+
pub = signingKey.public_key;
|
|
2498
|
+
this.cache.setPublicKey(kid, pub);
|
|
2499
|
+
} catch (e) {
|
|
2500
|
+
return { valid: false, offline: true, code: "no_public_key" };
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
try {
|
|
2504
|
+
const ok = await this.verifyOfflineToken(signed, pub);
|
|
2505
|
+
if (!ok) {
|
|
2506
|
+
return { valid: false, offline: true, code: "signature_invalid" };
|
|
2507
|
+
}
|
|
2508
|
+
const token = signed.token;
|
|
2509
|
+
const cached = this.cache.getLicense();
|
|
2510
|
+
if (!cached || !constantTimeEqual(token.license_key || "", cached.license_key || "")) {
|
|
2511
|
+
return { valid: false, offline: true, code: "license_mismatch" };
|
|
2512
|
+
}
|
|
2513
|
+
const now = Date.now();
|
|
2514
|
+
const expAt = token.exp ? token.exp * 1e3 : null;
|
|
2515
|
+
if (expAt && expAt < now) {
|
|
2516
|
+
return { valid: false, offline: true, code: "expired" };
|
|
2517
|
+
}
|
|
2518
|
+
if (!expAt && this.config.maxOfflineDays > 0) {
|
|
2519
|
+
const pivot = cached.last_validated || cached.activated_at;
|
|
2520
|
+
if (pivot) {
|
|
2521
|
+
const ageMs = now - new Date(pivot).getTime();
|
|
2522
|
+
if (ageMs > this.config.maxOfflineDays * 24 * 60 * 60 * 1e3) {
|
|
2523
|
+
return {
|
|
2524
|
+
valid: false,
|
|
2525
|
+
offline: true,
|
|
2526
|
+
code: "grace_period_expired"
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
const lastSeen = this.cache.getLastSeenTimestamp();
|
|
2532
|
+
if (lastSeen && now + this.config.maxClockSkewMs < lastSeen) {
|
|
2533
|
+
return { valid: false, offline: true, code: "clock_tamper" };
|
|
2534
|
+
}
|
|
2535
|
+
this.cache.setLastSeenTimestamp(now);
|
|
2536
|
+
const active = parseActiveEntitlements(token);
|
|
2537
|
+
return {
|
|
2538
|
+
valid: true,
|
|
2539
|
+
offline: true,
|
|
2540
|
+
...active.length ? { active_entitlements: active } : {}
|
|
2541
|
+
};
|
|
2542
|
+
} catch (e) {
|
|
2543
|
+
return { valid: false, offline: true, code: "verification_error" };
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
/**
|
|
2547
|
+
* Quick offline verification using only local data (no network)
|
|
2548
|
+
* Performs signature verification plus basic validity checks (expiry, license key match)
|
|
2549
|
+
* @returns {Promise<import('./types.js').ValidationResult|null>}
|
|
2550
|
+
* @private
|
|
2551
|
+
*/
|
|
2552
|
+
async quickVerifyCachedOfflineLocal() {
|
|
2553
|
+
const signed = this.cache.getOfflineToken();
|
|
2554
|
+
if (!signed)
|
|
2555
|
+
return null;
|
|
2556
|
+
const kid = signed.signature?.key_id || signed.token?.kid;
|
|
2557
|
+
const pub = kid ? this.cache.getPublicKey(kid) : null;
|
|
2558
|
+
if (!pub)
|
|
2559
|
+
return null;
|
|
2560
|
+
try {
|
|
2561
|
+
const ok = await this.verifyOfflineToken(signed, pub);
|
|
2562
|
+
if (!ok) {
|
|
2563
|
+
return { valid: false, offline: true, code: "signature_invalid" };
|
|
2564
|
+
}
|
|
2565
|
+
const token = signed.token;
|
|
2566
|
+
const cached = this.cache.getLicense();
|
|
2567
|
+
if (!cached || !constantTimeEqual(token.license_key || "", cached.license_key || "")) {
|
|
2568
|
+
return { valid: false, offline: true, code: "license_mismatch" };
|
|
2569
|
+
}
|
|
2570
|
+
const now = Date.now();
|
|
2571
|
+
const expAt = token.exp ? token.exp * 1e3 : null;
|
|
2572
|
+
if (expAt && expAt < now) {
|
|
2573
|
+
return { valid: false, offline: true, code: "expired" };
|
|
2574
|
+
}
|
|
2575
|
+
const lastSeen = this.cache.getLastSeenTimestamp();
|
|
2576
|
+
if (lastSeen && now + this.config.maxClockSkewMs < lastSeen) {
|
|
2577
|
+
return { valid: false, offline: true, code: "clock_tamper" };
|
|
2578
|
+
}
|
|
2579
|
+
const active = parseActiveEntitlements(token);
|
|
2580
|
+
return {
|
|
2581
|
+
valid: true,
|
|
2582
|
+
offline: true,
|
|
2583
|
+
...active.length ? { active_entitlements: active } : {}
|
|
2584
|
+
};
|
|
2585
|
+
} catch (_) {
|
|
2586
|
+
return { valid: false, offline: true, code: "verification_error" };
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
// ============================================================
|
|
2590
|
+
// API Communication
|
|
2591
|
+
// ============================================================
|
|
2592
|
+
/**
|
|
2593
|
+
* Make an API call with retry logic
|
|
2594
|
+
* @param {string} endpoint - API endpoint (will be appended to apiBaseUrl)
|
|
2595
|
+
* @param {Object} [options={}] - Fetch options
|
|
2596
|
+
* @param {string} [options.method="GET"] - HTTP method
|
|
2597
|
+
* @param {Object} [options.body] - Request body (will be JSON-stringified)
|
|
2598
|
+
* @param {Object} [options.headers] - Additional headers
|
|
2599
|
+
* @returns {Promise<Object>} API response data
|
|
2600
|
+
* @throws {APIError} When the request fails after all retries
|
|
2601
|
+
* @private
|
|
2602
|
+
*/
|
|
2603
|
+
async apiCall(endpoint, options = {}) {
|
|
2604
|
+
const url = `${this.config.apiBaseUrl}${endpoint}`;
|
|
2605
|
+
let lastError;
|
|
2606
|
+
const headers = {
|
|
2607
|
+
"Content-Type": "application/json",
|
|
2608
|
+
Accept: "application/json",
|
|
2609
|
+
...options.headers
|
|
2610
|
+
};
|
|
2611
|
+
if (this.config.apiKey) {
|
|
2612
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
2613
|
+
} else {
|
|
2614
|
+
this.log(
|
|
2615
|
+
"[Warning] No API key configured for LicenseSeat SDK. Authenticated endpoints will fail."
|
|
2616
|
+
);
|
|
2617
|
+
}
|
|
2618
|
+
const method = options.method || "GET";
|
|
2619
|
+
let body = options.body;
|
|
2620
|
+
if (method === "POST" && body && this.config.telemetryEnabled !== false) {
|
|
2621
|
+
body = { ...body, telemetry: collectTelemetry(SDK_VERSION, {
|
|
2622
|
+
appVersion: this.config.appVersion,
|
|
2623
|
+
appBuild: this.config.appBuild
|
|
2624
|
+
}) };
|
|
2625
|
+
}
|
|
2626
|
+
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
|
|
2627
|
+
try {
|
|
2628
|
+
const response = await fetch(url, {
|
|
2629
|
+
method,
|
|
2630
|
+
headers,
|
|
2631
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
2632
|
+
credentials: "omit"
|
|
2633
|
+
});
|
|
2634
|
+
const data = await response.json();
|
|
2635
|
+
if (!response.ok) {
|
|
2636
|
+
const errorObj = data.error;
|
|
2637
|
+
let errorMessage = "Request failed";
|
|
2638
|
+
if (typeof errorObj === "object" && errorObj !== null) {
|
|
2639
|
+
errorMessage = errorObj.message || "Request failed";
|
|
2640
|
+
} else if (typeof errorObj === "string") {
|
|
2641
|
+
errorMessage = errorObj;
|
|
2642
|
+
}
|
|
2643
|
+
throw new APIError(errorMessage, response.status, data);
|
|
2644
|
+
}
|
|
2645
|
+
if (!this.online) {
|
|
2646
|
+
this.online = true;
|
|
2647
|
+
this.emit("network:online");
|
|
2648
|
+
}
|
|
2649
|
+
this.stopConnectivityPolling();
|
|
2650
|
+
if (!this.validationTimer && this.currentAutoLicenseKey) {
|
|
2651
|
+
this.startAutoValidation(this.currentAutoLicenseKey);
|
|
2652
|
+
}
|
|
2653
|
+
return data;
|
|
2654
|
+
} catch (error) {
|
|
2655
|
+
const networkFailure = error instanceof TypeError && error.message.includes("fetch") || error instanceof APIError && error.status === 0;
|
|
2656
|
+
if (networkFailure && this.online) {
|
|
2657
|
+
this.online = false;
|
|
2658
|
+
this.emit("network:offline", { error });
|
|
2659
|
+
this.stopAutoValidation();
|
|
2660
|
+
this.startConnectivityPolling();
|
|
2661
|
+
}
|
|
2662
|
+
lastError = error;
|
|
2663
|
+
const shouldRetry = attempt < this.config.maxRetries && this.shouldRetryError(error);
|
|
2664
|
+
if (shouldRetry) {
|
|
2665
|
+
const delay = this.config.retryDelay * Math.pow(2, attempt);
|
|
2666
|
+
this.log(
|
|
2667
|
+
`Retry attempt ${attempt + 1} after ${delay}ms for error:`,
|
|
2668
|
+
error.message
|
|
2669
|
+
);
|
|
2670
|
+
await sleep(delay);
|
|
2671
|
+
} else {
|
|
2672
|
+
throw error;
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
throw lastError;
|
|
2677
|
+
}
|
|
2678
|
+
/**
|
|
2679
|
+
* Determine if an error should be retried
|
|
2680
|
+
* @param {Error} error - The error to check
|
|
2681
|
+
* @returns {boolean} True if the error should trigger a retry
|
|
2682
|
+
* @private
|
|
2683
|
+
*/
|
|
2684
|
+
shouldRetryError(error) {
|
|
2685
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
2686
|
+
return true;
|
|
2687
|
+
}
|
|
2688
|
+
if (error instanceof APIError) {
|
|
2689
|
+
const status = error.status;
|
|
2690
|
+
if (status >= 502 && status < 600) {
|
|
2691
|
+
return true;
|
|
2692
|
+
}
|
|
2693
|
+
if (status === 0 || status === 408 || status === 429) {
|
|
2694
|
+
return true;
|
|
2695
|
+
}
|
|
2696
|
+
return false;
|
|
2697
|
+
}
|
|
2698
|
+
return false;
|
|
2699
|
+
}
|
|
2700
|
+
// ============================================================
|
|
2701
|
+
// Utilities
|
|
2702
|
+
// ============================================================
|
|
2703
|
+
/**
|
|
2704
|
+
* Get CSRF token from meta tag
|
|
2705
|
+
* @returns {string} CSRF token or empty string
|
|
2706
|
+
*/
|
|
2707
|
+
getCsrfToken() {
|
|
2708
|
+
return getCsrfToken();
|
|
2709
|
+
}
|
|
2710
|
+
/**
|
|
2711
|
+
* Log a message (if debug mode is enabled)
|
|
2712
|
+
* @param {...*} args - Arguments to log
|
|
2713
|
+
* @returns {void}
|
|
2714
|
+
* @private
|
|
2715
|
+
*/
|
|
2716
|
+
log(...args) {
|
|
2717
|
+
if (this.config.debug) {
|
|
2718
|
+
console.log("[LicenseSeat SDK]", ...args);
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
};
|
|
2722
|
+
var sharedInstance = null;
|
|
2723
|
+
function getSharedInstance(config) {
|
|
2724
|
+
if (!sharedInstance) {
|
|
2725
|
+
sharedInstance = new LicenseSeatSDK(config);
|
|
2726
|
+
}
|
|
2727
|
+
return sharedInstance;
|
|
2728
|
+
}
|
|
2729
|
+
function configure(config, force = false) {
|
|
2730
|
+
if (sharedInstance && !force) {
|
|
2731
|
+
console.warn(
|
|
2732
|
+
"[LicenseSeat SDK] Already configured. Call configure with force=true to reconfigure."
|
|
2733
|
+
);
|
|
2734
|
+
return sharedInstance;
|
|
2735
|
+
}
|
|
2736
|
+
sharedInstance = new LicenseSeatSDK(config);
|
|
2737
|
+
return sharedInstance;
|
|
2738
|
+
}
|
|
2739
|
+
function resetSharedInstance() {
|
|
2740
|
+
if (sharedInstance) {
|
|
2741
|
+
sharedInstance.reset();
|
|
2742
|
+
sharedInstance = null;
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
// src/index.js
|
|
2747
|
+
var src_default = LicenseSeatSDK;
|
|
2748
|
+
export {
|
|
2749
|
+
APIError,
|
|
2750
|
+
ConfigurationError,
|
|
2751
|
+
CryptoError,
|
|
2752
|
+
LicenseCache,
|
|
2753
|
+
LicenseError,
|
|
2754
|
+
LicenseSeatSDK,
|
|
2755
|
+
SDK_VERSION,
|
|
2756
|
+
base64UrlDecode,
|
|
2757
|
+
canonicalJsonStringify,
|
|
2758
|
+
collectTelemetry,
|
|
2759
|
+
configure,
|
|
2760
|
+
constantTimeEqual,
|
|
2761
|
+
src_default as default,
|
|
2762
|
+
generateDeviceId,
|
|
2763
|
+
getCsrfToken,
|
|
2764
|
+
getSharedInstance,
|
|
2765
|
+
parseActiveEntitlements,
|
|
2766
|
+
resetSharedInstance
|
|
2767
|
+
};
|
|
2768
|
+
/*! Bundled license information:
|
|
2769
|
+
|
|
2770
|
+
@noble/ed25519/index.js:
|
|
2771
|
+
(*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) *)
|
|
2772
|
+
|
|
2773
|
+
@noble/hashes/esm/utils.js:
|
|
2774
|
+
(*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
2775
|
+
*/
|