@aventara/client 0.1.0-pilot.1 → 0.1.0-pilot.3
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 +44 -9
- 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 +20 -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 +8 -9
- package/dist/init/client-init.planner.d.ts +1 -9
- package/dist/init/client-init.planner.js +16 -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 +49 -27
- package/dist/output/output.validator.js +113 -74
- package/dist/output/output.writer.d.ts +59 -52
- package/dist/output/output.writer.js +72 -134
- package/package.json +6 -4
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createFilesMatcher, getTsconfig, parseTsconfig, } from "get-tsconfig";
|
|
4
|
+
import { ClientConfigError } from "./config.resolver.js";
|
|
5
|
+
export function locateTsconfig(location) {
|
|
6
|
+
if (location.tsconfigFile !== undefined) {
|
|
7
|
+
return namedTsconfig(location.tsconfigFile);
|
|
8
|
+
}
|
|
9
|
+
const found = getTsconfig(location.generateAt);
|
|
10
|
+
if (found === null) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
if (found.config.references === undefined ||
|
|
14
|
+
createFilesMatcher(found)(location.entryFile) !== undefined) {
|
|
15
|
+
return found;
|
|
16
|
+
}
|
|
17
|
+
for (const reference of found.config.references) {
|
|
18
|
+
const referenced = referencedTsconfig(found.path, reference.path);
|
|
19
|
+
if (referenced !== undefined &&
|
|
20
|
+
createFilesMatcher(referenced)(location.entryFile) !== undefined) {
|
|
21
|
+
return referenced;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return found;
|
|
25
|
+
}
|
|
26
|
+
function namedTsconfig(file) {
|
|
27
|
+
try {
|
|
28
|
+
readFileSync(file, "utf8");
|
|
29
|
+
return { path: file, config: parseTsconfig(file) };
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const reason = error instanceof Error && "code" in error
|
|
33
|
+
? String(error.code)
|
|
34
|
+
: error instanceof Error
|
|
35
|
+
? error.message
|
|
36
|
+
: String(error);
|
|
37
|
+
throw new ClientConfigError(`tsconfigFile names ${file}, which could not be read (${reason}); it must name the tsconfig.json your project compiles the generated client with.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function referencedTsconfig(from, reference) {
|
|
41
|
+
const target = path.resolve(path.dirname(from), reference);
|
|
42
|
+
for (const candidate of [target, path.join(target, "tsconfig.json")]) {
|
|
43
|
+
try {
|
|
44
|
+
readFileSync(candidate, "utf8");
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
return { path: candidate, config: parseTsconfig(candidate) };
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
@@ -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;
|