@aventara/cli 0.1.0-pilot.1 → 0.1.0-pilot.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 (44) hide show
  1. package/README.md +2 -2
  2. package/dist/apply/app-module.anchor.d.ts +5 -11
  3. package/dist/apply/app-module.anchor.js +0 -23
  4. package/dist/apply/conflict.confirmer.d.ts +0 -9
  5. package/dist/apply/conflict.confirmer.js +0 -15
  6. package/dist/apply/e2e-spec.anchor.js +0 -17
  7. package/dist/apply/manifest.merger.d.ts +11 -13
  8. package/dist/apply/manifest.merger.js +0 -18
  9. package/dist/aventara.bin.js +0 -10
  10. package/dist/catalog/adapter.catalog.generated.js +0 -4
  11. package/dist/catalog/catalog-entry.interface.d.ts +16 -13
  12. package/dist/catalog/catalog.matcher.d.ts +7 -13
  13. package/dist/catalog/catalog.matcher.js +0 -3
  14. package/dist/catalog/range.reader.d.ts +4 -10
  15. package/dist/catalog/range.reader.js +0 -14
  16. package/dist/cli.d.ts +2 -3
  17. package/dist/cli.js +0 -6
  18. package/dist/command/command.parser.d.ts +7 -7
  19. package/dist/command/command.parser.js +2 -7
  20. package/dist/node-version.guard.js +0 -12
  21. package/dist/plan/project.planner.d.ts +7 -13
  22. package/dist/plan/project.planner.js +6 -32
  23. package/dist/project/package-manager.detector.d.ts +0 -7
  24. package/dist/project/package-manager.detector.js +0 -7
  25. package/dist/project/project.inspector.d.ts +3 -9
  26. package/dist/project/project.inspector.js +0 -10
  27. package/dist/project/service.detector.d.ts +5 -10
  28. package/dist/project/service.detector.js +0 -2
  29. package/dist/project/source.scanner.js +0 -13
  30. package/dist/run/command.runner.d.ts +4 -4
  31. package/dist/run/init.orchestrator.d.ts +4 -6
  32. package/dist/run/init.orchestrator.js +1 -12
  33. package/dist/run/new.orchestrator.d.ts +6 -9
  34. package/dist/run/new.orchestrator.js +0 -16
  35. package/dist/templates/prisma7/prisma7.templates.js +0 -27
  36. package/dist/templates/template.registry.d.ts +13 -17
  37. package/dist/templates/template.registry.js +0 -5
  38. package/dist/wizard/answer.resolver.d.ts +5 -7
  39. package/dist/wizard/answer.resolver.js +0 -10
  40. package/dist/wizard/readline.prompter.d.ts +5 -7
  41. package/dist/wizard/readline.prompter.js +0 -10
  42. package/dist/wizard/wizard.questions.d.ts +16 -23
  43. package/dist/wizard/wizard.questions.js +0 -29
  44. package/package.json +2 -2
package/README.md CHANGED
@@ -9,7 +9,7 @@ aventara init # Aventara added to the NestJS 12 project in this
9
9
  ```
10
10
 
11
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.mts` and the generated client — is `@aventara/client`'s (`npx @aventara/client@pilot init`).
12
+ `framework.client.ts` and the generated client — is `@aventara/client`'s (`npx @aventara/client@pilot init`).
13
13
 
14
14
  ## `aventara new <name>`
15
15
 
@@ -59,7 +59,7 @@ when you confirm, or with `--yes`; where nobody can be asked, the run stops and
59
59
  | `src/aventara.config.ts` — `aventaraConfig(prisma)`: the entrypoint (`/api`), and the restrictions and pipelines you add | you |
60
60
  | `src/app.module.ts` — one edit: the imports, `PrismaModule` and `AventaraModule.forRootAsync({ … })`; printed instead when the file is not the shape expected | you |
61
61
  | `test/app.e2e-spec.ts` — one edit: a `GET /api/_contract` → 200 test after Nest's `GET /` test; left alone when the file is not Nest's | you |
62
- | `.env` (`DATABASE_URL`), `.gitignore` (`/src/generated/`, `/dev.db*`) | you |
62
+ | `.env` (`DATABASE_URL`), `.gitignore` (`/src/generated/`, `/dev.db*`, `.env`) | you |
63
63
  | `package.json` — `@aventara/*` at this CLI's version, `prisma`, `@prisma/client` and the driver at `7.10.0`; `aventara:prepare`, `postinstall`, and the `.env` on the start scripts and `test:e2e` (`--env-file`, `--env-file-if-exists`) | you |
64
64
  | `pnpm-workspace.yaml` (pnpm only) — `allowBuilds` for Prisma's and SQLite's install scripts | you |
65
65
  | `src/generated/prisma/**`, `src/generated/aventara/discovery.artifact.ts` | **generated** by `aventara:prepare` (run on every install); never edit |
@@ -1,15 +1,9 @@
1
1
  /**
2
- * R2 — the one edit `aventara init` makes to code the developer owns:
3
- * `src/app.module.ts` gains import lines and `imports:` entries for
4
- * `AventaraModule`, spliced at an **anchor** — the `imports: [ … ]` array of the
5
- * one `@Module({ … })` that decorates `AppModule`.
6
- *
7
- * The scan is string- and comment-aware (a `@Module(` or an `imports: [` inside
8
- * a comment, a string or a template literal is not code), and it never
9
- * reformats: everything outside the two insertion points keeps its bytes. When
10
- * the anchor is not found exactly once — two `@Module`s, a computed `imports`,
11
- * no `imports` at all — nothing is edited and the caller prints the lines to
12
- * add instead.
2
+ * The scan is string- and comment-aware (a `@Module(` or an `imports: [` inside a
3
+ * comment, a string or a template literal is not code), and it never reformats:
4
+ * everything outside the two insertion points keeps its bytes. When the anchor is
5
+ * not found exactly once — two `@Module`s, a computed `imports`, no `imports` at
6
+ * all — nothing is edited and the caller prints the lines to add instead.
13
7
  */
14
8
  export type AppModuleWiring = {
15
9
  /** Complete import declarations, one per line. */
@@ -1,16 +1,3 @@
1
- /**
2
- * R2 — the one edit `aventara init` makes to code the developer owns:
3
- * `src/app.module.ts` gains import lines and `imports:` entries for
4
- * `AventaraModule`, spliced at an **anchor** — the `imports: [ … ]` array of the
5
- * one `@Module({ … })` that decorates `AppModule`.
6
- *
7
- * The scan is string- and comment-aware (a `@Module(` or an `imports: [` inside
8
- * a comment, a string or a template literal is not code), and it never
9
- * reformats: everything outside the two insertion points keeps its bytes. When
10
- * the anchor is not found exactly once — two `@Module`s, a computed `imports`,
11
- * no `imports` at all — nothing is edited and the caller prints the lines to
12
- * add instead.
13
- */
14
1
  import { closing, codeIndexes, codeMask, nextCode, OPENERS, } from "../project/source.scanner.js";
15
2
  function indentOf(text, at) {
16
3
  const lineStart = text.lastIndexOf("\n", at - 1) + 1;
@@ -22,7 +9,6 @@ function indented(block, indent) {
22
9
  .map((line) => (line === "" ? line : `${indent}${line}`))
23
10
  .join("\n");
24
11
  }
25
- /** The local names a file's import declarations bind. */
26
12
  function importedNames(text, code) {
27
13
  const names = new Set();
28
14
  for (const at of codeIndexes(text, code, /^import\b/gm)) {
@@ -46,7 +32,6 @@ function importedNames(text, code) {
46
32
  }
47
33
  return names;
48
34
  }
49
- /** Splices `wiring` into `text` at the anchor, or says why there is no anchor. */
50
35
  export function spliceAppModule(text, wiring) {
51
36
  const code = codeMask(text);
52
37
  const decorators = codeIndexes(text, code, /@Module\s*\(/g);
@@ -74,7 +59,6 @@ export function spliceAppModule(text, wiring) {
74
59
  };
75
60
  }
76
61
  const objectEnd = closing(text, code, object);
77
- // `imports` at the object's own level: not inside a nested bracket.
78
62
  let depth = 0;
79
63
  let key = -1;
80
64
  for (let at = object + 1; at < objectEnd; at += 1) {
@@ -116,7 +100,6 @@ export function spliceAppModule(text, wiring) {
116
100
  }
117
101
  const base = indentOf(text, key);
118
102
  const element = `${base} `;
119
- // An entry the developer already lists (their own `PrismaModule`) is not added twice.
120
103
  const listed = text.slice(array + 1, arrayEnd);
121
104
  const entries = wiring.entries
122
105
  .filter((entry) => {
@@ -125,8 +108,6 @@ export function spliceAppModule(text, wiring) {
125
108
  !new RegExp(`(^|[\\s,\\[])${name}\\s*,?\\s*($|\\])`, "m").test(listed));
126
109
  })
127
110
  .map((entry) => indented(entry, element));
128
- // The last element needs a trailing comma: placed after its last code
129
- // character, so a comment after it stays a comment.
130
111
  let lastCode = arrayEnd - 1;
131
112
  while (lastCode > array &&
132
113
  (!code[lastCode] || /\s/.test(text[lastCode]))) {
@@ -141,7 +122,6 @@ export function spliceAppModule(text, wiring) {
141
122
  ...entries,
142
123
  ];
143
124
  const spliced = `[\n${lines.join("\n")}\n${base}]`;
144
- // The import lines go after the last import declaration.
145
125
  const declarations = codeIndexes(text, code, /^import\b/gm);
146
126
  let insertAt = 0;
147
127
  for (const declaration of declarations) {
@@ -149,8 +129,6 @@ export function spliceAppModule(text, wiring) {
149
129
  const lineEnd = text.indexOf("\n", end === -1 ? declaration : end);
150
130
  insertAt = lineEnd === -1 ? text.length : lineEnd + 1;
151
131
  }
152
- // A name the file already imports is not imported twice: a wiring line keeps
153
- // only the names still missing, and is dropped when none are.
154
132
  const imported = importedNames(text, code);
155
133
  const missing = wiring.imports.flatMap((line) => {
156
134
  const named = /^import\s+(type\s+)?\{([^}]*)\}(\s*from\s*.*)$/.exec(line);
@@ -174,7 +152,6 @@ export function spliceAppModule(text, wiring) {
174
152
  text: edited.slice(0, insertAt) + imports + edited.slice(insertAt),
175
153
  };
176
154
  }
177
- /** What to add by hand when there is no anchor. */
178
155
  export function wiringInstructions(wiring) {
179
156
  return [
180
157
  "Add to src/app.module.ts:",
@@ -1,13 +1,4 @@
1
1
  import type { Prompter } from "../wizard/answer.resolver.js";
2
- /**
3
- * The `generateAt` rule (architect, 2026-10-04; R5 makes it every wizard's
4
- * conflict rule; P7 — implemented here and in `@aventara/client`, which this
5
- * package does not import, in the same sentence shape): content the tool did not
6
- * produce is listed with a warning and replaced only once someone confirms;
7
- * `--yes` confirms in advance; where nobody can be asked (stdin is not a
8
- * terminal) the run cancels in one sentence naming `--yes`, exit 1, with
9
- * nothing touched.
10
- */
11
2
  /** Nobody confirmed replacing existing content. A refusal: nothing was touched. */
12
3
  export declare class ConflictsNotConfirmedError extends Error {
13
4
  readonly name = "ConflictsNotConfirmedError";
@@ -1,24 +1,9 @@
1
- /**
2
- * The `generateAt` rule (architect, 2026-10-04; R5 makes it every wizard's
3
- * conflict rule; P7 — implemented here and in `@aventara/client`, which this
4
- * package does not import, in the same sentence shape): content the tool did not
5
- * produce is listed with a warning and replaced only once someone confirms;
6
- * `--yes` confirms in advance; where nobody can be asked (stdin is not a
7
- * terminal) the run cancels in one sentence naming `--yes`, exit 1, with
8
- * nothing touched.
9
- */
10
- /** Nobody confirmed replacing existing content. A refusal: nothing was touched. */
11
1
  export class ConflictsNotConfirmedError extends Error {
12
2
  name = "ConflictsNotConfirmedError";
13
3
  }
14
4
  export function conflictWarning(conflicts) {
15
5
  return `${conflicts.join(", ")} already ${conflicts.length === 1 ? "has" : "have"} other content, and initializing will replace ${conflicts.length === 1 ? "it" : "them"}`;
16
6
  }
17
- /**
18
- * Resolves when `conflicts` may be replaced; throws when they may not.
19
- *
20
- * @throws ConflictsNotConfirmedError
21
- */
22
7
  export async function confirmConflicts(conflicts, consent) {
23
8
  if (conflicts.length === 0 || consent.yes) {
24
9
  return;
@@ -1,17 +1,4 @@
1
- /**
2
- * pilot.1 — the scaffold's own e2e spec also proves the protocol is mounted.
3
- *
4
- * `nest new`'s `test/app.e2e-spec.ts` (`@nestjs/schematics` 12's template, ESM
5
- * and CommonJS alike) tests `GET /` and nothing else. Like the `app.module.ts`
6
- * edit, this one is anchored on Nest's exact text: the `/ (GET)` test is found
7
- * exactly once, and a `GET <entrypoint>/_contract` test is added right after
8
- * it, through the spec's own `app` and `request`. A spec that is not Nest's —
9
- * the developer's own — is left as it is, and nothing is said: the test is a
10
- * convenience, and the developer's tests are theirs.
11
- */
12
- /** The e2e spec `nest new` writes. */
13
1
  export const E2E_SPEC = "test/app.e2e-spec.ts";
14
- /** `nest new`'s `GET /` test, byte for byte: the anchor. */
15
2
  const ROOT_TEST = ` it('/ (GET)', () => {
16
3
  return request(app.getHttpServer())
17
4
  .get('/')
@@ -19,10 +6,6 @@ const ROOT_TEST = ` it('/ (GET)', () => {
19
6
  .expect('Hello World!');
20
7
  });
21
8
  `;
22
- /**
23
- * `spec` with the contract test after Nest's `GET /` test, or `undefined` when
24
- * the anchor is not there exactly once, or the contract test already is.
25
- */
26
9
  export function spliceContractTest(spec, entrypoint) {
27
10
  const path = `${entrypoint}/_contract`;
28
11
  const at = spec.indexOf(ROOT_TEST);
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * The merges `aventara init` makes into files a project already has —
3
- * `package.json`, `.env`, `.gitignore`, `pnpm-workspace.yaml` — each answering
4
- * the merged text and the **conflicts**: values already there that differ from
5
- * what would be written (the `generateAt` rule's "content the tool did not
6
- * produce"). A value already equal to what would be written is no change (P3);
7
- * a conflict is replaced only once confirmed (`conflict.confirmer.ts`), so each
8
- * merge is computed with conflicts both kept and replaced.
3
+ * `package.json`, `.env`, `.gitignore`, `pnpm-workspace.yaml` — each answering the
4
+ * merged text and the **conflicts**: values already there that differ from what
5
+ * would be written (the `generateAt` rule's "content the tool did not produce"). A
6
+ * value already equal to what would be written is no change; a conflict is
7
+ * replaced only once confirmed (`conflict.confirmer.ts`), so each merge is
8
+ * computed with conflicts both kept and replaced.
9
9
  */
10
10
  export type Merge = {
11
11
  readonly text: string;
@@ -24,8 +24,8 @@ export type ManifestChanges = {
24
24
  /** Scripts to set; a different existing value is a conflict. */
25
25
  readonly scripts: Readonly<Record<string, string>>;
26
26
  /**
27
- * Scripts to rewrite only from a known value (D3): `from` → `to`. Any other
28
- * existing value is a conflict; an absent script is left absent.
27
+ * Scripts to rewrite only from a known value: `from` → `to`. Any other existing
28
+ * value is a conflict; an absent script is left absent.
29
29
  */
30
30
  readonly rewrites: Readonly<Record<string, {
31
31
  readonly from: string;
@@ -34,14 +34,12 @@ export type ManifestChanges = {
34
34
  };
35
35
  /** `package.json`, in npm's own layout: two-space JSON, dependencies sorted. */
36
36
  export declare function mergePackageManifest(text: string, changes: ManifestChanges, replaceConflicts: boolean): Merge;
37
- /** `.env`: a missing key is appended as `KEY="value"`, after a final newline (B5). */
37
+ /** `.env`: a missing key is appended as `KEY="value"`, after a final newline. */
38
38
  export declare function mergeEnvFile(text: string | undefined, entries: Readonly<Record<string, string>>, replaceConflicts: boolean): Merge;
39
39
  /** `.gitignore`: missing lines appended, nothing else touched. */
40
40
  export declare function mergeGitignore(text: string | undefined, additions: readonly string[]): Merge;
41
41
  /**
42
- * `pnpm-workspace.yaml`'s `allowBuilds:` (B9: pnpm 12 refuses install scripts
43
- * until they are allowed — what `pnpm approve-builds` writes). Keys missing
44
- * under an existing `allowBuilds:` are added there; otherwise the block is
45
- * appended.
42
+ * Keys missing under an existing `allowBuilds:` are added there; otherwise the
43
+ * block is appended.
46
44
  */
47
45
  export declare function mergePnpmWorkspace(text: string | undefined, packages: readonly string[]): Merge;
@@ -1,16 +1,6 @@
1
- /**
2
- * The merges `aventara init` makes into files a project already has —
3
- * `package.json`, `.env`, `.gitignore`, `pnpm-workspace.yaml` — each answering
4
- * the merged text and the **conflicts**: values already there that differ from
5
- * what would be written (the `generateAt` rule's "content the tool did not
6
- * produce"). A value already equal to what would be written is no change (P3);
7
- * a conflict is replaced only once confirmed (`conflict.confirmer.ts`), so each
8
- * merge is computed with conflicts both kept and replaced.
9
- */
10
1
  function sorted(record) {
11
2
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0));
12
3
  }
13
- /** `package.json`, in npm's own layout: two-space JSON, dependencies sorted. */
14
4
  export function mergePackageManifest(text, changes, replaceConflicts) {
15
5
  const manifest = JSON.parse(text);
16
6
  const conflicts = [];
@@ -66,7 +56,6 @@ function unquoted(value) {
66
56
  ? value.slice(1, -1)
67
57
  : value;
68
58
  }
69
- /** `.env`: a missing key is appended as `KEY="value"`, after a final newline (B5). */
70
59
  export function mergeEnvFile(text, entries, replaceConflicts) {
71
60
  const lines = text === undefined || text === ""
72
61
  ? []
@@ -90,7 +79,6 @@ export function mergeEnvFile(text, entries, replaceConflicts) {
90
79
  }
91
80
  return { text: `${lines.join("\n")}\n`, conflicts };
92
81
  }
93
- /** `.gitignore`: missing lines appended, nothing else touched. */
94
82
  export function mergeGitignore(text, additions) {
95
83
  const lines = text === undefined || text === ""
96
84
  ? []
@@ -103,12 +91,6 @@ export function mergeGitignore(text, additions) {
103
91
  conflicts: [],
104
92
  };
105
93
  }
106
- /**
107
- * `pnpm-workspace.yaml`'s `allowBuilds:` (B9: pnpm 12 refuses install scripts
108
- * until they are allowed — what `pnpm approve-builds` writes). Keys missing
109
- * under an existing `allowBuilds:` are added there; otherwise the block is
110
- * appended.
111
- */
112
94
  export function mergePnpmWorkspace(text, packages) {
113
95
  const quoted = (name) => /^[A-Za-z0-9_-][A-Za-z0-9._-]*$/.test(name) ? name : `"${name}"`;
114
96
  const lines = text === undefined || text === ""
@@ -1,15 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  import { refuseUnsupportedNode } from "./node-version.guard.js";
3
- /**
4
- * `aventara`'s entry, what the manifest's `bin` names (F-855). It always runs — no
5
- * `import.meta.main` — and checks this Node against the package's
6
- * `engines.node` before it loads anything else: the program is imported only
7
- * once the guard admits this Node, so an older Node meets one sentence and exit
8
- * 1, never a silent exit 0 or a parse error from a module it cannot run.
9
- *
10
- * A promise chain rather than a top-level `await`, which Node 12 cannot parse:
11
- * this file is read by the Nodes the package does not support.
12
- */
13
3
  if (!refuseUnsupportedNode("aventara", new URL("../package.json", import.meta.url))) {
14
4
  void import("./cli.js").then((program) => program.runFromProcess());
15
5
  }
@@ -1,7 +1,3 @@
1
- // biome-ignore-all format: generated output; these bytes are the catalog
2
- // biome-ignore-all lint: generated output
3
- /* !!! Generated by @aventara/cli's adapter-catalog generator from each adapter's manifest. Do not edit. !!! */
4
- /* Regenerate with `pnpm --filter @aventara/cli build` (its `prebuild`). */
5
1
  export const ADAPTER_CATALOG = [
6
2
  {
7
3
  "id": "prisma7",
@@ -1,15 +1,18 @@
1
1
  import type { ADAPTER_CATALOG } from "./adapter.catalog.generated.js";
2
2
  /**
3
- * One entry per Aventara adapter package that exists (R13, R14): what the CLI
4
- * may offer and what it must refuse, every value traced to that adapter's own
5
- * manifest — its `aventara.adapter` declaration, its `peerDependencies` and its
6
- * `bin` (Q16-27). The entries are **generated** at this package's build by
7
- * `scripts/adapter-catalog.generator.ts` into `adapter.catalog.generated.ts`
8
- * and never edited (P9). The CLI holds no list of ORMs, majors, providers or
9
- * drivers of its own.
3
+ * One entry per Aventara adapter package that exists: what the CLI may offer and
4
+ * what it must refuse, every value traced to that adapter's own manifest — its
5
+ * `aventara.adapter` declaration, its `peerDependencies` and its `bin`. The
6
+ * entries are **generated** at this package's build by
7
+ * `scripts/adapter-catalog.generator.ts` into `adapter.catalog.generated.ts` and
8
+ * never edited. The CLI holds no list of ORMs, majors, providers or drivers of its
9
+ * own.
10
10
  */
11
11
  export type CatalogEntry = {
12
- /** The adapter package's ORM stem, e.g. `prisma7` (Q16-33): the wizard's value and the templates' key. */
12
+ /**
13
+ * The adapter package's ORM stem, e.g. `prisma7`: the wizard's value and the
14
+ * templates' key.
15
+ */
13
16
  readonly id: string;
14
17
  /** The adapter package a project installs, e.g. `@aventara/prisma7-adapter`. */
15
18
  readonly adapterPackage: string;
@@ -23,17 +26,17 @@ export type CatalogEntry = {
23
26
  /** The one major `range` admits. */
24
27
  readonly major: number;
25
28
  };
26
- /** `peerDependencies[orm.package]`: the ORM versions the adapter supports (D8). */
29
+ /** `peerDependencies[orm.package]`: the ORM versions the adapter supports. */
27
30
  readonly range: string;
28
- /** Packages that identify the ORM family in a project (D10, Q16-27). */
31
+ /** Packages that identify the ORM family in a project. */
29
32
  readonly familyPackages: readonly string[];
30
- /** Database providers, in the adapter's order; the first is the default (R10). */
33
+ /** Database providers, in the adapter's order; the first is the default. */
31
34
  readonly providers: readonly string[];
32
35
  /** Provider → the driver adapter package for it. */
33
36
  readonly drivers: Readonly<Record<string, string>>;
34
- /** The ORM generators whose output the adapter reads (B27). */
37
+ /** The ORM generators whose output the adapter reads. */
35
38
  readonly generators: readonly string[];
36
- /** The adapter's generate step: its one `bin` (Q16-30). */
39
+ /** The adapter's generate step: its one `bin`. */
37
40
  readonly bin: string;
38
41
  };
39
42
  export type AdapterCatalog = readonly CatalogEntry[];
@@ -1,23 +1,17 @@
1
1
  import type { AdapterCatalog, CatalogEntry } from "./catalog-entry.interface.js";
2
2
  /**
3
- * R7, R13, R14, D10, P10 — which catalog entry serves a project that already
4
- * has an ORM, decided from what is **installed** and what each adapter
5
- * **declares**; this module holds no ORM, version or provider of its own.
6
- *
7
- * 1. The ORM family: the catalog entries whose family packages the project
8
- * depends on (a pattern ending in `*` is a prefix).
9
- * 2. Its major: the installed version of the family's ORM package, never the
10
- * range in `package.json` (P10: `^7` can resolve to anything in 7, and a
11
- * stale lockfile to 6.x). Every other family package installed must be of
12
- * that same major — a Prisma 8 CLI or an `@prisma/orm-*` beside a Prisma 7
13
- * client is a project mid-migration, refused (D10).
3
+ * 1. The ORM family: the catalog entries whose family packages the project depends
4
+ * on (a pattern ending in `*` is a prefix).
5
+ * 2. Every other family package installed must be of that same major — a Prisma 8
6
+ * CLI or an `@prisma/orm-*` beside a Prisma 7 client is a project
7
+ * mid-migration, refused.
14
8
  * 3. The entry for that major, or a refusal naming what exists.
15
9
  * 4. The installed version inside the entry's declared range.
16
10
  * 5. Then what the project's ORM setup declares — provider, driver, generator —
17
11
  * against the entry's providers, drivers and generators.
18
12
  *
19
- * Every refusal is one sentence naming what was found and what the catalog
20
- * offers. Nothing has been written when one is raised.
13
+ * Every refusal is one sentence naming what was found and what the catalog offers.
14
+ * Nothing has been written when one is raised.
21
15
  */
22
16
  export type MatchVerdict = {
23
17
  readonly kind: "matched";
@@ -35,8 +35,6 @@ export function matchInstalledOrm(catalog, project) {
35
35
  }
36
36
  const client = installed.find((member) => member.name === ormPackage);
37
37
  const clientMajor = client?.version === undefined ? undefined : majorOf(client.version);
38
- // D10: one major across the family. The first package off the client's major
39
- // (or any, when there is no client) names the project's other major.
40
38
  const off = installed.find((member) => member.name !== ormPackage &&
41
39
  majorOf(member.version) !== clientMajor);
42
40
  const decided = off ?? client;
@@ -62,7 +60,6 @@ export function matchInstalledOrm(catalog, project) {
62
60
  }
63
61
  return { kind: "matched", entry };
64
62
  }
65
- /** Step 5: the project's provider, driver and generator against what `entry` declares. */
66
63
  export function matchOrmSetup(entry, setup) {
67
64
  const name = `the ${entry.orm.label} ${entry.orm.major} adapter`;
68
65
  if (setup.provider === undefined ||
@@ -1,17 +1,11 @@
1
- /**
2
- * Reading a declared version range for the two facts the CLI needs from one:
3
- * its lowest version (D1, D9: what `init` pins when it installs the ORM — the
4
- * adapter's measured floor) and its lowest major (a project's declared Nest and
5
- * TypeScript ranges, checked before anything is written).
6
- */
7
1
  /** `^7.10.0` → `7.10.0`; `>=7.10.0 <8` → `7.10.0`. */
8
2
  export declare function lowestVersionOf(range: string): string;
9
3
  /** `^12.0.1` → 12; `~6.0.2` → 6; a range with no number (`latest`, `*`) → `undefined`. */
10
4
  export declare function lowestMajorOf(range: string): number | undefined;
11
5
  /**
12
- * Whether an installed `version` is in a declared `range` (R7: the adapter's
13
- * declaration decides support). Reads the comparator forms an adapter's peer
14
- * range uses; a pre-release never satisfies (semver's rule for a range whose
15
- * comparators carry none). Any other form is refused rather than guessed.
6
+ * Whether an installed `version` is in a declared `range`. Reads the comparator
7
+ * forms an adapter's peer range uses; a pre-release never satisfies (semver's rule
8
+ * for a range whose comparators carry none). Any other form is refused rather than
9
+ * guessed.
16
10
  */
17
11
  export declare function satisfiesRange(version: string, range: string): boolean;
@@ -1,11 +1,4 @@
1
- /**
2
- * Reading a declared version range for the two facts the CLI needs from one:
3
- * its lowest version (D1, D9: what `init` pins when it installs the ORM — the
4
- * adapter's measured floor) and its lowest major (a project's declared Nest and
5
- * TypeScript ranges, checked before anything is written).
6
- */
7
1
  const VERSION = /(\d+)(?:\.(\d+))?(?:\.(\d+))?/;
8
- /** `^7.10.0` → `7.10.0`; `>=7.10.0 <8` → `7.10.0`. */
9
2
  export function lowestVersionOf(range) {
10
3
  const found = VERSION.exec(range);
11
4
  if (found === null) {
@@ -13,19 +6,12 @@ export function lowestVersionOf(range) {
13
6
  }
14
7
  return `${found[1]}.${found[2] ?? 0}.${found[3] ?? 0}`;
15
8
  }
16
- /** `^12.0.1` → 12; `~6.0.2` → 6; a range with no number (`latest`, `*`) → `undefined`. */
17
9
  export function lowestMajorOf(range) {
18
10
  const found = VERSION.exec(range);
19
11
  return found === null ? undefined : Number(found[1]);
20
12
  }
21
13
  const RELEASE = /^(\d+)\.(\d+)\.(\d+)$/;
22
14
  const COMPARATOR = /^(\^|>=|>|<=|<|=)?(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
23
- /**
24
- * Whether an installed `version` is in a declared `range` (R7: the adapter's
25
- * declaration decides support). Reads the comparator forms an adapter's peer
26
- * range uses; a pre-release never satisfies (semver's rule for a range whose
27
- * comparators carry none). Any other form is refused rather than guessed.
28
- */
29
15
  export function satisfiesRange(version, range) {
30
16
  const release = RELEASE.exec(version);
31
17
  if (release === null) {
package/dist/cli.d.ts CHANGED
@@ -2,9 +2,8 @@ import type { AdapterCatalog } from "./catalog/catalog-entry.interface.js";
2
2
  import { type CommandRunner } from "./run/command.runner.js";
3
3
  import { type Prompter } from "./wizard/answer.resolver.js";
4
4
  /**
5
- * `aventara` — the bin (R1): `new` and `init`. A refusal is one sentence on
6
- * stderr and exit 1, never a stack; anything else is a defect and keeps its
7
- * stack.
5
+ * `aventara` — the bin: `new` and `init`. A refusal is one sentence on stderr and
6
+ * exit 1, never a stack; anything else is a defect and keeps its stack.
8
7
  *
9
8
  * `init` initializes the project in the working directory
10
9
  * (`run/init.orchestrator.ts`); `new` creates one with the pinned `nest new` and
package/dist/cli.js CHANGED
@@ -66,12 +66,6 @@ export async function runCli(argv, io) {
66
66
  return 1;
67
67
  }
68
68
  }
69
- /**
70
- * Runs `aventara` over this process — its arguments, its terminal, its exit
71
- * code. Called by the bin's entry (`aventara.bin.ts`) once the Node guard has
72
- * admitted this Node; importing this module runs nothing, which lets `runCli`
73
- * be tested in process.
74
- */
75
69
  export async function runFromProcess() {
76
70
  const prompter = createReadlinePrompter({
77
71
  input: process.stdin,
@@ -1,10 +1,10 @@
1
1
  import { type QuestionId } from "../wizard/wizard.questions.js";
2
2
  /**
3
- * The `aventara` command line (R1, R2, Q16): `new <name>` and `init`, each
4
- * question's flag from the wizard's table, and the options that are not
5
- * questions. Unknown, misplaced or repeated arguments are refused in one
6
- * sentence; a flag's VALUE is the question's to judge, because what is valid
7
- * (`--db`'s providers) depends on the catalog and on earlier answers.
3
+ * The `aventara` command line: `new <name>` and `init`, each question's flag from
4
+ * the wizard's table, and the options that are not questions. Unknown, misplaced
5
+ * or repeated arguments are refused in one sentence; a flag's VALUE is the
6
+ * question's to judge, because what is valid (`--db`'s providers) depends on the
7
+ * catalog and on earlier answers.
8
8
  */
9
9
  export type ScaffoldCommandName = "new" | "init";
10
10
  export type ScaffoldCommand = {
@@ -12,9 +12,9 @@ export type ScaffoldCommand = {
12
12
  /** Raw answers by question, from flags and `new`'s positional `<name>`. */
13
13
  readonly given: Readonly<Partial<Record<QuestionId, string>>>;
14
14
  readonly skipInstall: boolean;
15
- /** `new` only: passed through to `nest new` (Q16-7). */
15
+ /** `new` only: passed through to `nest new`. */
16
16
  readonly skipGit: boolean;
17
- /** Accepts each unanswered question's default and confirms overwrites (R5). */
17
+ /** Accepts each unanswered question's default and confirms overwrites. */
18
18
  readonly yes: boolean;
19
19
  };
20
20
  export type CliCommand =
@@ -1,9 +1,7 @@
1
1
  import { INIT_FLAG_QUESTIONS, NEW_QUESTIONS, } from "../wizard/wizard.questions.js";
2
- /** The command line cannot be understood. A refusal: one sentence, exit 1. */
3
2
  export class CliCommandError extends Error {
4
3
  name = "CliCommandError";
5
4
  }
6
- /** The options that are not wizard questions. */
7
5
  const OPTIONS = [
8
6
  {
9
7
  flag: "--skip-install",
@@ -48,7 +46,6 @@ function usageLines(command) {
48
46
  .map(([left, right]) => ` ${left.padEnd(width)} ${right}`)
49
47
  .join("\n");
50
48
  }
51
- /** What each command does, in one line: the top-level usage and its own. */
52
49
  const SUMMARY = {
53
50
  new: "Create a NestJS project with Aventara (nest new, then init).",
54
51
  init: "Add Aventara to the NestJS 12 project in this directory.",
@@ -60,7 +57,6 @@ const SYNOPSIS = {
60
57
  const ASKED = `On a terminal every unanswered question is asked; anywhere else, pass its flag
61
58
  or --yes, or the run stops before writing anything.
62
59
  `;
63
- /** `aventara <command> --help` (pilot.1): that command's usage alone. */
64
60
  export function commandUsage(command) {
65
61
  return `Usage: ${SYNOPSIS[command]}
66
62
 
@@ -82,10 +78,9 @@ ${usageLines("init")}
82
78
 
83
79
  aventara --help Print this and exit 0.
84
80
  aventara <command> --help Print that command's usage and exit 0.
85
- aventara --version Print this CLI's version and exit 0.
81
+ aventara --version, -v Print this CLI's version and exit 0.
86
82
 
87
83
  ${ASKED}`;
88
- /** @throws CliCommandError when `argv` is not a command this CLI has. */
89
84
  export function parseCliCommand(argv) {
90
85
  const [command, ...rest] = argv;
91
86
  if (command === undefined) {
@@ -94,7 +89,7 @@ export function parseCliCommand(argv) {
94
89
  if (command === "--help" || command === "-h") {
95
90
  return { command: "help" };
96
91
  }
97
- if (command === "--version") {
92
+ if (command === "--version" || command === "-v") {
98
93
  return { command: "version" };
99
94
  }
100
95
  if (command !== "new" && command !== "init") {
@@ -1,4 +1,3 @@
1
- // biome-ignore lint/style/useNodejsImportProtocol: Node 14.0–14.13.0 resolves no "node:" specifier in an ES module, and this module runs on the Nodes the packages do not support.
2
1
  import { readFileSync } from "fs";
3
2
  function versionOf(text) {
4
3
  const match = /^v?(\d+)\.(\d+)\.(\d+)/.exec(text);
@@ -16,11 +15,6 @@ function compareVersions(left, right) {
16
15
  }
17
16
  return 0;
18
17
  }
19
- /**
20
- * Whether `range` admits `version`. Reads `^x.y.z` (the same major, from the
21
- * floor; majors ≥ 1) and `>=x.y.z`, joined by `||`; anything else throws rather
22
- * than admit or refuse by guess.
23
- */
24
18
  function admits(range, version) {
25
19
  return range.split("||").some((part) => {
26
20
  const comparator = /^\s*(\^|>=)(\d+\.\d+\.\d+)\s*$/.exec(part);
@@ -32,17 +26,11 @@ function admits(range, version) {
32
26
  (comparator[1] === ">=" || version[0] === floor[0]));
33
27
  });
34
28
  }
35
- /** The sentence a bin answers on `version`, or `undefined` when `range` admits it. */
36
29
  export function nodeVersionRefusal(bin, packageName, range, version) {
37
30
  return admits(range, versionOf(version))
38
31
  ? undefined
39
32
  : `${bin}: Node ${version} is not supported; ${packageName} needs Node ${range}.`;
40
33
  }
41
- /**
42
- * Reads the manifest at `manifestUrl`; on a Node its `engines.node` does not
43
- * admit, writes the sentence to stderr, sets exit code 1 and answers `true` —
44
- * the bin then loads nothing else.
45
- */
46
34
  export function refuseUnsupportedNode(bin, manifestUrl) {
47
35
  const manifest = JSON.parse(readFileSync(manifestUrl, "utf8"));
48
36
  const range = manifest.engines === undefined ? undefined : manifest.engines.node;