@licenseseat/tauri-plugin 0.1.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 ADDED
@@ -0,0 +1,638 @@
1
+ # LicenseSeat Tauri Plugin
2
+
3
+ [![Crates.io](https://img.shields.io/crates/v/tauri-plugin-licenseseat.svg)](https://crates.io/crates/tauri-plugin-licenseseat)
4
+ [![npm](https://img.shields.io/npm/v/@licenseseat/tauri-plugin.svg)](https://www.npmjs.com/package/@licenseseat/tauri-plugin)
5
+ [![Documentation](https://docs.rs/tauri-plugin-licenseseat/badge.svg)](https://docs.rs/tauri-plugin-licenseseat)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](../../LICENSE)
7
+ [![Tauri](https://img.shields.io/badge/tauri-v2-24C8D8.svg)](https://v2.tauri.app)
8
+
9
+ Official Tauri v2 plugin for [LicenseSeat](https://licenseseat.com) — simple, secure software licensing for your Tauri apps.
10
+
11
+ ## Table of Contents
12
+
13
+ - [Features](#features)
14
+ - [Requirements](#requirements)
15
+ - [Installation](#installation)
16
+ - [Setup](#setup)
17
+ - [Usage](#usage)
18
+ - [TypeScript/JavaScript](#typescriptjavascript)
19
+ - [Rust (Backend)](#rust-backend)
20
+ - [API Reference](#api-reference)
21
+ - [Configuration](#configuration)
22
+ - [Entitlements](#entitlements)
23
+ - [Event Handling](#event-handling)
24
+ - [Offline Support](#offline-support)
25
+ - [React Integration](#react-integration)
26
+ - [Vue Integration](#vue-integration)
27
+ - [Svelte Integration](#svelte-integration)
28
+ - [Error Handling](#error-handling)
29
+ - [Security](#security)
30
+ - [Troubleshooting](#troubleshooting)
31
+
32
+ ## Features
33
+
34
+ - **Full License Lifecycle** — Activate, validate, deactivate from your frontend
35
+ - **TypeScript Bindings** — Fully typed API with autocomplete
36
+ - **Entitlement Checking** — Feature gating made simple
37
+ - **Event System** — React to license changes in real-time
38
+ - **Offline Support** — Ed25519 cryptographic validation (optional)
39
+ - **Zero Config** — Just add your API key and product slug
40
+ - **Tauri v2** — Built for the latest Tauri architecture
41
+
42
+ ## Requirements
43
+
44
+ - Tauri v2.0.0 or later
45
+ - Rust 1.70+
46
+ - Node.js 18+ (for the JS bindings)
47
+
48
+ ## Installation
49
+
50
+ ### 1. Add the Rust Plugin
51
+
52
+ ```bash
53
+ cd src-tauri
54
+ cargo add tauri-plugin-licenseseat
55
+ ```
56
+
57
+ ### 2. Add the JavaScript Bindings
58
+
59
+ ```bash
60
+ # npm
61
+ npm add @licenseseat/tauri-plugin
62
+
63
+ # pnpm
64
+ pnpm add @licenseseat/tauri-plugin
65
+
66
+ # yarn
67
+ yarn add @licenseseat/tauri-plugin
68
+
69
+ # bun
70
+ bun add @licenseseat/tauri-plugin
71
+ ```
72
+
73
+ ## Setup
74
+
75
+ ### 1. Register the Plugin
76
+
77
+ ```rust
78
+ // src-tauri/src/main.rs (or lib.rs for Tauri v2)
79
+ fn main() {
80
+ tauri::Builder::default()
81
+ .plugin(tauri_plugin_licenseseat::init())
82
+ .run(tauri::generate_context!())
83
+ .expect("error while running tauri application");
84
+ }
85
+ ```
86
+
87
+ ### 2. Add Configuration
88
+
89
+ ```json
90
+ // tauri.conf.json
91
+ {
92
+ "plugins": {
93
+ "licenseseat": {
94
+ "apiKey": "your-api-key",
95
+ "productSlug": "your-product"
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ ### 3. Add Permissions
102
+
103
+ ```json
104
+ // src-tauri/capabilities/default.json
105
+ {
106
+ "identifier": "default",
107
+ "windows": ["main"],
108
+ "permissions": [
109
+ "core:default",
110
+ "licenseseat:default"
111
+ ]
112
+ }
113
+ ```
114
+
115
+ The `licenseseat:default` permission grants access to all licensing commands. For fine-grained control, see [Permissions](#permissions).
116
+
117
+ ## Usage
118
+
119
+ ### TypeScript/JavaScript
120
+
121
+ ```typescript
122
+ import {
123
+ activate,
124
+ validate,
125
+ deactivate,
126
+ getStatus,
127
+ hasEntitlement,
128
+ checkEntitlement,
129
+ heartbeat
130
+ } from '@licenseseat/tauri-plugin';
131
+
132
+ // Activate a license (first launch or new key)
133
+ async function activateLicense(key: string) {
134
+ try {
135
+ const license = await activate(key);
136
+ console.log(`Activated! Device ID: ${license.deviceId}`);
137
+ return license;
138
+ } catch (error) {
139
+ console.error('Activation failed:', error);
140
+ throw error;
141
+ }
142
+ }
143
+
144
+ // Validate the current license (subsequent launches)
145
+ async function validateLicense() {
146
+ const result = await validate();
147
+
148
+ if (result.valid) {
149
+ console.log('License is valid!');
150
+ console.log('Plan:', result.license.planKey);
151
+ return true;
152
+ } else {
153
+ console.log('Invalid:', result.code, result.message);
154
+ return false;
155
+ }
156
+ }
157
+
158
+ // Check entitlements for feature gating
159
+ async function checkFeatures() {
160
+ if (await hasEntitlement('pro-features')) {
161
+ enableProFeatures();
162
+ }
163
+
164
+ if (await hasEntitlement('cloud-sync')) {
165
+ enableCloudSync();
166
+ }
167
+ }
168
+
169
+ // Get current license status
170
+ async function showStatus() {
171
+ const status = await getStatus();
172
+
173
+ switch (status.status) {
174
+ case 'active':
175
+ showActiveBadge();
176
+ break;
177
+ case 'expired':
178
+ showRenewalPrompt();
179
+ break;
180
+ case 'not_activated':
181
+ showActivationPrompt();
182
+ break;
183
+ }
184
+ }
185
+
186
+ // Deactivate (release the seat)
187
+ async function deactivateLicense() {
188
+ await deactivate();
189
+ console.log('License deactivated');
190
+ }
191
+ ```
192
+
193
+ ### Rust (Backend)
194
+
195
+ Access the SDK directly from Rust for advanced use cases:
196
+
197
+ ```rust
198
+ use tauri::Manager;
199
+ use tauri_plugin_licenseseat::LicenseSeatExt;
200
+
201
+ #[tauri::command]
202
+ async fn custom_validation(app: tauri::AppHandle) -> Result<bool, String> {
203
+ let sdk = app.licenseseat();
204
+
205
+ match sdk.validate().await {
206
+ Ok(result) => Ok(result.valid),
207
+ Err(e) => Err(e.to_string()),
208
+ }
209
+ }
210
+ ```
211
+
212
+ ## API Reference
213
+
214
+ ### Functions
215
+
216
+ | Function | Description | Returns |
217
+ |----------|-------------|---------|
218
+ | `activate(key)` | Activate a license key | `Promise<License>` |
219
+ | `validate()` | Validate current license | `Promise<ValidationResult>` |
220
+ | `deactivate()` | Deactivate and release seat | `Promise<void>` |
221
+ | `getStatus()` | Get current license status | `Promise<LicenseStatus>` |
222
+ | `hasEntitlement(key)` | Check if entitlement is active | `Promise<boolean>` |
223
+ | `checkEntitlement(key)` | Get detailed entitlement status | `Promise<EntitlementStatus>` |
224
+ | `heartbeat()` | Send heartbeat ping | `Promise<HeartbeatResponse>` |
225
+ | `getEntitlements()` | List all entitlements | `Promise<Entitlement[]>` |
226
+
227
+ ### Types
228
+
229
+ ```typescript
230
+ interface License {
231
+ key: string;
232
+ status: 'active' | 'expired' | 'suspended' | 'revoked';
233
+ planKey: string;
234
+ seatLimit: number;
235
+ expiresAt?: string;
236
+ deviceId: string;
237
+ activationId?: string;
238
+ }
239
+
240
+ interface ValidationResult {
241
+ valid: boolean;
242
+ code?: string;
243
+ message?: string;
244
+ warnings?: string[];
245
+ license: License;
246
+ }
247
+
248
+ interface LicenseStatus {
249
+ status: 'active' | 'expired' | 'suspended' | 'not_activated';
250
+ license?: License;
251
+ expiresAt?: string;
252
+ }
253
+
254
+ interface Entitlement {
255
+ key: string;
256
+ expiresAt?: string;
257
+ metadata?: Record<string, unknown>;
258
+ }
259
+
260
+ interface EntitlementStatus {
261
+ active: boolean;
262
+ reason: 'active' | 'expired' | 'not_found' | 'no_license';
263
+ expiresAt?: string;
264
+ }
265
+
266
+ interface HeartbeatResponse {
267
+ receivedAt: string;
268
+ }
269
+ ```
270
+
271
+ ## Configuration
272
+
273
+ ### Full Configuration Options
274
+
275
+ ```json
276
+ // tauri.conf.json
277
+ {
278
+ "plugins": {
279
+ "licenseseat": {
280
+ "apiKey": "your-api-key",
281
+ "productSlug": "your-product",
282
+ "apiBaseUrl": "https://licenseseat.com/api/v1",
283
+ "autoValidateInterval": 3600,
284
+ "heartbeatInterval": 300,
285
+ "offlineFallbackMode": "network_only",
286
+ "maxOfflineDays": 0,
287
+ "telemetryEnabled": true,
288
+ "debug": false
289
+ }
290
+ }
291
+ }
292
+ ```
293
+
294
+ ### Configuration Reference
295
+
296
+ | Option | Type | Default | Description |
297
+ |--------|------|---------|-------------|
298
+ | `apiKey` | `string` | — | Your LicenseSeat API key (required) |
299
+ | `productSlug` | `string` | — | Your product slug (required) |
300
+ | `apiBaseUrl` | `string` | `https://licenseseat.com/api/v1` | API base URL |
301
+ | `autoValidateInterval` | `number` | `3600` | Background validation interval (seconds) |
302
+ | `heartbeatInterval` | `number` | `300` | Heartbeat interval (seconds) |
303
+ | `offlineFallbackMode` | `string` | `"network_only"` | `"network_only"`, `"allow_offline"`, or `"offline_first"` |
304
+ | `maxOfflineDays` | `number` | `0` | Grace period for offline mode (days) |
305
+ | `telemetryEnabled` | `boolean` | `true` | Send device telemetry |
306
+ | `debug` | `boolean` | `false` | Enable debug logging |
307
+
308
+ ### Environment-Specific Config
309
+
310
+ Use Tauri's environment configuration for different API keys:
311
+
312
+ ```json
313
+ // tauri.conf.json (development)
314
+ {
315
+ "plugins": {
316
+ "licenseseat": {
317
+ "apiKey": "$LICENSESEAT_DEV_API_KEY",
318
+ "productSlug": "my-app-dev"
319
+ }
320
+ }
321
+ }
322
+ ```
323
+
324
+ ## Entitlements
325
+
326
+ ### Simple Check
327
+
328
+ ```typescript
329
+ if (await hasEntitlement('cloud-sync')) {
330
+ enableCloudSync();
331
+ }
332
+ ```
333
+
334
+ ### Detailed Status
335
+
336
+ ```typescript
337
+ const status = await checkEntitlement('pro-features');
338
+
339
+ if (status.active) {
340
+ enableProFeatures();
341
+ } else {
342
+ switch (status.reason) {
343
+ case 'expired':
344
+ showUpgradePrompt('Your Pro features have expired');
345
+ break;
346
+ case 'not_found':
347
+ showUpgradePrompt('Upgrade to Pro for this feature');
348
+ break;
349
+ case 'no_license':
350
+ showActivationPrompt();
351
+ break;
352
+ }
353
+ }
354
+ ```
355
+
356
+ ### List All Entitlements
357
+
358
+ ```typescript
359
+ const entitlements = await getEntitlements();
360
+
361
+ for (const ent of entitlements) {
362
+ console.log(`${ent.key}: expires ${ent.expiresAt ?? 'never'}`);
363
+ }
364
+ ```
365
+
366
+ ## Event Handling
367
+
368
+ Listen to license events from Rust using Tauri's event system:
369
+
370
+ ```typescript
371
+ import { listen } from '@tauri-apps/api/event';
372
+
373
+ // Listen for license events
374
+ await listen('licenseseat://validation-success', (event) => {
375
+ console.log('License validated!', event.payload);
376
+ refreshUI();
377
+ });
378
+
379
+ await listen('licenseseat://validation-failed', (event) => {
380
+ console.log('Validation failed:', event.payload);
381
+ showLicenseError();
382
+ });
383
+
384
+ await listen('licenseseat://heartbeat-success', () => {
385
+ updateConnectionStatus(true);
386
+ });
387
+
388
+ await listen('licenseseat://heartbeat-error', () => {
389
+ updateConnectionStatus(false);
390
+ });
391
+ ```
392
+
393
+ ### Available Events
394
+
395
+ | Event | Payload | Description |
396
+ |-------|---------|-------------|
397
+ | `licenseseat://activation-success` | `License` | License activated |
398
+ | `licenseseat://activation-error` | `string` | Activation failed |
399
+ | `licenseseat://validation-success` | `ValidationResult` | Validation succeeded |
400
+ | `licenseseat://validation-failed` | `string` | Validation failed |
401
+ | `licenseseat://deactivation-success` | — | License deactivated |
402
+ | `licenseseat://heartbeat-success` | — | Heartbeat acknowledged |
403
+ | `licenseseat://heartbeat-error` | `string` | Heartbeat failed |
404
+
405
+ ## Offline Support
406
+
407
+ Enable offline validation for air-gapped or unreliable network environments:
408
+
409
+ ```json
410
+ {
411
+ "plugins": {
412
+ "licenseseat": {
413
+ "offlineFallbackMode": "allow_offline",
414
+ "maxOfflineDays": 7
415
+ }
416
+ }
417
+ }
418
+ ```
419
+
420
+ **Modes:**
421
+
422
+ | Mode | Description |
423
+ |------|-------------|
424
+ | `network_only` | Always require network (default) |
425
+ | `allow_offline` | Fall back to cached token when offline |
426
+ | `offline_first` | Prefer offline, sync when online |
427
+
428
+ ## React Integration
429
+
430
+ ```tsx
431
+ import { useState, useEffect } from 'react';
432
+ import { getStatus, hasEntitlement, activate } from '@licenseseat/tauri-plugin';
433
+
434
+ function useLicense() {
435
+ const [status, setStatus] = useState<LicenseStatus | null>(null);
436
+ const [loading, setLoading] = useState(true);
437
+
438
+ useEffect(() => {
439
+ getStatus()
440
+ .then(setStatus)
441
+ .finally(() => setLoading(false));
442
+ }, []);
443
+
444
+ return { status, loading };
445
+ }
446
+
447
+ function useEntitlement(key: string) {
448
+ const [active, setActive] = useState(false);
449
+
450
+ useEffect(() => {
451
+ hasEntitlement(key).then(setActive);
452
+ }, [key]);
453
+
454
+ return active;
455
+ }
456
+
457
+ // Usage
458
+ function App() {
459
+ const { status, loading } = useLicense();
460
+ const hasProFeatures = useEntitlement('pro-features');
461
+
462
+ if (loading) return <Loading />;
463
+
464
+ if (status?.status !== 'active') {
465
+ return <ActivationScreen />;
466
+ }
467
+
468
+ return (
469
+ <div>
470
+ <h1>Welcome!</h1>
471
+ {hasProFeatures && <ProFeatures />}
472
+ </div>
473
+ );
474
+ }
475
+ ```
476
+
477
+ ## Vue Integration
478
+
479
+ ```vue
480
+ <script setup lang="ts">
481
+ import { ref, onMounted } from 'vue';
482
+ import { getStatus, hasEntitlement } from '@licenseseat/tauri-plugin';
483
+
484
+ const status = ref<LicenseStatus | null>(null);
485
+ const hasProFeatures = ref(false);
486
+
487
+ onMounted(async () => {
488
+ status.value = await getStatus();
489
+ hasProFeatures.value = await hasEntitlement('pro-features');
490
+ });
491
+ </script>
492
+
493
+ <template>
494
+ <div v-if="status?.status === 'active'">
495
+ <h1>Welcome!</h1>
496
+ <ProFeatures v-if="hasProFeatures" />
497
+ </div>
498
+ <ActivationScreen v-else />
499
+ </template>
500
+ ```
501
+
502
+ ## Svelte Integration
503
+
504
+ ```svelte
505
+ <script lang="ts">
506
+ import { onMount } from 'svelte';
507
+ import { getStatus, hasEntitlement } from '@licenseseat/tauri-plugin';
508
+
509
+ let status: LicenseStatus | null = null;
510
+ let hasProFeatures = false;
511
+
512
+ onMount(async () => {
513
+ status = await getStatus();
514
+ hasProFeatures = await hasEntitlement('pro-features');
515
+ });
516
+ </script>
517
+
518
+ {#if status?.status === 'active'}
519
+ <h1>Welcome!</h1>
520
+ {#if hasProFeatures}
521
+ <ProFeatures />
522
+ {/if}
523
+ {:else}
524
+ <ActivationScreen />
525
+ {/if}
526
+ ```
527
+
528
+ ## Error Handling
529
+
530
+ ```typescript
531
+ import { activate } from '@licenseseat/tauri-plugin';
532
+
533
+ try {
534
+ const license = await activate(key);
535
+ showSuccess('License activated!');
536
+ } catch (error) {
537
+ // Error is a string message from the backend
538
+ const message = error as string;
539
+
540
+ if (message.includes('invalid')) {
541
+ showError('Invalid license key');
542
+ } else if (message.includes('limit')) {
543
+ showError('Device limit reached. Deactivate another device first.');
544
+ } else if (message.includes('expired')) {
545
+ showError('This license has expired');
546
+ } else if (message.includes('network')) {
547
+ showError('Network error. Please check your connection.');
548
+ } else {
549
+ showError(`Activation failed: ${message}`);
550
+ }
551
+ }
552
+ ```
553
+
554
+ ## Security
555
+
556
+ ### API Key Protection
557
+
558
+ Your API key is stored in `tauri.conf.json` and compiled into your app binary. It is not exposed to the JavaScript frontend.
559
+
560
+ ### Permissions
561
+
562
+ The plugin uses Tauri's permission system. Available permissions:
563
+
564
+ | Permission | Description |
565
+ |------------|-------------|
566
+ | `licenseseat:default` | All commands (recommended) |
567
+ | `licenseseat:allow-activate` | Only activation |
568
+ | `licenseseat:allow-validate` | Only validation |
569
+ | `licenseseat:allow-deactivate` | Only deactivation |
570
+ | `licenseseat:allow-status` | Only status checks |
571
+ | `licenseseat:allow-entitlements` | Only entitlement checks |
572
+
573
+ ### Device Fingerprinting
574
+
575
+ The SDK generates a stable device ID based on hardware characteristics. This ID is used to:
576
+ - Track seat usage
577
+ - Prevent unauthorized device transfers
578
+ - Enable offline validation
579
+
580
+ The device ID is not personally identifiable.
581
+
582
+ ## Troubleshooting
583
+
584
+ ### Plugin Not Loading
585
+
586
+ 1. Ensure the plugin is registered in `main.rs`:
587
+ ```rust
588
+ .plugin(tauri_plugin_licenseseat::init())
589
+ ```
590
+
591
+ 2. Check that permissions are added to your capability file.
592
+
593
+ 3. Rebuild the Rust backend:
594
+ ```bash
595
+ cd src-tauri && cargo build
596
+ ```
597
+
598
+ ### "Command not found" Error
599
+
600
+ Make sure you've installed the JS bindings:
601
+ ```bash
602
+ npm add @licenseseat/tauri-plugin
603
+ ```
604
+
605
+ ### Network Errors
606
+
607
+ 1. Check your API key is correct
608
+ 2. Verify network connectivity
609
+ 3. Enable debug mode for detailed logs:
610
+ ```json
611
+ { "plugins": { "licenseseat": { "debug": true } } }
612
+ ```
613
+
614
+ ### Offline Validation Not Working
615
+
616
+ 1. Ensure the `offline` feature is enabled (it's built-in for the Tauri plugin)
617
+ 2. Check that `offlineFallbackMode` is set to `"allow_offline"` or `"offline_first"`
618
+ 3. Verify `maxOfflineDays` is greater than 0
619
+
620
+ ### Debug Logging
621
+
622
+ Enable debug mode to see detailed SDK logs:
623
+
624
+ ```json
625
+ {
626
+ "plugins": {
627
+ "licenseseat": {
628
+ "debug": true
629
+ }
630
+ }
631
+ }
632
+ ```
633
+
634
+ Then check the Tauri console output for `[licenseseat]` prefixed messages.
635
+
636
+ ## License
637
+
638
+ MIT License. See [LICENSE](../../LICENSE) for details.
@@ -0,0 +1,178 @@
1
+ /**
2
+ * LicenseSeat Tauri Plugin - TypeScript Bindings
3
+ *
4
+ * @module @licenseseat/tauri-plugin
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { activate, getStatus, hasEntitlement } from '@licenseseat/tauri-plugin';
9
+ *
10
+ * // Activate a license
11
+ * const license = await activate('YOUR-LICENSE-KEY');
12
+ *
13
+ * // Check status
14
+ * const status = await getStatus();
15
+ * console.log(status.status); // 'active' | 'inactive' | 'invalid' | ...
16
+ *
17
+ * // Check entitlements
18
+ * if (await hasEntitlement('pro-features')) {
19
+ * // Enable pro features
20
+ * }
21
+ * ```
22
+ */
23
+ /** License activation response */
24
+ export interface License {
25
+ licenseKey: string;
26
+ deviceId: string;
27
+ activationId: string;
28
+ activatedAt: string;
29
+ }
30
+ /** License status response */
31
+ export interface LicenseStatus {
32
+ status: 'active' | 'inactive' | 'invalid' | 'pending' | 'offlineValid' | 'offlineInvalid';
33
+ message?: string;
34
+ license?: string;
35
+ device?: string;
36
+ activatedAt?: string;
37
+ lastValidated?: string;
38
+ }
39
+ /** Entitlement check response */
40
+ export interface EntitlementStatus {
41
+ active: boolean;
42
+ reason?: 'nolicense' | 'notfound' | 'expired';
43
+ expiresAt?: string;
44
+ }
45
+ /** Activation options */
46
+ export interface ActivationOptions {
47
+ deviceId?: string;
48
+ deviceName?: string;
49
+ metadata?: Record<string, unknown>;
50
+ }
51
+ /** Validation result from the API */
52
+ export interface ValidationResult {
53
+ object: string;
54
+ valid: boolean;
55
+ code?: string;
56
+ message?: string;
57
+ license: {
58
+ key: string;
59
+ status: string;
60
+ planKey: string;
61
+ activeEntitlements: Array<{
62
+ key: string;
63
+ expiresAt?: string;
64
+ }>;
65
+ };
66
+ }
67
+ /** Plugin error */
68
+ export interface LicenseSeatError {
69
+ code?: string;
70
+ message: string;
71
+ status?: number;
72
+ }
73
+ /**
74
+ * Activate a license key.
75
+ *
76
+ * @param licenseKey - The license key to activate
77
+ * @param options - Optional activation options
78
+ * @returns The activated license details
79
+ * @throws {LicenseSeatError} If activation fails
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * const license = await activate('XXXX-XXXX-XXXX-XXXX');
84
+ * console.log(`Activated on device: ${license.deviceId}`);
85
+ * ```
86
+ */
87
+ export declare function activate(licenseKey: string, options?: ActivationOptions): Promise<License>;
88
+ /**
89
+ * Validate the current license.
90
+ *
91
+ * @returns The validation result
92
+ * @throws {LicenseSeatError} If validation fails
93
+ */
94
+ export declare function validate(): Promise<ValidationResult>;
95
+ /**
96
+ * Deactivate the current license.
97
+ *
98
+ * @throws {LicenseSeatError} If deactivation fails
99
+ */
100
+ export declare function deactivate(): Promise<void>;
101
+ /**
102
+ * Send a heartbeat for the current license.
103
+ *
104
+ * @throws {LicenseSeatError} If heartbeat fails
105
+ */
106
+ export declare function heartbeat(): Promise<void>;
107
+ /**
108
+ * Get the current license status.
109
+ *
110
+ * @returns The current status
111
+ */
112
+ export declare function getStatus(): Promise<LicenseStatus>;
113
+ /**
114
+ * Check if an entitlement is active.
115
+ *
116
+ * @param entitlementKey - The entitlement key to check
117
+ * @returns The entitlement status with details
118
+ */
119
+ export declare function checkEntitlement(entitlementKey: string): Promise<EntitlementStatus>;
120
+ /**
121
+ * Check if an entitlement is active (convenience function).
122
+ *
123
+ * @param entitlementKey - The entitlement key to check
124
+ * @returns true if the entitlement is active
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * if (await hasEntitlement('pro-features')) {
129
+ * enableProFeatures();
130
+ * }
131
+ * ```
132
+ */
133
+ export declare function hasEntitlement(entitlementKey: string): Promise<boolean>;
134
+ /**
135
+ * Get the current cached license.
136
+ *
137
+ * @returns The cached license or null if not activated
138
+ */
139
+ export declare function getLicense(): Promise<License | null>;
140
+ /**
141
+ * Reset the SDK state (clears cache).
142
+ */
143
+ export declare function reset(): Promise<void>;
144
+ /**
145
+ * LicenseSeat SDK class for object-oriented usage.
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * const sdk = new LicenseSeat();
150
+ *
151
+ * await sdk.activate('LICENSE-KEY');
152
+ * const status = await sdk.getStatus();
153
+ * ```
154
+ */
155
+ export declare class LicenseSeat {
156
+ activate: typeof activate;
157
+ validate: typeof validate;
158
+ deactivate: typeof deactivate;
159
+ heartbeat: typeof heartbeat;
160
+ getStatus: typeof getStatus;
161
+ checkEntitlement: typeof checkEntitlement;
162
+ hasEntitlement: typeof hasEntitlement;
163
+ getLicense: typeof getLicense;
164
+ reset: typeof reset;
165
+ }
166
+ declare const _default: {
167
+ activate: typeof activate;
168
+ validate: typeof validate;
169
+ deactivate: typeof deactivate;
170
+ heartbeat: typeof heartbeat;
171
+ getStatus: typeof getStatus;
172
+ checkEntitlement: typeof checkEntitlement;
173
+ hasEntitlement: typeof hasEntitlement;
174
+ getLicense: typeof getLicense;
175
+ reset: typeof reset;
176
+ };
177
+ export default _default;
178
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../guest-js/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,161 @@
1
+ /**
2
+ * LicenseSeat Tauri Plugin - TypeScript Bindings
3
+ *
4
+ * @module @licenseseat/tauri-plugin
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { activate, getStatus, hasEntitlement } from '@licenseseat/tauri-plugin';
9
+ *
10
+ * // Activate a license
11
+ * const license = await activate('YOUR-LICENSE-KEY');
12
+ *
13
+ * // Check status
14
+ * const status = await getStatus();
15
+ * console.log(status.status); // 'active' | 'inactive' | 'invalid' | ...
16
+ *
17
+ * // Check entitlements
18
+ * if (await hasEntitlement('pro-features')) {
19
+ * // Enable pro features
20
+ * }
21
+ * ```
22
+ */
23
+ import { invoke } from '@tauri-apps/api/core';
24
+ // ============================================================================
25
+ // API Functions
26
+ // ============================================================================
27
+ /**
28
+ * Activate a license key.
29
+ *
30
+ * @param licenseKey - The license key to activate
31
+ * @param options - Optional activation options
32
+ * @returns The activated license details
33
+ * @throws {LicenseSeatError} If activation fails
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const license = await activate('XXXX-XXXX-XXXX-XXXX');
38
+ * console.log(`Activated on device: ${license.deviceId}`);
39
+ * ```
40
+ */
41
+ export async function activate(licenseKey, options) {
42
+ return invoke('plugin:licenseseat|activate', {
43
+ licenseKey,
44
+ options,
45
+ });
46
+ }
47
+ /**
48
+ * Validate the current license.
49
+ *
50
+ * @returns The validation result
51
+ * @throws {LicenseSeatError} If validation fails
52
+ */
53
+ export async function validate() {
54
+ return invoke('plugin:licenseseat|validate');
55
+ }
56
+ /**
57
+ * Deactivate the current license.
58
+ *
59
+ * @throws {LicenseSeatError} If deactivation fails
60
+ */
61
+ export async function deactivate() {
62
+ return invoke('plugin:licenseseat|deactivate');
63
+ }
64
+ /**
65
+ * Send a heartbeat for the current license.
66
+ *
67
+ * @throws {LicenseSeatError} If heartbeat fails
68
+ */
69
+ export async function heartbeat() {
70
+ return invoke('plugin:licenseseat|heartbeat');
71
+ }
72
+ /**
73
+ * Get the current license status.
74
+ *
75
+ * @returns The current status
76
+ */
77
+ export async function getStatus() {
78
+ return invoke('plugin:licenseseat|get_status');
79
+ }
80
+ /**
81
+ * Check if an entitlement is active.
82
+ *
83
+ * @param entitlementKey - The entitlement key to check
84
+ * @returns The entitlement status with details
85
+ */
86
+ export async function checkEntitlement(entitlementKey) {
87
+ return invoke('plugin:licenseseat|check_entitlement', {
88
+ entitlementKey,
89
+ });
90
+ }
91
+ /**
92
+ * Check if an entitlement is active (convenience function).
93
+ *
94
+ * @param entitlementKey - The entitlement key to check
95
+ * @returns true if the entitlement is active
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * if (await hasEntitlement('pro-features')) {
100
+ * enableProFeatures();
101
+ * }
102
+ * ```
103
+ */
104
+ export async function hasEntitlement(entitlementKey) {
105
+ return invoke('plugin:licenseseat|has_entitlement', {
106
+ entitlementKey,
107
+ });
108
+ }
109
+ /**
110
+ * Get the current cached license.
111
+ *
112
+ * @returns The cached license or null if not activated
113
+ */
114
+ export async function getLicense() {
115
+ return invoke('plugin:licenseseat|get_license');
116
+ }
117
+ /**
118
+ * Reset the SDK state (clears cache).
119
+ */
120
+ export async function reset() {
121
+ return invoke('plugin:licenseseat|reset');
122
+ }
123
+ // ============================================================================
124
+ // Convenience Class
125
+ // ============================================================================
126
+ /**
127
+ * LicenseSeat SDK class for object-oriented usage.
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const sdk = new LicenseSeat();
132
+ *
133
+ * await sdk.activate('LICENSE-KEY');
134
+ * const status = await sdk.getStatus();
135
+ * ```
136
+ */
137
+ export class LicenseSeat {
138
+ constructor() {
139
+ this.activate = activate;
140
+ this.validate = validate;
141
+ this.deactivate = deactivate;
142
+ this.heartbeat = heartbeat;
143
+ this.getStatus = getStatus;
144
+ this.checkEntitlement = checkEntitlement;
145
+ this.hasEntitlement = hasEntitlement;
146
+ this.getLicense = getLicense;
147
+ this.reset = reset;
148
+ }
149
+ }
150
+ // Default export
151
+ export default {
152
+ activate,
153
+ validate,
154
+ deactivate,
155
+ heartbeat,
156
+ getStatus,
157
+ checkEntitlement,
158
+ hasEntitlement,
159
+ getLicense,
160
+ reset,
161
+ };
@@ -0,0 +1,238 @@
1
+ /**
2
+ * LicenseSeat Tauri Plugin - TypeScript Bindings
3
+ *
4
+ * @module @licenseseat/tauri-plugin
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { activate, getStatus, hasEntitlement } from '@licenseseat/tauri-plugin';
9
+ *
10
+ * // Activate a license
11
+ * const license = await activate('YOUR-LICENSE-KEY');
12
+ *
13
+ * // Check status
14
+ * const status = await getStatus();
15
+ * console.log(status.status); // 'active' | 'inactive' | 'invalid' | ...
16
+ *
17
+ * // Check entitlements
18
+ * if (await hasEntitlement('pro-features')) {
19
+ * // Enable pro features
20
+ * }
21
+ * ```
22
+ */
23
+
24
+ import { invoke } from '@tauri-apps/api/core';
25
+
26
+ // ============================================================================
27
+ // Types
28
+ // ============================================================================
29
+
30
+ /** License activation response */
31
+ export interface License {
32
+ licenseKey: string;
33
+ deviceId: string;
34
+ activationId: string;
35
+ activatedAt: string;
36
+ }
37
+
38
+ /** License status response */
39
+ export interface LicenseStatus {
40
+ status: 'active' | 'inactive' | 'invalid' | 'pending' | 'offlineValid' | 'offlineInvalid';
41
+ message?: string;
42
+ license?: string;
43
+ device?: string;
44
+ activatedAt?: string;
45
+ lastValidated?: string;
46
+ }
47
+
48
+ /** Entitlement check response */
49
+ export interface EntitlementStatus {
50
+ active: boolean;
51
+ reason?: 'nolicense' | 'notfound' | 'expired';
52
+ expiresAt?: string;
53
+ }
54
+
55
+ /** Activation options */
56
+ export interface ActivationOptions {
57
+ deviceId?: string;
58
+ deviceName?: string;
59
+ metadata?: Record<string, unknown>;
60
+ }
61
+
62
+ /** Validation result from the API */
63
+ export interface ValidationResult {
64
+ object: string;
65
+ valid: boolean;
66
+ code?: string;
67
+ message?: string;
68
+ license: {
69
+ key: string;
70
+ status: string;
71
+ planKey: string;
72
+ activeEntitlements: Array<{
73
+ key: string;
74
+ expiresAt?: string;
75
+ }>;
76
+ };
77
+ }
78
+
79
+ /** Plugin error */
80
+ export interface LicenseSeatError {
81
+ code?: string;
82
+ message: string;
83
+ status?: number;
84
+ }
85
+
86
+ // ============================================================================
87
+ // API Functions
88
+ // ============================================================================
89
+
90
+ /**
91
+ * Activate a license key.
92
+ *
93
+ * @param licenseKey - The license key to activate
94
+ * @param options - Optional activation options
95
+ * @returns The activated license details
96
+ * @throws {LicenseSeatError} If activation fails
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * const license = await activate('XXXX-XXXX-XXXX-XXXX');
101
+ * console.log(`Activated on device: ${license.deviceId}`);
102
+ * ```
103
+ */
104
+ export async function activate(
105
+ licenseKey: string,
106
+ options?: ActivationOptions
107
+ ): Promise<License> {
108
+ return invoke<License>('plugin:licenseseat|activate', {
109
+ licenseKey,
110
+ options,
111
+ });
112
+ }
113
+
114
+ /**
115
+ * Validate the current license.
116
+ *
117
+ * @returns The validation result
118
+ * @throws {LicenseSeatError} If validation fails
119
+ */
120
+ export async function validate(): Promise<ValidationResult> {
121
+ return invoke<ValidationResult>('plugin:licenseseat|validate');
122
+ }
123
+
124
+ /**
125
+ * Deactivate the current license.
126
+ *
127
+ * @throws {LicenseSeatError} If deactivation fails
128
+ */
129
+ export async function deactivate(): Promise<void> {
130
+ return invoke<void>('plugin:licenseseat|deactivate');
131
+ }
132
+
133
+ /**
134
+ * Send a heartbeat for the current license.
135
+ *
136
+ * @throws {LicenseSeatError} If heartbeat fails
137
+ */
138
+ export async function heartbeat(): Promise<void> {
139
+ return invoke<void>('plugin:licenseseat|heartbeat');
140
+ }
141
+
142
+ /**
143
+ * Get the current license status.
144
+ *
145
+ * @returns The current status
146
+ */
147
+ export async function getStatus(): Promise<LicenseStatus> {
148
+ return invoke<LicenseStatus>('plugin:licenseseat|get_status');
149
+ }
150
+
151
+ /**
152
+ * Check if an entitlement is active.
153
+ *
154
+ * @param entitlementKey - The entitlement key to check
155
+ * @returns The entitlement status with details
156
+ */
157
+ export async function checkEntitlement(
158
+ entitlementKey: string
159
+ ): Promise<EntitlementStatus> {
160
+ return invoke<EntitlementStatus>('plugin:licenseseat|check_entitlement', {
161
+ entitlementKey,
162
+ });
163
+ }
164
+
165
+ /**
166
+ * Check if an entitlement is active (convenience function).
167
+ *
168
+ * @param entitlementKey - The entitlement key to check
169
+ * @returns true if the entitlement is active
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * if (await hasEntitlement('pro-features')) {
174
+ * enableProFeatures();
175
+ * }
176
+ * ```
177
+ */
178
+ export async function hasEntitlement(entitlementKey: string): Promise<boolean> {
179
+ return invoke<boolean>('plugin:licenseseat|has_entitlement', {
180
+ entitlementKey,
181
+ });
182
+ }
183
+
184
+ /**
185
+ * Get the current cached license.
186
+ *
187
+ * @returns The cached license or null if not activated
188
+ */
189
+ export async function getLicense(): Promise<License | null> {
190
+ return invoke<License | null>('plugin:licenseseat|get_license');
191
+ }
192
+
193
+ /**
194
+ * Reset the SDK state (clears cache).
195
+ */
196
+ export async function reset(): Promise<void> {
197
+ return invoke<void>('plugin:licenseseat|reset');
198
+ }
199
+
200
+ // ============================================================================
201
+ // Convenience Class
202
+ // ============================================================================
203
+
204
+ /**
205
+ * LicenseSeat SDK class for object-oriented usage.
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * const sdk = new LicenseSeat();
210
+ *
211
+ * await sdk.activate('LICENSE-KEY');
212
+ * const status = await sdk.getStatus();
213
+ * ```
214
+ */
215
+ export class LicenseSeat {
216
+ activate = activate;
217
+ validate = validate;
218
+ deactivate = deactivate;
219
+ heartbeat = heartbeat;
220
+ getStatus = getStatus;
221
+ checkEntitlement = checkEntitlement;
222
+ hasEntitlement = hasEntitlement;
223
+ getLicense = getLicense;
224
+ reset = reset;
225
+ }
226
+
227
+ // Default export
228
+ export default {
229
+ activate,
230
+ validate,
231
+ deactivate,
232
+ heartbeat,
233
+ getStatus,
234
+ checkEntitlement,
235
+ hasEntitlement,
236
+ getLicense,
237
+ reset,
238
+ };
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@licenseseat/tauri-plugin",
3
+ "version": "0.1.0",
4
+ "description": "Tauri plugin for LicenseSeat software licensing",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "guest-js"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "tauri",
25
+ "tauri-plugin",
26
+ "licensing",
27
+ "license-management",
28
+ "software-licensing",
29
+ "licenseseat"
30
+ ],
31
+ "author": "LicenseSeat <hello@licenseseat.com>",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/licenseseat/licenseseat-rust.git",
36
+ "directory": "crates/tauri-plugin-licenseseat"
37
+ },
38
+ "homepage": "https://licenseseat.com",
39
+ "bugs": {
40
+ "url": "https://github.com/licenseseat/licenseseat-rust/issues"
41
+ },
42
+ "peerDependencies": {
43
+ "@tauri-apps/api": ">=2.0.0"
44
+ },
45
+ "devDependencies": {
46
+ "@tauri-apps/api": "^2.0.0",
47
+ "typescript": "^5.0.0"
48
+ }
49
+ }