@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.cjs ADDED
@@ -0,0 +1,925 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ActivationMethod: () => ActivationMethod,
34
+ ErrorType: () => ErrorType,
35
+ FINGERPRINT_PREFIX: () => FINGERPRINT_PREFIX,
36
+ FINGERPRINT_VERSION: () => FINGERPRINT_VERSION,
37
+ FileLicenseStore: () => FileLicenseStore,
38
+ IDENTIFYING_PARAM_NAMES: () => IDENTIFYING_PARAM_NAMES,
39
+ InMemoryLicenseStore: () => InMemoryLicenseStore,
40
+ InsufficientDeviceIdentityError: () => InsufficientDeviceIdentityError,
41
+ LegacyDeviceIdResolver: () => LegacyDeviceIdResolver,
42
+ LicenseClient: () => LicenseClient,
43
+ LicenseValidator: () => LicenseValidator,
44
+ MAX_VALUE_LENGTH: () => MAX_VALUE_LENGTH,
45
+ MigratingDeviceIdResolver: () => MigratingDeviceIdResolver,
46
+ MoonbaseDeviceIdResolver: () => MoonbaseDeviceIdResolver,
47
+ MoonbaseError: () => MoonbaseError,
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
61
+ });
62
+ module.exports = __toCommonJS(index_exports);
63
+ var import_node_buffer2 = require("buffer");
64
+ var import_node_process3 = __toESM(require("process"), 1);
65
+
66
+ // src/client.ts
67
+ var import_cross_fetch = __toESM(require("cross-fetch"), 1);
68
+
69
+ // src/schemas.ts
70
+ var import_zod = require("zod");
71
+
72
+ // src/types.ts
73
+ var ActivationMethod = /* @__PURE__ */ ((ActivationMethod2) => {
74
+ ActivationMethod2["Online"] = "Online";
75
+ ActivationMethod2["Offline"] = "Offline";
76
+ return ActivationMethod2;
77
+ })(ActivationMethod || {});
78
+
79
+ // src/schemas.ts
80
+ var activationRequestResponseSchema = import_zod.z.object({
81
+ id: import_zod.z.string(),
82
+ request: import_zod.z.string(),
83
+ browser: import_zod.z.string()
84
+ });
85
+ var productSchema = import_zod.z.object({
86
+ id: import_zod.z.string(),
87
+ name: import_zod.z.string(),
88
+ currentReleaseVersion: import_zod.z.string().nullish(),
89
+ properties: import_zod.z.record(import_zod.z.unknown()).nullish()
90
+ });
91
+ var userSchema = import_zod.z.object({
92
+ id: import_zod.z.string(),
93
+ name: import_zod.z.string(),
94
+ email: import_zod.z.string(),
95
+ properties: import_zod.z.record(import_zod.z.unknown()).nullish()
96
+ });
97
+ var licenseSchema = import_zod.z.object({
98
+ id: import_zod.z.string(),
99
+ trial: import_zod.z.boolean(),
100
+ activationMethod: import_zod.z.nativeEnum(ActivationMethod),
101
+ product: productSchema,
102
+ ownedSubProductIds: import_zod.z.array(import_zod.z.string()).default([]),
103
+ subscriptionId: import_zod.z.string().nullish(),
104
+ issuedAt: import_zod.z.coerce.date(),
105
+ issuedTo: userSchema,
106
+ expiresAt: import_zod.z.coerce.date().nullish(),
107
+ validatedAt: import_zod.z.coerce.date(),
108
+ properties: import_zod.z.record(import_zod.z.unknown()).nullish(),
109
+ token: import_zod.z.string()
110
+ });
111
+
112
+ // src/client.ts
113
+ var defaultFetchOptions = {
114
+ method: "GET",
115
+ mode: "cors",
116
+ headers: {
117
+ "Accept": "application/json",
118
+ "Content-Type": "application/json",
119
+ "x-mb-client": `moonbase.js`
120
+ }
121
+ };
122
+ var LicenseClient = class {
123
+ constructor(configuration, deviceIdResolver, licenseValidator) {
124
+ this.configuration = configuration;
125
+ this.deviceIdResolver = deviceIdResolver;
126
+ this.licenseValidator = licenseValidator;
127
+ }
128
+ async requestActivation() {
129
+ const content = {
130
+ deviceName: await this.deviceIdResolver.resolveDeviceName(),
131
+ deviceSignature: await this.deviceIdResolver.resolveDeviceId()
132
+ };
133
+ const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/activations/${this.configuration.productId}/request${this.buildQueryString()}`, {
134
+ ...defaultFetchOptions,
135
+ method: "POST",
136
+ body: JSON.stringify(content)
137
+ });
138
+ if (response.status >= 400) {
139
+ throw new MoonbaseError("Request not successful", `The API responded with a ${response.status} ${response.statusText} response`, "ApiError" /* ApiError */);
140
+ }
141
+ try {
142
+ return activationRequestResponseSchema.parse(await response.json());
143
+ } catch (err) {
144
+ const error = err;
145
+ throw new MoonbaseError(
146
+ "Could not request activation",
147
+ error.message,
148
+ "ApiError" /* ApiError */,
149
+ error
150
+ );
151
+ }
152
+ }
153
+ async getRequestedActivation(request) {
154
+ const response = await (0, import_cross_fetch.default)(request.request, defaultFetchOptions);
155
+ if (response.status === 204 || response.status === 404)
156
+ return null;
157
+ return await this.handleLicenseResponse(response);
158
+ }
159
+ async requestTrial() {
160
+ const content = {
161
+ deviceName: await this.deviceIdResolver.resolveDeviceName(),
162
+ deviceSignature: await this.deviceIdResolver.resolveDeviceId()
163
+ };
164
+ const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/trials/${this.configuration.productId}/request${this.buildQueryString()}`, {
165
+ ...defaultFetchOptions,
166
+ method: "POST",
167
+ headers: {
168
+ ...defaultFetchOptions.headers,
169
+ "Content-Type": "application/json"
170
+ },
171
+ body: JSON.stringify(content)
172
+ });
173
+ return await this.handleLicenseResponse(response);
174
+ }
175
+ async validateLicense(license) {
176
+ const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/licenses/${this.configuration.productId}/validate${this.buildQueryString()}`, {
177
+ ...defaultFetchOptions,
178
+ method: "POST",
179
+ headers: {
180
+ ...defaultFetchOptions.headers,
181
+ "Content-Type": "text/plain"
182
+ },
183
+ body: license.token
184
+ });
185
+ return await this.handleLicenseResponse(response);
186
+ }
187
+ async validateRawLicense(rawLicense) {
188
+ const license = await this.licenseValidator.validateLicense(rawLicense.toString("utf8"));
189
+ if (license.activationMethod === "Offline" /* Offline */)
190
+ return license;
191
+ return await this.validateLicense(license);
192
+ }
193
+ async revokeLicense(license) {
194
+ const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/licenses/${this.configuration.productId}/revoke?format=JWT`, {
195
+ ...defaultFetchOptions,
196
+ method: "POST",
197
+ headers: {
198
+ ...defaultFetchOptions.headers,
199
+ "Content-Type": "text/plain"
200
+ },
201
+ body: license.token
202
+ });
203
+ if (response.status >= 400) {
204
+ throw new MoonbaseError("Request not successful", `The API responded with a ${response.status} ${response.statusText} response`, "ApiError" /* ApiError */);
205
+ }
206
+ }
207
+ async handleLicenseResponse(response) {
208
+ if (response.status >= 400) {
209
+ throw new MoonbaseError("Request not successful", `The API responded with a ${response.status} ${response.statusText} response`, "ApiError" /* ApiError */);
210
+ }
211
+ return await this.licenseValidator.validateLicense(await response.text());
212
+ }
213
+ buildQueryString() {
214
+ const parts = ["format=JWT"];
215
+ const { platform, appVersion, metadata } = this.configuration;
216
+ if (platform)
217
+ parts.push(`platform=${encodeURIComponent(platform)}`);
218
+ if (appVersion)
219
+ parts.push(`appVersion=${encodeURIComponent(appVersion)}`);
220
+ if (metadata) {
221
+ for (const [key, value] of Object.entries(metadata)) {
222
+ if (value === null || value === void 0 || value === "")
223
+ continue;
224
+ parts.push(`meta[${encodeURIComponent(key)}]=${encodeURIComponent(value)}`);
225
+ }
226
+ }
227
+ return `?${parts.join("&")}`;
228
+ }
229
+ };
230
+
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);
236
+ var import_systeminformation = __toESM(require("systeminformation"), 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");
276
+ var import_node_child_process = __toESM(require("child_process"), 1);
277
+ var import_node_crypto = require("crypto");
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 {
630
+ async resolveDeviceName() {
631
+ switch (import_node_process2.default.platform) {
632
+ case "darwin":
633
+ try {
634
+ return import_node_child_process2.default.execSync("scutil --get ComputerName").toString().trim();
635
+ } catch (e) {
636
+ return import_node_os2.default.hostname();
637
+ }
638
+ case "win32":
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
+ }
644
+ default:
645
+ return import_node_os2.default.hostname();
646
+ }
647
+ }
648
+ async resolveDeviceId() {
649
+ const [cpu, hw] = await Promise.all([import_systeminformation.default.cpu(), import_systeminformation.default.system()]);
650
+ const parts = [
651
+ import_node_os2.default.hostname(),
652
+ hw.manufacturer,
653
+ hw.model,
654
+ hw.serial,
655
+ hw.uuid,
656
+ hw.sku,
657
+ cpu.manufacturer,
658
+ cpu.brand,
659
+ cpu.vendor,
660
+ cpu.family,
661
+ cpu.model
662
+ ];
663
+ const hash = (0, import_node_crypto2.createHash)("sha256").update(parts.join("")).digest();
664
+ return hash.toString("base64").replaceAll("=", "");
665
+ }
666
+ };
667
+
668
+ // src/store.ts
669
+ var import_node_fs2 = __toESM(require("fs"), 1);
670
+ var import_promises = __toESM(require("fs/promises"), 1);
671
+ var import_node_path = __toESM(require("path"), 1);
672
+ var InMemoryLicenseStore = class {
673
+ async loadLocalLicense() {
674
+ var _a;
675
+ return (_a = this.license) != null ? _a : null;
676
+ }
677
+ async storeLocalLicense(license) {
678
+ this.license = license;
679
+ }
680
+ async deleteLocalLicense() {
681
+ this.license = void 0;
682
+ }
683
+ };
684
+ var FileLicenseStore = class {
685
+ constructor(options) {
686
+ this.options = options;
687
+ }
688
+ async loadLocalLicense() {
689
+ if (!import_node_fs2.default.existsSync(this.path))
690
+ return null;
691
+ try {
692
+ const file = await import_promises.default.readFile(this.path, { encoding: "utf-8" });
693
+ const license = licenseSchema.parse(JSON.parse(file));
694
+ return license;
695
+ } catch (err) {
696
+ throw new MoonbaseError("File error", "Could not load local license", "LicenseInvalid" /* LicenseInvalid */, err);
697
+ }
698
+ }
699
+ async storeLocalLicense(license) {
700
+ await import_promises.default.writeFile(this.path, JSON.stringify(license), { encoding: "utf-8" });
701
+ }
702
+ async deleteLocalLicense() {
703
+ if (!import_node_fs2.default.existsSync(this.path))
704
+ return;
705
+ await import_promises.default.rm(this.path);
706
+ }
707
+ get path() {
708
+ var _a, _b, _c, _d;
709
+ return import_node_path.default.resolve((_b = (_a = this.options) == null ? void 0 : _a.dir) != null ? _b : import_node_path.default.resolve(), (_d = (_c = this.options) == null ? void 0 : _c.licenseFileName) != null ? _d : "license.mb");
710
+ }
711
+ };
712
+
713
+ // src/validator.ts
714
+ var import_jsonwebtoken = __toESM(require("jsonwebtoken"), 1);
715
+ var import_zod2 = require("zod");
716
+ var licenseClaimsSchema = import_zod2.z.object({
717
+ ["l:id" /* LicenseId */]: import_zod2.z.string(),
718
+ ["trial" /* Trial */]: import_zod2.z.string().or(import_zod2.z.boolean()).transform((v) => v === true || v === "true").pipe(import_zod2.z.boolean()),
719
+ ["method" /* ActivationMethod */]: import_zod2.z.nativeEnum(ActivationMethod),
720
+ ["sig" /* ComputerSignature */]: import_zod2.z.string(),
721
+ ["iat" /* IssuedAt */]: import_zod2.z.number().transform((v) => new Date(v * 1e3)).pipe(import_zod2.z.date()),
722
+ ["exp" /* ExpiresAt */]: import_zod2.z.number().nullish().transform((v) => v ? new Date(v * 1e3) : void 0).pipe(import_zod2.z.date().nullish()),
723
+ ["validated" /* ValidatedAt */]: import_zod2.z.number().transform((v) => new Date(v * 1e3)).pipe(import_zod2.z.date()),
724
+ ["p:id" /* ProductId */]: import_zod2.z.string(),
725
+ ["p:name" /* ProductName */]: import_zod2.z.string(),
726
+ ["p:rel" /* ProductReleaseVersion */]: import_zod2.z.string().nullish(),
727
+ ["sp:owned" /* OwnedSubProducts */]: import_zod2.z.string().nullish().transform((v) => v ? v.split(",") : []),
728
+ ["s:id" /* SubscriptionId */]: import_zod2.z.string().nullish(),
729
+ ["u:id" /* UserId */]: import_zod2.z.string(),
730
+ ["u:name" /* UserName */]: import_zod2.z.string(),
731
+ ["u:email" /* UserEmail */]: import_zod2.z.string(),
732
+ ["p:properties" /* ProductProperties */]: import_zod2.z.record(import_zod2.z.unknown()).nullish(),
733
+ ["u:properties" /* UserProperties */]: import_zod2.z.record(import_zod2.z.unknown()).nullish(),
734
+ ["l:properties" /* LicenseProperties */]: import_zod2.z.record(import_zod2.z.unknown()).nullish(),
735
+ ["t:properties" /* TrialProperties */]: import_zod2.z.record(import_zod2.z.unknown()).nullish()
736
+ });
737
+ var LicenseValidator = class {
738
+ constructor(configuration, deviceIdResolver) {
739
+ this.configuration = configuration;
740
+ this.deviceIdResolver = deviceIdResolver;
741
+ }
742
+ async validateLicense(token) {
743
+ const [license, claims] = await this.parseLicenseToken(token);
744
+ if (license.expiresAt && license.expiresAt < /* @__PURE__ */ new Date()) {
745
+ throw new MoonbaseError(
746
+ "License expired",
747
+ "This license has expired",
748
+ "LicenseExpired" /* LicenseExpired */
749
+ );
750
+ }
751
+ const expectedSignature = await this.deviceIdResolver.resolveDeviceId();
752
+ const boundSignature = claims["sig" /* ComputerSignature */];
753
+ if (expectedSignature !== boundSignature && !await this.acceptsHistoricalDeviceId(boundSignature))
754
+ throw deviceMismatchError(expectedSignature, boundSignature);
755
+ return license;
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
+ }
766
+ parseLicenseToken(token) {
767
+ return new Promise((resolve, reject) => {
768
+ import_jsonwebtoken.default.verify(token, this.configuration.publicKey, {
769
+ ignoreExpiration: true,
770
+ issuer: this.configuration.accountId,
771
+ audience: this.configuration.productId
772
+ }, (err, decoded) => {
773
+ var _a, _b, _c, _d, _e;
774
+ if (err) {
775
+ reject(new MoonbaseError(
776
+ "Could not validate license",
777
+ err.message,
778
+ "LicenseInvalid" /* LicenseInvalid */,
779
+ err
780
+ ));
781
+ return;
782
+ }
783
+ if (typeof decoded === "string") {
784
+ reject(new MoonbaseError(
785
+ "Could not validate license",
786
+ "The given license token could not be parsed",
787
+ "LicenseInvalid" /* LicenseInvalid */
788
+ ));
789
+ return;
790
+ }
791
+ try {
792
+ const claims = licenseClaimsSchema.parse(decoded);
793
+ resolve([{
794
+ id: claims["l:id" /* LicenseId */],
795
+ trial: claims["trial" /* Trial */],
796
+ activationMethod: claims["method" /* ActivationMethod */],
797
+ product: {
798
+ id: claims["p:id" /* ProductId */],
799
+ name: claims["p:name" /* ProductName */],
800
+ currentReleaseVersion: (_a = claims["p:rel" /* ProductReleaseVersion */]) != null ? _a : void 0,
801
+ properties: (_b = claims["p:properties" /* ProductProperties */]) != null ? _b : void 0
802
+ },
803
+ ownedSubProductIds: claims["sp:owned" /* OwnedSubProducts */],
804
+ subscriptionId: (_c = claims["s:id" /* SubscriptionId */]) != null ? _c : void 0,
805
+ issuedTo: {
806
+ id: claims["u:id" /* UserId */],
807
+ name: claims["u:name" /* UserName */],
808
+ email: claims["u:email" /* UserEmail */],
809
+ properties: (_d = claims["u:properties" /* UserProperties */]) != null ? _d : void 0
810
+ },
811
+ expiresAt: claims["exp" /* ExpiresAt */] || void 0,
812
+ issuedAt: claims["iat" /* IssuedAt */],
813
+ validatedAt: claims["validated" /* ValidatedAt */],
814
+ properties: (_e = claims["trial" /* Trial */] ? claims["t:properties" /* TrialProperties */] : claims["l:properties" /* LicenseProperties */]) != null ? _e : void 0,
815
+ token
816
+ }, claims]);
817
+ } catch (err2) {
818
+ const error = err2;
819
+ reject(new MoonbaseError(
820
+ "Could not validate license",
821
+ error.message,
822
+ "LicenseInvalid" /* LicenseInvalid */,
823
+ error
824
+ ));
825
+ }
826
+ });
827
+ });
828
+ }
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
+ }
852
+
853
+ // src/index.ts
854
+ function detectPlatform() {
855
+ if (typeof import_node_process3.default === "undefined")
856
+ return void 0;
857
+ switch (import_node_process3.default.platform) {
858
+ case "darwin":
859
+ return "Mac";
860
+ case "win32":
861
+ return "Windows";
862
+ case "linux":
863
+ return "Linux";
864
+ default:
865
+ return void 0;
866
+ }
867
+ }
868
+ var MoonbaseLicensing = class {
869
+ constructor(configuration) {
870
+ var _a, _b;
871
+ this.configuration = {
872
+ ...configuration,
873
+ endpoint: configuration.endpoint.replace(/\/$/, ""),
874
+ platform: configuration.platform === void 0 ? detectPlatform() : configuration.platform
875
+ };
876
+ this.store = (_a = configuration.licenseStore) != null ? _a : new InMemoryLicenseStore();
877
+ this.deviceIdResolver = (_b = configuration.deviceIdResolver) != null ? _b : new MoonbaseDeviceIdResolver();
878
+ this.validator = new LicenseValidator(this.configuration, this.deviceIdResolver);
879
+ this.client = new LicenseClient(this.configuration, this.deviceIdResolver, this.validator);
880
+ }
881
+ async generateDeviceToken() {
882
+ const token = {
883
+ id: await this.deviceIdResolver.resolveDeviceId(),
884
+ name: await this.deviceIdResolver.resolveDeviceName(),
885
+ productId: this.configuration.productId,
886
+ format: "JWT"
887
+ };
888
+ const json = JSON.stringify(token);
889
+ return import_node_buffer2.Buffer.from(import_node_buffer2.Buffer.from(json).toString("base64"));
890
+ }
891
+ async readRawLicense(license) {
892
+ return await this.client.validateRawLicense(license);
893
+ }
894
+ };
895
+ // Annotate the CommonJS export names for ESM import in node:
896
+ 0 && (module.exports = {
897
+ ActivationMethod,
898
+ ErrorType,
899
+ FINGERPRINT_PREFIX,
900
+ FINGERPRINT_VERSION,
901
+ FileLicenseStore,
902
+ IDENTIFYING_PARAM_NAMES,
903
+ InMemoryLicenseStore,
904
+ InsufficientDeviceIdentityError,
905
+ LegacyDeviceIdResolver,
906
+ LicenseClient,
907
+ LicenseValidator,
908
+ MAX_VALUE_LENGTH,
909
+ MigratingDeviceIdResolver,
910
+ MoonbaseDeviceIdResolver,
911
+ MoonbaseError,
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
925
+ });