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.
@@ -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 url = 'https://facebetter.pixpark.net/facebetter/v1/auth';
411
- const response = await fetch(url, {
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
- console.warn('[JS Engine] Online auth fetch failed with status:', response.status);
420
- return "";
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
- return await response.text();
423
- } catch (error) {
424
- console.error('[JS Engine] Online auth fetch exception:', error);
425
- return "";
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
- console.error('Failed to parse registered filters JSON:', e);
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
- console.error('Failed to parse registered stickers JSON:', e);
1076
+ logError(`Failed to parse registered stickers JSON: ${e}`);
880
1077
  return [];
881
1078
  }
882
1079
  }