summaryrefslogtreecommitdiff
path: root/src/background/settings-service.js
blob: 55d2a8f6859eee64502d3312b2851a7fe488d8d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
 * Settings service (background): storage access with validation.
 *
 * Load flow (Design.md §17.2):
 *   1. generate default settings
 *   2. read storage
 *   3. merge
 *   4. validate
 *   5. invalid values fall back to defaults
 *
 * The validated cache is refreshed on storage.onChanged.
 */

import { sanitizeSettings, defaultSettings } from '../shared/settings-schema.js';

const STORAGE_KEY = 'settings';

/** @type {object|null} cached validated settings */
let cache = null;

/**
 * @param {object} [browserApi] injectable browser API (defaults to globalThis.browser)
 * @returns {object}
 */
function api(browserApi) {
  return browserApi || globalThis.browser;
}

/**
 * Load settings from storage.local, sanitize and cache them.
 * @param {object} [browserApi]
 * @returns {Promise<object>} validated settings
 */
export async function loadSettings(browserApi) {
  const b = api(browserApi);
  let stored = null;
  try {
    const result = await b.storage.local.get({ settings: null });
    stored = result.settings;
  } catch {
    stored = null;
  }
  cache = sanitizeSettings(stored);
  return cache;
}

/**
 * Save settings. Values are sanitized before writing.
 * @param {object} settings raw settings object from the UI
 * @param {object} [browserApi]
 * @returns {Promise<object>} the validated settings actually stored
 */
export async function saveSettings(settings, browserApi) {
  const b = api(browserApi);
  const validated = sanitizeSettings(settings);
  await b.storage.local.set({ settings: validated });
  cache = validated;
  return validated;
}

/**
 * @param {object} [browserApi]
 * @returns {Promise<object>} current validated settings (cached when possible)
 */
export async function getSettings(browserApi) {
  if (cache) return cache;
  return loadSettings(browserApi);
}

/**
 * Register the storage.onChanged listener that keeps the cache in sync.
 * Must be called once from the background entry point.
 * @param {object} [browserApi]
 */
export function watchSettings(browserApi) {
  const b = api(browserApi);
  b.storage.onChanged.addListener((changes, areaName) => {
    if (areaName === 'local' && changes.settings) {
      cache = sanitizeSettings(changes.settings.newValue);
    }
  });
}

/**
 * Test-only: clear the module-level settings cache so each test starts from
 * storage state instead of inheriting the previous test's cache.
 * @visibleForTesting
 */
export function __resetCacheForTests() {
  cache = null;
}