@aventara/client 0.0.0-stage → 0.1.0-pilot.0
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/LICENSE +91 -0
- package/LICENSE-ADDITIONAL-PERMISSION.md +9 -0
- package/README.md +268 -2
- package/dist/avclient.bin.d.ts +2 -0
- package/dist/avclient.bin.js +15 -0
- package/dist/cli/command.parser.d.ts +30 -0
- package/dist/cli/command.parser.js +132 -0
- package/dist/cli/generate.command.d.ts +24 -0
- package/dist/cli/generate.command.js +41 -0
- package/dist/cli/generation-failure.renderer.d.ts +6 -0
- package/dist/cli/generation-failure.renderer.js +54 -0
- package/dist/cli/generation-success.renderer.d.ts +32 -0
- package/dist/cli/generation-success.renderer.js +47 -0
- package/dist/cli/terminal.prompter.d.ts +13 -0
- package/dist/cli/terminal.prompter.js +53 -0
- package/dist/cli/warning.renderer.d.ts +10 -0
- package/dist/cli/warning.renderer.js +14 -0
- package/dist/cli.d.ts +29 -0
- package/dist/cli.js +71 -0
- package/dist/config/client-config.interface.d.ts +62 -0
- package/dist/config/client-config.interface.js +14 -0
- package/dist/config/config.loader.d.ts +33 -0
- package/dist/config/config.loader.js +80 -0
- package/dist/config/config.resolver.d.ts +50 -0
- package/dist/config/config.resolver.js +126 -0
- package/dist/config/env.cascade.d.ts +84 -0
- package/dist/config/env.cascade.js +126 -0
- package/dist/contract/contract.acceptance.d.ts +77 -0
- package/dist/contract/contract.acceptance.js +124 -0
- package/dist/contract/contract.fetcher.d.ts +64 -0
- package/dist/contract/contract.fetcher.js +85 -0
- package/dist/contract/contract.loader.d.ts +32 -0
- package/dist/contract/contract.loader.js +32 -0
- package/dist/emit/banner.emitter.d.ts +31 -0
- package/dist/emit/banner.emitter.js +42 -0
- package/dist/emit/client-surface.emitter.d.ts +32 -0
- package/dist/emit/client-surface.emitter.js +236 -0
- package/dist/emit/client-tree.emitter.d.ts +37 -0
- package/dist/emit/client-tree.emitter.js +103 -0
- package/dist/emit/contract-carrier.emitter.d.ts +13 -0
- package/dist/emit/contract-carrier.emitter.js +60 -0
- package/dist/emit/derivation.emitter.d.ts +45 -0
- package/dist/emit/derivation.emitter.js +233 -0
- package/dist/emit/descriptor.emitter.d.ts +4 -0
- package/dist/emit/descriptor.emitter.js +97 -0
- package/dist/emit/emitted-tree.interface.d.ts +61 -0
- package/dist/emit/emitted-tree.interface.js +18 -0
- package/dist/emit/enum.emitter.d.ts +24 -0
- package/dist/emit/enum.emitter.js +42 -0
- package/dist/emit/name.deriver.d.ts +153 -0
- package/dist/emit/name.deriver.js +411 -0
- package/dist/emit/named-type.emitter.d.ts +32 -0
- package/dist/emit/named-type.emitter.js +50 -0
- package/dist/emit/runtime.emitter.d.ts +87 -0
- package/dist/emit/runtime.emitter.js +707 -0
- package/dist/emit/scalar.codec.d.ts +63 -0
- package/dist/emit/scalar.codec.js +498 -0
- package/dist/emit/transaction.emitter.d.ts +17 -0
- package/dist/emit/transaction.emitter.js +438 -0
- package/dist/generate.d.ts +123 -0
- package/dist/generate.js +98 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/init/client-config.template.d.ts +6 -0
- package/dist/init/client-config.template.js +22 -0
- package/dist/init/client-init.errors.d.ts +9 -0
- package/dist/init/client-init.errors.js +9 -0
- package/dist/init/client-init.orchestrator.d.ts +3 -0
- package/dist/init/client-init.orchestrator.js +82 -0
- package/dist/init/client-init.planner.d.ts +26 -0
- package/dist/init/client-init.planner.js +88 -0
- package/dist/init/client-init.questions.d.ts +52 -0
- package/dist/init/client-init.questions.js +124 -0
- package/dist/init/client-project.inspector.d.ts +15 -0
- package/dist/init/client-project.inspector.js +32 -0
- package/dist/init/command.runner.d.ts +8 -0
- package/dist/init/command.runner.js +17 -0
- package/dist/node-version.guard.d.ts +8 -0
- package/dist/node-version.guard.js +59 -0
- package/dist/output/output.validator.d.ts +76 -0
- package/dist/output/output.validator.js +254 -0
- package/dist/output/output.writer.d.ts +162 -0
- package/dist/output/output.writer.js +499 -0
- package/package.json +47 -3
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a conditional GET answers when the served ClientContract is the one named
|
|
3
|
+
* (`304 Not Modified`, Phase 10 Q8): no body, nothing to judge.
|
|
4
|
+
*/
|
|
5
|
+
export const CONTRACT_NOT_MODIFIED = Symbol("aventara.contract-not-modified");
|
|
6
|
+
export class ContractTransportError extends Error {
|
|
7
|
+
reason;
|
|
8
|
+
name = "ContractTransportError";
|
|
9
|
+
constructor(reason, message, options) {
|
|
10
|
+
super(message, options);
|
|
11
|
+
this.reason = reason;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* `<entrypoint>/_contract`: the canonical mount path and the route, concatenated.
|
|
16
|
+
* The path's canonical form is resolution's (F-822) — root is `""` — so the join
|
|
17
|
+
* repairs nothing and special-cases nothing. URL resolution would be wrong here:
|
|
18
|
+
* `new URL("_contract", "https://h/api")` replaces `api`.
|
|
19
|
+
*/
|
|
20
|
+
export function contractUrlOf(entrypoint) {
|
|
21
|
+
const url = new URL(entrypoint.deployment.href);
|
|
22
|
+
url.pathname = `${entrypoint.path}/_contract`;
|
|
23
|
+
return url;
|
|
24
|
+
}
|
|
25
|
+
/** The URL as it may be printed: credentials in the entrypoint never are. */
|
|
26
|
+
export function displayUrl(url) {
|
|
27
|
+
const shown = new URL(url.href);
|
|
28
|
+
shown.username = "";
|
|
29
|
+
shown.password = "";
|
|
30
|
+
return shown.href;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* GETs the ClientContract and returns the parsed body, unjudged — or, when
|
|
34
|
+
* `ifNoneMatch` (a quoted contract hash, the deployment's entity tag) is sent and
|
|
35
|
+
* the deployment answers `304`, {@link CONTRACT_NOT_MODIFIED} (Phase 12-rest Q6).
|
|
36
|
+
* A `304` to a request that sent none is no answer, like any other non-2xx.
|
|
37
|
+
*
|
|
38
|
+
* @throws ContractTransportError when no JSON value arrives.
|
|
39
|
+
*/
|
|
40
|
+
export async function fetchClientContractBody(entrypoint, fetch = globalThis.fetch, ifNoneMatch) {
|
|
41
|
+
const url = contractUrlOf(entrypoint);
|
|
42
|
+
const shown = displayUrl(url);
|
|
43
|
+
const remedy = "Check that the deployment is running and that the entrypoint is its origin plus mount path.";
|
|
44
|
+
let response;
|
|
45
|
+
try {
|
|
46
|
+
response = await fetch(url, ifNoneMatch === undefined
|
|
47
|
+
? { method: "GET" }
|
|
48
|
+
: { method: "GET", headers: { "If-None-Match": ifNoneMatch } });
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new ContractTransportError("request-failed", `Could not fetch the ClientContract from ${shown}: ${describeCause(error)}. ${remedy}`, { cause: error });
|
|
52
|
+
}
|
|
53
|
+
if (response.status === 304 && ifNoneMatch !== undefined) {
|
|
54
|
+
return CONTRACT_NOT_MODIFIED;
|
|
55
|
+
}
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
throw new ContractTransportError("http-status", `GET ${shown} answered HTTP ${response.status}, not the ClientContract. ${remedy}`);
|
|
58
|
+
}
|
|
59
|
+
let text;
|
|
60
|
+
try {
|
|
61
|
+
text = await response.text();
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
throw new ContractTransportError("body-unreadable", `The response from ${shown} could not be read: ${describeCause(error)}. ${remedy}`, { cause: error });
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(text);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
// The body itself is not printed: it may be a whole HTML page.
|
|
71
|
+
throw new ContractTransportError("body-not-json", `The response from ${shown} is not JSON, so it is not a ClientContract. ${remedy}`, { cause: error });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A failure's own words and, for Node's `fetch failed`, its cause's — the part
|
|
76
|
+
* that says ECONNREFUSED or ENOTFOUND. Messages only, never a stack.
|
|
77
|
+
*/
|
|
78
|
+
function describeCause(error) {
|
|
79
|
+
if (!(error instanceof Error)) {
|
|
80
|
+
return String(error);
|
|
81
|
+
}
|
|
82
|
+
return error.cause instanceof Error
|
|
83
|
+
? `${error.message} (${error.cause.message})`
|
|
84
|
+
: error.message;
|
|
85
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ClientContract } from "@aventara/core";
|
|
2
|
+
import type { ClientEntrypoint } from "../config/client-config.interface.js";
|
|
3
|
+
import { type ContractFetch } from "./contract.fetcher.js";
|
|
4
|
+
/**
|
|
5
|
+
* §15.3's first steps as one call: GET the ClientContract, then accept it — protocol
|
|
6
|
+
* support, structure, hash. Where the pipeline stops on a rejection, so it is where
|
|
7
|
+
* the `ContractRejected` value becomes a thrown `ContractProtocolError`.
|
|
8
|
+
*/
|
|
9
|
+
export interface ClientContractLoadInput {
|
|
10
|
+
/** The client config's resolved entrypoint (§15.2, S2). */
|
|
11
|
+
readonly entrypoint: ClientEntrypoint;
|
|
12
|
+
/** Injected for tests; the platform `fetch` otherwise. */
|
|
13
|
+
readonly fetch?: ContractFetch;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* @throws ContractTransportError when no JSON value arrives.
|
|
17
|
+
* @throws ContractProtocolError when what arrived is refused.
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadClientContract(input: ClientContractLoadInput): Promise<ClientContract>;
|
|
20
|
+
/** A ClientContract loaded against one the caller already holds (Phase 12-rest Q6). */
|
|
21
|
+
export interface ClientContractSince {
|
|
22
|
+
readonly contract: ClientContract;
|
|
23
|
+
/** The deployment answered `304`: `contract` is the one the caller held. */
|
|
24
|
+
readonly notModified: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* {@link loadClientContract}, conditional on `stored` — a ClientContract the
|
|
28
|
+
* caller holds, already verified (its parsed carrier): the GET sends
|
|
29
|
+
* `If-None-Match: "<its hash>"`, and a `304` answers with `stored` itself. Without
|
|
30
|
+
* `stored` the GET is unconditional.
|
|
31
|
+
*/
|
|
32
|
+
export declare function loadClientContractSince(input: ClientContractLoadInput, stored: ClientContract | undefined): Promise<ClientContractSince>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { acceptClientContract, ContractProtocolError, } from "./contract.acceptance.js";
|
|
2
|
+
import { CONTRACT_NOT_MODIFIED, contractUrlOf, displayUrl, fetchClientContractBody, } from "./contract.fetcher.js";
|
|
3
|
+
/**
|
|
4
|
+
* @throws ContractTransportError when no JSON value arrives.
|
|
5
|
+
* @throws ContractProtocolError when what arrived is refused.
|
|
6
|
+
*/
|
|
7
|
+
export async function loadClientContract(input) {
|
|
8
|
+
return accepted(await fetchClientContractBody(input.entrypoint, input.fetch), input);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* {@link loadClientContract}, conditional on `stored` — a ClientContract the
|
|
12
|
+
* caller holds, already verified (its parsed carrier): the GET sends
|
|
13
|
+
* `If-None-Match: "<its hash>"`, and a `304` answers with `stored` itself. Without
|
|
14
|
+
* `stored` the GET is unconditional.
|
|
15
|
+
*/
|
|
16
|
+
export async function loadClientContractSince(input, stored) {
|
|
17
|
+
if (stored === undefined) {
|
|
18
|
+
return { contract: await loadClientContract(input), notModified: false };
|
|
19
|
+
}
|
|
20
|
+
const body = await fetchClientContractBody(input.entrypoint, input.fetch, `"${stored.protocol.hash}"`);
|
|
21
|
+
if (body === CONTRACT_NOT_MODIFIED) {
|
|
22
|
+
return { contract: stored, notModified: true };
|
|
23
|
+
}
|
|
24
|
+
return { contract: await accepted(body, input), notModified: false };
|
|
25
|
+
}
|
|
26
|
+
async function accepted(body, input) {
|
|
27
|
+
const acceptance = await acceptClientContract(body);
|
|
28
|
+
if (!acceptance.accepted) {
|
|
29
|
+
throw new ContractProtocolError(acceptance, displayUrl(contractUrlOf(input.entrypoint)));
|
|
30
|
+
}
|
|
31
|
+
return acceptance.contract;
|
|
32
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The banner line that says who owns a file. It is what the output writer reads
|
|
3
|
+
* to decide that a directory is a previous generation it may replace whole
|
|
4
|
+
* (§15.3) rather than someone's files it would delete — so it is its own
|
|
5
|
+
* constant, and must not change between generator versions: an output written
|
|
6
|
+
* by an older generator has to stay recognisable to a newer one.
|
|
7
|
+
*/
|
|
8
|
+
export declare const GENERATED_OWNERSHIP_LINE = "/* !!! Generated by @aventara/client. Do not edit. !!! */";
|
|
9
|
+
/**
|
|
10
|
+
* The ownership banner every emitted file opens with (§15.3: "Files are stamped as
|
|
11
|
+
* generated/do-not-edit").
|
|
12
|
+
*
|
|
13
|
+
* The Biome suppressions are EMITTED rather than configured (M17, the adapter's
|
|
14
|
+
* `artifact.emitter.ts` precedent), so the output stays correct wherever a
|
|
15
|
+
* consumer drops it, including a repository whose formatter nobody here chose.
|
|
16
|
+
*
|
|
17
|
+
* Nothing in it can change between two runs over one contract: no timestamp, no
|
|
18
|
+
* generator version, no host, no path (Q4, §19.3) — any of them would break the
|
|
19
|
+
* exit gate's byte-equality on the first re-run. And no driver prose (Q4 = A).
|
|
20
|
+
*/
|
|
21
|
+
export declare const GENERATED_BANNER: readonly string[];
|
|
22
|
+
/** `source` with the banner before it and one blank line between. */
|
|
23
|
+
export declare function withGeneratedBanner(source: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* How far into a file the ownership line is looked for. The banner opens every
|
|
26
|
+
* emitted file, so the line sits well inside this; a reader never has to read a
|
|
27
|
+
* whole file to learn it is foreign.
|
|
28
|
+
*/
|
|
29
|
+
export declare const GENERATED_OWNERSHIP_HEAD_BYTES = 1024;
|
|
30
|
+
/** Whether `head` — a file's first bytes, as text — carries the ownership line. */
|
|
31
|
+
export declare function carriesGeneratedOwnership(head: string): boolean;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The banner line that says who owns a file. It is what the output writer reads
|
|
3
|
+
* to decide that a directory is a previous generation it may replace whole
|
|
4
|
+
* (§15.3) rather than someone's files it would delete — so it is its own
|
|
5
|
+
* constant, and must not change between generator versions: an output written
|
|
6
|
+
* by an older generator has to stay recognisable to a newer one.
|
|
7
|
+
*/
|
|
8
|
+
export const GENERATED_OWNERSHIP_LINE = "/* !!! Generated by @aventara/client. Do not edit. !!! */";
|
|
9
|
+
/**
|
|
10
|
+
* The ownership banner every emitted file opens with (§15.3: "Files are stamped as
|
|
11
|
+
* generated/do-not-edit").
|
|
12
|
+
*
|
|
13
|
+
* The Biome suppressions are EMITTED rather than configured (M17, the adapter's
|
|
14
|
+
* `artifact.emitter.ts` precedent), so the output stays correct wherever a
|
|
15
|
+
* consumer drops it, including a repository whose formatter nobody here chose.
|
|
16
|
+
*
|
|
17
|
+
* Nothing in it can change between two runs over one contract: no timestamp, no
|
|
18
|
+
* generator version, no host, no path (Q4, §19.3) — any of them would break the
|
|
19
|
+
* exit gate's byte-equality on the first re-run. And no driver prose (Q4 = A).
|
|
20
|
+
*/
|
|
21
|
+
export const GENERATED_BANNER = [
|
|
22
|
+
"// biome-ignore-all format: generated output; these bytes are the artifact",
|
|
23
|
+
"// biome-ignore-all lint: generated output",
|
|
24
|
+
GENERATED_OWNERSHIP_LINE,
|
|
25
|
+
"/* Regenerate with `avclient generate`. */",
|
|
26
|
+
];
|
|
27
|
+
/** `source` with the banner before it and one blank line between. */
|
|
28
|
+
export function withGeneratedBanner(source) {
|
|
29
|
+
return `${GENERATED_BANNER.join("\n")}\n\n${source}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* How far into a file the ownership line is looked for. The banner opens every
|
|
33
|
+
* emitted file, so the line sits well inside this; a reader never has to read a
|
|
34
|
+
* whole file to learn it is foreign.
|
|
35
|
+
*/
|
|
36
|
+
export const GENERATED_OWNERSHIP_HEAD_BYTES = 1024;
|
|
37
|
+
/** Whether `head` — a file's first bytes, as text — carries the ownership line. */
|
|
38
|
+
export function carriesGeneratedOwnership(head) {
|
|
39
|
+
return head
|
|
40
|
+
.slice(0, GENERATED_OWNERSHIP_HEAD_BYTES)
|
|
41
|
+
.includes(GENERATED_OWNERSHIP_LINE);
|
|
42
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ClientContract } from "@aventara/core";
|
|
2
|
+
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
3
|
+
import { type EmittedNames } from "./name.deriver.js";
|
|
4
|
+
/**
|
|
5
|
+
* `generated/client.ts` — the typed surface (Phase 12-rest S4; plan §6): the class
|
|
6
|
+
* `AvClient` (Q8), its options `AvClientOptions` and the per-call `CallOptions`
|
|
7
|
+
* (Q9, Q17), and the ready instance `avClient`, this module's default export
|
|
8
|
+
* (Q7).
|
|
9
|
+
*
|
|
10
|
+
* Every argument and result type is an instantiation of core's own derivation,
|
|
11
|
+
* copied under `generated/derivation/` (Q1 = A): the call grammar
|
|
12
|
+
* (`OperationGrammar`, `OperationCall`, `DeferredOperationCall`, S1) over the
|
|
13
|
+
* carrier's contract (`ClientContractShape`, Q4), in the client's forms (Q3 = A:
|
|
14
|
+
* core's application forms, with this tree's own `Decimal`). Nothing here
|
|
15
|
+
* re-spells a rule. A call resolves to the data (Q11 = a: `"data"`), a
|
|
16
|
+
* first-style miss to `null`.
|
|
17
|
+
*
|
|
18
|
+
* Each surface is one mapped alias over the one contract (Phase 9's variance
|
|
19
|
+
* lesson). A Resource is reached by its contract name, except where that name is
|
|
20
|
+
* one of the client's own members (Q12), which renames the property only.
|
|
21
|
+
* `tx` and `transaction` exist iff the contract advertises `interactive`
|
|
22
|
+
* transactions (P3), in the type as in the runtime.
|
|
23
|
+
*
|
|
24
|
+
* Also declared here, for `types.d.ts` alone, the two helpers the named types are
|
|
25
|
+
* aliases of (Q10): `ResourceRecord` and `ResourceArgument`.
|
|
26
|
+
*
|
|
27
|
+
* At runtime (S5) the class builds one frozen object per Resource from the
|
|
28
|
+
* advertised operations (P4) — each variant a function that runs the operation
|
|
29
|
+
* through the transport, reading the fetch when it is called — and the default
|
|
30
|
+
* entrypoint is the generated one (§15.2, Q5) unless the options name another.
|
|
31
|
+
*/
|
|
32
|
+
export declare function emitClientSurfaceModule(contract: ClientContract, names: EmittedNames): EmittedModule;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { ownPropertyKey } from "./name.deriver.js";
|
|
2
|
+
/**
|
|
3
|
+
* `generated/client.ts` — the typed surface (Phase 12-rest S4; plan §6): the class
|
|
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) {
|
|
31
|
+
const renamed = names.properties.filter((property) => property.property !== property.contractName);
|
|
32
|
+
const propertyOf = renamed.length === 0
|
|
33
|
+
? "R"
|
|
34
|
+
: `${renamed
|
|
35
|
+
.map((property) => `R extends ${JSON.stringify(property.contractName)} ? ${JSON.stringify(property.property)} : `)
|
|
36
|
+
.join("")}R`;
|
|
37
|
+
const resourceKey = renamed.length === 0 ? "R in Resource" : `R in Resource as ${propertyOf}`;
|
|
38
|
+
const interactive = contract.transactions === "interactive";
|
|
39
|
+
const renamedEntries = renamed.length === 0
|
|
40
|
+
? ""
|
|
41
|
+
: ` ${renamed.map((property) => `${ownPropertyKey(property.contractName)}: ${JSON.stringify(property.property)}`).join(", ")} `;
|
|
42
|
+
const surface = (name, call) => `type ${name} = {\n` +
|
|
43
|
+
`\treadonly [${resourceKey}]: {\n` +
|
|
44
|
+
"\t\treadonly [F in keyof Grammar[R]]: {\n" +
|
|
45
|
+
`\t\t\treadonly [V in keyof Grammar[R][F]]: ${call};\n` +
|
|
46
|
+
"\t\t};\n" +
|
|
47
|
+
"\t};\n" +
|
|
48
|
+
"};\n";
|
|
49
|
+
return {
|
|
50
|
+
path: "client.ts",
|
|
51
|
+
source: 'import type { ClientContractShape } from "./contract.js";\n' +
|
|
52
|
+
'import type { ApplicationScalarForms } from "./derivation/contracts/scalar-value-type.js";\n' +
|
|
53
|
+
'import type { OperationArgumentsFor, ResourceKey } from "./derivation/operations/operation-arguments.js";\n' +
|
|
54
|
+
`import type { ${interactive ? "DeferredOperationCall, " : ""}OperationCall, OperationGrammar } from "./derivation/operations/operation-call.js";\n` +
|
|
55
|
+
'import type { OperationFamily, OperationIdentity } from "./derivation/operations/operation-identity.js";\n' +
|
|
56
|
+
'import type { AdmittedOperationResult } from "./derivation/operations/operation-result.js";\n' +
|
|
57
|
+
(interactive
|
|
58
|
+
? 'import type { Operation, TransactionResults } from "./derivation/transactions/deferred-operation.js";\n'
|
|
59
|
+
: "") +
|
|
60
|
+
'import { DEFAULT_ENTRYPOINT } from "./metadata.js";\n' +
|
|
61
|
+
'import type { Decimal } from "./runtime/decimal.js";\n' +
|
|
62
|
+
'import { ADVERTISED_OPERATIONS } from "./runtime/descriptor.js";\n' +
|
|
63
|
+
(interactive
|
|
64
|
+
? 'import { deferOperation, runTransaction } from "./runtime/transaction.js";\n'
|
|
65
|
+
: "") +
|
|
66
|
+
'import { type CallOptions, execute, type Fetch, type TransportConnection } from "./runtime/transport.js";\n' +
|
|
67
|
+
"\n" +
|
|
68
|
+
'export type { CallOptions } from "./runtime/transport.js";\n' +
|
|
69
|
+
"\n" +
|
|
70
|
+
"/**\n" +
|
|
71
|
+
" * The forms this client reads and writes (§6.2, Q3): core's application forms —\n" +
|
|
72
|
+
" * `bigint`, `Date`, `Uint8Array` — with this client's own `Decimal`.\n" +
|
|
73
|
+
" */\n" +
|
|
74
|
+
'export type ClientScalarForms = Omit<ApplicationScalarForms, "decimal"> & {\n' +
|
|
75
|
+
"\treadonly decimal: Decimal;\n" +
|
|
76
|
+
"};\n" +
|
|
77
|
+
"\n" +
|
|
78
|
+
"/** How an `AvClient` reaches its deployment (§15.2, §15.7). */\n" +
|
|
79
|
+
"export interface AvClientOptions {\n" +
|
|
80
|
+
"\t/** Another deployment serving exactly the same ClientContract; the generated default otherwise. */\n" +
|
|
81
|
+
"\treadonly entrypoint?: string;\n" +
|
|
82
|
+
"\t/** A custom fetch — authentication, tests; the platform's otherwise. */\n" +
|
|
83
|
+
"\treadonly fetch?: Fetch;\n" +
|
|
84
|
+
"}\n" +
|
|
85
|
+
"\n" +
|
|
86
|
+
"type Grammar = OperationGrammar<ClientContractShape>;\n" +
|
|
87
|
+
"type Resource = ResourceKey<ClientContractShape>;\n" +
|
|
88
|
+
"\n" +
|
|
89
|
+
"/** `avClient.<resource>.<family>.<variant>(args, options?)`: one call per advertised operation. */\n" +
|
|
90
|
+
surface("AvClientSurface", "OperationCall<\n" +
|
|
91
|
+
"\t\t\t\tClientContractShape,\n" +
|
|
92
|
+
"\t\t\t\tR,\n" +
|
|
93
|
+
"\t\t\t\tGrammar[R][F][V] & OperationIdentity,\n" +
|
|
94
|
+
"\t\t\t\tClientScalarForms,\n" +
|
|
95
|
+
"\t\t\t\tClientScalarForms,\n" +
|
|
96
|
+
'\t\t\t\t"data",\n' +
|
|
97
|
+
"\t\t\t\tCallOptions\n" +
|
|
98
|
+
"\t\t\t>") +
|
|
99
|
+
(interactive
|
|
100
|
+
? "\n" +
|
|
101
|
+
"/** `avClient.tx.<resource>.<family>.<variant>(args)`: a deferred handle; no request (§14.1). */\n" +
|
|
102
|
+
surface("AvTxSurface", "DeferredOperationCall<\n" +
|
|
103
|
+
"\t\t\t\tClientContractShape,\n" +
|
|
104
|
+
"\t\t\t\tR,\n" +
|
|
105
|
+
"\t\t\t\tGrammar[R][F][V] & OperationIdentity,\n" +
|
|
106
|
+
"\t\t\t\tClientScalarForms,\n" +
|
|
107
|
+
"\t\t\t\tClientScalarForms\n" +
|
|
108
|
+
"\t\t\t>") +
|
|
109
|
+
"\n" +
|
|
110
|
+
"/** Sends the handles as one plan and resolves their results as a typed tuple (§14.1). */\n" +
|
|
111
|
+
"type TransactionCall = <const Steps extends readonly Operation<unknown>[]>(\n" +
|
|
112
|
+
"\tsteps: Steps,\n" +
|
|
113
|
+
"\toptions?: CallOptions,\n" +
|
|
114
|
+
") => Promise<TransactionResults<Steps>>;\n"
|
|
115
|
+
: "") +
|
|
116
|
+
"\n" +
|
|
117
|
+
"/** The generated client: one property per Resource this deployment's ClientContract advertises. */\n" +
|
|
118
|
+
"export interface AvClient extends AvClientSurface {}\n" +
|
|
119
|
+
"\n" +
|
|
120
|
+
"/** The generated client (§15.4). */\n" +
|
|
121
|
+
"export class AvClient {\n" +
|
|
122
|
+
(interactive
|
|
123
|
+
? "\t/** Deferred operations, for `transaction` (§14.1). */\n" +
|
|
124
|
+
"\tdeclare readonly tx: AvTxSurface;\n" +
|
|
125
|
+
"\t/** Runs deferred operations as one transaction (§14.1). */\n" +
|
|
126
|
+
"\tdeclare readonly transaction: TransactionCall;\n" +
|
|
127
|
+
"\n"
|
|
128
|
+
: "") +
|
|
129
|
+
"\tconstructor(options: AvClientOptions = {}) {\n" +
|
|
130
|
+
'\t\tconst entrypoint = (options.entrypoint ?? DEFAULT_ENTRYPOINT).replace(/\\/+$/, "");\n' +
|
|
131
|
+
"\t\tconst given = options.fetch;\n" +
|
|
132
|
+
"\t\tconst connect = (): TransportConnection => ({ entrypoint, fetch: platformFetch(given) });\n" +
|
|
133
|
+
"\t\tfor (const [resource, families] of operationTree(\n" +
|
|
134
|
+
"\t\t\t(resource, family, variant): OperationMethod =>\n" +
|
|
135
|
+
"\t\t\t\tasync (args = {}, callOptions = {}) =>\n" +
|
|
136
|
+
"\t\t\t\t\texecute(connect(), resource, family, variant, args, callOptions),\n" +
|
|
137
|
+
"\t\t)) {\n" +
|
|
138
|
+
"\t\t\tObject.defineProperty(this, propertyOf(resource), { value: families, enumerable: true });\n" +
|
|
139
|
+
"\t\t}\n" +
|
|
140
|
+
(interactive
|
|
141
|
+
? "\t\tconst deferred: object = Object.create(null);\n" +
|
|
142
|
+
"\t\tfor (const [resource, families] of operationTree(\n" +
|
|
143
|
+
"\t\t\t(resource, family, variant) =>\n" +
|
|
144
|
+
"\t\t\t\t(args: unknown = {}): object =>\n" +
|
|
145
|
+
"\t\t\t\t\tdeferOperation(resource, family, variant, args),\n" +
|
|
146
|
+
"\t\t)) {\n" +
|
|
147
|
+
"\t\t\tObject.defineProperty(deferred, propertyOf(resource), { value: families, enumerable: true });\n" +
|
|
148
|
+
"\t\t}\n" +
|
|
149
|
+
'\t\tObject.defineProperty(this, "tx", { value: Object.freeze(deferred) });\n' +
|
|
150
|
+
'\t\tObject.defineProperty(this, "transaction", {\n' +
|
|
151
|
+
"\t\t\tvalue: async (steps: unknown, callOptions: CallOptions = {}) =>\n" +
|
|
152
|
+
"\t\t\t\trunTransaction(connect(), steps, callOptions),\n" +
|
|
153
|
+
"\t\t});\n"
|
|
154
|
+
: "") +
|
|
155
|
+
"\t}\n" +
|
|
156
|
+
"}\n" +
|
|
157
|
+
"\n" +
|
|
158
|
+
"/**\n" +
|
|
159
|
+
" * One frozen object per Resource, one per family under it, one member per\n" +
|
|
160
|
+
" * advertised variant (P4) — by wire name; the caller places each Resource.\n" +
|
|
161
|
+
" */\n" +
|
|
162
|
+
"function operationTree<M>(member: (resource: string, family: string, variant: string) => M): Map<string, object> {\n" +
|
|
163
|
+
"\tconst resources = new Map<string, Map<string, Record<string, M>>>();\n" +
|
|
164
|
+
"\tfor (const [resource, family, variant] of ADVERTISED_OPERATIONS) {\n" +
|
|
165
|
+
"\t\tconst families = resources.get(resource) ?? new Map<string, Record<string, M>>();\n" +
|
|
166
|
+
"\t\tresources.set(resource, families);\n" +
|
|
167
|
+
"\t\tconst variants = families.get(family) ?? (Object.create(null) as Record<string, M>);\n" +
|
|
168
|
+
"\t\tfamilies.set(family, variants);\n" +
|
|
169
|
+
"\t\tvariants[variant] = member(resource, family, variant);\n" +
|
|
170
|
+
"\t}\n" +
|
|
171
|
+
"\treturn new Map(\n" +
|
|
172
|
+
"\t\t[...resources].map(([resource, families]) => [\n" +
|
|
173
|
+
"\t\t\tresource,\n" +
|
|
174
|
+
"\t\t\tObject.freeze(Object.fromEntries([...families].map(([family, variants]) => [family, Object.freeze(variants)]))),\n" +
|
|
175
|
+
"\t\t]),\n" +
|
|
176
|
+
"\t);\n" +
|
|
177
|
+
"}\n" +
|
|
178
|
+
"\n" +
|
|
179
|
+
"/** One advertised operation, as the runtime calls it: the types are the surface's. */\n" +
|
|
180
|
+
"type OperationMethod = (args?: Readonly<Record<string, unknown>>, options?: CallOptions) => Promise<unknown>;\n" +
|
|
181
|
+
"\n" +
|
|
182
|
+
"/** The Resources reached by a property other than their wire name (Q12). */\n" +
|
|
183
|
+
`const RENAMED_PROPERTIES: Readonly<Record<string, string>> = {${renamedEntries}};\n` +
|
|
184
|
+
"\n" +
|
|
185
|
+
"/** The client's property for a Resource: its wire name, unless that is one of the client's own members. */\n" +
|
|
186
|
+
"function propertyOf(resource: string): string {\n" +
|
|
187
|
+
"\treturn Object.hasOwn(RENAMED_PROPERTIES, resource) ? (RENAMED_PROPERTIES[resource] ?? resource) : resource;\n" +
|
|
188
|
+
"}\n" +
|
|
189
|
+
"\n" +
|
|
190
|
+
"/**\n" +
|
|
191
|
+
" * The fetch a call uses (§15.7): the one the client was given, else the\n" +
|
|
192
|
+
" * platform's, read when the call is made — so importing the client never\n" +
|
|
193
|
+
" * fails where there is no fetch, and a call does, saying why.\n" +
|
|
194
|
+
" */\n" +
|
|
195
|
+
"function platformFetch(given: Fetch | undefined): Fetch {\n" +
|
|
196
|
+
"\tconst found = given ?? (globalThis as { readonly fetch?: Fetch }).fetch;\n" +
|
|
197
|
+
'\tif (typeof found !== "function") {\n' +
|
|
198
|
+
'\t\tthrow new TypeError("No fetch is available here; pass one: new AvClient({ fetch }) (§15.7).");\n' +
|
|
199
|
+
"\t}\n" +
|
|
200
|
+
"\treturn found;\n" +
|
|
201
|
+
"}\n" +
|
|
202
|
+
"\n" +
|
|
203
|
+
"/** The client of the deployment this tree was generated from. */\n" +
|
|
204
|
+
"const avClient: AvClient = new AvClient();\n" +
|
|
205
|
+
"export default avClient;\n" +
|
|
206
|
+
"\n" +
|
|
207
|
+
"type Identity<F extends OperationFamily, V extends string> = Extract<\n" +
|
|
208
|
+
"\tOperationIdentity,\n" +
|
|
209
|
+
"\t{ readonly family: F; readonly variant: V }\n" +
|
|
210
|
+
">;\n" +
|
|
211
|
+
"\n" +
|
|
212
|
+
"/** A Resource's default record — `find.unique` with no projection (§15.6, Q10). */\n" +
|
|
213
|
+
"export type ResourceRecord<R extends Resource> = AdmittedOperationResult<\n" +
|
|
214
|
+
"\tClientContractShape,\n" +
|
|
215
|
+
"\tR,\n" +
|
|
216
|
+
'\tIdentity<"find", "unique">,\n' +
|
|
217
|
+
"\tunknown,\n" +
|
|
218
|
+
"\tClientScalarForms\n" +
|
|
219
|
+
">;\n" +
|
|
220
|
+
"\n" +
|
|
221
|
+
"/** One argument of one of a Resource's operations, present (§15.6, Q10). */\n" +
|
|
222
|
+
"export type ResourceArgument<\n" +
|
|
223
|
+
"\tR extends Resource,\n" +
|
|
224
|
+
"\tF extends OperationFamily,\n" +
|
|
225
|
+
"\tV extends string,\n" +
|
|
226
|
+
"\tK extends string,\n" +
|
|
227
|
+
"> = ArgumentOf<\n" +
|
|
228
|
+
"\tOperationArgumentsFor<ClientContractShape, R, Identity<F, V>, ClientScalarForms>,\n" +
|
|
229
|
+
"\tK\n" +
|
|
230
|
+
">;\n" +
|
|
231
|
+
"\n" +
|
|
232
|
+
"type ArgumentOf<Args, K extends string> = K extends keyof Args\n" +
|
|
233
|
+
"\t? Exclude<Args[K], undefined>\n" +
|
|
234
|
+
"\t: never;\n",
|
|
235
|
+
};
|
|
236
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ClientContract } from "@aventara/core";
|
|
2
|
+
import type { ClientEntrypoint } from "../config/client-config.interface.js";
|
|
3
|
+
import { type ClientEmission } from "./emitted-tree.interface.js";
|
|
4
|
+
/**
|
|
5
|
+
* §15.3's emit steps over an accepted ClientContract, as one value (plan §7).
|
|
6
|
+
*
|
|
7
|
+
* 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
|
+
* of this function rather than of each emitter's care (C-843):
|
|
10
|
+
*
|
|
11
|
+
* - every module opens with the generated banner (M17);
|
|
12
|
+
* - 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
|
+
* - files come in UTF-16 code-unit order of their path, each path once.
|
|
16
|
+
*
|
|
17
|
+
* The name derivation's rename warnings ride on the result beside the tree; this
|
|
18
|
+
* function prints nothing.
|
|
19
|
+
*
|
|
20
|
+
* The names and enums read the enum registry, registry NAMES and `protocol`.
|
|
21
|
+
* Core's derivation is copied under `generated/derivation/` as its published
|
|
22
|
+
* declarations (Q1 = A, `derivation.emitter.ts`): read from the installed
|
|
23
|
+
* `@aventara/core`, never from the Contract. The contract itself reaches the
|
|
24
|
+
* tree as three projections of the one accepted contract (plan §6): the carrier
|
|
25
|
+
* `generated/contract.ts` (Q4), the runtime's decode table and advertised
|
|
26
|
+
* operations `generated/runtime/descriptor.ts` (P1, P4), and `metadata.ts`'s
|
|
27
|
+
* hash, version and default entrypoint (Q5) — the entrypoint being the run's
|
|
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.
|
|
34
|
+
*
|
|
35
|
+
* @throws GeneratedNameError when a name cannot be emitted, even renamed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function emitClientTree(contract: ClientContract, entrypoint: ClientEntrypoint): ClientEmission;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { withGeneratedBanner } from "./banner.emitter.js";
|
|
2
|
+
import { emitClientSurfaceModule } from "./client-surface.emitter.js";
|
|
3
|
+
import { emitContractCarrierModule } from "./contract-carrier.emitter.js";
|
|
4
|
+
import { emitDerivationModules } from "./derivation.emitter.js";
|
|
5
|
+
import { emitDescriptorModule } from "./descriptor.emitter.js";
|
|
6
|
+
import { CLIENT_ENTRY_FILE, GENERATED_DIRECTORY, } from "./emitted-tree.interface.js";
|
|
7
|
+
import { emitEnumsModule } from "./enum.emitter.js";
|
|
8
|
+
import { deriveEmittedNames } from "./name.deriver.js";
|
|
9
|
+
import { emitNamedTypesModule } from "./named-type.emitter.js";
|
|
10
|
+
import { emitDecimalModule, emitErrorsModule, emitMetadataModule, emitTransportModule, errorsModuleExports, } from "./runtime.emitter.js";
|
|
11
|
+
import { emitScalarCodecModule } from "./scalar.codec.js";
|
|
12
|
+
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) {
|
|
47
|
+
const names = deriveEmittedNames(contract);
|
|
48
|
+
const generated = [
|
|
49
|
+
...emitDerivationModules(),
|
|
50
|
+
emitClientSurfaceModule(contract, names),
|
|
51
|
+
emitContractCarrierModule(contract),
|
|
52
|
+
emitDescriptorModule(contract),
|
|
53
|
+
emitEnumsModule(contract, names),
|
|
54
|
+
emitMetadataModule(contract.protocol, entrypoint),
|
|
55
|
+
emitScalarCodecModule(),
|
|
56
|
+
emitDecimalModule(),
|
|
57
|
+
emitErrorsModule(),
|
|
58
|
+
emitTransportModule(),
|
|
59
|
+
emitNamedTypesModule(contract, names),
|
|
60
|
+
// P3: the builder and runner exist iff transactions are interactive.
|
|
61
|
+
...(contract.transactions === "interactive"
|
|
62
|
+
? emitTransactionModules()
|
|
63
|
+
: []),
|
|
64
|
+
];
|
|
65
|
+
const files = [
|
|
66
|
+
{ path: CLIENT_ENTRY_FILE, source: clientEntrySource() },
|
|
67
|
+
...generated.map((module) => ({
|
|
68
|
+
path: `${GENERATED_DIRECTORY}/${module.path}`,
|
|
69
|
+
source: module.source,
|
|
70
|
+
})),
|
|
71
|
+
];
|
|
72
|
+
const encoder = new TextEncoder();
|
|
73
|
+
const tree = [...files]
|
|
74
|
+
.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0)
|
|
75
|
+
.map((file) => ({
|
|
76
|
+
path: file.path,
|
|
77
|
+
bytes: encoder.encode(withGeneratedBanner(file.source)),
|
|
78
|
+
}));
|
|
79
|
+
return { tree, warnings: names.warnings };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* `AvClient.ts`, the entry point a consumer imports (§15.4; Phase 12-rest Q7):
|
|
83
|
+
* the ready client `avClient` as the DEFAULT export, and as named exports the
|
|
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")}`);
|
|
93
|
+
return (`export { default } from ${from("client.ts")};\n` +
|
|
94
|
+
`export {\n\tAvClient,\n\ttype AvClientOptions,\n\ttype CallOptions,\n} from ${from("client.ts")};\n` +
|
|
95
|
+
`export type { Fetch } from ${from("runtime/transport.ts")};\n` +
|
|
96
|
+
`export type { Operation } from ${from("derivation/transactions/deferred-operation.d.ts")};\n` +
|
|
97
|
+
`export type * from ${from("types.d.ts")};\n` +
|
|
98
|
+
`export * from ${from("enums.ts")};\n` +
|
|
99
|
+
`export { Decimal } from ${from("runtime/decimal.ts")};\n` +
|
|
100
|
+
`export {\n${errorsModuleExports()
|
|
101
|
+
.map((name) => `\t${name},\n`)
|
|
102
|
+
.join("")}} from ${from("runtime/errors.ts")};\n`);
|
|
103
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ClientContract } from "@aventara/core";
|
|
2
|
+
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
3
|
+
/** `generated/contract.ts`, before the banner. */
|
|
4
|
+
export declare function emitContractCarrierModule(contract: ClientContract): EmittedModule;
|
|
5
|
+
/**
|
|
6
|
+
* The ClientContract a carrier file holds — its whole text, banner included —
|
|
7
|
+
* or `undefined` when it is not one this generator wrote over a contract that
|
|
8
|
+
* still verifies: a foreign prefix or suffix, bytes that are not JSON, a body
|
|
9
|
+
* that is not a ClientContract or whose hash does not re-verify, or bytes that
|
|
10
|
+
* are not that contract's canonical form. Never trusted otherwise (Q6: a carrier
|
|
11
|
+
* that does not re-hash is treated as absent).
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseContractCarrier(text: string): Promise<ClientContract | undefined>;
|