@designbasekorea/figma-license-core 0.3.3 → 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 +260 -14
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +264 -13
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +264 -13
- package/dist/index.umd.js.map +1 -1
- package/dist/licenseCore.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/README.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetchLicenseTransport.d.ts","sourceRoot":"","sources":["../src/fetchLicenseTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,SAAS,CAAC;AAwFjB,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,
|
|
1
|
+
{"version":3,"file":"fetchLicenseTransport.d.ts","sourceRoot":"","sources":["../src/fetchLicenseTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,SAAS,CAAC;AAwFjB,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,CAyEhF;AAED,wBAAgB,kCAAkC,CAAC,GAAG,EAAE,OAAO,GAAG,uBAAuB,CAexF;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC;IAC7D,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC;IAC/D,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,OAAO,CAAC;IACrE,uBAAuB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,mBAAmB,CAAC;IAChE,2BAA2B,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,uBAAuB,CAAC;CACzE;AAED,qBAAa,yBAA0B,SAAQ,KAAK;aAEhC,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,OAAO;gBADb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EAC7B,OAAO,EAAE,MAAM;CAKlB;AAED,qBAAa,qBAAsB,YAAW,gBAAgB;IAGhD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;gBAEV,MAAM,EAAE,2BAA2B;IAQ1D,MAAM,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAUnE,QAAQ,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAYrE,UAAU,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;YAevE,QAAQ;CAoEvB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LegacyLicenseStorageKeys } from './licenseMigration';
|
|
1
2
|
import { LicenseCore } from './licenseCore';
|
|
2
3
|
import { LicenseConfig, LicenseManagerMessageTypes, LicenseNotifierAdapter, LicenseStorageAdapter, LicenseTransport } from './types';
|
|
3
4
|
export interface FigmaClientStorageLike {
|
|
@@ -38,6 +39,7 @@ export interface FigmaLicenseStatusManagerOptions {
|
|
|
38
39
|
core: LicenseCore;
|
|
39
40
|
notifier?: LicenseNotifierAdapter;
|
|
40
41
|
messageTypes?: Partial<LicenseManagerMessageTypes>;
|
|
42
|
+
beforeInitialize?: () => Promise<void>;
|
|
41
43
|
}
|
|
42
44
|
export interface FigmaLicenseMessage {
|
|
43
45
|
type: string;
|
|
@@ -48,9 +50,11 @@ export declare class FigmaLicenseStatusManager {
|
|
|
48
50
|
private readonly messageTypes;
|
|
49
51
|
private readonly notifier?;
|
|
50
52
|
private initializationPromise?;
|
|
53
|
+
private messageQueue;
|
|
51
54
|
constructor(options: FigmaLicenseStatusManagerOptions);
|
|
52
55
|
initialize(): Promise<void>;
|
|
53
56
|
handleMessage(pluginMessage: unknown): Promise<boolean>;
|
|
57
|
+
private handleMessageNow;
|
|
54
58
|
bindToOnMessage(fallbackHandler?: (pluginMessage: unknown) => void | Promise<void>): void;
|
|
55
59
|
/**
|
|
56
60
|
* Check a feature from main code using the same policy as the UI messages.
|
|
@@ -77,6 +81,22 @@ export interface CreateFigmaLicenseStatusManagerOptions {
|
|
|
77
81
|
notifier?: LicenseNotifierAdapter;
|
|
78
82
|
messageTypes?: Partial<LicenseManagerMessageTypes>;
|
|
79
83
|
fetchFn?: typeof fetch;
|
|
84
|
+
/** Use the shared WordPress protocol instead of generic JSON. */
|
|
85
|
+
wordpress?: {
|
|
86
|
+
baseUrl: string;
|
|
87
|
+
};
|
|
88
|
+
/** Migrate old plugin-local storage before first initialization. */
|
|
89
|
+
migrateLegacyStorage?: boolean | LegacyLicenseStorageKeys;
|
|
80
90
|
}
|
|
81
91
|
export declare function createFigmaLicenseStatusManager(options: CreateFigmaLicenseStatusManagerOptions): FigmaLicenseStatusManager;
|
|
92
|
+
export interface CreateWordPressFigmaLicenseManagerOptions extends Omit<CreateFigmaLicenseStatusManagerOptions, 'config' | 'transport' | 'wordpress'> {
|
|
93
|
+
productId: string;
|
|
94
|
+
serverBaseUrl: string;
|
|
95
|
+
instanceName?: string;
|
|
96
|
+
storagePrefix?: string;
|
|
97
|
+
policy?: LicenseConfig['policy'];
|
|
98
|
+
timeoutMs?: number;
|
|
99
|
+
}
|
|
100
|
+
/** Shared membership + Lemon gateway setup. Product allowlists stay on the server. */
|
|
101
|
+
export declare function createWordPressFigmaLicenseManager(options: CreateWordPressFigmaLicenseManagerOptions): FigmaLicenseStatusManager;
|
|
82
102
|
//# sourceMappingURL=figmaStatusManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figmaStatusManager.d.ts","sourceRoot":"","sources":["../src/figmaStatusManager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"figmaStatusManager.d.ts","sourceRoot":"","sources":["../src/figmaStatusManager.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,4 +4,6 @@ export * from './licenseCore';
|
|
|
4
4
|
export * from './fetchLicenseTransport';
|
|
5
5
|
export * from './designbaseAccountAuth';
|
|
6
6
|
export * from './figmaStatusManager';
|
|
7
|
+
export * from './licenseMigration';
|
|
8
|
+
export * from './wordpressLicenseTransport';
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC"}
|
package/dist/index.esm.js
CHANGED
|
@@ -292,7 +292,7 @@ class LicenseCore {
|
|
|
292
292
|
}
|
|
293
293
|
async initialize() {
|
|
294
294
|
let state = await this.getStatus();
|
|
295
|
-
if (state.
|
|
295
|
+
if (state.licenseKey && state.licenseKey !== this.config.masterCode) {
|
|
296
296
|
try {
|
|
297
297
|
const validateResult = this.deps.transport.validate
|
|
298
298
|
? await this.deps.transport.validate({
|
|
@@ -433,10 +433,12 @@ class LicenseCore {
|
|
|
433
433
|
}
|
|
434
434
|
let verifyResult;
|
|
435
435
|
try {
|
|
436
|
+
const current = await this.getStatus();
|
|
436
437
|
verifyResult = await this.deps.transport.verify({
|
|
437
438
|
licenseKey: trimmedLicenseKey,
|
|
438
439
|
productId: this.config.productId,
|
|
439
440
|
instanceName: this.config.instanceName,
|
|
441
|
+
instanceId: current.licenseKey === trimmedLicenseKey ? current.instanceId : undefined,
|
|
440
442
|
});
|
|
441
443
|
}
|
|
442
444
|
catch (error) {
|
|
@@ -452,7 +454,9 @@ class LicenseCore {
|
|
|
452
454
|
}
|
|
453
455
|
const activationLimit = toNonNegativeNumber(verifyResult.activationLimit, 0);
|
|
454
456
|
const activationUsage = toNonNegativeNumber(verifyResult.activationUsage, 0);
|
|
455
|
-
|
|
457
|
+
// A verified instance already occupies its seat. usage === limit is not
|
|
458
|
+
// an error after successful activation (e.g. the first device on a 1-seat key).
|
|
459
|
+
if (!verifyResult.instanceId && isActivationLimitReached(activationLimit, activationUsage)) {
|
|
456
460
|
throw new LicenseCoreError(LicenseErrorCode.ACTIVATION_LIMIT_REACHED, '활성화 가능한 기기 수를 모두 사용했습니다. 기존 기기를 비활성화한 뒤 다시 시도해 주세요.', verifyResult);
|
|
457
461
|
}
|
|
458
462
|
const current = await this.getStatus();
|
|
@@ -815,9 +819,12 @@ function defaultNormalizeVerifyResponse(raw) {
|
|
|
815
819
|
const statusValid = typeof licenseStatus === 'string'
|
|
816
820
|
? licenseStatus.toLowerCase() === 'active'
|
|
817
821
|
: undefined;
|
|
818
|
-
const
|
|
822
|
+
const rejectedStatus = ['expired', 'revoked', 'invalid', 'inactive', 'not_started',
|
|
823
|
+
'product_not_allowed', 'rate_limited', 'activation_limit_reached', 'environment_mismatch', 'mode_unverified']
|
|
824
|
+
.includes(licenseStatus?.trim().toLowerCase() || '');
|
|
825
|
+
const valid = !rejectedStatus && (readBooleanField(body, ['valid', 'success', 'active'])
|
|
819
826
|
?? statusValid
|
|
820
|
-
?? false;
|
|
827
|
+
?? false);
|
|
821
828
|
const normalizedStatus = licenseStatus?.trim().toLowerCase();
|
|
822
829
|
const fallbackMessage = !valid ? failureMessageForStatus(normalizedStatus) : undefined;
|
|
823
830
|
const message = readTextField(body, ['message', 'error', 'detail']) || fallbackMessage;
|
|
@@ -1192,6 +1199,205 @@ class DesignbaseAccountLicenseTransport {
|
|
|
1192
1199
|
}
|
|
1193
1200
|
}
|
|
1194
1201
|
|
|
1202
|
+
/** Copy legacy storage once. Paid status is revalidated before any UI is published. */
|
|
1203
|
+
async function migrateLegacyLicenseStorage(storage, prefix, keys = {}) {
|
|
1204
|
+
const oldKey = (field) => {
|
|
1205
|
+
const oldPrefix = keys.sourcePrefix ? `${keys.sourcePrefix}:` : '';
|
|
1206
|
+
return `${oldPrefix}${keys[field] || field}`;
|
|
1207
|
+
};
|
|
1208
|
+
const marker = `${prefix}:legacyMigration`;
|
|
1209
|
+
if (await storage.get(marker))
|
|
1210
|
+
return;
|
|
1211
|
+
const existing = await storage.get(`${prefix}:paymentStatus`);
|
|
1212
|
+
if (existing === undefined || existing === null) {
|
|
1213
|
+
const key = await storage.get(oldKey('licenseKey'));
|
|
1214
|
+
const instanceId = await storage.get(oldKey('instanceId'));
|
|
1215
|
+
const paymentStatus = await storage.get(oldKey('paymentStatus'));
|
|
1216
|
+
const usage = await storage.get(oldKey('usageCount'));
|
|
1217
|
+
if (typeof key === 'string' && key.trim()) {
|
|
1218
|
+
await storage.set(`${prefix}:licenseKey`, key.trim());
|
|
1219
|
+
await storage.set(`${prefix}:instanceId`, typeof instanceId === 'string' ? instanceId : '');
|
|
1220
|
+
await storage.set(`${prefix}:planTier`, paymentStatus === 'PAID' ? 'PAID' : 'FREE');
|
|
1221
|
+
}
|
|
1222
|
+
if (typeof usage === 'number' && Number.isFinite(usage) && usage >= 0) {
|
|
1223
|
+
await storage.set(`${prefix}:usageCount`, Math.floor(usage));
|
|
1224
|
+
}
|
|
1225
|
+
if (typeof key === 'string' && key.trim()) {
|
|
1226
|
+
await storage.set(`${prefix}:paymentStatus`, paymentStatus === 'PAID' ? 'PAID' : 'FREE');
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
// Do not delete legacy keys: another version may still need them for rollback.
|
|
1230
|
+
await storage.set(marker, 1);
|
|
1231
|
+
}
|
|
1232
|
+
async function readOrCreateDeviceId(storage, prefix) {
|
|
1233
|
+
const storageKey = `${prefix}:deviceId`;
|
|
1234
|
+
const current = await storage.get(storageKey);
|
|
1235
|
+
if (typeof current === 'string' && current.length > 0)
|
|
1236
|
+
return current;
|
|
1237
|
+
const legacy = await storage.get('licenseDeviceId');
|
|
1238
|
+
const nonce = `${Math.random().toString(36).slice(2)}-${Math.random().toString(36).slice(2)}`;
|
|
1239
|
+
const device = typeof legacy === 'string' && legacy.length > 0
|
|
1240
|
+
? legacy
|
|
1241
|
+
: `license-${Date.now().toString(36)}-${nonce}`;
|
|
1242
|
+
await storage.set(storageKey, device);
|
|
1243
|
+
return device;
|
|
1244
|
+
}
|
|
1245
|
+
const pendingDeviceIds = new WeakMap();
|
|
1246
|
+
async function getPersistentLicenseDeviceId(storage, prefix) {
|
|
1247
|
+
let pending = pendingDeviceIds.get(storage);
|
|
1248
|
+
if (!pending) {
|
|
1249
|
+
pending = new Map();
|
|
1250
|
+
pendingDeviceIds.set(storage, pending);
|
|
1251
|
+
}
|
|
1252
|
+
const existing = pending.get(prefix);
|
|
1253
|
+
if (existing)
|
|
1254
|
+
return existing;
|
|
1255
|
+
const request = readOrCreateDeviceId(storage, prefix);
|
|
1256
|
+
pending.set(prefix, request);
|
|
1257
|
+
try {
|
|
1258
|
+
return await request;
|
|
1259
|
+
}
|
|
1260
|
+
finally {
|
|
1261
|
+
pending.delete(prefix);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
const errors = {
|
|
1266
|
+
expired: [
|
|
1267
|
+
LicenseErrorCode.LICENSE_EXPIRED,
|
|
1268
|
+
'라이선스가 만료되었습니다. 구매 내역 또는 이번 달 멤버십 키를 확인해 주세요.',
|
|
1269
|
+
],
|
|
1270
|
+
revoked: [LicenseErrorCode.LICENSE_REVOKED, '사용 중지된 라이선스입니다.'],
|
|
1271
|
+
not_started: [
|
|
1272
|
+
LicenseErrorCode.INVALID_LICENSE,
|
|
1273
|
+
'라이선스 사용 기간이 아직 시작되지 않았습니다.',
|
|
1274
|
+
],
|
|
1275
|
+
product_not_allowed: [
|
|
1276
|
+
LicenseErrorCode.PRODUCT_MISMATCH,
|
|
1277
|
+
'이 라이선스는 현재 플러그인 구매 키가 아닙니다.',
|
|
1278
|
+
],
|
|
1279
|
+
environment_mismatch: [
|
|
1280
|
+
LicenseErrorCode.INVALID_LICENSE,
|
|
1281
|
+
'테스트 구매 키는 테스트 인증 서버에서만 사용할 수 있습니다.',
|
|
1282
|
+
],
|
|
1283
|
+
mode_unverified: [
|
|
1284
|
+
LicenseErrorCode.NETWORK_ERROR,
|
|
1285
|
+
'라이선스의 테스트·운영 구분을 확인하지 못했습니다.',
|
|
1286
|
+
],
|
|
1287
|
+
rate_limited: [
|
|
1288
|
+
LicenseErrorCode.RATE_LIMITED,
|
|
1289
|
+
'인증 요청이 너무 많습니다. 잠시 후 다시 시도해 주세요.',
|
|
1290
|
+
],
|
|
1291
|
+
network_error: [
|
|
1292
|
+
LicenseErrorCode.NETWORK_ERROR,
|
|
1293
|
+
'라이선스 서버에 연결하지 못했습니다. 다시 시도해 주세요.',
|
|
1294
|
+
],
|
|
1295
|
+
activation_limit_reached: [
|
|
1296
|
+
LicenseErrorCode.ACTIVATION_LIMIT_REACHED,
|
|
1297
|
+
'사용 가능한 기기 수를 모두 사용했습니다. 다른 기기에서 해제해 주세요.',
|
|
1298
|
+
],
|
|
1299
|
+
activation_required: [LicenseErrorCode.INVALID_LICENSE, '라이선스를 다시 인증해 주세요.'],
|
|
1300
|
+
invalid_instance: [
|
|
1301
|
+
LicenseErrorCode.INVALID_LICENSE,
|
|
1302
|
+
'이 기기의 인증 정보가 만료되었습니다. 다시 인증해 주세요.',
|
|
1303
|
+
],
|
|
1304
|
+
};
|
|
1305
|
+
function normalizeWordPressLicenseResponse(raw, productId) {
|
|
1306
|
+
const data = raw && typeof raw === 'object' ? raw : {};
|
|
1307
|
+
const status = typeof data.status === 'string' ? data.status : 'invalid';
|
|
1308
|
+
if (data.valid !== true || status !== 'active') {
|
|
1309
|
+
const [code, message] = errors[status] || [
|
|
1310
|
+
LicenseErrorCode.INVALID_LICENSE,
|
|
1311
|
+
'유효하지 않은 라이선스 키입니다.',
|
|
1312
|
+
];
|
|
1313
|
+
throw new LicenseCoreError(code, message, undefined, code === LicenseErrorCode.NETWORK_ERROR || code === LicenseErrorCode.RATE_LIMITED);
|
|
1314
|
+
}
|
|
1315
|
+
if (data.product_id !== productId) {
|
|
1316
|
+
throw new LicenseCoreError(LicenseErrorCode.PRODUCT_MISMATCH, '인증 서버의 상품 정보가 일치하지 않습니다.');
|
|
1317
|
+
}
|
|
1318
|
+
const source = typeof data.source === 'string' ? data.source : '';
|
|
1319
|
+
if (data.provider === 'lemon_api' &&
|
|
1320
|
+
(typeof data.instance_id !== 'string' || !data.instance_id)) {
|
|
1321
|
+
throw new LicenseCoreError(LicenseErrorCode.INVALID_LICENSE, '기기 활성화 정보를 확인하지 못했습니다.');
|
|
1322
|
+
}
|
|
1323
|
+
return {
|
|
1324
|
+
valid: true,
|
|
1325
|
+
productId,
|
|
1326
|
+
planTier: 'PAID',
|
|
1327
|
+
licenseStatus: 'active',
|
|
1328
|
+
instanceId: typeof data.instance_id === 'string' ? data.instance_id : undefined,
|
|
1329
|
+
activationLimit: typeof data.activation_limit === 'number' ? data.activation_limit : 0,
|
|
1330
|
+
activationUsage: typeof data.activation_usage === 'number' ? data.activation_usage : 0,
|
|
1331
|
+
licenseSource: source,
|
|
1332
|
+
accessScope: typeof data.access_scope === 'string' ? data.access_scope : 'product',
|
|
1333
|
+
expiresAt: typeof data.expires_at === 'string' ? data.expires_at : undefined,
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
function createWordPressLicenseTransport(options) {
|
|
1337
|
+
const { storage, storagePrefix: prefix, productId } = options;
|
|
1338
|
+
if (!productId.trim() || !prefix.trim())
|
|
1339
|
+
throw new Error('productId and storagePrefix are required.');
|
|
1340
|
+
if (!/^https:\/\//i.test(options.baseUrl) &&
|
|
1341
|
+
!/^http:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?(\/|$)/i.test(options.baseUrl)) {
|
|
1342
|
+
throw new Error('License server must use HTTPS (HTTP is allowed only for local development).');
|
|
1343
|
+
}
|
|
1344
|
+
const timeout = options.timeoutMs || 20000;
|
|
1345
|
+
const fetchFn = options.fetchFn || fetch;
|
|
1346
|
+
const body = (payload) => ({
|
|
1347
|
+
license_key: payload.licenseKey,
|
|
1348
|
+
product_id: productId,
|
|
1349
|
+
instance_id: payload.instanceId || '',
|
|
1350
|
+
device_id: payload.deviceId,
|
|
1351
|
+
});
|
|
1352
|
+
const base = options.baseUrl.replace(/\/$/, '');
|
|
1353
|
+
const http = new FetchLicenseTransport({
|
|
1354
|
+
verifyUrl: `${base}/activate`,
|
|
1355
|
+
validateUrl: `${base}/validate`,
|
|
1356
|
+
deactivateUrl: `${base}/deactivate`,
|
|
1357
|
+
fetchFn,
|
|
1358
|
+
useAbortSignal: false,
|
|
1359
|
+
timeoutMs: timeout,
|
|
1360
|
+
buildVerifyBody: body,
|
|
1361
|
+
buildValidateBody: body,
|
|
1362
|
+
buildDeactivateBody: body,
|
|
1363
|
+
normalizeVerifyResponse: (raw) => normalizeWordPressLicenseResponse(raw, productId),
|
|
1364
|
+
normalizeDeactivateResponse: (raw) => {
|
|
1365
|
+
const data = raw && typeof raw === 'object' ? raw : {};
|
|
1366
|
+
const success = data.success === true || data.deactivated === true;
|
|
1367
|
+
return {
|
|
1368
|
+
success,
|
|
1369
|
+
message: success
|
|
1370
|
+
? undefined
|
|
1371
|
+
: '라이선스 연결을 해제하지 못했습니다. 다시 시도해 주세요.',
|
|
1372
|
+
};
|
|
1373
|
+
},
|
|
1374
|
+
});
|
|
1375
|
+
const enrich = async (payload) => ({
|
|
1376
|
+
...payload,
|
|
1377
|
+
deviceId: await getPersistentLicenseDeviceId(storage, prefix),
|
|
1378
|
+
});
|
|
1379
|
+
return {
|
|
1380
|
+
verify: async (payload) => {
|
|
1381
|
+
const storedKey = await storage.get(`${prefix}:licenseKey`);
|
|
1382
|
+
const storedInstance = await storage.get(`${prefix}:instanceId`);
|
|
1383
|
+
return http.verify(await enrich({
|
|
1384
|
+
...payload,
|
|
1385
|
+
instanceId: payload.instanceId ||
|
|
1386
|
+
(storedKey === payload.licenseKey && typeof storedInstance === 'string'
|
|
1387
|
+
? storedInstance
|
|
1388
|
+
: ''),
|
|
1389
|
+
}));
|
|
1390
|
+
},
|
|
1391
|
+
validate: async (payload) => {
|
|
1392
|
+
const enriched = await enrich(payload);
|
|
1393
|
+
// Legacy users may not have a Lemon instance yet. Activate once;
|
|
1394
|
+
// the server reuses its device mapping when a response was lost.
|
|
1395
|
+
return payload.instanceId ? http.validate(enriched) : http.verify(enriched);
|
|
1396
|
+
},
|
|
1397
|
+
deactivate: async (payload) => http.deactivate(await enrich(payload)),
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1195
1401
|
class FigmaStorageAdapter {
|
|
1196
1402
|
constructor(figma) {
|
|
1197
1403
|
this.figma = figma;
|
|
@@ -1236,6 +1442,7 @@ const DEFAULT_LICENSE_MESSAGE_TYPES = {
|
|
|
1236
1442
|
class FigmaLicenseStatusManager {
|
|
1237
1443
|
constructor(options) {
|
|
1238
1444
|
this.options = options;
|
|
1445
|
+
this.messageQueue = Promise.resolve();
|
|
1239
1446
|
this.messageTypes = {
|
|
1240
1447
|
...DEFAULT_LICENSE_MESSAGE_TYPES,
|
|
1241
1448
|
...(options.messageTypes || {}),
|
|
@@ -1245,6 +1452,7 @@ class FigmaLicenseStatusManager {
|
|
|
1245
1452
|
async initialize() {
|
|
1246
1453
|
if (!this.initializationPromise) {
|
|
1247
1454
|
this.initializationPromise = (async () => {
|
|
1455
|
+
await this.options.beforeInitialize?.();
|
|
1248
1456
|
const state = await this.options.core.initialize();
|
|
1249
1457
|
await this.postStatusUpdate(state);
|
|
1250
1458
|
})().catch((error) => {
|
|
@@ -1254,7 +1462,12 @@ class FigmaLicenseStatusManager {
|
|
|
1254
1462
|
}
|
|
1255
1463
|
await this.initializationPromise;
|
|
1256
1464
|
}
|
|
1257
|
-
|
|
1465
|
+
handleMessage(pluginMessage) {
|
|
1466
|
+
const operation = this.messageQueue.then(() => this.handleMessageNow(pluginMessage));
|
|
1467
|
+
this.messageQueue = operation.then(() => undefined, () => undefined);
|
|
1468
|
+
return operation;
|
|
1469
|
+
}
|
|
1470
|
+
async handleMessageNow(pluginMessage) {
|
|
1258
1471
|
if (!pluginMessage || typeof pluginMessage !== 'object') {
|
|
1259
1472
|
return false;
|
|
1260
1473
|
}
|
|
@@ -1268,15 +1481,18 @@ class FigmaLicenseStatusManager {
|
|
|
1268
1481
|
return true;
|
|
1269
1482
|
}
|
|
1270
1483
|
if (type === this.messageTypes.verifyLicense) {
|
|
1484
|
+
await this.initialize();
|
|
1271
1485
|
const licenseKey = typeof message.licenseKey === 'string' ? message.licenseKey : '';
|
|
1272
1486
|
await this.handleVerifyLicense(licenseKey);
|
|
1273
1487
|
return true;
|
|
1274
1488
|
}
|
|
1275
1489
|
if (type === this.messageTypes.deactivateLicense) {
|
|
1490
|
+
await this.initialize();
|
|
1276
1491
|
await this.handleDeactivateLicense();
|
|
1277
1492
|
return true;
|
|
1278
1493
|
}
|
|
1279
1494
|
if (type === this.messageTypes.checkFeatureAccess) {
|
|
1495
|
+
await this.initialize();
|
|
1280
1496
|
const featureKey = typeof message.featureKey === 'string' ? message.featureKey : undefined;
|
|
1281
1497
|
const amount = typeof message.amount === 'number' && Number.isFinite(message.amount) && message.amount > 0
|
|
1282
1498
|
? message.amount
|
|
@@ -1285,6 +1501,7 @@ class FigmaLicenseStatusManager {
|
|
|
1285
1501
|
return true;
|
|
1286
1502
|
}
|
|
1287
1503
|
if (type === this.messageTypes.consumeUsage) {
|
|
1504
|
+
await this.initialize();
|
|
1288
1505
|
const amount = typeof message.amount === 'number' && Number.isFinite(message.amount) && message.amount > 0
|
|
1289
1506
|
? message.amount
|
|
1290
1507
|
: 1;
|
|
@@ -1308,6 +1525,7 @@ class FigmaLicenseStatusManager {
|
|
|
1308
1525
|
* This is the preferred guard for operations handled entirely in main.
|
|
1309
1526
|
*/
|
|
1310
1527
|
async canUseFeature(featureKey, amount = 1) {
|
|
1528
|
+
await this.initialize();
|
|
1311
1529
|
const state = await this.options.core.getStatus();
|
|
1312
1530
|
const safeAmount = Number.isFinite(amount) && amount > 0 ? amount : 1;
|
|
1313
1531
|
return this.options.core.resolveUsageAccess(state, featureKey, safeAmount);
|
|
@@ -1316,6 +1534,7 @@ class FigmaLicenseStatusManager {
|
|
|
1316
1534
|
* Consume usage from main code and publish the refreshed status to the UI.
|
|
1317
1535
|
*/
|
|
1318
1536
|
async consumeUsage(amount = 1, featureKey) {
|
|
1537
|
+
await this.initialize();
|
|
1319
1538
|
const result = await this.options.core.consumeUsage(amount, featureKey);
|
|
1320
1539
|
await this.postStatusUpdate(result.state);
|
|
1321
1540
|
return result;
|
|
@@ -1452,14 +1671,21 @@ function createFigmaLicenseStatusManager(options) {
|
|
|
1452
1671
|
const figmaApi = resolveFigmaApi(options.figma);
|
|
1453
1672
|
const storage = options.storage ?? new FigmaStorageAdapter(figmaApi);
|
|
1454
1673
|
const notifier = options.notifier ?? new FigmaNotifierAdapter(figmaApi);
|
|
1455
|
-
const
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1674
|
+
const prefix = options.config.storagePrefix?.trim() || `${options.config.instanceName}:${options.config.productId}`;
|
|
1675
|
+
const transport = options.transport ?? (options.wordpress
|
|
1676
|
+
? createWordPressLicenseTransport({
|
|
1677
|
+
baseUrl: options.wordpress.baseUrl, productId: options.config.productId,
|
|
1678
|
+
storagePrefix: prefix, storage, fetchFn: options.fetchFn,
|
|
1679
|
+
timeoutMs: options.config.requestTimeoutMs,
|
|
1680
|
+
})
|
|
1681
|
+
: new FetchLicenseTransport({
|
|
1682
|
+
verifyUrl: options.config.endpoint,
|
|
1683
|
+
validateUrl: options.config.validateEndpoint,
|
|
1684
|
+
deactivateUrl: options.config.deactivateEndpoint,
|
|
1685
|
+
headers: options.config.requestHeaders,
|
|
1686
|
+
timeoutMs: options.config.requestTimeoutMs,
|
|
1687
|
+
fetchFn: options.fetchFn,
|
|
1688
|
+
}));
|
|
1463
1689
|
const coreDependencies = {
|
|
1464
1690
|
storage,
|
|
1465
1691
|
transport,
|
|
@@ -1471,8 +1697,28 @@ function createFigmaLicenseStatusManager(options) {
|
|
|
1471
1697
|
core,
|
|
1472
1698
|
notifier,
|
|
1473
1699
|
messageTypes: options.messageTypes,
|
|
1700
|
+
beforeInitialize: options.migrateLegacyStorage
|
|
1701
|
+
? () => migrateLegacyLicenseStorage(storage, prefix, typeof options.migrateLegacyStorage === 'object' ? options.migrateLegacyStorage : undefined)
|
|
1702
|
+
: undefined,
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
/** Shared membership + Lemon gateway setup. Product allowlists stay on the server. */
|
|
1706
|
+
function createWordPressFigmaLicenseManager(options) {
|
|
1707
|
+
const base = options.serverBaseUrl.replace(/\/$/, '');
|
|
1708
|
+
return createFigmaLicenseStatusManager({
|
|
1709
|
+
figma: options.figma, storage: options.storage, notifier: options.notifier,
|
|
1710
|
+
fetchFn: options.fetchFn, messageTypes: options.messageTypes,
|
|
1711
|
+
migrateLegacyStorage: options.migrateLegacyStorage ?? true,
|
|
1712
|
+
wordpress: { baseUrl: base },
|
|
1713
|
+
config: {
|
|
1714
|
+
productId: options.productId, instanceName: options.instanceName || options.productId,
|
|
1715
|
+
storagePrefix: options.storagePrefix || `designbase:${options.productId}`,
|
|
1716
|
+
endpoint: `${base}/activate`, validateEndpoint: `${base}/validate`, deactivateEndpoint: `${base}/deactivate`,
|
|
1717
|
+
requestTimeoutMs: options.timeoutMs || 20000,
|
|
1718
|
+
policy: options.policy,
|
|
1719
|
+
},
|
|
1474
1720
|
});
|
|
1475
1721
|
}
|
|
1476
1722
|
|
|
1477
|
-
export { DEFAULT_LICENSE_MESSAGE_TYPES, DesignbaseAccountAuthClient, DesignbaseAccountLicenseTransport, FetchLicenseTransport, FigmaLicenseStatusManager, FigmaNotifierAdapter, FigmaStorageAdapter, LicenseCore, LicenseCoreError, LicenseErrorCode, LicenseTransportHttpError, createFigmaLicenseStatusManager, defaultNormalizeDeactivateResponse, defaultNormalizeVerifyResponse, isActivationLimitReached, mapLicenseError };
|
|
1723
|
+
export { DEFAULT_LICENSE_MESSAGE_TYPES, DesignbaseAccountAuthClient, DesignbaseAccountLicenseTransport, FetchLicenseTransport, FigmaLicenseStatusManager, FigmaNotifierAdapter, FigmaStorageAdapter, LicenseCore, LicenseCoreError, LicenseErrorCode, LicenseTransportHttpError, createFigmaLicenseStatusManager, createWordPressFigmaLicenseManager, createWordPressLicenseTransport, defaultNormalizeDeactivateResponse, defaultNormalizeVerifyResponse, getPersistentLicenseDeviceId, isActivationLimitReached, mapLicenseError, migrateLegacyLicenseStorage, normalizeWordPressLicenseResponse };
|
|
1478
1724
|
//# sourceMappingURL=index.esm.js.map
|