@permitcore/permitcore 1.0.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 +229 -0
- package/dist/index.d.ts +174 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +440 -0
- package/dist/index.js.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# PermitCore Node.js SDK
|
|
2
|
+
|
|
3
|
+
Official Node.js/TypeScript client for [PermitCore](https://permitcore.dev) license management.
|
|
4
|
+
|
|
5
|
+
**Requirements:** Node.js 16+, zero runtime dependencies (uses the global `fetch` and Node's
|
|
6
|
+
built-in `crypto` module for offline license token verification).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install permitcore
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { PermitCoreClient } from 'permitcore';
|
|
22
|
+
|
|
23
|
+
const client = new PermitCoreClient('https://your-instance.com');
|
|
24
|
+
const result = await client.validate('PERMIT-XXXX-XXXX-XXXX-XXXX');
|
|
25
|
+
|
|
26
|
+
if (result.isValid) {
|
|
27
|
+
console.log(`Valid! Product: ${result.productName}`);
|
|
28
|
+
if (PermitCoreClient.hasFeature(result, 'export')) {
|
|
29
|
+
enableExport();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Validate
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
const result = await client.validate(licenseKey, '2.3.1'); // version is optional
|
|
40
|
+
|
|
41
|
+
// result.isValid boolean
|
|
42
|
+
// result.productName string | undefined
|
|
43
|
+
// result.remainingActivations number | undefined
|
|
44
|
+
// result.expiresAt string | undefined (ISO 8601)
|
|
45
|
+
// result.features string[] | undefined
|
|
46
|
+
// result.customFields Record<string, string> | undefined
|
|
47
|
+
// result.isTrial boolean | undefined
|
|
48
|
+
// result.trialDaysRemaining number | undefined
|
|
49
|
+
// result.nodeLocked boolean | undefined
|
|
50
|
+
// result.offlineGraceDays number | undefined
|
|
51
|
+
// result.minVersion string | undefined
|
|
52
|
+
// result.maxVersion string | undefined
|
|
53
|
+
// result.message string | undefined
|
|
54
|
+
// result.isOffline boolean | undefined (true when served from local cache)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`validate()` never consumes an activation slot. It falls back to the local disk cache when the
|
|
58
|
+
server is unreachable, as long as the license has `offlineGraceDays` configured. Passing
|
|
59
|
+
`version` lets the server enforce `minVersion`/`maxVersion` restrictions on the license.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Activate
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const result = await client.activate(
|
|
67
|
+
licenseKey,
|
|
68
|
+
undefined, // deviceId — auto-generated HWID when omitted
|
|
69
|
+
'Production Server #1', // deviceName
|
|
70
|
+
'2.3.1', // version (optional)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (!result.isValid) {
|
|
74
|
+
throw new Error(`Activation failed: ${result.message}`);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Call `activate()` **once** per installation. Use `validate()` on every subsequent launch.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Meter (usage events)
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Record a single API call
|
|
86
|
+
const recorded = await client.meter(licenseKey, 'api_call');
|
|
87
|
+
|
|
88
|
+
// Record bulk usage with metadata
|
|
89
|
+
const recorded2 = await client.meter(licenseKey, 'export', 5, { format: 'pdf', pages: 12 });
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Returns `true` if the event was recorded on the server, `false` on any failure (network error, or
|
|
93
|
+
the server rejecting the event).
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Floating licenses
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// Check out a seat at session start
|
|
101
|
+
const session = await client.checkout(licenseKey);
|
|
102
|
+
if (!session.success) {
|
|
103
|
+
throw new Error(`No seats available: ${session.message}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const token = session.sessionToken!;
|
|
107
|
+
|
|
108
|
+
// Heartbeat every 4-5 minutes to keep the seat alive
|
|
109
|
+
await client.heartbeat(token);
|
|
110
|
+
|
|
111
|
+
// Release the seat when done
|
|
112
|
+
await client.checkin(token);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Offline license tokens
|
|
118
|
+
|
|
119
|
+
An offline activation token (`pc_offline_v1.<payload>.<signature>`) lets your app verify a
|
|
120
|
+
license with **zero network calls**, using ECDSA P-256 signature verification against your
|
|
121
|
+
tenant's public key (`GET /api/v1/{tenantSlug}/public-key`). Useful for air-gapped or
|
|
122
|
+
intermittently-connected deployments.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// Pure local verification — no network call. Never throws.
|
|
126
|
+
const result = client.verifyOfflineToken(token, publicKeyBase64);
|
|
127
|
+
|
|
128
|
+
if (result.isValid) {
|
|
129
|
+
console.log(`Valid! Product: ${result.productName}`);
|
|
130
|
+
} else {
|
|
131
|
+
console.log(`Invalid: ${result.message}`);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// Verify + bind to this device + persist locally (call once, e.g. at install time)
|
|
137
|
+
const result = client.activateOffline(token, publicKeyBase64, deviceId);
|
|
138
|
+
|
|
139
|
+
// On every later launch — no token needed, reads the local cache, still no network call
|
|
140
|
+
const result2 = client.validateOffline(deviceId);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// Optional: ask the server to verify the token AND check its revocation status (requires network)
|
|
145
|
+
const result3 = await client.verifyOfflineOnline(token);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
All four methods return an `OfflineTokenResult` — on a valid token, the payload fields
|
|
149
|
+
(`tokenId`, `tenantSlug`, `tenantId`, `licenseId`, `deviceId`, `deviceName`, `productName`,
|
|
150
|
+
`maxActivations`, `issuedAt`, `expiresAt`) are merged in alongside `isValid`/`message`.
|
|
151
|
+
`verifyOfflineToken()` and `validateOffline()` never throw — malformed, tampered, expired, or
|
|
152
|
+
missing input all come back as `isValid: false` with a descriptive `message`.
|
|
153
|
+
|
|
154
|
+
`activateOffline()`'s local cache is stored under the OS temp directory as
|
|
155
|
+
`.permitcore_offline_<hash>` (same convention as the `validate()`/`activate()` cache, keyed by
|
|
156
|
+
device ID instead of license key).
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Version enforcement
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
const result = await client.validate(licenseKey);
|
|
164
|
+
|
|
165
|
+
const myVersion = '2.3.0';
|
|
166
|
+
if (result.minVersion && myVersion < result.minVersion) {
|
|
167
|
+
throw new Error(`Please update to version ${result.minVersion} or newer.`);
|
|
168
|
+
}
|
|
169
|
+
if (result.maxVersion && myVersion > result.maxVersion) {
|
|
170
|
+
throw new Error(`This build (${myVersion}) is not licensed for versions above ${result.maxVersion}.`);
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Pass a `version` argument to `validate()`/`activate()` to also have the *server* enforce this —
|
|
175
|
+
otherwise only client-side comparison happens.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Offline grace pattern
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const result = await client.validate(licenseKey); // falls back to cache automatically
|
|
183
|
+
|
|
184
|
+
if (!result.isValid) {
|
|
185
|
+
throw new Error(`License invalid: ${result.message}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (result.isOffline) {
|
|
189
|
+
// Server unreachable — running on cached result
|
|
190
|
+
showNotice('Running in offline mode. Connect to the internet to refresh your license.');
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The cache is stored under the OS temp directory as `.permitcore_cache_<hash>`. It expires after
|
|
195
|
+
`offlineGraceDays` days.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Constructor options
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const client = new PermitCoreClient({
|
|
203
|
+
baseUrl: 'https://your-instance.com',
|
|
204
|
+
enableOfflineCache: true, // default — set false to always require network
|
|
205
|
+
timeoutMs: 5000, // HTTP timeout in milliseconds
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## LicenseResult reference
|
|
212
|
+
|
|
213
|
+
| Field | Type | Description |
|
|
214
|
+
|---|---|---|
|
|
215
|
+
| `isValid` | `boolean` | True if the license is active and valid |
|
|
216
|
+
| `productName` | `string?` | Product the license belongs to |
|
|
217
|
+
| `remainingActivations` | `number?` | Slots left before MaxActivations is reached |
|
|
218
|
+
| `expiresAt` | `string?` | Expiry date (ISO 8601 UTC), undefined if perpetual |
|
|
219
|
+
| `features` | `string[]?` | Feature flag list, e.g. `['export', 'api']` |
|
|
220
|
+
| `customFields` | `Record<string, string>?` | Arbitrary key/value metadata set on the license |
|
|
221
|
+
| `isTrial` | `boolean?` | True for trial licenses |
|
|
222
|
+
| `trialDaysRemaining` | `number?` | Days until trial expires |
|
|
223
|
+
| `nodeLocked` | `boolean?` | True if bound to a specific device |
|
|
224
|
+
| `offlineGraceDays` | `number?` | How many days the cache is valid |
|
|
225
|
+
| `minVersion` / `maxVersion` | `string?` | Version enforcement bounds |
|
|
226
|
+
| `message` | `string?` | Reason when `isValid = false` |
|
|
227
|
+
| `isOffline` | `boolean?` | True when result came from local cache |
|
|
228
|
+
|
|
229
|
+
`PermitCoreClient.hasFeature(result, feature)` — static, case-insensitive feature check.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
export interface LicenseResult {
|
|
2
|
+
isValid: boolean;
|
|
3
|
+
productName?: string;
|
|
4
|
+
remainingActivations?: number;
|
|
5
|
+
expiresAt?: string;
|
|
6
|
+
message?: string;
|
|
7
|
+
customFields?: Record<string, string>;
|
|
8
|
+
vendorWarning?: string;
|
|
9
|
+
features?: string[];
|
|
10
|
+
isTrial?: boolean;
|
|
11
|
+
trialDaysRemaining?: number;
|
|
12
|
+
nodeLocked?: boolean;
|
|
13
|
+
offlineGraceDays?: number;
|
|
14
|
+
minVersion?: string;
|
|
15
|
+
maxVersion?: string;
|
|
16
|
+
/**
|
|
17
|
+
* ECDSA-signed `pc_grace_v1` token (non-null only when the license has an offline grace
|
|
18
|
+
* period configured and this call succeeded). The SDK caches this — not the rest of this
|
|
19
|
+
* response — and verifies it locally before trusting a cached result on a later offline
|
|
20
|
+
* call. See `verifyGraceCacheToken`.
|
|
21
|
+
*/
|
|
22
|
+
offlineCacheToken?: string | null;
|
|
23
|
+
/** True when served from local cache (server unreachable) */
|
|
24
|
+
isOffline?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface MeterResult {
|
|
27
|
+
recorded: boolean;
|
|
28
|
+
message?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface FloatingSession {
|
|
31
|
+
success: boolean;
|
|
32
|
+
sessionToken?: string;
|
|
33
|
+
expiresAt?: string;
|
|
34
|
+
message?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface PermitCoreOptions {
|
|
37
|
+
baseUrl: string;
|
|
38
|
+
/** Enable offline grace period caching. Default: true */
|
|
39
|
+
enableOfflineCache?: boolean;
|
|
40
|
+
/** Request timeout in milliseconds. Default: 5000 */
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
}
|
|
43
|
+
/** Decoded payload of a `pc_offline_v1` offline activation token. */
|
|
44
|
+
export interface OfflineTokenPayload {
|
|
45
|
+
version: number;
|
|
46
|
+
tokenId: string;
|
|
47
|
+
tenantSlug: string;
|
|
48
|
+
tenantId: string;
|
|
49
|
+
licenseId: string;
|
|
50
|
+
licenseKeyHash: string;
|
|
51
|
+
deviceId: string;
|
|
52
|
+
deviceName?: string | null;
|
|
53
|
+
productName: string;
|
|
54
|
+
maxActivations: number;
|
|
55
|
+
issuedAt: string;
|
|
56
|
+
expiresAt: string;
|
|
57
|
+
}
|
|
58
|
+
export interface OfflineTokenResult {
|
|
59
|
+
isValid: boolean;
|
|
60
|
+
tokenId?: string;
|
|
61
|
+
tenantSlug?: string;
|
|
62
|
+
tenantId?: string;
|
|
63
|
+
licenseId?: string;
|
|
64
|
+
deviceId?: string;
|
|
65
|
+
deviceName?: string | null;
|
|
66
|
+
productName?: string;
|
|
67
|
+
maxActivations?: number;
|
|
68
|
+
issuedAt?: string;
|
|
69
|
+
expiresAt?: string;
|
|
70
|
+
message?: string;
|
|
71
|
+
}
|
|
72
|
+
/** Decoded payload of a `pc_grace_v1` offline grace-cache token. */
|
|
73
|
+
export interface GraceCachePayload {
|
|
74
|
+
version: number;
|
|
75
|
+
tenantSlug: string;
|
|
76
|
+
kid?: string | null;
|
|
77
|
+
licenseKeyHash: string;
|
|
78
|
+
deviceId?: string | null;
|
|
79
|
+
isValid: boolean;
|
|
80
|
+
productName?: string | null;
|
|
81
|
+
features?: string[] | null;
|
|
82
|
+
remainingActivations?: number | null;
|
|
83
|
+
expiresAt?: string | null;
|
|
84
|
+
issuedAt: string;
|
|
85
|
+
validUntil: string;
|
|
86
|
+
}
|
|
87
|
+
export interface GraceCacheResult {
|
|
88
|
+
isValid: boolean;
|
|
89
|
+
message?: string;
|
|
90
|
+
payload?: GraceCachePayload;
|
|
91
|
+
}
|
|
92
|
+
export declare class PermitCoreClient {
|
|
93
|
+
private readonly baseUrl;
|
|
94
|
+
private readonly opts;
|
|
95
|
+
constructor(baseUrlOrOptions: string | PermitCoreOptions);
|
|
96
|
+
/**
|
|
97
|
+
* Validates a license key. Does NOT consume an activation slot.
|
|
98
|
+
* Falls back to local cache when server is unreachable (offline grace period).
|
|
99
|
+
*/
|
|
100
|
+
validate(licenseKey: string, version?: string): Promise<LicenseResult>;
|
|
101
|
+
/**
|
|
102
|
+
* Validates AND activates the key on this device.
|
|
103
|
+
* Call only once per installation.
|
|
104
|
+
* @param deviceId Optional — auto-generated HWID is used when omitted.
|
|
105
|
+
*/
|
|
106
|
+
activate(licenseKey: string, deviceId?: string, deviceName?: string, version?: string): Promise<LicenseResult>;
|
|
107
|
+
/**
|
|
108
|
+
* Records a usage event for metered billing.
|
|
109
|
+
* Returns true if the event was recorded on the server, false on any failure
|
|
110
|
+
* (network error, or the server rejecting the event).
|
|
111
|
+
*/
|
|
112
|
+
meter(licenseKey: string, eventName: string, quantity?: number, meta?: Record<string, unknown>): Promise<boolean>;
|
|
113
|
+
checkout(licenseKey: string, deviceId?: string, deviceName?: string): Promise<FloatingSession>;
|
|
114
|
+
heartbeat(sessionToken: string): Promise<FloatingSession>;
|
|
115
|
+
checkin(sessionToken: string): Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* Verifies a `pc_offline_v1` offline activation token entirely locally — no network I/O.
|
|
118
|
+
* The signed bytes are the UTF-8 bytes of the base64url payload *string* (not the decoded
|
|
119
|
+
* JSON), and the signature is ECDSA P-256/SHA-256 in raw IEEE P1363 format (not ASN.1 DER),
|
|
120
|
+
* matching `OfflineActivationService.Sign` server-side. Never throws — malformed input,
|
|
121
|
+
* a signature mismatch, or an expired token all just produce `{ isValid: false }`.
|
|
122
|
+
*/
|
|
123
|
+
verifyOfflineToken(token: string, publicKeyBase64: string): OfflineTokenResult;
|
|
124
|
+
/**
|
|
125
|
+
* Verifies an offline token (see `verifyOfflineToken`), confirms it was issued for
|
|
126
|
+
* `deviceId`, and — on success — persists the verified payload locally so a later
|
|
127
|
+
* `validateOffline()` call can re-check it with zero network calls (e.g. on every app start).
|
|
128
|
+
*/
|
|
129
|
+
activateOffline(token: string, publicKeyBase64: string, deviceId: string): OfflineTokenResult;
|
|
130
|
+
/**
|
|
131
|
+
* Re-checks a previously `activateOffline()`-persisted token against the local clock and
|
|
132
|
+
* device id — no network call, no token needed. This is what a long-running app should call
|
|
133
|
+
* on every start once it has already offline-activated once.
|
|
134
|
+
*/
|
|
135
|
+
validateOffline(deviceId: string): OfflineTokenResult;
|
|
136
|
+
/**
|
|
137
|
+
* Thin online wrapper around `POST /api/v1/offline/verify` — lets the server additionally
|
|
138
|
+
* check token revocation status, which is impossible to verify purely locally. Not required
|
|
139
|
+
* for offline use; a convenience for apps that have connectivity and want the extra check.
|
|
140
|
+
*/
|
|
141
|
+
verifyOfflineOnline(token: string): Promise<OfflineTokenResult>;
|
|
142
|
+
/**
|
|
143
|
+
* Verifies a `pc_grace_v1` offline grace-cache token entirely locally — no network I/O.
|
|
144
|
+
* Exposed publicly so a custom integration (not using the built-in save/load-from-cache
|
|
145
|
+
* flow) can implement its own caching around the same verified primitive. Never throws —
|
|
146
|
+
* malformed input, a signature mismatch, or an expired grace period all just produce
|
|
147
|
+
* `{ isValid: false }`.
|
|
148
|
+
*/
|
|
149
|
+
verifyGraceCacheToken(token: string, publicKeyBase64: string): GraceCacheResult;
|
|
150
|
+
/**
|
|
151
|
+
* Returns a stable hardware fingerprint (SHA-256 of machine identifiers).
|
|
152
|
+
*/
|
|
153
|
+
static getHardwareId(): string;
|
|
154
|
+
/** Check if a validate/activate result includes a specific feature flag. */
|
|
155
|
+
static hasFeature(result: LicenseResult, feature: string): boolean;
|
|
156
|
+
private fetch;
|
|
157
|
+
/**
|
|
158
|
+
* [S-Grace1] Caches the SIGNED token, not the raw response — the server only issues
|
|
159
|
+
* `offlineCacheToken` when a grace period is configured, so a null/missing token here
|
|
160
|
+
* already means "nothing to cache," same as the old `offlineGraceDays` check. The
|
|
161
|
+
* unverified `tenantSlug` is read out of the token purely to pick which tenant's
|
|
162
|
+
* public key to fetch (see `extractUnverifiedTenantSlug`); nothing is trusted or
|
|
163
|
+
* persisted until `verifyGraceCacheToken` confirms the signature against that key.
|
|
164
|
+
*/
|
|
165
|
+
private saveToCache;
|
|
166
|
+
/**
|
|
167
|
+
* No network call here — verification uses only the public key persisted alongside the
|
|
168
|
+
* token at save time. This is the entire point: a hand-edited cache file (or one copied
|
|
169
|
+
* to another machine) fails ECDSA verification instead of silently working.
|
|
170
|
+
*/
|
|
171
|
+
private loadFromCache;
|
|
172
|
+
}
|
|
173
|
+
export default PermitCoreClient;
|
|
174
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.PermitCoreClient = void 0;
|
|
37
|
+
const crypto = __importStar(require("crypto"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
// ── Client ─────────────────────────────────────────────────────────────────
|
|
42
|
+
class PermitCoreClient {
|
|
43
|
+
constructor(baseUrlOrOptions) {
|
|
44
|
+
if (typeof baseUrlOrOptions === 'string') {
|
|
45
|
+
this.opts = { baseUrl: baseUrlOrOptions, enableOfflineCache: true, timeoutMs: 5000 };
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.opts = { enableOfflineCache: true, timeoutMs: 5000, ...baseUrlOrOptions };
|
|
49
|
+
}
|
|
50
|
+
this.baseUrl = this.opts.baseUrl.replace(/\/$/, '');
|
|
51
|
+
}
|
|
52
|
+
// ── Validate ─────────────────────────────────────────────────────────
|
|
53
|
+
/**
|
|
54
|
+
* Validates a license key. Does NOT consume an activation slot.
|
|
55
|
+
* Falls back to local cache when server is unreachable (offline grace period).
|
|
56
|
+
*/
|
|
57
|
+
async validate(licenseKey, version) {
|
|
58
|
+
try {
|
|
59
|
+
const res = await this.fetch(`${this.baseUrl}/api/v1/validate`, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'Content-Type': 'application/json' },
|
|
62
|
+
body: JSON.stringify({ licenseKey, ...(version ? { version } : {}) }),
|
|
63
|
+
});
|
|
64
|
+
if (res.isValid)
|
|
65
|
+
await this.saveToCache(licenseKey, res);
|
|
66
|
+
return res;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
const cached = this.loadFromCache(licenseKey);
|
|
70
|
+
if (cached)
|
|
71
|
+
return cached;
|
|
72
|
+
return { isValid: false, message: 'Cannot reach license server.', isOffline: true };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// ── Activate ─────────────────────────────────────────────────────────
|
|
76
|
+
/**
|
|
77
|
+
* Validates AND activates the key on this device.
|
|
78
|
+
* Call only once per installation.
|
|
79
|
+
* @param deviceId Optional — auto-generated HWID is used when omitted.
|
|
80
|
+
*/
|
|
81
|
+
async activate(licenseKey, deviceId, deviceName, version) {
|
|
82
|
+
const hwid = deviceId ?? PermitCoreClient.getHardwareId();
|
|
83
|
+
try {
|
|
84
|
+
// [S-Nonce] Fetch a single-use nonce first (replay-attack protection)
|
|
85
|
+
const { nonce } = await this.fetch(`${this.baseUrl}/api/v1/nonce`);
|
|
86
|
+
const res = await this.fetch(`${this.baseUrl}/api/v1/activate`, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
headers: { 'Content-Type': 'application/json' },
|
|
89
|
+
body: JSON.stringify({ licenseKey, deviceId: hwid, deviceName, version, nonce }),
|
|
90
|
+
});
|
|
91
|
+
if (res.isValid)
|
|
92
|
+
await this.saveToCache(licenseKey, res);
|
|
93
|
+
return res;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// [S-Continuity] If this device already activated successfully before (e.g. an app
|
|
97
|
+
// that re-runs activate() on every launch, or a reinstall that kept the cache file),
|
|
98
|
+
// fall back to that cached result instead of failing outright.
|
|
99
|
+
const cached = this.loadFromCache(licenseKey);
|
|
100
|
+
if (cached)
|
|
101
|
+
return cached;
|
|
102
|
+
return { isValid: false, message: 'Cannot reach license server.', isOffline: true };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// ── Meter ────────────────────────────────────────────────────────────
|
|
106
|
+
/**
|
|
107
|
+
* Records a usage event for metered billing.
|
|
108
|
+
* Returns true if the event was recorded on the server, false on any failure
|
|
109
|
+
* (network error, or the server rejecting the event).
|
|
110
|
+
*/
|
|
111
|
+
async meter(licenseKey, eventName, quantity = 1, meta) {
|
|
112
|
+
try {
|
|
113
|
+
const res = await this.fetch(`${this.baseUrl}/api/v1/meter`, {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers: { 'Content-Type': 'application/json' },
|
|
116
|
+
body: JSON.stringify({ licenseKey, eventName, quantity, ...(meta ? { meta } : {}) }),
|
|
117
|
+
});
|
|
118
|
+
return res.recorded ?? false;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// ── Floating ─────────────────────────────────────────────────────────
|
|
125
|
+
async checkout(licenseKey, deviceId, deviceName) {
|
|
126
|
+
const hwid = deviceId ?? PermitCoreClient.getHardwareId();
|
|
127
|
+
return this.fetch(`${this.baseUrl}/api/v1/float/checkout`, {
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers: { 'Content-Type': 'application/json' },
|
|
130
|
+
body: JSON.stringify({ licenseKey, deviceId: hwid, deviceName }),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
// [S-Float] Session token in the POST body — never the URL path — matches
|
|
134
|
+
// FloatingController.Heartbeat's FloatingTokenRequest exactly.
|
|
135
|
+
async heartbeat(sessionToken) {
|
|
136
|
+
return this.fetch(`${this.baseUrl}/api/v1/float/heartbeat`, {
|
|
137
|
+
method: 'POST',
|
|
138
|
+
headers: { 'Content-Type': 'application/json' },
|
|
139
|
+
body: JSON.stringify({ sessionToken }),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// [S-Float] Session token in the POST body — never the URL path — matches
|
|
143
|
+
// FloatingController.Checkin's FloatingTokenRequest exactly (also: it's a POST, not DELETE).
|
|
144
|
+
async checkin(sessionToken) {
|
|
145
|
+
await this.fetch(`${this.baseUrl}/api/v1/float/checkin`, {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
headers: { 'Content-Type': 'application/json' },
|
|
148
|
+
body: JSON.stringify({ sessionToken }),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// ── Offline activation tokens ───────────────────────────────────────
|
|
152
|
+
/**
|
|
153
|
+
* Verifies a `pc_offline_v1` offline activation token entirely locally — no network I/O.
|
|
154
|
+
* The signed bytes are the UTF-8 bytes of the base64url payload *string* (not the decoded
|
|
155
|
+
* JSON), and the signature is ECDSA P-256/SHA-256 in raw IEEE P1363 format (not ASN.1 DER),
|
|
156
|
+
* matching `OfflineActivationService.Sign` server-side. Never throws — malformed input,
|
|
157
|
+
* a signature mismatch, or an expired token all just produce `{ isValid: false }`.
|
|
158
|
+
*/
|
|
159
|
+
verifyOfflineToken(token, publicKeyBase64) {
|
|
160
|
+
try {
|
|
161
|
+
const parts = token.split('.');
|
|
162
|
+
if (parts.length !== 3 || parts[0] !== 'pc_offline_v1') {
|
|
163
|
+
return { isValid: false, message: 'Malformed offline token.' };
|
|
164
|
+
}
|
|
165
|
+
const payloadBytes = Buffer.from(parts[1], 'utf8');
|
|
166
|
+
const signature = Buffer.from(parts[2], 'base64url');
|
|
167
|
+
const publicKey = crypto.createPublicKey({
|
|
168
|
+
key: Buffer.from(publicKeyBase64, 'base64'),
|
|
169
|
+
format: 'der',
|
|
170
|
+
type: 'spki',
|
|
171
|
+
});
|
|
172
|
+
const signatureOk = crypto.verify('sha256', payloadBytes, { key: publicKey, dsaEncoding: 'ieee-p1363' }, signature);
|
|
173
|
+
if (!signatureOk)
|
|
174
|
+
return { isValid: false, message: 'Offline token signature is invalid.' };
|
|
175
|
+
const json = Buffer.from(parts[1], 'base64url').toString('utf8');
|
|
176
|
+
const payload = JSON.parse(json);
|
|
177
|
+
if (new Date(payload.expiresAt).getTime() < Date.now()) {
|
|
178
|
+
return { isValid: false, message: 'Offline token has expired.' };
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
isValid: true,
|
|
182
|
+
tokenId: payload.tokenId,
|
|
183
|
+
tenantSlug: payload.tenantSlug,
|
|
184
|
+
tenantId: payload.tenantId,
|
|
185
|
+
licenseId: payload.licenseId,
|
|
186
|
+
deviceId: payload.deviceId,
|
|
187
|
+
deviceName: payload.deviceName,
|
|
188
|
+
productName: payload.productName,
|
|
189
|
+
maxActivations: payload.maxActivations,
|
|
190
|
+
issuedAt: payload.issuedAt,
|
|
191
|
+
expiresAt: payload.expiresAt,
|
|
192
|
+
message: 'Offline token is valid.',
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return { isValid: false, message: 'Failed to verify offline token.' };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Verifies an offline token (see `verifyOfflineToken`), confirms it was issued for
|
|
201
|
+
* `deviceId`, and — on success — persists the verified payload locally so a later
|
|
202
|
+
* `validateOffline()` call can re-check it with zero network calls (e.g. on every app start).
|
|
203
|
+
*/
|
|
204
|
+
activateOffline(token, publicKeyBase64, deviceId) {
|
|
205
|
+
const result = this.verifyOfflineToken(token, publicKeyBase64);
|
|
206
|
+
if (!result.isValid)
|
|
207
|
+
return result;
|
|
208
|
+
if (!result.deviceId || result.deviceId.toLowerCase() !== deviceId.toLowerCase()) {
|
|
209
|
+
return { isValid: false, message: 'Offline token was issued for a different device.' };
|
|
210
|
+
}
|
|
211
|
+
if (this.opts.enableOfflineCache) {
|
|
212
|
+
try {
|
|
213
|
+
fs.writeFileSync(getOfflineActivationPath(deviceId), JSON.stringify(result));
|
|
214
|
+
}
|
|
215
|
+
catch { /* non-critical */ }
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Re-checks a previously `activateOffline()`-persisted token against the local clock and
|
|
221
|
+
* device id — no network call, no token needed. This is what a long-running app should call
|
|
222
|
+
* on every start once it has already offline-activated once.
|
|
223
|
+
*/
|
|
224
|
+
validateOffline(deviceId) {
|
|
225
|
+
try {
|
|
226
|
+
const raw = fs.readFileSync(getOfflineActivationPath(deviceId), 'utf8');
|
|
227
|
+
const cached = JSON.parse(raw);
|
|
228
|
+
if (!cached.deviceId || cached.deviceId.toLowerCase() !== deviceId.toLowerCase()) {
|
|
229
|
+
return { isValid: false, message: 'No offline activation found for this device.' };
|
|
230
|
+
}
|
|
231
|
+
if (!cached.expiresAt || new Date(cached.expiresAt).getTime() < Date.now()) {
|
|
232
|
+
return { isValid: false, message: 'Offline activation has expired.' };
|
|
233
|
+
}
|
|
234
|
+
return { ...cached, isValid: true };
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return { isValid: false, message: 'No offline activation found for this device.' };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Thin online wrapper around `POST /api/v1/offline/verify` — lets the server additionally
|
|
242
|
+
* check token revocation status, which is impossible to verify purely locally. Not required
|
|
243
|
+
* for offline use; a convenience for apps that have connectivity and want the extra check.
|
|
244
|
+
*/
|
|
245
|
+
async verifyOfflineOnline(token) {
|
|
246
|
+
try {
|
|
247
|
+
const res = await this.fetch(`${this.baseUrl}/api/v1/offline/verify`, {
|
|
248
|
+
method: 'POST',
|
|
249
|
+
headers: { 'Content-Type': 'application/json' },
|
|
250
|
+
body: JSON.stringify({ token }),
|
|
251
|
+
});
|
|
252
|
+
return res;
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return { isValid: false, message: 'Cannot reach license server.' };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// ── Grace-cache token verification (pc_grace_v1, ECDSA P-256 / IEEE P1363) ──────────
|
|
259
|
+
// [S-Grace1] Same crypto convention as verifyOfflineToken above (raw IEEE P1363 signature
|
|
260
|
+
// over the UTF-8 bytes of the base64url payload *string*), different prefix and payload
|
|
261
|
+
// shape. Matches OfflineActivationService.VerifyGraceCache server-side byte-for-byte.
|
|
262
|
+
/**
|
|
263
|
+
* Verifies a `pc_grace_v1` offline grace-cache token entirely locally — no network I/O.
|
|
264
|
+
* Exposed publicly so a custom integration (not using the built-in save/load-from-cache
|
|
265
|
+
* flow) can implement its own caching around the same verified primitive. Never throws —
|
|
266
|
+
* malformed input, a signature mismatch, or an expired grace period all just produce
|
|
267
|
+
* `{ isValid: false }`.
|
|
268
|
+
*/
|
|
269
|
+
verifyGraceCacheToken(token, publicKeyBase64) {
|
|
270
|
+
try {
|
|
271
|
+
const parts = token.split('.');
|
|
272
|
+
if (parts.length !== 3 || parts[0] !== 'pc_grace_v1') {
|
|
273
|
+
return { isValid: false, message: 'Malformed grace cache token.' };
|
|
274
|
+
}
|
|
275
|
+
const payloadBytes = Buffer.from(parts[1], 'utf8');
|
|
276
|
+
const signature = Buffer.from(parts[2], 'base64url');
|
|
277
|
+
const publicKey = crypto.createPublicKey({
|
|
278
|
+
key: Buffer.from(publicKeyBase64, 'base64'),
|
|
279
|
+
format: 'der',
|
|
280
|
+
type: 'spki',
|
|
281
|
+
});
|
|
282
|
+
const signatureOk = crypto.verify('sha256', payloadBytes, { key: publicKey, dsaEncoding: 'ieee-p1363' }, signature);
|
|
283
|
+
if (!signatureOk)
|
|
284
|
+
return { isValid: false, message: 'Grace cache token signature is invalid.' };
|
|
285
|
+
const json = Buffer.from(parts[1], 'base64url').toString('utf8');
|
|
286
|
+
const payload = JSON.parse(json);
|
|
287
|
+
if (new Date(payload.validUntil).getTime() < Date.now()) {
|
|
288
|
+
return { isValid: false, message: 'Grace period has expired.', payload };
|
|
289
|
+
}
|
|
290
|
+
return { isValid: true, message: 'Valid.', payload };
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return { isValid: false, message: 'Invalid or corrupt grace cache token.' };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// ── HWID ─────────────────────────────────────────────────────────────
|
|
297
|
+
/**
|
|
298
|
+
* Returns a stable hardware fingerprint (SHA-256 of machine identifiers).
|
|
299
|
+
*/
|
|
300
|
+
static getHardwareId() {
|
|
301
|
+
const components = [
|
|
302
|
+
os.hostname(),
|
|
303
|
+
os.platform(),
|
|
304
|
+
os.arch(),
|
|
305
|
+
String(os.cpus().length),
|
|
306
|
+
getOrCreateSeedFile(),
|
|
307
|
+
].filter(Boolean);
|
|
308
|
+
return crypto
|
|
309
|
+
.createHash('sha256')
|
|
310
|
+
.update(components.join('|'))
|
|
311
|
+
.digest('hex');
|
|
312
|
+
}
|
|
313
|
+
// ── Feature helpers ───────────────────────────────────────────────────
|
|
314
|
+
/** Check if a validate/activate result includes a specific feature flag. */
|
|
315
|
+
static hasFeature(result, feature) {
|
|
316
|
+
return result.features?.some(f => f.toLowerCase() === feature.toLowerCase()) ?? false;
|
|
317
|
+
}
|
|
318
|
+
// ── Internal ──────────────────────────────────────────────────────────
|
|
319
|
+
async fetch(url, init) {
|
|
320
|
+
const controller = new AbortController();
|
|
321
|
+
const timer = setTimeout(() => controller.abort(), this.opts.timeoutMs);
|
|
322
|
+
try {
|
|
323
|
+
const res = await globalThis.fetch(url, {
|
|
324
|
+
...init,
|
|
325
|
+
signal: controller.signal,
|
|
326
|
+
headers: { 'User-Agent': 'PermitCore-Node/1.0', ...(init?.headers ?? {}) },
|
|
327
|
+
});
|
|
328
|
+
if (!res.ok && res.status >= 500)
|
|
329
|
+
throw new Error(`HTTP ${res.status}`);
|
|
330
|
+
// checkin() gets a 204 No Content on success — res.json() would throw on the empty body.
|
|
331
|
+
if (res.status === 204)
|
|
332
|
+
return undefined;
|
|
333
|
+
const text = await res.text();
|
|
334
|
+
return (text ? JSON.parse(text) : undefined);
|
|
335
|
+
}
|
|
336
|
+
finally {
|
|
337
|
+
clearTimeout(timer);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* [S-Grace1] Caches the SIGNED token, not the raw response — the server only issues
|
|
342
|
+
* `offlineCacheToken` when a grace period is configured, so a null/missing token here
|
|
343
|
+
* already means "nothing to cache," same as the old `offlineGraceDays` check. The
|
|
344
|
+
* unverified `tenantSlug` is read out of the token purely to pick which tenant's
|
|
345
|
+
* public key to fetch (see `extractUnverifiedTenantSlug`); nothing is trusted or
|
|
346
|
+
* persisted until `verifyGraceCacheToken` confirms the signature against that key.
|
|
347
|
+
*/
|
|
348
|
+
async saveToCache(licenseKey, result) {
|
|
349
|
+
if (!this.opts.enableOfflineCache || !result.offlineCacheToken)
|
|
350
|
+
return;
|
|
351
|
+
try {
|
|
352
|
+
const tenantSlug = extractUnverifiedTenantSlug(result.offlineCacheToken);
|
|
353
|
+
if (!tenantSlug)
|
|
354
|
+
return;
|
|
355
|
+
const pubKeyResp = await this.fetch(`${this.baseUrl}/api/v1/${encodeURIComponent(tenantSlug)}/public-key`);
|
|
356
|
+
if (!pubKeyResp?.publicKey)
|
|
357
|
+
return;
|
|
358
|
+
// Verify before persisting anything — never cache a token this SDK can't itself
|
|
359
|
+
// verify later; that would just recreate the old "trust an opaque file" problem.
|
|
360
|
+
const check = this.verifyGraceCacheToken(result.offlineCacheToken, pubKeyResp.publicKey);
|
|
361
|
+
if (!check.isValid)
|
|
362
|
+
return;
|
|
363
|
+
const entry = { token: result.offlineCacheToken, publicKey: pubKeyResp.publicKey };
|
|
364
|
+
fs.writeFileSync(getCachePath(licenseKey), JSON.stringify(entry));
|
|
365
|
+
}
|
|
366
|
+
catch { /* cache failure must never block normal flow */ }
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* No network call here — verification uses only the public key persisted alongside the
|
|
370
|
+
* token at save time. This is the entire point: a hand-edited cache file (or one copied
|
|
371
|
+
* to another machine) fails ECDSA verification instead of silently working.
|
|
372
|
+
*/
|
|
373
|
+
loadFromCache(licenseKey) {
|
|
374
|
+
if (!this.opts.enableOfflineCache)
|
|
375
|
+
return null;
|
|
376
|
+
try {
|
|
377
|
+
const data = fs.readFileSync(getCachePath(licenseKey), 'utf8');
|
|
378
|
+
const entry = JSON.parse(data);
|
|
379
|
+
if (!entry?.token || !entry?.publicKey)
|
|
380
|
+
return null;
|
|
381
|
+
const check = this.verifyGraceCacheToken(entry.token, entry.publicKey);
|
|
382
|
+
if (!check.isValid || !check.payload)
|
|
383
|
+
return null;
|
|
384
|
+
const p = check.payload;
|
|
385
|
+
return {
|
|
386
|
+
isValid: p.isValid,
|
|
387
|
+
productName: p.productName ?? undefined,
|
|
388
|
+
remainingActivations: p.remainingActivations ?? undefined,
|
|
389
|
+
expiresAt: p.expiresAt ?? undefined,
|
|
390
|
+
features: p.features ?? undefined,
|
|
391
|
+
isOffline: true,
|
|
392
|
+
message: `Offline mode — valid until ${p.validUntil} (cryptographically verified)`,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
exports.PermitCoreClient = PermitCoreClient;
|
|
401
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
402
|
+
function getCachePath(licenseKey) {
|
|
403
|
+
const hash = crypto.createHash('sha256').update(licenseKey).digest('hex').slice(0, 16);
|
|
404
|
+
return path.join(os.tmpdir(), `.permitcore_cache_${hash}`);
|
|
405
|
+
}
|
|
406
|
+
function getOfflineActivationPath(deviceId) {
|
|
407
|
+
const hash = crypto.createHash('sha256').update(deviceId).digest('hex').slice(0, 16);
|
|
408
|
+
return path.join(os.tmpdir(), `.permitcore_offline_${hash}`);
|
|
409
|
+
}
|
|
410
|
+
// Reads only the `tenantSlug` field out of a pc_grace_v1 token's payload, WITHOUT verifying
|
|
411
|
+
// the signature — safe to do because it's only used to pick which tenant's public-key
|
|
412
|
+
// endpoint to fetch. The subsequent verifyGraceCacheToken call is what actually establishes
|
|
413
|
+
// trust before anything gets persisted to disk.
|
|
414
|
+
function extractUnverifiedTenantSlug(token) {
|
|
415
|
+
try {
|
|
416
|
+
const parts = token.split('.');
|
|
417
|
+
if (parts.length !== 3 || parts[0] !== 'pc_grace_v1')
|
|
418
|
+
return null;
|
|
419
|
+
const json = Buffer.from(parts[1], 'base64url').toString('utf8');
|
|
420
|
+
const payload = JSON.parse(json);
|
|
421
|
+
return payload.tenantSlug ?? null;
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
function getOrCreateSeedFile() {
|
|
428
|
+
const seedPath = path.join(os.homedir(), '.permitcore_seed');
|
|
429
|
+
try {
|
|
430
|
+
if (!fs.existsSync(seedPath))
|
|
431
|
+
fs.writeFileSync(seedPath, crypto.randomUUID());
|
|
432
|
+
return fs.readFileSync(seedPath, 'utf8').trim();
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
return '';
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
// ── Default export ─────────────────────────────────────────────────────────
|
|
439
|
+
exports.default = PermitCoreClient;
|
|
440
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@permitcore/permitcore",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official Node.js SDK for PermitCore license management",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"test": "npm run build && node --test test/*.test.js",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"license",
|
|
18
|
+
"licensing",
|
|
19
|
+
"sdk",
|
|
20
|
+
"permitcore",
|
|
21
|
+
"activation"
|
|
22
|
+
],
|
|
23
|
+
"author": "PermitCore",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"homepage": "https://permitcore.dev",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/permitCore-spec/PermitCore",
|
|
29
|
+
"directory": "SDKs/node"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^26.1.2",
|
|
33
|
+
"typescript": "^7.0.2"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=16"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
}
|
|
41
|
+
}
|