diff --git a/apps/examples/src/misc/develop.tsx b/apps/examples/src/misc/develop.tsx
index db14ab8cd..dd1c01ee1 100644
--- a/apps/examples/src/misc/develop.tsx
+++ b/apps/examples/src/misc/develop.tsx
@@ -6,6 +6,7 @@ export default function Develop() {
{
;(window as any).app = editor
;(window as any).editor = editor
diff --git a/packages/editor/api-report.md b/packages/editor/api-report.md
index a462cd59a..9bff17307 100644
--- a/packages/editor/api-report.md
+++ b/packages/editor/api-report.md
@@ -570,7 +570,7 @@ export class Edge2d extends Geometry2d {
// @public (undocumented)
export class Editor extends EventEmitter {
- constructor({ store, user, shapeUtils, tools, getContainer, initialState, inferDarkMode, }: TLEditorOptions);
+ constructor({ store, user, shapeUtils, tools, getContainer, initialState, inferDarkMode, licenseKey, }: TLEditorOptions);
addOpenMenu(id: string): this;
alignShapes(shapes: TLShape[] | TLShapeId[], operation: 'bottom' | 'center-horizontal' | 'center-vertical' | 'left' | 'right' | 'top'): this;
animateShape(partial: null | TLShapePartial | undefined, animationOptions?: TLAnimationOptions): this;
@@ -2032,6 +2032,7 @@ export interface TldrawEditorBaseProps {
components?: TLEditorComponents;
inferDarkMode?: boolean;
initialState?: string;
+ licenseKey?: string;
onMount?: TLOnMountHandler;
shapeUtils?: readonly TLAnyShapeUtilConstructor[];
tools?: readonly TLStateNodeConstructor[];
diff --git a/packages/editor/api/api.json b/packages/editor/api/api.json
index 42b8d5496..8d5be0e7a 100644
--- a/packages/editor/api/api.json
+++ b/packages/editor/api/api.json
@@ -7447,7 +7447,7 @@
"excerptTokens": [
{
"kind": "Content",
- "text": "constructor({ store, user, shapeUtils, tools, getContainer, initialState, inferDarkMode, }: "
+ "text": "constructor({ store, user, shapeUtils, tools, getContainer, initialState, inferDarkMode, licenseKey, }: "
},
{
"kind": "Reference",
@@ -7464,7 +7464,7 @@
"overloadIndex": 1,
"parameters": [
{
- "parameterName": "{ store, user, shapeUtils, tools, getContainer, initialState, inferDarkMode, }",
+ "parameterName": "{ store, user, shapeUtils, tools, getContainer, initialState, inferDarkMode, licenseKey, }",
"parameterTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
@@ -36588,6 +36588,33 @@
"endIndex": 2
}
},
+ {
+ "kind": "PropertySignature",
+ "canonicalReference": "@tldraw/editor!TldrawEditorBaseProps#licenseKey:member",
+ "docComment": "/**\n * License key for the editor.\n */\n",
+ "excerptTokens": [
+ {
+ "kind": "Content",
+ "text": "licenseKey?: "
+ },
+ {
+ "kind": "Content",
+ "text": "string"
+ },
+ {
+ "kind": "Content",
+ "text": ";"
+ }
+ ],
+ "isReadonly": false,
+ "isOptional": true,
+ "releaseTag": "Public",
+ "name": "licenseKey",
+ "propertyTypeTokenRange": {
+ "startIndex": 1,
+ "endIndex": 2
+ }
+ },
{
"kind": "PropertySignature",
"canonicalReference": "@tldraw/editor!TldrawEditorBaseProps#onMount:member",
diff --git a/packages/editor/src/lib/TldrawEditor.tsx b/packages/editor/src/lib/TldrawEditor.tsx
index 976415fa5..6753312c3 100644
--- a/packages/editor/src/lib/TldrawEditor.tsx
+++ b/packages/editor/src/lib/TldrawEditor.tsx
@@ -113,6 +113,11 @@ export interface TldrawEditorBaseProps {
* Whether to infer dark mode from the user's OS. Defaults to false.
*/
inferDarkMode?: boolean
+
+ /**
+ * License key for the editor.
+ */
+ licenseKey?: string
}
/**
@@ -263,6 +268,7 @@ function TldrawEditorWithReadyStore({
shapeUtils,
user,
initialState,
+ licenseKey,
autoFocus = true,
inferDarkMode,
}: Required<
@@ -285,6 +291,7 @@ function TldrawEditorWithReadyStore({
user,
initialState,
inferDarkMode,
+ licenseKey,
})
setEditor(editor)
diff --git a/packages/editor/src/lib/editor/Editor.ts b/packages/editor/src/lib/editor/Editor.ts
index cb624c6ee..75b5ce8fd 100644
--- a/packages/editor/src/lib/editor/Editor.ts
+++ b/packages/editor/src/lib/editor/Editor.ts
@@ -200,6 +200,7 @@ export class Editor extends EventEmitter {
getContainer,
initialState,
inferDarkMode,
+ licenseKey,
}: TLEditorOptions) {
super()
@@ -211,7 +212,7 @@ export class Editor extends EventEmitter {
this.getContainer = getContainer ?? (() => document.body)
- this.licenseManager = new LicenseManager(this)
+ this.licenseManager = new LicenseManager(this, licenseKey)
this.textMeasure = new TextManager(this)
this._tickManager = new TickManager(this)
diff --git a/packages/editor/src/lib/editor/managers/LicenseManager.ts b/packages/editor/src/lib/editor/managers/LicenseManager.ts
index a7f08153e..ce5d74c46 100644
--- a/packages/editor/src/lib/editor/managers/LicenseManager.ts
+++ b/packages/editor/src/lib/editor/managers/LicenseManager.ts
@@ -20,13 +20,44 @@ export class LicenseManager {
) {
const key = licenseKey ?? process?.env?.TLDRAW_LICENSE_KEY
if (key) {
- const isValid = this.validateLicenseKey(key)
- if (!isValid) throw Error('Invalid license key')
- const dataFromKey = this.getDataFromKey(key)
- const now = Date.now()
- if (now > dataFromKey.expiration) throw Error('License key expired')
- return
+ try {
+ // Get the license data from the license key
+ // todo: replace with actual get data from key
+ const dataFromKey = {
+ customer: 'Example',
+ sku: 'commercial',
+ origins: ['http://localhost:3000'],
+ expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365).getTime(),
+ }
+
+ // Check that the license is valid
+ // todo: replace with actual validation
+ const isValid = true
+ if (!isValid) throw Error('Invalid license key')
+
+ // Check expiration
+ const now = Date.now()
+ if (now > dataFromKey.expiration) throw Error('License key expired')
+
+ if (dataFromKey.sku === 'grandfathered') {
+ // We don't check origins for grandfathered licenses
+ } else {
+ // Check that the current origin is allowed by the license
+ if (typeof window !== 'undefined' && window.location.origin) {
+ const allowedOrigins = dataFromKey.origins
+ const origin = window.location.origin
+ if (!allowedOrigins.includes(origin)) {
+ throw Error('License key is not valid for this origin')
+ }
+ }
+ }
+ } catch (e: any) {
+ console.error(
+ `Could not validate the license key. Reason: ${e.message}. See more at tldraw.dev/license`
+ )
+ }
}
+
// No license key provided, time to show the watermark
const link = document.createElement('a')
const id = this.getLinkId()
@@ -95,19 +126,6 @@ export class LicenseManager {
isDisposed = false
- validateLicenseKey(key: string) {
- return key === 'valid'
- }
-
- getDataFromKey(key: string) {
- return {
- customer: 'Example',
- sku: 'commercial',
- origins: ['http://localhost:3000'],
- expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365).getTime(),
- }
- }
-
getLinkId() {
return `removing-invalidates-license-${getHashForObject({ version })}`
}