@afora/core 0.0.2 → 0.0.4
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.md +220 -733
- package/dist/core/application/application.d.ts +9 -1
- package/dist/core/application/exceptions.d.ts +28 -0
- package/dist/core/application/registry.d.ts +33 -0
- package/dist/core/application/start.d.ts +75 -0
- package/dist/core/application/types/application.interface.d.ts +229 -7
- package/dist/core/blueprint/types/blueprint.interface.d.ts +66 -11
- package/dist/core/blueprint/types/slice.interface.d.ts +7 -2
- package/dist/core/command/command.d.ts +25 -0
- package/dist/core/command/exceptions.d.ts +33 -0
- package/dist/core/command/types/command.interface.d.ts +108 -0
- package/dist/core/entity/event.d.ts +12 -2
- package/dist/core/environment/environment.d.ts +4 -1
- package/dist/core/exception/exception.d.ts +4 -0
- package/dist/core/exception/exported.d.ts +4 -0
- package/dist/core/json.d.ts +10 -0
- package/dist/core/logger/exceptions.d.ts +4 -0
- package/dist/core/logger/logger.d.ts +106 -0
- package/dist/core/output/exceptions.d.ts +8 -0
- package/dist/core/output/openapi-sink.d.ts +83 -0
- package/dist/core/output/output.d.ts +19 -0
- package/dist/core/output/problem.d.ts +15 -0
- package/dist/core/output/select.d.ts +14 -0
- package/dist/core/output/stage.d.ts +24 -0
- package/dist/core/output/status.d.ts +17 -0
- package/dist/core/output/types/output.interface.d.ts +171 -0
- package/dist/core/property/abstract-property.d.ts +8 -7
- package/dist/core/property/define-property.d.ts +8 -7
- package/dist/core/property/property.d.ts +26 -15
- package/dist/core/property/types/abstract-property-constructor.type.d.ts +8 -7
- package/dist/core/property/types/define-property.interface.d.ts +5 -4
- package/dist/core/property/types/optional-property.type.d.ts +5 -5
- package/dist/core/property/types/property-collection.type.d.ts +2 -2
- package/dist/core/property/types/property-constructor.type.d.ts +5 -1
- package/dist/core/property/types/property-options.interface.d.ts +17 -8
- package/dist/core/property/types/property-type.interface.d.ts +7 -3
- package/dist/core/property/types/property.interface.d.ts +5 -4
- package/dist/core/record/abstract-record.d.ts +12 -4
- package/dist/core/record/types/plugin.interface.d.ts +13 -3
- package/dist/core/record/types/record.interface.d.ts +20 -8
- package/dist/core/repository/exceptions.d.ts +4 -0
- package/dist/core/repository/in-memory.d.ts +3 -0
- package/dist/core/repository/provider.d.ts +6 -0
- package/dist/core/repository/query.d.ts +9 -0
- package/dist/core/repository/references.d.ts +4 -2
- package/dist/core/repository/repository.d.ts +4 -2
- package/dist/core/repository/state.d.ts +4 -0
- package/dist/core/repository/types/query.interface.d.ts +67 -0
- package/dist/core/repository/types/repository.interface.d.ts +13 -13
- package/dist/core/schema/types/schema.interface.d.ts +11 -4
- package/dist/core/symbols/meta.d.ts +2 -0
- package/dist/core/use-case/exceptions.d.ts +10 -0
- package/dist/core/use-case/scope.d.ts +24 -0
- package/dist/core/use-case/steps/describe.d.ts +13 -0
- package/dist/core/use-case/steps/references.d.ts +27 -0
- package/dist/core/use-case/steps/run.d.ts +20 -0
- package/dist/core/use-case/steps/types/steps.interface.d.ts +153 -0
- package/dist/core/use-case/steps/validate.d.ts +17 -0
- package/dist/core/use-case/use-case.d.ts +79 -13
- package/dist/experimental.d.ts +6 -0
- package/dist/experimental.js +123 -0
- package/dist/index-ewm6r4nw.js +5335 -0
- package/dist/index-hn82hm31.js +1651 -0
- package/dist/index.d.ts +34 -12
- package/dist/index.js +343 -4142
- package/dist/std/blueprint-plugin.d.ts +10 -10
- package/dist/std/blueprint.d.ts +10 -10
- package/dist/std/entity.d.ts +2 -2
- package/dist/std/properties/contact.d.ts +4 -4
- package/dist/std/properties/enum.d.ts +1 -1
- package/dist/std/properties/headless.d.ts +1 -1
- package/dist/std/properties/identity.d.ts +2 -2
- package/dist/std/properties/password.d.ts +1 -1
- package/dist/std/properties/primitives.d.ts +5 -5
- package/dist/std/properties/time.d.ts +2 -2
- package/dist/std/record-plugins.d.ts +12 -12
- package/dist/std/record.d.ts +1 -1
- package/dist/std/stages.d.ts +2 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +502 -0
- package/dist/testing/provider-contract.d.ts +22 -0
- package/package.json +28 -8
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { IBlueprintCollection } from "../blueprint/types/blueprint.interface.js";
|
|
2
|
+
import { type EnvironmentDependencies } from "../environment/environment.js";
|
|
3
|
+
import type { IRepositoryProvider } from "../repository/types/repository.interface.js";
|
|
4
|
+
import type { AnyCommand, CommandOf, CommandSettings, CommandUseCases } from "./types/command.interface.js";
|
|
5
|
+
/**
|
|
6
|
+
* Checks now that the repositories of `command` share a provider that has
|
|
7
|
+
* `transaction`, as its first call would; an application calls it when it
|
|
8
|
+
* binds the command to its provider.
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export declare function verifyCommand(command: object): void;
|
|
12
|
+
/**
|
|
13
|
+
* The provider whose transaction runs the calls of `command`, or `undefined`
|
|
14
|
+
* without a transaction or a repository.
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export declare function commandTransactionProvider(command: object): IRepositoryProvider | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Groups use cases under operation names and is the boundary of a call: the
|
|
20
|
+
* context is validated once, a transaction can wrap the use case, and the
|
|
21
|
+
* events it raised are handled by a policy once it succeeded.
|
|
22
|
+
*/
|
|
23
|
+
export declare function commandOf<const UseCases extends CommandUseCases, Context extends IBlueprintCollection | undefined = undefined, Environment extends EnvironmentDependencies | undefined = undefined>(settings: CommandSettings<UseCases, Context, Environment>): CommandOf<UseCases, Context, Environment>;
|
|
24
|
+
/** Whether `value` is a command made by `commandOf`. */
|
|
25
|
+
export declare function isCommand(value: unknown): value is AnyCommand;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Issue } from "../parse/types/parse.interface.js";
|
|
2
|
+
/** `commandOf` got a setting it cannot use; `details.reason` says which. */
|
|
3
|
+
export declare const InvalidCommandDeclaration: import("../../index.js").DeclaredExceptionClass<{
|
|
4
|
+
readonly reason: string;
|
|
5
|
+
}>;
|
|
6
|
+
/** A command was asked for an operation it does not declare. */
|
|
7
|
+
export declare const UnknownCommandOperation: import("../../index.js").DeclaredExceptionClass<{
|
|
8
|
+
readonly operation: string;
|
|
9
|
+
}>;
|
|
10
|
+
/** The context of a call does not fit the context blueprint of the command. */
|
|
11
|
+
export declare const InvalidCommandContext: import("../../index.js").DeclaredExceptionClass<{
|
|
12
|
+
readonly issues: readonly Issue[];
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* The `env` of a call holds a key no environment of the command declares, or
|
|
16
|
+
* a value its blueprint rejects. It is configuration, not input.
|
|
17
|
+
*/
|
|
18
|
+
export declare const InvalidCommandEnvironment: import("../../index.js").DeclaredExceptionClass<{
|
|
19
|
+
readonly keys: readonly string[];
|
|
20
|
+
readonly issues: readonly Issue[];
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* An event handler failed after the call succeeded (and, with a
|
|
24
|
+
* transaction, committed). `handled` events ran before the failure and
|
|
25
|
+
* `remaining` events after it were not handled.
|
|
26
|
+
*/
|
|
27
|
+
export declare const CommandEventFailed: import("../../index.js").DeclaredExceptionClass<{
|
|
28
|
+
readonly operation: string;
|
|
29
|
+
/** The failed event; absent when a handler policy failed. */
|
|
30
|
+
readonly event?: string;
|
|
31
|
+
readonly handled: number;
|
|
32
|
+
readonly remaining: number;
|
|
33
|
+
}>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { BlueprintInput, IBlueprint, IBlueprintCollection } from "../../blueprint/types/blueprint.interface.js";
|
|
2
|
+
import type { EntityEvent } from "../../entity/event.js";
|
|
3
|
+
import type { AnyEnvironment, EnvironmentDependencies, MergedEnvironment } from "../../environment/environment.js";
|
|
4
|
+
import type { ILogger } from "../../logger/logger.js";
|
|
5
|
+
import type { Kind } from "../../symbols/kind.js";
|
|
6
|
+
import type { AnyUseCaseClass, UseCaseInput, UseCaseOptions, UseCaseResponse } from "../../use-case/use-case.js";
|
|
7
|
+
/** The use cases of a command, by operation name. */
|
|
8
|
+
export type CommandUseCases = Readonly<Record<string, AnyUseCaseClass>>;
|
|
9
|
+
/** What a handler policy gets next to the events. */
|
|
10
|
+
export interface CommandEventInfo {
|
|
11
|
+
readonly command: AnyCommand;
|
|
12
|
+
readonly operation: string;
|
|
13
|
+
/** The context of the call, validated by the command's blueprint if it has one. */
|
|
14
|
+
readonly context: unknown;
|
|
15
|
+
/** The variables of the command's environments, with the `env` of the call over them. */
|
|
16
|
+
readonly env: Readonly<Record<string, unknown>>;
|
|
17
|
+
/**
|
|
18
|
+
* The logger of the application that bound the command, else the started
|
|
19
|
+
* application's; a logger that writes nothing without either.
|
|
20
|
+
*/
|
|
21
|
+
readonly logger: ILogger;
|
|
22
|
+
}
|
|
23
|
+
/** Handles all the events of a call, once it succeeded. */
|
|
24
|
+
export type CommandEventHandler = (events: readonly EntityEvent[], info: CommandEventInfo) => void | Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* What the command does with the events of a call, after it commits:
|
|
27
|
+
* `"after-commit"` awaits `event.handle()` in order, `"manual"` leaves them
|
|
28
|
+
* to `execute`, a function receives them all.
|
|
29
|
+
*/
|
|
30
|
+
export type CommandEvents = "after-commit" | "manual" | CommandEventHandler;
|
|
31
|
+
/** Called when an event fails with the `"after-commit"` policy; the next event still runs. */
|
|
32
|
+
export type CommandEventErrorHandler = (error: unknown, event: EntityEvent, info: CommandEventInfo) => void | Promise<void>;
|
|
33
|
+
export interface CommandSettings<UseCases extends CommandUseCases, Context extends IBlueprintCollection | undefined = undefined, Environment extends EnvironmentDependencies | undefined = undefined> {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly description?: string;
|
|
36
|
+
readonly tags?: readonly string[];
|
|
37
|
+
/** What the caller supplies with each call; validated once by this blueprint. */
|
|
38
|
+
readonly context?: IBlueprint<Exclude<Context, undefined>>;
|
|
39
|
+
readonly useCases: UseCases;
|
|
40
|
+
/** Runs every call in a transaction of the provider of the repositories. Default `false`. */
|
|
41
|
+
readonly transaction?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* One environment, or a list merged in order: read on first use as
|
|
44
|
+
* `command.env`, given to the event handlers, and to the use cases that
|
|
45
|
+
* declare the same environment, with the `env` of a call over them.
|
|
46
|
+
*/
|
|
47
|
+
readonly environmentDependencies?: Environment;
|
|
48
|
+
readonly events?: CommandEvents;
|
|
49
|
+
readonly onEventError?: CommandEventErrorHandler;
|
|
50
|
+
}
|
|
51
|
+
/** The names of the operations of a command. */
|
|
52
|
+
export type CommandOperation<Command extends {
|
|
53
|
+
readonly useCases: object;
|
|
54
|
+
}> = keyof Command["useCases"] & string;
|
|
55
|
+
/** The input of an operation. */
|
|
56
|
+
export type CommandInput<Command extends {
|
|
57
|
+
readonly useCases: CommandUseCases;
|
|
58
|
+
}, Op extends CommandOperation<Command>> = UseCaseInput<Command["useCases"][Op]>;
|
|
59
|
+
/** The response of an operation. */
|
|
60
|
+
export type CommandResponse<Command extends {
|
|
61
|
+
readonly useCases: CommandUseCases;
|
|
62
|
+
}, Op extends CommandOperation<Command>> = UseCaseResponse<Command["useCases"][Op]>;
|
|
63
|
+
/**
|
|
64
|
+
* The options of a call: `{ context }` of the command when it declares one,
|
|
65
|
+
* otherwise those of the use case.
|
|
66
|
+
*/
|
|
67
|
+
export type CommandOptions<Class extends AnyUseCaseClass, Context extends IBlueprintCollection | undefined, Environment extends EnvironmentDependencies | undefined = undefined> = Context extends IBlueprintCollection ? [
|
|
68
|
+
options: {
|
|
69
|
+
readonly context: BlueprintInput<Context>;
|
|
70
|
+
} & CommandEnvOption<Environment>
|
|
71
|
+
] : Class["context"] extends IBlueprint<infer Properties> ? [options: UseCaseOptions<Properties> & CommandEnvOption<Environment>] : Environment extends undefined ? [] : [options?: CommandEnvOption<Environment>];
|
|
72
|
+
/**
|
|
73
|
+
* The `env` a call can give, when the command declares environments: some of
|
|
74
|
+
* their variables, in place of the loaded ones for this call.
|
|
75
|
+
*/
|
|
76
|
+
export type CommandEnvOption<Environment extends EnvironmentDependencies | undefined> = Environment extends undefined ? {
|
|
77
|
+
readonly env?: undefined;
|
|
78
|
+
} : {
|
|
79
|
+
readonly env?: Partial<MergedEnvironment<Environment>>;
|
|
80
|
+
};
|
|
81
|
+
/** What `execute` gives: the response and the events, with no policy applied. */
|
|
82
|
+
export interface CommandExecution<Value> {
|
|
83
|
+
readonly value: Value;
|
|
84
|
+
readonly events: EntityEvent[];
|
|
85
|
+
}
|
|
86
|
+
/** A command made by `commandOf`. */
|
|
87
|
+
export interface CommandOf<UseCases extends CommandUseCases, Context extends IBlueprintCollection | undefined = undefined, in out Environment extends EnvironmentDependencies | undefined = undefined> {
|
|
88
|
+
readonly [Kind]: "command";
|
|
89
|
+
readonly commandName: string;
|
|
90
|
+
readonly description: string | undefined;
|
|
91
|
+
readonly tags: readonly string[];
|
|
92
|
+
readonly context: Context extends IBlueprintCollection ? IBlueprint<Context> : undefined;
|
|
93
|
+
readonly useCases: Readonly<UseCases>;
|
|
94
|
+
readonly transaction: boolean;
|
|
95
|
+
/** The environments of `environmentDependencies`, as a list. */
|
|
96
|
+
readonly environments: readonly AnyEnvironment[];
|
|
97
|
+
/** Their variables, merged; read and validated on first use (`InvalidEnvironment`). */
|
|
98
|
+
readonly env: MergedEnvironment<Environment>;
|
|
99
|
+
readonly events: CommandEvents;
|
|
100
|
+
readonly onEventError: CommandEventErrorHandler | undefined;
|
|
101
|
+
readonly operations: readonly (keyof UseCases & string)[];
|
|
102
|
+
/** Runs `op` and applies the event policy; gives its response. */
|
|
103
|
+
run<Op extends keyof UseCases & string>(op: Op, input: UseCaseInput<UseCases[Op]>, ...options: CommandOptions<UseCases[Op], Context, Environment>): Promise<UseCaseResponse<UseCases[Op]>>;
|
|
104
|
+
/** Runs `op` and gives the response and the events, without applying the policy. */
|
|
105
|
+
execute<Op extends keyof UseCases & string>(op: Op, input: UseCaseInput<UseCases[Op]>, ...options: CommandOptions<UseCases[Op], Context, Environment>): Promise<CommandExecution<UseCaseResponse<UseCases[Op]>>>;
|
|
106
|
+
}
|
|
107
|
+
/** Any command. */
|
|
108
|
+
export type AnyCommand = CommandOf<any, any, any>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ILogger } from "../logger/logger.js";
|
|
1
2
|
/** A class made by `entityOf`, including subclasses. */
|
|
2
3
|
export declare const EntityClassBrand: unique symbol;
|
|
3
4
|
export type AnyEntityClass = (abstract new (value: never) => object) & {
|
|
@@ -9,11 +10,20 @@ export interface EventDefinition {
|
|
|
9
10
|
readonly name: string;
|
|
10
11
|
readonly [EventBrand]: true;
|
|
11
12
|
}
|
|
13
|
+
/** What the handler of an event gets next to the entity. */
|
|
14
|
+
export interface EventHandlerContext {
|
|
15
|
+
/**
|
|
16
|
+
* The logger of the application whose command raised the event; else the
|
|
17
|
+
* started application's; a logger that writes nothing without either.
|
|
18
|
+
*/
|
|
19
|
+
readonly logger: ILogger;
|
|
20
|
+
}
|
|
12
21
|
/** One queued occurrence, referencing the original entity rather than a snapshot. */
|
|
13
22
|
export interface EntityEvent<Entity = object> {
|
|
14
23
|
readonly name: string;
|
|
15
24
|
readonly entity: Entity;
|
|
16
|
-
|
|
25
|
+
/** Runs the handler; `logger` replaces the one it would get. */
|
|
26
|
+
handle(context?: Partial<EventHandlerContext>): Promise<void>;
|
|
17
27
|
}
|
|
18
28
|
export type EntityEventReference = EventDefinition | readonly EventDefinition[] | (() => EventDefinition | readonly EventDefinition[]);
|
|
19
29
|
export interface EntityEvents {
|
|
@@ -23,7 +33,7 @@ export interface EntityEvents {
|
|
|
23
33
|
}
|
|
24
34
|
export declare const EntityEventSettings: unique symbol;
|
|
25
35
|
/** Defines an event without executing its handler. */
|
|
26
|
-
export declare function eventOf<Class extends AnyEntityClass>(name: string, entityClass: Class, handler: (entity: InstanceType<Class
|
|
36
|
+
export declare function eventOf<Class extends AnyEntityClass>(name: string, entityClass: Class, handler: (entity: InstanceType<Class>, context: EventHandlerContext) => Promise<void>): EventDefinition;
|
|
27
37
|
export declare function enqueueEvent(entity: object, event: EventDefinition): void;
|
|
28
38
|
export declare function pullEntityEvents<Entity extends object>(entity: Entity): EntityEvent<Entity>[];
|
|
29
39
|
export declare function enqueueLifecycle(entity: object, cycle: keyof EntityEvents): void;
|
|
@@ -24,7 +24,10 @@ export type AnyEnvironment = IEnvironment<any>;
|
|
|
24
24
|
export type EnvironmentValue<Environment extends AnyEnvironment> = ReturnType<Environment["load"]>;
|
|
25
25
|
/** One environment, or a list of them, whose variables are merged in order. */
|
|
26
26
|
export type EnvironmentDependencies = AnyEnvironment | readonly AnyEnvironment[];
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* The variables of every environment of `Dependencies`, merged.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
28
31
|
export type MergedEnvironment<Dependencies extends EnvironmentDependencies | undefined> = Dependencies extends AnyEnvironment ? EnvironmentValue<Dependencies> : Dependencies extends readonly AnyEnvironment[] ? UnionToIntersection<EnvironmentValue<Dependencies[number]>> & Readonly<Record<never, never>> : Readonly<Record<never, never>>;
|
|
29
32
|
type UnionToIntersection<Union> = (Union extends unknown ? (value: Union) => void : never) extends (value: infer Intersection) => void ? Intersection : never;
|
|
30
33
|
/** A variable of the environment is missing or rejected by its blueprint. */
|
|
@@ -47,6 +47,10 @@ export interface DeclaredExceptionClass<Details = undefined> {
|
|
|
47
47
|
readonly layer: ExceptionLayers;
|
|
48
48
|
from: typeof Exception.from;
|
|
49
49
|
}
|
|
50
|
+
/** Whether `value` is a class made by `exceptionOf`. */
|
|
51
|
+
export declare function isDeclaredException(value: unknown): value is DeclaredExceptionClass<never>;
|
|
52
|
+
/** The message `exceptionOf` got, when it was a string; `undefined` otherwise. */
|
|
53
|
+
export declare function declaredMessageOf(Declared: DeclaredExceptionClass<never>): string | undefined;
|
|
50
54
|
/**
|
|
51
55
|
* Declares an exception of `layer`, e.g.
|
|
52
56
|
* `throw new PropertyRequired({ source: "email" })`. Its `details` are typed
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Counts `Exception` classes the package exports from a module the core cannot import. */
|
|
2
|
+
export declare function markExported(...classes: readonly unknown[]): void;
|
|
3
|
+
/** Whether `Declared` is an exception class exported by the package. */
|
|
4
|
+
export declare function isExportedException(Declared: unknown): boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** A JSON value. */
|
|
2
|
+
export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
|
|
3
|
+
readonly [key: string]: JsonValue;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* A deep, frozen copy of `value`, which must be finite, acyclic JSON made of
|
|
7
|
+
* plain objects and arrays. `subject` names it in the messages (e.g.
|
|
8
|
+
* "Provider options") and `fail` raises the error of the caller.
|
|
9
|
+
*/
|
|
10
|
+
export declare function frozenJson(value: unknown, subject: string, fail: (reason: string) => never, seen?: Set<object>): JsonValue;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Kind } from "../symbols/kind.js";
|
|
2
|
+
/** What a log line carries besides its event. */
|
|
3
|
+
export type LogFields = Readonly<Record<string, unknown>>;
|
|
4
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
5
|
+
export declare const LogLevels: readonly LogLevel[];
|
|
6
|
+
/** What logs: an application, a use case through its `Logger` dependency, your code. */
|
|
7
|
+
export interface ILogger {
|
|
8
|
+
debug(event: string, fields?: LogFields): void;
|
|
9
|
+
info(event: string, fields?: LogFields): void;
|
|
10
|
+
warn(event: string, fields?: LogFields): void;
|
|
11
|
+
error(event: string, fields?: LogFields): void;
|
|
12
|
+
}
|
|
13
|
+
/** One line, as a log provider gets it. */
|
|
14
|
+
export interface LogEntry {
|
|
15
|
+
/** ISO 8601. */
|
|
16
|
+
readonly at: string;
|
|
17
|
+
readonly level: LogLevel;
|
|
18
|
+
readonly event: string;
|
|
19
|
+
readonly fields: LogFields;
|
|
20
|
+
/** The name of the application that logged it. */
|
|
21
|
+
readonly application: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Where lines go: the console, JSON on stdout, a log service, memory. An
|
|
25
|
+
* application writes each line to every provider of its `log`.
|
|
26
|
+
*/
|
|
27
|
+
export interface ILogProvider {
|
|
28
|
+
write(entry: LogEntry): void | Promise<void>;
|
|
29
|
+
/** Called on `app.stop()`, to send what is still buffered. */
|
|
30
|
+
flush?(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
/** The `log` of an application. */
|
|
33
|
+
export interface LogSettings {
|
|
34
|
+
/** Lines below it are dropped. `"info"` by default. */
|
|
35
|
+
readonly level?: LogLevel;
|
|
36
|
+
readonly providers: readonly ILogProvider[];
|
|
37
|
+
}
|
|
38
|
+
/** What `logProviderOf` takes. */
|
|
39
|
+
export interface LogProviderSettings<Provider extends ILogProvider> {
|
|
40
|
+
/** Shown by `app.describe()`; anonymous providers are opaque. */
|
|
41
|
+
readonly name?: string;
|
|
42
|
+
readonly write: Provider["write"];
|
|
43
|
+
readonly flush?: Provider["flush"];
|
|
44
|
+
}
|
|
45
|
+
/** A log provider, checked, and named for `describe()` when `name` is given. */
|
|
46
|
+
export declare function logProviderOf(settings: LogProviderSettings<ILogProvider>): ILogProvider;
|
|
47
|
+
/** The name `logProviderOf` gave a provider, if any. */
|
|
48
|
+
export declare function logProviderNameOf(provider: ILogProvider): string | undefined;
|
|
49
|
+
/** A readable line on the console, by level: for local development. */
|
|
50
|
+
export declare function consoleLogProvider(): ILogProvider;
|
|
51
|
+
/** One JSON object per line on stdout: for Lambda, containers, CloudWatch. */
|
|
52
|
+
export declare function jsonLogProvider(): ILogProvider;
|
|
53
|
+
/** A provider that keeps the lines, for tests. */
|
|
54
|
+
export interface MemoryLogProvider extends ILogProvider {
|
|
55
|
+
readonly entries: readonly LogEntry[];
|
|
56
|
+
clear(): void;
|
|
57
|
+
}
|
|
58
|
+
/** Keeps every line in `entries`, for tests. */
|
|
59
|
+
export declare function memoryLogProvider(): MemoryLogProvider;
|
|
60
|
+
/** Throws unless `settings` is a usable `log`; gives it with the default level. */
|
|
61
|
+
export declare function checkLogSettings(settings: unknown): Required<LogSettings>;
|
|
62
|
+
/** A logger that writes nothing. */
|
|
63
|
+
export declare const silentLogger: ILogger;
|
|
64
|
+
/**
|
|
65
|
+
* The logger of an application: drops what is below `level`, writes the rest
|
|
66
|
+
* to every provider, and never lets a provider break the call or the others.
|
|
67
|
+
* `settings` can be set later (a `log` made on start) and unset on stop.
|
|
68
|
+
*/
|
|
69
|
+
export declare function applicationLogger(application: string): {
|
|
70
|
+
logger: ILogger;
|
|
71
|
+
readonly settings: Required<LogSettings> | undefined;
|
|
72
|
+
set(settings: Required<LogSettings> | undefined): void;
|
|
73
|
+
flush(): Promise<void>;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* The `Logger` dependency of a use case: `dependencies: { logger: Logger }`
|
|
77
|
+
* gives an `ILogger`, the one of the application it runs in.
|
|
78
|
+
*/
|
|
79
|
+
export interface LoggerDeclaration {
|
|
80
|
+
readonly [Kind]: "logger";
|
|
81
|
+
}
|
|
82
|
+
export declare const Logger: LoggerDeclaration;
|
|
83
|
+
export declare function isLoggerDeclaration(value: unknown): value is LoggerDeclaration;
|
|
84
|
+
/** Whether `value` can stand for a logger (an override in `execute`). */
|
|
85
|
+
export declare function isLogger(value: unknown): value is ILogger;
|
|
86
|
+
/**
|
|
87
|
+
* Carries the logger of an application, when it has a `log`: on the
|
|
88
|
+
* application and on the use case classes it binds.
|
|
89
|
+
*/
|
|
90
|
+
export declare const ApplicationLoggerKey: unique symbol;
|
|
91
|
+
/**
|
|
92
|
+
* The logger a call writes to: the one its scope carries, else the one of
|
|
93
|
+
* the application its class was bound by, else the started application's.
|
|
94
|
+
*/
|
|
95
|
+
export declare function resolveLogger(scoped: ILogger | undefined, owner: unknown): ILogger | undefined;
|
|
96
|
+
/** Milliseconds, for timing; `Date.now` where there is no `performance`. */
|
|
97
|
+
export declare function clock(): number;
|
|
98
|
+
export declare function elapsed(since: number): number;
|
|
99
|
+
/**
|
|
100
|
+
* Logs a failure once: the same exception passes through every use case and
|
|
101
|
+
* command up the call, each wrapping it. `warn` for a declared exception,
|
|
102
|
+
* `error` for an unexpected one, with its trace.
|
|
103
|
+
*/
|
|
104
|
+
export declare function logFailure(logger: ILogger | undefined, event: string, fields: LogFields, error: unknown): void;
|
|
105
|
+
/** Writes one line; a logger that throws never breaks the call it logs. */
|
|
106
|
+
export declare function writeLog(logger: ILogger | undefined, level: LogLevel, event: string, fields: LogFields): void;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** `outputOf`, an operation patch or a stage got something it cannot use; `details.reason` says what. */
|
|
2
|
+
export declare const InvalidOutputStage: import("../../index.js").DeclaredExceptionClass<{
|
|
3
|
+
readonly reason: string;
|
|
4
|
+
}>;
|
|
5
|
+
/** Two operations of an output share an id. */
|
|
6
|
+
export declare const DuplicateOperation: import("../../index.js").DeclaredExceptionClass<{
|
|
7
|
+
readonly id: string;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { OpenAPISchema } from "../openapi/types/openapi.interface.js";
|
|
2
|
+
import type { OperationDescriptor } from "./types/output.interface.js";
|
|
3
|
+
declare const METHODS: readonly ["get", "post", "put", "patch", "delete"];
|
|
4
|
+
/** An HTTP method an operation can be routed to. */
|
|
5
|
+
export type OpenAPIMethod = (typeof METHODS)[number];
|
|
6
|
+
/** The options of the `openapi` sink. */
|
|
7
|
+
export interface OpenAPISinkOptions {
|
|
8
|
+
readonly info: {
|
|
9
|
+
readonly title: string;
|
|
10
|
+
readonly version: string;
|
|
11
|
+
readonly description?: string;
|
|
12
|
+
};
|
|
13
|
+
readonly servers?: readonly {
|
|
14
|
+
readonly url: string;
|
|
15
|
+
readonly description?: string;
|
|
16
|
+
}[];
|
|
17
|
+
/** Prefix of every path, e.g. `"/api"`. Empty by default. */
|
|
18
|
+
readonly basePath?: string;
|
|
19
|
+
/** Method and path by operation id, e.g. `{ "Users.login": { path: "/session" } }`. */
|
|
20
|
+
readonly route?: {
|
|
21
|
+
readonly [operationId: string]: {
|
|
22
|
+
readonly method?: OpenAPIMethod;
|
|
23
|
+
readonly path?: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** An OpenAPI 3.1 document. */
|
|
28
|
+
export interface OpenAPIDocument {
|
|
29
|
+
readonly openapi: "3.1.0";
|
|
30
|
+
readonly info: OpenAPISinkOptions["info"];
|
|
31
|
+
readonly servers?: OpenAPISinkOptions["servers"];
|
|
32
|
+
readonly paths: {
|
|
33
|
+
readonly [path: string]: Record<string, unknown>;
|
|
34
|
+
};
|
|
35
|
+
readonly components: {
|
|
36
|
+
readonly schemas: {
|
|
37
|
+
readonly [name: string]: OpenAPISchema;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** The options that route operations: shared by the sinks that serve HTTP. */
|
|
42
|
+
export interface RouteOptions {
|
|
43
|
+
readonly basePath?: string;
|
|
44
|
+
readonly route?: OpenAPISinkOptions["route"];
|
|
45
|
+
}
|
|
46
|
+
/** Where an operation is served. */
|
|
47
|
+
export interface OperationRoute {
|
|
48
|
+
readonly method: OpenAPIMethod;
|
|
49
|
+
/** `basePath` included. */
|
|
50
|
+
readonly path: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Why `basePath` and `route` are not valid routing options, naming them
|
|
54
|
+
* `where.basePath` and `where.route`; `undefined` when they are.
|
|
55
|
+
*/
|
|
56
|
+
export declare function routeOptionsIssue(options: RouteOptions, where: string): string | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* The route of every operation, by id: `POST {basePath}/{kebab(command)}/{kebab(operation)}`,
|
|
59
|
+
* changed per operation id with `route`. Throws `InvalidOutputStage` (with
|
|
60
|
+
* `source`) when `route` names an unknown operation or two operations share a
|
|
61
|
+
* method and path. The routing of the `openapi` sink and of the sinks that
|
|
62
|
+
* serve HTTP.
|
|
63
|
+
*/
|
|
64
|
+
export declare function operationRoutes(operations: readonly OperationDescriptor[], options: RouteOptions, source?: string): ReadonlyMap<string, OperationRoute>;
|
|
65
|
+
/**
|
|
66
|
+
* Builds an OpenAPI 3.1 document from the operations.
|
|
67
|
+
*
|
|
68
|
+
* Routes: `POST {basePath}/{kebab(command)}/{kebab(operation)}`, changed per
|
|
69
|
+
* operation id with `route`; two operations on one method and path throw
|
|
70
|
+
* `InvalidOutputStage`. The `operationId` is the id with its dot as an
|
|
71
|
+
* underscore (`Users.register` -> `Users_register`), unique or the build
|
|
72
|
+
* throws. `get` and `delete` send the input as query parameters, which needs
|
|
73
|
+
* every input field to be a scalar property. Responses: `200` with the
|
|
74
|
+
* response schema, `400` and `500` always, and the status of each declared
|
|
75
|
+
* error (`statusOf`: `statuses` of the output, else 422). Operations carry no
|
|
76
|
+
* repository dependencies, so `404` and `409` appear only when an error that
|
|
77
|
+
* maps to them is declared. Records and entities reachable from inputs and
|
|
78
|
+
* responses are named by class name in `components.schemas`, beside `Problem`
|
|
79
|
+
* (RFC 9457). The `context` of an operation is transport-specific and not
|
|
80
|
+
* part of the document; sinks that serve HTTP map it (headers, middleware).
|
|
81
|
+
*/
|
|
82
|
+
export declare const openapi: import("./types/output.interface.js").SinkFactory<OpenAPIDocument, OpenAPISinkOptions>;
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AnyOutput, OperationDescriptor, OperationPatch, OutputOf, OutputSettings, PipeResult, Stage } from "./types/output.interface.js";
|
|
2
|
+
/**
|
|
3
|
+
* A modified copy of `operation`. Only `meta` (replaced), `tags`,
|
|
4
|
+
* `description`, `deprecated` and `invoke` can change; `kind`, `version`,
|
|
5
|
+
* `id`, `command`, `name`, `useCase`, `input`, `response`, `context` and
|
|
6
|
+
* `errors` cannot, and a patch naming them throws `InvalidOutputStage`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function operationOf(operation: OperationDescriptor, patch: OperationPatch): OperationDescriptor;
|
|
9
|
+
/**
|
|
10
|
+
* An output turns commands into operations and runs them through a pipe of
|
|
11
|
+
* stages: transforms change them, and a last sink builds something concrete
|
|
12
|
+
* (an OpenAPI document, an HTTP app, ...). The operations are computed once,
|
|
13
|
+
* at declaration; `build()` runs the sink the first time it is called.
|
|
14
|
+
* Throws `InvalidOutputStage` (or `DuplicateOperation`) for a bad declaration.
|
|
15
|
+
* `commands` takes commands, applications (all their commands) or a mix.
|
|
16
|
+
*/
|
|
17
|
+
export declare function outputOf<const Pipe extends readonly Stage[] = readonly []>(settings: OutputSettings<Pipe>): OutputOf<PipeResult<Pipe>>;
|
|
18
|
+
/** Whether `value` is an output made by `outputOf`. */
|
|
19
|
+
export declare function isOutput(value: unknown): value is AnyOutput;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Problem, ProblemOptions } from "./types/output.interface.js";
|
|
2
|
+
/** The media type of a problem response. */
|
|
3
|
+
export declare const PROBLEM_MEDIA_TYPE = "application/problem+json";
|
|
4
|
+
/**
|
|
5
|
+
* An RFC 9457 problem for `error`. `issues` (`path`, `code`, `message`; no
|
|
6
|
+
* values or causes) is present only for a 400, `trace` only with `debug`,
|
|
7
|
+
* and the `detail` of a 5xx is `"Internal error."` unless `debug` is set, so
|
|
8
|
+
* internal messages do not leak.
|
|
9
|
+
*/
|
|
10
|
+
export declare function problemOf(error: unknown, options: ProblemOptions): Problem;
|
|
11
|
+
/**
|
|
12
|
+
* The JSON a sink sends: `JSON.parse(JSON.stringify(value))`. Every record
|
|
13
|
+
* goes through its `toJSON`, so sensitive values are always masked.
|
|
14
|
+
*/
|
|
15
|
+
export declare function outputValueOf(value: unknown): unknown;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The options of `select`. */
|
|
2
|
+
export interface SelectOptions {
|
|
3
|
+
/** Keep operations that have any of these tags. */
|
|
4
|
+
tags?: string[];
|
|
5
|
+
/** Keep only these operation ids, e.g. `"Users.register"`. */
|
|
6
|
+
operations?: string[];
|
|
7
|
+
/** Drop these operation ids. */
|
|
8
|
+
exclude?: string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Keeps the operations that match every given option: `tags` (any of),
|
|
12
|
+
* `operations` (ids to keep) and `exclude` (ids to drop).
|
|
13
|
+
*/
|
|
14
|
+
export declare const select: import("./types/output.interface.js").TransformFactory<SelectOptions>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { IStageCatalog, SinkFactory, SinkSettings, Stage, StageDescription, StageFactory, StageOptions, TransformFactory, TransformSettings } from "./types/output.interface.js";
|
|
2
|
+
/** A factory of transform stages: `select({ tags: ["public"] })`. */
|
|
3
|
+
export declare function transformOf<Options extends object = StageOptions>(settings: TransformSettings<Options>): TransformFactory<Options>;
|
|
4
|
+
/** A factory of sink stages: `openapi({ info })`. */
|
|
5
|
+
export declare function sinkOf<Result, Options extends object = StageOptions>(settings: SinkSettings<Result, Options>): SinkFactory<Result, Options>;
|
|
6
|
+
/**
|
|
7
|
+
* What a stage is: its kind, name and JSON options when it came from a
|
|
8
|
+
* factory, `{ kind: "opaque" }` otherwise (a hand-written stage needs an
|
|
9
|
+
* import mapping to be extracted).
|
|
10
|
+
*/
|
|
11
|
+
export declare function stageDescriptionOf(stage: Stage): StageDescription;
|
|
12
|
+
/**
|
|
13
|
+
* Whether a factory made by `transformOf` or `sinkOf` builds transforms or
|
|
14
|
+
* sinks, without calling it; `undefined` for any other function.
|
|
15
|
+
*/
|
|
16
|
+
export declare function stageKindOf(factory: StageFactory): "transform" | "sink" | undefined;
|
|
17
|
+
/** Whether `value` is shaped as a stage (made by a factory or written by hand). */
|
|
18
|
+
export declare function isStage(value: unknown): value is Stage;
|
|
19
|
+
/**
|
|
20
|
+
* A catalog of stage `factories`, by name: what resolves the name and
|
|
21
|
+
* options of a JSON pipe back to a stage. Throws `InvalidOutputStage` for a
|
|
22
|
+
* function that is not a factory or for two with the same name.
|
|
23
|
+
*/
|
|
24
|
+
export declare function stageCatalogOf<const Factories extends readonly StageFactory[]>(...stages: Factories): IStageCatalog;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Exception } from "../exception/exception.js";
|
|
2
|
+
import type { OperationDescriptor, OutputStatuses } from "./types/output.interface.js";
|
|
3
|
+
/**
|
|
4
|
+
* The exception that raised `error` under its name: a use case or a command
|
|
5
|
+
* passes an exception up as a copy with the same name, no details and the
|
|
6
|
+
* original as its cause, so the original is the deepest one with that name.
|
|
7
|
+
*/
|
|
8
|
+
export declare function raisedOf(error: Exception): Exception;
|
|
9
|
+
/**
|
|
10
|
+
* The HTTP-style status of an error, in this order: `statuses` by exception
|
|
11
|
+
* name; 400 for invalid input or context (and any exception whose
|
|
12
|
+
* `details.issues` is a list, except `InvalidCommandEnvironment`, a 500); 404 for a missing entity or operation; 409 for
|
|
13
|
+
* a unique violation; 422 for a class in `operation.errors`; 500 for the
|
|
14
|
+
* rest, errors that are not exceptions included. Exceptions match by name,
|
|
15
|
+
* or by class for `operation.errors`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function statusOf(error: unknown, operation?: Pick<OperationDescriptor, "errors">, statuses?: OutputStatuses): number;
|