@licenseseat/js 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![npm version](https://img.shields.io/npm/v/@licenseseat/js.svg)](https://www.npmjs.com/package/@licenseseat/js)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- Official JavaScript/TypeScript SDK for [LicenseSeat](https://licenseseat.com) – the simple, secure licensing platform for apps, games, and plugins.
7
+ The official JavaScript/TypeScript SDK for [LicenseSeat](https://licenseseat.com) – the simple, secure licensing platform for apps, games, and plugins.
8
8
 
9
9
  ---
10
10
 
@@ -43,7 +43,10 @@ pnpm add @licenseseat/js
43
43
  <script type="module">
44
44
  import LicenseSeat from 'https://esm.sh/@licenseseat/js';
45
45
 
46
- const sdk = new LicenseSeat({ apiKey: 'your-api-key' });
46
+ const sdk = new LicenseSeat({
47
+ apiKey: 'your-api-key',
48
+ productSlug: 'your-product'
49
+ });
47
50
  </script>
48
51
 
49
52
  <!-- ESM via unpkg -->
@@ -69,6 +72,7 @@ import LicenseSeat from '@licenseseat/js';
69
72
  // Create SDK instance
70
73
  const sdk = new LicenseSeat({
71
74
  apiKey: 'your-api-key',
75
+ productSlug: 'your-product', // Required: Your product slug
72
76
  debug: true
73
77
  });
74
78
 
@@ -98,6 +102,7 @@ import LicenseSeat, {
98
102
 
99
103
  const config: LicenseSeatConfig = {
100
104
  apiKey: 'your-api-key',
105
+ productSlug: 'your-product',
101
106
  debug: true
102
107
  };
103
108
 
@@ -117,11 +122,14 @@ TypeScript users get full type support automatically – the package includes ge
117
122
 
118
123
  ```javascript
119
124
  const sdk = new LicenseSeat({
125
+ // Required
126
+ productSlug: 'your-product', // Your product slug from LicenseSeat dashboard
127
+
120
128
  // Required for authenticated operations
121
129
  apiKey: 'your-api-key',
122
130
 
123
131
  // API Configuration
124
- apiBaseUrl: 'https://licenseseat.com/api', // Default
132
+ apiBaseUrl: 'https://licenseseat.com/api/v1', // Default
125
133
 
126
134
  // Storage
127
135
  storagePrefix: 'licenseseat_', // localStorage key prefix
@@ -148,18 +156,19 @@ const sdk = new LicenseSeat({
148
156
 
149
157
  ### Configuration Options
150
158
 
151
- | Option | Type | Default | Description |
152
- | ------------------------ | --------- | ------------------------------- | --------------------------------------------------------- |
153
- | `apiKey` | `string` | `null` | API key for authentication (required for most operations) |
154
- | `apiBaseUrl` | `string` | `'https://licenseseat.com/api'` | API base URL |
155
- | `storagePrefix` | `string` | `'licenseseat_'` | Prefix for localStorage keys |
156
- | `autoValidateInterval` | `number` | `3600000` | Auto-validation interval in ms (1 hour) |
157
- | `autoInitialize` | `boolean` | `true` | Auto-initialize and validate cached license |
158
- | `offlineFallbackEnabled` | `boolean` | `false` | Enable offline validation on network errors |
159
- | `maxOfflineDays` | `number` | `0` | Maximum days license works offline (0 = disabled) |
160
- | `maxRetries` | `number` | `3` | Max retry attempts for failed API calls |
161
- | `retryDelay` | `number` | `1000` | Initial retry delay in ms (exponential backoff) |
162
- | `debug` | `boolean` | `false` | Enable debug logging to console |
159
+ | Option | Type | Default | Description |
160
+ | ------------------------ | --------- | ---------------------------------- | --------------------------------------------------------- |
161
+ | `productSlug` | `string` | | **Required.** Your product slug from the dashboard |
162
+ | `apiKey` | `string` | `null` | API key for authentication (required for most operations) |
163
+ | `apiBaseUrl` | `string` | `'https://licenseseat.com/api/v1'` | API base URL |
164
+ | `storagePrefix` | `string` | `'licenseseat_'` | Prefix for localStorage keys |
165
+ | `autoValidateInterval` | `number` | `3600000` | Auto-validation interval in ms (1 hour) |
166
+ | `autoInitialize` | `boolean` | `true` | Auto-initialize and validate cached license |
167
+ | `offlineFallbackEnabled` | `boolean` | `false` | Enable offline validation on network errors |
168
+ | `maxOfflineDays` | `number` | `0` | Maximum days license works offline (0 = disabled) |
169
+ | `maxRetries` | `number` | `3` | Max retry attempts for failed API calls |
170
+ | `retryDelay` | `number` | `1000` | Initial retry delay in ms (exponential backoff) |
171
+ | `debug` | `boolean` | `false` | Enable debug logging to console |
163
172
 
164
173
  ---
165
174
 
@@ -173,17 +182,24 @@ Activates a license key on this device.
173
182
 
174
183
  ```javascript
175
184
  const result = await sdk.activate('LICENSE-KEY', {
176
- deviceIdentifier: 'custom-device-id', // Optional: auto-generated if not provided
177
- softwareReleaseDate: '2024-01-15', // Optional: for version-aware licensing
178
- metadata: { version: '1.0.0' } // Optional: custom metadata
185
+ deviceId: 'custom-device-id', // Optional: auto-generated if not provided
186
+ deviceName: "John's MacBook Pro", // Optional: human-readable device name
187
+ metadata: { version: '1.0.0' } // Optional: custom metadata
179
188
  });
180
189
 
181
190
  console.log(result);
182
191
  // {
183
192
  // license_key: 'LICENSE-KEY',
184
- // device_identifier: 'web-abc123-xyz',
193
+ // device_id: 'web-abc123',
185
194
  // activated_at: '2024-01-15T10:30:00Z',
186
- // activation: { ... }
195
+ // activation: {
196
+ // object: 'activation',
197
+ // id: 123,
198
+ // device_id: 'web-abc123',
199
+ // license_key: 'LICENSE-KEY',
200
+ // activated_at: '2024-01-15T10:30:00Z',
201
+ // license: { ... }
202
+ // }
187
203
  // }
188
204
  ```
189
205
 
@@ -192,7 +208,13 @@ console.log(result);
192
208
  Deactivates the current license and clears cached data.
193
209
 
194
210
  ```javascript
195
- await sdk.deactivate();
211
+ const result = await sdk.deactivate();
212
+ console.log(result);
213
+ // {
214
+ // object: 'deactivation',
215
+ // activation_id: 123,
216
+ // deactivated_at: '2024-01-15T12:00:00Z'
217
+ // }
196
218
  ```
197
219
 
198
220
  #### `sdk.validateLicense(licenseKey, options?)`
@@ -201,17 +223,26 @@ Validates a license with the server.
201
223
 
202
224
  ```javascript
203
225
  const result = await sdk.validateLicense('LICENSE-KEY', {
204
- deviceIdentifier: 'device-id', // Optional
205
- productSlug: 'my-product' // Optional
226
+ deviceId: 'device-id' // Optional: required for hardware_locked mode
206
227
  });
207
228
 
208
229
  console.log(result);
209
230
  // {
210
231
  // valid: true,
211
- // active_entitlements: [
212
- // { key: 'pro', name: 'Pro Features', expires_at: null },
213
- // { key: 'beta', name: 'Beta Access', expires_at: '2024-12-31T23:59:59Z' }
214
- // ]
232
+ // license: {
233
+ // key: 'LICENSE-KEY',
234
+ // status: 'active',
235
+ // mode: 'hardware_locked',
236
+ // plan_key: 'pro',
237
+ // active_seats: 1,
238
+ // seat_limit: 3,
239
+ // active_entitlements: [
240
+ // { key: 'pro', expires_at: null, metadata: null },
241
+ // { key: 'beta', expires_at: '2024-12-31T23:59:59Z', metadata: null }
242
+ // ],
243
+ // product: { slug: 'your-product', name: 'Your Product' }
244
+ // },
245
+ // active_entitlements: [...]
215
246
  // }
216
247
  ```
217
248
 
@@ -268,7 +299,7 @@ console.log(status);
268
299
  // {
269
300
  // status: 'active',
270
301
  // license: 'LICENSE-KEY',
271
- // device: 'web-abc123-xyz',
302
+ // device: 'web-abc123',
272
303
  // activated_at: '2024-01-15T10:30:00Z',
273
304
  // last_validated: '2024-01-15T11:30:00Z',
274
305
  // entitlements: [...]
@@ -312,6 +343,7 @@ Manually initialize the SDK (only needed if `autoInitialize: false`).
312
343
  ```javascript
313
344
  const sdk = new LicenseSeat({
314
345
  apiKey: 'key',
346
+ productSlug: 'your-product',
315
347
  autoInitialize: false // Don't auto-initialize
316
348
  });
317
349
 
@@ -352,7 +384,7 @@ sdk.off('activation:success', handler);
352
384
  | `activation:error` | Activation failed | `{ licenseKey, error }` |
353
385
  | **Deactivation** | | |
354
386
  | `deactivation:start` | Deactivation started | `CachedLicense` |
355
- | `deactivation:success` | Deactivation succeeded | `Object` |
387
+ | `deactivation:success` | Deactivation succeeded | `DeactivationResponse` |
356
388
  | `deactivation:error` | Deactivation failed | `{ error, license }` |
357
389
  | **Validation** | | |
358
390
  | `validation:start` | Validation started | `{ licenseKey }` |
@@ -368,13 +400,13 @@ sdk.off('activation:success', handler);
368
400
  | **Network** | | |
369
401
  | `network:online` | Network connectivity restored | – |
370
402
  | `network:offline` | Network connectivity lost | `{ error }` |
371
- | **Offline License** | | |
372
- | `offlineLicense:fetching` | Fetching offline license | `{ licenseKey }` |
373
- | `offlineLicense:fetched` | Offline license fetched | `{ licenseKey, data }` |
374
- | `offlineLicense:fetchError` | Offline license fetch failed | `{ licenseKey, error }` |
375
- | `offlineLicense:ready` | Offline assets synced | `{ kid, exp_at }` |
376
- | `offlineLicense:verified` | Offline signature verified | `{ payload }` |
377
- | `offlineLicense:verificationFailed` | Offline signature invalid | `{ payload }` |
403
+ | **Offline Token** | | |
404
+ | `offlineToken:fetching` | Fetching offline token | `{ licenseKey }` |
405
+ | `offlineToken:fetched` | Offline token fetched | `{ licenseKey, data }` |
406
+ | `offlineToken:fetchError` | Offline token fetch failed | `{ licenseKey, error }` |
407
+ | `offlineToken:ready` | Offline assets synced | `{ kid, exp_at }` |
408
+ | `offlineToken:verified` | Offline signature verified | `{ payload }` |
409
+ | `offlineToken:verificationFailed` | Offline signature invalid | `{ payload }` |
378
410
 
379
411
  ---
380
412
 
@@ -386,7 +418,10 @@ For applications that need a shared SDK instance:
386
418
  import { configure, getSharedInstance, resetSharedInstance } from '@licenseseat/js';
387
419
 
388
420
  // Configure once at app startup
389
- configure({ apiKey: 'your-key' });
421
+ configure({
422
+ apiKey: 'your-key',
423
+ productSlug: 'your-product'
424
+ });
390
425
 
391
426
  // Use anywhere in your app
392
427
  const sdk = getSharedInstance();
@@ -400,11 +435,12 @@ resetSharedInstance();
400
435
 
401
436
  ## Offline Support
402
437
 
403
- The SDK supports offline license validation using cryptographically signed offline licenses (Ed25519).
438
+ The SDK supports offline license validation using cryptographically signed offline tokens (Ed25519).
404
439
 
405
440
  ```javascript
406
441
  const sdk = new LicenseSeat({
407
442
  apiKey: 'your-key',
443
+ productSlug: 'your-product',
408
444
  offlineFallbackEnabled: true, // Enable offline fallback
409
445
  maxOfflineDays: 7 // Allow 7 days offline
410
446
  });
@@ -421,11 +457,45 @@ if (result.offline) {
421
457
 
422
458
  ### How Offline Validation Works
423
459
 
424
- 1. On activation, the SDK fetches a signed offline license from the server
425
- 2. The offline license contains the license data + Ed25519 signature
460
+ 1. On activation, the SDK fetches a signed offline token from the server
461
+ 2. The offline token contains:
462
+ - License data (key, plan, entitlements, expiration)
463
+ - Ed25519 signature
464
+ - Canonical JSON for verification
426
465
  3. When offline, the SDK verifies the signature locally
427
466
  4. Clock tamper detection prevents users from bypassing expiration
428
467
 
468
+ ### Offline Token Structure
469
+
470
+ ```javascript
471
+ {
472
+ object: 'offline_token',
473
+ token: {
474
+ schema_version: 1,
475
+ license_key: 'LICENSE-KEY',
476
+ product_slug: 'your-product',
477
+ plan_key: 'pro',
478
+ mode: 'hardware_locked',
479
+ device_id: 'web-abc123',
480
+ iat: 1704067200, // Issued at (Unix timestamp)
481
+ exp: 1706659200, // Expires at (Unix timestamp)
482
+ nbf: 1704067200, // Not before (Unix timestamp)
483
+ license_expires_at: null,
484
+ kid: 'key-id-001',
485
+ entitlements: [
486
+ { key: 'pro', expires_at: null }
487
+ ],
488
+ metadata: {}
489
+ },
490
+ signature: {
491
+ algorithm: 'Ed25519',
492
+ key_id: 'key-id-001',
493
+ value: 'base64url-encoded-signature'
494
+ },
495
+ canonical: '{"entitlements":[...],"exp":...}'
496
+ }
497
+ ```
498
+
429
499
  ---
430
500
 
431
501
  ## Error Handling
@@ -445,9 +515,12 @@ try {
445
515
  } catch (error) {
446
516
  if (error instanceof APIError) {
447
517
  console.log('HTTP Status:', error.status);
448
- console.log('Response:', error.data);
518
+ console.log('Error Code:', error.data?.error?.code);
519
+ console.log('Error Message:', error.data?.error?.message);
449
520
  } else if (error instanceof LicenseError) {
450
521
  console.log('License error:', error.code);
522
+ } else if (error instanceof ConfigurationError) {
523
+ console.log('Config error:', error.message);
451
524
  }
452
525
  }
453
526
  ```
@@ -458,9 +531,33 @@ try {
458
531
  | -------------------- | ---------------------------------------------------- |
459
532
  | `APIError` | HTTP request failures (includes `status` and `data`) |
460
533
  | `LicenseError` | License operation failures (includes `code`) |
461
- | `ConfigurationError` | SDK misconfiguration |
534
+ | `ConfigurationError` | SDK misconfiguration (e.g., missing `productSlug`) |
462
535
  | `CryptoError` | Cryptographic operation failures |
463
536
 
537
+ ### API Error Format
538
+
539
+ API errors follow this structure:
540
+
541
+ ```javascript
542
+ {
543
+ error: {
544
+ code: 'license_not_found', // Machine-readable error code
545
+ message: 'License not found.', // Human-readable message
546
+ details: { ... } // Optional additional details
547
+ }
548
+ }
549
+ ```
550
+
551
+ Common error codes:
552
+ - `unauthorized` - Invalid or missing API key
553
+ - `license_not_found` - License key doesn't exist
554
+ - `license_expired` - License has expired
555
+ - `license_suspended` - License is suspended
556
+ - `license_revoked` - License has been revoked
557
+ - `seat_limit_reached` - No more seats available
558
+ - `device_already_activated` - Device is already activated
559
+ - `activation_not_found` - Activation doesn't exist (for deactivation)
560
+
464
561
  ---
465
562
 
466
563
  ## Browser Support
@@ -480,7 +577,10 @@ Simply import and use:
480
577
  ```javascript
481
578
  import LicenseSeat from '@licenseseat/js';
482
579
 
483
- const sdk = new LicenseSeat({ apiKey: 'your-key' });
580
+ const sdk = new LicenseSeat({
581
+ apiKey: 'your-key',
582
+ productSlug: 'your-product'
583
+ });
484
584
  ```
485
585
 
486
586
  ### For TypeScript Users
@@ -491,7 +591,10 @@ The package includes TypeScript declarations (`.d.ts` files) automatically. No a
491
591
  import LicenseSeat from '@licenseseat/js';
492
592
 
493
593
  // Types are automatically available
494
- const sdk = new LicenseSeat({ apiKey: 'your-key' });
594
+ const sdk = new LicenseSeat({
595
+ apiKey: 'your-key',
596
+ productSlug: 'your-product'
597
+ });
495
598
 
496
599
  // Import specific types if needed
497
600
  import type {
@@ -500,7 +603,10 @@ import type {
500
603
  EntitlementCheckResult,
501
604
  LicenseStatus,
502
605
  Entitlement,
503
- CachedLicense
606
+ CachedLicense,
607
+ ActivationResponse,
608
+ DeactivationResponse,
609
+ OfflineToken
504
610
  } from '@licenseseat/js';
505
611
  ```
506
612
 
@@ -520,6 +626,7 @@ Use ES modules via CDN:
520
626
 
521
627
  const sdk = new LicenseSeat({
522
628
  apiKey: 'your-api-key',
629
+ productSlug: 'your-product',
523
630
  debug: true
524
631
  });
525
632
 
@@ -681,7 +788,7 @@ Once published to npm, the package is automatically available on CDNs:
681
788
  **Version pinning** (recommended for production):
682
789
  ```html
683
790
  <script type="module">
684
- import LicenseSeat from 'https://esm.sh/@licenseseat/js@0.2.0';
791
+ import LicenseSeat from 'https://esm.sh/@licenseseat/js@0.3.0';
685
792
  </script>
686
793
  ```
687
794
 
@@ -711,7 +818,10 @@ This creates `dist/index.global.js`:
711
818
  ```html
712
819
  <script src="/path/to/index.global.js"></script>
713
820
  <script>
714
- const sdk = new LicenseSeat({ apiKey: 'your-key' });
821
+ const sdk = new LicenseSeat({
822
+ apiKey: 'your-key',
823
+ productSlug: 'your-product'
824
+ });
715
825
  </script>
716
826
  ```
717
827
 
@@ -727,22 +837,80 @@ This project follows [Semantic Versioning](https://semver.org/):
727
837
 
728
838
  ---
729
839
 
730
- ## Migration from v0.1.x
840
+ ## Migration from v0.2.x
841
+
842
+ ### Breaking Changes in v0.3.0
843
+
844
+ This version introduces the v1 API with significant changes:
731
845
 
732
- ### Breaking Changes in v0.2.0
846
+ | Change | Before (v0.2.x) | After (v0.3.0) |
847
+ | -------------------------------- | ------------------------------- | ----------------------------------------- |
848
+ | `productSlug` config | Not required | **Required** for all API operations |
849
+ | `apiBaseUrl` default | `https://licenseseat.com/api` | `https://licenseseat.com/api/v1` |
850
+ | `deviceIdentifier` option | `deviceIdentifier` | `deviceId` |
851
+ | `device_identifier` field | `device_identifier` | `device_id` |
852
+ | Deactivation response | Returns full activation object | Returns `{ object, activation_id, deactivated_at }` |
853
+ | `getOfflineLicense()` method | Available | Renamed to `getOfflineToken()` |
854
+ | `getPublicKey()` method | Available | Renamed to `getSigningKey()` |
855
+ | Offline license structure | Legacy format | New token/signature/canonical format |
856
+ | Error format | Various | `{ error: { code, message, details? } }` |
733
857
 
734
- | Change | Before | After | Migration |
735
- | -------------------------------- | ------ | ----------------------------- | --------------------------------------------------------------- |
736
- | `apiBaseUrl` default | `/api` | `https://licenseseat.com/api` | Set `apiBaseUrl` explicitly if using a relative URL |
737
- | `offlineFallbackEnabled` default | `true` | `false` | Set `offlineFallbackEnabled: true` if you need offline fallback |
858
+ ### Migration Steps
738
859
 
739
- ### New Features in v0.2.0
860
+ 1. **Add `productSlug` to configuration:**
861
+ ```javascript
862
+ // Before
863
+ const sdk = new LicenseSeat({ apiKey: 'key' });
740
864
 
741
- - `hasEntitlement(key)` method for simple boolean checks
742
- - `autoInitialize` config option for lazy initialization
743
- - Full TypeScript support with auto-generated `.d.ts` files
744
- - Singleton pattern with `configure()` and `getSharedInstance()`
745
- - New error classes: `LicenseError`, `ConfigurationError`, `CryptoError`
865
+ // After
866
+ const sdk = new LicenseSeat({
867
+ apiKey: 'key',
868
+ productSlug: 'your-product' // Required!
869
+ });
870
+ ```
871
+
872
+ 2. **Update activation options:**
873
+ ```javascript
874
+ // Before
875
+ await sdk.activate('KEY', { deviceIdentifier: 'id' });
876
+
877
+ // After
878
+ await sdk.activate('KEY', { deviceId: 'id' });
879
+ ```
880
+
881
+ 3. **Update response field access:**
882
+ ```javascript
883
+ // Before
884
+ const result = await sdk.activate('KEY');
885
+ console.log(result.device_identifier);
886
+
887
+ // After
888
+ const result = await sdk.activate('KEY');
889
+ console.log(result.device_id);
890
+ ```
891
+
892
+ 4. **Update deactivation handling:**
893
+ ```javascript
894
+ // Before
895
+ const result = await sdk.deactivate();
896
+ console.log(result.license_key);
897
+
898
+ // After
899
+ const result = await sdk.deactivate();
900
+ console.log(result.activation_id);
901
+ console.log(result.deactivated_at);
902
+ ```
903
+
904
+ 5. **Update offline method calls:**
905
+ ```javascript
906
+ // Before
907
+ await sdk.getOfflineLicense(key);
908
+ await sdk.getPublicKey(keyId);
909
+
910
+ // After
911
+ await sdk.getOfflineToken(key);
912
+ await sdk.getSigningKey(keyId);
913
+ ```
746
914
 
747
915
  ---
748
916
 
@@ -760,10 +928,3 @@ MIT License – see [LICENSE](LICENSE) for details.
760
928
  - [GitHub Repository](https://github.com/licenseseat/licenseseat-js)
761
929
  - [npm Package](https://www.npmjs.com/package/@licenseseat/js)
762
930
  - [Report Issues](https://github.com/licenseseat/licenseseat-js/issues)
763
-
764
- ---
765
-
766
- ## Support
767
-
768
- - **Email**: support@licenseseat.com
769
- - **GitHub Issues**: [Report a bug](https://github.com/licenseseat/licenseseat-js/issues/new)