@aventara/client 1.0.0-rc.0 → 1.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +651 -0
- package/README.md +195 -141
- package/dist/cli/command.parser.d.ts +7 -6
- package/dist/cli/command.parser.js +13 -13
- package/dist/cli/generation-failure.renderer.d.ts +2 -1
- package/dist/cli/generation-failure.renderer.js +24 -24
- package/dist/cli/generation-success.renderer.d.ts +2 -1
- package/dist/cli/step.reporter.d.ts +2 -1
- package/dist/cli/terminal.prompter.d.ts +2 -1
- package/dist/config/client-config.interface.d.ts +24 -7
- package/dist/config/config.loader.d.ts +12 -7
- package/dist/config/config.loader.js +26 -8
- package/dist/config/config.resolver.d.ts +16 -9
- package/dist/config/config.resolver.js +40 -12
- package/dist/config/env.cascade.d.ts +4 -8
- package/dist/config/env.cascade.js +7 -7
- package/dist/config/module-style.resolver.d.ts +4 -7
- package/dist/config/module-style.resolver.js +4 -4
- package/dist/config/tsconfig.locator.d.ts +3 -2
- package/dist/config/tsconfig.locator.js +16 -4
- package/dist/contract/contract.acceptance.d.ts +4 -5
- package/dist/contract/contract.acceptance.js +5 -5
- package/dist/contract/contract.fetcher.d.ts +5 -4
- package/dist/contract/contract.fetcher.js +6 -6
- package/dist/contract/contract.loader.d.ts +9 -8
- package/dist/contract/contract.loader.js +2 -2
- package/dist/emit/access.emitter.d.ts +12 -0
- package/dist/emit/access.emitter.js +263 -0
- package/dist/emit/banner.emitter.d.ts +6 -1
- package/dist/emit/banner.emitter.js +12 -8
- package/dist/emit/client-surface.emitter.d.ts +2 -2
- package/dist/emit/client-surface.emitter.js +136 -41
- package/dist/emit/client-tree.emitter.d.ts +11 -4
- package/dist/emit/client-tree.emitter.js +51 -17
- package/dist/emit/contract-carrier.emitter.d.ts +3 -3
- package/dist/emit/derivation.emitter.d.ts +3 -3
- package/dist/emit/derivation.emitter.js +7 -3
- package/dist/emit/descriptor.emitter.d.ts +2 -2
- package/dist/emit/descriptor.emitter.js +1 -1
- package/dist/emit/emitted-tree.interface.d.ts +1 -1
- package/dist/emit/enum.emitter.d.ts +2 -2
- package/dist/emit/field-check.emitter.d.ts +22 -0
- package/dist/emit/field-check.emitter.js +592 -0
- package/dist/emit/json-shape.emitter.d.ts +2 -2
- package/dist/emit/module-specifier.scanner.d.ts +2 -1
- package/dist/emit/name.deriver.d.ts +8 -9
- package/dist/emit/name.deriver.js +18 -15
- package/dist/emit/named-type.emitter.d.ts +2 -2
- package/dist/emit/named-type.emitter.js +12 -2
- package/dist/emit/runtime.emitter.d.ts +6 -5
- package/dist/emit/runtime.emitter.js +215 -116
- package/dist/emit/scalar.codec.d.ts +1 -1
- package/dist/emit/scalar.codec.js +246 -29
- package/dist/emit/transaction.emitter.js +30 -14
- package/dist/generate.d.ts +5 -4
- package/dist/generate.js +3 -3
- package/dist/hydration/hydration-tag.format.d.ts +11 -0
- package/dist/hydration/hydration-tag.format.js +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/init/client-init.errors.d.ts +4 -4
- package/dist/init/client-init.errors.js +4 -4
- package/dist/init/client-init.orchestrator.js +7 -7
- package/dist/init/client-init.planner.d.ts +3 -2
- package/dist/init/client-init.questions.d.ts +5 -16
- package/dist/init/client-init.questions.js +12 -12
- package/dist/init/client-project.inspector.d.ts +2 -2
- package/dist/init/client-project.inspector.js +3 -3
- package/dist/internal.d.ts +25 -0
- package/dist/internal.js +11 -0
- package/dist/output/output.validator.d.ts +3 -4
- package/dist/output/output.validator.js +2 -2
- package/dist/output/output.writer.d.ts +8 -7
- package/dist/output/output.writer.js +12 -12
- package/package.json +9 -5
|
@@ -4,9 +4,9 @@ import { GENERATION_STEPS, runGenerate, } from "../cli/generate.command.js";
|
|
|
4
4
|
import { createStepReporter } from "../cli/step.reporter.js";
|
|
5
5
|
import { CLIENT_CONFIG_FILE } from "../config/config.loader.js";
|
|
6
6
|
import { resolveModuleStyle } from "../config/module-style.resolver.js";
|
|
7
|
-
import {
|
|
7
|
+
import { AvContractTransportError } from "../contract/contract.fetcher.js";
|
|
8
8
|
import { CLIENT_ENTRY_FILE } from "../emit/emitted-tree.interface.js";
|
|
9
|
-
import {
|
|
9
|
+
import { AvClientInitNotConfirmedError, AvClientInitStepError, } from "./client-init.errors.js";
|
|
10
10
|
import { GENERATE_SCRIPT, planClientInit } from "./client-init.planner.js";
|
|
11
11
|
import { resolveClientInitAnswers } from "./client-init.questions.js";
|
|
12
12
|
import { inspectClientProject } from "./client-project.inspector.js";
|
|
@@ -40,11 +40,11 @@ export async function runClientInit(command, io) {
|
|
|
40
40
|
const them = plan.conflicts.length === 1 ? "it" : "them";
|
|
41
41
|
const has = plan.conflicts.length === 1 ? "has" : "have";
|
|
42
42
|
if (!io.interactive) {
|
|
43
|
-
throw new
|
|
43
|
+
throw new AvClientInitNotConfirmedError(`not initializing, and nothing was touched: ${plan.conflicts.join(", ")} already ${has} other content and nobody can be asked (stdin is not a terminal); run \`avclient init --yes\` to replace ${them}.`);
|
|
44
44
|
}
|
|
45
45
|
io.stderr(`avclient: warning: ${plan.conflicts.join(", ")} already ${has} other content, and initializing will replace ${them}.\n`);
|
|
46
46
|
if (!(await io.confirm("avclient: replace them and initialize? [y/N] "))) {
|
|
47
|
-
throw new
|
|
47
|
+
throw new AvClientInitNotConfirmedError(`cancelled, and nothing was touched; run again and answer yes to replace ${them}.`);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
const writes = plan.conflicts.length === 0 ? plan.keeping : plan.replacing;
|
|
@@ -80,7 +80,7 @@ export async function runClientInit(command, io) {
|
|
|
80
80
|
onOutput: progress.output,
|
|
81
81
|
});
|
|
82
82
|
if (installed.code !== 0) {
|
|
83
|
-
throw new
|
|
83
|
+
throw new AvClientInitStepError(`\`${install}\` exited with code ${installed.code} after ${project.configFile ?? CLIENT_CONFIG_FILE} was written; fix what it reports and run \`${install}\` again:\n${installed.stderr.trimEnd()}`);
|
|
84
84
|
}
|
|
85
85
|
});
|
|
86
86
|
if (command.skipGenerate) {
|
|
@@ -92,8 +92,8 @@ export async function runClientInit(command, io) {
|
|
|
92
92
|
return await runGenerate(io, command.yes, progress);
|
|
93
93
|
}
|
|
94
94
|
catch (error) {
|
|
95
|
-
if (error instanceof
|
|
96
|
-
throw new
|
|
95
|
+
if (error instanceof AvContractTransportError) {
|
|
96
|
+
throw new AvClientInitStepError(`the project is set up, but the server did not answer (${error.message.replace(/\.$/, "")}); once it is running, run \`avclient generate\`.`);
|
|
97
97
|
}
|
|
98
98
|
throw error;
|
|
99
99
|
}
|
|
@@ -2,11 +2,11 @@ import type { ClientInitAnswers } from "./client-init.questions.js";
|
|
|
2
2
|
import type { ClientProject } from "./client-project.inspector.js";
|
|
3
3
|
export declare const GENERATE_SCRIPT = "avclient:generate";
|
|
4
4
|
/** One change to a project file: its new content, or `undefined` to remove it. */
|
|
5
|
-
|
|
5
|
+
type ClientInitWrite = {
|
|
6
6
|
readonly path: string;
|
|
7
7
|
readonly content: string | undefined;
|
|
8
8
|
};
|
|
9
|
-
|
|
9
|
+
type ClientInitPlan = {
|
|
10
10
|
readonly keeping: readonly ClientInitWrite[];
|
|
11
11
|
readonly replacing: readonly ClientInitWrite[];
|
|
12
12
|
readonly conflicts: readonly string[];
|
|
@@ -16,3 +16,4 @@ export declare function planClientInit(input: {
|
|
|
16
16
|
readonly answers: ClientInitAnswers;
|
|
17
17
|
readonly clientVersion: string;
|
|
18
18
|
}): ClientInitPlan;
|
|
19
|
+
export {};
|
|
@@ -8,25 +8,14 @@ export type ClientInitAnswers = {
|
|
|
8
8
|
};
|
|
9
9
|
type QuestionId = "entrypoint" | "envVar" | "generateAt" | "packageManager";
|
|
10
10
|
/** An answer the question cannot take. A refusal: one sentence. */
|
|
11
|
-
export declare class
|
|
12
|
-
readonly name = "
|
|
11
|
+
export declare class AvClientInitAnswerError extends Error {
|
|
12
|
+
readonly name = "AvClientInitAnswerError";
|
|
13
13
|
}
|
|
14
14
|
/** Nobody can answer and some questions are unanswered. A refusal: one sentence. */
|
|
15
|
-
export declare class
|
|
16
|
-
readonly name = "
|
|
15
|
+
export declare class AvClientInitUnansweredError extends Error {
|
|
16
|
+
readonly name = "AvClientInitUnansweredError";
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
readonly id: QuestionId;
|
|
20
|
-
readonly flag: string;
|
|
21
|
-
readonly placeholder: string;
|
|
22
|
-
readonly label: string;
|
|
23
|
-
readonly defaultFor: (detectedPackageManager: PackageManager) => string;
|
|
24
|
-
/** @throws ClientInitAnswerError */
|
|
25
|
-
readonly parse: (raw: string) => string;
|
|
26
|
-
};
|
|
27
|
-
export declare const DEFAULT_ENV_VAR = "AVENTARA_API_URL";
|
|
28
|
-
export declare const CLIENT_INIT_QUESTIONS: readonly ClientInitQuestion[];
|
|
29
|
-
export type ClientInitSources = {
|
|
18
|
+
type ClientInitSources = {
|
|
30
19
|
readonly given: Readonly<Partial<Record<QuestionId, string>>>;
|
|
31
20
|
/** `--no-env-var`: the entrypoint is written as a literal, and no variable is asked. */
|
|
32
21
|
readonly noEnvVar: boolean;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { resolveEntrypoint } from "../config/config.resolver.js";
|
|
2
2
|
export const PACKAGE_MANAGERS = ["npm", "pnpm"];
|
|
3
|
-
export class
|
|
4
|
-
name = "
|
|
3
|
+
export class AvClientInitAnswerError extends Error {
|
|
4
|
+
name = "AvClientInitAnswerError";
|
|
5
5
|
}
|
|
6
|
-
export class
|
|
7
|
-
name = "
|
|
6
|
+
export class AvClientInitUnansweredError extends Error {
|
|
7
|
+
name = "AvClientInitUnansweredError";
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const DEFAULT_ENV_VAR = "AVENTARA_API_URL";
|
|
10
|
+
const CLIENT_INIT_QUESTIONS = [
|
|
11
11
|
{
|
|
12
12
|
id: "entrypoint",
|
|
13
13
|
flag: "--entrypoint",
|
|
@@ -19,7 +19,7 @@ export const CLIENT_INIT_QUESTIONS = [
|
|
|
19
19
|
resolveEntrypoint(raw.trim());
|
|
20
20
|
}
|
|
21
21
|
catch (error) {
|
|
22
|
-
throw new
|
|
22
|
+
throw new AvClientInitAnswerError(error.message.replace(/\.$/, ""));
|
|
23
23
|
}
|
|
24
24
|
return raw.trim();
|
|
25
25
|
},
|
|
@@ -33,7 +33,7 @@ export const CLIENT_INIT_QUESTIONS = [
|
|
|
33
33
|
parse: (raw) => {
|
|
34
34
|
const name = raw.trim();
|
|
35
35
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
|
|
36
|
-
throw new
|
|
36
|
+
throw new AvClientInitAnswerError(`${JSON.stringify(raw)} is not an environment variable name.`);
|
|
37
37
|
}
|
|
38
38
|
return name;
|
|
39
39
|
},
|
|
@@ -47,7 +47,7 @@ export const CLIENT_INIT_QUESTIONS = [
|
|
|
47
47
|
parse: (raw) => {
|
|
48
48
|
const directory = raw.trim();
|
|
49
49
|
if (directory === "") {
|
|
50
|
-
throw new
|
|
50
|
+
throw new AvClientInitAnswerError("--generate-at takes a directory.");
|
|
51
51
|
}
|
|
52
52
|
return directory;
|
|
53
53
|
},
|
|
@@ -61,7 +61,7 @@ export const CLIENT_INIT_QUESTIONS = [
|
|
|
61
61
|
parse: (raw) => {
|
|
62
62
|
const manager = PACKAGE_MANAGERS.find((candidate) => candidate === raw.trim());
|
|
63
63
|
if (manager === undefined) {
|
|
64
|
-
throw new
|
|
64
|
+
throw new AvClientInitAnswerError(`${JSON.stringify(raw)} is not a supported package manager; choose ${PACKAGE_MANAGERS.join(" or ")}.`);
|
|
65
65
|
}
|
|
66
66
|
return manager;
|
|
67
67
|
},
|
|
@@ -90,7 +90,7 @@ export async function resolveClientInitAnswers(sources) {
|
|
|
90
90
|
break;
|
|
91
91
|
}
|
|
92
92
|
catch (error) {
|
|
93
|
-
if (!(error instanceof
|
|
93
|
+
if (!(error instanceof AvClientInitAnswerError)) {
|
|
94
94
|
throw error;
|
|
95
95
|
}
|
|
96
96
|
sources.say(error.message);
|
|
@@ -102,7 +102,7 @@ export async function resolveClientInitAnswers(sources) {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
if (missing.length > 0) {
|
|
105
|
-
throw new
|
|
105
|
+
throw new AvClientInitUnansweredError(`stdin is not a terminal, so nothing can be asked: pass ${missing.join(", ")}, or pass --yes to accept their defaults.`);
|
|
106
106
|
}
|
|
107
107
|
return {
|
|
108
108
|
entrypoint: answered.entrypoint,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { PackageManager } from "./client-init.questions.js";
|
|
2
2
|
/** The frontend `avclient init` serves, read before anything is asked or written. */
|
|
3
3
|
/** The directory is not a project `avclient init` can set up. A refusal: nothing written. */
|
|
4
|
-
export declare class
|
|
5
|
-
readonly name = "
|
|
4
|
+
export declare class AvClientProjectRefusedError extends Error {
|
|
5
|
+
readonly name = "AvClientProjectRefusedError";
|
|
6
6
|
}
|
|
7
7
|
export type ClientProject = {
|
|
8
8
|
readonly directory: string;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { findClientConfigFile } from "../config/config.loader.js";
|
|
4
|
-
export class
|
|
5
|
-
name = "
|
|
4
|
+
export class AvClientProjectRefusedError extends Error {
|
|
5
|
+
name = "AvClientProjectRefusedError";
|
|
6
6
|
}
|
|
7
7
|
export function inspectClientProject(directory) {
|
|
8
8
|
const manifest = path.join(directory, "package.json");
|
|
9
9
|
if (!existsSync(manifest)) {
|
|
10
|
-
throw new
|
|
10
|
+
throw new AvClientProjectRefusedError("avclient init runs in a frontend project: no package.json here; create one first.");
|
|
11
11
|
}
|
|
12
12
|
const lockfiles = [
|
|
13
13
|
["package-lock.json", "npm"],
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@aventara/client/internal` is not public API.
|
|
3
|
+
*
|
|
4
|
+
* It holds what Aventara's own packages need from the client generator and an
|
|
5
|
+
* application does not. It carries no stability promise: any export here may
|
|
6
|
+
* change or disappear in any release, including a patch release.
|
|
7
|
+
*/
|
|
8
|
+
export type { ClientEntrypoint, EmittedEntrypoint, SameOriginEntrypoint, } from "./config/client-config.interface.js";
|
|
9
|
+
export { AvClientConfigError, sameOriginEntrypoint, } from "./config/config.resolver.js";
|
|
10
|
+
export type { EnvCascadeResolution } from "./config/env.cascade.js";
|
|
11
|
+
export { AvEnvFileError, resolveEnvCascade } from "./config/env.cascade.js";
|
|
12
|
+
export { resolveModuleStyle } from "./config/module-style.resolver.js";
|
|
13
|
+
export type { ContractAcceptance } from "./contract/contract.acceptance.js";
|
|
14
|
+
export { acceptClientContract } from "./contract/contract.acceptance.js";
|
|
15
|
+
export { accessVerbsOf } from "./emit/access.emitter.js";
|
|
16
|
+
export type { RegenerateCommand } from "./emit/banner.emitter.js";
|
|
17
|
+
export { carriesGeneratedOwnership } from "./emit/banner.emitter.js";
|
|
18
|
+
export type { ClientClassExport } from "./emit/client-tree.emitter.js";
|
|
19
|
+
export { clientEntryReExports, emitClientTree, } from "./emit/client-tree.emitter.js";
|
|
20
|
+
export type { ClientEmission, EmittedFile, EmittedTree, } from "./emit/emitted-tree.interface.js";
|
|
21
|
+
export { GENERATED_DIRECTORY } from "./emit/emitted-tree.interface.js";
|
|
22
|
+
export type { ClientModuleStyle } from "./emit/module-style.interface.js";
|
|
23
|
+
export { importSpecifier } from "./emit/module-style.interface.js";
|
|
24
|
+
export { AvGeneratedNameError } from "./emit/name.deriver.js";
|
|
25
|
+
export { HYDRATION_FORMAT } from "./hydration/hydration-tag.format.js";
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { AvClientConfigError, sameOriginEntrypoint, } from "./config/config.resolver.js";
|
|
2
|
+
export { AvEnvFileError, resolveEnvCascade } from "./config/env.cascade.js";
|
|
3
|
+
export { resolveModuleStyle } from "./config/module-style.resolver.js";
|
|
4
|
+
export { acceptClientContract } from "./contract/contract.acceptance.js";
|
|
5
|
+
export { accessVerbsOf } from "./emit/access.emitter.js";
|
|
6
|
+
export { carriesGeneratedOwnership } from "./emit/banner.emitter.js";
|
|
7
|
+
export { clientEntryReExports, emitClientTree, } from "./emit/client-tree.emitter.js";
|
|
8
|
+
export { GENERATED_DIRECTORY } from "./emit/emitted-tree.interface.js";
|
|
9
|
+
export { importSpecifier } from "./emit/module-style.interface.js";
|
|
10
|
+
export { AvGeneratedNameError } from "./emit/name.deriver.js";
|
|
11
|
+
export { HYDRATION_FORMAT } from "./hydration/hydration-tag.format.js";
|
|
@@ -14,7 +14,7 @@ export type TypeScriptResolver = () => Promise<TypeScriptCompiler | undefined>;
|
|
|
14
14
|
*/
|
|
15
15
|
export declare function typeScriptResolverFor(specifier: string, directory: string): TypeScriptResolver;
|
|
16
16
|
export declare function projectTypeScriptResolver(generateAt: string): TypeScriptResolver;
|
|
17
|
-
export declare const MINIMUM_TYPESCRIPT = "5.
|
|
17
|
+
export declare const MINIMUM_TYPESCRIPT = "5.7.0";
|
|
18
18
|
/** How deeply the tree was judged. */
|
|
19
19
|
export type OutputCheck = "types" | "syntax" | "shape";
|
|
20
20
|
export interface OutputAccepted {
|
|
@@ -45,9 +45,8 @@ export declare const SYNTAX_CHECK_UNAVAILABLE_WARNING: string;
|
|
|
45
45
|
export declare function typeScriptWithoutCompilerApiWarning(version: string, checked: "syntax" | "shape"): string;
|
|
46
46
|
/**
|
|
47
47
|
* The warning a run carries when the project's `typescript` is older than the
|
|
48
|
-
* peer range:
|
|
49
|
-
*
|
|
50
|
-
* supported compiler gives.
|
|
48
|
+
* peer range: Aventara's types are not measured under it, so its verdict
|
|
49
|
+
* would not be the one a supported compiler gives.
|
|
51
50
|
*/
|
|
52
51
|
export declare function typeScriptBelowRangeWarning(version: string, checked: "syntax" | "shape"): string;
|
|
53
52
|
/**
|
|
@@ -24,7 +24,7 @@ export function typeScriptResolverFor(specifier, directory) {
|
|
|
24
24
|
export function projectTypeScriptResolver(generateAt) {
|
|
25
25
|
return typeScriptResolverFor("typescript", generateAt);
|
|
26
26
|
}
|
|
27
|
-
export const MINIMUM_TYPESCRIPT = "5.
|
|
27
|
+
export const MINIMUM_TYPESCRIPT = "5.7.0";
|
|
28
28
|
function meetsMinimum(version) {
|
|
29
29
|
const parts = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
|
|
30
30
|
const minimum = MINIMUM_TYPESCRIPT.split(".").map(Number);
|
|
@@ -57,7 +57,7 @@ export const SYNTAX_CHECK_UNAVAILABLE_WARNING = "neither `typescript` nor Node's
|
|
|
57
57
|
export function typeScriptWithoutCompilerApiWarning(version, checked) {
|
|
58
58
|
return (`the installed \`typescript\` ${version} has no classic compiler API (\`createProgram\`), so the generated ` +
|
|
59
59
|
`output was NOT type-checked${checked === "shape" ? " or parsed — only its shape was" : " — only its shape and syntax were"}. ` +
|
|
60
|
-
"Your project's own `tsc` checks it when it compiles; a `typescript` 5.
|
|
60
|
+
"Your project's own `tsc` checks it when it compiles; a `typescript` 5.7 to 6 restores the generator's own type check.");
|
|
61
61
|
}
|
|
62
62
|
export function typeScriptBelowRangeWarning(version, checked) {
|
|
63
63
|
return (`the installed \`typescript\` ${version} is older than ${MINIMUM_TYPESCRIPT}, the lowest @aventara/client supports, so the generated ` +
|
|
@@ -5,9 +5,9 @@ import { type OutputCheck, type TypeScriptResolver } from "./output.validator.js
|
|
|
5
5
|
/** The four stages a write passes through, in order. */
|
|
6
6
|
export type OutputWriteStage = "prepare" | "temp" | "validate" | "replace";
|
|
7
7
|
/** A write that stopped; the previous output is intact unless it says otherwise. */
|
|
8
|
-
export declare class
|
|
8
|
+
export declare class AvOutputWriteError extends Error {
|
|
9
9
|
readonly stage: OutputWriteStage;
|
|
10
|
-
readonly name = "
|
|
10
|
+
readonly name = "AvOutputWriteError";
|
|
11
11
|
/**
|
|
12
12
|
* What the run said before it stopped, in the order a success returns its
|
|
13
13
|
* warnings: the emission's, the validator's, the writer's. Printed before the
|
|
@@ -19,7 +19,7 @@ export declare class OutputWriteError extends Error {
|
|
|
19
19
|
readonly warnings?: readonly string[];
|
|
20
20
|
});
|
|
21
21
|
/** The same refusal, with `earlier` said before the warnings it already carries. */
|
|
22
|
-
precededBy(earlier: readonly string[]):
|
|
22
|
+
precededBy(earlier: readonly string[]): AvOutputWriteError;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Records `earlier` as said before `defect`, ahead of anything already recorded,
|
|
@@ -32,7 +32,7 @@ export declare function precedeDefect(defect: unknown, earlier: readonly string[
|
|
|
32
32
|
*/
|
|
33
33
|
export declare function warningsRaisedBeforeDefect(defect: unknown): readonly string[];
|
|
34
34
|
export type OutputFileSystem = Pick<typeof fsPromises, "lstat" | "mkdir" | "open" | "readdir" | "rename" | "rm" | "rmdir" | "writeFile">;
|
|
35
|
-
|
|
35
|
+
interface ClientOutputWriteInput {
|
|
36
36
|
/** What `emitClientTree` returned: the tree, and the warnings it raised. */
|
|
37
37
|
readonly emission: ClientEmission;
|
|
38
38
|
readonly generateAt: AbsolutePath;
|
|
@@ -40,7 +40,7 @@ export interface ClientOutputWriteInput {
|
|
|
40
40
|
readonly resolveTypeScript?: TypeScriptResolver;
|
|
41
41
|
readonly fileSystem?: OutputFileSystem;
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
interface ClientOutputWritten {
|
|
44
44
|
readonly generateAt: AbsolutePath;
|
|
45
45
|
readonly checked: OutputCheck;
|
|
46
46
|
readonly warnings: readonly string[];
|
|
@@ -49,12 +49,13 @@ export interface ClientOutputWritten {
|
|
|
49
49
|
* Writes `emission.tree` as `<generateAt>/AvClient.ts` and
|
|
50
50
|
* `<generateAt>/generated/`.
|
|
51
51
|
*
|
|
52
|
-
* @throws
|
|
52
|
+
* @throws AvOutputWriteError when the write stops; the previous output is intact.
|
|
53
53
|
*/
|
|
54
54
|
export declare function writeClientOutput(input: ClientOutputWriteInput): Promise<ClientOutputWritten>;
|
|
55
55
|
/**
|
|
56
|
-
* @throws
|
|
56
|
+
* @throws AvOutputWriteError when `generateAt` is not a directory, `generated` is
|
|
57
57
|
* not a directory or an entry file is not a file: no confirmation repairs that.
|
|
58
58
|
*/
|
|
59
59
|
export declare function findForeignOutputContent(generateAt: AbsolutePath, fileSystem?: OutputFileSystem): Promise<readonly string[]>;
|
|
60
60
|
export declare function ownedOutputMatches(generateAt: AbsolutePath, tree: EmittedTree): Promise<boolean>;
|
|
61
|
+
export {};
|
|
@@ -4,9 +4,9 @@ import path from "node:path";
|
|
|
4
4
|
import { carriesGeneratedOwnership, GENERATED_OWNERSHIP_HEAD_BYTES, } from "../emit/banner.emitter.js";
|
|
5
5
|
import { CLIENT_ENTRY_FILES, GENERATED_DIRECTORY, LEGACY_CLIENT_ENTRY_FILES, } from "../emit/emitted-tree.interface.js";
|
|
6
6
|
import { projectTypeScriptResolver, validateOutputTree, } from "./output.validator.js";
|
|
7
|
-
export class
|
|
7
|
+
export class AvOutputWriteError extends Error {
|
|
8
8
|
stage;
|
|
9
|
-
name = "
|
|
9
|
+
name = "AvOutputWriteError";
|
|
10
10
|
warnings;
|
|
11
11
|
constructor(stage, message, options) {
|
|
12
12
|
super(message, options);
|
|
@@ -14,7 +14,7 @@ export class OutputWriteError extends Error {
|
|
|
14
14
|
this.warnings = options?.warnings ?? [];
|
|
15
15
|
}
|
|
16
16
|
precededBy(earlier) {
|
|
17
|
-
return new
|
|
17
|
+
return new AvOutputWriteError(this.stage, this.message, {
|
|
18
18
|
cause: this.cause,
|
|
19
19
|
warnings: [...earlier, ...this.warnings],
|
|
20
20
|
});
|
|
@@ -65,7 +65,7 @@ export async function writeClientOutput(input) {
|
|
|
65
65
|
...validationWarnings,
|
|
66
66
|
...writerWarnings,
|
|
67
67
|
];
|
|
68
|
-
throw error instanceof
|
|
68
|
+
throw error instanceof AvOutputWriteError
|
|
69
69
|
? error.precededBy(said)
|
|
70
70
|
: precedeDefect(error, said);
|
|
71
71
|
}
|
|
@@ -99,7 +99,7 @@ export async function writeClientOutput(input) {
|
|
|
99
99
|
validationWarnings = validation.warnings;
|
|
100
100
|
}
|
|
101
101
|
else {
|
|
102
|
-
throw new
|
|
102
|
+
throw new AvOutputWriteError("validate", `the generated output failed its ${validation.checked} check, so ${LEFT_AS_THEY_WERE(paths)} ` +
|
|
103
103
|
"This is a defect in @aventara/client; please report it with these findings:\n" +
|
|
104
104
|
validation.findings.map((finding) => ` ${finding}`).join("\n"));
|
|
105
105
|
}
|
|
@@ -267,7 +267,7 @@ async function inspectOwnedEntries(fs, paths, generatedSource = paths.generated)
|
|
|
267
267
|
const generatedStats = await lstatOrUndefined(fs, generatedSource);
|
|
268
268
|
if (generatedStats !== undefined) {
|
|
269
269
|
if (!generatedStats.isDirectory()) {
|
|
270
|
-
throw new
|
|
270
|
+
throw new AvOutputWriteError("prepare", `refusing to replace ${generatedSource}: it is not a directory, and the generator owns ` +
|
|
271
271
|
`${GENERATED_DIRECTORY}/ as one; move it, or choose another \`generateAt\`. Nothing was written.`);
|
|
272
272
|
}
|
|
273
273
|
const entries = await fs.readdir(generatedSource, {
|
|
@@ -292,7 +292,7 @@ async function inspectOwnedEntries(fs, paths, generatedSource = paths.generated)
|
|
|
292
292
|
continue;
|
|
293
293
|
}
|
|
294
294
|
if (!stats.isFile()) {
|
|
295
|
-
throw new
|
|
295
|
+
throw new AvOutputWriteError("prepare", `refusing to replace ${file}: it is not a file, and the generator owns ${name} ` +
|
|
296
296
|
"as one; move it, or choose another `generateAt`. Nothing was written.");
|
|
297
297
|
}
|
|
298
298
|
entries.push(name);
|
|
@@ -317,12 +317,12 @@ async function inspectOwnedEntries(fs, paths, generatedSource = paths.generated)
|
|
|
317
317
|
}
|
|
318
318
|
function foreignContentRefusal(paths, foreign) {
|
|
319
319
|
const them = foreign.length === 1 ? "it" : "them";
|
|
320
|
-
return new
|
|
320
|
+
return new AvOutputWriteError("prepare", `refusing to generate into ${paths.generateAt}: ${foreign.join(", ")} ${foreign.length === 1 ? "was" : "were"} ` +
|
|
321
321
|
`not generated by @aventara/client, and generating would overwrite or remove ${them}; confirm the ` +
|
|
322
322
|
`override, move ${them} out, or choose another \`generateAt\`. Nothing was written.`);
|
|
323
323
|
}
|
|
324
324
|
function notADirectory(generateAt) {
|
|
325
|
-
return new
|
|
325
|
+
return new AvOutputWriteError("prepare", `refusing to generate into ${generateAt}: it is not a directory. ` +
|
|
326
326
|
"`generateAt` must name a directory; choose another `generateAt`. Nothing was written.");
|
|
327
327
|
}
|
|
328
328
|
async function carriesOwnershipLine(fs, file) {
|
|
@@ -341,7 +341,7 @@ async function rollBack(paths, cause, undo) {
|
|
|
341
341
|
await undo();
|
|
342
342
|
}
|
|
343
343
|
catch (rollbackError) {
|
|
344
|
-
throw new
|
|
344
|
+
throw new AvOutputWriteError("replace", `the generated output could not be moved into ${paths.generateAt} (${describe(cause)}), and the ` +
|
|
345
345
|
`previous output could not be restored (${describe(rollbackError)}). The previous ` +
|
|
346
346
|
`${GENERATED_DIRECTORY}/ is intact at ${paths.previous}; the next run puts it back.`, { cause });
|
|
347
347
|
}
|
|
@@ -351,10 +351,10 @@ async function stage(name, paths, run) {
|
|
|
351
351
|
return await run();
|
|
352
352
|
}
|
|
353
353
|
catch (error) {
|
|
354
|
-
if (error instanceof
|
|
354
|
+
if (error instanceof AvOutputWriteError || !isSystemError(error)) {
|
|
355
355
|
throw error;
|
|
356
356
|
}
|
|
357
|
-
throw new
|
|
357
|
+
throw new AvOutputWriteError(name, `could not ${STAGE_ACTIONS[name]} (${describe(error)}); ${LEFT_AS_THEY_WERE(paths)}`, { cause: error });
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
const STAGE_ACTIONS = {
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aventara/client",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.10",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "Development-time generator for Aventara typed remote clients.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
8
|
-
"node": "^22.
|
|
8
|
+
"node": "^22.23.4 || >=24.22.1"
|
|
9
9
|
},
|
|
10
10
|
"main": "./dist/index.js",
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
".": {
|
|
17
17
|
"types": "./dist/index.d.ts",
|
|
18
18
|
"import": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./internal": {
|
|
21
|
+
"types": "./dist/internal.d.ts",
|
|
22
|
+
"import": "./dist/internal.js"
|
|
19
23
|
}
|
|
20
24
|
},
|
|
21
25
|
"files": [
|
|
@@ -24,12 +28,12 @@
|
|
|
24
28
|
"CHANGELOG.md"
|
|
25
29
|
],
|
|
26
30
|
"dependencies": {
|
|
27
|
-
"@aventara/core": "1.0.0-rc.
|
|
31
|
+
"@aventara/core": "1.0.0-rc.10",
|
|
28
32
|
"c12": "3.3.4",
|
|
29
33
|
"get-tsconfig": "4.10.0"
|
|
30
34
|
},
|
|
31
35
|
"peerDependencies": {
|
|
32
|
-
"typescript": ">=5.
|
|
36
|
+
"typescript": ">=5.7.0"
|
|
33
37
|
},
|
|
34
38
|
"peerDependenciesMeta": {
|
|
35
39
|
"typescript": {
|
|
@@ -40,7 +44,7 @@
|
|
|
40
44
|
"access": "public"
|
|
41
45
|
},
|
|
42
46
|
"devDependencies": {
|
|
43
|
-
"@aventara/testing": "1.0.0-rc.
|
|
47
|
+
"@aventara/testing": "1.0.0-rc.10",
|
|
44
48
|
"@types/node": "24.10.1",
|
|
45
49
|
"typescript": "^5.9.2"
|
|
46
50
|
},
|