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