@licenseseat/js 0.1.0 → 0.2.0

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