@loutrejs/loutre 0.9.2 → 0.10.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/dist/adapters/aws-lambda.d.ts +1 -1
- package/dist/adapters/aws-lambda.js +8 -2
- package/dist/adapters/bun.d.ts +3 -2
- package/dist/adapters/bun.js +12 -6
- package/dist/adapters/cloudflare-workers.d.ts +1 -1
- package/dist/adapters/cloudflare-workers.js +2 -1
- package/dist/adapters/electron.d.ts +1 -1
- package/dist/adapters/electron.js +8 -51
- package/dist/adapters/server-transports.js +2 -2
- package/dist/adapters/websocket-native.js +4 -1
- package/dist/application/definition.d.ts +4 -7
- package/dist/application/definition.js +3 -1
- package/dist/application/kernel.d.ts +0 -2
- package/dist/application/kernel.js +1 -3
- package/dist/core/args.d.ts +1 -0
- package/dist/core/args.js +2 -1
- package/dist/core/env.d.ts +1 -0
- package/dist/core/env.js +2 -1
- package/dist/core/extension.d.ts +23 -13
- package/dist/core/extension.js +3 -1
- package/dist/core/generic-layer.d.ts +9 -2
- package/dist/core/generic-layer.js +2 -1
- package/dist/core/identity.d.ts +18 -0
- package/dist/core/identity.js +5 -0
- package/dist/core/index.d.ts +13 -5
- package/dist/core/index.js +7 -1
- package/dist/core/injection.d.ts +2 -1
- package/dist/core/instrumentation.d.ts +7 -5
- package/dist/core/lifecycle.d.ts +4 -0
- package/dist/core/logger-identity.d.ts +4 -0
- package/dist/core/logger-identity.js +9 -0
- package/dist/core/logger-internal.d.ts +5 -0
- package/dist/core/logger-internal.js +27 -0
- package/dist/core/logger-module.d.ts +5 -0
- package/dist/core/logger-module.js +8 -0
- package/dist/core/logger.d.ts +42 -0
- package/dist/core/logger.js +221 -0
- package/dist/core/model/context.d.ts +6 -3
- package/dist/core/model/context.js +17 -17
- package/dist/core/model/identity.d.ts +19 -0
- package/dist/core/model/identity.js +8 -0
- package/dist/core/model/lifecycle.d.ts +3 -2
- package/dist/core/model/lifecycle.js +7 -3
- package/dist/core/model/logger.d.ts +3 -0
- package/dist/core/model/logger.js +59 -0
- package/dist/core/model/modules.d.ts +4 -1
- package/dist/core/model/modules.js +6 -4
- package/dist/core/model/providers.d.ts +1 -1
- package/dist/core/model/providers.js +8 -8
- package/dist/core/model/types.d.ts +25 -18
- package/dist/core/model/validation.js +3 -2
- package/dist/core/model/visibility.js +7 -2
- package/dist/core/model.js +15 -7
- package/dist/core/module.d.ts +7 -7
- package/dist/core/module.js +0 -3
- package/dist/core/runtime-input.d.ts +2 -1
- package/dist/core/source-location.d.ts +14 -2
- package/dist/core/source-location.js +8 -3
- package/dist/core/token-internal.js +3 -1
- package/dist/core/token.d.ts +4 -2
- package/dist/core/token.js +3 -0
- package/dist/core/units.d.ts +22 -0
- package/dist/core/units.js +14 -0
- package/dist/devtools/capsule.d.ts +17 -13
- package/dist/devtools/capsule.js +14 -10
- package/dist/devtools/decoder.d.ts +15 -0
- package/dist/devtools/decoder.js +179 -0
- package/dist/devtools/identity-internal.d.ts +6 -0
- package/dist/devtools/identity-internal.js +5 -0
- package/dist/devtools/identity.d.ts +21 -0
- package/dist/devtools/identity.js +7 -0
- package/dist/devtools/index.d.ts +3 -0
- package/dist/devtools/index.js +2 -0
- package/dist/devtools/inspector.d.ts +18 -16
- package/dist/devtools/observer.d.ts +32 -28
- package/dist/devtools/runtime.js +34 -30
- package/dist/devtools/values.js +3 -5
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/graph/model.d.ts +25 -14
- package/dist/http/auth.d.ts +4 -2
- package/dist/http/client.d.ts +6 -3
- package/dist/http/client.js +8 -9
- package/dist/http/cors-internal.d.ts +7 -5
- package/dist/http/cors-internal.js +8 -6
- package/dist/http/extension.d.ts +60 -21
- package/dist/http/extension.js +91 -20
- package/dist/http/identity.d.ts +20 -0
- package/dist/http/identity.js +10 -0
- package/dist/http/index.d.ts +6 -2
- package/dist/http/index.js +3 -1
- package/dist/http/openapi-identity.d.ts +9 -0
- package/dist/http/openapi-identity.js +4 -0
- package/dist/http/openapi.d.ts +1 -1
- package/dist/http/openapi.js +10 -7
- package/dist/http/path.d.ts +8 -5
- package/dist/http/route-validation.js +2 -1
- package/dist/http/runtime/request.d.ts +3 -2
- package/dist/http/runtime/request.js +1 -4
- package/dist/http/websocket-upgrade.d.ts +2 -0
- package/dist/http/websocket-upgrade.js +4 -0
- package/dist/internal/brand.d.ts +8 -0
- package/dist/internal/brand.js +1 -0
- package/dist/message-port/extension.d.ts +4 -2
- package/dist/message-port/extension.js +15 -8
- package/dist/message-port/identity.d.ts +9 -0
- package/dist/message-port/identity.js +16 -0
- package/dist/message-port/index.d.ts +2 -0
- package/dist/message-port/index.js +1 -0
- package/dist/message-port/transport.d.ts +4 -2
- package/dist/message-port/transport.js +6 -1
- package/dist/runtime/async-iterator-lifecycle.d.ts +2 -1
- package/dist/runtime/async-iterator-lifecycle.js +2 -1
- package/dist/runtime/capability.d.ts +6 -2
- package/dist/runtime/capability.js +3 -1
- package/dist/runtime/di.d.ts +1 -3
- package/dist/runtime/di.js +62 -35
- package/dist/runtime/error.d.ts +3 -1
- package/dist/runtime/index.d.ts +6 -2
- package/dist/runtime/index.js +3 -1
- package/dist/runtime/kernel.d.ts +0 -2
- package/dist/runtime/kernel.js +68 -30
- package/dist/runtime/logger-source.d.ts +4 -0
- package/dist/runtime/logger-source.js +47 -0
- package/dist/runtime/logger.d.ts +2 -38
- package/dist/runtime/logger.js +1 -209
- package/dist/runtime/node-capabilities.d.ts +1 -1
- package/dist/runtime/node-capabilities.js +2 -1
- package/dist/runtime/server-port.d.ts +5 -1
- package/dist/runtime/server-port.js +2 -1
- package/dist/runtime/shutdown-hooks.d.ts +1 -1
- package/dist/runtime/shutdown-hooks.js +5 -4
- package/dist/tasks/extension.d.ts +18 -16
- package/dist/tasks/extension.js +28 -23
- package/dist/tasks/identity.d.ts +9 -0
- package/dist/tasks/identity.js +4 -0
- package/dist/tasks/index.d.ts +1 -0
- package/dist/tasks/runtime/cron.d.ts +6 -2
- package/dist/tasks/runtime/cron.js +9 -0
- package/dist/websocket/extension.d.ts +19 -9
- package/dist/websocket/extension.js +103 -30
- package/dist/websocket/identity.d.ts +9 -0
- package/dist/websocket/identity.js +4 -0
- package/dist/websocket/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/core/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { LoggerService, LoggerToken } from './logger.js';
|
|
2
|
+
export { LoggerModule } from './logger-module.js';
|
|
1
3
|
export { defineArgs, isArgsClass, loadArgs } from './args.js';
|
|
2
4
|
export { diagnostic, hasErrorDiagnostics, isErrorDiagnostic, } from './diagnostic.js';
|
|
3
5
|
export { defineEnv, isEnvClass, loadEnv } from './env.js';
|
|
@@ -7,9 +9,13 @@ export { composeLayers, defineLayer } from './generic-layer.js';
|
|
|
7
9
|
export { collectInjectedDependencies, inject, injectInquirer, InjectionContextError, runInInjectionContext, } from './injection.js';
|
|
8
10
|
export { hook } from './lifecycle.js';
|
|
9
11
|
export { ApplicationModelError, assertValidApplicationModel, buildApplicationModel, } from './model.js';
|
|
10
|
-
export {
|
|
12
|
+
export { defineModule, moduleTypeInfo } from './module.js';
|
|
11
13
|
export { argumentsProvider, environmentProvider, normalizeProvider, provide, } from './provider.js';
|
|
12
14
|
export { getSourceLocation, inheritSourceLocation, registerSourceLocation, } from './source-location.js';
|
|
13
15
|
export { SchemaValidationError, supportsJsonSchema, validateSchema, } from './schema.js';
|
|
14
16
|
export { token, tokenName } from './token.js';
|
|
15
17
|
export { type } from './type.js';
|
|
18
|
+
export * from './identity.js';
|
|
19
|
+
export * from './units.js';
|
|
20
|
+
export { parseModelNodeId, parseProviderNodeId } from './model/identity.js';
|
|
21
|
+
export { sourceFileReference, sourceLine, sourceColumn, } from './source-location.js';
|
package/dist/core/injection.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { DependencyConsumerId } from './identity.js';
|
|
1
2
|
import type { TokenLike, TokenValue } from './token.js';
|
|
2
3
|
export interface DependencyConsumerDescriptor {
|
|
3
|
-
readonly id:
|
|
4
|
+
readonly id: DependencyConsumerId;
|
|
4
5
|
readonly name: string;
|
|
5
6
|
readonly kind?: string;
|
|
6
7
|
}
|
|
@@ -1,23 +1,25 @@
|
|
|
1
|
+
import type { RuntimeExecutionId } from './identity.js';
|
|
2
|
+
import type { ModelNodeId, ProviderNodeId } from './model/identity.js';
|
|
1
3
|
import type { Scope } from './provider.js';
|
|
2
4
|
export interface RuntimeInvocationRegistration {
|
|
3
5
|
readonly input: unknown;
|
|
4
6
|
invoke(input: unknown): unknown | Promise<unknown>;
|
|
5
7
|
}
|
|
6
8
|
export interface RuntimeExecutionMetadata {
|
|
7
|
-
readonly executionId:
|
|
9
|
+
readonly executionId: RuntimeExecutionId;
|
|
8
10
|
readonly executionKind: string;
|
|
9
11
|
readonly name?: string;
|
|
10
|
-
readonly graphNodeId?:
|
|
12
|
+
readonly graphNodeId?: ModelNodeId;
|
|
11
13
|
}
|
|
12
14
|
export interface RuntimeOperationMetadata {
|
|
13
15
|
readonly kind: string;
|
|
14
16
|
readonly name: string;
|
|
15
|
-
readonly graphNodeId?:
|
|
17
|
+
readonly graphNodeId?: ModelNodeId;
|
|
16
18
|
}
|
|
17
19
|
export interface RuntimeProviderMetadata {
|
|
18
|
-
readonly providerId:
|
|
20
|
+
readonly providerId: ProviderNodeId;
|
|
19
21
|
readonly name: string;
|
|
20
|
-
readonly graphNodeId:
|
|
22
|
+
readonly graphNodeId: ProviderNodeId;
|
|
21
23
|
readonly scope: Scope;
|
|
22
24
|
readonly providerKind: 'value' | 'class' | 'factory' | 'conditional' | 'environment' | 'arguments';
|
|
23
25
|
}
|
package/dist/core/lifecycle.d.ts
CHANGED
|
@@ -14,6 +14,9 @@ export interface BeforeApplicationShutdown {
|
|
|
14
14
|
export interface OnApplicationShutdown {
|
|
15
15
|
onApplicationShutdown(signal?: string): void | Promise<void>;
|
|
16
16
|
}
|
|
17
|
+
export interface OnApplicationDispose {
|
|
18
|
+
onApplicationDispose(): void | Promise<void>;
|
|
19
|
+
}
|
|
17
20
|
export interface LifecycleHook<TInject extends readonly TokenLike[] = readonly TokenLike[]> {
|
|
18
21
|
readonly kind: 'lifecycle-hook';
|
|
19
22
|
readonly inject: TInject;
|
|
@@ -31,4 +34,5 @@ export interface ModuleLifecycle {
|
|
|
31
34
|
readonly onModuleDestroy?: LifecycleHook<any>;
|
|
32
35
|
readonly beforeApplicationShutdown?: LifecycleHook<any>;
|
|
33
36
|
readonly onApplicationShutdown?: LifecycleHook<any>;
|
|
37
|
+
readonly onApplicationDispose?: LifecycleHook<any>;
|
|
34
38
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const loggerTokenBrand = Symbol.for('loutre.logger.token');
|
|
2
|
+
export const loggerInstanceBrand = Symbol.for('loutre.logger.instance');
|
|
3
|
+
export const loggerSourceRoot = Symbol.for('loutre.logger.source-root');
|
|
4
|
+
export function isLoggerTokenIdentity(value) {
|
|
5
|
+
return (typeof value === 'function' &&
|
|
6
|
+
Object.hasOwn(value, loggerTokenBrand) &&
|
|
7
|
+
loggerTokenBrand in value &&
|
|
8
|
+
value[loggerTokenBrand] === true);
|
|
9
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { LoggerService } from './logger.js';
|
|
2
|
+
import { type ProviderDescriptor } from './provider.js';
|
|
3
|
+
export declare function isLoggerToken(value: unknown): value is typeof LoggerService;
|
|
4
|
+
export declare function isLogger(value: unknown): value is LoggerService;
|
|
5
|
+
export declare function defaultLoggerProviders(): readonly ProviderDescriptor[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ConsoleLoggerBackend, LoggerService, LoggerToken, } from './logger.js';
|
|
2
|
+
import { inject } from './injection.js';
|
|
3
|
+
import { provide } from './provider.js';
|
|
4
|
+
import { token } from './token.js';
|
|
5
|
+
import { loggerInstanceBrand, isLoggerTokenIdentity, } from './logger-identity.js';
|
|
6
|
+
export function isLoggerToken(value) {
|
|
7
|
+
// Loggerの派生クラスもrootにできるため、継承された印は予約tokenの判定に使わない。
|
|
8
|
+
return isLoggerTokenIdentity(value);
|
|
9
|
+
}
|
|
10
|
+
export function isLogger(value) {
|
|
11
|
+
return (typeof value === 'object' &&
|
|
12
|
+
value !== null &&
|
|
13
|
+
loggerInstanceBrand in value &&
|
|
14
|
+
value[loggerInstanceBrand] === true);
|
|
15
|
+
}
|
|
16
|
+
const defaultBackendToken = token('loutre.logger.backend');
|
|
17
|
+
class DefaultLogger extends LoggerService {
|
|
18
|
+
constructor(backend = inject(defaultBackendToken)) {
|
|
19
|
+
super(backend);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function defaultLoggerProviders() {
|
|
23
|
+
return [
|
|
24
|
+
provide(defaultBackendToken).useClass(ConsoleLoggerBackend),
|
|
25
|
+
provide(LoggerToken).useClass(DefaultLogger),
|
|
26
|
+
];
|
|
27
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { LoggerToken } from './logger.js';
|
|
2
|
+
import { defaultLoggerProviders } from './logger-internal.js';
|
|
3
|
+
import { defineModule } from './module.js';
|
|
4
|
+
export const LoggerModule = defineModule({
|
|
5
|
+
name: 'LoggerModule',
|
|
6
|
+
providers: defaultLoggerProviders(),
|
|
7
|
+
exports: [LoggerToken],
|
|
8
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { frameworkProvidedTokenBrand } from './token-internal.js';
|
|
2
|
+
import { loggerInstanceBrand, loggerTokenBrand } from './logger-identity.js';
|
|
3
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
4
|
+
export interface LogRecord {
|
|
5
|
+
readonly timestamp: string;
|
|
6
|
+
readonly level: LogLevel;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
readonly [key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
export interface LoggerBackend {
|
|
11
|
+
write(record: LogRecord): void;
|
|
12
|
+
}
|
|
13
|
+
export interface ConsoleLoggerBackendOptions {
|
|
14
|
+
readonly colors?: boolean;
|
|
15
|
+
readonly prefix?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class ConsoleLoggerBackend implements LoggerBackend {
|
|
18
|
+
#private;
|
|
19
|
+
constructor(options?: ConsoleLoggerBackendOptions);
|
|
20
|
+
write(record: LogRecord): void;
|
|
21
|
+
}
|
|
22
|
+
export declare class JsonConsoleLoggerBackend implements LoggerBackend {
|
|
23
|
+
write(record: LogRecord): void;
|
|
24
|
+
}
|
|
25
|
+
export declare class LoggerService {
|
|
26
|
+
#private;
|
|
27
|
+
readonly backend: LoggerBackend;
|
|
28
|
+
static readonly [frameworkProvidedTokenBrand] = true;
|
|
29
|
+
static readonly [loggerTokenBrand] = true;
|
|
30
|
+
readonly [loggerInstanceBrand] = true;
|
|
31
|
+
readonly context: Readonly<Record<string, unknown>>;
|
|
32
|
+
constructor(backend?: LoggerBackend, context?: Readonly<Record<string, unknown>>);
|
|
33
|
+
debug(message: string, context?: Readonly<Record<string, unknown>>): void;
|
|
34
|
+
info(message: string, context?: Readonly<Record<string, unknown>>): void;
|
|
35
|
+
warn(message: string, context?: Readonly<Record<string, unknown>>): void;
|
|
36
|
+
error(message: string, context?: Readonly<Record<string, unknown>>): void;
|
|
37
|
+
onApplicationDispose(): void;
|
|
38
|
+
}
|
|
39
|
+
export declare const LoggerToken: typeof LoggerService;
|
|
40
|
+
export declare class SilentLogger extends LoggerService {
|
|
41
|
+
constructor(context?: Readonly<Record<string, unknown>>);
|
|
42
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { frameworkProvidedTokenBrand } from './token-internal.js';
|
|
2
|
+
import { loggerInstanceBrand, loggerTokenBrand } from './logger-identity.js';
|
|
3
|
+
export class ConsoleLoggerBackend {
|
|
4
|
+
#colors;
|
|
5
|
+
#prefix;
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
this.#colors = options.colors ?? detectConsoleColors();
|
|
8
|
+
this.#prefix = options.prefix ?? 'Loutre';
|
|
9
|
+
}
|
|
10
|
+
write(record) {
|
|
11
|
+
writeToConsole(record.level, formatConsoleLogRecord(record, this.#prefix, this.#colors));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class JsonConsoleLoggerBackend {
|
|
15
|
+
write(record) {
|
|
16
|
+
writeToConsole(record.level, serializeLogRecord(record));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
class SilentLoggerBackend {
|
|
20
|
+
write(_record) { }
|
|
21
|
+
}
|
|
22
|
+
export class LoggerService {
|
|
23
|
+
backend;
|
|
24
|
+
static #disposed = new WeakSet();
|
|
25
|
+
static [frameworkProvidedTokenBrand] = true;
|
|
26
|
+
static [loggerTokenBrand] = true;
|
|
27
|
+
[loggerInstanceBrand] = true;
|
|
28
|
+
context;
|
|
29
|
+
constructor(backend = new ConsoleLoggerBackend(), context = {}) {
|
|
30
|
+
this.backend = backend;
|
|
31
|
+
this.context = { source: 'Application', ...context };
|
|
32
|
+
}
|
|
33
|
+
debug(message, context = {}) {
|
|
34
|
+
LoggerService.#write(this, 'debug', message, context);
|
|
35
|
+
}
|
|
36
|
+
info(message, context = {}) {
|
|
37
|
+
LoggerService.#write(this, 'info', message, context);
|
|
38
|
+
}
|
|
39
|
+
warn(message, context = {}) {
|
|
40
|
+
LoggerService.#write(this, 'warn', message, context);
|
|
41
|
+
}
|
|
42
|
+
error(message, context = {}) {
|
|
43
|
+
LoggerService.#write(this, 'error', message, context);
|
|
44
|
+
}
|
|
45
|
+
onApplicationDispose() {
|
|
46
|
+
LoggerService.#disposed.add(this);
|
|
47
|
+
}
|
|
48
|
+
static #write(logger, level, message, context) {
|
|
49
|
+
const record = {
|
|
50
|
+
...logger.context,
|
|
51
|
+
...context,
|
|
52
|
+
timestamp: new Date().toISOString(),
|
|
53
|
+
level,
|
|
54
|
+
message,
|
|
55
|
+
};
|
|
56
|
+
if (LoggerService.#disposed.has(logger)) {
|
|
57
|
+
writeToConsole(level, serializeLogRecord(record));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
logger.backend.write(record);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export const LoggerToken = LoggerService;
|
|
64
|
+
export class SilentLogger extends LoggerService {
|
|
65
|
+
constructor(context = {}) {
|
|
66
|
+
super(new SilentLoggerBackend(), context);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function serializeLogRecord(record) {
|
|
70
|
+
const seen = new WeakSet();
|
|
71
|
+
try {
|
|
72
|
+
return JSON.stringify(record, (_key, value) => {
|
|
73
|
+
if (typeof value === 'bigint')
|
|
74
|
+
return value.toString();
|
|
75
|
+
if (value !== null && typeof value === 'object') {
|
|
76
|
+
if (seen.has(value))
|
|
77
|
+
return '[Circular]';
|
|
78
|
+
seen.add(value);
|
|
79
|
+
}
|
|
80
|
+
if (value instanceof Error) {
|
|
81
|
+
return {
|
|
82
|
+
...value,
|
|
83
|
+
name: value.name,
|
|
84
|
+
message: value.message,
|
|
85
|
+
...(value.stack === undefined ? {} : { stack: value.stack }),
|
|
86
|
+
...(value.cause === undefined ? {} : { cause: value.cause }),
|
|
87
|
+
...(value instanceof AggregateError ? { errors: value.errors } : {}),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return value;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
return JSON.stringify({
|
|
95
|
+
timestamp: record.timestamp,
|
|
96
|
+
level: 'error',
|
|
97
|
+
message: 'Log record serialization failed',
|
|
98
|
+
serializationError: error instanceof Error ? error.message : String(error),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function writeToConsole(level, value) {
|
|
103
|
+
const target = level === 'error'
|
|
104
|
+
? console.error
|
|
105
|
+
: level === 'warn'
|
|
106
|
+
? console.warn
|
|
107
|
+
: console.log;
|
|
108
|
+
target(value);
|
|
109
|
+
}
|
|
110
|
+
function formatConsoleLogRecord(record, prefix, colors) {
|
|
111
|
+
const { timestamp, level, message, ...recordContext } = record;
|
|
112
|
+
const context = { ...recordContext };
|
|
113
|
+
const source = typeof context.source === 'string'
|
|
114
|
+
? context.source
|
|
115
|
+
: typeof context.protocol === 'string'
|
|
116
|
+
? context.protocol
|
|
117
|
+
: undefined;
|
|
118
|
+
if (source !== undefined)
|
|
119
|
+
delete context.source;
|
|
120
|
+
const runtimeProcess = getRuntimeProcess();
|
|
121
|
+
const processLabel = runtimeProcess?.pid === undefined
|
|
122
|
+
? `[${prefix}]`
|
|
123
|
+
: `[${prefix}] ${runtimeProcess.pid}`;
|
|
124
|
+
const levelLabel = level === 'info' ? 'LOG' : level.toUpperCase();
|
|
125
|
+
const levelColor = level === 'error' ? 31 : level === 'warn' ? 33 : level === 'debug' ? 36 : 32;
|
|
126
|
+
const sourceLabel = source === undefined ? '' : `${paint(`[${source}]`, 33, colors)} `;
|
|
127
|
+
const formattedContext = formatLogContext(context);
|
|
128
|
+
const contextSuffix = formattedContext === '' ? '' : ` ${paint(formattedContext, 90, colors)}`;
|
|
129
|
+
return `${paint(processLabel, 32, colors)} - ${formatLocalTimestamp(timestamp)} ${paint(levelLabel.padStart(7), levelColor, colors)} ${sourceLabel}${paint(message, levelColor, colors)}${contextSuffix}`;
|
|
130
|
+
}
|
|
131
|
+
function formatLogContext(context) {
|
|
132
|
+
const entries = Object.entries(context);
|
|
133
|
+
if (entries.length === 0)
|
|
134
|
+
return '';
|
|
135
|
+
return `{ ${entries
|
|
136
|
+
.map(([key, value]) => `${formatLogKey(key)}=${formatLogValue(value)}`)
|
|
137
|
+
.join(', ')} }`;
|
|
138
|
+
}
|
|
139
|
+
function formatLogKey(key) {
|
|
140
|
+
return /^[A-Za-z_$][\w$]*$/u.test(key) ? key : JSON.stringify(key);
|
|
141
|
+
}
|
|
142
|
+
function formatLogValue(value, ancestors = new WeakSet()) {
|
|
143
|
+
try {
|
|
144
|
+
if (value === null)
|
|
145
|
+
return 'null';
|
|
146
|
+
if (value === undefined)
|
|
147
|
+
return 'undefined';
|
|
148
|
+
if (typeof value === 'string') {
|
|
149
|
+
return /^[\w./:@+-]+$/u.test(value) ? value : JSON.stringify(value);
|
|
150
|
+
}
|
|
151
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
152
|
+
return String(value);
|
|
153
|
+
if (typeof value === 'bigint')
|
|
154
|
+
return `${value}n`;
|
|
155
|
+
if (typeof value === 'symbol')
|
|
156
|
+
return String(value);
|
|
157
|
+
if (typeof value === 'function')
|
|
158
|
+
return `[Function ${value.name || 'anonymous'}]`;
|
|
159
|
+
if (ancestors.has(value))
|
|
160
|
+
return '[Circular]';
|
|
161
|
+
ancestors.add(value);
|
|
162
|
+
try {
|
|
163
|
+
if (value instanceof Date)
|
|
164
|
+
return value.toISOString();
|
|
165
|
+
if (value instanceof Error) {
|
|
166
|
+
return formatLogValue({
|
|
167
|
+
name: value.name,
|
|
168
|
+
message: value.message,
|
|
169
|
+
...(value.stack === undefined ? {} : { stack: value.stack }),
|
|
170
|
+
...(value.cause === undefined ? {} : { cause: value.cause }),
|
|
171
|
+
}, ancestors);
|
|
172
|
+
}
|
|
173
|
+
if (Array.isArray(value)) {
|
|
174
|
+
return `[${value.map((item) => formatLogValue(item, ancestors)).join(', ')}]`;
|
|
175
|
+
}
|
|
176
|
+
return `{ ${Object.entries(value)
|
|
177
|
+
.map(([key, item]) => `${formatLogKey(key)}=${formatLogValue(item, ancestors)}`)
|
|
178
|
+
.join(', ')} }`;
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
ancestors.delete(value);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
186
|
+
return `[Unformattable: ${message}]`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function formatLocalTimestamp(timestamp) {
|
|
190
|
+
const date = new Date(timestamp);
|
|
191
|
+
if (Number.isNaN(date.getTime()))
|
|
192
|
+
return timestamp;
|
|
193
|
+
return ([
|
|
194
|
+
date.getFullYear(),
|
|
195
|
+
String(date.getMonth() + 1).padStart(2, '0'),
|
|
196
|
+
String(date.getDate()).padStart(2, '0'),
|
|
197
|
+
].join('/') +
|
|
198
|
+
` ${[
|
|
199
|
+
String(date.getHours()).padStart(2, '0'),
|
|
200
|
+
String(date.getMinutes()).padStart(2, '0'),
|
|
201
|
+
String(date.getSeconds()).padStart(2, '0'),
|
|
202
|
+
].join(':')}.${String(date.getMilliseconds()).padStart(3, '0')}`);
|
|
203
|
+
}
|
|
204
|
+
function paint(value, color, enabled) {
|
|
205
|
+
return enabled ? `\u001B[${color}m${value}\u001B[39m` : value;
|
|
206
|
+
}
|
|
207
|
+
function getRuntimeProcess() {
|
|
208
|
+
return globalThis.process;
|
|
209
|
+
}
|
|
210
|
+
function detectConsoleColors() {
|
|
211
|
+
const runtimeProcess = getRuntimeProcess();
|
|
212
|
+
const environment = runtimeProcess?.env;
|
|
213
|
+
if (environment?.NO_COLOR !== undefined ||
|
|
214
|
+
environment?.NODE_DISABLE_COLORS !== undefined) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
if (environment?.FORCE_COLOR !== undefined) {
|
|
218
|
+
return environment.FORCE_COLOR !== '0';
|
|
219
|
+
}
|
|
220
|
+
return runtimeProcess?.stdout?.isTTY === true;
|
|
221
|
+
}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
+
import { type ModuleId, type ProviderNodeId } from './identity.js';
|
|
1
2
|
import type { Diagnostic } from '../diagnostic.js';
|
|
2
|
-
import {
|
|
3
|
+
import type { ModuleInstance } from '../module.js';
|
|
3
4
|
import type { ProviderDescriptor } from '../provider.js';
|
|
4
5
|
import type { TokenLike } from '../token.js';
|
|
5
6
|
import type { ApplicationModelBuildOptions, ApplicationModelEdge, ApplicationModelNode, ExecutionModelNode, ProviderModelNode } from './types.js';
|
|
6
7
|
export interface ModelBuildContext {
|
|
7
8
|
readonly options: ApplicationModelBuildOptions;
|
|
9
|
+
readonly roots: readonly ModuleInstance[];
|
|
8
10
|
readonly modules: readonly ModuleInstance[];
|
|
9
11
|
readonly imports: ReadonlyMap<ModuleInstance, readonly ModuleInstance[]>;
|
|
10
|
-
readonly moduleIds: ReadonlyMap<ModuleInstance,
|
|
12
|
+
readonly moduleIds: ReadonlyMap<ModuleInstance, ModuleId>;
|
|
11
13
|
readonly nodes: ApplicationModelNode[];
|
|
12
14
|
readonly edges: ApplicationModelEdge[];
|
|
13
15
|
readonly diagnostics: Diagnostic[];
|
|
14
16
|
readonly providers: ProviderDescriptor[];
|
|
15
17
|
readonly executions: ExecutionModelNode[];
|
|
16
18
|
readonly providerNodes: Map<TokenLike, ProviderModelNode>;
|
|
17
|
-
nextProviderId():
|
|
19
|
+
nextProviderId(): ProviderNodeId;
|
|
18
20
|
}
|
|
19
21
|
export declare function createModelBuildContext(options: ApplicationModelBuildOptions): ModelBuildContext;
|
|
22
|
+
export declare function findProviderNode(context: Pick<ModelBuildContext, 'providerNodes'>, token: TokenLike): ProviderModelNode | undefined;
|
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isLoggerToken } from '../logger-internal.js';
|
|
2
|
+
import { moduleId, providerNodeId, } from './identity.js';
|
|
2
3
|
function collectModules(roots) {
|
|
3
4
|
const modules = [];
|
|
4
5
|
const imports = new Map();
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (existing)
|
|
9
|
-
return existing;
|
|
10
|
-
const instance = asModuleInstance(input);
|
|
11
|
-
instances.set(input, instance);
|
|
12
|
-
return instance;
|
|
13
|
-
};
|
|
14
|
-
const visit = (input) => {
|
|
15
|
-
const module = normalize(input);
|
|
6
|
+
const visit = (module) => {
|
|
7
|
+
if (module?.kind !== 'module-instance')
|
|
8
|
+
throw new TypeError('Application modules and imports require Module instances.');
|
|
16
9
|
if (imports.has(module))
|
|
17
10
|
return;
|
|
18
|
-
const imported = (module.definition.imports ?? [])
|
|
11
|
+
const imported = [...(module.definition.imports ?? [])];
|
|
19
12
|
imports.set(module, imported);
|
|
20
13
|
for (const child of imported)
|
|
21
14
|
visit(child);
|
|
@@ -23,14 +16,15 @@ function collectModules(roots) {
|
|
|
23
16
|
};
|
|
24
17
|
for (const root of roots)
|
|
25
18
|
visit(root);
|
|
26
|
-
return { modules, imports };
|
|
19
|
+
return { modules, imports, rootModules: [...roots] };
|
|
27
20
|
}
|
|
28
21
|
export function createModelBuildContext(options) {
|
|
29
|
-
const { modules, imports } = collectModules(options.modules);
|
|
30
|
-
const moduleIds = new Map(modules.map((module, index) => [module,
|
|
22
|
+
const { modules, imports, rootModules } = collectModules(options.modules);
|
|
23
|
+
const moduleIds = new Map(modules.map((module, index) => [module, moduleId(index + 1)]));
|
|
31
24
|
let providerOrdinal = 0;
|
|
32
25
|
return {
|
|
33
26
|
options,
|
|
27
|
+
roots: rootModules,
|
|
34
28
|
modules,
|
|
35
29
|
imports,
|
|
36
30
|
moduleIds,
|
|
@@ -40,6 +34,12 @@ export function createModelBuildContext(options) {
|
|
|
40
34
|
providers: [],
|
|
41
35
|
executions: [],
|
|
42
36
|
providerNodes: new Map(),
|
|
43
|
-
nextProviderId: () =>
|
|
37
|
+
nextProviderId: () => providerNodeId(++providerOrdinal),
|
|
44
38
|
};
|
|
45
39
|
}
|
|
40
|
+
export function findProviderNode(context, token) {
|
|
41
|
+
return (context.providerNodes.get(token) ??
|
|
42
|
+
(isLoggerToken(token)
|
|
43
|
+
? [...context.providerNodes.values()].find((node) => isLoggerToken(node.token))
|
|
44
|
+
: undefined));
|
|
45
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type ModelId<Kind extends string> = string & {
|
|
2
|
+
readonly '~loutre.model-node-id': Kind;
|
|
3
|
+
};
|
|
4
|
+
export type ModuleId = ModelId<'module'>;
|
|
5
|
+
export type ProviderNodeId = ModelId<'provider'>;
|
|
6
|
+
export type ExecutionNodeId = ModelId<'execution'>;
|
|
7
|
+
export type LifecycleNodeId = ModelId<'lifecycle'>;
|
|
8
|
+
export type CapabilityNodeId = ModelId<'capability'>;
|
|
9
|
+
export type ExtensionNodeId = ModelId<'extension'>;
|
|
10
|
+
export type ModelNodeId = ModuleId | ProviderNodeId | ExecutionNodeId | LifecycleNodeId | CapabilityNodeId | ExtensionNodeId;
|
|
11
|
+
export declare const moduleId: (ordinal: number) => ModuleId;
|
|
12
|
+
export declare const providerNodeId: (ordinal: number) => ProviderNodeId;
|
|
13
|
+
export declare const executionNodeId: (value: string) => ExecutionNodeId;
|
|
14
|
+
export declare const lifecycleNodeId: (owner: ModuleId, phase: string) => LifecycleNodeId;
|
|
15
|
+
export declare const capabilityNodeId: (value: string) => CapabilityNodeId;
|
|
16
|
+
export declare const extensionNodeId: (value: string) => ExtensionNodeId;
|
|
17
|
+
export declare const parseModelNodeId: (value: unknown) => ModelNodeId | undefined;
|
|
18
|
+
export declare const parseProviderNodeId: (value: unknown) => ProviderNodeId | undefined;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const moduleId = (ordinal) => `module:${ordinal}`;
|
|
2
|
+
export const providerNodeId = (ordinal) => `provider:${ordinal}`;
|
|
3
|
+
export const executionNodeId = (value) => value;
|
|
4
|
+
export const lifecycleNodeId = (owner, phase) => `lifecycle:${owner}:${phase}`;
|
|
5
|
+
export const capabilityNodeId = (value) => `capability:${value}`;
|
|
6
|
+
export const extensionNodeId = (value) => `extension:${value}`;
|
|
7
|
+
export const parseModelNodeId = (value) => typeof value === 'string' ? value : undefined;
|
|
8
|
+
export const parseProviderNodeId = (value) => typeof value === 'string' ? value : undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type ModuleId } from './identity.js';
|
|
1
2
|
import type { ModuleInstance } from '../module.js';
|
|
2
3
|
import type { ApplicationModelEdge, ApplicationModelNode } from './types.js';
|
|
3
|
-
import type
|
|
4
|
-
export declare function appendLifecycleNodes(module: ModuleInstance, moduleId:
|
|
4
|
+
import { type ModelBuildContext } from './context.js';
|
|
5
|
+
export declare function appendLifecycleNodes(module: ModuleInstance, moduleId: ModuleId, nodes: ApplicationModelNode[], edges: ApplicationModelEdge[]): void;
|
|
5
6
|
export declare function resolveLifecycleDependencies(context: ModelBuildContext): void;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { isLoggerToken } from '../logger-internal.js';
|
|
2
|
+
import { lifecycleNodeId } from './identity.js';
|
|
1
3
|
import { diagnostic } from '../diagnostic.js';
|
|
2
4
|
import { isFrameworkProvidedToken } from '../token-internal.js';
|
|
3
5
|
import { tokenName } from '../token.js';
|
|
6
|
+
import { findProviderNode } from './context.js';
|
|
4
7
|
import { snapshotLifecycleHook } from './snapshot.js';
|
|
5
8
|
import { isTokenVisible, moduleDeclaresToken } from './visibility.js';
|
|
6
9
|
export function appendLifecycleNodes(module, moduleId, nodes, edges) {
|
|
@@ -8,7 +11,7 @@ export function appendLifecycleNodes(module, moduleId, nodes, edges) {
|
|
|
8
11
|
if (!lifecycle)
|
|
9
12
|
return;
|
|
10
13
|
for (const [phase, hook] of Object.entries(lifecycle)) {
|
|
11
|
-
const id =
|
|
14
|
+
const id = lifecycleNodeId(moduleId, phase);
|
|
12
15
|
nodes.push({
|
|
13
16
|
kind: 'lifecycle',
|
|
14
17
|
id,
|
|
@@ -20,15 +23,16 @@ export function appendLifecycleNodes(module, moduleId, nodes, edges) {
|
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
export function resolveLifecycleDependencies(context) {
|
|
23
|
-
const { nodes,
|
|
26
|
+
const { nodes, modules, moduleIds, edges, diagnostics } = context;
|
|
24
27
|
for (const lifecycle of nodes.filter((node) => node.kind === 'lifecycle')) {
|
|
25
28
|
for (const dependency of lifecycle.hook.inject) {
|
|
26
|
-
const provider =
|
|
29
|
+
const provider = findProviderNode(context, dependency);
|
|
27
30
|
if (provider) {
|
|
28
31
|
edges.push({ from: lifecycle.id, to: provider.id, kind: 'injects' });
|
|
29
32
|
const sourceModule = modules.find((candidate) => moduleIds.get(candidate) === lifecycle.moduleId);
|
|
30
33
|
const providerModule = modules.find((candidate) => moduleIds.get(candidate) === provider.moduleId);
|
|
31
34
|
if (sourceModule &&
|
|
35
|
+
!isLoggerToken(dependency) &&
|
|
32
36
|
providerModule &&
|
|
33
37
|
provider.moduleId !== lifecycle.moduleId &&
|
|
34
38
|
!moduleDeclaresToken(sourceModule, dependency) &&
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { diagnostic } from '../diagnostic.js';
|
|
2
|
+
import { LoggerToken } from '../logger.js';
|
|
3
|
+
import { defaultLoggerProviders, isLoggerToken } from '../logger-internal.js';
|
|
4
|
+
import { tokenName } from '../token.js';
|
|
5
|
+
import { findProviderNode } from './context.js';
|
|
6
|
+
import { collectProviderDependencies } from './modules.js';
|
|
7
|
+
import { snapshotProvider } from './snapshot.js';
|
|
8
|
+
export function compileApplicationLogger(context) {
|
|
9
|
+
const { providers, providerNodes, nodes, diagnostics } = context;
|
|
10
|
+
let root = findProviderNode(context, LoggerToken);
|
|
11
|
+
if (!root) {
|
|
12
|
+
for (const declaration of defaultLoggerProviders()) {
|
|
13
|
+
const provider = snapshotProvider(declaration);
|
|
14
|
+
const node = {
|
|
15
|
+
kind: 'provider',
|
|
16
|
+
id: context.nextProviderId(),
|
|
17
|
+
token: provider.provide,
|
|
18
|
+
provider,
|
|
19
|
+
moduleId: 'application',
|
|
20
|
+
dependencies: collectProviderDependencies(provider),
|
|
21
|
+
};
|
|
22
|
+
providers.push(provider);
|
|
23
|
+
providerNodes.set(node.token, node);
|
|
24
|
+
nodes.push(node);
|
|
25
|
+
}
|
|
26
|
+
root = findProviderNode(context, LoggerToken);
|
|
27
|
+
}
|
|
28
|
+
if (!root)
|
|
29
|
+
return;
|
|
30
|
+
if (root.provider.scope !== 'application') {
|
|
31
|
+
diagnostics.push(diagnostic('LUTRE_LOGGER_SCOPE', 'LoggerToken must have application scope.', root.id));
|
|
32
|
+
}
|
|
33
|
+
if (root.moduleId !== 'application' &&
|
|
34
|
+
!context.roots.some((module) => module.definition.exports?.some(isLoggerToken))) {
|
|
35
|
+
diagnostics.push(diagnostic('LUTRE_LOGGER_VISIBILITY', 'LoggerToken must be exported by a root Module to configure the Application logger.', root.id));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function validateApplicationLogger(context) {
|
|
39
|
+
const root = findProviderNode(context, LoggerToken);
|
|
40
|
+
if (!root)
|
|
41
|
+
return;
|
|
42
|
+
const visited = new Set();
|
|
43
|
+
const visit = (token, lineage) => {
|
|
44
|
+
const canonical = isLoggerToken(token) ? root.token : token;
|
|
45
|
+
const start = lineage.indexOf(canonical);
|
|
46
|
+
if (start >= 0) {
|
|
47
|
+
context.diagnostics.push(diagnostic('LUTRE_LOGGER_CYCLE', `Circular Logger dependency: ${[...lineage.slice(start), canonical].map(tokenName).join(' -> ')}`, root.id));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (visited.has(canonical))
|
|
51
|
+
return;
|
|
52
|
+
visited.add(canonical);
|
|
53
|
+
for (const dependency of findProviderNode(context, canonical)
|
|
54
|
+
?.dependencies ?? []) {
|
|
55
|
+
visit(dependency, [...lineage, canonical]);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
visit(root.token, []);
|
|
59
|
+
}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ProviderDescriptor } from '../provider.js';
|
|
2
|
+
import type { TokenLike } from '../token.js';
|
|
3
|
+
import { type ModelBuildContext } from './context.js';
|
|
2
4
|
export declare function collectModuleDeclarations(context: ModelBuildContext): void;
|
|
5
|
+
export declare function collectProviderDependencies(provider: ProviderDescriptor): readonly TokenLike[];
|