@afora/core 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -7
- package/dist/core/application/start.d.ts +7 -0
- package/dist/core/application/types/application.interface.d.ts +47 -2
- package/dist/core/command/command.d.ts +11 -4
- package/dist/core/command/types/command.interface.d.ts +26 -0
- package/dist/core/entity/abstract-entity.d.ts +28 -1
- package/dist/core/entity/entity.d.ts +1 -1
- package/dist/core/entity/event.d.ts +13 -2
- package/dist/core/entity/exceptions.d.ts +8 -0
- package/dist/core/entity/types/entity.interface.d.ts +66 -2
- package/dist/core/entity/version.d.ts +13 -0
- package/dist/core/event-policy/event-policy.d.ts +25 -0
- package/dist/core/event-policy/exceptions.d.ts +11 -0
- package/dist/core/event-policy/publish.d.ts +17 -0
- package/dist/core/event-policy/publishers.d.ts +18 -0
- package/dist/core/event-policy/types/event-policy.interface.d.ts +72 -0
- package/dist/core/output/coerce.d.ts +9 -0
- package/dist/core/output/openapi-sink.d.ts +138 -38
- package/dist/core/output/status.d.ts +2 -1
- package/dist/core/parse/parse.d.ts +10 -0
- package/dist/core/parse/strip.d.ts +13 -0
- package/dist/core/property/input.d.ts +20 -0
- package/dist/core/record/plugin.d.ts +6 -2
- package/dist/core/repository/exceptions.d.ts +30 -0
- package/dist/core/repository/in-memory.d.ts +14 -1
- package/dist/core/repository/plan.d.ts +20 -0
- package/dist/core/repository/provider.d.ts +1 -1
- package/dist/core/repository/query.d.ts +9 -2
- package/dist/core/repository/repository.d.ts +40 -8
- package/dist/core/repository/state.d.ts +39 -2
- package/dist/core/repository/types/query.interface.d.ts +106 -5
- package/dist/core/repository/types/repository.interface.d.ts +113 -7
- package/dist/core/use-case/exceptions.d.ts +36 -0
- package/dist/core/use-case/plugin-catalog.d.ts +11 -0
- package/dist/core/use-case/plugin.d.ts +25 -0
- package/dist/core/use-case/steps/types/steps.interface.d.ts +23 -6
- package/dist/core/use-case/target.d.ts +9 -0
- package/dist/core/use-case/types/plugin.interface.d.ts +185 -0
- package/dist/experimental.js +3 -3
- package/dist/{index-3nd9bjnc.js → index-qxfkyd6k.js} +646 -635
- package/dist/{index-gbgc40xe.js → index-t5qwtyj5.js} +2798 -295
- package/dist/index.d.ts +35 -15
- package/dist/index.js +99 -15
- package/dist/std/blueprint-plugin.d.ts +6 -2
- package/dist/std/blueprint.d.ts +3 -0
- package/dist/std/command.d.ts +11 -0
- package/dist/std/entity.d.ts +2 -1
- package/dist/std/event-policies.d.ts +2 -0
- package/dist/std/properties/exceptions.d.ts +11 -0
- package/dist/std/properties/json.d.ts +54 -0
- package/dist/std/properties/password.d.ts +16 -1
- package/dist/std/properties/time.d.ts +4 -0
- package/dist/std/use-cases/auth.d.ts +5 -0
- package/dist/std/use-cases/crud.d.ts +25 -0
- package/dist/std/use-cases/index.d.ts +11 -0
- package/dist/std/use-cases/lifecycle.d.ts +13 -0
- package/dist/std/use-cases/shared.d.ts +88 -0
- package/dist/std/use-cases/types.d.ts +291 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +355 -2
- package/dist/testing/memory-publisher.d.ts +16 -0
- package/dist/testing/provider-contract.d.ts +14 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">afora</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center"><strong>Model the domain. Keep the invariants.</strong><br>Domain modeling for TypeScript, built on TypeBox.</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/@afora/core"><img alt="npm" src="https://img.shields.io/npm/v/@afora/core?color=cb3837&logo=npm"></a>
|
|
7
|
+
<a href="https://github.com/afora-framework/core/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/afora-framework/core/actions/workflows/ci.yml/badge.svg?branch=main"></a>
|
|
8
|
+
<a href="https://www.npmjs.com/package/@afora/core"><img alt="npm provenance" src="https://img.shields.io/badge/npm-provenance-2ea44f?logo=npm"></a>
|
|
9
|
+
<a href="https://standardschema.dev"><img alt="Standard Schema V1" src="https://img.shields.io/badge/Standard%20Schema-V1-3178c6"></a>
|
|
10
|
+
<img alt="Bun >= 1.4.4 and Node >= 22" src="https://img.shields.io/badge/runtime-Bun%20%E2%89%A5%201.4.4%20%7C%20Node%20%E2%89%A5%2022-555">
|
|
11
|
+
<a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/github/license/afora-framework/core"></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://getafora.com">Website</a> ·
|
|
16
|
+
<a href="docs/README.md">Docs</a> ·
|
|
17
|
+
<a href="#quick-start">Quick start</a> ·
|
|
18
|
+
<a href="docs/afora-json.md">afora.json</a> ·
|
|
19
|
+
<a href="CHANGELOG.md">Changelog</a> ·
|
|
20
|
+
<a href="https://github.com/afora-framework/core/discussions">Discussions</a>
|
|
21
|
+
</p>
|
|
2
22
|
|
|
3
23
|
Domain modeling on [TypeBox](https://github.com/sinclairzx81/typebox): entities with invariants, sensitive data masking and exceptions that know their layer, usable by any [Standard Schema](https://standardschema.dev) consumer.
|
|
4
24
|
|
|
@@ -10,7 +30,8 @@ afora is not a general-purpose validator. It is for code that models a domain, a
|
|
|
10
30
|
- **Typed change namespace and side effects.** `change: ["password"]` generates `change.password(value)` (also destructurable, as `const { change } = user`), which re-validates the value and runs `onSetAll` and `onSet`.
|
|
11
31
|
- **Exceptions that know their layer.** Each error carries a layer (`Property`, `Record`, `UseCase`, `Output`...) and a `trace` that grows as it goes up.
|
|
12
32
|
- **Standard Schema V1 on every field.** Properties, blueprints, lists and record classes work as is in Hono, tRPC and TanStack Form, and `safeParse` never throws for an invalid value.
|
|
13
|
-
- **Commands and outputs.** `commandOf` groups use cases behind one execution boundary (context, transactions, event policies); `outputOf` turns commands into operations and pipes them to a sink: an HTTP API with `@afora/hono`, an OpenAPI 3.1 document, or MCP tools, with problem+json errors and masked responses.
|
|
33
|
+
- **Commands and outputs.** `commandOf` groups use cases behind one execution boundary (context, transactions, event policies); `outputOf` turns commands into operations and pipes them to a sink: an HTTP API with `@afora/hono`, WebSocket messages with `@afora/ws`, an OpenAPI 3.1 document, or MCP tools, with problem+json errors and masked responses.
|
|
34
|
+
- **Standard use cases.** `commandOf({ useCases: (ctx) => ({ create: ctx.create(Posts), publish: ctx.publish(Posts) }) })` picks ready-made use cases that follow the repository and its entity: the actions it declares, its record plugins and the properties of its keys ([docs](docs/std-use-cases.md)).
|
|
14
35
|
- **The whole application in JSON.** An `afora.json` generates the code (`@afora/codegen`), use cases included, as data (steps), and `extractSpec(app)` gives back the `afora.json` of an application written by hand.
|
|
15
36
|
- **Repositories on any store.** A provider contract with typed queries, soft deletion and transactions, a runner-agnostic kit to test one (`@afora/core/testing`), in memory by default and on Drizzle ORM with `@afora/drizzle`.
|
|
16
37
|
|
|
@@ -300,11 +321,18 @@ Every field (a property, blueprint, list or record class) implements Standard Sc
|
|
|
300
321
|
|
|
301
322
|
## Packages
|
|
302
323
|
|
|
303
|
-
|
|
324
|
+
| Package | What it does |
|
|
325
|
+
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
326
|
+
| [`@afora/core`](https://www.npmjs.com/package/@afora/core) | Properties, blueprints, records, entities, repositories, use cases, commands and outputs (this repository's root). |
|
|
327
|
+
| [`@afora/codegen`](packages/codegen/README.md) | Generates afora code from an `afora.json`, and extracts the `afora.json` of an `Application` back. |
|
|
328
|
+
| [`@afora/mcp`](packages/mcp/README.md) | An MCP server that lets an agent build a domain with `@afora/codegen`. |
|
|
329
|
+
| [`@afora/hono`](packages/hono/README.md) | A `hono` sink for outputs: serves the operations as a Hono app, with problem+json errors, masked responses, signed webhooks and an optional OpenAPI document. |
|
|
330
|
+
| [`@afora/ws`](packages/ws/README.md) | A `websocket` sink: operations over JSON messages on any runtime, with topic subscriptions that `publishEvents` feeds with domain events. |
|
|
331
|
+
| [`@afora/drizzle`](packages/drizzle/README.md) | A repository provider on Drizzle ORM (SQLite and Postgres) that passes the provider contract kit. |
|
|
304
332
|
|
|
305
333
|
## Example
|
|
306
334
|
|
|
307
|
-
`examples/blog` is a blog (users with the `authenticatable` plugin, posts with soft deletion, use
|
|
335
|
+
`examples/blog` is a blog (users with the `authenticatable` plugin, posts with soft deletion, every use case a [standard one](docs/std-use-cases.md), a Hono API on Drizzle and SQLite) whose whole `src/domain` is generated from one `afora.json` by `@afora/codegen`. Only the database, the schema, the provider (made from a `Database` environment with `providerFrom` when the server calls `App.start()`, and named `database` in the `providers` of the spec) and the server are hand-written. Its tests check that the committed code is what the spec generates, drive the API end to end and extract the spec back from the application: `bun run test:blog`.
|
|
308
336
|
|
|
309
337
|
## Performance
|
|
310
338
|
|
|
@@ -335,9 +363,9 @@ Where the others win: Zod and Valibot are faster, have far larger ecosystems and
|
|
|
335
363
|
|
|
336
364
|
## Contributing
|
|
337
365
|
|
|
338
|
-
Issues open from a template: bug report, feature request, or feedback from building on afora. Questions go to [Discussions](https://github.com/afora-framework/core/discussions)
|
|
366
|
+
Read [CONTRIBUTING.md](CONTRIBUTING.md) for the setup, the checks and how releases work. Issues open from a template: bug report, feature request, or feedback from building on afora. Questions go to [Discussions](https://github.com/afora-framework/core/discussions); security issues go through [SECURITY.md](SECURITY.md), never a public issue.
|
|
339
367
|
|
|
340
|
-
Commits follow [Conventional Commits](https://www.conventionalcommits.org) (`feat(repository): …`, `fix: …`), checked by commitlint in a Husky `commit-msg` hook; the `pre-commit` hook runs Biome and Knip. CI runs lint, Knip, type checks, unit and interop tests, the build and the Node smoke test on every push and pull request.
|
|
368
|
+
Commits follow [Conventional Commits](https://www.conventionalcommits.org) (`feat(repository): …`, `fix: …`), checked by commitlint in a Husky `commit-msg` hook; the `pre-commit` hook runs Biome and Knip. CI runs lint, Knip, type checks, unit and interop tests, the build and the Node smoke test on every push and pull request. A pull request that bumps the version in `package.json`, once merged to `main`, publishes to npm with provenance and creates the tag and the GitHub release.
|
|
341
369
|
|
|
342
370
|
Scripts:
|
|
343
371
|
|
|
@@ -359,8 +387,9 @@ bun run build # build dist/ (ESM + .d.ts)
|
|
|
359
387
|
bun run test:codegen # tests of packages/codegen
|
|
360
388
|
bun run test:mcp # tests of packages/mcp
|
|
361
389
|
bun run test:hono # tests of packages/hono
|
|
390
|
+
bun run test:ws # tests of packages/ws
|
|
362
391
|
bun run test:drizzle # tests of packages/drizzle (SQLite and PGlite)
|
|
363
|
-
bun run build:packages # build the core, then packages/codegen, packages/mcp, packages/hono and packages/drizzle
|
|
392
|
+
bun run build:packages # build the core, then packages/codegen, packages/mcp, packages/hono, packages/ws and packages/drizzle
|
|
364
393
|
```
|
|
365
394
|
|
|
366
395
|
## Versioning
|
|
@@ -73,3 +73,10 @@ export declare function stopProvider(provider: IRepositoryProvider): Promise<voi
|
|
|
73
73
|
* @internal
|
|
74
74
|
*/
|
|
75
75
|
export declare function checkTransactions(source: string, provider: IRepositoryProvider, commands: readonly string[]): void;
|
|
76
|
+
/**
|
|
77
|
+
* Throws `InvalidApplicationDeclaration` when `provider` does not read by
|
|
78
|
+
* cursor (`capabilities.keyset`) and some of `repositories` declare
|
|
79
|
+
* `findCursor`.
|
|
80
|
+
* @internal
|
|
81
|
+
*/
|
|
82
|
+
export declare function checkCapabilities(source: string, provider: IRepositoryProvider, repositories: readonly string[]): void;
|
|
@@ -1,11 +1,13 @@
|
|
|
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";
|
|
10
12
|
import type { Kind } from "../../symbols/kind.js";
|
|
11
13
|
import type { UseCaseStepDescription } from "../../use-case/steps/types/steps.interface.js";
|
|
@@ -96,6 +98,13 @@ export interface ApplicationSettings<Name extends string, Extended extends reado
|
|
|
96
98
|
* it, the one of the last extended application that has one; none by default.
|
|
97
99
|
*/
|
|
98
100
|
readonly log?: LogSettings | LogFactory;
|
|
101
|
+
/**
|
|
102
|
+
* Where event policies such as `publishEvents` publish, by key: a
|
|
103
|
+
* WebSocket hub, a queue, a broker. Commands of the application find them
|
|
104
|
+
* by key, those of the extended applications included; a command whose
|
|
105
|
+
* policy names a key that is not here throws `UnregisteredApplicationReference`.
|
|
106
|
+
*/
|
|
107
|
+
readonly publishers?: Readonly<Record<string, IEventPublisher>>;
|
|
99
108
|
}
|
|
100
109
|
/**
|
|
101
110
|
* What an application gives the factories it runs on start (`providerFrom`,
|
|
@@ -293,6 +302,22 @@ export interface RecordDescription {
|
|
|
293
302
|
readonly change: readonly string[];
|
|
294
303
|
};
|
|
295
304
|
readonly plugins: readonly RecordPluginDescriptor[];
|
|
305
|
+
/**
|
|
306
|
+
* The lifecycle events of an entity, by cycle: each event's name and
|
|
307
|
+
* whether `eventOf` was given a handler. Present only when it has some.
|
|
308
|
+
*/
|
|
309
|
+
readonly events?: Readonly<Partial<Record<"onCreate" | "onUpdate" | "onDelete", readonly {
|
|
310
|
+
readonly name: string;
|
|
311
|
+
readonly handler: boolean;
|
|
312
|
+
}[]>>>;
|
|
313
|
+
/** Whether the entity is versioned (`entityOf(..., { versioned: true })`); present only when it is. */
|
|
314
|
+
readonly versioned?: true;
|
|
315
|
+
/**
|
|
316
|
+
* When the records of an entity expire, as `entityOf` was given it:
|
|
317
|
+
* `{ after: duration }` (kept in its `expiresAt` identity key, left out of
|
|
318
|
+
* `fields`) or `{ at: key }`. Present only when it has one.
|
|
319
|
+
*/
|
|
320
|
+
readonly expires?: EntityExpires;
|
|
296
321
|
}
|
|
297
322
|
/** What a use case depends on, by the name it has in the application. */
|
|
298
323
|
export interface UseCaseDependencyDescription {
|
|
@@ -340,6 +365,17 @@ export interface UseCaseDescription {
|
|
|
340
365
|
} | {
|
|
341
366
|
readonly kind: "opaque";
|
|
342
367
|
};
|
|
368
|
+
/**
|
|
369
|
+
* The use case plugin that made it, the registered repository it was made
|
|
370
|
+
* for and the options given, with classes by name as in the steps; absent
|
|
371
|
+
* for a use case declared by hand.
|
|
372
|
+
*/
|
|
373
|
+
readonly generic?: {
|
|
374
|
+
readonly name: string;
|
|
375
|
+
readonly version: string;
|
|
376
|
+
readonly repository: string;
|
|
377
|
+
readonly options: Readonly<Record<string, unknown>>;
|
|
378
|
+
};
|
|
343
379
|
}
|
|
344
380
|
/** A command: its metadata, context, operations and policies. */
|
|
345
381
|
export interface CommandDescription {
|
|
@@ -359,7 +395,8 @@ export interface CommandDescription {
|
|
|
359
395
|
readonly transaction: boolean;
|
|
360
396
|
/** The names of its environments, when it declares any. */
|
|
361
397
|
readonly environments?: readonly string[];
|
|
362
|
-
|
|
398
|
+
/** The policy: a name, a policy made by `eventPolicyOf` by name and options, else opaque. */
|
|
399
|
+
readonly events: "after-commit" | "manual" | EventPolicyDescription | {
|
|
363
400
|
readonly kind: "opaque";
|
|
364
401
|
};
|
|
365
402
|
readonly onEventError?: {
|
|
@@ -411,6 +448,10 @@ export interface ApplicationDescription {
|
|
|
411
448
|
readonly log?: LogDescription | {
|
|
412
449
|
readonly kind: "opaque";
|
|
413
450
|
};
|
|
451
|
+
/** Its publishers (inherited ones included), by key, with the application that declared each; present only when it has some. */
|
|
452
|
+
readonly publishers?: Readonly<Record<string, {
|
|
453
|
+
readonly from: string;
|
|
454
|
+
}>>;
|
|
414
455
|
readonly properties: Readonly<Record<string, PropertyDescription & {
|
|
415
456
|
readonly from: string;
|
|
416
457
|
}>>;
|
|
@@ -429,6 +470,10 @@ export interface ApplicationDescription {
|
|
|
429
470
|
readonly entity: string;
|
|
430
471
|
readonly actions: readonly string[];
|
|
431
472
|
readonly deletion: "hard" | "soft";
|
|
473
|
+
/** Its declared indexes, by name. */
|
|
474
|
+
readonly indexes: Readonly<Record<string, RepositoryIndex>>;
|
|
475
|
+
/** What it does with a query only a scan serves. */
|
|
476
|
+
readonly scans: RepositoryScans;
|
|
432
477
|
/** The names of the repositories of the entities its entity holds. */
|
|
433
478
|
readonly relations: readonly string[];
|
|
434
479
|
/** The names of its `environmentDependencies`. */
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { IBlueprintCollection } from "../blueprint/types/blueprint.interface.js";
|
|
2
|
-
import { type EnvironmentDependencies } from "../environment/environment.js";
|
|
3
1
|
import type { IRepositoryProvider } from "../repository/types/repository.interface.js";
|
|
4
|
-
import type {
|
|
2
|
+
import type { IUseCaseCatalog, UseCasePluginContext } from "../use-case/types/plugin.interface.js";
|
|
3
|
+
import type { AnyCommand, CommandBuilder } from "./types/command.interface.js";
|
|
5
4
|
/**
|
|
6
5
|
* Checks now that the repositories of `command` share a provider that has
|
|
7
6
|
* `transaction`, as its first call would; an application calls it when it
|
|
@@ -15,11 +14,19 @@ export declare function verifyCommand(command: object): void;
|
|
|
15
14
|
* @internal
|
|
16
15
|
*/
|
|
17
16
|
export declare function commandTransactionProvider(command: object): IRepositoryProvider | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* A `commandOf` whose `useCases` may be a function of the use case plugins
|
|
19
|
+
* of `catalogs`, by name, a later catalog winning a name:
|
|
20
|
+
* `useCases: (ctx) => ({ create: ctx.create(Posts), custom: MyUseCase })`.
|
|
21
|
+
* Throws `InvalidCommandDeclaration` for what is not a catalog made by
|
|
22
|
+
* `useCaseCatalogOf`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function commandBuilderOf<const Catalogs extends readonly IUseCaseCatalog[] = []>(...catalogs: Catalogs): CommandBuilder<UseCasePluginContext<Catalogs>>;
|
|
18
25
|
/**
|
|
19
26
|
* Groups use cases under operation names and is the boundary of a call: the
|
|
20
27
|
* context is validated once, a transaction can wrap the use case, and the
|
|
21
28
|
* events it raised are handled by a policy once it succeeded.
|
|
22
29
|
*/
|
|
23
|
-
export declare
|
|
30
|
+
export declare const commandOf: CommandBuilder<Record<never, never>>;
|
|
24
31
|
/** Whether `value` is a command made by `commandOf`. */
|
|
25
32
|
export declare function isCommand(value: unknown): value is AnyCommand;
|
|
@@ -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>;
|
|
@@ -48,6 +55,25 @@ export interface CommandSettings<UseCases extends CommandUseCases, Context exten
|
|
|
48
55
|
readonly events?: CommandEvents;
|
|
49
56
|
readonly onEventError?: CommandEventErrorHandler;
|
|
50
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* The `useCases` of a command made by a builder: the object, or a function
|
|
60
|
+
* of the use case plugins of its catalogs that gives it.
|
|
61
|
+
*/
|
|
62
|
+
export type CommandUseCasesOption<UseCases extends CommandUseCases, Generics> = UseCases | ((ctx: Generics) => UseCases);
|
|
63
|
+
/** `CommandSettings` with `useCases` as a builder takes it. */
|
|
64
|
+
export interface CommandBuilderSettings<UseCases extends CommandUseCases, Context extends IBlueprintCollection | undefined, Environment extends EnvironmentDependencies | undefined, Generics> extends Omit<CommandSettings<UseCases, Context, Environment>, "useCases"> {
|
|
65
|
+
/**
|
|
66
|
+
* The use cases, by operation name; or a function that gets the use
|
|
67
|
+
* case plugins of the builder's catalogs, each called with a repository,
|
|
68
|
+
* and gives them: `(ctx) => ({ create: ctx.create(Posts) })`.
|
|
69
|
+
*/
|
|
70
|
+
readonly useCases: CommandUseCasesOption<UseCases, Generics>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* What `commandBuilderOf` gives: `commandOf`, with the use case plugins
|
|
74
|
+
* `Generics` to pick from.
|
|
75
|
+
*/
|
|
76
|
+
export type CommandBuilder<Generics> = <const UseCases extends CommandUseCases, Context extends IBlueprintCollection | undefined = undefined, Environment extends EnvironmentDependencies | undefined = undefined>(settings: CommandBuilderSettings<UseCases, Context, Environment, Generics>) => CommandOf<UseCases, Context, Environment>;
|
|
51
77
|
/** The names of the operations of a command. */
|
|
52
78
|
export type CommandOperation<Command extends {
|
|
53
79
|
readonly useCases: object;
|
|
@@ -12,10 +12,37 @@ 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
|
+
/** Whether `entityClass` was declared with `versioned: true`. */
|
|
29
|
+
export declare function isVersionedEntityClass(entityClass: unknown): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* The identity keys of the entities of `entityClass`, which only the entity
|
|
32
|
+
* changes: `ENTITY_IDENTITY_KEYS`, `version` when it is versioned, and
|
|
33
|
+
* `expiresAt` when it expires `after` a duration.
|
|
34
|
+
*/
|
|
35
|
+
export declare function identityKeysOf(entityClass: unknown): readonly string[];
|
|
36
|
+
/**
|
|
37
|
+
* Stores `version` as the version of `entity`, as its repository does once
|
|
38
|
+
* a write succeeded. @internal
|
|
39
|
+
*/
|
|
40
|
+
export declare function storeEntityVersion(entity: object, version: number): void;
|
|
15
41
|
/**
|
|
16
42
|
* A record with identity: an `id` and a `createdAt`, made when it is built
|
|
17
43
|
* and never changed after, an `updatedAt` set by each change, and a
|
|
18
|
-
* `deletedAt` set by `destroy`, after which it no longer changes.
|
|
44
|
+
* `deletedAt` set by `destroy`, after which it no longer changes. A versioned
|
|
45
|
+
* entity also has a `version`, 1 when built, which only its repository changes.
|
|
19
46
|
*/
|
|
20
47
|
export declare abstract class AbstractEntity<Properties extends IBlueprintCollection, Sensitive extends string = never> extends AbstractRecord<Properties, Sensitive> {
|
|
21
48
|
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[] = []>({ identity, generateId, now, expiresAt, plugins: catalogs, }: 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
|
-
/**
|
|
36
|
-
|
|
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,11 @@ 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
|
+
}>;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import type { TString } from "typebox";
|
|
1
2
|
import type { BlueprintField, BlueprintInput, BlueprintValue, IBlueprint, IBlueprintCollection, JoinedProperties } from "../../blueprint/types/blueprint.interface.js";
|
|
2
3
|
import type { ParseResult } from "../../parse/types/parse.interface.js";
|
|
4
|
+
import type { PropertyClass } from "../../property/property.js";
|
|
3
5
|
import type { AnyRecordPlugin, IRecordPluginCatalog, PluginMembers, PluginSensitiveKey, PluginUniqueKey, WithPluginProperties } from "../../record/types/plugin.interface.js";
|
|
4
6
|
import type { MarkedUniqueKey, RecordChanges, RecordKey, RecordOptions, RecordSettings } from "../../record/types/record.interface.js";
|
|
5
7
|
import type { StandardSchemaV1 } from "../../standard/standard-schema.js";
|
|
6
8
|
import type { Kind } from "../../symbols/kind.js";
|
|
7
9
|
import type { AbstractEntity, EntityUniqueKeys } from "../abstract-entity.js";
|
|
8
10
|
import type { EntityClassBrand, EntityEvents } from "../event.js";
|
|
11
|
+
import type { EntityVersion } from "../version.js";
|
|
9
12
|
/** The keys every entity has, which its blueprint cannot declare. */
|
|
10
13
|
export type EntityIdentityKey = "id" | "createdAt" | "updatedAt" | "deletedAt";
|
|
11
14
|
/**
|
|
@@ -15,6 +18,12 @@ export type EntityIdentityKey = "id" | "createdAt" | "updatedAt" | "deletedAt";
|
|
|
15
18
|
export type IEntityIdentity = {
|
|
16
19
|
readonly [Key in EntityIdentityKey]: BlueprintField;
|
|
17
20
|
};
|
|
21
|
+
/** The identity key a versioned entity adds: `version`, a positive integer. */
|
|
22
|
+
export type IEntityVersion = {
|
|
23
|
+
readonly version: typeof EntityVersion;
|
|
24
|
+
};
|
|
25
|
+
/** `Identity`, with `version` when `Versioned` is `true`. */
|
|
26
|
+
export type VersionedIdentity<Identity extends IEntityIdentity, Versioned extends boolean> = Versioned extends true ? Identity & IEntityVersion : Identity;
|
|
18
27
|
/** What an entity builder needs: the identity fields, and how to make an id and a timestamp. */
|
|
19
28
|
export interface EntityBuilderSettings<Identity extends IEntityIdentity, Catalogs extends readonly IRecordPluginCatalog[] = []> {
|
|
20
29
|
/** The blueprint of `id`, `createdAt`, `updatedAt` and `deletedAt`. */
|
|
@@ -23,6 +32,11 @@ export interface EntityBuilderSettings<Identity extends IEntityIdentity, Catalog
|
|
|
23
32
|
readonly generateId: () => string;
|
|
24
33
|
/** The current date and time, as the timestamps accept it. */
|
|
25
34
|
readonly now: () => string;
|
|
35
|
+
/**
|
|
36
|
+
* The field of `expiresAt`, a date and time, which entities declared with
|
|
37
|
+
* `expires: { after }` get; without it such a declaration throws.
|
|
38
|
+
*/
|
|
39
|
+
readonly expiresAt?: ExpiresAtField;
|
|
26
40
|
/**
|
|
27
41
|
* Catalogs of plugins, made by `recordPluginCatalogOf`, that
|
|
28
42
|
* `options.plugins` picks from, as `(ctx) => [ctx.publishable]`.
|
|
@@ -45,7 +59,55 @@ export type EntityProperties<Identity extends IEntityIdentity, Properties extend
|
|
|
45
59
|
*/
|
|
46
60
|
export type EntityInput<Identity extends IEntityIdentity, Properties extends IBlueprintCollection> = BlueprintInput<Properties> & Partial<BlueprintInput<Identity>>;
|
|
47
61
|
/** What `entityOf` takes, after the blueprint, to describe its keys. */
|
|
48
|
-
export type EntityOptions<Properties extends IBlueprintCollection, Change extends readonly RecordKey<Properties>[] = readonly RecordKey<Properties>[], Unique extends readonly RecordKey<Properties>[] = readonly RecordKey<Properties>[], Plugins extends readonly AnyRecordPlugin[] = readonly AnyRecordPlugin[], Context = unknown, Sensitive extends readonly RecordKey<Properties>[] = readonly RecordKey<Properties>[]> = RecordOptions<Properties, Change, Unique, Plugins, Context, Sensitive> & EntityEvents
|
|
62
|
+
export type EntityOptions<Properties extends IBlueprintCollection, Change extends readonly RecordKey<Properties>[] = readonly RecordKey<Properties>[], Unique extends readonly RecordKey<Properties>[] = readonly RecordKey<Properties>[], Plugins extends readonly AnyRecordPlugin[] = readonly AnyRecordPlugin[], Context = unknown, Sensitive extends readonly RecordKey<Properties>[] = readonly RecordKey<Properties>[], Expires extends EntityExpires<RecordKey<Properties>> | undefined = EntityExpires<RecordKey<Properties>> | undefined> = RecordOptions<Properties, Change, Unique, Plugins, Context, Sensitive> & EntityEvents & EntityStorageOptions<RecordKey<Properties>, Expires>;
|
|
63
|
+
/** How an entity is stored: optimistic concurrency and expiry. */
|
|
64
|
+
export interface EntityStorageOptions<Key extends string = string, Expires extends EntityExpires<Key> | undefined = EntityExpires<Key> | undefined> {
|
|
65
|
+
/**
|
|
66
|
+
* Adds a `version` identity key (1 when created) that the `update` of its
|
|
67
|
+
* repository checks and increments, throwing `RepositoryConcurrencyConflict`
|
|
68
|
+
* when the stored one differs: optimistic concurrency.
|
|
69
|
+
*/
|
|
70
|
+
readonly versioned?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* When records of the entity expire, which reads then leave out:
|
|
73
|
+
* `{ after: { days: 400 } }` from `createdAt`, kept in an `expiresAt`
|
|
74
|
+
* identity key, or `{ at: "until" }`, the date held by a key of the
|
|
75
|
+
* blueprint. A record with no date never expires.
|
|
76
|
+
*/
|
|
77
|
+
readonly expires?: Expires;
|
|
78
|
+
}
|
|
79
|
+
/** A duration, e.g. `{ days: 400 }`. */
|
|
80
|
+
export interface EntityDuration {
|
|
81
|
+
readonly days?: number;
|
|
82
|
+
readonly hours?: number;
|
|
83
|
+
readonly minutes?: number;
|
|
84
|
+
readonly seconds?: number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* `after` a duration from `createdAt` (stored as `expiresAt`), or `at` the
|
|
88
|
+
* date held by a key of the blueprint, `Key`.
|
|
89
|
+
*/
|
|
90
|
+
export type EntityExpires<Key extends string = string> = {
|
|
91
|
+
readonly after: EntityDuration;
|
|
92
|
+
} | {
|
|
93
|
+
readonly at: Key;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* The field `expiresAt` of the entities that expire `after` a duration: a
|
|
97
|
+
* required date and time, as `createdAt`.
|
|
98
|
+
*/
|
|
99
|
+
export type ExpiresAtField = PropertyClass<TString, string>;
|
|
100
|
+
/** `Identity`, with `expiresAt` when `Expires` is `{ after }`. */
|
|
101
|
+
export type ExpiringIdentity<Identity extends IEntityIdentity, Expires> = Expires extends {
|
|
102
|
+
readonly after: EntityDuration;
|
|
103
|
+
} ? Identity & {
|
|
104
|
+
readonly expiresAt: ExpiresAtField;
|
|
105
|
+
} : Identity;
|
|
106
|
+
/** What `entityOf` stores under `EntitySettings`. */
|
|
107
|
+
export interface EntityStorageSettings {
|
|
108
|
+
readonly versioned: boolean;
|
|
109
|
+
readonly expires?: EntityExpires;
|
|
110
|
+
}
|
|
49
111
|
/**
|
|
50
112
|
* The unique keys of an entity: `id`, those listed in `options.unique`, and
|
|
51
113
|
* those holding a property declared `unique`, as `Slug`.
|
|
@@ -86,4 +148,6 @@ export type EntityConstructor<Identity extends IEntityIdentity, Properties exten
|
|
|
86
148
|
* What `entityBuilderOf` returns: `entityOf`, declaring entities with the
|
|
87
149
|
* identity it was given, whose `options.plugins` picks from `Context`.
|
|
88
150
|
*/
|
|
89
|
-
export type EntityBuilder<Identity extends IEntityIdentity, Context = Record<never, never>> = <Properties extends IBlueprintCollection & Partial<Record<EntityIdentityKey, never>>, const Change extends readonly RecordKey<Properties>[] = [], const Unique extends readonly RecordKey<Properties>[] = [], const Plugins extends readonly AnyRecordPlugin[] = [], const Sensitive extends readonly RecordKey<Properties>[] = []>(blueprint: IBlueprint<Properties, boolean>, options?: EntityOptions<NoInfer<Properties>, Change, Unique, Plugins, Context, Sensitive
|
|
151
|
+
export type EntityBuilder<Identity extends IEntityIdentity, Context = Record<never, never>> = <Properties extends IBlueprintCollection & Partial<Record<EntityIdentityKey, never>>, const Change extends readonly RecordKey<Properties>[] = [], const Unique extends readonly RecordKey<Properties>[] = [], const Plugins extends readonly AnyRecordPlugin[] = [], const Sensitive extends readonly RecordKey<Properties>[] = [], const Versioned extends boolean = false, const Expires extends EntityExpires<RecordKey<Properties>> | undefined = undefined>(blueprint: IBlueprint<Properties, boolean>, options?: EntityOptions<NoInfer<Properties>, Change, Unique, Plugins, Context, Sensitive, Expires> & {
|
|
152
|
+
readonly versioned?: Versioned;
|
|
153
|
+
}) => EntityConstructor<VersionedIdentity<ExpiringIdentity<Identity, Expires>, Versioned>, WithPluginProperties<Properties, Plugins>, Change, readonly (Unique[number] | PluginUniqueKey<Plugins>)[], PluginMembers<Plugins>, Sensitive[number] | PluginSensitiveKey<Plugins>>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
declare const EntityVersion_base: import("../../index.js").PropertyClass<Type.TInteger, number>;
|
|
3
|
+
/**
|
|
4
|
+
* The `version` of a versioned entity: a positive integer, 1 when created,
|
|
5
|
+
* which each `update` of its repository checks and increments.
|
|
6
|
+
*/
|
|
7
|
+
export declare class EntityVersion extends EntityVersion_base {
|
|
8
|
+
}
|
|
9
|
+
/** The identity key a versioned entity adds after the others. */
|
|
10
|
+
export declare const VersionIdentity: import("../../index.js").IBlueprint<import("../property/types/property-options.interface.js").StoredFields<{
|
|
11
|
+
version: typeof EntityVersion;
|
|
12
|
+
}>, false>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EventPolicyDescription, EventPolicyFactory, EventPolicyOptions, EventPolicySettings, IEventPolicyCatalog } from "./types/event-policy.interface.js";
|
|
2
|
+
/**
|
|
3
|
+
* A factory of event policies for the `events` setting of `commandOf`:
|
|
4
|
+
* `events: publishEvents({ publisher: "realtime", topic: "posts/{id}" })`.
|
|
5
|
+
* Its options are JSON, checked by `validate` when the policy is made (an
|
|
6
|
+
* `InvalidEventPolicy` otherwise), so the policy can be written in an
|
|
7
|
+
* `afora.json` and an application describes it by name and options instead
|
|
8
|
+
* of `{ kind: "opaque" }`. `handle` gets the events of a call that succeeded,
|
|
9
|
+
* what a handler policy gets, and the options.
|
|
10
|
+
*/
|
|
11
|
+
export declare function eventPolicyOf<Options extends object = EventPolicyOptions>(settings: EventPolicySettings<Options>): EventPolicyFactory<Options>;
|
|
12
|
+
/**
|
|
13
|
+
* The name and options of a policy made by `eventPolicyOf`; `undefined` for
|
|
14
|
+
* any other value, such as a handler written by hand.
|
|
15
|
+
*/
|
|
16
|
+
export declare function eventPolicyDescriptionOf(handler: unknown): EventPolicyDescription | undefined;
|
|
17
|
+
/** The keys of the application `publishers` a policy uses; none for a handler written by hand. @internal */
|
|
18
|
+
export declare function eventPolicyPublishersOf(handler: unknown): readonly string[];
|
|
19
|
+
/**
|
|
20
|
+
* A catalog of event policy factories, by name: what resolves
|
|
21
|
+
* `{ "policy": name, "options": {...} }` back to a policy. Throws
|
|
22
|
+
* `InvalidEventPolicy` for a function that is not a factory made by
|
|
23
|
+
* `eventPolicyOf` or for two with the same name.
|
|
24
|
+
*/
|
|
25
|
+
export declare function eventPolicyCatalogOf(...policies: readonly EventPolicyFactory<never>[]): IEventPolicyCatalog;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** `eventPolicyOf`, a policy's options or a catalog got something it cannot use; `details.reason` says what. */
|
|
2
|
+
export declare const InvalidEventPolicy: import("../../index.js").DeclaredExceptionClass<{
|
|
3
|
+
readonly reason: string;
|
|
4
|
+
}>;
|
|
5
|
+
/**
|
|
6
|
+
* An event policy asked for a publisher that neither the application that
|
|
7
|
+
* bound the command nor the started application declares.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PublisherNotBound: import("../../index.js").DeclaredExceptionClass<{
|
|
10
|
+
readonly publisher: string;
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PublishEventsOptions } from "./types/event-policy.interface.js";
|
|
2
|
+
/**
|
|
3
|
+
* An event policy that publishes the events of a call to a publisher of the
|
|
4
|
+
* application, once the call succeeded (and committed):
|
|
5
|
+
* `events: publishEvents({ publisher: "realtime", topic: "posts/{id}" })`.
|
|
6
|
+
*
|
|
7
|
+
* For each event, in order, it runs the event's own handler first (unless
|
|
8
|
+
* `handle: false`), then publishes `{ name, command, operation, entity }`
|
|
9
|
+
* to the topic, the entity as JSON with sensitive values masked; the context
|
|
10
|
+
* and the variables are never published. `events` limits it to some names.
|
|
11
|
+
* A handler that fails stops the call with `CommandEventFailed`, as with
|
|
12
|
+
* `"after-commit"`. A publish that fails is logged and skipped
|
|
13
|
+
* (`onPublishError: "log"`, the default) or fails the call (`"throw"`). A
|
|
14
|
+
* publisher the application does not declare fails the call, caused by
|
|
15
|
+
* `PublisherNotBound`.
|
|
16
|
+
*/
|
|
17
|
+
export declare const publishEvents: import("./types/event-policy.interface.js").EventPolicyFactory<PublishEventsOptions>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IEventPublisher } from "./types/event-policy.interface.js";
|
|
2
|
+
/** The publishers of an application, by key. */
|
|
3
|
+
export type Publishers = Readonly<Record<string, IEventPublisher>>;
|
|
4
|
+
/**
|
|
5
|
+
* Carries the publishers of an application, when it declares any: on the
|
|
6
|
+
* application itself. @internal
|
|
7
|
+
*/
|
|
8
|
+
export declare const ApplicationPublishersKey: unique symbol;
|
|
9
|
+
/** Gives `command` the publishers of the application that bound it. @internal */
|
|
10
|
+
export declare function bindPublishers(command: object, publishers: Publishers): void;
|
|
11
|
+
/** Whether `value` can publish: an object with a `publish` function. @internal */
|
|
12
|
+
export declare function isEventPublisher(value: unknown): value is IEventPublisher;
|
|
13
|
+
/**
|
|
14
|
+
* The publisher `name` for a call of `command`: one of the application that
|
|
15
|
+
* bound the command, else one of the started application. Throws
|
|
16
|
+
* `PublisherNotBound` when neither declares it. @internal
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolvePublisher(command: object, name: string, source: string): IEventPublisher;
|