@aventara/client 0.1.0-pilot.1 → 0.1.0-pilot.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -7
- package/dist/avclient.bin.js +0 -10
- package/dist/cli/command.parser.d.ts +15 -10
- package/dist/cli/command.parser.js +13 -19
- package/dist/cli/generate.command.js +0 -6
- package/dist/cli/generation-failure.renderer.js +0 -14
- package/dist/cli/generation-success.renderer.d.ts +4 -1
- package/dist/cli/generation-success.renderer.js +0 -13
- package/dist/cli/terminal.prompter.d.ts +1 -2
- package/dist/cli/warning.renderer.d.ts +2 -2
- package/dist/cli/warning.renderer.js +0 -8
- package/dist/cli.d.ts +8 -16
- package/dist/cli.js +5 -25
- package/dist/config/client-config.interface.d.ts +18 -16
- package/dist/config/client-config.interface.js +0 -13
- package/dist/config/config.loader.d.ts +34 -22
- package/dist/config/config.loader.js +49 -52
- package/dist/config/config.resolver.d.ts +13 -19
- package/dist/config/config.resolver.js +9 -48
- package/dist/config/env.cascade.d.ts +12 -14
- package/dist/config/env.cascade.js +0 -19
- package/dist/config/module-style.resolver.d.ts +52 -0
- package/dist/config/module-style.resolver.js +75 -0
- package/dist/config/tsconfig.locator.d.ts +45 -0
- package/dist/config/tsconfig.locator.js +52 -0
- package/dist/contract/contract.acceptance.d.ts +12 -26
- package/dist/contract/contract.acceptance.js +0 -54
- package/dist/contract/contract.fetcher.d.ts +12 -17
- package/dist/contract/contract.fetcher.js +0 -24
- package/dist/contract/contract.loader.d.ts +4 -5
- package/dist/contract/contract.loader.js +0 -10
- package/dist/emit/banner.emitter.d.ts +11 -12
- package/dist/emit/banner.emitter.js +0 -26
- package/dist/emit/client-surface.emitter.d.ts +17 -21
- package/dist/emit/client-surface.emitter.js +29 -55
- package/dist/emit/client-tree.emitter.d.ts +11 -20
- package/dist/emit/client-tree.emitter.js +12 -54
- package/dist/emit/contract-carrier.emitter.d.ts +5 -6
- package/dist/emit/contract-carrier.emitter.js +0 -28
- package/dist/emit/derivation.emitter.d.ts +7 -7
- package/dist/emit/derivation.emitter.js +2 -161
- package/dist/emit/descriptor.emitter.js +2 -28
- package/dist/emit/emitted-tree.interface.d.ts +40 -17
- package/dist/emit/emitted-tree.interface.js +6 -16
- package/dist/emit/enum.emitter.d.ts +4 -4
- package/dist/emit/enum.emitter.js +0 -24
- package/dist/emit/module-specifier.scanner.d.ts +25 -0
- package/dist/emit/module-specifier.scanner.js +160 -0
- package/dist/emit/module-style.interface.d.ts +58 -0
- package/dist/emit/module-style.interface.js +8 -0
- package/dist/emit/name.deriver.d.ts +33 -61
- package/dist/emit/name.deriver.js +0 -134
- package/dist/emit/named-type.emitter.d.ts +14 -21
- package/dist/emit/named-type.emitter.js +3 -30
- package/dist/emit/runtime.emitter.d.ts +23 -50
- package/dist/emit/runtime.emitter.js +68 -159
- package/dist/emit/scalar.codec.d.ts +20 -33
- package/dist/emit/scalar.codec.js +13 -69
- package/dist/emit/transaction.emitter.d.ts +6 -14
- package/dist/emit/transaction.emitter.js +24 -33
- package/dist/generate.d.ts +17 -34
- package/dist/generate.js +14 -22
- package/dist/index.js +0 -5
- package/dist/init/client-config.template.d.ts +6 -4
- package/dist/init/client-config.template.js +10 -13
- package/dist/init/client-init.errors.js +0 -3
- package/dist/init/client-init.orchestrator.js +1 -9
- package/dist/init/client-init.planner.d.ts +1 -9
- package/dist/init/client-init.planner.js +6 -24
- package/dist/init/client-init.questions.d.ts +8 -12
- package/dist/init/client-init.questions.js +0 -11
- package/dist/init/client-project.inspector.d.ts +6 -0
- package/dist/init/client-project.inspector.js +2 -2
- package/dist/node-version.guard.js +0 -12
- package/dist/output/output.validator.d.ts +22 -22
- package/dist/output/output.validator.js +46 -59
- package/dist/output/output.writer.d.ts +56 -52
- package/dist/output/output.writer.js +71 -133
- package/package.json +6 -4
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { type ClientContract } from "@aventara/core";
|
|
2
2
|
/**
|
|
3
|
-
* §15.3's validation steps over a fetched body, in the specification's order, as
|
|
4
|
-
* one decision (plan §7, group 2): protocol support (§19.1), then ClientContract
|
|
5
|
-
* structure, then the advertised hash recomputed and compared (§19.2).
|
|
6
|
-
*
|
|
7
3
|
* # The structure check is core's, never a second implementation
|
|
8
4
|
*
|
|
9
5
|
* What a ClientContract IS belongs to core, so the structure step is core's
|
|
@@ -22,39 +18,29 @@ import { type ClientContract } from "@aventara/core";
|
|
|
22
18
|
*
|
|
23
19
|
* # A value, not a throw site
|
|
24
20
|
*
|
|
25
|
-
* M3's rule — failure is a message and an exit code, never a stack — needs both to
|
|
26
|
-
* be typed members, so a rejection is the `ContractRejected` arm carrying its
|
|
27
|
-
* sentence and its exit code, and `ContractProtocolError` is built from it only
|
|
28
|
-
* where generation stops.
|
|
29
|
-
*
|
|
30
21
|
* # The hash is core's, never a second implementation
|
|
31
22
|
*
|
|
32
23
|
* The advertised `protocol.hash` is recomputed with `computeClientContractHash` —
|
|
33
|
-
* the function the compiler stamps it with
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* # Bodies the hash cannot be recomputed over (S0)
|
|
24
|
+
* the function the compiler stamps it with — and compared. A hash computed here
|
|
25
|
+
* would be a second source of the Contract's identity.
|
|
37
26
|
*
|
|
38
27
|
* The ClientContract type admits values RFC 8785 cannot encode, and a valid JSON
|
|
39
|
-
* body produces two of them: `1e999` parses to `Infinity`, and `"\ud800"` parses
|
|
40
|
-
* a lone surrogate. Core rejects both with `CanonicalJsonError`. No server can
|
|
41
|
-
* stamped a hash over such a body — its own canonicalizer would have refused
|
|
42
|
-
* is refused at THIS step, as `hash-mismatch` with its own sentence and
|
|
43
|
-
* never let escape as a stack.
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* hashing is not a known property of the input, and keeps its stack as a defect.
|
|
28
|
+
* body produces two of them: `1e999` parses to `Infinity`, and `"\ud800"` parses
|
|
29
|
+
* to a lone surrogate. Core rejects both with `CanonicalJsonError`. No server can
|
|
30
|
+
* have stamped a hash over such a body — its own canonicalizer would have refused
|
|
31
|
+
* — so it is refused at THIS step, as `hash-mismatch` with its own sentence and
|
|
32
|
+
* core's path, never let escape as a stack. ONLY that class is caught: anything
|
|
33
|
+
* else thrown while hashing is not a known property of the input, and keeps its
|
|
34
|
+
* stack as a defect.
|
|
47
35
|
*/
|
|
48
|
-
/** The protocol versions this generator speaks
|
|
36
|
+
/** The protocol versions this generator speaks. */
|
|
49
37
|
export declare const SUPPORTED_PROTOCOL_VERSIONS: ReadonlySet<number>;
|
|
50
|
-
/** Closed at three (plan §7): §19.1, §19.2, and §15.3's structure step. */
|
|
51
38
|
export type ContractRejectionReason = "protocol-unsupported" | "hash-mismatch" | "structure-invalid";
|
|
52
39
|
export type ContractAccepted = {
|
|
53
40
|
readonly accepted: true;
|
|
54
41
|
/**
|
|
55
|
-
* Trusted to the depth this step checks: core's structure check and the hash.
|
|
56
|
-
*
|
|
57
|
-
* (plan §1).
|
|
42
|
+
* Trusted to the depth this step checks: core's structure check and the hash. It
|
|
43
|
+
* is the erased ClientContract: no capability is interpreted here.
|
|
58
44
|
*/
|
|
59
45
|
readonly contract: ClientContract;
|
|
60
46
|
};
|
|
@@ -1,55 +1,7 @@
|
|
|
1
1
|
import { AVENTARA_PROTOCOL_VERSION, CanonicalJsonError, computeClientContractHash, validateClientContractStructure, } from "@aventara/core";
|
|
2
|
-
/**
|
|
3
|
-
* §15.3's validation steps over a fetched body, in the specification's order, as
|
|
4
|
-
* one decision (plan §7, group 2): protocol support (§19.1), then ClientContract
|
|
5
|
-
* structure, then the advertised hash recomputed and compared (§19.2).
|
|
6
|
-
*
|
|
7
|
-
* # The structure check is core's, never a second implementation
|
|
8
|
-
*
|
|
9
|
-
* What a ClientContract IS belongs to core, so the structure step is core's
|
|
10
|
-
* `validateClientContractStructure` and nothing here restates a member, a kind or
|
|
11
|
-
* a vocabulary. Before it, a body with a valid envelope and a malformed member —
|
|
12
|
-
* `resources: { Spell: 5 }` — reached core's canonicalizer, which reads members
|
|
13
|
-
* without validating them, and escaped as a plain `TypeError` and a stack.
|
|
14
|
-
*
|
|
15
|
-
* # Protocol support is judged first, from a read rather than a rule
|
|
16
|
-
*
|
|
17
|
-
* A body from a protocol this generator does not speak may have a different
|
|
18
|
-
* structure, and "unsupported" is then the true and useful answer. So the
|
|
19
|
-
* advertised version is READ — leniently, never judged — and judged against the
|
|
20
|
-
* supported set before any structure is. A body whose version cannot be read at
|
|
21
|
-
* all goes on to the structure step, which names what is wrong with it.
|
|
22
|
-
*
|
|
23
|
-
* # A value, not a throw site
|
|
24
|
-
*
|
|
25
|
-
* M3's rule — failure is a message and an exit code, never a stack — needs both to
|
|
26
|
-
* be typed members, so a rejection is the `ContractRejected` arm carrying its
|
|
27
|
-
* sentence and its exit code, and `ContractProtocolError` is built from it only
|
|
28
|
-
* where generation stops.
|
|
29
|
-
*
|
|
30
|
-
* # The hash is core's, never a second implementation
|
|
31
|
-
*
|
|
32
|
-
* The advertised `protocol.hash` is recomputed with `computeClientContractHash` —
|
|
33
|
-
* the function the compiler stamps it with (M6a) — and compared. A hash computed
|
|
34
|
-
* here would be a second source of the Contract's identity.
|
|
35
|
-
*
|
|
36
|
-
* # Bodies the hash cannot be recomputed over (S0)
|
|
37
|
-
*
|
|
38
|
-
* The ClientContract type admits values RFC 8785 cannot encode, and a valid JSON
|
|
39
|
-
* body produces two of them: `1e999` parses to `Infinity`, and `"\ud800"` parses to
|
|
40
|
-
* a lone surrogate. Core rejects both with `CanonicalJsonError`. No server can have
|
|
41
|
-
* stamped a hash over such a body — its own canonicalizer would have refused — so it
|
|
42
|
-
* is refused at THIS step, as `hash-mismatch` with its own sentence and core's path,
|
|
43
|
-
* never let escape as a stack. Both values are ones the ClientContract TYPE admits
|
|
44
|
-
* (`number`, `string`), so core's structure check accepts them by design; what
|
|
45
|
-
* fails is §19.2's recompute. ONLY that class is caught: anything else thrown while
|
|
46
|
-
* hashing is not a known property of the input, and keeps its stack as a defect.
|
|
47
|
-
*/
|
|
48
|
-
/** The protocol versions this generator speaks (§19.1: support is explicit). */
|
|
49
2
|
export const SUPPORTED_PROTOCOL_VERSIONS = new Set([
|
|
50
3
|
AVENTARA_PROTOCOL_VERSION,
|
|
51
4
|
]);
|
|
52
|
-
/** A refused contract stops generation; the adapter CLI's precedent is 1 (M3). */
|
|
53
5
|
const REFUSED_EXIT_CODE = 1;
|
|
54
6
|
const CHECK_ENTRYPOINT = "Check that the entrypoint is the deployed Aventara entrypoint (origin plus mount path).";
|
|
55
7
|
export async function acceptClientContract(body) {
|
|
@@ -84,7 +36,6 @@ export async function acceptClientContract(body) {
|
|
|
84
36
|
}
|
|
85
37
|
return { accepted: true, contract };
|
|
86
38
|
}
|
|
87
|
-
/** The refusal that stops generation, carrying the rejection's reason and sentence. */
|
|
88
39
|
export class ContractProtocolError extends Error {
|
|
89
40
|
name = "ContractProtocolError";
|
|
90
41
|
reason;
|
|
@@ -95,11 +46,6 @@ export class ContractProtocolError extends Error {
|
|
|
95
46
|
this.exitCode = rejection.exitCode;
|
|
96
47
|
}
|
|
97
48
|
}
|
|
98
|
-
/**
|
|
99
|
-
* `protocol.version` where the body states a number there, `undefined` otherwise.
|
|
100
|
-
* A read, not a rule: it judges nothing, and a body it cannot read a version from
|
|
101
|
-
* is left for the structure step to describe.
|
|
102
|
-
*/
|
|
103
49
|
function advertisedProtocolVersion(body) {
|
|
104
50
|
if (!isJsonObject(body) || !Object.hasOwn(body, "protocol")) {
|
|
105
51
|
return undefined;
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import type { ClientEntrypoint } from "../config/client-config.interface.js";
|
|
2
2
|
/**
|
|
3
|
-
* §12.5's discovery request: one `GET <entrypoint>/_contract`, with no headers —
|
|
4
|
-
* §12.4: *"The contract GET itself requires neither header because it is the
|
|
5
|
-
* discovery mechanism."* The entrypoint is the CLIENT config's URL (§15.2,
|
|
6
|
-
* resolved in S2), not the server's mount-path setting.
|
|
7
|
-
*
|
|
8
3
|
* # Transport, and only transport
|
|
9
4
|
*
|
|
10
5
|
* This file's success is "a JSON value arrived". It does not judge the value: a
|
|
11
6
|
* body that is a JSON array is still a successful fetch, and refusing it is
|
|
12
|
-
* `contract.acceptance.ts`'s job. Every way of failing to produce a JSON value is
|
|
13
|
-
* `ContractTransportError`, a different class from `ContractProtocolError`,
|
|
14
|
-
* because the two have different remedies — reach the deployment, versus
|
|
15
|
-
* or upgrade against what it served — and a message that blurs them
|
|
16
|
-
* developer to the wrong one.
|
|
7
|
+
* `contract.acceptance.ts`'s job. Every way of failing to produce a JSON value is
|
|
8
|
+
* a `ContractTransportError`, a different class from `ContractProtocolError`,
|
|
9
|
+
* because the two have different remedies — reach the deployment, versus
|
|
10
|
+
* regenerate or upgrade against what it served — and a message that blurs them
|
|
11
|
+
* sends the developer to the wrong one.
|
|
17
12
|
*
|
|
18
13
|
* `fetch` is injected, so the transport is tested without a server; the default is
|
|
19
14
|
* the platform's.
|
|
@@ -26,8 +21,8 @@ export type ContractFetch = (url: URL, init: {
|
|
|
26
21
|
readonly headers?: Readonly<Record<string, string>>;
|
|
27
22
|
}) => Promise<ContractResponse>;
|
|
28
23
|
/**
|
|
29
|
-
* What a conditional GET answers when the served ClientContract is the one named
|
|
30
|
-
*
|
|
24
|
+
* What a conditional GET answers when the served ClientContract is the one named:
|
|
25
|
+
* no body, nothing to judge.
|
|
31
26
|
*/
|
|
32
27
|
export declare const CONTRACT_NOT_MODIFIED: unique symbol;
|
|
33
28
|
export type ContractTransportFailureReason =
|
|
@@ -46,9 +41,9 @@ export declare class ContractTransportError extends Error {
|
|
|
46
41
|
}
|
|
47
42
|
/**
|
|
48
43
|
* `<entrypoint>/_contract`: the canonical mount path and the route, concatenated.
|
|
49
|
-
* The path's canonical form is resolution's
|
|
50
|
-
*
|
|
51
|
-
*
|
|
44
|
+
* The path's canonical form is resolution's — root is `""` — so the join repairs
|
|
45
|
+
* nothing and special-cases nothing. URL resolution would be wrong here: `new
|
|
46
|
+
* URL("_contract", "https://h/api")` replaces `api`.
|
|
52
47
|
*/
|
|
53
48
|
export declare function contractUrlOf(entrypoint: ClientEntrypoint): URL;
|
|
54
49
|
/** The URL as it may be printed: credentials in the entrypoint never are. */
|
|
@@ -56,8 +51,8 @@ export declare function displayUrl(url: URL): string;
|
|
|
56
51
|
/**
|
|
57
52
|
* GETs the ClientContract and returns the parsed body, unjudged — or, when
|
|
58
53
|
* `ifNoneMatch` (a quoted contract hash, the deployment's entity tag) is sent and
|
|
59
|
-
* the deployment answers `304`, {@link CONTRACT_NOT_MODIFIED}
|
|
60
|
-
*
|
|
54
|
+
* the deployment answers `304`, {@link CONTRACT_NOT_MODIFIED}. A `304` to a
|
|
55
|
+
* request that sent none is no answer, like any other non-2xx.
|
|
61
56
|
*
|
|
62
57
|
* @throws ContractTransportError when no JSON value arrives.
|
|
63
58
|
*/
|
|
@@ -1,7 +1,3 @@
|
|
|
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
1
|
export const CONTRACT_NOT_MODIFIED = Symbol("aventara.contract-not-modified");
|
|
6
2
|
export class ContractTransportError extends Error {
|
|
7
3
|
reason;
|
|
@@ -11,32 +7,17 @@ export class ContractTransportError extends Error {
|
|
|
11
7
|
this.reason = reason;
|
|
12
8
|
}
|
|
13
9
|
}
|
|
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
10
|
export function contractUrlOf(entrypoint) {
|
|
21
11
|
const url = new URL(entrypoint.deployment.href);
|
|
22
12
|
url.pathname = `${entrypoint.path}/_contract`;
|
|
23
13
|
return url;
|
|
24
14
|
}
|
|
25
|
-
/** The URL as it may be printed: credentials in the entrypoint never are. */
|
|
26
15
|
export function displayUrl(url) {
|
|
27
16
|
const shown = new URL(url.href);
|
|
28
17
|
shown.username = "";
|
|
29
18
|
shown.password = "";
|
|
30
19
|
return shown.href;
|
|
31
20
|
}
|
|
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
21
|
export async function fetchClientContractBody(entrypoint, fetch = globalThis.fetch, ifNoneMatch) {
|
|
41
22
|
const url = contractUrlOf(entrypoint);
|
|
42
23
|
const shown = displayUrl(url);
|
|
@@ -67,14 +48,9 @@ export async function fetchClientContractBody(entrypoint, fetch = globalThis.fet
|
|
|
67
48
|
return JSON.parse(text);
|
|
68
49
|
}
|
|
69
50
|
catch (error) {
|
|
70
|
-
// The body itself is not printed: it may be a whole HTML page.
|
|
71
51
|
throw new ContractTransportError("body-not-json", `The response from ${shown} is not JSON, so it is not a ClientContract. ${remedy}`, { cause: error });
|
|
72
52
|
}
|
|
73
53
|
}
|
|
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
54
|
function describeCause(error) {
|
|
79
55
|
if (!(error instanceof Error)) {
|
|
80
56
|
return String(error);
|
|
@@ -2,12 +2,11 @@ import type { ClientContract } 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
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* the `ContractRejected` value becomes a thrown `ContractProtocolError`.
|
|
5
|
+
* Where the pipeline stops on a rejection, so it is where the `ContractRejected`
|
|
6
|
+
* value becomes a thrown `ContractProtocolError`.
|
|
8
7
|
*/
|
|
9
8
|
export interface ClientContractLoadInput {
|
|
10
|
-
/** The client config's resolved entrypoint
|
|
9
|
+
/** The client config's resolved entrypoint. */
|
|
11
10
|
readonly entrypoint: ClientEntrypoint;
|
|
12
11
|
/** Injected for tests; the platform `fetch` otherwise. */
|
|
13
12
|
readonly fetch?: ContractFetch;
|
|
@@ -17,7 +16,7 @@ export interface ClientContractLoadInput {
|
|
|
17
16
|
* @throws ContractProtocolError when what arrived is refused.
|
|
18
17
|
*/
|
|
19
18
|
export declare function loadClientContract(input: ClientContractLoadInput): Promise<ClientContract>;
|
|
20
|
-
/** A ClientContract loaded against one the caller already holds
|
|
19
|
+
/** A ClientContract loaded against one the caller already holds. */
|
|
21
20
|
export interface ClientContractSince {
|
|
22
21
|
readonly contract: ClientContract;
|
|
23
22
|
/** The deployment answered `304`: `contract` is the one the caller held. */
|
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
import { acceptClientContract, ContractProtocolError, } from "./contract.acceptance.js";
|
|
2
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
3
|
export async function loadClientContract(input) {
|
|
8
4
|
return accepted(await fetchClientContractBody(input.entrypoint, input.fetch), input);
|
|
9
5
|
}
|
|
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
6
|
export async function loadClientContractSince(input, stored) {
|
|
17
7
|
if (stored === undefined) {
|
|
18
8
|
return { contract: await loadClientContract(input), notModified: false };
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The banner line that says who owns a file. It is what the output writer reads
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* The banner line that says who owns a file. It is what the output writer reads to
|
|
3
|
+
* decide that a directory is a previous generation it may replace whole rather
|
|
4
|
+
* than someone's files it would delete — so it is its own constant, and must not
|
|
5
|
+
* change between generator versions: an output written by an older generator has
|
|
6
|
+
* to stay recognisable to a newer one.
|
|
7
7
|
*/
|
|
8
8
|
export declare const GENERATED_OWNERSHIP_LINE = "/* !!! Generated by @aventara/client. Do not edit. !!! */";
|
|
9
9
|
/**
|
|
10
|
-
* The ownership banner every emitted file opens with
|
|
11
|
-
* generated/do-not-edit").
|
|
10
|
+
* The ownership banner every emitted file opens with.
|
|
12
11
|
*
|
|
13
|
-
* The Biome suppressions are EMITTED rather than configured
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* The Biome suppressions are EMITTED rather than configured, so the output stays
|
|
13
|
+
* correct wherever a consumer drops it, including a repository whose formatter
|
|
14
|
+
* nobody here chose.
|
|
16
15
|
*
|
|
17
16
|
* Nothing in it can change between two runs over one contract: no timestamp, no
|
|
18
|
-
* generator version, no host, no path
|
|
19
|
-
*
|
|
17
|
+
* generator version, no host, no path — any of them would break the exit gate's
|
|
18
|
+
* byte-equality on the first re-run. And no driver prose.
|
|
20
19
|
*/
|
|
21
20
|
export declare const GENERATED_BANNER: readonly string[];
|
|
22
21
|
/** `source` with the banner before it and one blank line between. */
|
|
@@ -1,40 +1,14 @@
|
|
|
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
1
|
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
2
|
export const GENERATED_BANNER = [
|
|
22
3
|
"// biome-ignore-all format: generated output; these bytes are the artifact",
|
|
23
4
|
"// biome-ignore-all lint: generated output",
|
|
24
5
|
GENERATED_OWNERSHIP_LINE,
|
|
25
6
|
"/* Regenerate with `avclient generate`. */",
|
|
26
7
|
];
|
|
27
|
-
/** `source` with the banner before it and one blank line between. */
|
|
28
8
|
export function withGeneratedBanner(source) {
|
|
29
9
|
return `${GENERATED_BANNER.join("\n")}\n\n${source}`;
|
|
30
10
|
}
|
|
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
11
|
export const GENERATED_OWNERSHIP_HEAD_BYTES = 1024;
|
|
37
|
-
/** Whether `head` — a file's first bytes, as text — carries the ownership line. */
|
|
38
12
|
export function carriesGeneratedOwnership(head) {
|
|
39
13
|
return head
|
|
40
14
|
.slice(0, GENERATED_OWNERSHIP_HEAD_BYTES)
|
|
@@ -1,32 +1,28 @@
|
|
|
1
1
|
import type { ClientContract } from "@aventara/core";
|
|
2
2
|
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
3
|
+
import { type ClientModuleStyle } from "./module-style.interface.js";
|
|
3
4
|
import { type EmittedNames } from "./name.deriver.js";
|
|
4
5
|
/**
|
|
5
|
-
* `generated/client.ts` — the typed surface
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
8
|
-
* (Q7).
|
|
6
|
+
* `generated/client.ts` — the typed surface: the class `AvClient`, its options
|
|
7
|
+
* `AvClientOptions` and the per-call `CallOptions`, and the ready instance
|
|
8
|
+
* `avClient`, this module's default export.
|
|
9
9
|
*
|
|
10
10
|
* Every argument and result type is an instantiation of core's own derivation,
|
|
11
|
-
* copied under `generated/derivation
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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`.
|
|
11
|
+
* copied under `generated/derivation/`: the call grammar over the carrier's
|
|
12
|
+
* contract, in the client's forms. Nothing here re-spells a rule. A call resolves
|
|
13
|
+
* to the data, a first-style miss to `null`.
|
|
17
14
|
*
|
|
18
|
-
* Each surface is one mapped alias over the one contract
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* transactions (P3), in the type as in the runtime.
|
|
15
|
+
* Each surface is one mapped alias over the one contract. A Resource is reached by
|
|
16
|
+
* its contract name, except where that name is one of the client's own members,
|
|
17
|
+
* which renames the property only. `tx` and `transaction` exist iff the contract
|
|
18
|
+
* advertises `interactive` transactions, in the type as in the runtime.
|
|
23
19
|
*
|
|
24
20
|
* Also declared here, for `types.d.ts` alone, the two helpers the named types are
|
|
25
|
-
* aliases of
|
|
21
|
+
* aliases of: `ResourceRecord` and `ResourceArgument`.
|
|
26
22
|
*
|
|
27
|
-
* At runtime
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
23
|
+
* At runtime the class builds one frozen object per Resource from the advertised
|
|
24
|
+
* operations — each variant a function that runs the operation through the
|
|
25
|
+
* transport, reading the fetch when it is called — and the default entrypoint is
|
|
26
|
+
* the generated one unless the options name another.
|
|
31
27
|
*/
|
|
32
|
-
export declare function emitClientSurfaceModule(contract: ClientContract, names: EmittedNames): EmittedModule;
|
|
28
|
+
export declare function emitClientSurfaceModule(contract: ClientContract, names: EmittedNames, style: ClientModuleStyle): EmittedModule;
|
|
@@ -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" +
|