@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
@@ -1,4 +1,4 @@
1
- import { OPERATION_CODES, VALIDATION_CODES, } from "@aventara/core";
1
+ import { FRAMEWORK_ERROR_CLASS_OF, OPERATION_CODES, VALIDATION_CODES, } from "@aventara/core/internal";
2
2
  import { AvProtocol } from "@aventara/core/protocol";
3
3
  import { entrypointHref } from "../config/config.resolver.js";
4
4
  import { moduleSpecifierWriter, } from "./module-style.interface.js";
@@ -14,7 +14,7 @@ export function emitDecimalModule() {
14
14
  * It holds the decimal string it was made from, digit for digit, and is sent as
15
15
  * that string. It does no arithmetic.
16
16
  */
17
- export class Decimal {
17
+ export class AvDecimal {
18
18
  readonly #value: string;
19
19
 
20
20
  /** @throws TypeError when the value is not a decimal string. */
@@ -30,7 +30,7 @@ export class Decimal {
30
30
  return this.#value;
31
31
  }
32
32
 
33
- /** The decimal string, exactly as given: what JSON.stringify writes for a Decimal. */
33
+ /** The decimal string, exactly as given: what JSON.stringify writes for an AvDecimal. */
34
34
  toJSON(): string {
35
35
  return this.#value;
36
36
  }
@@ -52,50 +52,25 @@ export function emitMetadataModule(protocol, entrypoint) {
52
52
  };
53
53
  }
54
54
  const FRAMEWORK_ERROR_CLASSES = {
55
- AuthError: "The caller is not authenticated, or is not permitted to run the operation.",
56
- ConflictError: "The operation conflicted with stored state, and nothing was written. A2008 fails the same way if sent again; A2013 and A2014 may succeed if sent again, and no retry is automatic.",
57
- ContractMismatchError: "This client was generated against a ClientContract other than the one the server serves. Regenerate it with `avclient generate`.",
58
- InternalError: "The server failed. Nothing in its cause is actionable; the diagnostics are in the server's logs.",
59
- NotFoundError: "A strict unique target does not exist.",
60
- ProtocolError: "The request was not one the server could route or interpret: an unknown Resource or operation, a protocol version it does not speak, or a body, media type, size or method it refuses.",
61
- ValidationError: "The request broke a Contract rule: a field, type, capability, projection, reference or limit. The cause's issues say which, by code and path.",
55
+ AvAuthError: "The caller is not authenticated, or is not permitted to run the operation.",
56
+ AvConflictError: "The operation conflicted with stored state, and nothing was written. A2008 fails the same way if sent again; A2013 and A2014 may succeed if sent again, and no retry is automatic.",
57
+ AvContractMismatchError: "This client was generated against a ClientContract other than the one the server serves. Regenerate it with `avclient generate`.",
58
+ AvInternalError: "The server failed. Nothing in its cause is actionable; the diagnostics are in the server's logs.",
59
+ AvNotFoundError: "A strict unique target does not exist.",
60
+ AvProtocolError: "The request was not one the server could route or interpret: an unknown Resource or operation, a protocol version it does not speak, or a body, media type, size or method it refuses.",
61
+ AvValidationError: "The request broke a Contract rule: a field, type, capability, projection, reference or limit. The cause's issues say which, by code and path.",
62
62
  };
63
- const FRAMEWORK_ERROR_CLASS_OF = {
64
- A2000: "ProtocolError",
65
- A2001: "ProtocolError",
66
- A2002: "ProtocolError",
67
- A2003: "NotFoundError",
68
- A2004: "ValidationError",
69
- A2005: "ContractMismatchError",
70
- A2006: "ProtocolError",
71
- A2007: "ValidationError",
72
- A2008: "ConflictError",
73
- A2009: "ValidationError",
74
- A2010: "ProtocolError",
75
- A2011: "ProtocolError",
76
- A2012: "ProtocolError",
77
- A2013: "ConflictError",
78
- A2014: "ConflictError",
79
- A3000: "InternalError",
80
- A3001: "InternalError",
81
- A3002: "InternalError",
82
- A3003: "InternalError",
83
- A3004: "InternalError",
84
- A4000: "AuthError",
85
- A4001: "AuthError",
86
- A4002: "AuthError",
87
- };
88
- export const FRAMEWORK_ERROR_CLASS_NAMES = Object.keys(FRAMEWORK_ERROR_CLASSES);
63
+ export const FRAMEWORK_ERROR_CLASS_NAMES = Object.keys(FRAMEWORK_ERROR_CLASSES).sort((left, right) => (left < right ? -1 : left > right ? 1 : 0));
89
64
  const ERROR_CODES = OPERATION_CODES.filter((code) => !code.startsWith("A1"));
90
65
  export function errorsModuleExports() {
91
66
  return [
92
67
  ...FRAMEWORK_ERROR_CLASS_NAMES,
93
- "FrameworkError",
94
- "TransportError",
95
- "type Cause",
96
- "type OperationCode",
97
- "type ValidationCode",
98
- "type ValidationIssue",
68
+ "AvError",
69
+ "AvTransportError",
70
+ "type AvCause",
71
+ "type AvOperationCode",
72
+ "type AvValidationCode",
73
+ "type AvValidationIssue",
99
74
  ].sort((left, right) => {
100
75
  const a = left.replace(/^type /, "");
101
76
  const b = right.replace(/^type /, "");
@@ -108,17 +83,24 @@ function codeArray(codes) {
108
83
  const ERRORS_MODULE_HEAD = `/**
109
84
  * The outcome codes and error classes of Aventara protocol version 1.
110
85
  *
111
- * A1xxx resolves; every other code throws the FrameworkError subclass of its
86
+ * A1xxx resolves; every other code throws the AvError subclass of its
112
87
  * class; a response that is not a framework envelope throws
113
- * TransportError. Branch on the class or on \`code\`, never on the message.
88
+ * AvTransportError. Branch on the class or on \`code\`, never on the message.
114
89
  */
115
90
  `;
116
91
  const ERRORS_MODULE_TYPES = `
117
92
  /** One actionable validation issue; \`path\` locates it in the request. */
118
- export interface ValidationIssue {
93
+ export interface AvValidationIssue {
119
94
  readonly path?: readonly (string | number)[];
120
- readonly code: ValidationCode;
95
+ readonly code: AvValidationCode;
121
96
  readonly message: string;
97
+ /**
98
+ * The failed check's name (max_length, string_format, …): present, with
99
+ * \`params\`, only on the issue of a failed field validator or Json Shape check.
100
+ */
101
+ readonly check?: string;
102
+ /** That check's parameters, as the ClientContract states them ({ maximum: 80 }). */
103
+ readonly params?: Readonly<Record<string, unknown>>;
122
104
  }
123
105
 
124
106
  /**
@@ -126,26 +108,26 @@ export interface ValidationIssue {
126
108
  * a member the protocol does not define is never carried, so no stack, SQL or
127
109
  * server path the server should not have sent reaches it.
128
110
  */
129
- export interface Cause {
111
+ export interface AvCause {
130
112
  readonly message: string;
131
- readonly issues?: readonly ValidationIssue[];
113
+ readonly issues?: readonly AvValidationIssue[];
132
114
  /** The index of the failing operation in a transaction plan. */
133
115
  readonly operation?: number;
134
116
  }
135
117
 
136
118
  /**
137
119
  * A framework outcome that throws: every code outside A1xxx. It is
138
- * always thrown as the subclass of its code's class; catch FrameworkError to
120
+ * always thrown as the subclass of its code's class; catch AvError to
139
121
  * catch them all.
140
122
  */
141
- export class FrameworkError<C extends OperationErrorCode = OperationErrorCode> extends Error {
142
- override readonly name: string = "FrameworkError";
123
+ export class AvError<C extends AvOperationErrorCode = AvOperationErrorCode> extends Error {
124
+ override readonly name: string = "AvError";
143
125
  /** The outcome code. Branch on this, never on the message. */
144
126
  readonly code: C;
145
127
  /** The server's cause: its message, and its issues or failing operation when it gave them. */
146
- override readonly cause: Cause;
128
+ override readonly cause: AvCause;
147
129
 
148
- constructor(code: C, cause: Cause) {
130
+ constructor(code: C, cause: AvCause) {
149
131
  super(cause.message);
150
132
  this.code = code;
151
133
  this.cause = cause;
@@ -155,12 +137,12 @@ export class FrameworkError<C extends OperationErrorCode = OperationErrorCode> e
155
137
  const ERRORS_MODULE_TAIL = `
156
138
  /**
157
139
  * A failure that never produced a framework envelope: no response
158
- * arrived, or the one that did is not an Aventara response envelope — a proxy's
159
- * HTML error page, a truncated body, JSON of another shape. It carries no
140
+ * arrived, or the one that did is not an Aventara response envelope (a proxy's
141
+ * HTML error page, a truncated body, JSON of another shape). It carries no
160
142
  * cause: nothing in such a response is the framework's to report.
161
143
  */
162
- export class TransportError extends Error {
163
- override readonly name: string = "TransportError";
144
+ export class AvTransportError extends Error {
145
+ override readonly name: string = "AvTransportError";
164
146
  /** The HTTP status that arrived, or null when no response arrived at all. */
165
147
  readonly status: number | null;
166
148
 
@@ -174,7 +156,7 @@ export function emitErrorsModule() {
174
156
  const classes = Object.entries(FRAMEWORK_ERROR_CLASSES).map(([name, summary]) => {
175
157
  const codes = ERROR_CODES.filter((code) => FRAMEWORK_ERROR_CLASS_OF[code] === name);
176
158
  return (`\n/** ${summary} Thrown for ${codes.join(", ")}. */\n` +
177
- `export class ${name} extends FrameworkError<${codes.map((code) => JSON.stringify(code)).join(" | ")}> {\n` +
159
+ `export class ${name} extends AvError<${codes.map((code) => JSON.stringify(code)).join(" | ")}> {\n` +
178
160
  `\toverride readonly name: string = ${JSON.stringify(name)};\n}\n`);
179
161
  });
180
162
  const table = ERROR_CODES.map((code) => `\t${code}: ${FRAMEWORK_ERROR_CLASS_OF[code]},\n`).join("");
@@ -184,21 +166,21 @@ export function emitErrorsModule() {
184
166
  "\n/** The outcome codes of the protocol, in allocation order. */\n" +
185
167
  `export const OPERATION_CODES = ${codeArray(OPERATION_CODES)};\n` +
186
168
  "\n/** An outcome code. */\n" +
187
- "export type OperationCode = (typeof OPERATION_CODES)[number];\n" +
169
+ "export type AvOperationCode = (typeof OPERATION_CODES)[number];\n" +
188
170
  "\n/** An outcome code that throws: every code outside A1xxx. */\n" +
189
- `export type OperationErrorCode = Exclude<OperationCode, \`A1\${string}\`>;\n` +
171
+ `export type AvOperationErrorCode = Exclude<AvOperationCode, \`A1\${string}\`>;\n` +
190
172
  "\n/** The validation codes of the protocol, in allocation order. */\n" +
191
173
  `export const VALIDATION_CODES = ${codeArray(VALIDATION_CODES)};\n` +
192
174
  "\n/** A validation code: what one issue in a cause is about. */\n" +
193
- "export type ValidationCode = (typeof VALIDATION_CODES)[number];\n" +
175
+ "export type AvValidationCode = (typeof VALIDATION_CODES)[number];\n" +
194
176
  ERRORS_MODULE_TYPES +
195
177
  classes.join("") +
196
178
  ERRORS_MODULE_TAIL +
197
179
  "\nconst ERROR_CLASS_OF: {\n" +
198
- "\treadonly [C in OperationErrorCode]: new (code: C, cause: Cause) => FrameworkError;\n" +
180
+ "\treadonly [C in AvOperationErrorCode]: new (code: C, cause: AvCause) => AvError;\n" +
199
181
  `} = {\n${table}};\n` +
200
- "\n/** The FrameworkError subclass a failure envelope's code throws. */\n" +
201
- "export function frameworkErrorOf<C extends OperationErrorCode>(code: C, cause: Cause): FrameworkError {\n" +
182
+ "\n/** The AvError subclass a failure envelope's code throws. */\n" +
183
+ "export function frameworkErrorOf<C extends AvOperationErrorCode>(code: C, cause: AvCause): AvError {\n" +
202
184
  "\tconst ErrorClass = ERROR_CLASS_OF[code];\n" +
203
185
  "\treturn new ErrorClass(code, cause);\n" +
204
186
  "}\n",
@@ -227,26 +209,26 @@ const IDENTITY_HEADERS = (() => {
227
209
  })();
228
210
  function transportModuleSource(from) {
229
211
  return `import { CLIENT_CONTRACT_HASH, PROTOCOL_VERSION } from ${from("../metadata")};
230
- import { decodeResult, encodeArguments, serializeWireBody } from ${from("./codec")};
212
+ import { decodeResult, encodeArguments, readArgument, serializeWireBody } from ${from("./codec")};
231
213
  import { DECODE_TABLE } from ${from("./descriptor")};
232
214
  import {
233
- type Cause,
215
+ type AvCause,
234
216
  frameworkErrorOf,
235
217
  OPERATION_CODES,
236
- type OperationCode,
237
- type OperationErrorCode,
238
- TransportError,
218
+ type AvOperationCode,
219
+ type AvOperationErrorCode,
220
+ AvTransportError,
239
221
  VALIDATION_CODES,
240
- type ValidationCode,
241
- type ValidationIssue,
222
+ type AvValidationCode,
223
+ type AvValidationIssue,
242
224
  } from ${from("./errors")};
243
225
 
244
226
  /**
245
227
  * The transport of Aventara protocol version 1: one POST per operation,
246
228
  * sent with the protocol version and ClientContract hash, its response
247
- * read by the protocol's rule — A1xxx resolves with the envelope's data, every other
248
- * code throws its FrameworkError subclass, and anything that is not a framework
249
- * envelope throws TransportError. No request is ever retried.
229
+ * read by the protocol's rule: A1xxx resolves with the envelope's data, every other
230
+ * code throws its AvError subclass, and anything that is not a framework
231
+ * envelope throws AvTransportError. No request is ever retried.
250
232
  *
251
233
  * # A stale client
252
234
  *
@@ -254,9 +236,9 @@ import {
254
236
  * regenerate. The server checks the identity this transport sends before it
255
237
  * routes, so a request under a stale hash, to an operation the deployment still
256
238
  * advertises or to one it no longer does, is answered 409 A2005, which throws
257
- * ContractMismatchError naming the remedy. A response that is not a framework
239
+ * AvContractMismatchError naming the remedy. A response that is not a framework
258
240
  * envelope, such as a host's own 404 for a path it does not mount, is a
259
- * TransportError and names no remedy, because nothing in it says this client is
241
+ * AvTransportError and names no remedy, because nothing in it says this client is
260
242
  * stale.
261
243
  */
262
244
 
@@ -270,7 +252,7 @@ const FRAMEWORK_HEADERS: Readonly<Record<string, string>> = {
270
252
  [WIRE_PREFIX + ${JSON.stringify(IDENTITY_HEADERS.contractHash)}]: CLIENT_CONTRACT_HASH,
271
253
  };
272
254
 
273
- /** The request id header: CallOptions.requestId sends it. */
255
+ /** The request id header: AvCallOptions.requestId sends it. */
274
256
  const REQUEST_ID_HEADER = WIRE_PREFIX + ${JSON.stringify(IDENTITY_HEADERS.requestId)};
275
257
 
276
258
  /** What the transport reads of an AbortSignal, where the consumer's lib declares none. */
@@ -292,16 +274,16 @@ export interface FetchResponse {
292
274
  text(): Promise<string>;
293
275
  }
294
276
 
295
- /** The Fetch API as far as the transport calls it, where the consumer's lib declares none. */
277
+ /** The AvFetch API as far as the transport calls it, where the consumer's lib declares none. */
296
278
  export type StructuralFetch = (url: string, init: FetchInit) => Promise<FetchResponse>;
297
279
 
298
280
  /**
299
281
  * The fetch this client calls: the consumer's own platform fetch type when
300
- * their lib declares one — DOM's, Node's — so the platform fetch is accepted
282
+ * their lib declares one (DOM's, Node's), so the platform fetch is accepted
301
283
  * without a cast, and the structural fetch above where it declares none. Read
302
284
  * through typeof globalThis, so this module names neither lib.
303
285
  */
304
- export type Fetch = typeof globalThis extends { readonly fetch: infer F } ? F : StructuralFetch;
286
+ export type AvFetch = typeof globalThis extends { readonly fetch: infer F } ? F : StructuralFetch;
305
287
 
306
288
  /** The consumer's AbortSignal type when their lib declares one, else the structural signal. */
307
289
  export type PlatformSignal = typeof globalThis extends {
@@ -314,11 +296,11 @@ export type PlatformSignal = typeof globalThis extends {
314
296
  export interface TransportConnection {
315
297
  /** The framework entrypoint, an absolute URL without a trailing slash. */
316
298
  readonly entrypoint: string;
317
- readonly fetch: Fetch;
299
+ readonly fetch: AvFetch;
318
300
  }
319
301
 
320
302
  /** Per-call options. Never serialized into the operation body. */
321
- export interface CallOptions {
303
+ export interface AvCallOptions {
322
304
  /** Aborts the request; the call rejects with what fetch rejected with. */
323
305
  readonly signal?: PlatformSignal;
324
306
  /** Extra request headers. The framework's own headers cannot be set here. */
@@ -330,18 +312,18 @@ export interface CallOptions {
330
312
  readonly requestId?: string;
331
313
  }
332
314
 
333
- type SuccessCode = Exclude<OperationCode, OperationErrorCode>;
315
+ type SuccessCode = Exclude<AvOperationCode, AvOperationErrorCode>;
334
316
 
335
317
  /** A framework envelope, read and checked, told apart by \`outcome\`. */
336
318
  type Envelope =
337
319
  | { readonly outcome: "data"; readonly code: SuccessCode; readonly data: unknown }
338
- | { readonly outcome: "failure"; readonly code: OperationErrorCode; readonly cause: Cause };
320
+ | { readonly outcome: "failure"; readonly code: AvOperationErrorCode; readonly cause: AvCause };
339
321
 
340
322
  /**
341
323
  * Runs one operation: POSTs its arguments and settles its envelope.
342
324
  *
343
- * @returns the envelope's data when the code is A1xxx — null for A1001.
344
- * @throws the FrameworkError subclass of any other code; TransportError when no
325
+ * @returns the envelope's data when the code is A1xxx; null for A1001.
326
+ * @throws the AvError subclass of any other code; AvTransportError when no
345
327
  * framework envelope arrived; TypeError, before anything is sent, when the
346
328
  * arguments are not a JSON-safe object or a request header is the framework's.
347
329
  * A caller's abort rejects with what fetch rejected with.
@@ -352,7 +334,7 @@ export async function execute(
352
334
  family: string,
353
335
  variant: string,
354
336
  args: Readonly<Record<string, unknown>>,
355
- options: CallOptions = {},
337
+ options: AvCallOptions = {},
356
338
  ): Promise<unknown> {
357
339
  const operation = JSON.stringify(resource) + " " + family + "." + variant;
358
340
  const headers = requestHeaders(options);
@@ -368,13 +350,13 @@ export async function execute(
368
350
  * by its own operation's Resource (\`resources\`, in plan order).
369
351
  *
370
352
  * @throws as {@link execute} does; a rolled-back plan throws its failing
371
- * operation's FrameworkError subclass, its cause naming the operation.
353
+ * operation's AvError subclass, its cause naming the operation.
372
354
  */
373
355
  export async function executePlan(
374
356
  connection: TransportConnection,
375
357
  plan: { readonly operations: readonly unknown[] },
376
358
  resources: readonly string[],
377
- options: CallOptions = {},
359
+ options: AvCallOptions = {},
378
360
  ): Promise<unknown[]> {
379
361
  const operation = "the transaction";
380
362
  const headers = requestHeaders(options);
@@ -395,7 +377,7 @@ async function post(
395
377
  operation: string,
396
378
  headers: Readonly<Record<string, string>>,
397
379
  body: string,
398
- options: CallOptions,
380
+ options: AvCallOptions,
399
381
  ): Promise<{ readonly status: number; readonly data: unknown }> {
400
382
  const url = connection.entrypoint + path;
401
383
  const init: FetchInit =
@@ -412,7 +394,7 @@ async function post(
412
394
  if (options.signal?.aborted === true) {
413
395
  throw error;
414
396
  }
415
- throw new TransportError("The request for " + operation + " failed before any response arrived.", null);
397
+ throw new AvTransportError("The request for " + operation + " failed before any response arrived.", null);
416
398
  }
417
399
  let text: string;
418
400
  try {
@@ -421,7 +403,7 @@ async function post(
421
403
  if (options.signal?.aborted === true) {
422
404
  throw error;
423
405
  }
424
- throw new TransportError(
406
+ throw new AvTransportError(
425
407
  "The response to " + operation + " (HTTP " + response.status + ") could not be read.",
426
408
  response.status,
427
409
  );
@@ -430,14 +412,14 @@ async function post(
430
412
  }
431
413
 
432
414
  /**
433
- * The decoded data, or — a scalar in \`data\` that does not decode, inside an
434
- * otherwise valid envelope — a TransportError.
415
+ * The decoded data, or an AvTransportError when a scalar in \`data\` does not
416
+ * decode inside an otherwise valid envelope.
435
417
  */
436
418
  function decoded<T>(operation: string, status: number, decode: () => T): T {
437
419
  try {
438
420
  return decode();
439
421
  } catch (error) {
440
- throw new TransportError(
422
+ throw new AvTransportError(
441
423
  "The response to " + operation + " (HTTP " + status + ") holds a value this client cannot read: " +
442
424
  (error instanceof Error ? error.message : String(error)),
443
425
  status,
@@ -448,14 +430,14 @@ function decoded<T>(operation: string, status: number, decode: () => T): T {
448
430
  /**
449
431
  * Settles one response by the protocol's rule: the envelope's data, still in wire form,
450
432
  * which the caller decodes by the decode table.
451
- * A scalar in \`data\` that fails to decode, inside an otherwise valid envelope, throws TransportError
452
- * — not a FrameworkError, because the server reported no failure; the response is
453
- * one this client cannot read, which is what TransportError means.
433
+ * A scalar in \`data\` that fails to decode, inside an otherwise valid envelope, throws AvTransportError.
434
+ * It is no AvError, because the server reported no failure: the response is one
435
+ * this client cannot read, which is what AvTransportError means.
454
436
  */
455
437
  function settle(operation: string, status: number, text: string): unknown {
456
438
  const envelope = envelopeOf(parseJson(text));
457
439
  if (envelope === undefined) {
458
- throw new TransportError(
440
+ throw new AvTransportError(
459
441
  "The response to " + operation + " (HTTP " + status + ") is not an Aventara response envelope.",
460
442
  status,
461
443
  );
@@ -466,7 +448,7 @@ function settle(operation: string, status: number, text: string): unknown {
466
448
  return envelope.data;
467
449
  }
468
450
 
469
- /** The parsed body, or undefined — which no envelope is — when it is not JSON. */
451
+ /** The parsed body, or undefined (which no envelope is) when it is not JSON. */
470
452
  function parseJson(text: string): unknown {
471
453
  try {
472
454
  return JSON.parse(text);
@@ -479,15 +461,15 @@ function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
479
461
  return typeof value === "object" && value !== null && !Array.isArray(value);
480
462
  }
481
463
 
482
- function isOperationCode(value: unknown): value is OperationCode {
464
+ function isOperationCode(value: unknown): value is AvOperationCode {
483
465
  return OPERATION_CODES.some((code) => code === value);
484
466
  }
485
467
 
486
- function isValidationCode(value: unknown): value is ValidationCode {
468
+ function isValidationCode(value: unknown): value is AvValidationCode {
487
469
  return VALIDATION_CODES.some((code) => code === value);
488
470
  }
489
471
 
490
- function isSuccessCode(code: OperationCode): code is SuccessCode {
472
+ function isSuccessCode(code: AvOperationCode): code is SuccessCode {
491
473
  return code.startsWith("A1");
492
474
  }
493
475
 
@@ -511,9 +493,9 @@ function envelopeOf(body: unknown): Envelope | undefined {
511
493
  /**
512
494
  * The cause rebuilt from the members the protocol defines, or undefined when one of them
513
495
  * is malformed. Every other member is dropped, so whatever else a server put in
514
- * its cause — a stack, SQL, a file path — is never carried into a thrown error.
496
+ * its cause (a stack, SQL, a file path) is never carried into a thrown error.
515
497
  */
516
- function causeOf(value: unknown): Cause | undefined {
498
+ function causeOf(value: unknown): AvCause | undefined {
517
499
  if (!isRecord(value)) {
518
500
  return undefined;
519
501
  }
@@ -523,12 +505,12 @@ function causeOf(value: unknown): Cause | undefined {
523
505
  if (typeof message !== "string") {
524
506
  return undefined;
525
507
  }
526
- let cause: Cause = { message };
508
+ let cause: AvCause = { message };
527
509
  if (issues !== undefined) {
528
510
  if (!Array.isArray(issues)) {
529
511
  return undefined;
530
512
  }
531
- const rebuilt: ValidationIssue[] = [];
513
+ const rebuilt: AvValidationIssue[] = [];
532
514
  for (const issue of issues) {
533
515
  const copy = issueOf(issue);
534
516
  if (copy === undefined) {
@@ -547,18 +529,30 @@ function causeOf(value: unknown): Cause | undefined {
547
529
  return Object.freeze(cause);
548
530
  }
549
531
 
550
- function issueOf(value: unknown): ValidationIssue | undefined {
532
+ /**
533
+ * One issue rebuilt from the members the protocol defines (its path, code and
534
+ * message, and a failed check's name and parameters), or undefined when one of
535
+ * them is malformed.
536
+ */
537
+ function issueOf(value: unknown): AvValidationIssue | undefined {
551
538
  if (!isRecord(value)) {
552
539
  return undefined;
553
540
  }
554
541
  const path = value["path"];
555
542
  const code = value["code"];
556
543
  const message = value["message"];
544
+ const check = value["check"];
545
+ const params = value["params"];
557
546
  if (!isValidationCode(code) || typeof message !== "string") {
558
547
  return undefined;
559
548
  }
549
+ const checked = check === undefined && params === undefined;
550
+ if (!checked && (typeof check !== "string" || !isRecord(params))) {
551
+ return undefined;
552
+ }
553
+ const failed = checked ? {} : { check: check as string, params: frozen(params) as Readonly<Record<string, unknown>> };
560
554
  if (path === undefined) {
561
- return Object.freeze({ code, message });
555
+ return Object.freeze({ code, message, ...failed });
562
556
  }
563
557
  if (!Array.isArray(path)) {
564
558
  return undefined;
@@ -570,12 +564,23 @@ function issueOf(value: unknown): ValidationIssue | undefined {
570
564
  }
571
565
  segments.push(segment);
572
566
  }
573
- return Object.freeze({ path: Object.freeze(segments), code, message });
567
+ return Object.freeze({ path: Object.freeze(segments), code, message, ...failed });
568
+ }
569
+
570
+ /** A parsed JSON value, frozen at every depth. */
571
+ function frozen(value: unknown): unknown {
572
+ if (typeof value === "object" && value !== null) {
573
+ for (const member of Object.values(value)) {
574
+ frozen(member);
575
+ }
576
+ Object.freeze(value);
577
+ }
578
+ return value;
574
579
  }
575
580
 
576
581
  /** The arguments, refused unless they are an object: the body is always one. */
577
582
  function argumentObject(args: unknown): unknown {
578
- if (!isRecord(args)) {
583
+ if (!readArgument([], () => isRecord(args))) {
579
584
  throw new TypeError("An operation's arguments must be an object; send {} when there are none.");
580
585
  }
581
586
  return args;
@@ -586,7 +591,7 @@ function argumentObject(args: unknown): unknown {
586
591
  * the framework's; then requestId, which overwrites any request id header the
587
592
  * caller set.
588
593
  */
589
- function requestHeaders(options: CallOptions): Record<string, string> {
594
+ function requestHeaders(options: AvCallOptions): Record<string, string> {
590
595
  const headers: Record<string, string> = { ...FRAMEWORK_HEADERS };
591
596
  const owned = new Set(Object.keys(FRAMEWORK_HEADERS).map((name) => name.toLowerCase()));
592
597
  for (const [name, value] of Object.entries(options.headers ?? {})) {
@@ -3,7 +3,7 @@ import type { EmittedModule } from "./emitted-tree.interface.js";
3
3
  import { type ClientModuleStyle } from "./module-style.interface.js";
4
4
  export declare function wireGrammarLiteral(scalar: keyof typeof AvProtocol.scalarFormats): string;
5
5
  /**
6
- * `runtime/codec.ts`: the `BuiltInScalar` union and the codec table in core's
6
+ * `runtime/codec.ts`: the `AvBuiltInScalar` union and the codec table in core's
7
7
  * `BUILT_IN_SCALARS` order, the helpers the codecs share, and the three
8
8
  * functions the generated transport calls. Nothing here is re-exported from the
9
9
  * tree's `AvClient.ts`: the codec is the runtime's, not the consumer's.