@fluojs/runtime 1.1.8 → 3.0.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.
- package/README.ko.md +292 -21
- package/README.md +292 -21
- package/dist/abort.d.ts +6 -1
- package/dist/abort.d.ts.map +1 -1
- package/dist/abort.js +18 -2
- package/dist/adapters/internal-request-response-factory.d.ts +1 -1
- package/dist/adapters/internal-request-response-factory.d.ts.map +1 -1
- package/dist/adapters/internal-request-response-factory.js +1 -1
- package/dist/adapters/request-response-factory.d.ts +20 -1
- package/dist/adapters/request-response-factory.d.ts.map +1 -1
- package/dist/adapters/request-response-factory.js +75 -22
- package/dist/bootstrap.d.ts +3 -9
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +181 -76
- package/dist/devtools/contracts.d.ts +3 -135
- package/dist/devtools/contracts.d.ts.map +1 -1
- package/dist/devtools/index.d.ts +2 -3
- package/dist/devtools/index.d.ts.map +1 -1
- package/dist/devtools/index.js +1 -2
- package/dist/devtools/snapshot.d.ts +1 -1
- package/dist/devtools/snapshot.d.ts.map +1 -1
- package/dist/devtools/snapshot.js +18 -17
- package/dist/devtools/studio-request-observer.d.ts +14 -0
- package/dist/devtools/studio-request-observer.d.ts.map +1 -0
- package/dist/devtools/studio-request-observer.js +112 -0
- package/dist/devtools/studio-runtime-config.d.ts +25 -0
- package/dist/devtools/studio-runtime-config.d.ts.map +1 -0
- package/dist/devtools/studio-runtime-config.js +59 -0
- package/dist/devtools/studio-runtime.d.ts +12 -18
- package/dist/devtools/studio-runtime.d.ts.map +1 -1
- package/dist/devtools/studio-runtime.js +25 -169
- package/dist/errors.d.ts +26 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +38 -0
- package/dist/health/health.d.ts +18 -4
- package/dist/health/health.d.ts.map +1 -1
- package/dist/health/health.js +5 -2
- package/dist/http-adapter-shared.d.ts +1 -1
- package/dist/http-adapter-shared.d.ts.map +1 -1
- package/dist/http-adapter-shared.js +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/internal/route-inspection-metadata.d.ts +30 -0
- package/dist/internal/route-inspection-metadata.d.ts.map +1 -0
- package/dist/internal/route-inspection-metadata.js +68 -0
- package/dist/internal.d.ts +6 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +4 -1
- package/dist/module-definition.d.ts +10 -0
- package/dist/module-definition.d.ts.map +1 -0
- package/dist/module-definition.js +12 -0
- package/dist/module-graph.d.ts +42 -1
- package/dist/module-graph.d.ts.map +1 -1
- package/dist/module-graph.js +112 -16
- package/dist/multipart.d.ts +68 -4
- package/dist/multipart.d.ts.map +1 -1
- package/dist/multipart.js +666 -103
- package/dist/platform-component-registry.d.ts +9 -0
- package/dist/platform-component-registry.d.ts.map +1 -0
- package/dist/platform-component-registry.js +70 -0
- package/dist/platform-diagnostic-retention.d.ts +13 -0
- package/dist/platform-diagnostic-retention.d.ts.map +1 -0
- package/dist/platform-diagnostic-retention.js +43 -0
- package/dist/platform-shell-probe-reports.d.ts +8 -0
- package/dist/platform-shell-probe-reports.d.ts.map +1 -0
- package/dist/platform-shell-probe-reports.js +95 -0
- package/dist/platform-shell-probes.d.ts +16 -0
- package/dist/platform-shell-probes.d.ts.map +1 -0
- package/dist/platform-shell-probes.js +141 -0
- package/dist/platform-shell.d.ts +7 -13
- package/dist/platform-shell.d.ts.map +1 -1
- package/dist/platform-shell.js +58 -310
- package/dist/retryable-shutdown.d.ts +7 -0
- package/dist/retryable-shutdown.d.ts.map +1 -0
- package/dist/retryable-shutdown.js +20 -0
- package/dist/route-inspection.d.ts +56 -0
- package/dist/route-inspection.d.ts.map +1 -0
- package/dist/route-inspection.js +80 -0
- package/dist/tokens.d.ts +6 -4
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +5 -0
- package/dist/types.d.ts +53 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/web.d.ts +22 -9
- package/dist/web.d.ts.map +1 -1
- package/dist/web.js +80 -34
- package/package.json +13 -26
- package/dist/internal-node.d.ts +0 -2
- package/dist/internal-node.d.ts.map +0 -1
- package/dist/internal-node.js +0 -1
- package/dist/logging/json-logger.d.ts +0 -8
- package/dist/logging/json-logger.d.ts.map +0 -1
- package/dist/logging/json-logger.js +0 -45
- package/dist/logging/logger.d.ts +0 -37
- package/dist/logging/logger.d.ts.map +0 -1
- package/dist/logging/logger.js +0 -103
- package/dist/node/internal-node-compression.d.ts +0 -22
- package/dist/node/internal-node-compression.d.ts.map +0 -1
- package/dist/node/internal-node-compression.js +0 -84
- package/dist/node/internal-node-request.d.ts +0 -162
- package/dist/node/internal-node-request.d.ts.map +0 -1
- package/dist/node/internal-node-request.js +0 -475
- package/dist/node/internal-node-response.d.ts +0 -28
- package/dist/node/internal-node-response.d.ts.map +0 -1
- package/dist/node/internal-node-response.js +0 -205
- package/dist/node/internal-node-shutdown.d.ts +0 -34
- package/dist/node/internal-node-shutdown.d.ts.map +0 -1
- package/dist/node/internal-node-shutdown.js +0 -83
- package/dist/node/internal-node.d.ts +0 -117
- package/dist/node/internal-node.d.ts.map +0 -1
- package/dist/node/internal-node.js +0 -283
- package/dist/node/node-compression.d.ts +0 -2
- package/dist/node/node-compression.d.ts.map +0 -1
- package/dist/node/node-compression.js +0 -1
- package/dist/node/node-request.d.ts +0 -2
- package/dist/node/node-request.d.ts.map +0 -1
- package/dist/node/node-request.js +0 -1
- package/dist/node/node-response.d.ts +0 -2
- package/dist/node/node-response.d.ts.map +0 -1
- package/dist/node/node-response.js +0 -1
- package/dist/node/node-shutdown.d.ts +0 -2
- package/dist/node/node-shutdown.d.ts.map +0 -1
- package/dist/node/node-shutdown.js +0 -1
- package/dist/node/node.d.ts +0 -2
- package/dist/node/node.d.ts.map +0 -1
- package/dist/node/node.js +0 -1
- package/dist/node.d.ts +0 -5
- package/dist/node.d.ts.map +0 -1
- package/dist/node.js +0 -3
|
@@ -2,6 +2,55 @@
|
|
|
2
2
|
|
|
3
3
|
/** Options for dispatching one raw platform request through a request/response factory. */
|
|
4
4
|
|
|
5
|
+
/** An in-progress factory dispatch with its response shell available immediately. */
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Starts one raw platform request dispatch while exposing its mutable response shell.
|
|
9
|
+
*
|
|
10
|
+
* @param options - Factory, dispatcher, and raw platform request/response values for one dispatch.
|
|
11
|
+
* @returns The response shell and its independently settling dispatch lifecycle.
|
|
12
|
+
*/
|
|
13
|
+
export function startDispatchWithRequestResponseFactory({
|
|
14
|
+
dispatcher,
|
|
15
|
+
dispatcherNotReadyMessage,
|
|
16
|
+
factory,
|
|
17
|
+
rawRequest,
|
|
18
|
+
rawResponse
|
|
19
|
+
}) {
|
|
20
|
+
const response = factory.createResponse(rawResponse, rawRequest);
|
|
21
|
+
const signal = factory.createRequestSignal(rawResponse);
|
|
22
|
+
let frameworkRequest;
|
|
23
|
+
const completion = (async () => {
|
|
24
|
+
try {
|
|
25
|
+
frameworkRequest = await factory.createRequest(rawRequest, signal);
|
|
26
|
+
const materializeRequest = factory.materializeRequest;
|
|
27
|
+
if (materializeRequest) {
|
|
28
|
+
await materializeRequest(frameworkRequest);
|
|
29
|
+
}
|
|
30
|
+
if (!dispatcher) {
|
|
31
|
+
throw new Error(dispatcherNotReadyMessage);
|
|
32
|
+
}
|
|
33
|
+
await dispatcher.dispatch(frameworkRequest, response);
|
|
34
|
+
if (!response.committed) {
|
|
35
|
+
await response.send(undefined);
|
|
36
|
+
}
|
|
37
|
+
return response;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (signal.aborted || response.committed) {
|
|
40
|
+
return response;
|
|
41
|
+
}
|
|
42
|
+
await factory.writeErrorResponse(error, response, factory.resolveRequestId(rawRequest));
|
|
43
|
+
return response;
|
|
44
|
+
} finally {
|
|
45
|
+
await finalizeRouteOwnedMultipartBody(frameworkRequest);
|
|
46
|
+
}
|
|
47
|
+
})();
|
|
48
|
+
return {
|
|
49
|
+
completion,
|
|
50
|
+
response
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
5
54
|
/**
|
|
6
55
|
* Dispatches one raw platform request through the shared request/response factory lifecycle.
|
|
7
56
|
*
|
|
@@ -15,27 +64,31 @@ export async function dispatchWithRequestResponseFactory({
|
|
|
15
64
|
rawRequest,
|
|
16
65
|
rawResponse
|
|
17
66
|
}) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
67
|
+
return await startDispatchWithRequestResponseFactory({
|
|
68
|
+
dispatcher,
|
|
69
|
+
dispatcherNotReadyMessage,
|
|
70
|
+
factory,
|
|
71
|
+
rawRequest,
|
|
72
|
+
rawResponse
|
|
73
|
+
}).completion;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Finalizes a route-owned multipart body iterator after dispatch completes.
|
|
78
|
+
*
|
|
79
|
+
* @param request Framework request whose multipart body may need finalization.
|
|
80
|
+
* @returns A promise that resolves after the iterator has been returned when applicable.
|
|
81
|
+
*/
|
|
82
|
+
export async function finalizeRouteOwnedMultipartBody(request) {
|
|
83
|
+
const body = request?.body;
|
|
84
|
+
if (!isAsyncIterable(body)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const iterator = body[Symbol.asyncIterator]();
|
|
88
|
+
if (iterator.return) {
|
|
89
|
+
await iterator.return();
|
|
40
90
|
}
|
|
91
|
+
}
|
|
92
|
+
function isAsyncIterable(value) {
|
|
93
|
+
return value !== null && (typeof value === 'object' || typeof value === 'function') && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === 'function';
|
|
41
94
|
}
|
package/dist/bootstrap.d.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* @param moduleType Module class that should receive runtime module metadata.
|
|
6
|
-
* @param definition Module definition contract (`imports`, `providers`, `controllers`, `exports`, etc.).
|
|
7
|
-
* @returns The same `moduleType` reference for fluent helper composition.
|
|
8
|
-
*/
|
|
9
|
-
export declare function defineModule<T extends ModuleType>(moduleType: T, definition: ModuleDefinition): T;
|
|
1
|
+
import { defineModule } from './module-definition.js';
|
|
2
|
+
import type { Application, ApplicationContext, BootstrapApplicationOptions, BootstrapModuleOptions, BootstrapResult, CreateApplicationContextOptions, CreateApplicationOptions, CreateMicroserviceOptions, MicroserviceApplication, ModuleType } from './types.js';
|
|
3
|
+
export { defineModule };
|
|
10
4
|
/**
|
|
11
5
|
* Bootstraps the module graph and returns the root container baseline.
|
|
12
6
|
*
|
package/dist/bootstrap.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAmBtD,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAGlB,2BAA2B,EAE3B,sBAAsB,EACtB,eAAe,EAEf,+BAA+B,EAC/B,wBAAwB,EACxB,yBAAyB,EAEzB,uBAAuB,EAEvB,UAAU,EAMX,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,CAAC;AAuexB;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,sBAA2B,GAAG,eAAe,CA+B7G;AA+8BD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,WAAW,CAAC,CAiJrG;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB;;;;;;;OAOG;WACU,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,WAAW,CAAC;IAOzG;;;;;;;OAOG;WACU,wBAAwB,CACnC,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,kBAAkB,CAAC;IAwH9B;;;;;;;;OAQG;WACU,kBAAkB,CAC7B,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,uBAAuB,CAAC;CA+BpC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,oBAAc,CAAC"}
|
package/dist/bootstrap.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import { Container } from '@fluojs/di';
|
|
2
1
|
import { InvariantError } from '@fluojs/core';
|
|
3
|
-
import {
|
|
2
|
+
import { Container } from '@fluojs/di';
|
|
3
|
+
import { resolveMultiContribution } from '@fluojs/di/internal';
|
|
4
|
+
import { createDispatcher, createHandlerMapping } from '@fluojs/http/portable';
|
|
5
|
+
import { applyStudioDevtoolsApplicationOptions, applyStudioDevtoolsContextOptions, createStudioDevtoolsRuntimeFromConfig, publishStudioBootstrapSnapshot } from './devtools/studio-runtime.js';
|
|
4
6
|
import { DuplicateProviderError } from './errors.js';
|
|
5
7
|
import { createBootstrapTimingDiagnostics } from './health/diagnostics.js';
|
|
6
|
-
import {
|
|
7
|
-
import { defineRuntimeModuleMetadata, getRuntimeClassDiMetadata } from './internal/core-metadata.js';
|
|
8
|
+
import { getRuntimeClassDiMetadata } from './internal/core-metadata.js';
|
|
8
9
|
import { RuntimeDefaultBinder } from './internal/http-runtime.js';
|
|
9
10
|
import { createDefaultApplicationLogger } from './logging/default-logger.js';
|
|
11
|
+
import { defineModule } from './module-definition.js';
|
|
10
12
|
import { compileModuleGraph, providerToken } from './module-graph.js';
|
|
11
13
|
import { createRuntimePlatformShell } from './platform-shell.js';
|
|
12
|
-
import {
|
|
14
|
+
import { createLifecycleCloseError, createRetryableShutdownState } from './retryable-shutdown.js';
|
|
15
|
+
import { APPLICATION_LOGGER, BOOTSTRAP_PROVIDER_TOKENS, BOOTSTRAP_READY_SIGNAL, COMPILED_MODULES, HTTP_APPLICATION_ADAPTER, PLATFORM_SHELL, RUNTIME_CLEANUP_REGISTRATION, RUNTIME_CONTAINER } from './tokens.js';
|
|
16
|
+
export { defineModule };
|
|
13
17
|
const DEFAULT_MICROSERVICE_TOKEN = Symbol.for('fluo.microservices.service');
|
|
14
18
|
const runtimePerformance = globalThis.performance;
|
|
15
19
|
async function runExceptionFilters(filters, error, request, response, requestId) {
|
|
@@ -46,20 +50,11 @@ async function disposeContainer(container) {
|
|
|
46
50
|
}
|
|
47
51
|
await container.dispose();
|
|
48
52
|
}
|
|
49
|
-
function toError(error) {
|
|
50
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
51
|
-
}
|
|
52
|
-
function createLifecycleCloseError(errors) {
|
|
53
|
-
if (errors.length === 1) {
|
|
54
|
-
return toError(errors[0]);
|
|
55
|
-
}
|
|
56
|
-
return new AggregateError(errors, 'Application close failed for one or more shutdown steps.');
|
|
57
|
-
}
|
|
58
53
|
async function runCleanupCallbacks(cleanups) {
|
|
59
54
|
const errors = [];
|
|
60
55
|
for (const cleanup of cleanups) {
|
|
61
56
|
try {
|
|
62
|
-
cleanup();
|
|
57
|
+
await cleanup();
|
|
63
58
|
} catch (error) {
|
|
64
59
|
errors.push(error);
|
|
65
60
|
}
|
|
@@ -95,24 +90,41 @@ function createBootstrapReadySignal() {
|
|
|
95
90
|
}
|
|
96
91
|
async function closeRuntimeResources(options) {
|
|
97
92
|
const errors = [];
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
93
|
+
if (!options.shutdownState.isComplete('readiness')) {
|
|
94
|
+
resetReadinessState(options.modules);
|
|
95
|
+
options.shutdownState.complete('readiness');
|
|
96
|
+
}
|
|
97
|
+
if (!options.shutdownState.isComplete('runtime-cleanup')) {
|
|
98
|
+
const cleanupErrors = await runCleanupCallbacks(options.runtimeCleanup);
|
|
99
|
+
if (cleanupErrors.length === 0) {
|
|
100
|
+
options.shutdownState.complete('runtime-cleanup');
|
|
101
|
+
} else {
|
|
102
|
+
errors.push(...cleanupErrors);
|
|
103
|
+
}
|
|
104
104
|
}
|
|
105
|
-
if (options.
|
|
105
|
+
if (!options.shutdownState.isComplete('hooks')) {
|
|
106
106
|
try {
|
|
107
|
-
await options.
|
|
107
|
+
await runShutdownHooks(options.lifecycleInstances, options.signal);
|
|
108
|
+
options.shutdownState.complete('hooks');
|
|
108
109
|
} catch (error) {
|
|
109
110
|
errors.push(error);
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
if (!options.shutdownState.isComplete('adapter')) {
|
|
114
|
+
try {
|
|
115
|
+
await options.adapter?.close(options.signal);
|
|
116
|
+
options.shutdownState.complete('adapter');
|
|
117
|
+
} catch (error) {
|
|
118
|
+
errors.push(error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!options.shutdownState.isComplete('container')) {
|
|
122
|
+
try {
|
|
123
|
+
await disposeContainer(options.container);
|
|
124
|
+
options.shutdownState.complete('container');
|
|
125
|
+
} catch (error) {
|
|
126
|
+
errors.push(error);
|
|
127
|
+
}
|
|
116
128
|
}
|
|
117
129
|
if (errors.length > 0) {
|
|
118
130
|
throw createLifecycleCloseError(errors);
|
|
@@ -335,18 +347,6 @@ function registerModuleMiddleware(container, modules) {
|
|
|
335
347
|
}
|
|
336
348
|
}
|
|
337
349
|
|
|
338
|
-
/**
|
|
339
|
-
* Associates module metadata with a module type.
|
|
340
|
-
*
|
|
341
|
-
* @param moduleType Module class that should receive runtime module metadata.
|
|
342
|
-
* @param definition Module definition contract (`imports`, `providers`, `controllers`, `exports`, etc.).
|
|
343
|
-
* @returns The same `moduleType` reference for fluent helper composition.
|
|
344
|
-
*/
|
|
345
|
-
export function defineModule(moduleType, definition) {
|
|
346
|
-
defineRuntimeModuleMetadata(moduleType, definition);
|
|
347
|
-
return moduleType;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
350
|
/**
|
|
351
351
|
* Bootstraps the module graph and returns the root container baseline.
|
|
352
352
|
*
|
|
@@ -384,8 +384,11 @@ export function bootstrapModule(rootModule, options = {}) {
|
|
|
384
384
|
class FluoApplication {
|
|
385
385
|
applicationState = 'bootstrapped';
|
|
386
386
|
closed = false;
|
|
387
|
+
closeStarted = false;
|
|
387
388
|
closingPromise;
|
|
388
389
|
listenPromise;
|
|
390
|
+
connectedMicroservicesClosed = false;
|
|
391
|
+
runtimeShutdownState = createRetryableShutdownState();
|
|
389
392
|
contextResolutionCache = new Map();
|
|
390
393
|
lifecycleInstances;
|
|
391
394
|
connectedMicroservices = [];
|
|
@@ -411,7 +414,10 @@ class FluoApplication {
|
|
|
411
414
|
if (this.closed) {
|
|
412
415
|
return this.container.resolve(token);
|
|
413
416
|
}
|
|
414
|
-
|
|
417
|
+
this.assertProviderResolutionAllowed();
|
|
418
|
+
const resolved = await resolveContextToken(this.container, token, this.contextCacheableTokens, this.contextResolutionCache);
|
|
419
|
+
this.assertProviderResolutionAllowed();
|
|
420
|
+
return resolved;
|
|
415
421
|
}
|
|
416
422
|
|
|
417
423
|
/**
|
|
@@ -424,8 +430,10 @@ class FluoApplication {
|
|
|
424
430
|
await this.platformShell.assertCriticalReadiness();
|
|
425
431
|
}
|
|
426
432
|
async connectMicroservice(options = {}) {
|
|
433
|
+
this.assertApplicationOperationAllowed('connect a microservice');
|
|
427
434
|
const microserviceToken = options.microserviceToken ?? DEFAULT_MICROSERVICE_TOKEN;
|
|
428
435
|
const runtime = await this.container.resolve(microserviceToken);
|
|
436
|
+
this.assertApplicationOperationAllowed('connect a microservice');
|
|
429
437
|
if (!isMicroserviceRuntime(runtime)) {
|
|
430
438
|
throw new InvariantError('Resolved microservice token does not implement listen().');
|
|
431
439
|
}
|
|
@@ -434,9 +442,11 @@ class FluoApplication {
|
|
|
434
442
|
return microservice;
|
|
435
443
|
}
|
|
436
444
|
async startAllMicroservices() {
|
|
445
|
+
this.assertApplicationOperationAllowed('start microservices');
|
|
437
446
|
const startedMicroservices = [];
|
|
438
447
|
for (const microservice of this.connectedMicroservices) {
|
|
439
448
|
try {
|
|
449
|
+
this.assertApplicationOperationAllowed('start microservices');
|
|
440
450
|
await microservice.listen();
|
|
441
451
|
startedMicroservices.push(microservice);
|
|
442
452
|
} catch (error) {
|
|
@@ -467,12 +477,22 @@ class FluoApplication {
|
|
|
467
477
|
throw createLifecycleCloseError(errors);
|
|
468
478
|
}
|
|
469
479
|
}
|
|
480
|
+
assertApplicationOperationAllowed(operation) {
|
|
481
|
+
if (this.closeStarted) {
|
|
482
|
+
throw new InvariantError(`Application cannot ${operation} after shutdown has started.`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
assertProviderResolutionAllowed() {
|
|
486
|
+
if (this.closeStarted) {
|
|
487
|
+
throw new InvariantError('Application cannot resolve providers after shutdown has started.');
|
|
488
|
+
}
|
|
489
|
+
}
|
|
470
490
|
|
|
471
491
|
/**
|
|
472
492
|
* 준비 검사를 통과한 뒤 어댑터에 바인딩을 위임하고 상태를 `ready`로 전이한다.
|
|
473
493
|
*/
|
|
474
494
|
async listen() {
|
|
475
|
-
if (this.
|
|
495
|
+
if (this.closeStarted) {
|
|
476
496
|
throw new InvariantError('Application cannot listen after it has been closed.');
|
|
477
497
|
}
|
|
478
498
|
if (this.applicationState === 'ready') {
|
|
@@ -490,7 +510,7 @@ class FluoApplication {
|
|
|
490
510
|
}
|
|
491
511
|
}
|
|
492
512
|
async startListening() {
|
|
493
|
-
if (this.
|
|
513
|
+
if (this.closeStarted) {
|
|
494
514
|
throw new InvariantError('Application cannot listen after it has been closed.');
|
|
495
515
|
}
|
|
496
516
|
if (!this.hasHttpAdapter) {
|
|
@@ -503,13 +523,14 @@ class FluoApplication {
|
|
|
503
523
|
this.logger.error('Failed to start the HTTP adapter.', error, 'FluoApplication');
|
|
504
524
|
throw error;
|
|
505
525
|
}
|
|
506
|
-
if (this.
|
|
526
|
+
if (this.closeStarted) {
|
|
507
527
|
throw new InvariantError('Application startup was interrupted by shutdown.');
|
|
508
528
|
}
|
|
509
529
|
this.applicationState = 'ready';
|
|
510
530
|
this.logger.log('fluo application successfully started.', 'FluoApplication');
|
|
511
531
|
}
|
|
512
532
|
dispatch = async (...args) => {
|
|
533
|
+
this.assertApplicationOperationAllowed('dispatch');
|
|
513
534
|
await this.dispatcher.dispatch(...args);
|
|
514
535
|
};
|
|
515
536
|
|
|
@@ -524,6 +545,7 @@ class FluoApplication {
|
|
|
524
545
|
await this.closingPromise;
|
|
525
546
|
return;
|
|
526
547
|
}
|
|
548
|
+
this.closeStarted = true;
|
|
527
549
|
this.closingPromise = (async () => {
|
|
528
550
|
const errors = [];
|
|
529
551
|
if (this.listenPromise) {
|
|
@@ -531,10 +553,13 @@ class FluoApplication {
|
|
|
531
553
|
await this.listenPromise;
|
|
532
554
|
} catch {}
|
|
533
555
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
556
|
+
if (!this.connectedMicroservicesClosed) {
|
|
557
|
+
try {
|
|
558
|
+
await this.closeConnectedMicroservices(signal);
|
|
559
|
+
this.connectedMicroservicesClosed = true;
|
|
560
|
+
} catch (error) {
|
|
561
|
+
errors.push(error);
|
|
562
|
+
}
|
|
538
563
|
}
|
|
539
564
|
try {
|
|
540
565
|
await closeRuntimeResources({
|
|
@@ -543,6 +568,7 @@ class FluoApplication {
|
|
|
543
568
|
lifecycleInstances: this.lifecycleInstances,
|
|
544
569
|
modules: this.modules,
|
|
545
570
|
runtimeCleanup: this.runtimeCleanup,
|
|
571
|
+
shutdownState: this.runtimeShutdownState,
|
|
546
572
|
signal
|
|
547
573
|
});
|
|
548
574
|
} catch (error) {
|
|
@@ -564,8 +590,10 @@ class FluoApplication {
|
|
|
564
590
|
}
|
|
565
591
|
class FluoApplicationContext {
|
|
566
592
|
closed = false;
|
|
593
|
+
closeStarted = false;
|
|
567
594
|
closingPromise;
|
|
568
595
|
contextResolutionCache = new Map();
|
|
596
|
+
runtimeShutdownState = createRetryableShutdownState();
|
|
569
597
|
constructor(container, modules, rootModule, bootstrapTiming, lifecycleInstances, runtimeCleanup, contextCacheableTokens) {
|
|
570
598
|
this.container = container;
|
|
571
599
|
this.modules = modules;
|
|
@@ -580,7 +608,15 @@ class FluoApplicationContext {
|
|
|
580
608
|
if (this.closed) {
|
|
581
609
|
return this.container.resolve(token);
|
|
582
610
|
}
|
|
583
|
-
|
|
611
|
+
this.assertProviderResolutionAllowed();
|
|
612
|
+
const resolved = await resolveContextToken(this.container, token, this.contextCacheableTokens, this.contextResolutionCache);
|
|
613
|
+
this.assertProviderResolutionAllowed();
|
|
614
|
+
return resolved;
|
|
615
|
+
}
|
|
616
|
+
assertProviderResolutionAllowed() {
|
|
617
|
+
if (this.closeStarted) {
|
|
618
|
+
throw new InvariantError('Application context cannot resolve providers after shutdown has started.');
|
|
619
|
+
}
|
|
584
620
|
}
|
|
585
621
|
async close(signal) {
|
|
586
622
|
if (this.closed) {
|
|
@@ -590,12 +626,14 @@ class FluoApplicationContext {
|
|
|
590
626
|
await this.closingPromise;
|
|
591
627
|
return;
|
|
592
628
|
}
|
|
629
|
+
this.closeStarted = true;
|
|
593
630
|
this.closingPromise = (async () => {
|
|
594
631
|
await closeRuntimeResources({
|
|
595
632
|
container: this.container,
|
|
596
633
|
lifecycleInstances: this.lifecycleInstances,
|
|
597
634
|
modules: this.modules,
|
|
598
635
|
runtimeCleanup: this.runtimeCleanup,
|
|
636
|
+
shutdownState: this.runtimeShutdownState,
|
|
599
637
|
signal
|
|
600
638
|
});
|
|
601
639
|
this.closed = true;
|
|
@@ -610,6 +648,7 @@ class FluoApplicationContext {
|
|
|
610
648
|
}
|
|
611
649
|
class FluoMicroserviceApplication {
|
|
612
650
|
closed = false;
|
|
651
|
+
closeStarted = false;
|
|
613
652
|
closingPromise;
|
|
614
653
|
listenPromise;
|
|
615
654
|
microserviceState = 'bootstrapped';
|
|
@@ -635,7 +674,7 @@ class FluoMicroserviceApplication {
|
|
|
635
674
|
return this.context.get(token);
|
|
636
675
|
}
|
|
637
676
|
async listen() {
|
|
638
|
-
if (this.
|
|
677
|
+
if (this.closeStarted) {
|
|
639
678
|
throw new InvariantError('Microservice cannot listen after it has been closed.');
|
|
640
679
|
}
|
|
641
680
|
if (this.microserviceState === 'ready') {
|
|
@@ -653,23 +692,25 @@ class FluoMicroserviceApplication {
|
|
|
653
692
|
}
|
|
654
693
|
}
|
|
655
694
|
async startListening() {
|
|
656
|
-
if (this.
|
|
695
|
+
if (this.closeStarted) {
|
|
657
696
|
throw new InvariantError('Microservice cannot listen after it has been closed.');
|
|
658
697
|
}
|
|
659
698
|
await this.runtime.listen();
|
|
660
|
-
if (this.
|
|
699
|
+
if (this.closeStarted) {
|
|
661
700
|
throw new InvariantError('Microservice startup was interrupted by shutdown.');
|
|
662
701
|
}
|
|
663
702
|
this.microserviceState = 'ready';
|
|
664
703
|
this.logger.log('fluo microservice successfully started.', 'FluoFactory');
|
|
665
704
|
}
|
|
666
705
|
async send(pattern, payload, signal) {
|
|
706
|
+
this.assertTransportIngressOpen('send');
|
|
667
707
|
if (!this.runtime.send) {
|
|
668
708
|
throw new InvariantError('Resolved microservice runtime does not implement send().');
|
|
669
709
|
}
|
|
670
710
|
return await this.runtime.send(pattern, payload, signal);
|
|
671
711
|
}
|
|
672
712
|
async emit(pattern, payload) {
|
|
713
|
+
this.assertTransportIngressOpen('emit');
|
|
673
714
|
if (!this.runtime.emit) {
|
|
674
715
|
throw new InvariantError('Resolved microservice runtime does not implement emit().');
|
|
675
716
|
}
|
|
@@ -683,7 +724,15 @@ class FluoMicroserviceApplication {
|
|
|
683
724
|
await this.closingPromise;
|
|
684
725
|
return;
|
|
685
726
|
}
|
|
686
|
-
this.
|
|
727
|
+
this.closeStarted = true;
|
|
728
|
+
let resolveClose = () => undefined;
|
|
729
|
+
let rejectClose = () => undefined;
|
|
730
|
+
this.closingPromise = new Promise((resolve, reject) => {
|
|
731
|
+
resolveClose = resolve;
|
|
732
|
+
rejectClose = reject;
|
|
733
|
+
});
|
|
734
|
+
void (async () => {
|
|
735
|
+
this.runtime.markShutdownStarted?.();
|
|
687
736
|
if (this.listenPromise) {
|
|
688
737
|
try {
|
|
689
738
|
await this.listenPromise;
|
|
@@ -709,12 +758,12 @@ class FluoMicroserviceApplication {
|
|
|
709
758
|
}
|
|
710
759
|
this.closed = true;
|
|
711
760
|
this.microserviceState = 'closed';
|
|
712
|
-
})();
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
throw
|
|
761
|
+
})().then(resolveClose, rejectClose);
|
|
762
|
+
await this.closingPromise;
|
|
763
|
+
}
|
|
764
|
+
assertTransportIngressOpen(operation) {
|
|
765
|
+
if (this.closeStarted) {
|
|
766
|
+
throw new InvariantError(`Microservice cannot ${operation} after shutdown has started.`);
|
|
718
767
|
}
|
|
719
768
|
}
|
|
720
769
|
}
|
|
@@ -724,14 +773,33 @@ class FluoMicroserviceApplication {
|
|
|
724
773
|
*/
|
|
725
774
|
async function resolveLifecycleInstances(container, providers, resolvedInstances = []) {
|
|
726
775
|
const lifecycleEntries = [];
|
|
727
|
-
const
|
|
776
|
+
const seenSingleTokens = new Set();
|
|
777
|
+
const multiContributionIndexes = new Map();
|
|
728
778
|
for (const provider of providers) {
|
|
729
779
|
const token = providerToken(provider);
|
|
730
|
-
if (
|
|
780
|
+
if (isMultiProvider(provider)) {
|
|
781
|
+
const contributionIndex = multiContributionIndexes.get(token) ?? 0;
|
|
782
|
+
multiContributionIndexes.set(token, contributionIndex + 1);
|
|
783
|
+
if (isHookBearingValueProvider(provider)) {
|
|
784
|
+
lifecycleEntries.push({
|
|
785
|
+
token,
|
|
786
|
+
useValue: provider.useValue
|
|
787
|
+
});
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
if (isDirectSingletonContextProvider(provider, true)) {
|
|
791
|
+
lifecycleEntries.push({
|
|
792
|
+
contributionIndex,
|
|
793
|
+
token
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
if (seenSingleTokens.has(token)) {
|
|
731
799
|
continue;
|
|
732
800
|
}
|
|
733
801
|
if (isHookBearingValueProvider(provider)) {
|
|
734
|
-
|
|
802
|
+
seenSingleTokens.add(token);
|
|
735
803
|
lifecycleEntries.push({
|
|
736
804
|
token,
|
|
737
805
|
useValue: provider.useValue
|
|
@@ -741,17 +809,28 @@ async function resolveLifecycleInstances(container, providers, resolvedInstances
|
|
|
741
809
|
if (!isDirectSingletonContextProvider(provider)) {
|
|
742
810
|
continue;
|
|
743
811
|
}
|
|
744
|
-
|
|
812
|
+
seenSingleTokens.add(token);
|
|
745
813
|
lifecycleEntries.push({
|
|
746
814
|
token
|
|
747
815
|
});
|
|
748
816
|
}
|
|
749
|
-
const resolutionResults = await Promise.allSettled(lifecycleEntries.map(entry =>
|
|
817
|
+
const resolutionResults = await Promise.allSettled(lifecycleEntries.map(entry => {
|
|
818
|
+
if (entry.useValue !== undefined) {
|
|
819
|
+
return entry.useValue;
|
|
820
|
+
}
|
|
821
|
+
if (entry.contributionIndex === undefined) {
|
|
822
|
+
return container.resolve(entry.token);
|
|
823
|
+
}
|
|
824
|
+
return resolveMultiContribution(container, entry.token, entry.contributionIndex);
|
|
825
|
+
}));
|
|
750
826
|
let resolutionError;
|
|
751
827
|
let hasResolutionError = false;
|
|
752
|
-
for (const result of resolutionResults) {
|
|
828
|
+
for (const [index, result] of resolutionResults.entries()) {
|
|
753
829
|
if (result.status === 'fulfilled') {
|
|
754
|
-
|
|
830
|
+
const entry = lifecycleEntries[index];
|
|
831
|
+
if (entry.contributionIndex === undefined || hasLifecycleHook(result.value)) {
|
|
832
|
+
resolvedInstances.push(result.value);
|
|
833
|
+
}
|
|
755
834
|
continue;
|
|
756
835
|
}
|
|
757
836
|
if (!hasResolutionError) {
|
|
@@ -780,8 +859,8 @@ function createContextCacheableTokenSet(effectiveProviders, runtimeTokens) {
|
|
|
780
859
|
}
|
|
781
860
|
return cacheableTokens;
|
|
782
861
|
}
|
|
783
|
-
function isDirectSingletonContextProvider(provider) {
|
|
784
|
-
if (isMultiProvider(provider)) {
|
|
862
|
+
function isDirectSingletonContextProvider(provider, includeMulti = false) {
|
|
863
|
+
if (isMultiProvider(provider) && !includeMulti) {
|
|
785
864
|
return false;
|
|
786
865
|
}
|
|
787
866
|
if (typeof provider === 'function') {
|
|
@@ -851,21 +930,34 @@ async function runBootstrapHooks(instances) {
|
|
|
851
930
|
* 종료 단계의 hook을 역순으로 실행해 이미 시작한 리소스를 정리한다.
|
|
852
931
|
*/
|
|
853
932
|
async function runShutdownHooks(instances, signal) {
|
|
933
|
+
const errors = [];
|
|
854
934
|
for (const instance of [...instances].reverse()) {
|
|
855
|
-
|
|
856
|
-
|
|
935
|
+
try {
|
|
936
|
+
if (isOnModuleDestroy(instance)) {
|
|
937
|
+
await instance.onModuleDestroy();
|
|
938
|
+
}
|
|
939
|
+
} catch (error) {
|
|
940
|
+
errors.push(error);
|
|
857
941
|
}
|
|
858
942
|
}
|
|
859
943
|
for (const instance of [...instances].reverse()) {
|
|
860
|
-
|
|
861
|
-
|
|
944
|
+
try {
|
|
945
|
+
if (isOnApplicationShutdown(instance)) {
|
|
946
|
+
await instance.onApplicationShutdown(signal);
|
|
947
|
+
}
|
|
948
|
+
} catch (error) {
|
|
949
|
+
errors.push(error);
|
|
862
950
|
}
|
|
863
951
|
}
|
|
952
|
+
if (errors.length > 0) {
|
|
953
|
+
throw createLifecycleCloseError(errors);
|
|
954
|
+
}
|
|
864
955
|
}
|
|
865
956
|
function createHandlerSources(modules) {
|
|
957
|
+
const globalModules = modules.filter(compiledModule => compiledModule.definition.global);
|
|
866
958
|
return modules.flatMap(compiledModule => (compiledModule.definition.controllers ?? []).map(controllerToken => ({
|
|
867
959
|
controllerToken,
|
|
868
|
-
moduleMiddleware: compiledModule.definition.middleware ?? [],
|
|
960
|
+
moduleMiddleware: [...globalModules.filter(globalModule => globalModule !== compiledModule).flatMap(globalModule => globalModule.definition.middleware ?? []), ...(compiledModule.definition.middleware ?? [])],
|
|
869
961
|
moduleType: compiledModule.type
|
|
870
962
|
})));
|
|
871
963
|
}
|
|
@@ -896,7 +988,11 @@ function logRouteMappings(logger, descriptors) {
|
|
|
896
988
|
}
|
|
897
989
|
}
|
|
898
990
|
function createRuntimeProviders(options, logger) {
|
|
991
|
+
const bootstrapProviderTokens = new Set((options.providers ?? []).map(providerToken));
|
|
899
992
|
return [...(options.providers ?? []), {
|
|
993
|
+
provide: BOOTSTRAP_PROVIDER_TOKENS,
|
|
994
|
+
useValue: bootstrapProviderTokens
|
|
995
|
+
}, {
|
|
900
996
|
provide: APPLICATION_LOGGER,
|
|
901
997
|
useValue: logger
|
|
902
998
|
}];
|
|
@@ -959,6 +1055,15 @@ function createRuntimeDispatcherOptions(bootstrapped, options, handlerMapping, e
|
|
|
959
1055
|
const converters = options.converters ?? [];
|
|
960
1056
|
const dispatcherOptions = {
|
|
961
1057
|
appMiddleware: options.middleware ?? [],
|
|
1058
|
+
...(options.contentNegotiation === undefined ? {} : {
|
|
1059
|
+
contentNegotiation: options.contentNegotiation
|
|
1060
|
+
}),
|
|
1061
|
+
...(options.conditionalRequest === undefined ? {} : {
|
|
1062
|
+
conditionalRequest: options.conditionalRequest
|
|
1063
|
+
}),
|
|
1064
|
+
...(options.errorRepresentation === undefined ? {} : {
|
|
1065
|
+
errorRepresentation: options.errorRepresentation
|
|
1066
|
+
}),
|
|
962
1067
|
handlerMapping,
|
|
963
1068
|
interceptors: options.interceptors ?? [],
|
|
964
1069
|
logger,
|
|
@@ -995,7 +1100,7 @@ function createRuntimeDispatcher(bootstrapped, options, logger) {
|
|
|
995
1100
|
* @throws {Error} Propagates module-graph, lifecycle, or runtime initialization failures.
|
|
996
1101
|
*/
|
|
997
1102
|
export async function bootstrapApplication(options) {
|
|
998
|
-
const studioDevtools = createStudioDevtoolsRuntimeFromConfig();
|
|
1103
|
+
const studioDevtools = options.studioDevtools ?? createStudioDevtoolsRuntimeFromConfig();
|
|
999
1104
|
const effectiveOptions = applyStudioDevtoolsApplicationOptions(options, studioDevtools);
|
|
1000
1105
|
const logger = effectiveOptions.logger ?? createDefaultApplicationLogger();
|
|
1001
1106
|
let lifecycleInstances = [];
|
|
@@ -1044,7 +1149,7 @@ export async function bootstrapApplication(options) {
|
|
|
1044
1149
|
bootstrappedModules = bootstrapped.modules;
|
|
1045
1150
|
const resolveLifecycleStart = timingEnabled ? runtimePerformance.now() : 0;
|
|
1046
1151
|
lifecycleInstances = await resolveBootstrapLifecycleInstances(bootstrapped, lifecycleInstances);
|
|
1047
|
-
lifecycleInstances.
|
|
1152
|
+
lifecycleInstances.unshift({
|
|
1048
1153
|
onModuleDestroy() {
|
|
1049
1154
|
return platformShell.stop();
|
|
1050
1155
|
}
|
|
@@ -1122,7 +1227,7 @@ export class FluoFactory {
|
|
|
1122
1227
|
* @throws {Error} Propagates module-graph, lifecycle, and context bootstrap failures.
|
|
1123
1228
|
*/
|
|
1124
1229
|
static async createApplicationContext(rootModule, options = {}) {
|
|
1125
|
-
const studioDevtools = createStudioDevtoolsRuntimeFromConfig();
|
|
1230
|
+
const studioDevtools = options.studioDevtools ?? createStudioDevtoolsRuntimeFromConfig();
|
|
1126
1231
|
const effectiveOptions = applyStudioDevtoolsContextOptions(options, studioDevtools);
|
|
1127
1232
|
const logger = createDefaultApplicationLogger();
|
|
1128
1233
|
let lifecycleInstances = [];
|
|
@@ -1166,7 +1271,7 @@ export class FluoFactory {
|
|
|
1166
1271
|
bootstrappedModules = bootstrapped.modules;
|
|
1167
1272
|
const resolveLifecycleStart = timingEnabled ? runtimePerformance.now() : 0;
|
|
1168
1273
|
lifecycleInstances = await resolveBootstrapLifecycleInstances(bootstrapped, lifecycleInstances);
|
|
1169
|
-
lifecycleInstances.
|
|
1274
|
+
lifecycleInstances.unshift({
|
|
1170
1275
|
onModuleDestroy() {
|
|
1171
1276
|
return platformShell.stop();
|
|
1172
1277
|
}
|