@tldraw/editor
Version:
tldraw infinite canvas SDK (editor).
285 lines (284 loc) • 10.7 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var LicenseManager_exports = {};
__export(LicenseManager_exports, {
FLAGS: () => FLAGS,
LicenseManager: () => LicenseManager,
PROPERTIES: () => PROPERTIES,
isEditorUnlicensed: () => isEditorUnlicensed
});
module.exports = __toCommonJS(LicenseManager_exports);
var import_state = require("@tldraw/state");
var import_utils = require("@tldraw/utils");
var import_version = require("../../version");
var import_assets = require("../utils/assets");
var import_licensing = require("../utils/licensing");
const GRACE_PERIOD_DAYS = 5;
const FLAGS = {
ANNUAL_LICENSE: 1,
PERPETUAL_LICENSE: 2,
INTERNAL_LICENSE: 4,
WITH_WATERMARK: 8
};
const HIGHEST_FLAG = Math.max(...Object.values(FLAGS));
const PROPERTIES = {
ID: 0,
HOSTS: 1,
FLAGS: 2,
EXPIRY_DATE: 3
};
const NUMBER_OF_KNOWN_PROPERTIES = Object.keys(PROPERTIES).length;
const LICENSE_EMAIL = "sales@tldraw.com";
const WATERMARK_TRACK_SRC = `${(0, import_assets.getDefaultCdnBaseUrl)()}/watermarks/watermark-track.svg`;
class LicenseManager {
publicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHJh0uUfxHtCGyerXmmatE368Hd9rI6LH9oPDQihnaCryRFWEVeOvf9U/SPbyxX74LFyJs5tYeAHq5Nc0Ax25LQ";
isDevelopment;
isTest;
isCryptoAvailable;
state = (0, import_state.atom)(
"license state",
"pending"
);
verbose = true;
constructor(licenseKey, testPublicKey, testEnvironment) {
this.isTest = process.env.NODE_ENV === "test";
this.isDevelopment = this.getIsDevelopment(testEnvironment);
this.publicKey = testPublicKey || this.publicKey;
this.isCryptoAvailable = !!crypto.subtle;
this.getLicenseFromKey(licenseKey).then((result) => {
const isUnlicensed = isEditorUnlicensed(result);
if (!this.isDevelopment && isUnlicensed) {
(0, import_utils.fetch)(WATERMARK_TRACK_SRC);
}
if (isUnlicensed) {
this.state.set("unlicensed");
} else if (result.isLicensedWithWatermark) {
this.state.set("licensed-with-watermark");
} else {
this.state.set("licensed");
}
});
}
getIsDevelopment(testEnvironment) {
if (testEnvironment === "development") return true;
if (testEnvironment === "production") return false;
return !["https:", "vscode-webview:"].includes(window.location.protocol) || window.location.hostname === "localhost";
}
async extractLicenseKey(licenseKey) {
const [data, signature] = licenseKey.split(".");
const [prefix, encodedData] = data.split("/");
if (!prefix.startsWith("tldraw-")) {
throw new Error(`Unsupported prefix '${prefix}'`);
}
const publicCryptoKey = await (0, import_licensing.importPublicKey)(this.publicKey);
let isVerified;
try {
isVerified = await crypto.subtle.verify(
{
name: "ECDSA",
hash: { name: "SHA-256" }
},
publicCryptoKey,
new Uint8Array((0, import_licensing.str2ab)(atob(signature))),
new Uint8Array((0, import_licensing.str2ab)(atob(encodedData)))
);
} catch (e) {
console.error(e);
throw new Error("Could not perform signature validation");
}
if (!isVerified) {
throw new Error("Invalid signature");
}
let decodedData;
try {
decodedData = JSON.parse(atob(encodedData));
} catch {
throw new Error("Could not parse object");
}
if (decodedData.length > NUMBER_OF_KNOWN_PROPERTIES) {
this.outputMessages([
"License key contains some unknown properties.",
"You may want to update tldraw packages to a newer version to get access to new functionality."
]);
}
return {
id: decodedData[PROPERTIES.ID],
hosts: decodedData[PROPERTIES.HOSTS],
flags: decodedData[PROPERTIES.FLAGS],
expiryDate: decodedData[PROPERTIES.EXPIRY_DATE]
};
}
async getLicenseFromKey(licenseKey) {
if (!licenseKey) {
if (!this.isDevelopment) {
this.outputNoLicenseKeyProvided();
}
return { isLicenseParseable: false, reason: "no-key-provided" };
}
if (this.isDevelopment && !this.isCryptoAvailable) {
if (this.verbose) {
console.log(
"tldraw: you seem to be in a development environment that does not support crypto. License not verified."
);
console.log("You should check that this works in production separately.");
}
return { isLicenseParseable: false, reason: "has-key-development-mode" };
}
let cleanedLicenseKey = licenseKey.replace(/[\u200B-\u200D\uFEFF]/g, "");
cleanedLicenseKey = cleanedLicenseKey.replace(/\r?\n|\r/g, "");
try {
const licenseInfo = await this.extractLicenseKey(cleanedLicenseKey);
const expiryDate = new Date(licenseInfo.expiryDate);
const isAnnualLicense = this.isFlagEnabled(licenseInfo.flags, FLAGS.ANNUAL_LICENSE);
const isPerpetualLicense = this.isFlagEnabled(licenseInfo.flags, FLAGS.PERPETUAL_LICENSE);
const result = {
license: licenseInfo,
isLicenseParseable: true,
isDevelopment: this.isDevelopment,
isDomainValid: this.isDomainValid(licenseInfo),
expiryDate,
isAnnualLicense,
isAnnualLicenseExpired: isAnnualLicense && this.isAnnualLicenseExpired(expiryDate),
isPerpetualLicense,
isPerpetualLicenseExpired: isPerpetualLicense && this.isPerpetualLicenseExpired(expiryDate),
isInternalLicense: this.isFlagEnabled(licenseInfo.flags, FLAGS.INTERNAL_LICENSE),
isLicensedWithWatermark: this.isFlagEnabled(licenseInfo.flags, FLAGS.WITH_WATERMARK)
};
this.outputLicenseInfoIfNeeded(result);
return result;
} catch (e) {
this.outputInvalidLicenseKey(e.message);
return { isLicenseParseable: false, reason: "invalid-license-key" };
}
}
isDomainValid(licenseInfo) {
const currentHostname = window.location.hostname.toLowerCase();
return licenseInfo.hosts.some((host) => {
const normalizedHost = host.toLowerCase().trim();
if (normalizedHost === currentHostname || `www.${normalizedHost}` === currentHostname || normalizedHost === `www.${currentHostname}`) {
return true;
}
if (host === "*") {
return true;
}
if (host.includes("*")) {
const globToRegex = new RegExp(host.replace(/\*/g, ".*?"));
return globToRegex.test(currentHostname) || globToRegex.test(`www.${currentHostname}`);
}
if (window.location.protocol === "vscode-webview:") {
const currentUrl = new URL(window.location.href);
const extensionId = currentUrl.searchParams.get("extensionId");
if (normalizedHost === extensionId) {
return true;
}
}
return false;
});
}
getExpirationDateWithoutGracePeriod(expiryDate) {
return new Date(expiryDate.getFullYear(), expiryDate.getMonth(), expiryDate.getDate());
}
getExpirationDateWithGracePeriod(expiryDate) {
return new Date(
expiryDate.getFullYear(),
expiryDate.getMonth(),
expiryDate.getDate() + GRACE_PERIOD_DAYS + 1
// Add 1 day to include the expiration day
);
}
isAnnualLicenseExpired(expiryDate) {
const expiration = this.getExpirationDateWithGracePeriod(expiryDate);
const isExpired = /* @__PURE__ */ new Date() >= expiration;
if (!isExpired && /* @__PURE__ */ new Date() >= this.getExpirationDateWithoutGracePeriod(expiryDate)) {
this.outputMessages([
"tldraw license is about to expire, you are in a grace period.",
`Please reach out to ${LICENSE_EMAIL} if you would like to renew your license.`
]);
}
return isExpired;
}
isPerpetualLicenseExpired(expiryDate) {
const expiration = this.getExpirationDateWithGracePeriod(expiryDate);
const dates = {
major: new Date(import_version.publishDates.major),
minor: new Date(import_version.publishDates.minor)
};
return dates.major >= expiration || dates.minor >= expiration;
}
isFlagEnabled(flags, flag) {
return (flags & flag) === flag;
}
outputNoLicenseKeyProvided() {
}
outputInvalidLicenseKey(msg) {
this.outputMessages(["Invalid tldraw license key", `Reason: ${msg}`]);
}
outputLicenseInfoIfNeeded(result) {
if (result.isAnnualLicenseExpired) {
this.outputMessages([
"Your tldraw license has expired!",
`Please reach out to ${LICENSE_EMAIL} to renew.`
]);
}
if (!result.isDomainValid && !result.isDevelopment) {
this.outputMessages([
"This tldraw license key is not valid for this domain!",
`Please reach out to ${LICENSE_EMAIL} if you would like to use tldraw on other domains.`
]);
}
if (result.license.flags >= HIGHEST_FLAG * 2) {
this.outputMessages([
"This tldraw license contains some unknown flags.",
"You may want to update tldraw packages to a newer version to get access to new functionality."
]);
}
}
outputMessages(messages) {
if (this.isTest) return;
if (this.verbose) {
this.outputDelimiter();
for (const message of messages) {
console.log(
`%c${message}`,
`color: white; background: crimson; padding: 2px; border-radius: 3px;`
);
}
this.outputDelimiter();
}
}
outputDelimiter() {
console.log(
"%c-------------------------------------------------------------------",
`color: white; background: crimson; padding: 2px; border-radius: 3px;`
);
}
static className = "tl-watermark_SEE-LICENSE";
}
function isEditorUnlicensed(result) {
if (!result.isLicenseParseable) return true;
if (!result.isDomainValid && !result.isDevelopment) return true;
if (result.isPerpetualLicenseExpired || result.isAnnualLicenseExpired) {
if (result.isInternalLicense) {
throw new Error("License: Internal license expired.");
}
return true;
}
return false;
}
//# sourceMappingURL=LicenseManager.js.map