@vizamodo/modo-dispatcher 1.1.77

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.
Files changed (109) hide show
  1. package/README.md +43 -0
  2. package/dist/artifacts/builders.d.ts +32 -0
  3. package/dist/artifacts/builders.js +72 -0
  4. package/dist/artifacts/downloader.d.ts +28 -0
  5. package/dist/artifacts/downloader.js +102 -0
  6. package/dist/artifacts/normalizer.d.ts +22 -0
  7. package/dist/artifacts/normalizer.js +65 -0
  8. package/dist/artifacts/types.d.ts +94 -0
  9. package/dist/artifacts/types.js +1 -0
  10. package/dist/auth/identity/extract-identity.d.ts +8 -0
  11. package/dist/auth/identity/extract-identity.js +15 -0
  12. package/dist/auth/identity/normalize-teams.d.ts +1 -0
  13. package/dist/auth/identity/normalize-teams.js +5 -0
  14. package/dist/auth/identity/validate-claims.d.ts +2 -0
  15. package/dist/auth/identity/validate-claims.js +5 -0
  16. package/dist/auth/jwt/parse.d.ts +2 -0
  17. package/dist/auth/jwt/parse.js +16 -0
  18. package/dist/auth/jwt/verify.d.ts +1 -0
  19. package/dist/auth/jwt/verify.js +9 -0
  20. package/dist/auth/oauth/auth-error.d.ts +12 -0
  21. package/dist/auth/oauth/auth-error.js +47 -0
  22. package/dist/auth/oauth/auth-session-store.d.ts +30 -0
  23. package/dist/auth/oauth/auth-session-store.js +46 -0
  24. package/dist/auth/oauth/callback-server.d.ts +21 -0
  25. package/dist/auth/oauth/callback-server.js +319 -0
  26. package/dist/auth/oauth/github.d.ts +14 -0
  27. package/dist/auth/oauth/github.js +100 -0
  28. package/dist/auth/oauth/sse.d.ts +11 -0
  29. package/dist/auth/oauth/sse.js +67 -0
  30. package/dist/auth/oauth/templates/failed.html +629 -0
  31. package/dist/auth/oauth/templates/pending.html +620 -0
  32. package/dist/auth/oauth/templates/success.html +577 -0
  33. package/dist/auth/session/access-token.d.ts +15 -0
  34. package/dist/auth/session/access-token.js +64 -0
  35. package/dist/auth/session/login.d.ts +18 -0
  36. package/dist/auth/session/login.js +144 -0
  37. package/dist/auth/session/logout.d.ts +3 -0
  38. package/dist/auth/session/logout.js +21 -0
  39. package/dist/auth/session/refresh-token.d.ts +8 -0
  40. package/dist/auth/session/refresh-token.js +16 -0
  41. package/dist/auth/session/refresh.d.ts +2 -0
  42. package/dist/auth/session/refresh.js +61 -0
  43. package/dist/auth/session/rotate.d.ts +4 -0
  44. package/dist/auth/session/rotate.js +4 -0
  45. package/dist/auth/session/session-manager.d.ts +16 -0
  46. package/dist/auth/session/session-manager.js +54 -0
  47. package/dist/auth/session/token-state.d.ts +20 -0
  48. package/dist/auth/session/token-state.js +55 -0
  49. package/dist/auth/session/types.d.ts +35 -0
  50. package/dist/auth/session/types.js +1 -0
  51. package/dist/auth/storage/keychain.d.ts +16 -0
  52. package/dist/auth/storage/keychain.js +107 -0
  53. package/dist/auth/storage/memory.d.ts +10 -0
  54. package/dist/auth/storage/memory.js +15 -0
  55. package/dist/auth/storage/types.d.ts +5 -0
  56. package/dist/auth/storage/types.js +1 -0
  57. package/dist/config/defaults.d.ts +94 -0
  58. package/dist/config/defaults.js +116 -0
  59. package/dist/core/auth-bootstrap.d.ts +15 -0
  60. package/dist/core/auth-bootstrap.js +33 -0
  61. package/dist/core/bootstrap.d.ts +73 -0
  62. package/dist/core/bootstrap.js +248 -0
  63. package/dist/core/dispatcher.d.ts +2 -0
  64. package/dist/core/dispatcher.js +28 -0
  65. package/dist/core/ensure-bootstrap.d.ts +3 -0
  66. package/dist/core/ensure-bootstrap.js +25 -0
  67. package/dist/core/errors.d.ts +69 -0
  68. package/dist/core/errors.js +121 -0
  69. package/dist/core/runtime-orchestrator.d.ts +17 -0
  70. package/dist/core/runtime-orchestrator.js +47 -0
  71. package/dist/core/runtime.d.ts +16 -0
  72. package/dist/core/runtime.js +52 -0
  73. package/dist/core/validation.d.ts +2 -0
  74. package/dist/core/validation.js +21 -0
  75. package/dist/crypto/encrypt.d.ts +12 -0
  76. package/dist/crypto/encrypt.js +80 -0
  77. package/dist/flows/email-otp-flow.d.ts +7 -0
  78. package/dist/flows/email-otp-flow.js +249 -0
  79. package/dist/gateway/auth-header.d.ts +2 -0
  80. package/dist/gateway/auth-header.js +21 -0
  81. package/dist/gateway/client.d.ts +7 -0
  82. package/dist/gateway/client.js +24 -0
  83. package/dist/gateway/dispatch-with-bootstrap-retry.d.ts +25 -0
  84. package/dist/gateway/dispatch-with-bootstrap-retry.js +157 -0
  85. package/dist/gateway/dispatch.d.ts +23 -0
  86. package/dist/gateway/dispatch.js +110 -0
  87. package/dist/gateway/session-waiter.d.ts +11 -0
  88. package/dist/gateway/session-waiter.js +126 -0
  89. package/dist/gateway/session.d.ts +21 -0
  90. package/dist/gateway/session.js +74 -0
  91. package/dist/gateway/types.d.ts +113 -0
  92. package/dist/gateway/types.js +19 -0
  93. package/dist/index.d.ts +22 -0
  94. package/dist/index.js +21 -0
  95. package/dist/runtime/runtime-context.d.ts +6 -0
  96. package/dist/runtime/runtime-context.js +1 -0
  97. package/dist/types/dispatcher.d.ts +275 -0
  98. package/dist/types/dispatcher.js +1 -0
  99. package/dist/types/payload.d.ts +5 -0
  100. package/dist/types/payload.js +2 -0
  101. package/dist/types/runtime-env.d.ts +2 -0
  102. package/dist/types/runtime-env.js +5 -0
  103. package/dist/ui/banner.d.ts +4 -0
  104. package/dist/ui/banner.js +20 -0
  105. package/dist/utils/request-dedup.d.ts +75 -0
  106. package/dist/utils/request-dedup.js +102 -0
  107. package/dist/utils/type-guards.d.ts +173 -0
  108. package/dist/utils/type-guards.js +232 -0
  109. package/package.json +43 -0
@@ -0,0 +1,620 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Viza Development Platform — Authorizing</title>
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap"
9
+ rel="stylesheet" />
10
+ <style>
11
+ :root {
12
+ --bg: #0d1117;
13
+ --surface: #161b22;
14
+ --surface2: #1f242d;
15
+ --border: rgba(255, 255, 255, 0.08);
16
+ --border-accent: rgba(96, 165, 250, 0.18);
17
+ --text-primary: #e6edf3;
18
+ --text-secondary: #9aa4b2;
19
+ --text-muted: #6b7280;
20
+ --accent: #60a5fa;
21
+ --accent-dim: rgba(96, 165, 250, 0.06);
22
+ --accent-glow: rgba(96, 165, 250, 0.05);
23
+ --warning: #f59e0b;
24
+ --mono: 'JetBrains Mono', monospace;
25
+ --sans: 'Inter', sans-serif;
26
+ }
27
+
28
+ @media (prefers-color-scheme: light) {
29
+ :root {
30
+ --bg: #eef2f6;
31
+ --surface: #ffffff;
32
+ --surface2: #f3f5f8;
33
+ --border: rgba(0, 0, 0, 0.08);
34
+ --border-accent: rgba(37, 99, 235, 0.16);
35
+ --text-primary: #0f1117;
36
+ --text-secondary: #5a6476;
37
+ --text-muted: #a0aab8;
38
+ --accent: #2563eb;
39
+ --accent-dim: rgba(37, 99, 235, 0.05);
40
+ --accent-glow: rgba(37, 99, 235, 0.04);
41
+ --warning: #d97706;
42
+ }
43
+ }
44
+
45
+ *,
46
+ *::before,
47
+ *::after {
48
+ box-sizing: border-box;
49
+ margin: 0;
50
+ padding: 0;
51
+ }
52
+
53
+ html,
54
+ body {
55
+ height: 100%;
56
+ background: var(--bg);
57
+ color: var(--text-primary);
58
+ font-family: var(--sans);
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ padding: 24px;
63
+ }
64
+
65
+ /* Subtle background grid */
66
+ body::before {
67
+ content: '';
68
+ position: fixed;
69
+ inset: 0;
70
+ background-image:
71
+ linear-gradient(var(--border) 1px, transparent 1px),
72
+ linear-gradient(90deg, var(--border) 1px, transparent 1px);
73
+ background-size: 72px 72px;
74
+ opacity: 0.28;
75
+ pointer-events: none;
76
+ z-index: 0;
77
+ }
78
+
79
+ body::after {
80
+ content: '';
81
+ position: fixed;
82
+ inset: 0;
83
+ background: radial-gradient(ellipse 55% 45% at 50% 50%, var(--accent-glow), transparent 75%);
84
+ pointer-events: none;
85
+ z-index: 0;
86
+ }
87
+
88
+ .card {
89
+ position: relative;
90
+ z-index: 1;
91
+ width: 100%;
92
+ max-width: 620px;
93
+ background: var(--surface);
94
+ border: 1px solid var(--border);
95
+ border-radius: 20px;
96
+ padding: 40px 40px 36px;
97
+ box-shadow:
98
+ 0 0 0 1px var(--border),
99
+ 0 18px 48px rgba(0, 0, 0, 0.28),
100
+ 0 0 0 1px rgba(255,255,255,0.02);
101
+ animation: fadeUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
102
+ }
103
+
104
+ @keyframes fadeUp {
105
+ from {
106
+ opacity: 0;
107
+ transform: translateY(20px) scale(0.98);
108
+ }
109
+
110
+ to {
111
+ opacity: 1;
112
+ transform: translateY(0) scale(1);
113
+ }
114
+ }
115
+
116
+ /* Header */
117
+ .header {
118
+ display: flex;
119
+ align-items: center;
120
+ gap: 12px;
121
+ margin-bottom: 24px;
122
+ padding-bottom: 15px;
123
+ border-bottom: 1px solid var(--border);
124
+ }
125
+
126
+ .logo {
127
+ width: 36px;
128
+ height: 36px;
129
+ background: var(--accent);
130
+ border-radius: 10px;
131
+ display: flex;
132
+ align-items: center;
133
+ justify-content: center;
134
+ flex-shrink: 0;
135
+ }
136
+
137
+ .logo svg {
138
+ width: 20px;
139
+ height: 20px;
140
+ }
141
+
142
+ .brand {
143
+ display: flex;
144
+ flex-direction: column;
145
+ gap: 1px;
146
+ }
147
+
148
+ .brand-name {
149
+ font-size: 14px;
150
+ font-weight: 700;
151
+ letter-spacing: 0.02em;
152
+ text-transform: uppercase;
153
+ color: var(--text-primary);
154
+ }
155
+
156
+ .brand-sub {
157
+ font-family: var(--mono);
158
+ font-size: 10px;
159
+ color: var(--text-muted);
160
+ letter-spacing: 0.04em;
161
+ }
162
+
163
+ /* Success badge */
164
+ .status-badge {
165
+ margin-left: auto;
166
+ display: flex;
167
+ align-items: center;
168
+ gap: 6px;
169
+ background: var(--accent-dim);
170
+ border: 1px solid var(--border-accent);
171
+ border-radius: 100px;
172
+ padding: 5px 12px 5px 8px;
173
+ font-family: var(--mono);
174
+ font-size: 10px;
175
+ font-weight: 500;
176
+ color: #7dd3fc;
177
+ letter-spacing: 0.03em;
178
+ animation: fadeIn 0.4s 0.4s both;
179
+ }
180
+
181
+ @keyframes fadeIn {
182
+ from {
183
+ opacity: 0;
184
+ }
185
+
186
+ to {
187
+ opacity: 1;
188
+ }
189
+ }
190
+
191
+ .dot {
192
+ width: 7px;
193
+ height: 7px;
194
+ border-radius: 50%;
195
+ background: var(--accent);
196
+ animation: pulse 2s ease-in-out infinite;
197
+ }
198
+
199
+ @keyframes pulse {
200
+
201
+ 0%,
202
+ 100% {
203
+ opacity: 1;
204
+ transform: scale(1);
205
+ }
206
+
207
+ 50% {
208
+ opacity: 0.5;
209
+ transform: scale(0.8);
210
+ }
211
+ }
212
+
213
+ /* Title area */
214
+ .title-area {
215
+ display: flex;
216
+ align-items: center;
217
+ gap: 20px;
218
+ margin-bottom: 13px;
219
+ animation: fadeUp 0.5s 0.15s cubic-bezier(0.16, 1, 0.3, 1) both;
220
+ padding-bottom: 10px;
221
+ }
222
+
223
+ .title-content {
224
+ display: flex;
225
+ flex-direction: column;
226
+ justify-content: center;
227
+ min-width: 0;
228
+ }
229
+
230
+ .check-icon {
231
+ width: 72px;
232
+ height: 72px;
233
+ border-radius: 18px;
234
+ background: var(--accent-dim);
235
+ border: 1px solid var(--border-accent);
236
+ display: flex;
237
+ align-items: center;
238
+ justify-content: center;
239
+ margin-bottom: 0;
240
+ flex-shrink: 0;
241
+ }
242
+
243
+ .check-icon svg {
244
+ width: 34px;
245
+ height: 34px;
246
+ color: var(--accent);
247
+ animation: spin 3s linear infinite;
248
+ opacity: 0.92;
249
+ }
250
+
251
+ @keyframes spin {
252
+ from {
253
+ transform: rotate(0deg);
254
+ }
255
+
256
+ to {
257
+ transform: rotate(360deg);
258
+ }
259
+ }
260
+
261
+ h1 {
262
+ font-size: 25px;
263
+ font-weight: 700;
264
+ letter-spacing: -0.03em;
265
+ color: var(--text-primary);
266
+ line-height: 1.05;
267
+ margin-bottom: 10px;
268
+ }
269
+
270
+ .subtitle {
271
+ font-size: 13px;
272
+ font-weight: 400;
273
+ color: var(--text-secondary);
274
+ line-height: 1.75;
275
+ max-width: 520px;
276
+ }
277
+
278
+ /* Info rows */
279
+ .info-block {
280
+ background: var(--surface2);
281
+ border: 1px solid var(--border);
282
+ border-radius: 12px;
283
+ overflow: hidden;
284
+ margin-bottom: 20px;
285
+ animation: fadeUp 0.5s 0.25s cubic-bezier(0.16, 1, 0.3, 1) both;
286
+ backdrop-filter: blur(8px);
287
+ }
288
+
289
+ .info-row {
290
+ display: flex;
291
+ align-items: center;
292
+ justify-content: space-between;
293
+ padding: 16px 18px;
294
+ gap: 12px;
295
+ }
296
+
297
+ .info-row+.info-row {
298
+ border-top: 1px solid var(--border);
299
+ }
300
+
301
+ .info-label {
302
+ font-family: var(--mono);
303
+ font-size: 10px;
304
+ color: var(--text-muted);
305
+ text-transform: uppercase;
306
+ letter-spacing: 0.07em;
307
+ flex-shrink: 0;
308
+ }
309
+
310
+ .info-value {
311
+ font-family: var(--mono);
312
+ font-size: 13px;
313
+ color: var(--text-secondary);
314
+ text-align: right;
315
+ word-break: break-word;
316
+ }
317
+
318
+ .info-value.highlight {
319
+ color: var(--text-primary);
320
+ font-weight: 500;
321
+ }
322
+
323
+ .avatar-row {
324
+ display: flex;
325
+ align-items: center;
326
+ gap: 8px;
327
+ }
328
+
329
+ .avatar {
330
+ width: 22px;
331
+ height: 22px;
332
+ border-radius: 50%;
333
+ background: var(--accent-dim);
334
+ border: 1px solid var(--border-accent);
335
+ display: flex;
336
+ align-items: center;
337
+ justify-content: center;
338
+ overflow: hidden;
339
+ flex-shrink: 0;
340
+ }
341
+
342
+ .avatar img {
343
+ width: 100%;
344
+ height: 100%;
345
+ object-fit: cover;
346
+ }
347
+
348
+ .avatar-placeholder {
349
+ font-size: 11px;
350
+ font-weight: 700;
351
+ color: var(--accent);
352
+ font-family: var(--sans);
353
+ }
354
+
355
+ /* Scope tags */
356
+ .scopes {
357
+ display: flex;
358
+ flex-wrap: wrap;
359
+ gap: 6px;
360
+ justify-content: flex-end;
361
+ }
362
+
363
+ .scope-tag {
364
+ font-family: var(--mono);
365
+ font-size: 10px;
366
+ padding: 3px 8px;
367
+ border-radius: 4px;
368
+ background: rgba(96, 165, 250, 0.05);
369
+ border: 1px solid var(--border-accent);
370
+ color: var(--accent);
371
+ letter-spacing: 0.03em;
372
+ }
373
+
374
+ /* Close hint */
375
+ .close-hint {
376
+ display: flex;
377
+ align-items: center;
378
+ gap: 10px;
379
+ padding: 14px 16px;
380
+ background: var(--surface2);
381
+ border: 1px solid var(--border);
382
+ border-radius: 12px;
383
+ animation: fadeUp 0.5s 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
384
+ }
385
+
386
+ .close-hint svg {
387
+ color: var(--text-muted);
388
+ flex-shrink: 0;
389
+ width: 16px;
390
+ height: 16px;
391
+ }
392
+
393
+ .close-hint-text {
394
+ font-family: var(--mono);
395
+ font-size: 12px;
396
+ color: var(--text-secondary);
397
+ line-height: 1.4;
398
+ }
399
+
400
+ .close-hint-text strong {
401
+ color: var(--text-primary);
402
+ font-weight: 500;
403
+ }
404
+
405
+ /* Footer */
406
+ .footer {
407
+ margin-top: 28px;
408
+ display: flex;
409
+ align-items: center;
410
+ justify-content: space-between;
411
+ animation: fadeIn 0.4s 0.5s both;
412
+ }
413
+
414
+ .timestamp {
415
+ font-family: var(--mono);
416
+ font-size: 11px;
417
+ color: var(--text-muted);
418
+ }
419
+
420
+ .security-badge {
421
+ display: flex;
422
+ align-items: center;
423
+ gap: 5px;
424
+ font-family: var(--mono);
425
+ font-size: 10px;
426
+ color: var(--text-muted);
427
+ letter-spacing: 0.04em;
428
+ }
429
+
430
+ .security-badge svg {
431
+ width: 12px;
432
+ height: 12px;
433
+ }
434
+
435
+ @media (max-width: 720px) {
436
+ .title-area {
437
+ align-items: flex-start;
438
+ gap: 16px;
439
+ }
440
+
441
+ .check-icon {
442
+ width: 58px;
443
+ height: 58px;
444
+ }
445
+
446
+ .check-icon svg {
447
+ width: 28px;
448
+ height: 28px;
449
+ }
450
+
451
+ h1 {
452
+ font-size: 28px;
453
+ }
454
+
455
+ .subtitle {
456
+ font-size: 14px;
457
+ }
458
+ }
459
+ </style>
460
+ </head>
461
+
462
+ <body>
463
+ <div class="card">
464
+
465
+ <!-- Header -->
466
+ <div class="header">
467
+ <div class="logo">
468
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
469
+ <path d="M12 2v4" stroke="#0a0c10" stroke-width="2.5" stroke-linecap="round" />
470
+ <path d="M12 18v4" stroke="#0a0c10" stroke-width="2.5" stroke-linecap="round" />
471
+ <path d="M4.93 4.93l2.83 2.83" stroke="#0a0c10" stroke-width="2.5"
472
+ stroke-linecap="round" />
473
+ <path d="M16.24 16.24l2.83 2.83" stroke="#0a0c10" stroke-width="2.5"
474
+ stroke-linecap="round" />
475
+ <path d="M2 12h4" stroke="#0a0c10" stroke-width="2.5" stroke-linecap="round" />
476
+ <path d="M18 12h4" stroke="#0a0c10" stroke-width="2.5" stroke-linecap="round" />
477
+ <path d="M4.93 19.07l2.83-2.83" stroke="#0a0c10" stroke-width="2.5"
478
+ stroke-linecap="round" />
479
+ <path d="M16.24 7.76l2.83-2.83" stroke="#0a0c10" stroke-width="2.5"
480
+ stroke-linecap="round" />
481
+ </svg>
482
+ </div>
483
+ <div class="brand">
484
+ <span class="brand-name">Viza Development Platform</span>
485
+ <span class="brand-sub">github.com · oauth2</span>
486
+ </div>
487
+ <div class="status-badge" aria-live="polite">
488
+ <span class="dot"></span>
489
+ AUTHORIZING
490
+ </div>
491
+ </div>
492
+
493
+ <!-- Title -->
494
+ <div class="title-area">
495
+ <div class="check-icon">
496
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"
497
+ stroke-linecap="round" stroke-linejoin="round">
498
+ <path d="M12 2v4" />
499
+ <path d="M12 18v4" />
500
+ <path d="M4.93 4.93l2.83 2.83" />
501
+ <path d="M16.24 16.24l2.83 2.83" />
502
+ <path d="M2 12h4" />
503
+ <path d="M18 12h4" />
504
+ <path d="M4.93 19.07l2.83-2.83" />
505
+ <path d="M16.24 7.76l2.83-2.83" />
506
+ </svg>
507
+ </div>
508
+
509
+ <div class="title-content">
510
+ <h1>Authorizing with Viza...</h1>
511
+ <p class="subtitle">
512
+ Completing secure authentication flow.
513
+ Please wait while Viza verifies your access permissions.
514
+ </p>
515
+ </div>
516
+ </div>
517
+
518
+ <!-- Info block -->
519
+ <div class="info-block" aria-live="polite">
520
+ <div class="info-row">
521
+ <span class="info-label">Provider</span>
522
+ <span class="info-value highlight">GitHub OAuth</span>
523
+ </div>
524
+ <div class="info-row">
525
+ <span class="info-label">Status</span>
526
+ <span class="info-value highlight" id="auth-status-value">Exchanging authorization code</span>
527
+ </div>
528
+ <div class="info-row">
529
+ <span class="info-label">Security</span>
530
+ <div class="scopes">
531
+ <span class="scope-tag">PKCE</span>
532
+ <span class="scope-tag">TLS 1.3</span>
533
+ <span class="scope-tag">JWT</span>
534
+ </div>
535
+ </div>
536
+ </div>
537
+
538
+ <!-- Close hint -->
539
+ <div class="close-hint">
540
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
541
+ stroke-linejoin="round">
542
+ <rect x="3" y="3" width="18" height="18" rx="3" />
543
+ <path d="M9 3v18M15 3v18M3 9h18M3 15h18" opacity="0.4" />
544
+ <path d="M9 12h6" />
545
+ </svg>
546
+ <span class="close-hint-text">
547
+ <strong>Do not close this window.</strong> Authentication is still being processed by the Viza
548
+ authorization service.
549
+ </span>
550
+ </div>
551
+
552
+ <!-- Footer -->
553
+ <div class="footer">
554
+ <span class="timestamp" id="timestamp">—</span>
555
+ <div class="security-badge">
556
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
557
+ stroke-linecap="round" stroke-linejoin="round">
558
+ <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
559
+ </svg>
560
+ OAuth 2.0 · TLS 1.3
561
+ </div>
562
+ </div>
563
+
564
+ </div>
565
+
566
+ <script>
567
+ const sessionId = "__SESSION_ID__";
568
+
569
+ const timestampEl = document.getElementById("timestamp");
570
+ if (timestampEl) {
571
+ timestampEl.textContent = new Date().toLocaleString();
572
+ }
573
+
574
+ const statusValueEl = document.getElementById(
575
+ "auth-status-value"
576
+ );
577
+
578
+ const es = new EventSource(
579
+ `/events?sessionId=${encodeURIComponent(sessionId)}`
580
+ );
581
+ es.onopen = () => {
582
+ if (statusValueEl) {
583
+ statusValueEl.textContent =
584
+ "Exchanging authorization code";
585
+ }
586
+ };
587
+
588
+ es.addEventListener("success", (event) => {
589
+ const params = new URLSearchParams({
590
+ sessionId,
591
+ });
592
+
593
+ es.close();
594
+ window.location.href = `/success?${params.toString()}`;
595
+ });
596
+
597
+ es.addEventListener("failed", (event) => {
598
+ const params = new URLSearchParams({
599
+ sessionId,
600
+ });
601
+
602
+ es.close();
603
+ window.location.href = `/failed?${params.toString()}`;
604
+ });
605
+
606
+ es.onerror = () => {
607
+ if (statusValueEl) {
608
+ statusValueEl.textContent = "Connection lost — retrying...";
609
+ }
610
+ };
611
+ setTimeout(() => {
612
+ if (statusValueEl) {
613
+ statusValueEl.textContent =
614
+ "Authentication timeout — please retry";
615
+ }
616
+ }, 60000);
617
+ </script>
618
+ </body>
619
+
620
+ </html>