@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
@@ -16,13 +16,17 @@ export declare function isApplication(value: unknown): value is AnyApplication;
16
16
  * `describe()` gives the graph. With a `provider` (or that of the last
17
17
  * application extended that has one), `repositories` are bound to it.
18
18
  */
19
- export declare function applicationOf<const Name extends string, const Extended extends readonly AnyApplication[] = readonly [], const Properties extends ApplicationCollections["properties"] = Record<never, never>, const Blueprints extends ApplicationCollections["blueprints"] = Record<never, never>, const Records extends ApplicationCollections["records"] = Record<never, never>, const Entities extends ApplicationCollections["entities"] = Record<never, never>, const Environments extends ApplicationCollections["environments"] = Record<never, never>, const Repositories extends ApplicationCollections["repositories"] = Record<never, never>>(settings: ApplicationSettings<Name, Extended, {
19
+ export declare function applicationOf<const Name extends string, const Extended extends readonly AnyApplication[] = readonly [], const Properties extends ApplicationCollections["properties"] = Record<never, never>, const Blueprints extends ApplicationCollections["blueprints"] = Record<never, never>, const Records extends ApplicationCollections["records"] = Record<never, never>, const Entities extends ApplicationCollections["entities"] = Record<never, never>, const Environments extends ApplicationCollections["environments"] = Record<never, never>, const Repositories extends ApplicationCollections["repositories"] = Record<never, never>, const UseCases extends ApplicationCollections["useCases"] = Record<never, never>, const Commands extends ApplicationCollections["commands"] = Record<never, never>, const Outputs extends ApplicationCollections["outputs"] = Record<never, never>, const Exceptions extends ApplicationCollections["exceptions"] = Record<never, never>>(settings: ApplicationSettings<Name, Extended, {
20
20
  properties: Properties;
21
21
  blueprints: Blueprints;
22
22
  records: Records;
23
23
  entities: Entities;
24
24
  environments: Environments;
25
25
  repositories: Repositories;
26
+ useCases: UseCases;
27
+ commands: Commands;
28
+ outputs: Outputs;
29
+ exceptions: Exceptions;
26
30
  }>): IApplication<Name, MergedCollections<{
27
31
  properties: Properties;
28
32
  blueprints: Blueprints;
@@ -30,4 +34,8 @@ export declare function applicationOf<const Name extends string, const Extended
30
34
  entities: Entities;
31
35
  environments: Environments;
32
36
  repositories: Repositories;
37
+ useCases: UseCases;
38
+ commands: Commands;
39
+ outputs: Outputs;
40
+ exceptions: Exceptions;
33
41
  }, Extended>>;
@@ -27,3 +27,31 @@ export declare const UnregisteredRepositoryDependency: import("../../index.js").
27
27
  readonly repository: string;
28
28
  readonly dependency: string;
29
29
  }>;
30
+ /** A dependency of a use case of the application (a use case, repository or environment) is not registered in it. */
31
+ export declare const UnregisteredUseCaseDependency: import("../../index.js").DeclaredExceptionClass<{
32
+ readonly useCase: string;
33
+ readonly dependency: string;
34
+ }>;
35
+ /**
36
+ * The application provider is made by `providerFrom` and the application has
37
+ * not been started: `await app.start()` creates it.
38
+ */
39
+ export declare const ApplicationNotStarted: import("../../index.js").DeclaredExceptionClass<{
40
+ /** The application, or none when `useApplication()` found none started. */
41
+ readonly application?: string;
42
+ }>;
43
+ /**
44
+ * `app.start()` was called while another application is the started one of
45
+ * the process; `start({ replace: true })` stops it and takes its place.
46
+ */
47
+ export declare const ApplicationAlreadyStarted: import("../../index.js").DeclaredExceptionClass<{
48
+ readonly current: string;
49
+ readonly attempted: string;
50
+ }>;
51
+ /**
52
+ * `app.start()` found environments whose variables are missing or rejected;
53
+ * `details.environments` gives the keys of each, by name.
54
+ */
55
+ export declare const InvalidApplicationEnvironment: import("../../index.js").DeclaredExceptionClass<{
56
+ readonly environments: Readonly<Record<string, readonly string[]>>;
57
+ }>;
@@ -0,0 +1,33 @@
1
+ import type { AnyApplication } from "./types/application.interface.js";
2
+ /**
3
+ * Declares the type of the application `useApplication()` gives, by module
4
+ * augmentation, once per project:
5
+ *
6
+ * ```ts
7
+ * declare module "@afora/core" {
8
+ * interface Register { application: typeof App }
9
+ * }
10
+ * ```
11
+ */
12
+ export interface Register {
13
+ }
14
+ /** The application declared in `Register`, or any application. */
15
+ export type RegisteredApplication = Register extends {
16
+ readonly application: infer Application extends AnyApplication;
17
+ } ? Application : AnyApplication;
18
+ /**
19
+ * The application started in this process, from anywhere, without importing
20
+ * it (so a hand-written use case or handler that the application imports can
21
+ * reach it without an import cycle). Throws `ApplicationNotStarted` when none
22
+ * is. Typed by `Register`.
23
+ */
24
+ export declare function useApplication(): RegisteredApplication;
25
+ /**
26
+ * Forgets the started application without stopping it, e.g. between tests,
27
+ * so that another one can start.
28
+ */
29
+ export declare function resetApplication(): void;
30
+ /** The started application of the process, if any. @internal */
31
+ export declare function startedApplication(): AnyApplication | undefined;
32
+ /** Makes `application` the started one. @internal */
33
+ export declare function setStartedApplication(application: AnyApplication | undefined): void;
@@ -0,0 +1,75 @@
1
+ import { type AnyEnvironment, type EnvironmentDependencies, type MergedEnvironment } from "../environment/environment.js";
2
+ import type { LogSettings } from "../logger/logger.js";
3
+ import type { IRepositoryProvider } from "../repository/types/repository.interface.js";
4
+ import { Kind } from "../symbols/kind.js";
5
+ import type { ApplicationContext } from "./types/application.interface.js";
6
+ /**
7
+ * A provider an application creates when it starts, from the variables of
8
+ * its environments: what `providerFrom` makes.
9
+ */
10
+ export interface ProviderFactory<Environment extends EnvironmentDependencies = EnvironmentDependencies> {
11
+ readonly [Kind]: "provider-factory";
12
+ /** The environments whose variables `create` gets, as a list. */
13
+ readonly environments: readonly AnyEnvironment[];
14
+ readonly create: (env: MergedEnvironment<Environment>) => IRepositoryProvider | Promise<IRepositoryProvider>;
15
+ /** Whether `create` takes the context of the application instead of variables. */
16
+ readonly withContext: boolean;
17
+ }
18
+ /**
19
+ * A provider made on `app.start()`: `create` gets the variables of
20
+ * `environmentDependencies` (one environment or a list, merged in order),
21
+ * read and validated then, e.g.
22
+ * `providerFrom(Database, (env) => drizzleProvider({ db: connect(env.DATABASE_URL), tables }))`.
23
+ */
24
+ export declare function providerFrom<const Environment extends EnvironmentDependencies>(environmentDependencies: Environment, create: (env: MergedEnvironment<Environment>) => IRepositoryProvider | Promise<IRepositoryProvider>): ProviderFactory<Environment>;
25
+ /**
26
+ * A provider made on `app.start()` from the context of the application:
27
+ * `ctx.env(Database)` gives the variables of a registered environment and
28
+ * `ctx.logger` its logger, e.g.
29
+ * `providerFrom((ctx) => drizzleProvider({ db: connect(ctx.env(Database).DATABASE_URL), tables }))`.
30
+ */
31
+ export declare function providerFrom(create: (context: ApplicationContext) => IRepositoryProvider | Promise<IRepositoryProvider>): ProviderFactory<readonly []>;
32
+ /** A `log` an application makes on `app.start()`: what `logFrom` makes. */
33
+ export interface LogFactory {
34
+ readonly [Kind]: "log-factory";
35
+ readonly create: (context: ApplicationContext) => LogSettings | Promise<LogSettings>;
36
+ }
37
+ /**
38
+ * A `log` made on `app.start()` from the context of the application, before
39
+ * its provider, e.g. `logFrom((ctx) => ({ level: ctx.env(Logging).LOG_LEVEL,
40
+ * providers: [jsonLogProvider()] }))`. Until then the application logs nothing.
41
+ */
42
+ export declare function logFrom(create: (context: ApplicationContext) => LogSettings | Promise<LogSettings>): LogFactory;
43
+ /** Whether `value` was made by `logFrom`. */
44
+ export declare function isLogFactory(value: unknown): value is LogFactory;
45
+ /** Whether `value` was made by `providerFrom`. */
46
+ export declare function isProviderFactory(value: unknown): value is ProviderFactory;
47
+ /**
48
+ * The provider the repositories of an application are bound to when it was
49
+ * declared with `providerFrom`: each call goes to the provider made on start,
50
+ * and throws `ApplicationNotStarted` before it.
51
+ * @internal
52
+ */
53
+ export declare function deferredProvider(factory: ProviderFactory, application: string): IRepositoryProvider;
54
+ /** Whether `provider` was made by `deferredProvider`. @internal */
55
+ export declare function isDeferredProvider(provider: unknown): boolean;
56
+ /** The environments the factory behind `provider` reads, if it is deferred. @internal */
57
+ export declare function deferredEnvironments(provider: unknown): readonly AnyEnvironment[];
58
+ /**
59
+ * Makes the provider behind `provider` once, from the variables of its
60
+ * environments (already validated by the caller); later calls give the same.
61
+ * @internal
62
+ */
63
+ export declare function startDeferred(provider: IRepositoryProvider, context: ApplicationContext): Promise<IRepositoryProvider> | undefined;
64
+ /**
65
+ * Closes the provider made on start and forgets it, so that calls throw
66
+ * `ApplicationNotStarted` again; a provider that is not deferred is closed.
67
+ * @internal
68
+ */
69
+ export declare function stopProvider(provider: IRepositoryProvider): Promise<void>;
70
+ /**
71
+ * Throws `InvalidCommandDeclaration` when `provider` cannot run the
72
+ * transactions of `commands` (it has no `transaction`, or says it has none).
73
+ * @internal
74
+ */
75
+ export declare function checkTransactions(source: string, provider: IRepositoryProvider, commands: readonly string[]): void;
@@ -1,7 +1,15 @@
1
- import type { AnyEnvironment } from "../../environment/environment.js";
1
+ import type { AnyCommand } from "../../command/types/command.interface.js";
2
+ import type { AnyEnvironment, EnvironmentValue } from "../../environment/environment.js";
3
+ import type { ExceptionLayers } from "../../exception/enums/exception-layers.js";
4
+ import type { ILogger, LogLevel, LogSettings } from "../../logger/logger.js";
5
+ import type { AnyOutput, StageDescription } from "../../output/types/output.interface.js";
2
6
  import type { RecordPluginDescriptor } from "../../record/types/plugin.interface.js";
7
+ import type { ProviderDescription } from "../../repository/types/query.interface.js";
3
8
  import type { AnyRepository, IRepositoryProvider } from "../../repository/types/repository.interface.js";
4
9
  import type { Kind } from "../../symbols/kind.js";
10
+ import type { UseCaseStepDescription } from "../../use-case/steps/types/steps.interface.js";
11
+ import type { AnyExceptionClass, AnyUseCaseClass } from "../../use-case/use-case.js";
12
+ import type { LogFactory, ProviderFactory } from "../start.js";
5
13
  type Named<Value> = Readonly<Record<string, Value>>;
6
14
  /** The declarations of an application, by name, one collection for each kind. */
7
15
  export interface ApplicationCollections {
@@ -11,8 +19,15 @@ export interface ApplicationCollections {
11
19
  readonly entities: Named<unknown>;
12
20
  readonly environments: Named<AnyEnvironment>;
13
21
  readonly repositories: Named<AnyRepository>;
22
+ readonly useCases: Named<AnyUseCaseClass>;
23
+ readonly commands: Named<AnyCommand>;
24
+ readonly outputs: Named<AnyOutput>;
25
+ readonly exceptions: Named<AnyExceptionClass>;
14
26
  }
15
- /** The keys of `ApplicationCollections`. */
27
+ /**
28
+ * The keys of `ApplicationCollections`.
29
+ * @internal
30
+ */
16
31
  export type ApplicationCollectionKey = keyof ApplicationCollections;
17
32
  /** What `applicationOf` takes. */
18
33
  export interface ApplicationSettings<Name extends string, Extended extends readonly AnyApplication[], Collections extends ApplicationCollections> {
@@ -26,14 +41,49 @@ export interface ApplicationSettings<Name extends string, Extended extends reado
26
41
  readonly entities?: Collections["entities"];
27
42
  readonly environments?: Collections["environments"];
28
43
  readonly repositories?: Collections["repositories"];
44
+ /** Use cases, keyed by their `useCaseName`. */
45
+ readonly useCases?: Collections["useCases"];
46
+ /** Commands, keyed by their `commandName`; every use case of each must be in `useCases`. */
47
+ readonly commands?: Collections["commands"];
48
+ /** Outputs, keyed by their `outputName`; every command of each must be in `commands`. */
49
+ readonly outputs?: Collections["outputs"];
50
+ /** Exceptions made by `exceptionOf`, keyed by their `name`. */
51
+ readonly exceptions?: Collections["exceptions"];
29
52
  /**
30
53
  * The provider of the repositories that did not declare one; the
31
- * repositories of the application read and write through it.
54
+ * repositories of the application read and write through it. With
55
+ * `providerFrom`, it is made by `app.start()` from the variables of its
56
+ * environments, and calls before that throw `ApplicationNotStarted`.
57
+ */
58
+ readonly provider?: IRepositoryProvider | ProviderFactory;
59
+ /**
60
+ * How it logs: the providers its lines go to and the lowest level kept.
61
+ * Use cases, commands and repository calls of the application are logged
62
+ * through it, and a use case gets it as its `Logger` dependency. Without
63
+ * it, the one of the last extended application that has one; none by default.
32
64
  */
33
- readonly provider?: IRepositoryProvider;
65
+ readonly log?: LogSettings | LogFactory;
66
+ }
67
+ /**
68
+ * What an application gives the factories it runs on start (`providerFrom`,
69
+ * `logFrom`) and your code (`app.context`).
70
+ */
71
+ export interface ApplicationContext {
72
+ /** The name of the application. */
73
+ readonly name: string;
74
+ /**
75
+ * The variables of an environment registered in the application, typed by
76
+ * its blueprint; one that is not registered throws `UnregisteredApplicationReference`.
77
+ */
78
+ env<Environment extends AnyEnvironment>(environment: Environment): EnvironmentValue<Environment>;
79
+ /** Its logger; a `log` of `logFrom` writes nothing before `start()`. */
80
+ readonly logger: ILogger;
34
81
  }
35
82
  type UnionToIntersection<Union> = (Union extends unknown ? (value: Union) => void : never) extends (value: infer Intersection) => void ? Intersection : never;
36
- /** `Own` with the declarations of `Extended` merged in. */
83
+ /**
84
+ * `Own` with the declarations of `Extended` merged in.
85
+ * @internal
86
+ */
37
87
  export type MergedCollections<Own extends ApplicationCollections, Extended extends readonly AnyApplication[]> = {
38
88
  readonly [Key in ApplicationCollectionKey]: Own[Key] & UnionToIntersection<Extended[number][Key]>;
39
89
  };
@@ -45,6 +95,10 @@ export interface IApplication<Name extends string, Collections extends Applicati
45
95
  readonly extends: readonly AnyApplication[];
46
96
  /** The default provider of its repositories, if any. */
47
97
  readonly provider: IRepositoryProvider | undefined;
98
+ /** Logs through the providers of its `log`; writes nothing without one. */
99
+ readonly logger: ILogger;
100
+ /** Its context: its name, the variables of its environments and its logger. */
101
+ readonly context: ApplicationContext;
48
102
  readonly properties: Collections["properties"];
49
103
  readonly blueprints: Collections["blueprints"];
50
104
  readonly records: Collections["records"];
@@ -56,12 +110,72 @@ export interface IApplication<Name extends string, Collections extends Applicati
56
110
  * write through that provider (unless they declared their own).
57
111
  */
58
112
  readonly repositories: Collections["repositories"];
113
+ /**
114
+ * Its use cases. With a `provider` they are subclasses whose repositories
115
+ * are the bound ones, through child use cases too; the same subclass every
116
+ * time. Without one they are the declared classes.
117
+ */
118
+ readonly useCases: Collections["useCases"];
119
+ /**
120
+ * Its commands. With a `provider` they are rebuilt over the bound use
121
+ * cases (same settings), memoized; without one, the declared commands.
122
+ */
123
+ readonly commands: Collections["commands"];
124
+ /**
125
+ * Its outputs. With a `provider` they are rebuilt over the bound commands
126
+ * (same name, statuses and stages), memoized; without one, the declared outputs.
127
+ */
128
+ readonly outputs: Collections["outputs"];
129
+ /** Its exceptions. */
130
+ readonly exceptions: Collections["exceptions"];
59
131
  /** The bound version of a repository registered in the application. */
60
132
  repository<Repository extends AnyRepository>(repository: Repository): Repository;
133
+ /** The bound version of a use case registered in the application. */
134
+ useCase<UseCase extends AnyUseCaseClass>(useCase: UseCase): UseCase;
135
+ /** The bound version of a command registered in the application. */
136
+ command<Command extends AnyCommand>(command: Command): Command;
137
+ /** The bound version of an output registered in the application. */
138
+ output<Output extends AnyOutput>(output: Output): Output;
61
139
  /** A new application with the declarations of `others` too; this one is left as it is. */
62
140
  extend<const Others extends readonly AnyApplication[]>(...others: Others): IApplication<Name, MergedCollections<Collections, Others>>;
63
141
  /** A format-neutral, JSON-serializable graph of its declarations. */
64
142
  describe(): ApplicationDescription;
143
+ /**
144
+ * Validates every environment it registers at once
145
+ * (`InvalidApplicationEnvironment`), makes the provider of `providerFrom`
146
+ * and checks the transactions of its commands against it. Idempotent: the
147
+ * same promise until it fails or `stop()` is called.
148
+ */
149
+ start(options?: ApplicationStartOptions): Promise<IApplication<Name, Collections>>;
150
+ /**
151
+ * Closes its provider (`close`, if it has one); a provider of
152
+ * `providerFrom` is made again by the next `start()`. It stops being the
153
+ * started application of the process.
154
+ */
155
+ stop(): Promise<void>;
156
+ /** The variables of an environment registered in the application. */
157
+ get<Environment extends AnyEnvironment>(environment: Environment): EnvironmentValue<Environment>;
158
+ /** The bound version of a command registered in the application. */
159
+ get<Command extends AnyCommand>(command: Command): Command;
160
+ /** The bound version of an output registered in the application. */
161
+ get<Output extends AnyOutput>(output: Output): Output;
162
+ /** The bound version of a use case registered in the application. */
163
+ get<UseCase extends AnyUseCaseClass>(useCase: UseCase): UseCase;
164
+ /** The bound version of a repository registered in the application. */
165
+ get<Repository extends AnyRepository>(repository: Repository): Repository;
166
+ /** Whether `start()` completed and `stop()` was not called since. */
167
+ readonly started: boolean;
168
+ }
169
+ /** What `app.start()` takes. */
170
+ export interface ApplicationStartOptions {
171
+ /**
172
+ * Become the started application of the process (what `useApplication()`
173
+ * gives). Default `true`; another one already started throws
174
+ * `ApplicationAlreadyStarted`.
175
+ */
176
+ readonly register?: boolean;
177
+ /** Stop the started application of the process and take its place, e.g. on hot reload. Default `false`. */
178
+ readonly replace?: boolean;
65
179
  }
66
180
  /** Any application. */
67
181
  export type AnyApplication = IApplication<string, any>;
@@ -134,11 +248,114 @@ export interface RecordDescription {
134
248
  };
135
249
  readonly plugins: readonly RecordPluginDescriptor[];
136
250
  }
251
+ /** What a use case depends on, by the name it has in the application. */
252
+ export interface UseCaseDependencyDescription {
253
+ /** `logger` is the core's `Logger`, whose `ref` is always `"Logger"`. */
254
+ readonly kind: "use-case" | "repository" | "environment" | "logger";
255
+ readonly ref: string;
256
+ }
257
+ /** A use case: its metadata, fields and what it depends on. */
258
+ export interface UseCaseDescription {
259
+ readonly from: string;
260
+ /** The `useCaseName`, equal to its key. */
261
+ readonly name: string;
262
+ readonly description?: string;
263
+ readonly tags: readonly string[];
264
+ readonly deprecated: boolean;
265
+ readonly input: FieldsDescription;
266
+ readonly response: FieldsDescription;
267
+ readonly context?: FieldsDescription;
268
+ /**
269
+ * The names of the registered blueprints that are the `input`, `response`
270
+ * or `context` (their fields stay described above). Absent for the ones
271
+ * that are not registered.
272
+ */
273
+ readonly refs?: {
274
+ readonly input?: string;
275
+ readonly response?: string;
276
+ readonly context?: string;
277
+ };
278
+ /** A registered exception, or one exported by the package, by name. */
279
+ readonly errors: readonly ({
280
+ readonly kind: "exception";
281
+ readonly ref: string;
282
+ } | {
283
+ readonly kind: "core";
284
+ readonly name: string;
285
+ })[];
286
+ readonly dependencies: Readonly<Record<string, UseCaseDependencyDescription>>;
287
+ /**
288
+ * How it runs: `steps` when `execute` is the interpreter of the declared
289
+ * steps, `opaque` for a hand-written `execute`.
290
+ */
291
+ readonly implementation: {
292
+ readonly kind: "steps";
293
+ readonly steps: readonly UseCaseStepDescription[];
294
+ } | {
295
+ readonly kind: "opaque";
296
+ };
297
+ }
298
+ /** A command: its metadata, context, operations and policies. */
299
+ export interface CommandDescription {
300
+ readonly from: string;
301
+ readonly name: string;
302
+ readonly description?: string;
303
+ readonly tags: readonly string[];
304
+ readonly context?: FieldsDescription;
305
+ /** The name of the registered blueprint that is the `context`, if it is one. */
306
+ readonly refs?: {
307
+ readonly context?: string;
308
+ };
309
+ /** Each operation and the name of its use case in the application. */
310
+ readonly operations: Readonly<Record<string, {
311
+ readonly ref: string;
312
+ }>>;
313
+ readonly transaction: boolean;
314
+ /** The names of its environments, when it declares any. */
315
+ readonly environments?: readonly string[];
316
+ readonly events: "after-commit" | "manual" | {
317
+ readonly kind: "opaque";
318
+ };
319
+ readonly onEventError?: {
320
+ readonly kind: "opaque";
321
+ };
322
+ }
323
+ /** An output: its commands, statuses and pipe. */
324
+ export interface OutputDescription {
325
+ readonly from: string;
326
+ readonly name: string;
327
+ readonly commands: readonly string[];
328
+ readonly statuses: Readonly<Record<string, number>>;
329
+ readonly pipe: readonly StageDescription[];
330
+ }
331
+ /** An exception: its layer and, when declared as a string, its message. */
332
+ export interface ExceptionDescription {
333
+ readonly from: string;
334
+ readonly layer: ExceptionLayers;
335
+ readonly message?: string;
336
+ }
337
+ /** How `describe` gives the `log` of an application. */
338
+ export interface LogDescription {
339
+ readonly level: LogLevel;
340
+ /** The name of each provider made by `logProviderOf` with one; `opaque` otherwise (and for a `logFrom` before start). */
341
+ readonly providers: readonly ({
342
+ readonly kind: "log-provider";
343
+ readonly name: string;
344
+ } | {
345
+ readonly kind: "opaque";
346
+ })[];
347
+ }
137
348
  /** The graph `describe` gives: every declaration by name, with its origin in `from`. */
138
349
  export interface ApplicationDescription {
139
350
  readonly name: string;
140
351
  /** The names of the applications it extends directly. */
141
352
  readonly extends: readonly string[];
353
+ /** The provider of the application (or of the last extended one), present only when configured. */
354
+ readonly provider?: ProviderDescription;
355
+ /** Its `log` (or the one of the last extended application), present only when configured. */
356
+ readonly log?: LogDescription | {
357
+ readonly kind: "opaque";
358
+ };
142
359
  readonly properties: Readonly<Record<string, PropertyDescription & {
143
360
  readonly from: string;
144
361
  }>>;
@@ -156,12 +373,17 @@ export interface ApplicationDescription {
156
373
  readonly from: string;
157
374
  readonly entity: string;
158
375
  readonly actions: readonly string[];
376
+ readonly deletion: "hard" | "soft";
159
377
  /** The names of the repositories of the entities its entity holds. */
160
378
  readonly relations: readonly string[];
161
379
  /** The names of its `environmentDependencies`. */
162
380
  readonly environments: readonly string[];
163
- /** Whether it declared a provider of its own. */
164
- readonly provider: boolean;
381
+ /** Its own provider, present only when it declared one. */
382
+ readonly provider?: ProviderDescription;
165
383
  }>>;
384
+ readonly useCases: Readonly<Record<string, UseCaseDescription>>;
385
+ readonly commands: Readonly<Record<string, CommandDescription>>;
386
+ readonly outputs: Readonly<Record<string, OutputDescription>>;
387
+ readonly exceptions: Readonly<Record<string, ExceptionDescription>>;
166
388
  }
167
389
  export {};
@@ -1,4 +1,4 @@
1
- import type { Static, TArray, TObject, TOptional, TSchema, TUnsafe } from "typebox";
1
+ import type { Static, TArray, TObject, TOptional, TReadonly, TSchema, TUnsafe } from "typebox";
2
2
  import type { ParseResult } from "../../parse/types/parse.interface.js";
3
3
  import type { propertyOf } from "../../property/property.js";
4
4
  import type { OptionalProperty } from "../../property/types/optional-property.type.js";
@@ -59,7 +59,7 @@ type FieldMode = "data" | "value" | "input";
59
59
  * recurse into `AnyBlueprint` endlessly.
60
60
  */
61
61
  type FieldSchema<Field, Mode extends FieldMode> = 0 extends 1 & Field ? TSchema : Field extends {
62
- readonly type: IPropertyType<infer SchemaType extends TSchema>;
62
+ readonly type: IPropertyType<infer SchemaType extends TSchema, any>;
63
63
  } ? SchemaType : Field extends AnyRecordClass ? RecordSchema<Field, Mode> : Field extends OptionalRecord<infer Record> ? TOptional<RecordSchema<Record, Mode>> : Field extends {
64
64
  readonly properties: infer Properties extends IBlueprintCollection;
65
65
  readonly optional: infer Optional;
@@ -83,26 +83,70 @@ type RecordInput<Record extends AnyRecordClass> = ConstructorParameters<Record>[
83
83
  type CollectionSchema<Properties extends IBlueprintCollection, Mode extends FieldMode> = TObject<{
84
84
  [Key in keyof Properties]: FieldSchema<Properties[Key], Mode>;
85
85
  }>;
86
- /** The schema of a single field of a blueprint; a record is its blueprint's object. */
86
+ /**
87
+ * The schema of a single field of a blueprint; a record is its blueprint's object.
88
+ * @internal
89
+ */
87
90
  export type BlueprintPropertySchema<Field> = FieldSchema<Field, "data">;
88
91
  /** The object schema described by a collection, one key per field. */
89
92
  export type BlueprintSchema<Properties extends IBlueprintCollection> = CollectionSchema<Properties, "data">;
93
+ /**
94
+ * The value of a single field of a blueprint, its records as `Mode` says:
95
+ * `Static` of its `FieldSchema`, read from the fields themselves. A property
96
+ * gives the value its type carries, so `Static` is never measured on a
97
+ * generic schema, which costs TypeScript about a second on every check.
98
+ */
99
+ type FieldValue<Field, Mode extends FieldMode> = 0 extends 1 & Field ? Static<TSchema> : Field extends {
100
+ readonly type: IPropertyType<infer SchemaType, infer Value>;
101
+ } ? 0 extends 1 & SchemaType ? Static<TSchema> : Value : Field extends AnyRecordClass ? RecordValueOf<Field, Mode> : Field extends OptionalRecord<infer Record> ? RecordValueOf<Record, Mode> : Field extends {
102
+ readonly properties: infer Properties extends IBlueprintCollection;
103
+ } ? CollectionValue<Properties, Mode> : Field extends {
104
+ readonly item: infer Item;
105
+ } ? FieldValue<Item, Mode>[] : never;
106
+ /** The value of a record field, as `RecordSchema` describes it. */
107
+ type RecordValueOf<Record extends AnyRecordClass, Mode extends FieldMode> = Mode extends "value" ? InstanceType<Record> : Mode extends "input" ? InstanceType<Record> | RecordInput<Record> | AcceptedBy<Record> : Record["blueprint"] extends IBlueprint<infer Properties, boolean> ? CollectionValue<Properties, "data"> : never;
108
+ type ReadonlyOptionalKeys<Schemas> = {
109
+ [Key in keyof Schemas]: Schemas[Key] extends TReadonly<TSchema> ? Schemas[Key] extends TOptional<Schemas[Key]> ? Key : never : never;
110
+ }[keyof Schemas];
111
+ type ReadonlyKeys<Schemas> = {
112
+ [Key in keyof Schemas]: Schemas[Key] extends TReadonly<TSchema> ? Schemas[Key] extends TOptional<Schemas[Key]> ? never : Key : never;
113
+ }[keyof Schemas];
114
+ type OptionalKeys<Schemas> = {
115
+ [Key in keyof Schemas]: Schemas[Key] extends TOptional<TSchema> ? Schemas[Key] extends TReadonly<Schemas[Key]> ? never : Key : never;
116
+ }[keyof Schemas];
117
+ type RequiredKeys<Schemas> = keyof Omit<Schemas, ReadonlyOptionalKeys<Schemas> | ReadonlyKeys<Schemas> | OptionalKeys<Schemas>>;
118
+ /**
119
+ * `Static` of `CollectionSchema`, built as TypeBox builds the value of an
120
+ * object (the same keys and modifiers, in the same order, so it displays the
121
+ * same), but from the value of each field. `Result` is a parameter so that no
122
+ * alias names the object.
123
+ */
124
+ type CollectionValue<Properties extends IBlueprintCollection, Mode extends FieldMode, Schemas = {
125
+ [Key in keyof Properties]: FieldSchema<Properties[Key], Mode>;
126
+ }, Values extends Record<PropertyKey, unknown> = {
127
+ [Key in keyof Properties]: FieldValue<Properties[Key], Mode>;
128
+ }, WithModifiers extends Record<PropertyKey, unknown> = Evaluate<Readonly<Partial<Pick<Values, ReadonlyOptionalKeys<Schemas>>>> & Readonly<Pick<Values, ReadonlyKeys<Schemas>>> & Partial<Pick<Values, OptionalKeys<Schemas>>> & Required<Pick<Values, RequiredKeys<Schemas>>>>, Result = keyof Properties extends never ? object : {
129
+ [Key in keyof WithModifiers]: WithModifiers[Key];
130
+ }> = Result;
131
+ type Evaluate<Type> = {
132
+ [Key in keyof Type]: Type[Key];
133
+ } & {};
90
134
  /**
91
135
  * The value accepted by a collection; optional fields become optional keys,
92
136
  * and a record field takes an instance of its class, e.g. `{ author: User }`.
93
137
  */
94
- export type BlueprintValue<Properties extends IBlueprintCollection> = Static<CollectionSchema<Properties, "value">>;
138
+ export type BlueprintValue<Properties extends IBlueprintCollection> = CollectionValue<Properties, "value">;
95
139
  /**
96
140
  * What a blueprint accepts to build its value: as `BlueprintValue`, but a
97
141
  * record field also takes the plain input of its blueprint, which becomes an
98
142
  * instance of the record, e.g. `{ author: { name: "Jane" } }`.
99
143
  */
100
- export type BlueprintInput<Properties extends IBlueprintCollection> = Static<CollectionSchema<Properties, "input">>;
144
+ export type BlueprintInput<Properties extends IBlueprintCollection> = CollectionValue<Properties, "input">;
101
145
  /**
102
146
  * The plain value of a collection, as its schema describes it: a record
103
147
  * field is the object of the record's blueprint, as its `toJSON` gives.
104
148
  */
105
- export type BlueprintData<Properties extends IBlueprintCollection> = Static<BlueprintSchema<Properties>>;
149
+ export type BlueprintData<Properties extends IBlueprintCollection> = CollectionValue<Properties, "data">;
106
150
  type Override<Base, Overrides> = {
107
151
  [Key in keyof Base | keyof Overrides]: Key extends keyof Overrides ? Overrides[Key] : Key extends keyof Base ? Base[Key] : never;
108
152
  };
@@ -123,6 +167,7 @@ export type BlueprintPluginContext<Sources extends readonly {
123
167
  /**
124
168
  * The field factories of every plugin source, in order; a later source
125
169
  * replaces a factory of an earlier one with the same name.
170
+ * @internal
126
171
  */
127
172
  export type BlueprintFactoryContext<Sources extends readonly BlueprintSource[]> = Sources extends readonly [
128
173
  infer Head,
@@ -130,11 +175,17 @@ export type BlueprintFactoryContext<Sources extends readonly BlueprintSource[]>
130
175
  ] ? Override<Head extends {
131
176
  readonly factories?: infer Factories;
132
177
  } ? NonNullable<Factories> extends infer Defined extends IBlueprintFactories ? string extends keyof Defined ? Record<never, never> : Defined : Record<never, never> : Record<never, never>, BlueprintFactoryContext<Tail>> : Record<never, never>;
133
- /** The fields of the joined blueprints; a later one replaces a key of an earlier one. */
178
+ /**
179
+ * The fields of the joined blueprints; a later one replaces a key of an earlier one.
180
+ * @internal
181
+ */
134
182
  export type JoinedProperties<Blueprints extends readonly {
135
183
  readonly properties: IBlueprintCollection;
136
184
  }[]> = BlueprintPluginContext<Blueprints> extends infer Properties extends IBlueprintCollection ? Properties : never;
137
- /** A list of every field of a collection, e.g. `{ tags: IBlueprintList<Tag> }`. */
185
+ /**
186
+ * A list of every field of a collection, e.g. `{ tags: IBlueprintList<Tag> }`.
187
+ * @internal
188
+ */
138
189
  export type ListProperties<Properties> = {
139
190
  readonly [Key in keyof Properties]: Properties[Key] extends BlueprintField ? IBlueprintList<Properties[Key]> : never;
140
191
  };
@@ -162,11 +213,15 @@ export type BlueprintContext<Sources extends readonly BlueprintSource[]> = Calla
162
213
  /**
163
214
  * Every field of a collection, its properties callable with options, as
164
215
  * `ctx.number({ min: 1 })`.
216
+ * @internal
165
217
  */
166
218
  export type CallableProperties<Fields> = {
167
219
  readonly [Key in keyof Fields]: Fields[Key] extends AnyPropertyConstructor ? CallableProperty<Fields[Key]> : Fields[Key];
168
220
  };
169
- /** The optional version of every field of a collection, blueprints and records included. */
221
+ /**
222
+ * The optional version of every field of a collection, blueprints and records included.
223
+ * @internal
224
+ */
170
225
  export type OptionalProperties<Properties> = {
171
226
  readonly [Key in keyof Properties]: Properties[Key] extends AnyPropertyConstructor ? OptionalProperty<Properties[Key]> : Properties[Key] extends AnyBlueprint ? OptionalBlueprint<Properties[Key]> : Properties[Key] extends AnyBlueprintList ? OptionalBlueprintList<Properties[Key]> : Properties[Key] extends AnyRecordClass ? OptionalRecord<Properties[Key]> : Properties[Key] extends AnyOptionalRecord ? Properties[Key] : never;
172
227
  };
@@ -178,7 +233,7 @@ export interface IBlueprint<Properties extends IBlueprintCollection, Optional ex
178
233
  /** The fields picked by the blueprint's definition. */
179
234
  readonly properties: Properties;
180
235
  /** The general schema of every field of the blueprint. */
181
- readonly schema: ISchema<BlueprintSchema<Properties>>;
236
+ readonly schema: ISchema<BlueprintSchema<Properties>, BlueprintData<Properties>>;
182
237
  /** Whether, as a field of another blueprint, it accepts `undefined`. */
183
238
  readonly optional: Optional;
184
239
  /**
@@ -232,7 +287,7 @@ export interface IBlueprintList<Item extends BlueprintField, Optional extends bo
232
287
  */
233
288
  readonly "~standard": StandardSchemaV1.Props<ListShape<Item, "input", Optional>, ListShape<Item, "value", Optional>>;
234
289
  }
235
- type ListShape<Item, Mode extends FieldMode, Optional extends boolean> = Optional extends true ? Static<TArray<FieldSchema<Item, Mode>>> | undefined : Static<TArray<FieldSchema<Item, Mode>>>;
290
+ type ListShape<Item, Mode extends FieldMode, Optional extends boolean> = Optional extends true ? FieldValue<Item, Mode>[] | undefined : FieldValue<Item, Mode>[];
236
291
  /** `List` as a field that accepts `undefined`. */
237
292
  export type OptionalBlueprintList<List extends AnyBlueprintList> = List extends IBlueprintList<infer Item, boolean> ? IBlueprintList<Item, true> : never;
238
293
  export {};
@@ -1,5 +1,6 @@
1
1
  import type { RecordUniqueKeys } from "../../record/types/plugin.interface.js";
2
2
  import type { RecordConstructor } from "../../record/types/record.interface.js";
3
+ import type { Meta } from "../../symbols/meta.js";
3
4
  import type { AnyBlueprint, AnyBlueprintList, AnyOptionalRecord, AnyRecordClass, BlueprintData, BlueprintField, IBlueprint, IBlueprintCollection, IBlueprintList, OptionalRecord } from "./blueprint.interface.js";
4
5
  /** What a mask of a field is: the mask of its item, of its keys, or nothing for a property. */
5
6
  type FieldMask<Field> = 0 extends 1 & Field ? object : Field extends AnyBlueprintList ? FieldMask<Field["item"]> : Field extends AnyBlueprint ? SliceMask<Field["properties"]> : Field extends AnyRecordClass ? SliceMask<Field["blueprint"]["properties"]> : Field extends AnyOptionalRecord ? SliceMask<Field["record"]["blueprint"]["properties"]> : never;
@@ -33,7 +34,9 @@ type SliceField<Field, Mask> = 0 extends 1 & Field ? never : Field extends AnyBl
33
34
  * `change` keys among them. A plain record, not an entity.
34
35
  */
35
36
  type SlicedRecord<Original extends AnyRecordClass, Mask> = SlicedProperties<Original["blueprint"]["properties"], Mask> extends infer Properties extends IBlueprintCollection ? RecordConstructor<Properties, KeptChange<InstanceType<Original>, keyof Properties>[], KeptUnique<Original, keyof Properties>, unknown, {
36
- readonly blueprint: Original["blueprint"];
37
+ readonly [Meta]: {
38
+ readonly blueprint: Original["blueprint"];
39
+ };
37
40
  }> : never;
38
41
  type KeptChange<Instance, Keys> = Instance extends {
39
42
  readonly change: infer Change;
@@ -50,7 +53,9 @@ export type SlicedBlueprint<Properties, Mask> = AsBlueprint<SlicedProperties<Pro
50
53
  * the keys their blueprint requires) and lists.
51
54
  */
52
55
  export type SlicedValue<Value, Mask> = Value extends readonly (infer Item)[] ? SlicedValue<Item, Mask>[] : Value extends {
53
- readonly blueprint: IBlueprint<infer Properties, boolean>;
56
+ readonly [Meta]: {
57
+ readonly blueprint: IBlueprint<infer Properties, boolean>;
58
+ };
54
59
  toJSON(): unknown;
55
60
  } ? SlicedValue<BlueprintData<Properties>, Mask> : Value extends {
56
61
  toJSON(): infer Json;