UNPKG

@tldraw/editor

Version:

tldraw infinite canvas SDK (editor).

285 lines (284 loc) • 10.7 kB
"use strict"; 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