@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.
- package/README.md +41 -7
- package/dist/avclient.bin.js +0 -10
- package/dist/cli/command.parser.d.ts +15 -10
- package/dist/cli/command.parser.js +13 -19
- package/dist/cli/generate.command.js +0 -6
- package/dist/cli/generation-failure.renderer.js +0 -14
- package/dist/cli/generation-success.renderer.d.ts +4 -1
- package/dist/cli/generation-success.renderer.js +0 -13
- package/dist/cli/terminal.prompter.d.ts +1 -2
- package/dist/cli/warning.renderer.d.ts +2 -2
- package/dist/cli/warning.renderer.js +0 -8
- package/dist/cli.d.ts +8 -16
- package/dist/cli.js +5 -25
- package/dist/config/client-config.interface.d.ts +18 -16
- package/dist/config/client-config.interface.js +0 -13
- package/dist/config/config.loader.d.ts +34 -22
- package/dist/config/config.loader.js +49 -52
- package/dist/config/config.resolver.d.ts +13 -19
- package/dist/config/config.resolver.js +9 -48
- package/dist/config/env.cascade.d.ts +12 -14
- package/dist/config/env.cascade.js +0 -19
- package/dist/config/module-style.resolver.d.ts +52 -0
- package/dist/config/module-style.resolver.js +75 -0
- package/dist/config/tsconfig.locator.d.ts +45 -0
- package/dist/config/tsconfig.locator.js +52 -0
- package/dist/contract/contract.acceptance.d.ts +12 -26
- package/dist/contract/contract.acceptance.js +0 -54
- package/dist/contract/contract.fetcher.d.ts +12 -17
- package/dist/contract/contract.fetcher.js +0 -24
- package/dist/contract/contract.loader.d.ts +4 -5
- package/dist/contract/contract.loader.js +0 -10
- package/dist/emit/banner.emitter.d.ts +11 -12
- package/dist/emit/banner.emitter.js +0 -26
- package/dist/emit/client-surface.emitter.d.ts +17 -21
- package/dist/emit/client-surface.emitter.js +29 -55
- package/dist/emit/client-tree.emitter.d.ts +11 -20
- package/dist/emit/client-tree.emitter.js +12 -54
- package/dist/emit/contract-carrier.emitter.d.ts +5 -6
- package/dist/emit/contract-carrier.emitter.js +0 -28
- package/dist/emit/derivation.emitter.d.ts +7 -7
- package/dist/emit/derivation.emitter.js +2 -161
- package/dist/emit/descriptor.emitter.js +2 -28
- package/dist/emit/emitted-tree.interface.d.ts +40 -17
- package/dist/emit/emitted-tree.interface.js +6 -16
- package/dist/emit/enum.emitter.d.ts +4 -4
- package/dist/emit/enum.emitter.js +0 -24
- package/dist/emit/module-specifier.scanner.d.ts +25 -0
- package/dist/emit/module-specifier.scanner.js +160 -0
- package/dist/emit/module-style.interface.d.ts +58 -0
- package/dist/emit/module-style.interface.js +8 -0
- package/dist/emit/name.deriver.d.ts +33 -61
- package/dist/emit/name.deriver.js +0 -134
- package/dist/emit/named-type.emitter.d.ts +14 -21
- package/dist/emit/named-type.emitter.js +3 -30
- package/dist/emit/runtime.emitter.d.ts +23 -50
- package/dist/emit/runtime.emitter.js +68 -159
- package/dist/emit/scalar.codec.d.ts +20 -33
- package/dist/emit/scalar.codec.js +13 -69
- package/dist/emit/transaction.emitter.d.ts +6 -14
- package/dist/emit/transaction.emitter.js +24 -33
- package/dist/generate.d.ts +17 -34
- package/dist/generate.js +14 -22
- package/dist/index.js +0 -5
- package/dist/init/client-config.template.d.ts +6 -4
- package/dist/init/client-config.template.js +10 -13
- package/dist/init/client-init.errors.js +0 -3
- package/dist/init/client-init.orchestrator.js +1 -9
- package/dist/init/client-init.planner.d.ts +1 -9
- package/dist/init/client-init.planner.js +6 -24
- package/dist/init/client-init.questions.d.ts +8 -12
- package/dist/init/client-init.questions.js +0 -11
- package/dist/init/client-project.inspector.d.ts +6 -0
- package/dist/init/client-project.inspector.js +2 -2
- package/dist/node-version.guard.js +0 -12
- package/dist/output/output.validator.d.ts +22 -22
- package/dist/output/output.validator.js +46 -59
- package/dist/output/output.writer.d.ts +56 -52
- package/dist/output/output.writer.js +71 -133
- package/package.json +6 -4
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import type { ClientContract } from "@aventara/core";
|
|
2
2
|
import type { ClientEntrypoint } from "../config/client-config.interface.js";
|
|
3
3
|
import { type ClientEmission } from "./emitted-tree.interface.js";
|
|
4
|
+
import { type ClientModuleStyle } from "./module-style.interface.js";
|
|
4
5
|
/**
|
|
5
|
-
* §15.3's emit steps over an accepted ClientContract, as one value (plan §7).
|
|
6
|
-
*
|
|
7
6
|
* The banner, the encoding and the file order are applied HERE, once, over every
|
|
8
|
-
* module — so no emitter can forget the banner, and byte-equality is a property
|
|
9
|
-
*
|
|
7
|
+
* module — so no emitter can forget the banner, and byte-equality is a property of
|
|
8
|
+
* this function rather than of each emitter's care:
|
|
10
9
|
*
|
|
11
|
-
* - every module opens with the generated banner
|
|
10
|
+
* - every module opens with the generated banner;
|
|
12
11
|
* - every file is UTF-8;
|
|
13
|
-
* - the layout is the architect's (2026-10-04): `AvClient.ts` at the root of
|
|
14
|
-
* `generateAt`, every other module under `generated/`;
|
|
15
12
|
* - files come in UTF-16 code-unit order of their path, each path once.
|
|
16
13
|
*
|
|
17
14
|
* The name derivation's rename warnings ride on the result beside the tree; this
|
|
@@ -19,19 +16,13 @@ import { type ClientEmission } from "./emitted-tree.interface.js";
|
|
|
19
16
|
*
|
|
20
17
|
* The names and enums read the enum registry, registry NAMES and `protocol`.
|
|
21
18
|
* Core's derivation is copied under `generated/derivation/` as its published
|
|
22
|
-
* declarations
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* `generated/
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* other input.
|
|
29
|
-
*
|
|
30
|
-
* The runtime (`generated/runtime/codec.ts`, `decimal.ts`, `errors.ts`,
|
|
31
|
-
* `transport.ts`) reads nothing of the Contract at all: §6.2's codecs and
|
|
32
|
-
* §13's codes are fixed by protocol version (M10, Q3), and the transport sends
|
|
33
|
-
* the operation its caller names.
|
|
19
|
+
* declarations: read from the installed `@aventara/core`, never from the Contract.
|
|
20
|
+
* The contract itself reaches the tree as three projections of the one accepted
|
|
21
|
+
* contract: the carrier `generated/contract.ts`, the runtime's decode table and
|
|
22
|
+
* advertised operations `generated/runtime/descriptor.ts`, and `metadata.ts`'s
|
|
23
|
+
* hash, version and default entrypoint — the entrypoint being the run's other
|
|
24
|
+
* input.
|
|
34
25
|
*
|
|
35
26
|
* @throws GeneratedNameError when a name cannot be emitted, even renamed.
|
|
36
27
|
*/
|
|
37
|
-
export declare function emitClientTree(contract: ClientContract, entrypoint: ClientEntrypoint): ClientEmission;
|
|
28
|
+
export declare function emitClientTree(contract: ClientContract, entrypoint: ClientEntrypoint, style: ClientModuleStyle): ClientEmission;
|
|
@@ -5,65 +5,32 @@ import { emitDerivationModules } from "./derivation.emitter.js";
|
|
|
5
5
|
import { emitDescriptorModule } from "./descriptor.emitter.js";
|
|
6
6
|
import { CLIENT_ENTRY_FILE, GENERATED_DIRECTORY, } from "./emitted-tree.interface.js";
|
|
7
7
|
import { emitEnumsModule } from "./enum.emitter.js";
|
|
8
|
+
import { moduleSpecifierWriter, } from "./module-style.interface.js";
|
|
8
9
|
import { deriveEmittedNames } from "./name.deriver.js";
|
|
9
10
|
import { emitNamedTypesModule } from "./named-type.emitter.js";
|
|
10
11
|
import { emitDecimalModule, emitErrorsModule, emitMetadataModule, emitTransportModule, errorsModuleExports, } from "./runtime.emitter.js";
|
|
11
12
|
import { emitScalarCodecModule } from "./scalar.codec.js";
|
|
12
13
|
import { emitTransactionModules } from "./transaction.emitter.js";
|
|
13
|
-
|
|
14
|
-
* §15.3's emit steps over an accepted ClientContract, as one value (plan §7).
|
|
15
|
-
*
|
|
16
|
-
* The banner, the encoding and the file order are applied HERE, once, over every
|
|
17
|
-
* module — so no emitter can forget the banner, and byte-equality is a property
|
|
18
|
-
* of this function rather than of each emitter's care (C-843):
|
|
19
|
-
*
|
|
20
|
-
* - every module opens with the generated banner (M17);
|
|
21
|
-
* - every file is UTF-8;
|
|
22
|
-
* - the layout is the architect's (2026-10-04): `AvClient.ts` at the root of
|
|
23
|
-
* `generateAt`, every other module under `generated/`;
|
|
24
|
-
* - files come in UTF-16 code-unit order of their path, each path once.
|
|
25
|
-
*
|
|
26
|
-
* The name derivation's rename warnings ride on the result beside the tree; this
|
|
27
|
-
* function prints nothing.
|
|
28
|
-
*
|
|
29
|
-
* The names and enums read the enum registry, registry NAMES and `protocol`.
|
|
30
|
-
* Core's derivation is copied under `generated/derivation/` as its published
|
|
31
|
-
* declarations (Q1 = A, `derivation.emitter.ts`): read from the installed
|
|
32
|
-
* `@aventara/core`, never from the Contract. The contract itself reaches the
|
|
33
|
-
* tree as three projections of the one accepted contract (plan §6): the carrier
|
|
34
|
-
* `generated/contract.ts` (Q4), the runtime's decode table and advertised
|
|
35
|
-
* operations `generated/runtime/descriptor.ts` (P1, P4), and `metadata.ts`'s
|
|
36
|
-
* hash, version and default entrypoint (Q5) — the entrypoint being the run's
|
|
37
|
-
* other input.
|
|
38
|
-
*
|
|
39
|
-
* The runtime (`generated/runtime/codec.ts`, `decimal.ts`, `errors.ts`,
|
|
40
|
-
* `transport.ts`) reads nothing of the Contract at all: §6.2's codecs and
|
|
41
|
-
* §13's codes are fixed by protocol version (M10, Q3), and the transport sends
|
|
42
|
-
* the operation its caller names.
|
|
43
|
-
*
|
|
44
|
-
* @throws GeneratedNameError when a name cannot be emitted, even renamed.
|
|
45
|
-
*/
|
|
46
|
-
export function emitClientTree(contract, entrypoint) {
|
|
14
|
+
export function emitClientTree(contract, entrypoint, style) {
|
|
47
15
|
const names = deriveEmittedNames(contract);
|
|
48
16
|
const generated = [
|
|
49
17
|
...emitDerivationModules(),
|
|
50
|
-
emitClientSurfaceModule(contract, names),
|
|
18
|
+
emitClientSurfaceModule(contract, names, style),
|
|
51
19
|
emitContractCarrierModule(contract),
|
|
52
20
|
emitDescriptorModule(contract),
|
|
53
21
|
emitEnumsModule(contract, names),
|
|
54
22
|
emitMetadataModule(contract.protocol, entrypoint),
|
|
55
|
-
emitScalarCodecModule(),
|
|
23
|
+
emitScalarCodecModule(style),
|
|
56
24
|
emitDecimalModule(),
|
|
57
25
|
emitErrorsModule(),
|
|
58
|
-
emitTransportModule(),
|
|
59
|
-
emitNamedTypesModule(contract, names),
|
|
60
|
-
// P3: the builder and runner exist iff transactions are interactive.
|
|
26
|
+
emitTransportModule(style),
|
|
27
|
+
emitNamedTypesModule(contract, names, style),
|
|
61
28
|
...(contract.transactions === "interactive"
|
|
62
|
-
? emitTransactionModules()
|
|
29
|
+
? emitTransactionModules(style)
|
|
63
30
|
: []),
|
|
64
31
|
];
|
|
65
32
|
const files = [
|
|
66
|
-
{ path: CLIENT_ENTRY_FILE, source: clientEntrySource() },
|
|
33
|
+
{ path: CLIENT_ENTRY_FILE, source: clientEntrySource(style) },
|
|
67
34
|
...generated.map((module) => ({
|
|
68
35
|
path: `${GENERATED_DIRECTORY}/${module.path}`,
|
|
69
36
|
source: module.source,
|
|
@@ -76,20 +43,11 @@ export function emitClientTree(contract, entrypoint) {
|
|
|
76
43
|
path: file.path,
|
|
77
44
|
bytes: encoder.encode(withGeneratedBanner(file.source)),
|
|
78
45
|
}));
|
|
79
|
-
return { tree, warnings: names.warnings };
|
|
46
|
+
return { tree, style, warnings: names.warnings };
|
|
80
47
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
* class `AvClient`, `AvClientOptions`, `CallOptions`, `Fetch`, `Operation<T>`,
|
|
85
|
-
* the named Resource types, the enums, `Decimal`, the errors and the code unions —
|
|
86
|
-
* re-exported from `generated/`. Never the transport's primitive (Q1 = B), never
|
|
87
|
-
* the derived forms map (Q3: internal).
|
|
88
|
-
*
|
|
89
|
-
* import avClient, { AvClient, User, UserWhere } from "./AvClient";
|
|
90
|
-
*/
|
|
91
|
-
function clientEntrySource() {
|
|
92
|
-
const from = (module) => JSON.stringify(`./${GENERATED_DIRECTORY}/${module.replace(/\.d\.ts$|\.ts$/, ".js")}`);
|
|
48
|
+
function clientEntrySource(style) {
|
|
49
|
+
const specifier = moduleSpecifierWriter(style);
|
|
50
|
+
const from = (module) => specifier(`./${GENERATED_DIRECTORY}/${module.replace(/\.d\.ts$|\.ts$/, "")}`);
|
|
93
51
|
return (`export { default } from ${from("client.ts")};\n` +
|
|
94
52
|
`export {\n\tAvClient,\n\ttype AvClientOptions,\n\ttype CallOptions,\n} from ${from("client.ts")};\n` +
|
|
95
53
|
`export type { Fetch } from ${from("runtime/transport.ts")};\n` +
|
|
@@ -3,11 +3,10 @@ import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
|
3
3
|
/** `generated/contract.ts`, before the banner. */
|
|
4
4
|
export declare function emitContractCarrierModule(contract: ClientContract): EmittedModule;
|
|
5
5
|
/**
|
|
6
|
-
* The ClientContract a carrier file holds — its whole text, banner included —
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* that does not re-hash is treated as absent).
|
|
6
|
+
* The ClientContract a carrier file holds — its whole text, banner included — or
|
|
7
|
+
* `undefined` when it is not one this generator wrote over a contract that still
|
|
8
|
+
* verifies: a foreign prefix or suffix, bytes that are not JSON, a body that is
|
|
9
|
+
* not a ClientContract or whose hash does not re-verify, or bytes that are not
|
|
10
|
+
* that contract's canonical form. Never trusted otherwise.
|
|
12
11
|
*/
|
|
13
12
|
export declare function parseContractCarrier(text: string): Promise<ClientContract | undefined>;
|
|
@@ -1,42 +1,14 @@
|
|
|
1
1
|
import { canonicalizeContract } from "@aventara/core";
|
|
2
2
|
import { acceptClientContract } from "../contract/contract.acceptance.js";
|
|
3
3
|
import { withGeneratedBanner } from "./banner.emitter.js";
|
|
4
|
-
/**
|
|
5
|
-
* The carrier (Q4 = A): `generated/contract.ts` holds the ClientContract the
|
|
6
|
-
* client was generated against, as its served canonical bytes (RFC 8785, §19.2)
|
|
7
|
-
* verbatim — which are a valid TypeScript type literal (plan M4):
|
|
8
|
-
*
|
|
9
|
-
* ```ts
|
|
10
|
-
* export type ClientContractShape = {"enums":{},…};
|
|
11
|
-
* ```
|
|
12
|
-
*
|
|
13
|
-
* One artefact, three readers: the derivation's `C` (S4); the generator on a
|
|
14
|
-
* `304` (Q6, S7), which parses it back with {@link parseContractCarrier}; and a
|
|
15
|
-
* person auditing what the client was generated against. It costs no runtime
|
|
16
|
-
* byte — nothing imports it as a value.
|
|
17
|
-
*
|
|
18
|
-
* The bytes are `canonicalizeContract`'s over the ACCEPTED contract: the body a
|
|
19
|
-
* deployment serves is those bytes (`_contract` serves `canonicalizeContract`'s
|
|
20
|
-
* output, and acceptance verified the hash over the same canonical form), and a
|
|
21
|
-
* contract that arrives in another key order still emits them (C-843).
|
|
22
|
-
*/
|
|
23
4
|
const CARRIER_PREFIX = "export type ClientContractShape = ";
|
|
24
5
|
const CARRIER_SUFFIX = ";\n";
|
|
25
|
-
/** `generated/contract.ts`, before the banner. */
|
|
26
6
|
export function emitContractCarrierModule(contract) {
|
|
27
7
|
return {
|
|
28
8
|
path: "contract.ts",
|
|
29
9
|
source: `${CARRIER_PREFIX}${canonicalizeContract(contract)}${CARRIER_SUFFIX}`,
|
|
30
10
|
};
|
|
31
11
|
}
|
|
32
|
-
/**
|
|
33
|
-
* The ClientContract a carrier file holds — its whole text, banner included —
|
|
34
|
-
* or `undefined` when it is not one this generator wrote over a contract that
|
|
35
|
-
* still verifies: a foreign prefix or suffix, bytes that are not JSON, a body
|
|
36
|
-
* that is not a ClientContract or whose hash does not re-verify, or bytes that
|
|
37
|
-
* are not that contract's canonical form. Never trusted otherwise (Q6: a carrier
|
|
38
|
-
* that does not re-hash is treated as absent).
|
|
39
|
-
*/
|
|
40
12
|
export async function parseContractCarrier(text) {
|
|
41
13
|
const opening = withGeneratedBanner(CARRIER_PREFIX);
|
|
42
14
|
if (!text.startsWith(opening) || !text.endsWith(CARRIER_SUFFIX)) {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
2
2
|
/**
|
|
3
|
-
* The derivation, transported
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* The derivation, transported. A generated client's argument and result types are
|
|
4
|
+
* core's own `OperationArgumentsFor`, `OperationResultFor` and the call grammar
|
|
5
|
+
* (`OperationCall`, …) — never a second spelling. They reach the emitted tree as
|
|
6
|
+
* core's PUBLISHED DECLARATIONS, copied under `generated/derivation/` at their
|
|
7
|
+
* path relative to core's `dist`:
|
|
8
8
|
*
|
|
9
9
|
* - declarations, not sources: the sources' closure carries value imports (the
|
|
10
|
-
* canonicaliser, the wire grammars), the declarations' carries none
|
|
11
|
-
*
|
|
10
|
+
* canonicaliser, the wire grammars), the declarations' carries none; a `.d.ts`
|
|
11
|
+
* cannot carry runtime, so a bundler never sees one;
|
|
12
12
|
* - under their own directory, so core's `runtime/decimal.d.ts` cannot shadow the
|
|
13
13
|
* emitted `generated/runtime/decimal.ts`;
|
|
14
14
|
* - byte for byte, except the trailing `//# sourceMappingURL=…` line, which names
|
|
@@ -1,34 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
* The derivation, transported (Phase 12-rest S2, Q1 = A). A generated client's
|
|
6
|
-
* argument and result types are core's own `OperationArgumentsFor`,
|
|
7
|
-
* `OperationResultFor` and the call grammar (`OperationCall`, …) — never a second
|
|
8
|
-
* spelling. They reach the emitted tree as core's PUBLISHED DECLARATIONS, copied
|
|
9
|
-
* under `generated/derivation/` at their path relative to core's `dist`:
|
|
10
|
-
*
|
|
11
|
-
* - declarations, not sources: the sources' closure carries value imports (the
|
|
12
|
-
* canonicaliser, the wire grammars), the declarations' carries none (plan M1);
|
|
13
|
-
* a `.d.ts` cannot carry runtime, so a bundler never sees one;
|
|
14
|
-
* - under their own directory, so core's `runtime/decimal.d.ts` cannot shadow the
|
|
15
|
-
* emitted `generated/runtime/decimal.ts`;
|
|
16
|
-
* - byte for byte, except the trailing `//# sourceMappingURL=…` line, which names
|
|
17
|
-
* a map the tree does not hold.
|
|
18
|
-
*
|
|
19
|
-
* The closure is walked here, at generation time, from {@link DERIVATION_ROOTS}
|
|
20
|
-
* over whichever `@aventara/core` this package resolves — so the output is a
|
|
21
|
-
* function of the core version the generator depends on, as the rest of it is a
|
|
22
|
-
* function of the generator. A closure that names a package, carries a
|
|
23
|
-
* triple-slash reference, or leaves core's declarations is refused: it cannot be
|
|
24
|
-
* transported, and core's own gate (`emittable-closure.gate.spec.ts`) exists so
|
|
25
|
-
* that it never is.
|
|
26
|
-
*/
|
|
27
|
-
/**
|
|
28
|
-
* The modules whose declarations the generated client copies — the roots of core's
|
|
29
|
-
* `emittable-closure.gate.spec.ts`, mirrored (core cannot export a test constant,
|
|
30
|
-
* and this package cannot read core's tests).
|
|
31
|
-
*/
|
|
4
|
+
import { scanModuleSpecifiers } from "./module-specifier.scanner.js";
|
|
32
5
|
export const DERIVATION_ROOTS = [
|
|
33
6
|
"contracts/contract",
|
|
34
7
|
"contracts/scalar-value-type",
|
|
@@ -39,7 +12,6 @@ export const DERIVATION_ROOTS = [
|
|
|
39
12
|
"transactions/deferred-operation",
|
|
40
13
|
"transactions/transaction-reference",
|
|
41
14
|
];
|
|
42
|
-
/** The published declarations of the `@aventara/core` this package resolves. */
|
|
43
15
|
export function publishedCoreDeclarations() {
|
|
44
16
|
const dist = path.dirname(fileURLToPath(import.meta.resolve("@aventara/core")));
|
|
45
17
|
return (relative) => {
|
|
@@ -54,13 +26,6 @@ export function publishedCoreDeclarations() {
|
|
|
54
26
|
}
|
|
55
27
|
};
|
|
56
28
|
}
|
|
57
|
-
/**
|
|
58
|
-
* Core's declaration closure from {@link DERIVATION_ROOTS}, as modules under
|
|
59
|
-
* `derivation/`, in UTF-16 code-unit order of their path.
|
|
60
|
-
*
|
|
61
|
-
* @throws Error when the closure cannot be transported — a defect of the
|
|
62
|
-
* installed `@aventara/core`, never of the consumer's input.
|
|
63
|
-
*/
|
|
64
29
|
export function emitDerivationModules(read = publishedCoreDeclarations()) {
|
|
65
30
|
const copied = new Map();
|
|
66
31
|
const queue = DERIVATION_ROOTS.map((root) => `${root}.d.ts`);
|
|
@@ -73,7 +38,7 @@ export function emitDerivationModules(read = publishedCoreDeclarations()) {
|
|
|
73
38
|
if (text === undefined) {
|
|
74
39
|
throw new Error(`@aventara/core's declaration closure names ${file}, which it does not publish.`);
|
|
75
40
|
}
|
|
76
|
-
const scanned =
|
|
41
|
+
const scanned = scanModuleSpecifiers(text);
|
|
77
42
|
if (scanned.references) {
|
|
78
43
|
throw untransportable(`${file} carries a triple-slash reference, which the emitted tree cannot satisfy`);
|
|
79
44
|
}
|
|
@@ -92,10 +57,6 @@ export function emitDerivationModules(read = publishedCoreDeclarations()) {
|
|
|
92
57
|
function untransportable(reason) {
|
|
93
58
|
return new Error(`@aventara/core's declarations cannot be copied into the generated client: ${reason}.`);
|
|
94
59
|
}
|
|
95
|
-
/**
|
|
96
|
-
* The declaration file a specifier names, relative to core's `dist`: a relative
|
|
97
|
-
* `.js` specifier inside it, as NodeNext resolves an emitted declaration's import.
|
|
98
|
-
*/
|
|
99
60
|
function declarationTarget(file, specifier) {
|
|
100
61
|
const leaves = () => untransportable(`${file} names ${JSON.stringify(specifier)}, which is not a declaration file beside it`);
|
|
101
62
|
if (!(specifier.startsWith("./") || specifier.startsWith("../")) ||
|
|
@@ -108,126 +69,6 @@ function declarationTarget(file, specifier) {
|
|
|
108
69
|
}
|
|
109
70
|
return target.replace(/\.js$/, ".d.ts");
|
|
110
71
|
}
|
|
111
|
-
/** `text` without its final `//# sourceMappingURL=…` line, when it ends with one. */
|
|
112
72
|
function withoutSourceMapTrailer(text) {
|
|
113
73
|
return text.replace(/(^|\n)\/\/# sourceMappingURL=[^\n]*\n?$/, "$1");
|
|
114
74
|
}
|
|
115
|
-
/**
|
|
116
|
-
* A word followed directly by a string literal names a module. A pattern, not two
|
|
117
|
-
* string comparisons: the packaging gate reads shipped JavaScript lexically, and
|
|
118
|
-
* a keyword spelled as a quoted literal would read to it as an import.
|
|
119
|
-
*/
|
|
120
|
-
const SPECIFIER_KEYWORD = /^(?:from|import)$/;
|
|
121
|
-
/**
|
|
122
|
-
* The specifiers one declaration file names, read by a small lexer rather than
|
|
123
|
-
* by `typescript` (an optional peer, absent from a consumer's install): comments,
|
|
124
|
-
* string literals and template literals are skipped as units, so text inside them
|
|
125
|
-
* is never mistaken for an import. Agrees with TypeScript's own pre-processor
|
|
126
|
-
* over core's closure (`derivation.emitter.spec.ts`).
|
|
127
|
-
*/
|
|
128
|
-
function scanDeclaration(text) {
|
|
129
|
-
const tokens = [];
|
|
130
|
-
let references = false;
|
|
131
|
-
let index = 0;
|
|
132
|
-
const readQuoted = (quote) => {
|
|
133
|
-
let value = "";
|
|
134
|
-
index += 1;
|
|
135
|
-
while (index < text.length && text[index] !== quote) {
|
|
136
|
-
if (text[index] === "\\") {
|
|
137
|
-
value += text[index + 1] ?? "";
|
|
138
|
-
index += 2;
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
value += text[index];
|
|
142
|
-
index += 1;
|
|
143
|
-
}
|
|
144
|
-
index += 1;
|
|
145
|
-
return value;
|
|
146
|
-
};
|
|
147
|
-
/** Scans code until the end, or until the `}` closing a `${` when `inPlaceholder`. */
|
|
148
|
-
const scanCode = (inPlaceholder) => {
|
|
149
|
-
let depth = 0;
|
|
150
|
-
while (index < text.length) {
|
|
151
|
-
const character = text[index];
|
|
152
|
-
const next = text[index + 1];
|
|
153
|
-
if (character === "/" && next === "/") {
|
|
154
|
-
const end = text.indexOf("\n", index);
|
|
155
|
-
const line = text.slice(index, end === -1 ? text.length : end);
|
|
156
|
-
if (/^\/\/\/\s*<reference\b/.test(line)) {
|
|
157
|
-
references = true;
|
|
158
|
-
}
|
|
159
|
-
index = end === -1 ? text.length : end + 1;
|
|
160
|
-
}
|
|
161
|
-
else if (character === "/" && next === "*") {
|
|
162
|
-
const end = text.indexOf("*/", index + 2);
|
|
163
|
-
index = end === -1 ? text.length : end + 2;
|
|
164
|
-
}
|
|
165
|
-
else if (character === '"' || character === "'") {
|
|
166
|
-
tokens.push({ kind: "string", value: readQuoted(character) });
|
|
167
|
-
}
|
|
168
|
-
else if (character === "`") {
|
|
169
|
-
scanTemplate();
|
|
170
|
-
}
|
|
171
|
-
else if (/[A-Za-z_$]/.test(character)) {
|
|
172
|
-
const start = index;
|
|
173
|
-
while (index < text.length && /[\w$]/.test(text[index])) {
|
|
174
|
-
index += 1;
|
|
175
|
-
}
|
|
176
|
-
tokens.push({ kind: "word", value: text.slice(start, index) });
|
|
177
|
-
}
|
|
178
|
-
else if (/\s/.test(character)) {
|
|
179
|
-
index += 1;
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
if (inPlaceholder && character === "{") {
|
|
183
|
-
depth += 1;
|
|
184
|
-
}
|
|
185
|
-
else if (inPlaceholder && character === "}") {
|
|
186
|
-
if (depth === 0) {
|
|
187
|
-
index += 1;
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
depth -= 1;
|
|
191
|
-
}
|
|
192
|
-
tokens.push({ kind: "punctuation", value: character });
|
|
193
|
-
index += 1;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
|
-
const scanTemplate = () => {
|
|
198
|
-
index += 1;
|
|
199
|
-
while (index < text.length && text[index] !== "`") {
|
|
200
|
-
if (text[index] === "\\") {
|
|
201
|
-
index += 2;
|
|
202
|
-
}
|
|
203
|
-
else if (text[index] === "$" && text[index + 1] === "{") {
|
|
204
|
-
index += 2;
|
|
205
|
-
scanCode(true);
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
index += 1;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
index += 1;
|
|
212
|
-
// A template is one opaque token: nothing before it pairs with it.
|
|
213
|
-
tokens.push({ kind: "punctuation", value: "`" });
|
|
214
|
-
};
|
|
215
|
-
scanCode(false);
|
|
216
|
-
const specifiers = [];
|
|
217
|
-
tokens.forEach((token, at) => {
|
|
218
|
-
const following = tokens[at + 1];
|
|
219
|
-
if (token.kind !== "word") {
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
if (SPECIFIER_KEYWORD.test(token.value) && following?.kind === "string") {
|
|
223
|
-
specifiers.push(following.value);
|
|
224
|
-
}
|
|
225
|
-
else if (token.value === "import" &&
|
|
226
|
-
following?.kind === "punctuation" &&
|
|
227
|
-
following.value === "(" &&
|
|
228
|
-
tokens[at + 2]?.kind === "string") {
|
|
229
|
-
specifiers.push(tokens[at + 2].value);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
return { specifiers, references };
|
|
233
|
-
}
|
|
@@ -1,36 +1,11 @@
|
|
|
1
1
|
import { isOperationVariantAvailable, } from "@aventara/core";
|
|
2
2
|
import { ownPropertyKey } from "./name.deriver.js";
|
|
3
|
-
/**
|
|
4
|
-
* `generated/runtime/descriptor.ts` — what the RUNTIME needs of the ClientContract,
|
|
5
|
-
* as data (P1, P4). A projection of the same accepted contract the carrier
|
|
6
|
-
* (`generated/contract.ts`) holds, derived in the same run, so it is not a second
|
|
7
|
-
* source: `descriptor.emitter.spec.ts` regenerates it from the parsed carrier and
|
|
8
|
-
* compares.
|
|
9
|
-
*
|
|
10
|
-
* - **The decode table (P1).** Encoding is decidable by a value's runtime class;
|
|
11
|
-
* decoding is not — a wire `"12"` is a `string`, a `bigint` or a `decimal`
|
|
12
|
-
* depending only on the field (M13). So per Resource the table names each
|
|
13
|
-
* result field whose wire string is revived (`bigint`, `decimal`, `datetime`,
|
|
14
|
-
* `bytes`, §6.2) and each relation with its target and whether it is to-many,
|
|
15
|
-
* which the decode walk recurses into (S5 added `many`: a to-one record may
|
|
16
|
-
* itself hold a `data` and a `count` field, so the shape alone cannot say). Every other key passes through untouched; `json` is never
|
|
17
|
-
* revived.
|
|
18
|
-
* - **The advertised operations (P4).** Read here through core's
|
|
19
|
-
* `isOperationVariantAvailable`, emitted as `[resource, family, variant]`; the
|
|
20
|
-
* type grammar reads the carrier's `operations` keys. They agree while present
|
|
21
|
-
* ⇔ advertised (Phase 10 A3), and a gate asserts it over both pilots.
|
|
22
|
-
*
|
|
23
|
-
* Every registry is walked in UTF-16 code-unit order, so the key order a
|
|
24
|
-
* contract arrives in never reaches the bytes (C-843).
|
|
25
|
-
*/
|
|
26
|
-
/** The scalars whose wire form a decoder revives into another runtime class (§6.2). */
|
|
27
3
|
const REVIVED_SCALARS = new Set([
|
|
28
4
|
"bigint",
|
|
29
5
|
"bytes",
|
|
30
6
|
"datetime",
|
|
31
7
|
"decimal",
|
|
32
8
|
]);
|
|
33
|
-
/** `generated/runtime/descriptor.ts`, before the banner. */
|
|
34
9
|
export function emitDescriptorModule(contract) {
|
|
35
10
|
const resources = codeUnitOrder(Object.keys(contract.resources));
|
|
36
11
|
const table = resources.map((name) => {
|
|
@@ -55,9 +30,9 @@ export function emitDescriptorModule(contract) {
|
|
|
55
30
|
return {
|
|
56
31
|
path: "runtime/descriptor.ts",
|
|
57
32
|
source: "/**\n" +
|
|
58
|
-
" * A result field the decoder revives from its wire string
|
|
33
|
+
" * A result field the decoder revives from its wire string, or a relation\n" +
|
|
59
34
|
" * it recurses into, read as the relation's target Resource — `many` saying the\n" +
|
|
60
|
-
" * value is a list, `{ data, count }` or `{ count }` rather than one record
|
|
35
|
+
" * value is a list, `{ data, count }` or `{ count }` rather than one record.\n" +
|
|
61
36
|
" */\n" +
|
|
62
37
|
"export type FieldDecoding =\n" +
|
|
63
38
|
'\t| "bigint"\n' +
|
|
@@ -79,7 +54,6 @@ export function emitDescriptorModule(contract) {
|
|
|
79
54
|
`])[] = [${advertised.length === 0 ? "" : `\n${advertised.join("")}`}];\n`,
|
|
80
55
|
};
|
|
81
56
|
}
|
|
82
|
-
/** The table entry a field needs, as source, or `undefined` when it passes through. */
|
|
83
57
|
function decodingOf(field) {
|
|
84
58
|
if (field === undefined) {
|
|
85
59
|
return undefined;
|
|
@@ -1,37 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The generator's output as a VALUE before it is a filesystem effect
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* every emitter.
|
|
2
|
+
* The generator's output as a VALUE before it is a filesystem effect.
|
|
3
|
+
* Byte-equality is therefore testable without touching disk, and temp → validate →
|
|
4
|
+
* replace is a property of the writer alone rather than of every emitter.
|
|
6
5
|
*/
|
|
6
|
+
import type { ClientModuleStyle } from "./module-style.interface.js";
|
|
7
7
|
/**
|
|
8
|
-
* The output layout
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The output layout: the consumer names a directory, `generateAt`, which is SHARED
|
|
9
|
+
* — they may keep their own files there. The generator owns exactly two entries in
|
|
10
|
+
* it:
|
|
11
11
|
*
|
|
12
12
|
* - `AvClient.ts`, the entry point a consumer imports;
|
|
13
13
|
* - `generated/`, every other module, owned and replaced whole.
|
|
14
|
+
*
|
|
15
|
+
* The tree is TypeScript source that the developer's own toolchain compiles like
|
|
16
|
+
* their own files — `tsc`, Next.js (Turbopack or webpack), Vite, `tsx`, Node's
|
|
17
|
+
* type stripping. pilot.1's tree always spelled its internal imports NodeNext's
|
|
18
|
+
* way (`./generated/client.js`), which a bundler that does not map `.js` back to
|
|
19
|
+
* `.ts` (Turbopack) and plain Node could not follow. Now those specifiers are the
|
|
20
|
+
* project's: extensionless, `.js` or `.ts`, as its `tsconfig.json` decides
|
|
21
|
+
* (`module-style.interface.ts`, Prisma 7's rules). A consumer imports the entry as
|
|
22
|
+
* they import their own files — `./api/AvClient`, `./api/AvClient.js` or
|
|
23
|
+
* `./api/AvClient.ts`.
|
|
14
24
|
*/
|
|
15
25
|
/** The entry point, at the root of `generateAt`. */
|
|
16
26
|
export declare const CLIENT_ENTRY_FILE = "AvClient.ts";
|
|
27
|
+
/** The files the generator owns at the root of `generateAt`. */
|
|
28
|
+
export declare const CLIENT_ENTRY_FILES: readonly ["AvClient.ts"];
|
|
29
|
+
/** One of the entry point's files. */
|
|
30
|
+
export type ClientEntryFile = (typeof CLIENT_ENTRY_FILES)[number];
|
|
31
|
+
/**
|
|
32
|
+
* Entry files an unreleased build of this generator wrote — JavaScript beside
|
|
33
|
+
* declarations, never published. Each is the generator's only while it carries
|
|
34
|
+
* the ownership line: a generation removes it then, so no stale `AvClient.mjs`
|
|
35
|
+
* is left beside the `AvClient.ts` it was replaced by. One without the line is
|
|
36
|
+
* the developer's file, and nothing reads, refuses on or removes it. (pilot.0's
|
|
37
|
+
* and pilot.1's entry is `AvClient.ts` itself, replaced in place.)
|
|
38
|
+
*/
|
|
39
|
+
export declare const LEGACY_CLIENT_ENTRY_FILES: readonly string[];
|
|
17
40
|
/** The directory holding every other emitted module, under `generateAt`. */
|
|
18
41
|
export declare const GENERATED_DIRECTORY = "generated";
|
|
19
42
|
/**
|
|
20
43
|
* One of core's published declaration files, copied under `generated/derivation/`
|
|
21
|
-
* at its path relative to core's `dist
|
|
44
|
+
* at its path relative to core's `dist`.
|
|
22
45
|
*/
|
|
23
46
|
export type DerivationModulePath = `derivation/${string}.d.ts`;
|
|
24
47
|
/**
|
|
25
|
-
* The modules under `generated/`: the emitted sources, the carrier
|
|
26
|
-
* (`contract.ts`, Q4), the typed surface (`client.ts`) and named types
|
|
27
|
-
* (`types.d.ts`, S4 — a declaration file, architect 2026-10-05), and core's declarations under `derivation/`. §15.4's
|
|
28
|
-
* `runtime/transaction.ts` and `runtime/fingerprint.ts` exist iff transactions
|
|
29
|
-
* are interactive (S6, P3); `resources/` and `runtime/projection.ts` are not
|
|
30
|
-
* emitted (plan §7).
|
|
31
|
-
*
|
|
32
48
|
* Relative to `generated/`, POSIX-separated.
|
|
33
49
|
*/
|
|
34
50
|
export type GeneratedModulePath = DerivationModulePath | "client.ts" | "contract.ts" | "enums.ts" | "metadata.ts" | "runtime/codec.ts" | "runtime/decimal.ts" | "runtime/descriptor.ts" | "runtime/errors.ts" | "runtime/fingerprint.ts" | "runtime/transaction.ts" | "runtime/transport.ts" | "types.d.ts";
|
|
51
|
+
/**
|
|
52
|
+
* The carrier: the ClientContract the tree was generated against, under
|
|
53
|
+
* `generated/` — the module a rerun reads back to ask for a `304`.
|
|
54
|
+
*/
|
|
55
|
+
export declare const CONTRACT_CARRIER_MODULE = "contract.ts";
|
|
35
56
|
/** A path in the emitted tree: relative to `generateAt`, POSIX-separated. */
|
|
36
57
|
export type EmittedFilePath = typeof CLIENT_ENTRY_FILE | `${typeof GENERATED_DIRECTORY}/${GeneratedModulePath}`;
|
|
37
58
|
/** One module's TypeScript source under `generated/`, before the banner and before encoding. */
|
|
@@ -46,8 +67,8 @@ export interface EmittedFile {
|
|
|
46
67
|
}
|
|
47
68
|
/**
|
|
48
69
|
* Every file one generation emits, in UTF-16 code-unit order of `path`, each path
|
|
49
|
-
* once — `AvClient.ts` first, then `generated/**`. Replaced, never merged
|
|
50
|
-
*
|
|
70
|
+
* once — `AvClient.ts` first, then `generated/**`. Replaced, never merged:
|
|
71
|
+
* `generated/` whole, `AvClient.ts` as one file.
|
|
51
72
|
*/
|
|
52
73
|
export type EmittedTree = readonly EmittedFile[];
|
|
53
74
|
/**
|
|
@@ -57,5 +78,7 @@ export type EmittedTree = readonly EmittedFile[];
|
|
|
57
78
|
*/
|
|
58
79
|
export interface ClientEmission {
|
|
59
80
|
readonly tree: EmittedTree;
|
|
81
|
+
/** The project's spelling the tree was emitted in, and is validated in. */
|
|
82
|
+
readonly style: ClientModuleStyle;
|
|
60
83
|
readonly warnings: readonly string[];
|
|
61
84
|
}
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The generator's output as a VALUE before it is a filesystem effect (plan §7,
|
|
3
|
-
* group 3). Byte-equality is therefore testable without touching disk, and
|
|
4
|
-
* temp → validate → replace (S7) is a property of the writer alone rather than of
|
|
5
|
-
* every emitter.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* The output layout (architect, 2026-10-04): the consumer names a directory,
|
|
9
|
-
* `generateAt`, which is SHARED — they may keep their own files there. The
|
|
10
|
-
* generator owns exactly two entries in it:
|
|
11
|
-
*
|
|
12
|
-
* - `AvClient.ts`, the entry point a consumer imports;
|
|
13
|
-
* - `generated/`, every other module, owned and replaced whole.
|
|
14
|
-
*/
|
|
15
|
-
/** The entry point, at the root of `generateAt`. */
|
|
16
1
|
export const CLIENT_ENTRY_FILE = "AvClient.ts";
|
|
17
|
-
|
|
2
|
+
export const CLIENT_ENTRY_FILES = [CLIENT_ENTRY_FILE];
|
|
3
|
+
export const LEGACY_CLIENT_ENTRY_FILES = [
|
|
4
|
+
"AvClient.d.mts",
|
|
5
|
+
"AvClient.mjs",
|
|
6
|
+
];
|
|
18
7
|
export const GENERATED_DIRECTORY = "generated";
|
|
8
|
+
export const CONTRACT_CARRIER_MODULE = "contract.ts";
|
|
@@ -3,7 +3,7 @@ import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
|
3
3
|
import { type EmittedNames } from "./name.deriver.js";
|
|
4
4
|
/**
|
|
5
5
|
* `enums.ts`: per enum, a string-literal union type and a same-named `as const`
|
|
6
|
-
* object
|
|
6
|
+
* object:
|
|
7
7
|
*
|
|
8
8
|
* ```ts
|
|
9
9
|
* export type Role = "ADMIN" | "USER";
|
|
@@ -17,8 +17,8 @@ import { type EmittedNames } from "./name.deriver.js";
|
|
|
17
17
|
* renamed.
|
|
18
18
|
*
|
|
19
19
|
* Enums come in the order `names` gives them — UTF-16 code units of the contract
|
|
20
|
-
* name — so the key order a contract arrives in never reaches the bytes
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* name — so the key order a contract arrives in never reaches the bytes. An enum's
|
|
21
|
+
* VALUES keep their declared order, in the type and in the object: that order is
|
|
22
|
+
* meaning, and canonical form keeps it too.
|
|
23
23
|
*/
|
|
24
24
|
export declare function emitEnumsModule(contract: Pick<ClientContract, "enums">, names: EmittedNames): EmittedModule;
|