Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chelonia in SW #2357

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9b89d48
Chelonia in SW
corrideat Sep 19, 2024
bce3c5c
Debug
corrideat Sep 22, 2024
4601d99
Merge branch 'master' into feature/chelonia-in-service-worker
corrideat Sep 29, 2024
15d5f86
Merge branch 'master' into feature/chelonia-in-service-worker
corrideat Oct 4, 2024
2861605
Test fixes
corrideat Oct 6, 2024
80b1385
Fix group-chat-direct-message spec
corrideat Oct 6, 2024
693173f
Fix issue with the no-results slot being (incorrectly) used
corrideat Oct 6, 2024
ba57fb4
Changes supporting failing chat tests
corrideat Oct 7, 2024
13b4357
Fix attachments in SW
corrideat Oct 9, 2024
0a90ec4
Fix Flow fypes
corrideat Oct 13, 2024
22a0f6e
Use atomic for chatroom members
corrideat Oct 13, 2024
e5f8ed2
Chat bugfixes
corrideat Oct 14, 2024
286570a
Merge branch 'master' into feature/chelonia-in-service-worker
corrideat Oct 15, 2024
ab7d69d
Merge branch 'master' into feature/chelonia-in-service-worker
corrideat Oct 17, 2024
3c78f54
Use session storage for tab logs. Refactor logging to be more generic.
corrideat Oct 17, 2024
c8a4441
Bugfixes and removal of unnecessary selectors
corrideat Oct 17, 2024
17f3e77
SW logs
corrideat Oct 19, 2024
fe4521a
Bugfix for loading preferences (fix event handlers)
corrideat Oct 20, 2024
870d9a6
State for KV events
corrideat Oct 20, 2024
001e813
Serious banner error
corrideat Oct 20, 2024
48db75e
More consistent chelonia / vuex state use
corrideat Oct 20, 2024
d99c1e0
Remove debug logging
corrideat Oct 20, 2024
90460e7
Merge branch 'master' into feature/chelonia-in-service-worker
corrideat Oct 21, 2024
279e171
Logs UI
corrideat Oct 23, 2024
933957b
Merge branch 'master' into feature/chelonia-in-service-worker
corrideat Oct 23, 2024
ce94156
Lint
corrideat Oct 23, 2024
bdd0f84
Autologout for non-exisiting identity contracts
corrideat Oct 23, 2024
218dd9b
Merge branch 'master' into feature/chelonia-in-service-worker
corrideat Oct 24, 2024
aa12e08
Safari workaround
corrideat Oct 24, 2024
fd77e0b
Bugfix
corrideat Oct 24, 2024
18046a7
Bugfixes
corrideat Oct 25, 2024
bbb7277
Logout flow fixes
corrideat Oct 25, 2024
f7bc3d4
Last logged in event
corrideat Oct 25, 2024
b08b48b
Avoid SW logs spam
corrideat Oct 25, 2024
0f55f24
Chatroom position events
corrideat Oct 25, 2024
332e8cb
Feedback
corrideat Oct 27, 2024
6fd370f
WIP
corrideat Oct 28, 2024
fe4e55d
Stability bugfixes
corrideat Nov 2, 2024
ca116a5
Merge branch 'master' into feature/chelonia-in-service-worker
corrideat Nov 2, 2024
e4f40bc
Port notifications code
corrideat Nov 3, 2024
74b2dfa
Backend functions for sending
corrideat Nov 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions frontend/controller/actions/chatroom.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ sbp('okTurtles.events/on', MESSAGE_RECEIVE_RAW, ({
// If newMessage is undefined, it means that an existing message is being edited
newMessage
}) => {
const state = sbp('chelonia/contract/state', contractID)
const getters = sbp('state/vuex/getters')
const mentions = makeMentionFromUserID(getters.ourIdentityContractId)
const msgData = newMessage || data
Expand All @@ -42,10 +43,10 @@ sbp('okTurtles.events/on', MESSAGE_RECEIVE_RAW, ({
messageHash: msgData.hash,
height: msgData.height,
text: msgData.text,
isDMOrMention: isMentionedMe || getters.chatRoomAttributes.type === CHATROOM_TYPES.DIRECT_MESSAGE,
isDMOrMention: isMentionedMe || state.attributes?.type === CHATROOM_TYPES.DIRECT_MESSAGE,
messageType: !newMessage ? MESSAGE_TYPES.TEXT : data.type,
memberID: innerSigningContractID,
chatRoomName: getters.chatRoomAttributes.name
chatRoomName: state.attributes?.name
Comment on lines +47 to +50
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is preventing us from moving the getter definitions into a file that is shared by the frontend and the SW?

That would be much preferable to this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chatroom getters are a Vuex module, which are harder to emulate without Vuex because of state partitioning. In addition, those getters rely on currentGroupId and currentChatroomId which can't be used in contracts.

Copy link
Member

@taoeffect taoeffect Oct 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there nothing creative that's possible here? We can't define getters in a way that has them referring to the "the chatroom that corresponds to this state"?

i.e. "state paritioned getters" ala gettersProxy? (see bottom of chelonia.js)

Copy link
Member Author

@corrideat corrideat Oct 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the chatroom that corresponds to this state

How would that even be possible the way getters are defined? I don't really see an issue with the lines highlighted, and I'm not even sure why we'd need a getter that returns a single property.

But going to the definition:

  chatRoomAttributes (state, getters) {
    return getters.currentChatRoomState.attributes || {}
  },

Sure, in this instance state corresponds to getters.currentChatRoomState, but I don't see how generically we can know what getters.currentChatRoomState should be (generically). In the app, it works because (remember this is a global getter) there is a currentChatRoomId. In contracts it works, because currentChatRoomState can be defined to return 'state'. In the SW, the situation is analogous to the app, but there's no currentChatRoomId.

Now, this could maybe work by having a getter that takes the contractID as a function parameter, but this requires refactoring and adds complexity. I also think such getters that are simply accessors should be avoided, as they increase code line count and complexity for no benefit.

Copy link
Member Author

@corrideat corrideat Oct 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about it, another solution along the lines of what you suggested could be something like:

const getters = sbp('chelonia/contract/getters', 'gi.contracts/chatroom', state)
getters.currentChatRoomState

But honestly, for simple accessors such as these I don't see the benefit of adding this complexity. This also adds two other factors:

  1. If the getters are hardcoded (as in, statically imported), it increases bundle size
  2. If the getters are dynamic, they'll probably need to be async (in case the contract isn't loaded) and because of sandboxing (maybe).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@corrideat would it be possible to instantiate dynamically a new batch of getters where getters. currentChatRoomState returns a specific contract state in the SW using some sort of factory function?

I also think such getters that are simply accessors should be avoided, as they increase code line count and complexity for no benefit.

Getters are very important, as they solve a lot of DRY related problems. By using getters we can ensure that the way that data is accessed everywhere will have consistent return values (e.g. because it uses tricks like || {} etc.). Whereas those considerations would have to be thought of each time you access state data directly.

Copy link
Member

@taoeffect taoeffect Oct 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const getters = sbp('chelonia/contract/getters', 'gi.contracts/chatroom', state)

Yeah, that's a great idea!

I'd make one slight modification: instead of passing in state, passing in contractID:

const getters = sbp('chelonia/contract/getters', contractID)

(EDIT: I don't think you need to pass in the name, that can be retrieved using contractID if needed)

Then yeah, at least we could reuse the contract getters that are already defined in the contracts!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update based on today's conversation: we could use getters synchronously by importing the contract getters from this actions file. However, it'd have the downside of being 'statically hardcoded' based on whatever version is imported at build time and will increase bundle size. Doing it via Chelonia is potentially possible, but it'd need to be async in case the contract isn't cached and need to be loading and also maybe because of sandboxing.

}).catch(e => {
console.error('[action/chatroom.js] Error on messageReceivePostEffect', e)
})
Expand Down
4 changes: 2 additions & 2 deletions frontend/controller/actions/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -860,8 +860,8 @@ export default (sbp('sbp/selectors/register', {
// inside of the exception handler :-(
}
},
'gi.actions/group/notifyProposalStateInGeneralChatRoom': function ({ groupID, proposal }: { groupID: string, proposal: Object }) {
const { generalChatRoomId } = sbp('chelonia/rootState')[groupID]
'gi.actions/group/notifyProposalStateInGeneralChatRoom': async function ({ groupID, proposal }: { groupID: string, proposal: Object }) {
const { generalChatRoomId } = await sbp('chelonia/contract/state', groupID)
return sbp('gi.actions/chatroom/addMessage', {
contractID: generalChatRoomId,
data: { type: MESSAGE_TYPES.INTERACTIVE, proposal }
Expand Down
8 changes: 3 additions & 5 deletions frontend/controller/actions/identity-kv.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'
import sbp from '@sbp/sbp'
import { KV_KEYS } from '~/frontend/utils/constants.js'
import { KV_QUEUE, ONLINE } from '~/frontend/utils/events.js'
import { KV_QUEUE, NEW_PREFERENCES, NEW_UNREAD_MESSAGES, ONLINE } from '~/frontend/utils/events.js'
import { isExpired } from '@model/notifications/utils.js'

const initNotificationStatus = (data = {}) => ({ ...data, read: false })
Expand Down Expand Up @@ -51,8 +51,7 @@ export default (sbp('sbp/selectors/register', {
'gi.actions/identity/kv/loadChatRoomUnreadMessages': () => {
return sbp('okTurtles.eventQueue/queueEvent', KV_QUEUE, async () => {
const currentChatRoomUnreadMessages = await sbp('gi.actions/identity/kv/fetchChatRoomUnreadMessages')
// TODO: Can't use state/vuex/commit
sbp('state/vuex/commit', 'setUnreadMessages', currentChatRoomUnreadMessages)
sbp('okTurtles.events/emit', NEW_UNREAD_MESSAGES, currentChatRoomUnreadMessages)
})
},
'gi.actions/identity/kv/initChatRoomUnreadMessages': ({ contractID, messageHash, createdHeight }: {
Expand Down Expand Up @@ -192,8 +191,7 @@ export default (sbp('sbp/selectors/register', {
'gi.actions/identity/kv/loadPreferences': () => {
return sbp('okTurtles.eventQueue/queueEvent', KV_QUEUE, async () => {
const preferences = await sbp('gi.actions/identity/kv/fetchPreferences')
// TODO: Can't use state/vuex/commit
sbp('state/vuex/commit', 'setPreferences', preferences)
sbp('okTurtles.events/emit', NEW_PREFERENCES, preferences)
})
},
'gi.actions/identity/kv/updateDistributionBannerVisibility': ({ contractID, hidden }: { contractID: string, hidden: boolean }) => {
Expand Down
4 changes: 1 addition & 3 deletions frontend/controller/actions/identity.js
Original file line number Diff line number Diff line change
Expand Up @@ -497,9 +497,7 @@ export default (sbp('sbp/selectors/register', {
const partnerIDs = params.data.memberIDs
.filter(memberID => memberID !== rootGetters.ourIdentityContractId)
.map(memberID => rootGetters.ourContactProfilesById[memberID].contractID)
// NOTE: 'rootState.currentGroupId' could be changed while waiting for the sbp functions to be proceeded
// So should save it as a constant variable 'currentGroupId', and use it which can't be changed
const currentGroupId = rootState.currentGroupId
const currentGroupId = params.currentGroupId

const message = await sbp('gi.actions/chatroom/create', {
data: {
Expand Down
11 changes: 10 additions & 1 deletion frontend/controller/app/identity.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { GIErrorUIRuntimeError, L, LError, LTags } from '@common/common.js'
import { cloneDeep } from '@model/contracts/shared/giLodash.js'
import sbp from '@sbp/sbp'
import Vue from 'vue'
import { LOGIN, LOGIN_COMPLETE, LOGIN_ERROR } from '~/frontend/utils/events.js'
import { LOGIN, LOGIN_COMPLETE, LOGIN_ERROR, NEW_PREFERENCES, NEW_UNREAD_MESSAGES } from '~/frontend/utils/events.js'
import { Secret } from '~/shared/domains/chelonia/Secret.js'
import { boxKeyPair, buildRegisterSaltRequest, computeCAndHc, decryptContractSalt, hash, hashPassword, randomNonce } from '~/shared/zkpp.js'
// Using relative path to crypto.js instead of ~-path to workaround some esbuild bug
Expand Down Expand Up @@ -147,6 +147,14 @@ sbp('okTurtles.events/on', LOGIN, async ({ identityContractID, encryptionParams,
}
})

sbp('okTurtles.events/on', NEW_PREFERENCES, (currentChatRoomUnreadMessages) => {
sbp('state/vuex/commit', 'setUnreadMessages', currentChatRoomUnreadMessages)
})

sbp('okTurtles.events/on', NEW_UNREAD_MESSAGES, (preferences) => {
sbp('state/vuex/commit', 'setPreferences', preferences)
})

/* Commented out as persistentActions are not being used
sbp('okTurtles.events/on', LOGOUT, (a) => {
// TODO: [SW] It may make more sense to load persistent actions in
Expand Down Expand Up @@ -223,6 +231,7 @@ export default (sbp('sbp/selectors/register', {

return userID
} catch (e) {
console.error('@@@gi.app/identity/create failed!', e.message, e.stack)
console.error('gi.app/identity/create failed!', e)
throw new GIErrorUIRuntimeError(L('Failed to create user identity: {reportError}', LError(e)))
}
Expand Down
24 changes: 3 additions & 21 deletions frontend/controller/namespace.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
'use strict'

import sbp from '@sbp/sbp'
import Vue from 'vue'

// NOTE: prefix groups with `group/` and users with `user/` ?
sbp('sbp/selectors/register', {
'namespace/lookupCached': (name: string) => {
const cache = sbp('state/vuex/state').namespaceLookups
return cache[name] ?? null
return cache?.[name] ?? null
},
'namespace/lookupReverseCached': (id: string) => {
const cache = sbp('state/vuex/state').reverseNamespaceLookups
return cache[id] ?? null
return cache?.[id] ?? null
},
'namespace/lookup': (name: string, { skipCache }: { skipCache: boolean } = { skipCache: false }) => {
if (!skipCache) {
Expand All @@ -23,23 +22,6 @@ sbp('sbp/selectors/register', {
return Promise.resolve(cached)
}
}
return fetch(`${sbp('okTurtles.data/get', 'API_URL')}/name/${encodeURIComponent(name)}`).then((r: Object) => {
if (!r.ok) {
console.warn(`namespace/lookup: ${r.status} for ${name}`)
if (r.status !== 404) {
throw new Error(`${r.status}: ${r.statusText}`)
}
return null
}
return r['text']()
}).then(value => {
if (value !== null) {
const cache = sbp('state/vuex/state').namespaceLookups
const reverseCache = sbp('state/vuex/state').reverseNamespaceLookups
Vue.set(cache, name, value)
Vue.set(reverseCache, value, name)
}
return value
})
return sbp('sw-namespace/lookup', name, { skipCache })
}
})
17 changes: 14 additions & 3 deletions frontend/controller/service-worker.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
'use strict'

import sbp from '@sbp/sbp'
import { PUBSUB_INSTANCE } from '@controller/instance-keys.js'
import { REQUEST_TYPE, PUSH_SERVER_ACTION_TYPE, PUBSUB_RECONNECTION_SUCCEEDED, createMessage } from '~/shared/pubsub.js'
import { HOURS_MILLIS } from '~/frontend/model/contracts/shared/time.js'
import sbp from '@sbp/sbp'
import { PWA_INSTALLABLE } from '@utils/events.js'
import { HOURS_MILLIS } from '~/frontend/model/contracts/shared/time.js'
import { PUBSUB_RECONNECTION_SUCCEEDED, PUSH_SERVER_ACTION_TYPE, REQUEST_TYPE, createMessage } from '~/shared/pubsub.js'
import { deserializer } from '~/shared/serdes/index.js'

const pwa = {
deferredInstallPrompt: null,
Expand All @@ -25,6 +26,7 @@ window.addEventListener('beforeinstallprompt', e => {

sbp('sbp/selectors/register', {
'service-workers/setup': async function () {
console.error('@@@SW SETUP')
// setup service worker
// TODO: move ahead with encryption stuff ignoring this service worker stuff for now
// TODO: improve updating the sw: https://stackoverflow.com/a/49748437
Expand Down Expand Up @@ -82,12 +84,19 @@ sbp('sbp/selectors/register', {
sbp('service-worker/resubscribe-push', data.subscription)
break
}
case 'event': {
console.error('@@@EVENT RECEIVED', event.data.subtype, ...deserializer(event.data.data))
sbp('okTurtles.events/emit', event.data.subtype, ...deserializer(event.data.data))
break
}
default:
console.error('[sw] Received unknown message type from the service worker:', data)
break
}
}
})

console.error('@@@SW DONE, returning')
} catch (e) {
console.error('error setting up service worker:', e)
}
Expand All @@ -104,6 +113,8 @@ sbp('sbp/selectors/register', {
}

const pubsub = sbp('okTurtles.data/get', PUBSUB_INSTANCE)
if (!pubsub) return // TODO: This needs to be moved into the service worker
// proper. pubsub will be undefined in this context.
const existingSubscription = await registration.pushManager.getSubscription()
const messageToPushServerIfSocketConnected = (msgPayload) => {
// make sure the websocket client is not in the state of CLOSING, CLOSED before sending a message.
Expand Down
127 changes: 126 additions & 1 deletion frontend/controller/serviceworkers/sw-primary.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,126 @@
'use strict'

import { PROPOSAL_ARCHIVED } from '@model/contracts/shared/constants.js'
import '@sbp/okturtles.data'
import '@sbp/okturtles.eventqueue'
import '@sbp/okturtles.events'
import sbp from '@sbp/sbp'
import '~/frontend/controller/actions/index.js'
import '~/frontend/controller/sw-namespace.js'
import getters from '~/frontend/model/getters.js'
import '~/frontend/model/notifications/selectors.js'
import setupChelonia from '~/frontend/setupChelonia.js'
import { LOGIN, LOGIN_ERROR, LOGOUT } from '~/frontend/utils/events.js'
import { GIMessage } from '~/shared/domains/chelonia/GIMessage.js'
import { Secret } from '~/shared/domains/chelonia/Secret.js'
import { CONTRACTS_MODIFIED, CONTRACT_IS_SYNCING, EVENT_HANDLED } from '~/shared/domains/chelonia/events.js'
import { deserializer, serializer } from '~/shared/serdes/index.js'
import { ACCEPTED_GROUP, DELETED_CHATROOM, JOINED_CHATROOM, JOINED_GROUP, KV_EVENT, LEFT_CHATROOM, LEFT_GROUP, NAMESPACE_REGISTRATION, NEW_PREFERENCES, NEW_UNREAD_MESSAGES, NOTIFICATION_EMITTED, NOTIFICATION_REMOVED, NOTIFICATION_STATUS_LOADED, SWITCH_GROUP } from '../../utils/events.js'

deserializer.register(GIMessage)
deserializer.register(Secret)

// https://serviceworke.rs/message-relay_service-worker_doc.html
// https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers
// https://jakearchibald.com/2014/using-serviceworker-today/
// https://github.com/w3c/ServiceWorker/blob/master/explainer.md
// https://frontendian.co/service-workers
// https://stackoverflow.com/a/49748437 => https://medium.com/@nekrtemplar/self-destroying-serviceworker-73d62921d717 => https://love2dev.com/blog/how-to-uninstall-a-service-worker/

sbp('sbp/filters/global/add', (domain, selector, data) => {
// if (domainBlacklist[domain] || selectorBlacklist[selector]) return
console.debug(`[sw] [sbp] ${selector}`, data)
});

[EVENT_HANDLED, CONTRACTS_MODIFIED, CONTRACT_IS_SYNCING, LOGIN, LOGIN_ERROR, LOGOUT, ACCEPTED_GROUP, DELETED_CHATROOM, LEFT_CHATROOM, LEFT_GROUP, JOINED_CHATROOM, JOINED_GROUP, KV_EVENT, NAMESPACE_REGISTRATION, NEW_PREFERENCES, NEW_UNREAD_MESSAGES, NOTIFICATION_EMITTED, NOTIFICATION_REMOVED, NOTIFICATION_STATUS_LOADED, SWITCH_GROUP, PROPOSAL_ARCHIVED].forEach(et => {
sbp('okTurtles.events/on', et, (...args) => {
const { data } = serializer(args)
const message = {
type: 'event',
subtype: et,
data
}
self.clients.matchAll()
.then((clientList) => {
clientList.forEach((client) => {
client.postMessage(message)
})
})
})
})

sbp('sbp/selectors/register', {
'state/vuex/state': () => {
// TODO: Remove this selector once it's removed from contracts
return sbp('chelonia/rootState')
},
'state/vuex/reset': () => {
console.error('[sw] CALLED state/vuex/reset WHICH IS UNDEFINED')
},
'state/vuex/save': () => {
console.error('[sw] CALLED state/vuex/save WHICH IS UNDEFINED')
},
'state/vuex/commit': () => {
console.error('[sw] CALLED state/vuex/commit WHICH IS UNDEFINED')
},
'state/vuex/getters': () => {
const obj = Object.create(null)
Object.defineProperties(obj, Object.fromEntries(Object.entries(getters).map(([getter, fn]) => {
return [getter, {
get: () => {
const state = sbp('chelonia/rootState')
return fn(state, obj)
}
}]
})))

return obj
/* return {
chatRoomUnreadMessages () {
return []
},
getChatroomNameById () {
return ''
},
groupIdFromChatRoomId () {
return ''
},
isGroupDirectMessage () {
return false
},
notificationsByGroup () {
return []
},
ourContactProfilesByUsername: {},
ourIdentityContractId: '',
userDisplayNameFromID () {
return ''
},
usernameFromID () {
return ''
},
ourContactProfilesById: {}
}
*/
}
})

sbp('sbp/selectors/register', {
'controller/router': () => {
return { options: { base: '/app/' } }
}
})

sbp('sbp/selectors/register', {
'gi.ui/seriousErrorBanner': (...args) => {
console.error('### SERIOUS ERROR ###', ...args)
}
})

sbp('sbp/selectors/register', {
'appLogs/save': () => {}
})

self.addEventListener('install', function (event) {
console.debug('[sw] install')
event.waitUntil(self.skipWaiting())
Expand All @@ -16,7 +130,7 @@ self.addEventListener('activate', function (event) {
console.debug('[sw] activate')

// 'clients.claim()' reference: https://web.dev/articles/service-worker-lifecycle#clientsclaim
event.waitUntil(self.clients.claim())
event.waitUntil(setupChelonia().then(() => self.clients.claim()))
})

self.addEventListener('fetch', function (event) {
Expand Down Expand Up @@ -57,6 +171,17 @@ self.addEventListener('message', function (event) {
case 'store-client-id':
store.clientId = event.source.id
break
case 'sbp': {
const port = event.data.port;
(async () => await sbp(...deserializer(event.data.data)))().then((r) => {
const { data, transferables } = serializer(r)
port.postMessage([true, data], transferables)
}).catch((e) => {
const { data, transferables } = serializer(e)
port.postMessage([false, data], transferables)
})
break
}
case 'ping':
event.source.postMessage({ type: 'pong' })
break
Expand Down
Loading