UNPKG

@stacksjs/stx

Version:

A performant UI Framework. Powered by Bun.

1,319 lines (1,144 loc) 523 kB
// @bun var{defineProperty:T7,getOwnPropertyNames:M1,getOwnPropertyDescriptor:T1}=Object,w1=Object.prototype.hasOwnProperty;var Q6=new WeakMap,K6=($)=>{var Z=Q6.get($),Y;if(Z)return Z;if(Z=T7({},"__esModule",{value:!0}),$&&typeof $==="object"||typeof $==="function")M1($).map((J)=>!w1.call(Z,J)&&T7(Z,J,{get:()=>$[J],enumerable:!(Y=T1($,J))||Y.enumerable}));return Q6.set($,Z),Z};var o=($,Z)=>{for(var Y in Z)T7($,Y,{get:Z[Y],enumerable:!0,configurable:!0,set:(J)=>Z[Y]=()=>J})};var I=($,Z)=>()=>($&&(Z=$($=0)),Z);var t=import.meta.require;import I5 from"fs";import D5 from"path";function E1($){let Z=0;for(let Y=0;Y<$.length;Y++){let J=$[Y];Z=(Z<<5)-Z+J,Z=Z&Z}return Math.abs(Z).toString(16).padStart(8,"0")}function k1($,Z){let Y=D5.basename($);for(let J of Z)if(J.startsWith("*.")){let G=J.slice(1);if(Y.endsWith(G))return!0}else if(J.includes("*")){let G=new RegExp(`^${J.replace(/\*/g,".*")}$`);if(G.test(Y)||G.test($))return!0}else if(Y===J||$.includes(J))return!0;return!1}function S1($,Z,Y){let J=D5.extname($).toLowerCase(),G=D1[J],X=(Y.maxFileSizeKB||500)*1024;if(Z>X)return!1;if(Y.exclude&&k1($,Y.exclude))return!1;if(Y.include&&Y.include.length>0){for(let Q of Y.include)if(Q.startsWith("*.")){if($.endsWith(Q.slice(1)))return!0}else if($.includes(Q))return!0}if(G&&Y[G]===!0)return!0;if(!G)return!1;return!1}function z6($,Z,Y,J){let G=I5.readdirSync($,{withFileTypes:!0});for(let X of G){let Q=D5.join($,X.name);if(X.isDirectory()){if(["node_modules",".git",".stx"].includes(X.name))continue;z6(Q,Z,Y,J)}else if(X.isFile()){let K=I5.statSync(Q),q=D5.relative(Z,Q);if(S1(q,K.size,Y)){let _=I5.readFileSync(Q),j=E1(_),B=`/${q.replace(/\\/g,"/")}`;J.push({url:B,revision:j,size:K.size})}}}}function b4($,Z){let Y=Z.pwa?.precache,J=[];if(!Y?.enabled)return{entries:[],totalSize:0,fileCount:0};if(!I5.existsSync($))return{entries:[],totalSize:0,fileCount:0};z6($,$,Y,J),J.sort((X,Q)=>X.url.localeCompare(Q.url));let G=J.reduce((X,Q)=>X+Q.size,0);return{entries:J,totalSize:G,fileCount:J.length}}function E5($){if($.entries.length===0)return"[]";return`[ ${$.entries.map((Y)=>` { url: '${Y.url}', revision: '${Y.revision}' }`).join(`, `)} ]`}function H6($){if($<1024)return`${$} B`;if($<1048576)return`${($/1024).toFixed(1)} KB`;return`${($/1048576).toFixed(2)} MB`}var D1;var G5=I(()=>{D1={".html":"includeHtml",".htm":"includeHtml",".js":"includeJs",".mjs":"includeJs",".css":"includeCss",".png":"includeImages",".jpg":"includeImages",".jpeg":"includeImages",".gif":"includeImages",".webp":"includeImages",".svg":"includeImages",".ico":"includeImages",".woff":"includeFonts",".woff2":"includeFonts",".ttf":"includeFonts",".eot":"includeFonts",".otf":"includeFonts"}});function b1($){let Z={"cache-first":{className:"CacheFirst",importFrom:"workbox-strategies"},"network-first":{className:"NetworkFirst",importFrom:"workbox-strategies"},"stale-while-revalidate":{className:"StaleWhileRevalidate",importFrom:"workbox-strategies"},"network-only":{className:"NetworkOnly",importFrom:"workbox-strategies"},"cache-only":{className:"CacheOnly",importFrom:"workbox-strategies"}};return Z[$]||Z["network-first"]}function w7($){return b1($).className}function O6($){let Z=$.pwa;if(!Z?.enabled)return"";let Y=Z.routes||[],J=Z.cacheStorage,G=[];for(let X of Y){let Q=w7(X.strategy),K=[];if(X.maxAgeSeconds||X.maxEntries||J?.maxEntries){let _=X.maxEntries||J?.maxEntries||100,j=X.maxAgeSeconds||(J?.maxAge?J.maxAge*24*60*60:void 0);K.push(`new ExpirationPlugin({ maxEntries: ${_}, ${j?`maxAgeSeconds: ${j},`:""} purgeOnQuotaError: true, })`)}if(X.strategy==="network-first"||X.strategy==="stale-while-revalidate")K.push(`new CacheableResponsePlugin({ statuses: [0, 200], })`);let q=P1(X.pattern);G.push(` // ${X.pattern} registerRoute( ${q}, new ${Q}({ cacheName: '${X.cacheName||C1(X.pattern)}', ${K.length>0?`plugins: [ ${K.join(`, `)} ],`:""} }) );`)}return G.join(` `)}function P1($){if($.startsWith("*.")){let Z=$.slice(2);return`({ request }) => request.destination === '${y1(Z)}'`}if($.endsWith("/**"))return`({ url }) => url.pathname.startsWith('${$.slice(0,-3)}')`;if($.endsWith("/*")){let Z=$.slice(0,-2);return`({ url }) => url.pathname.startsWith('${Z}') && !url.pathname.slice(${Z.length+1}).includes('/')`}if($.includes("*")&&!$.startsWith("*")){let[Z,Y]=$.split("*");return`({ url }) => url.pathname.startsWith('${Z}') && url.pathname.endsWith('${Y}')`}if($==="/"||!$.includes("*"))return`({ url }) => url.pathname === '${$}'`;return`new RegExp('${v1($).replace(/\\\*/g,".*")}')`}function y1($){return{js:"script",mjs:"script",css:"style",html:"document",htm:"document",png:"image",jpg:"image",jpeg:"image",gif:"image",svg:"image",webp:"image",avif:"image",ico:"image",woff:"font",woff2:"font",ttf:"font",eot:"font",otf:"font"}[$.toLowerCase()]||"empty"}function C1($){return"stx-"+$.replace(/[^\w]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").toLowerCase().slice(0,30)+"-cache"}function v1($){return $.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var A6={};o(A6,{isWorkboxEnabled:()=>R6,getServiceWorkerGenerator:()=>d1,generateWorkboxServiceWorker:()=>F6,generateWorkboxConfig:()=>L6});function L6($,Z){let Y=$.pwa;if(!Y?.enabled)return{mode:"generate",modules:[],skipWaiting:!0,clientsClaim:!0};let J=Y.serviceWorker||{},G=Y.routes||[],X=[{name:"workbox-precaching",exports:["precacheAndRoute","cleanupOutdatedCaches"]},{name:"workbox-routing",exports:["registerRoute","NavigationRoute","setDefaultHandler"]}],Q=new Set;for(let _ of G)Q.add(w7(_.strategy));if(Q.size>0)X.push({name:"workbox-strategies",exports:Array.from(Q)});if(G.some((_)=>_.maxAgeSeconds||_.maxEntries)||Y.cacheStorage)X.push({name:"workbox-expiration",exports:["ExpirationPlugin"]});if(G.some((_)=>_.strategy==="network-first"||_.strategy==="stale-while-revalidate"))X.push({name:"workbox-cacheable-response",exports:["CacheableResponsePlugin"]});if(Y.backgroundSync?.enabled)X.push({name:"workbox-background-sync",exports:["BackgroundSyncPlugin","Queue"]});if(J.navigationPreload)X.push({name:"workbox-navigation-preload",exports:["enable"]});let q=Z?b4(Z,$):void 0;return{mode:"generate",modules:X,precacheManifest:q?.entries,skipWaiting:J.skipWaiting??!0,clientsClaim:J.clientsClaim??!0,offlineFallback:Y.offline?.enabled?"/offline.html":void 0,navigationPreload:J.navigationPreload??!1,cacheVersion:J.cacheVersion||"1.0.0"}}function F6($,Z){let Y=$.pwa;if(!Y?.enabled)return"";let J=L6($,Z),G=Y.serviceWorker||{},X=Y.push,Q=Y.backgroundSync,K=Z?b4(Z,$):null,q=K?E5(K):"[]",_=x1(J.modules),j=O6($);return`/** * Service Worker - Generated by stx with Workbox * Cache Version: ${J.cacheVersion} * * This service worker uses Google's Workbox library for robust caching. * https://developers.google.com/web/tools/workbox * * Features: * - Precaching with automatic revision management * - Runtime caching with multiple strategies * - Offline fallback support * - Push notifications: ${X?.enabled?"enabled":"disabled"} * - Background sync: ${Q?.enabled?"enabled":"disabled"} */ // Import Workbox modules from CDN importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.0.0/workbox-sw.js'); // Disable Workbox logging in production workbox.setConfig({ debug: true }); // Extract modules from workbox global ${_} const CACHE_VERSION = '${J.cacheVersion}'; const OFFLINE_PAGE = '${J.offlineFallback||"/offline.html"}'; // ============================================================================ // Precaching // ============================================================================ // Clean up outdated caches from previous versions cleanupOutdatedCaches(); // Precache static assets // The manifest is injected during build or defined below const PRECACHE_MANIFEST = ${q}; // Add essential assets PRECACHE_MANIFEST.push( { url: '/', revision: CACHE_VERSION }, { url: '/manifest.json', revision: CACHE_VERSION }, ${J.offlineFallback?`{ url: '${J.offlineFallback}', revision: CACHE_VERSION },`:""} ); // Precache and route all manifest entries precacheAndRoute(PRECACHE_MANIFEST); // ============================================================================ // Navigation Preload // ============================================================================ ${J.navigationPreload?`// Enable navigation preload for faster page loads enable();`:"// Navigation preload disabled"} // ============================================================================ // Runtime Caching // ============================================================================ ${j} // ============================================================================ // Offline Fallback // ============================================================================ ${J.offlineFallback?` // Set up offline fallback for navigation requests const navigationRoute = new NavigationRoute( new NetworkFirst({ cacheName: 'stx-pages-cache', plugins: [ new CacheableResponsePlugin({ statuses: [0, 200] }), ], }), { // Return offline page when navigation fails async allowedHandler(request) { try { const response = await this.handle(request); return response; } catch (error) { const cache = await caches.open('stx-offline-cache'); return cache.match(OFFLINE_PAGE) || new Response('You are offline', { status: 503, statusText: 'Service Unavailable', }); } }, } ); registerRoute(navigationRoute); `:"// No offline fallback configured"} // Default handler for unmatched requests setDefaultHandler(new NetworkFirst({ cacheName: 'stx-default-cache', plugins: [ new CacheableResponsePlugin({ statuses: [0, 200] }), new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 60 * 60 * 24 }), // 1 day ], })); // ============================================================================ // Service Worker Lifecycle // ============================================================================ ${J.skipWaiting?`// Skip waiting - activate immediately self.skipWaiting();`:"// Manual skipWaiting - user must trigger update"} ${J.clientsClaim?`// Claim clients - take control immediately self.addEventListener('activate', () => { self.clients.claim(); });`:"// Manual clients claim"} // ============================================================================ // Message Handling // ============================================================================ self.addEventListener('message', (event) => { const { type, payload } = event.data || {}; switch (type) { case 'SKIP_WAITING': self.skipWaiting(); break; case 'GET_VERSION': event.source?.postMessage({ type: 'VERSION', payload: CACHE_VERSION }); break; case 'CACHE_URLS': if (Array.isArray(payload)) { event.waitUntil( caches.open('stx-dynamic-cache').then(cache => cache.addAll(payload)) ); } break; case 'CLEAR_CACHE': event.waitUntil( caches.keys().then(names => Promise.all(names.map(name => caches.delete(name))) ) ); break; } }); ${X?.enabled?g1(X):"// Push notifications disabled"} ${Q?.enabled?h1(Q):"// Background sync disabled"} console.log('[Workbox SW] Service Worker loaded - Version:', CACHE_VERSION); `}function x1($){let Z=[];for(let Y of $){let J=Y.name.replace("workbox-","");Z.push(`const { ${Y.exports.join(", ")} } = workbox.${f1(J)};`)}return Z.join(` `)}function f1($){return $.replace(/-([a-z])/g,(Z,Y)=>Y.toUpperCase())}function g1($){return` // ============================================================================ // Push Notifications // ============================================================================ const PUSH_CONFIG = { defaultIcon: '${$?.defaultIcon||"/pwa-icons/icon-192x192.png"}', defaultBadge: '${$?.defaultBadge||"/pwa-icons/icon-72x72.png"}', }; self.addEventListener('push', (event) => { console.log('[Workbox SW] Push notification received'); let data = { title: 'Notification', body: 'You have a new notification', icon: PUSH_CONFIG.defaultIcon, badge: PUSH_CONFIG.defaultBadge, }; if (event.data) { try { const payload = event.data.json(); data = { ...data, ...payload }; } catch { data.body = event.data.text(); } } const options = { body: data.body, icon: data.icon || PUSH_CONFIG.defaultIcon, badge: data.badge || PUSH_CONFIG.defaultBadge, vibrate: data.vibrate || [100, 50, 100], data: { url: data.url || '/', ...data.data, }, actions: data.actions || [], tag: data.tag, renotify: data.renotify || false, requireInteraction: data.requireInteraction || false, }; event.waitUntil( self.registration.showNotification(data.title, options) ); }); self.addEventListener('notificationclick', (event) => { console.log('[Workbox SW] Notification clicked'); event.notification.close(); const url = event.notification.data?.url || '/'; event.waitUntil( clients.matchAll({ type: 'window', includeUncontrolled: true }).then(windowClients => { for (const client of windowClients) { if (client.url === url && 'focus' in client) { return client.focus(); } } if (clients.openWindow) { return clients.openWindow(url); } }) ); }); `}function h1($){return` // ============================================================================ // Background Sync // ============================================================================ const syncQueue = new Queue('${$?.queueName||"stx-sync-queue"}', { maxRetentionTime: ${$?.maxRetentionMinutes||1440}, // minutes onSync: async ({ queue }) => { let entry; while ((entry = await queue.shiftRequest())) { try { await fetch(entry.request.clone()); console.log('[Workbox SW] Sync succeeded:', entry.request.url); // Notify clients const allClients = await self.clients.matchAll(); allClients.forEach(client => { client.postMessage({ type: 'SYNC_COMPLETE', url: entry.request.url, success: true, }); }); } catch (error) { console.log('[Workbox SW] Sync failed, re-queuing:', entry.request.url); await queue.unshiftRequest(entry); throw error; } } }, }); // Register routes that should use background sync ${$?.endpoints?.map((Z)=>` registerRoute( ({ url }) => url.pathname.startsWith('${Z}'), new NetworkOnly({ plugins: [ new BackgroundSyncPlugin('${$?.queueName||"stx-sync-queue"}', { maxRetentionTime: ${$?.maxRetentionMinutes||1440}, }), ], }), 'POST' );`).join(` `)||"// No background sync endpoints configured"} // Handle form sync messages self.addEventListener('message', (event) => { if (event.data?.type === 'REGISTER_FORM_SYNC') { const { action, method, data } = event.data.payload; const request = new Request(action, { method: method || 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams(data).toString(), }); event.waitUntil(syncQueue.pushRequest({ request })); } }); `}function R6($){return $.pwa?.enabled===!0&&$.pwa?.serviceWorker?.useWorkbox===!0}function d1($){if(R6($))return F6;let{generateServiceWorker:Z}=(Q5(),K6(V6));return Z}var N6=I(()=>{G5()});var V6={};o(V6,{isWorkboxEnabled:()=>M6,getServiceWorkerFileName:()=>m4,generateServiceWorkerAuto:()=>p1,generateServiceWorker:()=>k5});function k5($,Z){let Y=$.pwa;if(!Y?.enabled)return"";let J=Y.serviceWorker||{},G=Y.routes||[],X=J.cacheVersion||"1.0.0",Q=Y.offline,K=Q?.page?"/offline.html":"/offline.html",q=Y.push,_=Y.backgroundSync,j=Y.cacheStorage,B=Z?b4(Z,$):null,U=B?E5(B):"[]",z=["/","/manifest.json",...Q?.enabled?[K]:[],...Q?.precacheAssets||[]];return`/** * Service Worker - Auto-generated by stx * Cache Version: ${X} * * This service worker implements configurable caching strategies: * - cache-first: Serve from cache, fall back to network * - network-first: Try network, fall back to cache * - stale-while-revalidate: Serve cached, update in background * - network-only: Always fetch from network * - cache-only: Only serve from cache * * Additional features: * - Push notifications: ${q?.enabled?"enabled":"disabled"} * - Background sync: ${_?.enabled?"enabled":"disabled"} * - Cache limits: ${j?.maxSize?`${H6(j.maxSize*1024*1024)}`:"unlimited"} */ const CACHE_VERSION = '${X}'; const CACHE_NAME = 'stx-pwa-v' + CACHE_VERSION; const OFFLINE_PAGE = '${K}'; // Assets to precache on install const PRECACHE_ASSETS = ${JSON.stringify(z,null,2)}; // Auto-generated precache manifest from build output const BUILD_PRECACHE_MANIFEST = ${U}; // Route caching strategies const ROUTE_STRATEGIES = ${JSON.stringify(G,null,2)}; // Routes to exclude from caching const EXCLUDED_ROUTES = ${JSON.stringify(J.excludeRoutes||[],null,2)}; // Cache storage configuration const CACHE_CONFIG = { maxSize: ${j?.maxSize||0} * 1024 * 1024, // Convert MB to bytes maxAge: ${j?.maxAge||0} * 24 * 60 * 60 * 1000, // Convert days to ms maxEntries: ${j?.maxEntries||0}, purgeStrategy: '${j?.purgeStrategy||"lru"}', }; // Background sync configuration const SYNC_CONFIG = { enabled: ${_?.enabled||!1}, queueName: '${_?.queueName||"stx-sync-queue"}', maxRetries: ${_?.maxRetries||3}, minInterval: ${_?.minInterval||1000}, }; // Push notification configuration const PUSH_CONFIG = { enabled: ${q?.enabled||!1}, defaultIcon: '${q?.defaultIcon||"/pwa-icons/icon-192x192.png"}', defaultBadge: '${q?.defaultBadge||"/pwa-icons/icon-72x72.png"}', }; /** * Install event - precache essential assets */ self.addEventListener('install', (event) => { console.log('[SW] Installing service worker...'); ${J.skipWaiting?"self.skipWaiting();":""} event.waitUntil( caches.open(CACHE_NAME).then(async (cache) => { // Combine manual precache assets with auto-generated manifest const allAssets = [...PRECACHE_ASSETS]; // Add build precache manifest entries for (const entry of BUILD_PRECACHE_MANIFEST) { if (!allAssets.includes(entry.url)) { allAssets.push(entry.url); } } console.log('[SW] Precaching assets:', allAssets.length); // Cache assets one by one to handle failures gracefully const results = await Promise.allSettled( allAssets.map(async (url) => { try { const response = await fetch(url); if (response.ok) { await cache.put(url, response); return { url, success: true }; } return { url, success: false, reason: response.status }; } catch (error) { return { url, success: false, reason: error.message }; } }) ); const failed = results.filter(r => r.status === 'rejected' || (r.status === 'fulfilled' && !r.value.success)); if (failed.length > 0) { console.warn('[SW] Failed to precache some assets:', failed.length); } }) ); }); /** * Activate event - clean up old caches */ self.addEventListener('activate', (event) => { console.log('[SW] Activating service worker...'); ${J.clientsClaim?"self.clients.claim();":""} event.waitUntil( (async () => { // Clean up old caches const cacheNames = await caches.keys(); await Promise.all( cacheNames .filter((name) => name.startsWith('stx-pwa-') && name !== CACHE_NAME) .map((name) => { console.log('[SW] Deleting old cache:', name); return caches.delete(name); }) ); // Enforce cache storage limits if (CACHE_CONFIG.maxSize > 0 || CACHE_CONFIG.maxEntries > 0) { await enforceCacheLimits(); } })() ); }); /** * Fetch event - apply caching strategies */ self.addEventListener('fetch', (event) => { const { request } = event; const url = new URL(request.url); // Only handle GET requests if (request.method !== 'GET') { return; } // Skip cross-origin requests if (url.origin !== location.origin) { return; } // Skip excluded routes if (isExcludedRoute(url.pathname)) { return; } // Find matching strategy and apply const routeConfig = getRouteConfig(url.pathname); event.respondWith(applyStrategy(request, routeConfig)); }); /** * Message event - handle messages from main thread */ self.addEventListener('message', (event) => { const { type, payload } = event.data || {}; switch (type) { case 'SKIP_WAITING': console.log('[SW] Received SKIP_WAITING message'); self.skipWaiting(); break; case 'CACHE_URLS': if (Array.isArray(payload)) { event.waitUntil( caches.open(CACHE_NAME).then((cache) => cache.addAll(payload)) ); } break; case 'CLEAR_CACHE': event.waitUntil( caches.keys().then((names) => Promise.all(names.map((name) => caches.delete(name))) ) ); break; case 'GET_CACHE_SIZE': event.waitUntil( getCacheSize().then((size) => { event.source?.postMessage({ type: 'CACHE_SIZE', payload: size }); }) ); break; } }); ${q?.enabled?m1():"// Push notifications disabled"} ${_?.enabled?u1():"// Background sync disabled"} /** * Check if a route should be excluded from caching */ function isExcludedRoute(pathname) { return EXCLUDED_ROUTES.some((pattern) => matchPattern(pathname, pattern)); } /** * Match a path against a glob pattern * Supports: *.ext, /path/*, /path/**, exact matches */ function matchPattern(path, pattern) { // Handle file extension patterns like *.js, *.css if (pattern.startsWith('*.')) { return path.endsWith(pattern.slice(1)); } // Handle patterns ending with /** if (pattern.endsWith('/**')) { const prefix = pattern.slice(0, -3); return path.startsWith(prefix); } // Handle patterns ending with /* if (pattern.endsWith('/*')) { const prefix = pattern.slice(0, -2); if (!path.startsWith(prefix)) return false; const rest = path.slice(prefix.length); return rest.length > 0 && !rest.slice(1).includes('/'); } // Handle patterns with * in the middle if (pattern.includes('*')) { const parts = pattern.split('*'); if (parts.length === 2) { return path.startsWith(parts[0]) && path.endsWith(parts[1]); } } // Exact match return path === pattern; } /** * Get the cache configuration for a given path */ function getRouteConfig(pathname) { for (const route of ROUTE_STRATEGIES) { if (matchPattern(pathname, route.pattern)) { return route; } } // Default to network-first for unmatched routes return { strategy: 'network-first' }; } /** * Apply the appropriate caching strategy */ async function applyStrategy(request, routeConfig) { const { strategy, cacheName, maxAgeSeconds, maxEntries } = routeConfig; const cache = await caches.open(cacheName || CACHE_NAME); switch (strategy) { case 'cache-first': return cacheFirst(request, cache, maxAgeSeconds); case 'network-first': return networkFirst(request, cache); case 'stale-while-revalidate': return staleWhileRevalidate(request, cache); case 'network-only': return networkOnly(request); case 'cache-only': return cacheOnly(request, cache); default: return networkFirst(request, cache); } } /** * Cache-first strategy * Serve from cache if available, otherwise fetch from network */ async function cacheFirst(request, cache, maxAge) { const cached = await cache.match(request); if (cached) { // Check if cache is still valid (if maxAge is set) if (maxAge) { const cachedDate = cached.headers.get('sw-cache-date'); if (cachedDate) { const age = (Date.now() - new Date(cachedDate).getTime()) / 1000; if (age > maxAge) { // Cache expired, fetch fresh return fetchAndCache(request, cache); } } } return cached; } return fetchAndCache(request, cache); } /** * Network-first strategy * Try network first, fall back to cache if offline */ async function networkFirst(request, cache) { try { const response = await fetch(request); if (response.ok) { const cloned = response.clone(); cache.put(request, cloned); } return response; } catch (error) { const cached = await cache.match(request); if (cached) { return cached; } return offlineResponse(request); } } /** * Stale-while-revalidate strategy * Serve cached immediately, update cache in background */ async function staleWhileRevalidate(request, cache) { const cached = await cache.match(request); // Fetch in background to update cache const fetchPromise = fetch(request) .then((response) => { if (response.ok) { cache.put(request, response.clone()); } return response; }) .catch(() => null); // Return cached immediately if available, otherwise wait for fetch if (cached) { return cached; } const response = await fetchPromise; return response || offlineResponse(request); } /** * Network-only strategy * Always fetch from network, no caching */ async function networkOnly(request) { try { return await fetch(request); } catch (error) { return offlineResponse(request); } } /** * Cache-only strategy * Only serve from cache, never fetch */ async function cacheOnly(request, cache) { const cached = await cache.match(request); return cached || offlineResponse(request); } /** * Fetch and cache a request */ async function fetchAndCache(request, cache) { try { const response = await fetch(request); if (response.ok) { // Add cache timestamp header const headers = new Headers(response.headers); headers.set('sw-cache-date', new Date().toISOString()); const cachedResponse = new Response(response.clone().body, { status: response.status, statusText: response.statusText, headers, }); cache.put(request, cachedResponse); } return response; } catch (error) { return offlineResponse(request); } } /** * Generate offline response */ async function offlineResponse(request) { // For navigation requests, return offline page if (request.mode === 'navigate') { const cache = await caches.open(CACHE_NAME); const offlinePage = await cache.match(OFFLINE_PAGE); if (offlinePage) { return offlinePage; } } // Return a basic offline response return new Response('You are offline', { status: 503, statusText: 'Service Unavailable', headers: { 'Content-Type': 'text/plain', }, }); } /** * Get total cache size */ async function getCacheSize() { let totalSize = 0; const cacheNames = await caches.keys(); for (const cacheName of cacheNames) { const cache = await caches.open(cacheName); const keys = await cache.keys(); for (const request of keys) { const response = await cache.match(request); if (response) { const blob = await response.clone().blob(); totalSize += blob.size; } } } return totalSize; } /** * Enforce cache storage limits */ async function enforceCacheLimits() { const cache = await caches.open(CACHE_NAME); const keys = await cache.keys(); // Get cache entries with metadata const entries = []; for (const request of keys) { const response = await cache.match(request); if (response) { const blob = await response.clone().blob(); const cachedDate = response.headers.get('sw-cache-date'); entries.push({ request, size: blob.size, date: cachedDate ? new Date(cachedDate) : new Date(), }); } } // Check total size limit if (CACHE_CONFIG.maxSize > 0) { const totalSize = entries.reduce((sum, e) => sum + e.size, 0); if (totalSize > CACHE_CONFIG.maxSize) { console.log('[SW] Cache size exceeded, purging...'); await purgeCache(entries, cache, CACHE_CONFIG.maxSize); } } // Check max entries limit if (CACHE_CONFIG.maxEntries > 0 && entries.length > CACHE_CONFIG.maxEntries) { console.log('[SW] Cache entries exceeded, purging...'); await purgeByEntryCount(entries, cache, CACHE_CONFIG.maxEntries); } // Check max age limit if (CACHE_CONFIG.maxAge > 0) { const now = Date.now(); const expiredEntries = entries.filter(e => (now - e.date.getTime()) > CACHE_CONFIG.maxAge); for (const entry of expiredEntries) { console.log('[SW] Removing expired cache entry:', entry.request.url); await cache.delete(entry.request); } } } /** * Purge cache to meet size limit */ async function purgeCache(entries, cache, maxSize) { // Sort by purge strategy const sorted = [...entries]; switch (CACHE_CONFIG.purgeStrategy) { case 'lru': // Least Recently Used - remove oldest first sorted.sort((a, b) => a.date.getTime() - b.date.getTime()); break; case 'fifo': // First In First Out - same as lru for our purposes sorted.sort((a, b) => a.date.getTime() - b.date.getTime()); break; case 'lfu': // Least Frequently Used - we don't track frequency, use size as proxy sorted.sort((a, b) => b.size - a.size); break; default: sorted.sort((a, b) => a.date.getTime() - b.date.getTime()); } let currentSize = entries.reduce((sum, e) => sum + e.size, 0); for (const entry of sorted) { if (currentSize <= maxSize * 0.8) { // Keep 80% buffer break; } // Don't delete essential assets const url = entry.request.url; if (PRECACHE_ASSETS.some(asset => url.endsWith(asset))) { continue; } console.log('[SW] Purging cache entry:', url); await cache.delete(entry.request); currentSize -= entry.size; } } /** * Purge cache by entry count */ async function purgeByEntryCount(entries, cache, maxEntries) { const sorted = [...entries].sort((a, b) => a.date.getTime() - b.date.getTime()); const toRemove = sorted.slice(0, entries.length - maxEntries); for (const entry of toRemove) { // Don't delete essential assets const url = entry.request.url; if (PRECACHE_ASSETS.some(asset => url.endsWith(asset))) { continue; } await cache.delete(entry.request); } } console.log('[SW] Service Worker loaded - Version:', CACHE_VERSION); `}function m1(){return` /** * Push event - handle incoming push notifications */ self.addEventListener('push', (event) => { console.log('[SW] Push notification received'); let data = { title: 'Notification', body: 'You have a new notification', icon: PUSH_CONFIG.defaultIcon, badge: PUSH_CONFIG.defaultBadge, }; if (event.data) { try { const payload = event.data.json(); data = { ...data, ...payload }; } catch { data.body = event.data.text(); } } const options = { body: data.body, icon: data.icon || PUSH_CONFIG.defaultIcon, badge: data.badge || PUSH_CONFIG.defaultBadge, vibrate: data.vibrate || [100, 50, 100], data: { url: data.url || '/', ...data.data, }, actions: data.actions || [], tag: data.tag, renotify: data.renotify || false, requireInteraction: data.requireInteraction || false, silent: data.silent || false, }; event.waitUntil( self.registration.showNotification(data.title, options) ); }); /** * Notification click event - handle notification interactions */ self.addEventListener('notificationclick', (event) => { console.log('[SW] Notification clicked:', event.action); event.notification.close(); const url = event.notification.data?.url || '/'; event.waitUntil( clients.matchAll({ type: 'window', includeUncontrolled: true }).then((windowClients) => { // Check if there's already a window open with this URL for (const client of windowClients) { if (client.url === url && 'focus' in client) { return client.focus(); } } // Open a new window if (clients.openWindow) { return clients.openWindow(url); } }) ); }); /** * Notification close event */ self.addEventListener('notificationclose', (event) => { console.log('[SW] Notification closed'); // Track notification dismissal if needed const data = event.notification.data; if (data?.trackDismiss) { // Could send analytics here } }); `}function u1(){return` /** * Background sync queue storage (IndexedDB) */ const SYNC_STORE = 'stx-sync-store'; let syncDb = null; async function getSyncDb() { if (syncDb) return syncDb; return new Promise((resolve, reject) => { const request = indexedDB.open(SYNC_STORE, 1); request.onerror = () => reject(request.error); request.onsuccess = () => { syncDb = request.result; resolve(syncDb); }; request.onupgradeneeded = (event) => { const db = event.target.result; if (!db.objectStoreNames.contains('queue')) { db.createObjectStore('queue', { keyPath: 'id', autoIncrement: true }); } }; }); } /** * Add request to sync queue */ async function addToSyncQueue(request) { const db = await getSyncDb(); const tx = db.transaction('queue', 'readwrite'); const store = tx.objectStore('queue'); const data = { url: request.url, method: request.method, headers: Object.fromEntries(request.headers.entries()), body: await request.clone().text(), timestamp: Date.now(), retries: 0, }; return new Promise((resolve, reject) => { const req = store.add(data); req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); } /** * Get all pending sync requests */ async function getPendingSyncRequests() { const db = await getSyncDb(); const tx = db.transaction('queue', 'readonly'); const store = tx.objectStore('queue'); return new Promise((resolve, reject) => { const req = store.getAll(); req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); } /** * Remove request from sync queue */ async function removeSyncRequest(id) { const db = await getSyncDb(); const tx = db.transaction('queue', 'readwrite'); const store = tx.objectStore('queue'); return new Promise((resolve, reject) => { const req = store.delete(id); req.onsuccess = () => resolve(); req.onerror = () => reject(req.error); }); } /** * Update sync request retry count */ async function updateSyncRetries(id, retries) { const db = await getSyncDb(); const tx = db.transaction('queue', 'readwrite'); const store = tx.objectStore('queue'); const req = store.get(id); req.onsuccess = () => { const data = req.result; if (data) { data.retries = retries; store.put(data); } }; } /** * Sync event - process queued requests when online */ self.addEventListener('sync', (event) => { console.log('[SW] Sync event:', event.tag); if (event.tag === SYNC_CONFIG.queueName || event.tag === 'stx-form-sync') { event.waitUntil(processBackgroundSync()); } }); /** * Process all queued sync requests */ async function processBackgroundSync() { console.log('[SW] Processing background sync queue'); const requests = await getPendingSyncRequests(); console.log('[SW] Pending sync requests:', requests.length); for (const request of requests) { if (request.retries >= SYNC_CONFIG.maxRetries) { console.log('[SW] Max retries reached, removing:', request.url); await removeSyncRequest(request.id); continue; } try { const response = await fetch(request.url, { method: request.method, headers: request.headers, body: request.method !== 'GET' ? request.body : undefined, }); if (response.ok) { console.log('[SW] Sync succeeded:', request.url); await removeSyncRequest(request.id); // Notify clients of successful sync const clients = await self.clients.matchAll(); clients.forEach(client => { client.postMessage({ type: 'SYNC_COMPLETE', url: request.url, success: true, }); }); } else { throw new Error('Response not ok: ' + response.status); } } catch (error) { console.log('[SW] Sync failed, will retry:', request.url, error.message); await updateSyncRetries(request.id, request.retries + 1); } } } /** * Register a form for background sync * Called from main thread via message */ async function registerFormSync(formData) { // Store form data in IndexedDB await addToSyncQueue({ url: formData.action, method: formData.method || 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, clone: () => ({ text: async () => new URLSearchParams(formData.data).toString() }), }); // Register for background sync await self.registration.sync.register(SYNC_CONFIG.queueName); } // Listen for form sync requests self.addEventListener('message', (event) => { if (event.data?.type === 'REGISTER_FORM_SYNC') { event.waitUntil(registerFormSync(event.data.payload)); } }); `}function m4($){return $.pwa?.serviceWorker?.fileName||"sw.js"}function M6($){return $.pwa?.enabled===!0&&$.pwa?.serviceWorker?.useWorkbox===!0}function p1($,Z){if(!$.pwa?.enabled)return"";if(M6($)){let{generateWorkboxServiceWorker:Y}=(N6(),K6(A6));return Y($,Z)}return k5($,Z)}var Q5=I(()=>{G5()});var w6={};o(w6,{injectPwaTags:()=>I7,generateThemeColorMeta:()=>i1,generateSwRegistrationScript:()=>c1,generatePwaTags:()=>S5,generateManifestLink:()=>l1});function S5($){let Z=$.pwa;if(!Z?.enabled)return"";let Y=Z.manifest,J=Z.icons?.outputDir?`/${Z.icons.outputDir}`:"/pwa-icons",G=m4($),X=` <!-- stx PWA Meta Tags --> <link rel="manifest" href="/manifest.json"> <meta name="theme-color" content="${Y?.themeColor||"#000000"}"> <meta name="mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="default"> <meta name="apple-mobile-web-app-title" content="${T6(Y?.shortName||Y?.name||"App")}">`;if(Z.icons?.generateAppleIcons!==!1)X+=` <link rel="apple-touch-icon" href="${J}/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="120x120" href="${J}/apple-touch-icon-120x120.png"> <link rel="apple-touch-icon" sizes="152x152" href="${J}/apple-touch-icon-152x152.png"> <link rel="apple-touch-icon" sizes="167x167" href="${J}/apple-touch-icon-167x167.png"> <link rel="apple-touch-icon" sizes="180x180" href="${J}/apple-touch-icon-180x180.png">`;return X+=` <link rel="icon" type="image/png" sizes="32x32" href="${J}/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="16x16" href="${J}/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="192x192" href="${J}/icon-192x192.png">`,X+=` <!-- stx Service Worker Registration --> <script> if ('serviceWorker' in navigator) { window.addEventListener('load', function() { navigator.serviceWorker.register('/${G}') .then(function(registration) { console.log('[PWA] Service Worker registered:', registration.scope); // Check for updates registration.addEventListener('updatefound', function() { const newWorker = registration.installing; if (newWorker) { newWorker.addEventListener('statechange', function() { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { console.log('[PWA] New content available, refresh to update'); // Optionally dispatch a custom event for UI notification window.dispatchEvent(new CustomEvent('pwa-update-available')); } }); } }); }) .catch(function(error) { console.error('[PWA] Service Worker registration failed:', error); }); }); } </script> <!-- End stx PWA -->`,X}function I7($,Z){let Y=Z.pwa;if(!Y?.enabled||Y.autoInject===!1)return $;if($.includes("<!-- stx PWA Meta Tags -->"))return $;if(!$.includes("</head>")){if($.includes("</body>")){let G=S5(Z);return $.replace("</body>",`${G} </body>`)}return $+S5(Z)}let J=S5(Z);return $.replace("</head>",`${J} </head>`)}function c1($){if(!$.pwa?.enabled)return"";return`<script> if ('serviceWorker' in navigator) { window.addEventListener('load', function() { navigator.serviceWorker.register('/${m4($)}') .then(function(registration) { console.log('[PWA] Service Worker registered:', registration.scope); }) .catch(function(error) { console.error('[PWA] Service Worker registration failed:', error); }); }); } </script>`}function l1(){return'<link rel="manifest" href="/manifest.json">'}function i1($){return`<meta name="theme-color" content="${T6($)}">`}function T6($){return $.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}var b5=I(()=>{Q5()});var U4;var D6=I(()=>{U4={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",underscore:"\x1B[4m",blink:"\x1B[5m",reverse:"\x1B[7m",hidden:"\x1B[8m",black:"\x1B[30m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",cyan:"\x1B[36m",white:"\x1B[37m",gray:"\x1B[90m",bgBlack:"\x1B[40m",bgRed:"\x1B[41m",bgGreen:"\x1B[42m",bgYellow:"\x1B[43m",bgBlue:"\x1B[44m",bgMagenta:"\x1B[45m",bgCyan:"\x1B[46m",bgWhite:"\x1B[47m",bgGray:"\x1B[100m"}});import S6 from"fs";import K4 from"path";async function k6($){try{let Z=await import($);if(Z&&Z.CSSGenerator)return{CSSGenerator:Z.CSSGenerator,config:Z.config,build:Z.build,defaultConfig:Z.defaultConfig}}catch{}return null}function e1(){let $=[],Z=process.env.HOME||process.env.USERPROFILE||"",Y=process.cwd();while(Y!==K4.dirname(Y))$.push(K4.join(Y,"node_modules","@cwcss","crosswind","dist","index.js")),$.push(K4.join(Y,"node_modules","@cwcss","crosswind","src","index.ts")),$.push(K4.join(Y,"node_modules","@stacksjs","crosswind","dist","index.js")),Y=K4.dirname(Y);if(Z){let J=[K4.join(Z,"Code","Tools","crosswind","packages","crosswind","dist","index.js"),K4.join(Z,"Code","Tools","crosswind","packages","crosswind","src","index.ts"),K4.join(Z,"repos","stacks-org","crosswind","packages","crosswind","dist","index.js"),K4.join(Z,"repos","stacks-org","crosswind","packages","crosswind","src","index.ts"),K4.join(Z,"Code","Tools","stx","packages","stx","node_modules","@cwcss","crosswind","dist","index.js"),K4.join(Z,"Code","Tools","stx","packages","stx","node_modules","@stacksjs","crosswind","dist","index.js")];$.push(...J)}return $.push(K4.join(process.cwd(),"..","crosswind","packages","crosswind","dist","index.js")),$.push(K4.join(process.cwd(),"..","crosswind","packages","crosswind","src","index.ts")),$}async function $Y(){if(E6)return K5;E6=!0;try{let $=["@cwcss/crosswind","@cwcss/crosswind/dist/index.js","@stacksjs/crosswind","@stacksjs/crosswind/dist/index.js"];for(let Y of $){let J=await k6(Y);if(J)return K5=J,console.log(`${U4.green}[Crosswind]${U4.reset} CSS engine loaded`),K5}let Z=e1();for(let Y of Z)if(S6.existsSync(Y)){let J=await k6(Y);if(J)return K5=J,console.log(`${U4.green}[Crosswind]${U4.reset} CSS engine loaded from ${K4.dirname(K4.dirname(Y))}`),K5}throw Error("Crosswind CSSGenerator not found in any location")}catch{return console.warn(`${U4.yellow}[Crosswind] CSS engine not available, Tailwind styles will not be generated${U4.reset}`),console.warn(`${U4.yellow}Run 'bun add @stacksjs/crosswind' to enable CSS generation${U4.reset}`),null}}async function ZY($){let Z=["crosswind.config.ts","crosswind.config.js","crosswind.config.mjs"];for(let Y of Z){let J=K4.join($,Y);if(S6.existsSync(J))try{let G=await import(J),X=G.default||G;return console.log(`${U4.green}[Crosswind]${U4.reset} Loaded config from ${Y}`),X}catch(G){console.warn(`${U4.yellow}[Crosswind]${U4.reset} Failed to load ${Y}:`,G)}}return null}function YY($){let Z=/class\s*=\s*["']([^"']+)["']/gi,Y=new Set,J=Z.exec($);while(J!==null){let G=J[1];for(let X of G.split(/\s+/))if(X.trim())Y.add(X.trim());J=Z.exec($)}return Y}async function JY($){try{let Z=await $Y();if(!Z)return"";let Y=YY($);if(Y.size===0)return"";if(!D7)D7=await ZY(process.cwd());let J=Z.defaultConfig||Z.config,G=D7||{},X=J.theme||{},K=(G.theme||{}).extend||{},q={...X};if(Object.keys(K).length>0)q.extend=K;let _=[...J.safelist||[],...G.safelist||[]],j={...J,...G,content:[],output:"",preflight:!0,minify:!1,theme:q,safelist:_},B=new Z.CSSGenerator(j);for(let U of _)B.generate(U);for(let U of Y)B.generate(U);return B.toCSS(!0,!1)}catch(Z){return console.warn("Failed to generate Crosswind CSS:",Z),""}}async function b6($){let Z=await JY($);if(!Z)return $;let Y=`<style data-crosswind="generated"> ${Z} </style>`;if($.includes("</head>"))return $.replace("</head>",`${Y} </head>`);if($.includes("<body"))return $.replace(/<body([^>]*)>/,`<body$1> ${Y}`);return Y+$}var K5=null,E6=!1,D7=null;var P6=I(()=>{D6()});function y6($,Z,Y,J){let G=$;G=G.replace(/@a11y\(\s*['"]([^'"]+)['"]\s*(?:,\s*['"]([^'"]+)['"]\s*)?\)/g,(Q,K,q)=>{let j={"aria-label":"Ensure interactive elements have accessible labels","alt-text":"Provide alternative text for images",focus:"Ensure the element can receive keyboard focus",landmark:"Use appropriate landmark roles","heading-order":"Maintain proper heading hierarchy","color-contrast":"Ensure sufficient color contrast","keyboard-nav":"Make sure element is keyboard navigable","screen-reader":"Optimize for screen reader users"}[K]||"Make this element accessible";return`<!-- a11y-hint: ${q||j} -->`});let X=/@screenReader\(([^@]*)\)@endScreenReader/g;return G=G.replace(X,(Q,K)=>{return`<span class="sr-only">${K.trim()}</span>`}),G=G.replace(/@ariaDescribe\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]\s*\)/g,(Q,K,q)=>{return`<span id="${`desc-${K}`}" class="sr-only">${q}</span>`}),G}var C6,v6;var E7=I(()=>{C6={name:"a11y",handler:($,Z,Y,J)=>{if(!Z.length)return $;let G=Z[0].replace(/['"]/g,""),X=Z.length>1?Z[1].replace(/['"]/g,""):"",K={"aria-label":"Ensure interactive elements have accessible labels","alt-text":"Provide alternative text for images",focus:"Ensure the element can receive keyboard focus",landmark:"Use appropriate landmark roles","heading-order":"Maintain proper heading hierarchy","color-contrast":"Ensure sufficient color contrast","keyboard-nav":"Make sure element is keyboard navigable","screen-reader":"Optimize for screen reader users"}[G]||"Make this element accessible";return`<!-- a11y-hint: ${X||K} -->${$}`},hasEndTag:!1},v6={name:"screenReader",handler:($)=>{return`<span class="sr-only">${$}</span>`},hasEndTag:!0}});function x6(){return` // STX Lifecycle Runtime (function() { if (typeof window === 'undefined') return; const instances = new Map(); let currentInstance = null; let idCounter = 0; function generateId() { return 'stx-' + (++idCounter) + '-' + Date.now().toString(36); } function createInstance(element) { const instance = { id: generateId(), element: element, mountHooks: [], destroyHooks: [], updateHooks: [], refs: new Map(), watchers: [], isMounted: false }; instances.set(instance.id, instance); return instance; } window.STX = window.STX || {}; // Lifecycle hooks window.STX.onMount = function(hook) { if (currentInstance) currentInstance.mountHooks.push(hook); }; window.STX.onDestroy = function(hook) { if (currentInstance) currentInstance.destroyHooks.push(hook); }; window.STX.onUpdate = function(hook) { if (currentInstance) currentInstance.updateHooks.push(hook); }; // Aliases window.STX.onMounted = window.STX.onMount; window.STX.onUnmounted = window.STX.onDestroy; window.STX.onUpdated = window.STX.onUpdate; // Refs window.STX.ref = function(initialValue) { const r = { value: initialValue || null }; Object.defineProperty(r, 'current', { get: function() { return this.value; } }); if (currentInstance) { currentInstance.refs.set('ref-' + currentInstance.refs.size, r); } return r; }; // Watch window.STX.watch = function(source, callback, options) { options = options || {}; let oldValue; let cleanup; let isActive = true; function run() { if (!isActive) return; const newValue = source(); if (oldValue !== newValue || options.immediate) { if (cleanup) cleanup(); cleanup = callback(newValue, oldValue); oldValue = newValue; } } if (options.immediate) { run(); } else { oldValue = source(); } const intervalId = setInterval(run, 16); function stop() { isActive = false; clearInterval(intervalId); if (cleanup) cleanup(); } if (currentInstance) { currentInstance.watchers.push({ stop: stop }); currentInstance.destroyHooks.push(stop); } return stop; }; // Computed window.STX.computed = function(getter) { let cached; let dirty = true; return { get value() { if (dirty) { cached = getter(); dirty = false; queueMicrotask(function() { dirty = true; }); } return cached; } }; }; // Component management window.STX.setupComponent = function(element, setup) { const instance = createInstance(element); currentInstance = instance; try { setup(); } finally { currentInstance = null; } return instance; }; window.STX.mountComponent = function(instance) { if (instance.isMounted) return; instance.isMounted = true; instance.mountHooks.forEach(function(hook) { try { const