@licenseseat/js 0.1.0 โ†’ 0.2.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,168 +1,759 @@
1
- # ๐Ÿ’บ LicenseSeat - JavaScript SDK
1
+ # LicenseSeat JavaScript SDK
2
2
 
3
- Official JavaScript client for [LicenseSeat](https://licenseseat.com) โ€“ the simple, secure licensing platform for apps, games, and plugins.
3
+ Official JavaScript/TypeScript SDK for [LicenseSeat](https://licenseseat.com) โ€“ the simple, secure licensing platform for apps, games, and plugins.
4
4
 
5
- This SDK helps you integrate license activation, validation, offline caching, entitlement checks, and more into your JavaScript and browser-based apps.
5
+ [![CI](https://github.com/licenseseat/licenseseat-js/actions/workflows/ci.yml/badge.svg)](https://github.com/licenseseat/licenseseat-js/actions/workflows/ci.yml)
6
+ [![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)
6
8
 
7
9
  ---
8
10
 
9
- ## ๐Ÿš€ Installation
11
+ ## Features
10
12
 
11
- ```bash
12
- npm install @licenseseat/js
13
- ````
13
+ - **License activation & deactivation** โ€“ Activate licenses with automatic device fingerprinting
14
+ - **Online & offline validation** โ€“ Validate licenses with optional offline fallback
15
+ - **Entitlement checking** โ€“ Check feature access with `hasEntitlement()` and `checkEntitlement()`
16
+ - **Local caching** โ€“ Secure localStorage-based caching with clock tamper detection
17
+ - **Auto-retry with exponential backoff** โ€“ Resilient network handling
18
+ - **Event-driven architecture** โ€“ Subscribe to SDK lifecycle events
19
+ - **TypeScript support** โ€“ Full type definitions included (auto-generated from JSDoc)
20
+ - **Modern ESM package** โ€“ Native ES modules, tree-shakeable
21
+
22
+ ---
23
+
24
+ ## Installation
14
25
 
15
- Or via `yarn`:
26
+ ### npm / yarn / pnpm
16
27
 
17
28
  ```bash
29
+ # npm
30
+ npm install @licenseseat/js
31
+
32
+ # yarn
18
33
  yarn add @licenseseat/js
34
+
35
+ # pnpm
36
+ pnpm add @licenseseat/js
37
+ ```
38
+
39
+ ### CDN (Browser)
40
+
41
+ ```html
42
+ <!-- ESM via esm.sh -->
43
+ <script type="module">
44
+ import LicenseSeat from 'https://esm.sh/@licenseseat/js';
45
+
46
+ const sdk = new LicenseSeat({ apiKey: 'your-api-key' });
47
+ </script>
48
+
49
+ <!-- ESM via unpkg -->
50
+ <script type="module">
51
+ import LicenseSeat from 'https://unpkg.com/@licenseseat/js/dist/index.js';
52
+ </script>
53
+
54
+ <!-- ESM via jsDelivr -->
55
+ <script type="module">
56
+ import LicenseSeat from 'https://cdn.jsdelivr.net/npm/@licenseseat/js/dist/index.js';
57
+ </script>
19
58
  ```
20
59
 
21
60
  ---
22
61
 
23
- ## ๐Ÿงช Quickstart
62
+ ## Quick Start
24
63
 
25
- ```js
64
+ ### JavaScript (ESM)
65
+
66
+ ```javascript
26
67
  import LicenseSeat from '@licenseseat/js';
27
68
 
28
- const license = new LicenseSeat({
29
- publicKey: 'LS_PUBLIC_...',
30
- product: 'my-app-id',
69
+ // Create SDK instance
70
+ const sdk = new LicenseSeat({
71
+ apiKey: 'your-api-key',
31
72
  debug: true
32
73
  });
33
74
 
34
- // Activate a license key
35
- await license.activate('your-license-key');
75
+ // Activate a license
76
+ await sdk.activate('YOUR-LICENSE-KEY');
36
77
 
37
- // Check if the user is entitled to a feature
38
- if (license.hasEntitlement('pro')) {
39
- // unlock features
78
+ // Check entitlements (simple boolean)
79
+ if (sdk.hasEntitlement('pro')) {
80
+ // Enable pro features
40
81
  }
82
+
83
+ // Get current status
84
+ const status = sdk.getStatus();
85
+ console.log(status);
86
+ // { status: 'active', license: '...', entitlements: [...] }
41
87
  ```
42
88
 
43
- ---
89
+ ### TypeScript
44
90
 
45
- ## ๐ŸŒ Browser Support
91
+ ```typescript
92
+ import LicenseSeat, {
93
+ type LicenseSeatConfig,
94
+ type ValidationResult,
95
+ type EntitlementCheckResult,
96
+ type LicenseStatus
97
+ } from '@licenseseat/js';
46
98
 
47
- This SDK works natively in:
99
+ const config: LicenseSeatConfig = {
100
+ apiKey: 'your-api-key',
101
+ debug: true
102
+ };
103
+
104
+ const sdk = new LicenseSeat(config);
48
105
 
49
- * Modern browsers (`import` via ESM)
50
- * Any bundler (Vite, Webpack, Rollup)
51
- * Node.js (>= 18)
106
+ // Full type inference
107
+ const result: ValidationResult = await sdk.validateLicense('LICENSE-KEY');
108
+ const status: LicenseStatus = sdk.getStatus();
109
+ const hasPro: boolean = sdk.hasEntitlement('pro');
110
+ ```
52
111
 
53
- If you prefer `<script>` tag usage, you can use the [global bundle](#browser-global-usage) instead.
112
+ TypeScript users get full type support automatically โ€“ the package includes generated `.d.ts` declaration files.
54
113
 
55
114
  ---
56
115
 
57
- ## ๐Ÿ“ฆ Features
116
+ ## Configuration
58
117
 
59
- * ๐Ÿ” **License activation**
60
- * ๐Ÿ“ **Device fingerprinting**
61
- * ๐ŸŒ **Online & offline validation**
62
- * ๐ŸŽซ **Entitlements support**
63
- * ๐Ÿ“ฅ **Encrypted local caching**
64
- * ๐ŸŽฏ **Auto-retry on network failure**
65
- * ๐Ÿ“ก **Event emitters for reactive state**
66
- * โš™๏ธ **Fully ESM-compatible**
118
+ ```javascript
119
+ const sdk = new LicenseSeat({
120
+ // Required for authenticated operations
121
+ apiKey: 'your-api-key',
67
122
 
68
- ---
123
+ // API Configuration
124
+ apiBaseUrl: 'https://api.licenseseat.com', // Default
69
125
 
70
- ## ๐Ÿ“˜ API Reference
126
+ // Storage
127
+ storagePrefix: 'licenseseat_', // localStorage key prefix
71
128
 
72
- ### `new LicenseSeat(options)`
129
+ // Auto-Validation
130
+ autoValidateInterval: 3600000, // 1 hour (in ms)
131
+ autoInitialize: true, // Auto-validate cached license on init
73
132
 
74
- | Option | Type | Description |
75
- | ----------- | ------- | --------------------------------------------- |
76
- | `publicKey` | string | Your LicenseSeat public signing key |
77
- | `product` | string | Product identifier from LicenseSeat dashboard |
78
- | `debug` | boolean | Optional. Logs debug info to console |
133
+ // Offline Support
134
+ offlineFallbackEnabled: false, // Enable offline validation fallback
135
+ maxOfflineDays: 0, // Max days offline (0 = disabled)
136
+ offlineLicenseRefreshInterval: 259200000, // 72 hours
137
+ maxClockSkewMs: 300000, // 5 minutes
79
138
 
80
- ---
139
+ // Network
140
+ maxRetries: 3, // Retry attempts for failed requests
141
+ retryDelay: 1000, // Initial retry delay (ms)
142
+ networkRecheckInterval: 30000, // Check connectivity every 30s when offline
81
143
 
82
- ### `await license.activate(licenseKey)`
144
+ // Debug
145
+ debug: false // Enable console logging
146
+ });
147
+ ```
83
148
 
84
- Activates a license key and stores metadata.
149
+ ### Configuration Options
150
+
151
+ | Option | Type | Default | Description |
152
+ |--------|------|---------|-------------|
153
+ | `apiKey` | `string` | `null` | API key for authentication (required for most operations) |
154
+ | `apiBaseUrl` | `string` | `'https://api.licenseseat.com'` | 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 |
85
163
 
86
164
  ---
87
165
 
88
- ### `license.hasEntitlement(entitlementName)`
166
+ ## API Reference
89
167
 
90
- Returns `true` if the current license includes the given entitlement.
168
+ ### Core Methods
91
169
 
92
- ---
170
+ #### `sdk.activate(licenseKey, options?)`
171
+
172
+ Activates a license key on this device.
93
173
 
94
- ### `await license.validate()`
174
+ ```javascript
175
+ 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
179
+ });
180
+
181
+ console.log(result);
182
+ // {
183
+ // license_key: 'LICENSE-KEY',
184
+ // device_identifier: 'web-abc123-xyz',
185
+ // activated_at: '2024-01-15T10:30:00Z',
186
+ // activation: { ... }
187
+ // }
188
+ ```
95
189
 
96
- Force online validation of the current license (optional).
190
+ #### `sdk.deactivate()`
191
+
192
+ Deactivates the current license and clears cached data.
193
+
194
+ ```javascript
195
+ await sdk.deactivate();
196
+ ```
197
+
198
+ #### `sdk.validateLicense(licenseKey, options?)`
199
+
200
+ Validates a license with the server.
201
+
202
+ ```javascript
203
+ const result = await sdk.validateLicense('LICENSE-KEY', {
204
+ deviceIdentifier: 'device-id', // Optional
205
+ productSlug: 'my-product' // Optional
206
+ });
207
+
208
+ console.log(result);
209
+ // {
210
+ // 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
+ // ]
215
+ // }
216
+ ```
217
+
218
+ ### Entitlement Methods
219
+
220
+ #### `sdk.hasEntitlement(key)`
221
+
222
+ Check if an entitlement is active. Returns a simple boolean.
223
+
224
+ ```javascript
225
+ if (sdk.hasEntitlement('pro')) {
226
+ enableProFeatures();
227
+ }
228
+
229
+ if (sdk.hasEntitlement('beta')) {
230
+ showBetaUI();
231
+ }
232
+ ```
233
+
234
+ #### `sdk.checkEntitlement(key)`
235
+
236
+ Check entitlement with detailed information.
237
+
238
+ ```javascript
239
+ const result = sdk.checkEntitlement('pro');
240
+
241
+ if (result.active) {
242
+ console.log('Entitlement:', result.entitlement);
243
+ console.log('Expires:', result.entitlement.expires_at);
244
+ } else {
245
+ console.log('Reason:', result.reason);
246
+ // Possible reasons: 'no_license', 'not_found', 'expired'
247
+ }
248
+ ```
249
+
250
+ ### Status Methods
251
+
252
+ #### `sdk.getStatus()`
253
+
254
+ Get current license status.
255
+
256
+ ```javascript
257
+ const status = sdk.getStatus();
258
+
259
+ // Possible status values:
260
+ // - 'inactive': No license activated
261
+ // - 'pending': License pending validation
262
+ // - 'active': License valid (online)
263
+ // - 'invalid': License invalid
264
+ // - 'offline-valid': License valid (offline verification)
265
+ // - 'offline-invalid': License invalid (offline verification)
266
+
267
+ console.log(status);
268
+ // {
269
+ // status: 'active',
270
+ // license: 'LICENSE-KEY',
271
+ // device: 'web-abc123-xyz',
272
+ // activated_at: '2024-01-15T10:30:00Z',
273
+ // last_validated: '2024-01-15T11:30:00Z',
274
+ // entitlements: [...]
275
+ // }
276
+ ```
277
+
278
+ #### `sdk.testAuth()`
279
+
280
+ Test API authentication (useful for verifying API key).
281
+
282
+ ```javascript
283
+ try {
284
+ const result = await sdk.testAuth();
285
+ console.log('Authenticated:', result.authenticated);
286
+ } catch (error) {
287
+ console.error('Auth failed:', error);
288
+ }
289
+ ```
290
+
291
+ #### `sdk.reset()`
292
+
293
+ Clear all cached data and reset SDK state.
294
+
295
+ ```javascript
296
+ sdk.reset();
297
+ ```
298
+
299
+ #### `sdk.initialize()`
300
+
301
+ Manually initialize the SDK (only needed if `autoInitialize: false`).
302
+
303
+ ```javascript
304
+ const sdk = new LicenseSeat({
305
+ apiKey: 'key',
306
+ autoInitialize: false // Don't auto-initialize
307
+ });
308
+
309
+ // Later, when ready:
310
+ sdk.initialize();
311
+ ```
97
312
 
98
313
  ---
99
314
 
100
- ### `license.on(event, callback)`
315
+ ## Events
101
316
 
102
- Subscribe to SDK lifecycle events:
317
+ Subscribe to SDK lifecycle events for reactive UIs.
103
318
 
104
- ```js
105
- license.on('activated', (data) => {
319
+ ```javascript
320
+ // Subscribe
321
+ const unsubscribe = sdk.on('activation:success', (data) => {
106
322
  console.log('License activated:', data);
107
323
  });
108
324
 
109
- license.on('error', (err) => {
110
- console.error('LicenseSeat error:', err);
325
+ // Unsubscribe
326
+ unsubscribe();
327
+ // or
328
+ sdk.off('activation:success', handler);
329
+ ```
330
+
331
+ ### Available Events
332
+
333
+ | Event | Description | Data |
334
+ |-------|-------------|------|
335
+ | **Lifecycle** | | |
336
+ | `license:loaded` | Cached license loaded on init | `CachedLicense` |
337
+ | `sdk:reset` | SDK was reset | โ€“ |
338
+ | `sdk:error` | General SDK error | `{ message, error? }` |
339
+ | **Activation** | | |
340
+ | `activation:start` | Activation started | `{ licenseKey, deviceId }` |
341
+ | `activation:success` | Activation succeeded | `CachedLicense` |
342
+ | `activation:error` | Activation failed | `{ licenseKey, error }` |
343
+ | **Deactivation** | | |
344
+ | `deactivation:start` | Deactivation started | `CachedLicense` |
345
+ | `deactivation:success` | Deactivation succeeded | `Object` |
346
+ | `deactivation:error` | Deactivation failed | `{ error, license }` |
347
+ | **Validation** | | |
348
+ | `validation:start` | Validation started | `{ licenseKey }` |
349
+ | `validation:success` | Online validation succeeded | `ValidationResult` |
350
+ | `validation:failed` | Validation failed (invalid license) | `ValidationResult` |
351
+ | `validation:error` | Validation error (network, etc.) | `{ licenseKey, error }` |
352
+ | `validation:offline-success` | Offline validation succeeded | `ValidationResult` |
353
+ | `validation:offline-failed` | Offline validation failed | `ValidationResult` |
354
+ | `validation:auth-failed` | Auth failed during validation | `{ licenseKey, error, cached }` |
355
+ | **Auto-Validation** | | |
356
+ | `autovalidation:cycle` | Auto-validation scheduled | `{ nextRunAt: Date }` |
357
+ | `autovalidation:stopped` | Auto-validation stopped | โ€“ |
358
+ | **Network** | | |
359
+ | `network:online` | Network connectivity restored | โ€“ |
360
+ | `network:offline` | Network connectivity lost | `{ error }` |
361
+ | **Offline License** | | |
362
+ | `offlineLicense:fetching` | Fetching offline license | `{ licenseKey }` |
363
+ | `offlineLicense:fetched` | Offline license fetched | `{ licenseKey, data }` |
364
+ | `offlineLicense:fetchError` | Offline license fetch failed | `{ licenseKey, error }` |
365
+ | `offlineLicense:ready` | Offline assets synced | `{ kid, exp_at }` |
366
+ | `offlineLicense:verified` | Offline signature verified | `{ payload }` |
367
+ | `offlineLicense:verificationFailed` | Offline signature invalid | `{ payload }` |
368
+
369
+ ---
370
+
371
+ ## Singleton Pattern
372
+
373
+ For applications that need a shared SDK instance:
374
+
375
+ ```javascript
376
+ import { configure, getSharedInstance, resetSharedInstance } from '@licenseseat/js';
377
+
378
+ // Configure once at app startup
379
+ configure({ apiKey: 'your-key' });
380
+
381
+ // Use anywhere in your app
382
+ const sdk = getSharedInstance();
383
+ await sdk.activate('LICENSE-KEY');
384
+
385
+ // Reset if needed
386
+ resetSharedInstance();
387
+ ```
388
+
389
+ ---
390
+
391
+ ## Offline Support
392
+
393
+ The SDK supports offline license validation using cryptographically signed offline licenses (Ed25519).
394
+
395
+ ```javascript
396
+ const sdk = new LicenseSeat({
397
+ apiKey: 'your-key',
398
+ offlineFallbackEnabled: true, // Enable offline fallback
399
+ maxOfflineDays: 7 // Allow 7 days offline
111
400
  });
401
+
402
+ // After activation, offline assets are automatically synced
403
+ await sdk.activate('LICENSE-KEY');
404
+
405
+ // Later, even offline, validation will work using cached data
406
+ const result = await sdk.validateLicense('LICENSE-KEY');
407
+ if (result.offline) {
408
+ console.log('Validated offline');
409
+ }
112
410
  ```
113
411
 
114
- Supported events:
412
+ ### How Offline Validation Works
115
413
 
116
- * `activated`
117
- * `deactivated`
118
- * `error`
119
- * `offline`
120
- * `validated`
414
+ 1. On activation, the SDK fetches a signed offline license from the server
415
+ 2. The offline license contains the license data + Ed25519 signature
416
+ 3. When offline, the SDK verifies the signature locally
417
+ 4. Clock tamper detection prevents users from bypassing expiration
121
418
 
122
419
  ---
123
420
 
124
- ## ๐Ÿ’ป Browser Global Usage
421
+ ## Error Handling
422
+
423
+ The SDK exports custom error classes for precise error handling:
424
+
425
+ ```javascript
426
+ import LicenseSeat, {
427
+ APIError,
428
+ LicenseError,
429
+ ConfigurationError,
430
+ CryptoError
431
+ } from '@licenseseat/js';
432
+
433
+ try {
434
+ await sdk.activate('INVALID-KEY');
435
+ } catch (error) {
436
+ if (error instanceof APIError) {
437
+ console.log('HTTP Status:', error.status);
438
+ console.log('Response:', error.data);
439
+ } else if (error instanceof LicenseError) {
440
+ console.log('License error:', error.code);
441
+ }
442
+ }
443
+ ```
444
+
445
+ ### Error Types
125
446
 
126
- To use without a build system:
447
+ | Error | Description |
448
+ |-------|-------------|
449
+ | `APIError` | HTTP request failures (includes `status` and `data`) |
450
+ | `LicenseError` | License operation failures (includes `code`) |
451
+ | `ConfigurationError` | SDK misconfiguration |
452
+ | `CryptoError` | Cryptographic operation failures |
127
453
 
128
- ```html
129
- <script src="https://cdn.licenseseat.com/sdk/latest/index.global.js"></script>
130
- <script>
131
- const license = new LicenseSeat({
132
- publicKey: 'LS_PUBLIC_...',
133
- product: 'my-app-id'
134
- });
454
+ ---
135
455
 
136
- license.activate('YOUR-LICENSE');
137
- </script>
456
+ ## Browser Support
457
+
458
+ - **Modern browsers**: Chrome 80+, Firefox 75+, Safari 14+, Edge 80+
459
+ - **Bundlers**: Vite, Webpack, Rollup, esbuild, Parcel
460
+ - **Node.js**: 18+ (requires polyfills for `localStorage`, `document`)
461
+
462
+ ---
463
+
464
+ ## Usage Guide
465
+
466
+ ### For JavaScript Users
467
+
468
+ Simply import and use:
469
+
470
+ ```javascript
471
+ import LicenseSeat from '@licenseseat/js';
472
+
473
+ const sdk = new LicenseSeat({ apiKey: 'your-key' });
138
474
  ```
139
475
 
140
- *(CDN link optional โ€” you can also host your own global bundle.)*
476
+ ### For TypeScript Users
477
+
478
+ The package includes TypeScript declarations (`.d.ts` files) automatically. No additional `@types/` package needed.
479
+
480
+ ```typescript
481
+ import LicenseSeat from '@licenseseat/js';
482
+
483
+ // Types are automatically available
484
+ const sdk = new LicenseSeat({ apiKey: 'your-key' });
485
+
486
+ // Import specific types if needed
487
+ import type {
488
+ LicenseSeatConfig,
489
+ ValidationResult,
490
+ EntitlementCheckResult,
491
+ LicenseStatus,
492
+ Entitlement,
493
+ CachedLicense
494
+ } from '@licenseseat/js';
495
+ ```
496
+
497
+ ### For CDN/Browser Users
498
+
499
+ Use ES modules via CDN:
500
+
501
+ ```html
502
+ <!DOCTYPE html>
503
+ <html>
504
+ <head>
505
+ <title>LicenseSeat Demo</title>
506
+ </head>
507
+ <body>
508
+ <script type="module">
509
+ import LicenseSeat from 'https://esm.sh/@licenseseat/js';
510
+
511
+ const sdk = new LicenseSeat({
512
+ apiKey: 'your-api-key',
513
+ debug: true
514
+ });
515
+
516
+ // Check for existing license
517
+ const status = sdk.getStatus();
518
+ if (status.status === 'active') {
519
+ console.log('Already licensed!');
520
+ }
521
+
522
+ // Activate (example with user input)
523
+ document.getElementById('activate-btn').onclick = async () => {
524
+ const key = document.getElementById('license-key').value;
525
+ try {
526
+ await sdk.activate(key);
527
+ alert('License activated!');
528
+ } catch (e) {
529
+ alert('Activation failed: ' + e.message);
530
+ }
531
+ };
532
+ </script>
533
+
534
+ <input id="license-key" placeholder="Enter license key" />
535
+ <button id="activate-btn">Activate</button>
536
+ </body>
537
+ </html>
538
+ ```
141
539
 
142
540
  ---
143
541
 
144
- ## ๐Ÿ›  Development
542
+ ## Development
145
543
 
146
- Clone the SDK:
544
+ ### Setup
147
545
 
148
546
  ```bash
149
547
  git clone https://github.com/licenseseat/licenseseat-js.git
150
548
  cd licenseseat-js
151
549
  npm install
152
- npm run build
153
550
  ```
154
551
 
155
- This builds `dist/index.js` (ESM) and optionally `index.global.js` (IIFE).
552
+ ### Scripts
553
+
554
+ | Command | Description |
555
+ |---------|-------------|
556
+ | `npm run build` | Build JS bundle + TypeScript declarations |
557
+ | `npm run build:js` | Build JavaScript bundle only |
558
+ | `npm run build:types` | Generate TypeScript declarations |
559
+ | `npm run build:iife` | Build global/IIFE bundle |
560
+ | `npm run dev` | Watch mode for development |
561
+ | `npm test` | Run tests |
562
+ | `npm run test:watch` | Run tests in watch mode |
563
+ | `npm run test:coverage` | Run tests with coverage report |
564
+ | `npm run typecheck` | Type-check without emitting |
565
+
566
+ ### Project Structure
567
+
568
+ ```
569
+ licenseseat-js/
570
+ โ”œโ”€โ”€ src/
571
+ โ”‚ โ”œโ”€โ”€ index.js # Entry point, exports
572
+ โ”‚ โ”œโ”€โ”€ LicenseSeat.js # Main SDK class
573
+ โ”‚ โ”œโ”€โ”€ cache.js # LicenseCache (localStorage)
574
+ โ”‚ โ”œโ”€โ”€ errors.js # Error classes
575
+ โ”‚ โ”œโ”€โ”€ types.js # JSDoc type definitions
576
+ โ”‚ โ””โ”€โ”€ utils.js # Utility functions
577
+ โ”œโ”€โ”€ tests/
578
+ โ”‚ โ”œโ”€โ”€ setup.js # Test setup
579
+ โ”‚ โ”œโ”€โ”€ mocks/ # MSW handlers
580
+ โ”‚ โ”œโ”€โ”€ LicenseSeat.test.js
581
+ โ”‚ โ””โ”€โ”€ utils.test.js
582
+ โ”œโ”€โ”€ dist/ # Build output
583
+ โ”‚ โ”œโ”€โ”€ index.js # ESM bundle
584
+ โ”‚ โ””โ”€โ”€ types/ # TypeScript declarations
585
+ โ”œโ”€โ”€ package.json
586
+ โ”œโ”€โ”€ tsconfig.json
587
+ โ””โ”€โ”€ vitest.config.js
588
+ ```
589
+
590
+ ---
591
+
592
+ ## Publishing
593
+
594
+ ### Publishing to npm
595
+
596
+ 1. **Update version** in `package.json`:
597
+ ```bash
598
+ npm version patch # or minor, major
599
+ ```
600
+
601
+ 2. **Build the package**:
602
+ ```bash
603
+ npm run build
604
+ ```
605
+
606
+ 3. **Verify the build**:
607
+ ```bash
608
+ # Check what will be published
609
+ npm pack --dry-run
610
+
611
+ # Verify TypeScript types
612
+ ls dist/types/
613
+ ```
614
+
615
+ 4. **Publish**:
616
+ ```bash
617
+ # Login if needed
618
+ npm login
619
+
620
+ # Publish (public package)
621
+ npm publish --access public
622
+ ```
623
+
624
+ ### What Gets Published
625
+
626
+ The `files` field in `package.json` controls what's included:
627
+
628
+ ```json
629
+ {
630
+ "files": ["dist/", "src/"]
631
+ }
632
+ ```
633
+
634
+ Users receive:
635
+ - `dist/index.js` โ€“ ESM bundle (JavaScript)
636
+ - `dist/types/*.d.ts` โ€“ TypeScript declarations
637
+ - `src/*.js` โ€“ Source files (for debugging/reference)
638
+
639
+ ### Package Exports
640
+
641
+ ```json
642
+ {
643
+ "main": "dist/index.js",
644
+ "module": "dist/index.js",
645
+ "types": "dist/types/index.d.ts",
646
+ "exports": {
647
+ ".": {
648
+ "import": "./dist/index.js",
649
+ "types": "./dist/types/index.d.ts"
650
+ }
651
+ }
652
+ }
653
+ ```
654
+
655
+ This ensures:
656
+ - JavaScript users get `dist/index.js`
657
+ - TypeScript users get type definitions from `dist/types/index.d.ts`
658
+ - Both ESM `import` and bundlers work correctly
659
+
660
+ ### CDN Distribution
661
+
662
+ Once published to npm, the package is automatically available on CDNs:
663
+
664
+ | CDN | URL |
665
+ |-----|-----|
666
+ | **esm.sh** | `https://esm.sh/@licenseseat/js` |
667
+ | **unpkg** | `https://unpkg.com/@licenseseat/js/dist/index.js` |
668
+ | **jsDelivr** | `https://cdn.jsdelivr.net/npm/@licenseseat/js/dist/index.js` |
669
+ | **Skypack** | `https://cdn.skypack.dev/@licenseseat/js` |
670
+
671
+ **Version pinning** (recommended for production):
672
+ ```html
673
+ <script type="module">
674
+ import LicenseSeat from 'https://esm.sh/@licenseseat/js@0.2.0';
675
+ </script>
676
+ ```
677
+
678
+ ### Self-Hosting
679
+
680
+ To host the SDK yourself:
681
+
682
+ 1. Build the package:
683
+ ```bash
684
+ npm run build
685
+ ```
686
+
687
+ 2. Copy `dist/index.js` to your CDN/server
688
+
689
+ 3. Serve with correct MIME type (`application/javascript`) and CORS headers
690
+
691
+ ### Building an IIFE Bundle (Legacy Browsers)
692
+
693
+ For a global `LicenseSeat` variable (non-module script tags):
694
+
695
+ ```bash
696
+ npm run build:iife
697
+ ```
698
+
699
+ This creates `dist/index.global.js`:
700
+
701
+ ```html
702
+ <script src="/path/to/index.global.js"></script>
703
+ <script>
704
+ const sdk = new LicenseSeat({ apiKey: 'your-key' });
705
+ </script>
706
+ ```
707
+
708
+ ---
709
+
710
+ ## Versioning
711
+
712
+ This project follows [Semantic Versioning](https://semver.org/):
713
+
714
+ - **MAJOR** (1.0.0 โ†’ 2.0.0): Breaking changes
715
+ - **MINOR** (1.0.0 โ†’ 1.1.0): New features (backward compatible)
716
+ - **PATCH** (1.0.0 โ†’ 1.0.1): Bug fixes
717
+
718
+ ---
719
+
720
+ ## Migration from v0.1.x
721
+
722
+ ### Breaking Changes in v0.2.0
723
+
724
+ | Change | Before | After | Migration |
725
+ |--------|--------|-------|-----------|
726
+ | `apiBaseUrl` default | `/api` | `https://api.licenseseat.com` | Set `apiBaseUrl` explicitly if using a relative URL |
727
+ | `offlineFallbackEnabled` default | `true` | `false` | Set `offlineFallbackEnabled: true` if you need offline fallback |
728
+
729
+ ### New Features in v0.2.0
730
+
731
+ - `hasEntitlement(key)` method for simple boolean checks
732
+ - `autoInitialize` config option for lazy initialization
733
+ - Full TypeScript support with auto-generated `.d.ts` files
734
+ - Singleton pattern with `configure()` and `getSharedInstance()`
735
+ - New error classes: `LicenseError`, `ConfigurationError`, `CryptoError`
736
+
737
+ ---
738
+
739
+ ## License
740
+
741
+ MIT License โ€“ see [LICENSE](LICENSE) for details.
156
742
 
157
743
  ---
158
744
 
159
- ## ๐Ÿง  LicenseSeat Docs
745
+ ## Links
160
746
 
161
- * ๐Ÿ“˜ [Documentation](https://licenseseat.com/docs)
162
- * ๐Ÿ’ฌ [Contact support](https://licenseseat.com/contact)
747
+ - [LicenseSeat Website](https://licenseseat.com)
748
+ - [Documentation](https://licenseseat.com/docs)
749
+ - [API Reference](https://licenseseat.com/docs/api)
750
+ - [GitHub Repository](https://github.com/licenseseat/licenseseat-js)
751
+ - [npm Package](https://www.npmjs.com/package/@licenseseat/js)
752
+ - [Report Issues](https://github.com/licenseseat/licenseseat-js/issues)
163
753
 
164
754
  ---
165
755
 
166
- ## ๐Ÿชช License
756
+ ## Support
167
757
 
168
- MIT License ยฉ 2025 [LicenseSeat](https://licenseseat.com)
758
+ - **Email**: support@licenseseat.com
759
+ - **GitHub Issues**: [Report a bug](https://github.com/licenseseat/licenseseat-js/issues/new)