openvibers/openvibe.games Commits
Games serves the shared page shell's web app manifest and touch icon (/steam, /updates and world pages logged a manifest 404) (#143)
Changes
@@@ -40,6 +40,24 @@ describe('/shared (D42)', () => {expect(r.status).toBe(304)})++ it('serves the web app manifest and touch icon the shared page shell links (/steam, /updates, world pages)', async () => {++ const shell = req('openvibe-shared/serve') as { manifestUrl(s: string): string | null }++ const url = shell.manifestUrl('games')++ expect(url).toMatch(/^\/shared\/manifest\/games\.webmanifest\?v=/)++ const r = await fetch(base + url)++ expect(r.status).toBe(200)++ expect(r.headers.get('content-type')).toMatch(/manifest\+json/)++ expect(r.headers.get('cache-control')).toMatch(/immutable/)++ const m = (await r.json()) as { name?: string; icons?: { src: string }[] }++ expect(m.name).toBeTruthy()++ const icon = m.icons?.[0]?.src ?? ''++ expect(icon).toMatch(/^\/shared\/icons\//)++ const ir = await fetch(base + icon)++ expect(ir.status).toBe(200)++ expect(ir.headers.get('content-type')).toMatch(/image\/png/)++ expect((await fetch(`${base}/shared/manifest/no-such-site.webmanifest`)).status).toBe(404)++ })++it('serves only the browser files openvibe-shared lists', async () => {for (const p of ['/shared/serve.js',@@@ -16,17 +16,28 @@ interface SharedFiles {}interface SharedServe {hashOf(name: string): string++ handler(): (req: IncomingMessage & { path: string }, res: ServerResponse, next: (err?: unknown) => void) => void}export function sharedAssetsHandler(req: NodeRequire = createRequire(import.meta.url)) {const files = req('openvibe-shared/files') as SharedFilesconst serve = req('openvibe-shared/serve') as SharedServe++ const shared = serve.handler()return (rq: IncomingMessage, res: ServerResponse): boolean => {if (rq.method !== 'GET' && rq.method !== 'HEAD') return falseconst url = new URL(rq.url ?? '/', 'http://games.local')if (!url.pathname.startsWith('/shared/')) return falseconst name = url.pathname.slice('/shared/'.length)-- if (!files.isBrowserFile(name)) return false++ // The rest openvibe-shared serves goes to its own handler: the web app manifest and touch icon its page shell links++ // on /steam, /updates and world pages (manifest/games.webmanifest, icons/games-192.png), and the social cards.++ if (!files.isBrowserFile(name)) {++ let handled = true++ shared(Object.assign(rq, { path: `/${name}` }), res, (err) => {++ if (err) res.destroy()++ else handled = false++ })++ return handled++ }const hash = serve.hashOf(name)const headers = {'content-type': 'application/javascript; charset=utf-8',
@@ -40,6 +40,24 @@ describe('/shared (D42)', () => {
@@ -40,6 +40,24 @@ describe('/shared (D42)', () => {
expect(r.status).toBe(304)
expect(r.status).toBe(304)
})
})
+ it('serves the web app manifest and touch icon the shared page shell links (/steam, /updates, world pages)', async () => {
+ const shell = req('openvibe-shared/serve') as { manifestUrl(s: string): string | null }
+ const url = shell.manifestUrl('games')
+ expect(url).toMatch(/^\/shared\/manifest\/games\.webmanifest\?v=/)
+ const r = await fetch(base + url)
+ expect(r.status).toBe(200)
+ expect(r.headers.get('content-type')).toMatch(/manifest\+json/)
+ expect(r.headers.get('cache-control')).toMatch(/immutable/)
+ const m = (await r.json()) as { name?: string; icons?: { src: string }[] }
+ expect(m.name).toBeTruthy()
+ const icon = m.icons?.[0]?.src ?? ''
+ expect(icon).toMatch(/^\/shared\/icons\//)
+ const ir = await fetch(base + icon)
+ expect(ir.status).toBe(200)
+ expect(ir.headers.get('content-type')).toMatch(/image\/png/)
+ expect((await fetch(`${base}/shared/manifest/no-such-site.webmanifest`)).status).toBe(404)
+ })
+
it('serves only the browser files openvibe-shared lists', async () => {
it('serves only the browser files openvibe-shared lists', async () => {
for (const p of [
for (const p of [
'/shared/serve.js',
'/shared/serve.js',
@@ -16,17 +16,28 @@ interface SharedFiles {
@@ -16,17 +16,28 @@ interface SharedFiles {
}
}
interface SharedServe {
interface SharedServe {
hashOf(name: string): string
hashOf(name: string): string
+ handler(): (req: IncomingMessage & { path: string }, res: ServerResponse, next: (err?: unknown) => void) => void
}
}
export function sharedAssetsHandler(req: NodeRequire = createRequire(import.meta.url)) {
export function sharedAssetsHandler(req: NodeRequire = createRequire(import.meta.url)) {
const files = req('openvibe-shared/files') as SharedFiles
const files = req('openvibe-shared/files') as SharedFiles
const serve = req('openvibe-shared/serve') as SharedServe
const serve = req('openvibe-shared/serve') as SharedServe
+ const shared = serve.handler()
return (rq: IncomingMessage, res: ServerResponse): boolean => {
return (rq: IncomingMessage, res: ServerResponse): boolean => {
if (rq.method !== 'GET' && rq.method !== 'HEAD') return false
if (rq.method !== 'GET' && rq.method !== 'HEAD') return false
const url = new URL(rq.url ?? '/', 'http://games.local')
const url = new URL(rq.url ?? '/', 'http://games.local')
if (!url.pathname.startsWith('/shared/')) return false
if (!url.pathname.startsWith('/shared/')) return false
const name = url.pathname.slice('/shared/'.length)
const name = url.pathname.slice('/shared/'.length)
- if (!files.isBrowserFile(name)) return false
+ // The rest openvibe-shared serves goes to its own handler: the web app manifest and touch icon its page shell links
+ // on /steam, /updates and world pages (manifest/games.webmanifest, icons/games-192.png), and the social cards.
+ if (!files.isBrowserFile(name)) {
+ let handled = true
+ shared(Object.assign(rq, { path: `/${name}` }), res, (err) => {
+ if (err) res.destroy()
+ else handled = false
+ })
+ return handled
+ }
const hash = serve.hashOf(name)
const hash = serve.hashOf(name)
const headers = {
const headers = {
'content-type': 'application/javascript; charset=utf-8',
'content-type': 'application/javascript; charset=utf-8',