A music player that connects to your cloud/distributed storage.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: sync issues

+183 -150
+17
src/common/compare.js
··· 1 + import deepDiff from "@fry69/deep-diff"; 2 + 3 + /** 4 + * @param {any} a 5 + * @param {any} b 6 + */ 7 + export function diff(a, b) { 8 + return !deepDiff(a, b); 9 + } 10 + 11 + /** 12 + * @param {any} a 13 + * @param {any} b 14 + */ 15 + export function strictEquality(a, b) { 16 + return a === b; 17 + }
+14 -4
src/common/element.js
··· 64 64 } 65 65 66 66 /** */ 67 + get identifier() { 68 + const ns = this.namespace; 69 + return `${this.constructor.prototype.constructor.NAME}/${this.group}${ 70 + ns?.length ? "/" + ns : "" 71 + }`; 72 + } 73 + 74 + /** */ 67 75 get label() { 68 76 return this.getAttribute("label") ?? this.id ?? this.localName; 69 77 } 70 78 71 79 /** */ 72 - get nameWithGroup() { 73 - return `${this.constructor.prototype.constructor.NAME}/${this.group}`; 80 + get namespace() { 81 + return this.getAttribute("namespace") 82 + ? this.getAttribute("namespace") 83 + : undefined; 74 84 } 75 85 76 86 /** */ ··· 151 161 ); 152 162 153 163 // Setup worker 154 - const name = this.nameWithGroup; 164 + const name = this.identifier; 155 165 const url = import.meta.resolve("./" + WORKER_URL) + `?${query}`; 156 166 157 167 let worker; ··· 169 179 dependencies() { 170 180 return Object.fromEntries( 171 181 Array.from(this.children).flatMap((element) => { 172 - if ("nameWithGroup" in element === false) { 182 + if ("identifier" in element === false) { 173 183 return []; 174 184 } 175 185
+3 -3
src/components/configurator/output/element.js
··· 1 1 import { BroadcastableDiffuseElement } from "@common/element.js"; 2 - import { batch, computed, signal, trigger } from "@common/signal.js"; 2 + import { batch, computed, signal } from "@common/signal.js"; 3 3 4 4 /** 5 5 * @import {DiffuseElement} from "@common/element.js" ··· 273 273 async connectedCallback() { 274 274 // Broadcast if needed 275 275 if (this.hasAttribute("group")) { 276 - const actions = this.broadcast(this.nameWithGroup, { 276 + const actions = this.broadcast(this.identifier, { 277 277 selectOutput: { 278 278 strategy: "replicate", 279 279 fn: this.#selectOutput, ··· 332 332 await customElements.whenDefined(el.localName); 333 333 334 334 if ( 335 - "nameWithGroup" in el === false || 335 + "identifier" in el === false || 336 336 "tracks" in el === false 337 337 ) { 338 338 return null;
+4 -4
src/components/engine/audio/element.js
··· 51 51 // Setup broadcasting if part of group 52 52 if (this.hasAttribute("group")) { 53 53 const actions = this.broadcast( 54 - this.nameWithGroup, 54 + this.identifier, 55 55 { 56 56 adjustVolume: { strategy: "replicate", fn: this.adjustVolume }, 57 57 pause: { strategy: "leaderOnly", fn: this.pause }, ··· 415 415 // Setup broadcasting if part of group 416 416 if (this.hasAttribute("group")) { 417 417 const actions = this.broadcast( 418 - this.nameWithGroup, 418 + this.identifier, 419 419 { 420 420 getDuration: { strategy: "leaderOnly", fn: this.$state.duration.get }, 421 421 getHasEnded: { strategy: "leaderOnly", fn: this.$state.hasEnded.get }, ··· 487 487 get state() { 488 488 return { 489 489 id: this.id, 490 - mimeType: (this.getAttribute("mime-type") ?? undefined), 491 - url: (this.getAttribute("url") ?? ""), 490 + mimeType: this.getAttribute("mime-type") ?? undefined, 491 + url: this.getAttribute("url") ?? "", 492 492 493 493 duration: this.$state.duration.get, 494 494 hasEnded: this.$state.hasEnded.get,
+2 -2
src/components/engine/repeat-shuffle/element.js
··· 24 24 connectedCallback() { 25 25 // Broadcast if needed 26 26 if (this.hasAttribute("group")) { 27 - const actions = this.broadcast(this.nameWithGroup, { 27 + const actions = this.broadcast(this.identifier, { 28 28 setRepeat: { strategy: "replicate", fn: this.setRepeat }, 29 29 setShuffle: { strategy: "replicate", fn: this.setShuffle }, 30 30 }); ··· 40 40 41 41 // Signals 42 42 const storagePrefix = 43 - `${this.constructor.prototype.constructor.NAME}/${this.group}/`; 43 + `${this.constructor.prototype.constructor.NAME}/${this.group}`; 44 44 45 45 this.#repeat.value = 46 46 localStorage.getItem(`${storagePrefix}/repeat`) === "true" ? true : false;
+1 -1
src/components/engine/scope/element.js
··· 24 24 connectedCallback() { 25 25 // Broadcast if needed 26 26 if (this.hasAttribute("group")) { 27 - const actions = this.broadcast(this.nameWithGroup, { 27 + const actions = this.broadcast(this.identifier, { 28 28 setPlaylist: { strategy: "replicate", fn: this.setPlaylist }, 29 29 setSearchTerm: { strategy: "replicate", fn: this.setSearchTerm }, 30 30 });
+1 -1
src/components/orchestrator/auto-queue/element.js
··· 26 26 async connectedCallback() { 27 27 // Broadcast if needed 28 28 if (this.hasAttribute("group")) { 29 - this.broadcast(this.nameWithGroup, {}); 29 + this.broadcast(this.identifier, {}); 30 30 } 31 31 32 32 // Super
+1 -1
src/components/orchestrator/favourites/element.js
··· 57 57 async connectedCallback() { 58 58 // Broadcast if needed 59 59 if (this.hasAttribute("group")) { 60 - const actions = this.broadcast(this.nameWithGroup, { 60 + const actions = this.broadcast(this.identifier, { 61 61 include: { strategy: "leaderOnly", fn: this.include }, 62 62 expel: { strategy: "leaderOnly", fn: this.expel }, 63 63 toggle: { strategy: "leaderOnly", fn: this.toggle },
+1 -1
src/components/orchestrator/media-session/element.js
··· 30 30 async connectedCallback() { 31 31 // Broadcast if needed 32 32 if (this.hasAttribute("group")) { 33 - this.broadcast(this.nameWithGroup, {}); 33 + this.broadcast(this.identifier, {}); 34 34 } 35 35 36 36 // Super
+1
src/components/orchestrator/output/element.js
··· 195 195 id="do-output__dc-output__s3" 196 196 namespace="s3" 197 197 output-selector="#do-output__dob-s3" 198 + group="${ifDefined(group)}" 198 199 label="S3" 199 200 ></dtob-dasl-sync> 200 201 </dc-output>
+1 -1
src/components/orchestrator/process-tracks/element.js
··· 54 54 async connectedCallback() { 55 55 // Broadcast if needed 56 56 if (this.hasAttribute("group")) { 57 - const actions = this.broadcast(this.nameWithGroup, { 57 + const actions = this.broadcast(this.identifier, { 58 58 getPerfInit: { 59 59 strategy: "leaderOnly", 60 60 fn: this.#performedInitialProcess.get,
+1 -1
src/components/orchestrator/queue-audio/element.js
··· 29 29 async connectedCallback() { 30 30 // Broadcast if needed 31 31 if (this.hasAttribute("group")) { 32 - this.broadcast(this.nameWithGroup, {}); 32 + this.broadcast(this.identifier, {}); 33 33 } 34 34 35 35 // Super
+1 -1
src/components/orchestrator/scoped-tracks/element.js
··· 57 57 async connectedCallback() { 58 58 // Broadcast if needed 59 59 if (this.hasAttribute("group")) { 60 - const actions = this.broadcast(this.nameWithGroup, { 60 + const actions = this.broadcast(this.identifier, { 61 61 getTracksAvailable: { 62 62 strategy: "leaderOnly", 63 63 fn: this.#tracksAvailable.get,
-6
src/components/orchestrator/sources/element.js
··· 28 28 * @override 29 29 */ 30 30 async connectedCallback() { 31 - // Broadcast if needed 32 - if (this.hasAttribute("group")) { 33 - this.broadcast(this.nameWithGroup, {}); 34 - } 35 - 36 - // Super 37 31 super.connectedCallback(); 38 32 39 33 /** @type {InputElement} */
+3 -4
src/components/output/bytes/s3/element.js
··· 142 142 143 143 /** @param {string} name; @param {any} data */ 144 144 #put = async (name, data) => { 145 + console.log("PUT", name); 145 146 const bucket = await this.getBucket(); 146 147 if (!bucket) return undefined; 147 148 return this.proxy.put({ bucket, data, name: this.#cat(name) }); ··· 151 152 152 153 /** @param {string} name */ 153 154 #cat(name) { 154 - const namespace = this.hasAttribute("namespace") 155 - ? this.getAttribute("namespace") + "/" 156 - : ""; 157 - return `${namespace}${name}`; 155 + const ns = this.namespace; 156 + return `${ns?.length ? ns + "/" : ""}${name}`; 158 157 } 159 158 } 160 159
+22 -10
src/components/output/common.js
··· 1 1 import { BroadcastableDiffuseElement } from "@common/element.js"; 2 2 import { batch, computed, effect, signal, untracked } from "@common/signal.js"; 3 + import { strictEquality } from "@common/compare.js"; 3 4 4 5 /** 5 6 * @import {Facet, PlaylistItem, Theme, Track} from "@definitions/types.d.ts" ··· 21 22 * @returns {(data: T) => Promise<void>} 22 23 */ 23 24 const fn = ({ save, set }) => async (data) => { 24 - if (await this.isLeader()) { 25 - return save(data); 26 - } else { 27 - return set(data); 28 - } 25 + await untracked(async () => { 26 + if (await this.isLeader()) { 27 + return save(data); 28 + } else { 29 + return set(data); 30 + } 31 + }); 29 32 }; 30 33 31 - const actions = this.broadcast(this.nameWithGroup, { 34 + const ogFacetsSave = manager.facets.save.bind(this); 35 + const ogPlaylistItemsSave = manager.playlistItems.save.bind(this); 36 + const ogThemesSave = manager.themes.save.bind(this); 37 + const ogTracksSave = manager.tracks.save.bind(this); 38 + 39 + const actions = this.broadcast(this.identifier, { 32 40 saveFacets: { 33 41 strategy: "replicate", 34 - fn: fn({ save: manager.facets.save, set: manager.signals.facets.set }), 42 + fn: fn({ save: ogFacetsSave, set: manager.signals.facets.set }), 35 43 }, 36 44 savePlaylistItems: { 37 45 strategy: "replicate", 38 46 fn: fn({ 39 - save: manager.playlistItems.save, 47 + save: ogPlaylistItemsSave, 40 48 set: manager.signals.playlistItems.set, 41 49 }), 42 50 }, 43 51 saveThemes: { 44 52 strategy: "replicate", 45 - fn: fn({ save: manager.themes.save, set: manager.signals.themes.set }), 53 + fn: fn({ save: ogThemesSave, set: manager.signals.themes.set }), 46 54 }, 47 55 saveTracks: { 48 56 strategy: "replicate", 49 - fn: fn({ save: manager.tracks.save, set: manager.signals.tracks.set }), 57 + fn: fn({ save: ogTracksSave, set: manager.signals.tracks.set }), 50 58 }, 51 59 }); 52 60 ··· 107 115 ); 108 116 const cs = signal( 109 117 /** @type {"loading" | "loaded" | "sleeping"} */ ("sleeping"), 118 + { compare: strictEquality }, 110 119 ); 111 120 112 121 const pl = signal( ··· 115 124 ); 116 125 const pls = signal( 117 126 /** @type {"loading" | "loaded" | "sleeping"} */ ("sleeping"), 127 + { compare: strictEquality }, 118 128 ); 119 129 120 130 const th = signal( ··· 122 132 ); 123 133 const ths = signal( 124 134 /** @type {"loading" | "loaded" | "sleeping"} */ ("sleeping"), 135 + { compare: strictEquality }, 125 136 ); 126 137 127 138 const t = signal( ··· 129 140 ); 130 141 const ts = signal( 131 142 /** @type {"loading" | "loaded" | "sleeping"} */ ("sleeping"), 143 + { compare: strictEquality }, 132 144 ); 133 145 134 146 async function loadFacets() {
+1 -8
src/components/output/polymorphic/indexed-db/element.js
··· 62 62 /** @override */ 63 63 connectedCallback() { 64 64 this.replicateSavedData(this.#manager); 65 - 66 65 super.connectedCallback(); 67 66 } 68 67 ··· 76 75 77 76 // 🛠️ 78 77 79 - get namespace() { 80 - return this.hasAttribute("namespace") 81 - ? this.getAttribute("namespace") + "/" 82 - : ""; 83 - } 84 - 85 78 /** @param {string} name */ 86 79 #cat(name) { 87 - return `${this.namespace}${name}`; 80 + return `${this.namespace?.length ? this.namespace + "/" : ""}${name}`; 88 81 } 89 82 } 90 83
+93 -86
src/components/transformer/output/bytes/dasl-sync/element.js
··· 1 - import * as IDB from "idb-keyval"; 2 1 import { decode, encode } from "@atcute/cbor"; 2 + import { ifDefined } from "lit-html/directives/if-defined.js"; 3 3 import deepDiff from "@fry69/deep-diff"; 4 4 5 5 import "@components/output/polymorphic/indexed-db/element.js"; 6 6 7 7 import * as CID from "@common/cid.js"; 8 - import { computed, signal, untracked } from "@common/signal.js"; 8 + import { diff, strictEquality } from "@common/compare.js"; 9 + import { computed, signal } from "@common/signal.js"; 9 10 import { compareTimestamps } from "@common/utils.js"; 10 11 import { OutputTransformer } from "../../base.js"; 11 - import { IDB_PREFIX } from "./constants.js"; 12 - import { promiseLoadedState } from "@toko/diffuse/components/output/common.js"; 12 + import { promiseLoadedState } from "@components/output/common.js"; 13 13 14 14 /** 15 - * @import { Signal, SignalReader } from "@common/signal.d.ts"; 15 + * @import { SignalReader } from "@common/signal.d.ts"; 16 + * @import { RenderArg } from "@common/element.d.ts" 17 + * @import { OutputElement } from "@components/output/types.d.ts" 18 + * 16 19 * @import { Container } from "./types.d.ts" 17 20 */ 18 21 ··· 27 30 * @extends {OutputTransformer<Uint8Array>} 28 31 */ 29 32 class DaslBytesSyncOutputTransformer extends OutputTransformer { 33 + static NAME = "diffuse/transformer/output/bytes/dasl-sync"; 34 + 30 35 constructor() { 31 36 super(); 32 37 33 38 const remote = this.base(); 39 + const local = this.#localOutput.get; 34 40 35 41 /** 36 42 * @template {{ id: string; updatedAt: string }} T ··· 49 55 ) => { 50 56 const container = signal( 51 57 /** @type {Container<T>} */ (EMPTY), 52 - { compare: (a, b) => a.cid === b.cid }, 58 + { compare: strictEquality }, 53 59 ); 54 60 55 61 const isReady = signal(false); 56 - const merging = signal({ isBusy: false, lastCID: "" }); 62 + const merging = signal({ isBusy: false, lastCID: "" }, { 63 + compare: diff, 64 + }); 57 65 58 66 this.effect(() => { 59 67 if (!isReady.value) return; ··· 74 82 container.value = l; 75 83 76 84 if (remote.ready() && rs === "loaded") { 77 - const bytes = this.save(l); 78 - untracked(() => saveRemote(bytes)); 85 + this.isLeader().then((isLeader) => { 86 + if (!isLeader) return; 87 + const bytes = this.save(l); 88 + saveRemote(bytes); 89 + }); 79 90 } 80 91 } 81 92 } else if (!l) { 82 93 container.value = r; 83 94 84 - const bytes = this.save(r); 85 - saveLocal(bytes); 95 + this.isLeader().then((isLeader) => { 96 + if (!isLeader) return; 97 + const bytes = this.save(r); 98 + saveLocal(bytes); 99 + }); 86 100 } else if ( 87 101 rs === "loaded" && this.hasDiverged({ local: l, remote: r }) 88 102 ) { 89 - untracked(() => { 103 + // Async merge 104 + this.isLeader().then((isLeader) => { 105 + if (!isLeader) return; 106 + 90 107 merging.value = { isBusy: true, lastCID: merging.value.lastCID }; 91 - }); 92 108 93 - this.merge(l, r).then(async (c) => { 94 - if (c.cid === merging.value.lastCID) return; 109 + this.merge(l, r).then(async (c) => { 110 + container.value = c; 95 111 96 - container.value = c; 112 + if (c.cid === merging.value.lastCID) return; 97 113 98 - const bytes = this.save(c); 99 - await saveLocal(bytes); 114 + const bytes = this.save(c); 100 115 101 - if (remote.ready() && rs === "loaded") { 102 - await untracked(() => saveRemote(bytes)); 103 - } 116 + if (c.cid !== l.cid) { 117 + await saveLocal(bytes); 118 + } 119 + 120 + if (remote.ready() && rs === "loaded" && c.cid !== r.cid) { 121 + await saveRemote(bytes); 122 + } 104 123 105 - merging.value = { isBusy: false, lastCID: c.cid ?? "" }; 124 + merging.value = { isBusy: false, lastCID: c.cid ?? "" }; 125 + }); 106 126 }); 127 + } else { 128 + container.value = l; 107 129 } 108 130 }); 109 131 ··· 113 135 }); 114 136 }; 115 137 116 - // Local 117 - const local = { 118 - facets: this.local("facets"), 119 - playlistItems: this.local("playlistItems"), 120 - themes: this.local("themes"), 121 - tracks: this.local("tracks"), 122 - }; 123 - 124 138 // Container signals 125 139 const facets = state( 126 140 "facets", 127 - local.facets.get, 141 + computed(() => local()?.facets.collection()), 128 142 remote.facets.collection, 129 143 remote.facets.state, 130 144 { 131 - saveLocal: this.putLocalFn("facets", local.facets), 145 + saveLocal: async (v) => local()?.facets.save(v), 132 146 saveRemote: remote.facets.save, 133 147 }, 134 148 ); 135 149 136 150 const playlistItems = state( 137 151 "playlistItems", 138 - local.playlistItems.get, 152 + computed(() => local()?.playlistItems.collection()), 139 153 remote.playlistItems.collection, 140 154 remote.playlistItems.state, 141 155 { 142 - saveLocal: this.putLocalFn("playlistItems", local.playlistItems), 156 + saveLocal: async (v) => local()?.playlistItems.save(v), 143 157 saveRemote: remote.playlistItems.save, 144 158 }, 145 159 ); 146 160 147 161 const themes = state( 148 162 "themes", 149 - local.themes.get, 163 + computed(() => local()?.themes.collection()), 150 164 remote.themes.collection, 151 165 remote.themes.state, 152 166 { 153 - saveLocal: this.putLocalFn("themes", local.themes), 167 + saveLocal: async (v) => local()?.themes.save(v), 154 168 saveRemote: remote.themes.save, 155 169 }, 156 170 ); 157 171 158 172 const tracks = state( 159 173 "tracks", 160 - local.tracks.get, 174 + computed(() => local()?.tracks.collection()), 161 175 remote.tracks.collection, 162 176 remote.tracks.state, 163 177 { 164 - saveLocal: this.putLocalFn("tracks", local.tracks), 178 + saveLocal: async (v) => local()?.tracks.save(v), 165 179 saveRemote: remote.tracks.save, 166 180 }, 167 181 ); 168 182 169 183 // Output manager 170 184 this.facets = this.managerProp( 171 - { save: this.putLocalFn("facets", local.facets) }, 185 + { save: async (v) => local()?.facets.save(v) }, 172 186 remote.facets, 173 187 facets, 174 188 ); 175 189 176 190 this.playlistItems = this.managerProp( 177 - { save: this.putLocalFn("playlistItems", local.playlistItems) }, 191 + { save: async (v) => local()?.playlistItems.save(v) }, 178 192 remote.playlistItems, 179 193 playlistItems, 180 194 ); 181 195 182 196 this.themes = this.managerProp( 183 - { save: this.putLocalFn("themes", local.themes) }, 197 + { save: async (v) => local()?.themes.save(v) }, 184 198 remote.themes, 185 199 themes, 186 200 ); 187 201 188 202 this.tracks = this.managerProp( 189 - { save: this.putLocalFn("tracks", local.tracks) }, 203 + { save: async (v) => local()?.tracks.save(v) }, 190 204 remote.tracks, 191 205 tracks, 192 206 ); 193 207 194 208 this.ready = () => true; 209 + } 210 + 211 + // SIGNALS 212 + 213 + #localOutput = signal( 214 + /** @type {OutputElement<any> | undefined} */ (undefined), 215 + ); 216 + 217 + // LIFECYCLE 218 + 219 + /** 220 + * @override 221 + */ 222 + async connectedCallback() { 223 + // Broadcast if needed 224 + if (this.hasAttribute("group")) { 225 + this.broadcast(this.identifier, {}); 226 + } 227 + 228 + super.connectedCallback(); 229 + 230 + /** @type {OutputElement<any> | null} */ 231 + const local = this.root().querySelector("dop-indexed-db"); 232 + if (!local) throw new Error("Can't find local output"); 233 + 234 + customElements.whenDefined(local.localName).then(() => { 235 + this.#localOutput.value = local; 236 + }); 195 237 } 196 238 197 239 // DATA FUNCTIONS ··· 400 442 }; 401 443 } 402 444 403 - // INDEXED-DB 445 + // RENDER 404 446 405 447 /** 406 - * @param {string} name 448 + * @param {RenderArg} _ 407 449 */ 408 - local(name) { 409 - const s = signal(/** @type {Uint8Array | undefined} */ (undefined)); 410 - 411 - this.getLocal(name).then(s.set); 412 - 413 - return s; 414 - } 415 - 416 - /** 417 - * @param {string} name 418 - * @returns {Promise<Uint8Array | undefined>} 419 - */ 420 - getLocal(name) { 421 - return IDB.get(`${IDB_PREFIX}/${this.#cat(name)}`); 422 - } 423 - 424 - /** @param {string} name; @param {Uint8Array} data */ 425 - putLocal(name, data) { 426 - return IDB.set(`${IDB_PREFIX}/${this.#cat(name)}`, data); 427 - } 428 - 429 - /** 430 - * @param {string} name 431 - * @param {Signal<Uint8Array | undefined>} signal 432 - */ 433 - putLocalFn = 434 - (name, signal) => /** @param {Uint8Array} data */ async (data) => { 435 - signal.value = data; 436 - await this.putLocal(name, data); 437 - }; 438 - 439 - // 🛠️ 440 - 441 - get namespace() { 442 - return this.hasAttribute("namespace") 443 - ? this.getAttribute("namespace") + "/" 444 - : ""; 445 - } 446 - 447 - /** @param {string} name */ 448 - #cat(name) { 449 - return `${this.namespace}${name}`; 450 + render({ html }) { 451 + return html` 452 + <dop-indexed-db 453 + group="${ifDefined(this.getAttribute(`group`))}" 454 + namespace="${ifDefined(this.getAttribute(`namespace`))}" 455 + ></dop-indexed-db> 456 + `; 450 457 } 451 458 } 452 459
+1
src/components/transformer/output/raw/atproto-sync/element.js
··· 350 350 render({ html }) { 351 351 return html` 352 352 <dop-indexed-db 353 + group="${ifDefined(this.getAttribute(`group`))}" 353 354 namespace="${ifDefined(this.getAttribute(`namespace`))}" 354 355 ></dop-indexed-db> 355 356 `;
+9 -12
src/components/transformer/output/refiner/track-uri-passkey/element.js
··· 113 113 passkeyActive = computed(() => this.#encryptionKey.get() !== null); 114 114 lockedTracks = this.#lockedTracks.get; 115 115 116 - // NAMESPACE 117 - 118 - get namespace() { 119 - return this.getAttribute("namespace") ?? ""; 120 - } 121 - 122 116 // LIFECYCLE 123 117 124 118 /** @override */ ··· 150 144 151 145 super.connectedCallback(); 152 146 153 - loadStoredCipherKey(this.namespace).then((key) => { 147 + loadStoredCipherKey(this.namespace ?? "").then((key) => { 154 148 if (key) { 155 149 this.#encryptionKey.value = key; 156 150 } ··· 166 160 * Throws if the authenticator does not support the PRF extension. 167 161 */ 168 162 async setupPasskey() { 169 - const result = await createPasskey(this.namespace); 163 + const namespace = this.namespace ?? ""; 164 + const result = await createPasskey(namespace); 170 165 171 166 if (!result.supported) { 172 167 throw new Error(result.reason); 173 168 } 174 169 175 170 const key = await deriveCipherKey(result.prfSecond); 176 - await storeCipherKey(this.namespace, key); 171 + await storeCipherKey(namespace, key); 177 172 this.#encryptionKey.value = key; 178 173 179 174 let saved = false; ··· 197 192 * lookup. Stores the credential ID locally and derives the cipher key. 198 193 */ 199 194 async adoptPasskey() { 200 - const result = await adoptPasskeyPrfResult(this.namespace); 195 + const namespace = this.namespace ?? ""; 196 + const result = await adoptPasskeyPrfResult(namespace); 201 197 202 198 if (!result.supported) { 203 199 throw new Error(result.reason); 204 200 } 205 201 206 202 const key = await deriveCipherKey(result.prfSecond); 207 - await storeCipherKey(this.namespace, key); 203 + await storeCipherKey(namespace, key); 208 204 this.#encryptionKey.value = key; 209 205 210 206 let saved = false; ··· 227 223 * Remove the stored passkey credential and clear in-memory key material. 228 224 */ 229 225 async removePasskey() { 230 - await removeStoredPasskey(this.namespace); 226 + const namespace = this.namespace ?? ""; 227 + await removeStoredPasskey(namespace); 231 228 232 229 let removed = false; 233 230
+6 -4
src/components/transformer/output/refiner/track-uri-passkey/passkey.js
··· 16 16 * @returns {{ credential: string, cipher: string }} 17 17 */ 18 18 function idbKeys(namespace) { 19 - const prefix = namespace && namespace.length 20 - ? `${IDB_PREFIX}/${namespace}` 21 - : IDB_PREFIX; 19 + const prefix = namespace?.length ? `${IDB_PREFIX}/${namespace}` : IDB_PREFIX; 22 20 return { 23 21 credential: `${prefix}/passkey`, 24 22 cipher: `${prefix}/passkey/cipher-key`, ··· 126 124 credentialId: Array.from(credentialId), 127 125 }); 128 126 129 - return { supported: true, credentialId, prfSecond: /** @type {ArrayBuffer} */ (prfSecond) }; 127 + return { 128 + supported: true, 129 + credentialId, 130 + prfSecond: /** @type {ArrayBuffer} */ (prfSecond), 131 + }; 130 132 } 131 133 132 134 /**