@moonbase.sh/licensing 2.0.1 → 3.0.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.cjs CHANGED
@@ -31,20 +31,44 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ActivationMethod: () => ActivationMethod,
34
- DefaultDeviceIdResolver: () => DefaultDeviceIdResolver,
35
34
  ErrorType: () => ErrorType,
35
+ FINGERPRINT_PREFIX: () => FINGERPRINT_PREFIX,
36
+ FINGERPRINT_VERSION: () => FINGERPRINT_VERSION,
36
37
  FileLicenseStore: () => FileLicenseStore,
38
+ IDENTIFYING_PARAM_NAMES: () => IDENTIFYING_PARAM_NAMES,
37
39
  InMemoryLicenseStore: () => InMemoryLicenseStore,
40
+ InsufficientDeviceIdentityError: () => InsufficientDeviceIdentityError,
41
+ LegacyDeviceIdResolver: () => LegacyDeviceIdResolver,
38
42
  LicenseClient: () => LicenseClient,
39
43
  LicenseValidator: () => LicenseValidator,
44
+ MAX_VALUE_LENGTH: () => MAX_VALUE_LENGTH,
45
+ MigratingDeviceIdResolver: () => MigratingDeviceIdResolver,
46
+ MoonbaseDeviceIdResolver: () => MoonbaseDeviceIdResolver,
40
47
  MoonbaseError: () => MoonbaseError,
41
- MoonbaseLicensing: () => MoonbaseLicensing
48
+ MoonbaseLicensing: () => MoonbaseLicensing,
49
+ buildFingerprintMaterial: () => buildFingerprintMaterial,
50
+ canonicalizeParams: () => canonicalizeParams,
51
+ canonicalizeValue: () => canonicalizeValue,
52
+ defaultDeviceIdentityReader: () => defaultDeviceIdentityReader,
53
+ fingerprintDeviceId: () => fingerprintDeviceId,
54
+ fingerprintDigest: () => fingerprintDigest,
55
+ parseDeviceIdStamp: () => parseDeviceIdStamp,
56
+ parseIoregPlatformUuid: () => parseIoregPlatformUuid,
57
+ parseSmbiosParams: () => parseSmbiosParams,
58
+ platformTag: () => platformTag,
59
+ selectMachineId: () => selectMachineId,
60
+ stampDeviceId: () => stampDeviceId
42
61
  });
43
62
  module.exports = __toCommonJS(index_exports);
63
+ var import_node_buffer2 = require("buffer");
64
+ var import_node_process3 = __toESM(require("process"), 1);
44
65
 
45
66
  // src/client.ts
46
67
  var import_cross_fetch = __toESM(require("cross-fetch"), 1);
47
68
 
69
+ // src/schemas.ts
70
+ var import_zod = require("zod");
71
+
48
72
  // src/types.ts
49
73
  var ActivationMethod = /* @__PURE__ */ ((ActivationMethod2) => {
50
74
  ActivationMethod2["Online"] = "Online";
@@ -53,7 +77,6 @@ var ActivationMethod = /* @__PURE__ */ ((ActivationMethod2) => {
53
77
  })(ActivationMethod || {});
54
78
 
55
79
  // src/schemas.ts
56
- var import_zod = require("zod");
57
80
  var activationRequestResponseSchema = import_zod.z.object({
58
81
  id: import_zod.z.string(),
59
82
  request: import_zod.z.string(),
@@ -107,7 +130,7 @@ var LicenseClient = class {
107
130
  deviceName: await this.deviceIdResolver.resolveDeviceName(),
108
131
  deviceSignature: await this.deviceIdResolver.resolveDeviceId()
109
132
  };
110
- const response = await (0, import_cross_fetch.default)(this.configuration.endpoint + `/api/client/activations/${this.configuration.productId}/request${this.buildQueryString()}`, {
133
+ const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/activations/${this.configuration.productId}/request${this.buildQueryString()}`, {
111
134
  ...defaultFetchOptions,
112
135
  method: "POST",
113
136
  body: JSON.stringify(content)
@@ -129,7 +152,8 @@ var LicenseClient = class {
129
152
  }
130
153
  async getRequestedActivation(request) {
131
154
  const response = await (0, import_cross_fetch.default)(request.request, defaultFetchOptions);
132
- if (response.status === 204 || response.status === 404) return null;
155
+ if (response.status === 204 || response.status === 404)
156
+ return null;
133
157
  return await this.handleLicenseResponse(response);
134
158
  }
135
159
  async requestTrial() {
@@ -137,7 +161,7 @@ var LicenseClient = class {
137
161
  deviceName: await this.deviceIdResolver.resolveDeviceName(),
138
162
  deviceSignature: await this.deviceIdResolver.resolveDeviceId()
139
163
  };
140
- const response = await (0, import_cross_fetch.default)(this.configuration.endpoint + `/api/client/trials/${this.configuration.productId}/request${this.buildQueryString()}`, {
164
+ const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/trials/${this.configuration.productId}/request${this.buildQueryString()}`, {
141
165
  ...defaultFetchOptions,
142
166
  method: "POST",
143
167
  headers: {
@@ -149,7 +173,7 @@ var LicenseClient = class {
149
173
  return await this.handleLicenseResponse(response);
150
174
  }
151
175
  async validateLicense(license) {
152
- const response = await (0, import_cross_fetch.default)(this.configuration.endpoint + `/api/client/licenses/${this.configuration.productId}/validate${this.buildQueryString()}`, {
176
+ const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/licenses/${this.configuration.productId}/validate${this.buildQueryString()}`, {
153
177
  ...defaultFetchOptions,
154
178
  method: "POST",
155
179
  headers: {
@@ -162,11 +186,12 @@ var LicenseClient = class {
162
186
  }
163
187
  async validateRawLicense(rawLicense) {
164
188
  const license = await this.licenseValidator.validateLicense(rawLicense.toString("utf8"));
165
- if (license.activationMethod === "Offline" /* Offline */) return license;
189
+ if (license.activationMethod === "Offline" /* Offline */)
190
+ return license;
166
191
  return await this.validateLicense(license);
167
192
  }
168
193
  async revokeLicense(license) {
169
- const response = await (0, import_cross_fetch.default)(this.configuration.endpoint + `/api/client/licenses/${this.configuration.productId}/revoke?format=JWT`, {
194
+ const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/licenses/${this.configuration.productId}/revoke?format=JWT`, {
170
195
  ...defaultFetchOptions,
171
196
  method: "POST",
172
197
  headers: {
@@ -188,11 +213,14 @@ var LicenseClient = class {
188
213
  buildQueryString() {
189
214
  const parts = ["format=JWT"];
190
215
  const { platform, appVersion, metadata } = this.configuration;
191
- if (platform) parts.push(`platform=${encodeURIComponent(platform)}`);
192
- if (appVersion) parts.push(`appVersion=${encodeURIComponent(appVersion)}`);
216
+ if (platform)
217
+ parts.push(`platform=${encodeURIComponent(platform)}`);
218
+ if (appVersion)
219
+ parts.push(`appVersion=${encodeURIComponent(appVersion)}`);
193
220
  if (metadata) {
194
221
  for (const [key, value] of Object.entries(metadata)) {
195
- if (value === null || value === void 0 || value === "") continue;
222
+ if (value === null || value === void 0 || value === "")
223
+ continue;
196
224
  parts.push(`meta[${encodeURIComponent(key)}]=${encodeURIComponent(value)}`);
197
225
  }
198
226
  }
@@ -201,32 +229,426 @@ var LicenseClient = class {
201
229
  };
202
230
 
203
231
  // src/deviceIdResolver.ts
232
+ var import_node_child_process2 = __toESM(require("child_process"), 1);
233
+ var import_node_crypto2 = require("crypto");
234
+ var import_node_os2 = __toESM(require("os"), 1);
235
+ var import_node_process2 = __toESM(require("process"), 1);
204
236
  var import_systeminformation = __toESM(require("systeminformation"), 1);
205
- var import_node_os = __toESM(require("os"), 1);
237
+
238
+ // src/errors.ts
239
+ var ErrorType = /* @__PURE__ */ ((ErrorType2) => {
240
+ ErrorType2["None"] = "None";
241
+ ErrorType2["ApiError"] = "ApiError";
242
+ ErrorType2["NoEligibleLicense"] = "NoEligibleLicense";
243
+ ErrorType2["LicenseInvalid"] = "LicenseInvalid";
244
+ ErrorType2["LicenseRevoked"] = "LicenseRevoked";
245
+ ErrorType2["LicenseActivationRevoked"] = "LicenseActivationRevoked";
246
+ ErrorType2["LicenseExpired"] = "LicenseExpired";
247
+ ErrorType2["LicenseDeviceMismatch"] = "LicenseDeviceMismatch";
248
+ ErrorType2["DeviceIdentityUnavailable"] = "DeviceIdentityUnavailable";
249
+ return ErrorType2;
250
+ })(ErrorType || {});
251
+ var MoonbaseError = class extends Error {
252
+ constructor(title, detail, type, inner) {
253
+ super();
254
+ this.title = title;
255
+ this.detail = detail;
256
+ this.type = type;
257
+ this.inner = inner;
258
+ this.name = "MoonbaseError";
259
+ this.message = detail != null ? detail : title;
260
+ }
261
+ };
262
+ var InsufficientDeviceIdentityError = class extends MoonbaseError {
263
+ constructor(platform, reason = "no identity parameter could be read") {
264
+ super(
265
+ "No device identity",
266
+ `Could not identify this device (platform: ${platform}): ${reason}`,
267
+ "DeviceIdentityUnavailable" /* DeviceIdentityUnavailable */
268
+ );
269
+ this.platform = platform;
270
+ this.name = "InsufficientDeviceIdentityError";
271
+ }
272
+ };
273
+
274
+ // src/fingerprint.ts
275
+ var import_node_buffer = require("buffer");
206
276
  var import_node_child_process = __toESM(require("child_process"), 1);
207
277
  var import_node_crypto = require("crypto");
208
- var DefaultDeviceIdResolver = class {
278
+ var import_node_fs = __toESM(require("fs"), 1);
279
+ var import_node_os = __toESM(require("os"), 1);
280
+ var import_node_process = __toESM(require("process"), 1);
281
+ var FINGERPRINT_PREFIX = "moonbase:fingerprint:v2";
282
+ var FINGERPRINT_VERSION = 2;
283
+ var MAX_VALUE_LENGTH = 128;
284
+ var PRINTABLE_ASCII_MIN = 32;
285
+ var PRINTABLE_ASCII_MAX = 126;
286
+ var STAMP_PATTERN = /^mbd(\d+)(n?)_([0-9a-f]{64})$/;
287
+ function canonicalizeValue(value) {
288
+ var _a;
289
+ let printable = "";
290
+ for (const char of value.normalize("NFC")) {
291
+ const code = (_a = char.codePointAt(0)) != null ? _a : 0;
292
+ if (code >= PRINTABLE_ASCII_MIN && code <= PRINTABLE_ASCII_MAX)
293
+ printable += char;
294
+ }
295
+ return printable.slice(0, MAX_VALUE_LENGTH).replace(/^ +| +$/g, "");
296
+ }
297
+ function platformTag(platform = import_node_process.default.platform) {
298
+ switch (platform) {
299
+ case "darwin":
300
+ return "mac";
301
+ case "win32":
302
+ return "windows";
303
+ case "android":
304
+ return "android";
305
+ case "linux":
306
+ return "linux";
307
+ case "freebsd":
308
+ case "openbsd":
309
+ case "netbsd":
310
+ return "bsd";
311
+ default:
312
+ return "unknown";
313
+ }
314
+ }
315
+ var IDENTIFYING_PARAMS = /* @__PURE__ */ new Set([
316
+ "ioPlatformUuid",
317
+ "machineId",
318
+ "systemUuid",
319
+ "baseboardSerialNumber",
320
+ "deviceName"
321
+ ]);
322
+ var IDENTIFYING_PARAM_NAMES = Object.freeze([...IDENTIFYING_PARAMS]);
323
+ var NOT_PROGRAMMED_VALUES = /* @__PURE__ */ new Set([
324
+ "to be filled by o.e.m.",
325
+ // The same filler without the dots, which plenty of firmware writes instead.
326
+ "to be filled by oem",
327
+ "default string",
328
+ "system serial number",
329
+ "base board serial number",
330
+ "chassis serial number",
331
+ "not specified",
332
+ "not applicable",
333
+ "not available",
334
+ "none",
335
+ "unknown",
336
+ "invalid",
337
+ "n/a",
338
+ "0123456789",
339
+ // `machine-id(5)`: the literal marker systemd writes to say "no id yet",
340
+ // e.g. in an initrd or a golden image awaiting first boot. Every machine
341
+ // deployed from such an image reads it, so it is the opposite of an identifier.
342
+ "uninitialized"
343
+ ]);
344
+ function isNotProgrammed(value) {
345
+ return NOT_PROGRAMMED_VALUES.has(value.toLowerCase()) || /^0+$/.test(value) || /^f+$/i.test(value);
346
+ }
347
+ function canonicalizeParams(params) {
348
+ const kept = [];
349
+ const seen = /* @__PURE__ */ new Set();
350
+ for (const [name, rawValue] of params) {
351
+ const value = canonicalizeValue(rawValue);
352
+ if (value.length === 0)
353
+ continue;
354
+ if (IDENTIFYING_PARAMS.has(name) && isNotProgrammed(value))
355
+ continue;
356
+ if (seen.has(name))
357
+ throw new Error(`Duplicate fingerprint parameter name: ${name}`);
358
+ seen.add(name);
359
+ kept.push([name, value]);
360
+ }
361
+ return kept;
362
+ }
363
+ function buildFingerprintMaterial(platform, params) {
364
+ const kept = canonicalizeParams(params);
365
+ if (kept.length === 0)
366
+ throw new InsufficientDeviceIdentityError(platform, "no identity parameter could be read");
367
+ if (!kept.some(([name]) => IDENTIFYING_PARAMS.has(name))) {
368
+ throw new InsufficientDeviceIdentityError(
369
+ platform,
370
+ `only model-level parameters could be read (${kept.map(([name]) => name).join(", ")}), none of which identify this individual machine`
371
+ );
372
+ }
373
+ const lines = [FINGERPRINT_PREFIX, `platform=${platform}`];
374
+ for (const [name, value] of kept)
375
+ lines.push(`${name}=${value}`);
376
+ return lines.join("\n");
377
+ }
378
+ function fingerprintDigest(material) {
379
+ return (0, import_node_crypto.createHash)("sha256").update(material, "utf8").digest("hex");
380
+ }
381
+ function stampDeviceId(digest, source = "identity") {
382
+ return `mbd${FINGERPRINT_VERSION}${source === "deviceName" ? "n" : ""}_${digest}`;
383
+ }
384
+ function fingerprintDeviceId(material, source = "identity") {
385
+ return stampDeviceId(fingerprintDigest(material), source);
386
+ }
387
+ function parseDeviceIdStamp(deviceId) {
388
+ const match = STAMP_PATTERN.exec(deviceId);
389
+ if (!match)
390
+ return null;
391
+ return {
392
+ version: Number(match[1]),
393
+ source: match[2] === "n" ? "deviceName" : "identity",
394
+ digest: match[3]
395
+ };
396
+ }
397
+ function commandOutput(command) {
398
+ try {
399
+ return import_node_child_process.default.execSync(command, { encoding: "utf8" });
400
+ } catch (e) {
401
+ return "";
402
+ }
403
+ }
404
+ function readSysFile(path2) {
405
+ try {
406
+ return import_node_fs.default.readFileSync(path2, "utf8");
407
+ } catch (e) {
408
+ return "";
409
+ }
410
+ }
411
+ function parseIoregPlatformUuid(ioregOutput) {
412
+ const match = ioregOutput.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
413
+ return match ? match[1].replaceAll("-", "").toUpperCase() : "";
414
+ }
415
+ function readMacIdentity() {
416
+ const uuid = parseIoregPlatformUuid(commandOutput("ioreg -rd1 -c IOPlatformExpertDevice"));
417
+ const params = [["ioPlatformUuid", uuid]];
418
+ return { params, deviceName: import_node_os.default.hostname().replace(/\.local$/i, "") };
419
+ }
420
+ function selectMachineId(...sources) {
421
+ var _a;
422
+ return (_a = sources.map(canonicalizeValue).find((value) => /^[0-9a-f]{32}$/.test(value) && !isNotProgrammed(value))) != null ? _a : "";
423
+ }
424
+ function readMachineId() {
425
+ return selectMachineId(
426
+ readSysFile("/etc/machine-id"),
427
+ readSysFile("/var/lib/dbus/machine-id")
428
+ );
429
+ }
430
+ function readLinuxIdentity() {
431
+ const params = [
432
+ ["machineId", readMachineId()],
433
+ ["sysVendor", readSysFile("/sys/class/dmi/id/sys_vendor")],
434
+ ["productName", readSysFile("/sys/class/dmi/id/product_name")],
435
+ ["boardVendor", readSysFile("/sys/class/dmi/id/board_vendor")],
436
+ ["boardName", readSysFile("/sys/class/dmi/id/board_name")]
437
+ ];
438
+ return { params, deviceName: import_node_os.default.hostname() };
439
+ }
440
+ function parseSmbiosStructures(data) {
441
+ const structures = [];
442
+ let offset = 0;
443
+ while (offset + 4 <= data.length) {
444
+ const type = data[offset];
445
+ const length = data[offset + 1];
446
+ if (length < 4 || offset + length > data.length)
447
+ break;
448
+ const formatted = data.subarray(offset, offset + length);
449
+ const strings = [];
450
+ let p = offset + length;
451
+ if (p + 1 < data.length && data[p] === 0 && data[p + 1] === 0) {
452
+ p += 2;
453
+ } else {
454
+ while (p < data.length) {
455
+ let end = p;
456
+ while (end < data.length && data[end] !== 0)
457
+ end++;
458
+ strings.push(data.toString("latin1", p, end));
459
+ p = end + 1;
460
+ if (p < data.length && data[p] === 0) {
461
+ p += 1;
462
+ break;
463
+ }
464
+ }
465
+ }
466
+ structures.push({ type, formatted, strings });
467
+ if (type === 127)
468
+ break;
469
+ offset = p;
470
+ }
471
+ return structures;
472
+ }
473
+ function resolveSmbiosString(structure, fieldOffset) {
474
+ if (fieldOffset >= structure.formatted.length)
475
+ return "";
476
+ const index = structure.formatted[fieldOffset];
477
+ if (index === 0 || index > structure.strings.length)
478
+ return "";
479
+ return structure.strings[index - 1];
480
+ }
481
+ function formatSmbiosUuid(structure, fieldOffset) {
482
+ if (fieldOffset + 16 > structure.formatted.length)
483
+ return "";
484
+ const bytes = structure.formatted.subarray(fieldOffset, fieldOffset + 16);
485
+ if (bytes.every((byte) => byte === 0) || bytes.every((byte) => byte === 255))
486
+ return "";
487
+ let hex = "";
488
+ for (const byte of bytes)
489
+ hex += byte.toString(16).padStart(2, "0").toUpperCase();
490
+ return hex;
491
+ }
492
+ function parseSmbiosParams(smbiosData) {
493
+ const structures = parseSmbiosStructures(smbiosData);
494
+ const params = [];
495
+ const system = structures.find((structure) => structure.type === 1);
496
+ if (system) {
497
+ params.push(["systemManufacturer", resolveSmbiosString(system, 4)]);
498
+ params.push(["systemProductName", resolveSmbiosString(system, 5)]);
499
+ params.push(["systemUuid", formatSmbiosUuid(system, 8)]);
500
+ }
501
+ const baseboard = structures.find((structure) => structure.type === 2);
502
+ if (baseboard) {
503
+ params.push(["baseboardManufacturer", resolveSmbiosString(baseboard, 4)]);
504
+ params.push(["baseboardProduct", resolveSmbiosString(baseboard, 5)]);
505
+ params.push(["baseboardSerialNumber", resolveSmbiosString(baseboard, 7)]);
506
+ }
507
+ return params;
508
+ }
509
+ function readWindowsSmbios() {
510
+ const script = "[Convert]::ToBase64String((Get-CimInstance -Namespace root/wmi -ClassName MSSmBios_RawSMBiosTables).SMBiosData)";
511
+ const output = commandOutput(`powershell -NoProfile -NonInteractive -Command "${script}"`);
512
+ try {
513
+ return import_node_buffer.Buffer.from(output.trim(), "base64");
514
+ } catch (e) {
515
+ return import_node_buffer.Buffer.alloc(0);
516
+ }
517
+ }
518
+ function readWindowsIdentity() {
519
+ return { params: parseSmbiosParams(readWindowsSmbios()), deviceName: import_node_os.default.hostname() };
520
+ }
521
+ function defaultDeviceIdentityReader(platform = import_node_process.default.platform) {
522
+ return {
523
+ read() {
524
+ switch (platform) {
525
+ case "darwin":
526
+ return readMacIdentity();
527
+ case "linux":
528
+ return readLinuxIdentity();
529
+ case "win32":
530
+ return readWindowsIdentity();
531
+ // android, bsd and anything else define no identity params: the spec
532
+ // has no stable hardware source for them, so they resolve to an
533
+ // insufficient-identity error unless the deviceName fallback is enabled.
534
+ default:
535
+ return { params: [], deviceName: import_node_os.default.hostname() };
536
+ }
537
+ }
538
+ };
539
+ }
540
+
541
+ // src/deviceIdResolver.ts
542
+ var MoonbaseDeviceIdResolver = class {
543
+ constructor(options) {
544
+ var _a, _b, _c;
545
+ const platform = (_a = options == null ? void 0 : options.platform) != null ? _a : import_node_process2.default.platform;
546
+ this.platform = platformTag(platform);
547
+ this.reader = (_b = options == null ? void 0 : options.reader) != null ? _b : defaultDeviceIdentityReader(platform);
548
+ this.fallback = (_c = options == null ? void 0 : options.fallback) != null ? _c : "none";
549
+ }
550
+ async resolveDeviceName() {
551
+ return this.readIdentity().deviceName;
552
+ }
553
+ async resolveDeviceId() {
554
+ return this.compute().deviceId;
555
+ }
556
+ /**
557
+ * A fresh copy each call. The device binding is what activation sends and what
558
+ * validation compares, so handing out the resolver's own object would let a
559
+ * consumer that edits a diagnostic — or logs it through something that
560
+ * normalizes in place — silently change the id every later call returns.
561
+ */
562
+ async describeDevice() {
563
+ const described = this.compute();
564
+ return { ...described, paramNames: [...described.paramNames] };
565
+ }
566
+ compute() {
567
+ var _a;
568
+ (_a = this.described) != null ? _a : this.described = this.computeDescription();
569
+ return this.described;
570
+ }
571
+ /**
572
+ * Read identity at most once. Both halves of an activation request ask for it —
573
+ * the name and then the id — and a read can mean spawning `ioreg` or
574
+ * PowerShell, so reading per call would double the cost of every request and
575
+ * let the name and the id come from two different reads of the machine.
576
+ */
577
+ readIdentity() {
578
+ var _a;
579
+ (_a = this.identity) != null ? _a : this.identity = this.reader.read();
580
+ return this.identity;
581
+ }
582
+ computeDescription() {
583
+ const { params, deviceName } = this.readIdentity();
584
+ try {
585
+ return this.describe(params, "identity");
586
+ } catch (err) {
587
+ if (this.fallback !== "deviceName" || !(err instanceof InsufficientDeviceIdentityError))
588
+ throw err;
589
+ return this.describe([["deviceName", deviceName]], "deviceName");
590
+ }
591
+ }
592
+ describe(params, source) {
593
+ return {
594
+ deviceId: fingerprintDeviceId(buildFingerprintMaterial(this.platform, params), source),
595
+ version: FINGERPRINT_VERSION,
596
+ platform: this.platform,
597
+ source,
598
+ paramNames: canonicalizeParams(params).map(([name]) => name)
599
+ };
600
+ }
601
+ };
602
+ var MigratingDeviceIdResolver = class {
603
+ constructor(current, ...previous) {
604
+ this.current = current;
605
+ this.previous = previous;
606
+ const describe = current.describeDevice;
607
+ if (typeof describe === "function")
608
+ this.describeDevice = () => describe.call(current);
609
+ }
610
+ async resolveDeviceName() {
611
+ return this.current.resolveDeviceName();
612
+ }
613
+ async resolveDeviceId() {
614
+ return this.current.resolveDeviceId();
615
+ }
616
+ async acceptsDeviceId(deviceId) {
617
+ if (!this.previousIds) {
618
+ this.previousIds = Promise.all(this.previous.map(async (resolver) => {
619
+ try {
620
+ return await resolver.resolveDeviceId();
621
+ } catch (e) {
622
+ return "";
623
+ }
624
+ }));
625
+ }
626
+ return (await this.previousIds).some((previous) => previous.length > 0 && previous === deviceId);
627
+ }
628
+ };
629
+ var LegacyDeviceIdResolver = class {
209
630
  async resolveDeviceName() {
210
- switch (process.platform) {
631
+ switch (import_node_process2.default.platform) {
211
632
  case "darwin":
212
633
  try {
213
- return import_node_child_process.default.execSync("scutil --get ComputerName").toString().trim();
634
+ return import_node_child_process2.default.execSync("scutil --get ComputerName").toString().trim();
214
635
  } catch (e) {
215
- return import_node_os.default.hostname();
636
+ return import_node_os2.default.hostname();
216
637
  }
217
638
  case "win32":
218
- return process.env.COMPUTERNAME || import_node_os.default.hostname();
219
- case "linux":
220
- const prettyname = import_node_child_process.default.execSync("hostnamectl --pretty").toString().trim();
221
- return prettyname || import_node_os.default.hostname();
639
+ return import_node_process2.default.env.COMPUTERNAME || import_node_os2.default.hostname();
640
+ case "linux": {
641
+ const prettyname = import_node_child_process2.default.execSync("hostnamectl --pretty").toString().trim();
642
+ return prettyname || import_node_os2.default.hostname();
643
+ }
222
644
  default:
223
- return import_node_os.default.hostname();
645
+ return import_node_os2.default.hostname();
224
646
  }
225
647
  }
226
648
  async resolveDeviceId() {
227
649
  const [cpu, hw] = await Promise.all([import_systeminformation.default.cpu(), import_systeminformation.default.system()]);
228
650
  const parts = [
229
- import_node_os.default.hostname(),
651
+ import_node_os2.default.hostname(),
230
652
  hw.manufacturer,
231
653
  hw.model,
232
654
  hw.serial,
@@ -238,40 +660,15 @@ var DefaultDeviceIdResolver = class {
238
660
  cpu.family,
239
661
  cpu.model
240
662
  ];
241
- const hash = (0, import_node_crypto.createHash)("sha256").update(parts.join("")).digest();
663
+ const hash = (0, import_node_crypto2.createHash)("sha256").update(parts.join("")).digest();
242
664
  return hash.toString("base64").replaceAll("=", "");
243
665
  }
244
666
  };
245
667
 
246
668
  // src/store.ts
247
- var import_node_fs = __toESM(require("fs"), 1);
669
+ var import_node_fs2 = __toESM(require("fs"), 1);
248
670
  var import_promises = __toESM(require("fs/promises"), 1);
249
671
  var import_node_path = __toESM(require("path"), 1);
250
-
251
- // src/errors.ts
252
- var ErrorType = /* @__PURE__ */ ((ErrorType2) => {
253
- ErrorType2["None"] = "None";
254
- ErrorType2["ApiError"] = "ApiError";
255
- ErrorType2["NoEligibleLicense"] = "NoEligibleLicense";
256
- ErrorType2["LicenseInvalid"] = "LicenseInvalid";
257
- ErrorType2["LicenseRevoked"] = "LicenseRevoked";
258
- ErrorType2["LicenseActivationRevoked"] = "LicenseActivationRevoked";
259
- ErrorType2["LicenseExpired"] = "LicenseExpired";
260
- return ErrorType2;
261
- })(ErrorType || {});
262
- var MoonbaseError = class extends Error {
263
- constructor(title, detail, type, inner) {
264
- super();
265
- this.title = title;
266
- this.detail = detail;
267
- this.type = type;
268
- this.inner = inner;
269
- this.name = "MoonbaseError";
270
- this.message = detail != null ? detail : title;
271
- }
272
- };
273
-
274
- // src/store.ts
275
672
  var InMemoryLicenseStore = class {
276
673
  async loadLocalLicense() {
277
674
  var _a;
@@ -289,7 +686,8 @@ var FileLicenseStore = class {
289
686
  this.options = options;
290
687
  }
291
688
  async loadLocalLicense() {
292
- if (!import_node_fs.default.existsSync(this.path)) return null;
689
+ if (!import_node_fs2.default.existsSync(this.path))
690
+ return null;
293
691
  try {
294
692
  const file = await import_promises.default.readFile(this.path, { encoding: "utf-8" });
295
693
  const license = licenseSchema.parse(JSON.parse(file));
@@ -302,7 +700,8 @@ var FileLicenseStore = class {
302
700
  await import_promises.default.writeFile(this.path, JSON.stringify(license), { encoding: "utf-8" });
303
701
  }
304
702
  async deleteLocalLicense() {
305
- if (!import_node_fs.default.existsSync(this.path)) return;
703
+ if (!import_node_fs2.default.existsSync(this.path))
704
+ return;
306
705
  await import_promises.default.rm(this.path);
307
706
  }
308
707
  get path() {
@@ -312,8 +711,8 @@ var FileLicenseStore = class {
312
711
  };
313
712
 
314
713
  // src/validator.ts
315
- var import_zod2 = require("zod");
316
714
  var import_jsonwebtoken = __toESM(require("jsonwebtoken"), 1);
715
+ var import_zod2 = require("zod");
317
716
  var licenseClaimsSchema = import_zod2.z.object({
318
717
  ["l:id" /* LicenseId */]: import_zod2.z.string(),
319
718
  ["trial" /* Trial */]: import_zod2.z.string().or(import_zod2.z.boolean()).transform((v) => v === true || v === "true").pipe(import_zod2.z.boolean()),
@@ -350,15 +749,20 @@ var LicenseValidator = class {
350
749
  );
351
750
  }
352
751
  const expectedSignature = await this.deviceIdResolver.resolveDeviceId();
353
- if (expectedSignature !== claims["sig" /* ComputerSignature */]) {
354
- throw new MoonbaseError(
355
- "License invalide",
356
- "This license is not for this device",
357
- "LicenseExpired" /* LicenseExpired */
358
- );
359
- }
752
+ const boundSignature = claims["sig" /* ComputerSignature */];
753
+ if (expectedSignature !== boundSignature && !await this.acceptsHistoricalDeviceId(boundSignature))
754
+ throw deviceMismatchError(expectedSignature, boundSignature);
360
755
  return license;
361
756
  }
757
+ /**
758
+ * Give a {@link IMigratingDeviceIdResolver} the chance to vouch for a device id
759
+ * this machine used to have. Only consulted after the fast path fails, so
760
+ * apps that have not opted into a migration pay nothing.
761
+ */
762
+ async acceptsHistoricalDeviceId(boundSignature) {
763
+ const accepts = this.deviceIdResolver.acceptsDeviceId;
764
+ return typeof accepts === "function" && await accepts.call(this.deviceIdResolver, boundSignature);
765
+ }
362
766
  parseLicenseToken(token) {
363
767
  return new Promise((resolve, reject) => {
364
768
  import_jsonwebtoken.default.verify(token, this.configuration.publicKey, {
@@ -423,11 +827,34 @@ var LicenseValidator = class {
423
827
  });
424
828
  }
425
829
  };
830
+ function deviceMismatchError(expected, bound) {
831
+ const detail = "This license is not for this device";
832
+ const versionNote = describeVersionDifference(expected, bound);
833
+ return new MoonbaseError(
834
+ "License is for another device",
835
+ versionNote ? `${detail}. ${versionNote}` : detail,
836
+ "LicenseDeviceMismatch" /* LicenseDeviceMismatch */
837
+ );
838
+ }
839
+ function describeVersionDifference(expected, bound) {
840
+ const expectedStamp = parseDeviceIdStamp(expected);
841
+ if (!expectedStamp)
842
+ return null;
843
+ const boundStamp = parseDeviceIdStamp(bound);
844
+ if (boundStamp && boundStamp.version === expectedStamp.version)
845
+ return null;
846
+ if (boundStamp && boundStamp.version > expectedStamp.version) {
847
+ return `The binding was created by device fingerprint v${boundStamp.version}, which is newer than the v${expectedStamp.version} this SDK computes \u2014 update the SDK rather than re-activating, which would rebind the device to the older algorithm.`;
848
+ }
849
+ const boundVersion = boundStamp ? `device fingerprint v${boundStamp.version}` : "an SDK predating versioned device fingerprints";
850
+ return `The binding was created by ${boundVersion}, while this SDK computes v${expectedStamp.version}, so this may instead be the same machine bound under the older algorithm \u2014 re-activate to find out, or configure a MigratingDeviceIdResolver to keep accepting the previous id.`;
851
+ }
426
852
 
427
853
  // src/index.ts
428
854
  function detectPlatform() {
429
- if (typeof process === "undefined") return void 0;
430
- switch (process.platform) {
855
+ if (typeof import_node_process3.default === "undefined")
856
+ return void 0;
857
+ switch (import_node_process3.default.platform) {
431
858
  case "darwin":
432
859
  return "Mac";
433
860
  case "win32":
@@ -447,7 +874,7 @@ var MoonbaseLicensing = class {
447
874
  platform: configuration.platform === void 0 ? detectPlatform() : configuration.platform
448
875
  };
449
876
  this.store = (_a = configuration.licenseStore) != null ? _a : new InMemoryLicenseStore();
450
- this.deviceIdResolver = (_b = configuration.deivceIdResolver) != null ? _b : new DefaultDeviceIdResolver();
877
+ this.deviceIdResolver = (_b = configuration.deviceIdResolver) != null ? _b : new MoonbaseDeviceIdResolver();
451
878
  this.validator = new LicenseValidator(this.configuration, this.deviceIdResolver);
452
879
  this.client = new LicenseClient(this.configuration, this.deviceIdResolver, this.validator);
453
880
  }
@@ -459,7 +886,7 @@ var MoonbaseLicensing = class {
459
886
  format: "JWT"
460
887
  };
461
888
  const json = JSON.stringify(token);
462
- return Buffer.from(Buffer.from(json).toString("base64"));
889
+ return import_node_buffer2.Buffer.from(import_node_buffer2.Buffer.from(json).toString("base64"));
463
890
  }
464
891
  async readRawLicense(license) {
465
892
  return await this.client.validateRawLicense(license);
@@ -468,12 +895,31 @@ var MoonbaseLicensing = class {
468
895
  // Annotate the CommonJS export names for ESM import in node:
469
896
  0 && (module.exports = {
470
897
  ActivationMethod,
471
- DefaultDeviceIdResolver,
472
898
  ErrorType,
899
+ FINGERPRINT_PREFIX,
900
+ FINGERPRINT_VERSION,
473
901
  FileLicenseStore,
902
+ IDENTIFYING_PARAM_NAMES,
474
903
  InMemoryLicenseStore,
904
+ InsufficientDeviceIdentityError,
905
+ LegacyDeviceIdResolver,
475
906
  LicenseClient,
476
907
  LicenseValidator,
908
+ MAX_VALUE_LENGTH,
909
+ MigratingDeviceIdResolver,
910
+ MoonbaseDeviceIdResolver,
477
911
  MoonbaseError,
478
- MoonbaseLicensing
912
+ MoonbaseLicensing,
913
+ buildFingerprintMaterial,
914
+ canonicalizeParams,
915
+ canonicalizeValue,
916
+ defaultDeviceIdentityReader,
917
+ fingerprintDeviceId,
918
+ fingerprintDigest,
919
+ parseDeviceIdStamp,
920
+ parseIoregPlatformUuid,
921
+ parseSmbiosParams,
922
+ platformTag,
923
+ selectMachineId,
924
+ stampDeviceId
479
925
  });