[READ-ONLY] Mirror of https://github.com/danielroe/cross-origin-storage. Load shared dependencies from Cross-Origin Storage (COS).
cross-origin-storage
experimental
nuxt
vite
vite-plugin
1declare global {
2 interface Navigator {
3 crossOriginStorage?: {
4 requestFileHandles: (
5 descriptors: Array<{ algorithm: string, value: string }>,
6 options?: { create?: boolean },
7 ) => Promise<Array<{
8 getFile: () => Promise<File>
9 createWritable: () => Promise<{
10 write: (data: Blob) => Promise<void>
11 close: () => Promise<void>
12 }>
13 }>>
14 }
15 }
16}
17
18export interface CosManifest {
19 /** Public base path that managed chunks are served from, e.g. `/_nuxt/`. */
20 base: string
21 /** Bare specifier of the entry chunk to import once the import map is ready. */
22 entry: string
23 /** Map of bare specifier to `{ file, hash }` for every managed chunk. */
24 chunks: Record<string, { file: string, hash: string }>
25}
26
27export async function runCosLoader(manifest: CosManifest): Promise<void> {
28 const cos = navigator.crossOriginStorage
29 const imports: Record<string, string> = {}
30
31 async function resolveChunk(hash: string, file: string): Promise<string> {
32 if (cos) {
33 try {
34 const [handle] = await cos.requestFileHandles([{ algorithm: 'SHA-256', value: hash }])
35 if (handle) {
36 const blob = await handle.getFile()
37 return URL.createObjectURL(new Blob([blob], { type: 'text/javascript' }))
38 }
39 }
40 catch (error) {
41 if ((error as Error)?.name !== 'NotFoundError') {
42 console.error('[cos] lookup failed', error)
43 }
44 }
45 }
46
47 const response = await fetch(file)
48 const blob = new Blob([await response.blob()], { type: 'text/javascript' })
49
50 if (cos) {
51 try {
52 const [handle] = await cos.requestFileHandles([{ algorithm: 'SHA-256', value: hash }], { create: true })
53 if (handle) {
54 const writable = await handle.createWritable()
55 await writable.write(blob)
56 await writable.close()
57 }
58 }
59 catch (error) {
60 console.error('[cos] store failed', error)
61 }
62 }
63
64 return URL.createObjectURL(blob)
65 }
66
67 await Promise.all(
68 Object.entries(manifest.chunks).map(async ([specifier, { file, hash }]) => {
69 imports[specifier] = await resolveChunk(hash, manifest.base + file)
70 }),
71 )
72
73 const script = document.createElement('script')
74 script.type = 'importmap'
75 script.textContent = JSON.stringify({ imports })
76 document.head.appendChild(script)
77
78 await new Promise(resolve => setTimeout(resolve, 0))
79 await import(/* @vite-ignore */ manifest.entry)
80}