mbkauthe 4.0.0 → 4.0.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/index.js CHANGED
@@ -95,5 +95,8 @@ export {
95
95
  } from "./lib/middleware/auth.js";
96
96
  export { renderError } from "./lib/utils/response.js";
97
97
  export { dblogin } from "./lib/database/pool.js";
98
- export { ErrorCodes, ErrorMessages, getErrorByCode, createErrorResponse, logError } from "./lib/utils/errors.js";
98
+ export {
99
+ ErrorCodes, ErrorMessages, getErrorByCode,
100
+ createErrorResponse, logError
101
+ } from "./lib/utils/errors.js";
99
102
  export default router;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "MBKTech's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -4,6 +4,7 @@
4
4
  {{> head pageCode=code pageError=error ogUrl="/error" extraStyles="<style>
5
5
  .login-box {
6
6
  max-width: 600px;
7
+ padding: 2.5rem;
7
8
  }
8
9
 
9
10
  .status-code {
@@ -35,7 +36,6 @@
35
36
  }
36
37
 
37
38
  .status-content {
38
- margin-bottom: 1.5rem;
39
39
  text-align: center;
40
40
  }
41
41
 
@@ -43,12 +43,12 @@
43
43
  font-size: 1.2rem;
44
44
  margin: 0.5rem 0;
45
45
  color: var(--warning);
46
+ line-height: 1.4;
46
47
  }
47
48
 
48
49
  .status-links {
49
50
  display: flex;
50
51
  justify-content: center;
51
- margin-top: 1rem;
52
52
  }
53
53
 
54
54
  .status-link {
@@ -57,90 +57,146 @@
57
57
  transition: var(--transition);
58
58
  margin: 0 auto;
59
59
  font-size: 1rem;
60
- font-weight: 600;
60
+ font-weight: 700;
61
+ padding: 0.5rem 0.75rem;
62
+ border-radius: var(--border-radius);
61
63
  }
62
64
 
63
65
  .status-link:hover {
64
- text-decoration: underline;
66
+ text-decoration: none;
67
+ background: rgba(255,255,255,0.02);
65
68
  color: var(--secondary);
69
+ transform: translateY(-2px);
66
70
  }
67
71
 
72
+ /* Details wrapper: improved layout, accessibility and animation */
68
73
  .details-wrapper {
69
- margin-top: 1.5rem;
70
- background: rgba(255, 255, 255, .05);
74
+ margin-top: 1.25rem;
75
+ background: rgba(255, 255, 255, .03);
71
76
  border-radius: var(--border-radius);
72
- padding: 1.5rem;
73
- box-shadow: var(--box-shadow);
74
- cursor: pointer;
75
- transition: var(--transition);
76
- border: 1px solid rgba(255, 255, 255, .1);
77
+ padding: 0;
78
+ transition: box-shadow .25s ease, transform .25s ease;
79
+ border: 1px solid rgba(255, 255, 255, .06);
80
+ overflow: visible;
77
81
  }
78
82
 
79
- .details-wrapper:hover {
80
- background: rgba(255, 255, 255, .08);
81
- }
82
-
83
- .details-header {
83
+ .details-toggle {
84
84
  display: flex;
85
- justify-content: space-between;
86
85
  align-items: center;
86
+ justify-content: space-between;
87
+ width: 100%;
88
+ background: transparent;
89
+ border: 0;
87
90
  color: var(--light);
88
- font-size: 1.1rem;
91
+ font-size: 1.05rem;
89
92
  font-weight: 600;
93
+ padding: 0.85rem 1rem;
94
+ cursor: pointer;
90
95
  }
91
96
 
92
- .details-header i {
93
- transition: var(--transition);
97
+ .details-toggle:focus {
98
+ outline: 3px solid rgba(100, 150, 255, .18);
99
+ outline-offset: 2px;
100
+ border-radius: var(--border-radius);
101
+ }
102
+
103
+ .details-toggle .chev {
104
+ transition: transform .25s ease, color .2s ease;
105
+ color: var(--muted);
94
106
  }
95
107
 
96
- .details-wrapper:hover .details-header i {
108
+ .details-wrapper:hover .details-toggle .chev {
97
109
  color: var(--accent);
98
110
  }
99
111
 
100
112
  .error-details-wrapper {
101
- display: none;
102
- margin-top: 1rem;
113
+ max-height: 0;
114
+ overflow: hidden;
115
+ opacity: 0;
116
+ transition: max-height .33s cubic-bezier(.2,.9,.2,1), opacity .25s ease;
117
+ padding: 0 1rem;
103
118
  }
104
119
 
105
120
  .details-wrapper.active .error-details-wrapper {
106
- display: block;
107
- }
108
-
109
- .details-wrapper.active .details-header i {
110
- transform: rotate(180deg);
121
+ max-height: 480px; /* large enough for content */
122
+ opacity: 1;
123
+ padding: 0.9rem 1rem 1.25rem 1rem;
111
124
  }
112
125
 
113
126
  .error-details {
114
127
  width: 100%;
115
- height: 150px;
116
- background: rgba(0, 0, 0, .3);
117
- border: 1px solid rgba(255, 255, 255, .2);
118
- border-radius: var(--border-radius);
128
+ min-height: 140px;
129
+ background: rgba(0, 0, 0, .28);
130
+ border: 1px solid rgba(255, 255, 255, .08);
131
+ border-radius: calc(var(--border-radius) - 2px);
119
132
  color: var(--text);
120
- padding: 1rem;
121
- resize: none;
133
+ padding: 0.9rem;
134
+ padding-right: 3.6rem; /* leave space for overlay button */
135
+ box-sizing: border-box;
136
+ resize: vertical;
122
137
  font-size: 0.9rem;
123
- font-family: monospace;
138
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, 'Roboto Mono', monospace;
139
+ line-height: 1.45;
124
140
  }
125
141
 
142
+ .error-details-container {
143
+ position: relative;
144
+ width: 100%;
145
+ }
146
+
147
+ /* Overlay copy button positioned inside the details container */
126
148
  .copy-btn {
127
- margin-top: 0.5rem;
128
- background: var(--accent);
129
- color: var(--dark);
130
- border: none;
131
- border-radius: var(--border-radius);
132
- padding: 0.75rem 1.5rem;
149
+ position: absolute;
150
+ top: 0.6rem;
151
+ right: 0.65rem;
152
+ background: rgba(0,0,0,0.55);
153
+ color: var(--text);
154
+ border: 1px solid rgba(255,255,255,0.06);
155
+ border-radius: 6px;
156
+ padding: 0.45rem 0.6rem;
133
157
  cursor: pointer;
134
- transition: var(--transition);
135
- font-size: 1rem;
136
- font-weight: 600;
137
- box-shadow: var(--box-shadow);
158
+ transition: transform .12s ease, box-shadow .12s ease, background .12s ease;
159
+ font-size: 0.95rem;
160
+ font-weight: 700;
161
+ box-shadow: 0 4px 12px rgba(0,0,0,0.18);
162
+ display: inline-flex;
163
+ gap: 0.45rem;
164
+ align-items: center;
165
+ z-index: 6;
166
+ backdrop-filter: blur(4px);
138
167
  }
139
168
 
140
169
  .copy-btn:hover {
141
- background: var(--secondary);
170
+ background: var(--accent);
171
+ color: var(--dark);
142
172
  transform: translateY(-2px);
143
- box-shadow: 0 6px 15px rgba(0, 0, 0, 0.2);
173
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.28);
174
+ }
175
+
176
+ .copy-feedback {
177
+ position: absolute;
178
+ top: 2.4rem;
179
+ right: 0.6rem;
180
+ background: rgba(0,0,0,0.65);
181
+ color: var(--accent);
182
+ padding: 0.25rem 0.5rem;
183
+ font-weight: 700;
184
+ border-radius: 4px;
185
+ font-size: 0.85rem;
186
+ display: none;
187
+ z-index: 6;
188
+ }
189
+
190
+ .sr-only {
191
+ position: absolute !important;
192
+ width: 1px !important;
193
+ height: 1px !important;
194
+ padding: 0 !important;
195
+ margin: -1px !important;
196
+ overflow: hidden !important;
197
+ clip: rect(0, 0, 0, 0) !important;
198
+ white-space: nowrap !important;
199
+ border: 0 !important;
144
200
  }
145
201
 
146
202
  @media (max-width: 768px) {
@@ -149,8 +205,10 @@
149
205
  }
150
206
 
151
207
  .status-title {
152
- font-size: 1.3rem;
208
+ font-size: 1.25rem;
153
209
  }
210
+
211
+ .login-box { padding: 1.5rem; }
154
212
  }
155
213
 
156
214
  @media (max-width: 576px) {
@@ -186,17 +244,19 @@
186
244
  </div>
187
245
 
188
246
  {{#if details}}
189
- <div class="details-wrapper" onclick="toggleDetailsDropdown(this)">
190
- <div class="details-header">
191
- <span>Show Error Details</span>
192
- <i class="fas fa-chevron-down"></i>
193
- </div>
194
- <div class="error-details-wrapper">
195
- <textarea class="error-details" id="errorDetails" readonly>{{details}}</textarea>
196
- <button class="copy-btn" type="button" aria-label="Copy error details"
197
- onclick="copyErrorDetails(this); event.stopPropagation();">
198
- <i class="fas fa-copy"></i> Copy
199
- </button>
247
+ <div class="details-wrapper">
248
+ <button class="details-toggle" type="button" aria-expanded="false" aria-controls="errorDetails">
249
+ <span>Show error details</span>
250
+ <i class="fas fa-chevron-down chev" aria-hidden="true"></i>
251
+ </button>
252
+ <div class="error-details-wrapper" id="errorDetails" hidden>
253
+ <div class="error-details-container">
254
+ <textarea class="error-details" readonly>{{details}}</textarea>
255
+ <button class="copy-btn" type="button" aria-label="Copy error details">
256
+ <i class="fas fa-copy" aria-hidden="true"></i><span class="sr-only"> Copy</span>
257
+ </button>
258
+ <span class="copy-feedback" aria-live="polite">Copied!</span>
259
+ </div>
200
260
  </div>
201
261
  </div>
202
262
  {{/if}}
@@ -207,36 +267,63 @@
207
267
  {{> versionInfo}}
208
268
 
209
269
  <script>
210
- function toggleDetailsDropdown(element) {
211
- const errorDetailsWrapper = element.querySelector('.error-details-wrapper');
212
- const icon = element.querySelector('.details-header i');
213
-
214
- if (errorDetailsWrapper.style.display === 'block') {
215
- errorDetailsWrapper.style.display = 'none';
216
- icon.style.transform = 'rotate(0deg)';
217
- } else {
218
- errorDetailsWrapper.style.display = 'block';
219
- icon.style.transform = 'rotate(180deg)';
220
- }
221
- }
222
-
223
- function copyErrorDetails(button) {
224
- const details = button.previousElementSibling.value;
225
- navigator.clipboard.writeText(details).then(() => {
226
- button.textContent = 'Copied!';
227
- setTimeout(() => {
228
- button.textContent = 'Copy';
229
- }, 2000);
230
- }).catch(err => {
231
- console.error('[mbkauthe] Failed to copy: ', err);
232
- });
233
- }
234
-
235
270
  document.addEventListener('DOMContentLoaded', () => {
236
- const detailsWrappers = document.querySelectorAll('.details-wrapper');
237
- detailsWrappers.forEach(wrapper => {
238
- wrapper.addEventListener('click', () => {
239
- wrapper.classList.toggle('active');
271
+ document.querySelectorAll('.details-wrapper').forEach(wrapper => {
272
+ const toggle = wrapper.querySelector('.details-toggle');
273
+ const details = wrapper.querySelector('.error-details-wrapper');
274
+ const icon = wrapper.querySelector('.chev');
275
+ const copyBtn = wrapper.querySelector('.copy-btn');
276
+ const feedback = wrapper.querySelector('.copy-feedback');
277
+
278
+ if (!toggle || !details) return;
279
+
280
+ // initialize hidden state and maxHeight for animation
281
+ details.style.maxHeight = details.hidden ? '0px' : details.scrollHeight + 'px';
282
+
283
+ toggle.addEventListener('click', (e) => {
284
+ e.stopPropagation();
285
+ const isOpen = wrapper.classList.toggle('active');
286
+ toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
287
+ details.hidden = !isOpen;
288
+
289
+ if (isOpen) {
290
+ details.style.maxHeight = details.scrollHeight + 'px';
291
+ if (icon) icon.style.transform = 'rotate(180deg)';
292
+ } else {
293
+ details.style.maxHeight = '0px';
294
+ if (icon) icon.style.transform = 'rotate(0deg)';
295
+ }
296
+ });
297
+
298
+ if (copyBtn) {
299
+ copyBtn.addEventListener('click', async (e) => {
300
+ e.stopPropagation();
301
+ const textarea = wrapper.querySelector('.error-details');
302
+ try {
303
+ await navigator.clipboard.writeText(textarea.value);
304
+ if (feedback) {
305
+ feedback.style.display = 'inline';
306
+ setTimeout(() => { feedback.style.display = 'none'; }, 2000);
307
+ } else {
308
+ const orig = copyBtn.innerHTML;
309
+ copyBtn.innerHTML = '<i class="fas fa-check" aria-hidden="true"></i> Copied';
310
+ setTimeout(() => { copyBtn.innerHTML = orig; }, 2000);
311
+ }
312
+ } catch (err) {
313
+ console.error('[mbkauthe] Failed to copy: ', err);
314
+ }
315
+ });
316
+ }
317
+
318
+ // close the details when user clicks outside
319
+ document.addEventListener('click', (e) => {
320
+ if (!wrapper.contains(e.target) && wrapper.classList.contains('active')) {
321
+ wrapper.classList.remove('active');
322
+ toggle.setAttribute('aria-expanded', 'false');
323
+ details.hidden = true;
324
+ if (icon) icon.style.transform = 'rotate(0deg)';
325
+ details.style.maxHeight = '0px';
326
+ }
240
327
  });
241
328
  });
242
329
  });