@licenseseat/js 0.2.1 → 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
@@ -1,10 +1,10 @@
1
- # LicenseSeat JavaScript SDK
2
-
3
- Official JavaScript/TypeScript SDK for [LicenseSeat](https://licenseseat.com) – the simple, secure licensing platform for apps, games, and plugins.
1
+ # LicenseSeat - JavaScript SDK
4
2
 
5
3
  [![CI](https://github.com/licenseseat/licenseseat-js/actions/workflows/ci.yml/badge.svg)](https://github.com/licenseseat/licenseseat-js/actions/workflows/ci.yml)
6
4
  [![npm version](https://img.shields.io/npm/v/@licenseseat/js.svg)](https://www.npmjs.com/package/@licenseseat/js)
7
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+
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
 
@@ -339,42 +371,42 @@ sdk.off('activation:success', handler);
339
371
 
340
372
  ### Available Events
341
373
 
342
- | Event | Description | Data |
343
- |-------|-------------|------|
344
- | **Lifecycle** | | |
345
- | `license:loaded` | Cached license loaded on init | `CachedLicense` |
346
- | `sdk:reset` | SDK was reset | – |
347
- | `sdk:destroyed` | SDK was destroyed | – |
348
- | `sdk:error` | General SDK error | `{ message, error? }` |
349
- | **Activation** | | |
350
- | `activation:start` | Activation started | `{ licenseKey, deviceId }` |
351
- | `activation:success` | Activation succeeded | `CachedLicense` |
352
- | `activation:error` | Activation failed | `{ licenseKey, error }` |
353
- | **Deactivation** | | |
354
- | `deactivation:start` | Deactivation started | `CachedLicense` |
355
- | `deactivation:success` | Deactivation succeeded | `Object` |
356
- | `deactivation:error` | Deactivation failed | `{ error, license }` |
357
- | **Validation** | | |
358
- | `validation:start` | Validation started | `{ licenseKey }` |
359
- | `validation:success` | Online validation succeeded | `ValidationResult` |
360
- | `validation:failed` | Validation failed (invalid license) | `ValidationResult` |
361
- | `validation:error` | Validation error (network, etc.) | `{ licenseKey, error }` |
362
- | `validation:offline-success` | Offline validation succeeded | `ValidationResult` |
363
- | `validation:offline-failed` | Offline validation failed | `ValidationResult` |
364
- | `validation:auth-failed` | Auth failed during validation | `{ licenseKey, error, cached }` |
365
- | **Auto-Validation** | | |
366
- | `autovalidation:cycle` | Auto-validation scheduled | `{ nextRunAt: Date }` |
367
- | `autovalidation:stopped` | Auto-validation stopped | – |
368
- | **Network** | | |
369
- | `network:online` | Network connectivity restored | – |
370
- | `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 }` |
374
+ | Event | Description | Data |
375
+ | ----------------------------------- | ----------------------------------- | ------------------------------- |
376
+ | **Lifecycle** | | |
377
+ | `license:loaded` | Cached license loaded on init | `CachedLicense` |
378
+ | `sdk:reset` | SDK was reset | – |
379
+ | `sdk:destroyed` | SDK was destroyed | – |
380
+ | `sdk:error` | General SDK error | `{ message, error? }` |
381
+ | **Activation** | | |
382
+ | `activation:start` | Activation started | `{ licenseKey, deviceId }` |
383
+ | `activation:success` | Activation succeeded | `CachedLicense` |
384
+ | `activation:error` | Activation failed | `{ licenseKey, error }` |
385
+ | **Deactivation** | | |
386
+ | `deactivation:start` | Deactivation started | `CachedLicense` |
387
+ | `deactivation:success` | Deactivation succeeded | `DeactivationResponse` |
388
+ | `deactivation:error` | Deactivation failed | `{ error, license }` |
389
+ | **Validation** | | |
390
+ | `validation:start` | Validation started | `{ licenseKey }` |
391
+ | `validation:success` | Online validation succeeded | `ValidationResult` |
392
+ | `validation:failed` | Validation failed (invalid license) | `ValidationResult` |
393
+ | `validation:error` | Validation error (network, etc.) | `{ licenseKey, error }` |
394
+ | `validation:offline-success` | Offline validation succeeded | `ValidationResult` |
395
+ | `validation:offline-failed` | Offline validation failed | `ValidationResult` |
396
+ | `validation:auth-failed` | Auth failed during validation | `{ licenseKey, error, cached }` |
397
+ | **Auto-Validation** | | |
398
+ | `autovalidation:cycle` | Auto-validation scheduled | `{ nextRunAt: Date }` |
399
+ | `autovalidation:stopped` | Auto-validation stopped | – |
400
+ | **Network** | | |
401
+ | `network:online` | Network connectivity restored | – |
402
+ | `network:offline` | Network connectivity lost | `{ error }` |
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,21 +515,48 @@ 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
  ```
454
527
 
455
528
  ### Error Types
456
529
 
457
- | Error | Description |
458
- |-------|-------------|
459
- | `APIError` | HTTP request failures (includes `status` and `data`) |
460
- | `LicenseError` | License operation failures (includes `code`) |
461
- | `ConfigurationError` | SDK misconfiguration |
462
- | `CryptoError` | Cryptographic operation failures |
530
+ | Error | Description |
531
+ | -------------------- | ---------------------------------------------------- |
532
+ | `APIError` | HTTP request failures (includes `status` and `data`) |
533
+ | `LicenseError` | License operation failures (includes `code`) |
534
+ | `ConfigurationError` | SDK misconfiguration (e.g., missing `productSlug`) |
535
+ | `CryptoError` | Cryptographic operation failures |
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)
463
560
 
464
561
  ---
465
562
 
@@ -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
 
@@ -561,17 +668,17 @@ npm install
561
668
 
562
669
  ### Scripts
563
670
 
564
- | Command | Description |
565
- |---------|-------------|
566
- | `npm run build` | Build JS bundle + TypeScript declarations |
567
- | `npm run build:js` | Build JavaScript bundle only |
568
- | `npm run build:types` | Generate TypeScript declarations |
569
- | `npm run build:iife` | Build global/IIFE bundle |
570
- | `npm run dev` | Watch mode for development |
571
- | `npm test` | Run tests |
572
- | `npm run test:watch` | Run tests in watch mode |
573
- | `npm run test:coverage` | Run tests with coverage report |
574
- | `npm run typecheck` | Type-check without emitting |
671
+ | Command | Description |
672
+ | ----------------------- | ----------------------------------------- |
673
+ | `npm run build` | Build JS bundle + TypeScript declarations |
674
+ | `npm run build:js` | Build JavaScript bundle only |
675
+ | `npm run build:types` | Generate TypeScript declarations |
676
+ | `npm run build:iife` | Build global/IIFE bundle |
677
+ | `npm run dev` | Watch mode for development |
678
+ | `npm test` | Run tests |
679
+ | `npm run test:watch` | Run tests in watch mode |
680
+ | `npm run test:coverage` | Run tests with coverage report |
681
+ | `npm run typecheck` | Type-check without emitting |
575
682
 
576
683
  ### Project Structure
577
684
 
@@ -671,17 +778,17 @@ This ensures:
671
778
 
672
779
  Once published to npm, the package is automatically available on CDNs:
673
780
 
674
- | CDN | URL |
675
- |-----|-----|
676
- | **esm.sh** | `https://esm.sh/@licenseseat/js` |
677
- | **unpkg** | `https://unpkg.com/@licenseseat/js/dist/index.js` |
781
+ | CDN | URL |
782
+ | ------------ | ------------------------------------------------------------ |
783
+ | **esm.sh** | `https://esm.sh/@licenseseat/js` |
784
+ | **unpkg** | `https://unpkg.com/@licenseseat/js/dist/index.js` |
678
785
  | **jsDelivr** | `https://cdn.jsdelivr.net/npm/@licenseseat/js/dist/index.js` |
679
- | **Skypack** | `https://cdn.skypack.dev/@licenseseat/js` |
786
+ | **Skypack** | `https://cdn.skypack.dev/@licenseseat/js` |
680
787
 
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:
845
+
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? } }` |
857
+
858
+ ### Migration Steps
859
+
860
+ 1. **Add `productSlug` to configuration:**
861
+ ```javascript
862
+ // Before
863
+ const sdk = new LicenseSeat({ apiKey: 'key' });
864
+
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
+ ```
731
891
 
732
- ### Breaking Changes in v0.2.0
892
+ 4. **Update deactivation handling:**
893
+ ```javascript
894
+ // Before
895
+ const result = await sdk.deactivate();
896
+ console.log(result.license_key);
733
897
 
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 |
898
+ // After
899
+ const result = await sdk.deactivate();
900
+ console.log(result.activation_id);
901
+ console.log(result.deactivated_at);
902
+ ```
738
903
 
739
- ### New Features in v0.2.0
904
+ 5. **Update offline method calls:**
905
+ ```javascript
906
+ // Before
907
+ await sdk.getOfflineLicense(key);
908
+ await sdk.getPublicKey(keyId);
740
909
 
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`
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)