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

$mol_offline refactor, disable cache on page reload via dev server #710

Open
wants to merge 61 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
b22b11a
$mol_offline refactor, disable cache on page reload via dev server
zerkalica Oct 25, 2024
9d5f058
$mol_offline refactor
zerkalica Oct 25, 2024
ce606ba
$mol_offline refactor 2 - simplify respond
zerkalica Oct 25, 2024
ca3be5d
$mol_offline refactor 3 - simplify respond
zerkalica Oct 25, 2024
f965597
$mol_offline refactor 4 - extracted send
zerkalica Oct 25, 2024
81a9ac6
$mol_offline refactor
zerkalica Oct 25, 2024
c83d213
$mol_offline mol_build_obsolete message pass to worker to disable cache
zerkalica Oct 25, 2024
9f3722b
$mol_offline review fixes
zerkalica Oct 26, 2024
84c0253
$mol_offline review fixes 2
zerkalica Oct 26, 2024
c476bb3
$mol_offline fixes 3
zerkalica Oct 26, 2024
f8eab6a
$mol_offline refactor, extract $mol_worker
zerkalica Oct 26, 2024
f17f33b
$mol_offline fix refresh
zerkalica Oct 26, 2024
6efe454
$mol_offline use force-cache
zerkalica Oct 26, 2024
5864255
$mol_offline rules refactored, some bugs fixed
zerkalica Oct 27, 2024
201dcb2
$mol_offline configure cached and blocked urls regexp
zerkalica Oct 27, 2024
cd84bfa
$mol_offline fix regexp
zerkalica Oct 27, 2024
958e207
$mol_worker_service plugins, refactor
zerkalica Oct 28, 2024
2dcd5aa
$mol_offline save ignore_cache between window.reload()
zerkalica Oct 28, 2024
08aa70c
$mol_offline fix test.html caching
zerkalica Oct 28, 2024
3593a0a
$mol_worker refactor
zerkalica Oct 29, 2024
e25b677
$mol_worker fix worker singletone
zerkalica Oct 29, 2024
db14037
$mol_worker fix try/catch in attach
zerkalica Oct 29, 2024
bb46b57
$mol_worker_service renamed to $mol_service, $mol_offline refactor to…
zerkalica Oct 29, 2024
121c90e
$mol_service fix init hook
zerkalica Oct 29, 2024
9f86a71
$mol_service refactor extracted fetch service and notify
zerkalica Oct 29, 2024
7d3ce8a
$mol_fetch_service fix bug with fetch_event binded scope
zerkalica Oct 29, 2024
b168b72
$mol_fetch_service refactor simplify add logic
zerkalica Oct 29, 2024
354468b
$mol_service_plugin extracted, used static, fetch hook moved back to …
zerkalica Oct 30, 2024
7a0cf1e
$mol_service refactor detach method, auto init
zerkalica Oct 30, 2024
d53c252
$mol_service_plugin namespace
zerkalica Oct 30, 2024
21da91e
$mol_service_plugin remove plugin filter, simplify
zerkalica Oct 30, 2024
cbcef3f
$mol_service_plugin moved to $ namespace
zerkalica Oct 30, 2024
ea7d089
$mol_build_client removed unused postMessage
zerkalica Oct 30, 2024
02d8dcc
$mol_service_plugin - include mol_service, add waitUntil to modify, $…
zerkalica Oct 30, 2024
7f7ce8d
$mol_service fix inner namespace
zerkalica Oct 30, 2024
4b10be0
$mol_service_host refactor, better update handling api
zerkalica Oct 31, 2024
d12f71f
$mol_service review fixes
zerkalica Nov 1, 2024
a202b1b
$mol_offline regexp to URL parser
zerkalica Nov 1, 2024
61fb6db
$mol_service separate worker plugins, fixes
zerkalica Nov 1, 2024
5b68802
$mol_service moved to wires, better errors
zerkalica Nov 2, 2024
00dcb49
$mol_service refactor
zerkalica Nov 2, 2024
33b1fc6
$mol_service refactor
zerkalica Nov 2, 2024
3ee7e45
$mol_worker async
zerkalica Nov 2, 2024
6fabdac
$mol_offline refactoring
zerkalica Nov 3, 2024
10b0cef
$mol_service push event handler added, sync scope
zerkalica Nov 3, 2024
1efed9b
$mol_service_worker refactor, extracted prompt events
zerkalica Nov 3, 2024
7f008e6
$mol_service_worker type fix
zerkalica Nov 3, 2024
2169769
$mol_service_worker simple notify
zerkalica Nov 3, 2024
2265c73
$mol_service_prompt moved to offline, refactor
zerkalica Nov 3, 2024
9dafc68
$mol_offline web
zerkalica Nov 3, 2024
7778411
$mol_service_worker fix start
zerkalica Nov 4, 2024
a2aa4c3
$mol_offline fix prompt offline
zerkalica Nov 4, 2024
b13f25e
Merge branch 'master' of github.com:hyoo-ru/mam_mol into offline2
zerkalica Nov 4, 2024
6715f91
$mol_service_worker minor fixes
zerkalica Nov 5, 2024
001e562
$mol_fetch removed unused request
zerkalica Nov 5, 2024
60811c1
$mol_service refactor, split self and worker
zerkalica Nov 6, 2024
d579f8a
$mol_service fixes
zerkalica Nov 6, 2024
370372a
$mol_service fixes 2
zerkalica Nov 6, 2024
a33dcaf
$mol_worker rpc refactored
zerkalica Nov 6, 2024
7849b8a
$mol_notify fixes
zerkalica Nov 6, 2024
854046d
$mol_rpc_client fix call bug
zerkalica Nov 7, 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
1 change: 1 addition & 0 deletions build/client/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ function $mol_build_client() {

socket.onmessage = message => {
if( message.data !== '$mol_build_obsolete' ) return
postMessage('mol_build_obsolete')
location.reload()
}

Expand Down
2 changes: 1 addition & 1 deletion offline/install/install.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace $ {
try {
$mol_offline()
new $mol_offline().run()
} catch( error ) {
console.error( error )
}
Expand Down
6 changes: 5 additions & 1 deletion offline/offline.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
namespace $ {

export function $mol_offline( ) {}
export class $mol_offline {
run() {
return false
}
}

}
281 changes: 180 additions & 101 deletions offline/offline.web.ts
Original file line number Diff line number Diff line change
@@ -1,109 +1,188 @@
/// <reference lib="webworker" />

namespace $ {

const blacklist = new Set([
'//cse.google.com/adsense/search/async-ads.js'
])

/** Installs service worker proxy, which caches all requests and respond from cache on http errors. */
export function $mol_offline_web() {

if( typeof window === 'undefined' ) {
export class $mol_offline_web extends $mol_offline {
web_js() { return 'web.js' }

blacklist = new Set([
'//cse.google.com/adsense/search/async-ads.js'
])

in_worker() { return typeof window === 'undefined' }

is_supported() {
if( location.protocol !== 'https:' && location.hostname !== 'localhost' ) {
console.warn( 'HTTPS or localhost is required for service workers.' )
return false
}

self.addEventListener( 'install' , ( event : any )=> {
;( self as any ).skipWaiting()
} )

self.addEventListener( 'activate' , ( event : any )=> {

// caches.delete( '$mol_offline' )

;( self as any ).clients.claim()

$$.$mol_log3_done({
place: '$mol_offline',
message: 'Activated',
})

} )

self.addEventListener( 'fetch' , ( event : any )=> {

const request = event.request as Request

if( blacklist.has( request.url.replace( /^https?:/, '' ) ) ) {
return event.respondWith(
new Response(
null,
{
status: 418,
statusText: 'Blocked'
},
)
)
}

if( request.method !== 'GET' ) return
if( !/^https?:/.test( request.url ) ) return
if( /\?/.test( request.url ) ) return
if( request.cache === 'no-store' ) return

const fetch_data = () => fetch( request ).then( response => {
if (response.status !== 200) return response
event.waitUntil(
caches.open( '$mol_offline' ).then(
cache => cache.put( request , response )
)
)

return response.clone()
} )

const fresh = request.cache === 'force-cache' ? null : fetch_data()

if (fresh) event.waitUntil( fresh )

event.respondWith(
caches.match( request ).then(
cached => request.cache === 'no-cache' || request.cache === 'reload'
? ( cached
? fresh!
.then(actual => {
if (actual.status === cached.status) return actual
throw new Error(
`${actual.status}${actual.statusText ? ` ${actual.statusText}` : ''}`,
{ cause: actual }
)
})
.catch((err: Error) => {
const cloned = cached.clone()
const message = `${err.cause instanceof Response ? '' : '500 '}${err.message} $mol_offline fallback to cache`
cloned.headers.set( '$mol_offline_remote_status', message )
return cloned
})
: fresh
)
: ( cached || fresh || fetch_data() )
if( ! navigator.serviceWorker ) {
console.warn( 'Service Worker is not supported.' )
return false
}

return true
}

protected _registration = null as null | Promise<ServiceWorkerRegistration>

registration() {
if (this._registration) return this._registration
if ( this.in_worker() ) return null
if ( ! this.is_supported() ) return null

window.addEventListener('message', this.window_message.bind(this))

navigator.serviceWorker.register(this.web_js())

return this._registration = navigator.serviceWorker.ready
}

window_message(e: MessageEvent) {
if (e.data === 'mol_build_obsolete') return this.send(e.data)
}

async send(data: unknown) {
try {
const reg = await this.registration()
reg?.active?.postMessage(data)
} catch (e) {
console.error(e)
}
}

override run() {
if (! this.registration()) {
this.worker()
return false
}

// const reg = await this.registration()
// reg?.addEventListener( 'updatefound', ()=> {
// const worker = reg.installing!
// worker.addEventListener( 'statechange', ()=> {
// if( worker.state !== 'activated' ) return
// window.location.reload()
// } )
// } )

return true
}

_worker = null as null | ServiceWorkerGlobalScope

worker() {
if (this._worker) return this._worker
const worker = this._worker = self as unknown as ServiceWorkerGlobalScope
// as unknown as NonNullable<typeof this['_worker']>
worker.addEventListener( 'beforeinstallprompt' , this.beforeinstallprompt.bind(this) )
zerkalica marked this conversation as resolved.
Show resolved Hide resolved
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))

return worker
}

message(event: ExtendableMessageEvent) {
if (event.data === 'mol_build_obsolete') this.ignore_cache = true
}

beforeinstallprompt(event: Event & { prompt?(): void }) {
event.prompt?.()
}

install(event: ExtendableEvent) { this.worker().skipWaiting() }

activate(event: ExtendableEvent) {
// caches.delete( '$mol_offline' )

this.worker().clients.claim()

$$.$mol_log3_done({
place: '$mol_offline',
message: 'Activated',
})
}

protected ignore_cache = false

fetch_event(event: FetchEvent) {
const request = event.request

if( this.blacklist.has( request.url.replace( /^https?:/, '' ) ) ) {
return event.respondWith(
new Response(
null,
{
status: 418,
statusText: 'Blocked'
},
)
)

})
}

if( request.method !== 'GET' ) return
if( !/^https?:/.test( request.url ) ) return
if( /\?/.test( request.url ) ) return
if( request.cache === 'no-store' ) return

const response = this.respond(event)
event.waitUntil( response )
event.respondWith( response )
zerkalica marked this conversation as resolved.
Show resolved Hide resolved
}

async respond(event: FetchEvent) {
const request = event.request
let cached
try {
cached = await caches.match( request )
} catch (e) {
console.error(e)
}

if ( ! cached) return this.fetch_and_cache(event)

if (request.cache === 'force-cache') return cached

if (this.ignore_cache || request.cache === 'no-cache' || request.cache === 'reload') {
zerkalica marked this conversation as resolved.
Show resolved Hide resolved
// fetch with fallback to cache if statuses not match
try {
const actual = await this.fetch_and_cache(event)
if (actual.status === cached.status) return actual
zerkalica marked this conversation as resolved.
Show resolved Hide resolved

throw new Error(
`${actual.status}${actual.statusText ? ` ${actual.statusText}` : ''}`,
{ cause: actual }
)

} catch (err) {
const message = `${(err as Error).cause instanceof Response ? '' : '500 '}${
(err as Error).message} $mol_offline fallback to cache`

const cloned = cached.clone()
cloned.headers.set( '$mol_offline_remote_status', message )

return cloned
}
}

return cached
}

async put_cache(request: Request, response: Response) {
const cache = await caches.open( '$mol_offline' )
return cache.put( request , response )
}

async fetch_and_cache(event: FetchEvent) {
const request = event.request
const response = await fetch( request )
if (response.status === 200) {
event.waitUntil(this.put_cache(request, response.clone()))
}

self.addEventListener( 'beforeinstallprompt' , ( event : any )=> event.prompt() )

} else if( location.protocol !== 'https:' && location.hostname !== 'localhost' ) {
console.warn( 'HTTPS or localhost is required for service workers.' )
} else if( !navigator.serviceWorker ) {
console.warn( 'Service Worker is not supported.' )
} else {
navigator.serviceWorker.register( 'web.js' ).then( reg => {
// reg.addEventListener( 'updatefound', ()=> {
// const worker = reg.installing!
// worker.addEventListener( 'statechange', ()=> {
// if( worker.state !== 'activated' ) return
// window.location.reload()
// } )
// } )
} )
return response
}

}
Expand Down
Loading