Files
upstream-tldraw/internal/scripts/generate-test-licenses.ts
upstream-mirror a774ac673a
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
2026-09-23 16:12:52 +05:00

258 lines
6.7 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env npx tsx
/**
* Test License Key Generator
*
* This script generates various license keys for manual testing of the LicenseManager.
* Uses the existing test utilities from LicenseManager.test.ts
*/
import crypto from 'crypto'
import { FLAGS } from '../../packages/editor/src/lib/license/LicenseManager'
import { str2ab } from '../../packages/editor/src/lib/utils/licensing'
// Utility function copied from LicenseManager.test.ts to avoid import issues
function ab2str(buf: ArrayBuffer) {
return String.fromCharCode.apply(null, Array.from(new Uint8Array(buf)))
}
// Utility functions copied from LicenseManager.test.ts (not exported)
async function generateLicenseKey(
message: string,
keyPair: { publicKey: string; privateKey: string }
) {
const enc = new TextEncoder()
const encodedMsg = enc.encode(message)
const privateKey = await importPrivateKey(keyPair.privateKey)
const signedLicenseKeyBuffer = await crypto.subtle.sign(
{
name: 'ECDSA',
hash: { name: 'SHA-256' },
},
privateKey,
encodedMsg
)
const signature = btoa(ab2str(signedLicenseKeyBuffer))
const prefix = 'tldraw-'
const licenseKey = `${prefix}/${btoa(message)}.${signature}`
return licenseKey
}
function importPrivateKey(pemContents: string) {
const binaryDerString = atob(pemContents)
const binaryDer = str2ab(binaryDerString)
return crypto.subtle.importKey(
'pkcs8',
new Uint8Array(binaryDer),
{
name: 'ECDSA',
namedCurve: 'P-256',
},
true,
['sign']
)
}
async function exportCryptoKey(key: CryptoKey, isPublic = false) {
const exported = await crypto.subtle.exportKey(isPublic ? 'spki' : 'pkcs8', key)
return btoa(ab2str(exported))
}
// Generate a fresh key pair for each run
async function generateKeyPair() {
const keyPair = await crypto.subtle.generateKey(
{
name: 'ECDSA',
namedCurve: 'P-256',
},
true,
['sign', 'verify']
)
const publicKey = await exportCryptoKey(keyPair.publicKey, true /* isPublic */)
const privateKey = await exportCryptoKey(keyPair.privateKey)
return { publicKey, privateKey }
}
function createLicenseInfo(id: string, hosts: string[], flags: number, expiryDate: string) {
return JSON.stringify([id, hosts, flags, expiryDate])
}
function getDateOffset(days: number): string {
const date = new Date()
date.setDate(date.getDate() + days)
return date.toISOString().slice(0, 10)
}
async function main() {
console.log('🔑 Generating test license keys for LicenseManager testing...\n')
console.log('Generating fresh key pair for testing...')
const keyPair = await generateKeyPair()
console.log('Public Key:', keyPair.publicKey)
console.log('Private Key:', keyPair.privateKey)
console.log('')
const testCases = [
// === VALID LICENSES ===
{
name: 'Valid Annual License',
info: createLicenseInfo(
'test-annual-valid',
['localhost'],
FLAGS.ANNUAL_LICENSE,
getDateOffset(60) // 2 months future
),
},
{
name: 'Valid Perpetual License',
info: createLicenseInfo(
'test-perpetual-valid',
['localhost'],
FLAGS.PERPETUAL_LICENSE,
getDateOffset(365) // 1 year future
),
},
{
name: 'Valid Evaluation License',
info: createLicenseInfo(
'test-eval-valid',
['localhost'],
FLAGS.EVALUATION_LICENSE,
getDateOffset(14) // 2 weeks future
),
},
{
name: 'Valid Internal License',
info: createLicenseInfo(
'test-internal-valid',
['localhost'],
FLAGS.ANNUAL_LICENSE | FLAGS.INTERNAL_LICENSE,
getDateOffset(60) // 2 months future
),
},
{
name: 'License with Watermark Flag',
info: createLicenseInfo(
'test-watermark-only',
['localhost'],
FLAGS.WITH_WATERMARK,
getDateOffset(60) // 2 months future
),
},
// === FEATURE FLAGS ===
{
name: 'License with Commenting Feature',
info: createLicenseInfo(
'test-feat-commenting',
['localhost'],
FLAGS.ANNUAL_LICENSE | FLAGS.FEAT_COMMENTING,
getDateOffset(60) // 2 months future
),
},
{
name: 'License with Collaboration Feature (grants commenting)',
info: createLicenseInfo(
'test-feat-collaboration',
['localhost'],
FLAGS.ANNUAL_LICENSE | FLAGS.FEAT_COLLABORATION,
getDateOffset(60) // 2 months future
),
},
// === GRACE PERIOD TESTING ===
{
name: 'Annual License (expired 15 days - within grace period)',
info: createLicenseInfo(
'test-grace-15days',
['localhost'],
FLAGS.ANNUAL_LICENSE,
getDateOffset(-15) // 15 days past
),
},
{
name: 'Annual License (expired 45 days - beyond grace period)',
info: createLicenseInfo(
'test-annual-expired-45days',
['localhost'],
FLAGS.ANNUAL_LICENSE,
getDateOffset(-45) // 45 days past
),
},
// === EXPIRED INTERNAL LICENSES ===
{
name: 'Internal License (expired 15 days - within grace period)',
info: createLicenseInfo(
'test-internal-expired-15days',
['localhost'],
FLAGS.ANNUAL_LICENSE | FLAGS.INTERNAL_LICENSE,
getDateOffset(-15) // 15 days past
),
},
{
name: 'Internal License (expired 45 days - beyond grace period)',
info: createLicenseInfo(
'test-internal-expired-45days',
['localhost'],
FLAGS.ANNUAL_LICENSE | FLAGS.INTERNAL_LICENSE,
getDateOffset(-45) // 45 days past
),
},
// === EXPIRED EVALUATION LICENSES ===
{
name: 'Evaluation License (expired 1 day)',
info: createLicenseInfo(
'test-eval-expired-1day',
['localhost'],
FLAGS.EVALUATION_LICENSE,
getDateOffset(-1) // 1 day past
),
},
{
name: 'Evaluation License (expired 15 days)',
info: createLicenseInfo(
'test-eval-expired-15days',
['localhost'],
FLAGS.EVALUATION_LICENSE,
getDateOffset(-15) // 15 days past
),
},
]
console.log('🎫 Generated Test License Keys:\n')
for (const testCase of testCases) {
try {
const licenseKey = await generateLicenseKey(testCase.info, keyPair)
console.log(`## ${testCase.name}`)
console.log(`License Info: ${testCase.info}`)
console.log(`License Key: ${licenseKey}`)
console.log('')
} catch (error) {
console.error(`❌ Failed to generate license for ${testCase.name}:`, error)
}
}
console.log('📝 Usage Instructions:')
console.log('')
console.log('🚨 DO NOT COMMIT THESE KEYS TO THE REPOSITORY! 🚨')
console.log('These are temporary test keys - generate fresh ones for each session')
console.log('')
console.log('Option 1 - Update LicenseManager (development):')
console.log(` Replace publicKey in LicenseManager.ts with: "${keyPair.publicKey}"`)
console.log(' Then use: <Tldraw licenseKey="license-key-from-above" />')
console.log('')
console.log('Option 2 - Pass testPublicKey (automated testing):')
console.log(` new LicenseManager("license-key", "${keyPair.publicKey}", "production")`)
console.log('')
console.log('ℹ️ For production licenses: sales@tldraw.com')
}
main().catch(console.error)