-
-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
$mol_worker_service plugins, refactor
- Loading branch information
Showing
8 changed files
with
263 additions
and
222 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,3 @@ | ||
namespace $ { | ||
try { | ||
$mol_offline.main.run() | ||
} catch( error ) { | ||
console.error( error ) | ||
} | ||
$mol_offline.attach_to($mol_worker_service) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,21 @@ | ||
namespace $ { | ||
|
||
export type $mol_offline_web_message = { | ||
ignore_cache?: boolean | ||
blocked_urls?: readonly string[] | ||
cached_urls?: readonly string[] | ||
} | ||
|
||
export class $mol_offline extends $mol_worker { | ||
static main = new $mol_offline | ||
export class $mol_offline extends $mol_worker_service_plugin { | ||
constructor() { | ||
super() | ||
this.$.$mol_dom_context.addEventListener('message', e => { | ||
if (e.data === 'mol_build_obsolete') this.value('ignore_cache', true) | ||
}) | ||
} | ||
|
||
blocked(urls?: readonly string[]) { return urls ?? [] } | ||
cached(urls?: readonly string[]) { return urls ?? [] } | ||
override defaults() { | ||
return { | ||
ignore_cache: false, | ||
blocked_urls: [ | ||
'//cse.google.com/adsense/search/async-ads.js' | ||
] | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/// <reference lib="webworker" /> | ||
namespace $ { | ||
export class $mol_worker_service_plugin extends $mol_object { | ||
static attach_to< This extends typeof $mol_worker_service_plugin >( | ||
this : This, | ||
worker = $mol_worker_service, | ||
config?: Partial< InstanceType< This > >, | ||
) { | ||
const plugin = new this | ||
if (config) { | ||
for( let key in config ) ( plugin as any )[ key ] = config[ key ]! | ||
} | ||
|
||
worker.attach(plugin) | ||
|
||
return plugin as InstanceType< This > | ||
} | ||
|
||
defaults() { return {} } | ||
|
||
id = this.toString() | ||
|
||
@ $mol_mem | ||
data_actual( next?: Partial<ReturnType<typeof this['defaults']>> | null ) { | ||
return next ?? null | ||
} | ||
|
||
@ $mol_mem | ||
data( next?: Partial<ReturnType<typeof this['defaults']>> | null ) { | ||
return this.data_actual(next) ?? this.defaults() as ReturnType< this['defaults'] > | ||
} | ||
|
||
value< Field extends keyof NonNullable<ReturnType< this['data'] >> >( | ||
field: Field, | ||
value?: NonNullable<ReturnType< this['data'] >>[ Field ] | null, | ||
) { | ||
const next = value === undefined ? undefined : { ...this.data(), [ field ]: value } | ||
|
||
return this.data(next)?.[ field as never ] as NonNullable<ReturnType< this['data'] >>[ Field ] | ||
} | ||
|
||
init(worker: ServiceWorkerGlobalScope) {} | ||
|
||
fetch_event(event: FetchEvent) { return false as void | boolean } | ||
activate(event: ExtendableEvent) { return false as void | boolean } | ||
install(event: ExtendableEvent) { return false as void | boolean } | ||
before_install(event: Event & { prompt?(): void }) { return false as void | boolean } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
namespace $ { | ||
|
||
export class $mol_worker_service extends $mol_object { | ||
static in_worker() { return typeof window === 'undefined' } | ||
|
||
static path() { return 'web.js' } | ||
|
||
static plugins = {} as Record<string, $mol_worker_service_plugin> | ||
|
||
@ $mol_mem_key | ||
static data_actual<State extends {} | null>(plugin_name: string, next?: State) { | ||
if (next && ! this.in_worker()) { | ||
this.send({ [plugin_name]: next }) | ||
} | ||
|
||
return next ?? null | ||
} | ||
|
||
static attach(plugin: $mol_worker_service_plugin) { | ||
this.plugins[plugin.id] = plugin | ||
plugin.data_actual = state => this.data_actual(plugin.id, state) | ||
} | ||
|
||
static send(data: unknown) {} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
namespace $ { | ||
export type $mol_worker_service_reg_active = ServiceWorkerRegistration & { active: ServiceWorker } | ||
export type $mol_worker_service_reg_installing = ServiceWorkerRegistration & { installing: ServiceWorker } | ||
|
||
export class $mol_worker_service_web extends $mol_worker_service { | ||
static is_supported() { | ||
if( location.protocol !== 'https:' && location.hostname !== 'localhost' ) { | ||
console.warn( 'HTTPS or localhost is required for service workers.' ) | ||
return false | ||
} | ||
|
||
if( ! navigator.serviceWorker ) { | ||
console.warn( 'Service Worker is not supported.' ) | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
protected static registration = null as null | $mol_worker_service_reg_active | ||
|
||
static async registration_init() { | ||
let reg | ||
let ready | ||
|
||
try { | ||
const reg_promise = navigator.serviceWorker.register(this.path()) | ||
ready = navigator.serviceWorker.ready as Promise<$mol_worker_service_reg_active> | ||
reg = (await reg_promise) as $mol_worker_service_reg_active | ||
if (reg.installing) this.installing(reg as $mol_worker_service_reg_installing) | ||
|
||
await ready | ||
|
||
this.registration = reg | ||
} catch (error) { | ||
console.error(error) | ||
return null | ||
} | ||
|
||
let intial_state = {} as Record<string, unknown> | ||
for (let name in this.plugins) { | ||
const data = this.plugins[name].data() | ||
intial_state[name] = data | ||
} | ||
|
||
this.send(intial_state) | ||
|
||
return reg | ||
} | ||
|
||
|
||
static installing(reg: $mol_worker_service_reg_installing) { | ||
reg.addEventListener( 'updatefound', this.update_found.bind(this, reg.installing)) | ||
} | ||
|
||
static update_found(worker: ServiceWorker) { | ||
worker.addEventListener( 'statechange', this.statechange.bind(this, worker)) | ||
} | ||
|
||
static statechange(worker: ServiceWorker) {} | ||
|
||
static override send(data: unknown) { | ||
if (! this.registration?.active) { | ||
throw new Error('Send called before worker ready') | ||
} | ||
|
||
this.registration.active.postMessage(data) | ||
} | ||
|
||
static override attach(plugin: $mol_worker_service_plugin) { | ||
super.attach(plugin) | ||
|
||
if ( this.in_worker() ) { | ||
this.worker() | ||
return | ||
} | ||
|
||
if (this.registration) return | ||
if ( ! this.is_supported() ) return | ||
|
||
this.registration_init() | ||
} | ||
|
||
protected static _worker = null as null | ServiceWorkerGlobalScope | ||
|
||
static worker() { | ||
if (this._worker) return this._worker | ||
|
||
const worker = this._worker = self as unknown as ServiceWorkerGlobalScope | ||
|
||
worker.addEventListener( 'beforeinstallprompt' , this.before_install.bind(this) ) | ||
worker.addEventListener( 'install' , this.install.bind(this)) | ||
worker.addEventListener( 'activate' , this.activate.bind(this)) | ||
worker.addEventListener( 'message', this.message.bind(this)) | ||
worker.addEventListener( 'fetch', this.fetch_event.bind(this)) | ||
|
||
for (let name in this.plugins) this.plugins[name].init(worker) | ||
|
||
return worker | ||
} | ||
|
||
static message(event: ExtendableMessageEvent) { | ||
const data = event.data as string | null | { | ||
[k: string]: unknown | ||
} | ||
if ( ! data || typeof data !== 'object' ) return false | ||
|
||
for (let name in data) { | ||
this.data_actual(name, data[name]) | ||
} | ||
|
||
return true | ||
} | ||
|
||
static before_install(event: Event & { prompt?(): void }) { | ||
let handled | ||
for (let name in this.plugins) { | ||
if (this.plugins[name]?.before_install(event)) handled = true | ||
} | ||
|
||
if (! handled) event.prompt?.() | ||
|
||
return true | ||
} | ||
|
||
static install(event: ExtendableEvent) { | ||
let handled | ||
for (let name in this.plugins) { | ||
if (this.plugins[name]?.install(event)) handled = true | ||
} | ||
if (! handled) this.worker().skipWaiting() | ||
return true | ||
} | ||
|
||
static activate(event: ExtendableEvent) { | ||
let handled | ||
for (let name in this.plugins) { | ||
if (this.plugins[name]?.activate(event)) handled = true | ||
} | ||
|
||
if (handled) return true | ||
|
||
event.waitUntil( this.worker().clients.claim() ) | ||
|
||
this.$.$mol_log3_done({ | ||
place: `${this}.activate()`, | ||
message: 'Activated', | ||
}) | ||
|
||
return true | ||
} | ||
|
||
static fetch_event(event: FetchEvent) { | ||
let handled | ||
for (let name in this.plugins) { | ||
if (this.plugins[name]?.fetch_event(event)) handled = true | ||
} | ||
return handled | ||
} | ||
} | ||
|
||
$.$mol_worker_service = $mol_worker_service_web | ||
|
||
} |
Oops, something went wrong.