@ti-engine/web-framework 1.19.1 → 1.20.1
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.
- package/CHANGELOG.md +29 -0
- package/bin/web-app-manager.js +3 -6
- package/bin/web-server.js +8 -9
- package/components/admin-config-handlers.js +3 -0
- package/components/auth-manager.js +3 -7
- package/components/authorization.js +2 -2
- package/components/config-change-notifier.js +4 -4
- package/components/config-registry.js +3 -6
- package/components/config-service.js +9 -6
- package/components/config-store.js +1 -3
- package/components/definitions.types.js +5 -3
- package/components/session-store.js +9 -6
- package/components/user.js +2 -0
- package/components/web-handlers.js +7 -4
- package/package.json +84 -21
- package/types/bin/web-app-manager.d.ts +194 -0
- package/types/bin/web-server.d.ts +374 -0
- package/types/components/admin-config-handlers.d.ts +11 -0
- package/types/components/auth-manager.d.ts +125 -0
- package/types/components/authorization.d.ts +54 -0
- package/types/components/config-change-notifier.d.ts +73 -0
- package/types/components/config-registry.d.ts +149 -0
- package/types/components/config-service.d.ts +218 -0
- package/types/components/config-store.d.ts +128 -0
- package/types/components/definitions.types.d.ts +31 -0
- package/types/components/session-store.d.ts +56 -0
- package/types/components/user.d.ts +83 -0
- package/types/components/web-config-env.d.ts +17 -0
- package/types/components/web-handlers.d.ts +23 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
This document will contain the list of changes made to the framework. The format is based on the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
|
|
4
4
|
|
|
5
|
+
## Version 1.20.1
|
|
6
|
+
|
|
7
|
+
* fix(types): emit `/// <reference types="node" />` into `web-server.d.ts`, which names `node:http`.
|
|
8
|
+
Without it a consumer who has not separately configured Node's types gets `TS2591` from inside the
|
|
9
|
+
published declarations. See `@ti-engine/core` 1.9.1 for why the 1.20.0 gate did not catch this
|
|
10
|
+
* build(release): bump package version from `1.20.0` to `1.20.1`
|
|
11
|
+
|
|
12
|
+
## Version 1.20.0
|
|
13
|
+
|
|
14
|
+
TypeScript declarations now ship with the package, verified against a consumer type-checking with `skipLibCheck: false`
|
|
15
|
+
before they can be committed. See `@ti-engine/core` 1.9.0 for why the previous attempt was withdrawn.
|
|
16
|
+
|
|
17
|
+
* feat(types): generate and publish `.d.ts` declarations for every module, wired through `types` conditions in both
|
|
18
|
+
`exports` and `imports`
|
|
19
|
+
* feat(definitions): expose the shared type definitions as `@ti-engine/web-framework/definitions`
|
|
20
|
+
* fix(session-store)!: the store's `set`, `get` and `touch` are typed against `express-session`'s `SessionData`, which
|
|
21
|
+
is what the `Store` contract hands them — not `TiSession`, which additionally requires `id`, `save`, `regenerate` and
|
|
22
|
+
`destroy`. The implementation only ever reads `cookie.maxAge`, so the annotation was describing a stricter input than
|
|
23
|
+
the base class can supply and than the code needs. Documentation only; no behaviour changes
|
|
24
|
+
* fix(web-handlers): `ExpressResponse` referred to `import("express").res`, which express does not export. It is
|
|
25
|
+
`Response`
|
|
26
|
+
* refactor(exports): the extras hung off `module.exports` after a class assignment — `instance`, `authMethod`,
|
|
27
|
+
`applyAuthMethodVisibility`, the static route matchers — are assigned to the class itself. `module.exports` *is* the
|
|
28
|
+
class, so this is the same object with the same properties at runtime; as a declaration it is the difference between
|
|
29
|
+
a namespace merge and an export assignment colliding with named exports, which is not valid TypeScript
|
|
30
|
+
* fix(types): the same closure-style `{function(...)}` and `@private`-on-`#member` corrections as `core` 1.9.0
|
|
31
|
+
* build(deps): add `@types/express`, `@types/express-session` and `@types/node`. The published declarations name types
|
|
32
|
+
from all three
|
|
33
|
+
|
|
5
34
|
## Version 1.19.1
|
|
6
35
|
|
|
7
36
|
Documentation and packaging only — no functional change.
|
package/bin/web-app-manager.js
CHANGED
|
@@ -15,6 +15,8 @@ const configRegistry = require( "#config-registry" );
|
|
|
15
15
|
const configService = require( "#config-service" );
|
|
16
16
|
const authorization = require( "#authorization" );
|
|
17
17
|
|
|
18
|
+
/** @import { TiSession } from "#definitions" */
|
|
19
|
+
|
|
18
20
|
const RE_NONCE_ATTR = /\{ti-nonce-placeholder}/g;
|
|
19
21
|
const RE_CSRF_ATTR = /\{ti-csrf-placeholder}/g;
|
|
20
22
|
const RE_HTMX_CONFIG = /\{ti-htmx-config-placeholder}/g;
|
|
@@ -462,7 +464,6 @@ class TiWebAppManager {
|
|
|
462
464
|
* @param {boolean} [options.isHome] Optional flag to indicate whether the requested route is the home page.
|
|
463
465
|
* @param {string} [options.nonce] Optional CSP nonce to inject into inline scripts/styles.
|
|
464
466
|
* @returns {Promise<string>}
|
|
465
|
-
* @private
|
|
466
467
|
*/
|
|
467
468
|
#getHtmlFragment( session, staticContentPaths, fragment, options = {} ) {
|
|
468
469
|
return new Promise( ( resolve, reject ) => {
|
|
@@ -487,7 +488,6 @@ class TiWebAppManager {
|
|
|
487
488
|
* @param {string[]} staticContentPaths A list of directories to search for static content. If sent as expected by the web server, the first item in the array should be the system default path.
|
|
488
489
|
* @param {string} filePath Relative path to the static file to locate.
|
|
489
490
|
* @returns {Promise<string>}
|
|
490
|
-
* @private
|
|
491
491
|
*/
|
|
492
492
|
#locateStaticFile( staticContentPaths, filePath ) {
|
|
493
493
|
return new Promise( ( resolve, reject ) => {
|
|
@@ -530,7 +530,6 @@ class TiWebAppManager {
|
|
|
530
530
|
* @param {string[]} staticContentPaths
|
|
531
531
|
* @param {Array<string>} components
|
|
532
532
|
* @returns {Promise<string>}
|
|
533
|
-
* @private
|
|
534
533
|
*/
|
|
535
534
|
#replaceComponentPlaceholders( html, staticContentPaths, components ) {
|
|
536
535
|
return new Promise( ( resolve, reject ) => {
|
|
@@ -573,7 +572,6 @@ class TiWebAppManager {
|
|
|
573
572
|
* @param {string} tagName
|
|
574
573
|
* @param {string} replacement
|
|
575
574
|
* @returns {string}
|
|
576
|
-
* @private
|
|
577
575
|
*/
|
|
578
576
|
#replacePlaceholderElement( html, tagName, replacement ) {
|
|
579
577
|
const start = html.indexOf( `<${ tagName }` );
|
|
@@ -643,7 +641,6 @@ class TiWebAppManager {
|
|
|
643
641
|
* @method
|
|
644
642
|
* @param {string} openingTag The full opening tag text, e.g. `<ti-foo-placeholder bar="baz">`.
|
|
645
643
|
* @returns {Object<string, string>}
|
|
646
|
-
* @private
|
|
647
644
|
*/
|
|
648
645
|
#parsePlaceholderAttributes( openingTag ) {
|
|
649
646
|
const attributes = {};
|
|
@@ -660,4 +657,4 @@ class TiWebAppManager {
|
|
|
660
657
|
}
|
|
661
658
|
|
|
662
659
|
module.exports = TiWebAppManager;
|
|
663
|
-
|
|
660
|
+
TiWebAppManager.applyAuthMethodVisibility = applyAuthMethodVisibility;
|
package/bin/web-server.js
CHANGED
|
@@ -98,6 +98,12 @@ const applyWebConfigEnvOverrides = require( "#web-config-env" );
|
|
|
98
98
|
|
|
99
99
|
const webServerConfig = require( "#web-server-config" );
|
|
100
100
|
|
|
101
|
+
/** @import { TiAuthMethod, TiTokenEndpointAuthMethod } from "#auth-manager" */
|
|
102
|
+
/** @import { TiSession } from "#definitions" */
|
|
103
|
+
/** @import User from "#user" */
|
|
104
|
+
/** @import TiWebAppManager from "#web-app-manager" */
|
|
105
|
+
/** @import { ServiceAddress, ServiceConfiguration } from "@ti-engine/core/definitions" */
|
|
106
|
+
|
|
101
107
|
/**
|
|
102
108
|
* Default unprotected static-asset route matchers. The path segments are matched with `(?:[^/]+\/)*` rather than
|
|
103
109
|
* `(?:.+\/)*`: the inner `[^/]+` cannot also consume the "/" delimiter, so the pattern is unambiguous and matches
|
|
@@ -643,7 +649,6 @@ class TiWebServer extends ServiceConsumer {
|
|
|
643
649
|
* middleware mounting is ever needed.
|
|
644
650
|
*
|
|
645
651
|
* @type {Set<string>}
|
|
646
|
-
* @private
|
|
647
652
|
*/
|
|
648
653
|
static #REGISTRABLE_METHODS = new Set( [ "get", "post", "put", "patch", "delete", "options", "head", "all" ] );
|
|
649
654
|
|
|
@@ -666,7 +671,6 @@ class TiWebServer extends ServiceConsumer {
|
|
|
666
671
|
* clear a default entry. Absent from the config file, an explicitly empty array means exactly that.
|
|
667
672
|
*
|
|
668
673
|
* @type {Object}
|
|
669
|
-
* @private
|
|
670
674
|
*/
|
|
671
675
|
static #STATIC_CACHE_DEFAULTS = Object.freeze( {
|
|
672
676
|
maxAge: 0,
|
|
@@ -682,7 +686,6 @@ class TiWebServer extends ServiceConsumer {
|
|
|
682
686
|
* value any cache treats as meaningful, and the conventional pairing for `immutable`).
|
|
683
687
|
*
|
|
684
688
|
* @type {number}
|
|
685
|
-
* @private
|
|
686
689
|
*/
|
|
687
690
|
static #IMMUTABLE_MAX_AGE = 31536000;
|
|
688
691
|
|
|
@@ -694,7 +697,6 @@ class TiWebServer extends ServiceConsumer {
|
|
|
694
697
|
* @static
|
|
695
698
|
* @param {string} entry
|
|
696
699
|
* @returns {string|null}
|
|
697
|
-
* @private
|
|
698
700
|
*/
|
|
699
701
|
static #normalizeImmutablePath( entry ) {
|
|
700
702
|
if ( typeof entry !== "string" || entry.trim() === "" ) {
|
|
@@ -715,7 +717,6 @@ class TiWebServer extends ServiceConsumer {
|
|
|
715
717
|
* @param {string} rootPath
|
|
716
718
|
* @param {string} filePath
|
|
717
719
|
* @returns {string}
|
|
718
|
-
* @private
|
|
719
720
|
*/
|
|
720
721
|
static #toServedPath( rootPath, filePath ) {
|
|
721
722
|
// Split on the platform separator only: on POSIX a backslash is a legal filename character, not a delimiter.
|
|
@@ -881,7 +882,6 @@ class TiWebServer extends ServiceConsumer {
|
|
|
881
882
|
* @param {number} port The port to listen on.
|
|
882
883
|
* @param {string} host The host to listen on.
|
|
883
884
|
* @returns {Promise<NodeServer>}
|
|
884
|
-
* @private
|
|
885
885
|
*/
|
|
886
886
|
#beginListening( server, port, host ) {
|
|
887
887
|
return new Promise( ( resolve, reject ) => {
|
|
@@ -901,7 +901,6 @@ class TiWebServer extends ServiceConsumer {
|
|
|
901
901
|
* @method
|
|
902
902
|
* @param {NodeServer} server The server instance to stop listening on.
|
|
903
903
|
* @returns {Promise}
|
|
904
|
-
* @private
|
|
905
904
|
*/
|
|
906
905
|
#endListening( server ) {
|
|
907
906
|
return new Promise( ( resolve, reject ) => {
|
|
@@ -933,5 +932,5 @@ class TiWebServer extends ServiceConsumer {
|
|
|
933
932
|
|
|
934
933
|
module.exports = TiWebServer;
|
|
935
934
|
// Exported for unit testing of the ReDoS-hardened matchers; not part of the customization surface.
|
|
936
|
-
|
|
937
|
-
|
|
935
|
+
TiWebServer.RE_STATIC_UNPROTECTED = RE_STATIC_UNPROTECTED;
|
|
936
|
+
TiWebServer.RE_WELL_KNOWN_UNPROTECTED = RE_WELL_KNOWN_UNPROTECTED;
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
|
|
20
20
|
const exceptions = require( "@ti-engine/core/exceptions" );
|
|
21
21
|
|
|
22
|
+
/** @import ConfigService from "#config-service" */
|
|
23
|
+
/** @import { ExpressHandler } from "#web-handlers" */
|
|
24
|
+
|
|
22
25
|
function sendData( response, data ) {
|
|
23
26
|
response.set( "Cache-Control", "no-store" );
|
|
24
27
|
response.set( "Content-Type", "application/json; charset=utf-8" );
|
|
@@ -13,6 +13,8 @@ const { randomBytes } = require( "node:crypto" );
|
|
|
13
13
|
const openidClient = require( "openid-client" );
|
|
14
14
|
const User = require( "#user" );
|
|
15
15
|
|
|
16
|
+
/** @import { SettingsAuth } from "#web-server" */
|
|
17
|
+
|
|
16
18
|
/**
|
|
17
19
|
* Enum for specifying the authentication method.
|
|
18
20
|
*
|
|
@@ -281,7 +283,6 @@ class AuthManager {
|
|
|
281
283
|
* unavailable so a sign-in attempt against it is rejected per-request instead of taking down startup.
|
|
282
284
|
*
|
|
283
285
|
* @method
|
|
284
|
-
* @private
|
|
285
286
|
*/
|
|
286
287
|
#dropUnconfiguredOpenIDProviders() {
|
|
287
288
|
const providers = [
|
|
@@ -302,7 +303,6 @@ class AuthManager {
|
|
|
302
303
|
* @method
|
|
303
304
|
* @param {SettingsOAuth2Client} [oauth2] The provider's OAuth2 settings.
|
|
304
305
|
* @returns {boolean}
|
|
305
|
-
* @private
|
|
306
306
|
*/
|
|
307
307
|
#isOpenIDConfigured( oauth2 ) {
|
|
308
308
|
return !!( oauth2 && typeof oauth2.clientID === "string" && oauth2.clientID.trim() !== "" );
|
|
@@ -318,7 +318,6 @@ class AuthManager {
|
|
|
318
318
|
* @method
|
|
319
319
|
* @param {SettingsOAuth2Client} oauth2
|
|
320
320
|
* @returns {Promise<openidClient.Configuration>}
|
|
321
|
-
* @private
|
|
322
321
|
*/
|
|
323
322
|
#initializeOpenIDClient( oauth2 ) {
|
|
324
323
|
return new Promise( ( resolve, reject ) => {
|
|
@@ -362,7 +361,6 @@ class AuthManager {
|
|
|
362
361
|
* @param {string} username
|
|
363
362
|
* @param {string} password
|
|
364
363
|
* @returns {Promise}
|
|
365
|
-
* @private
|
|
366
364
|
*/
|
|
367
365
|
#authenticateLocal( username, password ) {
|
|
368
366
|
return new Promise( ( resolve, reject ) => {
|
|
@@ -383,7 +381,6 @@ class AuthManager {
|
|
|
383
381
|
* @param {SettingsOAuth2Client} oauth2
|
|
384
382
|
* @param {openidClient.Configuration} clientConfig
|
|
385
383
|
* @returns {Promise<Object>}
|
|
386
|
-
* @private
|
|
387
384
|
*/
|
|
388
385
|
#authenticateOpenID( baseUrl, oauth2, clientConfig ) {
|
|
389
386
|
return new Promise( ( resolve, reject ) => {
|
|
@@ -416,7 +413,6 @@ class AuthManager {
|
|
|
416
413
|
* @param {Object} oidc
|
|
417
414
|
* @param {openidClient.Configuration} clientConfig
|
|
418
415
|
* @returns {Promise<User>}
|
|
419
|
-
* @private
|
|
420
416
|
*/
|
|
421
417
|
#authorizeOpenID( currentUrl, oidc, clientConfig ) {
|
|
422
418
|
return new Promise( ( resolve, reject ) => {
|
|
@@ -439,4 +435,4 @@ class AuthManager {
|
|
|
439
435
|
}
|
|
440
436
|
|
|
441
437
|
module.exports = AuthManager;
|
|
442
|
-
|
|
438
|
+
AuthManager.authMethod = authMethodEnum;
|
|
@@ -108,7 +108,7 @@ function isAccessAllowed( requiredRoles, userRoles ) {
|
|
|
108
108
|
* Responds `401` when unauthenticated (no session user) and `403` when authenticated but lacking the role.
|
|
109
109
|
*
|
|
110
110
|
* @param {...(string|number)} roles
|
|
111
|
-
* @returns {
|
|
111
|
+
* @returns {(request: Object, response: Object, next: Function) => void}
|
|
112
112
|
*/
|
|
113
113
|
function requireRole( ...roles ) {
|
|
114
114
|
return ( request, response, next ) => {
|
|
@@ -128,7 +128,7 @@ function requireRole( ...roles ) {
|
|
|
128
128
|
/**
|
|
129
129
|
* Express middleware that admits only `admin`-role users.
|
|
130
130
|
*
|
|
131
|
-
* @type {
|
|
131
|
+
* @type {(request: Object, response: Object, next: Function) => void}
|
|
132
132
|
*/
|
|
133
133
|
const requireAdmin = requireRole( ADMIN_ROLE );
|
|
134
134
|
|
|
@@ -70,8 +70,8 @@ class ConfigChangeNotifier {
|
|
|
70
70
|
* Subscribes a listener to configuration-change events.
|
|
71
71
|
*
|
|
72
72
|
* @method
|
|
73
|
-
* @param {
|
|
74
|
-
* @returns {
|
|
73
|
+
* @param {(event: ConfigChangeEvent) => void} listener
|
|
74
|
+
* @returns {() => void} An unsubscribe function.
|
|
75
75
|
* @public
|
|
76
76
|
*/
|
|
77
77
|
subscribe( listener ) {
|
|
@@ -94,5 +94,5 @@ class ConfigChangeNotifier {
|
|
|
94
94
|
|
|
95
95
|
const instance = new ConfigChangeNotifier();
|
|
96
96
|
module.exports = ConfigChangeNotifier;
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
ConfigChangeNotifier.instance = instance;
|
|
98
|
+
ConfigChangeNotifier.CONFIG_CHANGED = CONFIG_CHANGED;
|
|
@@ -45,11 +45,11 @@ const instancePathToDataPath = ( instancePath ) => {
|
|
|
45
45
|
* {@link ConfigService#applyEdits} call.
|
|
46
46
|
*
|
|
47
47
|
* @typedef {Object} ValidatorContext
|
|
48
|
-
* @property {
|
|
48
|
+
* @property {(key: string) => Promise<*>} getConfig Resolves the *pending* value of `key` when it is part of the
|
|
49
49
|
* current edit batch, otherwise its current committed value. Lets a validator check a sibling document's
|
|
50
50
|
* post-edit state — but calling this for the document being validated itself just returns the same incoming
|
|
51
51
|
* value already passed as the validator's first argument, not its prior state.
|
|
52
|
-
* @property {
|
|
52
|
+
* @property {(key: string) => Promise<*>} getStoredConfig Always resolves the current committed value of `key`,
|
|
53
53
|
* even when `key` is the document currently under validation. Use this to compare a document against its own
|
|
54
54
|
* previous state (e.g. detecting an edit that should have bumped a version marker).
|
|
55
55
|
*/
|
|
@@ -214,7 +214,6 @@ class ConfigRegistry {
|
|
|
214
214
|
* @method
|
|
215
215
|
* @param {Object} registration
|
|
216
216
|
* @returns {Function} The compiled (and cached) ajv validate function for the document's schema.
|
|
217
|
-
* @private
|
|
218
217
|
*/
|
|
219
218
|
#schemaValidator( registration ) {
|
|
220
219
|
if ( !registration.compiled ) {
|
|
@@ -229,7 +228,6 @@ class ConfigRegistry {
|
|
|
229
228
|
* @method
|
|
230
229
|
* @param {Object} schema
|
|
231
230
|
* @returns {Object} A shallow copy without the ajv-6-incompatible Draft-2020-12 `$schema` annotation.
|
|
232
|
-
* @private
|
|
233
231
|
*/
|
|
234
232
|
#stripUnsupportedMeta( schema ) {
|
|
235
233
|
if ( schema && schema.$schema && String( schema.$schema ).includes( "draft/2020-12" ) ) {
|
|
@@ -244,7 +242,6 @@ class ConfigRegistry {
|
|
|
244
242
|
* @method
|
|
245
243
|
* @param {ConfigValidationIssue|string} issue
|
|
246
244
|
* @returns {ConfigValidationIssue}
|
|
247
|
-
* @private
|
|
248
245
|
*/
|
|
249
246
|
#normalizeIssue( issue ) {
|
|
250
247
|
if ( typeof issue === "string" ) {
|
|
@@ -257,4 +254,4 @@ class ConfigRegistry {
|
|
|
257
254
|
|
|
258
255
|
const instance = new ConfigRegistry();
|
|
259
256
|
module.exports = ConfigRegistry;
|
|
260
|
-
|
|
257
|
+
ConfigRegistry.instance = instance;
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
const exceptions = require( "@ti-engine/core/exceptions" );
|
|
10
10
|
|
|
11
|
+
/** @import ConfigChangeNotifier from "#config-change-notifier" */
|
|
12
|
+
/** @import ConfigRegistry from "#config-registry" */
|
|
13
|
+
/** @import ConfigStore from "#config-store" */
|
|
14
|
+
|
|
11
15
|
/**
|
|
12
16
|
* Orchestrates validated, versioned configuration edits on top of {@link ConfigStore} and {@link ConfigRegistry}.
|
|
13
17
|
*
|
|
@@ -115,8 +119,8 @@ class ConfigService {
|
|
|
115
119
|
* @param {string} editorKey
|
|
116
120
|
* @param {Object} definition
|
|
117
121
|
* @param {string[]} definition.documents The configKeys this editor spans.
|
|
118
|
-
* @param {
|
|
119
|
-
* @param {
|
|
122
|
+
* @param {(configs: Object) => *} definition.compose Maps `{ [configKey]: value }` → a view for the UI.
|
|
123
|
+
* @param {(editedView: *, currentDocs: Object) => Object.<string, Object>} definition.decompose Maps `(editedView, currentDocs)` → the
|
|
120
124
|
* full new values for the documents that changed (`{ [configKey]: newValue }`).
|
|
121
125
|
* @param {Object} [definition.metadata]
|
|
122
126
|
* @returns {ConfigService} this (chainable)
|
|
@@ -321,8 +325,8 @@ class ConfigService {
|
|
|
321
325
|
* Subscribes a listener to `config:changed` events (delegates to the change notifier). Returns an unsubscribe fn.
|
|
322
326
|
*
|
|
323
327
|
* @method
|
|
324
|
-
* @param {
|
|
325
|
-
* @returns {
|
|
328
|
+
* @param {(configs: Object) => void} listener
|
|
329
|
+
* @returns {() => void}
|
|
326
330
|
* @public
|
|
327
331
|
*/
|
|
328
332
|
onConfigChanged( listener ) {
|
|
@@ -335,7 +339,6 @@ class ConfigService {
|
|
|
335
339
|
* @method
|
|
336
340
|
* @param {string[]} keys
|
|
337
341
|
* @returns {Promise<{docs: Object<string, Object>, versions: Object<string, number>}>}
|
|
338
|
-
* @private
|
|
339
342
|
*/
|
|
340
343
|
#loadDocuments( keys ) {
|
|
341
344
|
return Promise.all( keys.map( ( key ) => this.#store.getCurrent( key ) ) ).then( ( currents ) => {
|
|
@@ -357,4 +360,4 @@ function clone( value ) {
|
|
|
357
360
|
|
|
358
361
|
const instance = new ConfigService();
|
|
359
362
|
module.exports = ConfigService;
|
|
360
|
-
|
|
363
|
+
ConfigService.instance = instance;
|
|
@@ -222,7 +222,6 @@ class ConfigStore {
|
|
|
222
222
|
* @method
|
|
223
223
|
* @param {string} key
|
|
224
224
|
* @returns {Promise<Object|null>}
|
|
225
|
-
* @private
|
|
226
225
|
*/
|
|
227
226
|
#readJSON( key ) {
|
|
228
227
|
return cache.instance.getJSON( key ).then( ( result ) => ( Array.isArray( result ) ? ( result[ 0 ] ?? null ) : ( result ?? null ) ) );
|
|
@@ -233,7 +232,6 @@ class ConfigStore {
|
|
|
233
232
|
* @param {string} key
|
|
234
233
|
* @param {Object} value
|
|
235
234
|
* @returns {Promise}
|
|
236
|
-
* @private
|
|
237
235
|
*/
|
|
238
236
|
#writeJSON( key, value ) {
|
|
239
237
|
return cache.instance.setJSON( key, value );
|
|
@@ -243,4 +241,4 @@ class ConfigStore {
|
|
|
243
241
|
|
|
244
242
|
const instance = new ConfigStore();
|
|
245
243
|
module.exports = ConfigStore;
|
|
246
|
-
|
|
244
|
+
ConfigStore.instance = Object.freeze( instance );
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/** @import { TiLocalizationLanguage } from "@ti-engine/core/localization" */
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* @callback TiSessionCallback
|
|
11
13
|
* @param {Error|null} [error]
|
|
@@ -20,7 +22,7 @@
|
|
|
20
22
|
* @property {Object} [cookie]
|
|
21
23
|
* @property {Object} [oidc]
|
|
22
24
|
* @property {string} [csrfToken]
|
|
23
|
-
* @property {
|
|
24
|
-
* @property {
|
|
25
|
-
* @property {
|
|
25
|
+
* @property {(callback: TiSessionCallback) => TiSession} regenerate
|
|
26
|
+
* @property {(callback: TiSessionCallback) => TiSession} destroy
|
|
27
|
+
* @property {(callback?: TiSessionCallback) => TiSession} save
|
|
26
28
|
*/
|
|
@@ -12,6 +12,9 @@ const exceptions = require( "@ti-engine/core/exceptions" );
|
|
|
12
12
|
const _ = require( "lodash" );
|
|
13
13
|
const session = require( "express-session" );
|
|
14
14
|
|
|
15
|
+
/** @import { SessionData } from "express-session" */
|
|
16
|
+
/** @import { TiException } from "@ti-engine/core/exceptions" */
|
|
17
|
+
|
|
15
18
|
// The name of the session store in the cache:
|
|
16
19
|
const sessionStoreName = "ti:web:sessions";
|
|
17
20
|
|
|
@@ -37,8 +40,8 @@ class SessionStore extends session.Store {
|
|
|
37
40
|
*
|
|
38
41
|
* @method
|
|
39
42
|
* @param {string} sessionID
|
|
40
|
-
* @param {
|
|
41
|
-
* @param {
|
|
43
|
+
* @param {SessionData} session
|
|
44
|
+
* @param {(error?: Error|TiException|null) => void} callback
|
|
42
45
|
* @public
|
|
43
46
|
*/
|
|
44
47
|
set( sessionID, session, callback ) {
|
|
@@ -58,7 +61,7 @@ class SessionStore extends session.Store {
|
|
|
58
61
|
*
|
|
59
62
|
* @method
|
|
60
63
|
* @param {string} sessionID
|
|
61
|
-
* @param {
|
|
64
|
+
* @param {(error?: Error|TiException|null, session?: SessionData|null) => void} callback
|
|
62
65
|
* @public
|
|
63
66
|
*/
|
|
64
67
|
get( sessionID, callback ) {
|
|
@@ -75,7 +78,7 @@ class SessionStore extends session.Store {
|
|
|
75
78
|
*
|
|
76
79
|
* @method
|
|
77
80
|
* @param {string} sessionID
|
|
78
|
-
* @param {
|
|
81
|
+
* @param {(error?: Error|TiException|null) => void} callback
|
|
79
82
|
* @public
|
|
80
83
|
*/
|
|
81
84
|
destroy( sessionID, callback ) {
|
|
@@ -92,8 +95,8 @@ class SessionStore extends session.Store {
|
|
|
92
95
|
*
|
|
93
96
|
* @method
|
|
94
97
|
* @param {string} sessionID
|
|
95
|
-
* @param {
|
|
96
|
-
* @param {
|
|
98
|
+
* @param {SessionData} session
|
|
99
|
+
* @param {(error?: Error|TiException|null) => void} callback
|
|
97
100
|
* @public
|
|
98
101
|
*/
|
|
99
102
|
touch( sessionID, session, callback ) {
|
package/components/user.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/** @import { TiLocalizationLanguage } from "@ti-engine/core/localization" */
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* Represents a user in the system.
|
|
11
13
|
*
|
|
@@ -17,8 +17,11 @@ const cache = require( "@ti-engine/core/cache" );
|
|
|
17
17
|
const authMethod = require( "#auth-manager" ).authMethod;
|
|
18
18
|
const authorization = require( "#authorization" );
|
|
19
19
|
|
|
20
|
+
/** @import { TiAuthMethod } from "#auth-manager" */
|
|
21
|
+
/** @import TiWebServer from "#web-server" */
|
|
22
|
+
|
|
20
23
|
/** @typedef {import("express").Request} ExpressRequest */
|
|
21
|
-
/** @typedef {import("express").
|
|
24
|
+
/** @typedef {import("express").Response} ExpressResponse */
|
|
22
25
|
|
|
23
26
|
/**
|
|
24
27
|
* Express middleware callback.
|
|
@@ -26,7 +29,7 @@ const authorization = require( "#authorization" );
|
|
|
26
29
|
* @callback ExpressHandler
|
|
27
30
|
* @param {ExpressRequest} request
|
|
28
31
|
* @param {ExpressResponse} response
|
|
29
|
-
* @param {
|
|
32
|
+
* @param {(error: Error|null) => void} next
|
|
30
33
|
* @returns {void}
|
|
31
34
|
*/
|
|
32
35
|
|
|
@@ -37,7 +40,7 @@ const authorization = require( "#authorization" );
|
|
|
37
40
|
* @param {Error} error
|
|
38
41
|
* @param {ExpressRequest} request
|
|
39
42
|
* @param {ExpressResponse} response
|
|
40
|
-
* @param {
|
|
43
|
+
* @param {(error: Error|null) => void} next
|
|
41
44
|
* @returns {void}
|
|
42
45
|
*/
|
|
43
46
|
|
|
@@ -154,7 +157,7 @@ let getRequestOrigin = ( request ) => {
|
|
|
154
157
|
* @method
|
|
155
158
|
* @param {ExpressRequest} request
|
|
156
159
|
* @param {string} redirectTo
|
|
157
|
-
* @param {
|
|
160
|
+
* @param {(session: TiSession) => TiSession} modifier
|
|
158
161
|
* @returns {Promise<string>}
|
|
159
162
|
* @private
|
|
160
163
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ti-engine/web-framework",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.1",
|
|
4
4
|
"description": "A web-framework based on the ti-engine. It provides a customizable ready-to-use web-server microservice and a set of tools for creating web applications. NOTICE: This is still a work in progress and the full architecture, design, and functionality are not available!",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ti-engine",
|
|
@@ -15,43 +15,105 @@
|
|
|
15
15
|
"author": "Boris Kostadinov <kostadinov.boris@gmail.com>",
|
|
16
16
|
"license": "GPL-3.0-or-later",
|
|
17
17
|
"exports": {
|
|
18
|
-
"./config-management":
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
"./config-management": {
|
|
19
|
+
"types": "./types/components/config-service.d.ts",
|
|
20
|
+
"default": "./components/config-service.js"
|
|
21
|
+
},
|
|
22
|
+
"./web-application": {
|
|
23
|
+
"types": "./types/bin/web-app-manager.d.ts",
|
|
24
|
+
"default": "./bin/web-app-manager.js"
|
|
25
|
+
},
|
|
26
|
+
"./web-server": {
|
|
27
|
+
"types": "./types/bin/web-server.d.ts",
|
|
28
|
+
"default": "./bin/web-server.js"
|
|
29
|
+
},
|
|
30
|
+
"./definitions": {
|
|
31
|
+
"types": "./types/components/definitions.types.d.ts",
|
|
32
|
+
"default": "./components/definitions.types.js"
|
|
33
|
+
}
|
|
21
34
|
},
|
|
22
35
|
"imports": {
|
|
23
|
-
"#admin-config-handlers":
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"#
|
|
36
|
+
"#admin-config-handlers": {
|
|
37
|
+
"types": "./types/components/admin-config-handlers.d.ts",
|
|
38
|
+
"default": "./components/admin-config-handlers.js"
|
|
39
|
+
},
|
|
40
|
+
"#auth-manager": {
|
|
41
|
+
"types": "./types/components/auth-manager.d.ts",
|
|
42
|
+
"default": "./components/auth-manager.js"
|
|
43
|
+
},
|
|
44
|
+
"#authorization": {
|
|
45
|
+
"types": "./types/components/authorization.d.ts",
|
|
46
|
+
"default": "./components/authorization.js"
|
|
47
|
+
},
|
|
48
|
+
"#config-change-notifier": {
|
|
49
|
+
"types": "./types/components/config-change-notifier.d.ts",
|
|
50
|
+
"default": "./components/config-change-notifier.js"
|
|
51
|
+
},
|
|
52
|
+
"#config-registry": {
|
|
53
|
+
"types": "./types/components/config-registry.d.ts",
|
|
54
|
+
"default": "./components/config-registry.js"
|
|
55
|
+
},
|
|
56
|
+
"#config-service": {
|
|
57
|
+
"types": "./types/components/config-service.d.ts",
|
|
58
|
+
"default": "./components/config-service.js"
|
|
59
|
+
},
|
|
60
|
+
"#config-store": {
|
|
61
|
+
"types": "./types/components/config-store.d.ts",
|
|
62
|
+
"default": "./components/config-store.js"
|
|
63
|
+
},
|
|
64
|
+
"#definitions": {
|
|
65
|
+
"types": "./types/components/definitions.types.d.ts",
|
|
66
|
+
"default": "./components/definitions.types.js"
|
|
67
|
+
},
|
|
68
|
+
"#session-store": {
|
|
69
|
+
"types": "./types/components/session-store.d.ts",
|
|
70
|
+
"default": "./components/session-store.js"
|
|
71
|
+
},
|
|
72
|
+
"#user": {
|
|
73
|
+
"types": "./types/components/user.d.ts",
|
|
74
|
+
"default": "./components/user.js"
|
|
75
|
+
},
|
|
76
|
+
"#web-app-manager": {
|
|
77
|
+
"types": "./types/bin/web-app-manager.d.ts",
|
|
78
|
+
"default": "./bin/web-app-manager.js"
|
|
79
|
+
},
|
|
80
|
+
"#web-config-env": {
|
|
81
|
+
"types": "./types/components/web-config-env.d.ts",
|
|
82
|
+
"default": "./components/web-config-env.js"
|
|
83
|
+
},
|
|
84
|
+
"#web-handlers": {
|
|
85
|
+
"types": "./types/components/web-handlers.d.ts",
|
|
86
|
+
"default": "./components/web-handlers.js"
|
|
87
|
+
},
|
|
88
|
+
"#web-server": {
|
|
89
|
+
"types": "./types/bin/web-server.d.ts",
|
|
90
|
+
"default": "./bin/web-server.js"
|
|
91
|
+
},
|
|
36
92
|
"#web-server-config": "./bin/web-server.json"
|
|
37
93
|
},
|
|
38
94
|
"dependencies": {
|
|
39
|
-
"@ti-engine/core": "*",
|
|
40
95
|
"@alpinejs/csp": "^3.15.12",
|
|
41
|
-
"
|
|
96
|
+
"@ti-engine/core": "*",
|
|
97
|
+
"@types/express": "^5.0.6",
|
|
98
|
+
"@types/express-session": "^1.18.2",
|
|
99
|
+
"@types/node": "^24.10.1",
|
|
42
100
|
"ajv": "^8.20.0",
|
|
43
101
|
"cookie-parser": "^1.4.7",
|
|
44
102
|
"express": "^5.2.1",
|
|
45
103
|
"express-session": "^1.19.0",
|
|
46
104
|
"helmet": "^8.3.0",
|
|
105
|
+
"htmx.org": "^2.0.10",
|
|
47
106
|
"lodash": "^4.18.1",
|
|
48
107
|
"openid-client": "^6.8.4"
|
|
49
108
|
},
|
|
50
|
-
"devDependencies": {
|
|
109
|
+
"devDependencies": {
|
|
110
|
+
"typescript": "^7.0.2"
|
|
111
|
+
},
|
|
51
112
|
"files": [
|
|
52
113
|
"bin/",
|
|
53
114
|
"!bin/tls/**/*",
|
|
54
115
|
"components/",
|
|
116
|
+
"types/",
|
|
55
117
|
".env",
|
|
56
118
|
"package.json",
|
|
57
119
|
"README.md",
|
|
@@ -71,6 +133,7 @@
|
|
|
71
133
|
},
|
|
72
134
|
"scripts": {
|
|
73
135
|
"postinstall": "node ./bin/build/post-install.js",
|
|
74
|
-
"test": "node --test test/*.test.js"
|
|
136
|
+
"test": "node --test test/*.test.js",
|
|
137
|
+
"build:types": "tsc -p tsconfig.types.json"
|
|
75
138
|
}
|
|
76
139
|
}
|