@socketsupply/socket
Version:
A Cross-Platform, Native Runtime for Desktop and Mobile Apps — Create apps using HTML, CSS, and JavaScript. Written from the ground up to be small and maintainable.
237 lines (201 loc) • 6.39 kB
JavaScript
/* eslint-disable import/first */
globalThis.isSharedWorkerScope = true
import { SharedWorkerMessagePort, channel } from './index.js'
import { SharedWorkerGlobalScope } from './global.js'
import { Module, createRequire } from '../module.js'
import { Environment } from '../service-worker/env.js'
import { Buffer } from '../buffer.js'
import { Cache } from '../commonjs/cache.js'
import globals from '../internal/globals.js'
import process from '../process.js'
import debug from './debug.js'
import hooks from '../hooks.js'
import state from './state.js'
import path from '../path.js'
import ipc from '../ipc.js'
import '../console.js'
export default null
Object.defineProperties(
globalThis,
Object.getOwnPropertyDescriptors(SharedWorkerGlobalScope.prototype)
)
export const SHARED_WORKER_READY_TOKEN = { __shared_worker_ready: true }
// state
export const module = { exports: {} }
export const connections = new Set()
// event listeners
hooks.onReady(onReady)
globalThis.addEventListener('message', onMessage)
// shared worker globals
globals.register('SharedWorker.state', state)
globals.register('SharedWorker.module', module)
export function onReady () {
globalThis.postMessage(SHARED_WORKER_READY_TOKEN)
}
export async function onMessage (event) {
const { data } = event
if (data?.install) {
event.stopImmediatePropagation()
const { id, scriptURL } = data.install
const url = new URL(scriptURL)
if (!url.pathname.startsWith('/socket/')) {
// preload commonjs cache for user space server workers
Cache.restore(['loader.status', 'loader.response'])
}
state.id = id
state.sharedWorker.id = id
state.sharedWorker.scriptURL = scriptURL
Module.main.addEventListener('error', (event) => {
if (event.error) {
debug(event.error)
}
})
Object.defineProperties(globalThis, {
require: {
configurable: false,
enumerable: false,
writable: false,
value: createRequire(scriptURL)
},
origin: {
configurable: false,
enumerable: true,
writable: false,
value: url.origin
},
__dirname: {
configurable: false,
enumerable: false,
writable: false,
value: path.dirname(url.pathname)
},
__filename: {
configurable: false,
enumerable: false,
writable: false,
value: url.pathname
},
module: {
configurable: false,
enumerable: false,
writable: false,
value: module
},
exports: {
configurable: false,
enumerable: false,
get: () => module.exports
},
process: {
configurable: false,
enumerable: false,
get: () => process
},
Buffer: {
configurable: false,
enumerable: false,
get: () => Buffer
},
global: {
configurable: false,
enumerable: false,
get: () => globalThis
}
})
try {
// define the actual location of the worker. not `blob:...`
globalThis.RUNTIME_WORKER_LOCATION = scriptURL
// update state
state.sharedWorker.state = 'installing'
// open envirnoment
state.env = await Environment.open({ type: 'sharedWorker', id })
// import module, which could be ESM, CommonJS,
// or a simple SharedWorker
const result = await import(scriptURL)
if (typeof module.exports === 'function') {
module.exports = {
default: module.exports
}
} else {
Object.assign(module.exports, result)
}
// update state
state.sharedWorker.state = 'installed'
} catch (err) {
debug(err)
state.sharedWorker.state = 'error'
channel.postMessage({
error: { id: state.id, message: err.message }
})
return
}
if (module.exports.default && typeof module.exports.default === 'object') {
if (typeof module.exports.default.connect === 'function') {
state.connect = module.exports.default.connect.bind(module.exports.default)
}
} else if (typeof module.exports.connect === 'function') {
state.connect = module.exports.connect.bind(module.exports)
}
if (module.exports.default && typeof module.exports.default === 'object') {
if (typeof module.exports.default.reportError === 'function') {
state.reportError = module.exports.default.reportError.bind(module.exports.default)
}
} else if (typeof module.exports.reportError === 'function') {
state.reportError = module.reportError.bind(module.exports)
}
if (typeof state.connect === 'function') {
globalThis.addEventListener('connect', async (event) => {
try {
const promise = state.connect(state.env, event.data, event.ports[0])
await promise
} catch (err) {
debug(err)
channel.postMessage({
error: { id: state.id, message: err.message }
})
}
})
}
channel.postMessage({ installed: { id: state.id } })
debug(
'[%s]: SharedWorker (%s) installed',
new URL(scriptURL).pathname.replace(/^\/socket\//, 'socket:'),
state.id
)
return
}
if (data?.connect) {
event.stopImmediatePropagation()
const connection = ipc.inflateIPCMessageTransfers(event.data.connect, new Map(Object.entries({
SharedWorkerMessagePort
})))
for (const entry of connections) {
if (entry.id === connection.port.id) {
entry.close(false)
connections.delete(entry)
break
}
}
connections.add(connection.port)
const connectEvent = new MessageEvent('connect')
Object.defineProperty(connectEvent, 'ports', {
configurable: false,
writable: false,
value: Object.seal(Object.freeze([connection.port]))
})
globalThis.dispatchEvent(connectEvent)
debug(
'[%s]: SharedWorker (%s) connection from client (%s/%s) at %s',
new URL(state.sharedWorker.scriptURL).pathname.replace(/^\/socket\//, 'socket:'),
state.id,
connection.client.id,
[
connection.client.frameType.replace('none', ''),
connection.client.type
].filter(Boolean).join('-'),
connection.client.location
)
// eslint-disable-next-line
return
}
}