Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@
"vite-plugin-pwa": "^1.3.0",
"vite-plugin-singlefile": "^2.3.3",
"vitest": "^4.1.7",
"workbox-cacheable-response": "^7.4.1",
"workbox-expiration": "^7.4.1",
"workbox-precaching": "^7.4.1",
"workbox-routing": "^7.4.1",
"workbox-strategies": "^7.4.1",
"workbox-window": "^7.4.1",
"@base-ui/react": "^1.5.0",
"@dnd-kit/core": "^6.3.1",
Expand Down
53 changes: 53 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions src/sw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/// <reference lib="webworker" />
import { cleanupOutdatedCaches, precacheAndRoute } from "workbox-precaching"
import { registerRoute } from "workbox-routing"
import { NetworkFirst } from "workbox-strategies"
import { ExpirationPlugin } from "workbox-expiration"
import { CacheableResponsePlugin } from "workbox-cacheable-response"
import { isPrivateOrLocalHost } from "@/lib/openapi/url-guard"

declare const self: ServiceWorkerGlobalScope & {
__WB_MANIFEST: Array<{ url: string; revision: string | null }>
}

cleanupOutdatedCaches()
precacheAndRoute(self.__WB_MANIFEST)

// Only cache cross-origin *public* specs. Same-origin specs may be
// cookie/session-authenticated, and Authorization-bearing requests are
// excluded outright. Private/local network hosts are skipped so the fetch
// falls through to the page context, where Chrome can show the Private
// Network Access permission prompt.
registerRoute(
({ request, url, sameOrigin }) => {
if (sameOrigin || request.headers.has("authorization")) return false
if (isPrivateOrLocalHost(url.hostname)) return false
return /(?:^|\/)(?:spec|openapi|swagger|asyncapi)[^/]*\.(json|ya?ml)$/i.test(url.pathname)
},
new NetworkFirst({
cacheName: "api-specs",
plugins: [
new ExpirationPlugin({ maxEntries: 20, maxAgeSeconds: 7 * 24 * 60 * 60 }),
new CacheableResponsePlugin({ statuses: [200] }),
],
}),
)

self.addEventListener("message", (event) => {
if (event.data?.type === "SKIP_WAITING") {
self.skipWaiting()
}
})
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@
}
},
"include": ["src", "cli/src"],
"exclude": ["src/components/animate-ui/**"]
"exclude": ["src/components/animate-ui/**", "src/sw.ts"]
}
24 changes: 4 additions & 20 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,13 @@ export default defineConfig(({ mode }) => {
tailwindcss(),
cspPlugin(),
VitePWA({
strategies: "injectManifest",
srcDir: "src",
filename: "sw.ts",
registerType: "prompt",
workbox: {
injectManifest: {
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
globPatterns: ["**/*.{js,css,html,svg}"],
runtimeCaching: [
{
// Only cache cross-origin *public* specs. Same-origin specs may be
// cookie/session-authenticated (the Cookie header isn't visible to JS,
// so we can't filter on it), and Authorization-bearing requests are
// excluded outright. Caching a protected doc could expose it to a later,
// unauthenticated reader on a shared machine. Only cache 200s.
urlPattern: ({ request, url, sameOrigin }: { request: Request; url: URL; sameOrigin: boolean }) =>
!sameOrigin
&& !request.headers.has("authorization")
&& /(?:^|\/)(?:spec|openapi|swagger|asyncapi)[^/]*\.(json|ya?ml)$/i.test(url.pathname),
handler: "NetworkFirst",
options: {
cacheName: "api-specs",
expiration: { maxEntries: 20, maxAgeSeconds: 7 * 24 * 60 * 60 },
cacheableResponse: { statuses: [200] },
},
},
],
},
manifest: {
name: "Apilot",
Expand Down
Loading