@aventara/client 0.1.0-pilot.0 → 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 +61 -61
- package/dist/avclient.bin.js +0 -10
- package/dist/cli/command.parser.d.ts +21 -9
- package/dist/cli/command.parser.js +51 -12
- package/dist/cli/generate.command.js +0 -6
- package/dist/cli/generation-failure.renderer.js +0 -16
- 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 +11 -27
- 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 +33 -13
- package/dist/config/config.loader.js +52 -40
- 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 +9 -2
- package/dist/init/client-config.template.js +12 -10
- package/dist/init/client-init.errors.js +0 -3
- package/dist/init/client-init.orchestrator.js +7 -11
- package/dist/init/client-init.planner.d.ts +3 -10
- package/dist/init/client-init.planner.js +5 -12
- 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 +28 -29
- package/dist/output/output.validator.js +68 -73
- package/dist/output/output.writer.d.ts +56 -52
- package/dist/output/output.writer.js +71 -133
- package/package.json +7 -5
|
@@ -1,33 +1,7 @@
|
|
|
1
|
+
import { importSpecifier, } from "./module-style.interface.js";
|
|
1
2
|
import { ownPropertyKey } from "./name.deriver.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* `AvClient` (Q8), its options `AvClientOptions` and the per-call `CallOptions`
|
|
5
|
-
* (Q9, Q17), and the ready instance `avClient`, this module's default export
|
|
6
|
-
* (Q7).
|
|
7
|
-
*
|
|
8
|
-
* Every argument and result type is an instantiation of core's own derivation,
|
|
9
|
-
* copied under `generated/derivation/` (Q1 = A): the call grammar
|
|
10
|
-
* (`OperationGrammar`, `OperationCall`, `DeferredOperationCall`, S1) over the
|
|
11
|
-
* carrier's contract (`ClientContractShape`, Q4), in the client's forms (Q3 = A:
|
|
12
|
-
* core's application forms, with this tree's own `Decimal`). Nothing here
|
|
13
|
-
* re-spells a rule. A call resolves to the data (Q11 = a: `"data"`), a
|
|
14
|
-
* first-style miss to `null`.
|
|
15
|
-
*
|
|
16
|
-
* Each surface is one mapped alias over the one contract (Phase 9's variance
|
|
17
|
-
* lesson). A Resource is reached by its contract name, except where that name is
|
|
18
|
-
* one of the client's own members (Q12), which renames the property only.
|
|
19
|
-
* `tx` and `transaction` exist iff the contract advertises `interactive`
|
|
20
|
-
* transactions (P3), in the type as in the runtime.
|
|
21
|
-
*
|
|
22
|
-
* Also declared here, for `types.d.ts` alone, the two helpers the named types are
|
|
23
|
-
* aliases of (Q10): `ResourceRecord` and `ResourceArgument`.
|
|
24
|
-
*
|
|
25
|
-
* At runtime (S5) the class builds one frozen object per Resource from the
|
|
26
|
-
* advertised operations (P4) — each variant a function that runs the operation
|
|
27
|
-
* through the transport, reading the fetch when it is called — and the default
|
|
28
|
-
* entrypoint is the generated one (§15.2, Q5) unless the options name another.
|
|
29
|
-
*/
|
|
30
|
-
export function emitClientSurfaceModule(contract, names) {
|
|
3
|
+
export function emitClientSurfaceModule(contract, names, style) {
|
|
4
|
+
const from = (module) => JSON.stringify(importSpecifier(module, style));
|
|
31
5
|
const renamed = names.properties.filter((property) => property.property !== property.contractName);
|
|
32
6
|
const propertyOf = renamed.length === 0
|
|
33
7
|
? "R"
|
|
@@ -48,34 +22,34 @@ export function emitClientSurfaceModule(contract, names) {
|
|
|
48
22
|
"};\n";
|
|
49
23
|
return {
|
|
50
24
|
path: "client.ts",
|
|
51
|
-
source:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
`import type { ${interactive ? "DeferredOperationCall, " : ""}OperationCall, OperationGrammar } from "./derivation/operations/operation-call
|
|
55
|
-
|
|
56
|
-
|
|
25
|
+
source: `import type { ClientContractShape } from ${from("./contract")};\n` +
|
|
26
|
+
`import type { ApplicationScalarForms } from ${from("./derivation/contracts/scalar-value-type")};\n` +
|
|
27
|
+
`import type { OperationArgumentsFor, ResourceKey } from ${from("./derivation/operations/operation-arguments")};\n` +
|
|
28
|
+
`import type { ${interactive ? "DeferredOperationCall, " : ""}OperationCall, OperationGrammar } from ${from("./derivation/operations/operation-call")};\n` +
|
|
29
|
+
`import type { OperationFamily, OperationIdentity } from ${from("./derivation/operations/operation-identity")};\n` +
|
|
30
|
+
`import type { AdmittedOperationResult } from ${from("./derivation/operations/operation-result")};\n` +
|
|
57
31
|
(interactive
|
|
58
|
-
?
|
|
32
|
+
? `import type { Operation, TransactionResults } from ${from("./derivation/transactions/deferred-operation")};\n`
|
|
59
33
|
: "") +
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
34
|
+
`import { DEFAULT_ENTRYPOINT } from ${from("./metadata")};\n` +
|
|
35
|
+
`import type { Decimal } from ${from("./runtime/decimal")};\n` +
|
|
36
|
+
`import { ADVERTISED_OPERATIONS } from ${from("./runtime/descriptor")};\n` +
|
|
63
37
|
(interactive
|
|
64
|
-
?
|
|
38
|
+
? `import { deferOperation, runTransaction } from ${from("./runtime/transaction")};\n`
|
|
65
39
|
: "") +
|
|
66
|
-
|
|
40
|
+
`import { type CallOptions, execute, type Fetch, type TransportConnection } from ${from("./runtime/transport")};\n` +
|
|
67
41
|
"\n" +
|
|
68
|
-
|
|
42
|
+
`export type { CallOptions } from ${from("./runtime/transport")};\n` +
|
|
69
43
|
"\n" +
|
|
70
44
|
"/**\n" +
|
|
71
|
-
" * The forms this client reads and writes
|
|
45
|
+
" * The forms this client reads and writes: core's application forms —\n" +
|
|
72
46
|
" * `bigint`, `Date`, `Uint8Array` — with this client's own `Decimal`.\n" +
|
|
73
47
|
" */\n" +
|
|
74
48
|
'export type ClientScalarForms = Omit<ApplicationScalarForms, "decimal"> & {\n' +
|
|
75
49
|
"\treadonly decimal: Decimal;\n" +
|
|
76
50
|
"};\n" +
|
|
77
51
|
"\n" +
|
|
78
|
-
"/** How an `AvClient` reaches its deployment
|
|
52
|
+
"/** How an `AvClient` reaches its deployment. */\n" +
|
|
79
53
|
"export interface AvClientOptions {\n" +
|
|
80
54
|
"\t/** Another deployment serving exactly the same ClientContract; the generated default otherwise. */\n" +
|
|
81
55
|
"\treadonly entrypoint?: string;\n" +
|
|
@@ -98,7 +72,7 @@ export function emitClientSurfaceModule(contract, names) {
|
|
|
98
72
|
"\t\t\t>") +
|
|
99
73
|
(interactive
|
|
100
74
|
? "\n" +
|
|
101
|
-
"/** `avClient.tx.<resource>.<family>.<variant>(args)`: a deferred handle; no request
|
|
75
|
+
"/** `avClient.tx.<resource>.<family>.<variant>(args)`: a deferred handle; no request. */\n" +
|
|
102
76
|
surface("AvTxSurface", "DeferredOperationCall<\n" +
|
|
103
77
|
"\t\t\t\tClientContractShape,\n" +
|
|
104
78
|
"\t\t\t\tR,\n" +
|
|
@@ -107,7 +81,7 @@ export function emitClientSurfaceModule(contract, names) {
|
|
|
107
81
|
"\t\t\t\tClientScalarForms\n" +
|
|
108
82
|
"\t\t\t>") +
|
|
109
83
|
"\n" +
|
|
110
|
-
"/** Sends the handles as one plan and resolves their results as a typed tuple
|
|
84
|
+
"/** Sends the handles as one plan and resolves their results as a typed tuple. */\n" +
|
|
111
85
|
"type TransactionCall = <const Steps extends readonly Operation<unknown>[]>(\n" +
|
|
112
86
|
"\tsteps: Steps,\n" +
|
|
113
87
|
"\toptions?: CallOptions,\n" +
|
|
@@ -117,12 +91,12 @@ export function emitClientSurfaceModule(contract, names) {
|
|
|
117
91
|
"/** The generated client: one property per Resource this deployment's ClientContract advertises. */\n" +
|
|
118
92
|
"export interface AvClient extends AvClientSurface {}\n" +
|
|
119
93
|
"\n" +
|
|
120
|
-
"/** The generated client
|
|
94
|
+
"/** The generated client. */\n" +
|
|
121
95
|
"export class AvClient {\n" +
|
|
122
96
|
(interactive
|
|
123
|
-
? "\t/** Deferred operations, for `transaction
|
|
97
|
+
? "\t/** Deferred operations, for `transaction`. */\n" +
|
|
124
98
|
"\tdeclare readonly tx: AvTxSurface;\n" +
|
|
125
|
-
"\t/** Runs deferred operations as one transaction
|
|
99
|
+
"\t/** Runs deferred operations as one transaction. */\n" +
|
|
126
100
|
"\tdeclare readonly transaction: TransactionCall;\n" +
|
|
127
101
|
"\n"
|
|
128
102
|
: "") +
|
|
@@ -157,7 +131,7 @@ export function emitClientSurfaceModule(contract, names) {
|
|
|
157
131
|
"\n" +
|
|
158
132
|
"/**\n" +
|
|
159
133
|
" * One frozen object per Resource, one per family under it, one member per\n" +
|
|
160
|
-
" * advertised variant
|
|
134
|
+
" * advertised variant — by wire name; the caller places each Resource.\n" +
|
|
161
135
|
" */\n" +
|
|
162
136
|
"function operationTree<M>(member: (resource: string, family: string, variant: string) => M): Map<string, object> {\n" +
|
|
163
137
|
"\tconst resources = new Map<string, Map<string, Record<string, M>>>();\n" +
|
|
@@ -179,7 +153,7 @@ export function emitClientSurfaceModule(contract, names) {
|
|
|
179
153
|
"/** One advertised operation, as the runtime calls it: the types are the surface's. */\n" +
|
|
180
154
|
"type OperationMethod = (args?: Readonly<Record<string, unknown>>, options?: CallOptions) => Promise<unknown>;\n" +
|
|
181
155
|
"\n" +
|
|
182
|
-
"/** The Resources reached by a property other than their wire name
|
|
156
|
+
"/** The Resources reached by a property other than their wire name. */\n" +
|
|
183
157
|
`const RENAMED_PROPERTIES: Readonly<Record<string, string>> = {${renamedEntries}};\n` +
|
|
184
158
|
"\n" +
|
|
185
159
|
"/** The client's property for a Resource: its wire name, unless that is one of the client's own members. */\n" +
|
|
@@ -188,14 +162,14 @@ export function emitClientSurfaceModule(contract, names) {
|
|
|
188
162
|
"}\n" +
|
|
189
163
|
"\n" +
|
|
190
164
|
"/**\n" +
|
|
191
|
-
" * The fetch a call uses
|
|
165
|
+
" * The fetch a call uses: the one the client was given, else the\n" +
|
|
192
166
|
" * platform's, read when the call is made — so importing the client never\n" +
|
|
193
167
|
" * fails where there is no fetch, and a call does, saying why.\n" +
|
|
194
168
|
" */\n" +
|
|
195
169
|
"function platformFetch(given: Fetch | undefined): Fetch {\n" +
|
|
196
170
|
"\tconst found = given ?? (globalThis as { readonly fetch?: Fetch }).fetch;\n" +
|
|
197
171
|
'\tif (typeof found !== "function") {\n' +
|
|
198
|
-
'\t\tthrow new TypeError("No fetch is available here; pass one: new AvClient({ fetch })
|
|
172
|
+
'\t\tthrow new TypeError("No fetch is available here; pass one: new AvClient({ fetch }).");\n' +
|
|
199
173
|
"\t}\n" +
|
|
200
174
|
"\treturn found;\n" +
|
|
201
175
|
"}\n" +
|
|
@@ -209,7 +183,7 @@ export function emitClientSurfaceModule(contract, names) {
|
|
|
209
183
|
"\t{ readonly family: F; readonly variant: V }\n" +
|
|
210
184
|
">;\n" +
|
|
211
185
|
"\n" +
|
|
212
|
-
"/** A Resource's default record — `find.unique` with no projection
|
|
186
|
+
"/** A Resource's default record — `find.unique` with no projection. */\n" +
|
|
213
187
|
"export type ResourceRecord<R extends Resource> = AdmittedOperationResult<\n" +
|
|
214
188
|
"\tClientContractShape,\n" +
|
|
215
189
|
"\tR,\n" +
|
|
@@ -218,7 +192,7 @@ export function emitClientSurfaceModule(contract, names) {
|
|
|
218
192
|
"\tClientScalarForms\n" +
|
|
219
193
|
">;\n" +
|
|
220
194
|
"\n" +
|
|
221
|
-
"/** One argument of one of a Resource's operations, present
|
|
195
|
+
"/** One argument of one of a Resource's operations, present. */\n" +
|
|
222
196
|
"export type ResourceArgument<\n" +
|
|
223
197
|
"\tR extends Resource,\n" +
|
|
224
198
|
"\tF extends OperationFamily,\n" +
|
|
@@ -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
|
-
}
|