@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 +638 -0
- package/dist/index.d.ts +178 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +161 -0
- package/guest-js/index.ts +238 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
# LicenseSeat Tauri Plugin
|
|
2
|
+
|
|
3
|
+
[](https://crates.io/crates/tauri-plugin-licenseseat)
|
|
4
|
+
[](https://www.npmjs.com/package/@licenseseat/tauri-plugin)
|
|
5
|
+
[](https://docs.rs/tauri-plugin-licenseseat)
|
|
6
|
+
[](../../LICENSE)
|
|
7
|
+
[](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.
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|