@modernman00/shared-js-lib 1.2.81 โ 1.2.83
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/AcctMgt/AdminAuthForm.js +26 -0
- package/AcctMgt/createFormHandler.js +25 -13
- package/AcctMgt/processAll.js +23 -1
- package/AcctMgt/recaptchaToken.js +25 -10
- package/AcctMgt/update.js +66 -33
- package/Alert.js +158 -0
- package/AuthInterceptor.js +56 -0
- package/ButtonLoader.js +133 -0
- package/CardCanvasGenerator.js +367 -0
- package/Clipboard.js +45 -0
- package/Cookie.js +1 -19
- package/FormLock.js +58 -0
- package/Http.js +158 -51
- package/InlineValidation.js +78 -0
- package/NetworkGuard.js +54 -0
- package/OfflineQueue.js +278 -0
- package/PwaInstall.js +165 -0
- package/PwaManager.js +224 -0
- package/README.md +74 -0
- package/ShareManager.js +462 -0
- package/ShowResponse.js +120 -0
- package/axiosWrapper.js +3 -0
- package/config/version.php +1 -1
- package/index.js +14 -1
- package/package.json +18 -3
- package/release-history.md +35 -0
- package/templates/sw.template.js +239 -0
package/release-history.md
CHANGED
|
@@ -101,3 +101,38 @@
|
|
|
101
101
|
|
|
102
102
|
- ๐ Bump version to v1.2.74 _(by modernman00)_
|
|
103
103
|
- ๐งน Auto-commit before release _(by modernman00)_
|
|
104
|
+
|
|
105
|
+
## v1.2.76 โ 2026-07-17 17:47
|
|
106
|
+
### Changelog for v1.2.76
|
|
107
|
+
|
|
108
|
+
- ๐ Bump version to v1.2.76 _(by modernman00)_
|
|
109
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
110
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
111
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
112
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
113
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
114
|
+
- refactor: improve robustness of UI utilities and add comprehensive unit test coverage _(by modernman00)_
|
|
115
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
116
|
+
|
|
117
|
+
## v1.2.77 โ 2026-07-17 22:53
|
|
118
|
+
### Changelog for v1.2.77
|
|
119
|
+
|
|
120
|
+
- ๐ Bump version to v1.2.77 _(by modernman00)_
|
|
121
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
122
|
+
- ๐ feat: Enterprise-wide WebAuthn Biometric Login Rollout (FaceID / TouchID) across all 6 applications.
|
|
123
|
+
- ๐ sec: Enforced strict FIDO2 challenge invalidation, origin verification, and defensive degradation per TAT governance.
|
|
124
|
+
|
|
125
|
+
## v1.2.82 โ 2026-09-17 16:57
|
|
126
|
+
### Changelog for v1.2.82
|
|
127
|
+
|
|
128
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
129
|
+
|
|
130
|
+
## v1.2.83 โ 2026-09-21 23:38
|
|
131
|
+
### Changelog for v1.2.83
|
|
132
|
+
|
|
133
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
134
|
+
- ๐ Release v1.2.82 _(by modernman00)_
|
|
135
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
136
|
+
- ๐ Bump version to 1.2.83 _(by modernman00)_
|
|
137
|
+
- feat(pwa): centralize resilient OfflineQueue, PwaManager and standardized Service Worker template _(by modernman00)_
|
|
138
|
+
- feat: enhance release script with interactive npm login and improved workflow _(by modernman00)_
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* โก CANONICAL SERVICE WORKER BLUEPRINT โ @modernman00/shared-js-lib
|
|
3
|
+
* Standardized across PartyPlatform, FamilyPlatform, ExecMind, iAccount, iDecide, LoanEasy
|
|
4
|
+
*
|
|
5
|
+
* Implements:
|
|
6
|
+
* 1. Marcus Lindqvist (Deloitte) 3000ms navigation timeout race (no hanging Safari white screens)
|
|
7
|
+
* 2. Scoped runtime cache names: ${APP_NAME}-code-${SW_VERSION}, ${APP_NAME}-media, ${APP_NAME}-api
|
|
8
|
+
* 3. Strict NetworkOnly security gate for /auth, /admin, /login, /logout, /csrf
|
|
9
|
+
* 4. Self-hosted Workbox 7.0 fallback & background sync
|
|
10
|
+
* 5. Universal Push Notification, Web Badging API, and Cross-Tab BroadcastChannel
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Workbox 7.0.0 โ self-hosted
|
|
14
|
+
importScripts('/public/js/workbox/workbox-sw.js');
|
|
15
|
+
if (typeof workbox !== 'undefined') {
|
|
16
|
+
workbox.setConfig({ modulePathPrefix: '/public/js/workbox/' });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Build stamp & app identification
|
|
20
|
+
const SW_VERSION = 'dev';
|
|
21
|
+
const APP_NAME = 'app';
|
|
22
|
+
|
|
23
|
+
// Dynamic cache names scoped per app & build
|
|
24
|
+
const CODE_CACHE = `${APP_NAME}-code-${SW_VERSION}`;
|
|
25
|
+
const MEDIA_CACHE = `${APP_NAME}-media-v1`;
|
|
26
|
+
const API_CACHE = `${APP_NAME}-api-cache`;
|
|
27
|
+
const OFFLINE_CACHE = `${APP_NAME}-offline-v1`;
|
|
28
|
+
const KEEP_CACHES = [CODE_CACHE, MEDIA_CACHE, API_CACHE, OFFLINE_CACHE];
|
|
29
|
+
|
|
30
|
+
const OFFLINE_HTML = '/public/offline.html';
|
|
31
|
+
|
|
32
|
+
// Activation & Cache Pruning
|
|
33
|
+
self.addEventListener('activate', (event) => {
|
|
34
|
+
event.waitUntil((async () => {
|
|
35
|
+
const keys = await caches.keys();
|
|
36
|
+
await Promise.all(keys.map((key) => {
|
|
37
|
+
if (KEEP_CACHES.includes(key)) return undefined;
|
|
38
|
+
// Delete old versioned code caches or stale legacy buckets
|
|
39
|
+
if (key.startsWith(`${APP_NAME}-code-`) || key === 'static-resources') {
|
|
40
|
+
return caches.delete(key);
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}));
|
|
44
|
+
await self.clients.claim();
|
|
45
|
+
})());
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Helper: 3000ms Timeout Promise Race (Deloitte Safari Navigation Protection)
|
|
49
|
+
function fetchWithTimeout(request, timeoutMs = 3000) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const timer = setTimeout(() => {
|
|
52
|
+
reject(new Error('[SW] Navigation network timeout (3000ms) exceeded'));
|
|
53
|
+
}, timeoutMs);
|
|
54
|
+
|
|
55
|
+
fetch(request)
|
|
56
|
+
.then((response) => {
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
resolve(response);
|
|
59
|
+
})
|
|
60
|
+
.catch((err) => {
|
|
61
|
+
clearTimeout(timer);
|
|
62
|
+
reject(err);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Workbox Routing & Strategies
|
|
68
|
+
if (typeof workbox !== 'undefined') {
|
|
69
|
+
console.log(`[${APP_NAME} SW] Workbox initialized successfully ๐`);
|
|
70
|
+
|
|
71
|
+
// 1. Strict Security Gate โ Never cache authentication, admin, or session endpoints
|
|
72
|
+
workbox.routing.registerRoute(
|
|
73
|
+
({ url }) => ['/admin', '/login', '/register', '/auth', '/logout', '/signout', '/api/csrf', '/2fa'].some(p => url.pathname.startsWith(p)),
|
|
74
|
+
new workbox.strategies.NetworkOnly()
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// 2. Navigation Requests with 3000ms WebKit Timeout Race
|
|
78
|
+
workbox.routing.registerRoute(
|
|
79
|
+
({ request }) => request.mode === 'navigate',
|
|
80
|
+
async ({ request }) => {
|
|
81
|
+
try {
|
|
82
|
+
return await fetchWithTimeout(request, 3000);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.warn(`[${APP_NAME} SW] Navigation failed or timed out. Serving offline fallback shell.`);
|
|
85
|
+
const fallback = await caches.match(OFFLINE_HTML);
|
|
86
|
+
return fallback || new Response('<h1>Offline</h1><p>Please check your internet connection.</p>', {
|
|
87
|
+
status: 503,
|
|
88
|
+
headers: { 'Content-Type': 'text/html' }
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// 3. Same-origin app assets (Webpack JS & CSS)
|
|
95
|
+
const isDev = SW_VERSION === 'dev' ||
|
|
96
|
+
self.location.hostname === 'localhost' ||
|
|
97
|
+
self.location.hostname === '127.0.0.1' ||
|
|
98
|
+
self.location.hostname.endsWith('.test');
|
|
99
|
+
|
|
100
|
+
if (isDev) {
|
|
101
|
+
workbox.routing.registerRoute(
|
|
102
|
+
({ url, request }) => url.origin === self.location.origin && (request.destination === 'script' || request.destination === 'style'),
|
|
103
|
+
new workbox.strategies.NetworkOnly()
|
|
104
|
+
);
|
|
105
|
+
} else {
|
|
106
|
+
workbox.routing.registerRoute(
|
|
107
|
+
({ url, request }) => url.origin === self.location.origin && (request.destination === 'script' || request.destination === 'style'),
|
|
108
|
+
new workbox.strategies.NetworkFirst({
|
|
109
|
+
cacheName: CODE_CACHE,
|
|
110
|
+
networkTimeoutSeconds: 4,
|
|
111
|
+
plugins: [
|
|
112
|
+
new workbox.expiration.ExpirationPlugin({
|
|
113
|
+
maxEntries: 80,
|
|
114
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
115
|
+
}),
|
|
116
|
+
],
|
|
117
|
+
})
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 4. Media, fonts, and images (StaleWhileRevalidate)
|
|
122
|
+
workbox.routing.registerRoute(
|
|
123
|
+
({ request }) => ['style', 'script', 'font', 'image'].includes(request.destination),
|
|
124
|
+
new workbox.strategies.StaleWhileRevalidate({
|
|
125
|
+
cacheName: MEDIA_CACHE,
|
|
126
|
+
plugins: [
|
|
127
|
+
new workbox.expiration.ExpirationPlugin({
|
|
128
|
+
maxEntries: 120,
|
|
129
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
130
|
+
}),
|
|
131
|
+
],
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// 5. Precache Offline Shell
|
|
136
|
+
workbox.precaching.precacheAndRoute([
|
|
137
|
+
{ url: OFFLINE_HTML, revision: '1' }
|
|
138
|
+
]);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Push Notifications & Badging Engine
|
|
142
|
+
self.addEventListener('push', (event) => {
|
|
143
|
+
let data = {
|
|
144
|
+
title: APP_NAME,
|
|
145
|
+
body: 'You have a new update!',
|
|
146
|
+
icon: '/public/resources/assets/images/pwa/icons/icon-192x192.png',
|
|
147
|
+
badge: '/public/resources/assets/images/pwa/icons/icon-72x72.png',
|
|
148
|
+
url: '/',
|
|
149
|
+
tag: `${APP_NAME}-notification`
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
if (event.data) {
|
|
153
|
+
try {
|
|
154
|
+
data = { ...data, ...event.data.json() };
|
|
155
|
+
} catch (e) {
|
|
156
|
+
data.body = event.data.text();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const promises = [];
|
|
161
|
+
|
|
162
|
+
// 1. Show System Notification Banner
|
|
163
|
+
promises.push(
|
|
164
|
+
self.registration.showNotification(data.title, {
|
|
165
|
+
body: data.body,
|
|
166
|
+
icon: data.icon,
|
|
167
|
+
badge: data.badge,
|
|
168
|
+
data: { url: data.url, id: data.targetNotificationId },
|
|
169
|
+
tag: data.tag
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// 2. Web Badging API Sync (Phone App Icon Red Counter)
|
|
174
|
+
if ('setAppBadge' in navigator) {
|
|
175
|
+
let count = null;
|
|
176
|
+
if (typeof data.badgeCount === 'number') {
|
|
177
|
+
count = Math.max(0, Math.floor(data.badgeCount));
|
|
178
|
+
} else if (data.badgeCount !== null && data.badgeCount !== undefined && !isNaN(parseInt(data.badgeCount, 10))) {
|
|
179
|
+
count = Math.max(0, parseInt(data.badgeCount, 10));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (count !== null) {
|
|
183
|
+
promises.push(
|
|
184
|
+
count > 0
|
|
185
|
+
? navigator.setAppBadge(count).catch(() => {})
|
|
186
|
+
: navigator.clearAppBadge().catch(() => {})
|
|
187
|
+
);
|
|
188
|
+
} else {
|
|
189
|
+
// Fallback: When an active push notification arrives, ensure at least 1 badge is set on the app icon
|
|
190
|
+
promises.push(navigator.setAppBadge(1).catch(() => {}));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 3. Notify any active client windows so in-app bell counter updates immediately
|
|
195
|
+
promises.push(
|
|
196
|
+
clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
|
|
197
|
+
clientList.forEach((client) => {
|
|
198
|
+
client.postMessage({
|
|
199
|
+
type: 'ON_APP_NOTIFICATION',
|
|
200
|
+
payload: data
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
}).catch(() => {})
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
event.waitUntil(Promise.all(promises));
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Notification Click & Deep-Linking
|
|
210
|
+
self.addEventListener('notificationclick', (event) => {
|
|
211
|
+
event.notification.close();
|
|
212
|
+
const targetUrl = event.notification.data?.url || '/';
|
|
213
|
+
|
|
214
|
+
const promises = [];
|
|
215
|
+
|
|
216
|
+
if ('clearAppBadge' in navigator) {
|
|
217
|
+
promises.push(navigator.clearAppBadge().catch(() => {}));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
promises.push(
|
|
221
|
+
clients.matchAll({ type: 'window', includeUncontrolled: true }).then((list) => {
|
|
222
|
+
for (const client of list) {
|
|
223
|
+
if (client.url.includes(targetUrl) && 'focus' in client) {
|
|
224
|
+
if ('navigate' in client) client.navigate(targetUrl);
|
|
225
|
+
return client.focus();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (clients.openWindow) return clients.openWindow(targetUrl);
|
|
229
|
+
})
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
event.waitUntil(Promise.all(promises));
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Cross-Tab Client Messages
|
|
236
|
+
self.addEventListener('message', (event) => {
|
|
237
|
+
if (event.data?.action === 'skipWaiting') self.skipWaiting();
|
|
238
|
+
if (event.data?.action === 'clearUserCache') event.waitUntil(caches.delete(API_CACHE));
|
|
239
|
+
});
|