@mieweb/ui 0.10.0-dev.2 → 0.10.0-dev.4

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.
@@ -0,0 +1,162 @@
1
+ // src/components/AI/voiceprintStore.ts
2
+ var DB_NAME = "ozwell-voice";
3
+ var STORE = "voiceprints";
4
+ var DB_VERSION = 1;
5
+ function voiceprintStorageKey(key, namespace) {
6
+ if (namespace === void 0) return key;
7
+ const ns = namespace.trim();
8
+ if (ns.length === 0)
9
+ throw new Error("Voiceprint namespace must not be empty");
10
+ return `${key}:${encodeURIComponent(ns)}`;
11
+ }
12
+ function openDb() {
13
+ return new Promise((resolve, reject) => {
14
+ const req = indexedDB.open(DB_NAME, DB_VERSION);
15
+ req.onupgradeneeded = () => {
16
+ if (!req.result.objectStoreNames.contains(STORE))
17
+ req.result.createObjectStore(STORE);
18
+ };
19
+ req.onsuccess = () => resolve(req.result);
20
+ req.onerror = () => reject(req.error);
21
+ req.onblocked = () => reject(
22
+ new Error("IndexedDB open blocked (another tab holds an upgrade lock)")
23
+ );
24
+ });
25
+ }
26
+ function idbGet(key) {
27
+ return openDb().then(
28
+ (db) => new Promise((resolve, reject) => {
29
+ const req = db.transaction(STORE, "readonly").objectStore(STORE).get(key);
30
+ req.onsuccess = () => {
31
+ resolve(req.result);
32
+ db.close();
33
+ };
34
+ req.onerror = () => {
35
+ reject(req.error);
36
+ db.close();
37
+ };
38
+ })
39
+ );
40
+ }
41
+ function idbSet(key, value) {
42
+ return openDb().then(
43
+ (db) => new Promise((resolve, reject) => {
44
+ const tx = db.transaction(STORE, "readwrite");
45
+ tx.objectStore(STORE).put(value, key);
46
+ tx.oncomplete = () => {
47
+ resolve();
48
+ db.close();
49
+ };
50
+ tx.onerror = () => {
51
+ reject(tx.error);
52
+ db.close();
53
+ };
54
+ tx.onabort = () => {
55
+ reject(tx.error);
56
+ db.close();
57
+ };
58
+ })
59
+ );
60
+ }
61
+ function idbDel(key) {
62
+ return openDb().then(
63
+ (db) => new Promise((resolve, reject) => {
64
+ const tx = db.transaction(STORE, "readwrite");
65
+ tx.objectStore(STORE).delete(key);
66
+ tx.oncomplete = () => {
67
+ resolve();
68
+ db.close();
69
+ };
70
+ tx.onerror = () => {
71
+ reject(tx.error);
72
+ db.close();
73
+ };
74
+ tx.onabort = () => {
75
+ reject(tx.error);
76
+ db.close();
77
+ };
78
+ })
79
+ );
80
+ }
81
+ async function getVoiceprints(key, migrateLegacy) {
82
+ try {
83
+ const existing = await idbGet(key);
84
+ if (existing !== void 0) return existing;
85
+ if (migrateLegacy) {
86
+ let raw = null;
87
+ try {
88
+ raw = localStorage.getItem(key);
89
+ } catch {
90
+ }
91
+ if (raw != null) {
92
+ let migrated;
93
+ try {
94
+ migrated = migrateLegacy(raw);
95
+ } catch {
96
+ try {
97
+ localStorage.removeItem(key);
98
+ } catch {
99
+ }
100
+ return void 0;
101
+ }
102
+ try {
103
+ await idbSet(key, migrated);
104
+ } catch {
105
+ }
106
+ try {
107
+ localStorage.removeItem(key);
108
+ } catch {
109
+ }
110
+ return migrated;
111
+ }
112
+ }
113
+ return void 0;
114
+ } catch {
115
+ return void 0;
116
+ }
117
+ }
118
+ async function setVoiceprints(key, value) {
119
+ try {
120
+ await idbSet(key, value);
121
+ } catch {
122
+ }
123
+ }
124
+ async function clearVoiceprints(key) {
125
+ try {
126
+ await idbDel(key);
127
+ } catch {
128
+ }
129
+ try {
130
+ localStorage.removeItem(key);
131
+ } catch {
132
+ }
133
+ }
134
+ var WHAT_KEY = "ozwellWhatPrints";
135
+ function parseLegacyWhat(raw) {
136
+ let o;
137
+ try {
138
+ o = JSON.parse(raw || "{}") || {};
139
+ } catch {
140
+ return {};
141
+ }
142
+ const out = /* @__PURE__ */ Object.create(null);
143
+ for (const k of Object.keys(o))
144
+ out[k] = (o[k] || []).map((a) => Float32Array.from(a));
145
+ return out;
146
+ }
147
+ function loadWhatPrints(namespace) {
148
+ return getVoiceprints(
149
+ voiceprintStorageKey(WHAT_KEY, namespace),
150
+ namespace === void 0 ? parseLegacyWhat : void 0
151
+ ).then((v) => v ?? {});
152
+ }
153
+ function saveWhatPrints(map, namespace) {
154
+ return setVoiceprints(voiceprintStorageKey(WHAT_KEY, namespace), map);
155
+ }
156
+ function clearWhatPrints(namespace) {
157
+ return clearVoiceprints(voiceprintStorageKey(WHAT_KEY, namespace));
158
+ }
159
+
160
+ export { clearVoiceprints, clearWhatPrints, getVoiceprints, loadWhatPrints, saveWhatPrints, setVoiceprints, voiceprintStorageKey };
161
+ //# sourceMappingURL=chunk-74ENCTIK.js.map
162
+ //# sourceMappingURL=chunk-74ENCTIK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/AI/voiceprintStore.ts"],"names":[],"mappings":"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","file":"chunk-74ENCTIK.js","sourcesContent":["/**\n * IndexedDB store for on-device voiceprints (enrollment vectors), replacing localStorage for this data.\n *\n * Why IndexedDB and not localStorage: it's asynchronous (off the main thread), stores `Float32Array`\n * natively via structured clone (no JSON-stringifying embeddings into bloated number arrays), and isn't\n * capped at ~5 MB. Why not OPFS: these are small *structured records* keyed by id, not large files —\n * IndexedDB is the right tool; OPFS is for the big model files.\n *\n * One object store, keyed by a string id; values are any structured-cloneable object, so `Float32Array`s\n * survive the round-trip intact. On first read of a key it migrates a legacy localStorage value (if a\n * parser is supplied) and then clears it.\n */\n\nconst DB_NAME = 'ozwell-voice';\nconst STORE = 'voiceprints';\nconst DB_VERSION = 1;\n\nexport function voiceprintStorageKey(key: string, namespace?: string): string {\n if (namespace === undefined) return key;\n // Trim before encoding so accidental whitespace (\"user-a \" vs \"user-a\") can't silently split a\n // user's voiceprints across distinct stores.\n const ns = namespace.trim();\n if (ns.length === 0)\n throw new Error('Voiceprint namespace must not be empty');\n return `${key}:${encodeURIComponent(ns)}`;\n}\n\nfunction openDb(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, DB_VERSION);\n req.onupgradeneeded = () => {\n if (!req.result.objectStoreNames.contains(STORE))\n req.result.createObjectStore(STORE);\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n // If another tab holds an open connection during a version upgrade, the open stays \"blocked\" and\n // would otherwise hang forever — surface it instead so callers hit their catch/fallback paths.\n req.onblocked = () =>\n reject(\n new Error('IndexedDB open blocked (another tab holds an upgrade lock)')\n );\n });\n}\n\nfunction idbGet<T>(key: string): Promise<T | undefined> {\n return openDb().then(\n (db) =>\n new Promise<T | undefined>((resolve, reject) => {\n const req = db\n .transaction(STORE, 'readonly')\n .objectStore(STORE)\n .get(key);\n req.onsuccess = () => {\n resolve(req.result as T | undefined);\n db.close();\n };\n req.onerror = () => {\n reject(req.error);\n db.close();\n };\n })\n );\n}\n\nfunction idbSet(key: string, value: unknown): Promise<void> {\n return openDb().then(\n (db) =>\n new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE, 'readwrite');\n tx.objectStore(STORE).put(value, key);\n tx.oncomplete = () => {\n resolve();\n db.close();\n };\n tx.onerror = () => {\n reject(tx.error);\n db.close();\n };\n tx.onabort = () => {\n reject(tx.error);\n db.close();\n }; // else an aborted tx leaves the promise pending\n })\n );\n}\n\nfunction idbDel(key: string): Promise<void> {\n return openDb().then(\n (db) =>\n new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE, 'readwrite');\n tx.objectStore(STORE).delete(key);\n tx.oncomplete = () => {\n resolve();\n db.close();\n };\n tx.onerror = () => {\n reject(tx.error);\n db.close();\n };\n tx.onabort = () => {\n reject(tx.error);\n db.close();\n }; // else an aborted tx leaves the promise pending\n })\n );\n}\n\n/**\n * Read a voiceprint record. If IndexedDB has nothing for `key` but localStorage does, migrate it via\n * `migrateLegacy` (parse the old JSON string → the new shape), persist it to IndexedDB, and clear the\n * localStorage copy. Returns `undefined` if neither has it. Never throws (best-effort storage).\n */\nexport async function getVoiceprints<T>(\n key: string,\n migrateLegacy?: (raw: string) => T\n): Promise<T | undefined> {\n try {\n const existing = await idbGet<T>(key);\n if (existing !== undefined) return existing;\n if (migrateLegacy) {\n let raw: string | null = null;\n try {\n raw = localStorage.getItem(key);\n } catch {\n /* ignore */\n }\n if (raw != null) {\n let migrated: T;\n try {\n migrated = migrateLegacy(raw);\n } catch {\n // Corrupt legacy value — drop it so we don't retry the failing migration on every load.\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n return undefined;\n }\n try {\n await idbSet(key, migrated);\n } catch {\n /* persist is best-effort; still return the migrated value */\n }\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n return migrated;\n }\n }\n return undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Persist a voiceprint record (Float32Arrays stored natively). Best-effort — resolves even on failure. */\nexport async function setVoiceprints(\n key: string,\n value: unknown\n): Promise<void> {\n try {\n await idbSet(key, value);\n } catch {\n /* best-effort */\n }\n}\n\n/** Remove a voiceprint record (and any stale localStorage copy). */\nexport async function clearVoiceprints(key: string): Promise<void> {\n try {\n await idbDel(key);\n } catch {\n /* ignore */\n }\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n}\n\n// --- WHAT phrase-prints: per-phrase wake-embedding templates (the WHAT gate). Previously each story kept\n// its own copy of these load/save helpers against localStorage; centralized here on IndexedDB. ---\nconst WHAT_KEY = 'ozwellWhatPrints';\n\n// Legacy localStorage format: JSON `{ phrase: number[][] }`. New IndexedDB format stores Float32Array[]\n// natively, so migration just rehydrates the number arrays once.\nfunction parseLegacyWhat(raw: string): Record<string, Float32Array[]> {\n let o: Record<string, number[][]>;\n try {\n o = (JSON.parse(raw || '{}') || {}) as Record<string, number[][]>;\n } catch {\n return {};\n }\n // Null-prototype output + Object.keys so a legacy `__proto__` key can't pollute Object.prototype.\n const out: Record<string, Float32Array[]> = Object.create(null);\n for (const k of Object.keys(o))\n out[k] = (o[k] || []).map((a) => Float32Array.from(a));\n return out;\n}\n\nexport function loadWhatPrints(\n namespace?: string\n): Promise<Record<string, Float32Array[]>> {\n return getVoiceprints<Record<string, Float32Array[]>>(\n voiceprintStorageKey(WHAT_KEY, namespace),\n namespace === undefined ? parseLegacyWhat : undefined\n ).then((v) => v ?? {});\n}\n\nexport function saveWhatPrints(\n map: Record<string, Float32Array[]>,\n namespace?: string\n): Promise<void> {\n return setVoiceprints(voiceprintStorageKey(WHAT_KEY, namespace), map);\n}\n\nexport function clearWhatPrints(namespace?: string): Promise<void> {\n return clearVoiceprints(voiceprintStorageKey(WHAT_KEY, namespace));\n}\n"]}
@@ -0,0 +1,170 @@
1
+ 'use strict';
2
+
3
+ // src/components/AI/voiceprintStore.ts
4
+ var DB_NAME = "ozwell-voice";
5
+ var STORE = "voiceprints";
6
+ var DB_VERSION = 1;
7
+ function voiceprintStorageKey(key, namespace) {
8
+ if (namespace === void 0) return key;
9
+ const ns = namespace.trim();
10
+ if (ns.length === 0)
11
+ throw new Error("Voiceprint namespace must not be empty");
12
+ return `${key}:${encodeURIComponent(ns)}`;
13
+ }
14
+ function openDb() {
15
+ return new Promise((resolve, reject) => {
16
+ const req = indexedDB.open(DB_NAME, DB_VERSION);
17
+ req.onupgradeneeded = () => {
18
+ if (!req.result.objectStoreNames.contains(STORE))
19
+ req.result.createObjectStore(STORE);
20
+ };
21
+ req.onsuccess = () => resolve(req.result);
22
+ req.onerror = () => reject(req.error);
23
+ req.onblocked = () => reject(
24
+ new Error("IndexedDB open blocked (another tab holds an upgrade lock)")
25
+ );
26
+ });
27
+ }
28
+ function idbGet(key) {
29
+ return openDb().then(
30
+ (db) => new Promise((resolve, reject) => {
31
+ const req = db.transaction(STORE, "readonly").objectStore(STORE).get(key);
32
+ req.onsuccess = () => {
33
+ resolve(req.result);
34
+ db.close();
35
+ };
36
+ req.onerror = () => {
37
+ reject(req.error);
38
+ db.close();
39
+ };
40
+ })
41
+ );
42
+ }
43
+ function idbSet(key, value) {
44
+ return openDb().then(
45
+ (db) => new Promise((resolve, reject) => {
46
+ const tx = db.transaction(STORE, "readwrite");
47
+ tx.objectStore(STORE).put(value, key);
48
+ tx.oncomplete = () => {
49
+ resolve();
50
+ db.close();
51
+ };
52
+ tx.onerror = () => {
53
+ reject(tx.error);
54
+ db.close();
55
+ };
56
+ tx.onabort = () => {
57
+ reject(tx.error);
58
+ db.close();
59
+ };
60
+ })
61
+ );
62
+ }
63
+ function idbDel(key) {
64
+ return openDb().then(
65
+ (db) => new Promise((resolve, reject) => {
66
+ const tx = db.transaction(STORE, "readwrite");
67
+ tx.objectStore(STORE).delete(key);
68
+ tx.oncomplete = () => {
69
+ resolve();
70
+ db.close();
71
+ };
72
+ tx.onerror = () => {
73
+ reject(tx.error);
74
+ db.close();
75
+ };
76
+ tx.onabort = () => {
77
+ reject(tx.error);
78
+ db.close();
79
+ };
80
+ })
81
+ );
82
+ }
83
+ async function getVoiceprints(key, migrateLegacy) {
84
+ try {
85
+ const existing = await idbGet(key);
86
+ if (existing !== void 0) return existing;
87
+ if (migrateLegacy) {
88
+ let raw = null;
89
+ try {
90
+ raw = localStorage.getItem(key);
91
+ } catch {
92
+ }
93
+ if (raw != null) {
94
+ let migrated;
95
+ try {
96
+ migrated = migrateLegacy(raw);
97
+ } catch {
98
+ try {
99
+ localStorage.removeItem(key);
100
+ } catch {
101
+ }
102
+ return void 0;
103
+ }
104
+ try {
105
+ await idbSet(key, migrated);
106
+ } catch {
107
+ }
108
+ try {
109
+ localStorage.removeItem(key);
110
+ } catch {
111
+ }
112
+ return migrated;
113
+ }
114
+ }
115
+ return void 0;
116
+ } catch {
117
+ return void 0;
118
+ }
119
+ }
120
+ async function setVoiceprints(key, value) {
121
+ try {
122
+ await idbSet(key, value);
123
+ } catch {
124
+ }
125
+ }
126
+ async function clearVoiceprints(key) {
127
+ try {
128
+ await idbDel(key);
129
+ } catch {
130
+ }
131
+ try {
132
+ localStorage.removeItem(key);
133
+ } catch {
134
+ }
135
+ }
136
+ var WHAT_KEY = "ozwellWhatPrints";
137
+ function parseLegacyWhat(raw) {
138
+ let o;
139
+ try {
140
+ o = JSON.parse(raw || "{}") || {};
141
+ } catch {
142
+ return {};
143
+ }
144
+ const out = /* @__PURE__ */ Object.create(null);
145
+ for (const k of Object.keys(o))
146
+ out[k] = (o[k] || []).map((a) => Float32Array.from(a));
147
+ return out;
148
+ }
149
+ function loadWhatPrints(namespace) {
150
+ return getVoiceprints(
151
+ voiceprintStorageKey(WHAT_KEY, namespace),
152
+ namespace === void 0 ? parseLegacyWhat : void 0
153
+ ).then((v) => v ?? {});
154
+ }
155
+ function saveWhatPrints(map, namespace) {
156
+ return setVoiceprints(voiceprintStorageKey(WHAT_KEY, namespace), map);
157
+ }
158
+ function clearWhatPrints(namespace) {
159
+ return clearVoiceprints(voiceprintStorageKey(WHAT_KEY, namespace));
160
+ }
161
+
162
+ exports.clearVoiceprints = clearVoiceprints;
163
+ exports.clearWhatPrints = clearWhatPrints;
164
+ exports.getVoiceprints = getVoiceprints;
165
+ exports.loadWhatPrints = loadWhatPrints;
166
+ exports.saveWhatPrints = saveWhatPrints;
167
+ exports.setVoiceprints = setVoiceprints;
168
+ exports.voiceprintStorageKey = voiceprintStorageKey;
169
+ //# sourceMappingURL=chunk-OGNADPM4.cjs.map
170
+ //# sourceMappingURL=chunk-OGNADPM4.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/AI/voiceprintStore.ts"],"names":[],"mappings":"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","file":"chunk-OGNADPM4.cjs","sourcesContent":["/**\n * IndexedDB store for on-device voiceprints (enrollment vectors), replacing localStorage for this data.\n *\n * Why IndexedDB and not localStorage: it's asynchronous (off the main thread), stores `Float32Array`\n * natively via structured clone (no JSON-stringifying embeddings into bloated number arrays), and isn't\n * capped at ~5 MB. Why not OPFS: these are small *structured records* keyed by id, not large files —\n * IndexedDB is the right tool; OPFS is for the big model files.\n *\n * One object store, keyed by a string id; values are any structured-cloneable object, so `Float32Array`s\n * survive the round-trip intact. On first read of a key it migrates a legacy localStorage value (if a\n * parser is supplied) and then clears it.\n */\n\nconst DB_NAME = 'ozwell-voice';\nconst STORE = 'voiceprints';\nconst DB_VERSION = 1;\n\nexport function voiceprintStorageKey(key: string, namespace?: string): string {\n if (namespace === undefined) return key;\n // Trim before encoding so accidental whitespace (\"user-a \" vs \"user-a\") can't silently split a\n // user's voiceprints across distinct stores.\n const ns = namespace.trim();\n if (ns.length === 0)\n throw new Error('Voiceprint namespace must not be empty');\n return `${key}:${encodeURIComponent(ns)}`;\n}\n\nfunction openDb(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, DB_VERSION);\n req.onupgradeneeded = () => {\n if (!req.result.objectStoreNames.contains(STORE))\n req.result.createObjectStore(STORE);\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n // If another tab holds an open connection during a version upgrade, the open stays \"blocked\" and\n // would otherwise hang forever — surface it instead so callers hit their catch/fallback paths.\n req.onblocked = () =>\n reject(\n new Error('IndexedDB open blocked (another tab holds an upgrade lock)')\n );\n });\n}\n\nfunction idbGet<T>(key: string): Promise<T | undefined> {\n return openDb().then(\n (db) =>\n new Promise<T | undefined>((resolve, reject) => {\n const req = db\n .transaction(STORE, 'readonly')\n .objectStore(STORE)\n .get(key);\n req.onsuccess = () => {\n resolve(req.result as T | undefined);\n db.close();\n };\n req.onerror = () => {\n reject(req.error);\n db.close();\n };\n })\n );\n}\n\nfunction idbSet(key: string, value: unknown): Promise<void> {\n return openDb().then(\n (db) =>\n new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE, 'readwrite');\n tx.objectStore(STORE).put(value, key);\n tx.oncomplete = () => {\n resolve();\n db.close();\n };\n tx.onerror = () => {\n reject(tx.error);\n db.close();\n };\n tx.onabort = () => {\n reject(tx.error);\n db.close();\n }; // else an aborted tx leaves the promise pending\n })\n );\n}\n\nfunction idbDel(key: string): Promise<void> {\n return openDb().then(\n (db) =>\n new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE, 'readwrite');\n tx.objectStore(STORE).delete(key);\n tx.oncomplete = () => {\n resolve();\n db.close();\n };\n tx.onerror = () => {\n reject(tx.error);\n db.close();\n };\n tx.onabort = () => {\n reject(tx.error);\n db.close();\n }; // else an aborted tx leaves the promise pending\n })\n );\n}\n\n/**\n * Read a voiceprint record. If IndexedDB has nothing for `key` but localStorage does, migrate it via\n * `migrateLegacy` (parse the old JSON string → the new shape), persist it to IndexedDB, and clear the\n * localStorage copy. Returns `undefined` if neither has it. Never throws (best-effort storage).\n */\nexport async function getVoiceprints<T>(\n key: string,\n migrateLegacy?: (raw: string) => T\n): Promise<T | undefined> {\n try {\n const existing = await idbGet<T>(key);\n if (existing !== undefined) return existing;\n if (migrateLegacy) {\n let raw: string | null = null;\n try {\n raw = localStorage.getItem(key);\n } catch {\n /* ignore */\n }\n if (raw != null) {\n let migrated: T;\n try {\n migrated = migrateLegacy(raw);\n } catch {\n // Corrupt legacy value — drop it so we don't retry the failing migration on every load.\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n return undefined;\n }\n try {\n await idbSet(key, migrated);\n } catch {\n /* persist is best-effort; still return the migrated value */\n }\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n return migrated;\n }\n }\n return undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Persist a voiceprint record (Float32Arrays stored natively). Best-effort — resolves even on failure. */\nexport async function setVoiceprints(\n key: string,\n value: unknown\n): Promise<void> {\n try {\n await idbSet(key, value);\n } catch {\n /* best-effort */\n }\n}\n\n/** Remove a voiceprint record (and any stale localStorage copy). */\nexport async function clearVoiceprints(key: string): Promise<void> {\n try {\n await idbDel(key);\n } catch {\n /* ignore */\n }\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n}\n\n// --- WHAT phrase-prints: per-phrase wake-embedding templates (the WHAT gate). Previously each story kept\n// its own copy of these load/save helpers against localStorage; centralized here on IndexedDB. ---\nconst WHAT_KEY = 'ozwellWhatPrints';\n\n// Legacy localStorage format: JSON `{ phrase: number[][] }`. New IndexedDB format stores Float32Array[]\n// natively, so migration just rehydrates the number arrays once.\nfunction parseLegacyWhat(raw: string): Record<string, Float32Array[]> {\n let o: Record<string, number[][]>;\n try {\n o = (JSON.parse(raw || '{}') || {}) as Record<string, number[][]>;\n } catch {\n return {};\n }\n // Null-prototype output + Object.keys so a legacy `__proto__` key can't pollute Object.prototype.\n const out: Record<string, Float32Array[]> = Object.create(null);\n for (const k of Object.keys(o))\n out[k] = (o[k] || []).map((a) => Float32Array.from(a));\n return out;\n}\n\nexport function loadWhatPrints(\n namespace?: string\n): Promise<Record<string, Float32Array[]>> {\n return getVoiceprints<Record<string, Float32Array[]>>(\n voiceprintStorageKey(WHAT_KEY, namespace),\n namespace === undefined ? parseLegacyWhat : undefined\n ).then((v) => v ?? {});\n}\n\nexport function saveWhatPrints(\n map: Record<string, Float32Array[]>,\n namespace?: string\n): Promise<void> {\n return setVoiceprints(voiceprintStorageKey(WHAT_KEY, namespace), map);\n}\n\nexport function clearWhatPrints(namespace?: string): Promise<void> {\n return clearVoiceprints(voiceprintStorageKey(WHAT_KEY, namespace));\n}\n"]}