@adonis-agora/telescope 0.10.0 → 0.12.0

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 (64) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/providers/telescope_ui_provider.d.ts.map +1 -1
  3. package/dist/providers/telescope_ui_provider.js +46 -3
  4. package/dist/providers/telescope_ui_provider.js.map +1 -1
  5. package/dist/src/ai/index.d.ts +1 -1
  6. package/dist/src/ai/index.js +1 -1
  7. package/dist/src/alerts/exception_source.d.ts.map +1 -1
  8. package/dist/src/alerts/exception_source.js +2 -4
  9. package/dist/src/alerts/exception_source.js.map +1 -1
  10. package/dist/src/alerts/index.d.ts +1 -1
  11. package/dist/src/alerts/index.js +1 -1
  12. package/dist/src/context_accessor.d.ts +13 -0
  13. package/dist/src/context_accessor.d.ts.map +1 -1
  14. package/dist/src/context_accessor.js +20 -0
  15. package/dist/src/context_accessor.js.map +1 -1
  16. package/dist/src/entry.d.ts +13 -0
  17. package/dist/src/entry.d.ts.map +1 -1
  18. package/dist/src/entry.js +15 -0
  19. package/dist/src/entry.js.map +1 -1
  20. package/dist/src/index.d.ts +4 -4
  21. package/dist/src/index.d.ts.map +1 -1
  22. package/dist/src/index.js +3 -3
  23. package/dist/src/index.js.map +1 -1
  24. package/dist/src/mcp/index.d.ts +1 -1
  25. package/dist/src/mcp/index.js +1 -1
  26. package/dist/src/metrics/metrics_service.d.ts +13 -0
  27. package/dist/src/metrics/metrics_service.d.ts.map +1 -1
  28. package/dist/src/metrics/metrics_service.js +23 -4
  29. package/dist/src/metrics/metrics_service.js.map +1 -1
  30. package/dist/src/metrics/pulse.d.ts.map +1 -1
  31. package/dist/src/metrics/pulse.js +5 -2
  32. package/dist/src/metrics/pulse.js.map +1 -1
  33. package/dist/src/metrics/stats.d.ts.map +1 -1
  34. package/dist/src/metrics/stats.js +6 -2
  35. package/dist/src/metrics/stats.js.map +1 -1
  36. package/dist/src/profiling/index.d.ts +1 -1
  37. package/dist/src/profiling/index.js +1 -1
  38. package/dist/src/request_watcher.d.ts +46 -8
  39. package/dist/src/request_watcher.d.ts.map +1 -1
  40. package/dist/src/request_watcher.js +63 -16
  41. package/dist/src/request_watcher.js.map +1 -1
  42. package/dist/src/ui/access_denied_page.d.ts +102 -0
  43. package/dist/src/ui/access_denied_page.d.ts.map +1 -0
  44. package/dist/src/ui/access_denied_page.js +266 -0
  45. package/dist/src/ui/access_denied_page.js.map +1 -0
  46. package/dist/src/ui/define_config.d.ts +27 -0
  47. package/dist/src/ui/define_config.d.ts.map +1 -1
  48. package/dist/src/ui/define_config.js +1 -0
  49. package/dist/src/ui/define_config.js.map +1 -1
  50. package/dist/src/ui/guard.d.ts +29 -0
  51. package/dist/src/ui/guard.d.ts.map +1 -1
  52. package/dist/src/ui/guard.js +49 -0
  53. package/dist/src/ui/guard.js.map +1 -1
  54. package/dist/src/ui/index.d.ts +6 -4
  55. package/dist/src/ui/index.d.ts.map +1 -1
  56. package/dist/src/ui/index.js +4 -2
  57. package/dist/src/ui/index.js.map +1 -1
  58. package/dist/src/ui/login_page.d.ts +24 -11
  59. package/dist/src/ui/login_page.d.ts.map +1 -1
  60. package/dist/src/ui/login_page.js +29 -41
  61. package/dist/src/ui/login_page.js.map +1 -1
  62. package/dist/src/watchers/index.d.ts +1 -1
  63. package/dist/src/watchers/index.js +1 -1
  64. package/package.json +1 -1
@@ -1,27 +1,26 @@
1
+ import { escapeHtml } from './access_denied_page.js';
2
+ import { sanitizeReturnTo } from './auth.js';
1
3
  /**
2
- * The built-in `dashboardAuth` login page (`GET <basePath>/login`). Deliberately a small,
3
- * dependency-free, hand-authored HTML page — NOT part of the bundled dashboard SPA — so gating the
4
- * dashboard shell doesn't require rebuilding or extending the bundled Vite app.
5
- *
6
- * `returnTo` and any error state are read CLIENT-SIDE from `location.search` (never server-echoed
7
- * into the HTML), so this function's only per-request-ish input is `basePath` — a developer-
8
- * controlled config value, not user input — and the page body is otherwise a static template.
9
- * This sidesteps HTML-escaping entirely: there is no reflected query-param interpolation surface
10
- * to get wrong.
11
- *
12
- * The visual language (dark zinc card, mono type, emerald accent) mirrors the Agora consoles so
13
- * they feel like one family. The submit flow POSTs JSON via `fetch` and follows the JSON
14
- * `redirectTo` it gets back (rather than a classic form POST + server redirect), which is why the
15
- * markup keeps its own `<script>`.
16
- *
17
- * The password input has NO `required` attribute and the value is forwarded AS-IS (empty string
18
- * when blank): the host's `login` hook — not this page — decides whether a password matters, so an
19
- * email-only host (gate on username, ignore password) works with the same page as a host with real
20
- * passwords.
4
+ * JSON for embedding INSIDE an inline `<script>`: `JSON.stringify` alone leaves `</script>` (and
5
+ * `<!--`) untouched, and the HTML parser ends the script element on the first `</script>` it sees
6
+ * regardless of JavaScript string boundaries — so a `returnTo` of `/x</script><script>…` would run.
7
+ * Escaping the angle brackets (and `&`, plus the two line terminators JSON allows but JS doesn't)
8
+ * as `\uXXXX` keeps the value byte-identical once parsed and inert to the HTML parser.
21
9
  */
22
- export function renderLoginPage(basePath) {
10
+ function jsonForScript(value) {
11
+ return JSON.stringify(value)
12
+ .replaceAll('<', '\\u003c')
13
+ .replaceAll('>', '\\u003e')
14
+ .replaceAll('&', '\\u0026')
15
+ .replaceAll('\u2028', '\\u2028')
16
+ .replaceAll('\u2029', '\\u2029');
17
+ }
18
+ export function renderLoginPage(basePath, options = {}) {
23
19
  const loginAction = `${basePath}/login`;
24
20
  const defaultReturnTo = basePath === '' ? '/' : basePath;
21
+ const returnTo = sanitizeReturnTo(options.returnTo, defaultReturnTo);
22
+ const nonceAttr = options.nonce !== undefined ? ` nonce="${escapeHtml(options.nonce)}"` : '';
23
+ const errorStyle = options.error ? ' style="display:block"' : '';
25
24
  return `<!doctype html>
26
25
  <html lang="en">
27
26
  <head>
@@ -29,7 +28,7 @@ export function renderLoginPage(basePath) {
29
28
  <meta name="viewport" content="width=device-width, initial-scale=1" />
30
29
  <meta name="robots" content="noindex, nofollow" />
31
30
  <title>Sign in — Telescope</title>
32
- <style>
31
+ <style${nonceAttr}>
33
32
  :root { color-scheme: dark; }
34
33
  * { box-sizing: border-box; }
35
34
  body {
@@ -104,7 +103,8 @@ export function renderLoginPage(basePath) {
104
103
  <body>
105
104
  <div class="card">
106
105
  <p class="brand">Telescope</p>
107
- <form id="login-form" autocomplete="on">
106
+ <form id="login-form" method="post" action="${escapeHtml(loginAction)}" autocomplete="on">
107
+ <input type="hidden" name="returnTo" value="${escapeHtml(returnTo)}" />
108
108
  <label>
109
109
  <span class="field-label">Username</span>
110
110
  <input id="username" name="username" type="text" autocomplete="username" required autofocus />
@@ -113,41 +113,29 @@ export function renderLoginPage(basePath) {
113
113
  <span class="field-label">Password</span>
114
114
  <input id="password" name="password" type="password" autocomplete="current-password" />
115
115
  </label>
116
- <p id="error" role="alert">Invalid username or password.</p>
116
+ <p id="error" role="alert"${errorStyle}>Invalid username or password.</p>
117
117
  <button id="submit" type="submit">Sign in</button>
118
118
  </form>
119
119
  </div>
120
- <script>
120
+ <script${nonceAttr}>
121
121
  (function () {
122
- var LOGIN_ACTION = ${JSON.stringify(loginAction)};
123
- var DEFAULT_RETURN_TO = ${JSON.stringify(defaultReturnTo)};
124
- var params = new URLSearchParams(window.location.search);
122
+ var LOGIN_ACTION = ${jsonForScript(loginAction)};
123
+ var RETURN_TO = ${jsonForScript(returnTo)};
125
124
  var errorBox = document.getElementById('error');
126
- if (params.get('error')) errorBox.style.display = 'block';
127
-
128
- function sameOriginReturnTo(value) {
129
- if (typeof value !== 'string' || value === '') return DEFAULT_RETURN_TO;
130
- if (value.charAt(0) !== '/' || value.charAt(1) === '/' || value.indexOf('://') !== -1) {
131
- return DEFAULT_RETURN_TO;
132
- }
133
- return value;
134
- }
135
-
136
125
  var form = document.getElementById('login-form');
137
126
  var submitButton = document.getElementById('submit');
138
127
  form.addEventListener('submit', function (event) {
139
128
  event.preventDefault();
140
129
  submitButton.disabled = true;
141
130
  errorBox.style.display = 'none';
142
- var returnTo = sameOriginReturnTo(params.get('returnTo'));
143
131
  fetch(LOGIN_ACTION, {
144
132
  method: 'POST',
145
- headers: { 'content-type': 'application/json' },
133
+ headers: { 'content-type': 'application/json', accept: 'application/json' },
146
134
  credentials: 'same-origin',
147
135
  body: JSON.stringify({
148
136
  username: document.getElementById('username').value,
149
137
  password: document.getElementById('password').value,
150
- returnTo: returnTo,
138
+ returnTo: RETURN_TO,
151
139
  }),
152
140
  })
153
141
  .then(function (response) {
@@ -155,7 +143,7 @@ export function renderLoginPage(basePath) {
155
143
  return response.json();
156
144
  })
157
145
  .then(function (data) {
158
- window.location.href = data.redirectTo || DEFAULT_RETURN_TO;
146
+ window.location.href = data.redirectTo || RETURN_TO;
159
147
  })
160
148
  .catch(function () {
161
149
  errorBox.style.display = 'block';
@@ -1 +1 @@
1
- {"version":3,"file":"login_page.js","sourceRoot":"","sources":["../../../src/ui/login_page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,WAAW,GAAG,GAAG,QAAQ,QAAQ,CAAC;IACxC,MAAM,eAAe,GAAG,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;IACzD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAiGc,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;4BACtB,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA6CnD,CAAC;AACT,CAAC"}
1
+ {"version":3,"file":"login_page.js","sourceRoot":"","sources":["../../../src/ui/login_page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAqC7C;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;SACzB,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC;SAC1B,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC;SAC1B,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC;SAC1B,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC;SAC/B,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,OAAO,GAAqB,EAAE;IAC9E,MAAM,WAAW,GAAG,GAAG,QAAQ,QAAQ,CAAC;IACxC,MAAM,eAAe,GAAG,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;IACzD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7F,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE,CAAC;IACjE,OAAO;;;;;;;QAOD,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kDA2EiC,UAAU,CAAC,WAAW,CAAC;oDACrB,UAAU,CAAC,QAAQ,CAAC;;;;;;;;;kCAStC,UAAU;;;;SAInC,SAAS;;uBAEK,aAAa,CAAC,WAAW,CAAC;oBAC7B,aAAa,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiCnC,CAAC;AACT,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /** Keep in sync with this package's `version` in package.json. */
2
- export declare const VERSION = "0.10.0";
2
+ export declare const VERSION = "0.12.0";
3
3
  export type { CacheEntryContent, CacheEventLike, CacheOperation } from './cache_watcher.js';
4
4
  export { buildCacheEntry, CACHE_EVENTS, CacheWatcher } from './cache_watcher.js';
5
5
  export { nextCronRunMs } from './cron_next_run.js';
@@ -1,5 +1,5 @@
1
1
  /** Keep in sync with this package's `version` in package.json. */
2
- export const VERSION = '0.10.0';
2
+ export const VERSION = '0.12.0';
3
3
  // — cache watcher —
4
4
  export { buildCacheEntry, CACHE_EVENTS, CacheWatcher } from './cache_watcher.js';
5
5
  export { nextCronRunMs } from './cron_next_run.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonis-agora/telescope",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Laravel Telescope-style headless observability for AdonisJS — records HTTP requests and every Agora diagnostics event as queryable entries, plus opt-in subpaths for per-technology watchers, a web dashboard, AI exception diagnosis, and alerting.",
5
5
  "license": "MIT",
6
6
  "repository": {