@afora/core 0.0.9 → 0.0.12
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 +62 -189
- package/dist/adapters.js +31 -55
- package/dist/core/application/application.d.ts +30 -0
- package/dist/core/application/application.js +1291 -0
- package/dist/core/application/exceptions.js +85 -0
- package/dist/core/application/migrate.js +67 -0
- package/dist/core/application/providers.js +108 -0
- package/dist/core/application/registry.js +23 -0
- package/dist/core/application/schema.js +398 -0
- package/dist/core/application/start.js +162 -0
- package/dist/core/application/types/application.interface.d.ts +23 -3
- package/dist/core/auth/jwt.js +200 -0
- package/dist/core/auth/types/jwt.interface.d.ts +10 -2
- package/dist/core/blueprint/blueprint-plugin.js +7 -0
- package/dist/core/blueprint/blueprint.js +146 -0
- package/dist/core/blueprint/callable.js +13 -0
- package/dist/core/blueprint/exceptions.js +20 -0
- package/dist/core/blueprint/is-blueprint.js +4 -0
- package/dist/core/blueprint/list.js +19 -0
- package/dist/core/blueprint/optional.js +69 -0
- package/dist/core/blueprint/record-field.js +14 -0
- package/dist/core/blueprint/slice.js +127 -0
- package/dist/core/blueprint/types/blueprint.interface.d.ts +16 -0
- package/dist/core/blueprint/variant.js +72 -0
- package/dist/core/command/command.js +428 -0
- package/dist/core/command/exceptions.js +30 -0
- package/dist/core/command/types/command.interface.d.ts +10 -1
- package/dist/core/entity/abstract-entity.d.ts +19 -0
- package/dist/core/entity/abstract-entity.js +181 -0
- package/dist/core/entity/entity.js +209 -0
- package/dist/core/entity/event.d.ts +39 -0
- package/dist/core/entity/event.js +80 -0
- package/dist/core/entity/exceptions.js +27 -0
- package/dist/core/entity/trust.js +7 -0
- package/dist/core/entity/version.js +13 -0
- package/dist/core/environment/environment.d.ts +26 -3
- package/dist/core/environment/environment.js +71 -0
- package/dist/core/event-policy/event-policy.js +76 -0
- package/dist/core/event-policy/exceptions.js +12 -0
- package/dist/core/event-policy/publish.js +98 -0
- package/dist/core/event-policy/publishers.js +18 -0
- package/dist/core/event-policy/types/event-policy.interface.d.ts +8 -2
- package/dist/core/exception/enums/exception-layers.js +15 -0
- package/dist/core/exception/exception.d.ts +15 -0
- package/dist/core/exception/exception.js +128 -0
- package/dist/core/exception/exported.js +49 -0
- package/dist/core/exception/guard.d.ts +20 -1
- package/dist/core/exception/guard.js +16 -0
- package/dist/core/exception/types/exception.interface.d.ts +13 -0
- package/dist/core/json.js +19 -0
- package/dist/core/logger/exceptions.js +7 -0
- package/dist/core/logger/logger.d.ts +14 -1
- package/dist/core/logger/logger.js +178 -0
- package/dist/core/openapi/exceptions.js +7 -0
- package/dist/core/openapi/openapi.js +132 -0
- package/dist/core/openapi/types/openapi.interface.d.ts +7 -1
- package/dist/core/output/access.js +66 -0
- package/dist/core/output/coerce.js +38 -0
- package/dist/core/output/exceptions.js +22 -0
- package/dist/{index-y280ew0k.js → core/output/http.js} +35 -80
- package/dist/core/output/openapi-sink.d.ts +10 -2
- package/dist/{index-hg1z2s2v.js → core/output/openapi-sink.js} +60 -544
- package/dist/core/output/output.js +192 -0
- package/dist/core/output/problem.js +30 -0
- package/dist/core/output/select.js +28 -0
- package/dist/core/output/stage.js +111 -0
- package/dist/core/output/status.js +52 -0
- package/dist/core/output/types/http.interface.d.ts +5 -1
- package/dist/core/output/types/output.interface.d.ts +24 -0
- package/dist/core/parse/issue.js +31 -0
- package/dist/core/parse/parse.d.ts +8 -1
- package/dist/core/parse/parse.js +323 -0
- package/dist/core/parse/strip.js +48 -0
- package/dist/core/property/abstract-property.js +37 -0
- package/dist/core/property/define-property.js +209 -0
- package/dist/core/property/enums/property-states.js +8 -0
- package/dist/core/property/exceptions.js +12 -0
- package/dist/core/property/input.js +10 -0
- package/dist/core/property/is-property.js +3 -0
- package/dist/core/property/lineage.js +8 -0
- package/dist/core/property/property.d.ts +27 -0
- package/dist/core/property/property.js +46 -0
- package/dist/core/property/refine.js +103 -0
- package/dist/core/record/abstract-record.d.ts +2 -1
- package/dist/core/record/abstract-record.js +307 -0
- package/dist/core/record/exceptions.js +49 -0
- package/dist/core/record/marks.js +7 -0
- package/dist/core/record/mask.js +60 -0
- package/dist/core/record/name.js +23 -0
- package/dist/core/record/plugin-catalog.js +115 -0
- package/dist/core/record/plugin.js +198 -0
- package/dist/core/record/record.js +117 -0
- package/dist/core/record/types/record.interface.d.ts +5 -0
- package/dist/core/repository/cursor.js +83 -0
- package/dist/core/repository/exceptions.js +72 -0
- package/dist/core/repository/in-memory.js +196 -0
- package/dist/core/repository/migrate.js +254 -0
- package/dist/core/repository/plan.js +27 -0
- package/dist/core/repository/provider.js +29 -0
- package/dist/core/repository/query.js +153 -0
- package/dist/core/repository/references.js +196 -0
- package/dist/core/repository/repository.d.ts +22 -2
- package/dist/core/repository/repository.js +720 -0
- package/dist/core/repository/state.js +138 -0
- package/dist/core/repository/types/query.interface.d.ts +13 -0
- package/dist/core/repository/types/repository.interface.d.ts +20 -8
- package/dist/core/repository/types/schema.interface.d.ts +8 -2
- package/dist/core/repository/upcast.js +179 -0
- package/dist/core/schema/schema.d.ts +9 -0
- package/dist/core/schema/schema.js +542 -0
- package/dist/core/standard/standard-schema.js +1 -0
- package/dist/core/standard/standard.js +23 -0
- package/dist/core/symbols/demo.d.ts +4 -0
- package/dist/core/symbols/demo.js +1 -0
- package/dist/core/symbols/kind.js +1 -0
- package/dist/core/symbols/meta.js +1 -0
- package/dist/core/symbols/state.d.ts +5 -0
- package/dist/core/symbols/state.js +1 -0
- package/dist/core/tracing/exceptions.d.ts +4 -0
- package/dist/core/tracing/exceptions.js +7 -0
- package/dist/core/tracing/tracing.d.ts +72 -0
- package/dist/core/tracing/tracing.js +101 -0
- package/dist/core/use-case/access.js +56 -0
- package/dist/core/use-case/decorators.js +65 -0
- package/dist/core/use-case/exceptions.d.ts +5 -1
- package/dist/core/use-case/exceptions.js +48 -0
- package/dist/core/use-case/plugin-catalog.js +68 -0
- package/dist/core/use-case/plugin.js +133 -0
- package/dist/core/use-case/route.js +200 -0
- package/dist/core/use-case/scope.d.ts +5 -0
- package/dist/core/use-case/scope.js +4 -0
- package/dist/core/use-case/steps/describe.d.ts +5 -1
- package/dist/core/use-case/steps/describe.js +24 -0
- package/dist/core/use-case/steps/references.js +105 -0
- package/dist/core/use-case/steps/run.js +139 -0
- package/dist/core/use-case/steps/types/check.interface.d.ts +27 -8
- package/dist/core/use-case/steps/types/steps.interface.d.ts +8 -1
- package/dist/core/use-case/steps/validate.js +368 -0
- package/dist/core/use-case/target.js +100 -0
- package/dist/core/use-case/types/route.interface.d.ts +25 -5
- package/dist/core/use-case/use-case.d.ts +77 -2
- package/dist/core/use-case/use-case.js +493 -0
- package/dist/experimental.d.ts +5 -0
- package/dist/experimental.js +18 -161
- package/dist/index.d.ts +7 -5
- package/dist/index.js +213 -367
- package/dist/std/blueprint-plugin.js +42 -0
- package/dist/std/blueprint.d.ts +21 -0
- package/dist/std/blueprint.js +3 -0
- package/dist/std/command.js +3 -0
- package/dist/std/entity.d.ts +20 -1
- package/dist/std/entity.js +30 -0
- package/dist/std/event-policies.js +3 -0
- package/dist/std/properties/contact.d.ts +3 -0
- package/dist/std/properties/contact.js +29 -0
- package/dist/std/properties/enum.js +26 -0
- package/dist/std/properties/exceptions.js +14 -0
- package/dist/std/properties/headless.js +45 -0
- package/dist/std/properties/identity.js +28 -0
- package/dist/std/properties/json.js +161 -0
- package/dist/std/properties/password.js +79 -0
- package/dist/std/properties/primitives.d.ts +4 -0
- package/dist/std/properties/primitives.js +28 -0
- package/dist/std/properties/time.js +55 -0
- package/dist/std/record-plugins.js +73 -0
- package/dist/std/record.d.ts +20 -1
- package/dist/std/record.js +3 -0
- package/dist/std/resource.js +39 -0
- package/dist/std/runtime.js +193 -0
- package/dist/std/stages.js +4 -0
- package/dist/std/use-cases/auth.js +193 -0
- package/dist/std/use-cases/crud.js +261 -0
- package/dist/std/use-cases/index.js +12 -0
- package/dist/std/use-cases/lifecycle.js +97 -0
- package/dist/std/use-cases/shared.js +232 -0
- package/dist/std/use-cases/types.d.ts +5 -1
- package/dist/std/use-cases/types.js +0 -0
- package/dist/testing/http-provider-contract.js +344 -0
- package/dist/testing/index.js +3 -1435
- package/dist/testing/memory-publisher.js +12 -0
- package/dist/testing/provider-contract.d.ts +12 -3
- package/dist/testing/provider-contract.js +1071 -0
- package/package.json +17 -5
- package/dist/index-3aae5jfq.js +0 -2997
- package/dist/index-ny3as79r.js +0 -4567
- package/dist/index-xegv6a1p.js +0 -4291
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<picture>
|
|
3
3
|
<source media="(prefers-color-scheme: dark)" srcset=".github/assets/afora-banner-dark.png">
|
|
4
|
-
<img alt="afora —
|
|
4
|
+
<img alt="afora — Simple to write. Built to hold." src=".github/assets/afora-banner-light.png" width="100%">
|
|
5
5
|
</picture>
|
|
6
6
|
</p>
|
|
7
7
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<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>
|
|
11
11
|
<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>
|
|
12
12
|
<a href="https://standardschema.dev"><img alt="Standard Schema V1" src="https://img.shields.io/badge/Standard%20Schema-V1-3178c6"></a>
|
|
13
|
-
<img alt="Bun >= 1.4.4 and Node >= 22" src="https://img.shields.io/badge/runtime-Bun%20%
|
|
13
|
+
<img alt="Bun >= 1.4.4 and Node >= 22" src="https://img.shields.io/badge/runtime-Bun%20%3E%3D%201.4.4%20%7C%20Node%20%3E%3D%2022-555">
|
|
14
14
|
<a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/github/license/afora-framework/core"></a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
@@ -23,47 +23,42 @@
|
|
|
23
23
|
<a href="https://github.com/afora-framework/core/discussions">Discussions</a>
|
|
24
24
|
</p>
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
<h3 align="center">Simple to write. Built to hold.</h3>
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
**afora is a domain-first framework for TypeScript backends.** You declare what your entities, use cases and API are, and the runtime keeps the guarantees that are easy to forget: secrets stay masked, unknown fields are rejected, unique keys hold and business errors get the right status. Whoever writes the code, you or your agent, it holds.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
bun add @afora/core @afora/hono hono typebox && bun add -d @afora/codegen
|
|
32
|
-
bunx afora-codegen generate afora.json --stages ./stages.ts # writes src/domain/*.ts
|
|
33
|
-
bun src/server.ts
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
With the [quick start's `afora.json`](#the-same-app-as-an-aforajson) and three small files written by hand (the hashing function, the `hono` stage catalog and the server, shown there), that is the whole API: a `User` with a masked password and a unique email, a use case written as steps, problem+json errors and an OpenAPI document. The same app written by hand in TypeScript is the [quick start](#quick-start), and the [afora.json guide](docs/afora-json.md) covers the whole spec.
|
|
30
|
+
Model the domain; the runtime keeps the invariants. Built on [TypeBox](https://github.com/sinclairzx81/typebox), with [Standard Schema](https://standardschema.dev) on every field, on Bun and Node.
|
|
37
31
|
|
|
38
32
|
## Why afora
|
|
39
33
|
|
|
40
|
-
An HTTP backend written by an agent (or in a hurry) tends to break the same few guarantees. In afora each one is
|
|
34
|
+
An HTTP backend written by an agent (or in a hurry) tends to break the same few guarantees. In afora each one is part of the runtime, not a convention the code has to remember:
|
|
41
35
|
|
|
42
|
-
| What usually breaks | What afora does
|
|
43
|
-
| ---------------------------------------------- |
|
|
44
|
-
| A password hash or token ends up in a response | A
|
|
45
|
-
| Extra fields in a request body are accepted |
|
|
46
|
-
| A business error is returned as a 500 |
|
|
47
|
-
| A duplicate email is written twice | `unique` keys are enforced by the repository
|
|
36
|
+
| What usually breaks | What afora does |
|
|
37
|
+
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
38
|
+
| A password hash or token ends up in a response | A `sensitive` property is masked by `toJSON()` at any depth, so no serializer can leak it ([records](docs/records.md)). |
|
|
39
|
+
| Extra fields in a request body are accepted | Undeclared keys are rejected at any depth, and the API answers 400 ([Standard Schema](docs/standard-schema.md)). |
|
|
40
|
+
| A business error is returned as a 500 | Errors know their layer and become problem+json with the right status ([outputs](docs/outputs.md)). |
|
|
41
|
+
| A duplicate email is written twice | `unique` keys are enforced by the repository, and a violation is a 409 ([repositories](docs/repositories.md)). |
|
|
42
|
+
| One user reads or changes another user's data | A use case declares its `access` (scopes, owner), and every output enforces it before it runs ([access](docs/auth.md)). |
|
|
48
43
|
|
|
49
|
-
[`examples/agent-built`](examples/agent-built/README.md) checks these
|
|
44
|
+
[`examples/agent-built`](examples/agent-built/README.md) checks these five on the running API of a ticketing backend an agent built through the MCP server, next to a Hono + Zod baseline written from the same brief. Both hold them; the afora one wrote no code for any of them.
|
|
50
45
|
|
|
51
|
-
|
|
46
|
+
## For you and your agent
|
|
52
47
|
|
|
53
|
-
|
|
48
|
+
**Developer experience**
|
|
54
49
|
|
|
55
|
-
- **
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
58
|
-
- **
|
|
59
|
-
- **
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- **
|
|
64
|
-
- **
|
|
65
|
-
- **
|
|
66
|
-
- **
|
|
50
|
+
- **Four declarations to a CRUD API:** an entity, a repository, a command and an output ([overview](docs/README.md)).
|
|
51
|
+
- **Types that follow the model:** inputs and responses are inferred, `findByEmail` exists because `email` is unique, and `user.change.password()` because you allowed it.
|
|
52
|
+
- **Fits what you already use:** every field is a Standard Schema, so Hono, tRPC and TanStack Form take it as is.
|
|
53
|
+
- **Any server, any store:** [Hono](packages/hono/README.md), [Fastify](packages/fastify/README.md), [Express](packages/express/README.md) or [Elysia](packages/elysia/README.md); in memory, [Drizzle](packages/drizzle/README.md) or [DynamoDB](packages/dynamodb/README.md).
|
|
54
|
+
- **Errors you can read:** each one names its layer and carries the trace of the layers it crossed ([exceptions](docs/exceptions.md)).
|
|
55
|
+
|
|
56
|
+
**Agent experience**
|
|
57
|
+
|
|
58
|
+
- **The application is data:** an `afora.json` declares the entities, the use cases (as steps), the commands and the HTTP API ([guide](docs/afora-json.md)).
|
|
59
|
+
- **An MCP server to write it:** [`@afora/mcp`](packages/mcp/README.md) lets an agent read the catalog, edit and validate the spec, generate the code and see its TypeScript diagnostics.
|
|
60
|
+
- **Code you review:** [`@afora/codegen`](packages/codegen/README.md) turns the spec into the TypeScript you would write by hand, committed; `extractSpec(app)` gives the spec back from code written by hand.
|
|
61
|
+
- **The runtime holds what the agent forgets:** the five rows above, with no code written for them. `examples/blog` is a whole API generated from one `afora.json`.
|
|
67
62
|
|
|
68
63
|
## Install
|
|
69
64
|
|
|
@@ -71,24 +66,11 @@ afora is not a general-purpose validator. It models a domain on [TypeBox](https:
|
|
|
71
66
|
bun add @afora/core typebox # or: npm install @afora/core typebox
|
|
72
67
|
```
|
|
73
68
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
`Password` hashes with argon2id: with `Bun.password` on Bun, with native `node:crypto` argon2 on a Node that has it (24.7+), and otherwise it throws `PasswordHasherRequired` until you call `configure({ hasher })`. `Id.generate()` makes a portable, monotonic UUID v7 on any runtime. afora imports no `node:crypto` (or `Buffer`) at load, so it loads in browsers and Workers; the native hasher is looked up on first use through `process.getBuiltinModule` (Node 22.3+). The native hasher derives argon2 version 19 only; a hash of another version throws `UnsupportedPasswordHash` (verify it with `Bun.password`, or `configure({ hasher })`).
|
|
77
|
-
|
|
78
|
-
```ts
|
|
79
|
-
import { configure, type PasswordHasher } from "@afora/core";
|
|
80
|
-
|
|
81
|
-
const hasher: PasswordHasher = {
|
|
82
|
-
hash: (plain) => myArgon2.hash(plain),
|
|
83
|
-
verify: (plain, hash) => myArgon2.verify(plain, hash),
|
|
84
|
-
verifySync: (plain, hash) => myArgon2.verifySync(plain, hash), // optional; `Password.verifySync` throws without it
|
|
85
|
-
};
|
|
86
|
-
configure({ hasher, idGenerator: () => myUuidV7() }); // both optional; `resetRuntime()` restores the defaults
|
|
87
|
-
```
|
|
69
|
+
Bun >= 1.4.4 or Node.js >= 22; it also loads in browsers and Workers. TypeBox (`typebox`, `^1`) is a peer dependency. How passwords are hashed and ids made on each runtime, and how to replace them with `configure`: [Runtime](docs/runtime.md).
|
|
88
70
|
|
|
89
71
|
## Quick start
|
|
90
72
|
|
|
91
|
-
A `User`
|
|
73
|
+
A `User` with a masked password and a unique email, a use case written as steps, a transaction, and an HTTP API with an OpenAPI document. Install what it uses:
|
|
92
74
|
|
|
93
75
|
```bash
|
|
94
76
|
bun add @afora/core @afora/hono hono typebox
|
|
@@ -148,6 +130,7 @@ class Register extends useCaseOf({
|
|
|
148
130
|
response: blueprintOf(() => ({ user: User })),
|
|
149
131
|
dependencies: { users: Users },
|
|
150
132
|
errors: [UserAlreadyExists],
|
|
133
|
+
strictSteps: true, // TypeScript checks every reference, arg and the return
|
|
151
134
|
steps: [
|
|
152
135
|
{ find: "users.findByEmail", args: ["$input.email"], as: "existing" },
|
|
153
136
|
{ ensure: "absent", value: "$existing", error: UserAlreadyExists },
|
|
@@ -155,6 +138,7 @@ class Register extends useCaseOf({
|
|
|
155
138
|
run: "hashPassword",
|
|
156
139
|
fn: hashPassword,
|
|
157
140
|
args: ["$input.plainPassword"],
|
|
141
|
+
params: blueprintOf((ctx) => ({ plain: ctx.string })),
|
|
158
142
|
as: "hash",
|
|
159
143
|
},
|
|
160
144
|
{
|
|
@@ -212,7 +196,12 @@ GET /openapi.json -> the OpenAPI 3.1 document of the route
|
|
|
212
196
|
|
|
213
197
|
### The same app as an afora.json
|
|
214
198
|
|
|
215
|
-
Everything above
|
|
199
|
+
Everything above except the hashing function is data, so it can be written as an `afora.json`. `@afora/codegen` turns it into the same TypeScript, committed and reviewable, and `@afora/mcp` lets an agent write it.
|
|
200
|
+
|
|
201
|
+
<details>
|
|
202
|
+
<summary>The <code>afora.json</code>, the three hand-written files and the commands</summary>
|
|
203
|
+
|
|
204
|
+
This is `examples/interop/quickstart.afora.json`, which the same test validates and generates.
|
|
216
205
|
|
|
217
206
|
```json
|
|
218
207
|
{
|
|
@@ -264,6 +253,7 @@ Everything above, except the hashing function, is data, so it can be written as
|
|
|
264
253
|
"run": "hashPassword",
|
|
265
254
|
"fn": { "import": "../hash-password.ts#hashPassword" },
|
|
266
255
|
"args": ["$input.plainPassword"],
|
|
256
|
+
"params": { "fields": { "plain": "string" } },
|
|
267
257
|
"as": "hash"
|
|
268
258
|
},
|
|
269
259
|
{
|
|
@@ -328,6 +318,7 @@ bunx afora-codegen generate afora.json --stages ./stages.ts # writes src/domai
|
|
|
328
318
|
|
|
329
319
|
and serve the application from `src/server.ts` (`bun src/server.ts`):
|
|
330
320
|
|
|
321
|
+
<!-- docs:code skip: src/server.ts of examples/interop/from-json; ./domain is what afora-codegen generates from the afora.json above -->
|
|
331
322
|
```ts
|
|
332
323
|
import { App } from "./domain/index.ts";
|
|
333
324
|
|
|
@@ -341,164 +332,46 @@ These three files are `examples/interop/from-json`, which `bun run test:interop`
|
|
|
341
332
|
|
|
342
333
|
`generate` writes one file per kind (`entities.ts`, `repositories.ts`, `use-cases.ts`, `commands.ts`, `outputs.ts`...), an `application.ts` and an `index.ts`, each starting with a banner saying not to edit it. `afora-codegen extract` goes the other way and gives the `afora.json` of an `Application`. The guide is in [docs/afora-json.md](docs/afora-json.md).
|
|
343
334
|
|
|
344
|
-
|
|
335
|
+
</details>
|
|
345
336
|
|
|
346
|
-
|
|
337
|
+
## Learn more
|
|
347
338
|
|
|
348
|
-
- [
|
|
349
|
-
- [
|
|
350
|
-
- [
|
|
351
|
-
- [
|
|
352
|
-
- [Repositories](docs/repositories.md): typed async methods for an entity, queries, soft deletion, providers, transactions and relations.
|
|
353
|
-
- [Schema evolution](docs/schema-evolution.md): `fill`, `renamedFrom`, `schemaVersion` with `upgrade` steps and `previousNames`, so old rows are still read; `app.migrate` to rewrite them, and a schema lock checked in CI and on `start`.
|
|
354
|
-
- [Use cases](docs/use-cases.md): the Command pattern with method injection, written as code or as steps.
|
|
355
|
-
- [Commands](docs/commands.md): use cases under operation names, with a context, a transaction and an event policy.
|
|
356
|
-
- [Outputs](docs/outputs.md): commands through stages into a sink, such as an HTTP API or an OpenAPI document.
|
|
357
|
-
- [Applications](docs/applications.md): a named, checked manifest of every declaration, and `describe()`.
|
|
358
|
-
- [Exceptions](docs/exceptions.md): errors that know their layer and keep a trace.
|
|
359
|
-
- [Environments](docs/environments.md): validated environment variables.
|
|
360
|
-
- [Standard Schema](docs/standard-schema.md), [OpenAPI](docs/openapi.md) and the [afora.json guide](docs/afora-json.md).
|
|
361
|
-
|
|
362
|
-
## Standard Schema
|
|
363
|
-
|
|
364
|
-
Every field (a property, blueprint, list or record class) implements Standard Schema V1 (`vendor: "afora"`), so Hono, tRPC, TanStack Form and any other library that takes one takes it as is. `validate` is synchronous, never throws for an invalid value and reports every issue with its `path`. Without a framework, `safeParse` returns `{ value }` or `{ issues }`. See [docs/standard-schema.md](docs/standard-schema.md); the real-library tests are in `examples/interop`.
|
|
339
|
+
- [Tutorial](docs/tutorial.md): an API built step by step, and [coming from Hono + Zod](docs/coming-from-hono-zod.md).
|
|
340
|
+
- [Concepts](docs/README.md), in the order they build on one another: property → blueprint → record → entity → repository → use case → command → output → application.
|
|
341
|
+
- [The afora.json guide](docs/afora-json.md) and the [MCP server](packages/mcp/README.md).
|
|
342
|
+
- [API reference](docs/reference/api.md), [FAQ](docs/faq.md) and [troubleshooting](docs/troubleshooting.md).
|
|
365
343
|
|
|
366
344
|
## Packages
|
|
367
345
|
|
|
368
|
-
| Package | What it does
|
|
369
|
-
| ---------------------------------------------------------- |
|
|
370
|
-
| [`@afora/core`](https://www.npmjs.com/package/@afora/core) |
|
|
371
|
-
| [`@afora/codegen`](packages/codegen/README.md) |
|
|
372
|
-
| [`@afora/mcp`](packages/mcp/README.md) | An MCP server that lets an agent build
|
|
373
|
-
| [`@afora/hono`](packages/hono/README.md) |
|
|
374
|
-
| [`@afora/fastify`](packages/fastify/README.md) |
|
|
375
|
-
| [`@afora/express`](packages/express/README.md) |
|
|
376
|
-
| [`@afora/elysia`](packages/elysia/README.md) |
|
|
377
|
-
| [`@afora/ws`](packages/ws/README.md) |
|
|
378
|
-
| [`@afora/drizzle`](packages/drizzle/README.md) |
|
|
379
|
-
| [`@afora/dynamodb`](packages/dynamodb/README.md) |
|
|
380
|
-
|
|
381
|
-
## Example
|
|
382
|
-
|
|
383
|
-
`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`.
|
|
384
|
-
|
|
385
|
-
`examples/agent-built` is an event ticketing API (organizers, events with a capacity, tickets) built only through the `@afora/mcp` tools from a written brief, with the tool calls committed and replayed byte for byte, and an evaluation of four invariants on its running API and on a Hono + Zod baseline: `bun run test:agent-built`. See [its write-up](examples/agent-built/README.md).
|
|
346
|
+
| Package | What it does |
|
|
347
|
+
| ---------------------------------------------------------- | ----------------------------------------------------------------- |
|
|
348
|
+
| [`@afora/core`](https://www.npmjs.com/package/@afora/core) | The framework: from properties to outputs (this repository). |
|
|
349
|
+
| [`@afora/codegen`](packages/codegen/README.md) | `afora.json` to TypeScript, and an application back to its spec. |
|
|
350
|
+
| [`@afora/mcp`](packages/mcp/README.md) | An MCP server that lets an agent build the domain. |
|
|
351
|
+
| [`@afora/hono`](packages/hono/README.md) | Serves the operations on Hono, with an optional OpenAPI document. |
|
|
352
|
+
| [`@afora/fastify`](packages/fastify/README.md) | The same on Fastify. |
|
|
353
|
+
| [`@afora/express`](packages/express/README.md) | The same on Express 5. |
|
|
354
|
+
| [`@afora/elysia`](packages/elysia/README.md) | The same on Elysia. |
|
|
355
|
+
| [`@afora/ws`](packages/ws/README.md) | Operations and domain events over WebSocket. |
|
|
356
|
+
| [`@afora/drizzle`](packages/drizzle/README.md) | Repositories on Drizzle ORM (SQLite and Postgres). |
|
|
357
|
+
| [`@afora/dynamodb`](packages/dynamodb/README.md) | Repositories on one DynamoDB table. |
|
|
358
|
+
| [`@afora/otel`](packages/otel/README.md) | OpenTelemetry spans per command, use case and repository call. |
|
|
386
359
|
|
|
387
360
|
## Performance
|
|
388
361
|
|
|
389
|
-
|
|
362
|
+
afora validates and transforms faster than Zod 4 on most of the fourteen measured cases; checkers that compile and do not transform (TypeBox, Ajv, Typia) are faster still. The method, every case and the trade-offs are in [Performance](docs/performance.md).
|
|
390
363
|
|
|
391
364
|
<!-- bench:start -->
|
|
392
365
|
|
|
393
366
|

|
|
394
367
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
| Node 24 | afora | TypeBox | Ajv | Typia | Zod | Valibot | ArkType | Effect Schema | Yup | Joi |
|
|
398
|
-
| -------------------------------------------------------- | -------: | ------: | ----: | ----: | ----: | ------: | ------: | ------------: | -----: | -----: |
|
|
399
|
-
| Small flat object, valid | 299.5 ns | 0.13x | 0.14x | 0.15x | 1.23x | 1.97x | 10.30x | 2.74x | 12.44x | 8.25x |
|
|
400
|
-
| Small flat object, invalid | 474.6 ns | 9.50x | 0.13x | 0.58x | 3.22x | 2.71x | 27.57x | 4.04x | 68.43x | 7.66x |
|
|
401
|
-
| Nested user (address, 5 tags max, 10 addresses), valid | 2036 ns | 0.12x | 0.17x | 0.13x | 1.27x | 1.02x | 14.85x | 3.24x | 22.52x | 6.67x |
|
|
402
|
-
| Nested user (address, 5 tags max, 10 addresses), invalid | 2688 ns | 9.83x | 0.15x | 0.83x | 1.84x | 1.21x | 21.43x | 3.46x | 38.49x | 6.84x |
|
|
403
|
-
| API request body (an order with 5 items), valid | 2518 ns | 0.35x | 0.35x | 0.24x | 1.21x | 1.16x | 9.55x | 2.46x | 17.06x | 5.63x |
|
|
404
|
-
| API request body (an order with 5 items), invalid | 3540 ns | 5.17x | 0.12x | 0.86x | 1.90x | 1.10x | 14.67x | 2.50x | 30.63x | 5.11x |
|
|
405
|
-
| List of 1,000 items, valid | 152.4 µs | 0.06x | 0.10x | 0.06x | 0.67x | 2.34x | 20.61x | 3.96x | 27.77x | 7.40x |
|
|
406
|
-
| List of 1,000 items, invalid | 155.5 µs | 20.58x | 0.10x | 0.18x | 0.72x | 2.35x | 26.62x | 3.70x | 28.82x | 7.43x |
|
|
407
|
-
| Union of 4 variants (12 events), valid | 579.7 ns | 0.47x | 0.59x | 0.51x | 3.90x | 14.54x | 1.54x | 13.42x | 91.04x | 94.11x |
|
|
408
|
-
| Union of 4 variants (12 events), invalid | 55.0 µs | 0.64x | 0.01x | 0.03x | 0.09x | 0.19x | 0.34x | 0.17x | 1.44x | 1.69x |
|
|
409
|
-
| 6 levels of nesting, valid | 829.3 ns | 0.06x | 0.09x | 0.06x | 0.78x | 1.13x | 14.16x | 4.12x | 19.31x | 5.65x |
|
|
410
|
-
| 6 levels of nesting, invalid | 971.4 ns | 21.55x | 0.10x | 1.69x | 1.99x | 1.72x | 27.50x | 5.35x | 52.46x | 7.37x |
|
|
411
|
-
| Small flat object, checks only, valid | 177.6 ns | 0.23x | 0.24x | 0.25x | 0.82x | 2.68x | 0.44x | 2.34x | 18.10x | 12.73x |
|
|
412
|
-
| Small flat object, checks only, invalid | 424.3 ns | 10.48x | 0.15x | 0.68x | 2.94x | 2.85x | 22.80x | 3.92x | 74.92x | 8.39x |
|
|
413
|
-
|
|
414
|
-
| Bun | afora | TypeBox | Ajv | Typia | Zod | Valibot | ArkType | Effect Schema | Yup | Joi |
|
|
415
|
-
| -------------------------------------------------------- | -------: | ------: | ----: | ----: | ----: | ------: | ------: | ------------: | -----: | -----: |
|
|
416
|
-
| Small flat object, valid | 195.7 ns | 0.15x | 0.18x | 0.17x | 1.63x | 0.95x | 10.95x | 2.58x | 22.27x | 10.44x |
|
|
417
|
-
| Small flat object, invalid | 426.1 ns | 7.87x | 0.21x | 1.26x | 1.62x | 0.97x | 16.14x | 2.50x | 39.07x | 8.12x |
|
|
418
|
-
| Nested user (address, 5 tags max, 10 addresses), valid | 3154 ns | 0.05x | 0.08x | 0.03x | 0.93x | 0.41x | 4.73x | 1.35x | 16.66x | 3.81x |
|
|
419
|
-
| Nested user (address, 5 tags max, 10 addresses), invalid | 2260 ns | 8.15x | 0.16x | 0.99x | 1.64x | 0.91x | 13.57x | 2.40x | 39.16x | 6.88x |
|
|
420
|
-
| API request body (an order with 5 items), valid | 2355 ns | 0.19x | 0.21x | 0.32x | 1.34x | 0.68x | 5.08x | 1.83x | 21.04x | 4.97x |
|
|
421
|
-
| API request body (an order with 5 items), invalid | 2456 ns | 5.33x | 0.13x | 1.13x | 2.06x | 0.97x | 11.38x | 2.24x | 38.34x | 7.81x |
|
|
422
|
-
| List of 1,000 items, valid | 185.7 µs | 0.02x | 0.05x | 0.01x | 1.05x | 0.45x | 7.61x | 1.53x | 29.36x | 5.07x |
|
|
423
|
-
| List of 1,000 items, invalid | 92.7 µs | 23.63x | 0.12x | 0.45x | 2.25x | 0.96x | 23.45x | 3.00x | 65.59x | 11.20x |
|
|
424
|
-
| Union of 4 variants (12 events), valid | 1627 ns | 0.11x | 0.14x | 0.20x | 1.83x | 1.97x | 0.26x | 2.06x | 39.17x | 27.28x |
|
|
425
|
-
| Union of 4 variants (12 events), invalid | 41.8 µs | 0.64x | 0.01x | 0.04x | 0.10x | 0.11x | 0.21x | 0.11x | 1.81x | 2.01x |
|
|
426
|
-
| 6 levels of nesting, valid | 528.2 ns | 0.02x | 0.10x | 0.06x | 1.98x | 1.54x | 11.94x | 2.87x | 39.56x | 7.63x |
|
|
427
|
-
| 6 levels of nesting, invalid | 838.9 ns | 17.86x | 0.09x | 1.60x | 1.74x | 1.59x | 18.13x | 3.09x | 53.04x | 7.92x |
|
|
428
|
-
| Small flat object, checks only, valid | 110.1 ns | 0.28x | 0.35x | 0.29x | 1.83x | 0.92x | 0.34x | 1.91x | 33.82x | 15.15x |
|
|
429
|
-
| Small flat object, checks only, invalid | 414.7 ns | 7.74x | 0.21x | 1.25x | 1.52x | 0.86x | 11.63x | 2.06x | 39.99x | 7.60x |
|
|
368
|
+
afora 0.0.11 on Bun 1.4.4, Node v22.23.4 and Node v24.22.2, AMD EPYC 9V74 80-Core Processor; generated from `bench/results/latest.json`. Every case, runtime and library, and how it is measured: [Performance](docs/performance.md).
|
|
430
369
|
|
|
431
370
|
<!-- bench:end -->
|
|
432
371
|
|
|
433
|
-
What the table says, for `blueprint.safeParse`, on a GitHub runner (AMD EPYC, Linux, Bun 1.4.4 and Node 24). The table is of afora at commit `8c4395e`; what changed after it is said where it applies.
|
|
434
|
-
|
|
435
|
-
- **Against Zod 4.** On Bun, faster in 12 of the 14 cases, by 1.05x to 2.25x; Zod 4 is ahead on the valid nested object (7%). On Node, faster in 9 of the 14, by 1.2x to 3.9x; Zod 4 is ahead on the list of 1,000 items (1.4x to 1.5x), on the valid deep object (1.3x) and on the valid flat object with nothing to transform (1.2x). The invalid union is told apart below.
|
|
436
|
-
- **Against Valibot.** On Node afora is faster in 13 of the 14 cases, by 1.02x to 2.9x outside the valid union (14.5x). On Bun it is the other way: Valibot is ahead in 11 of them, within 15% on small objects and by 1.5x to 2.4x on the valid nested object, the valid API body and the valid list, and afora is ahead on the valid union (2x) and the deep object (1.5x to 1.6x).
|
|
437
|
-
- **Against Effect Schema, Joi and Yup.** Faster in every case but the invalid union: 1.35x to 5.4x Effect Schema, 3.8x to 15x Joi and 12x to 75x Yup, and more than that on the valid union (up to 13x, 94x and 91x).
|
|
438
|
-
- **Against ArkType.** Faster wherever a value is transformed or rejected, by 4.7x to 28x, because its morphs and its errors take a slower path. With nothing to transform and a valid value, ArkType is 2.3x to 3x faster than afora, and 3.8x on the valid union on Bun.
|
|
439
|
-
- **An invalid union.** In the table it takes about 10x the time of Zod 4: TypeBox `Errors` reported every variant of the union for each rejected value. Since then a property whose schema is a discriminated union tells a rejected value by the variant it names, without `Errors`, and takes about 0.5x the time of Zod 4 on Bun (measured on an Apple M2; the table follows at the next measurement). Valid values of the same union are 1.8x to 3.9x faster than Zod 4.
|
|
440
|
-
- **Floor.** The checkers that compile and do not transform (TypeBox, Ajv, Typia) are 3x to 9x faster than afora on a valid small object, and 10x to 70x on the larger ones. afora builds on TypeBox. On invalid input afora is 5x to 24x faster than TypeBox `Errors` (outside the union), and Ajv stays 5x to 11x ahead.
|
|
441
|
-
- **Records and entities.** `Record.safeParse`, which also creates the instance, takes 1.0x to 2.1x the time of `blueprint.safeParse` on Bun and up to 4x on Node. `Entity.safeParse` also makes an id and the time stamps, which on a small object is most of its time: 5x to 13x `blueprint.safeParse` on the flat object in the table, 1.0x to 4.7x on the larger ones; since then it takes about three quarters of that on a small object, for not checking again the id and the date it has just made. `new Record(value)` throws on invalid input, which costs an exception with its trace (2x to 6x `blueprint.safeParse` on Bun, 2.5x to 13x on Node); use `safeParse` where invalid input is expected.
|
|
442
|
-
- **Bundle and cold start.** afora is a framework with one entry point: validating one object bundles to 100 kB gzipped (Zod 4: 24 kB, Valibot: 1.7 kB) and takes 40 ms to load, build and run once in a new Node process (85 ms on Bun; Zod 4: 7 to 8 ms).
|
|
443
|
-
|
|
444
|
-
What afora does that the fast rows do not all do: a pipeline per property (`preHandle`, schema check, `afterHandle`), where a property with a handler has the shape of the value checked first, so handlers only see the right types; objects walked key by key instead of one compiled function; frozen results; every issue with `code`, `path` and `source`; and, for `Record.safeParse` and `new Record`, creating the instance. If you validate bulk data in a hot path and need none of that, a compiled checker alone is an order of magnitude faster.
|
|
445
|
-
|
|
446
|
-
CI keeps it there: `bun run bench:guard` times `blueprint.safeParse` and Zod 4 `safeParse` on the fourteen cases and fails when afora is over the limit of a case as a multiple of Zod 4 (1.15x unless `bench/guard.limits.ts` states another, with the reason).
|
|
447
|
-
|
|
448
|
-
## Comparison
|
|
449
|
-
|
|
450
|
-
A rough guide, not a verdict. "Partial" means it is possible but not what the library is for.
|
|
451
|
-
|
|
452
|
-
| | afora | Zod 4 | Valibot | Effect Schema |
|
|
453
|
-
| --------------------------- | ---------------------------------------------- | ------------------- | -------------- | ---------------------------------- |
|
|
454
|
-
| Standard Schema V1 | yes, on every field | yes | yes | yes |
|
|
455
|
-
| Transforms | yes (`preHandle`, `afterHandle`) | yes | yes | yes |
|
|
456
|
-
| Domain entities / classes | yes (records with change methods and effects) | no | no | partial (`Schema.Class`) |
|
|
457
|
-
| Sensitive masking | yes (`sensitive`, in `toJSON`) | no | no | partial (`Redacted`) |
|
|
458
|
-
| Layered errors with a trace | yes (`ExceptionLayers`, `trace`) | no | no | partial (tagged errors, no layers) |
|
|
459
|
-
| Runtime speed | slower; see Performance | faster | faster | not measured here |
|
|
460
|
-
| Bundle size | not measured; depends on TypeBox and `slugify` | larger than Valibot | small, modular | larger |
|
|
461
|
-
| Ecosystem and maturity | new, small | very large | large | large, in the Effect ecosystem |
|
|
462
|
-
|
|
463
|
-
Where the others win: Zod and Valibot are faster, have far larger ecosystems and documentation, and have integrations and code generators for most tools; Valibot is modular and small; Effect Schema fits a codebase already built on Effect. afora gives up those to model entities, masking and layers in the type you already validate with.
|
|
464
|
-
|
|
465
372
|
## Contributing
|
|
466
373
|
|
|
467
|
-
Read [CONTRIBUTING.md](CONTRIBUTING.md) for the setup, the checks and how releases work.
|
|
468
|
-
|
|
469
|
-
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.
|
|
470
|
-
|
|
471
|
-
Scripts:
|
|
472
|
-
|
|
473
|
-
```bash
|
|
474
|
-
bun install
|
|
475
|
-
bun test # unit tests
|
|
476
|
-
bun check # type check
|
|
477
|
-
bun run test:types # build, then type-check the packed package on TypeScript, bundler and nodenext resolution
|
|
478
|
-
bun run test:type-perf # build, then budget the editor cost per edit and snapshot hovers, completions and errors
|
|
479
|
-
bun run test:api # build, then snapshot the public surface (exports and declarations) and the afora.json schema
|
|
480
|
-
bun run test:interop # Hono, tRPC and TanStack Form tests (examples/interop), including the quick start
|
|
481
|
-
bun run test:blog # the generated blog (examples/blog): drift, API and extraction
|
|
482
|
-
bun run test:agent-built # the agent-built example (examples/agent-built): drift, replay, API and the invariant evaluation
|
|
483
|
-
bun run smoke # build, then run the package on plain Node
|
|
484
|
-
bun run bench # benchmark against TypeBox, Ajv, Typia, Zod, Valibot, ArkType, Effect Schema, Yup and Joi
|
|
485
|
-
bun run lint # biome lint
|
|
486
|
-
bun run knip # unused files, exports and dependencies
|
|
487
|
-
bun run fmt # biome format + fix
|
|
488
|
-
bun run build # build dist/ (ESM + .d.ts)
|
|
489
|
-
bun run test:codegen # tests of packages/codegen
|
|
490
|
-
bun run test:mcp # tests of packages/mcp
|
|
491
|
-
bun run test:hono # tests of packages/hono
|
|
492
|
-
bun run test:fastify # tests of packages/fastify (also test:express, test:elysia)
|
|
493
|
-
bun run test:ws # tests of packages/ws
|
|
494
|
-
bun run test:drizzle # tests of packages/drizzle (SQLite and PGlite)
|
|
495
|
-
bun run test:dynamodb # tests of packages/dynamodb (DynamoDB Local, with DYNAMODB_ENDPOINT)
|
|
496
|
-
bun run build:packages # build the core, then packages/codegen, packages/mcp, packages/hono, packages/fastify, packages/express, packages/elysia, packages/ws, packages/drizzle and packages/dynamodb
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
## Versioning
|
|
500
|
-
|
|
501
|
-
What is stable, what may break and how deprecations work is in [VERSIONING.md](VERSIONING.md).
|
|
374
|
+
Read [CONTRIBUTING.md](CONTRIBUTING.md) for the setup, the checks and how releases work. Questions go to [Discussions](https://github.com/afora-framework/core/discussions); security issues go through [SECURITY.md](SECURITY.md), never a public issue. What is stable and how deprecations work is in [VERSIONING.md](VERSIONING.md).
|
|
502
375
|
|
|
503
376
|
## License
|
|
504
377
|
|
package/dist/adapters.js
CHANGED
|
@@ -1,56 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
coerceTextValues2,
|
|
3
|
-
HTTP_OPTION_KEYS2,
|
|
4
|
-
httpOptionsIssue2,
|
|
5
|
-
compileRoutes2
|
|
6
|
-
} from "./index-y280ew0k.js";
|
|
7
|
-
import {
|
|
8
|
-
schemaLockOf2,
|
|
9
|
-
diffSchemas2,
|
|
10
|
-
schemaLockProblemsOf2,
|
|
11
|
-
statusOf2,
|
|
12
|
-
PROBLEM_MEDIA_TYPE2,
|
|
13
|
-
problemOf2,
|
|
14
|
-
outputValueOf2,
|
|
15
|
-
ROUTE_OPTION_KEYS2,
|
|
16
|
-
routeOptionsIssue2,
|
|
17
|
-
requestIdHeaderOf2,
|
|
18
|
-
operationRoutes2
|
|
19
|
-
} from "./index-hg1z2s2v.js";
|
|
20
|
-
import {
|
|
21
|
-
Demo2,
|
|
22
|
-
Kind2,
|
|
23
|
-
RESERVED_RECORD_KEYS2,
|
|
24
|
-
ENTITY_IDENTITY_KEYS2,
|
|
25
|
-
RESERVED_ENTITY_KEYS2,
|
|
26
|
-
SCHEMA_VERSION_KEY2,
|
|
27
|
-
sinkOf2,
|
|
28
|
-
stageKindOf2,
|
|
29
|
-
stageCatalogOf2
|
|
30
|
-
} from "./index-xegv6a1p.js";
|
|
31
1
|
export {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
};
|
|
2
|
+
diffSchemas,
|
|
3
|
+
schemaLockOf,
|
|
4
|
+
schemaLockProblemsOf
|
|
5
|
+
} from "./core/application/schema.js";
|
|
6
|
+
export {
|
|
7
|
+
ENTITY_IDENTITY_KEYS,
|
|
8
|
+
RESERVED_ENTITY_KEYS
|
|
9
|
+
} from "./core/entity/abstract-entity.js";
|
|
10
|
+
export { coerceTextValues } from "./core/output/coerce.js";
|
|
11
|
+
export {
|
|
12
|
+
compileRoutes,
|
|
13
|
+
HTTP_OPTION_KEYS,
|
|
14
|
+
httpOptionsIssue
|
|
15
|
+
} from "./core/output/http.js";
|
|
16
|
+
export {
|
|
17
|
+
operationRoutes,
|
|
18
|
+
ROUTE_OPTION_KEYS,
|
|
19
|
+
requestIdHeaderOf,
|
|
20
|
+
routeOptionsIssue
|
|
21
|
+
} from "./core/output/openapi-sink.js";
|
|
22
|
+
export {
|
|
23
|
+
outputValueOf,
|
|
24
|
+
PROBLEM_MEDIA_TYPE,
|
|
25
|
+
problemOf
|
|
26
|
+
} from "./core/output/problem.js";
|
|
27
|
+
export { sinkOf, stageCatalogOf, stageKindOf } from "./core/output/stage.js";
|
|
28
|
+
export { statusOf } from "./core/output/status.js";
|
|
29
|
+
export { RESERVED_RECORD_KEYS } from "./core/record/abstract-record.js";
|
|
30
|
+
export { SCHEMA_VERSION_KEY } from "./core/repository/state.js";
|
|
31
|
+
export { Demo } from "./core/symbols/demo.js";
|
|
32
|
+
export { Kind } from "./core/symbols/kind.js";
|
|
@@ -15,6 +15,36 @@ export declare function isApplication(value: unknown): value is AnyApplication;
|
|
|
15
15
|
* under one name (or one object under two names) throw `ApplicationConflict`.
|
|
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
|
+
*
|
|
19
|
+
* @throws {@link DuplicateApplicationDeclaration} when one object is registered under two names.
|
|
20
|
+
* @throws {@link ApplicationConflict} when extended applications register different objects under one name.
|
|
21
|
+
* @throws {@link InvalidApplicationDeclaration} when a declaration is not of the kind its collection holds.
|
|
22
|
+
* @throws {@link UnregisteredApplicationReference} when a registered declaration holds one that is not registered.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import {
|
|
27
|
+
* applicationOf,
|
|
28
|
+
* blueprintOf,
|
|
29
|
+
* entityOf,
|
|
30
|
+
* inMemoryProvider,
|
|
31
|
+
* repositoryOf,
|
|
32
|
+
* } from "@afora/core";
|
|
33
|
+
*
|
|
34
|
+
* class User extends entityOf(blueprintOf((ctx) => ({ name: ctx.string }))) {}
|
|
35
|
+
* const Users = repositoryOf({ entity: User, actions: ["create"] });
|
|
36
|
+
*
|
|
37
|
+
* const Blog = applicationOf({
|
|
38
|
+
* name: "blog",
|
|
39
|
+
* entities: { User },
|
|
40
|
+
* repositories: { Users },
|
|
41
|
+
* provider: inMemoryProvider(), // the default of its repositories
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* await Blog.start();
|
|
45
|
+
* await Blog.repositories.Users.create({ name: "Ana" });
|
|
46
|
+
* await Blog.stop();
|
|
47
|
+
* ```
|
|
18
48
|
*/
|
|
19
49
|
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
50
|
properties: Properties;
|