/**
 * Licence keys carry a **signature**, and this is the whole point of the format.
 *
 * Keys used to be `APEX-` plus base64 of a JSON object and nothing else.
 * Validation decoded it, checked three fields existed, and returned
 * `valid: true`. Anyone could mint an enterprise licence valid until 2099 by
 * typing one line in a console:
 *
 * ```js
 * setLicense('APEX-' + btoa(JSON.stringify({issueDate: '2026-01-01', expiryDate: '2099-01-01', plan: 'enterprise'})))
 * ```
 *
 * The domain lock did not help, because a forger writes their own hostname into
 * the payload before encoding it. The payload now carries a `sig` field signed
 * with ECDSA P-256, and the client holds only the public key, so an edited
 * payload fails verification.
 *
 * **The envelope is deliberately unchanged.** `APEX-<base64(JSON)>` is exactly
 * what it always was, and `sig` is just another field inside. That is what makes
 * a signed key acceptable to clients built before signing existed: they parse the
 * JSON, find the three fields they require, and ignore `sig`. Putting the
 * signature outside the envelope (`APEX-<payload>.<sig>`) was tried first and is
 * a trap: the old decoder runs `atob` over everything after the first dash, a `.`
 * makes that throw, and every customer running a shipped build would have
 * rejected their new key the day we started signing.
 *
 * **What this achieves, and what it does not.** No client-side scheme prevents
 * unlicensed use: the code runs on the user's machine and the bundle can always
 * be patched. Signing means unlicensed use requires deliberate, demonstrable
 * tampering rather than typing base64, which is what makes the terms enforceable,
 * and it makes expiry and domain locking mean something. It is the same bar
 * AG Grid and Highcharts settle at.
 *
 * ECDSA P-256 rather than Ed25519 because `crypto.subtle` has supported it in
 * every browser for a decade, while WebCrypto Ed25519 only arrived in Chrome 137.
 */
export interface LicenseData {
    /** Hostnames the key is restricted to. Absent means unrestricted. */
    readonly domains?: readonly string[];
    /** `YYYY-MM-DD`. */
    readonly expiryDate: string;
    /** `YYYY-MM-DD`. */
    readonly issueDate: string;
    /**
     * The plan the key claims, free-form by design: this type describes the
     * envelope, and verification must not reject a key over an unknown word.
     * Use `normalisePlan` and `planAtLeast` in `tier.ts` to interpret it, never a
     * string comparison here. Currently `pro`, `premium`, `embedded` or `oem`.
     */
    readonly plan: string;
    readonly valid: boolean;
}
export interface LicenseValidationResult {
    readonly data?: LicenseData;
    readonly expired: boolean;
    readonly message?: string;
    /**
     * Whether the signature has been checked yet.
     *
     * Verification is asynchronous (`crypto.subtle` has no sync API) but
     * `isLicenseValid()` is synchronous and is called during render by every
     * product. So a structurally valid key is accepted provisionally and verified
     * on the next microtask. `false` means "accepted on structure alone: pending,
     * unverifiable, or an unsigned legacy key".
     */
    readonly signatureVerified: boolean;
    readonly valid: boolean;
}
export type LicenseChangeListener = (result: LicenseValidationResult) => void;
export declare class LicenseManager {
    /**
     * Overridable so tests can sign with an ephemeral key. Not public API, and
     * deliberately per-copy rather than shared: it is a build constant, and
     * sharing it would let one product's key list decide another's verdicts.
     */
    private static publicKeysSpki;
    /**
     * Signature verdicts by key, so `validateKey` can stay pure and synchronous
     * while verification settles on a microtask. Per-copy for the same reason as
     * `publicKeysSpki`: a verdict only means anything against the key list that
     * produced it. The duplicated work is one ECDSA verify per product per key.
     */
    private static readonly verdicts;
    private static readonly verifying;
    private static warnedUnverifiable;
    /**
     * Bumped by `_resetSignatureState`. A verification that began before a reset
     * must not deliver its verdict afterwards. Verdicts for a key that is no
     * longer the page key are still broadcast (that is how a forged per-instance
     * key gets watermarked), and listeners are shared, so without this a
     * verification left in flight by one test would surface as a stray
     * notification inside an unrelated one.
     */
    private static epoch;
    /**
     * Key, result and listeners live in the shared slot, so every copy of this
     * module on the page reads and writes one licence. The listener set has to be
     * shared too, not just the key: products re-render off `onChange`, so a chart
     * that painted before another product called `setLicense` would otherwise
     * stay watermarked forever.
     */
    private static get licenseKey();
    private static set licenseKey(value);
    private static get listeners();
    private static get validationResult();
    private static set validationResult(value);
    /**
     * The page-wide key set via `setLicense`, or null.
     *
     * Products that also accept a licence per instance (ApexCharts' `chart.license`,
     * or a global `Apex.license`) need this to resolve precedence between the two.
     */
    static getKey(): null | string;
    /**
     * Current validation result, including whether the signature has been checked.
     */
    static getLicenseStatus(): LicenseValidationResult;
    /**
     * Whether a specific key is valid, without adopting it as the page licence.
     *
     * The convenience form of `validateKey` for products resolving a per-instance
     * key, where only the verdict matters and the message does not.
     */
    static isKeyValid(key: null | string | undefined): boolean;
    /**
     * Whether the current licence is valid. Synchronous, because every product
     * calls it while rendering.
     *
     * A structurally sound key reads as valid before its signature has been
     * checked, and flips to invalid a microtask later if the signature is bad. See
     * `signatureVerified` on the result, and `onChange` to react to the flip.
     */
    static isLicenseValid(): boolean;
    /**
     * Subscribe to validation changes, i.e. the moment asynchronous signature
     * verification confirms or rejects a key.
     *
     * Without this a forged key would go unnoticed by anything that asked once and
     * cached the answer. `Watermark` subscribes internally, so products that use it
     * need no changes.
     *
     * @returns An unsubscribe function.
     */
    static onChange(listener: LicenseChangeListener): () => void;
    /**
     * Set the global licence key.
     *
     * Structural validation happens synchronously; signature verification starts
     * now and settles on a later microtask, notifying `onChange` listeners if the
     * outcome changes.
     */
    static setLicense(key: string): void;
    /**
     * Validate an arbitrary key without touching the page-wide licence.
     *
     * Synchronous by contract, because products call it during render. A
     * structurally sound signed key reads as valid before its signature has been
     * checked and is corrected on a later microtask, which `onChange` reports.
     * Failing closed instead would watermark every paying customer for a frame on
     * every page load.
     */
    static validateKey(key: string): LicenseValidationResult;
    /**
     * Test-only: forget cached signature verdicts, in-flight verifications and the
     * one-time warnings.
     *
     * Verdicts are keyed by the key string, so without this a suite reusing a
     * fixture key would have its second use answered from cache instead of going
     * through the provisional window it is trying to assert on.
     */
    static _resetSignatureState(): void;
    /** Decode base64 (standard or url-safe) to bytes, without browser globals. */
    private static base64ToBytes;
    /**
     * The exact bytes a signature covers.
     *
     * A fixed, delimited string rather than re-serialised JSON, because JSON key
     * order is not guaranteed and a verifier that rebuilt the object could produce
     * different bytes from the signer for the same licence. The `v1` prefix means
     * adding a field later is a visible version bump rather than a silent break.
     *
     * Must match `signLicensePayload` in editor-api and `licence-generator.js`
     * byte for byte. `test/license-utils.test.js` in editor-api pins that.
     */
    private static canonicalPayload;
    private static notify;
    /**
     * Decode `APEX-<base64(JSON)>` into its fields and signature.
     *
     * @returns null when the key is not decodable at all.
     */
    private static parseKey;
    private static publish;
    /**
     * Everything checkable without crypto: format, payload shape, expiry, domain
     * lock, and whether an unsigned key is still inside the legacy window.
     */
    private static validateStructure;
    /**
     * Verify the signature against the accepted public keys.
     *
     * Unverifiable is deliberately distinct from invalid. `crypto.subtle` is only
     * exposed in secure contexts, so a page served over plain HTTP cannot verify
     * anything; treating that as invalid would watermark paying customers running
     * an HTTP intranet. It does mean serving over HTTP sidesteps verification,
     * which is the same ceiling as patching the bundle rather than a new hole.
     */
    private static verifySignature;
}
//# sourceMappingURL=LicenseManager.d.ts.map