@moonbase.sh/licensing 0.0.0-next-20260729150401

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 ADDED
@@ -0,0 +1,863 @@
1
+ // src/index.ts
2
+ import { Buffer as Buffer2 } from "buffer";
3
+ import process3 from "process";
4
+
5
+ // src/client.ts
6
+ import fetch from "cross-fetch";
7
+
8
+ // src/schemas.ts
9
+ import { z } from "zod";
10
+
11
+ // src/types.ts
12
+ var ActivationMethod = /* @__PURE__ */ ((ActivationMethod2) => {
13
+ ActivationMethod2["Online"] = "Online";
14
+ ActivationMethod2["Offline"] = "Offline";
15
+ return ActivationMethod2;
16
+ })(ActivationMethod || {});
17
+
18
+ // src/schemas.ts
19
+ var activationRequestResponseSchema = z.object({
20
+ id: z.string(),
21
+ request: z.string(),
22
+ browser: z.string()
23
+ });
24
+ var productSchema = z.object({
25
+ id: z.string(),
26
+ name: z.string(),
27
+ currentReleaseVersion: z.string().nullish(),
28
+ properties: z.record(z.unknown()).nullish()
29
+ });
30
+ var userSchema = z.object({
31
+ id: z.string(),
32
+ name: z.string(),
33
+ email: z.string(),
34
+ properties: z.record(z.unknown()).nullish()
35
+ });
36
+ var licenseSchema = z.object({
37
+ id: z.string(),
38
+ trial: z.boolean(),
39
+ activationMethod: z.nativeEnum(ActivationMethod),
40
+ product: productSchema,
41
+ ownedSubProductIds: z.array(z.string()).default([]),
42
+ subscriptionId: z.string().nullish(),
43
+ issuedAt: z.coerce.date(),
44
+ issuedTo: userSchema,
45
+ expiresAt: z.coerce.date().nullish(),
46
+ validatedAt: z.coerce.date(),
47
+ properties: z.record(z.unknown()).nullish(),
48
+ token: z.string()
49
+ });
50
+
51
+ // src/client.ts
52
+ var defaultFetchOptions = {
53
+ method: "GET",
54
+ mode: "cors",
55
+ headers: {
56
+ "Accept": "application/json",
57
+ "Content-Type": "application/json",
58
+ "x-mb-client": `moonbase.js`
59
+ }
60
+ };
61
+ var LicenseClient = class {
62
+ constructor(configuration, deviceIdResolver, licenseValidator) {
63
+ this.configuration = configuration;
64
+ this.deviceIdResolver = deviceIdResolver;
65
+ this.licenseValidator = licenseValidator;
66
+ }
67
+ async requestActivation() {
68
+ const content = {
69
+ deviceName: await this.deviceIdResolver.resolveDeviceName(),
70
+ deviceSignature: await this.deviceIdResolver.resolveDeviceId()
71
+ };
72
+ const response = await fetch(`${this.configuration.endpoint}/api/client/activations/${this.configuration.productId}/request${this.buildQueryString()}`, {
73
+ ...defaultFetchOptions,
74
+ method: "POST",
75
+ body: JSON.stringify(content)
76
+ });
77
+ if (response.status >= 400) {
78
+ throw new MoonbaseError("Request not successful", `The API responded with a ${response.status} ${response.statusText} response`, "ApiError" /* ApiError */);
79
+ }
80
+ try {
81
+ return activationRequestResponseSchema.parse(await response.json());
82
+ } catch (err) {
83
+ const error = err;
84
+ throw new MoonbaseError(
85
+ "Could not request activation",
86
+ error.message,
87
+ "ApiError" /* ApiError */,
88
+ error
89
+ );
90
+ }
91
+ }
92
+ async getRequestedActivation(request) {
93
+ const response = await fetch(request.request, defaultFetchOptions);
94
+ if (response.status === 204 || response.status === 404)
95
+ return null;
96
+ return await this.handleLicenseResponse(response);
97
+ }
98
+ async requestTrial() {
99
+ const content = {
100
+ deviceName: await this.deviceIdResolver.resolveDeviceName(),
101
+ deviceSignature: await this.deviceIdResolver.resolveDeviceId()
102
+ };
103
+ const response = await fetch(`${this.configuration.endpoint}/api/client/trials/${this.configuration.productId}/request${this.buildQueryString()}`, {
104
+ ...defaultFetchOptions,
105
+ method: "POST",
106
+ headers: {
107
+ ...defaultFetchOptions.headers,
108
+ "Content-Type": "application/json"
109
+ },
110
+ body: JSON.stringify(content)
111
+ });
112
+ return await this.handleLicenseResponse(response);
113
+ }
114
+ async validateLicense(license) {
115
+ const response = await fetch(`${this.configuration.endpoint}/api/client/licenses/${this.configuration.productId}/validate${this.buildQueryString()}`, {
116
+ ...defaultFetchOptions,
117
+ method: "POST",
118
+ headers: {
119
+ ...defaultFetchOptions.headers,
120
+ "Content-Type": "text/plain"
121
+ },
122
+ body: license.token
123
+ });
124
+ return await this.handleLicenseResponse(response);
125
+ }
126
+ async validateRawLicense(rawLicense) {
127
+ const license = await this.licenseValidator.validateLicense(rawLicense.toString("utf8"));
128
+ if (license.activationMethod === "Offline" /* Offline */)
129
+ return license;
130
+ return await this.validateLicense(license);
131
+ }
132
+ async revokeLicense(license) {
133
+ const response = await fetch(`${this.configuration.endpoint}/api/client/licenses/${this.configuration.productId}/revoke?format=JWT`, {
134
+ ...defaultFetchOptions,
135
+ method: "POST",
136
+ headers: {
137
+ ...defaultFetchOptions.headers,
138
+ "Content-Type": "text/plain"
139
+ },
140
+ body: license.token
141
+ });
142
+ if (response.status >= 400) {
143
+ throw new MoonbaseError("Request not successful", `The API responded with a ${response.status} ${response.statusText} response`, "ApiError" /* ApiError */);
144
+ }
145
+ }
146
+ async handleLicenseResponse(response) {
147
+ if (response.status >= 400) {
148
+ throw new MoonbaseError("Request not successful", `The API responded with a ${response.status} ${response.statusText} response`, "ApiError" /* ApiError */);
149
+ }
150
+ return await this.licenseValidator.validateLicense(await response.text());
151
+ }
152
+ buildQueryString() {
153
+ const parts = ["format=JWT"];
154
+ const { platform, appVersion, metadata } = this.configuration;
155
+ if (platform)
156
+ parts.push(`platform=${encodeURIComponent(platform)}`);
157
+ if (appVersion)
158
+ parts.push(`appVersion=${encodeURIComponent(appVersion)}`);
159
+ if (metadata) {
160
+ for (const [key, value] of Object.entries(metadata)) {
161
+ if (value === null || value === void 0 || value === "")
162
+ continue;
163
+ parts.push(`meta[${encodeURIComponent(key)}]=${encodeURIComponent(value)}`);
164
+ }
165
+ }
166
+ return `?${parts.join("&")}`;
167
+ }
168
+ };
169
+
170
+ // src/deviceIdResolver.ts
171
+ import cp2 from "child_process";
172
+ import { createHash as createHash2 } from "crypto";
173
+ import os2 from "os";
174
+ import process2 from "process";
175
+ import si from "systeminformation";
176
+
177
+ // src/errors.ts
178
+ var ErrorType = /* @__PURE__ */ ((ErrorType2) => {
179
+ ErrorType2["None"] = "None";
180
+ ErrorType2["ApiError"] = "ApiError";
181
+ ErrorType2["NoEligibleLicense"] = "NoEligibleLicense";
182
+ ErrorType2["LicenseInvalid"] = "LicenseInvalid";
183
+ ErrorType2["LicenseRevoked"] = "LicenseRevoked";
184
+ ErrorType2["LicenseActivationRevoked"] = "LicenseActivationRevoked";
185
+ ErrorType2["LicenseExpired"] = "LicenseExpired";
186
+ ErrorType2["LicenseDeviceMismatch"] = "LicenseDeviceMismatch";
187
+ ErrorType2["DeviceIdentityUnavailable"] = "DeviceIdentityUnavailable";
188
+ return ErrorType2;
189
+ })(ErrorType || {});
190
+ var MoonbaseError = class extends Error {
191
+ constructor(title, detail, type, inner) {
192
+ super();
193
+ this.title = title;
194
+ this.detail = detail;
195
+ this.type = type;
196
+ this.inner = inner;
197
+ this.name = "MoonbaseError";
198
+ this.message = detail != null ? detail : title;
199
+ }
200
+ };
201
+ var InsufficientDeviceIdentityError = class extends MoonbaseError {
202
+ constructor(platform, reason = "no identity parameter could be read") {
203
+ super(
204
+ "No device identity",
205
+ `Could not identify this device (platform: ${platform}): ${reason}`,
206
+ "DeviceIdentityUnavailable" /* DeviceIdentityUnavailable */
207
+ );
208
+ this.platform = platform;
209
+ this.name = "InsufficientDeviceIdentityError";
210
+ }
211
+ };
212
+
213
+ // src/fingerprint.ts
214
+ import { Buffer } from "buffer";
215
+ import cp from "child_process";
216
+ import { createHash } from "crypto";
217
+ import fs from "fs";
218
+ import os from "os";
219
+ import process from "process";
220
+ var FINGERPRINT_PREFIX = "moonbase:fingerprint:v2";
221
+ var FINGERPRINT_VERSION = 2;
222
+ var MAX_VALUE_LENGTH = 128;
223
+ var PRINTABLE_ASCII_MIN = 32;
224
+ var PRINTABLE_ASCII_MAX = 126;
225
+ var STAMP_PATTERN = /^mbd(\d+)(n?)_([0-9a-f]{64})$/;
226
+ function canonicalizeValue(value) {
227
+ var _a;
228
+ let printable = "";
229
+ for (const char of value.normalize("NFC")) {
230
+ const code = (_a = char.codePointAt(0)) != null ? _a : 0;
231
+ if (code >= PRINTABLE_ASCII_MIN && code <= PRINTABLE_ASCII_MAX)
232
+ printable += char;
233
+ }
234
+ return printable.slice(0, MAX_VALUE_LENGTH).replace(/^ +| +$/g, "");
235
+ }
236
+ function platformTag(platform = process.platform) {
237
+ switch (platform) {
238
+ case "darwin":
239
+ return "mac";
240
+ case "win32":
241
+ return "windows";
242
+ case "android":
243
+ return "android";
244
+ case "linux":
245
+ return "linux";
246
+ case "freebsd":
247
+ case "openbsd":
248
+ case "netbsd":
249
+ return "bsd";
250
+ default:
251
+ return "unknown";
252
+ }
253
+ }
254
+ var IDENTIFYING_PARAMS = /* @__PURE__ */ new Set([
255
+ "ioPlatformUuid",
256
+ "machineId",
257
+ "systemUuid",
258
+ "baseboardSerialNumber",
259
+ "deviceName"
260
+ ]);
261
+ var IDENTIFYING_PARAM_NAMES = Object.freeze([...IDENTIFYING_PARAMS]);
262
+ var NOT_PROGRAMMED_VALUES = /* @__PURE__ */ new Set([
263
+ "to be filled by o.e.m.",
264
+ // The same filler without the dots, which plenty of firmware writes instead.
265
+ "to be filled by oem",
266
+ "default string",
267
+ "system serial number",
268
+ "base board serial number",
269
+ "chassis serial number",
270
+ "not specified",
271
+ "not applicable",
272
+ "not available",
273
+ "none",
274
+ "unknown",
275
+ "invalid",
276
+ "n/a",
277
+ "0123456789",
278
+ // `machine-id(5)`: the literal marker systemd writes to say "no id yet",
279
+ // e.g. in an initrd or a golden image awaiting first boot. Every machine
280
+ // deployed from such an image reads it, so it is the opposite of an identifier.
281
+ "uninitialized"
282
+ ]);
283
+ function isNotProgrammed(value) {
284
+ return NOT_PROGRAMMED_VALUES.has(value.toLowerCase()) || /^0+$/.test(value) || /^f+$/i.test(value);
285
+ }
286
+ function canonicalizeParams(params) {
287
+ const kept = [];
288
+ const seen = /* @__PURE__ */ new Set();
289
+ for (const [name, rawValue] of params) {
290
+ const value = canonicalizeValue(rawValue);
291
+ if (value.length === 0)
292
+ continue;
293
+ if (IDENTIFYING_PARAMS.has(name) && isNotProgrammed(value))
294
+ continue;
295
+ if (seen.has(name))
296
+ throw new Error(`Duplicate fingerprint parameter name: ${name}`);
297
+ seen.add(name);
298
+ kept.push([name, value]);
299
+ }
300
+ return kept;
301
+ }
302
+ function buildFingerprintMaterial(platform, params) {
303
+ const kept = canonicalizeParams(params);
304
+ if (kept.length === 0)
305
+ throw new InsufficientDeviceIdentityError(platform, "no identity parameter could be read");
306
+ if (!kept.some(([name]) => IDENTIFYING_PARAMS.has(name))) {
307
+ throw new InsufficientDeviceIdentityError(
308
+ platform,
309
+ `only model-level parameters could be read (${kept.map(([name]) => name).join(", ")}), none of which identify this individual machine`
310
+ );
311
+ }
312
+ const lines = [FINGERPRINT_PREFIX, `platform=${platform}`];
313
+ for (const [name, value] of kept)
314
+ lines.push(`${name}=${value}`);
315
+ return lines.join("\n");
316
+ }
317
+ function fingerprintDigest(material) {
318
+ return createHash("sha256").update(material, "utf8").digest("hex");
319
+ }
320
+ function stampDeviceId(digest, source = "identity") {
321
+ return `mbd${FINGERPRINT_VERSION}${source === "deviceName" ? "n" : ""}_${digest}`;
322
+ }
323
+ function fingerprintDeviceId(material, source = "identity") {
324
+ return stampDeviceId(fingerprintDigest(material), source);
325
+ }
326
+ function parseDeviceIdStamp(deviceId) {
327
+ const match = STAMP_PATTERN.exec(deviceId);
328
+ if (!match)
329
+ return null;
330
+ return {
331
+ version: Number(match[1]),
332
+ source: match[2] === "n" ? "deviceName" : "identity",
333
+ digest: match[3]
334
+ };
335
+ }
336
+ function commandOutput(command) {
337
+ try {
338
+ return cp.execSync(command, { encoding: "utf8" });
339
+ } catch (e) {
340
+ return "";
341
+ }
342
+ }
343
+ function readSysFile(path2) {
344
+ try {
345
+ return fs.readFileSync(path2, "utf8");
346
+ } catch (e) {
347
+ return "";
348
+ }
349
+ }
350
+ function parseIoregPlatformUuid(ioregOutput) {
351
+ const match = ioregOutput.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
352
+ return match ? match[1].replaceAll("-", "").toUpperCase() : "";
353
+ }
354
+ function readMacIdentity() {
355
+ const uuid = parseIoregPlatformUuid(commandOutput("ioreg -rd1 -c IOPlatformExpertDevice"));
356
+ const params = [["ioPlatformUuid", uuid]];
357
+ return { params, deviceName: os.hostname().replace(/\.local$/i, "") };
358
+ }
359
+ function selectMachineId(...sources) {
360
+ var _a;
361
+ return (_a = sources.map(canonicalizeValue).find((value) => /^[0-9a-f]{32}$/.test(value) && !isNotProgrammed(value))) != null ? _a : "";
362
+ }
363
+ function readMachineId() {
364
+ return selectMachineId(
365
+ readSysFile("/etc/machine-id"),
366
+ readSysFile("/var/lib/dbus/machine-id")
367
+ );
368
+ }
369
+ function readLinuxIdentity() {
370
+ const params = [
371
+ ["machineId", readMachineId()],
372
+ ["sysVendor", readSysFile("/sys/class/dmi/id/sys_vendor")],
373
+ ["productName", readSysFile("/sys/class/dmi/id/product_name")],
374
+ ["boardVendor", readSysFile("/sys/class/dmi/id/board_vendor")],
375
+ ["boardName", readSysFile("/sys/class/dmi/id/board_name")]
376
+ ];
377
+ return { params, deviceName: os.hostname() };
378
+ }
379
+ function parseSmbiosStructures(data) {
380
+ const structures = [];
381
+ let offset = 0;
382
+ while (offset + 4 <= data.length) {
383
+ const type = data[offset];
384
+ const length = data[offset + 1];
385
+ if (length < 4 || offset + length > data.length)
386
+ break;
387
+ const formatted = data.subarray(offset, offset + length);
388
+ const strings = [];
389
+ let p = offset + length;
390
+ if (p + 1 < data.length && data[p] === 0 && data[p + 1] === 0) {
391
+ p += 2;
392
+ } else {
393
+ while (p < data.length) {
394
+ let end = p;
395
+ while (end < data.length && data[end] !== 0)
396
+ end++;
397
+ strings.push(data.toString("latin1", p, end));
398
+ p = end + 1;
399
+ if (p < data.length && data[p] === 0) {
400
+ p += 1;
401
+ break;
402
+ }
403
+ }
404
+ }
405
+ structures.push({ type, formatted, strings });
406
+ if (type === 127)
407
+ break;
408
+ offset = p;
409
+ }
410
+ return structures;
411
+ }
412
+ function resolveSmbiosString(structure, fieldOffset) {
413
+ if (fieldOffset >= structure.formatted.length)
414
+ return "";
415
+ const index = structure.formatted[fieldOffset];
416
+ if (index === 0 || index > structure.strings.length)
417
+ return "";
418
+ return structure.strings[index - 1];
419
+ }
420
+ function formatSmbiosUuid(structure, fieldOffset) {
421
+ if (fieldOffset + 16 > structure.formatted.length)
422
+ return "";
423
+ const bytes = structure.formatted.subarray(fieldOffset, fieldOffset + 16);
424
+ if (bytes.every((byte) => byte === 0) || bytes.every((byte) => byte === 255))
425
+ return "";
426
+ let hex = "";
427
+ for (const byte of bytes)
428
+ hex += byte.toString(16).padStart(2, "0").toUpperCase();
429
+ return hex;
430
+ }
431
+ function parseSmbiosParams(smbiosData) {
432
+ const structures = parseSmbiosStructures(smbiosData);
433
+ const params = [];
434
+ const system = structures.find((structure) => structure.type === 1);
435
+ if (system) {
436
+ params.push(["systemManufacturer", resolveSmbiosString(system, 4)]);
437
+ params.push(["systemProductName", resolveSmbiosString(system, 5)]);
438
+ params.push(["systemUuid", formatSmbiosUuid(system, 8)]);
439
+ }
440
+ const baseboard = structures.find((structure) => structure.type === 2);
441
+ if (baseboard) {
442
+ params.push(["baseboardManufacturer", resolveSmbiosString(baseboard, 4)]);
443
+ params.push(["baseboardProduct", resolveSmbiosString(baseboard, 5)]);
444
+ params.push(["baseboardSerialNumber", resolveSmbiosString(baseboard, 7)]);
445
+ }
446
+ return params;
447
+ }
448
+ function readWindowsSmbios() {
449
+ const script = "[Convert]::ToBase64String((Get-CimInstance -Namespace root/wmi -ClassName MSSmBios_RawSMBiosTables).SMBiosData)";
450
+ const output = commandOutput(`powershell -NoProfile -NonInteractive -Command "${script}"`);
451
+ try {
452
+ return Buffer.from(output.trim(), "base64");
453
+ } catch (e) {
454
+ return Buffer.alloc(0);
455
+ }
456
+ }
457
+ function readWindowsIdentity() {
458
+ return { params: parseSmbiosParams(readWindowsSmbios()), deviceName: os.hostname() };
459
+ }
460
+ function defaultDeviceIdentityReader(platform = process.platform) {
461
+ return {
462
+ read() {
463
+ switch (platform) {
464
+ case "darwin":
465
+ return readMacIdentity();
466
+ case "linux":
467
+ return readLinuxIdentity();
468
+ case "win32":
469
+ return readWindowsIdentity();
470
+ // android, bsd and anything else define no identity params: the spec
471
+ // has no stable hardware source for them, so they resolve to an
472
+ // insufficient-identity error unless the deviceName fallback is enabled.
473
+ default:
474
+ return { params: [], deviceName: os.hostname() };
475
+ }
476
+ }
477
+ };
478
+ }
479
+
480
+ // src/deviceIdResolver.ts
481
+ var MoonbaseDeviceIdResolver = class {
482
+ constructor(options) {
483
+ var _a, _b, _c;
484
+ const platform = (_a = options == null ? void 0 : options.platform) != null ? _a : process2.platform;
485
+ this.platform = platformTag(platform);
486
+ this.reader = (_b = options == null ? void 0 : options.reader) != null ? _b : defaultDeviceIdentityReader(platform);
487
+ this.fallback = (_c = options == null ? void 0 : options.fallback) != null ? _c : "none";
488
+ }
489
+ async resolveDeviceName() {
490
+ return this.readIdentity().deviceName;
491
+ }
492
+ async resolveDeviceId() {
493
+ return this.compute().deviceId;
494
+ }
495
+ /**
496
+ * A fresh copy each call. The device binding is what activation sends and what
497
+ * validation compares, so handing out the resolver's own object would let a
498
+ * consumer that edits a diagnostic — or logs it through something that
499
+ * normalizes in place — silently change the id every later call returns.
500
+ */
501
+ async describeDevice() {
502
+ const described = this.compute();
503
+ return { ...described, paramNames: [...described.paramNames] };
504
+ }
505
+ compute() {
506
+ var _a;
507
+ (_a = this.described) != null ? _a : this.described = this.computeDescription();
508
+ return this.described;
509
+ }
510
+ /**
511
+ * Read identity at most once. Both halves of an activation request ask for it —
512
+ * the name and then the id — and a read can mean spawning `ioreg` or
513
+ * PowerShell, so reading per call would double the cost of every request and
514
+ * let the name and the id come from two different reads of the machine.
515
+ */
516
+ readIdentity() {
517
+ var _a;
518
+ (_a = this.identity) != null ? _a : this.identity = this.reader.read();
519
+ return this.identity;
520
+ }
521
+ computeDescription() {
522
+ const { params, deviceName } = this.readIdentity();
523
+ try {
524
+ return this.describe(params, "identity");
525
+ } catch (err) {
526
+ if (this.fallback !== "deviceName" || !(err instanceof InsufficientDeviceIdentityError))
527
+ throw err;
528
+ return this.describe([["deviceName", deviceName]], "deviceName");
529
+ }
530
+ }
531
+ describe(params, source) {
532
+ return {
533
+ deviceId: fingerprintDeviceId(buildFingerprintMaterial(this.platform, params), source),
534
+ version: FINGERPRINT_VERSION,
535
+ platform: this.platform,
536
+ source,
537
+ paramNames: canonicalizeParams(params).map(([name]) => name)
538
+ };
539
+ }
540
+ };
541
+ var MigratingDeviceIdResolver = class {
542
+ constructor(current, ...previous) {
543
+ this.current = current;
544
+ this.previous = previous;
545
+ const describe = current.describeDevice;
546
+ if (typeof describe === "function")
547
+ this.describeDevice = () => describe.call(current);
548
+ }
549
+ async resolveDeviceName() {
550
+ return this.current.resolveDeviceName();
551
+ }
552
+ async resolveDeviceId() {
553
+ return this.current.resolveDeviceId();
554
+ }
555
+ async acceptsDeviceId(deviceId) {
556
+ if (!this.previousIds) {
557
+ this.previousIds = Promise.all(this.previous.map(async (resolver) => {
558
+ try {
559
+ return await resolver.resolveDeviceId();
560
+ } catch (e) {
561
+ return "";
562
+ }
563
+ }));
564
+ }
565
+ return (await this.previousIds).some((previous) => previous.length > 0 && previous === deviceId);
566
+ }
567
+ };
568
+ var LegacyDeviceIdResolver = class {
569
+ async resolveDeviceName() {
570
+ switch (process2.platform) {
571
+ case "darwin":
572
+ try {
573
+ return cp2.execSync("scutil --get ComputerName").toString().trim();
574
+ } catch (e) {
575
+ return os2.hostname();
576
+ }
577
+ case "win32":
578
+ return process2.env.COMPUTERNAME || os2.hostname();
579
+ case "linux": {
580
+ const prettyname = cp2.execSync("hostnamectl --pretty").toString().trim();
581
+ return prettyname || os2.hostname();
582
+ }
583
+ default:
584
+ return os2.hostname();
585
+ }
586
+ }
587
+ async resolveDeviceId() {
588
+ const [cpu, hw] = await Promise.all([si.cpu(), si.system()]);
589
+ const parts = [
590
+ os2.hostname(),
591
+ hw.manufacturer,
592
+ hw.model,
593
+ hw.serial,
594
+ hw.uuid,
595
+ hw.sku,
596
+ cpu.manufacturer,
597
+ cpu.brand,
598
+ cpu.vendor,
599
+ cpu.family,
600
+ cpu.model
601
+ ];
602
+ const hash = createHash2("sha256").update(parts.join("")).digest();
603
+ return hash.toString("base64").replaceAll("=", "");
604
+ }
605
+ };
606
+
607
+ // src/store.ts
608
+ import fsSync from "fs";
609
+ import fs2 from "fs/promises";
610
+ import path from "path";
611
+ var InMemoryLicenseStore = class {
612
+ async loadLocalLicense() {
613
+ var _a;
614
+ return (_a = this.license) != null ? _a : null;
615
+ }
616
+ async storeLocalLicense(license) {
617
+ this.license = license;
618
+ }
619
+ async deleteLocalLicense() {
620
+ this.license = void 0;
621
+ }
622
+ };
623
+ var FileLicenseStore = class {
624
+ constructor(options) {
625
+ this.options = options;
626
+ }
627
+ async loadLocalLicense() {
628
+ if (!fsSync.existsSync(this.path))
629
+ return null;
630
+ try {
631
+ const file = await fs2.readFile(this.path, { encoding: "utf-8" });
632
+ const license = licenseSchema.parse(JSON.parse(file));
633
+ return license;
634
+ } catch (err) {
635
+ throw new MoonbaseError("File error", "Could not load local license", "LicenseInvalid" /* LicenseInvalid */, err);
636
+ }
637
+ }
638
+ async storeLocalLicense(license) {
639
+ await fs2.writeFile(this.path, JSON.stringify(license), { encoding: "utf-8" });
640
+ }
641
+ async deleteLocalLicense() {
642
+ if (!fsSync.existsSync(this.path))
643
+ return;
644
+ await fs2.rm(this.path);
645
+ }
646
+ get path() {
647
+ var _a, _b, _c, _d;
648
+ return path.resolve((_b = (_a = this.options) == null ? void 0 : _a.dir) != null ? _b : path.resolve(), (_d = (_c = this.options) == null ? void 0 : _c.licenseFileName) != null ? _d : "license.mb");
649
+ }
650
+ };
651
+
652
+ // src/validator.ts
653
+ import jwt from "jsonwebtoken";
654
+ import { z as z2 } from "zod";
655
+ var licenseClaimsSchema = z2.object({
656
+ ["l:id" /* LicenseId */]: z2.string(),
657
+ ["trial" /* Trial */]: z2.string().or(z2.boolean()).transform((v) => v === true || v === "true").pipe(z2.boolean()),
658
+ ["method" /* ActivationMethod */]: z2.nativeEnum(ActivationMethod),
659
+ ["sig" /* ComputerSignature */]: z2.string(),
660
+ ["iat" /* IssuedAt */]: z2.number().transform((v) => new Date(v * 1e3)).pipe(z2.date()),
661
+ ["exp" /* ExpiresAt */]: z2.number().nullish().transform((v) => v ? new Date(v * 1e3) : void 0).pipe(z2.date().nullish()),
662
+ ["validated" /* ValidatedAt */]: z2.number().transform((v) => new Date(v * 1e3)).pipe(z2.date()),
663
+ ["p:id" /* ProductId */]: z2.string(),
664
+ ["p:name" /* ProductName */]: z2.string(),
665
+ ["p:rel" /* ProductReleaseVersion */]: z2.string().nullish(),
666
+ ["sp:owned" /* OwnedSubProducts */]: z2.string().nullish().transform((v) => v ? v.split(",") : []),
667
+ ["s:id" /* SubscriptionId */]: z2.string().nullish(),
668
+ ["u:id" /* UserId */]: z2.string(),
669
+ ["u:name" /* UserName */]: z2.string(),
670
+ ["u:email" /* UserEmail */]: z2.string(),
671
+ ["p:properties" /* ProductProperties */]: z2.record(z2.unknown()).nullish(),
672
+ ["u:properties" /* UserProperties */]: z2.record(z2.unknown()).nullish(),
673
+ ["l:properties" /* LicenseProperties */]: z2.record(z2.unknown()).nullish(),
674
+ ["t:properties" /* TrialProperties */]: z2.record(z2.unknown()).nullish()
675
+ });
676
+ var LicenseValidator = class {
677
+ constructor(configuration, deviceIdResolver) {
678
+ this.configuration = configuration;
679
+ this.deviceIdResolver = deviceIdResolver;
680
+ }
681
+ async validateLicense(token) {
682
+ const [license, claims] = await this.parseLicenseToken(token);
683
+ if (license.expiresAt && license.expiresAt < /* @__PURE__ */ new Date()) {
684
+ throw new MoonbaseError(
685
+ "License expired",
686
+ "This license has expired",
687
+ "LicenseExpired" /* LicenseExpired */
688
+ );
689
+ }
690
+ const expectedSignature = await this.deviceIdResolver.resolveDeviceId();
691
+ const boundSignature = claims["sig" /* ComputerSignature */];
692
+ if (expectedSignature !== boundSignature && !await this.acceptsHistoricalDeviceId(boundSignature))
693
+ throw deviceMismatchError(expectedSignature, boundSignature);
694
+ return license;
695
+ }
696
+ /**
697
+ * Give a {@link IMigratingDeviceIdResolver} the chance to vouch for a device id
698
+ * this machine used to have. Only consulted after the fast path fails, so
699
+ * apps that have not opted into a migration pay nothing.
700
+ */
701
+ async acceptsHistoricalDeviceId(boundSignature) {
702
+ const accepts = this.deviceIdResolver.acceptsDeviceId;
703
+ return typeof accepts === "function" && await accepts.call(this.deviceIdResolver, boundSignature);
704
+ }
705
+ parseLicenseToken(token) {
706
+ return new Promise((resolve, reject) => {
707
+ jwt.verify(token, this.configuration.publicKey, {
708
+ ignoreExpiration: true,
709
+ issuer: this.configuration.accountId,
710
+ audience: this.configuration.productId
711
+ }, (err, decoded) => {
712
+ var _a, _b, _c, _d, _e;
713
+ if (err) {
714
+ reject(new MoonbaseError(
715
+ "Could not validate license",
716
+ err.message,
717
+ "LicenseInvalid" /* LicenseInvalid */,
718
+ err
719
+ ));
720
+ return;
721
+ }
722
+ if (typeof decoded === "string") {
723
+ reject(new MoonbaseError(
724
+ "Could not validate license",
725
+ "The given license token could not be parsed",
726
+ "LicenseInvalid" /* LicenseInvalid */
727
+ ));
728
+ return;
729
+ }
730
+ try {
731
+ const claims = licenseClaimsSchema.parse(decoded);
732
+ resolve([{
733
+ id: claims["l:id" /* LicenseId */],
734
+ trial: claims["trial" /* Trial */],
735
+ activationMethod: claims["method" /* ActivationMethod */],
736
+ product: {
737
+ id: claims["p:id" /* ProductId */],
738
+ name: claims["p:name" /* ProductName */],
739
+ currentReleaseVersion: (_a = claims["p:rel" /* ProductReleaseVersion */]) != null ? _a : void 0,
740
+ properties: (_b = claims["p:properties" /* ProductProperties */]) != null ? _b : void 0
741
+ },
742
+ ownedSubProductIds: claims["sp:owned" /* OwnedSubProducts */],
743
+ subscriptionId: (_c = claims["s:id" /* SubscriptionId */]) != null ? _c : void 0,
744
+ issuedTo: {
745
+ id: claims["u:id" /* UserId */],
746
+ name: claims["u:name" /* UserName */],
747
+ email: claims["u:email" /* UserEmail */],
748
+ properties: (_d = claims["u:properties" /* UserProperties */]) != null ? _d : void 0
749
+ },
750
+ expiresAt: claims["exp" /* ExpiresAt */] || void 0,
751
+ issuedAt: claims["iat" /* IssuedAt */],
752
+ validatedAt: claims["validated" /* ValidatedAt */],
753
+ properties: (_e = claims["trial" /* Trial */] ? claims["t:properties" /* TrialProperties */] : claims["l:properties" /* LicenseProperties */]) != null ? _e : void 0,
754
+ token
755
+ }, claims]);
756
+ } catch (err2) {
757
+ const error = err2;
758
+ reject(new MoonbaseError(
759
+ "Could not validate license",
760
+ error.message,
761
+ "LicenseInvalid" /* LicenseInvalid */,
762
+ error
763
+ ));
764
+ }
765
+ });
766
+ });
767
+ }
768
+ };
769
+ function deviceMismatchError(expected, bound) {
770
+ const detail = "This license is not for this device";
771
+ const versionNote = describeVersionDifference(expected, bound);
772
+ return new MoonbaseError(
773
+ "License is for another device",
774
+ versionNote ? `${detail}. ${versionNote}` : detail,
775
+ "LicenseDeviceMismatch" /* LicenseDeviceMismatch */
776
+ );
777
+ }
778
+ function describeVersionDifference(expected, bound) {
779
+ const expectedStamp = parseDeviceIdStamp(expected);
780
+ if (!expectedStamp)
781
+ return null;
782
+ const boundStamp = parseDeviceIdStamp(bound);
783
+ if (boundStamp && boundStamp.version === expectedStamp.version)
784
+ return null;
785
+ if (boundStamp && boundStamp.version > expectedStamp.version) {
786
+ 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.`;
787
+ }
788
+ const boundVersion = boundStamp ? `device fingerprint v${boundStamp.version}` : "an SDK predating versioned device fingerprints";
789
+ 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.`;
790
+ }
791
+
792
+ // src/index.ts
793
+ function detectPlatform() {
794
+ if (typeof process3 === "undefined")
795
+ return void 0;
796
+ switch (process3.platform) {
797
+ case "darwin":
798
+ return "Mac";
799
+ case "win32":
800
+ return "Windows";
801
+ case "linux":
802
+ return "Linux";
803
+ default:
804
+ return void 0;
805
+ }
806
+ }
807
+ var MoonbaseLicensing = class {
808
+ constructor(configuration) {
809
+ var _a, _b;
810
+ this.configuration = {
811
+ ...configuration,
812
+ endpoint: configuration.endpoint.replace(/\/$/, ""),
813
+ platform: configuration.platform === void 0 ? detectPlatform() : configuration.platform
814
+ };
815
+ this.store = (_a = configuration.licenseStore) != null ? _a : new InMemoryLicenseStore();
816
+ this.deviceIdResolver = (_b = configuration.deviceIdResolver) != null ? _b : new MoonbaseDeviceIdResolver();
817
+ this.validator = new LicenseValidator(this.configuration, this.deviceIdResolver);
818
+ this.client = new LicenseClient(this.configuration, this.deviceIdResolver, this.validator);
819
+ }
820
+ async generateDeviceToken() {
821
+ const token = {
822
+ id: await this.deviceIdResolver.resolveDeviceId(),
823
+ name: await this.deviceIdResolver.resolveDeviceName(),
824
+ productId: this.configuration.productId,
825
+ format: "JWT"
826
+ };
827
+ const json = JSON.stringify(token);
828
+ return Buffer2.from(Buffer2.from(json).toString("base64"));
829
+ }
830
+ async readRawLicense(license) {
831
+ return await this.client.validateRawLicense(license);
832
+ }
833
+ };
834
+ export {
835
+ ActivationMethod,
836
+ ErrorType,
837
+ FINGERPRINT_PREFIX,
838
+ FINGERPRINT_VERSION,
839
+ FileLicenseStore,
840
+ IDENTIFYING_PARAM_NAMES,
841
+ InMemoryLicenseStore,
842
+ InsufficientDeviceIdentityError,
843
+ LegacyDeviceIdResolver,
844
+ LicenseClient,
845
+ LicenseValidator,
846
+ MAX_VALUE_LENGTH,
847
+ MigratingDeviceIdResolver,
848
+ MoonbaseDeviceIdResolver,
849
+ MoonbaseError,
850
+ MoonbaseLicensing,
851
+ buildFingerprintMaterial,
852
+ canonicalizeParams,
853
+ canonicalizeValue,
854
+ defaultDeviceIdentityReader,
855
+ fingerprintDeviceId,
856
+ fingerprintDigest,
857
+ parseDeviceIdStamp,
858
+ parseIoregPlatformUuid,
859
+ parseSmbiosParams,
860
+ platformTag,
861
+ selectMachineId,
862
+ stampDeviceId
863
+ };