@afora/core 0.0.7 → 0.0.9

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 (99) hide show
  1. package/README.md +157 -25
  2. package/dist/adapters.d.ts +25 -0
  3. package/dist/adapters.js +56 -0
  4. package/dist/core/application/exceptions.d.ts +17 -0
  5. package/dist/core/application/migrate.d.ts +19 -0
  6. package/dist/core/application/schema.d.ts +30 -0
  7. package/dist/core/application/start.d.ts +7 -0
  8. package/dist/core/application/types/application.interface.d.ts +73 -2
  9. package/dist/core/auth/jwt.d.ts +30 -0
  10. package/dist/core/auth/types/jwt.interface.d.ts +66 -0
  11. package/dist/core/blueprint/exceptions.d.ts +5 -0
  12. package/dist/core/blueprint/optional.d.ts +3 -1
  13. package/dist/core/blueprint/types/blueprint.interface.d.ts +79 -5
  14. package/dist/core/blueprint/variant.d.ts +32 -0
  15. package/dist/core/command/types/command.interface.d.ts +7 -0
  16. package/dist/core/entity/abstract-entity.d.ts +33 -1
  17. package/dist/core/entity/entity.d.ts +1 -1
  18. package/dist/core/entity/event.d.ts +13 -2
  19. package/dist/core/entity/exceptions.d.ts +17 -0
  20. package/dist/core/entity/trust.d.ts +24 -0
  21. package/dist/core/entity/types/entity.interface.d.ts +96 -2
  22. package/dist/core/entity/version.d.ts +13 -0
  23. package/dist/core/event-policy/event-policy.d.ts +25 -0
  24. package/dist/core/event-policy/exceptions.d.ts +11 -0
  25. package/dist/core/event-policy/publish.d.ts +17 -0
  26. package/dist/core/event-policy/publishers.d.ts +18 -0
  27. package/dist/core/event-policy/types/event-policy.interface.d.ts +72 -0
  28. package/dist/core/output/access.d.ts +16 -0
  29. package/dist/core/output/coerce.d.ts +9 -0
  30. package/dist/core/output/exceptions.d.ts +16 -0
  31. package/dist/core/output/http.d.ts +36 -0
  32. package/dist/core/output/openapi-sink.d.ts +160 -39
  33. package/dist/core/output/stage.d.ts +7 -2
  34. package/dist/core/output/status.d.ts +2 -2
  35. package/dist/core/output/types/http.interface.d.ts +130 -0
  36. package/dist/core/output/types/output.interface.d.ts +25 -5
  37. package/dist/core/parse/issue.d.ts +28 -3
  38. package/dist/core/parse/parse.d.ts +19 -0
  39. package/dist/core/parse/strip.d.ts +13 -0
  40. package/dist/core/property/abstract-property.d.ts +9 -0
  41. package/dist/core/property/define-property.d.ts +34 -6
  42. package/dist/core/property/types/property-options.interface.d.ts +10 -0
  43. package/dist/core/property/types/property-type.interface.d.ts +4 -0
  44. package/dist/core/record/abstract-record.d.ts +7 -0
  45. package/dist/core/record/types/record.interface.d.ts +12 -1
  46. package/dist/core/repository/exceptions.d.ts +43 -0
  47. package/dist/core/repository/in-memory.d.ts +16 -3
  48. package/dist/core/repository/migrate.d.ts +20 -0
  49. package/dist/core/repository/plan.d.ts +20 -0
  50. package/dist/core/repository/provider.d.ts +1 -1
  51. package/dist/core/repository/query.d.ts +9 -2
  52. package/dist/core/repository/repository.d.ts +40 -8
  53. package/dist/core/repository/state.d.ts +32 -2
  54. package/dist/core/repository/types/query.interface.d.ts +106 -5
  55. package/dist/core/repository/types/repository.interface.d.ts +139 -7
  56. package/dist/core/repository/types/schema.interface.d.ts +126 -0
  57. package/dist/core/repository/upcast.d.ts +64 -0
  58. package/dist/core/schema/schema.d.ts +28 -0
  59. package/dist/core/symbols/kind.d.ts +1 -1
  60. package/dist/core/use-case/access.d.ts +12 -0
  61. package/dist/core/use-case/decorators.d.ts +40 -0
  62. package/dist/core/use-case/route.d.ts +18 -0
  63. package/dist/core/use-case/steps/types/check.interface.d.ts +214 -0
  64. package/dist/core/use-case/types/access.interface.d.ts +39 -0
  65. package/dist/core/use-case/types/route.interface.d.ts +66 -0
  66. package/dist/core/use-case/use-case.d.ts +52 -2
  67. package/dist/experimental.d.ts +13 -1
  68. package/dist/experimental.js +64 -26
  69. package/dist/index-3aae5jfq.js +2997 -0
  70. package/dist/index-hg1z2s2v.js +1055 -0
  71. package/dist/index-ny3as79r.js +4567 -0
  72. package/dist/index-xegv6a1p.js +4291 -0
  73. package/dist/index-y280ew0k.js +396 -0
  74. package/dist/index.d.ts +55 -48
  75. package/dist/index.js +141 -169
  76. package/dist/std/blueprint-plugin.d.ts +6 -2
  77. package/dist/std/blueprint.d.ts +3 -0
  78. package/dist/std/entity.d.ts +2 -1
  79. package/dist/std/event-policies.d.ts +2 -0
  80. package/dist/std/properties/exceptions.d.ts +11 -0
  81. package/dist/std/properties/json.d.ts +54 -0
  82. package/dist/std/properties/password.d.ts +0 -2
  83. package/dist/std/properties/time.d.ts +4 -0
  84. package/dist/std/record-plugins.d.ts +2 -2
  85. package/dist/std/resource.d.ts +31 -0
  86. package/dist/std/runtime.d.ts +6 -0
  87. package/dist/std/use-cases/auth.d.ts +5 -1
  88. package/dist/std/use-cases/crud.d.ts +6 -4
  89. package/dist/std/use-cases/index.d.ts +1 -1
  90. package/dist/std/use-cases/shared.d.ts +34 -6
  91. package/dist/std/use-cases/types.d.ts +197 -22
  92. package/dist/testing/http-provider-contract.d.ts +32 -0
  93. package/dist/testing/index.d.ts +4 -0
  94. package/dist/testing/index.js +839 -3
  95. package/dist/testing/memory-publisher.d.ts +16 -0
  96. package/dist/testing/provider-contract.d.ts +31 -1
  97. package/package.json +17 -3
  98. package/dist/index-q6hggpzw.js +0 -6667
  99. package/dist/index-r78dsvtc.js +0 -1721
@@ -1,14 +1,19 @@
1
1
  import type { AnyBlueprint, BlueprintInput, IBlueprint } from "../../blueprint/types/blueprint.interface.js";
2
2
  import type { AnyCommand } from "../../command/types/command.interface.js";
3
+ import type { EntityExpires } from "../../entity/types/entity.interface.js";
3
4
  import type { AnyEnvironment, EnvironmentValue } from "../../environment/environment.js";
5
+ import type { EventPolicyDescription, IEventPublisher } from "../../event-policy/types/event-policy.interface.js";
4
6
  import type { ExceptionLayers } from "../../exception/enums/exception-layers.js";
5
7
  import type { ILogger, LogLevel, LogSettings } from "../../logger/logger.js";
6
8
  import type { AnyOutput, StageDescription } from "../../output/types/output.interface.js";
7
9
  import type { RecordPluginDescriptor } from "../../record/types/plugin.interface.js";
8
- import type { ProviderDescription } from "../../repository/types/query.interface.js";
10
+ import type { ProviderDescription, RepositoryIndex, RepositoryScans } from "../../repository/types/query.interface.js";
9
11
  import type { AnyRepository, IRepositoryProvider } from "../../repository/types/repository.interface.js";
12
+ import type { SchemaLock, SchemaMigrationOptions, SchemaMigrationReport } from "../../repository/types/schema.interface.js";
10
13
  import type { Kind } from "../../symbols/kind.js";
11
14
  import type { UseCaseStepDescription } from "../../use-case/steps/types/steps.interface.js";
15
+ import type { AccessDeclaration } from "../../use-case/types/access.interface.js";
16
+ import type { RouteDeclaration } from "../../use-case/types/route.interface.js";
12
17
  import type { AnyExceptionClass, AnyUseCaseClass, UseCaseInput, UseCaseResponse } from "../../use-case/use-case.js";
13
18
  import type { ProviderSlot, ProviderStorage } from "../providers.js";
14
19
  import type { LogFactory, ProviderFactory } from "../start.js";
@@ -96,6 +101,13 @@ export interface ApplicationSettings<Name extends string, Extended extends reado
96
101
  * it, the one of the last extended application that has one; none by default.
97
102
  */
98
103
  readonly log?: LogSettings | LogFactory;
104
+ /**
105
+ * Where event policies such as `publishEvents` publish, by key: a
106
+ * WebSocket hub, a queue, a broker. Commands of the application find them
107
+ * by key, those of the extended applications included; a command whose
108
+ * policy names a key that is not here throws `UnregisteredApplicationReference`.
109
+ */
110
+ readonly publishers?: Readonly<Record<string, IEventPublisher>>;
99
111
  }
100
112
  /**
101
113
  * What an application gives the factories it runs on start (`providerFrom`,
@@ -197,6 +209,17 @@ export interface IApplication<Name extends string, Collections extends Applicati
197
209
  * started application of the process.
198
210
  */
199
211
  stop(): Promise<void>;
212
+ /**
213
+ * Brings the stored rows of its entities up to their current shape,
214
+ * walking each provider directly: in `check` it only reports, in `apply`
215
+ * it gives each provider the plan of its entity (`provider.migrate`, the
216
+ * changes from `lock`), then rewrites the rows below the current
217
+ * `schemaVersion` or with a `fill` or `renamedFrom` pending, and moves the
218
+ * rows stored under `previousNames`. Keys no longer declared, and the
219
+ * rows of the previous names, are only removed with `destructive: true`.
220
+ * Idempotent and resumable.
221
+ */
222
+ migrate(options: SchemaMigrationOptions): Promise<SchemaMigrationReport>;
200
223
  /** The variables of an environment registered in the application. */
201
224
  get<Environment extends AnyEnvironment>(environment: Environment): EnvironmentValue<Environment>;
202
225
  /** The bound version of a command registered in the application. */
@@ -222,6 +245,13 @@ export interface ApplicationStartOptions {
222
245
  readonly register?: boolean;
223
246
  /** Stop the started application of the process and take its place, e.g. on hot reload. Default `false`. */
224
247
  readonly replace?: boolean;
248
+ /**
249
+ * The committed schema lock (`afora.lock.json`): start throws
250
+ * `SchemaLockMismatch` when an entity of the lock is no longer named, and
251
+ * no entity lists it in `previousNames`, or when the `schemaVersion` of
252
+ * an entity is lower than the lock's.
253
+ */
254
+ readonly lock?: SchemaLock;
225
255
  }
226
256
  /** Any application. */
227
257
  export type AnyApplication = IApplication<string, any>;
@@ -281,6 +311,12 @@ export type FieldDescription = {
281
311
  readonly kind: "record" | "entity";
282
312
  readonly ref: string;
283
313
  readonly optional: boolean;
314
+ } | {
315
+ /** One of several blueprints, told apart by the value of `key`, the name of the variant. */
316
+ readonly kind: "variant";
317
+ readonly key: string;
318
+ readonly optional: boolean;
319
+ readonly variants: Readonly<Record<string, FieldDescription>>;
284
320
  };
285
321
  export type FieldsDescription = Readonly<Record<string, FieldDescription>>;
286
322
  /** A record or an entity: its fields (an entity's identity and its plugins' keys left out), options and plugins. */
@@ -293,6 +329,28 @@ export interface RecordDescription {
293
329
  readonly change: readonly string[];
294
330
  };
295
331
  readonly plugins: readonly RecordPluginDescriptor[];
332
+ /**
333
+ * The lifecycle events of an entity, by cycle: each event's name and
334
+ * whether `eventOf` was given a handler. Present only when it has some.
335
+ */
336
+ readonly events?: Readonly<Partial<Record<"onCreate" | "onUpdate" | "onDelete", readonly {
337
+ readonly name: string;
338
+ readonly handler: boolean;
339
+ }[]>>>;
340
+ /** Whether the entity is versioned (`entityOf(..., { versioned: true })`); present only when it is. */
341
+ readonly versioned?: true;
342
+ /**
343
+ * When the records of an entity expire, as `entityOf` was given it:
344
+ * `{ after: duration }` (kept in its `expiresAt` identity key, left out of
345
+ * `fields`) or `{ at: key }`. Present only when it has one.
346
+ */
347
+ readonly expires?: EntityExpires;
348
+ /** The `schemaVersion` of an entity; present only when declared. */
349
+ readonly schemaVersion?: number;
350
+ /** The versions the `upgrade` of an entity has a step to, ascending; present only when it has some. */
351
+ readonly upgrade?: readonly number[];
352
+ /** The `previousNames` of an entity; present only when it has some. */
353
+ readonly previousNames?: readonly string[];
296
354
  }
297
355
  /** What a use case depends on, by the name it has in the application. */
298
356
  export interface UseCaseDependencyDescription {
@@ -308,6 +366,10 @@ export interface UseCaseDescription {
308
366
  readonly description?: string;
309
367
  readonly tags: readonly string[];
310
368
  readonly deprecated: boolean;
369
+ /** Where it is served over HTTP, from the `route` setting or `@Route`. */
370
+ readonly route?: RouteDeclaration;
371
+ /** Who may call it, from the `access` setting or `@Access`. */
372
+ readonly access?: AccessDeclaration;
311
373
  readonly input: FieldsDescription;
312
374
  readonly response: FieldsDescription;
313
375
  readonly context?: FieldsDescription;
@@ -370,7 +432,8 @@ export interface CommandDescription {
370
432
  readonly transaction: boolean;
371
433
  /** The names of its environments, when it declares any. */
372
434
  readonly environments?: readonly string[];
373
- readonly events: "after-commit" | "manual" | {
435
+ /** The policy: a name, a policy made by `eventPolicyOf` by name and options, else opaque. */
436
+ readonly events: "after-commit" | "manual" | EventPolicyDescription | {
374
437
  readonly kind: "opaque";
375
438
  };
376
439
  readonly onEventError?: {
@@ -422,6 +485,10 @@ export interface ApplicationDescription {
422
485
  readonly log?: LogDescription | {
423
486
  readonly kind: "opaque";
424
487
  };
488
+ /** Its publishers (inherited ones included), by key, with the application that declared each; present only when it has some. */
489
+ readonly publishers?: Readonly<Record<string, {
490
+ readonly from: string;
491
+ }>>;
425
492
  readonly properties: Readonly<Record<string, PropertyDescription & {
426
493
  readonly from: string;
427
494
  }>>;
@@ -440,6 +507,10 @@ export interface ApplicationDescription {
440
507
  readonly entity: string;
441
508
  readonly actions: readonly string[];
442
509
  readonly deletion: "hard" | "soft";
510
+ /** Its declared indexes, by name. */
511
+ readonly indexes: Readonly<Record<string, RepositoryIndex>>;
512
+ /** What it does with a query only a scan serves. */
513
+ readonly scans: RepositoryScans;
443
514
  /** The names of the repositories of the entities its entity holds. */
444
515
  readonly relations: readonly string[];
445
516
  /** The names of its `environmentDependencies`. */
@@ -0,0 +1,30 @@
1
+ import type { Jwt, JwtOptions } from "./types/jwt.interface.js";
2
+ /** `jwtOf` got options it cannot use, or its secret is missing; `details.reason` says what. */
3
+ export declare const InvalidJwtOptions: import("../../index.js").DeclaredExceptionClass<{
4
+ readonly reason: string;
5
+ }>;
6
+ /**
7
+ * Why `options` cannot be the options of `jwtOf`, naming them `where`;
8
+ * `undefined` when they can. `extra` are other keys the caller accepts, and
9
+ * `described` is for options that `describe()` and `afora.json` show, where
10
+ * the secret must be named by its environment variable, never written.
11
+ */
12
+ export declare function jwtOptionsIssue(options: unknown, where: string, extra?: readonly string[], described?: boolean): string | undefined;
13
+ /**
14
+ * Signs and verifies JSON Web Tokens with a shared secret (HMAC), on Web
15
+ * Crypto, so it runs on Bun, Node and Workers alike:
16
+ *
17
+ * ```ts
18
+ * const jwt = jwtOf({ secret: { env: "JWT_SECRET" }, issuer: "blog" });
19
+ * const token = await jwt.sign({ subject: user.id, scopes: ["posts:write"] });
20
+ * const principal = await jwt.verify(token); // { subject, scopes, claims? }
21
+ * hono(options, { authenticator: jwt.authenticator() });
22
+ * ```
23
+ *
24
+ * A token is refused (`Unauthenticated`) when it is malformed, signed with
25
+ * another algorithm or secret, expired, not yet valid, of another issuer,
26
+ * audience or use, or has no subject. Throws `InvalidJwtOptions` for options
27
+ * it cannot use, and when the secret of `{ env }` is missing or shorter than
28
+ * 32 characters, on the first token.
29
+ */
30
+ export declare function jwtOf(options: JwtOptions): Jwt;
@@ -0,0 +1,66 @@
1
+ import type { Authenticator } from "../../output/types/http.interface.js";
2
+ import type { Principal } from "../../use-case/types/access.interface.js";
3
+ /** The algorithms `jwtOf` signs and verifies with: HMAC with a shared secret. */
4
+ export type JwtAlgorithm = "HS256" | "HS384" | "HS512";
5
+ /** The options of `jwtOf`. JSON, so they can live in the options of a sink. */
6
+ export interface JwtOptions {
7
+ /**
8
+ * The secret tokens are signed with: the text itself, or the name of the
9
+ * environment variable that holds it (`{ env: "JWT_SECRET" }`), read when
10
+ * the first token is signed or verified. At least 32 characters.
11
+ */
12
+ readonly secret: string | {
13
+ readonly env: string;
14
+ };
15
+ /** The `iss` claim: set when signing, required when verifying. */
16
+ readonly issuer?: string;
17
+ /** The `aud` claim: set when signing, required when verifying. */
18
+ readonly audience?: string;
19
+ /** The seconds a token lives: 900 (15 minutes) by default. */
20
+ readonly expiresIn?: number;
21
+ /** `"HS256"` by default. */
22
+ readonly algorithm?: JwtAlgorithm;
23
+ }
24
+ /** What a token is signed for. */
25
+ export interface JwtSubject {
26
+ readonly subject: string;
27
+ readonly scopes?: readonly string[];
28
+ readonly claims?: Principal["claims"];
29
+ }
30
+ /** What changes for one token. */
31
+ export interface JwtTokenOptions {
32
+ /** The seconds this token lives, instead of the `expiresIn` of the options. */
33
+ readonly expiresIn?: number;
34
+ /**
35
+ * What the token is for, e.g. `"refresh"`: kept in its `use` claim, and a
36
+ * token verifies only for the use it was signed for (none by default), so
37
+ * a refresh token is not accepted as an access token.
38
+ */
39
+ readonly use?: string;
40
+ }
41
+ /** Signs and verifies the tokens of one secret (see `jwtOf`). */
42
+ export interface Jwt {
43
+ /** A signed token for `subject`: `sub`, `scope` (the scopes, space separated), `iat`, `exp` and the claims. */
44
+ sign(subject: JwtSubject, options?: JwtTokenOptions): Promise<string>;
45
+ /** The caller a token was signed for. Rejects with `Unauthenticated` for a token it does not accept. */
46
+ verify(token: string, options?: Pick<JwtTokenOptions, "use">): Promise<Principal>;
47
+ /**
48
+ * An authenticator that reads the token from the `authorization` header
49
+ * (`Bearer <token>`; another header with `header`) and, with `cookie`,
50
+ * from that cookie when the header is absent. No token is an anonymous
51
+ * caller; one that does not verify is refused.
52
+ */
53
+ authenticator(options?: {
54
+ readonly header?: string;
55
+ readonly cookie?: string;
56
+ }): Authenticator;
57
+ }
58
+ /**
59
+ * The options of `jwtOf` where they are described (`describe()`,
60
+ * `afora.json`): the secret is named by its environment variable.
61
+ */
62
+ export interface DescribedJwtOptions extends Omit<JwtOptions, "secret"> {
63
+ readonly secret: {
64
+ readonly env: string;
65
+ };
66
+ }
@@ -2,6 +2,11 @@
2
2
  export declare const ReservedBlueprintKey: import("../../index.js").DeclaredExceptionClass<{
3
3
  readonly key: string;
4
4
  }>;
5
+ /** `variantOf` was given something it cannot make a field of; `reason` says what. */
6
+ export declare const InvalidBlueprintVariant: import("../../index.js").DeclaredExceptionClass<{
7
+ readonly key: string;
8
+ readonly reason: string;
9
+ }>;
5
10
  /** A blueprint picks a key that no plugin provides. */
6
11
  export declare const UndeclaredBlueprintProperty: import("../../index.js").DeclaredExceptionClass<{
7
12
  readonly key: string;
@@ -1,6 +1,6 @@
1
1
  import type { OptionalProperty } from "../property/types/optional-property.type.js";
2
2
  import type { AnyPropertyConstructor } from "../property/types/property-collection.type.js";
3
- import type { AnyBlueprint, AnyBlueprintList, AnyRecordClass, BlueprintField, OptionalBlueprint, OptionalBlueprintList, OptionalRecord } from "./types/blueprint.interface.js";
3
+ import type { AnyBlueprint, AnyBlueprintList, AnyBlueprintVariant, AnyRecordClass, BlueprintField, OptionalBlueprint, OptionalBlueprintList, OptionalBlueprintVariant, OptionalRecord } from "./types/blueprint.interface.js";
4
4
  /**
5
5
  * The version of `Property` that accepts `undefined`. Returns `Property` itself
6
6
  * when it is already optional, and the same class for the same `Property`.
@@ -13,6 +13,8 @@ export declare function optionalOf<Property extends AnyPropertyConstructor>(Prop
13
13
  export declare function optionalOf<Blueprint extends AnyBlueprint>(blueprint: Blueprint): OptionalBlueprint<Blueprint>;
14
14
  /** The version of `list` that, as a field of a blueprint, accepts `undefined`. */
15
15
  export declare function optionalOf<List extends AnyBlueprintList>(list: List): OptionalBlueprintList<List>;
16
+ /** The version of `variant` that, as a field of a blueprint, accepts `undefined`. */
17
+ export declare function optionalOf<Variant extends AnyBlueprintVariant>(variant: Variant): OptionalBlueprintVariant<Variant>;
16
18
  /**
17
19
  * The version of `record` that, as a field of a blueprint, accepts `undefined`
18
20
  * or an instance of `record`. Returns `record` itself when it is already optional.
@@ -10,9 +10,22 @@ import type { StandardSchemaV1 } from "../../standard/standard-schema.js";
10
10
  import type { Kind } from "../../symbols/kind.js";
11
11
  import type { listOf } from "../list.js";
12
12
  import type { optionalOf } from "../optional.js";
13
+ import type { variantOf } from "../variant.js";
13
14
  import type { IBlueprintFactories, IBlueprintPlugin } from "./blueprint-plugin.interface.js";
14
15
  /** Any blueprint, whatever its properties, optional or not. */
15
16
  export type AnyBlueprint = IBlueprint<any, boolean>;
17
+ /**
18
+ * Any variant field, whatever its key and its variants, optional or not.
19
+ * Written out, not as `IBlueprintVariant` of anything: every field of every
20
+ * blueprint is compared with it, and this way nothing is worked out for that.
21
+ */
22
+ export interface AnyBlueprintVariant {
23
+ readonly [Kind]: "variant";
24
+ readonly key: string;
25
+ readonly variants: any;
26
+ readonly optional: boolean;
27
+ readonly "~standard": StandardSchemaV1.Props<any, any>;
28
+ }
16
29
  /** Any list, whatever its item, optional or not. */
17
30
  export type AnyBlueprintList = IBlueprintList<any, boolean>;
18
31
  /**
@@ -40,9 +53,10 @@ export interface OptionalRecord<Record extends AnyRecordClass> {
40
53
  export type AnyOptionalRecord = OptionalRecord<AnyRecordClass>;
41
54
  /**
42
55
  * What a blueprint key holds: a property, a blueprint for a nested object, a
43
- * record, or a list of any of them.
56
+ * record, a list of any of them, or one of several blueprints told apart by a
57
+ * key.
44
58
  */
45
- export type BlueprintField = AnyPropertyConstructor | AnyBlueprint | AnyBlueprintList | AnyRecordClass | AnyOptionalRecord;
59
+ export type BlueprintField = AnyPropertyConstructor | AnyBlueprint | AnyBlueprintList | AnyRecordClass | AnyOptionalRecord | AnyBlueprintVariant;
46
60
  /** Fields by key, e.g. `{ name: Name, address: Address }`. */
47
61
  export type IBlueprintCollection = Record<string, BlueprintField>;
48
62
  /** What a blueprint picks its fields from: a plugin, or another blueprint. */
@@ -66,7 +80,17 @@ type FieldSchema<Field, Mode extends FieldMode> = 0 extends 1 & Field ? TSchema
66
80
  } ? Optional extends true ? TOptional<CollectionSchema<Properties, Mode>> : CollectionSchema<Properties, Mode> : Field extends {
67
81
  readonly item: infer Item;
68
82
  readonly optional: infer Optional;
69
- } ? Optional extends true ? TOptional<TArray<FieldSchema<Item, Mode>>> : TArray<FieldSchema<Item, Mode>> : never;
83
+ } ? Optional extends true ? TOptional<TArray<FieldSchema<Item, Mode>>> : TArray<FieldSchema<Item, Mode>> : VariantSchema<Field, Mode>;
84
+ /**
85
+ * The schema of a variant field: its value as it is, since a union of objects
86
+ * is only read for whether the key is optional. Apart from `FieldSchema`, so
87
+ * that a field that is not a variant costs one comparison there.
88
+ */
89
+ type VariantSchema<Field, Mode extends FieldMode> = Field extends {
90
+ readonly key: infer Key extends string;
91
+ readonly variants: infer Variants;
92
+ readonly optional: infer Optional;
93
+ } ? Optional extends true ? TOptional<TUnsafe<VariantValue<Key, Variants, Mode>>> : TUnsafe<VariantValue<Key, Variants, Mode>> : never;
70
94
  /** Declares the records a field of a record class takes besides its own. */
71
95
  export declare const RecordAccepts: unique symbol;
72
96
  /** The values besides its instances and plain input that a field of `Record` takes. */
@@ -102,7 +126,31 @@ type FieldValue<Field, Mode extends FieldMode> = 0 extends 1 & Field ? Static<TS
102
126
  readonly properties: infer Properties extends IBlueprintCollection;
103
127
  } ? CollectionValue<Properties, Mode> : Field extends {
104
128
  readonly item: infer Item;
105
- } ? FieldValue<Item, Mode>[] : never;
129
+ } ? FieldValue<Item, Mode>[] : Field extends {
130
+ readonly key: infer Key extends string;
131
+ readonly variants: infer Variants;
132
+ } ? VariantValue<Key, Variants, Mode> : never;
133
+ /**
134
+ * The value of a variant field: for each variant, its own value with `Key`
135
+ * set to its name, so that `value[Key]` tells which one it is. Anything for a
136
+ * field of any variants (as `AnyBlueprintVariant`'s).
137
+ */
138
+ type VariantValue<Key extends string, Variants, Mode extends FieldMode> = 0 extends 1 & Variants ? any : {
139
+ [Tag in keyof Variants & string]: VariantMember<Key, Tag, Variants[Tag], Mode>;
140
+ }[keyof Variants & string];
141
+ /**
142
+ * The value of one variant named `Tag`. A blueprint gives its value with the
143
+ * key. A record class gives an instance, which has no key and is told apart
144
+ * by its class; what it takes is an instance or its plain input with the key,
145
+ * and its plain data has the key.
146
+ */
147
+ type VariantMember<Key extends string, Tag, Variant, Mode extends FieldMode> = Variant extends AnyRecordClass ? Mode extends "value" ? InstanceType<Variant> : Mode extends "input" ? InstanceType<Variant> | Evaluate<{
148
+ readonly [Name in Key]: Tag;
149
+ } & RecordInput<Variant>> : Evaluate<{
150
+ readonly [Name in Key]: Tag;
151
+ } & RecordValueOf<Variant, "data">> : Evaluate<{
152
+ readonly [Name in Key]: Tag;
153
+ } & FieldValue<Variant, Mode>>;
106
154
  /** The value of a record field, as `RecordSchema` describes it. */
107
155
  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
156
  type ReadonlyOptionalKeys<Schemas> = {
@@ -196,6 +244,7 @@ export interface BlueprintHelpers {
196
244
  readonly listOf: typeof listOf;
197
245
  readonly optionalOf: typeof optionalOf;
198
246
  readonly propertyOf: typeof propertyOf;
247
+ readonly variantOf: typeof variantOf;
199
248
  }
200
249
  /**
201
250
  * What a blueprint's definition picks from: every source field, the field
@@ -223,7 +272,10 @@ export type CallableProperties<Fields> = {
223
272
  * @internal
224
273
  */
225
274
  export type OptionalProperties<Properties> = {
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;
275
+ 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] : Properties[Key] extends {
276
+ readonly key: infer Name extends string;
277
+ readonly variants: infer Variants extends BlueprintVariants;
278
+ } ? IBlueprintVariant<Name, Variants, true> : never;
227
279
  };
228
280
  export type BlueprintDefinition<Sources extends readonly BlueprintSource[], Properties extends IBlueprintCollection> = (ctx: BlueprintContext<Sources>) => Properties;
229
281
  export interface IBlueprint<Properties extends IBlueprintCollection, Optional extends boolean = false> {
@@ -288,6 +340,28 @@ export interface IBlueprintList<Item extends BlueprintField, Optional extends bo
288
340
  readonly "~standard": StandardSchemaV1.Props<ListShape<Item, "input", Optional>, ListShape<Item, "value", Optional>>;
289
341
  }
290
342
  type ListShape<Item, Mode extends FieldMode, Optional extends boolean> = Optional extends true ? FieldValue<Item, Mode>[] | undefined : FieldValue<Item, Mode>[];
343
+ /** The variants of a variant field, by the value its key has for each: blueprints, or record classes. */
344
+ export type BlueprintVariants = Readonly<Record<string, AnyBlueprint | AnyRecordClass>>;
345
+ /**
346
+ * A field holding one of the objects of `Variants`, told apart by the value
347
+ * of `Key`, which is the name of the variant.
348
+ */
349
+ export interface IBlueprintVariant<Key extends string, Variants extends BlueprintVariants, Optional extends boolean = false> {
350
+ readonly [Kind]: "variant";
351
+ /** The key that tells the variants apart; no variant declares it. */
352
+ readonly key: Key;
353
+ readonly variants: Variants;
354
+ /** Whether, as a field of a blueprint, it accepts `undefined`. */
355
+ readonly optional: Optional;
356
+ /**
357
+ * The Standard Schema of the field: `validate` gives the frozen value of
358
+ * the variant the key names, or the issues that reject the value.
359
+ */
360
+ readonly "~standard": StandardSchemaV1.Props<VariantShape<Key, Variants, "input", Optional>, VariantShape<Key, Variants, "value", Optional>>;
361
+ }
362
+ type VariantShape<Key extends string, Variants, Mode extends FieldMode, Optional extends boolean> = Optional extends true ? VariantValue<Key, Variants, Mode> | undefined : VariantValue<Key, Variants, Mode>;
363
+ /** `Variant` as a field that accepts `undefined`. */
364
+ export type OptionalBlueprintVariant<Variant extends AnyBlueprintVariant> = Variant extends IBlueprintVariant<infer Key, infer Variants, boolean> ? IBlueprintVariant<Key, Variants, true> : never;
291
365
  /** `List` as a field that accepts `undefined`. */
292
366
  export type OptionalBlueprintList<List extends AnyBlueprintList> = List extends IBlueprintList<infer Item, boolean> ? IBlueprintList<Item, true> : never;
293
367
  export {};
@@ -0,0 +1,32 @@
1
+ import type { AnyBlueprint, AnyBlueprintVariant, BlueprintVariants, IBlueprintVariant } from "./types/blueprint.interface.js";
2
+ /**
3
+ * A field holding one of several objects, told apart by the value of `key`:
4
+ * `variantOf("type", { click: Click, signup: Signup })` takes
5
+ * `{ type: "click", ...a Click }` or `{ type: "signup", ...a Signup }`. Each
6
+ * variant is a blueprint, which does not declare `key`: the field adds it,
7
+ * with the name of the variant for its value. A variant can also be a record
8
+ * class (an entity too): the value is then an instance of it, told apart by
9
+ * its class, and the key is only in what is given, in `toJSON` and in what is
10
+ * stored.
11
+ */
12
+ export declare function variantOf<const Key extends string, const Variants extends BlueprintVariants>(key: Key, variants: Variants): IBlueprintVariant<Key, Variants>;
13
+ /** `variant`, frozen, with the `~standard` that handles a value as `variant` does. */
14
+ export declare function standardVariantOf<Variant extends object>(variant: Variant): Variant;
15
+ type Variant = BlueprintVariants[string];
16
+ /** The blueprint of a variant: itself, or that of the record class it is. */
17
+ export declare function blueprintOfVariant(variant: Variant): AnyBlueprint;
18
+ /**
19
+ * The variant that the plain `value` names with the key of `field`, if it
20
+ * names one: a blueprint or a record class.
21
+ */
22
+ export declare function namedVariantOf(field: AnyBlueprintVariant, value: unknown): Variant | undefined;
23
+ /** The blueprint of the variant that the plain `value` names with the key of `field`, if it names one. */
24
+ export declare function variantOfValue(field: AnyBlueprintVariant, value: unknown): AnyBlueprint | undefined;
25
+ /**
26
+ * The name of the variant `instance` is, a record of one of the classes of
27
+ * `field`; `undefined` when it is of none.
28
+ */
29
+ export declare function variantNameOf(field: AnyBlueprintVariant, instance: unknown): string | undefined;
30
+ /** Whether `value` is a field made by `variantOf`. */
31
+ export declare function isBlueprintVariant(value: unknown): value is AnyBlueprintVariant;
32
+ export {};
@@ -1,6 +1,7 @@
1
1
  import type { BlueprintInput, IBlueprint, IBlueprintCollection } from "../../blueprint/types/blueprint.interface.js";
2
2
  import type { EntityEvent } from "../../entity/event.js";
3
3
  import type { AnyEnvironment, EnvironmentDependencies, MergedEnvironment } from "../../environment/environment.js";
4
+ import type { IEventPublisher } from "../../event-policy/types/event-policy.interface.js";
4
5
  import type { ILogger } from "../../logger/logger.js";
5
6
  import type { Kind } from "../../symbols/kind.js";
6
7
  import type { AnyUseCaseClass, UseCaseInput, UseCaseOptions, UseCaseResponse } from "../../use-case/use-case.js";
@@ -19,6 +20,12 @@ export interface CommandEventInfo {
19
20
  * application's; a logger that writes nothing without either.
20
21
  */
21
22
  readonly logger: ILogger;
23
+ /**
24
+ * The publisher declared under `name` in `applicationOf({ publishers })`:
25
+ * of the application that bound the command, else of the started
26
+ * application. Throws `PublisherNotBound` when neither declares it.
27
+ */
28
+ publisher(name: string): IEventPublisher;
22
29
  }
23
30
  /** Handles all the events of a call, once it succeeded. */
24
31
  export type CommandEventHandler = (events: readonly EntityEvent[], info: CommandEventInfo) => void | Promise<void>;
@@ -12,10 +12,42 @@ export declare const RESERVED_ENTITY_KEYS: readonly string[];
12
12
  export declare const EntityClock: unique symbol;
13
13
  /** The unique keys of a class made by `entityOf`, as `options.unique` lists them. */
14
14
  export declare const EntityUniqueKeys: unique symbol;
15
+ /** The storage options of an entity class (`versioned`, `expires`). */
16
+ export declare const EntitySettings: unique symbol;
17
+ /**
18
+ * The milliseconds after `createdAt` at which the entities of a class
19
+ * declared with `expires: { after }` expire, kept in `expiresAt`.
20
+ *
21
+ * @internal
22
+ */
23
+ export declare const EntityLifetime: unique symbol;
24
+ /** The key the entities of a class declared with `expires: { after }` keep their expiry in. */
25
+ export declare const EXPIRES_AT = "expiresAt";
26
+ /** The key a versioned entity adds to its identity. */
27
+ export declare const ENTITY_VERSION_KEY = "version";
28
+ /**
29
+ * The key of each stored row holding its schema version, for entities
30
+ * declared with `schemaVersion`; it is never a key of the entity.
31
+ */
32
+ export declare const SCHEMA_VERSION_KEY = "$v";
33
+ /** Whether `entityClass` was declared with `versioned: true`. */
34
+ export declare function isVersionedEntityClass(entityClass: unknown): boolean;
35
+ /**
36
+ * The identity keys of the entities of `entityClass`, which only the entity
37
+ * changes: `ENTITY_IDENTITY_KEYS`, `version` when it is versioned, and
38
+ * `expiresAt` when it expires `after` a duration.
39
+ */
40
+ export declare function identityKeysOf(entityClass: unknown): readonly string[];
41
+ /**
42
+ * Stores `version` as the version of `entity`, as its repository does once
43
+ * a write succeeded. @internal
44
+ */
45
+ export declare function storeEntityVersion(entity: object, version: number): void;
15
46
  /**
16
47
  * A record with identity: an `id` and a `createdAt`, made when it is built
17
48
  * and never changed after, an `updatedAt` set by each change, and a
18
- * `deletedAt` set by `destroy`, after which it no longer changes.
49
+ * `deletedAt` set by `destroy`, after which it no longer changes. A versioned
50
+ * entity also has a `version`, 1 when built, which only its repository changes.
19
51
  */
20
52
  export declare abstract class AbstractEntity<Properties extends IBlueprintCollection, Sensitive extends string = never> extends AbstractRecord<Properties, Sensitive> {
21
53
  constructor(value: BlueprintInput<Properties>);
@@ -9,7 +9,7 @@ import type { EntityBuilder, EntityBuilderSettings, IEntityIdentity } from "./ty
9
9
  * `options.plugins` picks from the plugins of `plugins`, catalogs made by
10
10
  * `recordPluginCatalogOf`.
11
11
  */
12
- export declare function entityBuilderOf<Identity extends IEntityIdentity, const Catalogs extends readonly IRecordPluginCatalog[] = []>({ identity, generateId, now, plugins: catalogs, }: EntityBuilderSettings<Identity, Catalogs>): EntityBuilder<Identity, RecordPluginContext<Catalogs>>;
12
+ export declare function entityBuilderOf<Identity extends IEntityIdentity, const Catalogs extends readonly IRecordPluginCatalog[] = []>(settings: EntityBuilderSettings<Identity, Catalogs>): EntityBuilder<Identity, RecordPluginContext<Catalogs>>;
13
13
  /** Whether `value` is a class made by `entityOf`, or one extending it. */
14
14
  export declare function isEntityClass(value: unknown): value is AnyRecordClass & AnyEntityClass;
15
15
  /** Whether `value` is an entity, an instance of a class made by `entityOf`. */
@@ -32,8 +32,19 @@ export interface EntityEvents {
32
32
  readonly onDelete?: EntityEventReference;
33
33
  }
34
34
  export declare const EntityEventSettings: unique symbol;
35
- /** Defines an event without executing its handler. */
36
- export declare function eventOf<Class extends AnyEntityClass>(name: string, entityClass: Class, handler: (entity: InstanceType<Class>, context: EventHandlerContext) => Promise<void>): EventDefinition;
35
+ /**
36
+ * Defines an event without executing its handler. Without a handler, the
37
+ * event only carries the entity, for a policy such as `publishEvents`, and
38
+ * `handle()` does nothing.
39
+ */
40
+ export declare function eventOf<Class extends AnyEntityClass>(name: string, entityClass: Class, handler?: (entity: InstanceType<Class>, context: EventHandlerContext) => Promise<void>): EventDefinition;
41
+ /** Whether `event` was given a handler by `eventOf`. @internal */
42
+ export declare function eventHasHandler(event: EventDefinition): boolean;
43
+ /**
44
+ * The lifecycle events of an entity class, by cycle, with their function
45
+ * references resolved: what `describe()` gives. @internal
46
+ */
47
+ export declare function lifecycleEventsOf(entityClass: object): Partial<Record<keyof EntityEvents, readonly EventDefinition[]>>;
37
48
  export declare function enqueueEvent(entity: object, event: EventDefinition): void;
38
49
  export declare function pullEntityEvents<Entity extends object>(entity: Entity): EntityEvent<Entity>[];
39
50
  export declare function enqueueLifecycle(entity: object, cycle: keyof EntityEvents): void;
@@ -17,3 +17,20 @@ export declare const ImmutableEntityKey: import("../../index.js").DeclaredExcept
17
17
  export declare const DeletedEntity: import("../../index.js").DeclaredExceptionClass<{
18
18
  readonly id: string;
19
19
  }>;
20
+ /**
21
+ * The `expires` option of an entity is invalid: `at` names no date key of its
22
+ * blueprint, `after` is not a duration of positive numbers, or the entity
23
+ * builder has no `expiresAt` field; `source` is the entity name.
24
+ */
25
+ export declare const InvalidEntityExpiry: import("../../index.js").DeclaredExceptionClass<{
26
+ readonly reason: string;
27
+ }>;
28
+ /**
29
+ * The schema evolution options of an entity are invalid: `schemaVersion` is
30
+ * not a positive integer, a step of `upgrade` is not a function or reaches a
31
+ * version outside `2..schemaVersion`, or `previousNames` holds something
32
+ * that cannot name a record, or its own name; `source` is the entity name.
33
+ */
34
+ export declare const InvalidEntitySchemaVersion: import("../../index.js").DeclaredExceptionClass<{
35
+ readonly reason: string;
36
+ }>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Under this key, the settings of an entity builder tell when what their
3
+ * `generateId` and `now` make is known to be valid, so that an entity does
4
+ * not check again the id and the date it has just made. Only for a builder
5
+ * that knows its own makers, as `std` does of its defaults: a generator an
6
+ * application configures is never vouched for.
7
+ * @internal
8
+ */
9
+ export declare const MakesValid: unique symbol;
10
+ /** @internal */
11
+ export interface ValidMakers {
12
+ /** Whether the id `generateId` is about to make is one `id` accepts as it is. */
13
+ readonly id: () => boolean;
14
+ /** Whether the date `now` is about to make is one `createdAt` accepts as it is. */
15
+ readonly createdAt: () => boolean;
16
+ }
17
+ /**
18
+ * `settings` of an entity builder with `makers` vouching for its
19
+ * `generateId` and `now`.
20
+ * @internal
21
+ */
22
+ export declare function vouching<const Settings extends object>(settings: Settings, makers: ValidMakers): Settings;
23
+ /** The makers `settings` vouch for, if any. @internal */
24
+ export declare function validMakersOf(settings: object): ValidMakers | undefined;