@aventara/cli 1.0.0-rc.0 → 1.0.0-rc.10
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/CHANGELOG.md +461 -0
- package/README.md +101 -44
- package/dist/apply/conflict.confirmer.js +5 -5
- package/dist/catalog/adapter.catalog.generated.js +1 -1
- package/dist/catalog/host-entry.interface.js +10 -0
- package/dist/catalog/host.catalog.generated.js +32 -0
- package/dist/cli.js +29 -16
- package/dist/command/command.parser.js +99 -13
- package/dist/plan/host-plan.interface.js +1 -0
- package/dist/plan/nest-host.planner.js +131 -0
- package/dist/plan/next-host.planner.js +133 -0
- package/dist/plan/project.planner.js +94 -167
- package/dist/project/project.inspector.js +56 -17
- package/dist/run/command.runner.js +37 -19
- package/dist/run/init.orchestrator.js +55 -38
- package/dist/run/new.orchestrator.js +105 -30
- package/dist/run/scaffold.committer.js +1 -1
- package/dist/run/skill.orchestrator.js +54 -0
- package/dist/skill/agent-skill.generated.js +7 -0
- package/dist/skill/agent-skill.planner.js +116 -0
- package/dist/templates/docs.links.js +2 -1
- package/dist/templates/next-server.entry.js +5 -0
- package/dist/templates/prisma7/prisma7-nest.templates.js +242 -0
- package/dist/templates/prisma7/prisma7-next.templates.js +189 -0
- package/dist/templates/prisma7/prisma7-orm.templates.js +180 -0
- package/dist/templates/prisma7/prisma7.templates.js +7 -365
- package/dist/templates/template.registry.js +2 -2
- package/dist/wizard/answer.resolver.js +5 -5
- package/dist/wizard/readline.prompter.js +3 -3
- package/dist/wizard/wizard.questions.js +11 -11
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,466 @@
|
|
|
1
1
|
# @aventara/cli
|
|
2
2
|
|
|
3
|
+
## 1.0.0-rc.10
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 82afc5e: The scaffolded configuration's `diagnostics` sink logs `String(error)` when an error's `stack` is empty or missing,
|
|
8
|
+
instead of an empty `raw error` line.
|
|
9
|
+
|
|
10
|
+
`aventara` prints a defect as `String(error)` when its `stack` is empty or missing, instead of a blank or `undefined`
|
|
11
|
+
line.
|
|
12
|
+
|
|
13
|
+
- 09585dd: The agent skill's configuration reference covers `managedElsewhere`: a model whose table another service owns, and
|
|
14
|
+
the Prisma 7 external-tables setup that keeps `prisma migrate` off it.
|
|
15
|
+
- 04bb39a: `engines.node` is now `^22.23.4 || >=24.22.1`, the Node security releases of 2026-10-20 (CVE-2026-105481,
|
|
16
|
+
CVE-2026-105482, CVE-2026-105483, CVE-2026-106069, CVE-2026-106072, CVE-2026-106073, CVE-2026-106074,
|
|
17
|
+
CVE-2026-111497). The bins refuse an older Node in one sentence naming that range.
|
|
18
|
+
- 4f0fd29: The Prisma 7 adapter's peer floor is now `^7.10.1` (`prisma`, `@prisma/client`, `@prisma/adapter-pg`,
|
|
19
|
+
`@prisma/adapter-better-sqlite3`). `prisma` 7.10.0 pins `mysql2` 3.15.3 (GHSA-3f6p-5ww8-9rcr, GHSA-rgwj-5xj2-c3m3)
|
|
20
|
+
and, through `@prisma/config`, `deepmerge-ts` 7.1.5 (GHSA-ggr8-5vv4-36mx); 7.10.1 pins `mysql2` 3.24.4 and
|
|
21
|
+
`deepmerge-ts` 8.0.2. `aventara new` and `aventara init` install Prisma at the floor, so new projects get 7.10.1, and
|
|
22
|
+
`aventara init` refuses a project whose Prisma is below it.
|
|
23
|
+
- 10341d5: The Next.js scaffold's `src/prisma.ts` keeps one PrismaClient per generated schema: after `aventara:prepare` regenerates
|
|
24
|
+
the client, the next call makes a new one and disconnects the old one, so `next dev` needs no restart.
|
|
25
|
+
- 88263a1: Startup checks, tightened:
|
|
26
|
+
|
|
27
|
+
- An `mcp.authorization.servers` entry with a query or a fragment is refused for https URLs too, not only for loopback
|
|
28
|
+
http ones: an authorization server's issuer has neither (RFC 8414).
|
|
29
|
+
- A `client.resources` or `agent.resources` entry for a Resource managed elsewhere is refused (`COMPILER_UNKNOWN_RESOURCE`):
|
|
30
|
+
those layers never have the Resource, so nothing configured there would run. Configure it at the root or in
|
|
31
|
+
`application`.
|
|
32
|
+
|
|
33
|
+
- 88263a1: Documentation: `$access.can([])` answers `[]`, not a refusal; the MCP README states the query and fragment rule for
|
|
34
|
+
every authorization server.
|
|
35
|
+
|
|
36
|
+
## 1.0.0-rc.9
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- a0bb05e: The agent skill that `aventara new`, `aventara init` and `aventara skill` write has a new section, "Calling the API over
|
|
41
|
+
HTTP", with a working curl example: the operation path uses the model's name as written in the Prisma schema
|
|
42
|
+
(`/api/_resources/User/create/one`), every request sends the `Aventara-Protocol-Version: 1` and
|
|
43
|
+
`Aventara-Contract-Hash` headers (the hash comes from `GET /api/_contract`), and a create's body is `{ "data": … }`. It
|
|
44
|
+
lists the answers each mistake gets. The printed next steps point to it. To refresh the skill in an existing
|
|
45
|
+
project, run `aventara skill`.
|
|
46
|
+
- 8c40b81: `aventara new` and `aventara init` now scaffold Next.js apps.
|
|
47
|
+
|
|
48
|
+
- `aventara new <name> --host next` runs `create-next-app@latest` (TypeScript, the App Router under `src/`, the `@/*`
|
|
49
|
+
alias, no install of its own), then the same `init` steps, then the install. create-next-app asks its own questions
|
|
50
|
+
on your terminal; with `--yes` it takes its defaults. Its own flags go after `--`, as they are:
|
|
51
|
+
`aventara new my-app --host next --yes -- --tailwind`. `--skip-git` becomes its `--disable-git`. Without a terminal,
|
|
52
|
+
`--yes` is required. `--host` defaults to `nest`: a NestJS project is scaffolded exactly as before.
|
|
53
|
+
- `aventara init` tells the host by `package.json`: `@nestjs/core` is NestJS 12, `next` is Next.js 16, and a project
|
|
54
|
+
that depends on both or neither is refused in one sentence, with nothing written. A Next.js app needs `src/app/`
|
|
55
|
+
and the `@/*` → `./src/*` path in `tsconfig.json`, and no ORM yet.
|
|
56
|
+
- In a Next.js app, init writes `src/aventara/config.ts`, `src/aventara/adapter.ts`, `src/aventara/server.ts`
|
|
57
|
+
(`Aventara()`, exporting `handlers`, `avClient`, `avApp` and `ready`), `src/app/api/[...aventara]/route.ts`,
|
|
58
|
+
`src/instrumentation.ts` (awaiting `ready()` on the Node.js runtime) and `src/prisma.ts`, with Prisma's schema and
|
|
59
|
+
config. `aventara:prepare` ends with `aventara-build --client` and runs on `postinstall` and `prebuild`; the files it
|
|
60
|
+
generates (`src/aventara/generated/`, `src/aventara/client.ts`, `src/aventara/universal.ts`) are added to
|
|
61
|
+
`.gitignore`. An instrumentation file of your own is never replaced: the lines to add are printed.
|
|
62
|
+
- Each host package now declares its project layout in its manifest (`"aventara": { "host": … }`). The CLI scaffolds
|
|
63
|
+
from it and `aventara-build` reads its paths from it, so the two always agree.
|
|
64
|
+
|
|
65
|
+
- 501b832: `aventara new --host next` and `aventara init` in a Next.js app add `serverExternalPackages: ["@aventara/core",
|
|
66
|
+
"@aventara/prisma7-adapter"]` to `next.config.ts`, so Node.js loads them once per server process: every route and
|
|
67
|
+
page shares one core, one Framework and one Prisma client. The line goes into create-next-app's config, or any config
|
|
68
|
+
whose `const nextConfig = {` object has no `serverExternalPackages`; an app with no config gets a `next.config.ts`. A
|
|
69
|
+
config that already names `serverExternalPackages`, or is written another way, is never changed, even with `--yes`:
|
|
70
|
+
the line to add is printed, with a warning. The agent skill's Next.js section says to keep it.
|
|
71
|
+
- The agent skill's Next.js section says what a page that calls `avClient` or `avApp` needs under Cache Components
|
|
72
|
+
(create-next-app's default): the calling component inside `<Suspense>` (or a `loading.tsx`), `"use cache"` on the
|
|
73
|
+
function that makes the call, or `export const instant = false`.
|
|
74
|
+
|
|
75
|
+
## 1.0.0-rc.8
|
|
76
|
+
|
|
77
|
+
1.0.0-rc.5, rc.6 and rc.7 were tagged but never published; 1.0.0-rc.8 contains everything listed under them and the changes below.
|
|
78
|
+
|
|
79
|
+
## 1.0.0-rc.7
|
|
80
|
+
|
|
81
|
+
1.0.0-rc.5 and 1.0.0-rc.6 were tagged but never published; 1.0.0-rc.7 contains everything listed under them and the changes below.
|
|
82
|
+
|
|
83
|
+
## 1.0.0-rc.6
|
|
84
|
+
|
|
85
|
+
1.0.0-rc.5 was tagged but never published. 1.0.0-rc.6 contains everything listed under 1.0.0-rc.5 and the fix below.
|
|
86
|
+
|
|
87
|
+
## 1.0.0-rc.5
|
|
88
|
+
|
|
89
|
+
## 1.0.0-rc.4
|
|
90
|
+
|
|
91
|
+
### Patch Changes
|
|
92
|
+
|
|
93
|
+
- 7c3b65f: Build-time Contracts for serverless and edge cold starts.
|
|
94
|
+
|
|
95
|
+
- New package `@aventara/build`, bin `aventara-build`, a development dependency. It runs `src/aventara.config.ts` and
|
|
96
|
+
`src/aventara.adapter.ts` without a database client, compiles the Contracts with the project's `@aventara/core` and
|
|
97
|
+
writes `src/generated/aventara/contracts.generated.ts` (`aventaraContracts`), which a serverless or edge entry passes
|
|
98
|
+
to `createFramework` as `prebuilt`. A configuration that startup would refuse fails the build with the same
|
|
99
|
+
`CONFIG_…`/`COMPILER_…` diagnostics. `aventara-build --check` writes nothing and exits 1 when the file is missing or
|
|
100
|
+
stale, naming the Resources that changed.
|
|
101
|
+
- `@aventara/prisma7-adapter`: `describePrismaAdapter({ discovery, provider, driver })` states the adapter's passive
|
|
102
|
+
facts (the model, the driver's transaction capability, the referential actions, the computed fields) with no client.
|
|
103
|
+
`createPrismaAdapter({ ...description, client })` builds the adapter from the same facts, and still checks the client
|
|
104
|
+
against the discovery artifact.
|
|
105
|
+
- `@aventara/cli`: the scaffold's `src/aventara.config.ts` exports the configuration as a plain value
|
|
106
|
+
(`export const aventaraConfig = { … } as const`) with no `adapter`. A new `src/aventara.adapter.ts` default-exports
|
|
107
|
+
`describePrismaAdapter(…)`, and `app.module.ts` joins the two:
|
|
108
|
+
`config: { ...aventaraConfig, adapter: await createPrismaAdapter({ ...adapter, client: prisma }) }`. The Nest server
|
|
109
|
+
still compiles its Contracts at startup and passes no `prebuilt`. `aventara:prepare` ends with `&& aventara-build`,
|
|
110
|
+
and `@aventara/build` is an exact development dependency.
|
|
111
|
+
|
|
112
|
+
- 2b1b17e: `aventara-build`'s files no longer land inside a Prisma client generated into `src/generated`.
|
|
113
|
+
|
|
114
|
+
When `aventara init` reuses a project whose Prisma client is generated into `src/generated`, it already puts the
|
|
115
|
+
discovery artifact in `src/aventara/`, because `prisma generate` deletes every file in its output directory that it did
|
|
116
|
+
not write. `aventara-build` still wrote its three files to `src/generated/aventara/`, so each `prisma generate` deleted
|
|
117
|
+
them and the project stopped compiling until the next `aventara:prepare`. Now all of Aventara's generated files are
|
|
118
|
+
in one place.
|
|
119
|
+
|
|
120
|
+
- `@aventara/build`: `aventara-build` writes `resources.generated.ts`, `computed-fields.generated.ts` and
|
|
121
|
+
`contracts.generated.ts` to the directory the configuration imports `aventaraResources` from, so the tree is always
|
|
122
|
+
written where the configuration reads it. A configuration that imports no tree keeps `src/generated/aventara/`.
|
|
123
|
+
The files of a project whose client is generated anywhere else are the same bytes, in the same place.
|
|
124
|
+
- `@aventara/prisma7-adapter`: with `--config`, the artifact imports the computed fields from its sibling,
|
|
125
|
+
`./computed-fields.generated`, wherever `--out` puts it. `--config` is still required for that import and the step
|
|
126
|
+
still checks that the file exists, but the step no longer uses its location. A scaffold's artifact is unchanged.
|
|
127
|
+
- `@aventara/cli`: in that project, `src/aventara.config.ts` imports the tree from `./aventara/resources.generated`,
|
|
128
|
+
and `.gitignore` gets the one `/src/aventara/` line that covers all four files (no separate
|
|
129
|
+
`/src/generated/aventara/contracts.generated.ts` line). The agent skill says where the generated files are in
|
|
130
|
+
both cases.
|
|
131
|
+
|
|
132
|
+
- 99169d2: Policy engines, reads first. `createFramework` takes an optional root `policy: { engine, principal }`: an engine that
|
|
133
|
+
answers three questions (`can`, `scopeOf`, `fieldsOf`, each now or as a promise), and a function that derives a
|
|
134
|
+
request's principal from its `ctx.$state`. Core never imports an engine; one satisfies `AvPolicyEngine` by its shape,
|
|
135
|
+
and a scope or grant is a fragment of Aventara's own `where` (`AvPolicyWhere`).
|
|
136
|
+
|
|
137
|
+
- Client and agent requests are checked (an MCP tool call is an agent request). An application call is checked only
|
|
138
|
+
when it names a principal: `framework.application.Note.find.many(args, { as: user })`. A call a stage makes through
|
|
139
|
+
`ctx` never is.
|
|
140
|
+
- Before any guard, `can` and the fields the request names or receives (`fieldsOf`); a refusal is `403 A4002`. At the
|
|
141
|
+
adapter call, after every developer stage, the read scope is ANDed into the root `where`, every to-many include and
|
|
142
|
+
its `$count`, and every relation filter, at every level. A to-one include of a row out of scope refuses the request;
|
|
143
|
+
it is never nulled.
|
|
144
|
+
- Governed fields (the engine's `governed` list) are left out of every default projection and carry `governed: true`
|
|
145
|
+
in the Contracts. Name one to get it: `select: ["$defaults", "email"]`, where `$defaults` is the level's default
|
|
146
|
+
projection. It is refused where a returned row does not grant it, and a filter or sort by it is checked before the
|
|
147
|
+
query runs, so it cannot reveal values the caller may not read.
|
|
148
|
+
- The engine's `governed` and `paths` lists are checked against the Core Contract at startup. A rule's paths are lists
|
|
149
|
+
of segments, as other paths in core are: `["Note", "campaign", "members", "some", "userId", "equals"]`.
|
|
150
|
+
- Writes are checked as well; see the policy writes entry.
|
|
151
|
+
|
|
152
|
+
Without `policy`, nothing changes: the Contracts and their hashes are the same bytes.
|
|
153
|
+
|
|
154
|
+
- 154e440: Policy engines check writes. Every create, update, delete and upsert a client or agent makes (or an application call
|
|
155
|
+
made `{ as }` a principal) is now checked by the configuration's `policy`, where before it was refused.
|
|
156
|
+
|
|
157
|
+
- The fields a write sends are held to `fieldsOf({ access: "write" })` before any guard; a field outside it answers
|
|
158
|
+
`403 A4002`. A field writable on some rows only is checked against every row an update reaches, and against the
|
|
159
|
+
row a create inserts, after the insert and inside its transaction.
|
|
160
|
+
- A create asks `can({ action: "create", row })` about each new row on its final data. Its rules can look rows up
|
|
161
|
+
through `exists`, which runs inside the write's transaction, so a plan that creates a campaign and then its first
|
|
162
|
+
note sees the campaign.
|
|
163
|
+
- An update or delete acts only on rows the caller may both change (`scopeOf` for the action) and read. A single-row
|
|
164
|
+
one that matches nothing answers `A4002` when the caller can read the row, and `A2003` when it cannot, so the
|
|
165
|
+
answer does not reveal that the row exists. A `many` or `count` one answers for the caller's rows only.
|
|
166
|
+
- An update that moves a row out of what the caller reads is rolled back with `A4002`. So is one that fails an `after`
|
|
167
|
+
condition: inside the transaction core asks `can({ action: "update", row })` about each changed row of every
|
|
168
|
+
checked update. The engine's new `afterChecks` member (`{ Note: ["update"] }`) names the updates whose grants carry
|
|
169
|
+
such a condition, which are the ones refused where there is no interactive transaction.
|
|
170
|
+
- Where the operation's `transactionsStyle` is `"interactive"`, a checked write runs in a transaction of its own. Where
|
|
171
|
+
it is `"none"` (Cloudflare D1), a write that can only be checked after it runs is refused before it runs; every other
|
|
172
|
+
write runs, a create looking up before it inserts, and an unlisted update without the after check.
|
|
173
|
+
- Nested writes (a relation directive in a write's data) are refused under a policy for now.
|
|
174
|
+
|
|
175
|
+
`afterChecks` is checked against the Core Contract at startup and changes no Contract and no prebuilt fingerprint.
|
|
176
|
+
|
|
177
|
+
- 3813207: The Resource tree and the computed fields come from `aventara-build`.
|
|
178
|
+
|
|
179
|
+
- `@aventara/build`: before it compiles the Contracts, `aventara-build` writes two more files under
|
|
180
|
+
`src/generated/aventara/`. `resources.generated.ts` exports `aventaraResources`: every `defineResource(…)` result
|
|
181
|
+
that the files `src/resources/**/*.resource.ts` export, found by its type (never run), one `resources` tree per
|
|
182
|
+
layer, written as the plain spreads you wrote by hand (`root` always, `application`, `client` and `agent` when some
|
|
183
|
+
Resource has a block for them). Two definitions of one Resource stop the build, naming both. Imports are spelled as
|
|
184
|
+
the project spells its own. `computed-fields.generated.ts` exports `computedFields` and its type `AvComputedFields`:
|
|
185
|
+
which fields the configuration's write compute fills, read off its type after the tree exists, so a fresh install is
|
|
186
|
+
typed right on its first `aventara:prepare`. `--check` covers all three files. `typescript` (`>=5.7.0 <7`) is now a
|
|
187
|
+
peer dependency.
|
|
188
|
+
- `@aventara/prisma7-adapter`: the generate step no longer reads the configuration. With `--config`, the discovery
|
|
189
|
+
artifact imports `computedFields` and `AvComputedFields` from the file `aventara-build` writes beside the
|
|
190
|
+
configuration (`generated/aventara/computed-fields.generated.ts`); a `--config` file that does not exist is still
|
|
191
|
+
refused. `readConfigComputedFields`, `AvPrismaConfigReaderError` and `ConfigComputedFieldsReading` are removed from
|
|
192
|
+
`@aventara/prisma7-adapter/generate`, as are `PrismaGenerateToolchain.readComputedFields` and
|
|
193
|
+
`PrismaGenerateResult.computedFields`.
|
|
194
|
+
- `@aventara/cli`: the scaffold's `src/aventara.config.ts` takes its Resources from the tree,
|
|
195
|
+
`resources: aventaraResources.root`, so a new Resource file needs no configuration edit; the agent skill says so.
|
|
196
|
+
|
|
197
|
+
## 1.0.0-rc.3
|
|
198
|
+
|
|
199
|
+
### Patch Changes
|
|
200
|
+
|
|
201
|
+
- b38034d: A refusal that quotes a failed child's output (`npm install`, `nest new`) ends with that output as the child wrote it.
|
|
202
|
+
It printed a period of its own after the child's last line (`npm ERR! the registry said no.`).
|
|
203
|
+
|
|
204
|
+
## 1.0.0-rc.2
|
|
205
|
+
|
|
206
|
+
### Patch Changes
|
|
207
|
+
|
|
208
|
+
- `aventara init`'s refusal of a TypeScript 7 project names the supported range: "use TypeScript 5.7 to 6" (it said
|
|
209
|
+
"use TypeScript 6 or 5"). The Prisma adapter's `typescript` peer range starts at 5.7.0 now.
|
|
210
|
+
- An agent skill for your AI coding agent. `aventara new` and `aventara init` write `.claude/skills/aventara/`:
|
|
211
|
+
`SKILL.md` (the Agent Skills format Claude Code reads: when to use it, and one rule per task) and
|
|
212
|
+
`reference/config.md`, `reference/stages.md`, `reference/recipes.md` and `reference/errors.md` (`@aventara/core`'s
|
|
213
|
+
`ERRORS.md`), for the Aventara version they install. They also add a short Aventara section to `AGENTS.md`, between
|
|
214
|
+
`<!-- aventara:start -->` and `<!-- aventara:end -->`, for agents that read `AGENTS.md`: an existing `AGENTS.md`
|
|
215
|
+
keeps everything else, and `CLAUDE.md` is never written.
|
|
216
|
+
- `aventara skill` refreshes those files for the `@aventara/core` installed in the project (its version and its
|
|
217
|
+
`ERRORS.md`; it runs none of the project's packages). It rewrites the skill files it wrote, keeps one you changed
|
|
218
|
+
and says so (`--force` replaces it), and replaces only the marked section of `AGENTS.md`.
|
|
219
|
+
- `aventara new` and `aventara init` write an `aventara:prepare` script that also passes
|
|
220
|
+
`--config src/aventara.config.ts`, so the generated types know which fields your configuration computes: a
|
|
221
|
+
variant's guards, pipes and filters may leave exactly those out of a create. A project set up before keeps working;
|
|
222
|
+
add the flag to its script to get the precise types.
|
|
223
|
+
- The agent skill says that a `Date` or bytes value a stage is told is that stage's own copy, so changing it in place
|
|
224
|
+
changes nothing another stage sees, what is written or what the caller receives; and that a `compute.read` stage with
|
|
225
|
+
no `dependsOn` computes from the same inputs for every row, so its value usually belongs in response metadata or
|
|
226
|
+
`ctx.$state`. In rc.1 it said neither.
|
|
227
|
+
- The agent skill says that a `compute.read` stage is told the row frozen: it returns its field's value, and writing
|
|
228
|
+
into `record` answers `A3000`. Its error reference lists a nested write that writes its relation back (`A2004`,
|
|
229
|
+
`V1012`).
|
|
230
|
+
- The agent skill says that a computed value its field's `format` refuses (a relative path for `format: "url"`)
|
|
231
|
+
answers `A3000`, as a value of the wrong type does.
|
|
232
|
+
- Released with the other Aventara packages, and `aventara new` and `aventara init` install them at 1.0.0-rc.2.
|
|
233
|
+
|
|
234
|
+
## 1.0.0-rc.1
|
|
235
|
+
|
|
236
|
+
### Patch Changes
|
|
237
|
+
|
|
238
|
+
- 7e3d06f: Breaking: the configuration has a new shape. Every layer (the root, `application` and `client`) now holds the
|
|
239
|
+
same six keys: `exposed`, `transactionsStyle`, `limits`, `pipelines`, `resources` and `exemptions`. Everything about
|
|
240
|
+
one Resource lives under `resources.<Resource>`: its fields, and each field's `definition` and `pipelines`. The old
|
|
241
|
+
keys are refused when the framework starts, with a message that names where each one moved:
|
|
242
|
+
|
|
243
|
+
| Before (rc.0) | Now (rc.1) |
|
|
244
|
+
| ------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
245
|
+
| `fields: { Post: { url: { type, nullable, … } } }` | `resources: { Post: { fields: { url: { definition: { type, nullable, … } } } } }` |
|
|
246
|
+
| `behaviors: { Post: { fields: { slug: { create } } } }` | `resources: { Post: { fields: { slug: { pipelines: { compute: { create } } } } } }` |
|
|
247
|
+
| `transactions: "none"` | `transactionsStyle: "none"` |
|
|
248
|
+
| `restrictions` | `exposed` + `exemptions` (see below) |
|
|
249
|
+
| `@aventara/core/json` | `@aventara/core/avz` |
|
|
250
|
+
|
|
251
|
+
Before:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
createFramework({
|
|
255
|
+
api: "/api",
|
|
256
|
+
adapter,
|
|
257
|
+
transactions: "interactive",
|
|
258
|
+
fields: {
|
|
259
|
+
Post: {
|
|
260
|
+
url: {
|
|
261
|
+
type: { scalar: "string" },
|
|
262
|
+
nullable: false,
|
|
263
|
+
list: false,
|
|
264
|
+
lifecycle: ["VIRTUAL"],
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
behaviors: {
|
|
269
|
+
Post: {
|
|
270
|
+
fields: {
|
|
271
|
+
url: {
|
|
272
|
+
read: {
|
|
273
|
+
dependsOn: ["slug"],
|
|
274
|
+
compute: ({ record }) => `/p/${record.slug}`,
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
After:
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
createFramework({
|
|
287
|
+
api: "/api",
|
|
288
|
+
adapter,
|
|
289
|
+
transactionsStyle: "interactive",
|
|
290
|
+
resources: {
|
|
291
|
+
Post: {
|
|
292
|
+
fields: {
|
|
293
|
+
url: {
|
|
294
|
+
definition: {
|
|
295
|
+
type: { scalar: "string" },
|
|
296
|
+
nullable: false,
|
|
297
|
+
list: false,
|
|
298
|
+
lifecycle: ["VIRTUAL"],
|
|
299
|
+
},
|
|
300
|
+
pipelines: {
|
|
301
|
+
compute: {
|
|
302
|
+
read: {
|
|
303
|
+
dependsOn: ["slug"],
|
|
304
|
+
compute: ({ record }) => `/p/${record.slug}`,
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
What else changed:
|
|
316
|
+
|
|
317
|
+
- A field is defined at the root only. A `definition` in `application` or `client` is refused
|
|
318
|
+
(`CONFIG_FORBIDDEN_IN_OVERLAY`). Define the field
|
|
319
|
+
at the root; `application` and `client` can still add compute stages to it.
|
|
320
|
+
- Computed values chain. When the root and an overlay both compute a field, the root's stage runs first and the
|
|
321
|
+
overlay's receives its result as `value`. A stage can no longer be turned off with `false`. Returning `undefined`
|
|
322
|
+
keeps the current value. An upsert computes its create branch with `compute.create` and its update branch with
|
|
323
|
+
`compute.update`.
|
|
324
|
+
- A discovered field keeps the adapter's facts. On a field the adapter discovered you may restate its own `type`,
|
|
325
|
+
`nullable` and `list`, and give a Json field its shape (`definition.json`). Setting a different type, any `format`
|
|
326
|
+
or any `capabilities` on it is refused. `format` is for fields you add, and it must be one of zod's named string
|
|
327
|
+
formats (`"email"`, `"url"`, `"uuid"`, …); any other name is a type error and refused at startup.
|
|
328
|
+
- A misplaced key is a type error. In a configuration written inline in `createFramework({ ... })` or
|
|
329
|
+
`AventaraModule.forRoot`, a key that has no place there (a typo, an old key, `compute` on a relation field, a field
|
|
330
|
+
name neither your schema nor the root has) is now a type error at that key, and still refused at startup.
|
|
331
|
+
- A Resource whose name starts with `$` is refused at startup (`$` names are the framework's).
|
|
332
|
+
- The Json shape builder moved from `@aventara/core/json` to `@aventara/core/avz`. Change the import; `AvZ` is
|
|
333
|
+
the same.
|
|
334
|
+
|
|
335
|
+
`exposed` and `exemptions`, which replace `restrictions`, have their own entry.
|
|
336
|
+
|
|
337
|
+
- 6317811: Breaking: `exposed` and `exemptions` replace `restrictions`. Every layer (the root, `application` and `client`)
|
|
338
|
+
says what it exposes with two keys: `exposed`, the layer's default, and `exemptions`, the entries that are the
|
|
339
|
+
opposite of it.
|
|
340
|
+
|
|
341
|
+
- `exposed: true` (the root's default) exposes everything the layer above offers, and `exemptions` lists what to
|
|
342
|
+
hide. `exposed: false` exposes nothing, and `exemptions` lists the only things to expose.
|
|
343
|
+
- In `exemptions`, `true` exempts an entry whole, `false` and `[]` exempt nothing, a list exempts the listed
|
|
344
|
+
entries one level down, and an object goes one level deeper. A Resource takes `true`, `false` or
|
|
345
|
+
`{ operations, fields }`. Operations are nested: `operations: { find: ["many"], update: { unique: true } }`.
|
|
346
|
+
A field takes a list of capability names, or an object per capability whose token lists name filter operators,
|
|
347
|
+
directives, reducers or pagination kinds.
|
|
348
|
+
- A list now always means "exempt these". rc.0's `filter: ["equals"]` meant "keep only `equals`"; under
|
|
349
|
+
`exposed: true` the same intent is the list of the operators to remove.
|
|
350
|
+
- Overlays only narrow. `application` and `client` start from the root's view. An overlay can no longer bring
|
|
351
|
+
back what the root hid (rc.0's `hidden: false`); that is now refused at startup, and the message names the other
|
|
352
|
+
overlay to hide it in instead. Expose an entry at the root and hide it in the overlay that must not see it.
|
|
353
|
+
- An overlay that sets `exemptions` must also set its own `exposed`.
|
|
354
|
+
- Relation positions are the adapter's: an exemption can no longer narrow where a relation is projected or written.
|
|
355
|
+
- A unique operation (`find.unique`, `update.unique`, `delete.unique`, `upsert.unique`) needs an identifier. Picking
|
|
356
|
+
one without the identifier's fields, or hiding every identifier while one stays exposed, is refused at startup
|
|
357
|
+
with `COMPILER_MISSING_IDENTIFIER` instead of the operation silently disappearing.
|
|
358
|
+
|
|
359
|
+
Every name in `exemptions` must exist, in both styles: a Resource, field, family, variant, capability or token the
|
|
360
|
+
adapter does not offer is refused at startup with its path (`COMPILER_UNKNOWN_RESOURCE`, `COMPILER_UNKNOWN_FIELD`,
|
|
361
|
+
`COMPILER_UNAVAILABLE_OPERATION`, `COMPILER_UNAVAILABLE_CAPABILITY`), and in a configuration written inline in
|
|
362
|
+
`createFramework({ ... })` it is a type error first. Under `exposed: true`, hiding in an overlay what the root
|
|
363
|
+
already hid does nothing.
|
|
364
|
+
|
|
365
|
+
Before (rc.0):
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
createFramework({
|
|
369
|
+
api: "/api",
|
|
370
|
+
adapter,
|
|
371
|
+
restrictions: {
|
|
372
|
+
User: {
|
|
373
|
+
fields: {
|
|
374
|
+
passwordHash: { hidden: true },
|
|
375
|
+
email: { capabilities: { filter: ["equals"] } },
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
client: {
|
|
380
|
+
restrictions: {
|
|
381
|
+
AuditLog: { hidden: true },
|
|
382
|
+
Post: { operations: { delete: { unique: false } } },
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
After (rc.1):
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
createFramework({
|
|
392
|
+
api: "/api",
|
|
393
|
+
adapter,
|
|
394
|
+
// The root: `exposed` defaults to true, so these are hidden from every origin.
|
|
395
|
+
exemptions: {
|
|
396
|
+
resources: {
|
|
397
|
+
User: {
|
|
398
|
+
fields: {
|
|
399
|
+
passwordHash: true,
|
|
400
|
+
email: { filter: ["contains", "startsWith"] }, // remove every operator but `equals`
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
client: {
|
|
406
|
+
exposed: true,
|
|
407
|
+
exemptions: {
|
|
408
|
+
resources: {
|
|
409
|
+
AuditLog: true,
|
|
410
|
+
Post: { operations: { delete: ["unique"] } },
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Or expose only what a frontend calls:
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
client: {
|
|
421
|
+
exposed: false,
|
|
422
|
+
exemptions: {
|
|
423
|
+
resources: {
|
|
424
|
+
Post: {
|
|
425
|
+
operations: { find: ["many", "unique"], create: ["one"] },
|
|
426
|
+
fields: { id: ["select"], title: ["select", "create"], body: ["select", "create"] },
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
`aventara init` and `aventara new` now write `exposed: true` into `src/aventara.config.ts`, with a comment that
|
|
434
|
+
links the exposure documentation.
|
|
435
|
+
|
|
436
|
+
- 9c52482: A `$connect` to a row that does not exist now answers `404 A2003` (not found), the same as reading a missing row.
|
|
437
|
+
Before, it answered `422 A2004`. This covers a to-one `$connect` in `create`, `update` and `upsert`, a to-many
|
|
438
|
+
`$connect`, and the same writes inside a transaction plan, where `cause.operation` names the step. The message is
|
|
439
|
+
unchanged: "The operation's arguments referenced a record that does not exist." On `update.first`, the same mistake
|
|
440
|
+
answered `A2014` before, as if the row had changed underneath; it now answers `A2003` too, and a duplicate unique value
|
|
441
|
+
written through `update.first` answers `409 A2008` instead of `A3001`.
|
|
442
|
+
|
|
443
|
+
Clearer top-level messages for an unknown Resource or operation. `A2001` now says "The requested Resource is not
|
|
444
|
+
available." and `A2002` says "The requested operation is not available on this Resource.", instead of "Operation
|
|
445
|
+
arguments failed framework validation.", which stays the message of `A2004`, `A2007` and `A2009`. A transaction step
|
|
446
|
+
whose operation is outside the standard grammar now carries the same issue message as a single call, naming the
|
|
447
|
+
operation and the Resource (for example `Operation "dance.one" is not available on Resource "posts".`), instead of
|
|
448
|
+
"Operation is not part of the standard operation grammar."
|
|
449
|
+
|
|
450
|
+
A `$ref` with no `fingerprint` is `V1000` (required value missing) and answers `422 A2004`. Before, it was `V1017`
|
|
451
|
+
(fingerprint mismatch) under `A2007`. A fingerprint that is present but wrong is still `V1017`.
|
|
452
|
+
|
|
453
|
+
`aventara init` and `aventara new` link to the documentation at https://aventara-framework.vercel.app. The old
|
|
454
|
+
address redirects, so links already written into projects keep working.
|
|
455
|
+
|
|
456
|
+
- 8db76e7: `aventara new` and `aventara init` write a `diagnostics` sink into `src/aventara.config.ts`. When something fails
|
|
457
|
+
inside the server, the caller gets a plain 500 and the Nest log said only which request failed; the raw error went to
|
|
458
|
+
a sink the scaffold did not set. Now the sink logs the raw error with its stack through Nest's `Logger` (context
|
|
459
|
+
`Aventara`), at error level, right after that line. Replace it with your own reporter when you have one.
|
|
460
|
+
|
|
461
|
+
- `aventara new` and `aventara init` write `api: '/api'` into `src/aventara.config.ts` (the root key was renamed from
|
|
462
|
+
`entrypoint`).
|
|
463
|
+
|
|
3
464
|
## 1.0.0-rc.0
|
|
4
465
|
|
|
5
466
|
### Major Changes
|