metaowl 0.4.0 → 0.5.0
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/CHANGELOG.md +52 -0
- package/README.md +13 -15
- package/build/runtime/bin/metaowl-build.js +10 -0
- package/{bin → build/runtime/bin}/metaowl-create.js +96 -177
- package/build/runtime/bin/metaowl-dev.js +10 -0
- package/build/runtime/bin/metaowl-generate.js +231 -0
- package/build/runtime/bin/metaowl-lint.js +58 -0
- package/build/runtime/bin/utils.js +68 -0
- package/build/runtime/index.js +141 -0
- package/build/runtime/modules/app-mounter.js +65 -0
- package/build/runtime/modules/auto-import.js +140 -0
- package/build/runtime/modules/cache.js +49 -0
- package/build/runtime/modules/composables.js +353 -0
- package/build/runtime/modules/error-boundary.js +116 -0
- package/build/runtime/modules/fetch.js +31 -0
- package/build/runtime/modules/file-router.js +205 -0
- package/build/runtime/modules/forms.js +193 -0
- package/build/runtime/modules/i18n.js +167 -0
- package/build/runtime/modules/layouts.js +163 -0
- package/build/runtime/modules/link.js +141 -0
- package/build/runtime/modules/meta.js +117 -0
- package/build/runtime/modules/odoo-rpc.js +264 -0
- package/build/runtime/modules/pwa.js +262 -0
- package/build/runtime/modules/router.js +389 -0
- package/build/runtime/modules/seo.js +186 -0
- package/build/runtime/modules/store.js +196 -0
- package/build/runtime/modules/templates-manager.js +52 -0
- package/build/runtime/modules/test-utils.js +238 -0
- package/build/runtime/vite/plugin.js +183 -0
- package/eslint.js +29 -0
- package/package.json +29 -11
- package/CONTRIBUTING.md +0 -49
- package/bin/metaowl-build.js +0 -12
- package/bin/metaowl-dev.js +0 -12
- package/bin/metaowl-generate.js +0 -339
- package/bin/metaowl-lint.js +0 -71
- package/bin/utils.js +0 -82
- package/index.js +0 -328
- package/modules/app-mounter.js +0 -104
- package/modules/auto-import.js +0 -225
- package/modules/cache.js +0 -59
- package/modules/composables.js +0 -600
- package/modules/error-boundary.js +0 -228
- package/modules/fetch.js +0 -51
- package/modules/file-router.js +0 -478
- package/modules/forms.js +0 -353
- package/modules/i18n.js +0 -333
- package/modules/layouts.js +0 -431
- package/modules/link.js +0 -255
- package/modules/meta.js +0 -119
- package/modules/odoo-rpc.js +0 -511
- package/modules/pwa.js +0 -515
- package/modules/router.js +0 -769
- package/modules/seo.js +0 -501
- package/modules/store.js +0 -409
- package/modules/templates-manager.js +0 -89
- package/modules/test-utils.js +0 -532
- package/test/auto-import.test.js +0 -110
- package/test/cache.test.js +0 -55
- package/test/composables.test.js +0 -103
- package/test/dynamic-routes.test.js +0 -469
- package/test/error-boundary.test.js +0 -126
- package/test/fetch.test.js +0 -100
- package/test/file-router.test.js +0 -55
- package/test/forms.test.js +0 -203
- package/test/i18n.test.js +0 -188
- package/test/layouts.test.js +0 -395
- package/test/link.test.js +0 -189
- package/test/meta.test.js +0 -146
- package/test/odoo-rpc.test.js +0 -547
- package/test/pwa.test.js +0 -154
- package/test/router-guards.test.js +0 -229
- package/test/router.test.js +0 -77
- package/test/seo.test.js +0 -353
- package/test/store.test.js +0 -476
- package/test/templates-manager.test.js +0 -83
- package/test/test-utils.test.js +0 -314
- package/vite/plugin.js +0 -277
- package/vitest.config.js +0 -8
package/modules/pwa.js
DELETED
|
@@ -1,515 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module PWA
|
|
3
|
-
*
|
|
4
|
-
* Progressive Web App utilities for MetaOwl applications.
|
|
5
|
-
*
|
|
6
|
-
* Features:
|
|
7
|
-
* - Web App Manifest generation
|
|
8
|
-
* - Service Worker registration and management
|
|
9
|
-
* - Offline/online status detection
|
|
10
|
-
* - Background sync helpers
|
|
11
|
-
* - Push notification utilities
|
|
12
|
-
* - Cache management for offline support
|
|
13
|
-
*
|
|
14
|
-
* Usage:
|
|
15
|
-
* import { PWA } from 'metaowl'
|
|
16
|
-
*
|
|
17
|
-
* // Generate manifest
|
|
18
|
-
* const manifest = PWA.generateManifest({
|
|
19
|
-
* name: 'My App',
|
|
20
|
-
* shortName: 'MyApp',
|
|
21
|
-
* themeColor: '#007bff'
|
|
22
|
-
* })
|
|
23
|
-
*
|
|
24
|
-
* // Register service worker
|
|
25
|
-
* await PWA.registerServiceWorker('/sw.js')
|
|
26
|
-
*
|
|
27
|
-
* // Check online status
|
|
28
|
-
* if (PWA.isOnline()) {
|
|
29
|
-
* // sync data
|
|
30
|
-
* }
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @typedef {Object} ManifestOptions
|
|
35
|
-
* @property {string} name - Full app name
|
|
36
|
-
* @property {string} shortName - Short name (max 12 chars)
|
|
37
|
-
* @property {string} [description] - App description
|
|
38
|
-
* @property {string} [startUrl='./'] - Start URL
|
|
39
|
-
* @property {string} [display='standalone'] - Display mode
|
|
40
|
-
* @property {string} [themeColor='#000000'] - Theme color
|
|
41
|
-
* @property {string} [backgroundColor='#ffffff'] - Background color
|
|
42
|
-
* @property {string} [scope='./'] - App scope
|
|
43
|
-
* @property {Array<{src: string, sizes: string, type: string}>} [icons] - App icons
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Generate Web App Manifest JSON.
|
|
48
|
-
*
|
|
49
|
-
* @param {ManifestOptions} options - Manifest options
|
|
50
|
-
* @returns {Object} Web App Manifest object
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* const manifest = generateManifest({
|
|
54
|
-
* name: 'My Awesome App',
|
|
55
|
-
* shortName: 'MyApp',
|
|
56
|
-
* description: 'A great app built with MetaOwl',
|
|
57
|
-
* themeColor: '#007bff',
|
|
58
|
-
* backgroundColor: '#ffffff',
|
|
59
|
-
* icons: [
|
|
60
|
-
* { src: '/icon-192.png', sizes: '192x192', type: 'image/png' },
|
|
61
|
-
* { src: '/icon-512.png', sizes: '512x512', type: 'image/png' }
|
|
62
|
-
* ]
|
|
63
|
-
* })
|
|
64
|
-
*/
|
|
65
|
-
export function generateManifest(options) {
|
|
66
|
-
const {
|
|
67
|
-
name,
|
|
68
|
-
shortName,
|
|
69
|
-
description,
|
|
70
|
-
startUrl = './',
|
|
71
|
-
display = 'standalone',
|
|
72
|
-
themeColor = '#000000',
|
|
73
|
-
backgroundColor = '#ffffff',
|
|
74
|
-
scope = './',
|
|
75
|
-
icons = []
|
|
76
|
-
} = options
|
|
77
|
-
|
|
78
|
-
const manifest = {
|
|
79
|
-
name,
|
|
80
|
-
short_name: shortName,
|
|
81
|
-
start_url: startUrl,
|
|
82
|
-
display,
|
|
83
|
-
theme_color: themeColor,
|
|
84
|
-
background_color: backgroundColor,
|
|
85
|
-
scope
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (description) {
|
|
89
|
-
manifest.description = description
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (icons.length > 0) {
|
|
93
|
-
manifest.icons = icons
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Add default orientation
|
|
97
|
-
manifest.orientation = 'any'
|
|
98
|
-
|
|
99
|
-
return manifest
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Register the service worker.
|
|
104
|
-
*
|
|
105
|
-
* @param {string} path - Service Worker script path
|
|
106
|
-
* @param {Object} options - Registration options
|
|
107
|
-
* @param {Function} [options.onUpdate] - Called when update found
|
|
108
|
-
* @param {Function} [options.onReady] - Called when SW is active
|
|
109
|
-
* @returns {Promise<ServiceWorkerRegistration|null>}
|
|
110
|
-
*
|
|
111
|
-
* @example
|
|
112
|
-
* await registerServiceWorker('/sw.js', {
|
|
113
|
-
* onUpdate: (registration) => {
|
|
114
|
-
* console.log('New version available!')
|
|
115
|
-
* },
|
|
116
|
-
* onReady: (registration) => {
|
|
117
|
-
* console.log('App is ready for offline use')
|
|
118
|
-
* }
|
|
119
|
-
* })
|
|
120
|
-
*/
|
|
121
|
-
export async function registerServiceWorker(path, options = {}) {
|
|
122
|
-
const { onUpdate, onReady } = options
|
|
123
|
-
|
|
124
|
-
if (!('serviceWorker' in navigator)) {
|
|
125
|
-
console.warn('[PWA] Service workers not supported')
|
|
126
|
-
return null
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
const registration = await navigator.serviceWorker.register(path)
|
|
131
|
-
|
|
132
|
-
registration.addEventListener('updatefound', () => {
|
|
133
|
-
const newWorker = registration.installing
|
|
134
|
-
|
|
135
|
-
newWorker.addEventListener('statechange', () => {
|
|
136
|
-
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
|
137
|
-
// New update available
|
|
138
|
-
if (onUpdate) {
|
|
139
|
-
onUpdate(registration)
|
|
140
|
-
}
|
|
141
|
-
} else if (newWorker.state === 'activated') {
|
|
142
|
-
// Service worker is active
|
|
143
|
-
if (onReady) {
|
|
144
|
-
onReady(registration)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
// Check if already active
|
|
151
|
-
if (registration.active) {
|
|
152
|
-
if (onReady) {
|
|
153
|
-
onReady(registration)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return registration
|
|
158
|
-
} catch (error) {
|
|
159
|
-
console.error('[PWA] Service worker registration failed:', error)
|
|
160
|
-
return null
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Unregister the service worker.
|
|
166
|
-
*
|
|
167
|
-
* @returns {Promise<boolean>} True if unregistered successfully
|
|
168
|
-
*/
|
|
169
|
-
export async function unregisterServiceWorker() {
|
|
170
|
-
if (!('serviceWorker' in navigator)) {
|
|
171
|
-
return false
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
try {
|
|
175
|
-
const registration = await navigator.serviceWorker.ready
|
|
176
|
-
await registration.unregister()
|
|
177
|
-
return true
|
|
178
|
-
} catch {
|
|
179
|
-
return false
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Check if app is running as installed PWA.
|
|
185
|
-
*
|
|
186
|
-
* @returns {boolean}
|
|
187
|
-
*/
|
|
188
|
-
export function isStandalone() {
|
|
189
|
-
// Check if matchMedia exists (browser environment)
|
|
190
|
-
if (window.matchMedia) {
|
|
191
|
-
return window.matchMedia('(display-mode: standalone)').matches ||
|
|
192
|
-
window.navigator?.standalone === true
|
|
193
|
-
}
|
|
194
|
-
return false
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Check if online.
|
|
199
|
-
*
|
|
200
|
-
* @returns {boolean}
|
|
201
|
-
*/
|
|
202
|
-
export function isOnline() {
|
|
203
|
-
return navigator.onLine
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Subscribe to online/offline events.
|
|
208
|
-
*
|
|
209
|
-
* @param {Object} callbacks - Callback functions
|
|
210
|
-
* @param {Function} callbacks.onOnline - Called when going online
|
|
211
|
-
* @param {Function} callbacks.onOffline - Called when going offline
|
|
212
|
-
* @returns {Function} Unsubscribe function
|
|
213
|
-
*
|
|
214
|
-
* @example
|
|
215
|
-
* const unsubscribe = subscribeToConnectivity({
|
|
216
|
-
* onOnline: () => console.log('Back online'),
|
|
217
|
-
* onOffline: () => console.log('Gone offline')
|
|
218
|
-
* })
|
|
219
|
-
*
|
|
220
|
-
* // Later: unsubscribe()
|
|
221
|
-
*/
|
|
222
|
-
export function subscribeToConnectivity(callbacks) {
|
|
223
|
-
const { onOnline, onOffline } = callbacks
|
|
224
|
-
|
|
225
|
-
const handleOnline = () => {
|
|
226
|
-
if (onOnline) onOnline()
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const handleOffline = () => {
|
|
230
|
-
if (onOffline) onOffline()
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
window.addEventListener('online', handleOnline)
|
|
234
|
-
window.addEventListener('offline', handleOffline)
|
|
235
|
-
|
|
236
|
-
return () => {
|
|
237
|
-
window.removeEventListener('online', handleOnline)
|
|
238
|
-
window.removeEventListener('offline', handleOffline)
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Request persistent storage.
|
|
244
|
-
*
|
|
245
|
-
* @returns {Promise<boolean>} True if persistent storage granted
|
|
246
|
-
*/
|
|
247
|
-
export async function requestPersistentStorage() {
|
|
248
|
-
if (navigator.storage && navigator.storage.persist) {
|
|
249
|
-
return await navigator.storage.persist()
|
|
250
|
-
}
|
|
251
|
-
return false
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Get storage usage information.
|
|
256
|
-
*
|
|
257
|
-
* @returns {Promise<{usage: number, quota: number}|null>}
|
|
258
|
-
*/
|
|
259
|
-
export async function getStorageInfo() {
|
|
260
|
-
if (navigator.storage && navigator.storage.estimate) {
|
|
261
|
-
return await navigator.storage.estimate()
|
|
262
|
-
}
|
|
263
|
-
return null
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Trigger a background sync event.
|
|
268
|
-
*
|
|
269
|
-
* @param {string} tag - Sync tag name
|
|
270
|
-
* @returns {Promise<boolean>} True if sync registered
|
|
271
|
-
*/
|
|
272
|
-
export async function sync(tag) {
|
|
273
|
-
if (!('serviceWorker' in navigator)) {
|
|
274
|
-
return false
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
try {
|
|
278
|
-
const registration = await navigator.serviceWorker.ready
|
|
279
|
-
|
|
280
|
-
if ('sync' in registration) {
|
|
281
|
-
await registration.sync.register(tag)
|
|
282
|
-
return true
|
|
283
|
-
}
|
|
284
|
-
} catch {
|
|
285
|
-
// Background sync not supported or failed
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return false
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Request notification permission and subscribe.
|
|
293
|
-
*
|
|
294
|
-
* @param {Object} options - Push options
|
|
295
|
-
* @param {string} options.serverUrl - Push server URL
|
|
296
|
-
* @param {string} options.publicKey - VAPID public key
|
|
297
|
-
* @returns {Promise<PushSubscription|null>}
|
|
298
|
-
*
|
|
299
|
-
* @example
|
|
300
|
-
* const subscription = await subscribeToPush({
|
|
301
|
-
* serverUrl: 'https://myserver.com/push',
|
|
302
|
-
* publicKey: 'BLCxhd...'
|
|
303
|
-
* })
|
|
304
|
-
*/
|
|
305
|
-
export async function subscribeToPush(options) {
|
|
306
|
-
const { serverUrl, publicKey } = options
|
|
307
|
-
|
|
308
|
-
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
|
|
309
|
-
console.warn('[PWA] Push notifications not supported')
|
|
310
|
-
return null
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
try {
|
|
314
|
-
// Check permission
|
|
315
|
-
const permission = await Notification.requestPermission()
|
|
316
|
-
if (permission !== 'granted') {
|
|
317
|
-
return null
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
const registration = await navigator.serviceWorker.ready
|
|
321
|
-
|
|
322
|
-
// Subscribe
|
|
323
|
-
const subscription = await registration.pushManager.subscribe({
|
|
324
|
-
userVisibleOnly: true,
|
|
325
|
-
applicationServerKey: urlBase64ToUint8Array(publicKey)
|
|
326
|
-
})
|
|
327
|
-
|
|
328
|
-
// Send to server
|
|
329
|
-
if (serverUrl) {
|
|
330
|
-
await fetch(serverUrl, {
|
|
331
|
-
method: 'POST',
|
|
332
|
-
headers: { 'Content-Type': 'application/json' },
|
|
333
|
-
body: JSON.stringify(subscription)
|
|
334
|
-
})
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return subscription
|
|
338
|
-
} catch (error) {
|
|
339
|
-
console.error('[PWA] Push subscription failed:', error)
|
|
340
|
-
return null
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Unsubscribe from push notifications.
|
|
346
|
-
*
|
|
347
|
-
* @returns {Promise<boolean>}
|
|
348
|
-
*/
|
|
349
|
-
export async function unsubscribeFromPush() {
|
|
350
|
-
if (!('serviceWorker' in navigator)) {
|
|
351
|
-
return false
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
try {
|
|
355
|
-
const registration = await navigator.serviceWorker.ready
|
|
356
|
-
const subscription = await registration.pushManager.getSubscription()
|
|
357
|
-
|
|
358
|
-
if (subscription) {
|
|
359
|
-
await subscription.unsubscribe()
|
|
360
|
-
return true
|
|
361
|
-
}
|
|
362
|
-
} catch {
|
|
363
|
-
// Ignore errors
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return false
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Show a notification.
|
|
371
|
-
*
|
|
372
|
-
* @param {string} title - Notification title
|
|
373
|
-
* @param {Object} options - Notification options
|
|
374
|
-
* @returns {Promise<void>}
|
|
375
|
-
*/
|
|
376
|
-
export async function showNotification(title, options = {}) {
|
|
377
|
-
if (!('serviceWorker' in navigator)) {
|
|
378
|
-
return
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
try {
|
|
382
|
-
const registration = await navigator.serviceWorker.ready
|
|
383
|
-
await registration.showNotification(title, options)
|
|
384
|
-
} catch (error) {
|
|
385
|
-
console.error('[PWA] Show notification failed:', error)
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Convert URL-safe base64 to Uint8Array.
|
|
391
|
-
*
|
|
392
|
-
* @param {string} base64String
|
|
393
|
-
* @returns {Uint8Array}
|
|
394
|
-
*/
|
|
395
|
-
function urlBase64ToUint8Array(base64String) {
|
|
396
|
-
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
|
|
397
|
-
const base64 = (base64String + padding)
|
|
398
|
-
.replace(/\-/g, '+')
|
|
399
|
-
.replace(/_/g, '/')
|
|
400
|
-
|
|
401
|
-
const rawData = window.atob(base64)
|
|
402
|
-
const outputArray = new Uint8Array(rawData.length)
|
|
403
|
-
|
|
404
|
-
for (let i = 0; i < rawData.length; ++i) {
|
|
405
|
-
outputArray[i] = rawData.charCodeAt(i)
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
return outputArray
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Cache management for PWA.
|
|
413
|
-
*/
|
|
414
|
-
export const cache = {
|
|
415
|
-
/**
|
|
416
|
-
* Add resources to cache.
|
|
417
|
-
*
|
|
418
|
-
* @param {string} cacheName - Cache name
|
|
419
|
-
* @param {string[]} urls - URLs to cache
|
|
420
|
-
* @returns {Promise<void>}
|
|
421
|
-
*/
|
|
422
|
-
async add(cacheName, urls) {
|
|
423
|
-
if (!('caches' in window)) return
|
|
424
|
-
|
|
425
|
-
const cache = await caches.open(cacheName)
|
|
426
|
-
await cache.addAll(urls)
|
|
427
|
-
},
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Remove resources from cache.
|
|
431
|
-
*
|
|
432
|
-
* @param {string} cacheName - Cache name
|
|
433
|
-
* @param {string[]} urls - URLs to remove
|
|
434
|
-
* @returns {Promise<void>}
|
|
435
|
-
*/
|
|
436
|
-
async remove(cacheName, urls) {
|
|
437
|
-
if (!('caches' in window)) return
|
|
438
|
-
|
|
439
|
-
const cache = await caches.open(cacheName)
|
|
440
|
-
for (const url of urls) {
|
|
441
|
-
await cache.delete(url)
|
|
442
|
-
}
|
|
443
|
-
},
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* Clear all caches.
|
|
447
|
-
*
|
|
448
|
-
* @returns {Promise<void>}
|
|
449
|
-
*/
|
|
450
|
-
async clear() {
|
|
451
|
-
if (!('caches' in window)) return
|
|
452
|
-
|
|
453
|
-
const keys = await caches.keys()
|
|
454
|
-
await Promise.all(keys.map(key => caches.delete(key)))
|
|
455
|
-
},
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Get cache info.
|
|
459
|
-
*
|
|
460
|
-
* @returns {Promise<Array<{name: string, size: number}>>}
|
|
461
|
-
*/
|
|
462
|
-
async info() {
|
|
463
|
-
if (!('caches' in window)) return []
|
|
464
|
-
|
|
465
|
-
const keys = await caches.keys()
|
|
466
|
-
const info = []
|
|
467
|
-
|
|
468
|
-
for (const key of keys) {
|
|
469
|
-
const cache = await caches.open(key)
|
|
470
|
-
const keys = await cache.keys()
|
|
471
|
-
info.push({ name: key, size: keys.length })
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
return info
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Check PWA capabilities.
|
|
480
|
-
*
|
|
481
|
-
* @returns {Object} Capability info
|
|
482
|
-
*/
|
|
483
|
-
export function checkCapabilities() {
|
|
484
|
-
return {
|
|
485
|
-
serviceWorker: 'serviceWorker' in navigator,
|
|
486
|
-
push: 'PushManager' in window,
|
|
487
|
-
notifications: 'Notification' in window,
|
|
488
|
-
backgroundSync: false, // Would require ServiceWorkerRegistration check in real browser
|
|
489
|
-
persistentStorage: !!(navigator.storage && navigator.storage.persist),
|
|
490
|
-
addToHomeScreen: !isStandalone(),
|
|
491
|
-
offline: 'serviceWorker' in navigator
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* PWA namespace for convenient access.
|
|
497
|
-
*/
|
|
498
|
-
export const PWA = {
|
|
499
|
-
generateManifest,
|
|
500
|
-
registerServiceWorker,
|
|
501
|
-
unregisterServiceWorker,
|
|
502
|
-
isStandalone,
|
|
503
|
-
isOnline,
|
|
504
|
-
subscribeToConnectivity,
|
|
505
|
-
requestPersistentStorage,
|
|
506
|
-
getStorageInfo,
|
|
507
|
-
sync,
|
|
508
|
-
subscribeToPush,
|
|
509
|
-
unsubscribeFromPush,
|
|
510
|
-
showNotification,
|
|
511
|
-
cache,
|
|
512
|
-
checkCapabilities
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
export default PWA
|