@aventara/cli 0.1.0-pilot.4 → 1.0.0-rc.1
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 +345 -0
- package/README.md +55 -42
- package/dist/apply/conflict.confirmer.js +4 -4
- package/dist/cli.js +18 -14
- package/dist/command/command.parser.js +17 -10
- package/dist/plan/project.planner.js +2 -4
- package/dist/project/project.inspector.js +8 -8
- package/dist/run/command.runner.js +2 -0
- package/dist/run/init.orchestrator.js +40 -20
- package/dist/run/new.orchestrator.js +32 -14
- package/dist/run/step.reporter.js +87 -0
- package/dist/templates/docs.links.js +2 -1
- package/dist/templates/prisma7/prisma7.templates.js +26 -5
- package/dist/templates/template.registry.js +1 -1
- package/dist/wizard/answer.resolver.js +5 -5
- package/dist/wizard/readline.prompter.js +3 -3
- package/dist/wizard/wizard.questions.js +9 -9
- package/package.json +4 -3
- package/dist/apply/app-module.anchor.d.ts +0 -24
- package/dist/apply/conflict.confirmer.d.ts +0 -15
- package/dist/apply/e2e-spec.anchor.d.ts +0 -18
- package/dist/apply/main-cors.anchor.d.ts +0 -50
- package/dist/apply/manifest.merger.d.ts +0 -51
- package/dist/aventara.bin.d.ts +0 -2
- package/dist/catalog/adapter.catalog.generated.d.ts +0 -20
- package/dist/catalog/catalog-entry.interface.d.ts +0 -54
- package/dist/catalog/catalog.matcher.d.ts +0 -40
- package/dist/catalog/range.reader.d.ts +0 -11
- package/dist/cli.d.ts +0 -29
- package/dist/command/command.parser.d.ts +0 -38
- package/dist/node-version.guard.d.ts +0 -8
- package/dist/plan/project.planner.d.ts +0 -53
- package/dist/project/package-manager.detector.d.ts +0 -5
- package/dist/project/project.inspector.d.ts +0 -32
- package/dist/project/service.detector.d.ts +0 -40
- package/dist/project/source.scanner.d.ts +0 -16
- package/dist/run/command.runner.d.ts +0 -15
- package/dist/run/init.orchestrator.d.ts +0 -27
- package/dist/run/new.orchestrator.d.ts +0 -20
- package/dist/run/scaffold.committer.d.ts +0 -28
- package/dist/templates/docs.links.d.ts +0 -8
- package/dist/templates/prisma7/prisma7.templates.d.ts +0 -34
- package/dist/templates/template.registry.d.ts +0 -152
- package/dist/wizard/answer.resolver.d.ts +0 -47
- package/dist/wizard/readline.prompter.d.ts +0 -22
- package/dist/wizard/wizard.questions.d.ts +0 -120
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# @aventara/cli
|
|
2
|
+
|
|
3
|
+
## 1.0.0-rc.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7e3d06f: Breaking: the configuration has a new shape. Every layer (the root, `application` and `client`) now holds the
|
|
8
|
+
same six keys: `exposed`, `transactionsStyle`, `limits`, `pipelines`, `resources` and `exemptions`. Everything about
|
|
9
|
+
one Resource lives under `resources.<Resource>`: its fields, and each field's `definition` and `pipelines`. The old
|
|
10
|
+
keys are refused when the framework starts, with a message that names where each one moved:
|
|
11
|
+
|
|
12
|
+
| Before (rc.0) | Now (rc.1) |
|
|
13
|
+
| ------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
14
|
+
| `fields: { Post: { url: { type, nullable, … } } }` | `resources: { Post: { fields: { url: { definition: { type, nullable, … } } } } }` |
|
|
15
|
+
| `behaviors: { Post: { fields: { slug: { create } } } }` | `resources: { Post: { fields: { slug: { pipelines: { compute: { create } } } } } }` |
|
|
16
|
+
| `transactions: "none"` | `transactionsStyle: "none"` |
|
|
17
|
+
| `restrictions` | `exposed` + `exemptions` (see below) |
|
|
18
|
+
| `@aventara/core/json` | `@aventara/core/avz` |
|
|
19
|
+
|
|
20
|
+
Before:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
createFramework({
|
|
24
|
+
api: "/api",
|
|
25
|
+
adapter,
|
|
26
|
+
transactions: "interactive",
|
|
27
|
+
fields: {
|
|
28
|
+
Post: {
|
|
29
|
+
url: {
|
|
30
|
+
type: { scalar: "string" },
|
|
31
|
+
nullable: false,
|
|
32
|
+
list: false,
|
|
33
|
+
lifecycle: ["VIRTUAL"],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
behaviors: {
|
|
38
|
+
Post: {
|
|
39
|
+
fields: {
|
|
40
|
+
url: {
|
|
41
|
+
read: {
|
|
42
|
+
dependsOn: ["slug"],
|
|
43
|
+
compute: ({ record }) => `/p/${record.slug}`,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
After:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
createFramework({
|
|
56
|
+
api: "/api",
|
|
57
|
+
adapter,
|
|
58
|
+
transactionsStyle: "interactive",
|
|
59
|
+
resources: {
|
|
60
|
+
Post: {
|
|
61
|
+
fields: {
|
|
62
|
+
url: {
|
|
63
|
+
definition: {
|
|
64
|
+
type: { scalar: "string" },
|
|
65
|
+
nullable: false,
|
|
66
|
+
list: false,
|
|
67
|
+
lifecycle: ["VIRTUAL"],
|
|
68
|
+
format: "url",
|
|
69
|
+
},
|
|
70
|
+
pipelines: {
|
|
71
|
+
compute: {
|
|
72
|
+
read: {
|
|
73
|
+
dependsOn: ["slug"],
|
|
74
|
+
compute: ({ record }) => `/p/${record.slug}`,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
What else changed:
|
|
86
|
+
|
|
87
|
+
- A field is defined at the root only. A `definition` in `application` or `client` is refused
|
|
88
|
+
(`CONFIG_FORBIDDEN_IN_OVERLAY`). Define the field
|
|
89
|
+
at the root; `application` and `client` can still add compute stages to it.
|
|
90
|
+
- Computed values chain. When the root and an overlay both compute a field, the root's stage runs first and the
|
|
91
|
+
overlay's receives its result as `value`. A stage can no longer be turned off with `false`. Returning `undefined`
|
|
92
|
+
keeps the current value. An upsert computes its create branch with `compute.create` and its update branch with
|
|
93
|
+
`compute.update`.
|
|
94
|
+
- A discovered field keeps the adapter's facts. On a field the adapter discovered you may restate its own `type`,
|
|
95
|
+
`nullable` and `list`, and give a Json field its shape (`definition.json`). Setting a different type, any `format`
|
|
96
|
+
or any `capabilities` on it is refused. `format` is for fields you add, and it must be one of zod's named string
|
|
97
|
+
formats (`"email"`, `"url"`, `"uuid"`, …); any other name is a type error and refused at startup.
|
|
98
|
+
- A misplaced key is a type error. In a configuration written inline in `createFramework({ ... })` or
|
|
99
|
+
`AventaraModule.forRoot`, a key that has no place there (a typo, an old key, `compute` on a relation field, a field
|
|
100
|
+
name neither your schema nor the root has) is now a type error at that key, and still refused at startup.
|
|
101
|
+
- A Resource whose name starts with `$` is refused at startup (`$` names are the framework's).
|
|
102
|
+
- The Json shape builder moved from `@aventara/core/json` to `@aventara/core/avz`. Change the import; `AvZ` is
|
|
103
|
+
the same.
|
|
104
|
+
|
|
105
|
+
`exposed` and `exemptions`, which replace `restrictions`, have their own entry.
|
|
106
|
+
|
|
107
|
+
- 6317811: Breaking: `exposed` and `exemptions` replace `restrictions`. Every layer (the root, `application` and `client`)
|
|
108
|
+
says what it exposes with two keys: `exposed`, the layer's default, and `exemptions`, the entries that are the
|
|
109
|
+
opposite of it.
|
|
110
|
+
|
|
111
|
+
- `exposed: true` (the root's default) exposes everything the layer above offers, and `exemptions` lists what to
|
|
112
|
+
hide. `exposed: false` exposes nothing, and `exemptions` lists the only things to expose.
|
|
113
|
+
- In `exemptions`, `true` exempts an entry whole, `false` and `[]` exempt nothing, a list exempts the listed
|
|
114
|
+
entries one level down, and an object goes one level deeper. A Resource takes `true`, `false` or
|
|
115
|
+
`{ operations, fields }`. Operations are nested: `operations: { find: ["many"], update: { unique: true } }`.
|
|
116
|
+
A field takes a list of capability names, or an object per capability whose token lists name filter operators,
|
|
117
|
+
directives, reducers or pagination kinds.
|
|
118
|
+
- A list now always means "exempt these". rc.0's `filter: ["equals"]` meant "keep only `equals`"; under
|
|
119
|
+
`exposed: true` the same intent is the list of the operators to remove.
|
|
120
|
+
- Overlays only narrow. `application` and `client` start from the root's view. An overlay can no longer bring
|
|
121
|
+
back what the root hid (rc.0's `hidden: false`); that is now refused at startup, and the message names the other
|
|
122
|
+
overlay to hide it in instead. Expose an entry at the root and hide it in the overlay that must not see it.
|
|
123
|
+
- An overlay that sets `exemptions` must also set its own `exposed`.
|
|
124
|
+
- Relation positions are the adapter's: an exemption can no longer narrow where a relation is projected or written.
|
|
125
|
+
- A unique operation (`find.unique`, `update.unique`, `delete.unique`, `upsert.unique`) needs an identifier. Picking
|
|
126
|
+
one without the identifier's fields, or hiding every identifier while one stays exposed, is refused at startup
|
|
127
|
+
with `COMPILER_MISSING_IDENTIFIER` instead of the operation silently disappearing.
|
|
128
|
+
|
|
129
|
+
Every name in `exemptions` must exist, in both styles: a Resource, field, family, variant, capability or token the
|
|
130
|
+
adapter does not offer is refused at startup with its path (`COMPILER_UNKNOWN_RESOURCE`, `COMPILER_UNKNOWN_FIELD`,
|
|
131
|
+
`COMPILER_UNAVAILABLE_OPERATION`, `COMPILER_UNAVAILABLE_CAPABILITY`), and in a configuration written inline in
|
|
132
|
+
`createFramework({ ... })` it is a type error first. Under `exposed: true`, hiding in an overlay what the root
|
|
133
|
+
already hid does nothing.
|
|
134
|
+
|
|
135
|
+
Before (rc.0):
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
createFramework({
|
|
139
|
+
api: "/api",
|
|
140
|
+
adapter,
|
|
141
|
+
restrictions: {
|
|
142
|
+
User: {
|
|
143
|
+
fields: {
|
|
144
|
+
passwordHash: { hidden: true },
|
|
145
|
+
email: { capabilities: { filter: ["equals"] } },
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
client: {
|
|
150
|
+
restrictions: {
|
|
151
|
+
AuditLog: { hidden: true },
|
|
152
|
+
Post: { operations: { delete: { unique: false } } },
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
After (rc.1):
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
createFramework({
|
|
162
|
+
api: "/api",
|
|
163
|
+
adapter,
|
|
164
|
+
// The root: `exposed` defaults to true, so these are hidden from every origin.
|
|
165
|
+
exemptions: {
|
|
166
|
+
resources: {
|
|
167
|
+
User: {
|
|
168
|
+
fields: {
|
|
169
|
+
passwordHash: true,
|
|
170
|
+
email: { filter: ["contains", "startsWith"] }, // remove every operator but `equals`
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
client: {
|
|
176
|
+
exposed: true,
|
|
177
|
+
exemptions: {
|
|
178
|
+
resources: {
|
|
179
|
+
AuditLog: true,
|
|
180
|
+
Post: { operations: { delete: ["unique"] } },
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Or expose only what a frontend calls:
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
client: {
|
|
191
|
+
exposed: false,
|
|
192
|
+
exemptions: {
|
|
193
|
+
resources: {
|
|
194
|
+
Post: {
|
|
195
|
+
operations: { find: ["many", "unique"], create: ["one"] },
|
|
196
|
+
fields: { id: ["select"], title: ["select", "create"], body: ["select", "create"] },
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`aventara init` and `aventara new` now write `exposed: true` into `src/aventara.config.ts`, with a comment that
|
|
204
|
+
links the exposure documentation.
|
|
205
|
+
|
|
206
|
+
- 9c52482: A `$connect` to a row that does not exist now answers `404 A2003` (not found), the same as reading a missing row.
|
|
207
|
+
Before, it answered `422 A2004`. This covers a to-one `$connect` in `create`, `update` and `upsert`, a to-many
|
|
208
|
+
`$connect`, and the same writes inside a transaction plan, where `cause.operation` names the step. The message is
|
|
209
|
+
unchanged: "The operation's arguments referenced a record that does not exist." On `update.first`, the same mistake
|
|
210
|
+
answered `A2014` before, as if the row had changed underneath; it now answers `A2003` too, and a duplicate unique value
|
|
211
|
+
written through `update.first` answers `409 A2008` instead of `A3001`.
|
|
212
|
+
|
|
213
|
+
Clearer top-level messages for an unknown Resource or operation. `A2001` now says "The requested Resource is not
|
|
214
|
+
available." and `A2002` says "The requested operation is not available on this Resource.", instead of "Operation
|
|
215
|
+
arguments failed framework validation.", which stays the message of `A2004`, `A2007` and `A2009`. A transaction step
|
|
216
|
+
whose operation is outside the standard grammar now carries the same issue message as a single call, naming the
|
|
217
|
+
operation and the Resource (for example `Operation "dance.one" is not available on Resource "posts".`), instead of
|
|
218
|
+
"Operation is not part of the standard operation grammar."
|
|
219
|
+
|
|
220
|
+
A `$ref` with no `fingerprint` is `V1000` (required value missing) and answers `422 A2004`. Before, it was `V1017`
|
|
221
|
+
(fingerprint mismatch) under `A2007`. A fingerprint that is present but wrong is still `V1017`.
|
|
222
|
+
|
|
223
|
+
`aventara init` and `aventara new` link to the documentation at https://aventara-framework.vercel.app. The old
|
|
224
|
+
address redirects, so links already written into projects keep working.
|
|
225
|
+
|
|
226
|
+
- 8db76e7: `aventara new` and `aventara init` write a `diagnostics` sink into `src/aventara.config.ts`. When something fails
|
|
227
|
+
inside the server, the caller gets a plain 500 and the Nest log said only which request failed; the raw error went to
|
|
228
|
+
a sink the scaffold did not set. Now the sink logs the raw error with its stack through Nest's `Logger` (context
|
|
229
|
+
`Aventara`), at error level, right after that line. Replace it with your own reporter when you have one.
|
|
230
|
+
|
|
231
|
+
- `aventara new` and `aventara init` write `api: '/api'` into `src/aventara.config.ts` (the root key was renamed from
|
|
232
|
+
`entrypoint`).
|
|
233
|
+
|
|
234
|
+
## 1.0.0-rc.0
|
|
235
|
+
|
|
236
|
+
### Major Changes
|
|
237
|
+
|
|
238
|
+
- First release candidate of Aventara 1.0. The public API is now considered stable; changes before 1.0.0 are possible
|
|
239
|
+
based on feedback.
|
|
240
|
+
|
|
241
|
+
### Minor Changes
|
|
242
|
+
|
|
243
|
+
- 24ffbab: `aventara new`, `aventara init`, `avclient init` and `avclient generate` show their steps while they work, so a long
|
|
244
|
+
install never looks hung: on a terminal, `[3/4] Installing dependencies (npm install)` with a spinner and the seconds so
|
|
245
|
+
far, then `✓` or `✗` and its time; in a pipe, in CI or with `NO_COLOR`, one plain line per step (`… done (12.4s)`),
|
|
246
|
+
with no control sequence. A closing line gives the total time. The steps go to stderr; stdout keeps the results and the
|
|
247
|
+
next steps. The output of `nest new` and of the install is shown only when it fails, as before, or as it runs with the
|
|
248
|
+
new `--verbose`.
|
|
249
|
+
|
|
250
|
+
### Patch Changes
|
|
251
|
+
|
|
252
|
+
- 24ffbab: Each package ships its `CHANGELOG.md`, so the release notes are in the installed package beside the README.
|
|
253
|
+
- 24ffbab: `aventara new` prints its next steps last — after the initial commit and the closing line — so they are what is on
|
|
254
|
+
screen when it ends, in a terminal and in a pipe alike.
|
|
255
|
+
|
|
256
|
+
## 0.1.0-pilot.4
|
|
257
|
+
|
|
258
|
+
### Patch Changes
|
|
259
|
+
|
|
260
|
+
- f37dcd6: `@aventara/cli` ships no type declarations: it is a command, run with `npx` or installed globally, and has no API to
|
|
261
|
+
import.
|
|
262
|
+
- f37dcd6: `aventara new` and `aventara init` add `prebuild`, `prestart`, `prestart:dev` and `prestart:debug` scripts that run
|
|
263
|
+
`aventara:prepare`, so after a change to `schema.prisma` the next `npm run start:dev` (or `build`, `start`,
|
|
264
|
+
`start:debug`) regenerates the Prisma client and the discovery artifact before the server compiles — no reinstall
|
|
265
|
+
needed. npm and pnpm both run these hooks. On an existing project, `aventara init` treats a `prebuild` or `prestart*`
|
|
266
|
+
script you already have as a conflict, like any other script: kept unless you confirm, replaced with `--yes`.
|
|
267
|
+
|
|
268
|
+
The scaffold's rate-limiting note in `src/main.ts` links to the docs:
|
|
269
|
+
`https://aventara-docs.vercel.app/docs/limits-and-safety#rate-limiting`.
|
|
270
|
+
|
|
271
|
+
## 0.1.0-pilot.3
|
|
272
|
+
|
|
273
|
+
### Patch Changes
|
|
274
|
+
|
|
275
|
+
- d8c128b: `aventara new` and `aventara init` turn CORS on in `src/main.ts`, so a browser frontend on another port can call the
|
|
276
|
+
API — 0.1.0-pilot.2's scaffold had none, and a browser's preflight got the protocol's answer instead. Right after
|
|
277
|
+
`const app = await NestFactory.create(AppModule);` they add `app.enableCors({ origin: … })` with the comma-separated
|
|
278
|
+
origins in `CORS_ORIGINS`, and write `CORS_ORIGINS="http://localhost:5173,http://localhost:3001"` into the `.env` they
|
|
279
|
+
write. Unset or empty, CORS is **off** (`origin: false`), never open to every origin. A `main.ts` that already
|
|
280
|
+
configures CORS is left as it is, with a warning; one without that line is not edited, and the lines are printed. Over an
|
|
281
|
+
existing Prisma setup, whose `.env` init never writes, adding `CORS_ORIGINS` is printed as a next step. Nest's
|
|
282
|
+
`enableCors` reaches Aventara's routes on Express and Fastify alike: preflights naming the `Aventara-*` headers are
|
|
283
|
+
allowed, and the calls answered with the origin.
|
|
284
|
+
- 05d39e8: `aventara new` ends with the project's first git commit, `Initial Aventara Scaffold`: every scaffolded file and the
|
|
285
|
+
install's lockfile, never `.env` (it is in `.gitignore`). 0.1.0-pilot.2 left a repository with nothing committed. Where
|
|
286
|
+
a commit cannot or should not be made the run still succeeds, saying why in one line: git is not installed, git has no
|
|
287
|
+
`user.name`/`user.email`, or the directory `new` ran in is already inside a git repository (a monorepo), where no
|
|
288
|
+
commit is made in either repository. `--skip-git` creates no repository and makes no commit; `aventara init` never
|
|
289
|
+
commits.
|
|
290
|
+
- 6755a1f: `aventara --help`, `aventara new --help` and `aventara init --help` list the values `--orm`, `--db` and
|
|
291
|
+
`--package-manager` take, read from the adapter catalog: `--orm <prisma7>`, `--db <sqlite|postgresql>`,
|
|
292
|
+
`--package-manager <npm|pnpm>`. A refused `--orm` now names the values it takes — "`--orm takes prisma7 (Prisma 7,
|
|
293
|
+
@aventara/prisma7-adapter)`" — where 0.1.0-pilot.2 said "available: Prisma 7", which is not a value the flag accepts.
|
|
294
|
+
- 2afc651: The `main.ts` lines `aventara new` and `aventara init` add now end with a commented-out, platform-level rate limiter
|
|
295
|
+
and a short note on why: `@nestjs/throttler` is a Nest guard and does not run on Aventara's routes, while Express
|
|
296
|
+
middleware does. Uncommented (after `npm i express-rate-limit` and its import), it limits the protocol's routes and
|
|
297
|
+
answers `429` past the limit. Nothing is installed or enabled by default.
|
|
298
|
+
- `CORS_ORIGINS="http://localhost:5173,http://localhost:3001"` is appended to the project's existing `.env` too — over an existing Prisma setup as well — every other line kept, and never when a line already sets `CORS_ORIGINS`; only a reused setup with no `.env` gets it as a printed next step.
|
|
299
|
+
- `--orm` takes an adapter's alias, `prisma7` (Prisma 7, `@aventara/prisma7-adapter`), declared in the adapter's manifest and listed by `--help`; `prisma@7` and `prisma` are no longer accepted.
|
|
300
|
+
|
|
301
|
+
## 0.1.0-pilot.2
|
|
302
|
+
|
|
303
|
+
### Patch Changes
|
|
304
|
+
|
|
305
|
+
- aa65a0c: `aventara new` and `aventara init` add `.env` to the project's `.gitignore` whenever they write the `DATABASE_URL`
|
|
306
|
+
entry into `.env`. The file is only appended to: existing lines are kept, and nothing is added when `.env` is already
|
|
307
|
+
listed.
|
|
308
|
+
- e69e88d: The published packages are written for the people who use them. The JavaScript ships without comments; the type
|
|
309
|
+
declarations keep their documentation, and it, the error messages, the protocol fixtures' labels and the comments in a
|
|
310
|
+
generated client no longer carry the project's internal development notes.
|
|
311
|
+
- 0ec37d4: `avclient --version` (and `-v`) prints the generator's version and exits 0, and `aventara` now accepts `-v` beside
|
|
312
|
+
`--version`, so both bins answer the same two flags.
|
|
313
|
+
|
|
314
|
+
## 0.1.0-pilot.1
|
|
315
|
+
|
|
316
|
+
### Patch Changes
|
|
317
|
+
|
|
318
|
+
- 7714ef3: The scaffold's e2e test now proves Aventara is mounted. `aventara new` and `aventara init` add a `GET /api/_contract`
|
|
319
|
+
→ 200 test to Nest's `test/app.e2e-spec.ts`, right after its `GET /` test (ESM and CommonJS), when AventaraModule was
|
|
320
|
+
added and the spec is Nest's own; a spec of the developer's own is left untouched.
|
|
321
|
+
- 7f2b5d4: The frontend next step is now a command that runs as printed: `pnpm dlx` under pnpm (not `npx`), and, while the CLI is a
|
|
322
|
+
prerelease, the client's dist-tag read from the CLI's own version — `npx @aventara/client@pilot init` for
|
|
323
|
+
0.1.0-pilot.N — so a frontend gets the client from the same release instead of nothing (`@aventara/client` has no
|
|
324
|
+
`latest`).
|
|
325
|
+
- 799346e: The package READMEs (each package's npm page) are rewritten for the people who use the packages. The Prisma 7
|
|
326
|
+
adapter's README now states the actual behaviour of a to-many relation or `$count` projected on
|
|
327
|
+
`update.many`/`create.many`: a validation refusal, `A2004` with `V1008`, before any statement runs (it said `A3001`).
|
|
328
|
+
Every install or run command names the `@pilot` dist-tag.
|
|
329
|
+
- e58aebf: The READMEs show how to call the API by hand: a copy-pasteable `curl` that fetches `/api/_contract` and sends a
|
|
330
|
+
`find.many` with both identity headers, and runs as printed against a scaffolded server. They also say that
|
|
331
|
+
Resource keys are the model names as written (`/_resources/User/…`, `avClient.User.find.many`), and that list results are
|
|
332
|
+
`readonly` arrays (`readonly User[]`).
|
|
333
|
+
- 88408be: `--help` (and `-h`) works after a command: `aventara new --help`, `aventara init --help`, `avclient init --help` and
|
|
334
|
+
`avclient generate --help` print that command's usage and exit 0, instead of refusing "unknown flag --help". Nothing
|
|
335
|
+
else on the line is read or run.
|
|
336
|
+
|
|
337
|
+
## 0.1.0-pilot.0
|
|
338
|
+
|
|
339
|
+
### Minor Changes
|
|
340
|
+
|
|
341
|
+
- b65a4f8: New package: `@aventara/cli`, bin `aventara`. `aventara new <name>` runs the pinned `nest new` and adds Aventara;
|
|
342
|
+
`aventara init` adds Aventara to an existing NestJS 12 project — the Prisma 7 schema, config and service when there is
|
|
343
|
+
no ORM yet, or the developer's own Prisma setup reused and never written. Every question has a flag; `--yes` accepts the
|
|
344
|
+
defaults; a non-interactive run without the answers refuses before writing anything. The ORMs and databases on offer
|
|
345
|
+
come from the adapter packages' own manifests.
|
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
The `aventara` command: it scaffolds an Aventara server on NestJS 12.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm i -g @aventara/cli@
|
|
6
|
+
npm i -g @aventara/cli@rc # or run it once: npx @aventara/cli@rc <command>
|
|
7
7
|
aventara new my-api # a new NestJS project with Aventara
|
|
8
8
|
aventara init # Aventara added to the NestJS 12 project in this directory
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
It is run once, globally or through `npx`; it is not a dependency of the projects it writes. The frontend's side
|
|
12
|
-
`framework.client.ts` and the generated client
|
|
11
|
+
It is run once, globally or through `npx`; it is not a dependency of the projects it writes. The frontend's side
|
|
12
|
+
(`framework.client.ts` and the generated client) belongs to `@aventara/client` (`npx @aventara/client@rc init`).
|
|
13
13
|
|
|
14
14
|
## `aventara new <name>`
|
|
15
15
|
|
|
@@ -17,22 +17,24 @@ Runs the pinned `nest new` (`npx -y @nestjs/cli@12.0.8 new …`, or `pnpm dlx
|
|
|
17
17
|
`src/app.module.ts` is what that version writes, then runs exactly `aventara init`'s pipeline over the new project and
|
|
18
18
|
installs it. A directory that exists and is not empty is refused before anything runs.
|
|
19
19
|
|
|
20
|
-
Once installed, the project gets its first git commit, `Initial Aventara Scaffold
|
|
21
|
-
(it is in `.gitignore`). No commit is made, and one line says why, when git is not installed, when git has no
|
|
20
|
+
Once installed, the project gets its first git commit, `Initial Aventara Scaffold`, with the lockfile in it and
|
|
21
|
+
without `.env` (it is in `.gitignore`). No commit is made, and one line says why, when git is not installed, when git has no
|
|
22
22
|
`user.name`/`user.email`, or when the directory is already inside a git repository (a monorepo: commit it there).
|
|
23
23
|
`--skip-git` makes no repository and no commit.
|
|
24
24
|
|
|
25
25
|
## `aventara init`
|
|
26
26
|
|
|
27
|
-
Runs inside an existing
|
|
28
|
-
lockfile, a project already initialized
|
|
27
|
+
Runs inside an existing NestJS 12 project. It refuses a directory with no `package.json`, another Nest major,
|
|
28
|
+
TypeScript 7, a yarn-only lockfile, or a project already initialized.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
In a project with no ORM yet, the ORM and the database are asked (or taken from the flags), and everything below is
|
|
31
|
+
written.
|
|
32
|
+
|
|
33
|
+
In a project that already uses Prisma, the installed Prisma must be one an Aventara adapter supports, or the run is
|
|
34
|
+
refused in one sentence with nothing written. Today that is Prisma 7 (`^7.10.0`) on SQLite or PostgreSQL, with a driver
|
|
35
|
+
adapter and the `prisma-client` generator. Your schema, Prisma config and service are reused and never written, and
|
|
36
|
+
your `.env` only gains a `CORS_ORIGINS` line at its end when it has none. Init finds the class extending `PrismaClient`
|
|
37
|
+
and the module exporting it, or asks.
|
|
36
38
|
|
|
37
39
|
### Questions and flags
|
|
38
40
|
|
|
@@ -42,15 +44,20 @@ possible answer is shown, not asked.
|
|
|
42
44
|
|
|
43
45
|
| Flag | Question | Default |
|
|
44
46
|
|---|---|---|
|
|
45
|
-
| `<name>` (`new` only) | The project directory. |
|
|
47
|
+
| `<name>` (`new` only) | The project directory. | required |
|
|
46
48
|
| `--orm <alias>` | The ORM, as an adapter's alias: `prisma7` (Prisma 7, `@aventara/prisma7-adapter`). `--help` lists the aliases that exist. | the only one there is |
|
|
47
49
|
| `--db <provider>` | The database: the adapter's providers (`sqlite`, `postgresql`). | `sqlite` |
|
|
48
|
-
| `--package-manager <npm\|pnpm>` |
|
|
50
|
+
| `--package-manager <npm\|pnpm>` | The package manager. | the lockfile's, else the one that launched the CLI, else npm |
|
|
49
51
|
| `--prisma-service <path#Export>` (`init`) | Your Prisma service, when more than one, or none, is found. | the one found; none → write `src/prisma.service.ts` |
|
|
50
52
|
| `--prisma-module <path#Export>` (`init`) | The Nest module that provides and exports it. | the one found |
|
|
51
53
|
| `--skip-install` | Write the files; print the install instead of running it. | |
|
|
52
54
|
| `--skip-git` (`new`) | Passed to `nest new`. | |
|
|
53
|
-
| `-y`, `--yes` | Accept every unanswered default
|
|
55
|
+
| `-y`, `--yes` | Accept every unanswered default, and replace existing content that differs. | |
|
|
56
|
+
| `--verbose` | Show the output of `nest new` and the install as they run. | |
|
|
57
|
+
|
|
58
|
+
While it works, each step is shown on stderr, such as `[3/4] Installing dependencies (npm install)`. On a terminal it
|
|
59
|
+
has a spinner and the seconds so far; in a pipe, in CI (`CI`) or with `NO_COLOR` it is one plain line per step
|
|
60
|
+
(`… done (12.4s)`). The output of `nest new` and the install is shown only if it fails, or with `--verbose`.
|
|
54
61
|
|
|
55
62
|
Existing content that differs from what would be written (a `.env` key, a script, a file) is listed, and replaced only
|
|
56
63
|
when you confirm, or with `--yes`; where nobody can be asked, the run stops and touches nothing.
|
|
@@ -59,28 +66,28 @@ when you confirm, or with `--yes`; where nobody can be asked, the run stops and
|
|
|
59
66
|
|
|
60
67
|
| Path | Owner afterwards |
|
|
61
68
|
|---|---|
|
|
62
|
-
| `prisma/schema.prisma
|
|
63
|
-
| `prisma.config.ts
|
|
64
|
-
| `src/prisma.service.ts
|
|
65
|
-
| `src/aventara.config.ts
|
|
66
|
-
| `src/app.module.ts
|
|
67
|
-
| `test/app.e2e-spec.ts
|
|
68
|
-
| `src/main.ts
|
|
69
|
+
| `prisma/schema.prisma`: `prisma-client` generating into `src/generated/prisma`, a `User` model | you |
|
|
70
|
+
| `prisma.config.ts`: loads `.env` itself (Prisma does not) | you |
|
|
71
|
+
| `src/prisma.service.ts`: `PrismaService` extending the client with the driver adapter, refusing a missing `DATABASE_URL` before the server listens; `PrismaModule` | you |
|
|
72
|
+
| `src/aventara.config.ts`: `aventaraConfig(prisma)`: the `api` mount path (`/api`), a `diagnostics` sink that logs the raw error of a server failure, with its stack, through Nest's `Logger`, and the `exposed`/`exemptions`, `pipelines` and `resources` you add | you |
|
|
73
|
+
| `src/app.module.ts`: one edit that adds the imports, `PrismaModule` and `AventaraModule.forRootAsync({ … })`; printed instead when the file is not the shape expected | you |
|
|
74
|
+
| `test/app.e2e-spec.ts`: one edit that adds a `GET /api/_contract` → 200 test after Nest's `GET /` test; left alone when the file is not Nest's | you |
|
|
75
|
+
| `src/main.ts`: one edit after `NestFactory.create`: `app.enableCors(…)` for the comma-separated origins in `CORS_ORIGINS`, and CORS off when it is unset or empty, then a commented-out platform-level rate limiter (`express-rate-limit`; Nest's `@nestjs/throttler` is a guard, which does not run on Aventara's routes); left alone when the file already configures CORS, printed when it has no such line | you |
|
|
69
76
|
| `.env` (`DATABASE_URL`, and `CORS_ORIGINS="http://localhost:5173,http://localhost:3001"` appended unless a line sets it), `.gitignore` (`/src/generated/`, `/dev.db*`, `.env`) | you |
|
|
70
|
-
| `package.json
|
|
71
|
-
| `pnpm-workspace.yaml` (pnpm only)
|
|
72
|
-
| `src/generated/prisma/**`, `src/generated/aventara/discovery.artifact.ts` |
|
|
77
|
+
| `package.json`: `@aventara/*` at this CLI's version, `prisma`, `@prisma/client` and the driver at `7.10.0`; `aventara:prepare`, run by `postinstall` and by `prebuild`, `prestart`, `prestart:dev` and `prestart:debug`, and the `.env` on the start scripts and `test:e2e` (`--env-file`, `--env-file-if-exists`) | you |
|
|
78
|
+
| `pnpm-workspace.yaml` (pnpm only): `allowBuilds` for Prisma's and SQLite's install scripts | you |
|
|
79
|
+
| `src/generated/prisma/**`, `src/generated/aventara/discovery.artifact.ts` | generated by `aventara:prepare` (run on every install, build and start, so a schema change needs only a restart); never edit |
|
|
73
80
|
|
|
74
81
|
### Next steps it prints
|
|
75
82
|
|
|
76
83
|
```bash
|
|
77
84
|
npx prisma db push # create the tables (init never touches a database); pnpm: pnpm exec prisma db push
|
|
78
85
|
npm run start:dev # GET http://localhost:3000/api/_contract
|
|
79
|
-
npx @aventara/client@
|
|
86
|
+
npx @aventara/client@rc init # in your frontend; pnpm: pnpm dlx @aventara/client@rc init
|
|
80
87
|
```
|
|
81
88
|
|
|
82
89
|
The frontend command is the package manager's own runner (`npx`, or `pnpm dlx` under pnpm) and, while the CLI is a
|
|
83
|
-
prerelease, carries its dist-tag (`@
|
|
90
|
+
prerelease, carries its dist-tag (`@rc`), so the client comes from the same release.
|
|
84
91
|
|
|
85
92
|
With PostgreSQL, set `DATABASE_URL` in `.env` first (init writes Prisma's placeholder, and never asks for a secret).
|
|
86
93
|
|
|
@@ -106,23 +113,29 @@ curl -s -X POST http://localhost:3000/api/_resources/User/find/many \
|
|
|
106
113
|
Leave out an identity header and the answer is `400 A2000`, naming the header that is missing. The hash changes
|
|
107
114
|
whenever the schema or the configuration does: fetch it again, and regenerate the client.
|
|
108
115
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
116
|
+
Resource keys are the model names, as written. `model User` is `User` everywhere: on the wire
|
|
117
|
+
(`/_resources/User/find/many`) and in the generated client (`avClient.User.find.many({})`). Nothing is renamed,
|
|
118
|
+
lower-cased or pluralized.
|
|
119
|
+
|
|
120
|
+
Results are read-only. A list result is a `readonly` array, so type it `readonly User[]`:
|
|
121
|
+
`const users: readonly User[] = await avClient.User.find.many({});`.
|
|
114
122
|
|
|
115
123
|
## Supported
|
|
116
124
|
|
|
117
|
-
NestJS 12
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
- NestJS 12.
|
|
126
|
+
- Node `^22.18.0 || >=24.2.0`. On any other Node the bin refuses in one sentence naming that range. On Node 22 use
|
|
127
|
+
npm ≥ 11, because Node 22's bundled npm 10 cannot install Nest 12's own scaffold. A CommonJS project's jest e2e needs
|
|
128
|
+
Node ≥ 24.9, which is Nest's and Jest's limit.
|
|
129
|
+
- npm and pnpm. Yarn is not supported.
|
|
130
|
+
- ESM and CommonJS projects.
|
|
131
|
+
- Prisma 7 (`^7.10.0`) on SQLite and PostgreSQL.
|
|
132
|
+
|
|
133
|
+
The ORMs, majors, databases and drivers on offer are generated from the Aventara adapter packages that exist
|
|
134
|
+
(`src/catalog/adapter.catalog.generated.ts`), never listed by hand, so a future `@aventara/prisma8-adapter` appears
|
|
135
|
+
once it exists.
|
|
124
136
|
|
|
125
137
|
## License
|
|
126
138
|
|
|
127
|
-
PolyForm Shield 1.0.0 with an additional permission
|
|
128
|
-
use it to build a product that competes with Aventara itself. See `LICENSE` and
|
|
139
|
+
PolyForm Shield 1.0.0 with an additional permission. It is free to use, including in commercial applications, but you
|
|
140
|
+
may not use it to build a product that competes with Aventara itself. See `LICENSE` and
|
|
141
|
+
`LICENSE-ADDITIONAL-PERMISSION.md`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export class
|
|
2
|
-
name = "
|
|
1
|
+
export class AvConflictsNotConfirmedError extends Error {
|
|
2
|
+
name = "AvConflictsNotConfirmedError";
|
|
3
3
|
}
|
|
4
4
|
export function conflictWarning(conflicts) {
|
|
5
5
|
return `${conflicts.join(", ")} already ${conflicts.length === 1 ? "has" : "have"} other content, and initializing will replace ${conflicts.length === 1 ? "it" : "them"}`;
|
|
@@ -10,7 +10,7 @@ export async function confirmConflicts(conflicts, consent) {
|
|
|
10
10
|
}
|
|
11
11
|
const them = conflicts.length === 1 ? "it" : "them";
|
|
12
12
|
if (!consent.prompter.interactive) {
|
|
13
|
-
throw new
|
|
13
|
+
throw new AvConflictsNotConfirmedError(`not initializing, and nothing was touched: ${conflicts.join(", ")} already ${conflicts.length === 1 ? "has" : "have"} other content and nobody can be asked (stdin is not a terminal); run \`aventara init --yes\` to replace ${them}`);
|
|
14
14
|
}
|
|
15
15
|
consent.prompter.say(`aventara: warning: ${conflictWarning(conflicts)}.`);
|
|
16
16
|
const answer = await consent.prompter.ask({
|
|
@@ -19,6 +19,6 @@ export async function confirmConflicts(conflicts, consent) {
|
|
|
19
19
|
defaultValue: "N",
|
|
20
20
|
});
|
|
21
21
|
if (!/^y(?:es)?$/i.test(answer.trim())) {
|
|
22
|
-
throw new
|
|
22
|
+
throw new AvConflictsNotConfirmedError(`cancelled, and nothing was touched; run again and answer yes to replace ${them}`);
|
|
23
23
|
}
|
|
24
24
|
}
|
package/dist/cli.js
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
2
|
+
import { AvConflictsNotConfirmedError } from "./apply/conflict.confirmer.js";
|
|
3
3
|
import { ADAPTER_CATALOG } from "./catalog/adapter.catalog.generated.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { AvCliCommandError, commandUsage, parseCliCommand, usage, } from "./command/command.parser.js";
|
|
5
|
+
import { AvProjectRefusedError } from "./project/project.inspector.js";
|
|
6
6
|
import { runCommand } from "./run/command.runner.js";
|
|
7
|
-
import {
|
|
7
|
+
import { AvInstallFailedError, runInit } from "./run/init.orchestrator.js";
|
|
8
8
|
import { runNew } from "./run/new.orchestrator.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { progressModeOf } from "./run/step.reporter.js";
|
|
10
|
+
import { AvUnansweredQuestionsError, } from "./wizard/answer.resolver.js";
|
|
11
|
+
import { AvWizardCancelledError, createReadlinePrompter, } from "./wizard/readline.prompter.js";
|
|
12
|
+
import { AvWizardAnswerError } from "./wizard/wizard.questions.js";
|
|
12
13
|
const REFUSALS = [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
AvCliCommandError,
|
|
15
|
+
AvWizardAnswerError,
|
|
16
|
+
AvUnansweredQuestionsError,
|
|
17
|
+
AvWizardCancelledError,
|
|
18
|
+
AvProjectRefusedError,
|
|
19
|
+
AvConflictsNotConfirmedError,
|
|
20
|
+
AvInstallFailedError,
|
|
20
21
|
];
|
|
21
22
|
const MANIFEST = new URL("../package.json", import.meta.url);
|
|
22
23
|
function ownVersion() {
|
|
@@ -47,6 +48,7 @@ export async function runCli(argv, io) {
|
|
|
47
48
|
runner: io.runner ?? runCommand,
|
|
48
49
|
catalog: io.catalog ?? ADAPTER_CATALOG,
|
|
49
50
|
aventaraVersion: ownVersion(),
|
|
51
|
+
progressMode: io.progress ?? "off",
|
|
50
52
|
});
|
|
51
53
|
}
|
|
52
54
|
return await runNew(command, {
|
|
@@ -58,6 +60,7 @@ export async function runCli(argv, io) {
|
|
|
58
60
|
runner: io.runner ?? runCommand,
|
|
59
61
|
catalog: io.catalog ?? ADAPTER_CATALOG,
|
|
60
62
|
aventaraVersion: ownVersion(),
|
|
63
|
+
progressMode: io.progress ?? "off",
|
|
61
64
|
});
|
|
62
65
|
}
|
|
63
66
|
catch (error) {
|
|
@@ -85,6 +88,7 @@ export async function runFromProcess() {
|
|
|
85
88
|
process.stderr.write(text);
|
|
86
89
|
},
|
|
87
90
|
prompter,
|
|
91
|
+
progress: progressModeOf(process.stderr.isTTY, process.env),
|
|
88
92
|
});
|
|
89
93
|
}
|
|
90
94
|
finally {
|