@aventara/client 1.0.0-rc.0 → 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 (56) hide show
  1. package/CHANGELOG.md +457 -0
  2. package/README.md +159 -141
  3. package/dist/cli/command.parser.d.ts +3 -3
  4. package/dist/cli/command.parser.js +10 -10
  5. package/dist/cli/generation-failure.renderer.js +24 -24
  6. package/dist/config/client-config.interface.d.ts +7 -7
  7. package/dist/config/config.loader.d.ts +4 -4
  8. package/dist/config/config.loader.js +7 -7
  9. package/dist/config/config.resolver.d.ts +7 -7
  10. package/dist/config/config.resolver.js +12 -12
  11. package/dist/config/env.cascade.d.ts +2 -2
  12. package/dist/config/env.cascade.js +4 -4
  13. package/dist/config/module-style.resolver.d.ts +1 -1
  14. package/dist/config/module-style.resolver.js +2 -2
  15. package/dist/config/tsconfig.locator.d.ts +1 -1
  16. package/dist/config/tsconfig.locator.js +2 -2
  17. package/dist/contract/contract.acceptance.d.ts +4 -4
  18. package/dist/contract/contract.acceptance.js +4 -4
  19. package/dist/contract/contract.fetcher.d.ts +3 -3
  20. package/dist/contract/contract.fetcher.js +6 -6
  21. package/dist/contract/contract.loader.d.ts +6 -6
  22. package/dist/contract/contract.loader.js +2 -2
  23. package/dist/emit/client-surface.emitter.d.ts +2 -2
  24. package/dist/emit/client-surface.emitter.js +63 -40
  25. package/dist/emit/client-tree.emitter.d.ts +3 -3
  26. package/dist/emit/client-tree.emitter.js +8 -5
  27. package/dist/emit/contract-carrier.emitter.d.ts +3 -3
  28. package/dist/emit/derivation.emitter.js +1 -0
  29. package/dist/emit/descriptor.emitter.d.ts +2 -2
  30. package/dist/emit/descriptor.emitter.js +1 -1
  31. package/dist/emit/emitted-tree.interface.d.ts +1 -1
  32. package/dist/emit/enum.emitter.d.ts +2 -2
  33. package/dist/emit/field-check.emitter.d.ts +21 -0
  34. package/dist/emit/field-check.emitter.js +603 -0
  35. package/dist/emit/json-shape.emitter.d.ts +2 -2
  36. package/dist/emit/name.deriver.d.ts +6 -6
  37. package/dist/emit/name.deriver.js +14 -14
  38. package/dist/emit/named-type.emitter.d.ts +2 -2
  39. package/dist/emit/runtime.emitter.d.ts +2 -2
  40. package/dist/emit/runtime.emitter.js +115 -110
  41. package/dist/emit/scalar.codec.d.ts +1 -1
  42. package/dist/emit/scalar.codec.js +24 -24
  43. package/dist/emit/transaction.emitter.js +26 -14
  44. package/dist/generate.d.ts +2 -2
  45. package/dist/generate.js +2 -2
  46. package/dist/index.d.ts +1 -1
  47. package/dist/init/client-init.errors.d.ts +4 -4
  48. package/dist/init/client-init.errors.js +4 -4
  49. package/dist/init/client-init.orchestrator.js +7 -7
  50. package/dist/init/client-init.questions.d.ts +5 -5
  51. package/dist/init/client-init.questions.js +10 -10
  52. package/dist/init/client-project.inspector.d.ts +2 -2
  53. package/dist/init/client-project.inspector.js +3 -3
  54. package/dist/output/output.writer.d.ts +5 -5
  55. package/dist/output/output.writer.js +12 -12
  56. package/package.json +3 -3
@@ -29,7 +29,7 @@ const SCALAR_CODEC_SOURCES = {
29
29
  : refuse("bigint", "wire", "a base-10 integer string")`,
30
30
  },
31
31
  decimal: {
32
- encode: `(value) => (value instanceof Decimal ? value.toString() : refuse("decimal", "runtime", "a Decimal"))`,
32
+ encode: `(value) => (value instanceof AvDecimal ? value.toString() : refuse("decimal", "runtime", "an AvDecimal"))`,
33
33
  decode: "decodeDecimal",
34
34
  },
35
35
  datetime: {
@@ -48,13 +48,13 @@ const SCALAR_CODEC_SOURCES = {
48
48
  },
49
49
  };
50
50
  function codecModuleHead(from) {
51
- return `import { Decimal } from ${from("./decimal")};
51
+ return `import { AvDecimal } from ${from("./decimal")};
52
52
  import type { FieldDecoding } from ${from("./descriptor")};
53
53
 
54
54
  /**
55
55
  * The scalar codecs of Aventara protocol version 1: one per built-in
56
56
  * scalar, converting between the generated runtime value and the JSON wire value.
57
- * A value is never wrapped per field — the Contract names the scalar, so the
57
+ * A value is never wrapped per field: the Contract names the scalar, so the
58
58
  * caller says which scalar a value is.
59
59
  *
60
60
  * null is explicit in both directions and passes through; whether a field admits
@@ -82,7 +82,7 @@ const DATETIME_WIRE = ${wireGrammarLiteral("datetime")};
82
82
  const BASE64_WIRE = ${wireGrammarLiteral("bytes")};
83
83
  const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
84
84
 
85
- function refuse(scalar: BuiltInScalar, side: Side, expected: string): never {
85
+ function refuse(scalar: AvBuiltInScalar, side: Side, expected: string): never {
86
86
  throw new TypeError('A "' + scalar + '" ' + side + " value must be " + expected + ".");
87
87
  }
88
88
 
@@ -94,10 +94,10 @@ function isFiniteNumber(value: unknown): value is number {
94
94
  return typeof value === "number" && Number.isFinite(value);
95
95
  }
96
96
 
97
- function decodeDecimal(wire: unknown): Decimal {
97
+ function decodeDecimal(wire: unknown): AvDecimal {
98
98
  if (typeof wire === "string") {
99
99
  try {
100
- return new Decimal(wire);
100
+ return new AvDecimal(wire);
101
101
  } catch {
102
102
  // Refused below, naming the scalar.
103
103
  }
@@ -195,8 +195,8 @@ function kindOf(value: unknown): string {
195
195
  if (value instanceof Uint8Array) {
196
196
  return "a Uint8Array";
197
197
  }
198
- if (value instanceof Decimal) {
199
- return "a Decimal";
198
+ if (value instanceof AvDecimal) {
199
+ return "an AvDecimal";
200
200
  }
201
201
  if (typeof value === "object" && value !== null && isPlainRecord(value)) {
202
202
  return "a symbol-keyed property";
@@ -288,7 +288,7 @@ function writeWireJson(value: unknown, path: Path, ancestors: Set<object>): stri
288
288
  return text;
289
289
  }
290
290
 
291
- function codecOf(scalar: BuiltInScalar): ScalarCodec {
291
+ function codecOf(scalar: AvBuiltInScalar): ScalarCodec {
292
292
  if (!Object.hasOwn(SCALAR_CODECS, scalar)) {
293
293
  throw new TypeError('"' + String(scalar) + '" is not a built-in scalar of this protocol version.');
294
294
  }
@@ -296,21 +296,21 @@ function codecOf(scalar: BuiltInScalar): ScalarCodec {
296
296
  }
297
297
 
298
298
  /** The JSON wire value of a scalar's runtime value; null passes through. */
299
- export function encodeScalar(scalar: BuiltInScalar, value: unknown): unknown {
299
+ export function encodeScalar(scalar: AvBuiltInScalar, value: unknown): unknown {
300
300
  const codec = codecOf(scalar);
301
301
  return value === null ? null : codec.encode(value);
302
302
  }
303
303
 
304
304
  /** The runtime value of a scalar's JSON wire value; null passes through. */
305
- export function decodeScalar(scalar: BuiltInScalar, wire: unknown): unknown {
305
+ export function decodeScalar(scalar: AvBuiltInScalar, wire: unknown): unknown {
306
306
  const codec = codecOf(scalar);
307
307
  return wire === null ? null : codec.decode(wire);
308
308
  }
309
309
 
310
310
  /**
311
311
  * The JSON text of a request body whose scalars are already encoded. Every
312
- * undefined property is stripped, at every depth — an optional property is
313
- * omitted, never sent — and null is kept as the explicit value it is.
312
+ * undefined property is stripped, at every depth (an optional property is
313
+ * omitted, never sent), and null is kept as the explicit value it is.
314
314
  * Anything else that is not JSON-safe is refused rather than coerced: it means a
315
315
  * value escaped its scalar codec.
316
316
  */
@@ -321,8 +321,8 @@ export function serializeWireBody(body: unknown): string {
321
321
  /**
322
322
  * An operation's arguments with every scalar in its wire form, read off each
323
323
  * value's runtime class: a
324
- * bigint, a Date, a Decimal or a Uint8Array is encoded wherever it stands — a
325
- * filter operand, a data value, a cursor — and plain objects and arrays are
324
+ * bigint, a Date, an AvDecimal or a Uint8Array is encoded wherever it stands (a
325
+ * filter operand, a data value, a cursor), and plain objects and arrays are
326
326
  * walked. Every other value is left for serializeWireBody, which refuses what is
327
327
  * not JSON-safe. A json value cannot hold one of the four classes (the types
328
328
  * exclude them), so nothing inside one is ever re-read as a scalar.
@@ -333,8 +333,8 @@ export function encodeArguments(value: unknown): unknown {
333
333
 
334
334
  /**
335
335
  * One read of a caller's arguments. Every native read on them happens through it,
336
- * here and in the transaction builder, so a value that cannot be read — a revoked
337
- * Proxy (an Immer draft after produce), a throwing getter or trap — is refused at its
336
+ * here and in the transaction builder, so a value that cannot be read (a revoked
337
+ * Proxy such as an Immer draft after produce, a throwing getter or trap) is refused at its
338
338
  * path with a TypeError before anything is sent, never with the engine's own error.
339
339
  */
340
340
  export function readArgument<T>(path: Path, read: () => T): T {
@@ -352,7 +352,7 @@ function objectKind(value: object): "datetime" | "decimal" | "bytes" | "containe
352
352
  if (value instanceof Date) {
353
353
  return "datetime";
354
354
  }
355
- if (value instanceof Decimal) {
355
+ if (value instanceof AvDecimal) {
356
356
  return "decimal";
357
357
  }
358
358
  if (value instanceof Uint8Array) {
@@ -413,13 +413,13 @@ export type DecodeTable = {
413
413
  * table: a number (a count), null, and the data of a Resource the table
414
414
  * does not name pass through; a list is a list of
415
415
  * records, anything else is one record of \`resource\`. In a record, a field the
416
- * table names is revived — each item of a list field — a relation recurses with
416
+ * table names is revived (each item of a list field), a relation recurses with
417
417
  * its target, and every other key passes through untouched: json is never
418
418
  * revived. A to-many relation's value is a list, \`{ data, count }\` or
419
- * \`{ count }\` — the three shapes the derivation gives it.
419
+ * \`{ count }\`, the three shapes the derivation gives it.
420
420
  *
421
- * @throws TypeError when a value is not the wire form its field's scalar takes —
422
- * which the transport answers as a TransportError.
421
+ * @throws TypeError when a value is not the wire form its field's scalar takes,
422
+ * which the transport answers as an AvTransportError.
423
423
  */
424
424
  export function decodeResult(table: DecodeTable, resource: string, value: unknown): unknown {
425
425
  // A Resource the table does not name has nothing to revive: its data passes.
@@ -477,9 +477,9 @@ export function emitScalarCodecModule(style) {
477
477
  path: "runtime/codec.ts",
478
478
  source: codecModuleHead(moduleSpecifierWriter(style)) +
479
479
  "\n/** The built-in scalars of the protocol. */\n" +
480
- `export type BuiltInScalar =\n${union};\n` +
480
+ `export type AvBuiltInScalar =\n${union};\n` +
481
481
  CODEC_MODULE_BODY +
482
- "\nconst SCALAR_CODECS: { readonly [S in BuiltInScalar]: ScalarCodec } = {\n" +
482
+ "\nconst SCALAR_CODECS: { readonly [S in AvBuiltInScalar]: ScalarCodec } = {\n" +
483
483
  `${table}\n};\n`,
484
484
  };
485
485
  }
@@ -24,8 +24,8 @@ export interface FingerprintNode {
24
24
  }
25
25
 
26
26
  /**
27
- * The node's fingerprint, or null when its arguments are not canonical JSON — a
28
- * lone surrogate, a non-finite number — exactly when core cannot compute one
27
+ * The node's fingerprint, or null when its arguments are not canonical JSON (a
28
+ * lone surrogate, a non-finite number), exactly when core cannot compute one
29
29
  * either; the server then answers the operation itself.
30
30
  */
31
31
  export function fingerprintOf(node: FingerprintNode): string | null {
@@ -210,20 +210,20 @@ function base64Url(bytes: Uint8Array): string {
210
210
  `;
211
211
  function transactionModuleSource(from) {
212
212
  return `import { encodeArguments, readArgument, serializeWireBody } from ${from("./codec")};
213
- import { type Cause, type FrameworkError, frameworkErrorOf } from ${from("./errors")};
213
+ import { type AvCause, type AvError, frameworkErrorOf } from ${from("./errors")};
214
214
  import { fingerprintOf } from ${from("./fingerprint")};
215
- import { type CallOptions, executePlan, type TransportConnection } from ${from("./transport")};
215
+ import { type AvCallOptions, executePlan, type TransportConnection } from ${from("./transport")};
216
216
 
217
217
  /**
218
218
  * The deferred transaction builder and runner: \`avClient.tx\` defers an operation
219
- * into a handle — pure data, no request — and \`avClient.transaction\` binds a
219
+ * into a handle (pure data, no request), and \`avClient.transaction\` binds a
220
220
  * list of handles into one transaction plan, sends it once, and resolves one
221
221
  * result per handle in list order. A handle has no connection, so any client of
222
222
  * this generated tree may run it.
223
223
  */
224
224
 
225
225
  /**
226
- * What a handle defers — or why its arguments could not be read: the builder
226
+ * What a handle defers, or why its arguments could not be read: the builder
227
227
  * never throws into app code, and running the handle refuses it instead.
228
228
  */
229
229
  interface DeferredRecord {
@@ -331,7 +331,7 @@ function placeholderSites(args: unknown): readonly PlaceholderSite[] {
331
331
  /**
332
332
  * Defers one operation into a handle: nothing is sent, and nothing is thrown.
333
333
  * Its arguments are copied now, so a later change to the caller's data cannot
334
- * move a reference — and a handle can reference only handles built before it.
334
+ * move a reference, and a handle can reference only handles built before it.
335
335
  * Arguments that cannot be read are kept as their refusal, which running the
336
336
  * handle rejects with.
337
337
  */
@@ -360,8 +360,8 @@ export function deferOperation(resource: string, family: string, variant: string
360
360
  }
361
361
 
362
362
  /** What core answers in process for a plan entry it cannot run (A2004 / V1001). */
363
- function entryRefusal(operation: number, message: string): FrameworkError {
364
- const cause: Cause = {
363
+ function entryRefusal(operation: number, message: string): AvError {
364
+ const cause: AvCause = {
365
365
  message: "Transaction plan failed framework validation.",
366
366
  issues: [{ path: ["operations", operation], code: "V1001", message }],
367
367
  operation,
@@ -369,19 +369,29 @@ function entryRefusal(operation: number, message: string): FrameworkError {
369
369
  return frameworkErrorOf("A2004", cause);
370
370
  }
371
371
 
372
+ /**
373
+ * The checks a plan's steps run before it is sent (the ClientContract's field
374
+ * validators, when it has any). Throws the AvError the server would answer.
375
+ */
376
+ export type PlanCheck = (
377
+ steps: readonly { readonly resource: string; readonly family: string; readonly args: unknown }[],
378
+ ) => void;
379
+
372
380
  /**
373
381
  * Binds \`steps\` into the plan, sends it once, and resolves one decoded result per
374
382
  * step. Refused before anything is sent, with what core answers in
375
383
  * process: a list that is not one, an entry that is not a handle of this
376
- * generated client, one handle twice (ValidationError A2004 / V1001 at
377
- * ["operations", i]: the entry itself); a reference to a handle not in the list (ValidationError A2007 /
384
+ * generated client, one handle twice (AvValidationError A2004 / V1001 at
385
+ * ["operations", i]: the entry itself); a reference to a handle not in the list (AvValidationError A2007 /
378
386
  * V1010 at its \`$ref\`); a handle whose arguments could not be read when it was
379
- * built (the TypeError naming where).
387
+ * built (the TypeError naming where); and, when \`check\` is given, a step whose
388
+ * values break a field check (AvValidationError A2004, as the server answers it).
380
389
  */
381
390
  export async function runTransaction(
382
391
  connection: TransportConnection,
383
392
  steps: unknown,
384
- options: CallOptions = {},
393
+ options: AvCallOptions = {},
394
+ check?: PlanCheck,
385
395
  ): Promise<unknown[]> {
386
396
  if (!Array.isArray(steps)) {
387
397
  throw frameworkErrorOf("A2004", {
@@ -423,9 +433,11 @@ export async function runTransaction(
423
433
  }
424
434
  }
425
435
 
436
+ check?.(records);
437
+
426
438
  // Bound lazily: a node's references need their source's
427
439
  // fingerprint, so each node is built once, on first need, wherever its source
428
- // stands in the list — the server, not this client, refuses a later one.
440
+ // stands in the list. The server, not this client, refuses a later one.
429
441
  const nodes = new Map<number, PlanNode>();
430
442
  const nodeAt = (index: number): PlanNode => {
431
443
  const built = nodes.get(index);
@@ -73,8 +73,8 @@ export type ClientGenerationOutcome = ClientGenerated | ClientUpToDate | Foreign
73
73
  * generator did not produce stands in its way and `overrideForeign` is not set,
74
74
  * says what it is and writes nothing.
75
75
  *
76
- * @throws a refusal (`EnvFileError`, `ClientConfigError`, `ContractTransportError`,
77
- * `ContractProtocolError`, `GeneratedNameError`, `OutputWriteError`) whose
76
+ * @throws a refusal (`AvEnvFileError`, `AvClientConfigError`, `AvContractTransportError`,
77
+ * `AvContractProtocolError`, `AvGeneratedNameError`, `AvOutputWriteError`) whose
78
78
  * message is the whole diagnosis; the previous output is intact. Anything else
79
79
  * is a defect.
80
80
  */
package/dist/generate.js CHANGED
@@ -9,7 +9,7 @@ import { loadClientContractSince } from "./contract/contract.loader.js";
9
9
  import { emitClientTree } from "./emit/client-tree.emitter.js";
10
10
  import { parseContractCarrier } from "./emit/contract-carrier.emitter.js";
11
11
  import { CLIENT_ENTRY_FILE, CONTRACT_CARRIER_MODULE, GENERATED_DIRECTORY, } from "./emit/emitted-tree.interface.js";
12
- import { findForeignOutputContent, OutputWriteError, ownedOutputMatches, precedeDefect, writeClientOutput, } from "./output/output.writer.js";
12
+ import { AvOutputWriteError, findForeignOutputContent, ownedOutputMatches, precedeDefect, writeClientOutput, } from "./output/output.writer.js";
13
13
  const runPhase = async (_title, body) => await body();
14
14
  async function storedContract(generateAt) {
15
15
  try {
@@ -87,7 +87,7 @@ export async function generateClient(input) {
87
87
  const into = path.relative(input.directory, config.generateAt) || ".";
88
88
  const write = (overrideForeign) => step(`Checking and writing the client into ${into}`, () => writeInto(overrideForeign));
89
89
  const foreign = await findForeignOutputContent(config.generateAt).catch((error) => {
90
- throw error instanceof OutputWriteError
90
+ throw error instanceof AvOutputWriteError
91
91
  ? error.precededBy(emission.warnings)
92
92
  : precedeDefect(error, emission.warnings);
93
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
  }
@@ -4,9 +4,9 @@ import { GENERATION_STEPS, runGenerate, } from "../cli/generate.command.js";
4
4
  import { createStepReporter } from "../cli/step.reporter.js";
5
5
  import { CLIENT_CONFIG_FILE } from "../config/config.loader.js";
6
6
  import { resolveModuleStyle } from "../config/module-style.resolver.js";
7
- import { ContractTransportError } from "../contract/contract.fetcher.js";
7
+ import { AvContractTransportError } from "../contract/contract.fetcher.js";
8
8
  import { CLIENT_ENTRY_FILE } from "../emit/emitted-tree.interface.js";
9
- import { ClientInitNotConfirmedError, ClientInitStepError, } from "./client-init.errors.js";
9
+ import { AvClientInitNotConfirmedError, AvClientInitStepError, } from "./client-init.errors.js";
10
10
  import { GENERATE_SCRIPT, planClientInit } from "./client-init.planner.js";
11
11
  import { resolveClientInitAnswers } from "./client-init.questions.js";
12
12
  import { inspectClientProject } from "./client-project.inspector.js";
@@ -40,11 +40,11 @@ export async function runClientInit(command, io) {
40
40
  const them = plan.conflicts.length === 1 ? "it" : "them";
41
41
  const has = plan.conflicts.length === 1 ? "has" : "have";
42
42
  if (!io.interactive) {
43
- 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}.`);
44
44
  }
45
45
  io.stderr(`avclient: warning: ${plan.conflicts.join(", ")} already ${has} other content, and initializing will replace ${them}.\n`);
46
46
  if (!(await io.confirm("avclient: replace them and initialize? [y/N] "))) {
47
- 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}.`);
48
48
  }
49
49
  }
50
50
  const writes = plan.conflicts.length === 0 ? plan.keeping : plan.replacing;
@@ -80,7 +80,7 @@ export async function runClientInit(command, io) {
80
80
  onOutput: progress.output,
81
81
  });
82
82
  if (installed.code !== 0) {
83
- 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()}`);
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
84
  }
85
85
  });
86
86
  if (command.skipGenerate) {
@@ -92,8 +92,8 @@ export async function runClientInit(command, io) {
92
92
  return await runGenerate(io, command.yes, progress);
93
93
  }
94
94
  catch (error) {
95
- if (error instanceof ContractTransportError) {
96
- 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\`.`);
97
97
  }
98
98
  throw error;
99
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"],
@@ -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": "1.0.0-rc.0",
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",
@@ -24,7 +24,7 @@
24
24
  "CHANGELOG.md"
25
25
  ],
26
26
  "dependencies": {
27
- "@aventara/core": "1.0.0-rc.0",
27
+ "@aventara/core": "1.0.0-rc.1",
28
28
  "c12": "3.3.4",
29
29
  "get-tsconfig": "4.10.0"
30
30
  },
@@ -40,7 +40,7 @@
40
40
  "access": "public"
41
41
  },
42
42
  "devDependencies": {
43
- "@aventara/testing": "1.0.0-rc.0",
43
+ "@aventara/testing": "1.0.0-rc.1",
44
44
  "@types/node": "24.10.1",
45
45
  "typescript": "^5.9.2"
46
46
  },