Skip to content

Commit

Permalink
$mol_service separate worker plugins, fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
zerkalica committed Nov 1, 2024
1 parent a202b1b commit 61fb6db
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 73 deletions.
2 changes: 1 addition & 1 deletion notify/notify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace $ {

}

export class $mol_notify_service extends $mol_service_plugin {
export class $mol_notify_service extends $mol_service_plugin_notify {
static override message_data(data: {}) {
if ('uri' in data && 'message' in data) {
this.show(data as $mol_notify_info)
Expand Down
25 changes: 8 additions & 17 deletions notify/notify.web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,10 @@ namespace $ {
$.$mol_notify = $mol_notify_web

export class $mol_notify_service_web extends $mol_notify_service {
static override init() {
const scope = this.$.$mol_service_worker_web.scope()
scope.addEventListener( 'notificationclick', this.notification_click_event.bind(this))
}

protected static notification_click_event(event: NotificationEvent) {
event.waitUntil(this.notification_click(event.notification))
}

static override async show({ context: title, message: body, uri: data }: $mol_notify_info) {
const scope = this.$.$mol_service_worker_web.scope()
const registration = this.$.$mol_service_worker_web.registration()
const tag = data
const existen = await scope.registration.getNotifications({ tag })
const existen = await registration.getNotifications({ tag })

for( const not of existen ) {

Expand All @@ -54,15 +45,15 @@ namespace $ {

// const vibrate = [ 100, 200, 300, 400, 500 ]

await scope.registration.showNotification( title, { body, data, /*vibrate,*/ tag } )
await registration.showNotification( title, { body, data, /*vibrate,*/ tag } )

}

static async notification_click( notification: Notification ) {
const scope = this.$.$mol_service_worker_web.scope()
static override async notification( notification: Notification ) {
const clients = this.$.$mol_service_worker_web.clients()

const clients = await scope.clients.matchAll({ includeUncontrolled: true, type: 'window' })
const last = clients.at(-1)
const matched = await clients.matchAll({ includeUncontrolled: true, type: 'window' })
const last = matched.at(-1)

if( last ) {
await last.focus()
Expand All @@ -71,7 +62,7 @@ namespace $ {
return
}

await scope.clients.openWindow( notification.data )
await clients.openWindow( notification.data )
}
}

Expand Down
2 changes: 1 addition & 1 deletion offline/offline.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace $ {

export class $mol_offline extends $mol_service_plugin {
export class $mol_offline extends $mol_service_plugin_cache {
static blocked_urls = [
'//cse.google.com/adsense/search/async-ads.js'
]
Expand Down
2 changes: 1 addition & 1 deletion offline/offline.web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace $ {
}

static override activate() {
return this.$.$mol_service_worker_web.scope().clients.claim()
return this.$.$mol_service_worker_web.clients().claim()
}
}

Expand Down
11 changes: 7 additions & 4 deletions service/plugin/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
namespace $ {
export class $mol_service_plugin extends $mol_object {
static init() {}
static prepare(event: $mol_service_prepare_event) { return null as null | undefined | boolean }
static install() { return null as undefined | null | Promise<unknown> }
static activate() { return null as undefined | null | Promise<unknown> }
static state_change() {}
static message_data(data: {}) { return null as null | undefined | Promise<unknown> }

static service() { return this.$.$mol_service_worker }
}

export class $mol_service_plugin_cache extends $mol_service_plugin {
static blocked(request: Request) { return false }
static modify(request: Request, waitUntil: (promise: Promise<unknown>) => void) {
return null as null | Response | PromiseLike<Response>
}
}

static service() { return this.$.$mol_service_worker }
export class $mol_service_plugin_notify extends $mol_service_plugin {
static notification(e: unknown) { return null as null | undefined | Promise<unknown> }
}
}
12 changes: 9 additions & 3 deletions service/worker/worker.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
namespace $ {
export class $mol_service_worker extends $mol_object {
protected static in_worker() { return typeof window === 'undefined' }

static path() { return 'web.js' }

@ $mol_action
static send(data: {}) {}

static init() {}
@ $mol_mem
static prepare(next?: $mol_service_prepare_event) {
return next ? next : null
}

@ $mol_mem
static prepare_choise() { return $mol_wire_sync(this).choise_promise()?.outcome ?? null }

static choise_promise() { return this.prepare()?.userChoise }

static blocked_response() {
return new Response(
Expand Down
126 changes: 80 additions & 46 deletions service/worker/worker.web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace $ {

export class $mol_service_worker_web extends $mol_service_worker {
static is_supported() {
protected static is_supported() {
if( location.protocol !== 'https:' && location.hostname !== 'localhost' ) {
console.warn( 'HTTPS or localhost is required for service workers.' )
return false
Expand All @@ -17,12 +17,7 @@ namespace $ {
return true
}

static handler< E extends ExtendableEvent >( handle : ( event: E )=> null | undefined | Promise<unknown> ) {
return ( event: E )=> {
const result = handle( event )
if (result) event.waitUntil( result )
}
}
protected static in_worker() { return typeof window === 'undefined' }

protected static _registration = null as null | ServiceWorkerRegistration

Expand All @@ -33,15 +28,19 @@ namespace $ {
return this._registration
}

static override init() {
static init() {
if ( this.in_worker() ) {
this.worker_init()
} else if ( this.is_supported() ) {
this.registration_init()
}
}

static plugins = [] as (typeof $mol_service_plugin)[]
static plugins = [] as (
typeof $mol_service_plugin
| typeof $mol_service_plugin_notify
| typeof $mol_service_plugin_cache
)[]

static async registration_init() {
window.addEventListener( 'beforeinstallprompt' , this.prepare.bind(this) as unknown as (e: Event) => unknown )
Expand All @@ -55,7 +54,7 @@ namespace $ {

this._registration = reg

if (reg.waiting) this.state_change(reg.waiting)
if (reg.waiting) this.state(null)
else if (reg.installing) this.update_found()
else {
reg.addEventListener( 'updatefound', this.update_found.bind(this))
Expand All @@ -76,25 +75,18 @@ namespace $ {
}
}

static update_found() {
const worker = this.registration().installing
if (! worker) throw new Error('No installing worker in updatefound event')
worker.addEventListener( 'statechange', this.state_change.bind(this, worker))
protected static update_found() {
const worker = this.registration().installing!
worker.addEventListener( 'statechange', e => this.state(null))
}

static worker() {
protected static worker() {
const reg = this.registration()
const worker = reg.installing ?? reg.waiting ?? reg.active
if (! worker) throw new Error('No worker available in registration')

return worker
return reg.installing ?? reg.waiting ?? reg.active
}

static state_change(worker: ServiceWorker) {
for (const plugin of this.plugins) {
plugin.state_change()
}
}
@ $mol_mem
static state(reset?: null) { return this.worker()?.state ?? null }

static async worker_init() {
await Promise.resolve()
Expand All @@ -103,50 +95,74 @@ namespace $ {
scope.addEventListener( 'activate' , this.activate.bind(this))
scope.addEventListener( 'message', this.message.bind(this))
scope.addEventListener( 'fetch', this.fetch.bind(this))
scope.addEventListener( 'notificationclick', this.notification_click.bind(this))

this.plugins = Object.values(this.$.$mol_service)

for (const plugin of this.plugins) {
try {
plugin.init()
} catch (error) {
this.log_error(plugin, error)
}
}
}

protected static log_error(plugin: typeof $mol_service_plugin, error: unknown) {
;(error as Error).message = `${plugin.toString()}: ${(error as Error).message}`
console.error(error)
}

static scope() {
protected static scope() {
return self as unknown as ServiceWorkerGlobalScope
}

static clients() {
return this.scope().clients
}

protected static send_delayed = [] as {}[]

@ $mol_action
static override send(data: {}) {
if ( this.in_worker() ) return
const worker = this.worker()
const worker = this.in_worker() ? this.worker() : null

if (worker) {
worker.postMessage(data)
} else {
this.send_delayed.push(data)
try {
worker.postMessage(data)
} catch (error) {
console.error(error)
}

return
}

this.send_delayed.push(data)

if (this.in_worker() && ! this.send_clients_promise) {
this.send_clients_promise = this.send_clients_async()
}
}

static prepare(event: $mol_service_prepare_event) {
for (const plugin of this.plugins) {
try {
if ( plugin.prepare(event) ) return
} catch (error) {
this.log_error(plugin, error)
protected static send_clients_promise = null as null | Promise<unknown>

protected static async send_clients_async() {

let clients = [] as readonly WindowClient[]

try {
clients = await this.clients().matchAll({ includeUncontrolled: true, type: 'window' })
} catch (error) {
console.error(error)
}

for (const client of clients) {
for (const data of this.send_delayed) {
try {
client.postMessage(data)
} catch (error) {
console.error(error)
}
}
}

this.send_delayed = []
this.send_clients_promise = null


return null
}

static message(event: ExtendableMessageEvent) {
Expand Down Expand Up @@ -194,12 +210,28 @@ namespace $ {
})
}

static notification_click(event: NotificationEvent) {
for (const plugin of this.plugins) {
if ( ! ( plugin.prototype instanceof $mol_service_plugin_notify ) ) continue

try {
const result = (plugin as typeof $mol_service_plugin_notify).notification(event.notification)
if ( result ) return event.waitUntil(result)
} catch (error) {
this.log_error(plugin, error)
}
}

}

static fetch(event: FetchEvent) {
const request = event.request

for (const plugin of this.plugins) {
if ( ! ( plugin.prototype instanceof $mol_service_plugin_cache ) ) continue

try {
if (plugin.blocked(request)) {
if ((plugin as typeof $mol_service_plugin_cache).blocked(request)) {
return event.respondWith(this.blocked_response())
}
} catch (error) {
Expand All @@ -210,8 +242,10 @@ namespace $ {
const waitUntil = event.waitUntil.bind(event)

for (const plugin of this.plugins) {
if ( ! ( plugin.prototype instanceof $mol_service_plugin_cache ) ) continue

try {
const response = plugin.modify(request, waitUntil)
const response = (plugin as typeof $mol_service_plugin_cache).modify(request, waitUntil)
if (response) return event.respondWith(response)
} catch (error) {
this.log_error(plugin, error)
Expand Down

0 comments on commit 61fb6db

Please sign in to comment.