@aventara/client 1.0.0-rc.0 → 1.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +504 -0
  2. package/README.md +159 -141
  3. package/dist/cli/command.parser.d.ts +5 -5
  4. package/dist/cli/command.parser.js +13 -13
  5. package/dist/cli/generation-failure.renderer.js +24 -24
  6. package/dist/config/client-config.interface.d.ts +7 -7
  7. package/dist/config/config.loader.d.ts +10 -4
  8. package/dist/config/config.loader.js +16 -7
  9. package/dist/config/config.resolver.d.ts +7 -7
  10. package/dist/config/config.resolver.js +12 -12
  11. package/dist/config/env.cascade.d.ts +2 -2
  12. package/dist/config/env.cascade.js +4 -4
  13. package/dist/config/module-style.resolver.d.ts +1 -1
  14. package/dist/config/module-style.resolver.js +2 -2
  15. package/dist/config/tsconfig.locator.d.ts +1 -1
  16. package/dist/config/tsconfig.locator.js +2 -2
  17. package/dist/contract/contract.acceptance.d.ts +4 -4
  18. package/dist/contract/contract.acceptance.js +4 -4
  19. package/dist/contract/contract.fetcher.d.ts +3 -3
  20. package/dist/contract/contract.fetcher.js +6 -6
  21. package/dist/contract/contract.loader.d.ts +6 -6
  22. package/dist/contract/contract.loader.js +2 -2
  23. package/dist/emit/client-surface.emitter.d.ts +2 -2
  24. package/dist/emit/client-surface.emitter.js +61 -40
  25. package/dist/emit/client-tree.emitter.d.ts +3 -3
  26. package/dist/emit/client-tree.emitter.js +8 -5
  27. package/dist/emit/contract-carrier.emitter.d.ts +3 -3
  28. package/dist/emit/derivation.emitter.js +1 -0
  29. package/dist/emit/descriptor.emitter.d.ts +2 -2
  30. package/dist/emit/descriptor.emitter.js +1 -1
  31. package/dist/emit/emitted-tree.interface.d.ts +1 -1
  32. package/dist/emit/enum.emitter.d.ts +2 -2
  33. package/dist/emit/field-check.emitter.d.ts +21 -0
  34. package/dist/emit/field-check.emitter.js +592 -0
  35. package/dist/emit/json-shape.emitter.d.ts +2 -2
  36. package/dist/emit/name.deriver.d.ts +6 -6
  37. package/dist/emit/name.deriver.js +14 -14
  38. package/dist/emit/named-type.emitter.d.ts +2 -2
  39. package/dist/emit/runtime.emitter.d.ts +2 -2
  40. package/dist/emit/runtime.emitter.js +166 -112
  41. package/dist/emit/scalar.codec.d.ts +1 -1
  42. package/dist/emit/scalar.codec.js +246 -29
  43. package/dist/emit/transaction.emitter.js +30 -14
  44. package/dist/generate.d.ts +2 -2
  45. package/dist/generate.js +2 -2
  46. package/dist/index.d.ts +1 -1
  47. package/dist/init/client-init.errors.d.ts +4 -4
  48. package/dist/init/client-init.errors.js +4 -4
  49. package/dist/init/client-init.orchestrator.js +7 -7
  50. package/dist/init/client-init.questions.d.ts +5 -5
  51. package/dist/init/client-init.questions.js +10 -10
  52. package/dist/init/client-project.inspector.d.ts +2 -2
  53. package/dist/init/client-project.inspector.js +3 -3
  54. package/dist/output/output.validator.d.ts +3 -4
  55. package/dist/output/output.validator.js +2 -2
  56. package/dist/output/output.writer.d.ts +5 -5
  57. package/dist/output/output.writer.js +12 -12
  58. package/package.json +4 -4
package/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  The development-time generator for Aventara typed remote clients. It fetches a deployed framework's `ClientContract`
4
4
  from `<entrypoint>/_contract`, verifies it, and writes a standalone TypeScript client into your project. The generated
5
- client is the product: it imports nothing from this package, or from `@aventara/core`, at runtime.
5
+ client is what you ship: at runtime it imports nothing from this package or from `@aventara/core`.
6
6
 
7
- Every argument and result type in the generated client is core's own derivation — `@aventara/core`'s published
8
- declarations, copied into the tree and instantiated over the deployment's ClientContract — so the client agrees with the
9
- server by construction.
7
+ Every argument and result type in the generated client is core's own derivation: `@aventara/core`'s published
8
+ declarations, copied into the tree and instantiated over the deployment's ClientContract. The client's types therefore
9
+ match the server's.
10
10
 
11
11
  ## Setting up a frontend: `avclient init`
12
12
 
@@ -14,7 +14,7 @@ server by construction.
14
14
  npx @aventara/client@rc init # in your frontend project; pnpm: pnpm dlx @aventara/client@rc init
15
15
  ```
16
16
 
17
- It asks — or takes from flags, or with `--yes` takes every default — four things, and writes the setup:
17
+ It asks four things and writes the setup. Each answer can also come from a flag, and `--yes` takes every default:
18
18
 
19
19
  | Flag | Question | Default |
20
20
  |---|---|---|
@@ -23,14 +23,16 @@ It asks — or takes from flags, or with `--yes` takes every default — four th
23
23
  | `--generate-at <dir>` | Where the client goes | `./src/api` |
24
24
  | `--package-manager <npm\|pnpm>` | | the lockfile's, else the launching one, else npm |
25
25
 
26
- It writes `framework.client.ts` (or reuses the config the project already has), the variable into `.env` (only with a variable), `"avclient:generate": "avclient
27
- generate"` into the scripts and `@aventara/client` as an **exact** devDependency at its own version; runs the install
28
- (`--skip-install` prints it instead); and generates the client right away (`--skip-generate` to skip; a server that does
29
- not answer leaves the files and names `avclient generate`; `--verbose` shows the install's output as it runs). Each step
30
- — the install, loading the config, fetching the ClientContract, emitting, checking and writing — is shown on stderr
31
- while it runs: live on a terminal, one plain line per step in a pipe, in CI or with `NO_COLOR`. Existing content that differs is listed and replaced only
32
- when you confirm, or with `--yes`; where nobody can be asked, nothing is touched. The generated tree is meant to be
33
- committed: regenerating it needs a running server.
26
+ It writes `framework.client.ts` (or reuses the config the project already has), the variable into `.env` (only with a
27
+ variable), `"avclient:generate": "avclient generate"` into the scripts, and `@aventara/client` as an exact
28
+ devDependency at its own version. Then it runs the install (`--skip-install` prints it instead) and generates the
29
+ client right away (`--skip-generate` skips that). A server that does not answer leaves the files in place and names
30
+ `avclient generate`; `--verbose` shows the install's output as it runs.
31
+
32
+ Each step (the install, loading the config, fetching the ClientContract, emitting, checking and writing) is shown on
33
+ stderr while it runs: live on a terminal, and one plain line per step in a pipe, in CI or with `NO_COLOR`. Existing
34
+ content that differs is listed and replaced only when you confirm, or with `--yes`; where nobody can be asked, nothing
35
+ is touched. Commit the generated tree, because regenerating it needs a running server.
34
36
 
35
37
  ## Generating a client
36
38
 
@@ -52,19 +54,26 @@ and run:
52
54
  npx avclient generate [--yes]
53
55
  ```
54
56
 
55
- **The config file** is loaded the way Prisma 7 loads `prisma.config.ts`: `framework.client.ts` — or `.mts`, `.cts`,
56
- `.js`, `.mjs`, `.cjs` — in ES module or CommonJS syntax, whatever your `package.json`'s `"type"`. A
57
- `framework.client.mts` written by 0.1.0-pilot.1 keeps working, and `avclient init` reuses it. Two config files in one
58
- directory are refused, naming both.
59
-
60
- - **`entrypoint`** is the deployment's framework entrypoint: origin plus mount path, one absolute `http(s)` URL. It
61
- may not carry credentials — the platform's `fetch` refuses such a URL, and the entrypoint ships inside the client.
62
- - **`generateAt`** is a directory you may share with your own files. The generator owns exactly two entries in it —
63
- `AvClient.ts` and `generated/` — and never reads, moves or removes anything else there.
57
+ The config file is loaded the way Prisma 7 loads `prisma.config.ts`: `framework.client.ts` (or `.mts`, `.cts`, `.js`,
58
+ `.mjs`, `.cjs`), in ES module or CommonJS syntax, whatever your `package.json`'s `"type"`. A `framework.client.mts`
59
+ written by 0.1.0-pilot.1 keeps working, and `avclient init` reuses it. Two config files in one directory are refused,
60
+ naming both.
61
+
62
+ - `entrypoint` is the deployment's framework entrypoint: origin plus mount path, one absolute `http(s)` URL. It may
63
+ not carry credentials, because the platform's `fetch` refuses such a URL and the entrypoint ships inside the client.
64
+ - `generateAt` is a directory you may share with your own files. The generator owns exactly two entries in it,
65
+ `AvClient.ts` and `generated/`, and never reads, moves or removes anything else there.
66
+ - The `.env` cascade is read from the current directory before the config is evaluated, highest precedence first: the
67
+ process environment, `.env.<mode>.local`, `.env.<mode>`, `.env.local`, `.env`; `mode` is `NODE_ENV`, or
68
+ `development`. The config reads it through `env("NAME")`; `process.env` is never written.
69
+ - Content the generator did not produce in `AvClient.ts` or `generated/` is listed, and you are asked before it is
70
+ overwritten or removed. That includes what a killed run left in the `generated/` it moved aside. `--yes` answers yes.
71
+ Where nobody can answer (stdin is not a terminal, as in CI), the run is refused and nothing is touched. A cancelled
72
+ or refused run still prints every warning it raised.
64
73
 
65
74
  ### Your tsconfig decides how the client is written
66
75
 
67
- The client is TypeScript source that **your own toolchain compiles**, like your own files — `tsc`, Next.js (Turbopack or
76
+ The client is TypeScript source that your own toolchain compiles, like your own files: `tsc`, Next.js (Turbopack or
68
77
  webpack), Vite, `tsx`, Node's type stripping. Its modules import each other the way your project imports its files,
69
78
  read from your `tsconfig.json` (the nearest one above `generateAt`, `extends` included) by Prisma 7's rules:
70
79
 
@@ -72,7 +81,7 @@ read from your `tsconfig.json` (the nearest one above `generateAt`, `extends` in
72
81
  |---|---|---|
73
82
  | `allowImportingTsExtensions` or `rewriteRelativeImportExtensions` (Vite's react-ts template, Node's type stripping) | `./generated/client.ts` | `./api/AvClient.ts` |
74
83
  | `moduleResolution: "bundler"` (Next.js) or `module: "commonjs"` | `./generated/client` | `./api/AvClient` |
75
- | anything else — `module: "nodenext"`/`"node16"` (NestJS 12) | `./generated/client.js` | `./api/AvClient.js` (ES module) or `./api/AvClient` (CommonJS) |
84
+ | anything else, such as `module: "nodenext"`/`"node16"` (NestJS 12) | `./generated/client.js` | `./api/AvClient.js` (ES module) or `./api/AvClient` (CommonJS) |
76
85
 
77
86
  Under `nodenext`, the nearest `package.json`'s `"type"` decides whether the client is checked as an ES module or as
78
87
  CommonJS. Vite's `tsconfig.json`, which only lists `references`, defers to the referenced project that includes the
@@ -80,29 +89,25 @@ client (`tsconfig.app.json`). The validate step type-checks the client in your p
80
89
 
81
90
  A project with no `tsconfig.json` is refused: the generated client is TypeScript, and JavaScript projects are not
82
91
  supported yet.
83
- - **The `.env` cascade** is read from the current directory before the config is evaluated, highest precedence first:
84
- the process environment, `.env.<mode>.local`, `.env.<mode>`, `.env.local`, `.env`; `mode` is `NODE_ENV`, or
85
- `development`. The config reads it through `env("NAME")`; `process.env` is never written.
86
- - **Content the generator did not produce** in `AvClient.ts` or `generated/` is listed and you are asked before it is
87
- overwritten or removed — including what a killed run left in the `generated/` it moved aside. `--yes` answers yes.
88
- Where nobody can answer — stdin is not a terminal, as in CI — the run is refused and nothing is touched. A cancelled
89
- or refused run still prints every warning it raised.
90
92
 
91
93
  ### What a run guarantees
92
94
 
93
- - **A failed run leaves the previous output exactly as it was.** The whole tree is written to a staging directory inside
94
- `generateAt`, validated there as one program, and only then moved into place. A run killed part-way is repaired by the
95
- next run that writes, before it writes anything. A first run that fails removes the directories it created.
96
- - **The validate step is a real type check** when your project has the optional peer `typescript` (5.5 to 6) — it is
97
- resolved from your project, so `npx @aventara/client@rc init` type-checks too — under a consumer's strictest plausible
98
- settings and with nothing outside the tree resolvable. Without it — or under
99
- TypeScript 7, which has no classic compiler API to check with — the check degrades to a parse with Node's own
100
- TypeScript parser, and says so loudly in one warning; it is never skipped, and the client is still written. The peer
101
- range has no upper bound, so a frontend on TypeScript 7 installs it; your own `tsc` checks the tree when it compiles.
102
- - **Determinism.** Two runs over one ClientContract write the same bytes; deleting the client and regenerating it gives
103
- the same bytes. Nothing volatile — no timestamp, generator version, host or path — is emitted.
104
- - **Failure is a sentence.** A refusal is one line and exit code 1, with no stack; anything the run warned about before
105
- it stopped is printed first. A stack means a defect in the generator.
95
+ A failed run leaves the previous output exactly as it was. The whole tree is written to a staging directory inside
96
+ `generateAt`, validated there as one program, and only then moved into place. A run killed part-way is repaired by the
97
+ next run that writes, before it writes anything. A first run that fails removes the directories it created.
98
+
99
+ The validate step is a real type check when your project has the optional peer `typescript` (5.7 to 6). It is resolved
100
+ from your project, so `npx @aventara/client@rc init` type-checks too, under a consumer's strictest plausible settings
101
+ and with nothing outside the tree resolvable. Without it, or under TypeScript 7 (which has no classic compiler API to
102
+ check with), the check falls back to a parse with Node's own TypeScript parser and says so in one warning. It is never
103
+ skipped, and the client is still written. The peer range has no upper bound, so a frontend on TypeScript 7 installs
104
+ it; your own `tsc` checks the tree when it compiles.
105
+
106
+ Two runs over one ClientContract write the same bytes, and deleting the client and regenerating it gives the same
107
+ bytes. Nothing volatile (no timestamp, generator version, host or path) is emitted.
108
+
109
+ A refusal is one line and exit code 1, with no stack; anything the run warned about before it stopped is printed
110
+ first. A stack means a defect in the generator.
106
111
 
107
112
  ## Calling the API
108
113
 
@@ -115,22 +120,22 @@ const one: User = await avClient.User.find.unique({ where: { id } });
115
120
  const maybe = await avClient.User.find.first({ where }); // User | null — a miss is null
116
121
  ```
117
122
 
118
- - **`avClient`** is the default export: the client of the deployment the tree was generated from.
119
- `new AvClient({ entrypoint, fetch })` makes another — `entrypoint` for a deployment serving **exactly the same**
120
- ClientContract (tests, SSR), `fetch` for a custom fetch.
121
- - **One property per Resource**, one per family under it, one per advertised variant: only what the ClientContract
122
- advertises exists, in the type and at runtime. A Resource named like one of the client's own members (`tx`,
123
- `transaction`, `then`, `constructor`, `Object.prototype`'s names) is reached as `<name>Model`, with one warning; its
124
- wire name is unchanged.
125
- - **A call resolves to the data**: the record, a list, a count; a first-style miss (`A1001`) is `null`. Every other
126
- outcome throws — the `FrameworkError` subclass of its code (`NotFoundError` for a strict-unique miss,
127
- `ContractMismatchError` for a stale client, …), or `TransportError` when no framework answer arrived.
128
- - **Values are application values**: `bigint`, `Date`, `Uint8Array`, the client's `Decimal`, JSON values as they are.
129
- Arguments are encoded and results revived for you; a value that cannot be read throws `TransportError`.
130
- - **Per-call options** (`CallOptions`), never sent in the body: `signal` (an abort rejects with what `fetch` rejected
123
+ - `avClient` is the default export: the client of the deployment the tree was generated from.
124
+ `new AvClient({ entrypoint, fetch })` makes another. `entrypoint` is for a deployment serving exactly the same
125
+ ClientContract (tests, SSR), and `fetch` is for a custom fetch.
126
+ - The client has one property per Resource, one per family under it, and one per advertised variant. Only what the
127
+ ClientContract advertises exists, in the type and at runtime. A Resource named like one of the client's own members
128
+ (`tx`, `transaction`, `then`, `constructor`, `Object.prototype`'s names) is reached as `<name>Model`, with one
129
+ warning; its wire name is unchanged.
130
+ - A call resolves to the data: the record, a list, a count. A first-style miss (`A1001`) is `null`. Every other
131
+ outcome throws the `AvError` subclass of its code (`AvNotFoundError` for a strict-unique miss,
132
+ `AvContractMismatchError` for a stale client, …), or `AvTransportError` when no framework answer arrived.
133
+ - Values are application values: `bigint`, `Date`, `Uint8Array`, the client's `AvDecimal`, JSON values as they are.
134
+ Arguments are encoded and results revived for you; a value that cannot be read throws `AvTransportError`.
135
+ - Per-call options (`AvCallOptions`) are never sent in the body: `signal` (an abort rejects with what `fetch` rejected
131
136
  with), `headers` (the framework's own identity headers are refused), and `requestId`, sent as `Aventara-Request-Id`
132
137
  and winning over that header in `headers`.
133
- - **Named types** (`User`, `UserWhere`, `UserUniqueWhere`, `UserOrderBy`, `UserCreateData`, `UserUpdateData`,
138
+ - The named types (`User`, `UserWhere`, `UserUniqueWhere`, `UserOrderBy`, `UserCreateData`, `UserUpdateData`,
134
139
  `UserSelect`, `UserInclude`) are named exports, each only when the operation it reads is advertised. A name that would
135
140
  clash walks the rename ladder (`User` beside a Resource `UserWhere` becomes `UserModel`), one warning each.
136
141
 
@@ -145,33 +150,37 @@ const profile = avClient.tx.Profile.create.one({ data: { bio: "…", userId: cre
145
150
  const [user, row] = await avClient.transaction([created, profile]);
146
151
  ```
147
152
 
148
- A `$ref` stands for one field of an earlier step's result, and may be written in a step's data at any depth (a field's
153
+ A `$ref` stands for one field of an earlier step's result. It may be written in a step's data at any depth (a field's
149
154
  value, the key a relation's `$connect`, `$disconnect`, `$set`, `$connectOrCreate` or `$upsert` names, the `where` of a
150
- nested `$update` or `$delete`, and the data `$create`, `$update` and `$upsert` nest), its `cursor`, and anywhere in its
151
- `where` — `AND`/`OR`/`NOT` and relation filters included. A projection's nested `where` and `orderBy` take none:
155
+ nested `$update` or `$delete`, and the data `$create`, `$update` and `$upsert` nest), in its `cursor`, and anywhere in
156
+ its `where`, `AND`/`OR`/`NOT` and relation filters included. A projection's nested `where` and `orderBy` take none:
152
157
 
153
158
  ```ts
154
159
  const post = avClient.tx.Post.create.one({ data: { title: "Hi", author: { $connect: { id: created.$ref("id") } } } });
155
160
  const mine = avClient.tx.Post.find.many({ where: { OR: [{ authorId: created.$ref("id") }, { title: { startsWith: created.$ref("email") } }] } });
156
161
  ```
157
162
 
158
- Its source field must have the same type as where it is written: for a filter's value, `equals` and `not`, a nullable
159
- source only where the field is nullable, and a `null` then matches the rows where the field is unset, as a literal `null`
160
- does; for a unique key, a `cursor`, a comparison or a text operator, a non-null source; for `in`, `notIn`, `hasEvery` and
161
- `hasSome`, a list field; for `has`, one value. A mismatch is a type error, and the server refuses it before anything runs
162
- (`A2007`/`V1011`).
163
+ Its source field must have the same type as where it is written:
164
+
165
+ - for a filter's value, `equals` and `not`, a nullable source only where the field is nullable, and a `null` then
166
+ matches the rows where the field is unset, as a literal `null` does;
167
+ - for a unique key, a `cursor`, a comparison or a text operator, a non-null source;
168
+ - for `in`, `notIn`, `hasEvery` and `hasSome`, a list field;
169
+ - for `has`, one value.
170
+
171
+ A mismatch is a type error, and the server refuses it before anything runs (`A2007`/`V1011`).
163
172
 
164
173
  `avClient.tx.…` builds a handle and sends nothing. `avClient.transaction([...])` sends the plan once and resolves one
165
174
  result per handle, typed, in order; a failing step throws its error with `cause.operation` naming the step. `$ref` binds
166
175
  to the handle's position in the list it runs with. Two mistakes are refused before anything is sent, as core refuses
167
- them in process: one handle twice (`ValidationError`, `A2004`/`V1001`), and a `$ref` to a handle not in the list
168
- (`ValidationError`, `A2007`/`V1010`). A handle is plain data: any client of the same generated tree may run it.
176
+ them in process: one handle twice (`AvValidationError`, `A2004`/`V1001`), and a `$ref` to a handle not in the list
177
+ (`AvValidationError`, `A2007`/`V1010`). A handle is plain data: any client of the same generated tree may run it.
169
178
 
170
179
  ## Custom fetch and authentication
171
180
 
172
- `fetch` is typed as **your platform's own `fetch`** when your `lib` declares one (DOM, `@types/node`), so the platform
173
- `fetch` is accepted without a cast; with neither, a structural fetch type stands in. The client never adds headers of its
174
- own beyond the protocol's; authentication is a custom fetch:
181
+ `fetch` is typed as your platform's own `fetch` when your `lib` declares one (DOM, `@types/node`), so the platform
182
+ `fetch` is accepted without a cast; with neither, a structural fetch type stands in. The client adds no headers of its
183
+ own beyond the protocol's, so authentication goes in a custom fetch:
175
184
 
176
185
  ```ts
177
186
  const client = new AvClient({
@@ -179,107 +188,116 @@ const client = new AvClient({
179
188
  });
180
189
  ```
181
190
 
182
- The platform `fetch` is read when a call is made, not when the client is imported, so importing the client never fails
183
- where there is none — a call does, saying why. An entrypoint carrying credentials is refused when the config is
191
+ The platform `fetch` is read when a call is made, not when the client is imported. Importing the client never fails
192
+ where there is no `fetch`; a call does, saying why. An entrypoint carrying credentials is refused when the config is
184
193
  resolved.
185
194
 
186
195
  ## Up to date
187
196
 
188
- A rerun asks the deployment conditionally (`If-None-Match`, the ClientContract hash of the output it
189
- finds — only when that output is intact and its carrier re-verifies). On `304 Not Modified` it re-emits from that
190
- stored ClientContract with **this** generator and **this** entrypoint: identical bytes print
197
+ A rerun asks the deployment conditionally (`If-None-Match`, the ClientContract hash of the output it finds), but only
198
+ when that output is intact and its carrier re-verifies. On `304 Not Modified` it re-emits from that stored
199
+ ClientContract with this generator and this entrypoint. Identical bytes print
191
200
  `avclient: up to date: … nothing was written.` and exit 0; otherwise the output is replaced, and the line says
192
201
  `the ClientContract is unchanged; the generator or the entrypoint changed`.
193
202
 
194
203
  ## What is emitted
195
204
 
196
205
  Every file opens with a banner stating that it is generated and must not be edited, and with the Biome suppressions a
197
- generated file needs — emitted, not configured, so the output stays correct in a repository whose formatter nobody here
198
- chose. The `.d.ts` files are core's copied declarations under `generated/derivation/` and the named types in
199
- `generated/types.d.ts`; the `.ts` sources carry their own types.
200
-
201
- - `AvClient.ts` — the entry point you import: `avClient` (the default export), `AvClient`, `AvClientOptions`,
202
- `CallOptions`, `Fetch`, `Operation`, the named Resource types, the enums, `Decimal`, `FrameworkError` and its
203
- subclasses, `TransportError`, and the types `Cause`, `ValidationIssue`, `OperationCode` and `ValidationCode`.
204
- - `generated/client.ts` — the typed client: the class `AvClient`, `AvClientOptions`, `CallOptions` and the ready
205
- instance `avClient`; every argument and result type an instantiation of core's own derivation.
206
- - `generated/contract.ts` — the ClientContract the client was generated against: its served canonical bytes, verbatim,
206
+ generated file needs. They are emitted rather than configured, so the output stays correct whatever formatter the
207
+ repository uses. The `.d.ts` files are core's copied declarations under `generated/derivation/`
208
+ and the named types in `generated/types.d.ts`; the `.ts` sources carry their own types.
209
+
210
+ - `AvClient.ts`: the entry point you import: `avClient` (the default export), `AvClient`, `AvClientOptions`,
211
+ `AvCallOptions`, `AvFetch`, `AvOperation`, the named Resource types, the enums, `AvDecimal`, `AvError` and its
212
+ subclasses, `AvTransportError`, and the types `AvCause`, `AvValidationIssue`, `AvOperationCode` and `AvValidationCode`.
213
+ - `generated/client.ts`: the typed client: the class `AvClient`, `AvClientOptions`, `AvCallOptions` and the ready
214
+ instance `avClient`. Every argument and result type is an instantiation of core's own derivation.
215
+ - `generated/contract.ts`: the ClientContract the client was generated against, its served canonical bytes verbatim,
207
216
  as the type `ClientContractShape` (no runtime byte).
208
- - `generated/derivation/` — `@aventara/core`'s published declarations of the argument/result derivation and the
209
- call grammar, copied byte for byte (bannered, source-map trailer dropped): the generated types are core's own.
210
- - `generated/enums.ts` — each enum as a union type and a same-named `as const` object.
211
- - `generated/metadata.ts` — the ClientContract hash and protocol version the client is bound to, and the entrypoint it
212
- was generated from, its default deployment (an entrypoint carrying credentials is refused at resolution).
213
- - `generated/runtime/codec.ts` — the nine scalars' wire codecs, and the request-body serializer.
214
- - `generated/runtime/decimal.ts` — the framework's `Decimal`: a constructor, `toString` and `toJSON`.
215
- - `generated/runtime/descriptor.ts` — what the runtime reads of the contract: the result fields it revives and the
217
+ - `generated/derivation/`: `@aventara/core`'s published declarations of the argument/result derivation and the call
218
+ grammar, copied byte for byte (bannered, source-map trailer dropped), so the generated types are core's own.
219
+ - `generated/enums.ts`: each enum as a union type and a same-named `as const` object.
220
+ - `generated/metadata.ts`: the ClientContract hash and protocol version the client is bound to, and the entrypoint it
221
+ was generated from, which is its default deployment (an entrypoint carrying credentials is refused at resolution).
222
+ - `generated/runtime/codec.ts`: the nine scalars' wire codecs, and the request-body serializer.
223
+ - `generated/runtime/decimal.ts`: the framework's `AvDecimal`, with a constructor, `toString` and `toJSON`.
224
+ - `generated/runtime/descriptor.ts`: what the runtime reads of the contract: the result fields it revives and the
216
225
  relations it follows (the decode table), and the advertised operations.
217
- - `generated/runtime/errors.ts` — `FrameworkError`, its subclasses by code class (`AuthError`, `ConflictError`,
218
- `ContractMismatchError`, `InternalError`, `NotFoundError`, `ProtocolError`, `ValidationError`), `TransportError`, and
219
- the code unions, emitted from core's code arrays rather than retyped.
220
- - `generated/runtime/fingerprint.ts` — the operation fingerprint (RFC 8785 canonical JSON, SHA-256,
221
- base64url), dependency-free and pinned to core's; emitted only when transactions are `interactive`.
222
- - `generated/runtime/transaction.ts` — `avClient.tx`'s deferred handles and `avClient.transaction`'s plan assembly,
223
- refusals and one POST to `/_transactions`; emitted only when transactions are `interactive`.
224
- - `generated/runtime/transport.ts` — one POST per operation with the identity headers, and the response read by the
225
- protocol's rule; arguments encoded by value, results revived by the decode table, the platform's own `fetch` type accepted.
226
- Its `execute` primitive is module-private: no public file re-exports it; `AvClient`'s methods wrap it.
227
- - `generated/types.d.ts` — the named Resource types, as a declaration file (`User`, `UserWhere`,
228
- `UserUniqueWhere`, `UserOrderBy`, `UserCreateData`, `UserUpdateData`, `UserSelect`, `UserInclude`), each only when
229
- its operation is advertised.
230
-
231
- The tree is self-contained: every import in it names a file in it, so it compiles with nothing else installed. Names a TypeScript
232
- declaration cannot carry as-is (a reserved word, a name the runtime owns, an enum sharing a Resource's name) are
233
- renamed, with one warning each; the wire name is kept.
226
+ - `generated/runtime/errors.ts`: `AvError`, its subclasses by code class (`AvAuthError`, `AvConflictError`,
227
+ `AvContractMismatchError`, `AvInternalError`, `AvNotFoundError`, `AvProtocolError`, `AvValidationError`),
228
+ `AvTransportError`, and the code unions, emitted from core's code arrays instead of retyped.
229
+ - `generated/runtime/field-check.evaluator.ts`: core's own field-check evaluator, copied byte for byte with its
230
+ imports pointed at `generated/derivation/`. Emitted only when the ClientContract carries a check (a field's
231
+ `validators`, or a check on a Json Shape node).
232
+ - `generated/runtime/field-validators.ts`: the ClientContract's checks as data, and the walk that runs them on an
233
+ operation's arguments before anything is sent. A failure throws the server's `A2004` with the same issues. A check
234
+ the client cannot judge exactly as the server would is left to the server. Emitted only when the ClientContract
235
+ carries a check.
236
+ - `generated/runtime/fingerprint.ts`: the operation fingerprint (RFC 8785 canonical JSON, SHA-256, base64url),
237
+ dependency-free and pinned to core's. Emitted only when transactions are `interactive`.
238
+ - `generated/runtime/transaction.ts`: `avClient.tx`'s deferred handles and `avClient.transaction`'s plan assembly,
239
+ refusals and one POST to `/_transactions`. Emitted only when transactions are `interactive`.
240
+ - `generated/runtime/transport.ts`: one POST per operation with the identity headers, and the response read by the
241
+ protocol's rule. Arguments are encoded by value, results revived by the decode table, and the platform's own `fetch`
242
+ type is accepted. Its `execute` primitive is module-private: no public file re-exports it, and `AvClient`'s methods
243
+ wrap it.
244
+ - `generated/types.d.ts`: the named Resource types, as a declaration file (`User`, `UserWhere`, `UserUniqueWhere`,
245
+ `UserOrderBy`, `UserCreateData`, `UserUpdateData`, `UserSelect`, `UserInclude`), each only when its operation is
246
+ advertised.
247
+
248
+ The tree is self-contained: every import in it names a file in it, so it compiles with nothing else installed. Names a
249
+ TypeScript declaration cannot carry as-is (a reserved word, a name the runtime owns, an enum sharing a Resource's name)
250
+ are renamed, with one warning each; the wire name is kept.
234
251
 
235
252
  ## What a generated client is bound to
236
253
 
237
- A generated client is bound to the **exact ClientContract hash and protocol version** it was generated from. Both are
238
- in `generated/metadata.ts` and both are sent on every request (`Aventara-Protocol-Version`, `Aventara-Contract-Hash`).
239
- A deployment whose ClientContract differs refuses the request with `A2005`, which the client throws as
240
- `ContractMismatchError`.
254
+ A generated client is bound to the exact ClientContract hash and protocol version it was generated from. Both are in
255
+ `generated/metadata.ts`, and both are sent on every request (`Aventara-Protocol-Version`, `Aventara-Contract-Hash`). A
256
+ deployment whose ClientContract differs refuses the request with `A2005`, which the client throws as
257
+ `AvContractMismatchError`.
241
258
 
242
- The hash covers everything the deployment advertises, not only its schema: two deployments of one schema can advertise
259
+ The hash covers everything the deployment advertises, not only its schema. Two deployments of one schema can advertise
243
260
  different ClientContracts, and a client generated against one is refused by the other. Whatever the cause, the remedy
244
- is the same — **generate against the deployment the client will call, and regenerate when it changes.**
261
+ is the same: generate against the deployment the client will call, and regenerate when it changes.
245
262
 
246
- That holds for an operation the deployment **no longer advertises**, too: the server checks the client's
247
- identity before it routes, so a stale client calling a removed operation also hears `A2005` and throws
248
- `ContractMismatchError`. A response that is not a framework envelope at all — a host's own 404 for a path it does not
249
- mount — is a `TransportError`, which names no remedy: nothing in it says the client is stale.
263
+ The same holds for an operation the deployment no longer advertises. The server checks the client's identity before it
264
+ routes, so a stale client calling a removed operation also hears `A2005` and throws `AvContractMismatchError`. A
265
+ response that is not a framework envelope at all, such as a host's own 404 for a path it does not mount, is an
266
+ `AvTransportError`. It names no remedy, because nothing in it says the client is stale.
250
267
 
251
- The embedded entrypoint is a **default**: `new AvClient({ entrypoint })` may point a client at another deployment, and
252
- doing so re-binds nothing — the ClientContract hash still decides whether that deployment accepts the client.
268
+ The embedded entrypoint is a default. `new AvClient({ entrypoint })` may point a client at another deployment without
269
+ re-binding anything: the ClientContract hash still decides whether that deployment accepts the client.
253
270
 
254
271
  ## Type-checking cost in a consumer
255
272
 
256
- A consumer pays for the calls it type-checks, not for the schema, under `skipLibCheck: true` (the common default):
257
- the named types (`generated/types.d.ts`) and core's derivation are declaration files checked only where read. Under
273
+ Under `skipLibCheck: true` (the common default), a consumer pays for the calls it type-checks, not for the schema: the
274
+ named types (`generated/types.d.ts`) and core's derivation are declaration files checked only where read. Under
258
275
  `skipLibCheck: false` every named type is resolved where it is declared, so the cost grows with the number of
259
276
  Resources.
260
277
 
261
278
  ## Before 1.0
262
279
 
263
- - **The `Aventara` wire prefix** of the protocol's headers (`Aventara-Protocol-Version`, `Aventara-Contract-Hash`,
264
- `Aventara-Request-Id`) may still change before the first stable release. It is one constant in the generated
265
- `generated/runtime/transport.ts`; regenerating picks up a change.
280
+ The `Aventara` wire prefix of the protocol's headers (`Aventara-Protocol-Version`, `Aventara-Contract-Hash`,
281
+ `Aventara-Request-Id`) may still change before the first stable release. It is one constant in the generated
282
+ `generated/runtime/transport.ts`, and regenerating picks up a change.
266
283
 
267
284
  ## Known issues
268
285
 
269
- - **The generator read the wrong `tsconfig.json`.** In a monorepo or a non-standard layout, the nearest
270
- `tsconfig.json` above `generateAt` may not be the one your project compiles the client with, and the client's
271
- imports or module format then do not match your compiler (`Cannot find module './generated/client.js'`, an
272
- extension your bundler will not resolve). Name the right one in `framework.client.ts`, relative to the config file:
286
+ The generator can read the wrong `tsconfig.json`. In a monorepo or a non-standard layout, the nearest `tsconfig.json`
287
+ above `generateAt` may not be the one your project compiles the client with. The client's imports or module format then
288
+ do not match your compiler (`Cannot find module './generated/client.js'`, or an extension your bundler will not
289
+ resolve). Name the right one in `framework.client.ts`, relative to the config file:
273
290
 
274
- ```ts
275
- export default defineClientConfig({
276
- entrypoint: env("AVENTARA_API_URL"),
277
- generateAt: "./src/api",
278
- tsconfigFile: "./tsconfig.app.json",
279
- });
280
- ```
291
+ ```ts
292
+ export default defineClientConfig({
293
+ entrypoint: env("AVENTARA_API_URL"),
294
+ generateAt: "./src/api",
295
+ tsconfigFile: "./tsconfig.app.json",
296
+ });
297
+ ```
281
298
 
282
299
  ## License
283
300
 
284
- PolyForm Shield 1.0.0 with an additional permission — free to use, including in commercial applications; you may not
285
- use it to build a product that competes with Aventara itself. See `LICENSE` and `LICENSE-ADDITIONAL-PERMISSION.md`.
301
+ PolyForm Shield 1.0.0 with an additional permission. It is free to use, including in commercial applications, but you
302
+ may not use it to build a product that competes with Aventara itself. See `LICENSE` and
303
+ `LICENSE-ADDITIONAL-PERMISSION.md`.
@@ -1,5 +1,5 @@
1
- export declare const USAGE = "avclient \u2014 generate a typed Aventara client from a deployed ClientContract.\n\nUsage:\n avclient generate [--yes] Read framework.client.ts (or .js, .mjs, .cjs, .mts,\n .cts) in the current directory, fetch\n <entrypoint>/_contract, and write AvClient.ts and\n generated/ into its generateAt directory, its imports\n spelled as the project's tsconfig.json says.\n avclient init [options] Set up this frontend: write framework.client.ts, the\n .env entry, the avclient:generate script and the\n @aventara/client devDependency; install; generate.\n --entrypoint <url> The server's entrypoint [http://localhost:3000/api].\n --env-var <NAME> Read it from this variable [AVENTARA_API_URL].\n --no-env-var Write it into framework.client.ts as a literal.\n --generate-at <dir> Where the client goes [./src/api].\n --package-manager <npm|pnpm> [the lockfile's, else the launching one, else npm]\n --skip-install Print the install instead of running it.\n --skip-generate Do not generate now.\n -y, --yes Accept every default, and replace differing content.\n avclient --help Print this and exit 0.\n avclient <command> --help Print that command's usage and exit 0.\n avclient --version, -v Print this generator's version and exit 0.\n\nOptions:\n -y, --yes Overwrite or remove content in AvClient.ts and generated/ that the\n generator did not produce, without asking. Without it, such content\n is listed and you are asked; where nobody can answer (stdin is not a\n terminal, as in CI), the run is refused and nothing is touched.\n\nThe generator owns AvClient.ts and generated/ in generateAt, and nothing else\nthere: your own files beside them are never read, moved or removed. Generated\nfiles are replaced on every run; do not edit them.\n\nBefore framework.client.ts is evaluated, the .env cascade is read from the\ncurrent directory, highest precedence first: the process environment,\n.env.<mode>.local, .env.<mode>, .env.local, .env \u2014 where mode is NODE_ENV, or\n\"development\".\n";
2
- export declare const GENERATE_USAGE = "Usage: avclient generate [--yes]\n\nRead framework.client.ts (or .js, .mjs, .cjs, .mts, .cts) in the current directory,\nfetch <entrypoint>/_contract, and write AvClient.ts and generated/ into its\ngenerateAt directory, its imports spelled as the project's tsconfig.json says.\n\nOptions:\n -y, --yes Overwrite or remove content in AvClient.ts and generated/ that the\n generator did not produce, without asking. Without it, such content\n is listed and you are asked; where nobody can answer (stdin is not a\n terminal, as in CI), the run is refused and nothing is touched.\n\nThe generator owns AvClient.ts and generated/ in generateAt, and nothing else\nthere: your own files beside them are never read, moved or removed.\n\nBefore the config is evaluated, the .env cascade is read from the current\ndirectory, highest precedence first: the process environment, .env.<mode>.local,\n.env.<mode>, .env.local, .env \u2014 where mode is NODE_ENV, or \"development\".\n";
1
+ export declare const USAGE = "avclient \u2014 generate a typed Aventara client from a deployed ClientContract.\n\nUsage:\n avclient generate [--yes] Read framework.client.ts (or .mts, .cts, .js, .mjs,\n .cjs) in the current directory, fetch\n <entrypoint>/_contract, and write AvClient.ts and\n generated/ into its generateAt directory, its imports\n spelled as the project's tsconfig.json says.\n avclient init [options] Set up this frontend: write framework.client.ts, the\n .env entry, the avclient:generate script and the\n @aventara/client devDependency; install; generate.\n --entrypoint <url> The server's entrypoint [http://localhost:3000/api].\n --env-var <NAME> Read it from this variable [AVENTARA_API_URL].\n --no-env-var Write it into framework.client.ts as a literal.\n --generate-at <dir> Where the client goes [./src/api].\n --package-manager <npm|pnpm> [the lockfile's, else the launching one, else npm]\n --skip-install Print the install instead of running it.\n --skip-generate Do not generate now.\n -y, --yes Accept every default, and replace differing content.\n avclient --help Print this and exit 0.\n avclient <command> --help Print that command's usage and exit 0.\n avclient --version, -v Print this generator's version and exit 0.\n\nOptions:\n -y, --yes Overwrite or remove content in AvClient.ts and generated/ that the\n generator did not produce, without asking. Without it, such content\n is listed and you are asked; where nobody can answer (stdin is not a\n terminal, as in CI), the run is refused and nothing is touched.\n\nThe generator owns AvClient.ts and generated/ in generateAt, and nothing else\nthere: your own files beside them are never read, moved or removed. Generated\nfiles are replaced on every run; do not edit them.\n\nBefore framework.client.ts is evaluated, the .env cascade is read from the\ncurrent directory, highest precedence first: the process environment,\n.env.<mode>.local, .env.<mode>, .env.local, .env \u2014 where mode is NODE_ENV, or\n\"development\".\n";
2
+ export declare const GENERATE_USAGE = "Usage: avclient generate [--yes]\n\nRead framework.client.ts (or .mts, .cts, .js, .mjs, .cjs) in the current directory,\nfetch <entrypoint>/_contract, and write AvClient.ts and generated/ into its\ngenerateAt directory, its imports spelled as the project's tsconfig.json says.\n\nOptions:\n -y, --yes Overwrite or remove content in AvClient.ts and generated/ that the\n generator did not produce, without asking. Without it, such content\n is listed and you are asked; where nobody can answer (stdin is not a\n terminal, as in CI), the run is refused and nothing is touched.\n\nThe generator owns AvClient.ts and generated/ in generateAt, and nothing else\nthere: your own files beside them are never read, moved or removed.\n\nBefore the config is evaluated, the .env cascade is read from the current\ndirectory, highest precedence first: the process environment, .env.<mode>.local,\n.env.<mode>, .env.local, .env \u2014 where mode is NODE_ENV, or \"development\".\n";
3
3
  export declare const INIT_USAGE = "Usage: avclient init [options]\n\nSet up this frontend: write framework.client.ts, the .env entry, the avclient:generate\nscript and the @aventara/client devDependency; install; generate.\n\nOptions:\n --entrypoint <url> The server's entrypoint [http://localhost:3000/api].\n --env-var <NAME> Read it from this variable [AVENTARA_API_URL].\n --no-env-var Write it into framework.client.ts as a literal.\n --generate-at <dir> Where the client goes [./src/api].\n --package-manager <npm|pnpm> [the lockfile's, else the launching one, else npm]\n --skip-install Print the install instead of running it.\n --skip-generate Do not generate now.\n --verbose Show the install's output as it runs.\n -y, --yes Accept every default, and replace differing content.\n\nOn a terminal every unanswered question is asked; anywhere else, pass its flag\nor --yes, or the run stops before writing anything.\n";
4
4
  export type ClientInitCommand = {
5
5
  readonly command: "init";
@@ -22,11 +22,11 @@ export type CliCommand = {
22
22
  readonly yes: boolean;
23
23
  } | ClientInitCommand;
24
24
  /** The command line cannot be understood. A refusal: one sentence, exit 1. */
25
- export declare class CliCommandError extends Error {
26
- readonly name = "CliCommandError";
25
+ export declare class AvCliCommandError extends Error {
26
+ readonly name = "AvCliCommandError";
27
27
  }
28
28
  /**
29
- * @throws CliCommandError when `argv` is not `generate [--yes|-y]`, `init …`,
29
+ * @throws AvCliCommandError when `argv` is not `generate [--yes|-y]`, `init …`,
30
30
  * `--help`/`-h` or `--version`/`-v`.
31
31
  */
32
32
  export declare function parseCliCommand(argv: readonly string[]): CliCommand;
@@ -2,8 +2,8 @@ import { CLIENT_CONFIG_FILE } from "../config/config.loader.js";
2
2
  export const USAGE = `avclient — generate a typed Aventara client from a deployed ClientContract.
3
3
 
4
4
  Usage:
5
- avclient generate [--yes] Read ${CLIENT_CONFIG_FILE} (or .js, .mjs, .cjs, .mts,
6
- .cts) in the current directory, fetch
5
+ avclient generate [--yes] Read ${CLIENT_CONFIG_FILE} (or .mts, .cts, .js, .mjs,
6
+ .cjs) in the current directory, fetch
7
7
  <entrypoint>/_contract, and write AvClient.ts and
8
8
  generated/ into its generateAt directory, its imports
9
9
  spelled as the project's tsconfig.json says.
@@ -39,7 +39,7 @@ current directory, highest precedence first: the process environment,
39
39
  `;
40
40
  export const GENERATE_USAGE = `Usage: avclient generate [--yes]
41
41
 
42
- Read ${CLIENT_CONFIG_FILE} (or .js, .mjs, .cjs, .mts, .cts) in the current directory,
42
+ Read ${CLIENT_CONFIG_FILE} (or .mts, .cts, .js, .mjs, .cjs) in the current directory,
43
43
  fetch <entrypoint>/_contract, and write AvClient.ts and generated/ into its
44
44
  generateAt directory, its imports spelled as the project's tsconfig.json says.
45
45
 
@@ -75,14 +75,14 @@ Options:
75
75
  On a terminal every unanswered question is asked; anywhere else, pass its flag
76
76
  or --yes, or the run stops before writing anything.
77
77
  `;
78
- export class CliCommandError extends Error {
79
- name = "CliCommandError";
78
+ export class AvCliCommandError extends Error {
79
+ name = "AvCliCommandError";
80
80
  }
81
81
  export function parseCliCommand(argv) {
82
82
  const [command, ...rest] = argv;
83
83
  const help = "run `avclient --help` for usage.";
84
84
  if (command === undefined) {
85
- throw new CliCommandError(`a command is required; ${help}`);
85
+ throw new AvCliCommandError(`a command is required; ${help}`);
86
86
  }
87
87
  if (command === "--help" || command === "-h") {
88
88
  return { command: "help" };
@@ -98,7 +98,7 @@ export function parseCliCommand(argv) {
98
98
  return parseInit(rest, help);
99
99
  }
100
100
  if (command !== "generate") {
101
- throw new CliCommandError(`unknown command ${JSON.stringify(command)}; ${help}`);
101
+ throw new AvCliCommandError(`unknown command ${JSON.stringify(command)}; ${help}`);
102
102
  }
103
103
  let yes = false;
104
104
  for (const argument of rest) {
@@ -106,7 +106,7 @@ export function parseCliCommand(argv) {
106
106
  yes = true;
107
107
  continue;
108
108
  }
109
- throw new CliCommandError(`unexpected argument ${JSON.stringify(argument)}: generate takes only --yes, ` +
109
+ throw new AvCliCommandError(`unexpected argument ${JSON.stringify(argument)}: generate takes only --yes, ` +
110
110
  `everything else it needs is in ${CLIENT_CONFIG_FILE}; ${help}`);
111
111
  }
112
112
  return { command: "generate", yes };
@@ -146,16 +146,16 @@ function parseInit(argv, help) {
146
146
  const value = INIT_VALUES[flag];
147
147
  const toggle = INIT_SWITCHES[flag];
148
148
  if (value === undefined && toggle === undefined) {
149
- throw new CliCommandError(`unexpected argument ${JSON.stringify(argument)} for init; ${help}`);
149
+ throw new AvCliCommandError(`unexpected argument ${JSON.stringify(argument)} for init; ${help}`);
150
150
  }
151
151
  const canonical = toggle === "yes" ? "--yes" : flag;
152
152
  if (seen.has(canonical)) {
153
- throw new CliCommandError(`${canonical} was given twice; ${help}`);
153
+ throw new AvCliCommandError(`${canonical} was given twice; ${help}`);
154
154
  }
155
155
  seen.add(canonical);
156
156
  if (toggle !== undefined) {
157
157
  if (inline !== undefined) {
158
- throw new CliCommandError(`${canonical} takes no value; ${help}`);
158
+ throw new AvCliCommandError(`${canonical} takes no value; ${help}`);
159
159
  }
160
160
  switches[toggle] = true;
161
161
  continue;
@@ -163,7 +163,7 @@ function parseInit(argv, help) {
163
163
  const taken = inline ?? argv[index + 1];
164
164
  if (taken === undefined ||
165
165
  (inline === undefined && taken.startsWith("-"))) {
166
- throw new CliCommandError(`${canonical} takes a value; ${help}`);
166
+ throw new AvCliCommandError(`${canonical} takes a value; ${help}`);
167
167
  }
168
168
  if (inline === undefined) {
169
169
  index += 1;
@@ -171,7 +171,7 @@ function parseInit(argv, help) {
171
171
  given[value] = taken;
172
172
  }
173
173
  if (switches.noEnvVar && given.envVar !== undefined) {
174
- throw new CliCommandError(`--env-var and --no-env-var contradict each other; ${help}`);
174
+ throw new AvCliCommandError(`--env-var and --no-env-var contradict each other; ${help}`);
175
175
  }
176
176
  return {
177
177
  command: "init",