@powerhousedao/builder-tools 6.2.2-dev.4 → 6.2.2-dev.40
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/dist/index.d.mts +301 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +342 -102
- package/dist/index.mjs.map +1 -1
- package/dist/service-worker/service-worker.ts +352 -0
- package/dist/service-worker/virtual-sw-config.d.ts +22 -0
- package/package.json +10 -4
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/// <reference lib="webworker" />
|
|
2
|
+
//
|
|
3
|
+
// Connect's service worker (injectManifest strategy).
|
|
4
|
+
//
|
|
5
|
+
// This file is NOT bundled into @powerhousedao/builder-tools' dist entry — it
|
|
6
|
+
// is copied verbatim into `dist/service-worker/` and compiled by
|
|
7
|
+
// vite-plugin-pwa's own build pass at the CONSUMER's build time (see
|
|
8
|
+
// connectPwaPlugins in ../vite-plugins/pwa.ts). It is deliberately excluded
|
|
9
|
+
// from the package's `tsc` project (webworker globals + the `virtual:` import
|
|
10
|
+
// don't belong to the Node build graph).
|
|
11
|
+
//
|
|
12
|
+
// It reproduces what Workbox `generateSW` used to bake from BASE_WORKBOX — the
|
|
13
|
+
// precache, the runtime-caching rules, the navigation fallback and the
|
|
14
|
+
// prompt/SKIP_WAITING lifecycle — PLUS the one thing generateSW cannot do:
|
|
15
|
+
// serve a DYNAMIC web-app manifest, merging the build-embedded base with the
|
|
16
|
+
// fragments dynamically-installed packages mirror into IndexedDB.
|
|
17
|
+
//
|
|
18
|
+
// Route registration order matters — Workbox is first-match-wins.
|
|
19
|
+
|
|
20
|
+
import { CacheableResponsePlugin } from "workbox-cacheable-response";
|
|
21
|
+
import { clientsClaim } from "workbox-core";
|
|
22
|
+
import { ExpirationPlugin } from "workbox-expiration";
|
|
23
|
+
import {
|
|
24
|
+
cleanupOutdatedCaches,
|
|
25
|
+
createHandlerBoundToURL,
|
|
26
|
+
precacheAndRoute,
|
|
27
|
+
} from "workbox-precaching";
|
|
28
|
+
import { registerRoute, Route } from "workbox-routing";
|
|
29
|
+
import {
|
|
30
|
+
CacheFirst,
|
|
31
|
+
CacheOnly,
|
|
32
|
+
NetworkFirst,
|
|
33
|
+
NetworkOnly,
|
|
34
|
+
StaleWhileRevalidate,
|
|
35
|
+
} from "workbox-strategies";
|
|
36
|
+
// Barrel import (not a subpath): shared's bundler only emits dist/connect/
|
|
37
|
+
// index.js, so subpaths like ./connect/pwa-manifest have no dist target. The
|
|
38
|
+
// barrel is `sideEffects: false`, so rolldown tree-shakes the unused siblings
|
|
39
|
+
// (env-config/json-adapter) — including their dynamic node:fs import — out of
|
|
40
|
+
// the worker bundle. The IDB schema constants are shared with the SPA writer
|
|
41
|
+
// (apps/connect/src/utils/pwa-idb.ts) so the two can't desync.
|
|
42
|
+
import {
|
|
43
|
+
mergeManifest,
|
|
44
|
+
type PHConnectPwaUrlPattern,
|
|
45
|
+
PWA_IDB_KEY,
|
|
46
|
+
PWA_IDB_NAME,
|
|
47
|
+
PWA_IDB_STORE,
|
|
48
|
+
PWA_IDB_VERSION,
|
|
49
|
+
toRegExpOrString,
|
|
50
|
+
} from "@powerhousedao/shared/connect";
|
|
51
|
+
import {
|
|
52
|
+
EMBEDDED_BASE_MANIFEST,
|
|
53
|
+
EXTRA_RUNTIME_CACHING,
|
|
54
|
+
NAVIGATE_FALLBACK_DENYLIST_EXTRA,
|
|
55
|
+
} from "virtual:ph-sw-config";
|
|
56
|
+
|
|
57
|
+
declare const self: ServiceWorkerGlobalScope &
|
|
58
|
+
typeof globalThis & {
|
|
59
|
+
// The injectManifest injection point (replaced with the precache list).
|
|
60
|
+
__WB_MANIFEST: Array<{ url: string; revision: string | null }>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// ── lifecycle ──────────────────────────────────────────────────────────────
|
|
64
|
+
// skipWaiting is NOT called on install: the SPA surfaces a refresh prompt and
|
|
65
|
+
// posts SKIP_WAITING when the user accepts (identical contract to the old
|
|
66
|
+
// generateSW `registerType: "prompt"` — registerServiceWorker.ts is unchanged).
|
|
67
|
+
self.addEventListener("message", (event) => {
|
|
68
|
+
if (event.data?.type === "SKIP_WAITING") self.skipWaiting();
|
|
69
|
+
});
|
|
70
|
+
clientsClaim();
|
|
71
|
+
cleanupOutdatedCaches();
|
|
72
|
+
|
|
73
|
+
// A runtime-installed package may override cosmetic manifest scalars (name,
|
|
74
|
+
// theme, display, …) — full parity with a build-time package — but NOT these
|
|
75
|
+
// navigation-critical ones: re-pointing start_url or re-scoping the installed
|
|
76
|
+
// PWA could break or hijack navigation, so they stay whatever the build baked.
|
|
77
|
+
const RUNTIME_PROTECTED_SCALARS = ["start_url", "scope"] as const;
|
|
78
|
+
|
|
79
|
+
// ── dynamic web-app manifest ─────────────────────────────────────────────────
|
|
80
|
+
// Registered before precache so it always wins for manifest.webmanifest (which
|
|
81
|
+
// is also excluded from the precache glob). Every request reads IndexedDB
|
|
82
|
+
// fresh, so a package install/removal is reflected on the next manifest fetch
|
|
83
|
+
// with no service-worker restart. Fragment-wins (with start_url/scope
|
|
84
|
+
// protected), so a dynamically-installed package extends and overrides the
|
|
85
|
+
// manifest exactly like a build-time contribution.
|
|
86
|
+
type DynamicFragmentRecord = {
|
|
87
|
+
fragment?: {
|
|
88
|
+
manifest?: unknown;
|
|
89
|
+
runtimeCaching?: unknown;
|
|
90
|
+
navigateFallbackDenylist?: unknown;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
async function readDynamicFragmentRecord(): Promise<
|
|
95
|
+
DynamicFragmentRecord | undefined
|
|
96
|
+
> {
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
let request: IDBOpenDBRequest;
|
|
99
|
+
try {
|
|
100
|
+
request = indexedDB.open(PWA_IDB_NAME, PWA_IDB_VERSION);
|
|
101
|
+
} catch {
|
|
102
|
+
resolve(undefined);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// If the DB doesn't exist yet (no package has synced), ABORT the upgrade so
|
|
106
|
+
// we don't leave a store-less v1 DB behind — that would later block the
|
|
107
|
+
// page's own open (same version → no upgrade → missing store → write
|
|
108
|
+
// fails). The page owns creating the DB/store.
|
|
109
|
+
request.onupgradeneeded = () => {
|
|
110
|
+
try {
|
|
111
|
+
request.transaction?.abort();
|
|
112
|
+
} catch {
|
|
113
|
+
/* ignore */
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
request.onerror = () => resolve(undefined);
|
|
117
|
+
request.onblocked = () => resolve(undefined);
|
|
118
|
+
request.onsuccess = () => {
|
|
119
|
+
const db = request.result;
|
|
120
|
+
if (!db.objectStoreNames.contains(PWA_IDB_STORE)) {
|
|
121
|
+
db.close();
|
|
122
|
+
resolve(undefined);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const tx = db.transaction(PWA_IDB_STORE, "readonly");
|
|
127
|
+
const getReq = tx.objectStore(PWA_IDB_STORE).get(PWA_IDB_KEY);
|
|
128
|
+
getReq.onsuccess = () => {
|
|
129
|
+
resolve(getReq.result);
|
|
130
|
+
db.close();
|
|
131
|
+
};
|
|
132
|
+
getReq.onerror = () => {
|
|
133
|
+
resolve(undefined);
|
|
134
|
+
db.close();
|
|
135
|
+
};
|
|
136
|
+
} catch {
|
|
137
|
+
resolve(undefined);
|
|
138
|
+
db.close();
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
registerRoute(
|
|
145
|
+
({ url }) => url.pathname.endsWith("manifest.webmanifest"),
|
|
146
|
+
async () => {
|
|
147
|
+
let manifest: unknown = EMBEDDED_BASE_MANIFEST;
|
|
148
|
+
try {
|
|
149
|
+
const record = await readDynamicFragmentRecord();
|
|
150
|
+
const fragmentManifest = record?.fragment?.manifest;
|
|
151
|
+
if (fragmentManifest) {
|
|
152
|
+
manifest = mergeManifest(
|
|
153
|
+
EMBEDDED_BASE_MANIFEST as Record<string, unknown>,
|
|
154
|
+
// The record was validated by the page before it was written.
|
|
155
|
+
fragmentManifest as never,
|
|
156
|
+
{
|
|
157
|
+
scalarPolicy: "fragment-wins",
|
|
158
|
+
protectedScalars: RUNTIME_PROTECTED_SCALARS,
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
} catch {
|
|
163
|
+
// Fall back to the embedded base — a broken fragment must never take the
|
|
164
|
+
// manifest down.
|
|
165
|
+
}
|
|
166
|
+
return new Response(JSON.stringify(manifest), {
|
|
167
|
+
headers: {
|
|
168
|
+
"Content-Type": "application/manifest+json",
|
|
169
|
+
"Cache-Control": "no-cache",
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// ── precache ─────────────────────────────────────────────────────────────────
|
|
176
|
+
// Precache the app shell + PGlite wasm/data. manifest.webmanifest is excluded
|
|
177
|
+
// from the glob (see connectPwaPlugins) so the dynamic route above is the sole
|
|
178
|
+
// producer at that URL. The __WB_MANIFEST property below is the injectManifest
|
|
179
|
+
// injection point — workbox-build replaces it with the precache list at build
|
|
180
|
+
// time (the token is written once, on the code line, never in prose).
|
|
181
|
+
precacheAndRoute(self.__WB_MANIFEST);
|
|
182
|
+
|
|
183
|
+
// ── runtime caching (ported 1:1 from the old BASE_WORKBOX, same order) ───────
|
|
184
|
+
// Inter font stays on Google's CDN; cache it after the first online load.
|
|
185
|
+
registerRoute(
|
|
186
|
+
({ url }) => url.origin === "https://fonts.googleapis.com",
|
|
187
|
+
new StaleWhileRevalidate({ cacheName: "google-fonts-stylesheets" }),
|
|
188
|
+
);
|
|
189
|
+
registerRoute(
|
|
190
|
+
({ url }) => url.origin === "https://fonts.gstatic.com",
|
|
191
|
+
new CacheFirst({
|
|
192
|
+
cacheName: "google-fonts-webfonts",
|
|
193
|
+
plugins: [
|
|
194
|
+
new ExpirationPlugin({
|
|
195
|
+
maxEntries: 30,
|
|
196
|
+
maxAgeSeconds: 60 * 60 * 24 * 365,
|
|
197
|
+
}),
|
|
198
|
+
// statuses [0, 200]: 0 permits opaque cross-origin font responses.
|
|
199
|
+
new CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
200
|
+
],
|
|
201
|
+
}),
|
|
202
|
+
);
|
|
203
|
+
// Document-model editors/packages loaded at runtime from the registry CDN. The
|
|
204
|
+
// registry ORIGIN is a runtime value, so match the stable "/-/cdn/" path. Two
|
|
205
|
+
// rules (order matters): unversioned ENTRY points first (SWR — a newer editor
|
|
206
|
+
// refreshes online, cached copy serves offline), then a catch-all CacheFirst
|
|
207
|
+
// for the content-hashed (immutable) assets. statuses [0, 200] on both — the
|
|
208
|
+
// editor JS is a CORS import (200) but its stylesheet/assets are no-cors
|
|
209
|
+
// (opaque/0); [200] alone silently dropped those and broke styles offline.
|
|
210
|
+
registerRoute(
|
|
211
|
+
({ url }) =>
|
|
212
|
+
url.pathname.includes("/-/cdn/") &&
|
|
213
|
+
(url.pathname.endsWith("/browser/index.js") ||
|
|
214
|
+
url.pathname.endsWith("/style.css") ||
|
|
215
|
+
url.pathname.endsWith("/package.json")),
|
|
216
|
+
new StaleWhileRevalidate({
|
|
217
|
+
cacheName: "ph-package-cdn-entry",
|
|
218
|
+
plugins: [
|
|
219
|
+
new ExpirationPlugin({ maxEntries: 60, maxAgeSeconds: 60 * 60 * 24 * 30 }),
|
|
220
|
+
new CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
221
|
+
],
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
224
|
+
registerRoute(
|
|
225
|
+
({ url }) => url.pathname.includes("/-/cdn/"),
|
|
226
|
+
new CacheFirst({
|
|
227
|
+
cacheName: "ph-package-cdn",
|
|
228
|
+
plugins: [
|
|
229
|
+
new ExpirationPlugin({
|
|
230
|
+
maxEntries: 200,
|
|
231
|
+
maxAgeSeconds: 60 * 60 * 24 * 30,
|
|
232
|
+
}),
|
|
233
|
+
new CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
234
|
+
],
|
|
235
|
+
}),
|
|
236
|
+
);
|
|
237
|
+
// Runtime config: NetworkFirst so a fresh value wins online, last-known serves
|
|
238
|
+
// offline (it is precache-ignored). The timeout stops a flaky network stalling
|
|
239
|
+
// boot until the browser's own fetch timeout.
|
|
240
|
+
registerRoute(
|
|
241
|
+
({ url }) => url.pathname.endsWith("/powerhouse.config.json"),
|
|
242
|
+
new NetworkFirst({ cacheName: "ph-runtime-config", networkTimeoutSeconds: 5 }),
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// ── extra runtime caching ────────────────────────────────────────────────────
|
|
246
|
+
// Appended AFTER the built-ins — Workbox is first-match-wins, so a contribution
|
|
247
|
+
// cannot shadow a built-in rule for the same URL (intentional). Two sources:
|
|
248
|
+
// build-time package/project rules (EXTRA_RUNTIME_CACHING, registered now) and
|
|
249
|
+
// runtime-installed package rules (read from IndexedDB at startup below).
|
|
250
|
+
// All five Workbox strategies a contributed rule may name, so a rule's caching
|
|
251
|
+
// semantics are honored faithfully (e.g. NetworkOnly never caches — remapping
|
|
252
|
+
// it to NetworkFirst would silently start caching an endpoint a package meant
|
|
253
|
+
// to keep uncached).
|
|
254
|
+
const STRATEGIES = {
|
|
255
|
+
CacheFirst,
|
|
256
|
+
CacheOnly,
|
|
257
|
+
NetworkFirst,
|
|
258
|
+
NetworkOnly,
|
|
259
|
+
StaleWhileRevalidate,
|
|
260
|
+
} as const;
|
|
261
|
+
|
|
262
|
+
type RuntimeCachingRule = (typeof EXTRA_RUNTIME_CACHING)[number];
|
|
263
|
+
|
|
264
|
+
function registerRuntimeCachingRule(rule: RuntimeCachingRule) {
|
|
265
|
+
const Strategy = STRATEGIES[rule.handler] ?? StaleWhileRevalidate;
|
|
266
|
+
const options = rule.options ?? {};
|
|
267
|
+
const plugins = [];
|
|
268
|
+
if (options.expiration) {
|
|
269
|
+
plugins.push(new ExpirationPlugin(options.expiration));
|
|
270
|
+
}
|
|
271
|
+
if (options.cacheableResponse) {
|
|
272
|
+
plugins.push(new CacheableResponsePlugin(options.cacheableResponse));
|
|
273
|
+
}
|
|
274
|
+
registerRoute(
|
|
275
|
+
toRegExpOrString(rule.urlPattern),
|
|
276
|
+
new Strategy({
|
|
277
|
+
...(options.cacheName ? { cacheName: options.cacheName } : {}),
|
|
278
|
+
...(typeof options.networkTimeoutSeconds === "number"
|
|
279
|
+
? { networkTimeoutSeconds: options.networkTimeoutSeconds }
|
|
280
|
+
: {}),
|
|
281
|
+
plugins,
|
|
282
|
+
}),
|
|
283
|
+
rule.method ?? "GET",
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
for (const rule of EXTRA_RUNTIME_CACHING) registerRuntimeCachingRule(rule);
|
|
288
|
+
|
|
289
|
+
// ── navigation fallback ──────────────────────────────────────────────────────
|
|
290
|
+
// SPA fallback to index.html (resolved against the SW's own location, so it
|
|
291
|
+
// tracks the deploy base). The `denylist` is MUTABLE and read live on every
|
|
292
|
+
// request: it is seeded synchronously with the built-ins plus the BUILD-TIME
|
|
293
|
+
// contributed patterns (NAVIGATE_FALLBACK_DENYLIST_EXTRA), and the async read
|
|
294
|
+
// below pushes any RUNTIME-installed package's patterns into it. Registering
|
|
295
|
+
// the route synchronously keeps offline SPA navigation working the instant a
|
|
296
|
+
// cold SW starts; the runtime patterns just join in once they've loaded.
|
|
297
|
+
function escapeForRegExp(value: string): string {
|
|
298
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
299
|
+
}
|
|
300
|
+
// Workbox denylists match against `pathname + search`; a plain string is
|
|
301
|
+
// matched as a literal substring (escaped), mirroring the built-ins.
|
|
302
|
+
function toDenylistRegExp(pattern: PHConnectPwaUrlPattern): RegExp {
|
|
303
|
+
const value = toRegExpOrString(pattern);
|
|
304
|
+
return typeof value === "string" ? new RegExp(escapeForRegExp(value)) : value;
|
|
305
|
+
}
|
|
306
|
+
const denylist: RegExp[] = [
|
|
307
|
+
/\/powerhouse\.config\.json$/,
|
|
308
|
+
/^\/health$/,
|
|
309
|
+
/\/__/,
|
|
310
|
+
...NAVIGATE_FALLBACK_DENYLIST_EXTRA.map(toDenylistRegExp),
|
|
311
|
+
];
|
|
312
|
+
// A plain Route (not NavigationRoute, which snapshots its denylist at
|
|
313
|
+
// construction) so the match reads the mutable `denylist` live — same
|
|
314
|
+
// navigation-request + denylist semantics, minus the frozen list.
|
|
315
|
+
registerRoute(
|
|
316
|
+
new Route(
|
|
317
|
+
({ request, url }) =>
|
|
318
|
+
request.mode === "navigate" &&
|
|
319
|
+
!denylist.some((re) => re.test(url.pathname + url.search)),
|
|
320
|
+
createHandlerBoundToURL("index.html"),
|
|
321
|
+
),
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// ── dynamic contributions from runtime-installed packages ────────────────────
|
|
325
|
+
// One IndexedDB read at SW startup applies BOTH the runtime-caching rules and
|
|
326
|
+
// the navigate-fallback denylist patterns a runtime-installed package
|
|
327
|
+
// contributes (the SPA mirrors the merged fragment there). Best-effort: they
|
|
328
|
+
// register shortly after startup, so a package installed in THIS session takes
|
|
329
|
+
// full effect on the SW's next activation — the same model for both. The
|
|
330
|
+
// manifest route above is the live path. A malformed entry never aborts the rest.
|
|
331
|
+
void readDynamicFragmentRecord().then((record) => {
|
|
332
|
+
const rules = record?.fragment?.runtimeCaching;
|
|
333
|
+
if (Array.isArray(rules)) {
|
|
334
|
+
for (const rule of rules) {
|
|
335
|
+
try {
|
|
336
|
+
registerRuntimeCachingRule(rule as RuntimeCachingRule);
|
|
337
|
+
} catch {
|
|
338
|
+
// skip a malformed rule
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const patterns = record?.fragment?.navigateFallbackDenylist;
|
|
343
|
+
if (Array.isArray(patterns)) {
|
|
344
|
+
for (const pattern of patterns) {
|
|
345
|
+
try {
|
|
346
|
+
denylist.push(toDenylistRegExp(pattern as PHConnectPwaUrlPattern));
|
|
347
|
+
} catch {
|
|
348
|
+
// skip a malformed pattern
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Ambient types for the build-time-generated virtual module the service worker
|
|
2
|
+
// imports. The concrete values are supplied by `phSwConfigPlugin` (see
|
|
3
|
+
// ../vite-plugins/pwa.ts), which resolves `virtual:ph-sw-config` during
|
|
4
|
+
// vite-plugin-pwa's injectManifest build pass.
|
|
5
|
+
declare module "virtual:ph-sw-config" {
|
|
6
|
+
import type {
|
|
7
|
+
PHConnectPwaRuntimeCaching,
|
|
8
|
+
PHConnectPwaUrlPattern,
|
|
9
|
+
} from "@powerhousedao/shared/connect";
|
|
10
|
+
|
|
11
|
+
/** The build-effective web-app manifest (Connect base + build-time package
|
|
12
|
+
* fragments + project config), the base the runtime manifest route merges
|
|
13
|
+
* dynamically-installed package fragments onto. */
|
|
14
|
+
export const EMBEDDED_BASE_MANIFEST: Record<string, unknown>;
|
|
15
|
+
|
|
16
|
+
/** Serialisable runtime-caching rules contributed at build time, registered
|
|
17
|
+
* after the built-in rules. */
|
|
18
|
+
export const EXTRA_RUNTIME_CACHING: PHConnectPwaRuntimeCaching[];
|
|
19
|
+
|
|
20
|
+
/** Extra SPA navigate-fallback denylist patterns. */
|
|
21
|
+
export const NAVIGATE_FALLBACK_DENYLIST_EXTRA: PHConnectPwaUrlPattern[];
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powerhousedao/builder-tools",
|
|
3
|
-
"version": "6.2.2-dev.
|
|
3
|
+
"version": "6.2.2-dev.40",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"publishConfig": {
|
|
@@ -29,9 +29,15 @@
|
|
|
29
29
|
"vite": "8.0.10",
|
|
30
30
|
"vite-plugin-html": "3.2.2",
|
|
31
31
|
"vite-plugin-pwa": "1.3.0",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
32
|
+
"workbox-cacheable-response": "7.4.1",
|
|
33
|
+
"workbox-core": "7.4.1",
|
|
34
|
+
"workbox-expiration": "7.4.1",
|
|
35
|
+
"workbox-precaching": "7.4.1",
|
|
36
|
+
"workbox-routing": "7.4.1",
|
|
37
|
+
"workbox-strategies": "7.4.1",
|
|
38
|
+
"@powerhousedao/shared": "6.2.2-dev.40",
|
|
39
|
+
"@powerhousedao/config": "6.2.2-dev.40",
|
|
40
|
+
"document-model": "6.2.2-dev.40"
|
|
35
41
|
},
|
|
36
42
|
"devDependencies": {
|
|
37
43
|
"@sentry/vite-plugin": "^4.3.0",
|