@adonis-agora/telescope 0.4.0 → 0.5.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 (195) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/configure.d.ts.map +1 -1
  3. package/dist/configure.js +6 -0
  4. package/dist/configure.js.map +1 -1
  5. package/dist/providers/telescope_ai_provider.d.ts +9 -1
  6. package/dist/providers/telescope_ai_provider.d.ts.map +1 -1
  7. package/dist/providers/telescope_ai_provider.js +22 -2
  8. package/dist/providers/telescope_ai_provider.js.map +1 -1
  9. package/dist/providers/telescope_alerts_provider.d.ts +1 -0
  10. package/dist/providers/telescope_alerts_provider.d.ts.map +1 -1
  11. package/dist/providers/telescope_alerts_provider.js +37 -1
  12. package/dist/providers/telescope_alerts_provider.js.map +1 -1
  13. package/dist/providers/telescope_mcp_provider.d.ts +26 -0
  14. package/dist/providers/telescope_mcp_provider.d.ts.map +1 -0
  15. package/dist/providers/telescope_mcp_provider.js +114 -0
  16. package/dist/providers/telescope_mcp_provider.js.map +1 -0
  17. package/dist/providers/telescope_provider.d.ts +8 -0
  18. package/dist/providers/telescope_provider.d.ts.map +1 -1
  19. package/dist/providers/telescope_provider.js +56 -1
  20. package/dist/providers/telescope_provider.js.map +1 -1
  21. package/dist/providers/telescope_ui_provider.d.ts +2 -0
  22. package/dist/providers/telescope_ui_provider.d.ts.map +1 -1
  23. package/dist/providers/telescope_ui_provider.js +106 -22
  24. package/dist/providers/telescope_ui_provider.js.map +1 -1
  25. package/dist/providers/telescope_watchers_provider.d.ts +13 -2
  26. package/dist/providers/telescope_watchers_provider.d.ts.map +1 -1
  27. package/dist/providers/telescope_watchers_provider.js +57 -7
  28. package/dist/providers/telescope_watchers_provider.js.map +1 -1
  29. package/dist/src/ai/define_config.d.ts +39 -7
  30. package/dist/src/ai/define_config.d.ts.map +1 -1
  31. package/dist/src/ai/define_config.js +14 -4
  32. package/dist/src/ai/define_config.js.map +1 -1
  33. package/dist/src/ai/diagnosis_coordinator.d.ts +106 -0
  34. package/dist/src/ai/diagnosis_coordinator.d.ts.map +1 -0
  35. package/dist/src/ai/diagnosis_coordinator.js +143 -0
  36. package/dist/src/ai/diagnosis_coordinator.js.map +1 -0
  37. package/dist/src/ai/factory.d.ts +8 -5
  38. package/dist/src/ai/factory.d.ts.map +1 -1
  39. package/dist/src/ai/factory.js +20 -8
  40. package/dist/src/ai/factory.js.map +1 -1
  41. package/dist/src/ai/index.d.ts +3 -1
  42. package/dist/src/ai/index.d.ts.map +1 -1
  43. package/dist/src/ai/index.js +3 -1
  44. package/dist/src/ai/index.js.map +1 -1
  45. package/dist/src/alerts/alert_rule.d.ts +79 -11
  46. package/dist/src/alerts/alert_rule.d.ts.map +1 -1
  47. package/dist/src/alerts/alerter.d.ts +11 -1
  48. package/dist/src/alerts/alerter.d.ts.map +1 -1
  49. package/dist/src/alerts/alerter.js +21 -1
  50. package/dist/src/alerts/alerter.js.map +1 -1
  51. package/dist/src/alerts/alerter_service.d.ts +103 -0
  52. package/dist/src/alerts/alerter_service.d.ts.map +1 -0
  53. package/dist/src/alerts/alerter_service.js +215 -0
  54. package/dist/src/alerts/alerter_service.js.map +1 -0
  55. package/dist/src/alerts/index.d.ts +4 -2
  56. package/dist/src/alerts/index.d.ts.map +1 -1
  57. package/dist/src/alerts/index.js +4 -2
  58. package/dist/src/alerts/index.js.map +1 -1
  59. package/dist/src/alerts/slack_format.d.ts.map +1 -1
  60. package/dist/src/alerts/slack_format.js +35 -2
  61. package/dist/src/alerts/slack_format.js.map +1 -1
  62. package/dist/src/client_errors/config.d.ts +66 -0
  63. package/dist/src/client_errors/config.d.ts.map +1 -0
  64. package/dist/src/client_errors/config.js +17 -0
  65. package/dist/src/client_errors/config.js.map +1 -0
  66. package/dist/src/client_errors/index.d.ts +5 -0
  67. package/dist/src/client_errors/index.d.ts.map +1 -0
  68. package/dist/src/client_errors/index.js +5 -0
  69. package/dist/src/client_errors/index.js.map +1 -0
  70. package/dist/src/client_errors/ingestor.d.ts +87 -0
  71. package/dist/src/client_errors/ingestor.d.ts.map +1 -0
  72. package/dist/src/client_errors/ingestor.js +135 -0
  73. package/dist/src/client_errors/ingestor.js.map +1 -0
  74. package/dist/src/client_errors/rate_limiter.d.ts +53 -0
  75. package/dist/src/client_errors/rate_limiter.d.ts.map +1 -0
  76. package/dist/src/client_errors/rate_limiter.js +83 -0
  77. package/dist/src/client_errors/rate_limiter.js.map +1 -0
  78. package/dist/src/client_errors/validation.d.ts +62 -0
  79. package/dist/src/client_errors/validation.d.ts.map +1 -0
  80. package/dist/src/client_errors/validation.js +109 -0
  81. package/dist/src/client_errors/validation.js.map +1 -0
  82. package/dist/src/define_config.d.ts +120 -0
  83. package/dist/src/define_config.d.ts.map +1 -1
  84. package/dist/src/define_config.js +41 -0
  85. package/dist/src/define_config.js.map +1 -1
  86. package/dist/src/entry.d.ts +20 -0
  87. package/dist/src/entry.d.ts.map +1 -1
  88. package/dist/src/entry.js +20 -0
  89. package/dist/src/entry.js.map +1 -1
  90. package/dist/src/index.d.ts +16 -3
  91. package/dist/src/index.d.ts.map +1 -1
  92. package/dist/src/index.js +15 -2
  93. package/dist/src/index.js.map +1 -1
  94. package/dist/src/mcp/define_config.d.ts +57 -0
  95. package/dist/src/mcp/define_config.d.ts.map +1 -0
  96. package/dist/src/mcp/define_config.js +35 -0
  97. package/dist/src/mcp/define_config.js.map +1 -0
  98. package/dist/src/mcp/index.d.ts +7 -0
  99. package/dist/src/mcp/index.d.ts.map +1 -0
  100. package/dist/src/mcp/index.js +7 -0
  101. package/dist/src/mcp/index.js.map +1 -0
  102. package/dist/src/mcp/server.d.ts +89 -0
  103. package/dist/src/mcp/server.d.ts.map +1 -0
  104. package/dist/src/mcp/server.js +279 -0
  105. package/dist/src/mcp/server.js.map +1 -0
  106. package/dist/src/metrics/pulse.d.ts +158 -0
  107. package/dist/src/metrics/pulse.d.ts.map +1 -0
  108. package/dist/src/metrics/pulse.js +414 -0
  109. package/dist/src/metrics/pulse.js.map +1 -0
  110. package/dist/src/overload_guard.d.ts +77 -0
  111. package/dist/src/overload_guard.d.ts.map +1 -0
  112. package/dist/src/overload_guard.js +134 -0
  113. package/dist/src/overload_guard.js.map +1 -0
  114. package/dist/src/pruner.d.ts +93 -0
  115. package/dist/src/pruner.d.ts.map +1 -0
  116. package/dist/src/pruner.js +120 -0
  117. package/dist/src/pruner.js.map +1 -0
  118. package/dist/src/registry.d.ts +26 -0
  119. package/dist/src/registry.d.ts.map +1 -1
  120. package/dist/src/registry.js +19 -0
  121. package/dist/src/registry.js.map +1 -1
  122. package/dist/src/service.d.ts +16 -1
  123. package/dist/src/service.d.ts.map +1 -1
  124. package/dist/src/service.js +19 -1
  125. package/dist/src/service.js.map +1 -1
  126. package/dist/src/telescope_middleware.d.ts.map +1 -1
  127. package/dist/src/telescope_middleware.js +3 -1
  128. package/dist/src/telescope_middleware.js.map +1 -1
  129. package/dist/src/ui/api.d.ts +9 -1
  130. package/dist/src/ui/api.d.ts.map +1 -1
  131. package/dist/src/ui/api.js +23 -1
  132. package/dist/src/ui/api.js.map +1 -1
  133. package/dist/src/ui/auth.d.ts +112 -0
  134. package/dist/src/ui/auth.d.ts.map +1 -0
  135. package/dist/src/ui/auth.js +130 -0
  136. package/dist/src/ui/auth.js.map +1 -0
  137. package/dist/src/ui/dashboard_auth.d.ts +34 -0
  138. package/dist/src/ui/dashboard_auth.d.ts.map +1 -0
  139. package/dist/src/ui/dashboard_auth.js +57 -0
  140. package/dist/src/ui/dashboard_auth.js.map +1 -0
  141. package/dist/src/ui/define_config.d.ts +15 -0
  142. package/dist/src/ui/define_config.d.ts.map +1 -1
  143. package/dist/src/ui/define_config.js +4 -0
  144. package/dist/src/ui/define_config.js.map +1 -1
  145. package/dist/src/ui/index.d.ts +7 -1
  146. package/dist/src/ui/index.d.ts.map +1 -1
  147. package/dist/src/ui/index.js +6 -1
  148. package/dist/src/ui/index.js.map +1 -1
  149. package/dist/src/ui/login_page.d.ts +23 -0
  150. package/dist/src/ui/login_page.d.ts.map +1 -0
  151. package/dist/src/ui/login_page.js +170 -0
  152. package/dist/src/ui/login_page.js.map +1 -0
  153. package/dist/src/ui/session_cookie.d.ts +42 -0
  154. package/dist/src/ui/session_cookie.d.ts.map +1 -0
  155. package/dist/src/ui/session_cookie.js +97 -0
  156. package/dist/src/ui/session_cookie.js.map +1 -0
  157. package/dist/src/watchers/define_config.d.ts +115 -12
  158. package/dist/src/watchers/define_config.d.ts.map +1 -1
  159. package/dist/src/watchers/define_config.js +48 -11
  160. package/dist/src/watchers/define_config.js.map +1 -1
  161. package/dist/src/watchers/http_client_watcher.d.ts +91 -31
  162. package/dist/src/watchers/http_client_watcher.d.ts.map +1 -1
  163. package/dist/src/watchers/http_client_watcher.js +161 -53
  164. package/dist/src/watchers/http_client_watcher.js.map +1 -1
  165. package/dist/src/watchers/index.d.ts +9 -5
  166. package/dist/src/watchers/index.d.ts.map +1 -1
  167. package/dist/src/watchers/index.js +6 -2
  168. package/dist/src/watchers/index.js.map +1 -1
  169. package/dist/src/watchers/lucid_query_watcher.d.ts +20 -2
  170. package/dist/src/watchers/lucid_query_watcher.d.ts.map +1 -1
  171. package/dist/src/watchers/lucid_query_watcher.js +44 -10
  172. package/dist/src/watchers/lucid_query_watcher.js.map +1 -1
  173. package/dist/src/watchers/profiling_watcher.d.ts +144 -0
  174. package/dist/src/watchers/profiling_watcher.d.ts.map +1 -0
  175. package/dist/src/watchers/profiling_watcher.js +231 -0
  176. package/dist/src/watchers/profiling_watcher.js.map +1 -0
  177. package/dist/src/watchers/query_family_hash.d.ts +7 -2
  178. package/dist/src/watchers/query_family_hash.d.ts.map +1 -1
  179. package/dist/src/watchers/query_family_hash.js +8 -3
  180. package/dist/src/watchers/query_family_hash.js.map +1 -1
  181. package/dist/src/watchers/record.d.ts.map +1 -1
  182. package/dist/src/watchers/record.js +4 -0
  183. package/dist/src/watchers/record.js.map +1 -1
  184. package/dist/src/watchers/schedule_watcher.d.ts +143 -0
  185. package/dist/src/watchers/schedule_watcher.d.ts.map +1 -0
  186. package/dist/src/watchers/schedule_watcher.js +195 -0
  187. package/dist/src/watchers/schedule_watcher.js.map +1 -0
  188. package/dist/stubs/config/telescope.stub +0 -65
  189. package/dist/stubs/config/telescope_ai.stub +0 -36
  190. package/dist/stubs/config/telescope_alerts.stub +0 -47
  191. package/dist/stubs/config/telescope_mcp.stub +0 -0
  192. package/dist/stubs/config/telescope_ui.stub +0 -53
  193. package/dist/stubs/config/telescope_watchers.stub +0 -41
  194. package/dist/stubs/database/migrations/create_telescope_entries_table.stub +2 -2
  195. package/package.json +12 -2
@@ -0,0 +1,112 @@
1
+ import { type DashboardSession, type DashboardSessionUser } from './session_cookie.js';
2
+ /**
3
+ * Framework-light `dashboardAuth` mechanism for the telescope dashboard — config resolution, the
4
+ * open-redirect guard, the login decision, session verification, and the composed session-guard
5
+ * decision. NO AdonisJS types leak in (mirroring `guard.ts`/`http.ts`), so every branch is
6
+ * unit-testable against a plain object with no HTTP server. The Adonis glue in `dashboard_auth.ts`
7
+ * adapts an `HttpContext` to these functions and owns the cookie read/write via AdonisJS's native
8
+ * cookie API.
9
+ *
10
+ * When configured it gates BOTH the dashboard SPA shell (a full-page navigation redirected `302`
11
+ * to a server-rendered login page) and the JSON API (a plain `401`) behind a signed session cookie.
12
+ * When left unconfigured the dashboard behaves exactly as before (no login/logout routes, no
13
+ * session guard).
14
+ */
15
+ /** Host hook validating submitted credentials from the built-in login page. */
16
+ export type LoginHook = (username: string, password: string) => Promise<DashboardSessionUser | null> | DashboardSessionUser | null;
17
+ /**
18
+ * Author-facing `dashboardAuth` option on `config/telescope_ui.ts`. Gates the dashboard behind a
19
+ * signed session cookie via the built-in server-rendered login page (`GET <path>/login`) — the
20
+ * bundled dashboard SPA stays untouched.
21
+ *
22
+ * `login` is just a function that may be `async` and close over `app.container`/services (import
23
+ * them at the top of `config/telescope_ui.ts`), so there is nothing extra to build for the async
24
+ * case.
25
+ */
26
+ export interface DashboardAuthOptions {
27
+ /** REQUIRED HMAC-SHA256 signing key. Missing/empty => boot error (fail closed). */
28
+ secret: string;
29
+ /** Cookie TTL as a duration string (`'8h'`, `'30m'`, `'7d'`). Default `'8h'`. */
30
+ ttl?: string;
31
+ /** Validates submitted username/password; return the session user, or `null` to deny. Thrown
32
+ * errors are treated as a denial (logged once, never surfaced to the client). May be async. */
33
+ login: LoginHook;
34
+ }
35
+ /** Resolved, validated `dashboardAuth` config shared by the guard, the login handler, and the page. */
36
+ export interface ResolvedDashboardAuth {
37
+ secret: string;
38
+ ttlMs: number;
39
+ login: LoginHook;
40
+ }
41
+ /** Cookie name carrying the signed dashboard session. */
42
+ export declare const SESSION_COOKIE_NAME = "telescope_dashboard_session";
43
+ /**
44
+ * Validate + resolve the `dashboardAuth` option. Returns `null` when unconfigured (today's
45
+ * unauthenticated behavior, unchanged). Throws at boot (fail closed) when configured but missing a
46
+ * secret or a `login` hook — the host learns immediately rather than shipping an un-mintable gate.
47
+ */
48
+ export declare function resolveDashboardAuth(options: DashboardAuthOptions | undefined): ResolvedDashboardAuth | null;
49
+ /**
50
+ * Guard a client-supplied redirect target against an open redirect: it must be a same-origin,
51
+ * root-relative path (`/telescope/foo`), never a protocol-relative (`//evil.com`) or absolute
52
+ * (`https://evil.com`) URL. Falls back to `fallback` (the dashboard's own `basePath`) for anything
53
+ * else, including a missing/non-string value.
54
+ */
55
+ export declare function sanitizeReturnTo(candidate: unknown, fallback: string): string;
56
+ /**
57
+ * The framework-light outcome of a login attempt, mapped to HTTP by the provider:
58
+ * - `ok` → set `cookieValue` as the session cookie, reply `200 { redirectTo }`.
59
+ * - `bad-request` → `400` (body missing string `username`/`password`).
60
+ * - `unauthorized` → uniform `401 { error }` for EVERY credential failure (unknown user, wrong
61
+ * password, or a throwing hook) — no user-enumeration. `hookError` is set only when the hook
62
+ * threw, so the provider can warn-log it once without changing the client-visible response.
63
+ */
64
+ export type LoginOutcome = {
65
+ kind: 'ok';
66
+ cookieValue: string;
67
+ redirectTo: string;
68
+ } | {
69
+ kind: 'bad-request';
70
+ message: string;
71
+ } | {
72
+ kind: 'unauthorized';
73
+ message: string;
74
+ hookError?: unknown;
75
+ };
76
+ /**
77
+ * Decide a login attempt end-to-end (validate body → run hook → mint cookie) with no HTTP types.
78
+ * On success returns the signed cookie value to set plus the sanitized `redirectTo`; the password
79
+ * is forwarded to the hook AS-IS (empty string when blank), so the hook alone decides whether a
80
+ * password is required.
81
+ */
82
+ export declare function performLogin(auth: ResolvedDashboardAuth, body: unknown, basePath: string, now?: number): Promise<LoginOutcome>;
83
+ /**
84
+ * Read + verify a session cookie value, returning the session or `null` when absent, tampered, or
85
+ * expired. Thin wrapper over {@link verifySessionCookie} so the provider never touches crypto.
86
+ */
87
+ export declare function readSession(auth: ResolvedDashboardAuth, cookieValue: string | undefined, now?: number): DashboardSession | null;
88
+ /**
89
+ * The framework-light outcome of the composed session guard, mapped to HTTP by the Adonis glue:
90
+ * - `allow` → let the request through (valid session, or `dashboardAuth` unconfigured).
91
+ * - `redirect` → a `page` navigation with no valid session: `302` to the login page carrying
92
+ * `returnTo` (the original, server-supplied request URL).
93
+ * - `unauthorized` → an `api` request with no valid session: a plain `401`.
94
+ */
95
+ export type DashboardAuthDecision = {
96
+ kind: 'allow';
97
+ } | {
98
+ kind: 'redirect';
99
+ returnTo: string;
100
+ } | {
101
+ kind: 'unauthorized';
102
+ };
103
+ /**
104
+ * Decide the session guard for a dashboard resource with NO HTTP types. Runs AFTER the existing
105
+ * `authorize` hook (they compose — both must pass). When `auth` is `null` (`dashboardAuth`
106
+ * unconfigured) it always allows, so behavior is byte-for-byte unchanged. Otherwise a valid session
107
+ * allows; a `page` request without one redirects (carrying `requestUrl` as `returnTo`) and an `api`
108
+ * request without one is unauthorized.
109
+ */
110
+ export declare function decideDashboardAuth(auth: ResolvedDashboardAuth | null, cookieValue: string | undefined, mode: 'page' | 'api', requestUrl: string, now?: number): DashboardAuthDecision;
111
+ export type { DashboardSession, DashboardSessionUser } from './session_cookie.js';
112
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/ui/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EAG1B,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;GAYG;AAEH,+EAA+E;AAC/E,MAAM,MAAM,SAAS,GAAG,CACtB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,GAAG,oBAAoB,GAAG,IAAI,CAAC;AAExE;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB;IACnC,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;oGACgG;IAChG,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,uGAAuG;AACvG,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,gCAAgC,CAAC;AAoBjE;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,oBAAoB,GAAG,SAAS,GACxC,qBAAqB,GAAG,IAAI,CAgB9B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM7E;AAaD;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAqBnE;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,qBAAqB,EAC3B,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC,CAiBvB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,qBAAqB,EAC3B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,GAAG,CAAC,EAAE,MAAM,GACX,gBAAgB,GAAG,IAAI,CAMzB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC;AAE7B;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,qBAAqB,GAAG,IAAI,EAClC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,IAAI,EAAE,MAAM,GAAG,KAAK,EACpB,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,MAAM,GACX,qBAAqB,CAMvB;AAED,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,130 @@
1
+ import { signSessionCookie, verifySessionCookie, } from './session_cookie.js';
2
+ /** Cookie name carrying the signed dashboard session. */
3
+ export const SESSION_COOKIE_NAME = 'telescope_dashboard_session';
4
+ const DEFAULT_TTL = '8h';
5
+ const DEFAULT_TTL_MS = 8 * 60 * 60 * 1000;
6
+ const DURATION_UNITS = {
7
+ s: 1000,
8
+ m: 60 * 1000,
9
+ h: 60 * 60 * 1000,
10
+ d: 24 * 60 * 60 * 1000,
11
+ };
12
+ /** Parse a `'<number><s|m|h|d>'` duration to ms; falls back to the 8h default on a bad value. */
13
+ function durationToMs(ttl) {
14
+ const match = /^(\d+)([smhd])$/.exec(ttl.trim());
15
+ if (!match)
16
+ return DEFAULT_TTL_MS;
17
+ const unit = DURATION_UNITS[match[2] ?? ''];
18
+ if (unit === undefined)
19
+ return DEFAULT_TTL_MS;
20
+ return Number(match[1]) * unit;
21
+ }
22
+ /**
23
+ * Validate + resolve the `dashboardAuth` option. Returns `null` when unconfigured (today's
24
+ * unauthenticated behavior, unchanged). Throws at boot (fail closed) when configured but missing a
25
+ * secret or a `login` hook — the host learns immediately rather than shipping an un-mintable gate.
26
+ */
27
+ export function resolveDashboardAuth(options) {
28
+ if (options === undefined)
29
+ return null;
30
+ if (typeof options.secret !== 'string' || options.secret === '') {
31
+ throw new Error('telescope_ui: dashboardAuth.secret is required and must be a non-empty string ' +
32
+ '(HMAC-SHA256 signing key, 32+ bytes recommended). Failing closed.');
33
+ }
34
+ if (typeof options.login !== 'function') {
35
+ throw new Error('telescope_ui: dashboardAuth.login is required (a login hook).');
36
+ }
37
+ return {
38
+ secret: options.secret,
39
+ ttlMs: durationToMs(options.ttl ?? DEFAULT_TTL),
40
+ login: options.login,
41
+ };
42
+ }
43
+ /**
44
+ * Guard a client-supplied redirect target against an open redirect: it must be a same-origin,
45
+ * root-relative path (`/telescope/foo`), never a protocol-relative (`//evil.com`) or absolute
46
+ * (`https://evil.com`) URL. Falls back to `fallback` (the dashboard's own `basePath`) for anything
47
+ * else, including a missing/non-string value.
48
+ */
49
+ export function sanitizeReturnTo(candidate, fallback) {
50
+ if (typeof candidate !== 'string')
51
+ return fallback;
52
+ if (!candidate.startsWith('/'))
53
+ return fallback;
54
+ if (candidate.startsWith('//'))
55
+ return fallback;
56
+ if (candidate.includes('://'))
57
+ return fallback;
58
+ return candidate;
59
+ }
60
+ function isString(value) {
61
+ return typeof value === 'string';
62
+ }
63
+ const UNAUTHORIZED_MESSAGE = 'Invalid username or password.';
64
+ /**
65
+ * Run the host's `login` hook defensively: a throw is treated as a denial (uniform failure) so a
66
+ * buggy hook never 500s the endpoint into a stack-trace leak. The thrown error is surfaced on the
67
+ * outcome (`hookError`) so the provider can warn-log it once, but it never reaches the client.
68
+ */
69
+ async function runLoginHook(auth, username, password) {
70
+ try {
71
+ return { user: (await auth.login(username, password)) ?? null };
72
+ }
73
+ catch (error) {
74
+ return { user: null, error };
75
+ }
76
+ }
77
+ /**
78
+ * Decide a login attempt end-to-end (validate body → run hook → mint cookie) with no HTTP types.
79
+ * On success returns the signed cookie value to set plus the sanitized `redirectTo`; the password
80
+ * is forwarded to the hook AS-IS (empty string when blank), so the hook alone decides whether a
81
+ * password is required.
82
+ */
83
+ export async function performLogin(auth, body, basePath, now) {
84
+ const parsed = (body ?? {});
85
+ if (!isString(parsed.username) || !isString(parsed.password)) {
86
+ return { kind: 'bad-request', message: 'Body must include string `username` and `password`.' };
87
+ }
88
+ const { user, error } = await runLoginHook(auth, parsed.username, parsed.password);
89
+ if (!user) {
90
+ return error !== undefined
91
+ ? { kind: 'unauthorized', message: UNAUTHORIZED_MESSAGE, hookError: error }
92
+ : { kind: 'unauthorized', message: UNAUTHORIZED_MESSAGE };
93
+ }
94
+ const cookieValue = signSessionCookie(user, {
95
+ secret: auth.secret,
96
+ ttlMs: auth.ttlMs,
97
+ ...(now !== undefined ? { now } : {}),
98
+ });
99
+ return { kind: 'ok', cookieValue, redirectTo: sanitizeReturnTo(parsed.returnTo, basePath) };
100
+ }
101
+ /**
102
+ * Read + verify a session cookie value, returning the session or `null` when absent, tampered, or
103
+ * expired. Thin wrapper over {@link verifySessionCookie} so the provider never touches crypto.
104
+ */
105
+ export function readSession(auth, cookieValue, now) {
106
+ if (cookieValue === undefined || cookieValue === '')
107
+ return null;
108
+ return verifySessionCookie(cookieValue, {
109
+ secret: auth.secret,
110
+ ...(now !== undefined ? { now } : {}),
111
+ });
112
+ }
113
+ /**
114
+ * Decide the session guard for a dashboard resource with NO HTTP types. Runs AFTER the existing
115
+ * `authorize` hook (they compose — both must pass). When `auth` is `null` (`dashboardAuth`
116
+ * unconfigured) it always allows, so behavior is byte-for-byte unchanged. Otherwise a valid session
117
+ * allows; a `page` request without one redirects (carrying `requestUrl` as `returnTo`) and an `api`
118
+ * request without one is unauthorized.
119
+ */
120
+ export function decideDashboardAuth(auth, cookieValue, mode, requestUrl, now) {
121
+ if (auth === null)
122
+ return { kind: 'allow' };
123
+ const session = readSession(auth, cookieValue, now);
124
+ if (session !== null)
125
+ return { kind: 'allow' };
126
+ if (mode === 'page')
127
+ return { kind: 'redirect', returnTo: requestUrl };
128
+ return { kind: 'unauthorized' };
129
+ }
130
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/ui/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { HttpContext } from '@adonisjs/core/http';
2
+ import { type ResolvedDashboardAuth } from './auth.js';
3
+ /**
4
+ * The AdonisJS glue for the built-in `dashboardAuth` session — the ONLY place that touches an
5
+ * `HttpContext`, so the decision logic in `auth.ts`/`session_cookie.ts` stays framework-light and
6
+ * unit-testable. Reads and writes the signed session via AdonisJS's NATIVE `plainCookie` API (the
7
+ * value carries its own HMAC signature, so `encode: false` skips Adonis's redundant cookie signing).
8
+ *
9
+ * The provider composes {@link enforceDashboardAuth} AFTER its existing `authorize` guard: both must
10
+ * pass. When `dashboardAuth` is unconfigured the provider simply never calls this, so behavior is
11
+ * byte-for-byte unchanged.
12
+ */
13
+ /** Read the raw (unencoded) session cookie value, or `undefined` when absent/blank. */
14
+ export declare function readSessionCookie(ctx: HttpContext): string | undefined;
15
+ /**
16
+ * Write the signed session as an unsigned (`plainCookie`, `encode: false`) cookie — the value
17
+ * carries its own HMAC signature, so AdonisJS's cookie signing would be redundant double-wrapping.
18
+ * `HttpOnly` + `SameSite=Lax` (blocks cross-site POSTs carrying the cookie — CSRF coverage) +
19
+ * `Secure` on https. `Path=/` so it reaches both the SPA and the API mounts regardless of prefix.
20
+ */
21
+ export declare function writeSessionCookie(ctx: HttpContext, auth: ResolvedDashboardAuth, value: string): void;
22
+ /** Clear the session cookie (logout). `Path=/` mirrors {@link writeSessionCookie}. */
23
+ export declare function clearSessionCookie(ctx: HttpContext): void;
24
+ /**
25
+ * The session half of the composed dashboard guard, run AFTER the `authorize` hook has passed.
26
+ * Reads the session cookie, and on no valid session either redirects a `page` navigation `302` to
27
+ * the login page (carrying a `returnTo`) or answers an `api` request with a plain `401`. Returns
28
+ * `true` to allow the request through (valid session), `false` when it has already written the
29
+ * response and the route must short-circuit.
30
+ *
31
+ * Callers only invoke this when `dashboardAuth` is configured, so `auth` is always non-null here.
32
+ */
33
+ export declare function enforceDashboardAuth(ctx: HttpContext, auth: ResolvedDashboardAuth, mode: 'page' | 'api', basePath: string): Promise<boolean>;
34
+ //# sourceMappingURL=dashboard_auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard_auth.d.ts","sourceRoot":"","sources":["../../../src/ui/dashboard_auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,KAAK,qBAAqB,EAA4C,MAAM,WAAW,CAAC;AAEjG;;;;;;;;;GASG;AAEH,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAGtE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,qBAAqB,EAC3B,KAAK,EAAE,MAAM,GACZ,IAAI,CASN;AAED,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAEzD;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,qBAAqB,EAC3B,IAAI,EAAE,MAAM,GAAG,KAAK,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CASlB"}
@@ -0,0 +1,57 @@
1
+ import { SESSION_COOKIE_NAME, decideDashboardAuth } from './auth.js';
2
+ /**
3
+ * The AdonisJS glue for the built-in `dashboardAuth` session — the ONLY place that touches an
4
+ * `HttpContext`, so the decision logic in `auth.ts`/`session_cookie.ts` stays framework-light and
5
+ * unit-testable. Reads and writes the signed session via AdonisJS's NATIVE `plainCookie` API (the
6
+ * value carries its own HMAC signature, so `encode: false` skips Adonis's redundant cookie signing).
7
+ *
8
+ * The provider composes {@link enforceDashboardAuth} AFTER its existing `authorize` guard: both must
9
+ * pass. When `dashboardAuth` is unconfigured the provider simply never calls this, so behavior is
10
+ * byte-for-byte unchanged.
11
+ */
12
+ /** Read the raw (unencoded) session cookie value, or `undefined` when absent/blank. */
13
+ export function readSessionCookie(ctx) {
14
+ const value = ctx.request.plainCookie(SESSION_COOKIE_NAME, undefined, false);
15
+ return typeof value === 'string' && value !== '' ? value : undefined;
16
+ }
17
+ /**
18
+ * Write the signed session as an unsigned (`plainCookie`, `encode: false`) cookie — the value
19
+ * carries its own HMAC signature, so AdonisJS's cookie signing would be redundant double-wrapping.
20
+ * `HttpOnly` + `SameSite=Lax` (blocks cross-site POSTs carrying the cookie — CSRF coverage) +
21
+ * `Secure` on https. `Path=/` so it reaches both the SPA and the API mounts regardless of prefix.
22
+ */
23
+ export function writeSessionCookie(ctx, auth, value) {
24
+ ctx.response.plainCookie(SESSION_COOKIE_NAME, value, {
25
+ httpOnly: true,
26
+ sameSite: 'lax',
27
+ secure: ctx.request.secure(),
28
+ path: '/',
29
+ maxAge: Math.floor(auth.ttlMs / 1000),
30
+ encode: false,
31
+ });
32
+ }
33
+ /** Clear the session cookie (logout). `Path=/` mirrors {@link writeSessionCookie}. */
34
+ export function clearSessionCookie(ctx) {
35
+ ctx.response.clearCookie(SESSION_COOKIE_NAME, { path: '/' });
36
+ }
37
+ /**
38
+ * The session half of the composed dashboard guard, run AFTER the `authorize` hook has passed.
39
+ * Reads the session cookie, and on no valid session either redirects a `page` navigation `302` to
40
+ * the login page (carrying a `returnTo`) or answers an `api` request with a plain `401`. Returns
41
+ * `true` to allow the request through (valid session), `false` when it has already written the
42
+ * response and the route must short-circuit.
43
+ *
44
+ * Callers only invoke this when `dashboardAuth` is configured, so `auth` is always non-null here.
45
+ */
46
+ export async function enforceDashboardAuth(ctx, auth, mode, basePath) {
47
+ const decision = decideDashboardAuth(auth, readSessionCookie(ctx), mode, ctx.request.url(true));
48
+ if (decision.kind === 'allow')
49
+ return true;
50
+ if (decision.kind === 'redirect') {
51
+ ctx.response.redirect().withQs('returnTo', decision.returnTo).toPath(`${basePath}/login`);
52
+ return false;
53
+ }
54
+ ctx.response.status(401).json({ error: 'unauthorized' });
55
+ return false;
56
+ }
57
+ //# sourceMappingURL=dashboard_auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard_auth.js","sourceRoot":"","sources":["../../../src/ui/dashboard_auth.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEjG;;;;;;;;;GASG;AAEH,uFAAuF;AACvF,MAAM,UAAU,iBAAiB,CAAC,GAAgB;IAChD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,mBAAmB,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAC7E,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAgB,EAChB,IAA2B,EAC3B,KAAa;IAEb,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,mBAAmB,EAAE,KAAK,EAAE;QACnD,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE;QAC5B,IAAI,EAAE,GAAG;QACT,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACrC,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;AACL,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,kBAAkB,CAAC,GAAgB;IACjD,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,mBAAmB,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,GAAgB,EAChB,IAA2B,EAC3B,IAAoB,EACpB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAChG,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,QAAQ,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACjC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,QAAQ,QAAQ,CAAC,CAAC;QAC1F,OAAO,KAAK,CAAC;IACf,CAAC;IACD,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;IACzD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { type DashboardAuthOptions, type ResolvedDashboardAuth } from './auth.js';
1
2
  import type { UiHttpContext } from './http.js';
2
3
  /**
3
4
  * The decision hook gating the dashboard + JSON API. Return `true` to allow the
@@ -52,6 +53,18 @@ export interface TelescopeUiConfig {
52
53
  * is supplied). Lets you gate a production dashboard without writing a hook.
53
54
  */
54
55
  credentials?: UiCredentials;
56
+ /**
57
+ * Optional built-in login screen. When set, the provider mounts a server-rendered
58
+ * `GET <path>/login` page plus `POST <path>/login` / `GET <path>/logout`, and stamps a session
59
+ * guard on the dashboard: an unauthenticated page navigation is redirected (`302`) to the login
60
+ * page and an unauthenticated API request gets `401`. The signed session cookie is minted only by
61
+ * the host's {@link DashboardAuthOptions.login} hook.
62
+ *
63
+ * This is ADDITIVE and composes WITH {@link authorize} (both must pass) — it does not replace it.
64
+ * Omit it entirely to keep today's behavior byte-for-byte (no login/logout routes, no session
65
+ * guard). Missing `secret`/`login` fails closed at boot.
66
+ */
67
+ dashboardAuth?: DashboardAuthOptions;
55
68
  /**
56
69
  * Request REPLAY: re-issue a captured `request` entry against the LOCAL server
57
70
  * from the dashboard. DISABLED BY DEFAULT — replaying re-runs a real request
@@ -81,6 +94,8 @@ export interface ResolvedTelescopeUiConfig {
81
94
  path: string;
82
95
  authorize: AuthorizeHook;
83
96
  credentials: UiCredentials;
97
+ /** Resolved built-in login config, or `null` when `dashboardAuth` is unconfigured. */
98
+ dashboardAuth: ResolvedDashboardAuth | null;
84
99
  /** Resolved request-replay settings (disabled by default). */
85
100
  replay: {
86
101
  enabled: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../../src/ui/define_config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE/E;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,KAAK,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAChD;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,aAAa,CAAC;IAG5B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mFAAmF;AACnF,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,aAAa,CAAC;IACzB,WAAW,EAAE,aAAa,CAAC;IAC3B,8DAA8D;IAC9D,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CAEzE;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD;AAiDD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,aAAa,GAAG,aAAa,CAS1E;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,MAAM,GAAE,iBAAsB,GAAG,yBAAyB,CAcvF"}
1
+ {"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../../src/ui/define_config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAE3B,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE/E;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,KAAK,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAChD;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,aAAa,CAAC;IAE5B;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAC;IAGrC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mFAAmF;AACnF,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,aAAa,CAAC;IACzB,WAAW,EAAE,aAAa,CAAC;IAC3B,sFAAsF;IACtF,aAAa,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAC5C,8DAA8D;IAC9D,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CAEzE;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD;AAiDD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,aAAa,GAAG,aAAa,CAS1E;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,MAAM,GAAE,iBAAsB,GAAG,yBAAyB,CAiBvF"}
@@ -1,3 +1,4 @@
1
+ import { resolveDashboardAuth, } from './auth.js';
1
2
  /**
2
3
  * Identity helper giving `config/telescope_ui.ts` full type-checking. Mirrors the
3
4
  * AdonisJS `defineConfig` convention.
@@ -99,6 +100,9 @@ export function resolveConfig(config = {}) {
99
100
  path: normalizePath(config.path ?? '/telescope'),
100
101
  authorize: config.authorize ?? defaultAuthorize(credentials),
101
102
  credentials,
103
+ // Validate + resolve now so a misconfigured secret/login fails closed at boot, not on the first
104
+ // login attempt. `null` when `dashboardAuth` is omitted (behavior unchanged).
105
+ dashboardAuth: resolveDashboardAuth(config.dashboardAuth),
102
106
  replay: {
103
107
  // Safe default: replay is OFF unless the host explicitly enables it.
104
108
  enabled: config.replay?.enabled ?? false,
@@ -1 +1 @@
1
- {"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../../src/ui/define_config.ts"],"names":[],"mappings":"AA2FA;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,MAAyB;IACpD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACpB,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,YAAY,CAAC;IAC/C,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;IACpC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,CAAC;AACX,CAAC;AAED,4DAA4D;AAC5D,SAAS,YAAY;IACnB,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,CAAS,EAAE,CAAS;IACrC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,QAAQ,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACjF,OAAO,QAAQ,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,wEAAwE;AACxE,SAAS,SAAS,CAAC,GAAkB;IACnC,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACjD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACnD,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC;IACvC,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3D,CAAC;AAED,kFAAkF;AAClF,SAAS,YAAY,CAAC,GAAkB,EAAE,KAA6C;IACrF,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACjD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACrC,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAClD,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IACxC,OAAO,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;AACpF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAA0B;IACzD,OAAO,CAAC,GAAG,EAAE,EAAE;QACb,IAAI,WAAW,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YACpC,MAAM,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;YACjC,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,CAAC,SAAS,EAAE,WAAW,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;QACtF,CAAC;QACD,IAAI,WAAW,CAAC,KAAK,KAAK,SAAS,IAAI,YAAY,CAAC,GAAG,EAAE,WAAW,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACzF,OAAO,CAAC,YAAY,EAAE,CAAC;IACzB,CAAC,CAAC;AACJ,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,aAAa,CAAC,SAA4B,EAAE;IAC1D,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;IAC7C,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,IAAI;QAC/B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,IAAI,YAAY,CAAC;QAChD,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,gBAAgB,CAAC,WAAW,CAAC;QAC5D,WAAW;QACX,MAAM,EAAE;YACN,qEAAqE;YACrE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,IAAI,KAAK;YACxC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1E,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1F;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../../src/ui/define_config.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,13 @@
1
1
  /** Keep in sync with this package's `version` in package.json. */
2
- export declare const VERSION = "0.4.0";
2
+ export declare const VERSION = "0.5.0";
3
3
  export { defineConfig, resolveConfig, normalizePath, defaultAuthorize } from './define_config.js';
4
4
  export type { AuthorizeHook, ReplayConfig, ResolvedTelescopeUiConfig, TelescopeUiConfig, UiCredentials, } from './define_config.js';
5
+ export { resolveDashboardAuth, performLogin, readSession, sanitizeReturnTo, decideDashboardAuth, SESSION_COOKIE_NAME, } from './auth.js';
6
+ export type { DashboardAuthOptions, ResolvedDashboardAuth, LoginHook, LoginOutcome, DashboardAuthDecision, } from './auth.js';
7
+ export { signSessionCookie, verifySessionCookie, } from './session_cookie.js';
8
+ export type { DashboardSession, DashboardSessionUser, SignOptions, VerifyOptions, } from './session_cookie.js';
9
+ export { renderLoginPage } from './login_page.js';
10
+ export { readSessionCookie, writeSessionCookie, clearSessionCookie, enforceDashboardAuth, } from './dashboard_auth.js';
5
11
  export { RecordingResponse, RecordingSink, formatSseFrame, formatSseHeartbeat, makeRequest, } from './http.js';
6
12
  export type { SseSink, UiHttpContext, UiRequest, UiResponse } from './http.js';
7
13
  export { enforceGuard, runGuard } from './guard.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAClG,YAAY,EACV,aAAa,EACb,YAAY,EACZ,yBAAyB,EACzB,iBAAiB,EACjB,aAAa,GACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAG/E,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC/D,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG7C,OAAO,EACL,aAAa,EACb,eAAe,EACf,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGxF,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAClG,YAAY,EACV,aAAa,EACb,YAAY,EACZ,yBAAyB,EACzB,iBAAiB,EACjB,aAAa,GACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,oBAAoB,EACpB,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,qBAAqB,GACtB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,WAAW,EACX,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAG/E,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC/D,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG7C,OAAO,EACL,aAAa,EACb,eAAe,EACf,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGxF,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,7 +1,12 @@
1
1
  /** Keep in sync with this package's `version` in package.json. */
2
- export const VERSION = '0.4.0';
2
+ export const VERSION = '0.5.0';
3
3
  // — config —
4
4
  export { defineConfig, resolveConfig, normalizePath, defaultAuthorize } from './define_config.js';
5
+ // — built-in `dashboardAuth` login screen (optional; opt-in via `config/telescope_ui.ts`) —
6
+ export { resolveDashboardAuth, performLogin, readSession, sanitizeReturnTo, decideDashboardAuth, SESSION_COOKIE_NAME, } from './auth.js';
7
+ export { signSessionCookie, verifySessionCookie, } from './session_cookie.js';
8
+ export { renderLoginPage } from './login_page.js';
9
+ export { readSessionCookie, writeSessionCookie, clearSessionCookie, enforceDashboardAuth, } from './dashboard_auth.js';
5
10
  // — HTTP shapes (framework-light) —
6
11
  export { RecordingResponse, RecordingSink, formatSseFrame, formatSseHeartbeat, makeRequest, } from './http.js';
7
12
  // — auth guard —
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,aAAa;AACb,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AASlG,oCAAoC;AACpC,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,WAAW,GACZ,MAAM,WAAW,CAAC;AAGnB,iBAAiB;AACjB,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGpD,eAAe;AACf,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG/D,qBAAqB;AACrB,OAAO,EACL,aAAa,EACb,eAAe,EACf,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAG7B,gBAAgB;AAChB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,aAAa;AACb,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AASlG,4FAA4F;AAC5F,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AAQnB,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAE7B,oCAAoC;AACpC,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,WAAW,GACZ,MAAM,WAAW,CAAC;AAGnB,iBAAiB;AACjB,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGpD,eAAe;AACf,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG/D,qBAAqB;AACrB,OAAO,EACL,aAAa,EACb,eAAe,EACf,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAG7B,gBAAgB;AAChB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
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.
21
+ */
22
+ export declare function renderLoginPage(basePath: string): string;
23
+ //# sourceMappingURL=login_page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login_page.d.ts","sourceRoot":"","sources":["../../../src/ui/login_page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAmJxD"}