@omnigrid/pro-license 0.0.0-stage → 0.1.1
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 +10 -3
- package/dist/baseProPlugin.d.ts +41 -0
- package/dist/baseProPlugin.js +50 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/publicKey.d.ts +8 -0
- package/dist/publicKey.js +8 -0
- package/dist/types.d.ts +37 -0
- package/dist/types.js +1 -0
- package/dist/verify.d.ts +15 -0
- package/dist/verify.js +80 -0
- package/dist/watermark.d.ts +41 -0
- package/dist/watermark.js +115 -0
- package/package.json +39 -6
package/README.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# @omnigrid/pro-license
|
|
2
|
+
|
|
3
|
+
Offline license verification and demo watermark shared by all OmniGrid Pro plugins.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Pass your commercial license key via the global grid options:
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
<OmniGrid "your_base64_encoded_license_key_here", ... // gridOptions="{{" licenseKey: }}/>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { GridApi, GridPlugin } from "@omnigrid/core";
|
|
2
|
+
import type { LicenseStatus } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Base class for every Pro plugin. On registration it verifies
|
|
5
|
+
* `GridOptions.licenseKey` offline; with a missing, invalid or expired key the
|
|
6
|
+
* plugin keeps working in demo mode and a dismissible watermark is shown.
|
|
7
|
+
*
|
|
8
|
+
* @api
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class BaseProPlugin<T> implements GridPlugin<T> {
|
|
11
|
+
/** @api Unique, namespaced plugin identifier. */
|
|
12
|
+
abstract readonly name: string;
|
|
13
|
+
private readonly publicKey;
|
|
14
|
+
private releaseWatermark;
|
|
15
|
+
private status;
|
|
16
|
+
/**
|
|
17
|
+
* @param publicKey - Base64url Ed25519 public key. @default PRO_LICENSE_PUBLIC_KEY
|
|
18
|
+
*/
|
|
19
|
+
protected constructor(publicKey?: string);
|
|
20
|
+
/**
|
|
21
|
+
* Verifies the grid's license key and shows the watermark when it is not valid.
|
|
22
|
+
*
|
|
23
|
+
* @param licenseKey - Token to verify.
|
|
24
|
+
* @param api - Grid API used to mount the watermark.
|
|
25
|
+
* @returns Whether the license is valid.
|
|
26
|
+
* @api
|
|
27
|
+
*/
|
|
28
|
+
protected validateLicense(licenseKey: string | undefined, api: GridApi<T>): boolean;
|
|
29
|
+
/** Last verification result of this plugin instance. @api */
|
|
30
|
+
getLicenseStatus(): LicenseStatus;
|
|
31
|
+
/**
|
|
32
|
+
* Plugin-specific registration, called after license validation.
|
|
33
|
+
*
|
|
34
|
+
* @param api - Grid API of the host grid.
|
|
35
|
+
* @returns Optional cleanup.
|
|
36
|
+
* @api
|
|
37
|
+
*/
|
|
38
|
+
protected abstract onRegister(api: GridApi<T>): void | (() => void);
|
|
39
|
+
/** @api */
|
|
40
|
+
register(api: GridApi<T>): () => void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { PRO_LICENSE_PUBLIC_KEY } from "./publicKey";
|
|
2
|
+
import { verifyToken } from "./verify";
|
|
3
|
+
import { mountWatermark } from "./watermark";
|
|
4
|
+
/**
|
|
5
|
+
* Base class for every Pro plugin. On registration it verifies
|
|
6
|
+
* `GridOptions.licenseKey` offline; with a missing, invalid or expired key the
|
|
7
|
+
* plugin keeps working in demo mode and a dismissible watermark is shown.
|
|
8
|
+
*
|
|
9
|
+
* @api
|
|
10
|
+
*/
|
|
11
|
+
export class BaseProPlugin {
|
|
12
|
+
/**
|
|
13
|
+
* @param publicKey - Base64url Ed25519 public key. @default PRO_LICENSE_PUBLIC_KEY
|
|
14
|
+
*/
|
|
15
|
+
constructor(publicKey = PRO_LICENSE_PUBLIC_KEY) {
|
|
16
|
+
this.releaseWatermark = null;
|
|
17
|
+
this.status = { isValid: false, reason: "missing" };
|
|
18
|
+
this.publicKey = publicKey;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Verifies the grid's license key and shows the watermark when it is not valid.
|
|
22
|
+
*
|
|
23
|
+
* @param licenseKey - Token to verify.
|
|
24
|
+
* @param api - Grid API used to mount the watermark.
|
|
25
|
+
* @returns Whether the license is valid.
|
|
26
|
+
* @api
|
|
27
|
+
*/
|
|
28
|
+
validateLicense(licenseKey, api) {
|
|
29
|
+
this.status = verifyToken(licenseKey, new Date(), this.publicKey);
|
|
30
|
+
if (!this.status.isValid) {
|
|
31
|
+
this.releaseWatermark = mountWatermark(api.slots, this.status.reason)?.unmount ?? null;
|
|
32
|
+
}
|
|
33
|
+
return this.status.isValid;
|
|
34
|
+
}
|
|
35
|
+
/** Last verification result of this plugin instance. @api */
|
|
36
|
+
getLicenseStatus() {
|
|
37
|
+
return this.status;
|
|
38
|
+
}
|
|
39
|
+
/** @api */
|
|
40
|
+
register(api) {
|
|
41
|
+
this.validateLicense(api.getLicenseKey(), api);
|
|
42
|
+
const cleanup = this.onRegister(api);
|
|
43
|
+
return () => {
|
|
44
|
+
if (typeof cleanup === "function")
|
|
45
|
+
cleanup();
|
|
46
|
+
this.releaseWatermark?.();
|
|
47
|
+
this.releaseWatermark = null;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { BaseProPlugin } from "./baseProPlugin";
|
|
2
|
+
export { PRO_LICENSE_PUBLIC_KEY } from "./publicKey";
|
|
3
|
+
export type { LicenseInvalidReason, LicenseStatus, ProLicensePayload } from "./types";
|
|
4
|
+
export { verifyToken } from "./verify";
|
|
5
|
+
export { createWatermarkContent, getWatermarkMessage, mountWatermark, WATERMARK_DISMISSED_KEY } from "./watermark";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ed25519 public key (32 bytes, base64url) used to verify license signatures.
|
|
3
|
+
* Only the public half ships in the package; the private signing key lives
|
|
4
|
+
* exclusively on the license-issuing side.
|
|
5
|
+
*
|
|
6
|
+
* @api
|
|
7
|
+
*/
|
|
8
|
+
export declare const PRO_LICENSE_PUBLIC_KEY: string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ed25519 public key (32 bytes, base64url) used to verify license signatures.
|
|
3
|
+
* Only the public half ships in the package; the private signing key lives
|
|
4
|
+
* exclusively on the license-issuing side.
|
|
5
|
+
*
|
|
6
|
+
* @api
|
|
7
|
+
*/
|
|
8
|
+
export const PRO_LICENSE_PUBLIC_KEY = "nXp9oSe1vI5CRLRcd3E7H-27VX74uST77OEPj0iGmCU";
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decoded license token payload. The Ed25519 signature is carried next to the
|
|
3
|
+
* payload in the token, not inside it.
|
|
4
|
+
*
|
|
5
|
+
* @api
|
|
6
|
+
*/
|
|
7
|
+
export interface ProLicensePayload {
|
|
8
|
+
/** Organization or individual the license is issued to. */
|
|
9
|
+
owner: string;
|
|
10
|
+
/** Contact email of the licensee. */
|
|
11
|
+
email: string;
|
|
12
|
+
/** ISO-8601 date the license was issued. */
|
|
13
|
+
issuedAt: string;
|
|
14
|
+
/** ISO-8601 date the license expires, or `null` for perpetual. */
|
|
15
|
+
expiresAt: string | null;
|
|
16
|
+
/** Licensed features, e.g. `["pro-all"]`. */
|
|
17
|
+
features?: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Reason a license is not valid.
|
|
21
|
+
*
|
|
22
|
+
* @api
|
|
23
|
+
*/
|
|
24
|
+
export type LicenseInvalidReason = "missing" | "malformed" | "bad-signature" | "expired";
|
|
25
|
+
/**
|
|
26
|
+
* Result of verifying a license token.
|
|
27
|
+
*
|
|
28
|
+
* @api
|
|
29
|
+
*/
|
|
30
|
+
export interface LicenseStatus {
|
|
31
|
+
/** Whether the token is cryptographically valid and not expired. */
|
|
32
|
+
isValid: boolean;
|
|
33
|
+
/** Machine-readable cause when `isValid` is false. */
|
|
34
|
+
reason?: LicenseInvalidReason;
|
|
35
|
+
/** Decoded payload when the token structure is well-formed. */
|
|
36
|
+
payload?: ProLicensePayload;
|
|
37
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LicenseStatus } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Verifies a license token entirely offline (no network access): checks the
|
|
4
|
+
* Ed25519 signature against the embedded public key and the expiry date.
|
|
5
|
+
*
|
|
6
|
+
* Token format: `base64url(payload JSON).base64url(signature)`, where the
|
|
7
|
+
* signature covers the ASCII bytes of the first segment.
|
|
8
|
+
*
|
|
9
|
+
* @param licenseKey - Token from `GridOptions.licenseKey`.
|
|
10
|
+
* @param now - Current time. @default new Date()
|
|
11
|
+
* @param publicKey - Base64url Ed25519 public key. @default PRO_LICENSE_PUBLIC_KEY
|
|
12
|
+
* @returns License status; never throws.
|
|
13
|
+
* @api
|
|
14
|
+
*/
|
|
15
|
+
export declare function verifyToken(licenseKey?: string, now?: Date, publicKey?: string): LicenseStatus;
|
package/dist/verify.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ed25519 } from "@noble/curves/ed25519";
|
|
2
|
+
import { PRO_LICENSE_PUBLIC_KEY } from "./publicKey";
|
|
3
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
4
|
+
const DATE_ONLY_LENGTH = 10;
|
|
5
|
+
function base64UrlToBytes(value) {
|
|
6
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
7
|
+
const padded = normalized + "=".repeat((4 - (normalized.length % 4)) % 4);
|
|
8
|
+
const binary = atob(padded);
|
|
9
|
+
const bytes = new Uint8Array(binary.length);
|
|
10
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
11
|
+
bytes[index] = binary.charCodeAt(index);
|
|
12
|
+
}
|
|
13
|
+
return bytes;
|
|
14
|
+
}
|
|
15
|
+
function isPayload(value) {
|
|
16
|
+
if (typeof value !== "object" || value === null)
|
|
17
|
+
return false;
|
|
18
|
+
const candidate = value;
|
|
19
|
+
return (typeof candidate.owner === "string" &&
|
|
20
|
+
typeof candidate.email === "string" &&
|
|
21
|
+
typeof candidate.issuedAt === "string" &&
|
|
22
|
+
(candidate.expiresAt === null || typeof candidate.expiresAt === "string") &&
|
|
23
|
+
(candidate.features === undefined || Array.isArray(candidate.features)));
|
|
24
|
+
}
|
|
25
|
+
/** A date-only expiry (`YYYY-MM-DD`) stays valid through the end of that UTC day. */
|
|
26
|
+
function isExpired(expiresAt, now) {
|
|
27
|
+
const parsed = Date.parse(expiresAt);
|
|
28
|
+
if (Number.isNaN(parsed))
|
|
29
|
+
return true;
|
|
30
|
+
const limit = expiresAt.length === DATE_ONLY_LENGTH ? parsed + DAY_MS : parsed;
|
|
31
|
+
return now.getTime() >= limit;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Verifies a license token entirely offline (no network access): checks the
|
|
35
|
+
* Ed25519 signature against the embedded public key and the expiry date.
|
|
36
|
+
*
|
|
37
|
+
* Token format: `base64url(payload JSON).base64url(signature)`, where the
|
|
38
|
+
* signature covers the ASCII bytes of the first segment.
|
|
39
|
+
*
|
|
40
|
+
* @param licenseKey - Token from `GridOptions.licenseKey`.
|
|
41
|
+
* @param now - Current time. @default new Date()
|
|
42
|
+
* @param publicKey - Base64url Ed25519 public key. @default PRO_LICENSE_PUBLIC_KEY
|
|
43
|
+
* @returns License status; never throws.
|
|
44
|
+
* @api
|
|
45
|
+
*/
|
|
46
|
+
export function verifyToken(licenseKey, now = new Date(), publicKey = PRO_LICENSE_PUBLIC_KEY) {
|
|
47
|
+
if (typeof licenseKey !== "string" || licenseKey.trim() === "") {
|
|
48
|
+
return { isValid: false, reason: "missing" };
|
|
49
|
+
}
|
|
50
|
+
const segments = licenseKey.trim().split(".");
|
|
51
|
+
if (segments.length !== 2 || segments[0] === "" || segments[1] === "") {
|
|
52
|
+
return { isValid: false, reason: "malformed" };
|
|
53
|
+
}
|
|
54
|
+
let payload;
|
|
55
|
+
let signature;
|
|
56
|
+
try {
|
|
57
|
+
payload = JSON.parse(new TextDecoder().decode(base64UrlToBytes(segments[0])));
|
|
58
|
+
signature = base64UrlToBytes(segments[1]);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return { isValid: false, reason: "malformed" };
|
|
62
|
+
}
|
|
63
|
+
if (!isPayload(payload)) {
|
|
64
|
+
return { isValid: false, reason: "malformed" };
|
|
65
|
+
}
|
|
66
|
+
let signatureValid;
|
|
67
|
+
try {
|
|
68
|
+
signatureValid = ed25519.verify(signature, new TextEncoder().encode(segments[0]), base64UrlToBytes(publicKey));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
signatureValid = false;
|
|
72
|
+
}
|
|
73
|
+
if (!signatureValid) {
|
|
74
|
+
return { isValid: false, reason: "bad-signature" };
|
|
75
|
+
}
|
|
76
|
+
if (payload.expiresAt !== null && isExpired(payload.expiresAt, now)) {
|
|
77
|
+
return { isValid: false, reason: "expired", payload };
|
|
78
|
+
}
|
|
79
|
+
return { isValid: true, payload };
|
|
80
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SlotMount, SlotNodeContent } from "@omnigrid/core";
|
|
2
|
+
import type { LicenseInvalidReason } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* `sessionStorage` key remembering that the user closed the watermark.
|
|
5
|
+
*
|
|
6
|
+
* @api
|
|
7
|
+
*/
|
|
8
|
+
export declare const WATERMARK_DISMISSED_KEY: string;
|
|
9
|
+
/**
|
|
10
|
+
* Returns the human-readable watermark text for a license failure.
|
|
11
|
+
*
|
|
12
|
+
* @param reason - Why the license is invalid.
|
|
13
|
+
* @api
|
|
14
|
+
*/
|
|
15
|
+
export declare function getWatermarkMessage(reason?: LicenseInvalidReason): string;
|
|
16
|
+
/**
|
|
17
|
+
* Builds the declarative demo-mode banner. `onDismiss` fires when the user
|
|
18
|
+
* clicks the close button.
|
|
19
|
+
*
|
|
20
|
+
* @param reason - Why the license is invalid.
|
|
21
|
+
* @param onDismiss - Called after the dismissal is persisted.
|
|
22
|
+
* @api
|
|
23
|
+
*/
|
|
24
|
+
export declare function createWatermarkContent<T>(reason: LicenseInvalidReason | undefined, onDismiss: () => void): SlotNodeContent<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Mounts the watermark into the `bodyTop` slot unless it was dismissed during
|
|
27
|
+
* the current browser session.
|
|
28
|
+
*
|
|
29
|
+
* @param slots - `api.slots` of the host grid.
|
|
30
|
+
* @param reason - Why the license is invalid.
|
|
31
|
+
* @returns An idempotent release handle, or `null` when already dismissed.
|
|
32
|
+
* @api
|
|
33
|
+
*/
|
|
34
|
+
export declare function mountWatermark<T>(slots: {
|
|
35
|
+
mount(slot: "bodyTop", content: SlotNodeContent<T>, options: {
|
|
36
|
+
id: string;
|
|
37
|
+
priority: number;
|
|
38
|
+
}): SlotMount<T>;
|
|
39
|
+
}, reason: LicenseInvalidReason | undefined): {
|
|
40
|
+
unmount(): void;
|
|
41
|
+
} | null;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** One watermark is shared across all Pro plugins registered on a grid. */
|
|
2
|
+
const activeWatermarks = new WeakMap();
|
|
3
|
+
/**
|
|
4
|
+
* `sessionStorage` key remembering that the user closed the watermark.
|
|
5
|
+
*
|
|
6
|
+
* @api
|
|
7
|
+
*/
|
|
8
|
+
export const WATERMARK_DISMISSED_KEY = "omnigrid-pro-watermark-dismissed";
|
|
9
|
+
const WATERMARK_MESSAGE = "OmniGrid Pro: license expired or not found";
|
|
10
|
+
function readDismissed() {
|
|
11
|
+
try {
|
|
12
|
+
return globalThis.sessionStorage?.getItem(WATERMARK_DISMISSED_KEY) === "1";
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function writeDismissed() {
|
|
19
|
+
try {
|
|
20
|
+
globalThis.sessionStorage?.setItem(WATERMARK_DISMISSED_KEY, "1");
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Storage unavailable (SSR, privacy mode): the banner is hidden for this grid only.
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns the human-readable watermark text for a license failure.
|
|
28
|
+
*
|
|
29
|
+
* @param reason - Why the license is invalid.
|
|
30
|
+
* @api
|
|
31
|
+
*/
|
|
32
|
+
export function getWatermarkMessage(reason) {
|
|
33
|
+
if (reason === "bad-signature" || reason === "malformed")
|
|
34
|
+
return "OmniGrid Pro: invalid license key";
|
|
35
|
+
return WATERMARK_MESSAGE;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Builds the declarative demo-mode banner. `onDismiss` fires when the user
|
|
39
|
+
* clicks the close button.
|
|
40
|
+
*
|
|
41
|
+
* @param reason - Why the license is invalid.
|
|
42
|
+
* @param onDismiss - Called after the dismissal is persisted.
|
|
43
|
+
* @api
|
|
44
|
+
*/
|
|
45
|
+
export function createWatermarkContent(reason, onDismiss) {
|
|
46
|
+
return {
|
|
47
|
+
type: "node",
|
|
48
|
+
tag: "div",
|
|
49
|
+
attrs: {
|
|
50
|
+
class: "omnigrid-pro-watermark flex items-center justify-between gap-2 px-3 py-1 text-xs bg-mint text-paper",
|
|
51
|
+
role: "status",
|
|
52
|
+
"data-omnigrid-pro-watermark": true,
|
|
53
|
+
style: "box-sizing:border-box;display:flex;width:100%;align-items:center;justify-content:space-between;pointer-events:auto",
|
|
54
|
+
},
|
|
55
|
+
children: [
|
|
56
|
+
{ type: "node", tag: "span", children: [getWatermarkMessage(reason)] },
|
|
57
|
+
{
|
|
58
|
+
type: "node",
|
|
59
|
+
tag: "button",
|
|
60
|
+
attrs: { class: "cursor-pointer", type: "button", "aria-label": "Dismiss license notice" },
|
|
61
|
+
on: {
|
|
62
|
+
click: (_context) => {
|
|
63
|
+
writeDismissed();
|
|
64
|
+
onDismiss();
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
children: ["✕"],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Mounts the watermark into the `bodyTop` slot unless it was dismissed during
|
|
74
|
+
* the current browser session.
|
|
75
|
+
*
|
|
76
|
+
* @param slots - `api.slots` of the host grid.
|
|
77
|
+
* @param reason - Why the license is invalid.
|
|
78
|
+
* @returns An idempotent release handle, or `null` when already dismissed.
|
|
79
|
+
* @api
|
|
80
|
+
*/
|
|
81
|
+
export function mountWatermark(slots, reason) {
|
|
82
|
+
if (readDismissed())
|
|
83
|
+
return null;
|
|
84
|
+
const key = slots;
|
|
85
|
+
let watermark = activeWatermarks.get(key);
|
|
86
|
+
if (!watermark) {
|
|
87
|
+
let mount = null;
|
|
88
|
+
watermark = {
|
|
89
|
+
references: 0,
|
|
90
|
+
unmount: () => mount?.unmount(),
|
|
91
|
+
};
|
|
92
|
+
mount = slots.mount("bodyTop", createWatermarkContent(reason, () => watermark?.unmount()), {
|
|
93
|
+
id: "omnigrid-pro-watermark",
|
|
94
|
+
priority: 1000,
|
|
95
|
+
});
|
|
96
|
+
activeWatermarks.set(key, watermark);
|
|
97
|
+
}
|
|
98
|
+
watermark.references += 1;
|
|
99
|
+
let released = false;
|
|
100
|
+
return {
|
|
101
|
+
unmount: () => {
|
|
102
|
+
if (released)
|
|
103
|
+
return;
|
|
104
|
+
released = true;
|
|
105
|
+
const current = activeWatermarks.get(key);
|
|
106
|
+
if (!current)
|
|
107
|
+
return;
|
|
108
|
+
current.references -= 1;
|
|
109
|
+
if (current.references === 0) {
|
|
110
|
+
current.unmount();
|
|
111
|
+
activeWatermarks.delete(key);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@omnigrid/pro-license",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Offline license verification and demo watermark for OmniGrid Pro plugins",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"omnigrid",
|
|
7
|
+
"pro",
|
|
8
|
+
"license"
|
|
9
|
+
],
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/KRZK-Apps/omnigrid-pro.git"
|
|
13
|
+
},
|
|
14
|
+
"license": "SEE LICENSE IN LICENSE.PRO.md",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "dist/index.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "npx tsc -p tsconfig.json",
|
|
29
|
+
"lint": "npx tsc --noEmit -p tsconfig.json",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"generate-keys": "node scripts/generate-keys.mjs",
|
|
32
|
+
"generate-license": "node scripts/generate-license.mjs",
|
|
33
|
+
"prepublishOnly": "npm run build"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@noble/curves": "^1.9.0",
|
|
37
|
+
"@omnigrid/core": "^0.1.0"
|
|
38
|
+
}
|
|
39
|
+
}
|