chore: upload sw script from runner iframe
All checks were successful
Docs / build (push) Successful in 2m14s
All checks were successful
Docs / build (push) Successful in 2m14s
hopefully this should fix firefox 🙏
This commit is contained in:
parent
c2107bf01d
commit
a504823e53
6 changed files with 68 additions and 45 deletions
|
@ -1,15 +1,23 @@
|
|||
import type { DropdownMenuTriggerProps } from '@kobalte/core/dropdown-menu'
|
||||
import type { mtcute } from 'mtcute-repl-worker/client'
|
||||
import type { CustomTypeScriptWorker } from '../editor/utils/custom-worker.ts'
|
||||
import { timers } from '@fuman/utils'
|
||||
import { persistentAtom } from '@nanostores/persistent'
|
||||
import { LucideCheck, LucidePlay, LucidePlug, LucideRefreshCw, LucideSkull, LucideUnplug } from 'lucide-solid'
|
||||
import { languages, Uri } from 'monaco-editor/esm/vs/editor/editor.api.js'
|
||||
import { type mtcute, workerInvoke } from 'mtcute-repl-worker/client'
|
||||
import { nanoid } from 'nanoid'
|
||||
import { createEffect, createSignal, on, onCleanup, onMount } from 'solid-js'
|
||||
import { Dynamic } from 'solid-js/web'
|
||||
import { Button } from '../../lib/components/ui/button.tsx'
|
||||
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '../../lib/components/ui/dropdown-menu.tsx'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuGroupLabel,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '../../lib/components/ui/dropdown-menu.tsx'
|
||||
import { cn } from '../../lib/utils.ts'
|
||||
import { $activeAccountId } from '../../store/accounts.ts'
|
||||
import { $tabs } from '../../store/tabs.ts'
|
||||
|
@ -41,7 +49,6 @@ export function Runner(props: { isResizing: boolean }) {
|
|||
const enableUpdates = useStore($enableUpdates)
|
||||
const enableVerbose = useStore($enableVerbose)
|
||||
|
||||
let currentScriptId: string | undefined
|
||||
let deadTimer: timers.Timer | undefined
|
||||
let inactivityTimer: timers.Timer | undefined
|
||||
let iframeContainerRef!: HTMLIFrameElement
|
||||
|
@ -102,8 +109,6 @@ export function Runner(props: { isResizing: boolean }) {
|
|||
}
|
||||
case 'SCRIPT_END': {
|
||||
setRunning(false)
|
||||
workerInvoke('sw', 'forgetScript', { name: currentScriptId! })
|
||||
currentScriptId = undefined
|
||||
rescheduleInactivityTimer()
|
||||
break
|
||||
}
|
||||
|
@ -180,12 +185,9 @@ export function Runner(props: { isResizing: boolean }) {
|
|||
}
|
||||
}
|
||||
|
||||
currentScriptId = nanoid()
|
||||
await workerInvoke('sw', 'uploadScript', { name: currentScriptId, files })
|
||||
|
||||
runnerIframe()!.contentWindow!.postMessage({
|
||||
event: 'RUN',
|
||||
scriptId: currentScriptId,
|
||||
files,
|
||||
exports,
|
||||
}, '*')
|
||||
setRunning(true)
|
||||
|
@ -328,7 +330,7 @@ export function Runner(props: { isResizing: boolean }) {
|
|||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-border h-px shrink-0" />
|
||||
<div class="h-px shrink-0 bg-border" />
|
||||
<Devtools
|
||||
class={cn('size-full grow-0', props.isResizing && 'pointer-events-none')}
|
||||
iframeRef={setDevtoolsIframe}
|
||||
|
|
|
@ -19,6 +19,8 @@ await build({
|
|||
format: 'esm',
|
||||
outfile: 'src/sw/iframe/script-bundled.js',
|
||||
define: defines,
|
||||
external: ['@mtcute/web'],
|
||||
external: [
|
||||
'@mtcute/web',
|
||||
],
|
||||
minify: true,
|
||||
})
|
||||
|
|
30
packages/worker/src/sw/client-inner.ts
Normal file
30
packages/worker/src/sw/client-inner.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import type { SwMessage } from './main.ts'
|
||||
import { Deferred } from '@fuman/utils'
|
||||
|
||||
let registered = false
|
||||
let nextId = 0
|
||||
const pending = new Map<number, Deferred<any>>()
|
||||
|
||||
export async function swInvokeMethodInner(request: SwMessage, sw: ServiceWorker) {
|
||||
if (!registered) {
|
||||
navigator.serviceWorker.addEventListener('message', (e) => {
|
||||
const { id, result, error } = (e as MessageEvent).data
|
||||
const def = pending.get(id)
|
||||
if (!def) return
|
||||
if (error) {
|
||||
def.reject(new Error(error))
|
||||
} else {
|
||||
def.resolve(result)
|
||||
}
|
||||
pending.delete(id)
|
||||
})
|
||||
registered = true
|
||||
}
|
||||
|
||||
const def = new Deferred<any>()
|
||||
const id = nextId++
|
||||
;(request as any).id = id
|
||||
pending.set(id, def)
|
||||
sw.postMessage(request)
|
||||
return def.promise
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import type { SwMessage } from './main.ts'
|
||||
import { asNonNull, Deferred } from '@fuman/utils'
|
||||
import { asNonNull } from '@fuman/utils'
|
||||
import { swInvokeMethodInner } from './client-inner.ts'
|
||||
import { waitForServiceWorkerInit } from './register.ts'
|
||||
|
||||
export async function getServiceWorker() {
|
||||
|
@ -7,31 +8,6 @@ export async function getServiceWorker() {
|
|||
return asNonNull(navigator.serviceWorker.controller)
|
||||
}
|
||||
|
||||
let registered = false
|
||||
let nextId = 0
|
||||
const pending = new Map<number, Deferred<any>>()
|
||||
|
||||
export async function swInvokeMethod(request: SwMessage) {
|
||||
const sw = await getServiceWorker()
|
||||
if (!registered) {
|
||||
navigator.serviceWorker.addEventListener('message', (e) => {
|
||||
const { id, result, error } = (e as MessageEvent).data
|
||||
const def = pending.get(id)
|
||||
if (!def) return
|
||||
if (error) {
|
||||
def.reject(new Error(error))
|
||||
} else {
|
||||
def.resolve(result)
|
||||
}
|
||||
pending.delete(id)
|
||||
})
|
||||
registered = true
|
||||
}
|
||||
|
||||
const def = new Deferred<any>()
|
||||
const id = nextId++
|
||||
;(request as any).id = id
|
||||
pending.set(id, def)
|
||||
sw.postMessage(request)
|
||||
return def.promise
|
||||
return swInvokeMethodInner(request, await getServiceWorker())
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { asNonNull } from '@fuman/utils'
|
||||
import { Long, TelegramClient } from '@mtcute/web'
|
||||
import { nanoid } from 'nanoid'
|
||||
import { swInvokeMethodInner } from '../client-inner.ts'
|
||||
|
||||
type ConnectionState = import('@mtcute/web').ConnectionState
|
||||
type TelegramClientOptions = import('@mtcute/web').TelegramClientOptions
|
||||
|
@ -31,6 +34,7 @@ chobitsu.setOnMessage((message: string) => {
|
|||
|
||||
let lastAccountId: string | undefined
|
||||
let lastConnectionState: ConnectionState | undefined
|
||||
let currentScriptId: string | undefined
|
||||
let logUpdates = false
|
||||
let verboseLogs = false
|
||||
|
||||
|
@ -70,7 +74,7 @@ function initClient(accountId: string, verbose: boolean) {
|
|||
})
|
||||
}
|
||||
|
||||
window.addEventListener('message', ({ data }) => {
|
||||
window.addEventListener('message', async ({ data }) => {
|
||||
if (data.event === 'INIT') {
|
||||
sendToDevtools({
|
||||
method: 'Page.frameNavigated',
|
||||
|
@ -102,9 +106,12 @@ window.addEventListener('message', ({ data }) => {
|
|||
window.parent.postMessage({ event: 'PING' }, HOST_ORIGIN)
|
||||
}, 500)
|
||||
} else if (data.event === 'RUN') {
|
||||
currentScriptId = nanoid()
|
||||
await swInvokeMethodInner({ event: 'UPLOAD_SCRIPT', name: currentScriptId, files: data.files }, asNonNull(navigator.serviceWorker.controller))
|
||||
|
||||
const el = document.createElement('script')
|
||||
el.type = 'module'
|
||||
let script = `import * as result from "/sw/runtime/script/${data.scriptId}/main.js";`
|
||||
let script = `import * as result from "/sw/runtime/script/${currentScriptId}/main.js";`
|
||||
for (const exportName of data.exports ?? []) {
|
||||
script += `window.${exportName} = result.${exportName};`
|
||||
}
|
||||
|
@ -114,9 +121,11 @@ window.addEventListener('message', ({ data }) => {
|
|||
script += 'console.log("[mtcute-repl] Script ended");'
|
||||
}
|
||||
script += 'window.__handleScriptEnd();'
|
||||
|
||||
el.textContent = script
|
||||
el.addEventListener('error', e => window.__handleScriptEnd(e.error))
|
||||
window.__currentScript = el
|
||||
|
||||
document.body.appendChild(el)
|
||||
} else if (data.event === 'FROM_DEVTOOLS') {
|
||||
chobitsu.sendRawMessage(data.value)
|
||||
|
@ -152,6 +161,10 @@ window.addEventListener('message', ({ data }) => {
|
|||
|
||||
window.__handleScriptEnd = (error) => {
|
||||
if (!window.__currentScript) return
|
||||
if (currentScriptId) {
|
||||
swInvokeMethodInner({ event: 'FORGET_SCRIPT', name: currentScriptId }, asNonNull(navigator.serviceWorker.controller))
|
||||
.catch(console.error)
|
||||
}
|
||||
window.parent.postMessage({ event: 'SCRIPT_END', error }, HOST_ORIGIN)
|
||||
window.__currentScript.remove()
|
||||
window.__currentScript = undefined
|
||||
|
|
|
@ -59,19 +59,19 @@ export type SwMessage =
|
|||
| { event: 'CLEAR_AVATAR_CACHE', accountId: string }
|
||||
| { event: 'CLEAR_CACHE' }
|
||||
|
||||
function handleMessage(msg: SwMessage) {
|
||||
async function handleMessage(msg: SwMessage) {
|
||||
switch (msg.event) {
|
||||
case 'UPLOAD_SCRIPT': {
|
||||
uploadScript(msg.name, msg.files)
|
||||
await uploadScript(msg.name, msg.files)
|
||||
break
|
||||
}
|
||||
case 'FORGET_SCRIPT': {
|
||||
forgetScript(msg.name)
|
||||
await forgetScript(msg.name)
|
||||
break
|
||||
}
|
||||
case 'CLEAR_CACHE': {
|
||||
clearCache()
|
||||
forgetAllScripts()
|
||||
await forgetAllScripts()
|
||||
break
|
||||
}
|
||||
case 'CLEAR_AVATAR_CACHE': {
|
||||
|
|
Loading…
Reference in a new issue