@aventara/client 0.1.0-pilot.4 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +679 -0
  2. package/README.md +168 -132
  3. package/dist/cli/command.parser.d.ts +6 -4
  4. package/dist/cli/command.parser.js +22 -11
  5. package/dist/cli/generate.command.d.ts +6 -1
  6. package/dist/cli/generate.command.js +12 -1
  7. package/dist/cli/generation-failure.renderer.js +24 -24
  8. package/dist/cli/step.reporter.d.ts +31 -0
  9. package/dist/cli/step.reporter.js +87 -0
  10. package/dist/cli.js +2 -0
  11. package/dist/config/client-config.interface.d.ts +7 -7
  12. package/dist/config/config.loader.d.ts +4 -4
  13. package/dist/config/config.loader.js +7 -7
  14. package/dist/config/config.resolver.d.ts +7 -7
  15. package/dist/config/config.resolver.js +12 -12
  16. package/dist/config/env.cascade.d.ts +2 -2
  17. package/dist/config/env.cascade.js +4 -4
  18. package/dist/config/module-style.resolver.d.ts +1 -1
  19. package/dist/config/module-style.resolver.js +2 -2
  20. package/dist/config/tsconfig.locator.d.ts +1 -1
  21. package/dist/config/tsconfig.locator.js +2 -2
  22. package/dist/contract/contract.acceptance.d.ts +4 -4
  23. package/dist/contract/contract.acceptance.js +4 -4
  24. package/dist/contract/contract.fetcher.d.ts +3 -3
  25. package/dist/contract/contract.fetcher.js +6 -6
  26. package/dist/contract/contract.loader.d.ts +6 -6
  27. package/dist/contract/contract.loader.js +2 -2
  28. package/dist/emit/client-surface.emitter.d.ts +2 -2
  29. package/dist/emit/client-surface.emitter.js +77 -43
  30. package/dist/emit/client-tree.emitter.d.ts +3 -3
  31. package/dist/emit/client-tree.emitter.js +8 -5
  32. package/dist/emit/contract-carrier.emitter.d.ts +3 -3
  33. package/dist/emit/contract-carrier.emitter.js +1 -1
  34. package/dist/emit/derivation.emitter.js +1 -0
  35. package/dist/emit/descriptor.emitter.d.ts +2 -2
  36. package/dist/emit/descriptor.emitter.js +2 -2
  37. package/dist/emit/emitted-tree.interface.d.ts +1 -1
  38. package/dist/emit/enum.emitter.d.ts +2 -2
  39. package/dist/emit/field-check.emitter.d.ts +21 -0
  40. package/dist/emit/field-check.emitter.js +603 -0
  41. package/dist/emit/json-shape.emitter.d.ts +16 -0
  42. package/dist/emit/json-shape.emitter.js +88 -0
  43. package/dist/emit/name.deriver.d.ts +6 -6
  44. package/dist/emit/name.deriver.js +15 -15
  45. package/dist/emit/named-type.emitter.d.ts +2 -2
  46. package/dist/emit/runtime.emitter.d.ts +2 -2
  47. package/dist/emit/runtime.emitter.js +117 -112
  48. package/dist/emit/scalar.codec.d.ts +1 -1
  49. package/dist/emit/scalar.codec.js +78 -35
  50. package/dist/emit/transaction.emitter.js +131 -83
  51. package/dist/generate.d.ts +11 -3
  52. package/dist/generate.js +40 -26
  53. package/dist/index.d.ts +1 -1
  54. package/dist/init/client-init.errors.d.ts +4 -4
  55. package/dist/init/client-init.errors.js +4 -4
  56. package/dist/init/client-init.orchestrator.js +37 -22
  57. package/dist/init/client-init.questions.d.ts +5 -5
  58. package/dist/init/client-init.questions.js +10 -10
  59. package/dist/init/client-project.inspector.d.ts +2 -2
  60. package/dist/init/client-project.inspector.js +3 -3
  61. package/dist/init/command.runner.d.ts +2 -0
  62. package/dist/init/command.runner.js +4 -1
  63. package/dist/output/output.writer.d.ts +5 -5
  64. package/dist/output/output.writer.js +12 -12
  65. package/package.json +6 -5
package/dist/generate.js CHANGED
@@ -4,11 +4,13 @@ import { loadClientConfigFile } from "./config/config.loader.js";
4
4
  import { resolveClientConfig } from "./config/config.resolver.js";
5
5
  import { resolveEnvCascade, } from "./config/env.cascade.js";
6
6
  import { resolveModuleStyle } from "./config/module-style.resolver.js";
7
+ import { contractUrlOf, displayUrl, } from "./contract/contract.fetcher.js";
7
8
  import { loadClientContractSince } from "./contract/contract.loader.js";
8
9
  import { emitClientTree } from "./emit/client-tree.emitter.js";
9
10
  import { parseContractCarrier } from "./emit/contract-carrier.emitter.js";
10
11
  import { CLIENT_ENTRY_FILE, CONTRACT_CARRIER_MODULE, GENERATED_DIRECTORY, } from "./emit/emitted-tree.interface.js";
11
- import { findForeignOutputContent, OutputWriteError, ownedOutputMatches, precedeDefect, writeClientOutput, } from "./output/output.writer.js";
12
+ import { AvOutputWriteError, findForeignOutputContent, ownedOutputMatches, precedeDefect, writeClientOutput, } from "./output/output.writer.js";
13
+ const runPhase = async (_title, body) => await body();
12
14
  async function storedContract(generateAt) {
13
15
  try {
14
16
  if ((await findForeignOutputContent(generateAt)).length > 0) {
@@ -21,32 +23,42 @@ async function storedContract(generateAt) {
21
23
  }
22
24
  }
23
25
  export async function generateClient(input) {
24
- const cascade = resolveEnvCascade({
25
- directory: input.directory,
26
- processEnv: input.processEnv,
27
- });
28
- const loaded = await loadClientConfigFile(input.directory);
29
- const config = resolveClientConfig({
30
- config: loaded.config,
31
- cascade,
32
- configDirectory: path.dirname(loaded.file),
33
- });
34
- const style = resolveModuleStyle({
35
- generateAt: config.generateAt,
36
- entryFile: path.join(config.generateAt, CLIENT_ENTRY_FILE),
37
- ...(config.tsconfigFile === undefined
38
- ? {}
39
- : { tsconfigFile: config.tsconfigFile }),
26
+ const step = input.step ?? runPhase;
27
+ const { config, style } = await step("Loading the client config", async () => {
28
+ const cascade = resolveEnvCascade({
29
+ directory: input.directory,
30
+ processEnv: input.processEnv,
31
+ });
32
+ const loaded = await loadClientConfigFile(input.directory);
33
+ const config = resolveClientConfig({
34
+ config: loaded.config,
35
+ cascade,
36
+ configDirectory: path.dirname(loaded.file),
37
+ });
38
+ const style = resolveModuleStyle({
39
+ generateAt: config.generateAt,
40
+ entryFile: path.join(config.generateAt, CLIENT_ENTRY_FILE),
41
+ ...(config.tsconfigFile === undefined
42
+ ? {}
43
+ : { tsconfigFile: config.tsconfigFile }),
44
+ });
45
+ return { config, style };
40
46
  });
41
- const { contract, notModified } = await loadClientContractSince(input.fetch === undefined
47
+ const { contract, notModified } = await step(`Fetching the ClientContract (${displayUrl(contractUrlOf(config.entrypoint))})`, async () => loadClientContractSince(input.fetch === undefined
42
48
  ? { entrypoint: config.entrypoint }
43
- : { entrypoint: config.entrypoint, fetch: input.fetch }, await storedContract(config.generateAt));
44
- const emission = emitClientTree(contract, config.entrypoint, {
45
- importFileExtension: style.importFileExtension,
46
- moduleFormat: style.moduleFormat,
49
+ : { entrypoint: config.entrypoint, fetch: input.fetch }, await storedContract(config.generateAt)));
50
+ const { emission, upToDate } = await step("Emitting the client", async () => {
51
+ const emission = emitClientTree(contract, config.entrypoint, {
52
+ importFileExtension: style.importFileExtension,
53
+ moduleFormat: style.moduleFormat,
54
+ });
55
+ return {
56
+ emission,
57
+ upToDate: notModified &&
58
+ (await ownedOutputMatches(config.generateAt, emission.tree)),
59
+ };
47
60
  });
48
- if (notModified &&
49
- (await ownedOutputMatches(config.generateAt, emission.tree))) {
61
+ if (upToDate) {
50
62
  return {
51
63
  kind: "up-to-date",
52
64
  generateAt: config.generateAt,
@@ -54,7 +66,7 @@ export async function generateClient(input) {
54
66
  warnings: emission.warnings,
55
67
  };
56
68
  }
57
- const write = async (overrideForeign) => {
69
+ const writeInto = async (overrideForeign) => {
58
70
  const written = await writeClientOutput(input.resolveTypeScript === undefined
59
71
  ? { emission, generateAt: config.generateAt, overrideForeign }
60
72
  : {
@@ -72,8 +84,10 @@ export async function generateClient(input) {
72
84
  warnings: written.warnings,
73
85
  };
74
86
  };
87
+ const into = path.relative(input.directory, config.generateAt) || ".";
88
+ const write = (overrideForeign) => step(`Checking and writing the client into ${into}`, () => writeInto(overrideForeign));
75
89
  const foreign = await findForeignOutputContent(config.generateAt).catch((error) => {
76
- throw error instanceof OutputWriteError
90
+ throw error instanceof AvOutputWriteError
77
91
  ? error.precededBy(emission.warnings)
78
92
  : precedeDefect(error, emission.warnings);
79
93
  });
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /** The version of `@aventara/client`. */
2
2
  export declare const AVENTARA_CLIENT_GENERATOR_VERSION: string;
3
- export type { ClientConfigInput, ConfigValue, EnvReference, } from "./config/client-config.interface.js";
3
+ export type { AvClientConfigInput, AvClientConfigValue, AvEnvReference, } from "./config/client-config.interface.js";
4
4
  export { defineClientConfig, env } from "./config/config.resolver.js";
@@ -1,9 +1,9 @@
1
1
  /** `avclient init`'s refusals after the questions: each one sentence, exit 1. */
2
2
  /** Nobody confirmed replacing existing content. A refusal: nothing was touched. */
3
- export declare class ClientInitNotConfirmedError extends Error {
4
- readonly name = "ClientInitNotConfirmedError";
3
+ export declare class AvClientInitNotConfirmedError extends Error {
4
+ readonly name = "AvClientInitNotConfirmedError";
5
5
  }
6
6
  /** A step after the write failed. Reported with what to run; the files are kept. */
7
- export declare class ClientInitStepError extends Error {
8
- readonly name = "ClientInitStepError";
7
+ export declare class AvClientInitStepError extends Error {
8
+ readonly name = "AvClientInitStepError";
9
9
  }
@@ -1,6 +1,6 @@
1
- export class ClientInitNotConfirmedError extends Error {
2
- name = "ClientInitNotConfirmedError";
1
+ export class AvClientInitNotConfirmedError extends Error {
2
+ name = "AvClientInitNotConfirmedError";
3
3
  }
4
- export class ClientInitStepError extends Error {
5
- name = "ClientInitStepError";
4
+ export class AvClientInitStepError extends Error {
5
+ name = "AvClientInitStepError";
6
6
  }
@@ -1,11 +1,12 @@
1
1
  import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import path from "node:path";
3
- import { runGenerate } from "../cli/generate.command.js";
3
+ import { GENERATION_STEPS, runGenerate, } from "../cli/generate.command.js";
4
+ import { createStepReporter } from "../cli/step.reporter.js";
4
5
  import { CLIENT_CONFIG_FILE } from "../config/config.loader.js";
5
6
  import { resolveModuleStyle } from "../config/module-style.resolver.js";
6
- import { ContractTransportError } from "../contract/contract.fetcher.js";
7
+ import { AvContractTransportError } from "../contract/contract.fetcher.js";
7
8
  import { CLIENT_ENTRY_FILE } from "../emit/emitted-tree.interface.js";
8
- import { ClientInitNotConfirmedError, ClientInitStepError, } from "./client-init.errors.js";
9
+ import { AvClientInitNotConfirmedError, AvClientInitStepError, } from "./client-init.errors.js";
9
10
  import { GENERATE_SCRIPT, planClientInit } from "./client-init.planner.js";
10
11
  import { resolveClientInitAnswers } from "./client-init.questions.js";
11
12
  import { inspectClientProject } from "./client-project.inspector.js";
@@ -39,46 +40,60 @@ export async function runClientInit(command, io) {
39
40
  const them = plan.conflicts.length === 1 ? "it" : "them";
40
41
  const has = plan.conflicts.length === 1 ? "has" : "have";
41
42
  if (!io.interactive) {
42
- throw new ClientInitNotConfirmedError(`not initializing, and nothing was touched: ${plan.conflicts.join(", ")} already ${has} other content and nobody can be asked (stdin is not a terminal); run \`avclient init --yes\` to replace ${them}.`);
43
+ throw new AvClientInitNotConfirmedError(`not initializing, and nothing was touched: ${plan.conflicts.join(", ")} already ${has} other content and nobody can be asked (stdin is not a terminal); run \`avclient init --yes\` to replace ${them}.`);
43
44
  }
44
45
  io.stderr(`avclient: warning: ${plan.conflicts.join(", ")} already ${has} other content, and initializing will replace ${them}.\n`);
45
46
  if (!(await io.confirm("avclient: replace them and initialize? [y/N] "))) {
46
- throw new ClientInitNotConfirmedError(`cancelled, and nothing was touched; run again and answer yes to replace ${them}.`);
47
+ throw new AvClientInitNotConfirmedError(`cancelled, and nothing was touched; run again and answer yes to replace ${them}.`);
47
48
  }
48
49
  }
49
50
  const writes = plan.conflicts.length === 0 ? plan.keeping : plan.replacing;
50
- for (const write of writes) {
51
- const target = path.join(io.cwd, write.path);
52
- if (write.content === undefined) {
53
- rmSync(target, { force: true });
54
- continue;
51
+ const progress = createStepReporter({
52
+ write: io.stderr,
53
+ mode: io.progress ?? "off",
54
+ total: command.skipInstall
55
+ ? 1
56
+ : 2 + (command.skipGenerate ? 0 : GENERATION_STEPS),
57
+ verbose: command.verbose === true,
58
+ });
59
+ await progress.step("Writing the client setup", () => {
60
+ for (const write of writes) {
61
+ const target = path.join(io.cwd, write.path);
62
+ if (write.content === undefined) {
63
+ rmSync(target, { force: true });
64
+ continue;
65
+ }
66
+ mkdirSync(path.dirname(target), { recursive: true });
67
+ writeFileSync(target, write.content);
55
68
  }
56
- mkdirSync(path.dirname(target), { recursive: true });
57
- writeFileSync(target, write.content);
58
- }
69
+ });
59
70
  io.stdout(`avclient: initialized ${io.cwd}: ${writes.map((write) => (write.content === undefined ? `removed ${write.path}` : write.path)).join(", ") || "nothing to change"}.\n`);
60
71
  const install = `${answers.packageManager} install`;
61
72
  if (command.skipInstall) {
73
+ progress.finish("avclient", "done");
62
74
  io.stdout(`Next:\n 1. Install @aventara/client ${clientVersion}: ${install}\n 2. With the server running: ${answers.packageManager} run ${GENERATE_SCRIPT}\n`);
63
75
  return 0;
64
76
  }
65
- io.stdout(`avclient: running ${install}…\n`);
66
- const installed = await (io.run ?? runCommand)(answers.packageManager, ["install"], {
67
- cwd: io.cwd,
77
+ await progress.step(`Installing dependencies (${install})`, async () => {
78
+ const installed = await (io.run ?? runCommand)(answers.packageManager, ["install"], {
79
+ cwd: io.cwd,
80
+ onOutput: progress.output,
81
+ });
82
+ if (installed.code !== 0) {
83
+ throw new AvClientInitStepError(`\`${install}\` exited with code ${installed.code} after ${project.configFile ?? CLIENT_CONFIG_FILE} was written; fix what it reports and run \`${install}\` again:\n${installed.stderr.trimEnd()}`);
84
+ }
68
85
  });
69
- if (installed.code !== 0) {
70
- throw new ClientInitStepError(`\`${install}\` exited with code ${installed.code} after ${project.configFile ?? CLIENT_CONFIG_FILE} was written; fix what it reports and run \`${install}\` again:\n${installed.stderr.trimEnd()}`);
71
- }
72
86
  if (command.skipGenerate) {
87
+ progress.finish("avclient", "done");
73
88
  io.stdout(`Next: with the server running, ${answers.packageManager} run ${GENERATE_SCRIPT}\n`);
74
89
  return 0;
75
90
  }
76
91
  try {
77
- return await runGenerate(io, command.yes);
92
+ return await runGenerate(io, command.yes, progress);
78
93
  }
79
94
  catch (error) {
80
- if (error instanceof ContractTransportError) {
81
- throw new ClientInitStepError(`the project is set up, but the server did not answer (${error.message.replace(/\.$/, "")}); once it is running, run \`avclient generate\`.`);
95
+ if (error instanceof AvContractTransportError) {
96
+ throw new AvClientInitStepError(`the project is set up, but the server did not answer (${error.message.replace(/\.$/, "")}); once it is running, run \`avclient generate\`.`);
82
97
  }
83
98
  throw error;
84
99
  }
@@ -8,12 +8,12 @@ export type ClientInitAnswers = {
8
8
  };
9
9
  type QuestionId = "entrypoint" | "envVar" | "generateAt" | "packageManager";
10
10
  /** An answer the question cannot take. A refusal: one sentence. */
11
- export declare class ClientInitAnswerError extends Error {
12
- readonly name = "ClientInitAnswerError";
11
+ export declare class AvClientInitAnswerError extends Error {
12
+ readonly name = "AvClientInitAnswerError";
13
13
  }
14
14
  /** Nobody can answer and some questions are unanswered. A refusal: one sentence. */
15
- export declare class ClientInitUnansweredError extends Error {
16
- readonly name = "ClientInitUnansweredError";
15
+ export declare class AvClientInitUnansweredError extends Error {
16
+ readonly name = "AvClientInitUnansweredError";
17
17
  }
18
18
  export type ClientInitQuestion = {
19
19
  readonly id: QuestionId;
@@ -21,7 +21,7 @@ export type ClientInitQuestion = {
21
21
  readonly placeholder: string;
22
22
  readonly label: string;
23
23
  readonly defaultFor: (detectedPackageManager: PackageManager) => string;
24
- /** @throws ClientInitAnswerError */
24
+ /** @throws AvClientInitAnswerError */
25
25
  readonly parse: (raw: string) => string;
26
26
  };
27
27
  export declare const DEFAULT_ENV_VAR = "AVENTARA_API_URL";
@@ -1,10 +1,10 @@
1
1
  import { resolveEntrypoint } from "../config/config.resolver.js";
2
2
  export const PACKAGE_MANAGERS = ["npm", "pnpm"];
3
- export class ClientInitAnswerError extends Error {
4
- name = "ClientInitAnswerError";
3
+ export class AvClientInitAnswerError extends Error {
4
+ name = "AvClientInitAnswerError";
5
5
  }
6
- export class ClientInitUnansweredError extends Error {
7
- name = "ClientInitUnansweredError";
6
+ export class AvClientInitUnansweredError extends Error {
7
+ name = "AvClientInitUnansweredError";
8
8
  }
9
9
  export const DEFAULT_ENV_VAR = "AVENTARA_API_URL";
10
10
  export const CLIENT_INIT_QUESTIONS = [
@@ -19,7 +19,7 @@ export const CLIENT_INIT_QUESTIONS = [
19
19
  resolveEntrypoint(raw.trim());
20
20
  }
21
21
  catch (error) {
22
- throw new ClientInitAnswerError(error.message.replace(/\.$/, ""));
22
+ throw new AvClientInitAnswerError(error.message.replace(/\.$/, ""));
23
23
  }
24
24
  return raw.trim();
25
25
  },
@@ -33,7 +33,7 @@ export const CLIENT_INIT_QUESTIONS = [
33
33
  parse: (raw) => {
34
34
  const name = raw.trim();
35
35
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
36
- throw new ClientInitAnswerError(`${JSON.stringify(raw)} is not an environment variable name.`);
36
+ throw new AvClientInitAnswerError(`${JSON.stringify(raw)} is not an environment variable name.`);
37
37
  }
38
38
  return name;
39
39
  },
@@ -47,7 +47,7 @@ export const CLIENT_INIT_QUESTIONS = [
47
47
  parse: (raw) => {
48
48
  const directory = raw.trim();
49
49
  if (directory === "") {
50
- throw new ClientInitAnswerError("--generate-at takes a directory.");
50
+ throw new AvClientInitAnswerError("--generate-at takes a directory.");
51
51
  }
52
52
  return directory;
53
53
  },
@@ -61,7 +61,7 @@ export const CLIENT_INIT_QUESTIONS = [
61
61
  parse: (raw) => {
62
62
  const manager = PACKAGE_MANAGERS.find((candidate) => candidate === raw.trim());
63
63
  if (manager === undefined) {
64
- throw new ClientInitAnswerError(`${JSON.stringify(raw)} is not a supported package manager; choose ${PACKAGE_MANAGERS.join(" or ")}.`);
64
+ throw new AvClientInitAnswerError(`${JSON.stringify(raw)} is not a supported package manager; choose ${PACKAGE_MANAGERS.join(" or ")}.`);
65
65
  }
66
66
  return manager;
67
67
  },
@@ -90,7 +90,7 @@ export async function resolveClientInitAnswers(sources) {
90
90
  break;
91
91
  }
92
92
  catch (error) {
93
- if (!(error instanceof ClientInitAnswerError)) {
93
+ if (!(error instanceof AvClientInitAnswerError)) {
94
94
  throw error;
95
95
  }
96
96
  sources.say(error.message);
@@ -102,7 +102,7 @@ export async function resolveClientInitAnswers(sources) {
102
102
  }
103
103
  }
104
104
  if (missing.length > 0) {
105
- throw new ClientInitUnansweredError(`stdin is not a terminal, so nothing can be asked: pass ${missing.join(", ")}, or pass --yes to accept their defaults.`);
105
+ throw new AvClientInitUnansweredError(`stdin is not a terminal, so nothing can be asked: pass ${missing.join(", ")}, or pass --yes to accept their defaults.`);
106
106
  }
107
107
  return {
108
108
  entrypoint: answered.entrypoint,
@@ -1,8 +1,8 @@
1
1
  import type { PackageManager } from "./client-init.questions.js";
2
2
  /** The frontend `avclient init` serves, read before anything is asked or written. */
3
3
  /** The directory is not a project `avclient init` can set up. A refusal: nothing written. */
4
- export declare class ClientProjectRefusedError extends Error {
5
- readonly name = "ClientProjectRefusedError";
4
+ export declare class AvClientProjectRefusedError extends Error {
5
+ readonly name = "AvClientProjectRefusedError";
6
6
  }
7
7
  export type ClientProject = {
8
8
  readonly directory: string;
@@ -1,13 +1,13 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { findClientConfigFile } from "../config/config.loader.js";
4
- export class ClientProjectRefusedError extends Error {
5
- name = "ClientProjectRefusedError";
4
+ export class AvClientProjectRefusedError extends Error {
5
+ name = "AvClientProjectRefusedError";
6
6
  }
7
7
  export function inspectClientProject(directory) {
8
8
  const manifest = path.join(directory, "package.json");
9
9
  if (!existsSync(manifest)) {
10
- throw new ClientProjectRefusedError("avclient init runs in a frontend project: no package.json here; create one first.");
10
+ throw new AvClientProjectRefusedError("avclient init runs in a frontend project: no package.json here; create one first.");
11
11
  }
12
12
  const lockfiles = [
13
13
  ["package-lock.json", "npm"],
@@ -1,6 +1,8 @@
1
1
  /** One child process, stdout and stderr piped and collected: `init`'s install. */
2
2
  export type CommandRunner = (command: string, args: readonly string[], options: {
3
3
  readonly cwd: string;
4
+ /** Each chunk of the child's stdout and stderr as it arrives (`--verbose`). */
5
+ readonly onOutput?: (chunk: string) => void;
4
6
  }) => Promise<{
5
7
  readonly code: number;
6
8
  readonly stderr: string;
@@ -6,9 +6,12 @@ export const runCommand = (command, args, options) => new Promise((resolve, reje
6
6
  env: process.env,
7
7
  });
8
8
  let stderr = "";
9
- child.stdout.resume();
9
+ child.stdout.setEncoding("utf8").on("data", (chunk) => {
10
+ options.onOutput?.(chunk);
11
+ });
10
12
  child.stderr.setEncoding("utf8").on("data", (chunk) => {
11
13
  stderr += chunk;
14
+ options.onOutput?.(chunk);
12
15
  });
13
16
  child.once("error", reject);
14
17
  child.once("close", (code) => {
@@ -5,9 +5,9 @@ import { type OutputCheck, type TypeScriptResolver } from "./output.validator.js
5
5
  /** The four stages a write passes through, in order. */
6
6
  export type OutputWriteStage = "prepare" | "temp" | "validate" | "replace";
7
7
  /** A write that stopped; the previous output is intact unless it says otherwise. */
8
- export declare class OutputWriteError extends Error {
8
+ export declare class AvOutputWriteError extends Error {
9
9
  readonly stage: OutputWriteStage;
10
- readonly name = "OutputWriteError";
10
+ readonly name = "AvOutputWriteError";
11
11
  /**
12
12
  * What the run said before it stopped, in the order a success returns its
13
13
  * warnings: the emission's, the validator's, the writer's. Printed before the
@@ -19,7 +19,7 @@ export declare class OutputWriteError extends Error {
19
19
  readonly warnings?: readonly string[];
20
20
  });
21
21
  /** The same refusal, with `earlier` said before the warnings it already carries. */
22
- precededBy(earlier: readonly string[]): OutputWriteError;
22
+ precededBy(earlier: readonly string[]): AvOutputWriteError;
23
23
  }
24
24
  /**
25
25
  * Records `earlier` as said before `defect`, ahead of anything already recorded,
@@ -49,11 +49,11 @@ export interface ClientOutputWritten {
49
49
  * Writes `emission.tree` as `<generateAt>/AvClient.ts` and
50
50
  * `<generateAt>/generated/`.
51
51
  *
52
- * @throws OutputWriteError when the write stops; the previous output is intact.
52
+ * @throws AvOutputWriteError when the write stops; the previous output is intact.
53
53
  */
54
54
  export declare function writeClientOutput(input: ClientOutputWriteInput): Promise<ClientOutputWritten>;
55
55
  /**
56
- * @throws OutputWriteError when `generateAt` is not a directory, `generated` is
56
+ * @throws AvOutputWriteError when `generateAt` is not a directory, `generated` is
57
57
  * not a directory or an entry file is not a file: no confirmation repairs that.
58
58
  */
59
59
  export declare function findForeignOutputContent(generateAt: AbsolutePath, fileSystem?: OutputFileSystem): Promise<readonly string[]>;
@@ -4,9 +4,9 @@ import path from "node:path";
4
4
  import { carriesGeneratedOwnership, GENERATED_OWNERSHIP_HEAD_BYTES, } from "../emit/banner.emitter.js";
5
5
  import { CLIENT_ENTRY_FILES, GENERATED_DIRECTORY, LEGACY_CLIENT_ENTRY_FILES, } from "../emit/emitted-tree.interface.js";
6
6
  import { projectTypeScriptResolver, validateOutputTree, } from "./output.validator.js";
7
- export class OutputWriteError extends Error {
7
+ export class AvOutputWriteError extends Error {
8
8
  stage;
9
- name = "OutputWriteError";
9
+ name = "AvOutputWriteError";
10
10
  warnings;
11
11
  constructor(stage, message, options) {
12
12
  super(message, options);
@@ -14,7 +14,7 @@ export class OutputWriteError extends Error {
14
14
  this.warnings = options?.warnings ?? [];
15
15
  }
16
16
  precededBy(earlier) {
17
- return new OutputWriteError(this.stage, this.message, {
17
+ return new AvOutputWriteError(this.stage, this.message, {
18
18
  cause: this.cause,
19
19
  warnings: [...earlier, ...this.warnings],
20
20
  });
@@ -65,7 +65,7 @@ export async function writeClientOutput(input) {
65
65
  ...validationWarnings,
66
66
  ...writerWarnings,
67
67
  ];
68
- throw error instanceof OutputWriteError
68
+ throw error instanceof AvOutputWriteError
69
69
  ? error.precededBy(said)
70
70
  : precedeDefect(error, said);
71
71
  }
@@ -99,7 +99,7 @@ export async function writeClientOutput(input) {
99
99
  validationWarnings = validation.warnings;
100
100
  }
101
101
  else {
102
- throw new OutputWriteError("validate", `the generated output failed its ${validation.checked} check, so ${LEFT_AS_THEY_WERE(paths)} ` +
102
+ throw new AvOutputWriteError("validate", `the generated output failed its ${validation.checked} check, so ${LEFT_AS_THEY_WERE(paths)} ` +
103
103
  "This is a defect in @aventara/client; please report it with these findings:\n" +
104
104
  validation.findings.map((finding) => ` ${finding}`).join("\n"));
105
105
  }
@@ -267,7 +267,7 @@ async function inspectOwnedEntries(fs, paths, generatedSource = paths.generated)
267
267
  const generatedStats = await lstatOrUndefined(fs, generatedSource);
268
268
  if (generatedStats !== undefined) {
269
269
  if (!generatedStats.isDirectory()) {
270
- throw new OutputWriteError("prepare", `refusing to replace ${generatedSource}: it is not a directory, and the generator owns ` +
270
+ throw new AvOutputWriteError("prepare", `refusing to replace ${generatedSource}: it is not a directory, and the generator owns ` +
271
271
  `${GENERATED_DIRECTORY}/ as one; move it, or choose another \`generateAt\`. Nothing was written.`);
272
272
  }
273
273
  const entries = await fs.readdir(generatedSource, {
@@ -292,7 +292,7 @@ async function inspectOwnedEntries(fs, paths, generatedSource = paths.generated)
292
292
  continue;
293
293
  }
294
294
  if (!stats.isFile()) {
295
- throw new OutputWriteError("prepare", `refusing to replace ${file}: it is not a file, and the generator owns ${name} ` +
295
+ throw new AvOutputWriteError("prepare", `refusing to replace ${file}: it is not a file, and the generator owns ${name} ` +
296
296
  "as one; move it, or choose another `generateAt`. Nothing was written.");
297
297
  }
298
298
  entries.push(name);
@@ -317,12 +317,12 @@ async function inspectOwnedEntries(fs, paths, generatedSource = paths.generated)
317
317
  }
318
318
  function foreignContentRefusal(paths, foreign) {
319
319
  const them = foreign.length === 1 ? "it" : "them";
320
- return new OutputWriteError("prepare", `refusing to generate into ${paths.generateAt}: ${foreign.join(", ")} ${foreign.length === 1 ? "was" : "were"} ` +
320
+ return new AvOutputWriteError("prepare", `refusing to generate into ${paths.generateAt}: ${foreign.join(", ")} ${foreign.length === 1 ? "was" : "were"} ` +
321
321
  `not generated by @aventara/client, and generating would overwrite or remove ${them}; confirm the ` +
322
322
  `override, move ${them} out, or choose another \`generateAt\`. Nothing was written.`);
323
323
  }
324
324
  function notADirectory(generateAt) {
325
- return new OutputWriteError("prepare", `refusing to generate into ${generateAt}: it is not a directory. ` +
325
+ return new AvOutputWriteError("prepare", `refusing to generate into ${generateAt}: it is not a directory. ` +
326
326
  "`generateAt` must name a directory; choose another `generateAt`. Nothing was written.");
327
327
  }
328
328
  async function carriesOwnershipLine(fs, file) {
@@ -341,7 +341,7 @@ async function rollBack(paths, cause, undo) {
341
341
  await undo();
342
342
  }
343
343
  catch (rollbackError) {
344
- throw new OutputWriteError("replace", `the generated output could not be moved into ${paths.generateAt} (${describe(cause)}), and the ` +
344
+ throw new AvOutputWriteError("replace", `the generated output could not be moved into ${paths.generateAt} (${describe(cause)}), and the ` +
345
345
  `previous output could not be restored (${describe(rollbackError)}). The previous ` +
346
346
  `${GENERATED_DIRECTORY}/ is intact at ${paths.previous}; the next run puts it back.`, { cause });
347
347
  }
@@ -351,10 +351,10 @@ async function stage(name, paths, run) {
351
351
  return await run();
352
352
  }
353
353
  catch (error) {
354
- if (error instanceof OutputWriteError || !isSystemError(error)) {
354
+ if (error instanceof AvOutputWriteError || !isSystemError(error)) {
355
355
  throw error;
356
356
  }
357
- throw new OutputWriteError(name, `could not ${STAGE_ACTIONS[name]} (${describe(error)}); ${LEFT_AS_THEY_WERE(paths)}`, { cause: error });
357
+ throw new AvOutputWriteError(name, `could not ${STAGE_ACTIONS[name]} (${describe(error)}); ${LEFT_AS_THEY_WERE(paths)}`, { cause: error });
358
358
  }
359
359
  }
360
360
  const STAGE_ACTIONS = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aventara/client",
3
- "version": "0.1.0-pilot.4",
3
+ "version": "1.0.0-rc.1",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "Development-time generator for Aventara typed remote clients.",
6
6
  "type": "module",
@@ -20,10 +20,11 @@
20
20
  },
21
21
  "files": [
22
22
  "dist",
23
- "LICENSE-ADDITIONAL-PERMISSION.md"
23
+ "LICENSE-ADDITIONAL-PERMISSION.md",
24
+ "CHANGELOG.md"
24
25
  ],
25
26
  "dependencies": {
26
- "@aventara/core": "0.1.0-pilot.4",
27
+ "@aventara/core": "1.0.0-rc.1",
27
28
  "c12": "3.3.4",
28
29
  "get-tsconfig": "4.10.0"
29
30
  },
@@ -39,12 +40,12 @@
39
40
  "access": "public"
40
41
  },
41
42
  "devDependencies": {
42
- "@aventara/testing": "0.1.0-pilot.4",
43
+ "@aventara/testing": "1.0.0-rc.1",
43
44
  "@types/node": "24.10.1",
44
45
  "typescript": "^5.9.2"
45
46
  },
46
47
  "scripts": {
47
- "build": "tsc -p tsconfig.json --removeComments --declaration false && tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist",
48
+ "build": "node ../../scripts/build/build-output.cleaner.ts dist && tsc -p tsconfig.json --removeComments --declaration false && tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist",
48
49
  "typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
49
50
  "test": "vitest run --typecheck --config ../../vitest.config.mts --root .",
50
51
  "lint": "biome lint src"