@lib-acr/browser 0.0.0-stage → 0.0.2

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -2
  3. package/dist/cjs/browser-acr.js +600 -0
  4. package/dist/cjs/browser-acr.js.map +1 -0
  5. package/dist/cjs/captured.js +19 -0
  6. package/dist/cjs/captured.js.map +1 -0
  7. package/dist/cjs/control/command-runner.js +289 -0
  8. package/dist/cjs/control/command-runner.js.map +1 -0
  9. package/dist/cjs/control/ui.js +277 -0
  10. package/dist/cjs/control/ui.js.map +1 -0
  11. package/dist/cjs/device.js +54 -0
  12. package/dist/cjs/device.js.map +1 -0
  13. package/dist/cjs/fetch-tracker.js +63 -0
  14. package/dist/cjs/fetch-tracker.js.map +1 -0
  15. package/dist/cjs/identity.js +120 -0
  16. package/dist/cjs/identity.js.map +1 -0
  17. package/dist/cjs/index.js +73 -0
  18. package/dist/cjs/index.js.map +1 -0
  19. package/dist/cjs/sdk-info.js +6 -0
  20. package/dist/cjs/sdk-info.js.map +1 -0
  21. package/dist/cjs/url.js +31 -0
  22. package/dist/cjs/url.js.map +1 -0
  23. package/dist/cjs/web-vitals.js +160 -0
  24. package/dist/cjs/web-vitals.js.map +1 -0
  25. package/dist/esm/browser-acr.js +595 -0
  26. package/dist/esm/browser-acr.js.map +1 -0
  27. package/dist/esm/captured.js +15 -0
  28. package/dist/esm/captured.js.map +1 -0
  29. package/dist/esm/control/command-runner.js +284 -0
  30. package/dist/esm/control/command-runner.js.map +1 -0
  31. package/dist/esm/control/ui.js +272 -0
  32. package/dist/esm/control/ui.js.map +1 -0
  33. package/dist/esm/device.js +50 -0
  34. package/dist/esm/device.js.map +1 -0
  35. package/dist/esm/fetch-tracker.js +60 -0
  36. package/dist/esm/fetch-tracker.js.map +1 -0
  37. package/dist/esm/identity.js +112 -0
  38. package/dist/esm/identity.js.map +1 -0
  39. package/dist/esm/index.js +13 -0
  40. package/dist/esm/index.js.map +1 -0
  41. package/dist/esm/package.json +3 -0
  42. package/dist/esm/sdk-info.js +3 -0
  43. package/dist/esm/sdk-info.js.map +1 -0
  44. package/dist/esm/url.js +27 -0
  45. package/dist/esm/url.js.map +1 -0
  46. package/dist/esm/web-vitals.js +156 -0
  47. package/dist/esm/web-vitals.js.map +1 -0
  48. package/dist/types/browser-acr.d.ts +142 -0
  49. package/dist/types/captured.d.ts +4 -0
  50. package/dist/types/control/command-runner.d.ts +57 -0
  51. package/dist/types/control/ui.d.ts +50 -0
  52. package/dist/types/device.d.ts +24 -0
  53. package/dist/types/fetch-tracker.d.ts +15 -0
  54. package/dist/types/identity.d.ts +48 -0
  55. package/dist/types/index.d.ts +17 -0
  56. package/dist/types/sdk-info.d.ts +4 -0
  57. package/dist/types/url.d.ts +10 -0
  58. package/dist/types/web-vitals.d.ts +50 -0
  59. package/package.json +40 -4
@@ -0,0 +1,284 @@
1
+ import { clearCacheParams, planCommands, reloadParams } from '@lib-acr/core';
2
+ /** Ids of commands already applied on this browser, newest last. */
3
+ export const COMMANDS_KEY = 'acr.cmds';
4
+ /** sessionStorage: epoch ms of the last reload the SDK triggered, a second guard against reload loops. */
5
+ export const RELOAD_GUARD_KEY = 'acr.reloadAt';
6
+ /** Storage keys starting with this prefix belong to the SDK and survive `clear_cache`. */
7
+ export const RESERVED_PREFIX = 'acr.';
8
+ export const COMMAND_APPLIED_EVENT = 'acr.command_applied';
9
+ const MAX_APPLIED_IDS = 50;
10
+ const RELOAD_GUARD_MS = 30000;
11
+ const FLUSH_BEFORE_RELOAD_MS = 2000;
12
+ const IDB_DELETE_TIMEOUT_MS = 3000;
13
+ const CATEGORIES = ['cacheStorage', 'serviceWorkers', 'localStorage', 'sessionStorage', 'indexedDb', 'cookies'];
14
+ /**
15
+ * Runs remote commands at most once per browser (docs/SDK-PROTOCOL.md §5): plans them with planCommands(), records
16
+ * each id in `localStorage['acr.cmds']` before acting (a command whose id cannot be recorded is not run, so a reload
17
+ * can never loop), acts, sends `acr.command_applied` and flushes. A reload, if any command asked for one, happens once
18
+ * at the end, after the acknowledgements were sent.
19
+ */
20
+ export class CommandRunner {
21
+ constructor(deps) {
22
+ this.deps = deps;
23
+ this.chain = Promise.resolve();
24
+ }
25
+ /** Runs are serialised: a run started while another is in progress waits for it. Never rejects. */
26
+ run(commands) {
27
+ const next = this.chain.then(() => this.execute(commands)).catch(() => []);
28
+ this.chain = next;
29
+ return next;
30
+ }
31
+ /** Ids recorded in `acr.cmds`. */
32
+ appliedIds() {
33
+ try {
34
+ const raw = this.deps.localStorage()?.getItem(COMMANDS_KEY);
35
+ const parsed = raw ? JSON.parse(raw) : [];
36
+ return Array.isArray(parsed) ? parsed.filter((v) => typeof v === 'string') : [];
37
+ }
38
+ catch {
39
+ return [];
40
+ }
41
+ }
42
+ async execute(commands) {
43
+ const now = this.deps.now?.() ?? Date.now();
44
+ const plan = planCommands(commands, { appliedIds: this.appliedIds(), firstSeenAt: this.deps.firstSeen(), now });
45
+ const outcomes = [];
46
+ let reload = false;
47
+ for (const decision of plan) {
48
+ if (decision.action !== 'apply')
49
+ continue;
50
+ const { command } = decision;
51
+ if (!this.record(command.id))
52
+ continue;
53
+ let outcome;
54
+ if (command.type === 'clear_cache') {
55
+ const params = clearCacheParams(command);
56
+ const { result, cleared } = await clearCaches(params, this.deps);
57
+ outcome = { commandId: command.id, type: command.type, result, cleared };
58
+ if (params.reload)
59
+ reload = true;
60
+ }
61
+ else {
62
+ if (reloadParams(command).mode === 'immediate')
63
+ reload = true;
64
+ else
65
+ this.deps.promptReload();
66
+ outcome = { commandId: command.id, type: command.type, result: 'ok', cleared: [] };
67
+ }
68
+ outcomes.push(outcome);
69
+ try {
70
+ this.deps.track(COMMAND_APPLIED_EVENT, {
71
+ commandId: outcome.commandId,
72
+ type: outcome.type,
73
+ result: outcome.result,
74
+ cleared: outcome.cleared.join(','),
75
+ });
76
+ }
77
+ catch {
78
+ /* acknowledgements are best effort */
79
+ }
80
+ }
81
+ if (outcomes.length > 0)
82
+ await withTimeout(this.deps.flush(), FLUSH_BEFORE_RELOAD_MS);
83
+ if (reload) {
84
+ if (this.reloadAllowed(now))
85
+ this.deps.reload();
86
+ else
87
+ this.deps.promptReload();
88
+ }
89
+ return outcomes;
90
+ }
91
+ /** Appends the id to `acr.cmds` (last 50 kept) and reads it back. False when storage is unavailable. */
92
+ record(id) {
93
+ try {
94
+ const store = this.deps.localStorage();
95
+ if (!store)
96
+ return false;
97
+ const ids = this.appliedIds().filter((v) => v !== id);
98
+ ids.push(id);
99
+ store.setItem(COMMANDS_KEY, JSON.stringify(ids.slice(-MAX_APPLIED_IDS)));
100
+ return this.appliedIds().includes(id);
101
+ }
102
+ catch {
103
+ return false;
104
+ }
105
+ }
106
+ /** No second SDK-triggered reload within 30 s in this tab. */
107
+ reloadAllowed(now) {
108
+ try {
109
+ const store = this.deps.sessionStorage();
110
+ const last = Number(store?.getItem(RELOAD_GUARD_KEY) ?? 0);
111
+ if (Number.isFinite(last) && now - last < RELOAD_GUARD_MS && now >= last)
112
+ return false;
113
+ store?.setItem(RELOAD_GUARD_KEY, String(now));
114
+ }
115
+ catch {
116
+ /* the id in acr.cmds already prevents a loop */
117
+ }
118
+ return true;
119
+ }
120
+ }
121
+ /** Clears the categories set in `params`. Returns the overall result and the categories cleared completely. */
122
+ export async function clearCaches(params, stores) {
123
+ const requested = CATEGORIES.filter((c) => params[c]);
124
+ const results = [];
125
+ for (const category of requested) {
126
+ let result;
127
+ try {
128
+ result = await clearCategory(category, stores);
129
+ }
130
+ catch {
131
+ result = 'failed';
132
+ }
133
+ results.push([category, result]);
134
+ }
135
+ const cleared = results.filter(([, r]) => r === 'ok').map(([c]) => c);
136
+ let result = 'ok';
137
+ if (results.some(([, r]) => r !== 'ok'))
138
+ result = results.every(([, r]) => r === 'failed') ? 'failed' : 'partial';
139
+ return { result, cleared };
140
+ }
141
+ function clearCategory(category, stores) {
142
+ switch (category) {
143
+ case 'cacheStorage':
144
+ return clearCacheStorage();
145
+ case 'serviceWorkers':
146
+ return unregisterServiceWorkers();
147
+ case 'localStorage':
148
+ return clearStorage(stores.localStorage());
149
+ case 'sessionStorage':
150
+ return clearStorage(stores.sessionStorage());
151
+ case 'indexedDb':
152
+ return clearIndexedDb();
153
+ case 'cookies':
154
+ return clearCookies();
155
+ }
156
+ }
157
+ async function clearCacheStorage() {
158
+ const cacheStorage = globalThis.caches;
159
+ if (!cacheStorage)
160
+ return 'ok'; // no Cache Storage, nothing cached
161
+ const keys = await cacheStorage.keys();
162
+ const results = await Promise.all(keys.map((key) => cacheStorage.delete(key).catch(() => false)));
163
+ return aggregate(results.map((deleted) => deleted !== false));
164
+ }
165
+ async function unregisterServiceWorkers() {
166
+ const container = typeof navigator !== 'undefined' ? navigator.serviceWorker : undefined;
167
+ if (!container || typeof container.getRegistrations !== 'function')
168
+ return 'ok';
169
+ const registrations = await container.getRegistrations();
170
+ const results = await Promise.all(registrations.map((r) => r.unregister().catch(() => false)));
171
+ return aggregate(results);
172
+ }
173
+ /** Removes every key except the SDK's own `acr.*` keys (anonymous id, first seen, applied commands, opt-out, …). */
174
+ function clearStorage(store) {
175
+ if (!store)
176
+ return 'failed';
177
+ const keys = [];
178
+ for (let i = 0; i < store.length; i++) {
179
+ const key = store.key(i);
180
+ if (key !== null && !key.startsWith(RESERVED_PREFIX))
181
+ keys.push(key);
182
+ }
183
+ for (const key of keys)
184
+ store.removeItem(key);
185
+ return keys.every((key) => store.getItem(key) === null) ? 'ok' : 'partial';
186
+ }
187
+ async function clearIndexedDb() {
188
+ const idb = globalThis.indexedDB;
189
+ if (!idb)
190
+ return 'ok'; // no IndexedDB, nothing stored
191
+ // Firefox before 126 and older Safari cannot list databases, so nothing can be deleted by name.
192
+ if (typeof idb.databases !== 'function')
193
+ return 'partial';
194
+ const databases = await idb.databases();
195
+ const results = await Promise.all(databases.filter((db) => db.name).map((db) => deleteDatabase(idb, db.name)));
196
+ return aggregate(results);
197
+ }
198
+ function deleteDatabase(idb, name) {
199
+ return new Promise((resolve) => {
200
+ const timer = setTimeout(() => resolve(false), IDB_DELETE_TIMEOUT_MS);
201
+ const done = (ok) => {
202
+ clearTimeout(timer);
203
+ resolve(ok);
204
+ };
205
+ try {
206
+ const request = idb.deleteDatabase(name);
207
+ request.onsuccess = () => done(true);
208
+ request.onerror = () => done(false);
209
+ // Blocked: another tab holds a connection. The deletion is queued and completes when it closes.
210
+ request.onblocked = () => done(true);
211
+ }
212
+ catch {
213
+ done(false);
214
+ }
215
+ });
216
+ }
217
+ /**
218
+ * Expires every cookie readable by script, for the current path and its parents and for the host and its parent
219
+ * domains (the attributes a cookie was set with cannot be read back). HttpOnly cookies are invisible to scripts and
220
+ * cannot be cleared by any SDK.
221
+ */
222
+ function clearCookies() {
223
+ if (typeof document === 'undefined' || typeof location === 'undefined')
224
+ return 'failed';
225
+ const names = cookieNames();
226
+ if (names.length === 0)
227
+ return 'ok';
228
+ const paths = pathPrefixes(location.pathname);
229
+ const domains = [null, ...parentDomains(location.hostname)];
230
+ for (const name of names) {
231
+ for (const domain of domains) {
232
+ for (const path of paths) {
233
+ document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; max-age=0; path=${path}${domain ? `; domain=${domain}` : ''}`;
234
+ }
235
+ }
236
+ }
237
+ const remaining = new Set(cookieNames());
238
+ return names.some((n) => remaining.has(n)) ? 'partial' : 'ok';
239
+ }
240
+ function cookieNames() {
241
+ return document.cookie
242
+ .split(';')
243
+ .map((part) => part.split('=')[0].trim())
244
+ .filter((name) => name !== '');
245
+ }
246
+ /** '/a/b/c' → ['/', '/a', '/a/b', '/a/b/c'] (at most 10 levels). */
247
+ function pathPrefixes(pathname) {
248
+ const segments = pathname.split('/').filter(Boolean).slice(0, 10);
249
+ const out = ['/'];
250
+ let current = '';
251
+ for (const segment of segments) {
252
+ current += `/${segment}`;
253
+ out.push(current);
254
+ }
255
+ return out;
256
+ }
257
+ /** 'app.shop.example.com' → ['app.shop.example.com', 'shop.example.com', 'example.com']; IPs and 'localhost' → []. */
258
+ function parentDomains(hostname) {
259
+ if (!hostname.includes('.') || /^[\d.]+$/.test(hostname) || hostname.includes(':'))
260
+ return [];
261
+ const labels = hostname.split('.');
262
+ const out = [];
263
+ for (let i = 0; i < labels.length - 1; i++)
264
+ out.push(labels.slice(i).join('.'));
265
+ return out;
266
+ }
267
+ function aggregate(results) {
268
+ if (results.every(Boolean))
269
+ return 'ok';
270
+ return results.some(Boolean) ? 'partial' : 'failed';
271
+ }
272
+ function withTimeout(promise, ms) {
273
+ let timer;
274
+ return Promise.race([
275
+ promise.then(() => undefined, () => undefined),
276
+ new Promise((resolve) => {
277
+ timer = setTimeout(resolve, ms);
278
+ }),
279
+ ]).finally(() => {
280
+ if (timer)
281
+ clearTimeout(timer);
282
+ });
283
+ }
284
+ //# sourceMappingURL=command-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-runner.js","sourceRoot":"","sources":["../../../src/control/command-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,272 @@
1
+ export function resolveUiOptions(ui) {
2
+ if (ui === false)
3
+ return { maintenance: false, announcement: false, release: false };
4
+ if (ui === true || ui === undefined)
5
+ return { maintenance: true, announcement: true, release: true };
6
+ return { maintenance: ui.maintenance ?? true, announcement: ui.announcement ?? true, release: ui.release ?? true };
7
+ }
8
+ /** Tag of the element that hosts the shadow root. */
9
+ export const UI_HOST_TAG = 'acr-ui';
10
+ const STYLES = `
11
+ :host{all:initial}
12
+ *{box-sizing:border-box}
13
+ .wrap{--acr-bg:#ffffff;--acr-fg:#111827;--acr-muted:#4b5563;--acr-border:#e5e7eb;--acr-accent:#2563eb;--acr-accent-fg:#ffffff;
14
+ --acr-info-bg:#eff6ff;--acr-info-fg:#1e3a8a;--acr-warn-bg:#fffbeb;--acr-warn-fg:#78350f;--acr-crit-bg:#fef2f2;--acr-crit-fg:#7f1d1d;
15
+ font:14px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;color:var(--acr-fg)}
16
+ @media (prefers-color-scheme:dark){.wrap{--acr-bg:#111827;--acr-fg:#f9fafb;--acr-muted:#9ca3af;--acr-border:#374151;--acr-accent:#3b82f6;
17
+ --acr-info-bg:#172554;--acr-info-fg:#dbeafe;--acr-warn-bg:#451a03;--acr-warn-fg:#fef3c7;--acr-crit-bg:#450a0a;--acr-crit-fg:#fee2e2}}
18
+ button{font:inherit;min-height:36px;padding:0 14px;border-radius:8px;border:1px solid var(--acr-border);background:transparent;color:inherit;cursor:pointer}
19
+ button:focus-visible,a:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--acr-accent);outline-offset:2px}
20
+ .primary{background:var(--acr-accent);border-color:transparent;color:var(--acr-accent-fg)}
21
+ .close{min-width:36px;padding:0;font-size:18px;line-height:1;border-color:transparent}
22
+ .overlay{position:fixed;inset:0;z-index:2147483647;display:flex;align-items:center;justify-content:center;padding:16px;background:rgba(17,24,39,.72)}
23
+ .dialog{width:100%;max-width:440px;padding:24px;border-radius:12px;background:var(--acr-bg);color:var(--acr-fg);box-shadow:0 20px 50px rgba(0,0,0,.35)}
24
+ .dialog:focus{outline:none}
25
+ .dialog h2{margin:0 0 8px;font-size:20px;line-height:1.3}
26
+ .dialog p{margin:0 0 12px}
27
+ .muted{color:var(--acr-muted);font-size:13px}
28
+ .banner{position:fixed;top:0;left:0;right:0;z-index:2147483646;display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;padding:8px 8px 8px 16px;box-shadow:0 1px 3px rgba(0,0,0,.15)}
29
+ .banner .msg{flex:1 1 200px}
30
+ .banner a{color:inherit;font-weight:600}
31
+ .sev-info{background:var(--acr-info-bg);color:var(--acr-info-fg)}
32
+ .sev-warning{background:var(--acr-warn-bg);color:var(--acr-warn-fg)}
33
+ .sev-critical{background:var(--acr-crit-bg);color:var(--acr-crit-fg)}
34
+ .toast{position:fixed;right:16px;bottom:16px;z-index:2147483646;display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;max-width:min(420px,calc(100vw - 32px));
35
+ padding:12px 12px 12px 16px;border:1px solid var(--acr-border);border-radius:12px;background:var(--acr-bg);color:var(--acr-fg);box-shadow:0 10px 30px rgba(0,0,0,.2)}
36
+ .toast .msg{flex:1 1 160px}
37
+ @media (max-width:480px){.toast{left:16px;right:16px;max-width:none}}
38
+ @media (prefers-reduced-motion:no-preference){.toast,.banner{animation:acr-in .18s ease-out}}
39
+ @keyframes acr-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
40
+ `;
41
+ /**
42
+ * The default remote-control UI: maintenance screen, announcement banner and "new version available" prompt. It lives
43
+ * in the shadow root of one `<acr-ui>` element, so the page's CSS cannot reach it and its CSS cannot leak out. Server
44
+ * text is always inserted as text, never as HTML. The element is added only while something is shown.
45
+ */
46
+ export class ControlUI {
47
+ constructor(options, actions) {
48
+ this.options = options;
49
+ this.actions = actions;
50
+ this.host = null;
51
+ this.wrap = null;
52
+ this.keys = { maintenance: '', banner: '', toast: '' };
53
+ this.elements = {};
54
+ this.dismissedToast = null;
55
+ this.lastState = null;
56
+ this.restoreFocus = null;
57
+ this.savedOverflow = null;
58
+ this.waitingForBody = false;
59
+ }
60
+ render(state) {
61
+ this.lastState = state;
62
+ if (typeof document === 'undefined')
63
+ return;
64
+ const maintenance = this.options.maintenance && state.maintenance.active ? state.maintenance : null;
65
+ const announcement = this.options.announcement && state.announcement && !state.announcement.dismissed ? state.announcement : null;
66
+ let toast = this.options.release ? toastModel(state) : null;
67
+ if (toast && toast.key === this.dismissedToast)
68
+ toast = null;
69
+ if (!maintenance && !announcement && !toast) {
70
+ this.unmount();
71
+ return;
72
+ }
73
+ if (!this.mount())
74
+ return;
75
+ this.update('maintenance', maintenance ? JSON.stringify(maintenance) : '', () => this.buildMaintenance(maintenance));
76
+ this.update('banner', announcement ? JSON.stringify(announcement) : '', () => this.buildBanner(announcement));
77
+ this.update('toast', toast ? `${toast.key}|${toast.text}` : '', () => this.buildToast(toast));
78
+ }
79
+ /** Removes the element and everything it shows. */
80
+ destroy() {
81
+ this.unmount();
82
+ this.lastState = null;
83
+ }
84
+ mount() {
85
+ if (this.wrap)
86
+ return true;
87
+ if (!document.body) {
88
+ if (!this.waitingForBody) {
89
+ this.waitingForBody = true;
90
+ document.addEventListener('DOMContentLoaded', () => {
91
+ this.waitingForBody = false;
92
+ if (this.lastState)
93
+ this.render(this.lastState);
94
+ }, { once: true });
95
+ }
96
+ return false;
97
+ }
98
+ const host = document.createElement(UI_HOST_TAG);
99
+ if (typeof host.attachShadow !== 'function')
100
+ return false;
101
+ // Open so tests and developer tools can inspect it; the page's styles are isolated either way.
102
+ const root = host.attachShadow({ mode: 'open' });
103
+ const style = document.createElement('style');
104
+ style.textContent = STYLES;
105
+ const wrap = document.createElement('div');
106
+ wrap.className = 'wrap';
107
+ root.append(style, wrap);
108
+ document.body.appendChild(host);
109
+ this.host = host;
110
+ this.wrap = wrap;
111
+ return true;
112
+ }
113
+ unmount() {
114
+ for (const section of ['maintenance', 'banner', 'toast'])
115
+ this.update(section, '', () => null);
116
+ this.host?.remove();
117
+ this.host = null;
118
+ this.wrap = null;
119
+ }
120
+ /** Replaces a section only when its content changes, so focus and animations are not disturbed. */
121
+ update(section, key, build) {
122
+ if (this.keys[section] === key)
123
+ return;
124
+ const previous = this.elements[section];
125
+ if (previous) {
126
+ previous.remove();
127
+ delete this.elements[section];
128
+ if (section === 'maintenance')
129
+ this.onMaintenanceHidden();
130
+ }
131
+ this.keys[section] = key;
132
+ if (!key || !this.wrap)
133
+ return;
134
+ const element = build();
135
+ this.elements[section] = element;
136
+ this.wrap.appendChild(element);
137
+ if (section === 'maintenance')
138
+ this.onMaintenanceShown(element);
139
+ }
140
+ buildMaintenance(m) {
141
+ const overlay = el('div', 'overlay');
142
+ const dialog = el('div', 'dialog');
143
+ dialog.setAttribute('role', 'dialog');
144
+ dialog.setAttribute('aria-modal', 'true');
145
+ dialog.setAttribute('aria-labelledby', 'acr-maintenance-title');
146
+ dialog.setAttribute('aria-describedby', 'acr-maintenance-message');
147
+ dialog.tabIndex = -1;
148
+ const title = el('h2', '', m.title || 'Down for maintenance');
149
+ title.id = 'acr-maintenance-title';
150
+ const message = el('p', '', m.message || 'We are making some improvements and will be back shortly.');
151
+ message.id = 'acr-maintenance-message';
152
+ dialog.append(title, message);
153
+ const ends = formatDate(m.endsAt);
154
+ if (ends)
155
+ dialog.append(el('p', 'muted', `Expected back ${ends}`));
156
+ const retry = el('button', 'primary', 'Check again');
157
+ retry.type = 'button';
158
+ retry.addEventListener('click', () => this.actions.refresh());
159
+ dialog.append(retry);
160
+ overlay.append(dialog);
161
+ // Focus trap: Tab and Shift+Tab stay inside the dialog; Escape does nothing (maintenance is not dismissible).
162
+ overlay.addEventListener('keydown', (event) => {
163
+ if (event.key !== 'Tab')
164
+ return;
165
+ event.preventDefault();
166
+ const focusables = [retry];
167
+ const active = overlay.getRootNode().activeElement;
168
+ const index = focusables.indexOf(active);
169
+ const next = focusables[(index + (event.shiftKey ? -1 : 1) + focusables.length) % focusables.length];
170
+ next.focus();
171
+ });
172
+ return overlay;
173
+ }
174
+ onMaintenanceShown(overlay) {
175
+ const active = document.activeElement;
176
+ this.restoreFocus = active instanceof HTMLElement && active !== this.host ? active : null;
177
+ this.savedOverflow = document.documentElement.style.overflow;
178
+ document.documentElement.style.overflow = 'hidden';
179
+ overlay.querySelector('button')?.focus();
180
+ }
181
+ onMaintenanceHidden() {
182
+ if (this.savedOverflow !== null)
183
+ document.documentElement.style.overflow = this.savedOverflow;
184
+ this.savedOverflow = null;
185
+ const target = this.restoreFocus;
186
+ this.restoreFocus = null;
187
+ if (target && target.isConnected)
188
+ target.focus();
189
+ }
190
+ buildBanner(a) {
191
+ const banner = el('div', `banner sev-${a.severity}`);
192
+ // Critical and warning announcements interrupt screen readers; informational ones are polite.
193
+ banner.setAttribute('role', a.severity === 'info' ? 'status' : 'alert');
194
+ banner.setAttribute('aria-label', 'Announcement');
195
+ banner.append(el('span', 'msg', a.message));
196
+ if (a.linkUrl) {
197
+ const link = el('a', '', 'Learn more');
198
+ link.href = a.linkUrl;
199
+ link.target = '_blank';
200
+ link.rel = 'noopener noreferrer';
201
+ banner.append(link);
202
+ }
203
+ if (a.dismissible) {
204
+ const close = el('button', 'close', '×');
205
+ close.type = 'button';
206
+ close.setAttribute('aria-label', 'Dismiss announcement');
207
+ close.addEventListener('click', () => this.actions.dismissAnnouncement(a.id));
208
+ banner.append(close);
209
+ banner.addEventListener('keydown', (event) => {
210
+ if (event.key === 'Escape')
211
+ this.actions.dismissAnnouncement(a.id);
212
+ });
213
+ }
214
+ return banner;
215
+ }
216
+ buildToast(t) {
217
+ const toast = el('div', 'toast');
218
+ toast.setAttribute('role', 'status');
219
+ toast.setAttribute('aria-live', 'polite');
220
+ toast.append(el('span', 'msg', t.text));
221
+ const reload = el('button', 'primary', 'Reload');
222
+ reload.type = 'button';
223
+ reload.addEventListener('click', () => this.actions.reload());
224
+ const close = el('button', 'close', '×');
225
+ close.type = 'button';
226
+ close.setAttribute('aria-label', 'Dismiss');
227
+ const dismiss = () => {
228
+ this.dismissedToast = t.key;
229
+ if (this.lastState)
230
+ this.render(this.lastState);
231
+ };
232
+ close.addEventListener('click', dismiss);
233
+ toast.addEventListener('keydown', (event) => {
234
+ if (event.key === 'Escape')
235
+ dismiss();
236
+ });
237
+ toast.append(reload, close);
238
+ return toast;
239
+ }
240
+ }
241
+ function toastModel(state) {
242
+ const r = state.release;
243
+ if (r?.belowMinimum)
244
+ return { key: `min:${r.minimumVersion}`, text: 'An update is required. Reload to get the latest version.' };
245
+ if (state.reloadRequested)
246
+ return { key: 'reload', text: 'A new version is available.' };
247
+ if (r?.updateAvailable)
248
+ return { key: `latest:${r.latestVersion}`, text: 'A new version is available.' };
249
+ return null;
250
+ }
251
+ function el(tag, className, text) {
252
+ const node = document.createElement(tag);
253
+ if (className)
254
+ node.className = className;
255
+ if (text !== undefined)
256
+ node.textContent = text;
257
+ return node;
258
+ }
259
+ function formatDate(iso) {
260
+ if (!iso)
261
+ return null;
262
+ const date = new Date(iso);
263
+ if (Number.isNaN(date.getTime()))
264
+ return null;
265
+ try {
266
+ return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' }).format(date);
267
+ }
268
+ catch {
269
+ return date.toLocaleString();
270
+ }
271
+ }
272
+ //# sourceMappingURL=ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../src/control/ui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /** Coarse browser, OS and device class from a user agent string. No version numbers are kept for the OS. */
2
+ export function parseUserAgent(ua) {
3
+ const browser = /Edg\//.test(ua) ? 'Edge'
4
+ : /OPR\/|Opera/.test(ua) ? 'Opera'
5
+ : /Firefox\//.test(ua) ? 'Firefox'
6
+ : /Chrome\//.test(ua) ? 'Chrome'
7
+ : /Safari\//.test(ua) ? 'Safari'
8
+ : 'Other';
9
+ const os = /Windows/.test(ua) ? 'Windows'
10
+ : /Android/.test(ua) ? 'Android'
11
+ : /iPhone|iPad|iPod/.test(ua) ? 'iOS'
12
+ : /Mac OS X|Macintosh/.test(ua) ? 'macOS'
13
+ : /CrOS/.test(ua) ? 'ChromeOS'
14
+ : /Linux/.test(ua) ? 'Linux'
15
+ : 'Other';
16
+ const deviceType = /iPad|Tablet/.test(ua) ? 'tablet'
17
+ : /Mobi|iPhone|Android.+Mobile/.test(ua) ? 'mobile'
18
+ : ua ? 'desktop'
19
+ : 'unknown';
20
+ return { browser, os, deviceType };
21
+ }
22
+ /** Device and locale facts for session_start. Reads only what the browser exposes without a permission prompt. */
23
+ export function getDeviceInfo(env = typeof navigator !== 'undefined' ? readGlobals() : {}) {
24
+ const parsed = parseUserAgent(env.userAgent ?? '');
25
+ let timezone = null;
26
+ try {
27
+ timezone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;
28
+ }
29
+ catch {
30
+ timezone = null;
31
+ }
32
+ return {
33
+ ...parsed,
34
+ language: env.language ?? null,
35
+ timezone,
36
+ screen: env.screen ? `${env.screen.width}x${env.screen.height}` : null,
37
+ viewport: env.viewport ? `${env.viewport.width}x${env.viewport.height}` : null,
38
+ };
39
+ }
40
+ function readGlobals() {
41
+ const nav = typeof navigator !== 'undefined' ? navigator : undefined;
42
+ const win = typeof window !== 'undefined' ? window : undefined;
43
+ return {
44
+ userAgent: nav?.userAgent,
45
+ language: nav?.language,
46
+ screen: win?.screen ? { width: win.screen.width, height: win.screen.height } : undefined,
47
+ viewport: win ? { width: win.innerWidth, height: win.innerHeight } : undefined,
48
+ };
49
+ }
50
+ //# sourceMappingURL=device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/device.ts"],"names":[],"mappings":"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"}