@aventara/client 0.1.0-pilot.1 → 0.1.0-pilot.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +41 -7
  2. package/dist/avclient.bin.js +0 -10
  3. package/dist/cli/command.parser.d.ts +15 -10
  4. package/dist/cli/command.parser.js +13 -19
  5. package/dist/cli/generate.command.js +0 -6
  6. package/dist/cli/generation-failure.renderer.js +0 -14
  7. package/dist/cli/generation-success.renderer.d.ts +4 -1
  8. package/dist/cli/generation-success.renderer.js +0 -13
  9. package/dist/cli/terminal.prompter.d.ts +1 -2
  10. package/dist/cli/warning.renderer.d.ts +2 -2
  11. package/dist/cli/warning.renderer.js +0 -8
  12. package/dist/cli.d.ts +8 -16
  13. package/dist/cli.js +5 -25
  14. package/dist/config/client-config.interface.d.ts +18 -16
  15. package/dist/config/client-config.interface.js +0 -13
  16. package/dist/config/config.loader.d.ts +34 -22
  17. package/dist/config/config.loader.js +49 -52
  18. package/dist/config/config.resolver.d.ts +13 -19
  19. package/dist/config/config.resolver.js +9 -48
  20. package/dist/config/env.cascade.d.ts +12 -14
  21. package/dist/config/env.cascade.js +0 -19
  22. package/dist/config/module-style.resolver.d.ts +52 -0
  23. package/dist/config/module-style.resolver.js +75 -0
  24. package/dist/config/tsconfig.locator.d.ts +45 -0
  25. package/dist/config/tsconfig.locator.js +52 -0
  26. package/dist/contract/contract.acceptance.d.ts +12 -26
  27. package/dist/contract/contract.acceptance.js +0 -54
  28. package/dist/contract/contract.fetcher.d.ts +12 -17
  29. package/dist/contract/contract.fetcher.js +0 -24
  30. package/dist/contract/contract.loader.d.ts +4 -5
  31. package/dist/contract/contract.loader.js +0 -10
  32. package/dist/emit/banner.emitter.d.ts +11 -12
  33. package/dist/emit/banner.emitter.js +0 -26
  34. package/dist/emit/client-surface.emitter.d.ts +17 -21
  35. package/dist/emit/client-surface.emitter.js +29 -55
  36. package/dist/emit/client-tree.emitter.d.ts +11 -20
  37. package/dist/emit/client-tree.emitter.js +12 -54
  38. package/dist/emit/contract-carrier.emitter.d.ts +5 -6
  39. package/dist/emit/contract-carrier.emitter.js +0 -28
  40. package/dist/emit/derivation.emitter.d.ts +7 -7
  41. package/dist/emit/derivation.emitter.js +2 -161
  42. package/dist/emit/descriptor.emitter.js +2 -28
  43. package/dist/emit/emitted-tree.interface.d.ts +40 -17
  44. package/dist/emit/emitted-tree.interface.js +6 -16
  45. package/dist/emit/enum.emitter.d.ts +4 -4
  46. package/dist/emit/enum.emitter.js +0 -24
  47. package/dist/emit/module-specifier.scanner.d.ts +25 -0
  48. package/dist/emit/module-specifier.scanner.js +160 -0
  49. package/dist/emit/module-style.interface.d.ts +58 -0
  50. package/dist/emit/module-style.interface.js +8 -0
  51. package/dist/emit/name.deriver.d.ts +33 -61
  52. package/dist/emit/name.deriver.js +0 -134
  53. package/dist/emit/named-type.emitter.d.ts +14 -21
  54. package/dist/emit/named-type.emitter.js +3 -30
  55. package/dist/emit/runtime.emitter.d.ts +23 -50
  56. package/dist/emit/runtime.emitter.js +68 -159
  57. package/dist/emit/scalar.codec.d.ts +20 -33
  58. package/dist/emit/scalar.codec.js +13 -69
  59. package/dist/emit/transaction.emitter.d.ts +6 -14
  60. package/dist/emit/transaction.emitter.js +24 -33
  61. package/dist/generate.d.ts +17 -34
  62. package/dist/generate.js +14 -22
  63. package/dist/index.js +0 -5
  64. package/dist/init/client-config.template.d.ts +6 -4
  65. package/dist/init/client-config.template.js +10 -13
  66. package/dist/init/client-init.errors.js +0 -3
  67. package/dist/init/client-init.orchestrator.js +1 -9
  68. package/dist/init/client-init.planner.d.ts +1 -9
  69. package/dist/init/client-init.planner.js +6 -24
  70. package/dist/init/client-init.questions.d.ts +8 -12
  71. package/dist/init/client-init.questions.js +0 -11
  72. package/dist/init/client-project.inspector.d.ts +6 -0
  73. package/dist/init/client-project.inspector.js +2 -2
  74. package/dist/node-version.guard.js +0 -12
  75. package/dist/output/output.validator.d.ts +22 -22
  76. package/dist/output/output.validator.js +46 -59
  77. package/dist/output/output.writer.d.ts +56 -52
  78. package/dist/output/output.writer.js +71 -133
  79. package/package.json +6 -4
@@ -1,58 +1,6 @@
1
1
  import { BUILT_IN_SCALARS } from "@aventara/core";
2
2
  import { AvProtocol } from "@aventara/core/protocol";
3
- /**
4
- * The scalar codecs of §6.2, as the generated client carries them in
5
- * `runtime/codec.ts`.
6
- *
7
- * # Keyed on `BuiltInScalar` alone
8
- *
9
- * §6.2's table is fixed by PROTOCOL VERSION, not carried in the Contract: the
10
- * scalar registry is a key registry (`{ builtin: true }`) and scalars carry no
11
- * position qualifier [M10]. So the codec reads no field, no capability and no
12
- * operation descriptor — the caller names the scalar, and the codec converts one
13
- * value. Walking a Resource's fields to find which scalar a value is belongs to
14
- * the method surface, below Phase 12-partial's boundary.
15
- *
16
- * One source for the scalar set: the emitted `BuiltInScalar` union and the
17
- * emitted table both come from core's `BUILT_IN_SCALARS`, in its order, and
18
- * {@link SCALAR_CODEC_SOURCES} is a mapped type over core's `BuiltInScalar` — a
19
- * scalar the protocol adds is a compile error here until its codec is written.
20
- *
21
- * # Untyped on purpose
22
- *
23
- * Every codec is `unknown → unknown`, checked at runtime. The TYPE of a scalar's
24
- * application form and wire form is Phase 9's `ScalarValueType` and its `Forms`
25
- * maps (architect decision Q3 = 3a); declaring a second scalar→type map here
26
- * would be a parallel source of that fact. When the boundary lifts, the methods
27
- * that call these codecs carry the types.
28
- *
29
- * # What the emitted codec promises
30
- *
31
- * - `encodeScalar`/`decodeScalar` convert between the generated runtime value
32
- * and the JSON wire value of §6.2, and `decodeScalar(s, encodeScalar(s, v))`
33
- * equals `v` for every scalar.
34
- * - `null` passes through both: it is explicit, and whether a field admits it is
35
- * the field's nullability, which the server validates (§6.2).
36
- * - `undefined` is never a value: an optional property is omitted, and
37
- * `serializeWireBody` strips every `undefined` property before transport.
38
- * - A value outside a scalar's runtime or wire form is refused with a
39
- * `TypeError` naming the scalar — never coerced. A `json` value carrying a
40
- * `BigInt`, `Date`, `Uint8Array`, `Decimal`, function, symbol or `undefined`
41
- * is refused (§6.2), with the path to the offending member.
42
- * - Wire grammars are the server's, read from core — `AvProtocol.scalarFormats`
43
- * (C2, R7) — and emitted by value as regular-expression literals
44
- * ({@link wireGrammarLiteral}): bigint `-?(0|[1-9]\d*)`, datetime exactly
45
- * `toISOString()`'s form, bytes RFC 4648 base64 with the standard alphabet and
46
- * padding, canonical (zero pad bits, like the server's — C1). Base64 is written
47
- * out rather than delegated to `atob`/`btoa`, so the module needs neither a DOM
48
- * nor a Node global (§15.7, U5).
49
- */
50
- /**
51
- * The regular-expression literal of one of core's wire grammars, as emitted
52
- * source: `/<source>/`, the grammar's own text. The sources are anchored,
53
- * flagless and carry a `/` only inside a character class, which core's
54
- * `av-protocol.spec.ts` pins, so the literal is the grammar itself.
55
- */
3
+ import { moduleSpecifierWriter, } from "./module-style.interface.js";
56
4
  export function wireGrammarLiteral(scalar) {
57
5
  return `/${AvProtocol.scalarFormats[scalar]}/`;
58
6
  }
@@ -99,11 +47,12 @@ const SCALAR_CODEC_SOURCES = {
99
47
  decode: `(wire) => copyJson("wire", wire, [], new Set())`,
100
48
  },
101
49
  };
102
- const CODEC_MODULE_HEAD = `import { Decimal } from "./decimal.js";
103
- import type { FieldDecoding } from "./descriptor.js";
50
+ function codecModuleHead(from) {
51
+ return `import { Decimal } from ${from("./decimal")};
52
+ import type { FieldDecoding } from ${from("./descriptor")};
104
53
 
105
54
  /**
106
- * The scalar codecs of Aventara protocol version 1 (§6.2): one per built-in
55
+ * The scalar codecs of Aventara protocol version 1: one per built-in
107
56
  * scalar, converting between the generated runtime value and the JSON wire value.
108
57
  * A value is never wrapped per field — the Contract names the scalar, so the
109
58
  * caller says which scalar a value is.
@@ -114,6 +63,7 @@ import type { FieldDecoding } from "./descriptor.js";
114
63
  * wire form is refused with a TypeError, never coerced.
115
64
  */
116
65
  `;
66
+ }
117
67
  const CODEC_MODULE_BODY = `
118
68
  interface ScalarCodec {
119
69
  /** The generated runtime value to its JSON wire value. */
@@ -360,7 +310,7 @@ export function decodeScalar(scalar: BuiltInScalar, wire: unknown): unknown {
360
310
  /**
361
311
  * The JSON text of a request body whose scalars are already encoded. Every
362
312
  * undefined property is stripped, at every depth — an optional property is
363
- * omitted, never sent — and null is kept as the explicit value it is (§6.2).
313
+ * omitted, never sent — and null is kept as the explicit value it is.
364
314
  * Anything else that is not JSON-safe is refused rather than coerced: it means a
365
315
  * value escaped its scalar codec.
366
316
  */
@@ -370,7 +320,7 @@ export function serializeWireBody(body: unknown): string {
370
320
 
371
321
  /**
372
322
  * An operation's arguments with every scalar in its wire form, read off each
373
- * value's runtime class (Phase 12-rest Q15 = a, the server's encode mirrored): a
323
+ * value's runtime class: a
374
324
  * bigint, a Date, a Decimal or a Uint8Array is encoded wherever it stands — a
375
325
  * filter operand, a data value, a cursor — and plain objects and arrays are
376
326
  * walked. Every other value is left for serializeWireBody, which refuses what is
@@ -417,7 +367,7 @@ export type DecodeTable = {
417
367
 
418
368
  /**
419
369
  * An operation's result with every scalar in its runtime form, read off the decode
420
- * table (P1): a number (a count), null, and the data of a Resource the table
370
+ * table: a number (a count), null, and the data of a Resource the table
421
371
  * does not name pass through; a list is a list of
422
372
  * records, anything else is one record of \`resource\`. In a record, a field the
423
373
  * table names is revived — each item of a list field — a relation recurses with
@@ -426,7 +376,7 @@ export type DecodeTable = {
426
376
  * \`{ count }\` — the three shapes the derivation gives it.
427
377
  *
428
378
  * @throws TypeError when a value is not the wire form its field's scalar takes —
429
- * which the transport answers as a TransportError (architect, 2026-10-04).
379
+ * which the transport answers as a TransportError.
430
380
  */
431
381
  export function decodeResult(table: DecodeTable, resource: string, value: unknown): unknown {
432
382
  // A Resource the table does not name has nothing to revive: its data passes.
@@ -474,13 +424,7 @@ function decodeField(table: DecodeTable, decoding: FieldDecoding, value: unknown
474
424
  return value;
475
425
  }
476
426
  `;
477
- /**
478
- * `runtime/codec.ts`: the `BuiltInScalar` union and the codec table in core's
479
- * `BUILT_IN_SCALARS` order, the helpers the codecs share, and the three
480
- * functions the generated transport calls. Nothing here is re-exported from the
481
- * tree's `AvClient.ts`: the codec is the runtime's, not the consumer's.
482
- */
483
- export function emitScalarCodecModule() {
427
+ export function emitScalarCodecModule(style) {
484
428
  const union = BUILT_IN_SCALARS.map((scalar) => `\t| ${JSON.stringify(scalar)}`).join("\n");
485
429
  const table = BUILT_IN_SCALARS.map((scalar) => {
486
430
  const { encode, decode } = SCALAR_CODEC_SOURCES[scalar];
@@ -488,8 +432,8 @@ export function emitScalarCodecModule() {
488
432
  }).join("\n");
489
433
  return {
490
434
  path: "runtime/codec.ts",
491
- source: CODEC_MODULE_HEAD +
492
- "\n/** The built-in scalars of the protocol (§6.1). */\n" +
435
+ source: codecModuleHead(moduleSpecifierWriter(style)) +
436
+ "\n/** The built-in scalars of the protocol. */\n" +
493
437
  `export type BuiltInScalar =\n${union};\n` +
494
438
  CODEC_MODULE_BODY +
495
439
  "\nconst SCALAR_CODECS: { readonly [S in BuiltInScalar]: ScalarCodec } = {\n" +
@@ -1,17 +1,9 @@
1
1
  import type { EmittedModule } from "./emitted-tree.interface.js";
2
+ import { type ClientModuleStyle } from "./module-style.interface.js";
2
3
  /**
3
- * The transaction builder and runner (§14; Phase 12-rest S6, Q13, Q14, P3):
4
- * `runtime/fingerprint.ts` and `runtime/transaction.ts`, emitted iff the
5
- * ClientContract advertises `interactive` transactions (P3) — `client.ts` wires
6
- * them to `avClient.tx` and `avClient.transaction`.
7
- *
8
- * Both are fixed by protocol version, not by the Contract, and mirror core's own
9
- * seams: the plan is assembled as `transactions/transaction-plan-assembler.ts`
10
- * assembles it — handles as pure data, a `$ref` bound lazily to its source
11
- * handle's position in the list it runs with, the two refusals core answers in
12
- * process (Q13) — and each node's fingerprint is core's
13
- * `computeOperationFingerprint` over the node's WIRE arguments (§14.4), computed
14
- * here by an emitted JCS and SHA-256 (Q14: `crypto.subtle` is absent from a
15
- * browser page served over plain http from a non-localhost host).
4
+ * The transaction builder and runner: `runtime/fingerprint.ts` and
5
+ * `runtime/transaction.ts`, emitted iff the ClientContract advertises
6
+ * `interactive` transactions — `client.ts` wires them to `avClient.tx` and
7
+ * `avClient.transaction`.
16
8
  */
17
- export declare function emitTransactionModules(): readonly EmittedModule[];
9
+ export declare function emitTransactionModules(style: ClientModuleStyle): readonly EmittedModule[];
@@ -1,29 +1,18 @@
1
- /**
2
- * The transaction builder and runner (§14; Phase 12-rest S6, Q13, Q14, P3):
3
- * `runtime/fingerprint.ts` and `runtime/transaction.ts`, emitted iff the
4
- * ClientContract advertises `interactive` transactions (P3) — `client.ts` wires
5
- * them to `avClient.tx` and `avClient.transaction`.
6
- *
7
- * Both are fixed by protocol version, not by the Contract, and mirror core's own
8
- * seams: the plan is assembled as `transactions/transaction-plan-assembler.ts`
9
- * assembles it — handles as pure data, a `$ref` bound lazily to its source
10
- * handle's position in the list it runs with, the two refusals core answers in
11
- * process (Q13) — and each node's fingerprint is core's
12
- * `computeOperationFingerprint` over the node's WIRE arguments (§14.4), computed
13
- * here by an emitted JCS and SHA-256 (Q14: `crypto.subtle` is absent from a
14
- * browser page served over plain http from a non-localhost host).
15
- */
16
- export function emitTransactionModules() {
1
+ import { moduleSpecifierWriter, } from "./module-style.interface.js";
2
+ export function emitTransactionModules(style) {
17
3
  return [
18
4
  { path: "runtime/fingerprint.ts", source: FINGERPRINT_MODULE },
19
- { path: "runtime/transaction.ts", source: TRANSACTION_MODULE },
5
+ {
6
+ path: "runtime/transaction.ts",
7
+ source: transactionModuleSource(moduleSpecifierWriter(style)),
8
+ },
20
9
  ];
21
10
  }
22
11
  const FINGERPRINT_MODULE = `/**
23
- * The v1 operation fingerprint (§14.4): RFC 8785 canonical JSON of
12
+ * The v1 operation fingerprint: RFC 8785 canonical JSON of
24
13
  * { resource, family, variant, args } over the WIRE-form arguments, its UTF-8
25
14
  * bytes hashed with SHA-256, the first 16 bytes in base64url, prefixed "fp1:".
26
- * Dependency-free (Q14), and pinned byte for byte to core's
15
+ * Dependency-free, and pinned byte for byte to core's
27
16
  * computeOperationFingerprint.
28
17
  */
29
18
 
@@ -220,18 +209,19 @@ function base64Url(bytes: Uint8Array): string {
220
209
  return text;
221
210
  }
222
211
  `;
223
- const TRANSACTION_MODULE = `import { encodeArguments, serializeWireBody } from "./codec.js";
224
- import { type Cause, type FrameworkError, frameworkErrorOf } from "./errors.js";
225
- import { fingerprintOf } from "./fingerprint.js";
226
- import { type CallOptions, executePlan, type TransportConnection } from "./transport.js";
212
+ function transactionModuleSource(from) {
213
+ return `import { encodeArguments, serializeWireBody } from ${from("./codec")};
214
+ import { type Cause, type FrameworkError, frameworkErrorOf } from ${from("./errors")};
215
+ import { fingerprintOf } from ${from("./fingerprint")};
216
+ import { type CallOptions, executePlan, type TransportConnection } from ${from("./transport")};
227
217
 
228
218
  /**
229
- * The deferred transaction builder and runner (§14), as core assembles a plan
219
+ * The deferred transaction builder and runner, as core assembles a plan
230
220
  * in process (transaction-plan-assembler.ts): \`avClient.tx\` defers an operation
231
221
  * into a handle — pure data, no request — and \`avClient.transaction\` binds a
232
- * list of handles into the one §14.2 plan, sends it once, and resolves one
222
+ * list of handles into one transaction plan, sends it once, and resolves one
233
223
  * result per handle in list order. A handle has no connection, so any client of
234
- * this generated tree may run it (Q13, a plan ruling).
224
+ * this generated tree may run it.
235
225
  */
236
226
 
237
227
  /** What a handle defers. */
@@ -250,7 +240,7 @@ interface Placeholder {
250
240
 
251
241
  type MutableRecord = Record<string, unknown>;
252
242
 
253
- /** One §14.2 wire node, its arguments in wire form. */
243
+ /** One wire node of a transaction plan, its arguments in wire form. */
254
244
  interface PlanNode {
255
245
  readonly resource: string;
256
246
  readonly family: string;
@@ -259,7 +249,7 @@ interface PlanNode {
259
249
  readonly fingerprint: string | null;
260
250
  }
261
251
 
262
- /** One DA-3 container: a mutation-data record and its path inside the arguments. */
252
+ /** One reference container: a mutation-data record and its path inside the arguments. */
263
253
  interface Container {
264
254
  readonly path: readonly (string | number)[];
265
255
  readonly record: Readonly<Record<string, unknown>>;
@@ -276,7 +266,7 @@ function isPlainRecord(value: unknown): value is Readonly<Record<string, unknown
276
266
  return prototype === Object.prototype || prototype === null;
277
267
  }
278
268
 
279
- /** The argument keys holding DA-3 reference positions: the top-level mutation data. */
269
+ /** The argument keys holding reference positions: the top-level mutation data. */
280
270
  function referenceKeys(family: string): readonly string[] {
281
271
  return family === "create" || family === "update" ? ["data"] : family === "upsert" ? ["create", "update"] : [];
282
272
  }
@@ -294,7 +284,7 @@ function containers(family: string, args: unknown): readonly Container[] {
294
284
  });
295
285
  }
296
286
 
297
- /** \`args\` with each DA-3 container replaced by \`rebuild\`, cloning only the containing path. */
287
+ /** \`args\` with each reference container replaced by \`rebuild\`, cloning only the containing path. */
298
288
  function replaceContainers(family: string, args: unknown, rebuild: (container: Container) => MutableRecord): unknown {
299
289
  if (!isPlainRecord(args)) {
300
290
  return args;
@@ -312,7 +302,7 @@ function replaceContainers(family: string, args: unknown, rebuild: (container: C
312
302
  }
313
303
 
314
304
  /**
315
- * Defers one operation into a handle (§14.1): nothing is sent. Its DA-3
305
+ * Defers one operation into a handle: nothing is sent. Its reference
316
306
  * containers are copied now, so a later change to the caller's data cannot move
317
307
  * a reference — and a handle can reference only handles built before it.
318
308
  */
@@ -345,8 +335,8 @@ function entryRefusal(operation: number, message: string): FrameworkError {
345
335
 
346
336
  /**
347
337
  * Binds \`steps\` into the plan, sends it once, and resolves one decoded result per
348
- * step (§14.1). Refused before anything is sent, with what core answers in
349
- * process (Q13): a list that is not one, an entry that is not a handle of this
338
+ * step. Refused before anything is sent, with what core answers in
339
+ * process: a list that is not one, an entry that is not a handle of this
350
340
  * generated client, one handle twice (ValidationError A2004 / V1001 at
351
341
  * ["operation"]); a reference to a handle not in the list (ValidationError A2007 /
352
342
  * V1010 at its \`$ref\`).
@@ -436,3 +426,4 @@ export async function runTransaction(
436
426
  );
437
427
  }
438
428
  `;
429
+ }
@@ -4,35 +4,19 @@ import type { ContractFetch } from "./contract/contract.fetcher.js";
4
4
  import { type EmittedFilePath } from "./emit/emitted-tree.interface.js";
5
5
  import type { OutputCheck, TypeScriptResolver } from "./output/output.validator.js";
6
6
  /**
7
- * §15.3's generation pipeline, as one call (S7b):
7
+ * The cascade is a returned record, never written into `process.env`: the config
8
+ * reaches it through `env("NAME")`.
8
9
  *
9
- * load env + config S2 — `.env` cascade, then `framework.client.ts`
10
- * → GET <entrypoint>/_contract S3 — conditional on the output's carrier (Q6):
11
- * `304` and identical bytes → "up to date", nothing written
12
- * → validate protocol support, ClientContract structure, advertised hash S3
13
- * → emit S4–S6 — enums, metadata, codecs, errors, transport
14
- * → emit into a temporary directory, validate, atomically replace S7
15
- *
16
- * Only what exists above Phase 12-partial's boundary is emitted: no Resource
17
- * method, no projection, no transaction builder — those read capabilities and
18
- * operation descriptors (plan §1).
19
- *
20
- * The cascade is resolved BEFORE the config is evaluated, as §15.2 orders it, so
21
- * an unreadable or malformed `.env` is reported even when the config would also
22
- * fail. The cascade is a returned record, never written into `process.env` (S2):
23
- * the config reaches it through `env("NAME")`.
24
- *
25
- * Output goes to `generateAt`: `AvClient.ts` and `generated/`, nothing else
26
- * there touched (architect, 2026-10-04). Content in those two the generator did
27
- * not produce is never overwritten unasked: the run returns
28
- * `ForeignOutputContent` instead, and the caller decides.
10
+ * Output goes to `generateAt`: `AvClient.ts` and `generated/`, nothing else there
11
+ * touched. Content in those two the generator did not produce is never overwritten
12
+ * unasked: the run returns `ForeignOutputContent` instead, and the caller decides.
29
13
  *
30
14
  * Every failure is thrown, every diagnostic that is not a failure is returned:
31
15
  * this function prints nothing and asks nothing. `cli.ts` owns the terminal. A
32
- * refusal raised after a warning (`OutputWriteError`) carries the warnings
33
- * raised before it, and so does `ForeignOutputContent`; a defect is rethrown as
34
- * itself, its warnings kept beside it (`warningsRaisedBeforeDefect`). No run's
35
- * warnings are lost because it stopped.
16
+ * refusal raised after a warning (`OutputWriteError`) carries the warnings raised
17
+ * before it, and so does `ForeignOutputContent`; a defect is rethrown as itself,
18
+ * its warnings kept beside it (`warningsRaisedBeforeDefect`). No run's warnings
19
+ * are lost because it stopped.
36
20
  */
37
21
  export interface ClientGenerationInput {
38
22
  /** The project directory: where `framework.client.ts` and the `.env` files are. */
@@ -48,7 +32,7 @@ export interface ClientGenerationInput {
48
32
  readonly overrideForeign?: boolean;
49
33
  /** Injected for tests; the platform `fetch` otherwise. */
50
34
  readonly fetch?: ContractFetch;
51
- /** The `typescript` optional peer; the installed one by default (Q6). */
35
+ /** The `typescript` optional peer; the installed one by default. */
52
36
  readonly resolveTypeScript?: TypeScriptResolver;
53
37
  }
54
38
  export interface ClientGenerated {
@@ -61,8 +45,8 @@ export interface ClientGenerated {
61
45
  readonly checked: OutputCheck;
62
46
  /**
63
47
  * The deployment answered `304` — the ClientContract is the one the previous
64
- * output was generated against — and the output was still replaced, because
65
- * this generator or this entrypoint emits other bytes (Q6).
48
+ * output was generated against — and the output was still replaced, because this
49
+ * generator or this entrypoint emits other bytes.
66
50
  */
67
51
  readonly contractUnchanged: boolean;
68
52
  /**
@@ -74,10 +58,9 @@ export interface ClientGenerated {
74
58
  }
75
59
  /**
76
60
  * The run found content in `AvClient.ts` or `generated/` the generator did not
77
- * produce, and stopped before writing anything (architect, 2026-10-04). Whoever
78
- * owns the terminal asks; `proceed` writes the emission already made — no second
79
- * fetch — overwriting or removing exactly `foreign`, through the same
80
- * temp → validate → replace path.
61
+ * produce, and stopped before writing anything. Whoever owns the terminal asks;
62
+ * `proceed` writes the emission already made — no second fetch — overwriting or
63
+ * removing exactly `foreign`, through the same temp → validate → replace path.
81
64
  */
82
65
  export interface ForeignOutputContent {
83
66
  readonly kind: "foreign-content";
@@ -98,8 +81,8 @@ export interface ForeignOutputContent {
98
81
  }
99
82
  /**
100
83
  * The deployment serves the ClientContract the output was generated against, and
101
- * this generator, with this entrypoint, emits exactly the bytes already there
102
- * (Phase 12-rest Q6): nothing was written.
84
+ * this generator, with this entrypoint, emits exactly the bytes already there:
85
+ * nothing was written.
103
86
  */
104
87
  export interface ClientUpToDate {
105
88
  readonly kind: "up-to-date";
package/dist/generate.js CHANGED
@@ -3,39 +3,23 @@ import path from "node:path";
3
3
  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
+ import { resolveModuleStyle } from "./config/module-style.resolver.js";
6
7
  import { loadClientContractSince } from "./contract/contract.loader.js";
7
8
  import { emitClientTree } from "./emit/client-tree.emitter.js";
8
9
  import { parseContractCarrier } from "./emit/contract-carrier.emitter.js";
9
- import { GENERATED_DIRECTORY, } from "./emit/emitted-tree.interface.js";
10
+ import { CLIENT_ENTRY_FILE, CONTRACT_CARRIER_MODULE, GENERATED_DIRECTORY, } from "./emit/emitted-tree.interface.js";
10
11
  import { findForeignOutputContent, OutputWriteError, ownedOutputMatches, precedeDefect, writeClientOutput, } from "./output/output.writer.js";
11
- /**
12
- * The ClientContract the output in `generateAt` holds, when its owned entries
13
- * are intact — nothing in them the generator did not produce — and its carrier
14
- * parses back and re-verifies (Q4, Q6); otherwise undefined, and the GET is
15
- * unconditional. A failure to inspect is no carrier: the run's own inspection,
16
- * later, reports it.
17
- */
18
12
  async function storedContract(generateAt) {
19
13
  try {
20
14
  if ((await findForeignOutputContent(generateAt)).length > 0) {
21
15
  return undefined;
22
16
  }
23
- return await parseContractCarrier(await readFile(path.join(generateAt, GENERATED_DIRECTORY, "contract.ts"), "utf8"));
17
+ return await parseContractCarrier(await readFile(path.join(generateAt, GENERATED_DIRECTORY, CONTRACT_CARRIER_MODULE), "utf8"));
24
18
  }
25
19
  catch {
26
20
  return undefined;
27
21
  }
28
22
  }
29
- /**
30
- * Generates the client the project's config describes — or, when content the
31
- * generator did not produce stands in its way and `overrideForeign` is not set,
32
- * says what it is and writes nothing.
33
- *
34
- * @throws a refusal (`EnvFileError`, `ClientConfigError`, `ContractTransportError`,
35
- * `ContractProtocolError`, `GeneratedNameError`, `OutputWriteError`) whose
36
- * message is the whole diagnosis; the previous output is intact. Anything else
37
- * is a defect.
38
- */
39
23
  export async function generateClient(input) {
40
24
  const cascade = resolveEnvCascade({
41
25
  directory: input.directory,
@@ -47,10 +31,20 @@ export async function generateClient(input) {
47
31
  cascade,
48
32
  configDirectory: path.dirname(loaded.file),
49
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 }),
40
+ });
50
41
  const { contract, notModified } = await loadClientContractSince(input.fetch === undefined
51
42
  ? { entrypoint: config.entrypoint }
52
43
  : { entrypoint: config.entrypoint, fetch: input.fetch }, await storedContract(config.generateAt));
53
- const emission = emitClientTree(contract, config.entrypoint);
44
+ const emission = emitClientTree(contract, config.entrypoint, {
45
+ importFileExtension: style.importFileExtension,
46
+ moduleFormat: style.moduleFormat,
47
+ });
54
48
  if (notModified &&
55
49
  (await ownedOutputMatches(config.generateAt, emission.tree))) {
56
50
  return {
@@ -78,8 +72,6 @@ export async function generateClient(input) {
78
72
  warnings: written.warnings,
79
73
  };
80
74
  };
81
- // A structural refusal or a defect here comes after the emission's renames
82
- // were raised; it carries them, as the writer's own do.
83
75
  const foreign = await findForeignOutputContent(config.generateAt).catch((error) => {
84
76
  throw error instanceof OutputWriteError
85
77
  ? error.precededBy(emission.warnings)
package/dist/index.js CHANGED
@@ -1,8 +1,3 @@
1
1
  import { readFileSync } from "node:fs";
2
- /**
3
- * This package's version, read from its own manifest — the `package.json` every
4
- * tarball carries beside `dist/` — so a release's `changeset version` is the one
5
- * statement of it.
6
- */
7
2
  export const AVENTARA_CLIENT_GENERATOR_VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
8
3
  export { defineClientConfig, env } from "./config/config.resolver.js";
@@ -1,8 +1,10 @@
1
1
  /**
2
- * R4, §15.2 — the config `avclient init` writes: developer-owned, read by
3
- * `avclient generate`. `file` names it in its own comment line: the
4
- * `framework.client.mts` it writes, or — to recognize what pilot.0 wrote —
5
- * `framework.client.ts`.
2
+ * `file` names it in its own comment line: the `framework.client.ts` it writes, or
3
+ * the project's own config of another extension, which init reuses rather than add
4
+ * a second (two configs make `avclient generate` refuse). A `.cjs` one is written
5
+ * in CommonJS — `require` and `module.exports` — the syntax its extension
6
+ * promises; every other one in ES module syntax, which the loader reads in any of
7
+ * them.
6
8
  */
7
9
  export declare function clientConfigSource(input: {
8
10
  readonly entrypoint: string;
@@ -1,22 +1,19 @@
1
1
  import { CLIENT_CONFIG_FILE } from "../config/config.loader.js";
2
- /** The package the written config imports its two functions from. */
3
2
  const CLIENT_PACKAGE = "@aventara/client";
4
- /**
5
- * R4, §15.2 — the config `avclient init` writes: developer-owned, read by
6
- * `avclient generate`. `file` names it in its own comment line: the
7
- * `framework.client.mts` it writes, or — to recognize what pilot.0 wrote —
8
- * `framework.client.ts`.
9
- */
10
3
  export function clientConfigSource(input, file = CLIENT_CONFIG_FILE) {
4
+ const names = input.envVar === undefined
5
+ ? "defineClientConfig"
6
+ : "defineClientConfig, env";
7
+ const commonJs = file.endsWith(".cjs");
11
8
  return [
12
- // The specifier is spliced in, not written after `from`, so this module's own
13
- // shipped JavaScript does not read as importing the package it names.
14
- input.envVar === undefined
15
- ? `import { defineClientConfig } from ${JSON.stringify(CLIENT_PACKAGE)};`
16
- : `import { defineClientConfig, env } from ${JSON.stringify(CLIENT_PACKAGE)};`,
9
+ commonJs
10
+ ? `const { ${names} } = require(${JSON.stringify(CLIENT_PACKAGE)});`
11
+ : `import { ${names} } from ${JSON.stringify(CLIENT_PACKAGE)};`,
17
12
  "",
18
13
  `// ${file}: where the Aventara server is, and where its typed client goes.`,
19
- "export default defineClientConfig({",
14
+ commonJs
15
+ ? "module.exports = defineClientConfig({"
16
+ : "export default defineClientConfig({",
20
17
  input.envVar === undefined
21
18
  ? `\tentrypoint: ${JSON.stringify(input.entrypoint)},`
22
19
  : `\tentrypoint: env(${JSON.stringify(input.envVar)}),`,
@@ -1,9 +1,6 @@
1
- /** `avclient init`'s refusals after the questions: each one sentence, exit 1. */
2
- /** Nobody confirmed replacing existing content. A refusal: nothing was touched. */
3
1
  export class ClientInitNotConfirmedError extends Error {
4
2
  name = "ClientInitNotConfirmedError";
5
3
  }
6
- /** A step after the write failed. Reported with what to run; the files are kept. */
7
4
  export class ClientInitStepError extends Error {
8
5
  name = "ClientInitStepError";
9
6
  }
@@ -8,13 +8,6 @@ import { GENERATE_SCRIPT, planClientInit } from "./client-init.planner.js";
8
8
  import { resolveClientInitAnswers } from "./client-init.questions.js";
9
9
  import { inspectClientProject } from "./client-project.inspector.js";
10
10
  import { runCommand } from "./command.runner.js";
11
- /**
12
- * R4 — `avclient init`, the whole frontend setup, in the order every wizard of
13
- * this item keeps: inspect, ask, plan, confirm, and only then write; then the
14
- * install (N6) and, unless skipped, the first generation through the very path
15
- * `avclient generate` takes. A refusal before the write leaves the project as
16
- * it was; a failure after it says what to run next, and keeps the files.
17
- */
18
11
  const MANIFEST = new URL("../../package.json", import.meta.url);
19
12
  function ownVersion() {
20
13
  return JSON.parse(readFileSync(MANIFEST, "utf8"))
@@ -35,7 +28,6 @@ export async function runClientInit(command, io) {
35
28
  });
36
29
  const clientVersion = ownVersion();
37
30
  const plan = planClientInit({ project, answers, clientVersion });
38
- // The generateAt rule (architect, 2026-10-04; R5).
39
31
  if (plan.conflicts.length > 0 && !command.yes) {
40
32
  const them = plan.conflicts.length === 1 ? "it" : "them";
41
33
  const has = plan.conflicts.length === 1 ? "has" : "have";
@@ -68,7 +60,7 @@ export async function runClientInit(command, io) {
68
60
  cwd: io.cwd,
69
61
  });
70
62
  if (installed.code !== 0) {
71
- throw new ClientInitStepError(`\`${install}\` exited with code ${installed.code} after ${CLIENT_CONFIG_FILE} was written; fix what it reports and run \`${install}\` again:\n${installed.stderr.trimEnd()}`);
63
+ 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()}`);
72
64
  }
73
65
  if (command.skipGenerate) {
74
66
  io.stdout(`Next: with the server running, ${answers.packageManager} run ${GENERATE_SCRIPT}\n`);
@@ -1,13 +1,5 @@
1
1
  import type { ClientInitAnswers } from "./client-init.questions.js";
2
2
  import type { ClientProject } from "./client-project.inspector.js";
3
- /**
4
- * §4.5 — what `avclient init` writes, decided before anything is: the config
5
- * file, the `.env` entry (only with a variable), the `avclient:generate` script
6
- * and the exact devDependency (N6) — each computed with existing content kept
7
- * and replaced, and every **conflict** (content it did not produce) named for
8
- * the generateAt rule.
9
- */
10
- /** R4: the script a frontend regenerates its client with. */
11
3
  export declare const GENERATE_SCRIPT = "avclient:generate";
12
4
  /** One change to a project file: its new content, or `undefined` to remove it. */
13
5
  export type ClientInitWrite = {
@@ -22,6 +14,6 @@ export type ClientInitPlan = {
22
14
  export declare function planClientInit(input: {
23
15
  readonly project: ClientProject;
24
16
  readonly answers: ClientInitAnswers;
25
- /** This package's own version: the devDependency is pinned to it exactly (N6). */
17
+ /** This package's own version: the devDependency is pinned to it exactly. */
26
18
  readonly clientVersion: string;
27
19
  }): ClientInitPlan;
@@ -1,13 +1,5 @@
1
- import { CLIENT_CONFIG_FILE, LEGACY_CLIENT_CONFIG_FILE, } from "../config/config.loader.js";
1
+ import { CLIENT_CONFIG_FILE } from "../config/config.loader.js";
2
2
  import { clientConfigSource } from "./client-config.template.js";
3
- /**
4
- * §4.5 — what `avclient init` writes, decided before anything is: the config
5
- * file, the `.env` entry (only with a variable), the `avclient:generate` script
6
- * and the exact devDependency (N6) — each computed with existing content kept
7
- * and replaced, and every **conflict** (content it did not produce) named for
8
- * the generateAt rule.
9
- */
10
- /** R4: the script a frontend regenerates its client with. */
11
3
  export const GENERATE_SCRIPT = "avclient:generate";
12
4
  const ENV_LINE = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/;
13
5
  function unquoted(value) {
@@ -29,23 +21,13 @@ export function planClientInit(input) {
29
21
  replacing.push({ path, content: replaced });
30
22
  }
31
23
  };
32
- const config = clientConfigSource(answers);
33
- const existingConfig = project.read(CLIENT_CONFIG_FILE);
34
- // pilot.1: a `framework.client.ts` is moved to `framework.client.mts` — two
35
- // configs would make `avclient generate` refuse. The one pilot.0 wrote for
36
- // these answers moves without asking; anything else is a conflict.
37
- const legacyConfig = project.read(LEGACY_CLIENT_CONFIG_FILE);
38
- if (legacyConfig !== undefined) {
39
- if (legacyConfig !== clientConfigSource(answers, LEGACY_CLIENT_CONFIG_FILE)) {
40
- conflicts.push(`${LEGACY_CLIENT_CONFIG_FILE} (replaced by ${CLIENT_CONFIG_FILE})`);
41
- }
42
- keeping.push({ path: LEGACY_CLIENT_CONFIG_FILE, content: undefined });
43
- replacing.push({ path: LEGACY_CLIENT_CONFIG_FILE, content: undefined });
44
- }
24
+ const configFile = project.configFile ?? CLIENT_CONFIG_FILE;
25
+ const config = clientConfigSource(answers, configFile);
26
+ const existingConfig = project.read(configFile);
45
27
  if (existingConfig !== undefined && existingConfig !== config) {
46
- conflicts.push(CLIENT_CONFIG_FILE);
28
+ conflicts.push(configFile);
47
29
  }
48
- add(CLIENT_CONFIG_FILE, existingConfig, existingConfig ?? config, config);
30
+ add(configFile, existingConfig, existingConfig ?? config, config);
49
31
  if (answers.envVar !== undefined) {
50
32
  const before = project.read(".env");
51
33
  const lines = before === undefined || before === ""