@afora/core 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +51 -0
- package/dist/core/application/types/application.interface.d.ts +186 -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 +102 -0
- 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/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 +21 -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 +74 -9
- package/dist/experimental.d.ts +6 -0
- package/dist/experimental.js +123 -0
- package/dist/index-hn82hm31.js +1651 -0
- package/dist/index-sw2b9ehx.js +5019 -0
- package/dist/index.d.ts +30 -11
- package/dist/index.js +325 -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,102 @@
|
|
|
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 { Kind } from "../../symbols/kind.js";
|
|
5
|
+
import type { AnyUseCaseClass, UseCaseInput, UseCaseOptions, UseCaseResponse } from "../../use-case/use-case.js";
|
|
6
|
+
/** The use cases of a command, by operation name. */
|
|
7
|
+
export type CommandUseCases = Readonly<Record<string, AnyUseCaseClass>>;
|
|
8
|
+
/** What a handler policy gets next to the events. */
|
|
9
|
+
export interface CommandEventInfo {
|
|
10
|
+
readonly command: AnyCommand;
|
|
11
|
+
readonly operation: string;
|
|
12
|
+
/** The context of the call, validated by the command's blueprint if it has one. */
|
|
13
|
+
readonly context: unknown;
|
|
14
|
+
/** The variables of the command's environments, with the `env` of the call over them. */
|
|
15
|
+
readonly env: Readonly<Record<string, unknown>>;
|
|
16
|
+
}
|
|
17
|
+
/** Handles all the events of a call, once it succeeded. */
|
|
18
|
+
export type CommandEventHandler = (events: readonly EntityEvent[], info: CommandEventInfo) => void | Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* What the command does with the events of a call, after it commits:
|
|
21
|
+
* `"after-commit"` awaits `event.handle()` in order, `"manual"` leaves them
|
|
22
|
+
* to `execute`, a function receives them all.
|
|
23
|
+
*/
|
|
24
|
+
export type CommandEvents = "after-commit" | "manual" | CommandEventHandler;
|
|
25
|
+
/** Called when an event fails with the `"after-commit"` policy; the next event still runs. */
|
|
26
|
+
export type CommandEventErrorHandler = (error: unknown, event: EntityEvent, info: CommandEventInfo) => void | Promise<void>;
|
|
27
|
+
export interface CommandSettings<UseCases extends CommandUseCases, Context extends IBlueprintCollection | undefined = undefined, Environment extends EnvironmentDependencies | undefined = undefined> {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly description?: string;
|
|
30
|
+
readonly tags?: readonly string[];
|
|
31
|
+
/** What the caller supplies with each call; validated once by this blueprint. */
|
|
32
|
+
readonly context?: IBlueprint<Exclude<Context, undefined>>;
|
|
33
|
+
readonly useCases: UseCases;
|
|
34
|
+
/** Runs every call in a transaction of the provider of the repositories. Default `false`. */
|
|
35
|
+
readonly transaction?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* One environment, or a list merged in order: read on first use as
|
|
38
|
+
* `command.env`, given to the event handlers, and to the use cases that
|
|
39
|
+
* declare the same environment, with the `env` of a call over them.
|
|
40
|
+
*/
|
|
41
|
+
readonly environmentDependencies?: Environment;
|
|
42
|
+
readonly events?: CommandEvents;
|
|
43
|
+
readonly onEventError?: CommandEventErrorHandler;
|
|
44
|
+
}
|
|
45
|
+
/** The names of the operations of a command. */
|
|
46
|
+
export type CommandOperation<Command extends {
|
|
47
|
+
readonly useCases: object;
|
|
48
|
+
}> = keyof Command["useCases"] & string;
|
|
49
|
+
/** The input of an operation. */
|
|
50
|
+
export type CommandInput<Command extends {
|
|
51
|
+
readonly useCases: CommandUseCases;
|
|
52
|
+
}, Op extends CommandOperation<Command>> = UseCaseInput<Command["useCases"][Op]>;
|
|
53
|
+
/** The response of an operation. */
|
|
54
|
+
export type CommandResponse<Command extends {
|
|
55
|
+
readonly useCases: CommandUseCases;
|
|
56
|
+
}, Op extends CommandOperation<Command>> = UseCaseResponse<Command["useCases"][Op]>;
|
|
57
|
+
/**
|
|
58
|
+
* The options of a call: `{ context }` of the command when it declares one,
|
|
59
|
+
* otherwise those of the use case.
|
|
60
|
+
*/
|
|
61
|
+
export type CommandOptions<Class extends AnyUseCaseClass, Context extends IBlueprintCollection | undefined, Environment extends EnvironmentDependencies | undefined = undefined> = Context extends IBlueprintCollection ? [
|
|
62
|
+
options: {
|
|
63
|
+
readonly context: BlueprintInput<Context>;
|
|
64
|
+
} & CommandEnvOption<Environment>
|
|
65
|
+
] : Class["context"] extends IBlueprint<infer Properties> ? [options: UseCaseOptions<Properties> & CommandEnvOption<Environment>] : Environment extends undefined ? [] : [options?: CommandEnvOption<Environment>];
|
|
66
|
+
/**
|
|
67
|
+
* The `env` a call can give, when the command declares environments: some of
|
|
68
|
+
* their variables, in place of the loaded ones for this call.
|
|
69
|
+
*/
|
|
70
|
+
export type CommandEnvOption<Environment extends EnvironmentDependencies | undefined> = Environment extends undefined ? {
|
|
71
|
+
readonly env?: undefined;
|
|
72
|
+
} : {
|
|
73
|
+
readonly env?: Partial<MergedEnvironment<Environment>>;
|
|
74
|
+
};
|
|
75
|
+
/** What `execute` gives: the response and the events, with no policy applied. */
|
|
76
|
+
export interface CommandExecution<Value> {
|
|
77
|
+
readonly value: Value;
|
|
78
|
+
readonly events: EntityEvent[];
|
|
79
|
+
}
|
|
80
|
+
/** A command made by `commandOf`. */
|
|
81
|
+
export interface CommandOf<UseCases extends CommandUseCases, Context extends IBlueprintCollection | undefined = undefined, in out Environment extends EnvironmentDependencies | undefined = undefined> {
|
|
82
|
+
readonly [Kind]: "command";
|
|
83
|
+
readonly commandName: string;
|
|
84
|
+
readonly description: string | undefined;
|
|
85
|
+
readonly tags: readonly string[];
|
|
86
|
+
readonly context: Context extends IBlueprintCollection ? IBlueprint<Context> : undefined;
|
|
87
|
+
readonly useCases: Readonly<UseCases>;
|
|
88
|
+
readonly transaction: boolean;
|
|
89
|
+
/** The environments of `environmentDependencies`, as a list. */
|
|
90
|
+
readonly environments: readonly AnyEnvironment[];
|
|
91
|
+
/** Their variables, merged; read and validated on first use (`InvalidEnvironment`). */
|
|
92
|
+
readonly env: MergedEnvironment<Environment>;
|
|
93
|
+
readonly events: CommandEvents;
|
|
94
|
+
readonly onEventError: CommandEventErrorHandler | undefined;
|
|
95
|
+
readonly operations: readonly (keyof UseCases & string)[];
|
|
96
|
+
/** Runs `op` and applies the event policy; gives its response. */
|
|
97
|
+
run<Op extends keyof UseCases & string>(op: Op, input: UseCaseInput<UseCases[Op]>, ...options: CommandOptions<UseCases[Op], Context, Environment>): Promise<UseCaseResponse<UseCases[Op]>>;
|
|
98
|
+
/** Runs `op` and gives the response and the events, without applying the policy. */
|
|
99
|
+
execute<Op extends keyof UseCases & string>(op: Op, input: UseCaseInput<UseCases[Op]>, ...options: CommandOptions<UseCases[Op], Context, Environment>): Promise<CommandExecution<UseCaseResponse<UseCases[Op]>>>;
|
|
100
|
+
}
|
|
101
|
+
/** Any command. */
|
|
102
|
+
export type AnyCommand = CommandOf<any, any, any>;
|
|
@@ -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,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;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { AnyBlueprint } from "../../blueprint/types/blueprint.interface.js";
|
|
2
|
+
import type { AnyCommand } from "../../command/types/command.interface.js";
|
|
3
|
+
import type { JsonValue } from "../../json.js";
|
|
4
|
+
import type { Kind } from "../../symbols/kind.js";
|
|
5
|
+
import type { AnyExceptionClass } from "../../use-case/use-case.js";
|
|
6
|
+
/** The options of a stage: a JSON object. */
|
|
7
|
+
export type StageOptions = {
|
|
8
|
+
readonly [key: string]: JsonValue;
|
|
9
|
+
};
|
|
10
|
+
/** An HTTP-style status by exception name. */
|
|
11
|
+
export type OutputStatuses = Readonly<Record<string, number>>;
|
|
12
|
+
/** The call of an operation: `context` is what the command (or use case) declares. */
|
|
13
|
+
export interface OperationCallOptions {
|
|
14
|
+
readonly context?: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* One operation of a command, as an output sees it. Frozen; transforms change
|
|
18
|
+
* a copy with `operationOf`.
|
|
19
|
+
*/
|
|
20
|
+
export interface OperationDescriptor {
|
|
21
|
+
readonly kind: "operation";
|
|
22
|
+
readonly version: 1;
|
|
23
|
+
/** `"Users.register"`. */
|
|
24
|
+
readonly id: string;
|
|
25
|
+
/** The name of the command. */
|
|
26
|
+
readonly command: string;
|
|
27
|
+
/** The operation name inside the command. */
|
|
28
|
+
readonly name: string;
|
|
29
|
+
/** The `useCaseName`. */
|
|
30
|
+
readonly useCase: string;
|
|
31
|
+
/** The use case description, else the command one. */
|
|
32
|
+
readonly description?: string;
|
|
33
|
+
/** The command tags, then the use case ones, deduplicated. */
|
|
34
|
+
readonly tags: readonly string[];
|
|
35
|
+
readonly deprecated: boolean;
|
|
36
|
+
readonly input: AnyBlueprint;
|
|
37
|
+
readonly response: AnyBlueprint;
|
|
38
|
+
/** The command context, else the use case one. */
|
|
39
|
+
readonly context?: AnyBlueprint;
|
|
40
|
+
/** The `errors` of the use case. */
|
|
41
|
+
readonly errors: readonly AnyExceptionClass[];
|
|
42
|
+
/** Set by transforms; `{}` initially. */
|
|
43
|
+
readonly meta: Readonly<Record<string, unknown>>;
|
|
44
|
+
/** `command.run(name, input, options)`. */
|
|
45
|
+
invoke(input: unknown, options?: OperationCallOptions): Promise<unknown>;
|
|
46
|
+
}
|
|
47
|
+
/** What `operationOf` can change. */
|
|
48
|
+
export interface OperationPatch {
|
|
49
|
+
readonly meta?: Readonly<Record<string, unknown>>;
|
|
50
|
+
readonly tags?: readonly string[];
|
|
51
|
+
readonly description?: string | undefined;
|
|
52
|
+
readonly deprecated?: boolean;
|
|
53
|
+
readonly invoke?: OperationDescriptor["invoke"];
|
|
54
|
+
}
|
|
55
|
+
/** What a stage knows about the output running it. */
|
|
56
|
+
export interface StageContext {
|
|
57
|
+
readonly output: {
|
|
58
|
+
readonly name: string;
|
|
59
|
+
readonly statuses: OutputStatuses;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** A stage that changes (or removes, with `null`) each operation. */
|
|
63
|
+
export interface TransformStage<Options extends object = StageOptions> {
|
|
64
|
+
readonly kind: "transform";
|
|
65
|
+
readonly name?: string;
|
|
66
|
+
readonly options?: Options;
|
|
67
|
+
apply(operation: OperationDescriptor, context: StageContext): OperationDescriptor | null;
|
|
68
|
+
}
|
|
69
|
+
/** The last stage: builds something concrete out of the operations. */
|
|
70
|
+
export interface SinkStage<Result = unknown, Options extends object = StageOptions> {
|
|
71
|
+
readonly kind: "sink";
|
|
72
|
+
readonly name?: string;
|
|
73
|
+
readonly options?: Options;
|
|
74
|
+
build(operations: readonly OperationDescriptor[], context: StageContext): Result;
|
|
75
|
+
}
|
|
76
|
+
/** Any stage. */
|
|
77
|
+
export type Stage = TransformStage<any> | SinkStage<any, any>;
|
|
78
|
+
type AnyTransform = TransformStage<any>;
|
|
79
|
+
type AnySink<Result> = SinkStage<Result, any>;
|
|
80
|
+
/** What a stage is, out of band: a name and JSON options, or opaque. */
|
|
81
|
+
export type StageDescription = {
|
|
82
|
+
readonly kind: "transform" | "sink";
|
|
83
|
+
readonly name: string;
|
|
84
|
+
readonly options: StageOptions;
|
|
85
|
+
} | {
|
|
86
|
+
readonly kind: "opaque";
|
|
87
|
+
};
|
|
88
|
+
export interface TransformSettings<Options extends object> {
|
|
89
|
+
readonly name: string;
|
|
90
|
+
/** Returns the operation, a changed copy, or `null` to remove it. */
|
|
91
|
+
readonly apply: (operation: OperationDescriptor, options: Options, context: StageContext) => OperationDescriptor | null;
|
|
92
|
+
/** Why `options` cannot be used, or `undefined`. Checked when the stage is made. */
|
|
93
|
+
readonly validate?: (options: Options) => string | undefined;
|
|
94
|
+
}
|
|
95
|
+
export interface SinkSettings<Result, Options extends object> {
|
|
96
|
+
readonly name: string;
|
|
97
|
+
readonly build: (operations: readonly OperationDescriptor[], options: Options, context: StageContext) => Result;
|
|
98
|
+
/** Why `options` cannot be used, or `undefined`. Checked when the stage is made. */
|
|
99
|
+
readonly validate?: (options: Options) => string | undefined;
|
|
100
|
+
}
|
|
101
|
+
/** Makes a stage from JSON options. */
|
|
102
|
+
export type TransformFactory<Options extends object = StageOptions> = (options?: Options) => TransformStage<Options>;
|
|
103
|
+
export type SinkFactory<Result = unknown, Options extends object = StageOptions> = (options?: Options) => SinkStage<Result, Options>;
|
|
104
|
+
/** Any stage factory. */
|
|
105
|
+
export type StageFactory = TransformFactory<any> | SinkFactory<any, any>;
|
|
106
|
+
/** Stage factories by name. */
|
|
107
|
+
export interface IStageCatalog<Factories extends Readonly<Record<string, StageFactory>> = Readonly<Record<string, StageFactory>>> {
|
|
108
|
+
readonly [Kind]: "stage-catalog";
|
|
109
|
+
readonly stages: Factories;
|
|
110
|
+
readonly list: readonly StageFactory[];
|
|
111
|
+
/** Makes the stage `name` with `options`; throws `InvalidOutputStage` for an unknown name. */
|
|
112
|
+
resolve(name: string, options?: StageOptions): Stage;
|
|
113
|
+
}
|
|
114
|
+
/** The result of the sink at the end of a pipe, or `never` when there is none. */
|
|
115
|
+
export type PipeResult<Pipe extends readonly Stage[]> = Pipe extends readonly [
|
|
116
|
+
...Stage[],
|
|
117
|
+
SinkStage<infer Result, any>
|
|
118
|
+
] ? Result : never;
|
|
119
|
+
/** An application, as far as an output is concerned: it gives its commands. */
|
|
120
|
+
export interface OutputApplication {
|
|
121
|
+
readonly [Kind]: "application";
|
|
122
|
+
readonly commands: Readonly<Record<string, AnyCommand>>;
|
|
123
|
+
}
|
|
124
|
+
/** What an output takes its commands from. */
|
|
125
|
+
export type OutputSource = AnyCommand | OutputApplication;
|
|
126
|
+
export interface OutputSettings<Pipe extends readonly Stage[]> {
|
|
127
|
+
readonly name: string;
|
|
128
|
+
/** The commands the output exposes: commands, applications (all their commands) or a mix. */
|
|
129
|
+
readonly commands: OutputApplication | readonly OutputSource[];
|
|
130
|
+
/** HTTP-style status by exception name. */
|
|
131
|
+
readonly statuses?: OutputStatuses;
|
|
132
|
+
readonly pipe?: Pipe;
|
|
133
|
+
}
|
|
134
|
+
/** An output made by `outputOf`. */
|
|
135
|
+
export interface OutputOf<Result = never> {
|
|
136
|
+
readonly [Kind]: "output";
|
|
137
|
+
readonly outputName: string;
|
|
138
|
+
readonly commands: readonly AnyCommand[];
|
|
139
|
+
/** The operations after the transforms, in declaration order. */
|
|
140
|
+
readonly operations: readonly OperationDescriptor[];
|
|
141
|
+
readonly stages: readonly Stage[];
|
|
142
|
+
readonly statuses: OutputStatuses;
|
|
143
|
+
/** The result of the sink (memoized); throws when the pipe has no sink. */
|
|
144
|
+
build(): Result;
|
|
145
|
+
/** The result of the sink when the last stage is one, else a new, unnamed output. */
|
|
146
|
+
pipe<Built>(...stages: [...AnyTransform[], AnySink<Built>]): Built;
|
|
147
|
+
pipe(...stages: Stage[]): OutputOf<never>;
|
|
148
|
+
}
|
|
149
|
+
/** Any output. */
|
|
150
|
+
export type AnyOutput = OutputOf<any>;
|
|
151
|
+
/** An RFC 9457 problem. */
|
|
152
|
+
export interface Problem {
|
|
153
|
+
readonly type: string;
|
|
154
|
+
readonly title: string;
|
|
155
|
+
readonly status: number;
|
|
156
|
+
readonly detail: string;
|
|
157
|
+
readonly layer?: string;
|
|
158
|
+
/** Only for a 400 with validation issues. */
|
|
159
|
+
readonly issues?: readonly {
|
|
160
|
+
readonly path: readonly (string | number)[];
|
|
161
|
+
readonly code: string;
|
|
162
|
+
readonly message: string;
|
|
163
|
+
}[];
|
|
164
|
+
/** Only with `debug`. */
|
|
165
|
+
readonly trace?: string;
|
|
166
|
+
}
|
|
167
|
+
export interface ProblemOptions {
|
|
168
|
+
readonly status: number;
|
|
169
|
+
readonly debug?: boolean;
|
|
170
|
+
}
|
|
171
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TSchema } from "typebox";
|
|
1
|
+
import type { Static, TSchema } from "typebox";
|
|
2
2
|
import type { ParseResult } from "../parse/types/parse.interface.js";
|
|
3
3
|
import type { StandardSchemaV1 } from "../standard/standard-schema.js";
|
|
4
4
|
import { State } from "../symbols/state.js";
|
|
@@ -9,11 +9,12 @@ import type { AnyPropertyConstructor } from "./types/property-collection.type.js
|
|
|
9
9
|
import type { PropertyValue } from "./types/property-constructor.type.js";
|
|
10
10
|
import type { PropertyOptions } from "./types/property-options.interface.js";
|
|
11
11
|
import type { IPropertyType } from "./types/property-type.interface.js";
|
|
12
|
-
|
|
12
|
+
/** A property instance; `Value` is the value of its schema, as in `ISchema`. */
|
|
13
|
+
export declare abstract class AbstractProperty<SchemaType extends TSchema, Value = Static<SchemaType>> implements IProperty<SchemaType, Value> {
|
|
13
14
|
get [State](): PropertyStates;
|
|
14
|
-
readonly definition: AbstractDefineProperty<SchemaType>;
|
|
15
|
-
readonly value: PropertyValue<SchemaType>;
|
|
16
|
-
constructor(value: PropertyValue<SchemaType>);
|
|
15
|
+
readonly definition: AbstractDefineProperty<SchemaType, Value>;
|
|
16
|
+
readonly value: PropertyValue<SchemaType, Value>;
|
|
17
|
+
constructor(value: PropertyValue<SchemaType, Value>);
|
|
17
18
|
/**
|
|
18
19
|
* Builds an instance from the property's `default` value. Throws if the
|
|
19
20
|
* property is required and has no `default`.
|
|
@@ -32,11 +33,11 @@ export declare abstract class AbstractProperty<SchemaType extends TSchema> imple
|
|
|
32
33
|
* `pattern`... are checked along with its own constraints, which they can
|
|
33
34
|
* only narrow. Its demo value is `default`, or the first of `examples`.
|
|
34
35
|
*/
|
|
35
|
-
static with<Self extends AnyPropertyConstructor>(this: Self, options:
|
|
36
|
+
static with<Self extends AnyPropertyConstructor>(this: Self, options: Self["type"] extends IPropertyType<infer SchemaType, infer Value> ? PropertyOptions<SchemaType, Value> : never): Self;
|
|
36
37
|
/**
|
|
37
38
|
* The Standard Schema of the class it is read from, a subclass included:
|
|
38
39
|
* `validate` gives the handled value, or the issues that reject it.
|
|
39
40
|
*/
|
|
40
41
|
static get "~standard"(): StandardSchemaV1.Props<any, any>;
|
|
41
|
-
protected abstract createDefinition(): AbstractDefineProperty<SchemaType>;
|
|
42
|
+
protected abstract createDefinition(): AbstractDefineProperty<SchemaType, Value>;
|
|
42
43
|
}
|
|
@@ -4,15 +4,16 @@ import { State } from "../symbols/state.js";
|
|
|
4
4
|
import { PropertyStates } from "./enums/property-states.js";
|
|
5
5
|
import type { IDefineProperty } from "./types/define-property.interface.js";
|
|
6
6
|
import type { IPropertyType } from "./types/property-type.interface.js";
|
|
7
|
-
|
|
7
|
+
/** How a property handles its value; `Value` is the value of its schema, as in `ISchema`. */
|
|
8
|
+
export declare abstract class AbstractDefineProperty<SchemaType extends TSchema, Value = Static<SchemaType>> implements IDefineProperty<SchemaType, Value> {
|
|
8
9
|
[State]: PropertyStates;
|
|
9
|
-
abstract get type(): IPropertyType<SchemaType>;
|
|
10
|
+
abstract get type(): IPropertyType<SchemaType, Value>;
|
|
10
11
|
/**
|
|
11
12
|
* The value handled by the property; throws `PropertyRequired` for
|
|
12
13
|
* `undefined` on a required property, and `InvalidPropertyValue` (or the
|
|
13
14
|
* exception of a handler) for a value it rejects.
|
|
14
15
|
*/
|
|
15
|
-
make(value:
|
|
16
|
+
make(value: Value): Value;
|
|
16
17
|
/**
|
|
17
18
|
* Like `make`, but never throws: the handled value, or an issue for each
|
|
18
19
|
* reason the value is rejected, whose `cause` is the exception `make` would
|
|
@@ -21,11 +22,11 @@ export declare abstract class AbstractDefineProperty<SchemaType extends TSchema>
|
|
|
21
22
|
* `Exception`, from any step, rejects the value too, as the cause of an
|
|
22
23
|
* `InvalidPropertyValue`.
|
|
23
24
|
*/
|
|
24
|
-
parse(value: unknown): ParseResult<
|
|
25
|
-
protected preHandle(value:
|
|
26
|
-
protected afterHandle(value:
|
|
25
|
+
parse(value: unknown): ParseResult<Value>;
|
|
26
|
+
protected preHandle(value: Value): Value;
|
|
27
|
+
protected afterHandle(value: Value): Value;
|
|
27
28
|
/** The value, or `MISMATCH` when the schema rejects it. */
|
|
28
|
-
protected handle(value:
|
|
29
|
+
protected handle(value: Value): Value;
|
|
29
30
|
/** `attempt`, with the error of any step as a failure. */
|
|
30
31
|
private attemptWithoutThrowing;
|
|
31
32
|
/** Runs the pipeline, updating the state, and tells why it stopped if it did. */
|