@aventara/client 0.1.0-pilot.4 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +679 -0
- package/README.md +168 -132
- package/dist/cli/command.parser.d.ts +6 -4
- package/dist/cli/command.parser.js +22 -11
- package/dist/cli/generate.command.d.ts +6 -1
- package/dist/cli/generate.command.js +12 -1
- package/dist/cli/generation-failure.renderer.js +24 -24
- package/dist/cli/step.reporter.d.ts +31 -0
- package/dist/cli/step.reporter.js +87 -0
- package/dist/cli.js +2 -0
- package/dist/config/client-config.interface.d.ts +7 -7
- package/dist/config/config.loader.d.ts +4 -4
- package/dist/config/config.loader.js +7 -7
- package/dist/config/config.resolver.d.ts +7 -7
- package/dist/config/config.resolver.js +12 -12
- package/dist/config/env.cascade.d.ts +2 -2
- package/dist/config/env.cascade.js +4 -4
- package/dist/config/module-style.resolver.d.ts +1 -1
- package/dist/config/module-style.resolver.js +2 -2
- package/dist/config/tsconfig.locator.d.ts +1 -1
- package/dist/config/tsconfig.locator.js +2 -2
- package/dist/contract/contract.acceptance.d.ts +4 -4
- package/dist/contract/contract.acceptance.js +4 -4
- package/dist/contract/contract.fetcher.d.ts +3 -3
- package/dist/contract/contract.fetcher.js +6 -6
- package/dist/contract/contract.loader.d.ts +6 -6
- package/dist/contract/contract.loader.js +2 -2
- package/dist/emit/client-surface.emitter.d.ts +2 -2
- package/dist/emit/client-surface.emitter.js +77 -43
- package/dist/emit/client-tree.emitter.d.ts +3 -3
- package/dist/emit/client-tree.emitter.js +8 -5
- package/dist/emit/contract-carrier.emitter.d.ts +3 -3
- package/dist/emit/contract-carrier.emitter.js +1 -1
- package/dist/emit/derivation.emitter.js +1 -0
- package/dist/emit/descriptor.emitter.d.ts +2 -2
- package/dist/emit/descriptor.emitter.js +2 -2
- package/dist/emit/emitted-tree.interface.d.ts +1 -1
- package/dist/emit/enum.emitter.d.ts +2 -2
- package/dist/emit/field-check.emitter.d.ts +21 -0
- package/dist/emit/field-check.emitter.js +603 -0
- package/dist/emit/json-shape.emitter.d.ts +16 -0
- package/dist/emit/json-shape.emitter.js +88 -0
- package/dist/emit/name.deriver.d.ts +6 -6
- package/dist/emit/name.deriver.js +15 -15
- package/dist/emit/named-type.emitter.d.ts +2 -2
- package/dist/emit/runtime.emitter.d.ts +2 -2
- package/dist/emit/runtime.emitter.js +117 -112
- package/dist/emit/scalar.codec.d.ts +1 -1
- package/dist/emit/scalar.codec.js +78 -35
- package/dist/emit/transaction.emitter.js +131 -83
- package/dist/generate.d.ts +11 -3
- package/dist/generate.js +40 -26
- package/dist/index.d.ts +1 -1
- package/dist/init/client-init.errors.d.ts +4 -4
- package/dist/init/client-init.errors.js +4 -4
- package/dist/init/client-init.orchestrator.js +37 -22
- package/dist/init/client-init.questions.d.ts +5 -5
- package/dist/init/client-init.questions.js +10 -10
- package/dist/init/client-project.inspector.d.ts +2 -2
- package/dist/init/client-project.inspector.js +3 -3
- package/dist/init/command.runner.d.ts +2 -0
- package/dist/init/command.runner.js +4 -1
- package/dist/output/output.writer.d.ts +5 -5
- package/dist/output/output.writer.js +12 -12
- package/package.json +6 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const CONTRACT_NOT_MODIFIED = Symbol("aventara.contract-not-modified");
|
|
2
|
-
export class
|
|
2
|
+
export class AvContractTransportError extends Error {
|
|
3
3
|
reason;
|
|
4
|
-
name = "
|
|
4
|
+
name = "AvContractTransportError";
|
|
5
5
|
constructor(reason, message, options) {
|
|
6
6
|
super(message, options);
|
|
7
7
|
this.reason = reason;
|
|
@@ -29,26 +29,26 @@ export async function fetchClientContractBody(entrypoint, fetch = globalThis.fet
|
|
|
29
29
|
: { method: "GET", headers: { "If-None-Match": ifNoneMatch } });
|
|
30
30
|
}
|
|
31
31
|
catch (error) {
|
|
32
|
-
throw new
|
|
32
|
+
throw new AvContractTransportError("request-failed", `Could not fetch the ClientContract from ${shown}: ${describeCause(error)}. ${remedy}`, { cause: error });
|
|
33
33
|
}
|
|
34
34
|
if (response.status === 304 && ifNoneMatch !== undefined) {
|
|
35
35
|
return CONTRACT_NOT_MODIFIED;
|
|
36
36
|
}
|
|
37
37
|
if (!response.ok) {
|
|
38
|
-
throw new
|
|
38
|
+
throw new AvContractTransportError("http-status", `GET ${shown} answered HTTP ${response.status}, not the ClientContract. ${remedy}`);
|
|
39
39
|
}
|
|
40
40
|
let text;
|
|
41
41
|
try {
|
|
42
42
|
text = await response.text();
|
|
43
43
|
}
|
|
44
44
|
catch (error) {
|
|
45
|
-
throw new
|
|
45
|
+
throw new AvContractTransportError("body-unreadable", `The response from ${shown} could not be read: ${describeCause(error)}. ${remedy}`, { cause: error });
|
|
46
46
|
}
|
|
47
47
|
try {
|
|
48
48
|
return JSON.parse(text);
|
|
49
49
|
}
|
|
50
50
|
catch (error) {
|
|
51
|
-
throw new
|
|
51
|
+
throw new AvContractTransportError("body-not-json", `The response from ${shown} is not JSON, so it is not a ClientContract. ${remedy}`, { cause: error });
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
function describeCause(error) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvContractClient } from "@aventara/core";
|
|
2
2
|
import type { ClientEntrypoint } from "../config/client-config.interface.js";
|
|
3
3
|
import { type ContractFetch } from "./contract.fetcher.js";
|
|
4
4
|
export interface ClientContractLoadInput {
|
|
@@ -6,12 +6,12 @@ export interface ClientContractLoadInput {
|
|
|
6
6
|
readonly fetch?: ContractFetch;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
* @throws
|
|
10
|
-
* @throws
|
|
9
|
+
* @throws AvContractTransportError when no JSON value arrives.
|
|
10
|
+
* @throws AvContractProtocolError when what arrived is refused.
|
|
11
11
|
*/
|
|
12
|
-
export declare function loadClientContract(input: ClientContractLoadInput): Promise<
|
|
12
|
+
export declare function loadClientContract(input: ClientContractLoadInput): Promise<AvContractClient>;
|
|
13
13
|
export interface ClientContractSince {
|
|
14
|
-
readonly contract:
|
|
14
|
+
readonly contract: AvContractClient;
|
|
15
15
|
/** The deployment answered `304`: `contract` is the one the caller held. */
|
|
16
16
|
readonly notModified: boolean;
|
|
17
17
|
}
|
|
@@ -21,4 +21,4 @@ export interface ClientContractSince {
|
|
|
21
21
|
* `If-None-Match: "<its hash>"`, and a `304` answers with `stored` itself. Without
|
|
22
22
|
* `stored` the GET is unconditional.
|
|
23
23
|
*/
|
|
24
|
-
export declare function loadClientContractSince(input: ClientContractLoadInput, stored:
|
|
24
|
+
export declare function loadClientContractSince(input: ClientContractLoadInput, stored: AvContractClient | undefined): Promise<ClientContractSince>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AvContractProtocolError, acceptClientContract, } from "./contract.acceptance.js";
|
|
2
2
|
import { CONTRACT_NOT_MODIFIED, contractUrlOf, displayUrl, fetchClientContractBody, } from "./contract.fetcher.js";
|
|
3
3
|
export async function loadClientContract(input) {
|
|
4
4
|
return accepted(await fetchClientContractBody(input.entrypoint, input.fetch), input);
|
|
@@ -16,7 +16,7 @@ export async function loadClientContractSince(input, stored) {
|
|
|
16
16
|
async function accepted(body, input) {
|
|
17
17
|
const acceptance = await acceptClientContract(body);
|
|
18
18
|
if (!acceptance.accepted) {
|
|
19
|
-
throw new
|
|
19
|
+
throw new AvContractProtocolError(acceptance, displayUrl(contractUrlOf(input.entrypoint)));
|
|
20
20
|
}
|
|
21
21
|
return acceptance.contract;
|
|
22
22
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvContractClient } from "@aventara/core";
|
|
2
2
|
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
3
3
|
import { type ClientModuleStyle } from "./module-style.interface.js";
|
|
4
4
|
import { type EmittedNames } from "./name.deriver.js";
|
|
5
|
-
export declare function emitClientSurfaceModule(contract:
|
|
5
|
+
export declare function emitClientSurfaceModule(contract: AvContractClient, names: EmittedNames, style: ClientModuleStyle): EmittedModule;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { contractHasFieldChecks } from "./field-check.emitter.js";
|
|
2
|
+
import { shapedContractType } from "./json-shape.emitter.js";
|
|
1
3
|
import { importSpecifier, } from "./module-style.interface.js";
|
|
2
4
|
import { ownPropertyKey } from "./name.deriver.js";
|
|
3
5
|
export function emitClientSurfaceModule(contract, names, style) {
|
|
@@ -10,6 +12,9 @@ export function emitClientSurfaceModule(contract, names, style) {
|
|
|
10
12
|
.join("")}R`;
|
|
11
13
|
const resourceKey = renamed.length === 0 ? "R in Resource" : `R in Resource as ${propertyOf}`;
|
|
12
14
|
const interactive = contract.transactions === "interactive";
|
|
15
|
+
const checked = contractHasFieldChecks(contract);
|
|
16
|
+
const shaped = shapedContractType(contract);
|
|
17
|
+
const C = shaped === undefined ? "ClientContractShape" : "ShapedContract";
|
|
13
18
|
const renamedEntries = renamed.length === 0
|
|
14
19
|
? ""
|
|
15
20
|
: ` ${renamed.map((property) => `${ownPropertyKey(property.contractName)}: ${JSON.stringify(property.property)}`).join(", ")} `;
|
|
@@ -23,75 +28,97 @@ export function emitClientSurfaceModule(contract, names, style) {
|
|
|
23
28
|
return {
|
|
24
29
|
path: "client.ts",
|
|
25
30
|
source: `import type { ClientContractShape } from ${from("./contract")};\n` +
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
`import type {
|
|
31
|
+
(shaped === undefined
|
|
32
|
+
? ""
|
|
33
|
+
: `import type { JsonFieldType } from ${from("./derivation/contracts/json-shape")};\n`) +
|
|
34
|
+
`import type { AvApplicationScalarForms } from ${from("./derivation/contracts/scalar-value-type")};\n` +
|
|
35
|
+
`import type { AvOperationArgumentsFor, AvResourceKey } from ${from("./derivation/operations/operation-arguments")};\n` +
|
|
36
|
+
`import type { ${interactive ? "AvDeferredOperationCall, " : ""}AvOperationCall, AvOperationGrammar } from ${from("./derivation/operations/operation-call")};\n` +
|
|
37
|
+
`import type { AvOperationFamily, AvOperationIdentity } from ${from("./derivation/operations/operation-identity")};\n` +
|
|
30
38
|
`import type { AdmittedOperationResult } from ${from("./derivation/operations/operation-result")};\n` +
|
|
31
39
|
(interactive
|
|
32
|
-
? `import type {
|
|
40
|
+
? `import type { AvOperation, AvTransactionResults } from ${from("./derivation/transactions/deferred-operation")};\n`
|
|
33
41
|
: "") +
|
|
34
42
|
`import { DEFAULT_ENTRYPOINT } from ${from("./metadata")};\n` +
|
|
35
|
-
`import type {
|
|
43
|
+
`import type { AvDecimal } from ${from("./runtime/decimal")};\n` +
|
|
36
44
|
`import { ADVERTISED_OPERATIONS } from ${from("./runtime/descriptor")};\n` +
|
|
45
|
+
(checked
|
|
46
|
+
? `import { ${interactive ? "checkOperation, checkPlan" : "checkOperation"} } from ${from("./runtime/field-validators")};\n`
|
|
47
|
+
: "") +
|
|
37
48
|
(interactive
|
|
38
49
|
? `import { deferOperation, runTransaction } from ${from("./runtime/transaction")};\n`
|
|
39
50
|
: "") +
|
|
40
|
-
`import { type
|
|
51
|
+
`import { type AvCallOptions, execute, type AvFetch, type TransportConnection } from ${from("./runtime/transport")};\n` +
|
|
41
52
|
"\n" +
|
|
42
|
-
`export type {
|
|
53
|
+
`export type { AvCallOptions } from ${from("./runtime/transport")};\n` +
|
|
43
54
|
"\n" +
|
|
44
55
|
"/**\n" +
|
|
45
|
-
" * The forms this client reads and writes: core's application forms
|
|
46
|
-
" * `bigint`, `Date`, `Uint8Array`
|
|
56
|
+
" * The forms this client reads and writes: core's application forms\n" +
|
|
57
|
+
" * (`bigint`, `Date`, `Uint8Array`) with this client's own `AvDecimal`.\n" +
|
|
47
58
|
" */\n" +
|
|
48
|
-
'export type ClientScalarForms = Omit<
|
|
49
|
-
"\treadonly decimal:
|
|
59
|
+
'export type ClientScalarForms = Omit<AvApplicationScalarForms, "decimal"> & {\n' +
|
|
60
|
+
"\treadonly decimal: AvDecimal;\n" +
|
|
50
61
|
"};\n" +
|
|
51
62
|
"\n" +
|
|
52
63
|
"/** How an `AvClient` reaches its deployment. */\n" +
|
|
53
64
|
"export interface AvClientOptions {\n" +
|
|
54
65
|
"\t/** Another deployment serving exactly the same ClientContract; the generated default otherwise. */\n" +
|
|
55
66
|
"\treadonly entrypoint?: string;\n" +
|
|
56
|
-
"\t/** A custom fetch
|
|
57
|
-
"\treadonly fetch?:
|
|
67
|
+
"\t/** A custom fetch, for authentication or tests; the platform's otherwise. */\n" +
|
|
68
|
+
"\treadonly fetch?: AvFetch;\n" +
|
|
58
69
|
"}\n" +
|
|
59
70
|
"\n" +
|
|
60
|
-
|
|
61
|
-
|
|
71
|
+
(shaped === undefined
|
|
72
|
+
? ""
|
|
73
|
+
: "/** The ClientContract, with each shaped Json field's own type. */\n" +
|
|
74
|
+
`type ShapedContract = ${shaped};\n` +
|
|
75
|
+
"\n") +
|
|
76
|
+
`type Grammar = AvOperationGrammar<${C}>;\n` +
|
|
77
|
+
`type Resource = AvResourceKey<${C}>;\n` +
|
|
62
78
|
"\n" +
|
|
63
79
|
"/** `avClient.<resource>.<family>.<variant>(args, options?)`: one call per advertised operation. */\n" +
|
|
64
|
-
surface("AvClientSurface", "
|
|
65
|
-
|
|
80
|
+
surface("AvClientSurface", "AvOperationCall<\n" +
|
|
81
|
+
`\t\t\t\t${C},\n` +
|
|
66
82
|
"\t\t\t\tR,\n" +
|
|
67
|
-
"\t\t\t\tGrammar[R][F][V] &
|
|
83
|
+
"\t\t\t\tGrammar[R][F][V] & AvOperationIdentity,\n" +
|
|
68
84
|
"\t\t\t\tClientScalarForms,\n" +
|
|
69
85
|
"\t\t\t\tClientScalarForms,\n" +
|
|
70
|
-
|
|
71
|
-
"\t\t\t\tCallOptions\n" +
|
|
86
|
+
"\t\t\t\tAvCallOptions\n" +
|
|
72
87
|
"\t\t\t>") +
|
|
73
88
|
(interactive
|
|
74
89
|
? "\n" +
|
|
75
90
|
"/** `avClient.tx.<resource>.<family>.<variant>(args)`: a deferred handle; no request. */\n" +
|
|
76
|
-
surface("AvTxSurface", "
|
|
77
|
-
|
|
91
|
+
surface("AvTxSurface", "AvDeferredOperationCall<\n" +
|
|
92
|
+
`\t\t\t\t${C},\n` +
|
|
78
93
|
"\t\t\t\tR,\n" +
|
|
79
|
-
"\t\t\t\tGrammar[R][F][V] &
|
|
94
|
+
"\t\t\t\tGrammar[R][F][V] & AvOperationIdentity,\n" +
|
|
80
95
|
"\t\t\t\tClientScalarForms,\n" +
|
|
81
96
|
"\t\t\t\tClientScalarForms\n" +
|
|
82
97
|
"\t\t\t>") +
|
|
83
98
|
"\n" +
|
|
84
99
|
"/** Sends the handles as one plan and resolves their results as a typed tuple. */\n" +
|
|
85
|
-
"type TransactionCall = <const Steps extends readonly
|
|
100
|
+
"type TransactionCall = <const Steps extends readonly AvOperation<unknown>[]>(\n" +
|
|
86
101
|
"\tsteps: Steps,\n" +
|
|
87
|
-
"\toptions?:
|
|
88
|
-
") => Promise<
|
|
102
|
+
"\toptions?: AvCallOptions,\n" +
|
|
103
|
+
") => Promise<AvTransactionResults<Steps>>;\n"
|
|
89
104
|
: "") +
|
|
90
105
|
"\n" +
|
|
91
106
|
"/** The generated client: one property per Resource this deployment's ClientContract advertises. */\n" +
|
|
92
107
|
"export interface AvClient extends AvClientSurface {}\n" +
|
|
93
108
|
"\n" +
|
|
94
|
-
|
|
109
|
+
(checked
|
|
110
|
+
? "/**\n" +
|
|
111
|
+
" * The generated client.\n" +
|
|
112
|
+
" *\n" +
|
|
113
|
+
" * A call runs the ClientContract's field validators before it is sent: one\n" +
|
|
114
|
+
" * whose values break a validator, or a check of a shaped Json field, is\n" +
|
|
115
|
+
" * refused with the AvValidationError the server would answer (A2004, with\n" +
|
|
116
|
+
" * the same issues), and nothing is sent. The client checks each value as\n" +
|
|
117
|
+
" * written; the server checks it again after its field pipes, so a field pipe\n" +
|
|
118
|
+
" * that normalizes a value must not turn one the client accepted into one the\n" +
|
|
119
|
+
" * server refuses. A $ref value in a transaction is the server's to check.\n" +
|
|
120
|
+
" */\n"
|
|
121
|
+
: "/** The generated client. */\n") +
|
|
95
122
|
"export class AvClient {\n" +
|
|
96
123
|
(interactive
|
|
97
124
|
? "\t/** Deferred operations, for `transaction`. */\n" +
|
|
@@ -106,8 +133,13 @@ export function emitClientSurfaceModule(contract, names, style) {
|
|
|
106
133
|
"\t\tconst connect = (): TransportConnection => ({ entrypoint, fetch: platformFetch(given) });\n" +
|
|
107
134
|
"\t\tfor (const [resource, families] of operationTree(\n" +
|
|
108
135
|
"\t\t\t(resource, family, variant): OperationMethod =>\n" +
|
|
109
|
-
|
|
110
|
-
|
|
136
|
+
(checked
|
|
137
|
+
? "\t\t\t\tasync (args = {}, callOptions = {}) => {\n" +
|
|
138
|
+
"\t\t\t\t\tcheckOperation(resource, family, args);\n" +
|
|
139
|
+
"\t\t\t\t\treturn execute(connect(), resource, family, variant, args, callOptions);\n" +
|
|
140
|
+
"\t\t\t\t},\n"
|
|
141
|
+
: "\t\t\t\tasync (args = {}, callOptions = {}) =>\n" +
|
|
142
|
+
"\t\t\t\t\texecute(connect(), resource, family, variant, args, callOptions),\n") +
|
|
111
143
|
"\t\t)) {\n" +
|
|
112
144
|
"\t\t\tObject.defineProperty(this, propertyOf(resource), { value: families, enumerable: true });\n" +
|
|
113
145
|
"\t\t}\n" +
|
|
@@ -122,8 +154,10 @@ export function emitClientSurfaceModule(contract, names, style) {
|
|
|
122
154
|
"\t\t}\n" +
|
|
123
155
|
'\t\tObject.defineProperty(this, "tx", { value: Object.freeze(deferred) });\n' +
|
|
124
156
|
'\t\tObject.defineProperty(this, "transaction", {\n' +
|
|
125
|
-
"\t\t\tvalue: async (steps: unknown, callOptions:
|
|
126
|
-
|
|
157
|
+
"\t\t\tvalue: async (steps: unknown, callOptions: AvCallOptions = {}) =>\n" +
|
|
158
|
+
(checked
|
|
159
|
+
? "\t\t\t\trunTransaction(connect(), steps, callOptions, checkPlan),\n"
|
|
160
|
+
: "\t\t\t\trunTransaction(connect(), steps, callOptions),\n") +
|
|
127
161
|
"\t\t});\n"
|
|
128
162
|
: "") +
|
|
129
163
|
"\t}\n" +
|
|
@@ -131,7 +165,7 @@ export function emitClientSurfaceModule(contract, names, style) {
|
|
|
131
165
|
"\n" +
|
|
132
166
|
"/**\n" +
|
|
133
167
|
" * One frozen object per Resource, one per family under it, one member per\n" +
|
|
134
|
-
" * advertised variant
|
|
168
|
+
" * advertised variant, by wire name; the caller places each Resource.\n" +
|
|
135
169
|
" */\n" +
|
|
136
170
|
"function operationTree<M>(member: (resource: string, family: string, variant: string) => M): Map<string, object> {\n" +
|
|
137
171
|
"\tconst resources = new Map<string, Map<string, Record<string, M>>>();\n" +
|
|
@@ -151,7 +185,7 @@ export function emitClientSurfaceModule(contract, names, style) {
|
|
|
151
185
|
"}\n" +
|
|
152
186
|
"\n" +
|
|
153
187
|
"/** One advertised operation, as the runtime calls it: the types are the surface's. */\n" +
|
|
154
|
-
"type OperationMethod = (args?: Readonly<Record<string, unknown>>, options?:
|
|
188
|
+
"type OperationMethod = (args?: Readonly<Record<string, unknown>>, options?: AvCallOptions) => Promise<unknown>;\n" +
|
|
155
189
|
"\n" +
|
|
156
190
|
"/** The Resources reached by a property other than their wire name. */\n" +
|
|
157
191
|
`const RENAMED_PROPERTIES: Readonly<Record<string, string>> = {${renamedEntries}};\n` +
|
|
@@ -163,11 +197,11 @@ export function emitClientSurfaceModule(contract, names, style) {
|
|
|
163
197
|
"\n" +
|
|
164
198
|
"/**\n" +
|
|
165
199
|
" * The fetch a call uses: the one the client was given, else the\n" +
|
|
166
|
-
" * platform's, read when the call is made
|
|
200
|
+
" * platform's, read when the call is made. Importing the client never\n" +
|
|
167
201
|
" * fails where there is no fetch, and a call does, saying why.\n" +
|
|
168
202
|
" */\n" +
|
|
169
|
-
"function platformFetch(given:
|
|
170
|
-
"\tconst found = given ?? (globalThis as { readonly fetch?:
|
|
203
|
+
"function platformFetch(given: AvFetch | undefined): AvFetch {\n" +
|
|
204
|
+
"\tconst found = given ?? (globalThis as { readonly fetch?: AvFetch }).fetch;\n" +
|
|
171
205
|
'\tif (typeof found !== "function") {\n' +
|
|
172
206
|
'\t\tthrow new TypeError("No fetch is available here; pass one: new AvClient({ fetch }).");\n' +
|
|
173
207
|
"\t}\n" +
|
|
@@ -178,14 +212,14 @@ export function emitClientSurfaceModule(contract, names, style) {
|
|
|
178
212
|
"const avClient: AvClient = new AvClient();\n" +
|
|
179
213
|
"export default avClient;\n" +
|
|
180
214
|
"\n" +
|
|
181
|
-
"type Identity<F extends
|
|
182
|
-
"\
|
|
215
|
+
"type Identity<F extends AvOperationFamily, V extends string> = Extract<\n" +
|
|
216
|
+
"\tAvOperationIdentity,\n" +
|
|
183
217
|
"\t{ readonly family: F; readonly variant: V }\n" +
|
|
184
218
|
">;\n" +
|
|
185
219
|
"\n" +
|
|
186
|
-
"/** A Resource's default record
|
|
220
|
+
"/** A Resource's default record: `find.unique` with no projection. */\n" +
|
|
187
221
|
"export type ResourceRecord<R extends Resource> = AdmittedOperationResult<\n" +
|
|
188
|
-
|
|
222
|
+
`\t${C},\n` +
|
|
189
223
|
"\tR,\n" +
|
|
190
224
|
'\tIdentity<"find", "unique">,\n' +
|
|
191
225
|
"\tunknown,\n" +
|
|
@@ -195,11 +229,11 @@ export function emitClientSurfaceModule(contract, names, style) {
|
|
|
195
229
|
"/** One argument of one of a Resource's operations, present. */\n" +
|
|
196
230
|
"export type ResourceArgument<\n" +
|
|
197
231
|
"\tR extends Resource,\n" +
|
|
198
|
-
"\tF extends
|
|
232
|
+
"\tF extends AvOperationFamily,\n" +
|
|
199
233
|
"\tV extends string,\n" +
|
|
200
234
|
"\tK extends string,\n" +
|
|
201
235
|
"> = ArgumentOf<\n" +
|
|
202
|
-
|
|
236
|
+
`\tAvOperationArgumentsFor<${C}, R, Identity<F, V>, ClientScalarForms>,\n` +
|
|
203
237
|
"\tK\n" +
|
|
204
238
|
">;\n" +
|
|
205
239
|
"\n" +
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvContractClient } 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
4
|
import { type ClientModuleStyle } from "./module-style.interface.js";
|
|
5
5
|
/**
|
|
6
|
-
* @throws
|
|
6
|
+
* @throws AvGeneratedNameError when a name cannot be emitted, even renamed.
|
|
7
7
|
*/
|
|
8
|
-
export declare function emitClientTree(contract:
|
|
8
|
+
export declare function emitClientTree(contract: AvContractClient, entrypoint: ClientEntrypoint, style: ClientModuleStyle): ClientEmission;
|
|
@@ -5,6 +5,7 @@ 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 { emitFieldCheckModules } from "./field-check.emitter.js";
|
|
8
9
|
import { moduleSpecifierWriter, } from "./module-style.interface.js";
|
|
9
10
|
import { deriveEmittedNames } from "./name.deriver.js";
|
|
10
11
|
import { emitNamedTypesModule } from "./named-type.emitter.js";
|
|
@@ -13,8 +14,9 @@ import { emitScalarCodecModule } from "./scalar.codec.js";
|
|
|
13
14
|
import { emitTransactionModules } from "./transaction.emitter.js";
|
|
14
15
|
export function emitClientTree(contract, entrypoint, style) {
|
|
15
16
|
const names = deriveEmittedNames(contract);
|
|
17
|
+
const derivation = emitDerivationModules();
|
|
16
18
|
const generated = [
|
|
17
|
-
...
|
|
19
|
+
...derivation,
|
|
18
20
|
emitClientSurfaceModule(contract, names, style),
|
|
19
21
|
emitContractCarrierModule(contract),
|
|
20
22
|
emitDescriptorModule(contract),
|
|
@@ -28,6 +30,7 @@ export function emitClientTree(contract, entrypoint, style) {
|
|
|
28
30
|
...(contract.transactions === "interactive"
|
|
29
31
|
? emitTransactionModules(style)
|
|
30
32
|
: []),
|
|
33
|
+
...emitFieldCheckModules(contract, style, new Set(derivation.map((module) => module.path))),
|
|
31
34
|
];
|
|
32
35
|
const files = [
|
|
33
36
|
{ path: CLIENT_ENTRY_FILE, source: clientEntrySource(style) },
|
|
@@ -49,12 +52,12 @@ function clientEntrySource(style) {
|
|
|
49
52
|
const specifier = moduleSpecifierWriter(style);
|
|
50
53
|
const from = (module) => specifier(`./${GENERATED_DIRECTORY}/${module.replace(/\.d\.ts$|\.ts$/, "")}`);
|
|
51
54
|
return (`export { default } from ${from("client.ts")};\n` +
|
|
52
|
-
`export {\n\tAvClient,\n\ttype AvClientOptions,\n\ttype
|
|
53
|
-
`export type {
|
|
54
|
-
`export type {
|
|
55
|
+
`export {\n\tAvClient,\n\ttype AvClientOptions,\n\ttype AvCallOptions,\n} from ${from("client.ts")};\n` +
|
|
56
|
+
`export type { AvFetch } from ${from("runtime/transport.ts")};\n` +
|
|
57
|
+
`export type { AvOperation } from ${from("derivation/transactions/deferred-operation.d.ts")};\n` +
|
|
55
58
|
`export type * from ${from("types.d.ts")};\n` +
|
|
56
59
|
`export * from ${from("enums.ts")};\n` +
|
|
57
|
-
`export {
|
|
60
|
+
`export { AvDecimal } from ${from("runtime/decimal.ts")};\n` +
|
|
58
61
|
`export {\n${errorsModuleExports()
|
|
59
62
|
.map((name) => `\t${name},\n`)
|
|
60
63
|
.join("")}} from ${from("runtime/errors.ts")};\n`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AvContractClient } from "@aventara/core";
|
|
2
2
|
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
3
3
|
/** `generated/contract.ts`, before the banner. */
|
|
4
|
-
export declare function emitContractCarrierModule(contract:
|
|
5
|
-
export declare function parseContractCarrier(text: string): Promise<
|
|
4
|
+
export declare function emitContractCarrierModule(contract: AvContractClient): EmittedModule;
|
|
5
|
+
export declare function parseContractCarrier(text: string): Promise<AvContractClient | undefined>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { canonicalizeContract } from "@aventara/core";
|
|
1
|
+
import { canonicalizeContract } from "@aventara/core/internal";
|
|
2
2
|
import { acceptClientContract } from "../contract/contract.acceptance.js";
|
|
3
3
|
import { withGeneratedBanner } from "./banner.emitter.js";
|
|
4
4
|
const CARRIER_PREFIX = "export type ClientContractShape = ";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AvContractClient } from "@aventara/core";
|
|
2
2
|
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
3
3
|
/** `generated/runtime/descriptor.ts`, before the banner. */
|
|
4
|
-
export declare function emitDescriptorModule(contract:
|
|
4
|
+
export declare function emitDescriptorModule(contract: AvContractClient): EmittedModule;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isOperationVariantAvailable
|
|
1
|
+
import { isOperationVariantAvailable } from "@aventara/core/internal";
|
|
2
2
|
import { ownPropertyKey } from "./name.deriver.js";
|
|
3
3
|
const REVIVED_SCALARS = new Set([
|
|
4
4
|
"bigint",
|
|
@@ -31,7 +31,7 @@ export function emitDescriptorModule(contract) {
|
|
|
31
31
|
path: "runtime/descriptor.ts",
|
|
32
32
|
source: "/**\n" +
|
|
33
33
|
" * A result field the decoder revives from its wire string, or a relation\n" +
|
|
34
|
-
" * it recurses into, read as the relation's target Resource
|
|
34
|
+
" * it recurses into, read as the relation's target Resource; `many` says the\n" +
|
|
35
35
|
" * value is a list, `{ data, count }` or `{ count }` rather than one record.\n" +
|
|
36
36
|
" */\n" +
|
|
37
37
|
"export type FieldDecoding =\n" +
|
|
@@ -8,7 +8,7 @@ export declare const LEGACY_CLIENT_ENTRY_FILES: readonly string[];
|
|
|
8
8
|
/** The directory holding every other emitted module, under `generateAt`. */
|
|
9
9
|
export declare const GENERATED_DIRECTORY = "generated";
|
|
10
10
|
export type DerivationModulePath = `derivation/${string}.d.ts`;
|
|
11
|
-
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";
|
|
11
|
+
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/field-check.evaluator.ts" | "runtime/field-validators.ts" | "runtime/fingerprint.ts" | "runtime/transaction.ts" | "runtime/transport.ts" | "types.d.ts";
|
|
12
12
|
export declare const CONTRACT_CARRIER_MODULE = "contract.ts";
|
|
13
13
|
/** A path in the emitted tree: relative to `generateAt`, POSIX-separated. */
|
|
14
14
|
export type EmittedFilePath = typeof CLIENT_ENTRY_FILE | `${typeof GENERATED_DIRECTORY}/${GeneratedModulePath}`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvContractClient } from "@aventara/core";
|
|
2
2
|
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
3
3
|
import { type EmittedNames } from "./name.deriver.js";
|
|
4
|
-
export declare function emitEnumsModule(contract: Pick<
|
|
4
|
+
export declare function emitEnumsModule(contract: Pick<AvContractClient, "enums">, names: EmittedNames): EmittedModule;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AvContractClient } from "@aventara/core";
|
|
2
|
+
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
3
|
+
import { type ClientModuleStyle } from "./module-style.interface.js";
|
|
4
|
+
/** Where `@aventara/core` publishes the evaluator's source, relative to its package root. */
|
|
5
|
+
export declare const EVALUATOR_SOURCE = "src/runtime/field-check.evaluator.ts";
|
|
6
|
+
/** Reads the evaluator's published source; `undefined` when core publishes none. */
|
|
7
|
+
export type EvaluatorSourceReader = () => string | undefined;
|
|
8
|
+
/** The evaluator's source as the `@aventara/core` this package resolves publishes it. */
|
|
9
|
+
export declare function publishedCoreEvaluator(): EvaluatorSourceReader;
|
|
10
|
+
/** Whether the ClientContract carries any check: a field's validators or a Json Shape node's. */
|
|
11
|
+
export declare function contractHasFieldChecks(contract: AvContractClient): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* The two field check modules, or none when the contract carries no check.
|
|
14
|
+
*
|
|
15
|
+
* @param derivation the declaration paths the tree copies under `derivation/`
|
|
16
|
+
* (`derivation/contracts/field-check.d.ts`, …): the evaluator's type imports
|
|
17
|
+
* must name one of them.
|
|
18
|
+
* @throws Error when the evaluator cannot be copied — a defect of the installed
|
|
19
|
+
* `@aventara/core`, never of the consumer's input.
|
|
20
|
+
*/
|
|
21
|
+
export declare function emitFieldCheckModules(contract: AvContractClient, style: ClientModuleStyle, derivation: ReadonlySet<string>, read?: EvaluatorSourceReader): readonly EmittedModule[];
|