facebetter 1.4.1 → 1.4.2
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/facebetter.esm.js +210 -13
- package/dist/facebetter.esm.js.map +1 -1
- package/dist/facebetter.js +210 -13
- package/dist/facebetter.js.map +1 -1
- package/package.json +2 -2
package/dist/facebetter.esm.js
CHANGED
|
@@ -212,6 +212,158 @@ var constants = /*#__PURE__*/Object.freeze({
|
|
|
212
212
|
VirtualBackgroundOptions: VirtualBackgroundOptions$1
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Browser localStorage cache for online license JSON (WASM path).
|
|
217
|
+
* Aligns with native: full server response body, TTL from response.timestamp (seconds).
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
/** @type {number} Same semantics as C++ kOnlineLicenseCacheTtlSecs */
|
|
221
|
+
const ONLINE_LICENSE_CACHE_TTL_SEC = 7 * 24 * 3600;
|
|
222
|
+
|
|
223
|
+
const STORAGE_PREFIX = 'facebetter.onlineLicense.v1:';
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* @returns {boolean}
|
|
227
|
+
*/
|
|
228
|
+
function isLocalStorageAvailable() {
|
|
229
|
+
try {
|
|
230
|
+
if (typeof globalThis.localStorage === 'undefined') {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
const k = '__fb_ls_test__';
|
|
234
|
+
globalThis.localStorage.setItem(k, '1');
|
|
235
|
+
globalThis.localStorage.removeItem(k);
|
|
236
|
+
return true;
|
|
237
|
+
} catch {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @param {string} appId
|
|
244
|
+
* @returns {string}
|
|
245
|
+
*/
|
|
246
|
+
function storageKey(appId) {
|
|
247
|
+
return `${STORAGE_PREFIX}${appId}`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* @param {string} responseText
|
|
252
|
+
* @param {string} expectedAppId
|
|
253
|
+
* @returns {{ ok: boolean, ts: number } | null}
|
|
254
|
+
*/
|
|
255
|
+
function parseAndValidateResponse(responseText, expectedAppId) {
|
|
256
|
+
try {
|
|
257
|
+
const o = JSON.parse(responseText);
|
|
258
|
+
if (!o || typeof o !== 'object') {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
if (o.success !== true) {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
if (typeof o.app_id !== 'string' || o.app_id !== expectedAppId) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
if (typeof o.timestamp !== 'number' || !Number.isFinite(o.timestamp)) {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
return { ok: true, ts: o.timestamp };
|
|
271
|
+
} catch {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Returns cached raw response body if still within TTL, else null.
|
|
278
|
+
* @param {string} appId
|
|
279
|
+
* @returns {string | null}
|
|
280
|
+
*/
|
|
281
|
+
function readOnlineLicenseCache(appId) {
|
|
282
|
+
if (!appId || !isLocalStorageAvailable()) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
let raw;
|
|
286
|
+
try {
|
|
287
|
+
raw = globalThis.localStorage.getItem(storageKey(appId));
|
|
288
|
+
} catch {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
if (!raw || typeof raw !== 'string') {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const meta = parseAndValidateResponse(raw, appId);
|
|
295
|
+
if (!meta) {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
299
|
+
const ts = meta.ts;
|
|
300
|
+
const ageSec = ts <= nowSec ? nowSec - ts : 0;
|
|
301
|
+
if (ageSec > ONLINE_LICENSE_CACHE_TTL_SEC) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
return raw;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Persists successful auth response; no-op if storage unavailable or payload invalid.
|
|
309
|
+
* @param {string} appId
|
|
310
|
+
* @param {string} responseText
|
|
311
|
+
* @returns {boolean}
|
|
312
|
+
*/
|
|
313
|
+
function writeOnlineLicenseCache(appId, responseText) {
|
|
314
|
+
if (!appId || !isLocalStorageAvailable() || typeof responseText !== 'string') {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
if (!parseAndValidateResponse(responseText, appId)) {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
globalThis.localStorage.setItem(storageKey(appId), responseText);
|
|
322
|
+
return true;
|
|
323
|
+
} catch {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Browser-side console lines aligned with C++ Logger (src/base/logging.cc) Release pattern:
|
|
330
|
+
* [%Y-%m-%d %H:%M:%S.%e] [facebetter] [%l] [%t] - %v
|
|
331
|
+
* spdlog level names: info, warning, error (see spdlog SPDLOG_LEVEL_NAMES).
|
|
332
|
+
*/
|
|
333
|
+
|
|
334
|
+
function pad2(n) {
|
|
335
|
+
return String(n).padStart(2, '0');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* @param {'info'|'warning'|'error'} levelTag — must match spdlog long level strings
|
|
340
|
+
* @param {string} message
|
|
341
|
+
* @returns {string}
|
|
342
|
+
*/
|
|
343
|
+
function formatLogLine(levelTag, message) {
|
|
344
|
+
const d = new Date();
|
|
345
|
+
const ms = String(d.getMilliseconds()).padStart(3, '0');
|
|
346
|
+
const ts = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(
|
|
347
|
+
d.getHours()
|
|
348
|
+
)}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}.${ms}`;
|
|
349
|
+
return `[${ts}] [facebetter] [${levelTag}] [0] - ${message}`;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** @param {string} message */
|
|
353
|
+
function logInfo(message) {
|
|
354
|
+
console.info(formatLogLine('info', message));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** @param {string} message */
|
|
358
|
+
function logWarn(message) {
|
|
359
|
+
console.warn(formatLogLine('warning', message));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** @param {string} message */
|
|
363
|
+
function logError(message) {
|
|
364
|
+
console.error(formatLogLine('error', message));
|
|
365
|
+
}
|
|
366
|
+
|
|
215
367
|
/**
|
|
216
368
|
* Beauty Effect Engine Core
|
|
217
369
|
* Platform-agnostic engine implementation with dependency injection
|
|
@@ -403,26 +555,71 @@ class BeautyEffectEngine {
|
|
|
403
555
|
};
|
|
404
556
|
}
|
|
405
557
|
|
|
406
|
-
// Setup online auth proxy for WASM
|
|
558
|
+
// Setup online auth proxy for WASM (localStorage cache + fetch, same TTL as native).
|
|
559
|
+
// 返回 { response, fromCache } 供 WASM 桥区分:仅 fromCache===true 时 C++ 跳过 5 分钟防重放。
|
|
407
560
|
if (!Module.onOnlineAuth) {
|
|
408
561
|
Module.onOnlineAuth = async (payloadJson) => {
|
|
562
|
+
let appId = '';
|
|
409
563
|
try {
|
|
410
|
-
const
|
|
411
|
-
|
|
564
|
+
const p = JSON.parse(payloadJson);
|
|
565
|
+
if (p && typeof p.app_id === 'string') {
|
|
566
|
+
appId = p.app_id;
|
|
567
|
+
}
|
|
568
|
+
} catch {
|
|
569
|
+
// ignore malformed payload
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const authResult = (body, fromCache) =>
|
|
573
|
+
body ? { response: body, fromCache } : '';
|
|
574
|
+
|
|
575
|
+
const cached = appId ? readOnlineLicenseCache(appId) : null;
|
|
576
|
+
if (cached) {
|
|
577
|
+
// 与 C++ 中「disk cache」对应;WASM 侧日志仍显示 online,以这里为准区分是否发 HTTP
|
|
578
|
+
logInfo(
|
|
579
|
+
'Online license: localStorage cache hit (no auth HTTP)'
|
|
580
|
+
);
|
|
581
|
+
return authResult(cached, true);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const authUrl = 'https://facebetter.pixpark.net/facebetter/v1/auth';
|
|
585
|
+
logInfo('Online license: requesting auth server');
|
|
586
|
+
try {
|
|
587
|
+
const response = await fetch(authUrl, {
|
|
412
588
|
method: 'POST',
|
|
413
589
|
headers: {
|
|
414
|
-
'Content-Type': 'application/json'
|
|
590
|
+
'Content-Type': 'application/json',
|
|
415
591
|
},
|
|
416
|
-
body: payloadJson
|
|
592
|
+
body: payloadJson,
|
|
417
593
|
});
|
|
418
594
|
if (!response.ok) {
|
|
419
|
-
|
|
420
|
-
|
|
595
|
+
const fallback = appId ? readOnlineLicenseCache(appId) : null;
|
|
596
|
+
if (fallback) {
|
|
597
|
+
logInfo(
|
|
598
|
+
`Online license: using cache after HTTP error (status ${response.status})`
|
|
599
|
+
);
|
|
600
|
+
} else {
|
|
601
|
+
logWarn(
|
|
602
|
+
`Online license: HTTP ${response.status}, no cache`
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
return authResult(fallback, true);
|
|
421
606
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
607
|
+
const text = await response.text();
|
|
608
|
+
if (appId && text) {
|
|
609
|
+
writeOnlineLicenseCache(appId, text);
|
|
610
|
+
}
|
|
611
|
+
logInfo(
|
|
612
|
+
'Online license: server response ok, cache updated'
|
|
613
|
+
);
|
|
614
|
+
return authResult(text, false);
|
|
615
|
+
} catch {
|
|
616
|
+
const fallback = appId ? readOnlineLicenseCache(appId) : null;
|
|
617
|
+
if (fallback) {
|
|
618
|
+
logInfo(
|
|
619
|
+
'Online license: using cache after network error'
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
return authResult(fallback, true);
|
|
426
623
|
}
|
|
427
624
|
};
|
|
428
625
|
}
|
|
@@ -855,7 +1052,7 @@ class BeautyEffectEngine {
|
|
|
855
1052
|
try {
|
|
856
1053
|
return JSON.parse(jsonStr || '[]');
|
|
857
1054
|
} catch (e) {
|
|
858
|
-
|
|
1055
|
+
logError(`Failed to parse registered filters JSON: ${e}`);
|
|
859
1056
|
return [];
|
|
860
1057
|
}
|
|
861
1058
|
}
|
|
@@ -876,7 +1073,7 @@ class BeautyEffectEngine {
|
|
|
876
1073
|
try {
|
|
877
1074
|
return JSON.parse(jsonStr || '[]');
|
|
878
1075
|
} catch (e) {
|
|
879
|
-
|
|
1076
|
+
logError(`Failed to parse registered stickers JSON: ${e}`);
|
|
880
1077
|
return [];
|
|
881
1078
|
}
|
|
882
1079
|
}
|