@afora/core 0.0.9 → 0.0.11
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 +52 -30
- package/dist/adapters.js +31 -55
- package/dist/core/application/application.d.ts +30 -0
- package/dist/core/application/application.js +1291 -0
- package/dist/core/application/exceptions.js +85 -0
- package/dist/core/application/migrate.js +67 -0
- package/dist/core/application/providers.js +108 -0
- package/dist/core/application/registry.js +23 -0
- package/dist/core/application/schema.js +398 -0
- package/dist/core/application/start.js +162 -0
- package/dist/core/application/types/application.interface.d.ts +23 -3
- package/dist/core/auth/jwt.js +200 -0
- package/dist/core/auth/types/jwt.interface.d.ts +10 -2
- package/dist/core/blueprint/blueprint-plugin.js +7 -0
- package/dist/core/blueprint/blueprint.js +146 -0
- package/dist/core/blueprint/callable.js +13 -0
- package/dist/core/blueprint/exceptions.js +20 -0
- package/dist/core/blueprint/is-blueprint.js +4 -0
- package/dist/core/blueprint/list.js +19 -0
- package/dist/core/blueprint/optional.js +69 -0
- package/dist/core/blueprint/record-field.js +14 -0
- package/dist/core/blueprint/slice.js +127 -0
- package/dist/core/blueprint/types/blueprint.interface.d.ts +16 -0
- package/dist/core/blueprint/variant.js +72 -0
- package/dist/core/command/command.js +428 -0
- package/dist/core/command/exceptions.js +30 -0
- package/dist/core/command/types/command.interface.d.ts +10 -1
- package/dist/core/entity/abstract-entity.d.ts +19 -0
- package/dist/core/entity/abstract-entity.js +181 -0
- package/dist/core/entity/entity.js +209 -0
- package/dist/core/entity/event.d.ts +39 -0
- package/dist/core/entity/event.js +80 -0
- package/dist/core/entity/exceptions.js +27 -0
- package/dist/core/entity/trust.js +7 -0
- package/dist/core/entity/version.js +13 -0
- package/dist/core/environment/environment.d.ts +26 -3
- package/dist/core/environment/environment.js +71 -0
- package/dist/core/event-policy/event-policy.js +76 -0
- package/dist/core/event-policy/exceptions.js +12 -0
- package/dist/core/event-policy/publish.js +98 -0
- package/dist/core/event-policy/publishers.js +18 -0
- package/dist/core/event-policy/types/event-policy.interface.d.ts +8 -2
- package/dist/core/exception/enums/exception-layers.js +15 -0
- package/dist/core/exception/exception.d.ts +15 -0
- package/dist/core/exception/exception.js +128 -0
- package/dist/core/exception/exported.js +49 -0
- package/dist/core/exception/guard.d.ts +20 -1
- package/dist/core/exception/guard.js +16 -0
- package/dist/core/exception/types/exception.interface.d.ts +13 -0
- package/dist/core/json.js +19 -0
- package/dist/core/logger/exceptions.js +7 -0
- package/dist/core/logger/logger.d.ts +14 -1
- package/dist/core/logger/logger.js +178 -0
- package/dist/core/openapi/exceptions.js +7 -0
- package/dist/core/openapi/openapi.js +132 -0
- package/dist/core/openapi/types/openapi.interface.d.ts +7 -1
- package/dist/core/output/access.js +66 -0
- package/dist/core/output/coerce.js +38 -0
- package/dist/core/output/exceptions.js +22 -0
- package/dist/{index-y280ew0k.js → core/output/http.js} +35 -80
- package/dist/core/output/openapi-sink.d.ts +10 -2
- package/dist/{index-hg1z2s2v.js → core/output/openapi-sink.js} +60 -544
- package/dist/core/output/output.js +192 -0
- package/dist/core/output/problem.js +30 -0
- package/dist/core/output/select.js +28 -0
- package/dist/core/output/stage.js +111 -0
- package/dist/core/output/status.js +52 -0
- package/dist/core/output/types/http.interface.d.ts +5 -1
- package/dist/core/output/types/output.interface.d.ts +24 -0
- package/dist/core/parse/issue.js +31 -0
- package/dist/core/parse/parse.d.ts +8 -1
- package/dist/core/parse/parse.js +323 -0
- package/dist/core/parse/strip.js +48 -0
- package/dist/core/property/abstract-property.js +37 -0
- package/dist/core/property/define-property.js +209 -0
- package/dist/core/property/enums/property-states.js +8 -0
- package/dist/core/property/exceptions.js +12 -0
- package/dist/core/property/input.js +10 -0
- package/dist/core/property/is-property.js +3 -0
- package/dist/core/property/lineage.js +8 -0
- package/dist/core/property/property.d.ts +27 -0
- package/dist/core/property/property.js +46 -0
- package/dist/core/property/refine.js +103 -0
- package/dist/core/record/abstract-record.d.ts +2 -1
- package/dist/core/record/abstract-record.js +307 -0
- package/dist/core/record/exceptions.js +49 -0
- package/dist/core/record/marks.js +7 -0
- package/dist/core/record/mask.js +60 -0
- package/dist/core/record/name.js +23 -0
- package/dist/core/record/plugin-catalog.js +115 -0
- package/dist/core/record/plugin.js +198 -0
- package/dist/core/record/record.js +117 -0
- package/dist/core/record/types/record.interface.d.ts +5 -0
- package/dist/core/repository/cursor.js +83 -0
- package/dist/core/repository/exceptions.js +72 -0
- package/dist/core/repository/in-memory.js +196 -0
- package/dist/core/repository/migrate.js +254 -0
- package/dist/core/repository/plan.js +27 -0
- package/dist/core/repository/provider.js +29 -0
- package/dist/core/repository/query.js +153 -0
- package/dist/core/repository/references.js +196 -0
- package/dist/core/repository/repository.d.ts +22 -2
- package/dist/core/repository/repository.js +720 -0
- package/dist/core/repository/state.js +138 -0
- package/dist/core/repository/types/query.interface.d.ts +13 -0
- package/dist/core/repository/types/repository.interface.d.ts +20 -8
- package/dist/core/repository/types/schema.interface.d.ts +8 -2
- package/dist/core/repository/upcast.js +179 -0
- package/dist/core/schema/schema.d.ts +9 -0
- package/dist/core/schema/schema.js +542 -0
- package/dist/core/standard/standard-schema.js +1 -0
- package/dist/core/standard/standard.js +23 -0
- package/dist/core/symbols/demo.d.ts +4 -0
- package/dist/core/symbols/demo.js +1 -0
- package/dist/core/symbols/kind.js +1 -0
- package/dist/core/symbols/meta.js +1 -0
- package/dist/core/symbols/state.d.ts +5 -0
- package/dist/core/symbols/state.js +1 -0
- package/dist/core/tracing/exceptions.d.ts +4 -0
- package/dist/core/tracing/exceptions.js +7 -0
- package/dist/core/tracing/tracing.d.ts +72 -0
- package/dist/core/tracing/tracing.js +101 -0
- package/dist/core/use-case/access.js +56 -0
- package/dist/core/use-case/decorators.js +65 -0
- package/dist/core/use-case/exceptions.d.ts +5 -1
- package/dist/core/use-case/exceptions.js +48 -0
- package/dist/core/use-case/plugin-catalog.js +68 -0
- package/dist/core/use-case/plugin.js +133 -0
- package/dist/core/use-case/route.js +200 -0
- package/dist/core/use-case/scope.d.ts +5 -0
- package/dist/core/use-case/scope.js +4 -0
- package/dist/core/use-case/steps/describe.d.ts +5 -1
- package/dist/core/use-case/steps/describe.js +24 -0
- package/dist/core/use-case/steps/references.js +105 -0
- package/dist/core/use-case/steps/run.js +139 -0
- package/dist/core/use-case/steps/types/check.interface.d.ts +27 -8
- package/dist/core/use-case/steps/types/steps.interface.d.ts +8 -1
- package/dist/core/use-case/steps/validate.js +368 -0
- package/dist/core/use-case/target.js +100 -0
- package/dist/core/use-case/types/route.interface.d.ts +25 -5
- package/dist/core/use-case/use-case.d.ts +77 -2
- package/dist/core/use-case/use-case.js +493 -0
- package/dist/experimental.d.ts +5 -0
- package/dist/experimental.js +18 -161
- package/dist/index.d.ts +7 -5
- package/dist/index.js +213 -367
- package/dist/std/blueprint-plugin.js +42 -0
- package/dist/std/blueprint.d.ts +21 -0
- package/dist/std/blueprint.js +3 -0
- package/dist/std/command.js +3 -0
- package/dist/std/entity.d.ts +20 -1
- package/dist/std/entity.js +30 -0
- package/dist/std/event-policies.js +3 -0
- package/dist/std/properties/contact.d.ts +3 -0
- package/dist/std/properties/contact.js +29 -0
- package/dist/std/properties/enum.js +26 -0
- package/dist/std/properties/exceptions.js +14 -0
- package/dist/std/properties/headless.js +45 -0
- package/dist/std/properties/identity.js +28 -0
- package/dist/std/properties/json.js +161 -0
- package/dist/std/properties/password.js +79 -0
- package/dist/std/properties/primitives.d.ts +4 -0
- package/dist/std/properties/primitives.js +28 -0
- package/dist/std/properties/time.js +55 -0
- package/dist/std/record-plugins.js +73 -0
- package/dist/std/record.d.ts +20 -1
- package/dist/std/record.js +3 -0
- package/dist/std/resource.js +39 -0
- package/dist/std/runtime.js +193 -0
- package/dist/std/stages.js +4 -0
- package/dist/std/use-cases/auth.js +193 -0
- package/dist/std/use-cases/crud.js +261 -0
- package/dist/std/use-cases/index.js +12 -0
- package/dist/std/use-cases/lifecycle.js +97 -0
- package/dist/std/use-cases/shared.js +232 -0
- package/dist/std/use-cases/types.d.ts +5 -1
- package/dist/std/use-cases/types.js +0 -0
- package/dist/testing/http-provider-contract.js +344 -0
- package/dist/testing/index.js +3 -1435
- package/dist/testing/memory-publisher.js +12 -0
- package/dist/testing/provider-contract.d.ts +12 -3
- package/dist/testing/provider-contract.js +1071 -0
- package/package.json +10 -3
- package/dist/index-3aae5jfq.js +0 -2997
- package/dist/index-ny3as79r.js +0 -4567
- package/dist/index-xegv6a1p.js +0 -4291
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ILogger } from "../logger/logger.js";
|
|
2
2
|
import type { IRepositoryProvider } from "../repository/types/repository.interface.js";
|
|
3
|
+
import type { ITracer, ITraceSpan } from "../tracing/tracing.js";
|
|
3
4
|
/**
|
|
4
5
|
* What a call carries to its repositories, internal: the providers that
|
|
5
6
|
* replace the ones the repositories declared (e.g. one inside a transaction).
|
|
@@ -14,6 +15,10 @@ export interface ExecutionScope {
|
|
|
14
15
|
readonly environments?: ReadonlyMap<object, Readonly<Record<string, unknown>>>;
|
|
15
16
|
/** The logger of the application running the call, given by its command. */
|
|
16
17
|
readonly logger?: ILogger;
|
|
18
|
+
/** The tracer of the application running the call, given by its command. */
|
|
19
|
+
readonly tracer?: ITracer;
|
|
20
|
+
/** The span of the caller (a command, a use case): the parent of the next. */
|
|
21
|
+
readonly span?: ITraceSpan;
|
|
17
22
|
}
|
|
18
23
|
/**
|
|
19
24
|
* Key of the constructor options (and of the repository view) that carries
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { FieldsDescription } from "../../application/types/application.interface.js";
|
|
1
2
|
import type { StepErrorDescription, UseCaseStepDescription } from "./types/steps.interface.js";
|
|
2
3
|
/** How a class of a step is named in the descriptor. */
|
|
3
4
|
export interface StepNames {
|
|
@@ -5,9 +6,12 @@ export interface StepNames {
|
|
|
5
6
|
readonly record: (Class: unknown) => string;
|
|
6
7
|
/** The registered name of an exception, or `{ core }` for one the package exports. */
|
|
7
8
|
readonly error: (Class: unknown) => StepErrorDescription;
|
|
9
|
+
/** The fields of the `params` blueprint of a `run`. */
|
|
10
|
+
readonly params: (blueprint: unknown) => FieldsDescription;
|
|
8
11
|
}
|
|
9
12
|
/**
|
|
10
13
|
* The steps as `describe()` gives them: the same data, with the classes of
|
|
11
|
-
* `build` and `ensure.error` by name
|
|
14
|
+
* `build` and `ensure.error` by name, the function of `run` as `opaque` and
|
|
15
|
+
* its `params` by their fields.
|
|
12
16
|
*/
|
|
13
17
|
export declare function describeSteps(steps: readonly Readonly<Record<string, unknown>>[], names: StepNames): UseCaseStepDescription[];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { isPlain } from "./references.js";
|
|
2
|
+
function cloneData(value) {
|
|
3
|
+
if (Array.isArray(value))
|
|
4
|
+
return value.map(cloneData);
|
|
5
|
+
if (isPlain(value)) {
|
|
6
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneData(item)]));
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
export function describeSteps(steps, names) {
|
|
11
|
+
return steps.map((step) => {
|
|
12
|
+
const copy = cloneData({ ...step });
|
|
13
|
+
if (Object.hasOwn(step, "build"))
|
|
14
|
+
copy.build = names.record(step.build);
|
|
15
|
+
if (Object.hasOwn(step, "ensure"))
|
|
16
|
+
copy.error = names.error(step.error);
|
|
17
|
+
if (Object.hasOwn(step, "run")) {
|
|
18
|
+
copy.fn = { kind: "opaque" };
|
|
19
|
+
if (step.params !== undefined)
|
|
20
|
+
copy.params = names.params(step.params);
|
|
21
|
+
}
|
|
22
|
+
return copy;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export const RESERVED_NAMES = [
|
|
2
|
+
"input",
|
|
3
|
+
"context",
|
|
4
|
+
"env",
|
|
5
|
+
"literal"
|
|
6
|
+
];
|
|
7
|
+
const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
8
|
+
const SEGMENT = /^(?:[A-Za-z_][A-Za-z0-9_]*|0|[1-9][0-9]*)$/;
|
|
9
|
+
const BLOCKED = new Set(["__proto__", "constructor", "prototype"]);
|
|
10
|
+
export function isStepName(value) {
|
|
11
|
+
return typeof value === "string" && IDENTIFIER.test(value);
|
|
12
|
+
}
|
|
13
|
+
const parsed = new Map;
|
|
14
|
+
export function parseReference(text) {
|
|
15
|
+
const cached = parsed.get(text);
|
|
16
|
+
if (cached !== undefined)
|
|
17
|
+
return cached ?? undefined;
|
|
18
|
+
let result = null;
|
|
19
|
+
if (text.startsWith("$")) {
|
|
20
|
+
const [root, ...path] = text.slice(1).split(".");
|
|
21
|
+
if (root !== undefined && IDENTIFIER.test(root) && path.every((segment) => SEGMENT.test(segment))) {
|
|
22
|
+
result = Object.freeze({ root, path: Object.freeze(path) });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (parsed.size < 4096)
|
|
26
|
+
parsed.set(text, result);
|
|
27
|
+
return result ?? undefined;
|
|
28
|
+
}
|
|
29
|
+
export function isPlain(value) {
|
|
30
|
+
if (typeof value !== "object" || value === null)
|
|
31
|
+
return false;
|
|
32
|
+
const prototype = Object.getPrototypeOf(value);
|
|
33
|
+
return prototype === Object.prototype || prototype === null;
|
|
34
|
+
}
|
|
35
|
+
function isLiteralEscape(value) {
|
|
36
|
+
return isPlain(value) && Object.keys(value).length === 1 && Object.hasOwn(value, "literal");
|
|
37
|
+
}
|
|
38
|
+
export function walkValue(value, onReference, onIssue) {
|
|
39
|
+
if (typeof value === "string") {
|
|
40
|
+
if (!value.startsWith("$"))
|
|
41
|
+
return;
|
|
42
|
+
const reference = parseReference(value);
|
|
43
|
+
if (!reference)
|
|
44
|
+
onIssue("invalid_reference", value);
|
|
45
|
+
else
|
|
46
|
+
onReference(reference, value);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (value === null || typeof value === "number" || typeof value === "boolean") {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (isLiteralEscape(value)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
for (const item of value)
|
|
57
|
+
walkValue(item, onReference, onIssue);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (isPlain(value)) {
|
|
61
|
+
for (const item of Object.values(value)) {
|
|
62
|
+
walkValue(item, onReference, onIssue);
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
onIssue("invalid_value", String(value));
|
|
67
|
+
}
|
|
68
|
+
export function followPath(from, path) {
|
|
69
|
+
let current = from;
|
|
70
|
+
for (const segment of path) {
|
|
71
|
+
if (typeof current !== "object" && typeof current !== "function" || current === null || BLOCKED.has(segment)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
current = current[segment];
|
|
75
|
+
}
|
|
76
|
+
return current;
|
|
77
|
+
}
|
|
78
|
+
export function resolveValue(value, lookup) {
|
|
79
|
+
if (typeof value === "string") {
|
|
80
|
+
if (!value.startsWith("$"))
|
|
81
|
+
return value;
|
|
82
|
+
const reference = parseReference(value);
|
|
83
|
+
return reference ? lookup(reference) : undefined;
|
|
84
|
+
}
|
|
85
|
+
if (isLiteralEscape(value))
|
|
86
|
+
return value.literal;
|
|
87
|
+
if (Array.isArray(value))
|
|
88
|
+
return value.map((item) => resolveValue(item, lookup));
|
|
89
|
+
if (isPlain(value)) {
|
|
90
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
91
|
+
key,
|
|
92
|
+
resolveValue(item, lookup)
|
|
93
|
+
]));
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
export function freezeData(value) {
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
return Object.freeze(value.map(freezeData));
|
|
100
|
+
}
|
|
101
|
+
if (isPlain(value)) {
|
|
102
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, freezeData(item)])));
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { isRecordClass } from "../../blueprint/record-field.js";
|
|
2
|
+
import { sameValue } from "../../repository/query.js";
|
|
3
|
+
import { InvalidUseCaseStep } from "../exceptions.js";
|
|
4
|
+
import { followPath, resolveValue } from "./references.js";
|
|
5
|
+
const FORBIDDEN_METHODS = new Set([
|
|
6
|
+
"set",
|
|
7
|
+
"emit",
|
|
8
|
+
"constructor",
|
|
9
|
+
"__proto__",
|
|
10
|
+
"hasOwnProperty",
|
|
11
|
+
"isPrototypeOf",
|
|
12
|
+
"propertyIsEnumerable",
|
|
13
|
+
"toLocaleString",
|
|
14
|
+
"valueOf",
|
|
15
|
+
"__defineGetter__",
|
|
16
|
+
"__defineSetter__",
|
|
17
|
+
"__lookupGetter__",
|
|
18
|
+
"__lookupSetter__"
|
|
19
|
+
]);
|
|
20
|
+
function isRecordInstance(value) {
|
|
21
|
+
return typeof value === "object" && value !== null && isRecordClass(value.constructor);
|
|
22
|
+
}
|
|
23
|
+
function parsedArgs(params, args, invalid) {
|
|
24
|
+
const keys = Object.keys(params.properties);
|
|
25
|
+
if (args.length !== keys.length) {
|
|
26
|
+
invalid(`run.params declares ${keys.length} parameter(s), args gave ${args.length}.`, "invalid_args");
|
|
27
|
+
}
|
|
28
|
+
const result = params.safeParse(Object.fromEntries(keys.map((key, at) => [key, args[at]])));
|
|
29
|
+
if (result.issues) {
|
|
30
|
+
const [first] = result.issues;
|
|
31
|
+
const more = result.issues.length > 1 ? ` (and ${result.issues.length - 1} more)` : "";
|
|
32
|
+
return invalid(`the argument "${first?.path.join(".")}" ${first?.message}${more}.`, "invalid_args", result.issues);
|
|
33
|
+
}
|
|
34
|
+
const value = result.value;
|
|
35
|
+
return keys.map((key) => value[key]);
|
|
36
|
+
}
|
|
37
|
+
export async function executeSteps(dependencies = {}) {
|
|
38
|
+
const { useCaseName, steps = [] } = this.constructor;
|
|
39
|
+
const named = new Map;
|
|
40
|
+
const lookup = ({ root, path }) => {
|
|
41
|
+
if (root === "input")
|
|
42
|
+
return followPath(this.input, path);
|
|
43
|
+
if (root === "context")
|
|
44
|
+
return followPath(this.context, path);
|
|
45
|
+
if (root === "env")
|
|
46
|
+
return followPath(dependencies[path[0]], path.slice(1));
|
|
47
|
+
return followPath(named.get(root), path);
|
|
48
|
+
};
|
|
49
|
+
const value = (raw) => resolveValue(raw, lookup);
|
|
50
|
+
for (const [index, step] of steps.entries()) {
|
|
51
|
+
const invalid = (reason, code = "invalid_target", issues) => {
|
|
52
|
+
throw new InvalidUseCaseStep({
|
|
53
|
+
source: useCaseName,
|
|
54
|
+
details: { index, reason, code, ...issues ? { issues } : {} }
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const list = (raw) => {
|
|
58
|
+
if (raw === undefined)
|
|
59
|
+
return [];
|
|
60
|
+
const resolved = value(raw);
|
|
61
|
+
if (!Array.isArray(resolved))
|
|
62
|
+
invalid("args must resolve to a list.");
|
|
63
|
+
return resolved;
|
|
64
|
+
};
|
|
65
|
+
const bind = (result) => {
|
|
66
|
+
if (typeof step.as === "string")
|
|
67
|
+
named.set(step.as, result);
|
|
68
|
+
};
|
|
69
|
+
const target = (field) => {
|
|
70
|
+
const resolved = value(step[field]);
|
|
71
|
+
if (!isRecordInstance(resolved)) {
|
|
72
|
+
invalid(`${field} must reference a record or entity.`);
|
|
73
|
+
}
|
|
74
|
+
return resolved;
|
|
75
|
+
};
|
|
76
|
+
if (Object.hasOwn(step, "return"))
|
|
77
|
+
return value(step.return);
|
|
78
|
+
if (Object.hasOwn(step, "find")) {
|
|
79
|
+
const [repository, action] = step.find.split(".");
|
|
80
|
+
const methods = dependencies[repository];
|
|
81
|
+
bind(await methods[action].call(methods, ...list(step.args)));
|
|
82
|
+
} else if (Object.hasOwn(step, "ensure")) {
|
|
83
|
+
const subject = value(step.value);
|
|
84
|
+
const passed = {
|
|
85
|
+
exists: subject !== undefined && subject !== null,
|
|
86
|
+
absent: subject === undefined || subject === null,
|
|
87
|
+
equals: sameValue(subject, value(step.other)),
|
|
88
|
+
notEquals: !sameValue(subject, value(step.other)),
|
|
89
|
+
true: subject === true,
|
|
90
|
+
false: subject === false
|
|
91
|
+
}[step.ensure];
|
|
92
|
+
if (!passed) {
|
|
93
|
+
const Thrown = step.error;
|
|
94
|
+
throw new Thrown({
|
|
95
|
+
source: useCaseName,
|
|
96
|
+
...step.details === undefined ? {} : { details: value(step.details) }
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
} else if (Object.hasOwn(step, "build")) {
|
|
100
|
+
const Built = step.build;
|
|
101
|
+
bind(new Built(value(step.values)));
|
|
102
|
+
} else if (Object.hasOwn(step, "change")) {
|
|
103
|
+
const record = target("change");
|
|
104
|
+
const namespace = record.change;
|
|
105
|
+
const key = step.key;
|
|
106
|
+
if (typeof namespace !== "object" || namespace === null || typeof namespace[key] !== "function") {
|
|
107
|
+
invalid(`"${key}" is not a change key of the record.`);
|
|
108
|
+
}
|
|
109
|
+
const changed = value(step.value);
|
|
110
|
+
if (step.ifPresent !== true || changed !== undefined) {
|
|
111
|
+
await namespace[key]?.(changed);
|
|
112
|
+
}
|
|
113
|
+
bind(record);
|
|
114
|
+
} else if (Object.hasOwn(step, "invoke")) {
|
|
115
|
+
const record = target("invoke");
|
|
116
|
+
const method = step.method;
|
|
117
|
+
if (FORBIDDEN_METHODS.has(method) || typeof record[method] !== "function") {
|
|
118
|
+
invalid(`"${method}" is not a public method of the record.`);
|
|
119
|
+
}
|
|
120
|
+
bind(await record[method].apply(record, list(step.args)));
|
|
121
|
+
} else if (Object.hasOwn(step, "call")) {
|
|
122
|
+
const child = dependencies[step.call];
|
|
123
|
+
bind(await child(value(step.input)));
|
|
124
|
+
} else if (Object.hasOwn(step, "run")) {
|
|
125
|
+
const fn = step.fn;
|
|
126
|
+
const args = list(step.args);
|
|
127
|
+
bind(await fn(...step.params === undefined ? args : parsedArgs(step.params, args, invalid), { context: this.context, dependencies }));
|
|
128
|
+
} else {
|
|
129
|
+
const action = ["create", "update", "delete", "restore"].find((kind) => Object.hasOwn(step, kind));
|
|
130
|
+
const methods = dependencies[step[action]];
|
|
131
|
+
bind(await methods[action].call(methods, value(step.value)));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
export function interpretedStepsOf(UseCase) {
|
|
137
|
+
const Declared = UseCase;
|
|
138
|
+
return Declared.prototype.execute === executeSteps ? Declared.steps : undefined;
|
|
139
|
+
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* the steps are not distributive (`[Step] extends [...]`): TypeScript then
|
|
14
14
|
* never evaluates them with the constraint of the steps while it infers them.
|
|
15
15
|
*/
|
|
16
|
-
import type { BlueprintValue, IBlueprint, IBlueprintCollection } from "../../../blueprint/types/blueprint.interface.js";
|
|
16
|
+
import type { BlueprintInput, BlueprintValue, IBlueprint, IBlueprintCollection } from "../../../blueprint/types/blueprint.interface.js";
|
|
17
17
|
import type { AnyEnvironment, EnvironmentValue } from "../../../environment/environment.js";
|
|
18
18
|
import type { Meta } from "../../../symbols/meta.js";
|
|
19
19
|
import type { AnyUseCaseClass, UseCaseInput, UseCaseResponse } from "../../use-case.js";
|
|
@@ -64,10 +64,21 @@ type PathsFrom<Value, Path extends string, Expected, Depth extends unknown[]> =
|
|
|
64
64
|
export type StepReferencesOf<Scope, Expected = unknown> = Scope extends unknown ? {
|
|
65
65
|
[Root in keyof Scope & string]-?: Root extends "env" ? Below<Scope[Root], `$${Root}`, Expected, [0]> : PathsFrom<Scope[Root], `$${Root}`, Expected, []>;
|
|
66
66
|
}[keyof Scope & string] : never;
|
|
67
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* The references of `Scope` that fit `Expected`, or `Expected` itself when
|
|
69
|
+
* none does, so that the error names the type that is taken rather than `never`.
|
|
70
|
+
*/
|
|
71
|
+
type Suggested<Expected, Scope> = [StepReferencesOf<Scope, Expected>] extends [
|
|
72
|
+
infer References
|
|
73
|
+
] ? [References] extends [never] ? Expected : References : never;
|
|
74
|
+
/**
|
|
75
|
+
* `Reference` when it names a value of `Scope` that fits `Expected`; else the
|
|
76
|
+
* references that would, and, when it only fails because it may be
|
|
77
|
+
* `undefined`, a hint to ensure it exists first.
|
|
78
|
+
*/
|
|
68
79
|
type CheckReference<Reference, Expected, Scope> = [
|
|
69
80
|
StepReferenceType<Scope, Reference>
|
|
70
|
-
] extends [infer Type] ? [Type] extends [never] ?
|
|
81
|
+
] extends [infer Type] ? [Type] extends [never] ? Suggested<Expected, Scope> : [Type] extends [Expected] ? Reference : [NonNullable<Type>] extends [Expected] ? Suggested<Expected, Scope> | `${Reference & string} may be undefined: ensure it exists first` : Suggested<Expected, Scope> : never;
|
|
71
82
|
type Primitive = string | number | boolean | null;
|
|
72
83
|
/** The type of the key `Key` in what `Expected` takes, over each object of it. */
|
|
73
84
|
type ExpectedAt<Expected, Key> = Expected extends object ? Key extends keyof Expected ? Expected[Key] : never : never;
|
|
@@ -158,7 +169,7 @@ type ChangeStep<Step, Record, Scope> = [Record] extends [never] ? ValueStep<Step
|
|
|
158
169
|
Record
|
|
159
170
|
] : [Step, Record];
|
|
160
171
|
/** `[what Step must be, what it gives to its as]`, read against `Scope`. */
|
|
161
|
-
type CheckStep<Step, Dependencies, Scope> = [Step] extends [
|
|
172
|
+
type CheckStep<Step, Dependencies, Scope, Response> = [Step] extends [
|
|
162
173
|
{
|
|
163
174
|
readonly find: infer Target;
|
|
164
175
|
}
|
|
@@ -183,7 +194,11 @@ type CheckStep<Step, Dependencies, Scope> = [Step] extends [
|
|
|
183
194
|
readonly delete: infer Repository;
|
|
184
195
|
}] ? WriteStep<Step, MemberOf<Dependencies, Repository, "delete">, Scope> : [Step] extends [{
|
|
185
196
|
readonly restore: infer Repository;
|
|
186
|
-
}] ? WriteStep<Step, MemberOf<Dependencies, Repository, "restore">, Scope> :
|
|
197
|
+
}] ? WriteStep<Step, MemberOf<Dependencies, Repository, "restore">, Scope> : [Step] extends [{
|
|
198
|
+
readonly return: unknown;
|
|
199
|
+
}] ? ValueStep<Step, "return", ResponseInput<Response>, unknown, Scope> : ValueStep<Step, "value" | "other" | "details", unknown, unknown, Scope>;
|
|
200
|
+
/** What the response blueprint `Response` takes, or anything when it is not known. */
|
|
201
|
+
type ResponseInput<Response> = [Response] extends [IBlueprintCollection] ? BlueprintInput<Response> : unknown;
|
|
187
202
|
type WriteStep<Step, Method, Scope> = ValueStep<Step, "value", FirstParameter<Method>, ResultOf<Method>, Scope>;
|
|
188
203
|
/** The scope after `Step`: its result bound to its `as`, or a value it ensures exists. */
|
|
189
204
|
type ScopeAfter<Step, Result, Scope> = [Step] extends [
|
|
@@ -201,14 +216,18 @@ type ScopeAfter<Step, Result, Scope> = [Step] extends [
|
|
|
201
216
|
readonly [Key in Name]: NonNullable<Scope[Name]>;
|
|
202
217
|
} : Scope : Scope;
|
|
203
218
|
/** The steps folded in order, each checked against the scope the earlier ones built. */
|
|
204
|
-
type CheckSteps<Steps extends readonly unknown[], Dependencies, Scope, Checked extends readonly unknown[] = []> = [Steps] extends [readonly [infer Step, ...infer Rest]] ? CheckStep<Step, Dependencies, Scope> extends [
|
|
219
|
+
type CheckSteps<Steps extends readonly unknown[], Dependencies, Scope, Response, Checked extends readonly unknown[] = []> = [Steps] extends [readonly [infer Step, ...infer Rest]] ? CheckStep<Step, Dependencies, Scope, Response> extends [
|
|
220
|
+
infer Current,
|
|
221
|
+
infer Result
|
|
222
|
+
] ? CheckSteps<Rest, Dependencies, ScopeAfter<Step, Result, Scope>, Response, [
|
|
205
223
|
...Checked,
|
|
206
224
|
Current
|
|
207
225
|
]> : never : readonly [...Checked];
|
|
208
226
|
/**
|
|
209
227
|
* What `useCaseOf` takes as `steps`: the steps, inferred as a tuple, each
|
|
210
|
-
* checked against the values in scope
|
|
228
|
+
* checked against the values in scope, and the `return` against what the
|
|
229
|
+
* response blueprint (the collection `Response`) takes. Steps that are not a tuple (built at
|
|
211
230
|
* run time) are checked only when the use case is declared.
|
|
212
231
|
*/
|
|
213
|
-
export type CheckedSteps<Steps, Input extends IBlueprintCollection, Context extends IBlueprintCollection | undefined, Dependencies> = [Steps] extends [readonly [unknown, ...unknown[]]] ? CheckSteps<Steps, Dependencies, StepsInitialScope<Input, Context, Dependencies
|
|
232
|
+
export type CheckedSteps<Steps, Input extends IBlueprintCollection, Context extends IBlueprintCollection | undefined, Dependencies, Response extends IBlueprintCollection | undefined = undefined> = [Steps] extends [readonly [unknown, ...unknown[]]] ? CheckSteps<Steps, Dependencies, StepsInitialScope<Input, Context, Dependencies>, Response> : Steps;
|
|
214
233
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FieldsDescription } from "../../../application/types/application.interface.js";
|
|
2
|
+
import type { AnyBlueprint, AnyRecordClass } from "../../../blueprint/types/blueprint.interface.js";
|
|
2
3
|
import type { AnyRepository } from "../../../repository/types/repository.interface.js";
|
|
3
4
|
import type { AnyExceptionClass, AnyUseCaseClass, UseCaseCollection } from "../../use-case.js";
|
|
4
5
|
/** A reference to a value: `$input.email`, `$context.userId`, `$env.mail`, `$saved.tags.0`. */
|
|
@@ -103,6 +104,11 @@ export type UseCaseStep<Dependencies extends UseCaseCollection = UseCaseCollecti
|
|
|
103
104
|
readonly run: string;
|
|
104
105
|
readonly fn: (...args: any[]) => unknown;
|
|
105
106
|
readonly args?: StepArgs;
|
|
107
|
+
/**
|
|
108
|
+
* Checks the `args` at execution: each against the key of the blueprint
|
|
109
|
+
* at its position; `fn` gets the values as the keys handled them.
|
|
110
|
+
*/
|
|
111
|
+
readonly params?: AnyBlueprint;
|
|
106
112
|
readonly as?: string;
|
|
107
113
|
} | {
|
|
108
114
|
readonly return: StepValue;
|
|
@@ -163,6 +169,7 @@ export type UseCaseStepDescription = {
|
|
|
163
169
|
readonly kind: "opaque";
|
|
164
170
|
};
|
|
165
171
|
readonly args?: StepArgs;
|
|
172
|
+
readonly params?: FieldsDescription;
|
|
166
173
|
readonly as?: string;
|
|
167
174
|
} | {
|
|
168
175
|
readonly return: StepValue;
|