@kazzle/app 0.1.800 → 0.1.802
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 +4 -2
- package/dist/pwa.d.ts +15 -0
- package/dist/pwa.js +21 -0
- package/dist/vite.d.ts +19 -2
- package/dist/vite.js +106 -8
- package/package.json +9 -1
- package/templates/ai-app/components/ui/src/main.tsx +3 -0
- package/templates/ai-app/components/ui/vite.config.ts +5 -5
- package/templates/realtime-app/components/ui/package.json +1 -3
- package/templates/realtime-app/components/ui/src/main.tsx +3 -6
- package/templates/realtime-app/components/ui/src/vite-env.d.ts +0 -1
- package/templates/realtime-app/components/ui/vite.config.ts +8 -54
- package/templates/ui-app/components/ui/package.json +1 -3
- package/templates/ui-app/components/ui/src/main.tsx +5 -10
- package/templates/ui-app/components/ui/src/vite-env.d.ts +1 -0
- package/templates/ui-app/components/ui/vite.config.ts +9 -61
- package/templates/ui-db-app/components/ui/src/main.tsx +3 -0
- package/templates/ui-db-app/components/ui/vite.config.ts +5 -5
- package/templates/realtime-app/bun.lock +0 -887
- package/templates/ui-app/components/ui/bun.lock +0 -894
package/README.md
CHANGED
|
@@ -17,7 +17,8 @@ the AI stale types. This package replaces the copies with real imports:
|
|
|
17
17
|
```ts
|
|
18
18
|
import { defineConfig } from '@kazzle/app';
|
|
19
19
|
import { defineTools } from '@kazzle/app/tools';
|
|
20
|
-
import { kazzleAppVite } from '@kazzle/app/vite';
|
|
20
|
+
import { kazzleAppVite, kazzleAppPwa } from '@kazzle/app/vite';
|
|
21
|
+
import { registerAppSW } from '@kazzle/app/pwa';
|
|
21
22
|
```
|
|
22
23
|
|
|
23
24
|
## Subpath exports — keep them separate
|
|
@@ -31,7 +32,8 @@ and the root export must stay tiny and dependency-free.
|
|
|
31
32
|
| `@kazzle/app` | `defineConfig`, `KazzleConfig` + component/trigger/env types | none |
|
|
32
33
|
| `@kazzle/app/tools` | `defineTools`, `KazzleTool` types, Zod input helper types | `zod` (peer, type-only) |
|
|
33
34
|
| `@kazzle/app/client` | install-scoped client: per-install secrets, sibling component URLs | none |
|
|
34
|
-
| `@kazzle/app/vite` | `kazzleAppVite`
|
|
35
|
+
| `@kazzle/app/vite` | `kazzleAppVite` + `kazzleAppPwa` (HOST/PORT + production SW plugins) | `vite` (peer), `vite-plugin-pwa` |
|
|
36
|
+
| `@kazzle/app/pwa` | `registerAppSW()` browser registration (owns `virtual:pwa-register`) | none (virtual module at build) |
|
|
35
37
|
| `@kazzle/app/templates` | programmatic template manifest + asset readers (server/generator only) | none |
|
|
36
38
|
| `@kazzle/app/migrations` | versioned app upgrade metadata + codemods (server/CLI only) | none |
|
|
37
39
|
|
package/dist/pwa.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type RegisterAppSWOptions = {
|
|
2
|
+
immediate?: boolean;
|
|
3
|
+
onNeedRefresh?: () => void;
|
|
4
|
+
onOfflineReady?: () => void;
|
|
5
|
+
onRegistered?: (registration: ServiceWorkerRegistration | undefined) => void;
|
|
6
|
+
onRegisterError?: (error: unknown) => void;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Register the production service worker. No-op in dev when
|
|
10
|
+
* `kazzleAppPwa()` left `devOptions.enabled: false` (required).
|
|
11
|
+
*
|
|
12
|
+
* `immediate: true` by default so autoUpdate can claim clients as soon as a
|
|
13
|
+
* new worker is waiting.
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerAppSW(options?: RegisterAppSWOptions): (reloadPage?: boolean) => Promise<void>;
|
package/dist/pwa.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// @kazzle/app/pwa — browser service-worker registration for Kazzle UI apps.
|
|
2
|
+
//
|
|
3
|
+
// Published apps need a service worker with autoUpdate so users pick up new
|
|
4
|
+
// builds without a hard refresh. Templates MUST call `registerAppSW()` from
|
|
5
|
+
// this module and MUST NOT import `virtual:pwa-register` themselves — the
|
|
6
|
+
// virtual module + its types are owned here so a missing vite-env.d.ts cannot
|
|
7
|
+
// break `appdev.check` (TS2307) on every fresh create.
|
|
8
|
+
//
|
|
9
|
+
// Pair with `kazzleAppPwa()` from `@kazzle/app/vite` in vite.config.ts.
|
|
10
|
+
// Never enable the PWA plugin's `devOptions` — a SW in dev breaks Vite HMR.
|
|
11
|
+
import { registerSW } from 'virtual:pwa-register';
|
|
12
|
+
/**
|
|
13
|
+
* Register the production service worker. No-op in dev when
|
|
14
|
+
* `kazzleAppPwa()` left `devOptions.enabled: false` (required).
|
|
15
|
+
*
|
|
16
|
+
* `immediate: true` by default so autoUpdate can claim clients as soon as a
|
|
17
|
+
* new worker is waiting.
|
|
18
|
+
*/
|
|
19
|
+
export function registerAppSW(options = {}) {
|
|
20
|
+
return registerSW({ immediate: true, ...options });
|
|
21
|
+
}
|
package/dist/vite.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { UserConfig } from 'vite';
|
|
1
|
+
import type { Plugin, UserConfig } from 'vite';
|
|
2
|
+
import { type VitePWAOptions } from 'vite-plugin-pwa';
|
|
2
3
|
export interface KazzleAppViteOptions {
|
|
3
4
|
/**
|
|
4
5
|
* Name of the sibling process component that owns this app's backend/API
|
|
@@ -18,12 +19,28 @@ export interface KazzleAppViteOptions {
|
|
|
18
19
|
*/
|
|
19
20
|
proxyPaths?: string[];
|
|
20
21
|
}
|
|
22
|
+
export type KazzleAppPwaOptions = {
|
|
23
|
+
/** Merged over Kazzle's default workbox precache/runtime config. */
|
|
24
|
+
workbox?: VitePWAOptions['workbox'];
|
|
25
|
+
/** Optional web app manifest. Omit for a SW-only install (no install prompt). */
|
|
26
|
+
manifest?: VitePWAOptions['manifest'];
|
|
27
|
+
includeAssets?: VitePWAOptions['includeAssets'];
|
|
28
|
+
};
|
|
21
29
|
/**
|
|
22
30
|
* Kazzle preview/runtime Vite config. Spread the result into your Vite config:
|
|
23
31
|
*
|
|
24
32
|
* export default defineConfig(({ command }) => ({
|
|
25
|
-
* plugins: [react(), tailwindcss()],
|
|
33
|
+
* plugins: [react(), tailwindcss(), ...kazzleAppPwa()],
|
|
26
34
|
* ...kazzleAppVite(command, { apiComponent: 'server', proxyPaths: ['/todos'] }),
|
|
27
35
|
* }));
|
|
28
36
|
*/
|
|
29
37
|
export declare function kazzleAppVite(command: string, options?: KazzleAppViteOptions): UserConfig;
|
|
38
|
+
/**
|
|
39
|
+
* Kazzle production PWA plugins. Spread into `plugins` next to react/tailwind:
|
|
40
|
+
*
|
|
41
|
+
* plugins: [react(), tailwindcss(), ...kazzleAppPwa()]
|
|
42
|
+
*
|
|
43
|
+
* Always pair with `registerAppSW()` from `@kazzle/app/pwa` in main.tsx.
|
|
44
|
+
* Do not import `vite-plugin-pwa` or `virtual:pwa-register` in the app.
|
|
45
|
+
*/
|
|
46
|
+
export declare function kazzleAppPwa(options?: KazzleAppPwaOptions): Plugin[];
|
package/dist/vite.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// @kazzle/app/vite — Kazzle preview/runtime Vite
|
|
1
|
+
// @kazzle/app/vite — Kazzle preview/runtime Vite helpers.
|
|
2
2
|
//
|
|
3
|
-
// BUILD-TIME ONLY. This module imports vite
|
|
4
|
-
// component's vite.config.ts. Never import it from process/runtime
|
|
5
|
-
// subpath split exists so process apps don't pull vite into their
|
|
6
|
-
// graph. See README.md.
|
|
3
|
+
// BUILD-TIME ONLY. This module imports vite (+ vite-plugin-pwa) and is meant
|
|
4
|
+
// for a UI component's vite.config.ts. Never import it from process/runtime
|
|
5
|
+
// code — the subpath split exists so process apps don't pull vite into their
|
|
6
|
+
// dependency graph. See README.md.
|
|
7
7
|
//
|
|
8
8
|
// READ THIS BEFORE TOUCHING vite.config.ts (human or AI):
|
|
9
9
|
// Kazzle assigns every UI component a specific HOST and PORT at preview/run
|
|
@@ -12,8 +12,19 @@
|
|
|
12
12
|
// 5173) the tunnel gets ECONNREFUSED and the preview looks broken. This helper
|
|
13
13
|
// is the ONE place that reads HOST/PORT and pins Vite to them (strictPort).
|
|
14
14
|
//
|
|
15
|
-
// Every UI component's vite.config.ts MUST
|
|
16
|
-
//
|
|
15
|
+
// Every UI component's vite.config.ts MUST:
|
|
16
|
+
// 1. Spread `...kazzleAppVite(command)` — never hand-write server.host/port.
|
|
17
|
+
// 2. Spread `...kazzleAppPwa()` into plugins — never import vite-plugin-pwa
|
|
18
|
+
// or `virtual:pwa-register` in the app. Browser entry calls
|
|
19
|
+
// `registerAppSW()` from `@kazzle/app/pwa`.
|
|
20
|
+
//
|
|
21
|
+
// Keep this file self-contained (no relative imports). Vite loads the
|
|
22
|
+
// published entry under Node ESM, which requires explicit .js extensions for
|
|
23
|
+
// relatives — a single file avoids that footgun.
|
|
24
|
+
import { createRequire } from 'node:module';
|
|
25
|
+
import { VitePWA } from 'vite-plugin-pwa';
|
|
26
|
+
const requireFromSdk = createRequire(import.meta.url);
|
|
27
|
+
const DEFAULT_PWA_GLOB_PATTERNS = ['**/*.{js,css,html,ico,png,svg,woff2}'];
|
|
17
28
|
function componentUrlEnvName(componentName) {
|
|
18
29
|
const normalized = componentName.toUpperCase().replace(/[^A-Z0-9]+/g, '_').replace(/^_|_$/g, '');
|
|
19
30
|
return `KAZZLE_APP_COMPONENT_${normalized}_URL`;
|
|
@@ -22,7 +33,7 @@ function componentUrlEnvName(componentName) {
|
|
|
22
33
|
* Kazzle preview/runtime Vite config. Spread the result into your Vite config:
|
|
23
34
|
*
|
|
24
35
|
* export default defineConfig(({ command }) => ({
|
|
25
|
-
* plugins: [react(), tailwindcss()],
|
|
36
|
+
* plugins: [react(), tailwindcss(), ...kazzleAppPwa()],
|
|
26
37
|
* ...kazzleAppVite(command, { apiComponent: 'server', proxyPaths: ['/todos'] }),
|
|
27
38
|
* }));
|
|
28
39
|
*/
|
|
@@ -75,3 +86,90 @@ export function kazzleAppVite(command, options = {}) {
|
|
|
75
86
|
},
|
|
76
87
|
};
|
|
77
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Dev-only: a browser that previously visited a built/PWA version of this app
|
|
91
|
+
* at the same origin still has a service worker. With `devOptions.enabled:
|
|
92
|
+
* false` we don't serve a SW in dev, so the stale SW keeps intercepting with
|
|
93
|
+
* its old precache. This plugin serves a tiny unregister script at /sw.js and
|
|
94
|
+
* /registerSW.js during `vite serve` only.
|
|
95
|
+
*/
|
|
96
|
+
function devUnregisterServiceWorker() {
|
|
97
|
+
const body = [
|
|
98
|
+
"self.addEventListener('install', e => self.skipWaiting());",
|
|
99
|
+
"self.addEventListener('activate', e => e.waitUntil((async () => {",
|
|
100
|
+
" try { await self.registration.unregister(); } catch {}",
|
|
101
|
+
" try { const keys = await caches.keys(); await Promise.all(keys.map(k => caches.delete(k))); } catch {}",
|
|
102
|
+
" const clients = await self.clients.matchAll({ type: 'window' });",
|
|
103
|
+
" for (const c of clients) { try { c.navigate(c.url); } catch {} }",
|
|
104
|
+
"})());",
|
|
105
|
+
].join('\n');
|
|
106
|
+
return {
|
|
107
|
+
name: 'kazzle:dev-unregister-sw',
|
|
108
|
+
apply: 'serve',
|
|
109
|
+
configureServer(server) {
|
|
110
|
+
server.middlewares.use((req, res, next) => {
|
|
111
|
+
if (req.method !== 'GET')
|
|
112
|
+
return next();
|
|
113
|
+
const url = req.url?.split('?')[0];
|
|
114
|
+
if (url !== '/sw.js' && url !== '/registerSW.js')
|
|
115
|
+
return next();
|
|
116
|
+
res.setHeader('content-type', 'application/javascript; charset=utf-8');
|
|
117
|
+
res.setHeader('cache-control', 'no-store');
|
|
118
|
+
res.end(body);
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Keep Vite from prebundling `@kazzle/app/pwa` — that entry imports
|
|
125
|
+
* `virtual:pwa-register`, which only exists after VitePWA's resolve hooks run.
|
|
126
|
+
* Also pin `workbox-window` to the SDK install so the app root does not need
|
|
127
|
+
* its own copy (vite-plugin-pwa's virtual register imports it).
|
|
128
|
+
*/
|
|
129
|
+
function excludeKazzlePwaFromOptimizeDeps() {
|
|
130
|
+
const workboxWindow = requireFromSdk.resolve('workbox-window');
|
|
131
|
+
return {
|
|
132
|
+
name: 'kazzle:exclude-pwa-optimize-deps',
|
|
133
|
+
config() {
|
|
134
|
+
return {
|
|
135
|
+
optimizeDeps: {
|
|
136
|
+
exclude: ['@kazzle/app/pwa'],
|
|
137
|
+
},
|
|
138
|
+
resolve: {
|
|
139
|
+
alias: {
|
|
140
|
+
'workbox-window': workboxWindow,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Kazzle production PWA plugins. Spread into `plugins` next to react/tailwind:
|
|
149
|
+
*
|
|
150
|
+
* plugins: [react(), tailwindcss(), ...kazzleAppPwa()]
|
|
151
|
+
*
|
|
152
|
+
* Always pair with `registerAppSW()` from `@kazzle/app/pwa` in main.tsx.
|
|
153
|
+
* Do not import `vite-plugin-pwa` or `virtual:pwa-register` in the app.
|
|
154
|
+
*/
|
|
155
|
+
export function kazzleAppPwa(options = {}) {
|
|
156
|
+
const workbox = options.workbox ?? {};
|
|
157
|
+
const pwaPlugins = VitePWA({
|
|
158
|
+
registerType: 'autoUpdate',
|
|
159
|
+
workbox: {
|
|
160
|
+
globPatterns: DEFAULT_PWA_GLOB_PATTERNS,
|
|
161
|
+
...workbox,
|
|
162
|
+
},
|
|
163
|
+
manifest: options.manifest,
|
|
164
|
+
includeAssets: options.includeAssets,
|
|
165
|
+
// Keep disabled in dev — enabling the SW breaks Vite HMR.
|
|
166
|
+
devOptions: {
|
|
167
|
+
enabled: false,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
return [
|
|
171
|
+
excludeKazzlePwaFromOptimizeDeps(),
|
|
172
|
+
devUnregisterServiceWorker(),
|
|
173
|
+
...(Array.isArray(pwaPlugins) ? pwaPlugins : [pwaPlugins]),
|
|
174
|
+
];
|
|
175
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kazzle/app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.802",
|
|
4
4
|
"description": "Contracts, tool helpers, Vite helper, and templates for building Kazzle apps.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
"types": "./dist/vite.d.ts",
|
|
29
29
|
"import": "./dist/vite.js"
|
|
30
30
|
},
|
|
31
|
+
"./pwa": {
|
|
32
|
+
"types": "./dist/pwa.d.ts",
|
|
33
|
+
"import": "./dist/pwa.js"
|
|
34
|
+
},
|
|
31
35
|
"./templates": {
|
|
32
36
|
"types": "./dist/templates.d.ts",
|
|
33
37
|
"import": "./dist/templates.js"
|
|
@@ -52,6 +56,10 @@
|
|
|
52
56
|
"check": "bun run scripts/build.ts && bun test src",
|
|
53
57
|
"test": "bun test src"
|
|
54
58
|
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"vite-plugin-pwa": "^1.0.0",
|
|
61
|
+
"workbox-window": "^7.4.1"
|
|
62
|
+
},
|
|
55
63
|
"peerDependencies": {
|
|
56
64
|
"vite": ">=5",
|
|
57
65
|
"zod": "^3 || ^4"
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { registerAppSW } from '@kazzle/app/pwa';
|
|
3
4
|
import { App } from './App';
|
|
4
5
|
import './styles/theme.css';
|
|
5
6
|
import './styles/app.css';
|
|
6
7
|
|
|
8
|
+
registerAppSW();
|
|
9
|
+
|
|
7
10
|
createRoot(document.getElementById('root')!).render(
|
|
8
11
|
<React.StrictMode>
|
|
9
12
|
<App />
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Vite config for the UI component.
|
|
3
3
|
*
|
|
4
|
-
* Kazzle
|
|
5
|
-
*
|
|
6
|
-
* never be reinvented. This template declares a backend `server` component; the
|
|
4
|
+
* Kazzle owns HOST/PORT (`kazzleAppVite`) and production PWA (`kazzleAppPwa`)
|
|
5
|
+
* in `@kazzle/app/vite`. This template declares a backend `server` component; the
|
|
7
6
|
* browser calls relative owned routes (/chat, /image, …) and the helper proxies
|
|
8
7
|
* them to the sibling component. Add new backend routes to `proxyPaths`. Do not
|
|
9
8
|
* expose Kazzle's internal component URLs to the browser.
|
|
10
9
|
*/
|
|
10
|
+
|
|
11
11
|
import { defineConfig } from 'vite';
|
|
12
12
|
import react from '@vitejs/plugin-react';
|
|
13
13
|
import tailwindcss from '@tailwindcss/vite';
|
|
14
|
-
import { kazzleAppVite } from '@kazzle/app/vite';
|
|
14
|
+
import { kazzleAppVite, kazzleAppPwa } from '@kazzle/app/vite';
|
|
15
15
|
|
|
16
16
|
export default defineConfig(({ command }) => ({
|
|
17
|
-
plugins: [react(), tailwindcss()],
|
|
17
|
+
plugins: [react(), tailwindcss(), ...kazzleAppPwa()],
|
|
18
18
|
...kazzleAppVite(command, {
|
|
19
19
|
apiComponent: 'server',
|
|
20
20
|
proxyPaths: ['/chat', '/image', '/speech', '/transcribe', '/video', '/health'],
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import { StrictMode } from 'react';
|
|
26
26
|
import { createRoot } from 'react-dom/client';
|
|
27
27
|
import { PowerSyncContext } from '@powersync/react';
|
|
28
|
-
import {
|
|
28
|
+
import { registerAppSW } from '@kazzle/app/pwa';
|
|
29
29
|
import { db } from './lib/sync';
|
|
30
30
|
import { AppConnector } from './lib/connector';
|
|
31
31
|
import App from './App';
|
|
@@ -34,11 +34,8 @@ import './styles/theme.css';
|
|
|
34
34
|
db.init();
|
|
35
35
|
db.connect(new AppConnector());
|
|
36
36
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
// `autoUpdate` swaps to a new SW as soon as one is available; subsequent
|
|
40
|
-
// reloads serve from the new cache.
|
|
41
|
-
registerSW({ immediate: true });
|
|
37
|
+
// Production SW — owned by `@kazzle/app/pwa` + `kazzleAppPwa()` in vite.config.
|
|
38
|
+
registerAppSW();
|
|
42
39
|
|
|
43
40
|
createRoot(document.getElementById('root')!).render(
|
|
44
41
|
<StrictMode>
|
|
@@ -1,71 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Vite config for the UI component.
|
|
3
3
|
*
|
|
4
|
-
* Kazzle
|
|
5
|
-
*
|
|
6
|
-
* never be reinvented. This template declares a backend `server` component
|
|
4
|
+
* Kazzle owns HOST/PORT (`kazzleAppVite`) and production PWA (`kazzleAppPwa`)
|
|
5
|
+
* in `@kazzle/app/vite`. This template declares a backend `server` component
|
|
7
6
|
* (PowerSync sync endpoint); we proxy `/sync` to it via the helper. Add new
|
|
8
7
|
* backend routes to `proxyPaths`.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Offline-first extras (manifest, worker format) stay here on top of the
|
|
10
|
+
* shared PWA + port contract. Do not import `vite-plugin-pwa` directly.
|
|
12
11
|
*/
|
|
13
|
-
|
|
12
|
+
|
|
13
|
+
import { defineConfig } from 'vite';
|
|
14
14
|
import react from '@vitejs/plugin-react';
|
|
15
15
|
import tailwindcss from '@tailwindcss/vite';
|
|
16
|
-
import {
|
|
17
|
-
import { kazzleAppVite } from '@kazzle/app/vite';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Dev-only: any browser that previously visited a built/PWA-enabled version
|
|
21
|
-
* of this app at the same origin still has a service worker installed. With
|
|
22
|
-
* `devOptions.enabled: false` we don't serve a SW in dev, so the stale SW
|
|
23
|
-
* keeps intercepting requests with its old precache and the page renders
|
|
24
|
-
* without current CSS/JS. This plugin responds to /sw.js (and the PWA
|
|
25
|
-
* register helper) with a tiny script that unregisters every SW, clears all
|
|
26
|
-
* caches, and reloads the page once. Once cleaned, the SW is gone and Vite
|
|
27
|
-
* HMR works normally. Safe to keep enabled — production builds skip this
|
|
28
|
-
* plugin (the `serve` command guard).
|
|
29
|
-
*/
|
|
30
|
-
function devUnregisterServiceWorker(): Plugin {
|
|
31
|
-
const body = [
|
|
32
|
-
"self.addEventListener('install', e => self.skipWaiting());",
|
|
33
|
-
"self.addEventListener('activate', e => e.waitUntil((async () => {",
|
|
34
|
-
" try { await self.registration.unregister(); } catch {}",
|
|
35
|
-
" try { const keys = await caches.keys(); await Promise.all(keys.map(k => caches.delete(k))); } catch {}",
|
|
36
|
-
" const clients = await self.clients.matchAll({ type: 'window' });",
|
|
37
|
-
" for (const c of clients) { try { c.navigate(c.url); } catch {} }",
|
|
38
|
-
"})());",
|
|
39
|
-
].join('\n');
|
|
40
|
-
return {
|
|
41
|
-
name: 'kazzle:dev-unregister-sw',
|
|
42
|
-
apply: 'serve',
|
|
43
|
-
configureServer(server) {
|
|
44
|
-
server.middlewares.use((req, res, next) => {
|
|
45
|
-
if (req.method !== 'GET') return next();
|
|
46
|
-
const url = req.url?.split('?')[0];
|
|
47
|
-
if (url !== '/sw.js' && url !== '/registerSW.js') return next();
|
|
48
|
-
res.setHeader('content-type', 'application/javascript; charset=utf-8');
|
|
49
|
-
res.setHeader('cache-control', 'no-store');
|
|
50
|
-
res.end(body);
|
|
51
|
-
});
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
}
|
|
16
|
+
import { kazzleAppVite, kazzleAppPwa } from '@kazzle/app/vite';
|
|
55
17
|
|
|
56
18
|
export default defineConfig(({ command }) => ({
|
|
57
19
|
plugins: [
|
|
58
20
|
react(),
|
|
59
21
|
tailwindcss(),
|
|
60
|
-
|
|
61
|
-
// Service worker for offline-first behavior. `devOptions.enabled: false`
|
|
62
|
-
// is intentional — enabling the SW in dev breaks Vite HMR
|
|
63
|
-
// (templates.mdc note). The SW only ships in the production build, where
|
|
64
|
-
// it pre-caches the app shell + JS bundles so reload-while-offline keeps
|
|
65
|
-
// working. PowerSync's local SQLite stays readable in both modes.
|
|
66
|
-
VitePWA({
|
|
67
|
-
registerType: 'autoUpdate',
|
|
68
|
-
devOptions: { enabled: false },
|
|
22
|
+
...kazzleAppPwa({
|
|
69
23
|
workbox: { globPatterns: ['**/*.{js,css,html,svg,png,ico}'] },
|
|
70
24
|
manifest: {
|
|
71
25
|
name: 'Kazzle App',
|
|
@@ -11,23 +11,18 @@
|
|
|
11
11
|
* `StrictMode` double-invokes effects in dev to catch bugs from accidental
|
|
12
12
|
* state mutation. It has no effect in production builds.
|
|
13
13
|
*
|
|
14
|
-
* Service worker: `
|
|
15
|
-
*
|
|
16
|
-
* `virtual:pwa-register`. Leaving that out leaves a registered worker that
|
|
17
|
-
* never claims updates — published builds would stay stale until a hard
|
|
18
|
-
* refresh. Keep this registration; do not enable `devOptions`.
|
|
14
|
+
* Service worker: call `registerAppSW()` from `@kazzle/app/pwa` (paired with
|
|
15
|
+
* `kazzleAppPwa()` in vite.config.ts). Do not import `virtual:pwa-register`.
|
|
19
16
|
*/
|
|
20
17
|
|
|
21
18
|
import { StrictMode } from 'react';
|
|
22
19
|
import { createRoot } from 'react-dom/client';
|
|
23
|
-
import {
|
|
20
|
+
import { registerAppSW } from '@kazzle/app/pwa';
|
|
24
21
|
import App from './App';
|
|
25
22
|
import './styles/theme.css';
|
|
26
23
|
|
|
27
|
-
// Production only —
|
|
28
|
-
|
|
29
|
-
// worker as soon as `registration.update()` (or periodic checks) finds one.
|
|
30
|
-
registerSW({ immediate: true });
|
|
24
|
+
// Production only — no-op in dev (`kazzleAppPwa` keeps `devOptions.enabled: false`).
|
|
25
|
+
registerAppSW();
|
|
31
26
|
|
|
32
27
|
createRoot(document.getElementById('root')!).render(
|
|
33
28
|
<StrictMode>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -1,62 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Vite config for the UI component.
|
|
3
3
|
*
|
|
4
|
-
* Kazzle
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
* proxy options are passed; if you add one later, pass
|
|
9
|
-
* `{ apiComponent, proxyPaths }` (see @kazzle/app/vite).
|
|
4
|
+
* Kazzle owns HOST/PORT (`kazzleAppVite`) and production PWA (`kazzleAppPwa`)
|
|
5
|
+
* in `@kazzle/app/vite`. This file only declares app plugins and spreads those
|
|
6
|
+
* helpers. Do not import `vite-plugin-pwa` or `virtual:pwa-register` here —
|
|
7
|
+
* browser entry calls `registerAppSW()` from `@kazzle/app/pwa`.
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* would aggressively cache module responses and break Vite's HMR. The
|
|
14
|
-
* `devUnregisterServiceWorker` plugin below cleans up stale SWs left by a
|
|
15
|
-
* previous built visit to the same origin. Do not enable `devOptions`.
|
|
9
|
+
* This template has no backend component, so no proxy options are passed; if
|
|
10
|
+
* you add one later, pass `{ apiComponent, proxyPaths }` (see @kazzle/app/vite).
|
|
16
11
|
*/
|
|
17
12
|
|
|
18
|
-
import { defineConfig
|
|
13
|
+
import { defineConfig } from 'vite';
|
|
19
14
|
import react from '@vitejs/plugin-react';
|
|
20
15
|
import tailwindcss from '@tailwindcss/vite';
|
|
21
|
-
import {
|
|
22
|
-
import { kazzleAppVite } from '@kazzle/app/vite';
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Dev-only: any browser that previously visited a built/PWA-enabled version
|
|
26
|
-
* of this app at the same origin still has a service worker installed. With
|
|
27
|
-
* `devOptions.enabled: false` we don't serve a SW in dev, so the stale SW
|
|
28
|
-
* keeps intercepting requests with its old precache and the page renders
|
|
29
|
-
* without current CSS/JS. This plugin responds to /sw.js (and the PWA
|
|
30
|
-
* register helper) with a tiny script that unregisters every SW, clears all
|
|
31
|
-
* caches, and reloads the page once. Once cleaned, the SW is gone and Vite
|
|
32
|
-
* HMR works normally. Safe to keep enabled — production builds skip this
|
|
33
|
-
* plugin (the `serve` command guard).
|
|
34
|
-
*/
|
|
35
|
-
function devUnregisterServiceWorker(): Plugin {
|
|
36
|
-
const body = [
|
|
37
|
-
"self.addEventListener('install', e => self.skipWaiting());",
|
|
38
|
-
"self.addEventListener('activate', e => e.waitUntil((async () => {",
|
|
39
|
-
" try { await self.registration.unregister(); } catch {}",
|
|
40
|
-
" try { const keys = await caches.keys(); await Promise.all(keys.map(k => caches.delete(k))); } catch {}",
|
|
41
|
-
" const clients = await self.clients.matchAll({ type: 'window' });",
|
|
42
|
-
" for (const c of clients) { try { c.navigate(c.url); } catch {} }",
|
|
43
|
-
"})());",
|
|
44
|
-
].join('\n');
|
|
45
|
-
return {
|
|
46
|
-
name: 'kazzle:dev-unregister-sw',
|
|
47
|
-
apply: 'serve',
|
|
48
|
-
configureServer(server) {
|
|
49
|
-
server.middlewares.use((req, res, next) => {
|
|
50
|
-
if (req.method !== 'GET') return next();
|
|
51
|
-
const url = req.url?.split('?')[0];
|
|
52
|
-
if (url !== '/sw.js' && url !== '/registerSW.js') return next();
|
|
53
|
-
res.setHeader('content-type', 'application/javascript; charset=utf-8');
|
|
54
|
-
res.setHeader('cache-control', 'no-store');
|
|
55
|
-
res.end(body);
|
|
56
|
-
});
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
}
|
|
16
|
+
import { kazzleAppVite, kazzleAppPwa } from '@kazzle/app/vite';
|
|
60
17
|
|
|
61
18
|
export default defineConfig(({ command }) => ({
|
|
62
19
|
plugins: [
|
|
@@ -65,13 +22,8 @@ export default defineConfig(({ command }) => ({
|
|
|
65
22
|
// tailwind.config.js or PostCSS setup needed; tokens/primitives live in
|
|
66
23
|
// src/styles/theme.css.
|
|
67
24
|
tailwindcss(),
|
|
68
|
-
|
|
69
|
-
VitePWA({
|
|
70
|
-
registerType: 'autoUpdate',
|
|
25
|
+
...kazzleAppPwa({
|
|
71
26
|
workbox: {
|
|
72
|
-
// Precache the static asset bundle. Network requests to anything
|
|
73
|
-
// ending in these extensions get served from cache first.
|
|
74
|
-
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
|
|
75
27
|
runtimeCaching: [
|
|
76
28
|
{
|
|
77
29
|
// External API responses get cached briefly and revalidated in
|
|
@@ -86,10 +38,6 @@ export default defineConfig(({ command }) => ({
|
|
|
86
38
|
},
|
|
87
39
|
],
|
|
88
40
|
},
|
|
89
|
-
// Keep disabled in dev — see header comment.
|
|
90
|
-
devOptions: {
|
|
91
|
-
enabled: false,
|
|
92
|
-
},
|
|
93
41
|
}),
|
|
94
42
|
],
|
|
95
43
|
// HOST/PORT/strictPort/allowedHosts — the port contract. See @kazzle/app/vite.
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { registerAppSW } from '@kazzle/app/pwa';
|
|
3
4
|
import { App } from './App';
|
|
4
5
|
import './styles/theme.css';
|
|
5
6
|
|
|
7
|
+
registerAppSW();
|
|
8
|
+
|
|
6
9
|
createRoot(document.getElementById('root')!).render(
|
|
7
10
|
<React.StrictMode>
|
|
8
11
|
<App />
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Vite config for the UI component.
|
|
3
3
|
*
|
|
4
|
-
* Kazzle
|
|
5
|
-
*
|
|
6
|
-
* never be reinvented. This template declares a backend `server` component, so
|
|
4
|
+
* Kazzle owns HOST/PORT (`kazzleAppVite`) and production PWA (`kazzleAppPwa`)
|
|
5
|
+
* in `@kazzle/app/vite`. This template declares a backend `server` component, so
|
|
7
6
|
* we pass its name plus the relative routes the browser calls; the helper
|
|
8
7
|
* proxies them to the sibling component and refuses to serve until Kazzle has
|
|
9
8
|
* injected the backend URL. Add new backend routes to `proxyPaths`.
|
|
10
9
|
*/
|
|
10
|
+
|
|
11
11
|
import { defineConfig } from 'vite';
|
|
12
12
|
import react from '@vitejs/plugin-react';
|
|
13
13
|
import tailwindcss from '@tailwindcss/vite';
|
|
14
|
-
import { kazzleAppVite } from '@kazzle/app/vite';
|
|
14
|
+
import { kazzleAppVite, kazzleAppPwa } from '@kazzle/app/vite';
|
|
15
15
|
|
|
16
16
|
export default defineConfig(({ command }) => ({
|
|
17
|
-
plugins: [react(), tailwindcss()],
|
|
17
|
+
plugins: [react(), tailwindcss(), ...kazzleAppPwa()],
|
|
18
18
|
...kazzleAppVite(command, { apiComponent: 'server', proxyPaths: ['/categories', '/todos'] }),
|
|
19
19
|
}));
|