@aventara/client 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 (79) hide show
  1. package/README.md +41 -7
  2. package/dist/avclient.bin.js +0 -10
  3. package/dist/cli/command.parser.d.ts +15 -10
  4. package/dist/cli/command.parser.js +13 -19
  5. package/dist/cli/generate.command.js +0 -6
  6. package/dist/cli/generation-failure.renderer.js +0 -14
  7. package/dist/cli/generation-success.renderer.d.ts +4 -1
  8. package/dist/cli/generation-success.renderer.js +0 -13
  9. package/dist/cli/terminal.prompter.d.ts +1 -2
  10. package/dist/cli/warning.renderer.d.ts +2 -2
  11. package/dist/cli/warning.renderer.js +0 -8
  12. package/dist/cli.d.ts +8 -16
  13. package/dist/cli.js +5 -25
  14. package/dist/config/client-config.interface.d.ts +18 -16
  15. package/dist/config/client-config.interface.js +0 -13
  16. package/dist/config/config.loader.d.ts +34 -22
  17. package/dist/config/config.loader.js +49 -52
  18. package/dist/config/config.resolver.d.ts +13 -19
  19. package/dist/config/config.resolver.js +9 -48
  20. package/dist/config/env.cascade.d.ts +12 -14
  21. package/dist/config/env.cascade.js +0 -19
  22. package/dist/config/module-style.resolver.d.ts +52 -0
  23. package/dist/config/module-style.resolver.js +75 -0
  24. package/dist/config/tsconfig.locator.d.ts +45 -0
  25. package/dist/config/tsconfig.locator.js +52 -0
  26. package/dist/contract/contract.acceptance.d.ts +12 -26
  27. package/dist/contract/contract.acceptance.js +0 -54
  28. package/dist/contract/contract.fetcher.d.ts +12 -17
  29. package/dist/contract/contract.fetcher.js +0 -24
  30. package/dist/contract/contract.loader.d.ts +4 -5
  31. package/dist/contract/contract.loader.js +0 -10
  32. package/dist/emit/banner.emitter.d.ts +11 -12
  33. package/dist/emit/banner.emitter.js +0 -26
  34. package/dist/emit/client-surface.emitter.d.ts +17 -21
  35. package/dist/emit/client-surface.emitter.js +29 -55
  36. package/dist/emit/client-tree.emitter.d.ts +11 -20
  37. package/dist/emit/client-tree.emitter.js +12 -54
  38. package/dist/emit/contract-carrier.emitter.d.ts +5 -6
  39. package/dist/emit/contract-carrier.emitter.js +0 -28
  40. package/dist/emit/derivation.emitter.d.ts +7 -7
  41. package/dist/emit/derivation.emitter.js +2 -161
  42. package/dist/emit/descriptor.emitter.js +2 -28
  43. package/dist/emit/emitted-tree.interface.d.ts +40 -17
  44. package/dist/emit/emitted-tree.interface.js +6 -16
  45. package/dist/emit/enum.emitter.d.ts +4 -4
  46. package/dist/emit/enum.emitter.js +0 -24
  47. package/dist/emit/module-specifier.scanner.d.ts +25 -0
  48. package/dist/emit/module-specifier.scanner.js +160 -0
  49. package/dist/emit/module-style.interface.d.ts +58 -0
  50. package/dist/emit/module-style.interface.js +8 -0
  51. package/dist/emit/name.deriver.d.ts +33 -61
  52. package/dist/emit/name.deriver.js +0 -134
  53. package/dist/emit/named-type.emitter.d.ts +14 -21
  54. package/dist/emit/named-type.emitter.js +3 -30
  55. package/dist/emit/runtime.emitter.d.ts +23 -50
  56. package/dist/emit/runtime.emitter.js +68 -159
  57. package/dist/emit/scalar.codec.d.ts +20 -33
  58. package/dist/emit/scalar.codec.js +13 -69
  59. package/dist/emit/transaction.emitter.d.ts +6 -14
  60. package/dist/emit/transaction.emitter.js +24 -33
  61. package/dist/generate.d.ts +17 -34
  62. package/dist/generate.js +14 -22
  63. package/dist/index.js +0 -5
  64. package/dist/init/client-config.template.d.ts +6 -4
  65. package/dist/init/client-config.template.js +10 -13
  66. package/dist/init/client-init.errors.js +0 -3
  67. package/dist/init/client-init.orchestrator.js +1 -9
  68. package/dist/init/client-init.planner.d.ts +1 -9
  69. package/dist/init/client-init.planner.js +6 -24
  70. package/dist/init/client-init.questions.d.ts +8 -12
  71. package/dist/init/client-init.questions.js +0 -11
  72. package/dist/init/client-project.inspector.d.ts +6 -0
  73. package/dist/init/client-project.inspector.js +2 -2
  74. package/dist/node-version.guard.js +0 -12
  75. package/dist/output/output.validator.d.ts +22 -22
  76. package/dist/output/output.validator.js +46 -59
  77. package/dist/output/output.writer.d.ts +56 -52
  78. package/dist/output/output.writer.js +71 -133
  79. package/package.json +6 -4
@@ -1,18 +1,11 @@
1
- /**
2
- * R4, R5, Q16 rows 12–15 — `avclient init`'s questions in one table: each
3
- * question's flag, label and default, from which the command line, the prompt,
4
- * `--yes`'s defaults and the non-interactive refusal are derived — the rule
5
- * `@aventara/cli`'s wizard follows, implemented here because the two packages
6
- * never import each other (P7).
7
- */
8
1
  export declare const PACKAGE_MANAGERS: readonly ["npm", "pnpm"];
9
2
  export type PackageManager = (typeof PACKAGE_MANAGERS)[number];
10
3
  export type ClientInitAnswers = {
11
- /** The deployment's entrypoint (Q16-12: `--entrypoint`, `defineClientConfig`'s key). */
4
+ /** The deployment's entrypoint. */
12
5
  readonly entrypoint: string;
13
- /** The variable `framework.client.ts` reads it from, or `undefined` for a literal (Q16-13). */
6
+ /** The variable `framework.client.ts` reads it from, or `undefined` for a literal. */
14
7
  readonly envVar: string | undefined;
15
- /** Where the client is generated (Q16-14: `--generate-at`, the config key). */
8
+ /** Where the client is generated. */
16
9
  readonly generateAt: string;
17
10
  readonly packageManager: PackageManager;
18
11
  };
@@ -34,7 +27,7 @@ export type ClientInitQuestion = {
34
27
  /** @throws ClientInitAnswerError */
35
28
  readonly parse: (raw: string) => string;
36
29
  };
37
- /** The project convention for the variable (spec §15.2). */
30
+ /** The project convention for the variable. */
38
31
  export declare const DEFAULT_ENV_VAR = "AVENTARA_API_URL";
39
32
  export declare const CLIENT_INIT_QUESTIONS: readonly ClientInitQuestion[];
40
33
  export type ClientInitSources = {
@@ -47,6 +40,9 @@ export type ClientInitSources = {
47
40
  readonly say: (line: string) => void;
48
41
  readonly detectedPackageManager: PackageManager;
49
42
  };
50
- /** Flag → `--yes`'s default → the prompt → the refusal naming exactly the unanswered flags (R5). */
43
+ /**
44
+ * Flag → `--yes`'s default → the prompt → the refusal naming exactly the
45
+ * unanswered flags.
46
+ */
51
47
  export declare function resolveClientInitAnswers(sources: ClientInitSources): Promise<ClientInitAnswers>;
52
48
  export {};
@@ -1,21 +1,11 @@
1
1
  import { resolveEntrypoint } from "../config/config.resolver.js";
2
- /**
3
- * R4, R5, Q16 rows 12–15 — `avclient init`'s questions in one table: each
4
- * question's flag, label and default, from which the command line, the prompt,
5
- * `--yes`'s defaults and the non-interactive refusal are derived — the rule
6
- * `@aventara/cli`'s wizard follows, implemented here because the two packages
7
- * never import each other (P7).
8
- */
9
2
  export const PACKAGE_MANAGERS = ["npm", "pnpm"];
10
- /** An answer the question cannot take. A refusal: one sentence. */
11
3
  export class ClientInitAnswerError extends Error {
12
4
  name = "ClientInitAnswerError";
13
5
  }
14
- /** Nobody can answer and some questions are unanswered. A refusal: one sentence. */
15
6
  export class ClientInitUnansweredError extends Error {
16
7
  name = "ClientInitUnansweredError";
17
8
  }
18
- /** The project convention for the variable (spec §15.2). */
19
9
  export const DEFAULT_ENV_VAR = "AVENTARA_API_URL";
20
10
  export const CLIENT_INIT_QUESTIONS = [
21
11
  {
@@ -77,7 +67,6 @@ export const CLIENT_INIT_QUESTIONS = [
77
67
  },
78
68
  },
79
69
  ];
80
- /** Flag → `--yes`'s default → the prompt → the refusal naming exactly the unanswered flags (R5). */
81
70
  export async function resolveClientInitAnswers(sources) {
82
71
  const answered = {};
83
72
  const missing = [];
@@ -7,6 +7,12 @@ export declare class ClientProjectRefusedError extends Error {
7
7
  export type ClientProject = {
8
8
  readonly directory: string;
9
9
  readonly manifestText: string;
10
+ /**
11
+ * Its `framework.client.<ext>`, by name, when it has one — `undefined` when it
12
+ * has none. Two or more are refused while inspecting, as `avclient generate`
13
+ * refuses them.
14
+ */
15
+ readonly configFile: string | undefined;
10
16
  /** The package manager its lockfile names, if exactly one does. */
11
17
  readonly lockfile: PackageManager | undefined;
12
18
  /** The current text of a project file, or `undefined`. */
@@ -1,7 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import path from "node:path";
3
- /** The frontend `avclient init` serves, read before anything is asked or written. */
4
- /** The directory is not a project `avclient init` can set up. A refusal: nothing written. */
3
+ import { findClientConfigFile } from "../config/config.loader.js";
5
4
  export class ClientProjectRefusedError extends Error {
6
5
  name = "ClientProjectRefusedError";
7
6
  }
@@ -19,6 +18,7 @@ export function inspectClientProject(directory) {
19
18
  return {
20
19
  directory,
21
20
  manifestText: readFileSync(manifest, "utf8"),
21
+ configFile: findClientConfigFile(directory),
22
22
  lockfile: lockfiles.length === 1 ? lockfiles[0] : undefined,
23
23
  read: (file) => {
24
24
  try {
@@ -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;
@@ -1,30 +1,30 @@
1
1
  import type * as ts from "typescript";
2
- import type { EmittedTree } from "../emit/emitted-tree.interface.js";
2
+ import type { ClientEmission } from "../emit/emitted-tree.interface.js";
3
3
  /**
4
- * §15.3's "type/sanity validation" step, between emit-to-temp and the replace:
5
- * the gate that makes the atomicity worth having (plan §4 Q6). It judges a
6
- * directory the writer has just filled with `tree`, and answers with a value — the
7
- * writer decides what a rejection means for the previous output.
4
+ * It judges a directory the writer has just filled with `tree`, and answers with a
5
+ * value — the writer decides what a rejection means for the previous output.
8
6
  *
9
7
  * # Three checks, the strongest available first
10
8
  *
11
9
  * - **Shape**, always: the directory holds exactly `tree` — no file missing, none
12
- * extra, every one byte-for-byte what was emitted — and every file carries the
13
- * ownership line. That last is not decoration: the NEXT run reads it to decide
14
- * that this directory is a previous generation it may replace whole, so a file
15
- * without it would make the next run refuse.
16
- * - **Types**, when the `typescript` optional peer resolves (Q5, Q6): a real
17
- * program over the tree under a consumer's strictest plausible settings. The
18
- * tree is judged ALONE — the compiler host serves nothing outside the directory
19
- * but TypeScript's own `lib` files, so an import the tree cannot satisfy itself
20
- * fails here even when a `node_modules` beside the output could satisfy it
21
- * (§15.5).
10
+ * extra, every one byte-for-byte what was emitted — every file carries the
11
+ * ownership line, and every module a file names is a file of the tree, spelled
12
+ * the project's way. The ownership line is not decoration: the NEXT run reads it
13
+ * to decide that this directory is a previous generation it may replace whole,
14
+ * so a file without it would make the next run refuse. The imports are read
15
+ * lexically (`module-specifier.scanner.ts`), with no compiler — so a wrongly
16
+ * spelled import is refused even where the type check cannot run (TypeScript 7).
17
+ * - **Types**, when the `typescript` optional peer resolves: a real program over
18
+ * the tree under a consumer's strictest plausible settings, in the project's
19
+ * module resolution and format (`compilerOptionsFor`). The tree is judged ALONE
20
+ * — the compiler host serves nothing outside the directory but TypeScript's own
21
+ * `lib` files, so an import the tree cannot satisfy itself fails here even when
22
+ * a `node_modules` beside the output could satisfy it.
22
23
  * - **Syntax**, when it does not — or when the `typescript` that resolves has no
23
- * classic compiler API (TypeScript 7, plan B3; pilot.1): each file through
24
- * Node's own TypeScript parser (`node:module`'s `stripTypeScriptTypes`), with a
25
- * loud warning that the output was NOT type-checked. Degraded, never skipped
26
- * (S7), and never a refusal: the developer's own `tsc` still checks the tree
27
- * when their project compiles.
24
+ * classic compiler API: each file through Node's own TypeScript parser
25
+ * (`node:module`'s `stripTypeScriptTypes`), with a loud warning that the output
26
+ * was NOT type-checked. Degraded, never skipped, and never a refusal: the
27
+ * developer's own `tsc` still checks the tree when their project compiles.
28
28
  */
29
29
  /** The `typescript` module, as the validator uses it. */
30
30
  export type TypeScriptCompiler = typeof ts;
@@ -39,7 +39,7 @@ export type TypeScriptResolver = () => Promise<TypeScriptCompiler | undefined>;
39
39
  * it up: an optional peer is linked beside the package that declares it.
40
40
  */
41
41
  export declare function typeScriptResolverFor(specifier: string): TypeScriptResolver;
42
- /** The `typescript` this package's optional peer names (Q5). */
42
+ /** The `typescript` this package's optional peer names. */
43
43
  export declare const resolveInstalledTypeScript: TypeScriptResolver;
44
44
  /** How deeply the tree was judged. */
45
45
  export type OutputCheck = "types" | "syntax" | "shape";
@@ -72,4 +72,4 @@ export declare function typeScriptWithoutCompilerApiWarning(version: string, che
72
72
  /**
73
73
  * Judges `directory`, which the caller has just filled with `tree`.
74
74
  */
75
- export declare function validateOutputTree(directory: string, tree: EmittedTree, resolveTypeScript?: TypeScriptResolver): Promise<OutputValidation>;
75
+ export declare function validateOutputTree(directory: string, emission: Pick<ClientEmission, "style" | "tree">, resolveTypeScript?: TypeScriptResolver): Promise<OutputValidation>;
@@ -4,10 +4,7 @@ import { createRequire } from "node:module";
4
4
  import path from "node:path";
5
5
  import { pathToFileURL } from "node:url";
6
6
  import { carriesGeneratedOwnership } from "../emit/banner.emitter.js";
7
- /**
8
- * A resolver for `specifier`, looked up from this package the way Node would look
9
- * it up: an optional peer is linked beside the package that declares it.
10
- */
7
+ import { scanModuleSpecifiers } from "../emit/module-specifier.scanner.js";
11
8
  export function typeScriptResolverFor(specifier) {
12
9
  return async () => {
13
10
  let resolved;
@@ -24,58 +21,35 @@ export function typeScriptResolverFor(specifier) {
24
21
  return loaded.default ?? loaded;
25
22
  };
26
23
  }
27
- /** The `typescript` this package's optional peer names (Q5). */
28
24
  export const resolveInstalledTypeScript = typeScriptResolverFor("typescript");
29
- /**
30
- * Whether `compiler` has the classic compiler API {@link typeFindingsOf} uses.
31
- * TypeScript 7 does not (plan B3: its package exports its version and nothing
32
- * else); before pilot.1 the generator refused it, and its peer range stopped
33
- * below it — which made `npm i` fail with ERESOLVE in any frontend whose
34
- * `typescript` is the current `latest`.
35
- */
36
25
  function hasClassicCompilerApi(compiler) {
37
- // One member, not `Partial<typeof ts>`: a mapped type over the whole
38
- // compiler namespace costs thousands of instantiations to ask one question.
39
26
  return (typeof compiler.createProgram ===
40
27
  "function");
41
28
  }
42
- /** The version a resolved `typescript` names, whatever its API. */
43
29
  function versionOf(compiler) {
44
30
  const { version } = compiler;
45
31
  return typeof version === "string" ? version : "(no version)";
46
32
  }
47
- /**
48
- * The warning a run without `typescript` carries. Loud on purpose, in its words:
49
- * the prefix is the CLI's one `warning:`, so the text carries none of its own.
50
- */
51
33
  export const TYPESCRIPT_UNRESOLVED_WARNING = "`typescript` could not be resolved, so the generated output was NOT type-checked — " +
52
34
  "only its shape and syntax were. Install `typescript` (an optional peer of @aventara/client) " +
53
35
  "in this project to restore the type check.";
54
- /** The warning a run with neither `typescript` nor Node's parser carries. */
55
36
  export const SYNTAX_CHECK_UNAVAILABLE_WARNING = "neither `typescript` nor Node's TypeScript parser is available, so the generated " +
56
37
  "output was NOT type-checked or parsed — only its shape was. Install `typescript` (an " +
57
38
  "optional peer of @aventara/client) in this project to restore the type check.";
58
- /**
59
- * The warning a run carries when the `typescript` that resolves is one the
60
- * generator cannot type-check with (TypeScript 7): what was checked instead,
61
- * and where the type check still happens.
62
- */
63
39
  export function typeScriptWithoutCompilerApiWarning(version, checked) {
64
40
  return (`the installed \`typescript\` ${version} has no classic compiler API (\`createProgram\`), so the generated ` +
65
41
  `output was NOT type-checked${checked === "shape" ? " or parsed — only its shape was" : " — only its shape and syntax were"}. ` +
66
42
  "Your project's own `tsc` checks it when it compiles; a `typescript` 5.5 to 6 restores the generator's own type check.");
67
43
  }
68
- /**
69
- * Judges `directory`, which the caller has just filled with `tree`.
70
- */
71
- export async function validateOutputTree(directory, tree, resolveTypeScript = resolveInstalledTypeScript) {
72
- const shapeFindings = await shapeFindingsOf(directory, tree);
44
+ export async function validateOutputTree(directory, emission, resolveTypeScript = resolveInstalledTypeScript) {
45
+ const { tree, style } = emission;
46
+ const shapeFindings = await shapeFindingsOf(directory, tree, style);
73
47
  if (shapeFindings.length > 0) {
74
48
  return { accepted: false, checked: "shape", findings: shapeFindings };
75
49
  }
76
50
  const compiler = await resolveTypeScript();
77
51
  if (compiler !== undefined && hasClassicCompilerApi(compiler)) {
78
- const findings = typeFindingsOf(compiler, directory, tree);
52
+ const findings = typeFindingsOf(compiler, directory, tree, style);
79
53
  return findings.length > 0
80
54
  ? { accepted: false, checked: "types", findings }
81
55
  : { accepted: true, checked: "types", warnings: [] };
@@ -105,10 +79,7 @@ export async function validateOutputTree(directory, tree, resolveTypeScript = re
105
79
  ],
106
80
  };
107
81
  }
108
- /* ------------------------------------------------------------------ *
109
- * Shape *
110
- * ------------------------------------------------------------------ */
111
- async function shapeFindingsOf(directory, tree) {
82
+ async function shapeFindingsOf(directory, tree, style) {
112
83
  const findings = [];
113
84
  const expected = new Map(tree.map((file) => [file.path, file.bytes]));
114
85
  const entries = await readdir(directory, {
@@ -151,29 +122,50 @@ async function shapeFindingsOf(directory, tree) {
151
122
  if (!carriesGeneratedOwnership(text)) {
152
123
  findings.push(`${relative}: does not carry the generated banner`);
153
124
  }
125
+ for (const specifier of scanModuleSpecifiers(text).specifiers) {
126
+ if (!namesTreeFile(relative, specifier, style, expected)) {
127
+ findings.push(`${relative}: imports ${JSON.stringify(specifier)}, which is not a file of the emitted tree spelled with ${JSON.stringify(style.importFileExtension)}`);
128
+ }
129
+ }
154
130
  }
155
131
  return findings.sort();
156
132
  }
157
- /* ------------------------------------------------------------------ *
158
- * Types: a real program over the tree, and nothing outside it *
159
- * ------------------------------------------------------------------ */
160
- /**
161
- * The emitted-tree fixture's settings (`__fixtures__/emitted-tree.fixture.ts`),
162
- * restated here because production code does not import a test fixture: a
163
- * consumer's strictest plausible configuration, with no DOM and no Node (§15.7:
164
- * both are first-class targets, so the tree may assume neither).
165
- */
166
- function compilerOptionsFor(compiler) {
133
+ function namesTreeFile(file, specifier, style, tree) {
134
+ if (!specifier.startsWith("./") && !specifier.startsWith("../")) {
135
+ return false;
136
+ }
137
+ const target = path.posix.normalize(path.posix.join(path.posix.dirname(file), specifier));
138
+ const extension = file.startsWith("generated/derivation/")
139
+ ? ".js"
140
+ : style.importFileExtension === ""
141
+ ? ""
142
+ : `.${style.importFileExtension}`;
143
+ const spelled = extension === ""
144
+ ? !/\.[cm]?[jt]s$/.test(target)
145
+ : target.endsWith(extension);
146
+ if (!spelled) {
147
+ return false;
148
+ }
149
+ const stem = target.slice(0, target.length - extension.length);
150
+ return tree.has(`${stem}.ts`) || tree.has(`${stem}.d.ts`);
151
+ }
152
+ function compilerOptionsFor(compiler, style) {
153
+ const bundler = style.importFileExtension === "" && style.moduleFormat === "esm";
167
154
  return {
168
155
  target: compiler.ScriptTarget.ES2022,
169
- module: compiler.ModuleKind.NodeNext,
170
- moduleResolution: compiler.ModuleResolutionKind.NodeNext,
156
+ module: bundler ? compiler.ModuleKind.ESNext : compiler.ModuleKind.NodeNext,
157
+ moduleResolution: bundler
158
+ ? compiler.ModuleResolutionKind.Bundler
159
+ : compiler.ModuleResolutionKind.NodeNext,
160
+ ...(style.importFileExtension === "ts"
161
+ ? { allowImportingTsExtensions: true }
162
+ : {}),
171
163
  lib: ["lib.es2022.d.ts"],
172
164
  types: [],
173
165
  strict: true,
174
166
  noUncheckedIndexedAccess: true,
175
167
  exactOptionalPropertyTypes: true,
176
- verbatimModuleSyntax: true,
168
+ verbatimModuleSyntax: style.moduleFormat === "esm",
177
169
  isolatedModules: true,
178
170
  erasableSyntaxOnly: true,
179
171
  noUnusedLocals: true,
@@ -186,15 +178,12 @@ function compilerOptionsFor(compiler) {
186
178
  noEmit: true,
187
179
  };
188
180
  }
189
- /**
190
- * The tree is served as an ES module package of its own: NodeNext reads the
191
- * nearest `package.json` to decide a `.ts` file's module format, and the one a
192
- * consumer's project happens to have must not decide this verdict.
193
- */
194
- const VIRTUAL_PACKAGE_JSON = '{ "type": "module" }';
195
- function typeFindingsOf(compiler, directory, tree) {
181
+ function virtualPackageManifest(style) {
182
+ return `{ "type": "${style.moduleFormat === "esm" ? "module" : "commonjs"}" }`;
183
+ }
184
+ function typeFindingsOf(compiler, directory, tree, style) {
196
185
  const root = path.resolve(directory);
197
- const options = compilerOptionsFor(compiler);
186
+ const options = compilerOptionsFor(compiler, style);
198
187
  const libraryDirectory = path.dirname(compiler.getDefaultLibFilePath(options));
199
188
  const virtualPackageJson = path.join(root, "package.json");
200
189
  const servable = (file) => isInside(root, file) || isInside(libraryDirectory, file);
@@ -205,7 +194,7 @@ function typeFindingsOf(compiler, directory, tree) {
205
194
  host.fileExists = (file) => path.resolve(file) === virtualPackageJson ||
206
195
  (servable(path.resolve(file)) && baseFileExists(file));
207
196
  host.readFile = (file) => path.resolve(file) === virtualPackageJson
208
- ? VIRTUAL_PACKAGE_JSON
197
+ ? virtualPackageManifest(style)
209
198
  : servable(path.resolve(file))
210
199
  ? baseReadFile(file)
211
200
  : undefined;
@@ -229,13 +218,11 @@ function typeFindingsOf(compiler, directory, tree) {
229
218
  })
230
219
  .trimEnd());
231
220
  }
232
- /** Whether `file` is `directory` or lies beneath it. */
233
221
  function isInside(directory, file) {
234
222
  const relative = path.relative(directory, file);
235
223
  return (relative === "" ||
236
224
  (!relative.startsWith("..") && !path.isAbsolute(relative)));
237
225
  }
238
- /** Node's TypeScript parser, where this Node has one. */
239
226
  function nodeTypeScriptParser() {
240
227
  const candidate = nodeModule.stripTypeScriptTypes;
241
228
  return typeof candidate === "function" ? candidate : undefined;
@@ -3,29 +3,30 @@ import type { AbsolutePath } from "../config/client-config.interface.js";
3
3
  import { type ClientEmission, type EmittedTree } from "../emit/emitted-tree.interface.js";
4
4
  import { type OutputCheck, type TypeScriptResolver } from "./output.validator.js";
5
5
  /**
6
- * §15.3's last two steps as one call — emit into a temporary directory, validate
7
- * it, atomically replace the generated output — and the property that makes them
8
- * one: **a failed generation leaves the previous valid output intact**, byte for
9
- * byte.
10
- *
11
- * # What it owns (architect, 2026-10-04)
12
- *
13
6
  * The output is written into `generateAt`, a directory the developer SHARES: it
14
- * may hold their own files. The generator owns exactly two entries in it —
15
- * `AvClient.ts` and `generated/` — and reads, refuses on, moves or deletes
16
- * nothing else there, apart from the transient entries it names itself
17
- * (`.aventara-next-*`, `.aventara-ready-*`, `.generated.aventara-previous`).
7
+ * may hold their own files. The generator owns exactly these entries in it — the
8
+ * entry file `AvClient.ts` and `generated/` — and reads, refuses on, moves or
9
+ * deletes nothing else there, apart from the transient entries it names itself
10
+ * (`.aventara-next-*`, `.aventara-ready-*`, `.generated.aventara-previous`) and an
11
+ * unreleased build's entry files, `AvClient.mjs` and `AvClient.d.mts`, which it
12
+ * removes only while they carry the ownership line.
18
13
  *
19
14
  * - `generated/` is replaced WHOLE, never merged: a file the previous run emitted
20
- * and this one does not is gone. It is replaced only when every file in it
21
- * carries the generated banner's ownership line (an empty directory owns
22
- * nothing and is fine); otherwise the run is refused, naming the foreign files.
23
- * - `AvClient.ts` is replaced only when it is absent or carries the ownership
15
+ * and this one does not is gone — an unreleased build's `.mjs` modules included.
16
+ * It is replaced only when every file in it carries the generated banner's
17
+ * ownership line (an empty directory owns nothing and is fine); otherwise the
18
+ * run is refused, naming the foreign files.
19
+ * - Each entry file is replaced only when it is absent or carries the ownership
24
20
  * line; otherwise the run is refused, naming it.
21
+ * - `AvClient.mjs` and `AvClient.d.mts` — an unreleased build's entry — are
22
+ * removed when they carry the ownership line, so no stale JavaScript is left
23
+ * beside `AvClient.ts`. Without the line each is the developer's own file: never
24
+ * read beyond its head, never named, never removed. (pilot.0's and pilot.1's
25
+ * `AvClient.ts` is the entry file itself, replaced in place.)
25
26
  *
26
27
  * # The order, and its one window
27
28
  *
28
- * The whole tree — `AvClient.ts` and `generated/` together — is written into a
29
+ * The whole tree — the entry files and `generated/` together — is written into a
29
30
  * staging directory inside `generateAt` (same filesystem, so every move is one
30
31
  * atomic `rename`) and validated there as one program. Then:
31
32
  *
@@ -33,19 +34,22 @@ import { type OutputCheck, type TypeScriptResolver } from "./output.validator.js
33
34
  * to be validated;
34
35
  * 2. the previous `generated/` → `.generated.aventara-previous`;
35
36
  * 3. the staged `generated/` → `generated/`;
36
- * 4. the staged `AvClient.ts` → `AvClient.ts`, one rename over the old file;
37
+ * 4. the previous entry files, and an owned earlier entry, move aside into the
38
+ * ready directory; then each staged entry file moves in;
37
39
  * 5. the previous `generated/` and the staging directory are removed.
38
40
  *
39
- * A failed step 3 renames the previous `generated/` straight back; a failed step
40
- * 4 moves the new `generated/` out again and the previous back, so a failure at
41
- * any step leaves the previous pair. Node has no atomic exchange of two entries,
42
- * so two windows remain for a process KILLED mid-replace, and the next run's
43
- * recovery closes each before it does anything else:
41
+ * A failed step 3 renames the previous `generated/` straight back; a failed step 4
42
+ * moves what it moved back — the new entry files out, the previous ones in — then
43
+ * the new `generated/` out and the previous back, so a failure at any step leaves
44
+ * the previous output. Node has no atomic exchange of two entries, so two windows
45
+ * remain for a process KILLED mid-replace, and the next run's recovery closes each
46
+ * before it does anything else:
44
47
  *
45
48
  * - between 2 and 3, `generated/` does not exist: the previous one is put back;
46
- * - between 3 and 4, the new `generated/` sits beside the old `AvClient.ts`: the
47
- * validated `AvClient.ts` is still staged in `.aventara-ready-*`, so it is
48
- * moved in, completing the new pair rather than leaving a mismatched one.
49
+ * - between 3 and the end of 4, the new `generated/` sits beside the old entry
50
+ * files, or beside only some new ones: the validated entry files still staged in
51
+ * `.aventara-ready-*` are moved in, completing the new output rather than
52
+ * leaving a mismatched one.
49
53
  *
50
54
  * Staging directories that never reached step 1 were not validated and are
51
55
  * removed.
@@ -54,16 +58,16 @@ import { type OutputCheck, type TypeScriptResolver } from "./output.validator.js
54
58
  *
55
59
  * A refusal, a rejected tree or a filesystem error (an `Error` with a `code`) is
56
60
  * an `OutputWriteError` naming its stage — the person running the generator can
57
- * act on it (M3). Any other error is a defect and propagates with its stack —
58
- * the very error thrown, not a wrapper — and what the run said before it is
59
- * kept beside it, read by `warningsRaisedBeforeDefect`.
61
+ * act on it. Any other error is a defect and propagates with its stack — the very
62
+ * error thrown, not a wrapper — and what the run said before it is kept beside it,
63
+ * read by `warningsRaisedBeforeDefect`.
60
64
  *
61
- * A refusal carries every warning the run raised before it stopped, in the order
62
- * a success would have returned them: a crash recovered in `prepare` changed the
65
+ * A refusal carries every warning the run raised before it stopped, in the order a
66
+ * success would have returned them: a crash recovered in `prepare` changed the
63
67
  * disk, and the person has to hear that even when the run then fails.
64
68
  *
65
- * A failed run removes the directories it created to reach `generateAt` — and
66
- * only those, and only while empty — so a first run that fails leaves no empty
69
+ * A failed run removes the directories it created to reach `generateAt` — and only
70
+ * those, and only while empty — so a first run that fails leaves no empty
67
71
  * `generateAt` behind.
68
72
  */
69
73
  /** The four stages a write passes through, in order. */
@@ -105,22 +109,22 @@ export type OutputFileSystem = Pick<typeof fsPromises, "lstat" | "mkdir" | "open
105
109
  export interface ClientOutputWriteInput {
106
110
  /** What `emitClientTree` returned: the tree, and the warnings it raised. */
107
111
  readonly emission: ClientEmission;
108
- /** The resolved `generateAt` (§15.2, architect 2026-10-04). Created when missing. */
112
+ /** The resolved `generateAt`. Created when missing. */
109
113
  readonly generateAt: AbsolutePath;
110
114
  /**
111
- * Content in `AvClient.ts` or `generated/` the generator did not produce, which
112
- * the person running it confirmed may be overwritten or removed (architect,
113
- * 2026-10-04) — the paths `findForeignOutputContent` listed. Anything foreign
114
- * that is not listed here is refused.
115
+ * Content in the entry files or `generated/` the generator did not produce, which
116
+ * the person running it confirmed may be overwritten or removed — the paths
117
+ * `findForeignOutputContent` listed. Anything foreign that is not listed here is
118
+ * refused.
115
119
  */
116
120
  readonly overrideForeign?: readonly string[];
117
- /** The `typescript` optional peer; the installed one by default (Q6). */
121
+ /** The `typescript` optional peer; the installed one by default. */
118
122
  readonly resolveTypeScript?: TypeScriptResolver;
119
123
  readonly fileSystem?: OutputFileSystem;
120
124
  }
121
125
  export interface ClientOutputWritten {
122
126
  readonly generateAt: AbsolutePath;
123
- /** How deeply the tree was validated before it replaced the output (Q6). */
127
+ /** How deeply the tree was validated before it replaced the output. */
124
128
  readonly checked: OutputCheck;
125
129
  /**
126
130
  * Everything the run has to say without failing, in order: the emission's own
@@ -131,32 +135,32 @@ export interface ClientOutputWritten {
131
135
  readonly warnings: readonly string[];
132
136
  }
133
137
  /**
134
- * Writes `emission.tree` as `<generateAt>/AvClient.ts` and `<generateAt>/generated/`.
138
+ * Writes `emission.tree` as `<generateAt>/AvClient.ts` and
139
+ * `<generateAt>/generated/`.
135
140
  *
136
141
  * @throws OutputWriteError when the write stops; the previous output is intact.
137
142
  */
138
143
  export declare function writeClientOutput(input: ClientOutputWriteInput): Promise<ClientOutputWritten>;
139
144
  /**
140
- * Lists what in `<generateAt>/AvClient.ts` and `<generateAt>/generated/` the
141
- * generator did not produce — a file without the ownership line, a symbolic link,
142
- * anything that is not a regular file — so the person running it can be asked
143
- * before a generation overwrites or removes it (architect, 2026-10-04). Reads
144
- * only; looks at nothing else in `generateAt`. Empty when `generateAt` does not
145
- * exist yet.
145
+ * Lists what in the entry files and `<generateAt>/generated/` the generator did
146
+ * not produce — a file without the ownership line, a symbolic link, anything that
147
+ * is not a regular file — so the person running it can be asked before a
148
+ * generation overwrites or removes it. Reads only; looks at nothing else in
149
+ * `generateAt`. Empty when `generateAt` does not exist yet.
146
150
  *
147
151
  * It sees what the write will see once it has recovered a killed run, without
148
152
  * recovering it: a `generated/` the killed run moved aside with nothing in its
149
153
  * place is read where it lies and its files named where recovery puts them back
150
- * (`generated/…`). So the question asked, and `--yes`, cover them too — and a
151
- * run that stops here has still touched nothing.
154
+ * (`generated/…`). So the question asked, and `--yes`, cover them too — and a run
155
+ * that stops here has still touched nothing.
152
156
  *
153
157
  * @throws OutputWriteError when `generateAt` is not a directory, `generated` is
154
- * not a directory or `AvClient.ts` is not a file: no confirmation repairs that.
158
+ * not a directory or an entry file is not a file: no confirmation repairs that.
155
159
  */
156
160
  export declare function findForeignOutputContent(generateAt: AbsolutePath, fileSystem?: OutputFileSystem): Promise<readonly string[]>;
157
161
  /**
158
- * Whether `AvClient.ts` and `generated/` in `generateAt` hold exactly `tree` —
159
- * every file byte for byte, no other file, and nothing a killed run moved aside —
160
- * so that writing `tree` would change nothing (Phase 12-rest Q6: "up to date").
162
+ * Whether the entry files and `generated/` in `generateAt` hold exactly `tree` —
163
+ * every file byte for byte, no other file, no owned entry of an earlier build, and
164
+ * nothing a killed run moved aside — so that writing `tree` would change nothing.
161
165
  */
162
166
  export declare function ownedOutputMatches(generateAt: AbsolutePath, tree: EmittedTree): Promise<boolean>;