@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.
Files changed (82) hide show
  1. package/README.md +220 -733
  2. package/dist/core/application/application.d.ts +9 -1
  3. package/dist/core/application/exceptions.d.ts +28 -0
  4. package/dist/core/application/registry.d.ts +33 -0
  5. package/dist/core/application/start.d.ts +75 -0
  6. package/dist/core/application/types/application.interface.d.ts +229 -7
  7. package/dist/core/blueprint/types/blueprint.interface.d.ts +66 -11
  8. package/dist/core/blueprint/types/slice.interface.d.ts +7 -2
  9. package/dist/core/command/command.d.ts +25 -0
  10. package/dist/core/command/exceptions.d.ts +33 -0
  11. package/dist/core/command/types/command.interface.d.ts +108 -0
  12. package/dist/core/entity/event.d.ts +12 -2
  13. package/dist/core/environment/environment.d.ts +4 -1
  14. package/dist/core/exception/exception.d.ts +4 -0
  15. package/dist/core/exception/exported.d.ts +4 -0
  16. package/dist/core/json.d.ts +10 -0
  17. package/dist/core/logger/exceptions.d.ts +4 -0
  18. package/dist/core/logger/logger.d.ts +106 -0
  19. package/dist/core/output/exceptions.d.ts +8 -0
  20. package/dist/core/output/openapi-sink.d.ts +83 -0
  21. package/dist/core/output/output.d.ts +19 -0
  22. package/dist/core/output/problem.d.ts +15 -0
  23. package/dist/core/output/select.d.ts +14 -0
  24. package/dist/core/output/stage.d.ts +24 -0
  25. package/dist/core/output/status.d.ts +17 -0
  26. package/dist/core/output/types/output.interface.d.ts +171 -0
  27. package/dist/core/property/abstract-property.d.ts +8 -7
  28. package/dist/core/property/define-property.d.ts +8 -7
  29. package/dist/core/property/property.d.ts +26 -15
  30. package/dist/core/property/types/abstract-property-constructor.type.d.ts +8 -7
  31. package/dist/core/property/types/define-property.interface.d.ts +5 -4
  32. package/dist/core/property/types/optional-property.type.d.ts +5 -5
  33. package/dist/core/property/types/property-collection.type.d.ts +2 -2
  34. package/dist/core/property/types/property-constructor.type.d.ts +5 -1
  35. package/dist/core/property/types/property-options.interface.d.ts +17 -8
  36. package/dist/core/property/types/property-type.interface.d.ts +7 -3
  37. package/dist/core/property/types/property.interface.d.ts +5 -4
  38. package/dist/core/record/abstract-record.d.ts +12 -4
  39. package/dist/core/record/types/plugin.interface.d.ts +13 -3
  40. package/dist/core/record/types/record.interface.d.ts +20 -8
  41. package/dist/core/repository/exceptions.d.ts +4 -0
  42. package/dist/core/repository/in-memory.d.ts +3 -0
  43. package/dist/core/repository/provider.d.ts +6 -0
  44. package/dist/core/repository/query.d.ts +9 -0
  45. package/dist/core/repository/references.d.ts +4 -2
  46. package/dist/core/repository/repository.d.ts +4 -2
  47. package/dist/core/repository/state.d.ts +4 -0
  48. package/dist/core/repository/types/query.interface.d.ts +67 -0
  49. package/dist/core/repository/types/repository.interface.d.ts +13 -13
  50. package/dist/core/schema/types/schema.interface.d.ts +11 -4
  51. package/dist/core/symbols/meta.d.ts +2 -0
  52. package/dist/core/use-case/exceptions.d.ts +10 -0
  53. package/dist/core/use-case/scope.d.ts +24 -0
  54. package/dist/core/use-case/steps/describe.d.ts +13 -0
  55. package/dist/core/use-case/steps/references.d.ts +27 -0
  56. package/dist/core/use-case/steps/run.d.ts +20 -0
  57. package/dist/core/use-case/steps/types/steps.interface.d.ts +153 -0
  58. package/dist/core/use-case/steps/validate.d.ts +17 -0
  59. package/dist/core/use-case/use-case.d.ts +79 -13
  60. package/dist/experimental.d.ts +6 -0
  61. package/dist/experimental.js +123 -0
  62. package/dist/index-ewm6r4nw.js +5335 -0
  63. package/dist/index-hn82hm31.js +1651 -0
  64. package/dist/index.d.ts +34 -12
  65. package/dist/index.js +343 -4142
  66. package/dist/std/blueprint-plugin.d.ts +10 -10
  67. package/dist/std/blueprint.d.ts +10 -10
  68. package/dist/std/entity.d.ts +2 -2
  69. package/dist/std/properties/contact.d.ts +4 -4
  70. package/dist/std/properties/enum.d.ts +1 -1
  71. package/dist/std/properties/headless.d.ts +1 -1
  72. package/dist/std/properties/identity.d.ts +2 -2
  73. package/dist/std/properties/password.d.ts +1 -1
  74. package/dist/std/properties/primitives.d.ts +5 -5
  75. package/dist/std/properties/time.d.ts +2 -2
  76. package/dist/std/record-plugins.d.ts +12 -12
  77. package/dist/std/record.d.ts +1 -1
  78. package/dist/std/stages.d.ts +2 -0
  79. package/dist/testing/index.d.ts +2 -0
  80. package/dist/testing/index.js +502 -0
  81. package/dist/testing/provider-contract.d.ts +22 -0
  82. package/package.json +28 -8
@@ -1,8 +1,11 @@
1
1
  import type { AnyBlueprint, BlueprintInput, BlueprintValue, IBlueprint, IBlueprintCollection } from "../blueprint/types/blueprint.interface.js";
2
2
  import type { EntityEvent } from "../entity/event.js";
3
3
  import { type AnyEnvironment, type EnvironmentValue } from "../environment/environment.js";
4
+ import { type DeclaredExceptionClass } from "../exception/exception.js";
5
+ import { type ILogger, type LoggerDeclaration } from "../logger/logger.js";
4
6
  import type { AnyRepository } from "../repository/types/repository.interface.js";
5
7
  import { Kind } from "../symbols/kind.js";
8
+ import type { UseCaseStep } from "./steps/types/steps.interface.js";
6
9
  /** A concrete UseCase declaration, usable as a dependency. */
7
10
  export interface AnyUseCaseClass {
8
11
  readonly [Kind]: "use-case";
@@ -10,16 +13,23 @@ export interface AnyUseCaseClass {
10
13
  readonly blueprint: AnyBlueprint;
11
14
  readonly response: AnyBlueprint;
12
15
  readonly dependencies: UseCaseCollection;
13
- new (input: any): {
16
+ readonly context: AnyBlueprint | undefined;
17
+ readonly description: string | undefined;
18
+ readonly tags: readonly string[];
19
+ readonly deprecated: boolean;
20
+ readonly errors: readonly AnyExceptionClass[];
21
+ new (input: any, ...options: any[]): {
14
22
  execute(): Promise<unknown>;
15
23
  pullEvents(): EntityEvent[];
16
24
  };
17
25
  }
26
+ /** Any class made by `exceptionOf`. */
27
+ export type AnyExceptionClass = DeclaredExceptionClass<any>;
18
28
  /**
19
29
  * What a use case can depend on: another use case, a repository made by
20
- * `repositoryOf`, or an environment made by `environmentOf`.
30
+ * `repositoryOf`, an environment made by `environmentOf`, or `Logger`.
21
31
  */
22
- export type UseCaseDependency = AnyUseCaseClass | AnyRepository | AnyEnvironment;
32
+ export type UseCaseDependency = AnyUseCaseClass | AnyRepository | AnyEnvironment | LoggerDeclaration;
23
33
  export type UseCaseCollection = Readonly<Record<string, UseCaseDependency>>;
24
34
  type Declaration = {
25
35
  readonly blueprint: AnyBlueprint;
@@ -29,11 +39,26 @@ type Declaration = {
29
39
  export type UseCaseInput<Class extends Declaration> = BlueprintInput<Class["blueprint"]["properties"]>;
30
40
  export type UseCaseResponse<Class extends Declaration> = BlueprintValue<Class["response"]["properties"]>;
31
41
  export type UseCaseDependencies<Class extends Declaration> = DependencyFunctions<Class["dependencies"]>;
42
+ /** The context of a use case class, as `this.context` gives it; `undefined` without one. */
43
+ export type UseCaseContext<Class extends {
44
+ readonly context: unknown;
45
+ }> = Class["context"] extends IBlueprint<infer Properties> ? Readonly<BlueprintValue<Properties>> : undefined;
46
+ /**
47
+ * The second argument of the constructor of a use case: `{ context }`,
48
+ * required when the use case declares a context, not accepted otherwise.
49
+ */
50
+ export type UseCaseOptions<Context extends IBlueprintCollection | undefined = undefined> = Context extends IBlueprintCollection ? {
51
+ readonly context: BlueprintInput<Context>;
52
+ } : {
53
+ readonly context?: undefined;
54
+ };
55
+ /** @internal */
56
+ type OptionsArgs<Context extends IBlueprintCollection | undefined> = Context extends IBlueprintCollection ? [options: UseCaseOptions<Context>] : [];
32
57
  /**
33
58
  * What a use case gets for a dependency: a function for a use case, the
34
- * repository itself, or the variables of an environment.
59
+ * repository itself, the variables of an environment, or an `ILogger`.
35
60
  */
36
- export type DependencyOf<Dependency> = Dependency extends AnyUseCaseClass ? (input: UseCaseInput<Dependency>) => Promise<UseCaseResponse<Dependency>> : Dependency extends AnyEnvironment ? EnvironmentValue<Dependency> : Dependency;
61
+ export type DependencyOf<Dependency> = Dependency extends AnyUseCaseClass ? (input: UseCaseInput<Dependency>) => Promise<UseCaseResponse<Dependency>> : Dependency extends AnyEnvironment ? EnvironmentValue<Dependency> : Dependency extends LoggerDeclaration ? ILogger : Dependency;
37
62
  export type DependencyFunctions<Dependencies extends UseCaseCollection> = {
38
63
  readonly [Key in keyof Dependencies]: DependencyOf<Dependencies[Key]>;
39
64
  };
@@ -44,36 +69,77 @@ export type DependencyFunctions<Dependencies extends UseCaseCollection> = {
44
69
  export type DependencyOverrides<Dependencies extends UseCaseCollection> = {
45
70
  readonly [Key in keyof Dependencies]?: Dependencies[Key] extends AnyRepository ? Partial<Dependencies[Key]> : DependencyOf<Dependencies[Key]>;
46
71
  };
47
- export interface UseCaseSettings<Input extends IBlueprintCollection, Output extends IBlueprintCollection, Dependencies extends UseCaseCollection> {
72
+ export interface UseCaseSettings<Input extends IBlueprintCollection, Output extends IBlueprintCollection, Dependencies extends UseCaseCollection, Context extends IBlueprintCollection | undefined = undefined> {
48
73
  readonly name: string;
49
74
  readonly blueprint: IBlueprint<Input>;
50
75
  readonly response: IBlueprint<Output>;
51
76
  readonly dependencies?: Dependencies;
77
+ /** What the caller supplies with each call, e.g. the user and the request; validated by this blueprint. */
78
+ readonly context?: IBlueprint<Exclude<Context, undefined>>;
79
+ readonly description?: string;
80
+ readonly tags?: readonly string[];
81
+ readonly deprecated?: boolean;
82
+ /** The exceptions (made by `exceptionOf`) a caller should expect; documentation only. */
83
+ readonly errors?: readonly AnyExceptionClass[];
52
84
  }
53
- export declare const InvalidUseCaseDeclaration: import("../../index.js").DeclaredExceptionClass<{
85
+ /** `UseCaseSettings` with the `steps` that are its `execute`. */
86
+ export interface UseCaseStepsSettings<Input extends IBlueprintCollection, Output extends IBlueprintCollection, Dependencies extends UseCaseCollection, Context extends IBlueprintCollection | undefined = undefined> extends UseCaseSettings<Input, Output, Dependencies, Context> {
87
+ /**
88
+ * What the use case does, as data (see `UseCaseStep`): validated here,
89
+ * interpreted by its `execute`, and described by `describe()`.
90
+ */
91
+ readonly steps: readonly UseCaseStep<NoInfer<Dependencies>>[];
92
+ }
93
+ export declare const InvalidUseCaseDeclaration: DeclaredExceptionClass<{
54
94
  reason: string;
55
95
  }>;
56
- export declare const UseCaseAlreadyExecuted: import("../../index.js").DeclaredExceptionClass<undefined>;
57
- export declare const InvalidUseCaseDependencies: import("../../index.js").DeclaredExceptionClass<undefined>;
96
+ export declare const UseCaseAlreadyExecuted: DeclaredExceptionClass<undefined>;
97
+ export declare const InvalidUseCaseDependencies: DeclaredExceptionClass<undefined>;
58
98
  /** An operation with validated input, dependency functions and a drainable event queue. */
59
99
  export declare abstract class AbstractUseCase<Input extends IBlueprintCollection, Output extends IBlueprintCollection, Dependencies extends UseCaseCollection> {
60
100
  readonly input: Readonly<BlueprintValue<Input>>;
61
- constructor(input: BlueprintInput<Input>);
101
+ constructor(input: BlueprintInput<Input>, ...args: unknown[]);
62
102
  /** Default parameter for implementations: `execute(deps = this.dependencies)`. */
63
103
  get dependencies(): DependencyFunctions<Dependencies>;
64
104
  abstract execute(dependencies?: DependencyOverrides<Dependencies>): Promise<BlueprintValue<Output>>;
65
105
  /** Immediately drains collected events; never executes their handlers. */
66
106
  pullEvents(): EntityEvent[];
67
107
  }
68
- export type UseCaseConstructor<Input extends IBlueprintCollection, Output extends IBlueprintCollection, Dependencies extends UseCaseCollection> = (abstract new (input: BlueprintInput<Input>) => AbstractUseCase<Input, Output, Dependencies>) & {
108
+ /** What every use case class carries, abstract or not. */
109
+ type UseCaseStatics<Input extends IBlueprintCollection, Output extends IBlueprintCollection, Dependencies extends UseCaseCollection, Context extends IBlueprintCollection | undefined> = {
69
110
  readonly [Kind]: "use-case";
70
111
  readonly useCaseName: string;
71
112
  readonly blueprint: IBlueprint<Input>;
72
113
  readonly response: IBlueprint<Output>;
73
114
  readonly dependencies: Dependencies;
115
+ /** The blueprint of the context of each call, or `undefined`. */
116
+ readonly context: Context extends IBlueprintCollection ? IBlueprint<Context> : undefined;
117
+ readonly description: string | undefined;
118
+ readonly tags: readonly string[];
119
+ readonly deprecated: boolean;
120
+ /** The exceptions a caller should expect. */
121
+ readonly errors: readonly AnyExceptionClass[];
122
+ };
123
+ /** What a use case instance carries beyond `AbstractUseCase`. */
124
+ type UseCaseInstanceContext<Context extends IBlueprintCollection | undefined> = {
125
+ /** What the caller supplied with the call, parsed and frozen; `undefined` without a declared context. */
126
+ readonly context: Context extends IBlueprintCollection ? Readonly<BlueprintValue<Context>> : undefined;
74
127
  };
75
- /** Declares an abstract operation. Extend it and implement an async execute method. */
76
- export declare function useCaseOf<Input extends IBlueprintCollection, Output extends IBlueprintCollection, const Dependencies extends UseCaseCollection = Record<never, never>>(settings: UseCaseSettings<Input, Output, Dependencies>): UseCaseConstructor<Input, Output, Dependencies>;
128
+ export type UseCaseConstructor<Input extends IBlueprintCollection, Output extends IBlueprintCollection, Dependencies extends UseCaseCollection, Context extends IBlueprintCollection | undefined = undefined> = (abstract new (input: BlueprintInput<Input>, ...options: OptionsArgs<Context>) => AbstractUseCase<Input, Output, Dependencies> & UseCaseInstanceContext<Context>) & UseCaseStatics<Input, Output, Dependencies, Context>;
129
+ /**
130
+ * What `useCaseOf` gives with `steps`: concrete, its `execute` interprets the
131
+ * steps. A subclass may still override `execute`.
132
+ */
133
+ export type StepsUseCaseConstructor<Input extends IBlueprintCollection, Output extends IBlueprintCollection, Dependencies extends UseCaseCollection, Context extends IBlueprintCollection | undefined = undefined> = (new (input: BlueprintInput<Input>, ...options: OptionsArgs<Context>) => Omit<AbstractUseCase<Input, Output, Dependencies>, "execute"> & UseCaseInstanceContext<Context> & {
134
+ execute(dependencies?: DependencyOverrides<Dependencies>): Promise<BlueprintValue<Output>>;
135
+ }) & UseCaseStatics<Input, Output, Dependencies, Context>;
136
+ /**
137
+ * Declares an operation. With `steps` the class is concrete: its `execute`
138
+ * interprets them. Without, it is abstract: extend it and implement an async
139
+ * execute method.
140
+ */
141
+ export declare function useCaseOf<Input extends IBlueprintCollection, Output extends IBlueprintCollection, const Dependencies extends UseCaseCollection = Record<never, never>, Context extends IBlueprintCollection | undefined = undefined>(settings: UseCaseStepsSettings<Input, Output, Dependencies, Context>): StepsUseCaseConstructor<Input, Output, Dependencies, Context>;
142
+ export declare function useCaseOf<Input extends IBlueprintCollection, Output extends IBlueprintCollection, const Dependencies extends UseCaseCollection = Record<never, never>, Context extends IBlueprintCollection | undefined = undefined>(settings: UseCaseSettings<Input, Output, Dependencies, Context>): UseCaseConstructor<Input, Output, Dependencies, Context>;
77
143
  export declare function isUseCaseClass(value: unknown): value is AnyUseCaseClass;
78
144
  export declare function isUseCase(value: unknown): value is AbstractUseCase<IBlueprintCollection, IBlueprintCollection, UseCaseCollection>;
79
145
  export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `@afora/core/experimental`: features that may change in any release. Not
3
+ * covered by the compatibility policy in `VERSIONING.md`.
4
+ */
5
+ export { sliceOf, sliceValue } from "./core/blueprint/slice.js";
6
+ export type { SlicedBlueprint, SlicedValue, SliceMask, ValueMask, } from "./core/blueprint/types/slice.interface.js";
@@ -0,0 +1,123 @@
1
+ import {
2
+ isBlueprint2,
3
+ isRecordClass2,
4
+ recordFieldOf2,
5
+ listOf2,
6
+ isBlueprintList2,
7
+ AbstractRecord2,
8
+ declareRecord,
9
+ UndeclaredBlueprintProperty2,
10
+ optionalOf2,
11
+ blueprintFromProperties
12
+ } from "./index-hn82hm31.js";
13
+
14
+ // src/core/blueprint/slice.ts
15
+ function propertiesOfField(field) {
16
+ if (field === undefined)
17
+ return;
18
+ if (isBlueprintList2(field))
19
+ return propertiesOfField(field.item);
20
+ const record = recordFieldOf2(field);
21
+ if (record)
22
+ return record.record.blueprint.properties;
23
+ return isBlueprint2(field) ? field.properties : undefined;
24
+ }
25
+ function maskOf(selector, against) {
26
+ if (isRecordClass2(selector))
27
+ return maskOf(selector.blueprint, against);
28
+ if (isBlueprint2(selector)) {
29
+ return Object.fromEntries(Object.entries(selector.properties).map(([key, field]) => [key, fieldMaskOf(field, against?.[key])]));
30
+ }
31
+ return selector;
32
+ }
33
+ function fieldMaskOf(field, other) {
34
+ if (isBlueprintList2(field)) {
35
+ return fieldMaskOf(field.item, isBlueprintList2(other) ? other.item : other);
36
+ }
37
+ const record = recordFieldOf2(field);
38
+ if (record) {
39
+ if (record.record === recordFieldOf2(other)?.record)
40
+ return true;
41
+ return maskOf(record.record, propertiesOfField(other));
42
+ }
43
+ if (isBlueprint2(field)) {
44
+ return field === other ? true : maskOf(field, propertiesOfField(other));
45
+ }
46
+ return true;
47
+ }
48
+ function sliceCollection(properties, mask, path) {
49
+ const sliced = {};
50
+ for (const [key, selected] of Object.entries(mask)) {
51
+ if (!selected)
52
+ continue;
53
+ if (!Object.hasOwn(properties, key)) {
54
+ throw new UndeclaredBlueprintProperty2({
55
+ source: "blueprint",
56
+ details: { key: path + key }
57
+ });
58
+ }
59
+ sliced[key] = sliceField(properties[key], selected, path + key);
60
+ }
61
+ return sliced;
62
+ }
63
+ function slicedRecordOf(Original, mask, path) {
64
+ const source = Original.blueprint;
65
+ const blueprint = blueprintFromProperties(source.plugins, sliceCollection(source.properties, mask, `${path}.`));
66
+ const kept = (keys) => keys.filter((key) => Object.hasOwn(blueprint.properties, key));
67
+ const { sensitive, unique, change } = Original.options;
68
+ const Sliced = declareRecord(AbstractRecord2, blueprint, {
69
+ sensitive: kept(sensitive),
70
+ unique: kept(unique),
71
+ change: kept(change)
72
+ }, Original.name);
73
+ Object.defineProperty(Sliced, Symbol.hasInstance, {
74
+ value(value) {
75
+ return Function.prototype[Symbol.hasInstance].call(this, value) || value instanceof Original;
76
+ }
77
+ });
78
+ return Sliced;
79
+ }
80
+ function sliceField(field, selected, path) {
81
+ if (selected === true)
82
+ return field;
83
+ if (isBlueprintList2(field)) {
84
+ const list = listOf2(sliceField(field.item, selected, path), {
85
+ minItems: field.minItems,
86
+ maxItems: field.maxItems
87
+ });
88
+ return field.optional ? optionalOf2(list) : list;
89
+ }
90
+ const record = recordFieldOf2(field);
91
+ if (record) {
92
+ const sliced = slicedRecordOf(record.record, selected, path);
93
+ return record.optional ? optionalOf2(sliced) : sliced;
94
+ }
95
+ if (isBlueprint2(field)) {
96
+ const sliced = blueprintFromProperties(field.plugins, sliceCollection(field.properties, selected, `${path}.`));
97
+ return field.optional ? optionalOf2(sliced) : sliced;
98
+ }
99
+ throw new UndeclaredBlueprintProperty2({
100
+ source: "blueprint",
101
+ details: { key: `${path}.${Object.keys(selected)[0] ?? ""}` }
102
+ });
103
+ }
104
+ var sliceOf = (blueprint, selector) => blueprintFromProperties(blueprint.plugins, sliceCollection(blueprint.properties, maskOf(selector, blueprint.properties), ""));
105
+ var sliceValue = (value, selector) => pick(value, maskOf(selector));
106
+ function pick(value, mask) {
107
+ if (Array.isArray(value))
108
+ return value.map((item) => pick(item, mask));
109
+ if (typeof value !== "object" || value === null)
110
+ return value;
111
+ const source = isRecordClass2(value.constructor) ? value.toJSON() : value;
112
+ const picked = {};
113
+ for (const [key, selected] of Object.entries(mask)) {
114
+ if (!selected || !Object.hasOwn(source, key))
115
+ continue;
116
+ picked[key] = selected === true ? source[key] : pick(source[key], selected);
117
+ }
118
+ return picked;
119
+ }
120
+ export {
121
+ sliceOf,
122
+ sliceValue
123
+ };