@wells-nelson/build-tools 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/service-worker.d.ts +5 -2
- package/dist/service-worker.js +5 -2
- package/dist/vite.d.ts +22 -0
- package/dist/vite.js +38 -0
- package/package.json +1 -1
- package/templates/deploy-pages.yml +3 -0
package/README.md
CHANGED
|
@@ -36,6 +36,17 @@ export default defineConfig(({ mode, command }) => ({
|
|
|
36
36
|
}));
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
### Content Security Policy
|
|
40
|
+
|
|
41
|
+
`contentSecurityPolicy({ supabaseUrl })` returns SvelteKit `csp` options: hashes for SvelteKit's inline
|
|
42
|
+
scripts and no other scripts, network access only to the app and its Supabase project (REST and
|
|
43
|
+
Realtime), images from the app, `data:`/`blob:` and Google profile pictures. Pass `connect` / `img` for
|
|
44
|
+
more sources.
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
sveltekit({ csp: contentSecurityPolicy({ supabaseUrl: env.VITE_SUPABASE_URL }), ... })
|
|
48
|
+
```
|
|
49
|
+
|
|
39
50
|
## Service worker (`@wells-nelson/build-tools/service-worker`)
|
|
40
51
|
|
|
41
52
|
```ts
|
|
@@ -53,6 +64,10 @@ offline they fall back to the cached shell. A new worker skips waiting and takes
|
|
|
53
64
|
pages. `networkOnly` paths are never cached (default: SvelteKit's `_app/version.json`, so
|
|
54
65
|
`updated` from `$app/state` notices new deploys). `dev` caches nothing.
|
|
55
66
|
|
|
67
|
+
On activate a worker deletes only its own older caches (`<cachePrefix>-cache-*`). Give each app
|
|
68
|
+
on an origin its own `cachePrefix`: GitHub Pages project sites of one user all share
|
|
69
|
+
`https://<user>.github.io`.
|
|
70
|
+
|
|
56
71
|
## Templates
|
|
57
72
|
|
|
58
73
|
`templates/deploy-pages.yml` (replace `__REPO__`) and `templates/manifest.webmanifest`.
|
package/dist/service-worker.d.ts
CHANGED
|
@@ -29,13 +29,16 @@ export interface ServiceWorkerScopeLike {
|
|
|
29
29
|
}
|
|
30
30
|
export interface AppShellCacheOptions {
|
|
31
31
|
sw: ServiceWorkerScopeLike;
|
|
32
|
-
/** Build version; each deploy gets a fresh cache and old ones are deleted. */
|
|
32
|
+
/** Build version; each deploy gets a fresh cache and this app's old ones are deleted. */
|
|
33
33
|
version: string;
|
|
34
34
|
/** Files to precache, relative to the scope (SvelteKit's `immutable` and `assets`). */
|
|
35
35
|
files: Array<string | {
|
|
36
36
|
path: string;
|
|
37
37
|
}>;
|
|
38
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Cache name prefix (default 'app'). Give every app on the same origin its own prefix: a worker
|
|
40
|
+
* only deletes caches named `<cachePrefix>-cache-*`, so apps sharing an origin keep each other's.
|
|
41
|
+
*/
|
|
39
42
|
cachePrefix?: string;
|
|
40
43
|
/**
|
|
41
44
|
* Development: cache nothing and delete what an earlier worker cached, so every code change
|
package/dist/service-worker.js
CHANGED
|
@@ -44,9 +44,12 @@ export function registerAppShellCache({ sw, version, files, cachePrefix = 'app',
|
|
|
44
44
|
});
|
|
45
45
|
sw.addEventListener('activate', (event) => {
|
|
46
46
|
event.waitUntil((async () => {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
// Only this app's older caches: other apps on the same origin (e.g. two GitHub Pages
|
|
48
|
+
// project sites under one user) keep theirs.
|
|
49
|
+
for (const key of await caches.keys()) {
|
|
50
|
+
if (key !== CACHE && key.startsWith(`${cachePrefix}-cache-`))
|
|
49
51
|
await caches.delete(key);
|
|
52
|
+
}
|
|
50
53
|
await sw.clients.claim();
|
|
51
54
|
})());
|
|
52
55
|
});
|
package/dist/vite.d.ts
CHANGED
|
@@ -44,3 +44,25 @@ export declare function devJsonModule({ id, file, enabled }: {
|
|
|
44
44
|
file: string;
|
|
45
45
|
enabled: boolean;
|
|
46
46
|
}): Plugin;
|
|
47
|
+
/**
|
|
48
|
+
* SvelteKit `csp` options for an offline-first app on static hosting. SvelteKit adds a
|
|
49
|
+
* `<meta http-equiv="content-security-policy">` with hashes of its own inline scripts, so only
|
|
50
|
+
* the app's bundled scripts run: injected markup can't load or run other scripts. Network access
|
|
51
|
+
* is limited to the app's origin and its Supabase project (REST and Realtime); images also allow
|
|
52
|
+
* data/blob URLs and Google profile pictures. Styles allow inline (Svelte transitions use them).
|
|
53
|
+
*
|
|
54
|
+
* ```js
|
|
55
|
+
* sveltekit({ csp: contentSecurityPolicy({ supabaseUrl: env.VITE_SUPABASE_URL }), ... })
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function contentSecurityPolicy({ supabaseUrl, connect, img }?: {
|
|
59
|
+
/** The project URL (`VITE_SUPABASE_URL`); without it any `*.supabase.co` project is allowed. */
|
|
60
|
+
supabaseUrl?: string;
|
|
61
|
+
/** Extra `connect-src` sources. */
|
|
62
|
+
connect?: string[];
|
|
63
|
+
/** Extra `img-src` sources. */
|
|
64
|
+
img?: string[];
|
|
65
|
+
}): {
|
|
66
|
+
mode: "hash";
|
|
67
|
+
directives: Record<string, any[]>;
|
|
68
|
+
};
|
package/dist/vite.js
CHANGED
|
@@ -80,3 +80,41 @@ export function devJsonModule({ id, file, enabled }) {
|
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* SvelteKit `csp` options for an offline-first app on static hosting. SvelteKit adds a
|
|
85
|
+
* `<meta http-equiv="content-security-policy">` with hashes of its own inline scripts, so only
|
|
86
|
+
* the app's bundled scripts run: injected markup can't load or run other scripts. Network access
|
|
87
|
+
* is limited to the app's origin and its Supabase project (REST and Realtime); images also allow
|
|
88
|
+
* data/blob URLs and Google profile pictures. Styles allow inline (Svelte transitions use them).
|
|
89
|
+
*
|
|
90
|
+
* ```js
|
|
91
|
+
* sveltekit({ csp: contentSecurityPolicy({ supabaseUrl: env.VITE_SUPABASE_URL }), ... })
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export function contentSecurityPolicy({ supabaseUrl, connect = [], img = [] } = {}) {
|
|
95
|
+
let supabase = ['https://*.supabase.co', 'wss://*.supabase.co'];
|
|
96
|
+
if (supabaseUrl) {
|
|
97
|
+
try {
|
|
98
|
+
const { host, protocol } = new URL(supabaseUrl);
|
|
99
|
+
supabase = [`${protocol}//${host}`, `${protocol === 'http:' ? 'ws' : 'wss'}://${host}`];
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
/* keep the wildcard */
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Typed loosely (`any[]`) so it fits SvelteKit's `CspDirectives` without depending on SvelteKit.
|
|
106
|
+
const directives = {
|
|
107
|
+
'default-src': ['self'],
|
|
108
|
+
'script-src': ['self'],
|
|
109
|
+
'style-src': ['self', 'unsafe-inline'],
|
|
110
|
+
'img-src': ['self', 'data:', 'blob:', 'https://*.googleusercontent.com', ...img],
|
|
111
|
+
'font-src': ['self'],
|
|
112
|
+
'connect-src': ['self', ...supabase, ...connect],
|
|
113
|
+
'worker-src': ['self'],
|
|
114
|
+
'manifest-src': ['self'],
|
|
115
|
+
'object-src': ['none'],
|
|
116
|
+
'base-uri': ['self'],
|
|
117
|
+
'form-action': ['self']
|
|
118
|
+
};
|
|
119
|
+
return { mode: 'hash', directives };
|
|
120
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wells-nelson/build-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Build and deployment tooling for Wells Nelson SvelteKit PWAs: postbuild, PWA icon generator, Vite config pieces, service worker, and templates.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,6 +26,9 @@ jobs:
|
|
|
26
26
|
node-version: 24
|
|
27
27
|
cache: npm
|
|
28
28
|
- run: npm ci
|
|
29
|
+
# Nothing ships unless it type-checks and its tests pass.
|
|
30
|
+
- run: npm run check --if-present
|
|
31
|
+
- run: npm test --if-present
|
|
29
32
|
- run: npm run build
|
|
30
33
|
env:
|
|
31
34
|
# Repository variables (Settings > Secrets and variables > Actions > Variables).
|