@expressots/adapter-express 4.0.0-preview.1 → 4.0.0-preview.3
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/LICENSE.md +21 -21
- package/README.md +61 -61
- package/lib/CHANGELOG.md +10 -5
- package/lib/README.md +61 -61
- package/lib/cjs/adapter-express/application-express.js +401 -45
- package/lib/cjs/adapter-express/express-utils/decorators.js +44 -15
- package/lib/cjs/adapter-express/express-utils/inversify-express-server.js +20 -4
- package/lib/cjs/adapter-express/express-utils/path-pattern-compat.js +129 -0
- package/lib/cjs/adapter-express/express-utils/route-constraints.js +12 -3
- package/lib/cjs/adapter-express/micro-api/application-express-micro.js +5 -9
- package/lib/cjs/adapter-express/micro-api/micro.js +96 -41
- package/lib/cjs/adapter-express/studio/index.js +2 -1
- package/lib/cjs/adapter-express/studio/studio-integration.js +64 -11
- package/lib/cjs/types/adapter-express/application-express.d.ts +51 -9
- package/lib/cjs/types/adapter-express/express-utils/path-pattern-compat.d.ts +66 -0
- package/lib/cjs/types/adapter-express/express-utils/route-constraints.d.ts +12 -3
- package/lib/cjs/types/adapter-express/micro-api/micro.d.ts +19 -2
- package/lib/cjs/types/adapter-express/studio/index.d.ts +1 -1
- package/lib/cjs/types/adapter-express/studio/studio-integration.d.ts +78 -0
- package/lib/esm/adapter-express/application-express.js +402 -46
- package/lib/esm/adapter-express/express-utils/decorators.js +44 -15
- package/lib/esm/adapter-express/express-utils/inversify-express-server.js +20 -4
- package/lib/esm/adapter-express/express-utils/path-pattern-compat.js +125 -0
- package/lib/esm/adapter-express/express-utils/route-constraints.js +12 -3
- package/lib/esm/adapter-express/micro-api/application-express-micro.js +6 -10
- package/lib/esm/adapter-express/micro-api/micro.js +97 -42
- package/lib/esm/adapter-express/studio/index.js +1 -1
- package/lib/esm/adapter-express/studio/studio-integration.js +63 -11
- package/lib/esm/types/adapter-express/application-express.d.ts +51 -9
- package/lib/esm/types/adapter-express/express-utils/path-pattern-compat.d.ts +66 -0
- package/lib/esm/types/adapter-express/express-utils/route-constraints.d.ts +12 -3
- package/lib/esm/types/adapter-express/micro-api/micro.d.ts +19 -2
- package/lib/esm/types/adapter-express/studio/index.d.ts +1 -1
- package/lib/esm/types/adapter-express/studio/studio-integration.d.ts +78 -0
- package/lib/package.json +24 -10
- package/package.json +25 -11
|
@@ -5,24 +5,52 @@
|
|
|
5
5
|
* when it's installed in the project. It enables request recording, tracing,
|
|
6
6
|
* and real-time monitoring without requiring manual setup.
|
|
7
7
|
*/
|
|
8
|
+
import { Logger } from "@expressots/core";
|
|
9
|
+
// Lazy logger accessor so `new Logger()` only fires the first time we
|
|
10
|
+
// actually emit a Studio message. Routing through Logger means the
|
|
11
|
+
// framework's log-level configuration (e.g. `LOG_LEVEL=WARN`) silences
|
|
12
|
+
// the "listening" line as expected, instead of `console.log` always
|
|
13
|
+
// printing it. Lazy construction also keeps consumers that mock
|
|
14
|
+
// `@expressots/core` (test environments) from blowing up at module
|
|
15
|
+
// load when their Logger mock omits `.withContext`.
|
|
16
|
+
let _studioLogger = null;
|
|
17
|
+
function logger() {
|
|
18
|
+
if (!_studioLogger) {
|
|
19
|
+
_studioLogger = new Logger().withContext("studio");
|
|
20
|
+
}
|
|
21
|
+
return _studioLogger;
|
|
22
|
+
}
|
|
8
23
|
let studioAgent = null;
|
|
9
24
|
let studioEnabled = false;
|
|
10
25
|
/**
|
|
11
|
-
* Check if
|
|
26
|
+
* Check if `@expressots/studio-agent` is installed and importable from the
|
|
27
|
+
* current process. Uses a dynamic `import()` rather than `require.resolve`
|
|
28
|
+
* because the latter is unavailable in pure-ESM consumers — adapter-express
|
|
29
|
+
* is published as a dual CJS/ESM build and this helper is exercised by both
|
|
30
|
+
* targets.
|
|
31
|
+
*
|
|
32
|
+
* The result is cached on first hit (success) so we avoid paying for the
|
|
33
|
+
* import twice. On failure we always retry, since the user may install the
|
|
34
|
+
* package mid-session in `expressots dev` workflows.
|
|
12
35
|
*/
|
|
36
|
+
let _studioAgentModule = null;
|
|
13
37
|
async function isStudioAgentInstalled() {
|
|
14
38
|
const debug = process.env.EXPRESSOTS_STUDIO_DEBUG === "true";
|
|
39
|
+
if (_studioAgentModule !== null)
|
|
40
|
+
return true;
|
|
15
41
|
try {
|
|
16
|
-
//
|
|
17
|
-
|
|
42
|
+
// Dynamic import works for both CJS (returns module.exports) and ESM
|
|
43
|
+
// (returns the ESM namespace). If the package isn't installed, Node
|
|
44
|
+
// throws ERR_MODULE_NOT_FOUND / MODULE_NOT_FOUND, which we treat as
|
|
45
|
+
// "agent not present" — adapter-express continues without Studio.
|
|
46
|
+
_studioAgentModule = await import("@expressots/studio-agent");
|
|
18
47
|
if (debug)
|
|
19
|
-
console.log("[Studio]
|
|
48
|
+
console.log("[Studio] Loaded studio-agent successfully");
|
|
20
49
|
return true;
|
|
21
50
|
}
|
|
22
51
|
catch (error) {
|
|
23
52
|
if (debug)
|
|
24
|
-
console.log("[Studio] Cannot
|
|
25
|
-
// Module not installed
|
|
53
|
+
console.log("[Studio] Cannot load studio-agent:", error instanceof Error ? error.message : error);
|
|
26
54
|
return false;
|
|
27
55
|
}
|
|
28
56
|
}
|
|
@@ -78,9 +106,9 @@ export async function initializeStudio(app, config = {}, appContainer) {
|
|
|
78
106
|
const studioAgentModuleAny = studioAgentModule;
|
|
79
107
|
const StudioAgent = studioAgentModuleAny.StudioAgent || studioAgentModuleAny.default?.StudioAgent;
|
|
80
108
|
if (!StudioAgent) {
|
|
81
|
-
|
|
109
|
+
logger().warn("Studio Agent module found but StudioAgent class not exported");
|
|
82
110
|
if (debug)
|
|
83
|
-
|
|
111
|
+
logger().debug(`Module contents: ${Object.keys(studioAgentModule).join(", ")}`);
|
|
84
112
|
return false;
|
|
85
113
|
}
|
|
86
114
|
if (debug)
|
|
@@ -103,7 +131,7 @@ export async function initializeStudio(app, config = {}, appContainer) {
|
|
|
103
131
|
// Start the agent (this also scans routes)
|
|
104
132
|
await studioAgent.start();
|
|
105
133
|
studioEnabled = true;
|
|
106
|
-
|
|
134
|
+
logger().info(`Studio Agent listening on ws://localhost:${agentOptions.port}`);
|
|
107
135
|
return true;
|
|
108
136
|
}
|
|
109
137
|
catch (error) {
|
|
@@ -131,12 +159,12 @@ export async function initializeStudio(app, config = {}, appContainer) {
|
|
|
131
159
|
// Friendlier message for the most common failure mode: hot-reload
|
|
132
160
|
// race left the port in TIME_WAIT.
|
|
133
161
|
if (errorCode === "EADDRINUSE") {
|
|
134
|
-
|
|
162
|
+
logger().warn(`Studio Agent could not bind its WebSocket port ` +
|
|
135
163
|
`(${errorMessage}). The host app will continue without Studio. ` +
|
|
136
164
|
`If this happened during hot-reload, the next restart should recover.`);
|
|
137
165
|
return false;
|
|
138
166
|
}
|
|
139
|
-
|
|
167
|
+
logger().warn(`Failed to initialize Studio Agent: ${errorMessage}`);
|
|
140
168
|
return false;
|
|
141
169
|
}
|
|
142
170
|
}
|
|
@@ -160,6 +188,30 @@ export function reportStudioRuntimeInfo(patch) {
|
|
|
160
188
|
// Best-effort — never break the host on a status-page update.
|
|
161
189
|
}
|
|
162
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Re-trigger the Studio Agent's route discovery. Used by the host
|
|
193
|
+
* after `app.listen()` so that the agent's runtime route scanner sees
|
|
194
|
+
* the fully-populated Express `_router` stack (controllers are bound
|
|
195
|
+
* by `InversifyExpressServer.build()` AFTER `initializeStudio()` runs,
|
|
196
|
+
* so the agent's first scan only catches static-source routes).
|
|
197
|
+
*
|
|
198
|
+
* No-ops when:
|
|
199
|
+
* - Studio isn't enabled, or
|
|
200
|
+
* - the installed agent is too old to expose `scanRoutes()`.
|
|
201
|
+
*/
|
|
202
|
+
export async function rescanStudioRoutes() {
|
|
203
|
+
if (!studioAgent)
|
|
204
|
+
return;
|
|
205
|
+
if (typeof studioAgent.scanRoutes !== "function")
|
|
206
|
+
return;
|
|
207
|
+
try {
|
|
208
|
+
await studioAgent.scanRoutes();
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
// Best-effort — never break the host on a Studio rescan.
|
|
212
|
+
logger().warn(`Studio route rescan failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
163
215
|
/**
|
|
164
216
|
* Stop the Studio Agent
|
|
165
217
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Server as HTTPServer } from "http";
|
|
2
|
-
import { AppContainer, IConsoleMessage, IMiddleware, ProviderManager, BannerConfig } from "@expressots/core";
|
|
2
|
+
import { AppContainer, IConsoleMessage, IMiddleware, Logger, ProviderManager, BannerConfig } from "@expressots/core";
|
|
3
3
|
import { IWebServerPublic, RenderEngine, Server } from "@expressots/shared";
|
|
4
4
|
import { interfaces } from "@expressots/core";
|
|
5
5
|
/**
|
|
@@ -14,7 +14,7 @@ import { interfaces } from "@expressots/core";
|
|
|
14
14
|
* @method isDevelopment - Verifies if the current environment is development.
|
|
15
15
|
*/
|
|
16
16
|
export declare class AppExpress implements Server.IWebServer {
|
|
17
|
-
|
|
17
|
+
protected logger: Logger;
|
|
18
18
|
private console;
|
|
19
19
|
private app;
|
|
20
20
|
private serverInstance;
|
|
@@ -51,9 +51,7 @@ export declare class AppExpress implements Server.IWebServer {
|
|
|
51
51
|
private static originalStderrWrite;
|
|
52
52
|
private static logBuffer;
|
|
53
53
|
private static isBuffering;
|
|
54
|
-
private static bufferingInitialized;
|
|
55
54
|
private static originalGlobalConsole;
|
|
56
|
-
private static initBuffering;
|
|
57
55
|
/**
|
|
58
56
|
* Disable log buffering. Called by micro() to restore normal console output
|
|
59
57
|
* since micro API doesn't use the banner system.
|
|
@@ -61,11 +59,17 @@ export declare class AppExpress implements Server.IWebServer {
|
|
|
61
59
|
*/
|
|
62
60
|
static disableBuffering(): void;
|
|
63
61
|
/**
|
|
64
|
-
* Start buffering all console output.
|
|
65
|
-
*
|
|
66
|
-
*
|
|
62
|
+
* Start buffering all console output for the banner-first display flow.
|
|
63
|
+
* Captures both `console.*` and direct `process.stdout.write` / `process.stderr.write`
|
|
64
|
+
* calls so they can be flushed in the correct order after the banner displays.
|
|
65
|
+
*
|
|
66
|
+
* Idempotent: calling this multiple times is safe.
|
|
67
|
+
*
|
|
68
|
+
* @public API — called by `bootstrap()` so logs emitted during container
|
|
69
|
+
* setup are captured before the `AppExpress` instance exists. Also called
|
|
70
|
+
* automatically inside the constructor as a safety net.
|
|
67
71
|
*/
|
|
68
|
-
|
|
72
|
+
static startLogBuffering(): void;
|
|
69
73
|
/**
|
|
70
74
|
* Stop buffering but keep the buffered logs for later flushing.
|
|
71
75
|
* This restores normal console/stdout output.
|
|
@@ -390,9 +394,47 @@ export declare class AppExpress implements Server.IWebServer {
|
|
|
390
394
|
* harvested (keeps the WS payload small).
|
|
391
395
|
*/
|
|
392
396
|
private collectStudioRuntimeItems;
|
|
397
|
+
/**
|
|
398
|
+
* Harvest controller- and route-scoped middleware bindings from
|
|
399
|
+
* Reflect metadata. Each entry describes a single edge the Studio
|
|
400
|
+
* architecture map should draw, e.g. "AuthMiddleware → UserController
|
|
401
|
+
* (route POST /users/:id)".
|
|
402
|
+
*
|
|
403
|
+
* The middleware values stored on `ControllerMetadata.middleware` are
|
|
404
|
+
* a polymorphic union (class, function, registered name, conditional
|
|
405
|
+
* config, …). We normalise each to a display name; entries we can't
|
|
406
|
+
* name (anonymous arrow functions, plain object configs without a
|
|
407
|
+
* `name` field) are omitted. The agent's static scan picks up the
|
|
408
|
+
* remaining named cases via decorator parsing — between the two
|
|
409
|
+
* sources Studio sees a complete graph for the common patterns.
|
|
410
|
+
*/
|
|
411
|
+
private collectMiddlewareBindings;
|
|
412
|
+
/**
|
|
413
|
+
* Combine a controller's base path with a route path, normalising
|
|
414
|
+
* leading/trailing slashes. Mirrors the simpler logic Studio uses to
|
|
415
|
+
* build `RouteInfo.path` so the bindings line up with route entries.
|
|
416
|
+
*/
|
|
417
|
+
private joinRoutePath;
|
|
418
|
+
/**
|
|
419
|
+
* Collect the ordered middleware pipeline from the Middleware service
|
|
420
|
+
* for forwarding to Studio. Uses feature-detection so older core
|
|
421
|
+
* versions that lack `getPipelineInfo()` won't break.
|
|
422
|
+
*/
|
|
423
|
+
private collectMiddlewarePipelineItems;
|
|
424
|
+
/**
|
|
425
|
+
* Build the middleware preset info snapshot for Studio. Reads the
|
|
426
|
+
* last applied preset from the Middleware service and transforms it
|
|
427
|
+
* into the shape Studio expects.
|
|
428
|
+
*/
|
|
429
|
+
private collectMiddlewarePresetInfo;
|
|
393
430
|
/**
|
|
394
431
|
* Display middleware startup logs after the banner.
|
|
395
|
-
*
|
|
432
|
+
*
|
|
433
|
+
* Warnings (e.g. missing optional packages like `helmet`) are always surfaced
|
|
434
|
+
* so the developer can act on them. Informational entries (e.g. "Security
|
|
435
|
+
* configured", "Applied preset: api") are demoted to `debug` since the
|
|
436
|
+
* dashboard already shows the active middleware count; set `LOG_LEVEL=DEBUG`
|
|
437
|
+
* to see the full breakdown.
|
|
396
438
|
* @private
|
|
397
439
|
*/
|
|
398
440
|
private displayMiddlewareStartupLogs;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express 5 / path-to-regexp v8 compatibility for the `:name(regex)`
|
|
3
|
+
* inline-constraint syntax.
|
|
4
|
+
*
|
|
5
|
+
* `path-to-regexp` v8 (the parser Express 5 ships with) removed the
|
|
6
|
+
* inline regex form entirely — `/users/:id(\\d+)` now throws
|
|
7
|
+
*
|
|
8
|
+
* Unexpected ( at index 10: /users/:id(\\d+)
|
|
9
|
+
*
|
|
10
|
+
* That breaks two things we ship as public API:
|
|
11
|
+
*
|
|
12
|
+
* 1. The {@link Patterns} / {@link pattern} helpers in
|
|
13
|
+
* `route-constraints.ts`, which were introduced specifically to
|
|
14
|
+
* encourage that pattern.
|
|
15
|
+
* 2. Hand-written controller routes upgraded from v3, where users
|
|
16
|
+
* relied on Express 4 inline regex.
|
|
17
|
+
*
|
|
18
|
+
* Rather than break those at the surface of preview-3, we keep the
|
|
19
|
+
* authoring-time syntax and translate it at decorator time:
|
|
20
|
+
*
|
|
21
|
+
* - {@link splitPathConstraints} parses the path into a
|
|
22
|
+
* plain-`:name`-only form plus a list of `(name, regex)` pairs.
|
|
23
|
+
* - {@link createPathConstraintMiddleware} returns a middleware that
|
|
24
|
+
* runs at request time and 404s when any captured `req.params[name]`
|
|
25
|
+
* fails to match its constraint.
|
|
26
|
+
*
|
|
27
|
+
* The middleware emits an HTTP 404 (not 400) so the behaviour matches
|
|
28
|
+
* Express 4's "no route matched" semantics — under v6 of path-to-regexp,
|
|
29
|
+
* a non-matching `:id(\\d+)` simply meant the route wasn't selected and
|
|
30
|
+
* the request fell through to the framework's NotFound handler.
|
|
31
|
+
*/
|
|
32
|
+
import type { RequestHandler } from "express";
|
|
33
|
+
export interface PathConstraint {
|
|
34
|
+
/** The `:name` placeholder, without the leading colon. */
|
|
35
|
+
paramName: string;
|
|
36
|
+
/** Compiled regex. Anchored with `^...$` to match the whole segment. */
|
|
37
|
+
regex: RegExp;
|
|
38
|
+
/** The original raw regex text, for diagnostics. */
|
|
39
|
+
rawPattern: string;
|
|
40
|
+
}
|
|
41
|
+
export interface SplitPath {
|
|
42
|
+
/** Path string ready to hand to Express 5 / path-to-regexp v8. */
|
|
43
|
+
path: string;
|
|
44
|
+
/** Param-level regex constraints, in path declaration order. */
|
|
45
|
+
constraints: Array<PathConstraint>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Split `:name(regex)` segments out of an Express-style route path.
|
|
49
|
+
*
|
|
50
|
+
* The walker honours balanced parens inside the regex (e.g.
|
|
51
|
+
* `(\\d{4})` or `((a|b)+)`), which is more forgiving than a naive
|
|
52
|
+
* single-pass regex match would be. Returns the original path and an
|
|
53
|
+
* empty constraints list when no inline patterns are found, so this is
|
|
54
|
+
* a no-op for the common case.
|
|
55
|
+
*/
|
|
56
|
+
export declare function splitPathConstraints(path: string): SplitPath;
|
|
57
|
+
/**
|
|
58
|
+
* Build a middleware that enforces the given param-level regex
|
|
59
|
+
* constraints on `req.params`. Returns `null` when the list is empty
|
|
60
|
+
* (so callers can avoid wiring an unnecessary middleware).
|
|
61
|
+
*
|
|
62
|
+
* When a constraint fails, the middleware delegates to `next()` without
|
|
63
|
+
* a value; the framework's NotFound handler then converts that into a
|
|
64
|
+
* 404 — same observable behaviour as Express 4's "no route matched".
|
|
65
|
+
*/
|
|
66
|
+
export declare function createPathConstraintMiddleware(constraints: Array<PathConstraint>): RequestHandler | null;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Route parameter patterns for common use cases.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Express 5 / path-to-regexp v8 dropped the inline-regex form
|
|
5
|
+
* (`:id(\\d+)`), so the framework no longer hands these patterns to
|
|
6
|
+
* the underlying matcher verbatim. Instead, the HTTP-method decorators
|
|
7
|
+
* (`@Get`, `@Post`, …) parse the constraint out of the path at decorator
|
|
8
|
+
* time, register the route under a plain `:id` placeholder, and inject
|
|
9
|
+
* a small validator middleware that 404s when the captured value
|
|
10
|
+
* doesn't match. The user-facing semantics are unchanged: a path that
|
|
11
|
+
* uses `Patterns.NUMERIC_ID` still rejects `/users/abc` and only
|
|
12
|
+
* dispatches the handler for matches like `/users/123`.
|
|
4
13
|
*
|
|
5
14
|
* @example
|
|
6
15
|
* ```typescript
|
|
@@ -8,12 +17,12 @@
|
|
|
8
17
|
*
|
|
9
18
|
* @Get(`/users/${pattern("id", Patterns.NUMERIC_ID)}`)
|
|
10
19
|
* getUserById(@param("id") id: number) {
|
|
11
|
-
* // Only
|
|
20
|
+
* // Only dispatches for numeric IDs like /users/123
|
|
12
21
|
* }
|
|
13
22
|
*
|
|
14
23
|
* @Get(`/documents/${pattern("uuid", Patterns.UUID)}`)
|
|
15
24
|
* getDocument(@param("uuid") uuid: string) {
|
|
16
|
-
* // Only
|
|
25
|
+
* // Only dispatches for valid UUIDs
|
|
17
26
|
* }
|
|
18
27
|
* ```
|
|
19
28
|
*
|
|
@@ -12,6 +12,19 @@ export interface MicroConfig {
|
|
|
12
12
|
globalPrefix?: string;
|
|
13
13
|
/** Show startup banner (default: true) */
|
|
14
14
|
showBanner?: boolean;
|
|
15
|
+
/** Application environment. Auto-detected from NODE_ENV if not provided. */
|
|
16
|
+
environment?: string;
|
|
17
|
+
/** Studio Agent configuration. Auto-enabled in development when the package is installed. */
|
|
18
|
+
studio?: {
|
|
19
|
+
/** Explicitly enable/disable Studio (default: auto-detect in development) */
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
/** WebSocket port for the Studio Agent (default: 3334) */
|
|
22
|
+
port?: number;
|
|
23
|
+
/** Path to the SQLite database (default: ".studio/studio.db") */
|
|
24
|
+
dbPath?: string;
|
|
25
|
+
/** Service name shown in Studio (default: "expressots-micro") */
|
|
26
|
+
serviceName?: string;
|
|
27
|
+
};
|
|
15
28
|
}
|
|
16
29
|
/**
|
|
17
30
|
* Route handler that can return a value or use res directly
|
|
@@ -43,10 +56,14 @@ export interface MicroApp {
|
|
|
43
56
|
setErrorHandler(handler: express.ErrorRequestHandler): this;
|
|
44
57
|
/** Start listening for requests */
|
|
45
58
|
listen(port: number | string, appInfo?: IConsoleMessage): Promise<void>;
|
|
46
|
-
/** Get the underlying HTTP server (
|
|
47
|
-
getHttpServer(): Server;
|
|
59
|
+
/** Get the underlying HTTP server (null before listen resolves) */
|
|
60
|
+
getHttpServer(): Server | null;
|
|
48
61
|
/** Get the Express app instance (for advanced use) */
|
|
49
62
|
getApp(): express.Application;
|
|
63
|
+
/** Configure Studio integration (call before listen) */
|
|
64
|
+
setStudio(config: NonNullable<MicroConfig["studio"]>): this;
|
|
65
|
+
/** Check if Studio Agent is currently enabled */
|
|
66
|
+
isStudioEnabled(): boolean;
|
|
50
67
|
}
|
|
51
68
|
/**
|
|
52
69
|
* Create a new micro API instance
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { initializeStudio, stopStudio, isStudioEnabled, getStudioAgent, reportStudioRuntimeInfo, } from "./studio-integration.js";
|
|
1
|
+
export { initializeStudio, stopStudio, isStudioEnabled, getStudioAgent, reportStudioRuntimeInfo, rescanStudioRoutes, } from "./studio-integration.js";
|
|
@@ -23,6 +23,7 @@ interface StudioAgentInstance {
|
|
|
23
23
|
providerCount?: number;
|
|
24
24
|
middlewareCount?: number;
|
|
25
25
|
runtimeItems?: StudioRuntimeItems;
|
|
26
|
+
middlewarePreset?: StudioMiddlewarePresetInfo;
|
|
26
27
|
}): void;
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
@@ -45,6 +46,70 @@ export interface StudioRuntimeItems {
|
|
|
45
46
|
priority?: number;
|
|
46
47
|
source?: string;
|
|
47
48
|
}>;
|
|
49
|
+
middleware?: Array<{
|
|
50
|
+
name: string;
|
|
51
|
+
category: string;
|
|
52
|
+
type: "built-in" | "custom";
|
|
53
|
+
order: number;
|
|
54
|
+
path?: string;
|
|
55
|
+
}>;
|
|
56
|
+
/**
|
|
57
|
+
* Controller- and route-scoped middleware bindings, harvested from
|
|
58
|
+
* `ControllerMetadata.middleware` Reflect entries after
|
|
59
|
+
* `app.listen()`. Used by the agent to draw scope-aware
|
|
60
|
+
* "middleware → controller / route" edges on the architecture map.
|
|
61
|
+
*
|
|
62
|
+
* Mirrors `MiddlewareBinding` in `@expressots/studio-agent`.
|
|
63
|
+
*/
|
|
64
|
+
middlewareBindings?: Array<{
|
|
65
|
+
middlewareName: string;
|
|
66
|
+
scope: "controller" | "route";
|
|
67
|
+
controllerName: string;
|
|
68
|
+
controllerMethod?: string;
|
|
69
|
+
httpMethod?: string;
|
|
70
|
+
routePath?: string;
|
|
71
|
+
}>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Middleware preset info forwarded to the Studio Agent. Mirrors
|
|
75
|
+
* `MiddlewarePresetInfo` in `@expressots/studio-agent` so the adapter
|
|
76
|
+
* doesn't need to import from the studio package.
|
|
77
|
+
*/
|
|
78
|
+
export interface StudioMiddlewarePresetInfo {
|
|
79
|
+
name: string;
|
|
80
|
+
hasOverrides: boolean;
|
|
81
|
+
parse?: {
|
|
82
|
+
json?: {
|
|
83
|
+
limit?: string;
|
|
84
|
+
};
|
|
85
|
+
urlencoded?: {
|
|
86
|
+
limit?: string;
|
|
87
|
+
extended?: boolean;
|
|
88
|
+
};
|
|
89
|
+
cookies?: boolean;
|
|
90
|
+
};
|
|
91
|
+
security?: {
|
|
92
|
+
tier?: string;
|
|
93
|
+
helmet?: boolean;
|
|
94
|
+
cors?: {
|
|
95
|
+
origin?: boolean | string;
|
|
96
|
+
credentials?: boolean;
|
|
97
|
+
methods?: Array<string>;
|
|
98
|
+
allowedHeaders?: Array<string>;
|
|
99
|
+
};
|
|
100
|
+
rateLimit?: {
|
|
101
|
+
windowMs?: number;
|
|
102
|
+
max?: number;
|
|
103
|
+
} | false;
|
|
104
|
+
};
|
|
105
|
+
compress?: {
|
|
106
|
+
enabled: boolean;
|
|
107
|
+
level?: number;
|
|
108
|
+
};
|
|
109
|
+
logger?: {
|
|
110
|
+
enabled: boolean;
|
|
111
|
+
implementation?: string;
|
|
112
|
+
};
|
|
48
113
|
}
|
|
49
114
|
interface StudioIntegrationConfig {
|
|
50
115
|
enabled?: boolean;
|
|
@@ -76,7 +141,20 @@ export declare function reportStudioRuntimeInfo(patch: {
|
|
|
76
141
|
providerCount?: number;
|
|
77
142
|
middlewareCount?: number;
|
|
78
143
|
runtimeItems?: StudioRuntimeItems;
|
|
144
|
+
middlewarePreset?: StudioMiddlewarePresetInfo;
|
|
79
145
|
}): void;
|
|
146
|
+
/**
|
|
147
|
+
* Re-trigger the Studio Agent's route discovery. Used by the host
|
|
148
|
+
* after `app.listen()` so that the agent's runtime route scanner sees
|
|
149
|
+
* the fully-populated Express `_router` stack (controllers are bound
|
|
150
|
+
* by `InversifyExpressServer.build()` AFTER `initializeStudio()` runs,
|
|
151
|
+
* so the agent's first scan only catches static-source routes).
|
|
152
|
+
*
|
|
153
|
+
* No-ops when:
|
|
154
|
+
* - Studio isn't enabled, or
|
|
155
|
+
* - the installed agent is too old to expose `scanRoutes()`.
|
|
156
|
+
*/
|
|
157
|
+
export declare function rescanStudioRoutes(): Promise<void>;
|
|
80
158
|
/**
|
|
81
159
|
* Stop the Studio Agent
|
|
82
160
|
*/
|
package/lib/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expressots/adapter-express",
|
|
3
|
-
"version": "4.0.0-preview.
|
|
3
|
+
"version": "4.0.0-preview.3",
|
|
4
4
|
"description": "Expressots - modern, fast, lightweight nodejs web framework (@adapter-express)",
|
|
5
5
|
"author": "",
|
|
6
6
|
"main": "./lib/cjs/index.js",
|
|
@@ -36,6 +36,9 @@
|
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20.18.0"
|
|
41
|
+
},
|
|
39
42
|
"keywords": [
|
|
40
43
|
"expressots",
|
|
41
44
|
"nodejs",
|
|
@@ -54,16 +57,21 @@
|
|
|
54
57
|
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
55
58
|
"build:esm": "node scripts/build-esm.js",
|
|
56
59
|
"release": "release-it",
|
|
60
|
+
"release:prepare": "node scripts/release/prepare-publish.mjs",
|
|
61
|
+
"release:restore": "node scripts/release/restore-package-json.mjs",
|
|
62
|
+
"release:publish": "npm run build && npm run release:prepare && npm publish --tag next --access public && npm run release:restore",
|
|
57
63
|
"prepublish": "npm run build && npm pack",
|
|
58
64
|
"test": "jest",
|
|
59
65
|
"test:watch": "jest --watch",
|
|
60
66
|
"coverage": "jest --coverage",
|
|
61
67
|
"format": "prettier --write \"src/**/*.ts\" --cache",
|
|
62
|
-
"lint": "eslint \"src/**/*.ts\"",
|
|
63
|
-
"lint:fix": "eslint \"src/**/*.ts\" --fix"
|
|
68
|
+
"lint": "eslint \"src/**/*.ts\" --cache --cache-location node_modules/.cache/eslint/",
|
|
69
|
+
"lint:fix": "eslint \"src/**/*.ts\" --fix --cache --cache-location node_modules/.cache/eslint/"
|
|
64
70
|
},
|
|
65
71
|
"dependencies": {
|
|
66
|
-
"
|
|
72
|
+
"@expressots/core": "file:../expressots/expressots-core-4.0.0-preview.3.tgz",
|
|
73
|
+
"@expressots/shared": "file:../shared/expressots-shared-4.0.0-preview.3.tgz",
|
|
74
|
+
"express": "^5.1.0",
|
|
67
75
|
"reflect-metadata": "0.2.2"
|
|
68
76
|
},
|
|
69
77
|
"peerDependencies": {
|
|
@@ -75,14 +83,12 @@
|
|
|
75
83
|
}
|
|
76
84
|
},
|
|
77
85
|
"devDependencies": {
|
|
78
|
-
"@codecov/vite-plugin": "
|
|
86
|
+
"@codecov/vite-plugin": "^2.0.1",
|
|
79
87
|
"@commitlint/cli": "19.2.1",
|
|
80
88
|
"@commitlint/config-conventional": "19.2.2",
|
|
81
|
-
"@expressots/core": "file:../expressots/expressots-core-4.0.0-preview.1.tgz",
|
|
82
|
-
"@expressots/shared": "file:../shared/expressots-shared-4.0.0-preview.1.tgz",
|
|
83
89
|
"@expressots/studio-agent": "file:../expressots-studio/packages/studio-agent",
|
|
84
|
-
"@release-it/conventional-changelog": "
|
|
85
|
-
"@types/express": "
|
|
90
|
+
"@release-it/conventional-changelog": "^11.0.0",
|
|
91
|
+
"@types/express": "^5.0.0",
|
|
86
92
|
"@types/jest": "29.5.14",
|
|
87
93
|
"@types/node": "20.14.10",
|
|
88
94
|
"@typescript-eslint/eslint-plugin": "7.16.1",
|
|
@@ -91,11 +97,19 @@
|
|
|
91
97
|
"eslint-config-prettier": "9.1.0",
|
|
92
98
|
"husky": "9.1.1",
|
|
93
99
|
"jest": "29.7.0",
|
|
100
|
+
"lint-staged": "^15.2.10",
|
|
94
101
|
"prettier": "3.3.3",
|
|
95
|
-
"release-it": "
|
|
102
|
+
"release-it": "^20.0.1",
|
|
96
103
|
"ts-jest": "29.2.5",
|
|
104
|
+
"ts-node": "^10.9.2",
|
|
97
105
|
"typescript": "5.5.3"
|
|
98
106
|
},
|
|
107
|
+
"lint-staged": {
|
|
108
|
+
"src/**/*.ts": [
|
|
109
|
+
"eslint --cache --cache-location node_modules/.cache/eslint/ --fix",
|
|
110
|
+
"prettier --write --cache"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
99
113
|
"release-it": {
|
|
100
114
|
"git": {
|
|
101
115
|
"commitMessage": "chore(release): ${version}"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expressots/adapter-express",
|
|
3
|
-
"version": "4.0.0-preview.
|
|
3
|
+
"version": "4.0.0-preview.3",
|
|
4
4
|
"description": "Expressots - modern, fast, lightweight nodejs web framework (@adapter-express)",
|
|
5
5
|
"author": "",
|
|
6
6
|
"main": "./lib/cjs/index.js",
|
|
@@ -36,6 +36,9 @@
|
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20.18.0"
|
|
41
|
+
},
|
|
39
42
|
"keywords": [
|
|
40
43
|
"expressots",
|
|
41
44
|
"nodejs",
|
|
@@ -54,16 +57,21 @@
|
|
|
54
57
|
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
55
58
|
"build:esm": "node scripts/build-esm.js",
|
|
56
59
|
"release": "release-it",
|
|
60
|
+
"release:prepare": "node scripts/release/prepare-publish.mjs",
|
|
61
|
+
"release:restore": "node scripts/release/restore-package-json.mjs",
|
|
62
|
+
"release:publish": "npm run build && npm run release:prepare && npm publish --tag next --access public && npm run release:restore",
|
|
57
63
|
"prepublish": "npm run build && npm pack",
|
|
58
64
|
"test": "jest",
|
|
59
65
|
"test:watch": "jest --watch",
|
|
60
66
|
"coverage": "jest --coverage",
|
|
61
67
|
"format": "prettier --write \"src/**/*.ts\" --cache",
|
|
62
|
-
"lint": "eslint \"src/**/*.ts\"",
|
|
63
|
-
"lint:fix": "eslint \"src/**/*.ts\" --fix"
|
|
68
|
+
"lint": "eslint \"src/**/*.ts\" --cache --cache-location node_modules/.cache/eslint/",
|
|
69
|
+
"lint:fix": "eslint \"src/**/*.ts\" --fix --cache --cache-location node_modules/.cache/eslint/"
|
|
64
70
|
},
|
|
65
71
|
"dependencies": {
|
|
66
|
-
"
|
|
72
|
+
"@expressots/core": "^4.0.0-preview.3",
|
|
73
|
+
"@expressots/shared": "^4.0.0-preview.3",
|
|
74
|
+
"express": "^5.1.0",
|
|
67
75
|
"reflect-metadata": "0.2.2"
|
|
68
76
|
},
|
|
69
77
|
"peerDependencies": {
|
|
@@ -75,14 +83,12 @@
|
|
|
75
83
|
}
|
|
76
84
|
},
|
|
77
85
|
"devDependencies": {
|
|
78
|
-
"@codecov/vite-plugin": "
|
|
86
|
+
"@codecov/vite-plugin": "^2.0.1",
|
|
79
87
|
"@commitlint/cli": "19.2.1",
|
|
80
88
|
"@commitlint/config-conventional": "19.2.2",
|
|
81
|
-
"@expressots/
|
|
82
|
-
"@
|
|
83
|
-
"@
|
|
84
|
-
"@release-it/conventional-changelog": "8.0.1",
|
|
85
|
-
"@types/express": "4.17.21",
|
|
89
|
+
"@expressots/studio-agent": "^4.0.0-preview.3",
|
|
90
|
+
"@release-it/conventional-changelog": "^11.0.0",
|
|
91
|
+
"@types/express": "^5.0.0",
|
|
86
92
|
"@types/jest": "29.5.14",
|
|
87
93
|
"@types/node": "20.14.10",
|
|
88
94
|
"@typescript-eslint/eslint-plugin": "7.16.1",
|
|
@@ -91,11 +97,19 @@
|
|
|
91
97
|
"eslint-config-prettier": "9.1.0",
|
|
92
98
|
"husky": "9.1.1",
|
|
93
99
|
"jest": "29.7.0",
|
|
100
|
+
"lint-staged": "^15.2.10",
|
|
94
101
|
"prettier": "3.3.3",
|
|
95
|
-
"release-it": "
|
|
102
|
+
"release-it": "^20.0.1",
|
|
96
103
|
"ts-jest": "29.2.5",
|
|
104
|
+
"ts-node": "^10.9.2",
|
|
97
105
|
"typescript": "5.5.3"
|
|
98
106
|
},
|
|
107
|
+
"lint-staged": {
|
|
108
|
+
"src/**/*.ts": [
|
|
109
|
+
"eslint --cache --cache-location node_modules/.cache/eslint/ --fix",
|
|
110
|
+
"prettier --write --cache"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
99
113
|
"release-it": {
|
|
100
114
|
"git": {
|
|
101
115
|
"commitMessage": "chore(release): ${version}"
|