@ti-engine/web-framework 1.19.0 → 1.20.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 (52) hide show
  1. package/.env +4 -4
  2. package/CHANGELOG.md +384 -353
  3. package/README.md +73 -73
  4. package/bin/build/post-install.js +18 -18
  5. package/bin/localization/web-server-labels.json +27 -27
  6. package/bin/static/.well-known/appspecific/com.chrome.devtools.json +5 -5
  7. package/bin/static/fragments/components/component-notification-bar.html +21 -21
  8. package/bin/static/fragments/components/component-sidebar.html +33 -33
  9. package/bin/static/fragments/components/component-tooltip.html +10 -10
  10. package/bin/static/fragments/components/component-topbar.html +5 -5
  11. package/bin/static/fragments/frame-administration.html +2 -2
  12. package/bin/static/fragments/frame-application.html +18 -18
  13. package/bin/static/fragments/frame-dashboard.html +2 -2
  14. package/bin/static/fragments/frame-login.html +119 -119
  15. package/bin/static/fragments/frame-not-found.html +2 -2
  16. package/bin/static/fragments/frame-profile.html +2 -2
  17. package/bin/static/index.html +22 -22
  18. package/bin/static/scripts/ti-charts.js +1591 -1591
  19. package/bin/static/scripts/ti-framework.css +3194 -3194
  20. package/bin/static/scripts/ti-framework.js +1427 -1427
  21. package/bin/static/scripts/ti-theme-black-glass.css +216 -216
  22. package/bin/static/scripts/ti-theme-daylight.css +87 -87
  23. package/bin/web-app-manager.js +660 -663
  24. package/bin/web-server.js +936 -937
  25. package/bin/web-server.json +48 -48
  26. package/components/admin-config-handlers.js +95 -92
  27. package/components/auth-manager.js +438 -442
  28. package/components/authorization.js +135 -135
  29. package/components/config-change-notifier.js +98 -98
  30. package/components/config-registry.js +257 -260
  31. package/components/config-service.js +363 -360
  32. package/components/config-store.js +244 -246
  33. package/components/definitions.types.js +28 -26
  34. package/components/session-store.js +113 -110
  35. package/components/user.js +134 -132
  36. package/components/web-config-env.js +85 -85
  37. package/components/web-handlers.js +803 -800
  38. package/package.json +139 -67
  39. package/types/bin/web-app-manager.d.ts +194 -0
  40. package/types/bin/web-server.d.ts +373 -0
  41. package/types/components/admin-config-handlers.d.ts +11 -0
  42. package/types/components/auth-manager.d.ts +125 -0
  43. package/types/components/authorization.d.ts +54 -0
  44. package/types/components/config-change-notifier.d.ts +73 -0
  45. package/types/components/config-registry.d.ts +149 -0
  46. package/types/components/config-service.d.ts +218 -0
  47. package/types/components/config-store.d.ts +128 -0
  48. package/types/components/definitions.types.d.ts +31 -0
  49. package/types/components/session-store.d.ts +56 -0
  50. package/types/components/user.d.ts +83 -0
  51. package/types/components/web-config-env.d.ts +17 -0
  52. package/types/components/web-handlers.d.ts +23 -0
@@ -0,0 +1,373 @@
1
+ export = TiWebServer;
2
+ import ServiceConsumer = require("@ti-engine/core/service-consumer");
3
+ export type NodeServer = import("node:http").Server;
4
+ export type TiWebServiceConfiguration = ServiceConfiguration;
5
+ export type TiWebApplicationConfig = {
6
+ classPath: string;
7
+ };
8
+ export type ApiConfig = {
9
+ endpointEnabled: boolean;
10
+ inventory: ApiInventory;
11
+ requestTimeout: number;
12
+ };
13
+ export type SettingsAuth = {
14
+ enabledMethods: string[];
15
+ local: Object;
16
+ oauth2: {
17
+ azure?: SettingsOAuth2Client;
18
+ google?: SettingsOAuth2Client;
19
+ };
20
+ };
21
+ export type SettingsOAuth2Client = {
22
+ clientID?: string;
23
+ clientSecret?: string;
24
+ callbackUrl?: string;
25
+ discoveryUrl?: string;
26
+ isPublic?: boolean;
27
+ tokenEndpointAuthMethod?: TiTokenEndpointAuthMethod;
28
+ };
29
+ export type SettingsStaticCache = {
30
+ /**
31
+ * The `max-age` for `/static` responses, in SECONDS (not a duration string). `0` means every use is revalidated.
32
+ */
33
+ maxAge: number;
34
+ /**
35
+ * Whether to add `immutable`. Only correct when the `/static` filenames are content-addressed.
36
+ */
37
+ immutable: boolean;
38
+ /**
39
+ * Path prefixes under `/static` that are served long-lived and `immutable` regardless of the two settings above.
40
+ */
41
+ immutablePaths: string[];
42
+ };
43
+ export type SettingsCookies = {
44
+ secret: string;
45
+ path: string;
46
+ httpOnly: boolean;
47
+ sameSite: "lax" | "strict" | "none";
48
+ maxAge: number;
49
+ };
50
+ export type ApiInventory = Record<string, Record<string, ServiceAddress>>;
51
+ import type { TiAuthMethod, TiTokenEndpointAuthMethod } from "#auth-manager";
52
+ import type { TiSession } from "#definitions";
53
+ import type User from "#user";
54
+ import type TiWebAppManager from "#web-app-manager";
55
+ import type { ServiceAddress, ServiceConfiguration } from "@ti-engine/core/definitions";
56
+ /** @import { TiAuthMethod, TiTokenEndpointAuthMethod } from "#auth-manager" */
57
+ /** @import { TiSession } from "#definitions" */
58
+ /** @import User from "#user" */
59
+ /** @import TiWebAppManager from "#web-app-manager" */
60
+ /** @import { ServiceAddress, ServiceConfiguration } from "@ti-engine/core/definitions" */
61
+ /**
62
+ * Default unprotected static-asset route matchers. The path segments are matched with `(?:[^/]+\/)*` rather than
63
+ * `(?:.+\/)*`: the inner `[^/]+` cannot also consume the "/" delimiter, so the pattern is unambiguous and matches
64
+ * in linear time. The previous `.+` form was ambiguous and backtracked exponentially on hostile request paths such
65
+ * as `/static/a/a/…/a/x` (no trailing extension) — and these matchers run against the raw request path in
66
+ * {@link TiWebServer#isUnprotectedRoute} BEFORE authentication, so that was a pre-auth denial-of-service vector
67
+ * (CodeQL js/redos). The matched language for realistic asset paths is unchanged.
68
+ *
69
+ * @type {RegExp}
70
+ */
71
+ declare const RE_STATIC_UNPROTECTED: RegExp;
72
+ /**
73
+ * Default unprotected `/.well-known/` route matcher. See {@link RE_STATIC_UNPROTECTED} for the ReDoS rationale.
74
+ *
75
+ * @type {RegExp}
76
+ */
77
+ declare const RE_WELL_KNOWN_UNPROTECTED: RegExp;
78
+ /**
79
+ * A web server microservice based on the ti-engine.
80
+ * <br/>
81
+ * Note: The web server is fully functional and already comes with all the necessary fundamentals and security features. However, it is designed to be extended
82
+ * with custom logic and functionality to fit your specific needs. Here is a list of methods that you can override to customize the web server behavior:
83
+ * - {@link TiWebServer#defineWebApplicationRoutes} Override this to define custom web application routes. Remember to call the base method if you want to preserve the default behavior as well (recommended).
84
+ * - {@link TiWebServer#defineUnprotectedRoutes} Override this to define unprotected routes. Remember to call the base method if you want to preserve the default behavior as well (recommended).
85
+ * - {@link TiWebServer#verifySession} Override this to implement custom session verification logic.
86
+ *
87
+ * @class TiWebServer
88
+ * @extends ServiceConsumer
89
+ * @public
90
+ */
91
+ declare class TiWebServer extends ServiceConsumer {
92
+ #private;
93
+ /**
94
+ * @constructor
95
+ * @param {string} serviceDomainName The service domain name for this service instance.
96
+ * @param {TiWebServiceConfiguration} serviceConfig The JSON configuration for this service. Note that the configuration provided will be merged with the default web server configuration, and it will override any conflicting properties.
97
+ * @throws {TiException.E_GEN_JS_INTERNAL_ERROR} If the web application manager cannot be loaded.
98
+ */
99
+ constructor(serviceDomainName: string, serviceConfig: TiWebServiceConfiguration);
100
+ /**
101
+ * Property returning the service configuration JSON.
102
+ *
103
+ * @property
104
+ * @returns {TiWebServiceConfiguration}
105
+ * @override
106
+ * @public
107
+ */
108
+ get serviceConfig(): TiWebServiceConfiguration;
109
+ /**
110
+ * Property returning if the web server is currently shutting down.
111
+ *
112
+ * @property
113
+ * @returns {boolean}
114
+ * @public
115
+ */
116
+ get isShuttingDown(): boolean;
117
+ /**
118
+ * Property returning the list of static content directories.
119
+ *
120
+ * @property
121
+ * @returns {string[]}
122
+ * @public
123
+ */
124
+ get staticContentPaths(): string[];
125
+ /**
126
+ * Property returning the server URL.
127
+ *
128
+ * @property
129
+ * @returns {string}
130
+ * @public
131
+ */
132
+ get serverUrl(): string;
133
+ /**
134
+ * Property returning the {@link TiWebAppManager} instance.
135
+ *
136
+ * @property
137
+ * @returns {TiWebAppManager}
138
+ * @public
139
+ */
140
+ get webAppManager(): TiWebAppManager;
141
+ /**
142
+ * Starts the web server.
143
+ *
144
+ * @method
145
+ * @returns {Promise}
146
+ * @override
147
+ * @public
148
+ */
149
+ onStart(): Promise<any>;
150
+ /**
151
+ * Shuts down the web server.
152
+ *
153
+ * @method
154
+ * @returns {Promise}
155
+ * @override
156
+ * @public
157
+ */
158
+ onStop(): Promise<any>;
159
+ /**
160
+ * Used to report health status of the service instance for external monitoring.
161
+ * This is a scheduled job that will be executed at SERVICE_HEALTH_CHECK_INTERVAL time.
162
+ *
163
+ * @method
164
+ * @override
165
+ * @public
166
+ */
167
+ reportHealthy(): void;
168
+ /**
169
+ * Used to verify the session of a request.
170
+ *
171
+ * @method
172
+ * @param {TiSession} session
173
+ * @returns {boolean}
174
+ * @public
175
+ */
176
+ verifySession(session: TiSession): boolean;
177
+ /**
178
+ * Hook for the application to augment the freshly-authenticated session (e.g. derive domain roles from an
179
+ * identity store or the org chart). Runs synchronously, once per login, before the framework's additive `admin`
180
+ * role is applied. The default is a no-op. Any test-user role injection is an override of whatever the app derives.
181
+ *
182
+ * @method
183
+ * @virtual
184
+ * @param {TiSession} session
185
+ * @param {Object} [request] Optional Express request object that can be used to read body/cookies/query data.
186
+ * @returns {TiSession}
187
+ * @public
188
+ */
189
+ augmentSession(session: TiSession, request?: Object): TiSession;
190
+ /**
191
+ * Used to authenticate a user via the specified auth method.
192
+ *
193
+ * @method
194
+ * @param {TiAuthMethod} authMethod
195
+ * @param {Object} [authDetails={}]
196
+ * @returns {Promise}
197
+ * @public
198
+ */
199
+ authenticate(authMethod: TiAuthMethod, authDetails?: Object): Promise<any>;
200
+ /**
201
+ * Used to set up user authorization according to the specified auth method.
202
+ *
203
+ * @method
204
+ * @param {TiAuthMethod} authMethod
205
+ * @param {URL} currentUrl
206
+ * @param {Object} oidc
207
+ * @returns {Promise<User>}
208
+ * @public
209
+ */
210
+ authorize(authMethod: TiAuthMethod, currentUrl: URL, oidc: Object): Promise<User>;
211
+ /**
212
+ * Used to get a service mapping if such exists.
213
+ *
214
+ * @method
215
+ * @param {string} serviceVersion
216
+ * @param {string} serviceName
217
+ * @returns {ServiceAddress}
218
+ * @public
219
+ */
220
+ getServiceAddress(serviceVersion: string, serviceName: string): ServiceAddress;
221
+ /**
222
+ * Used to check if the specified hostname is allowed to access the web server.
223
+ *
224
+ * @method
225
+ * @param {string} hostname
226
+ * @returns {boolean}
227
+ * @public
228
+ */
229
+ isAllowedHost(hostname: string): boolean;
230
+ /**
231
+ * Used to check if the specified route is unprotected (i.e., does not require authentication). The default unprotected routes are:
232
+ * - /
233
+ * - /static/...
234
+ * - /.well-known/...
235
+ * - /not-found
236
+ * - /app
237
+ * - /app/enter
238
+ * - /app/config
239
+ * - /logout
240
+ * - /login/:method
241
+ * <br/>
242
+ * NOTE: You can define custom unprotected routes by overriding the {@link TiWebServer#defineUnprotectedRoutes} method.
243
+ *
244
+ * @method
245
+ * @param {string} route
246
+ * @returns {boolean}
247
+ * @public
248
+ */
249
+ isUnprotectedRoute(route: string): boolean;
250
+ /**
251
+ * Used to define the web application routes.
252
+ * <br/>
253
+ * NOTE: Override this to define custom web application routes. Remember to call the base method if you want to preserve the default behavior as well.
254
+ *
255
+ * @method
256
+ * @virtual
257
+ * @public
258
+ */
259
+ defineWebApplicationRoutes(): void;
260
+ /**
261
+ * Used to define the unprotected routes (i.e., routes that do not require authentication).
262
+ * <br/>
263
+ * NOTE: Override this to define custom unprotected routes. Remember to call the base method if you want to preserve the default behavior as well.
264
+ *
265
+ * @method
266
+ * @virtual
267
+ * @public
268
+ */
269
+ defineUnprotectedRoutes(): void;
270
+ /**
271
+ * Registers a custom application route on the underlying Express app.
272
+ * <br/>
273
+ * NOTE: Call this from a {@link TiWebServer#defineWebApplicationRoutes} override AFTER invoking the base method,
274
+ * so the framework's own routes keep priority and any catch-all route you add resolves last (it will still be
275
+ * registered before the framework's own `*splat` 404 handler). It is only valid once the Express app exists —
276
+ * i.e., from within {@link TiWebServer#defineWebApplicationRoutes}, which {@link TiWebServer#onStart} invokes.
277
+ *
278
+ * @method
279
+ * @param {string} method One of the supported routing verbs: get, post, put, patch, delete, options, head, all.
280
+ * @param {string|RegExp} path The route path or pattern.
281
+ * @param {...Function} handlers One or more Express route handlers/middleware.
282
+ * @returns {TiWebServer} This instance, to allow chaining.
283
+ * @public
284
+ */
285
+ registerRoute(method: string, path: string | RegExp, ...handlers: Function[]): TiWebServer;
286
+ /**
287
+ * Adds a pattern to the unprotected-routes list — routes that bypass the authentication gate. A string is
288
+ * matched exactly against the request path; a RegExp is tested against it. Consulted at request time by
289
+ * {@link TiWebServer#isUnprotectedRoute}.
290
+ * <br/>
291
+ * NOTE: Call this from a {@link TiWebServer#defineUnprotectedRoutes} override AFTER invoking the base method, to
292
+ * extend (rather than replace) the defaults.
293
+ *
294
+ * @method
295
+ * @param {string|RegExp} pattern The exact path (string) or path matcher (RegExp) to treat as unprotected.
296
+ * @returns {TiWebServer} This instance, to allow chaining.
297
+ * @public
298
+ */
299
+ addUnprotectedRoute(pattern: string | RegExp): TiWebServer;
300
+ /**
301
+ * Resolves a `staticCache` configuration block into the policy the `/static` mounts apply, filling in
302
+ * {@link TiWebServer.#STATIC_CACHE_DEFAULTS} per key and rejecting values that cannot be honored. Pure: problems
303
+ * are returned as `warnings` rather than logged, so the caller decides how to surface them and a test can assert
304
+ * on them. Static and exposed for unit testing — not part of the customization surface.
305
+ * <br/>
306
+ * `maxAge` is a whole number of SECONDS, mapping 1:1 onto the `Cache-Control` directive — express's `"1y"`-style
307
+ * duration strings are NOT accepted, and are reported rather than silently reinterpreted as milliseconds.
308
+ * <br/>
309
+ * `immutable` is dropped (with a warning) when `maxAge` is 0, because a response that is stale on arrival yet
310
+ * promises never to change is a contradiction. Dropping it fails safe: the misconfiguration costs a revalidation,
311
+ * not a year of unreachable assets.
312
+ *
313
+ * @method
314
+ * @static
315
+ * @param {SettingsStaticCache} [staticCache] The configured block, if any.
316
+ * @returns {{maxAge: number, immutable: boolean, immutablePaths: string[], warnings: string[]}}
317
+ * @public
318
+ */
319
+ static resolveStaticCachePolicy(staticCache?: SettingsStaticCache): {
320
+ maxAge: number;
321
+ immutable: boolean;
322
+ immutablePaths: string[];
323
+ warnings: string[];
324
+ };
325
+ /**
326
+ * Builds the `Cache-Control` value for one static file: the long-lived immutable policy when its served path sits
327
+ * under a configured `immutablePaths` prefix (matched case-sensitively, so a case mismatch falls back to the safe
328
+ * side), otherwise the policy's own `maxAge`/`immutable`. A `maxAge` of 0 is emitted as an explicit
329
+ * `must-revalidate` rather than a bare `max-age=0`, matching what the sibling `web-content` package serves.
330
+ * Pure and static; exposed for unit testing — not part of the customization surface.
331
+ *
332
+ * @method
333
+ * @static
334
+ * @param {string} rootPath The directory this `/static` mount serves.
335
+ * @param {string} filePath The absolute path of the file being served.
336
+ * @param {Object} policy A policy as returned by {@link TiWebServer.resolveStaticCachePolicy}.
337
+ * @returns {string}
338
+ * @public
339
+ */
340
+ static staticCacheControlFor(rootPath: string, filePath: string, policy: Object): string;
341
+ /**
342
+ * Normalizes an HTTP method to a lower-case Express routing verb, or returns null if it is not a supported,
343
+ * registrable verb. Anything that is not a string is rejected outright rather than coerced — otherwise a value
344
+ * whose `toString()` happens to yield a verb (`[ "get" ]`, `new String( "get" )`) would register a route and
345
+ * bypass the `E_GEN_INVALID_ARGUMENT_TYPE` that {@link TiWebServer#registerRoute} raises for a bad method.
346
+ * Pure and static; exposed for unit testing — not part of the customization surface.
347
+ *
348
+ * @method
349
+ * @static
350
+ * @param {string} method
351
+ * @returns {string|null}
352
+ * @public
353
+ */
354
+ static normalizeRegistrableMethod(method: string): string | null;
355
+ /**
356
+ * Tests a request path against a list of unprotected-route patterns (string exact-match or RegExp test),
357
+ * returning true on the first match. A RegExp's `lastIndex` is reset defensively so a stateful 'g'/'y' flag
358
+ * cannot cause a match to be skipped. Pure and static; shared by {@link TiWebServer#isUnprotectedRoute} and
359
+ * exposed for unit testing — not part of the customization surface.
360
+ *
361
+ * @method
362
+ * @static
363
+ * @param {Array<string|RegExp>} patterns
364
+ * @param {string} pathOnly The request path with any query string already stripped.
365
+ * @returns {boolean}
366
+ * @public
367
+ */
368
+ static isRouteInList(patterns: Array<string | RegExp>, pathOnly: string): boolean;
369
+ }
370
+ declare namespace TiWebServer {
371
+ export { RE_STATIC_UNPROTECTED };
372
+ export { RE_WELL_KNOWN_UNPROTECTED };
373
+ }
@@ -0,0 +1,11 @@
1
+ export declare var listEditors: (service: ConfigService) => ExpressHandler;
2
+ export declare var composeView: (service: any) => (request: any, response: any, next: any) => void;
3
+ export declare var saveEditorEdit: (service: any) => (request: any, response: any, next: any) => void;
4
+ export declare var getCurrent: (service: any) => (request: any, response: any, next: any) => void;
5
+ export declare var getHistory: (service: any) => (request: any, response: any, next: any) => void;
6
+ export declare var listChanges: (service: any) => (request: any, response: any, next: any) => void;
7
+ export declare var getChange: (service: any) => (request: any, response: any, next: any) => void;
8
+ export declare var restoreChangeSet: (service: any) => (request: any, response: any, next: any) => void;
9
+ export declare var exportBundle: (service: any) => (request: any, response: any, next: any) => void;
10
+ import type ConfigService from "#config-service";
11
+ import type { ExpressHandler } from "#web-handlers";
@@ -0,0 +1,125 @@
1
+ export = AuthManager;
2
+ import User = require("#user");
3
+ import type { SettingsAuth } from "#web-server";
4
+ export type TiAuthMethod = string;
5
+ /** @import { SettingsAuth } from "#web-server" */
6
+ /**
7
+ * Enum for specifying the authentication method.
8
+ *
9
+ * @readonly
10
+ * @enum {string}
11
+ * @typedef {string} TiAuthMethod
12
+ */
13
+ declare const authMethodEnum: import("@ti-engine/core/definitions").TiEnumOf<{
14
+ LOCAL: string[];
15
+ OPENID_AZURE: string[];
16
+ OPENID_GOOGLE: string[];
17
+ }>;
18
+ export type TiTokenEndpointAuthMethod = string;
19
+ /**
20
+ * The AuthManager class is used to manage authentication and authorization.
21
+ *
22
+ * @class AuthManager
23
+ * @public
24
+ */
25
+ declare class AuthManager {
26
+ #private;
27
+ /**
28
+ * @constructor
29
+ * @param {SettingsAuth} settings
30
+ */
31
+ constructor(settings: SettingsAuth);
32
+ /**
33
+ * Used to initialize the authentication manager.
34
+ *
35
+ * @method
36
+ * @returns {Promise}
37
+ * @public
38
+ */
39
+ initialize(): Promise<any>;
40
+ /**
41
+ * Used to check whether the specified authentication method is enabled.
42
+ *
43
+ * @method
44
+ * @param {TiAuthMethod} authMethod
45
+ * @returns {boolean}
46
+ * @public
47
+ */
48
+ isAuthEnabled(authMethod: TiAuthMethod): boolean;
49
+ /**
50
+ * Returns the list of currently enabled authentication methods, reflecting any OpenID providers dropped by
51
+ * {@link AuthManager#initialize} for being enabled but unconfigured. Callers (e.g. the login-page renderer)
52
+ * use this to present only the methods a user can actually complete.
53
+ *
54
+ * @method
55
+ * @returns {TiAuthMethod[]}
56
+ * @public
57
+ */
58
+ getEnabledMethods(): TiAuthMethod[];
59
+ /**
60
+ * Used to authenticate a user via the specified authentication method.
61
+ *
62
+ * @method
63
+ * @param {TiAuthMethod} authMethod
64
+ * @param {Object} authDetails
65
+ * @returns {Promise<Object>}
66
+ * @throws {TiException.E_SEC_UNRECOGNIZED_AUTH_METHOD} If the authentication method is not recognized or enabled.
67
+ * @throws {TiException.E_GEN_NOT_INITIALIZED} If the auth manager was not properly initialized.
68
+ * @public
69
+ */
70
+ authenticate(authMethod: TiAuthMethod, authDetails: Object): Promise<Object>;
71
+ /**
72
+ * Used to set up user authorization according to the specified authentication method.
73
+ *
74
+ * @method
75
+ * @param {TiAuthMethod} authMethod
76
+ * @param {URL} currentUrl
77
+ * @param {Object} oidc
78
+ * @returns {Promise<User>}
79
+ * @throws {TiException.E_SEC_UNRECOGNIZED_AUTH_METHOD} If the authentication method is not recognized.
80
+ * @public
81
+ */
82
+ authorize(authMethod: TiAuthMethod, currentUrl: URL, oidc: Object): Promise<User>;
83
+ /**
84
+ * Used to get the callback URL for the specified OAuth2 authentication method.
85
+ *
86
+ * @method
87
+ * @param {TiAuthMethod} authMethod
88
+ * @returns {string}
89
+ * @throws {TiException.E_SEC_UNRECOGNIZED_AUTH_METHOD} If the requested OAuth2 method is not recognized or enabled.
90
+ * @public
91
+ */
92
+ getOAuth2CallbackUrl(authMethod: TiAuthMethod): string;
93
+ /**
94
+ * Used to get the local route path of the callback for the specified OAuth2 authentication method.
95
+ * <br/>
96
+ * A callback can legitimately be configured either as a path or as the full absolute URL registered with the
97
+ * identity provider. The absolute form is what the provider expects as the redirect URI, but it is not a usable
98
+ * Express route pattern, so this reduces whatever is configured to the path the server must actually listen on.
99
+ *
100
+ * @method
101
+ * @param {TiAuthMethod} authMethod
102
+ * @returns {string|null} The route path, or null if the configured callback yields no usable path.
103
+ * @throws {TiException.E_SEC_UNRECOGNIZED_AUTH_METHOD} If the requested OAuth2 method is not recognized or enabled.
104
+ * @public
105
+ */
106
+ getOAuth2CallbackPath(authMethod: TiAuthMethod): string | null;
107
+ /**
108
+ * Reduces a configured OAuth2 callback value to the local route path it corresponds to. Accepts an absolute URL
109
+ * ('https://host/login/azure-callback'), a protocol-relative URL, or a path with or without its leading slash,
110
+ * and strips any query string or fragment. Pure and static; exposed for unit testing.
111
+ * <br/>
112
+ * NOTE: This exists because Express 5 parses a route pattern with path-to-regexp v8, where ':' opens a parameter
113
+ * name — so an absolute URL used verbatim as a route path throws 'Missing parameter name' at startup.
114
+ *
115
+ * @method
116
+ * @static
117
+ * @param {string} callbackUrl
118
+ * @returns {string|null} The route path, or null if no usable path can be derived.
119
+ * @public
120
+ */
121
+ static toCallbackPath(callbackUrl: string): string | null;
122
+ }
123
+ declare namespace AuthManager {
124
+ export { authMethodEnum as authMethod };
125
+ }
@@ -0,0 +1,54 @@
1
+ declare const _exports: {
2
+ ADMIN_ROLE: string;
3
+ isAdminIdentity: typeof isAdminIdentity;
4
+ applyAdminRole: typeof applyAdminRole;
5
+ hasAnyRole: typeof hasAnyRole;
6
+ isAccessAllowed: typeof isAccessAllowed;
7
+ requireRole: typeof requireRole;
8
+ requireAdmin: (request: Object, response: Object, next: Function) => void;
9
+ };
10
+ export = _exports;
11
+ /**
12
+ * Returns `true` if the user matches any entry in the admin allowlist. An entry may match the user's `userID`,
13
+ * `username`, or `email` (case-insensitive).
14
+ *
15
+ * @param {Object} user A session user (`{ userID, username, email, roles, ... }`).
16
+ * @param {string[]} admins The configured allowlist of admin identifiers.
17
+ * @returns {boolean}
18
+ */
19
+ declare function isAdminIdentity(user: Object, admins: string[]): boolean;
20
+ /**
21
+ * Adds the `admin` role to the session user (additively, no duplicates) when the user is in the allowlist.
22
+ * Safe to call with an empty/missing allowlist or session — it is then a no-op. Returns the session for chaining.
23
+ *
24
+ * @param {Object} session
25
+ * @param {string[]} [admins]
26
+ * @returns {Object} The (possibly modified) session.
27
+ */
28
+ declare function applyAdminRole(session: Object, admins?: string[]): Object;
29
+ /**
30
+ * @param {Object} session
31
+ * @param {Array<string|number>} roles
32
+ * @returns {boolean} `true` if the session user holds any of the given roles.
33
+ */
34
+ declare function hasAnyRole(session: Object, roles: Array<string | number>): boolean;
35
+ /**
36
+ * Pure access decision for a resource (e.g. an HTML fragment) that declares a set of required roles. A resource with
37
+ * no required roles (`null` / `undefined` / empty) is public — any (authenticated) user may access it; otherwise the
38
+ * user must hold at least one of the required roles. Roles are treated opaquely, so this works equally for numeric
39
+ * application role codes and the string `admin` role — there is no implicit hierarchy (an `admin`-gated resource is
40
+ * reachable only by holders of the `admin` role, never by a high numeric role). Backs {@link TiWebAppManager#verifyAccess}.
41
+ *
42
+ * @param {Array<string|number>} [requiredRoles] The roles permitted to access the resource; empty/absent = public.
43
+ * @param {Array<string|number>} [userRoles] The roles held by the current session user.
44
+ * @returns {boolean}
45
+ */
46
+ declare function isAccessAllowed(requiredRoles?: Array<string | number>, userRoles?: Array<string | number>): boolean;
47
+ /**
48
+ * Express middleware factory that admits a request only if its session user holds at least one of the given roles.
49
+ * Responds `401` when unauthenticated (no session user) and `403` when authenticated but lacking the role.
50
+ *
51
+ * @param {...(string|number)} roles
52
+ * @returns {(request: Object, response: Object, next: Function) => void}
53
+ */
54
+ declare function requireRole(...roles: (string | number)[]): (request: Object, response: Object, next: Function) => void;
@@ -0,0 +1,73 @@
1
+ export = ConfigChangeNotifier;
2
+ export type ConfigChangeEvent = {
3
+ changeSetID: string;
4
+ /**
5
+ * The configuration documents affected by the change.
6
+ */
7
+ configKeys: string[];
8
+ /**
9
+ * Who committed the change.
10
+ */
11
+ adminID: string;
12
+ /**
13
+ * ISO timestamp.
14
+ */
15
+ timestamp: string;
16
+ };
17
+ /**
18
+ * @typedef {Object} ConfigChangeEvent
19
+ * @property {string} changeSetID
20
+ * @property {string[]} configKeys The configuration documents affected by the change.
21
+ * @property {string} adminID Who committed the change.
22
+ * @property {string} timestamp ISO timestamp.
23
+ */
24
+ declare const CONFIG_CHANGED = "config:changed";
25
+ /**
26
+ * Notifies subscribers that configuration changed, so they can react (e.g. invalidate an in-memory cache, or push a
27
+ * live update to an admin UI). This is the **in-process** implementation of a deliberately transport-agnostic
28
+ * contract — `publish(event)` (fire-and-forget) and `subscribe(listener) → unsubscribe`.
29
+ *
30
+ * **Designed for an eventual switch to a reusable core pub/sub.** Cross-instance propagation is out of scope for v1
31
+ * (the store-backed model already makes a committed change visible to every instance via the shared Redis cache;
32
+ * this emitter exists to invalidate optional *in-memory* caches and drive live UI within a process). When a Redis
33
+ * (or other) pub/sub primitive lands in `@ti-engine/core`, a drop-in implementation of this same contract can be
34
+ * provided and injected into {@link ConfigService} — no change to publishers or subscribers. To keep that swap
35
+ * behavior-safe, **delivery here is already asynchronous** (matching cross-instance transports); subscribers must
36
+ * not assume synchronous delivery, and the event payload is plain serializable JSON so it survives a wire transport.
37
+ *
38
+ * @class ConfigChangeNotifier
39
+ * @public
40
+ */
41
+ declare class ConfigChangeNotifier {
42
+ #private;
43
+ constructor();
44
+ /**
45
+ * Publishes a configuration-change event to all subscribers. Fire-and-forget; delivery is asynchronous.
46
+ *
47
+ * @method
48
+ * @param {ConfigChangeEvent} event
49
+ * @returns {ConfigChangeEvent} The (frozen) event that will be delivered.
50
+ * @public
51
+ */
52
+ publish(event: ConfigChangeEvent): ConfigChangeEvent;
53
+ /**
54
+ * Subscribes a listener to configuration-change events.
55
+ *
56
+ * @method
57
+ * @param {(event: ConfigChangeEvent) => void} listener
58
+ * @returns {() => void} An unsubscribe function.
59
+ * @public
60
+ */
61
+ subscribe(listener: (event: ConfigChangeEvent) => void): () => void;
62
+ /**
63
+ * @method
64
+ * @returns {number} The current number of subscribers.
65
+ * @public
66
+ */
67
+ subscriberCount(): number;
68
+ }
69
+ declare namespace ConfigChangeNotifier {
70
+ export { instance };
71
+ export { CONFIG_CHANGED };
72
+ }
73
+ declare const instance: ConfigChangeNotifier;