@designbasekorea/figma-license-core 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -54,6 +54,7 @@ function asText(input) {
54
54
  function inferCode(text) {
55
55
  const normalized = text.toLowerCase();
56
56
  if (normalized.includes('activation limit')
57
+ || normalized.includes('activation_limit_reached')
57
58
  || normalized.includes('seat')
58
59
  || normalized.includes('too many activations')
59
60
  || normalized.includes('already activated on maximum')) {
@@ -164,7 +165,7 @@ function validationMessage(result) {
164
165
  return '이 멤버십 라이선스는 철회되었습니다.';
165
166
  case 'expired':
166
167
  case 'license_expired':
167
- return '멤버십 라이선스가 만료되었습니다. YouTube 커뮤니티에서 이번 달 키를 확인해 주세요.';
168
+ return '라이선스가 만료되었습니다. 새 키를 확인해 주세요.';
168
169
  case 'not_started':
169
170
  return '이 멤버십 라이선스는 아직 시작되지 않았습니다.';
170
171
  case 'rate_limited':
@@ -181,6 +182,8 @@ function validationMessage(result) {
181
182
  function validationErrorCode(result) {
182
183
  const status = (result.errorCode || result.licenseStatus || '').toLowerCase();
183
184
  switch (status) {
185
+ case 'activation_limit_reached':
186
+ return exports.LicenseErrorCode.ACTIVATION_LIMIT_REACHED;
184
187
  case 'expired':
185
188
  case 'license_expired':
186
189
  return exports.LicenseErrorCode.LICENSE_EXPIRED;
@@ -291,7 +294,7 @@ class LicenseCore {
291
294
  }
292
295
  async initialize() {
293
296
  let state = await this.getStatus();
294
- if (state.paymentStatus === 'PAID' && state.licenseKey && state.licenseKey !== this.config.masterCode) {
297
+ if (state.licenseKey && state.licenseKey !== this.config.masterCode) {
295
298
  try {
296
299
  const validateResult = this.deps.transport.validate
297
300
  ? await this.deps.transport.validate({
@@ -432,10 +435,12 @@ class LicenseCore {
432
435
  }
433
436
  let verifyResult;
434
437
  try {
438
+ const current = await this.getStatus();
435
439
  verifyResult = await this.deps.transport.verify({
436
440
  licenseKey: trimmedLicenseKey,
437
441
  productId: this.config.productId,
438
442
  instanceName: this.config.instanceName,
443
+ instanceId: current.licenseKey === trimmedLicenseKey ? current.instanceId : undefined,
439
444
  });
440
445
  }
441
446
  catch (error) {
@@ -451,7 +456,9 @@ class LicenseCore {
451
456
  }
452
457
  const activationLimit = toNonNegativeNumber(verifyResult.activationLimit, 0);
453
458
  const activationUsage = toNonNegativeNumber(verifyResult.activationUsage, 0);
454
- if (isActivationLimitReached(activationLimit, activationUsage)) {
459
+ // A verified instance already occupies its seat. usage === limit is not
460
+ // an error after successful activation (e.g. the first device on a 1-seat key).
461
+ if (!verifyResult.instanceId && isActivationLimitReached(activationLimit, activationUsage)) {
455
462
  throw new LicenseCoreError(exports.LicenseErrorCode.ACTIVATION_LIMIT_REACHED, '활성화 가능한 기기 수를 모두 사용했습니다. 기존 기기를 비활성화한 뒤 다시 시도해 주세요.', verifyResult);
456
463
  }
457
464
  const current = await this.getStatus();
@@ -791,12 +798,14 @@ function asRecord$1(value) {
791
798
  }
792
799
  function failureMessageForStatus(status) {
793
800
  switch (status) {
801
+ case 'activation_limit_reached':
802
+ return '이 라이선스의 활성화 기기 수를 초과했습니다. 기존 기기에서 먼저 비활성화해 주세요.';
794
803
  case 'not_started':
795
804
  return '이번 달 라이선스의 사용 기간이 아직 시작되지 않았습니다.';
796
805
  case 'expired':
797
- return '라이선스가 만료되었습니다. 이번 달 키를 다시 확인해 주세요.';
806
+ return '라이선스가 만료되었습니다. 새 키를 확인해 주세요.';
798
807
  case 'revoked':
799
- return '사용 중지된 라이선스입니다. 새 키를 확인해 주세요.';
808
+ return '사용 중지된 라이선스입니다. 구매 내역이나 관리자에게 문의해 주세요.';
800
809
  case 'product_not_allowed':
801
810
  return '이 라이선스는 현재 플러그인에서 사용할 수 없습니다.';
802
811
  case 'rate_limited':
@@ -812,9 +821,12 @@ function defaultNormalizeVerifyResponse(raw) {
812
821
  const statusValid = typeof licenseStatus === 'string'
813
822
  ? licenseStatus.toLowerCase() === 'active'
814
823
  : undefined;
815
- const valid = readBooleanField(body, ['valid', 'success', 'active'])
824
+ const rejectedStatus = ['expired', 'revoked', 'invalid', 'inactive', 'not_started',
825
+ 'product_not_allowed', 'rate_limited', 'activation_limit_reached', 'environment_mismatch', 'mode_unverified']
826
+ .includes(licenseStatus?.trim().toLowerCase() || '');
827
+ const valid = !rejectedStatus && (readBooleanField(body, ['valid', 'success', 'active'])
816
828
  ?? statusValid
817
- ?? false;
829
+ ?? false);
818
830
  const normalizedStatus = licenseStatus?.trim().toLowerCase();
819
831
  const fallbackMessage = !valid ? failureMessageForStatus(normalizedStatus) : undefined;
820
832
  const message = readTextField(body, ['message', 'error', 'detail']) || fallbackMessage;
@@ -1189,6 +1201,205 @@ class DesignbaseAccountLicenseTransport {
1189
1201
  }
1190
1202
  }
1191
1203
 
1204
+ /** Copy legacy storage once. Paid status is revalidated before any UI is published. */
1205
+ async function migrateLegacyLicenseStorage(storage, prefix, keys = {}) {
1206
+ const oldKey = (field) => {
1207
+ const oldPrefix = keys.sourcePrefix ? `${keys.sourcePrefix}:` : '';
1208
+ return `${oldPrefix}${keys[field] || field}`;
1209
+ };
1210
+ const marker = `${prefix}:legacyMigration`;
1211
+ if (await storage.get(marker))
1212
+ return;
1213
+ const existing = await storage.get(`${prefix}:paymentStatus`);
1214
+ if (existing === undefined || existing === null) {
1215
+ const key = await storage.get(oldKey('licenseKey'));
1216
+ const instanceId = await storage.get(oldKey('instanceId'));
1217
+ const paymentStatus = await storage.get(oldKey('paymentStatus'));
1218
+ const usage = await storage.get(oldKey('usageCount'));
1219
+ if (typeof key === 'string' && key.trim()) {
1220
+ await storage.set(`${prefix}:licenseKey`, key.trim());
1221
+ await storage.set(`${prefix}:instanceId`, typeof instanceId === 'string' ? instanceId : '');
1222
+ await storage.set(`${prefix}:planTier`, paymentStatus === 'PAID' ? 'PAID' : 'FREE');
1223
+ }
1224
+ if (typeof usage === 'number' && Number.isFinite(usage) && usage >= 0) {
1225
+ await storage.set(`${prefix}:usageCount`, Math.floor(usage));
1226
+ }
1227
+ if (typeof key === 'string' && key.trim()) {
1228
+ await storage.set(`${prefix}:paymentStatus`, paymentStatus === 'PAID' ? 'PAID' : 'FREE');
1229
+ }
1230
+ }
1231
+ // Do not delete legacy keys: another version may still need them for rollback.
1232
+ await storage.set(marker, 1);
1233
+ }
1234
+ async function readOrCreateDeviceId(storage, prefix) {
1235
+ const storageKey = `${prefix}:deviceId`;
1236
+ const current = await storage.get(storageKey);
1237
+ if (typeof current === 'string' && current.length > 0)
1238
+ return current;
1239
+ const legacy = await storage.get('licenseDeviceId');
1240
+ const nonce = `${Math.random().toString(36).slice(2)}-${Math.random().toString(36).slice(2)}`;
1241
+ const device = typeof legacy === 'string' && legacy.length > 0
1242
+ ? legacy
1243
+ : `license-${Date.now().toString(36)}-${nonce}`;
1244
+ await storage.set(storageKey, device);
1245
+ return device;
1246
+ }
1247
+ const pendingDeviceIds = new WeakMap();
1248
+ async function getPersistentLicenseDeviceId(storage, prefix) {
1249
+ let pending = pendingDeviceIds.get(storage);
1250
+ if (!pending) {
1251
+ pending = new Map();
1252
+ pendingDeviceIds.set(storage, pending);
1253
+ }
1254
+ const existing = pending.get(prefix);
1255
+ if (existing)
1256
+ return existing;
1257
+ const request = readOrCreateDeviceId(storage, prefix);
1258
+ pending.set(prefix, request);
1259
+ try {
1260
+ return await request;
1261
+ }
1262
+ finally {
1263
+ pending.delete(prefix);
1264
+ }
1265
+ }
1266
+
1267
+ const errors = {
1268
+ expired: [
1269
+ exports.LicenseErrorCode.LICENSE_EXPIRED,
1270
+ '라이선스가 만료되었습니다. 구매 내역 또는 이번 달 멤버십 키를 확인해 주세요.',
1271
+ ],
1272
+ revoked: [exports.LicenseErrorCode.LICENSE_REVOKED, '사용 중지된 라이선스입니다.'],
1273
+ not_started: [
1274
+ exports.LicenseErrorCode.INVALID_LICENSE,
1275
+ '라이선스 사용 기간이 아직 시작되지 않았습니다.',
1276
+ ],
1277
+ product_not_allowed: [
1278
+ exports.LicenseErrorCode.PRODUCT_MISMATCH,
1279
+ '이 라이선스는 현재 플러그인 구매 키가 아닙니다.',
1280
+ ],
1281
+ environment_mismatch: [
1282
+ exports.LicenseErrorCode.INVALID_LICENSE,
1283
+ '테스트 구매 키는 테스트 인증 서버에서만 사용할 수 있습니다.',
1284
+ ],
1285
+ mode_unverified: [
1286
+ exports.LicenseErrorCode.NETWORK_ERROR,
1287
+ '라이선스의 테스트·운영 구분을 확인하지 못했습니다.',
1288
+ ],
1289
+ rate_limited: [
1290
+ exports.LicenseErrorCode.RATE_LIMITED,
1291
+ '인증 요청이 너무 많습니다. 잠시 후 다시 시도해 주세요.',
1292
+ ],
1293
+ network_error: [
1294
+ exports.LicenseErrorCode.NETWORK_ERROR,
1295
+ '라이선스 서버에 연결하지 못했습니다. 다시 시도해 주세요.',
1296
+ ],
1297
+ activation_limit_reached: [
1298
+ exports.LicenseErrorCode.ACTIVATION_LIMIT_REACHED,
1299
+ '사용 가능한 기기 수를 모두 사용했습니다. 다른 기기에서 해제해 주세요.',
1300
+ ],
1301
+ activation_required: [exports.LicenseErrorCode.INVALID_LICENSE, '라이선스를 다시 인증해 주세요.'],
1302
+ invalid_instance: [
1303
+ exports.LicenseErrorCode.INVALID_LICENSE,
1304
+ '이 기기의 인증 정보가 만료되었습니다. 다시 인증해 주세요.',
1305
+ ],
1306
+ };
1307
+ function normalizeWordPressLicenseResponse(raw, productId) {
1308
+ const data = raw && typeof raw === 'object' ? raw : {};
1309
+ const status = typeof data.status === 'string' ? data.status : 'invalid';
1310
+ if (data.valid !== true || status !== 'active') {
1311
+ const [code, message] = errors[status] || [
1312
+ exports.LicenseErrorCode.INVALID_LICENSE,
1313
+ '유효하지 않은 라이선스 키입니다.',
1314
+ ];
1315
+ throw new LicenseCoreError(code, message, undefined, code === exports.LicenseErrorCode.NETWORK_ERROR || code === exports.LicenseErrorCode.RATE_LIMITED);
1316
+ }
1317
+ if (data.product_id !== productId) {
1318
+ throw new LicenseCoreError(exports.LicenseErrorCode.PRODUCT_MISMATCH, '인증 서버의 상품 정보가 일치하지 않습니다.');
1319
+ }
1320
+ const source = typeof data.source === 'string' ? data.source : '';
1321
+ if (data.provider === 'lemon_api' &&
1322
+ (typeof data.instance_id !== 'string' || !data.instance_id)) {
1323
+ throw new LicenseCoreError(exports.LicenseErrorCode.INVALID_LICENSE, '기기 활성화 정보를 확인하지 못했습니다.');
1324
+ }
1325
+ return {
1326
+ valid: true,
1327
+ productId,
1328
+ planTier: 'PAID',
1329
+ licenseStatus: 'active',
1330
+ instanceId: typeof data.instance_id === 'string' ? data.instance_id : undefined,
1331
+ activationLimit: typeof data.activation_limit === 'number' ? data.activation_limit : 0,
1332
+ activationUsage: typeof data.activation_usage === 'number' ? data.activation_usage : 0,
1333
+ licenseSource: source,
1334
+ accessScope: typeof data.access_scope === 'string' ? data.access_scope : 'product',
1335
+ expiresAt: typeof data.expires_at === 'string' ? data.expires_at : undefined,
1336
+ };
1337
+ }
1338
+ function createWordPressLicenseTransport(options) {
1339
+ const { storage, storagePrefix: prefix, productId } = options;
1340
+ if (!productId.trim() || !prefix.trim())
1341
+ throw new Error('productId and storagePrefix are required.');
1342
+ if (!/^https:\/\//i.test(options.baseUrl) &&
1343
+ !/^http:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?(\/|$)/i.test(options.baseUrl)) {
1344
+ throw new Error('License server must use HTTPS (HTTP is allowed only for local development).');
1345
+ }
1346
+ const timeout = options.timeoutMs || 20000;
1347
+ const fetchFn = options.fetchFn || fetch;
1348
+ const body = (payload) => ({
1349
+ license_key: payload.licenseKey,
1350
+ product_id: productId,
1351
+ instance_id: payload.instanceId || '',
1352
+ device_id: payload.deviceId,
1353
+ });
1354
+ const base = options.baseUrl.replace(/\/$/, '');
1355
+ const http = new FetchLicenseTransport({
1356
+ verifyUrl: `${base}/activate`,
1357
+ validateUrl: `${base}/validate`,
1358
+ deactivateUrl: `${base}/deactivate`,
1359
+ fetchFn,
1360
+ useAbortSignal: false,
1361
+ timeoutMs: timeout,
1362
+ buildVerifyBody: body,
1363
+ buildValidateBody: body,
1364
+ buildDeactivateBody: body,
1365
+ normalizeVerifyResponse: (raw) => normalizeWordPressLicenseResponse(raw, productId),
1366
+ normalizeDeactivateResponse: (raw) => {
1367
+ const data = raw && typeof raw === 'object' ? raw : {};
1368
+ const success = data.success === true || data.deactivated === true;
1369
+ return {
1370
+ success,
1371
+ message: success
1372
+ ? undefined
1373
+ : '라이선스 연결을 해제하지 못했습니다. 다시 시도해 주세요.',
1374
+ };
1375
+ },
1376
+ });
1377
+ const enrich = async (payload) => ({
1378
+ ...payload,
1379
+ deviceId: await getPersistentLicenseDeviceId(storage, prefix),
1380
+ });
1381
+ return {
1382
+ verify: async (payload) => {
1383
+ const storedKey = await storage.get(`${prefix}:licenseKey`);
1384
+ const storedInstance = await storage.get(`${prefix}:instanceId`);
1385
+ return http.verify(await enrich({
1386
+ ...payload,
1387
+ instanceId: payload.instanceId ||
1388
+ (storedKey === payload.licenseKey && typeof storedInstance === 'string'
1389
+ ? storedInstance
1390
+ : ''),
1391
+ }));
1392
+ },
1393
+ validate: async (payload) => {
1394
+ const enriched = await enrich(payload);
1395
+ // Legacy users may not have a Lemon instance yet. Activate once;
1396
+ // the server reuses its device mapping when a response was lost.
1397
+ return payload.instanceId ? http.validate(enriched) : http.verify(enriched);
1398
+ },
1399
+ deactivate: async (payload) => http.deactivate(await enrich(payload)),
1400
+ };
1401
+ }
1402
+
1192
1403
  class FigmaStorageAdapter {
1193
1404
  constructor(figma) {
1194
1405
  this.figma = figma;
@@ -1233,6 +1444,7 @@ const DEFAULT_LICENSE_MESSAGE_TYPES = {
1233
1444
  class FigmaLicenseStatusManager {
1234
1445
  constructor(options) {
1235
1446
  this.options = options;
1447
+ this.messageQueue = Promise.resolve();
1236
1448
  this.messageTypes = {
1237
1449
  ...DEFAULT_LICENSE_MESSAGE_TYPES,
1238
1450
  ...(options.messageTypes || {}),
@@ -1242,6 +1454,7 @@ class FigmaLicenseStatusManager {
1242
1454
  async initialize() {
1243
1455
  if (!this.initializationPromise) {
1244
1456
  this.initializationPromise = (async () => {
1457
+ await this.options.beforeInitialize?.();
1245
1458
  const state = await this.options.core.initialize();
1246
1459
  await this.postStatusUpdate(state);
1247
1460
  })().catch((error) => {
@@ -1251,7 +1464,12 @@ class FigmaLicenseStatusManager {
1251
1464
  }
1252
1465
  await this.initializationPromise;
1253
1466
  }
1254
- async handleMessage(pluginMessage) {
1467
+ handleMessage(pluginMessage) {
1468
+ const operation = this.messageQueue.then(() => this.handleMessageNow(pluginMessage));
1469
+ this.messageQueue = operation.then(() => undefined, () => undefined);
1470
+ return operation;
1471
+ }
1472
+ async handleMessageNow(pluginMessage) {
1255
1473
  if (!pluginMessage || typeof pluginMessage !== 'object') {
1256
1474
  return false;
1257
1475
  }
@@ -1265,15 +1483,18 @@ class FigmaLicenseStatusManager {
1265
1483
  return true;
1266
1484
  }
1267
1485
  if (type === this.messageTypes.verifyLicense) {
1486
+ await this.initialize();
1268
1487
  const licenseKey = typeof message.licenseKey === 'string' ? message.licenseKey : '';
1269
1488
  await this.handleVerifyLicense(licenseKey);
1270
1489
  return true;
1271
1490
  }
1272
1491
  if (type === this.messageTypes.deactivateLicense) {
1492
+ await this.initialize();
1273
1493
  await this.handleDeactivateLicense();
1274
1494
  return true;
1275
1495
  }
1276
1496
  if (type === this.messageTypes.checkFeatureAccess) {
1497
+ await this.initialize();
1277
1498
  const featureKey = typeof message.featureKey === 'string' ? message.featureKey : undefined;
1278
1499
  const amount = typeof message.amount === 'number' && Number.isFinite(message.amount) && message.amount > 0
1279
1500
  ? message.amount
@@ -1282,6 +1503,7 @@ class FigmaLicenseStatusManager {
1282
1503
  return true;
1283
1504
  }
1284
1505
  if (type === this.messageTypes.consumeUsage) {
1506
+ await this.initialize();
1285
1507
  const amount = typeof message.amount === 'number' && Number.isFinite(message.amount) && message.amount > 0
1286
1508
  ? message.amount
1287
1509
  : 1;
@@ -1305,6 +1527,7 @@ class FigmaLicenseStatusManager {
1305
1527
  * This is the preferred guard for operations handled entirely in main.
1306
1528
  */
1307
1529
  async canUseFeature(featureKey, amount = 1) {
1530
+ await this.initialize();
1308
1531
  const state = await this.options.core.getStatus();
1309
1532
  const safeAmount = Number.isFinite(amount) && amount > 0 ? amount : 1;
1310
1533
  return this.options.core.resolveUsageAccess(state, featureKey, safeAmount);
@@ -1313,6 +1536,7 @@ class FigmaLicenseStatusManager {
1313
1536
  * Consume usage from main code and publish the refreshed status to the UI.
1314
1537
  */
1315
1538
  async consumeUsage(amount = 1, featureKey) {
1539
+ await this.initialize();
1316
1540
  const result = await this.options.core.consumeUsage(amount, featureKey);
1317
1541
  await this.postStatusUpdate(result.state);
1318
1542
  return result;
@@ -1449,14 +1673,21 @@ function createFigmaLicenseStatusManager(options) {
1449
1673
  const figmaApi = resolveFigmaApi(options.figma);
1450
1674
  const storage = options.storage ?? new FigmaStorageAdapter(figmaApi);
1451
1675
  const notifier = options.notifier ?? new FigmaNotifierAdapter(figmaApi);
1452
- const transport = options.transport ?? new FetchLicenseTransport({
1453
- verifyUrl: options.config.endpoint,
1454
- validateUrl: options.config.validateEndpoint,
1455
- deactivateUrl: options.config.deactivateEndpoint,
1456
- headers: options.config.requestHeaders,
1457
- timeoutMs: options.config.requestTimeoutMs,
1458
- fetchFn: options.fetchFn,
1459
- });
1676
+ const prefix = options.config.storagePrefix?.trim() || `${options.config.instanceName}:${options.config.productId}`;
1677
+ const transport = options.transport ?? (options.wordpress
1678
+ ? createWordPressLicenseTransport({
1679
+ baseUrl: options.wordpress.baseUrl, productId: options.config.productId,
1680
+ storagePrefix: prefix, storage, fetchFn: options.fetchFn,
1681
+ timeoutMs: options.config.requestTimeoutMs,
1682
+ })
1683
+ : new FetchLicenseTransport({
1684
+ verifyUrl: options.config.endpoint,
1685
+ validateUrl: options.config.validateEndpoint,
1686
+ deactivateUrl: options.config.deactivateEndpoint,
1687
+ headers: options.config.requestHeaders,
1688
+ timeoutMs: options.config.requestTimeoutMs,
1689
+ fetchFn: options.fetchFn,
1690
+ }));
1460
1691
  const coreDependencies = {
1461
1692
  storage,
1462
1693
  transport,
@@ -1468,6 +1699,26 @@ function createFigmaLicenseStatusManager(options) {
1468
1699
  core,
1469
1700
  notifier,
1470
1701
  messageTypes: options.messageTypes,
1702
+ beforeInitialize: options.migrateLegacyStorage
1703
+ ? () => migrateLegacyLicenseStorage(storage, prefix, typeof options.migrateLegacyStorage === 'object' ? options.migrateLegacyStorage : undefined)
1704
+ : undefined,
1705
+ });
1706
+ }
1707
+ /** Shared membership + Lemon gateway setup. Product allowlists stay on the server. */
1708
+ function createWordPressFigmaLicenseManager(options) {
1709
+ const base = options.serverBaseUrl.replace(/\/$/, '');
1710
+ return createFigmaLicenseStatusManager({
1711
+ figma: options.figma, storage: options.storage, notifier: options.notifier,
1712
+ fetchFn: options.fetchFn, messageTypes: options.messageTypes,
1713
+ migrateLegacyStorage: options.migrateLegacyStorage ?? true,
1714
+ wordpress: { baseUrl: base },
1715
+ config: {
1716
+ productId: options.productId, instanceName: options.instanceName || options.productId,
1717
+ storagePrefix: options.storagePrefix || `designbase:${options.productId}`,
1718
+ endpoint: `${base}/activate`, validateEndpoint: `${base}/validate`, deactivateEndpoint: `${base}/deactivate`,
1719
+ requestTimeoutMs: options.timeoutMs || 20000,
1720
+ policy: options.policy,
1721
+ },
1471
1722
  });
1472
1723
  }
1473
1724
 
@@ -1482,8 +1733,13 @@ exports.LicenseCore = LicenseCore;
1482
1733
  exports.LicenseCoreError = LicenseCoreError;
1483
1734
  exports.LicenseTransportHttpError = LicenseTransportHttpError;
1484
1735
  exports.createFigmaLicenseStatusManager = createFigmaLicenseStatusManager;
1736
+ exports.createWordPressFigmaLicenseManager = createWordPressFigmaLicenseManager;
1737
+ exports.createWordPressLicenseTransport = createWordPressLicenseTransport;
1485
1738
  exports.defaultNormalizeDeactivateResponse = defaultNormalizeDeactivateResponse;
1486
1739
  exports.defaultNormalizeVerifyResponse = defaultNormalizeVerifyResponse;
1740
+ exports.getPersistentLicenseDeviceId = getPersistentLicenseDeviceId;
1487
1741
  exports.isActivationLimitReached = isActivationLimitReached;
1488
1742
  exports.mapLicenseError = mapLicenseError;
1743
+ exports.migrateLegacyLicenseStorage = migrateLegacyLicenseStorage;
1744
+ exports.normalizeWordPressLicenseResponse = normalizeWordPressLicenseResponse;
1489
1745
  //# sourceMappingURL=index.js.map