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