@julien-lin/universal-pwa-core 1.3.0 → 1.3.1

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.cjs CHANGED
@@ -1290,6 +1290,7 @@ function injectMetaTags(htmlContent, options = {}) {
1290
1290
  const escapedSwPath = escapeJavaScriptString(swPath);
1291
1291
  const swScript = `
1292
1292
  <script>
1293
+ // Service Worker Registration
1293
1294
  if ('serviceWorker' in navigator) {
1294
1295
  window.addEventListener('load', () => {
1295
1296
  navigator.serviceWorker.register(${escapedSwPath})
@@ -1297,6 +1298,67 @@ if ('serviceWorker' in navigator) {
1297
1298
  .catch((error) => console.error('SW registration failed:', error));
1298
1299
  });
1299
1300
  }
1301
+
1302
+ // PWA Install Handler
1303
+ let deferredPrompt = null;
1304
+ let isInstalled = false;
1305
+
1306
+ // Check if app is already installed
1307
+ if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true) {
1308
+ isInstalled = true;
1309
+ }
1310
+
1311
+ // Listen for beforeinstallprompt event
1312
+ window.addEventListener('beforeinstallprompt', (e) => {
1313
+ // Prevent the mini-infobar from appearing on mobile
1314
+ e.preventDefault();
1315
+ // Stash the event so it can be triggered later
1316
+ deferredPrompt = e;
1317
+ // Update UI to notify the user they can install the PWA
1318
+ window.dispatchEvent(new CustomEvent('pwa-installable', { detail: { installable: true } }));
1319
+ console.log('PWA installable: beforeinstallprompt event captured');
1320
+ });
1321
+
1322
+ // Listen for app installed event
1323
+ window.addEventListener('appinstalled', () => {
1324
+ isInstalled = true;
1325
+ deferredPrompt = null;
1326
+ window.dispatchEvent(new CustomEvent('pwa-installed', { detail: { installed: true } }));
1327
+ console.log('PWA installed successfully');
1328
+ });
1329
+
1330
+ // Expose global install function
1331
+ window.installPWA = function() {
1332
+ if (!deferredPrompt) {
1333
+ console.warn('PWA install prompt not available. App may already be installed or not installable.');
1334
+ return Promise.reject(new Error('PWA install prompt not available'));
1335
+ }
1336
+
1337
+ // Show the install prompt
1338
+ deferredPrompt.prompt();
1339
+
1340
+ // Wait for the user to respond to the prompt
1341
+ return deferredPrompt.userChoice.then((choiceResult) => {
1342
+ if (choiceResult.outcome === 'accepted') {
1343
+ console.log('User accepted the install prompt');
1344
+ } else {
1345
+ console.log('User dismissed the install prompt');
1346
+ }
1347
+ deferredPrompt = null;
1348
+ window.dispatchEvent(new CustomEvent('pwa-install-choice', { detail: { outcome: choiceResult.outcome } }));
1349
+ return choiceResult;
1350
+ });
1351
+ };
1352
+
1353
+ // Expose global check function
1354
+ window.isPWAInstalled = function() {
1355
+ return isInstalled || window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
1356
+ };
1357
+
1358
+ // Expose global check if installable
1359
+ window.isPWAInstallable = function() {
1360
+ return deferredPrompt !== null;
1361
+ };
1300
1362
  </script>`;
1301
1363
  if (modifiedHtml.includes("</body>")) {
1302
1364
  modifiedHtml = modifiedHtml.replace("</body>", `${swScript}
@@ -1305,7 +1367,66 @@ if ('serviceWorker' in navigator) {
1305
1367
  modifiedHtml = `${modifiedHtml}${swScript}`;
1306
1368
  }
1307
1369
  result.injected.push("Service Worker registration script");
1370
+ result.injected.push("PWA install handler script");
1308
1371
  } else {
1372
+ if (!htmlContent.includes("beforeinstallprompt") && !htmlContent.includes("window.installPWA")) {
1373
+ const installScript = `
1374
+ <script>
1375
+ // PWA Install Handler
1376
+ let deferredPrompt = null;
1377
+ let isInstalled = false;
1378
+
1379
+ // Check if app is already installed
1380
+ if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true) {
1381
+ isInstalled = true;
1382
+ }
1383
+
1384
+ // Listen for beforeinstallprompt event
1385
+ window.addEventListener('beforeinstallprompt', (e) => {
1386
+ e.preventDefault();
1387
+ deferredPrompt = e;
1388
+ window.dispatchEvent(new CustomEvent('pwa-installable', { detail: { installable: true } }));
1389
+ console.log('PWA installable: beforeinstallprompt event captured');
1390
+ });
1391
+
1392
+ window.addEventListener('appinstalled', () => {
1393
+ isInstalled = true;
1394
+ deferredPrompt = null;
1395
+ window.dispatchEvent(new CustomEvent('pwa-installed', { detail: { installed: true } }));
1396
+ console.log('PWA installed successfully');
1397
+ });
1398
+
1399
+ window.installPWA = function() {
1400
+ if (!deferredPrompt) {
1401
+ console.warn('PWA install prompt not available.');
1402
+ return Promise.reject(new Error('PWA install prompt not available'));
1403
+ }
1404
+ deferredPrompt.prompt();
1405
+ return deferredPrompt.userChoice.then((choiceResult) => {
1406
+ deferredPrompt = null;
1407
+ window.dispatchEvent(new CustomEvent('pwa-install-choice', { detail: { outcome: choiceResult.outcome } }));
1408
+ return choiceResult;
1409
+ });
1410
+ };
1411
+
1412
+ window.isPWAInstalled = function() {
1413
+ return isInstalled || window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
1414
+ };
1415
+
1416
+ window.isPWAInstallable = function() {
1417
+ return deferredPrompt !== null;
1418
+ };
1419
+ </script>`;
1420
+ if (modifiedHtml.includes("</body>")) {
1421
+ modifiedHtml = modifiedHtml.replace("</body>", `${installScript}
1422
+ </body>`);
1423
+ } else {
1424
+ modifiedHtml = `${modifiedHtml}${installScript}`;
1425
+ }
1426
+ result.injected.push("PWA install handler script");
1427
+ } else {
1428
+ result.skipped.push("PWA install handler (already exists)");
1429
+ }
1309
1430
  result.skipped.push("Service Worker registration (already exists)");
1310
1431
  }
1311
1432
  }
package/dist/index.js CHANGED
@@ -1224,6 +1224,7 @@ function injectMetaTags(htmlContent, options = {}) {
1224
1224
  const escapedSwPath = escapeJavaScriptString(swPath);
1225
1225
  const swScript = `
1226
1226
  <script>
1227
+ // Service Worker Registration
1227
1228
  if ('serviceWorker' in navigator) {
1228
1229
  window.addEventListener('load', () => {
1229
1230
  navigator.serviceWorker.register(${escapedSwPath})
@@ -1231,6 +1232,67 @@ if ('serviceWorker' in navigator) {
1231
1232
  .catch((error) => console.error('SW registration failed:', error));
1232
1233
  });
1233
1234
  }
1235
+
1236
+ // PWA Install Handler
1237
+ let deferredPrompt = null;
1238
+ let isInstalled = false;
1239
+
1240
+ // Check if app is already installed
1241
+ if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true) {
1242
+ isInstalled = true;
1243
+ }
1244
+
1245
+ // Listen for beforeinstallprompt event
1246
+ window.addEventListener('beforeinstallprompt', (e) => {
1247
+ // Prevent the mini-infobar from appearing on mobile
1248
+ e.preventDefault();
1249
+ // Stash the event so it can be triggered later
1250
+ deferredPrompt = e;
1251
+ // Update UI to notify the user they can install the PWA
1252
+ window.dispatchEvent(new CustomEvent('pwa-installable', { detail: { installable: true } }));
1253
+ console.log('PWA installable: beforeinstallprompt event captured');
1254
+ });
1255
+
1256
+ // Listen for app installed event
1257
+ window.addEventListener('appinstalled', () => {
1258
+ isInstalled = true;
1259
+ deferredPrompt = null;
1260
+ window.dispatchEvent(new CustomEvent('pwa-installed', { detail: { installed: true } }));
1261
+ console.log('PWA installed successfully');
1262
+ });
1263
+
1264
+ // Expose global install function
1265
+ window.installPWA = function() {
1266
+ if (!deferredPrompt) {
1267
+ console.warn('PWA install prompt not available. App may already be installed or not installable.');
1268
+ return Promise.reject(new Error('PWA install prompt not available'));
1269
+ }
1270
+
1271
+ // Show the install prompt
1272
+ deferredPrompt.prompt();
1273
+
1274
+ // Wait for the user to respond to the prompt
1275
+ return deferredPrompt.userChoice.then((choiceResult) => {
1276
+ if (choiceResult.outcome === 'accepted') {
1277
+ console.log('User accepted the install prompt');
1278
+ } else {
1279
+ console.log('User dismissed the install prompt');
1280
+ }
1281
+ deferredPrompt = null;
1282
+ window.dispatchEvent(new CustomEvent('pwa-install-choice', { detail: { outcome: choiceResult.outcome } }));
1283
+ return choiceResult;
1284
+ });
1285
+ };
1286
+
1287
+ // Expose global check function
1288
+ window.isPWAInstalled = function() {
1289
+ return isInstalled || window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
1290
+ };
1291
+
1292
+ // Expose global check if installable
1293
+ window.isPWAInstallable = function() {
1294
+ return deferredPrompt !== null;
1295
+ };
1234
1296
  </script>`;
1235
1297
  if (modifiedHtml.includes("</body>")) {
1236
1298
  modifiedHtml = modifiedHtml.replace("</body>", `${swScript}
@@ -1239,7 +1301,66 @@ if ('serviceWorker' in navigator) {
1239
1301
  modifiedHtml = `${modifiedHtml}${swScript}`;
1240
1302
  }
1241
1303
  result.injected.push("Service Worker registration script");
1304
+ result.injected.push("PWA install handler script");
1242
1305
  } else {
1306
+ if (!htmlContent.includes("beforeinstallprompt") && !htmlContent.includes("window.installPWA")) {
1307
+ const installScript = `
1308
+ <script>
1309
+ // PWA Install Handler
1310
+ let deferredPrompt = null;
1311
+ let isInstalled = false;
1312
+
1313
+ // Check if app is already installed
1314
+ if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true) {
1315
+ isInstalled = true;
1316
+ }
1317
+
1318
+ // Listen for beforeinstallprompt event
1319
+ window.addEventListener('beforeinstallprompt', (e) => {
1320
+ e.preventDefault();
1321
+ deferredPrompt = e;
1322
+ window.dispatchEvent(new CustomEvent('pwa-installable', { detail: { installable: true } }));
1323
+ console.log('PWA installable: beforeinstallprompt event captured');
1324
+ });
1325
+
1326
+ window.addEventListener('appinstalled', () => {
1327
+ isInstalled = true;
1328
+ deferredPrompt = null;
1329
+ window.dispatchEvent(new CustomEvent('pwa-installed', { detail: { installed: true } }));
1330
+ console.log('PWA installed successfully');
1331
+ });
1332
+
1333
+ window.installPWA = function() {
1334
+ if (!deferredPrompt) {
1335
+ console.warn('PWA install prompt not available.');
1336
+ return Promise.reject(new Error('PWA install prompt not available'));
1337
+ }
1338
+ deferredPrompt.prompt();
1339
+ return deferredPrompt.userChoice.then((choiceResult) => {
1340
+ deferredPrompt = null;
1341
+ window.dispatchEvent(new CustomEvent('pwa-install-choice', { detail: { outcome: choiceResult.outcome } }));
1342
+ return choiceResult;
1343
+ });
1344
+ };
1345
+
1346
+ window.isPWAInstalled = function() {
1347
+ return isInstalled || window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
1348
+ };
1349
+
1350
+ window.isPWAInstallable = function() {
1351
+ return deferredPrompt !== null;
1352
+ };
1353
+ </script>`;
1354
+ if (modifiedHtml.includes("</body>")) {
1355
+ modifiedHtml = modifiedHtml.replace("</body>", `${installScript}
1356
+ </body>`);
1357
+ } else {
1358
+ modifiedHtml = `${modifiedHtml}${installScript}`;
1359
+ }
1360
+ result.injected.push("PWA install handler script");
1361
+ } else {
1362
+ result.skipped.push("PWA install handler (already exists)");
1363
+ }
1243
1364
  result.skipped.push("Service Worker registration (already exists)");
1244
1365
  }
1245
1366
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@julien-lin/universal-pwa-core",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Core engine for scanning, generation, and injection for UniversalPWA",
5
5
  "keywords": [
6
6
  "pwa",
@@ -61,7 +61,7 @@
61
61
  "workbox-routing": "^7.4.0",
62
62
  "workbox-strategies": "^7.4.0",
63
63
  "zod": "^4.2.1",
64
- "@julien-lin/universal-pwa-templates": "^1.3.0"
64
+ "@julien-lin/universal-pwa-templates": "^1.3.1"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@vitest/coverage-v8": "^2.1.4",