@gorilla-engine-sdk/ge-product-hub 0.1.4

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.
@@ -0,0 +1,1039 @@
1
+ import { ActivateProductParams, ActivateProductResponse, ActivationRestoreResult, StoredSessionActivationResult, ActivateTrialResponse, APIClientOptions, AudioPluginClientOptions, AudioPluginActivationParams, AudioPluginActivationWithSerialParams, AudioPluginClaimAndActivateParams, AudioPluginStoredSessionSerialParams, ActivationStatus, StoredSessionInfo, OfflineActivationFileOptions, LicenseType, LatestProductVersionResponse, BuildFile } from './types';
2
+ /**
3
+ * @internal
4
+ * Fixed signature seed version used for cryptographic verification.
5
+ */
6
+ export declare const SIGNATURE_SEED_VERSION = 6;
7
+ /**
8
+ * @internal
9
+ * Hardware fingerprint version for system identification.
10
+ */
11
+ export declare const HARDWARE_FINGERPRINT_VERSION = 2;
12
+ /**
13
+ * Gorilla Engine Product Hub Client Library
14
+ *
15
+ * Provides comprehensive audio plugin licensing with
16
+ * activation management & trial handling.
17
+ */
18
+ export declare namespace ProductHub {
19
+ /**
20
+ * @internal
21
+ * Low-level API client for Product Hub communication.
22
+ * This class is used internally and should not be used directly.
23
+ * Use AudioPluginClient instead for a complete audio plugin licensing solution.
24
+ */
25
+ class APIClient {
26
+ private manufacturerId;
27
+ private productId;
28
+ private hwfpUtil;
29
+ private bearerToken;
30
+ /**
31
+ * Create a low-level Product Hub API client.
32
+ *
33
+ * @param options - Product, manufacturer, and optional fingerprint settings.
34
+ * @throws {@link ProductHubError} If a required identifier is missing, the runtime is older
35
+ * than Node.js 18.3, global `fetch` is unavailable, or fingerprint generation fails.
36
+ * @remarks One stored login session is shared by all products from this manufacturer.
37
+ * `hardwareFingerprint` is intended for tests; production clients should use the Gorilla
38
+ * Engine hardware-fingerprinting binding.
39
+ * @example
40
+ * ```typescript
41
+ * const apiClient = new ProductHub.APIClient({
42
+ * manufacturerId: 'manufacturer-id',
43
+ * productId: 'product-id',
44
+ * });
45
+ * ```
46
+ */
47
+ constructor(options: APIClientOptions);
48
+ private credentialLocation;
49
+ private isTokenUsable;
50
+ private readPersistedToken;
51
+ private loadPersistedToken;
52
+ private persistToken;
53
+ /**
54
+ * Check whether a usable login session is stored for this product and device.
55
+ *
56
+ * @returns A promise resolving to `true` when a usable session exists; otherwise `false`.
57
+ * @remarks Malformed, expired, or wrong-scope stored tokens are removed before returning.
58
+ */
59
+ hasStoredSession(): Promise<boolean>;
60
+ /**
61
+ * Read non-sensitive claims from the active stored login session.
62
+ *
63
+ * @returns The stored session's email, user ID, and expiration, or `null` when no usable
64
+ * session exists. The bearer token is never exposed.
65
+ * @remarks Malformed, expired, or wrong-scope stored tokens are removed before returning.
66
+ */
67
+ getStoredSessionInfo(): Promise<StoredSessionInfo | null>;
68
+ /**
69
+ * Clear the active login session.
70
+ *
71
+ * @returns A promise that resolves after clearing the in-memory token and attempting to
72
+ * remove the persisted credential.
73
+ * @remarks Credential-store deletion is best-effort and failures are intentionally ignored.
74
+ */
75
+ clearStoredSession(): Promise<void>;
76
+ /**
77
+ * Activate the product using the active stored login session.
78
+ *
79
+ * @param serial - Optional reseller serial to claim and activate instead of using Product
80
+ * Hub's normal license selection.
81
+ * @returns The independent session status and the activation response, if one was available.
82
+ * A valid session with no matching activation returns `activation: null`.
83
+ * @throws {@link ProductHubError} If the API cannot be reached or an unexpected API error
84
+ * occurs. Authentication failures are returned as `SessionStatus.Invalid` instead.
85
+ * @remarks
86
+ * This method never performs a credential login. Missing sessions return
87
+ * `SessionStatus.None`; malformed, expired, wrong-scope, or API-rejected sessions return
88
+ * `SessionStatus.Invalid` and are removed. HTTP 404 and 409 responses return a valid session
89
+ * with no activation.
90
+ * @example
91
+ * ```typescript
92
+ * const result = await apiClient.activateProductWithStoredSession();
93
+ * if (result.sessionStatus === SessionStatus.Valid && result.activation) {
94
+ * console.log(result.activation.serial);
95
+ * }
96
+ * ```
97
+ */
98
+ activateProductWithStoredSession(serial?: string): Promise<StoredSessionActivationResult>;
99
+ private validateNodeVersion;
100
+ /**
101
+ * Authenticate a user and make the returned token the active session.
102
+ *
103
+ * @param email - End-user email address.
104
+ * @param password - End-user password.
105
+ * @returns The bearer token returned by Product Hub.
106
+ * @throws {@link NotAuthorizedError} If the credentials are rejected.
107
+ * @throws {@link ProductHubError} If fingerprint generation, networking, response parsing,
108
+ * or another API operation fails.
109
+ * @remarks The token is retained in memory and persisted in the operating system's secure
110
+ * credential store on a best-effort basis.
111
+ * @example
112
+ * ```typescript
113
+ * const token = await apiClient.loginUser('user@example.com', 'secret');
114
+ * ```
115
+ */
116
+ loginUser(email: string, password: string): Promise<string>;
117
+ /**
118
+ * Activate a product with a license
119
+ */
120
+ private activateProductWithToken;
121
+ /**
122
+ * Authenticate with explicit credentials and activate the product.
123
+ *
124
+ * @param params - Credentials and an optional reseller serial to claim and activate.
125
+ * @returns The signed Product Hub activation response.
126
+ * @throws {@link NotAuthorizedError} If login or activation authorization fails.
127
+ * @throws {@link ProductHubError} If fingerprint generation, networking, response parsing,
128
+ * or another API operation fails.
129
+ * @remarks Explicit credentials always replace the active stored session. If activation is
130
+ * rejected as unauthorized after login, the newly stored session is cleared.
131
+ * @example
132
+ * ```typescript
133
+ * const activation = await apiClient.activateProduct({
134
+ * email: 'user@example.com',
135
+ * password: 'secret',
136
+ * serial: 'STD-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
137
+ * });
138
+ * ```
139
+ */
140
+ activateProduct(params: ActivateProductParams): Promise<ActivateProductResponse>;
141
+ /**
142
+ * Activate a product using email and serial only — no login or Bearer token required.
143
+ *
144
+ * @remarks
145
+ * Only works when the manufacturer has "Authentication Type = None" and
146
+ * "Email+Serial Claiming" enabled in Product Hub. The license is claimed
147
+ * (if not claimed so far) and activated in a single API call.
148
+ *
149
+ * @param email - End-user email address used to identify or create the account.
150
+ * @param serial - License serial received after purchase.
151
+ * @returns The signed Product Hub activation response.
152
+ * @throws {@link NotAuthorizedError} If email-and-serial activation is not authorized.
153
+ * @throws {@link ProductHubError} If fingerprint generation, networking, response parsing,
154
+ * or another API operation fails.
155
+ * @example
156
+ * ```typescript
157
+ * const activation = await apiClient.activateProductWithEmailAndSerial(
158
+ * 'user@example.com',
159
+ * 'STD-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
160
+ * );
161
+ * ```
162
+ */
163
+ activateProductWithEmailAndSerial(email: string, serial: string): Promise<ActivateProductResponse>;
164
+ /**
165
+ * Activate a hardware-bound trial for the product.
166
+ *
167
+ * @returns The signed Product Hub trial response.
168
+ * @throws {@link ProductHubError} If fingerprint generation, networking, response parsing,
169
+ * or the trial API request fails.
170
+ * @remarks This low-level method returns API data without verifying or persisting it. Use
171
+ * {@link AudioPluginClient.activateTrial} for the complete plugin workflow.
172
+ * @example
173
+ * ```typescript
174
+ * const trial = await apiClient.activateTrial();
175
+ * console.log(trial.trial_days);
176
+ * ```
177
+ */
178
+ activateTrial(): Promise<ActivateTrialResponse>;
179
+ /**
180
+ * Get the latest product version from the Release distribution channel.
181
+ *
182
+ * @returns The available builds and optional update referral URL for this product.
183
+ * @throws {@link ProductHubError} If the request fails or its response cannot be parsed.
184
+ * @example
185
+ * ```typescript
186
+ * const latest = await apiClient.getLatestProductVersion();
187
+ * console.log(latest.builds);
188
+ * ```
189
+ */
190
+ getLatestProductVersion(): Promise<LatestProductVersionResponse>;
191
+ /**
192
+ * Get downloadable files for a specific product build.
193
+ *
194
+ * @param buildId - Product Hub build identifier.
195
+ * @returns The files attached to the selected build.
196
+ * @throws {@link ProductHubError} If the request fails or its response cannot be parsed.
197
+ * @example
198
+ * ```typescript
199
+ * const files = await apiClient.getBuildFiles('build-id');
200
+ * ```
201
+ */
202
+ getBuildFiles(buildId: string): Promise<BuildFile[]>;
203
+ }
204
+ /**
205
+ * Complete audio plugin licensing client with automatic activation management.
206
+ *
207
+ * This is the main class for integrating Gorilla Engine Product Hub licensing
208
+ * into your audio plugins. It handles everything from user authentication to
209
+ * local file persistence, signature verification, and trial management.
210
+ *
211
+ * ## Key Features
212
+ * - 🎫 **License Activation**: Activate full licenses with user credentials
213
+ * - ⏰ **Trial Management**: Start trials and track remaining time
214
+ * - 💾 **Local Persistence**: Automatic .ops/.lts file management
215
+ * - 🔄 **Subscription Auto-Refresh**: Silently refreshes persisted subscription activations when cached tokens are available
216
+ * - 🔒 **Signature Verification**: Built-in RSA-SHA256 cryptographic verification
217
+ * - 🖥️ **Hardware Fingerprinting**: Automatic system identification when used with Gorilla Engine "Hardware fingerprinting" native module
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * import { ProductHub, ActivationStatus, TrialExpiredError, ProductHubError, NotAuthorizedError } from "@gorilla-engine-sdk/ge-product-hub";
222
+ *
223
+ * const client = new ProductHub.AudioPluginClient({
224
+ * manufacturerId: "7222d546-9c72-43d5-8bd4-1667ce390ea4",
225
+ * productId: "1487a36d-9bc8-4220-ba31-9998d743071f",
226
+ * productName: "Groove Master 3000",
227
+ * productPublicKey: `-----BEGIN PUBLIC KEY-----
228
+ * MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
229
+ * -----END PUBLIC KEY-----`,
230
+ * userResourceBaseDirectory: "/Users/<username>/Library/Application Support/<manufacturername>"
231
+ * });
232
+ *
233
+ * ///////////////////////////////////////////
234
+ * // check whether we can unlock the plugin or need to show the activation prompt
235
+ * if( client.isActivated() ) {
236
+ * // already activated - ready to go!
237
+ * console.log("Plugin ready to use!");
238
+ *
239
+ * // unlock via function defined below
240
+ * unlockPlugin();
241
+ *
242
+ * if( client.getActivationStatus() == ActivationStatus.Trial ) {
243
+ * // if desired: disable non-realtime bouncing when in trial mode
244
+ * GorillaEngine.getPluginNRTB(false);
245
+ * }
246
+ * } else {
247
+ * // not yet activated, neither with a trial nor regular activation
248
+ * showActivationOverlay();
249
+ * }
250
+ *
251
+ *
252
+ *
253
+ * ///////////////////////////////////////////
254
+ * // Handle trial activation
255
+ * try {
256
+ * const success = await client.activateTrial();
257
+ * if( success ) {
258
+ * unlockPlugin();
259
+ * hideActivationOverlay();
260
+ * showMessageBox(`Trial active. ${getRemainingTrialDays(client.remainingTrialTime())} left.`);
261
+ * }
262
+ * } catch( ex ) {
263
+ * if( ex instanceof TrialExpiredError ) {
264
+ * activationOverlay.setMessage("Trial expired. Please activate with license.");
265
+ * } else {
266
+ * activationOverlay.setMessage(`An error occurred: ${(ex as ProductHubError).message}`);
267
+ * }
268
+ * }
269
+ *
270
+ *
271
+ * ///////////////////////////////////////////
272
+ * // Handle activation with license
273
+ * try {
274
+ * const success = await client.activateProduct({
275
+ * email: "user@example.com",
276
+ * password: "secret"
277
+ * });
278
+ *
279
+ * if( success ) {
280
+ * unlockPlugin();
281
+ * hideActivationOverlay();
282
+ * aboutDialog.setSerialCodeForDisplay(client.licenseSerial());
283
+ * }
284
+ * }
285
+ * catch( ex ) {
286
+ * if( ex instanceof NotAuthorizedError ) {
287
+ * activationOverlay.setMessage("Invalid credentials.");
288
+ * } else {
289
+ * activationOverlay.setMessage(`An error occurred: ${(ex as ProductHubError).message}`);
290
+ * }
291
+ * }
292
+ *
293
+ *
294
+ * ///////////////////////////////////////////
295
+ * // assess the activation status of the local installation
296
+ * localActivationState = client.getActivationStatus();
297
+ * switch( localActivationState ) {
298
+ * case ActivationStatus.None:
299
+ * console.log("No successful activation found");
300
+ * break;
301
+ * case ActivationStatus.Trial:
302
+ * console.log("Active trial");
303
+ * break;
304
+ * case ActivationStatus.TrialExpired:
305
+ * console.log("Found a trial activation but the trial period is expired");
306
+ * break;
307
+ * case ActivationStatus.Licensed:
308
+ * console.log(`Plugin is licensed with serial ${client.licenseSerial()}`);
309
+ * const expires = client.licenseExpiration();
310
+ * if (expires) {
311
+ * console.log(`License expires on ${expires.toDateString()}`);
312
+ * }
313
+ * break;
314
+ * case ActivationStatus.SubscriptionExpired:
315
+ * console.log("Subscription license has expired - please renew");
316
+ * break;
317
+ * case ActivationStatus.LicenseExpired:
318
+ * console.log("License has expired - please renew or reactivate");
319
+ * break;
320
+ * default:
321
+ * console.log(`Invalid activation state: ${localActivationState}`)
322
+ * }
323
+ *
324
+ *
325
+ * ///////////////////////////////////////////
326
+ * // unlock audio & MIDI in your plugin
327
+ * function unlockPlugin(trial: boolean = false) {
328
+ * // Note: It is vital to uncheck the following two options in Gorilla Compiler's
329
+ * // "Copy Protection" tab when building your plugin:
330
+ * // 1. MIDI enabled on startup
331
+ * // 2. Audio enabled on startup
332
+ * //
333
+ * // It is better to do it this way round rather than enabling MIDI and audio in Compiler
334
+ * // and disabling it here when isActivated() returns false as the plugin stays unlocked
335
+ * // even if an exception is provoked inside its JavaScript code
336
+ *
337
+ * // Enable MIDI processing
338
+ * GorillaEngine.getPluginMM(true);
339
+ *
340
+ * // Enable audio processing
341
+ * GorillaEngine.getPluginAE(true);
342
+ * }
343
+ *
344
+ *
345
+ * ///////////////////////////////////////////
346
+ * // get remaining trial time in days
347
+ * function getRemainingTrialDays(remainingMs: number) {
348
+ * return Math.floor(remainingMs / (24 * 60 * 60 * 1000));
349
+ * }
350
+ * ```
351
+ */
352
+ class AudioPluginClient {
353
+ private apiClient;
354
+ private manufacturerId;
355
+ private productId;
356
+ private productName;
357
+ private productPublicKey;
358
+ private userResourceBaseDirectory;
359
+ private hwfpUtil;
360
+ private readonly isTestEnvironment;
361
+ private currentVersion;
362
+ private currentBuildNumber;
363
+ private updateCheckResult;
364
+ /**
365
+ * Create a new AudioPluginClient instance.
366
+ *
367
+ * Sets up everything needed for audio plugin licensing including hardware
368
+ * fingerprinting, API communication, and local file management.
369
+ *
370
+ * @remarks
371
+ * The constructor performs two fire-and-forget background checks:
372
+ * - automatic product update check (`checkForUpdates()`)
373
+ * - automatic silent subscription refresh (`checkUpdateSubscription()`) when
374
+ * both a local subscription activation and a stored login token exist
375
+ *
376
+ * These checks are not awaited by construction and may log asynchronous failures.
377
+ *
378
+ * @param options - Configuration options for the client
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * const client = new ProductHub.AudioPluginClient({
383
+ * manufacturerId: "7222d546-9c72-43d5-8bd4-1667ce390ea4",
384
+ * productId: "1487a36d-9bc8-4220-ba31-9998d743071f",
385
+ * productName: "Groove Master 3000",
386
+ * productPublicKey: `-----BEGIN PUBLIC KEY-----
387
+ * MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
388
+ * -----END PUBLIC KEY-----`,
389
+ * userResourceBaseDirectory: "/Users/<username>/Library/Application Support/<manufacturername>"
390
+ * });
391
+ * ```
392
+ *
393
+ * @example Testing with fingerprint override
394
+ * ```typescript
395
+ * // For automated testing - never use in production!
396
+ * const testClient = new ProductHub.AudioPluginClient({
397
+ * // ... other options
398
+ * hardwareFingerprint: "test-fingerprint-12345"
399
+ * });
400
+ * ```
401
+ *
402
+ * @throws {@link ProductHubError} If required parameters are missing
403
+ */
404
+ constructor(options: AudioPluginClientOptions);
405
+ /**
406
+ * Check and silently refresh persisted subscription activations when possible.
407
+ *
408
+ * If a local activation exists and is a subscription, and a login token is available
409
+ * in the configured credential backend, this method calls {@link updateSubscription}.
410
+ *
411
+ * @returns Promise that resolves to `true` if a newer subscription activation was persisted
412
+ * @remarks Returns `false` unless a local subscription and usable stored session both exist.
413
+ * A successful refresh may overwrite the local activation with a later expiration.
414
+ * @example
415
+ * ```typescript
416
+ * const refreshed = await client.checkUpdateSubscription();
417
+ * ```
418
+ */
419
+ checkUpdateSubscription(): Promise<boolean>;
420
+ /**
421
+ * Refresh an existing persisted subscription activation from the Product Hub API.
422
+ *
423
+ * This method uses the token persisted in the configured credential backend to call product
424
+ * activation without interactive credentials. If the retrieved subscription has a later
425
+ * expiration timestamp than the currently persisted subscription activation, the local
426
+ * activation file is overwritten.
427
+ *
428
+ * @returns Promise that resolves to `true` if local subscription activation was updated
429
+ * @remarks Only an existing subscription can be updated, and only when the verified remote
430
+ * activation expires later. Non-applicable cases and operational failures return `false`.
431
+ * @example
432
+ * ```typescript
433
+ * const updated = await client.updateSubscription();
434
+ * ```
435
+ */
436
+ updateSubscription(): Promise<boolean>;
437
+ /**
438
+ * Activate your audio plugin with a full license using user credentials.
439
+ *
440
+ * This method authenticates the user, activates the product, verifies the activation
441
+ * signature, and automatically persists the activation data to local .ops files
442
+ * for future offline validation.
443
+ *
444
+ * @param params - User credentials for activation
445
+ * @returns Promise that resolves to `true` if activation was successful, `false` for network/server errors
446
+ * @throws {NotAuthorizedError} When email/password credentials are invalid
447
+ *
448
+ * @example
449
+ * ```typescript
450
+ * try {
451
+ * const success = await client.activateProduct({
452
+ * email: "user@example.com",
453
+ * password: "userPassword123"
454
+ * });
455
+ *
456
+ * if (success) {
457
+ * console.log("License activated successfully!");
458
+ * console.log("Serial:", client.licenseSerial());
459
+ * } else {
460
+ * console.log("Activation failed, please retry");
461
+ * }
462
+ * } catch (error) {
463
+ * if (error instanceof NotAuthorizedError) {
464
+ * console.log("Invalid email or password");
465
+ * }
466
+ * }
467
+ * ```
468
+ *
469
+ * @remarks
470
+ * - Hardware fingerprint is detected automatically
471
+ * - Activation data is cryptographically verified before persistence
472
+ * - Safe to call multiple times - won't create duplicate activations
473
+ * - Authentication errors (wrong credentials) throw `NotAuthorizedError`
474
+ * - Network/server errors, missing licenses etc. return `false`
475
+ */
476
+ activateProduct(params: AudioPluginActivationParams): Promise<boolean>;
477
+ /**
478
+ * Activate the product using only email and serial — no password or login required.
479
+ *
480
+ * The license is claimed and activated in a single call. Use this flow when the
481
+ * manufacturer has "Authentication Type = None" and "Email+Serial Claiming" enabled
482
+ * in Product Hub (i.e. no SSO provider is configured).
483
+ *
484
+ * @param params - Email and serial received from purchase
485
+ * @returns Promise that resolves to `true` if activation was successful, `false` otherwise
486
+ * @throws {NotAuthorizedError} When email+serial claiming is not enabled for this manufacturer, or the serial is not found
487
+ *
488
+ * @example
489
+ * ```typescript
490
+ * try {
491
+ * const success = await client.activateProductWithSerial({
492
+ * email: 'user@example.com',
493
+ * serial: 'STD-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
494
+ * });
495
+ * if (success) {
496
+ * unlockPlugin();
497
+ * aboutDialog.setSerialCodeForDisplay(client.licenseSerial());
498
+ * }
499
+ * } catch (ex) {
500
+ * if (ex instanceof NotAuthorizedError) {
501
+ * activationOverlay.setMessage('Unable to activate. Check your email and serial code.');
502
+ * }
503
+ * }
504
+ * ```
505
+ */
506
+ activateProductWithSerial(params: AudioPluginActivationWithSerialParams): Promise<boolean>;
507
+ /**
508
+ * Claim a reseller-purchased serial to the user's account and activate in one call.
509
+ *
510
+ * Use this when a user already has an account (SSO login) but bought a license
511
+ * serial from a reseller shop. The serial is claimed to their account and then
512
+ * activated, all in a single operation.
513
+ *
514
+ * This method always logs in with the supplied credentials. To claim a serial using
515
+ * the active stored session, call {@link claimAndActivateLicenseWithStoredSession}.
516
+ *
517
+ * @param params - User credentials and reseller serial
518
+ * @returns Promise that resolves to `true` if activation was successful, `false` otherwise
519
+ * @throws {@link NotAuthorizedError} When the supplied credentials are invalid.
520
+ *
521
+ * @example
522
+ * ```typescript
523
+ * try {
524
+ * const success = await client.claimAndActivateLicenseWithSerial({
525
+ * email: 'user@example.com',
526
+ * password: 'secret',
527
+ * serial: 'STD-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
528
+ * });
529
+ * if (success) {
530
+ * unlockPlugin();
531
+ * aboutDialog.setSerialCodeForDisplay(client.licenseSerial());
532
+ * }
533
+ * } catch (ex) {
534
+ * if (ex instanceof NotAuthorizedError) {
535
+ * activationOverlay.setMessage('Invalid credentials or serial already owned by another account.');
536
+ * }
537
+ * }
538
+ * ```
539
+ *
540
+ * @remarks A verified activation is persisted locally. Missing, disabled, or conflicting
541
+ * serials and other non-authentication failures return `false`.
542
+ */
543
+ claimAndActivateLicenseWithSerial(params: AudioPluginClaimAndActivateParams): Promise<boolean>;
544
+ /**
545
+ * Claim a reseller serial and activate it using the active stored session.
546
+ *
547
+ * @param params - Reseller serial to claim and activate.
548
+ * @returns `true` when a signed activation is verified and persisted; otherwise `false`.
549
+ * @remarks
550
+ * This method never performs a credential login. Missing or invalid sessions, serial
551
+ * conflicts, network failures, and invalid activation signatures are converted to `false`.
552
+ * Use {@link restoreActivationFromStoredSession} when the caller needs independent session
553
+ * and activation statuses.
554
+ * @example
555
+ * ```typescript
556
+ * const activated = await client.claimAndActivateLicenseWithStoredSession({
557
+ * serial: 'STD-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX',
558
+ * });
559
+ * ```
560
+ */
561
+ claimAndActivateLicenseWithStoredSession(params: AudioPluginStoredSessionSerialParams): Promise<boolean>;
562
+ /**
563
+ * Check whether a usable login session is stored for this product and device.
564
+ *
565
+ * @returns A promise resolving to `true` when a usable session exists; otherwise `false`.
566
+ * @remarks Invalid stored tokens are removed by the underlying credential-store check.
567
+ */
568
+ hasStoredSession(): Promise<boolean>;
569
+ /**
570
+ * Read non-sensitive claims from the active stored login session.
571
+ *
572
+ * @returns The stored session's email, user ID, and expiration, or `null` when no usable
573
+ * session exists. The bearer token is never exposed.
574
+ * @example
575
+ * ```typescript
576
+ * const session = await client.getStoredSessionInfo();
577
+ * if (session) console.log(session.email, session.expiresAt);
578
+ * ```
579
+ */
580
+ getStoredSessionInfo(): Promise<StoredSessionInfo | null>;
581
+ /**
582
+ * Clear the active stored login session.
583
+ *
584
+ * @returns A promise that resolves after the in-memory token and persisted credential have
585
+ * been cleared on a best-effort basis.
586
+ * @remarks This does not remove the locally persisted `.ops` activation file.
587
+ * @example
588
+ * ```typescript
589
+ * await client.clearStoredSession();
590
+ * ```
591
+ */
592
+ clearStoredSession(): Promise<void>;
593
+ /**
594
+ * Restore product activation using the active stored session.
595
+ *
596
+ * @returns Independent session validity and local product activation statuses.
597
+ * @throws {@link ProductHubError} If the API request fails unexpectedly or the returned
598
+ * activation has an invalid signature.
599
+ * @remarks
600
+ * Session validity and product activation are independent: a valid session can return an
601
+ * unlicensed or expired activation status. Valid signed activations are persisted locally;
602
+ * invalid sessions are removed by the underlying API client.
603
+ * @example
604
+ * ```typescript
605
+ * const result = await client.restoreActivationFromStoredSession();
606
+ * if (
607
+ * result.activationStatus === ActivationStatus.Licensed ||
608
+ * result.activationStatus === ActivationStatus.Subscription
609
+ * ) {
610
+ * unlockPlugin();
611
+ * }
612
+ * ```
613
+ */
614
+ restoreActivationFromStoredSession(): Promise<ActivationRestoreResult>;
615
+ /**
616
+ * Activate a trial period for your audio plugin.
617
+ *
618
+ * Starts a new trial or reuses an existing valid trial activation.
619
+ * No user authentication is required - trials are tied to the hardware fingerprint.
620
+ * The trial data is automatically verified and persisted to local .ops files.
621
+ *
622
+ * @returns Promise that resolves to `true` if trial activation was successful, `false` otherwise
623
+ * @throws {TrialExpiredError} When a trial was already used on this machine and has expired
624
+ *
625
+ * @example
626
+ * ```typescript
627
+ * const success = await client.activateTrial();
628
+ *
629
+ * if (success) {
630
+ * const remainingMs = client.remainingTrialTime();
631
+ * const days = Math.floor(remainingMs / (24 * 60 * 60 * 1000));
632
+ * console.log(`Trial activated! ${days} days remaining.`);
633
+ * } else {
634
+ * console.log("Trial activation failed");
635
+ * }
636
+ * ```
637
+ *
638
+ * @remarks
639
+ * - Hardware fingerprint prevents trial abuse across different machines
640
+ * - Trial period is determined by Product Hub configuration
641
+ * - Automatically updates last launch time
642
+ * - Expired trials throw `TrialExpiredError`
643
+ * - Safe to call multiple times - reuses existing valid trials
644
+ */
645
+ activateTrial(): Promise<boolean>;
646
+ /**
647
+ * Check if the plugin is activated with a full license.
648
+ *
649
+ * Verifies that a valid license activation exists locally, including
650
+ * signature verification and hardware fingerprint validation.
651
+ *
652
+ * @returns `true` if the plugin has a valid full license, `false` otherwise
653
+ *
654
+ * @example
655
+ * ```typescript
656
+ * if (client.isActivatedWithLicense()) {
657
+ * console.log("Full license active!");
658
+ * console.log("License serial:", client.licenseSerial());
659
+ * } else {
660
+ * console.log("No license - maybe try activation or trial");
661
+ * }
662
+ * ```
663
+ *
664
+ * @remarks
665
+ * - Validates hardware fingerprint to prevent license transfer
666
+ * - Safe to call frequently - uses cached local data
667
+ */
668
+ isActivatedWithLicense(): boolean;
669
+ /**
670
+ * Get the remaining trial time in milliseconds.
671
+ *
672
+ * Calculates how much time is left in the current trial period, including
673
+ * signature verification and hardware fingerprint validation. Returns 0
674
+ * if no valid trial exists or if the trial has expired.
675
+ *
676
+ * @returns Remaining trial time in milliseconds, or 0 if no valid trial
677
+ *
678
+ * @example
679
+ * ```typescript
680
+ * const remainingMs = client.remainingTrialTime();
681
+ *
682
+ * if (remainingMs > 0) {
683
+ * const days = Math.floor(remainingMs / (24 * 60 * 60 * 1000));
684
+ * const hours = Math.floor((remainingMs % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));
685
+ * console.log(`Trial: ${days} days, ${hours} hours remaining`);
686
+ * } else {
687
+ * console.log("No active trial");
688
+ * }
689
+ * ```
690
+ *
691
+ * @remarks
692
+ * - Automatically updates last launch time when called
693
+ * - Performs cryptographic verification of trial data
694
+ * - Returns exact milliseconds for precise timing calculations
695
+ */
696
+ remainingTrialTime(): number;
697
+ /**
698
+ * Check if the plugin is activated and ready to use.
699
+ *
700
+ * Returns `true` if the plugin has either a valid full license OR a valid trial
701
+ * with remaining time. This is the primary method to check if your plugin
702
+ * should be functional.
703
+ *
704
+ * @returns `true` if plugin is activated (licensed or trial), `false` otherwise
705
+ *
706
+ * @example
707
+ * ```typescript
708
+ * // Basic usage
709
+ * if (client.isActivated()) {
710
+ * // Enable all plugin features
711
+ * enableAudioProcessing();
712
+ * } else {
713
+ * // Show activation dialog or disable features
714
+ * showActivationDialog();
715
+ * }
716
+ *
717
+ * // Advanced usage with status details
718
+ * if (client.isActivated()) {
719
+ * if (client.isActivatedWithLicense()) {
720
+ * console.log("Full license - all features activated");
721
+ * } else {
722
+ * const days = Math.floor(client.remainingTrialTime() / (24 * 60 * 60 * 1000));
723
+ * console.log(`Trial mode - ${days} days remaining`);
724
+ * }
725
+ * }
726
+ * ```
727
+ *
728
+ * @remarks
729
+ * - This is typically the main method you'll use in your plugin logic
730
+ * - Combines both license and trial validation
731
+ * - Efficient - uses cached data where possible
732
+ */
733
+ isActivated(): boolean;
734
+ /**
735
+ * Get detailed activation status information.
736
+ *
737
+ * Provides more granular information about the current activation state
738
+ * than the simple boolean methods. Useful for showing specific UI states
739
+ * or handling different scenarios in your plugin.
740
+ *
741
+ * @returns Current activation status as an enum value
742
+ *
743
+ * @example
744
+ * ```typescript
745
+ * import { ActivationStatus } from "@gorilla-engine-sdk/ge-product-hub";
746
+ *
747
+ * switch (client.getActivationStatus()) {
748
+ * case ActivationStatus.Licensed:
749
+ * showLicensedUI();
750
+ * console.log("Serial:", client.licenseSerial());
751
+ * break;
752
+ *
753
+ * case ActivationStatus.Trial:
754
+ * const days = Math.floor(client.remainingTrialTime() / (24 * 60 * 60 * 1000));
755
+ * showTrialUI(days);
756
+ * break;
757
+ *
758
+ * case ActivationStatus.TrialExpired:
759
+ * showTrialExpiredUI();
760
+ * break;
761
+ *
762
+ * case ActivationStatus.None:
763
+ * case ActivationStatus.LicenseExpired:
764
+ * case ActivationStatus.SubscriptionExpired:
765
+ * showActivationRequiredUI();
766
+ * break;
767
+ * }
768
+ * ```
769
+ *
770
+ * @see {@link ActivationStatus} for all possible status values
771
+ *
772
+ * @remarks
773
+ * - More informative than `isActivated()` or `isActivatedWithLicense()`
774
+ * - Helps tailor user experience based on exact activation state
775
+ * - Always returns a valid enum value
776
+ * - Note that this is the local activation status. This is especially important in regard to trials as there can be a possible edge case.
777
+ If the user trialed the product in the past and then later deleted the installation and they now want to trial again, the local activation state would be `None` and
778
+ `activateTrial()` would throw `TrialExpiredError` with the local activation state staying `None`. If on the other hand the installation is not deleted so the activation
779
+ file still exists, the local activation state would be `TrialExpired`.
780
+ */
781
+ getActivationStatus(): ActivationStatus;
782
+ /**
783
+ * Get the license serial number if the plugin is fully activated.
784
+ *
785
+ * Returns the unique serial number associated with the current license
786
+ * activation. Useful for support purposes, logging, or displaying
787
+ * license information to users.
788
+ *
789
+ * @returns License serial number if licensed, empty string otherwise
790
+ *
791
+ * @example
792
+ * ```typescript
793
+ * const serial = client.licenseSerial();
794
+ *
795
+ * if (serial) {
796
+ * console.log(`Licensed version - Serial: ${serial}`);
797
+ * // Maybe show in about dialog
798
+ * showAboutDialog({ serial });
799
+ * } else {
800
+ * console.log("Not licensed");
801
+ * }
802
+ * ```
803
+ *
804
+ * @remarks
805
+ * - Only returns a value when `isActivatedWithLicense()` is `true`
806
+ * - Serial numbers are unique per license activation
807
+ * - Useful for customer support and license tracking
808
+ */
809
+ licenseSerial(): string;
810
+ /**
811
+ * Get the type of the current license.
812
+ *
813
+ * Returns the license type (Standard, NFR, Free, or Subscription) if the
814
+ * plugin is activated with a valid license. Returns null for trial activations
815
+ * or when no valid license exists.
816
+ *
817
+ * @returns License type if activated with license, null otherwise
818
+ *
819
+ * @example
820
+ * ```typescript
821
+ * const type = client.licenseType();
822
+ * if (type) {
823
+ * console.log(`License type: ${type}`);
824
+ * if (type === LicenseType.Subscription) {
825
+ * console.log("Subscription license - check expiration");
826
+ * }
827
+ * }
828
+ * ```
829
+ *
830
+ * @remarks
831
+ * - Returns null for trials (use `getActivationStatus()` to check for trials)
832
+ * - Useful for displaying license information to users
833
+ * - Only meaningful when `isActivatedWithLicense()` is `true`
834
+ */
835
+ licenseType(): LicenseType | null;
836
+ /**
837
+ * Check if the current license is time-limited (subscription or expiring).
838
+ *
839
+ * Returns `true` if the persisted license activation has an expiration date.
840
+ *
841
+ * @returns `true` if license is time-limited, `false` if perpetual, trial or no license
842
+ *
843
+ * @example
844
+ * ```typescript
845
+ * if (client.isTimeLimitedLicense()) {
846
+ * const expires = client.licenseExpiration();
847
+ * if (expires) {
848
+ * console.log(`License expires on ${expires.toDateString()}`);
849
+ * }
850
+ * } else {
851
+ * console.log("Perpetual license");
852
+ * }
853
+ * ```
854
+ *
855
+ * @remarks
856
+ * - This method does not verify the persisted activation or check whether it has expired
857
+ * - Helps determine if renewal reminders are needed
858
+ * - Time-limited licenses have an `expires` date in the activation data
859
+ */
860
+ isTimeLimitedLicense(): boolean;
861
+ /**
862
+ * Get the expiration date from the persisted license activation.
863
+ *
864
+ * Returns the persisted expiration date, or null if the license is perpetual
865
+ * (never expires), no license activation exists, or the activation is a trial.
866
+ * For trials, use `remainingTrialTime()` to check remaining time.
867
+ *
868
+ * @returns Expiration date if licensed and has expiration, null otherwise (even for active trials)
869
+ *
870
+ * @example
871
+ * ```typescript
872
+ * const expires = client.licenseExpiration();
873
+ * if (expires) {
874
+ * const daysLeft = Math.ceil((expires.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
875
+ * console.log(`License expires in ${daysLeft} days`);
876
+ * } else {
877
+ * console.log("Perpetual license");
878
+ * }
879
+ * ```
880
+ *
881
+ * @remarks This method does not verify the persisted activation or check whether the date
882
+ * has passed. Use {@link getActivationStatus} to determine current validity.
883
+ */
884
+ licenseExpiration(): Date | null;
885
+ /**
886
+ * Write an offline activation file for support purposes.
887
+ *
888
+ * This method creates an offline activation file (`.ops`) containing your product's hardware
889
+ * fingerprint and configuration details. This is useful when end users don't have internet
890
+ * access - they can send you this file to obtain their system information, allowing you to
891
+ * create a manual activation in the Product Hub backend and provide them with a signed
892
+ * activation file for offline use.
893
+ *
894
+ * @param options - Configuration options for the offline activation file
895
+ * @returns Nothing. Writes `{productName}.ops`, or leaves an existing file untouched when
896
+ * `overwriteExisting` is `false`.
897
+ * @throws {@link ProductHubError} If hardware fingerprint generation fails.
898
+ * @throws {@link Error} If the destination cannot be created or written.
899
+ *
900
+ * @example
901
+ * ```typescript
902
+ * // Write to default location without overwriting
903
+ * client.writeOfflineActivationFile();
904
+ *
905
+ * // Write to custom location with overwrite enabled
906
+ * client.writeOfflineActivationFile({
907
+ * customFolderPath: "/Users/username/Desktop",
908
+ * overwriteExisting: true
909
+ * });
910
+ *
911
+ * // Just enable overwriting in default location
912
+ * client.writeOfflineActivationFile({ overwriteExisting: true });
913
+ * ```
914
+ *
915
+ * @remarks
916
+ * - The file contains hardware fingerprint, product ID, and signature versions
917
+ * - File is named `{productName}.ops` (e.g., "MyPlugin.ops")
918
+ * - Safe to call multiple times - respects `overwriteExisting` option
919
+ * - Automatically creates necessary directories
920
+ * - For offline activation workflow with customer support
921
+ */
922
+ writeOfflineActivationFile(options?: OfflineActivationFileOptions): void;
923
+ /**
924
+ * Verify activation signature (v6 format only)
925
+ */
926
+ private verifyActivation;
927
+ /**
928
+ * Verify trial signature
929
+ */
930
+ private verifyTrial;
931
+ private opsFileFolderPath;
932
+ private opsFilePath;
933
+ private ltsFilePath;
934
+ private persistActivationOnDisk;
935
+ private persistTrialActivationOnDisk;
936
+ private loadActivationFromDisk;
937
+ private loadTrialActivationFromDisk;
938
+ private updateLastTrialLaunchTime;
939
+ private readLastTrialLaunchTime;
940
+ /**
941
+ * Check for product updates from the Product Hub API.
942
+ *
943
+ * This method queries the latest version available in the Release distribution channel
944
+ * and compares it with the currently installed version. Results are cached and can
945
+ * be accessed via `isUpdateAvailable()`, `getUpdateReferralURL()`, and `getUpdateFiles()`.
946
+ *
947
+ * The method is automatically called once during construction but can be called again
948
+ * to re-check for updates (e.g., if the initial check failed due to network issues).
949
+ *
950
+ * @returns Promise that resolves to `true` if the check succeeded, `false` if it failed
951
+ *
952
+ * @example
953
+ * ```typescript
954
+ * // Manual re-check for updates
955
+ * const success = await client.checkForUpdates();
956
+ * if (success && client.isUpdateAvailable()) {
957
+ * console.log("Update available!");
958
+ * }
959
+ * ```
960
+ */
961
+ checkForUpdates(): Promise<boolean>;
962
+ /**
963
+ * Check if an update is available for this product.
964
+ *
965
+ * @returns `true` if an update is available, `false` otherwise
966
+ *
967
+ * @remarks
968
+ * Returns `false` if the update check hasn't been performed yet, failed,
969
+ * or if no update is available.
970
+ *
971
+ * @example
972
+ * ```typescript
973
+ * if (client.isUpdateAvailable()) {
974
+ * console.log("New version available!");
975
+ * console.log("Download from:", client.getUpdateReferralURL());
976
+ * }
977
+ * ```
978
+ */
979
+ isUpdateAvailable(): boolean;
980
+ /**
981
+ * Get the update referral URL for this product.
982
+ *
983
+ * This URL can be used to direct users to a download page or update instructions.
984
+ * The URL is configured per-product in the Product Hub web interface.
985
+ *
986
+ * @returns The update referral URL, or `null` if not available or check failed
987
+ *
988
+ * @example
989
+ * ```typescript
990
+ * const url = client.getUpdateReferralURL();
991
+ * if (url) {
992
+ * // Open URL in browser or show to user
993
+ * console.log("Visit:", url);
994
+ * }
995
+ * ```
996
+ */
997
+ getUpdateReferralURL(): string | null;
998
+ /**
999
+ * Get the downloadable files for the latest product update.
1000
+ *
1001
+ * Returns an array of files (installers, packages, etc.) that can be downloaded
1002
+ * to update the product. Files are specific to the current operating system.
1003
+ *
1004
+ * @returns Promise that resolves to an array of build files, or empty array if unavailable
1005
+ *
1006
+ * @remarks
1007
+ * - Returns empty array if update check failed or no update is available
1008
+ * - Files are automatically filtered for the current operating system
1009
+ * - Each file includes: id, name, size, sha256 hash, and download URL
1010
+ *
1011
+ * @example
1012
+ * ```typescript
1013
+ * const files = await client.getUpdateFiles();
1014
+ * for (const file of files) {
1015
+ * console.log(`Download: ${file.name} (${file.size} bytes)`);
1016
+ * console.log(`URL: ${file.url}`);
1017
+ * console.log(`SHA256: ${file.sha256}`);
1018
+ * }
1019
+ * ```
1020
+ */
1021
+ getUpdateFiles(): Promise<BuildFile[]>;
1022
+ /**
1023
+ * Detect the current operating system.
1024
+ * @private
1025
+ */
1026
+ private detectOperatingSystem;
1027
+ /**
1028
+ * Select the appropriate build for the given OS.
1029
+ * Prefers exact OS match over "any".
1030
+ * @private
1031
+ */
1032
+ private selectBuildForOS;
1033
+ /**
1034
+ * Check if the remote build version is newer than the current version.
1035
+ * @private
1036
+ */
1037
+ private isNewerVersion;
1038
+ }
1039
+ }