@aventara/client 0.1.0-pilot.2 → 0.1.0-pilot.4
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 +11 -13
- package/dist/cli/command.parser.d.ts +1 -13
- package/dist/cli/generation-success.renderer.d.ts +0 -4
- package/dist/cli/terminal.prompter.d.ts +0 -6
- package/dist/cli/warning.renderer.d.ts +0 -6
- package/dist/cli.d.ts +0 -16
- package/dist/config/client-config.interface.d.ts +10 -20
- package/dist/config/config.loader.d.ts +0 -21
- package/dist/config/config.resolver.d.ts +2 -17
- package/dist/config/env.cascade.d.ts +0 -30
- package/dist/config/module-style.resolver.d.ts +0 -25
- package/dist/config/tsconfig.locator.d.ts +0 -24
- package/dist/contract/contract.acceptance.d.ts +0 -39
- package/dist/contract/contract.fetcher.d.ts +0 -29
- package/dist/contract/contract.loader.d.ts +0 -7
- package/dist/emit/banner.emitter.d.ts +0 -18
- package/dist/emit/banner.emitter.js +1 -1
- package/dist/emit/client-surface.emitter.d.ts +0 -23
- package/dist/emit/client-tree.emitter.d.ts +0 -20
- package/dist/emit/contract-carrier.emitter.d.ts +0 -7
- package/dist/emit/derivation.emitter.d.ts +0 -28
- package/dist/emit/emitted-tree.interface.d.ts +0 -53
- package/dist/emit/enum.emitter.d.ts +0 -20
- package/dist/emit/module-specifier.scanner.d.ts +0 -15
- package/dist/emit/module-style.interface.d.ts +0 -15
- package/dist/emit/name.deriver.d.ts +0 -71
- package/dist/emit/named-type.emitter.d.ts +0 -20
- package/dist/emit/runtime.emitter.d.ts +0 -50
- package/dist/emit/runtime.emitter.js +12 -19
- package/dist/emit/scalar.codec.d.ts +0 -39
- package/dist/emit/transaction.emitter.d.ts +0 -6
- package/dist/emit/transaction.emitter.js +3 -5
- package/dist/generate.d.ts +0 -33
- package/dist/index.d.ts +1 -5
- package/dist/init/client-config.template.d.ts +0 -8
- package/dist/init/client-init.orchestrator.js +7 -0
- package/dist/init/client-init.planner.d.ts +0 -1
- package/dist/init/client-init.planner.js +10 -0
- package/dist/init/client-init.questions.d.ts +0 -8
- package/dist/output/output.validator.d.ts +16 -33
- package/dist/output/output.validator.js +68 -16
- package/dist/output/output.writer.d.ts +1 -107
- package/dist/output/output.writer.js +2 -2
- package/package.json +3 -3
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
export declare const PACKAGE_MANAGERS: readonly ["npm", "pnpm"];
|
|
2
2
|
export type PackageManager = (typeof PACKAGE_MANAGERS)[number];
|
|
3
3
|
export type ClientInitAnswers = {
|
|
4
|
-
/** The deployment's entrypoint. */
|
|
5
4
|
readonly entrypoint: string;
|
|
6
|
-
/** The variable `framework.client.ts` reads it from, or `undefined` for a literal. */
|
|
7
5
|
readonly envVar: string | undefined;
|
|
8
|
-
/** Where the client is generated. */
|
|
9
6
|
readonly generateAt: string;
|
|
10
7
|
readonly packageManager: PackageManager;
|
|
11
8
|
};
|
|
@@ -27,7 +24,6 @@ export type ClientInitQuestion = {
|
|
|
27
24
|
/** @throws ClientInitAnswerError */
|
|
28
25
|
readonly parse: (raw: string) => string;
|
|
29
26
|
};
|
|
30
|
-
/** The project convention for the variable. */
|
|
31
27
|
export declare const DEFAULT_ENV_VAR = "AVENTARA_API_URL";
|
|
32
28
|
export declare const CLIENT_INIT_QUESTIONS: readonly ClientInitQuestion[];
|
|
33
29
|
export type ClientInitSources = {
|
|
@@ -40,9 +36,5 @@ export type ClientInitSources = {
|
|
|
40
36
|
readonly say: (line: string) => void;
|
|
41
37
|
readonly detectedPackageManager: PackageManager;
|
|
42
38
|
};
|
|
43
|
-
/**
|
|
44
|
-
* Flag → `--yes`'s default → the prompt → the refusal naming exactly the
|
|
45
|
-
* unanswered flags.
|
|
46
|
-
*/
|
|
47
39
|
export declare function resolveClientInitAnswers(sources: ClientInitSources): Promise<ClientInitAnswers>;
|
|
48
40
|
export {};
|
|
@@ -1,31 +1,5 @@
|
|
|
1
1
|
import type * as ts from "typescript";
|
|
2
2
|
import type { ClientEmission } from "../emit/emitted-tree.interface.js";
|
|
3
|
-
/**
|
|
4
|
-
* It judges a directory the writer has just filled with `tree`, and answers with a
|
|
5
|
-
* value — the writer decides what a rejection means for the previous output.
|
|
6
|
-
*
|
|
7
|
-
* # Three checks, the strongest available first
|
|
8
|
-
*
|
|
9
|
-
* - **Shape**, always: the directory holds exactly `tree` — no file missing, none
|
|
10
|
-
* extra, every one byte-for-byte what was emitted — every file carries the
|
|
11
|
-
* ownership line, and every module a file names is a file of the tree, spelled
|
|
12
|
-
* the project's way. The ownership line is not decoration: the NEXT run reads it
|
|
13
|
-
* to decide that this directory is a previous generation it may replace whole,
|
|
14
|
-
* so a file without it would make the next run refuse. The imports are read
|
|
15
|
-
* lexically (`module-specifier.scanner.ts`), with no compiler — so a wrongly
|
|
16
|
-
* spelled import is refused even where the type check cannot run (TypeScript 7).
|
|
17
|
-
* - **Types**, when the `typescript` optional peer resolves: a real program over
|
|
18
|
-
* the tree under a consumer's strictest plausible settings, in the project's
|
|
19
|
-
* module resolution and format (`compilerOptionsFor`). The tree is judged ALONE
|
|
20
|
-
* — the compiler host serves nothing outside the directory but TypeScript's own
|
|
21
|
-
* `lib` files, so an import the tree cannot satisfy itself fails here even when
|
|
22
|
-
* a `node_modules` beside the output could satisfy it.
|
|
23
|
-
* - **Syntax**, when it does not — or when the `typescript` that resolves has no
|
|
24
|
-
* classic compiler API: each file through Node's own TypeScript parser
|
|
25
|
-
* (`node:module`'s `stripTypeScriptTypes`), with a loud warning that the output
|
|
26
|
-
* was NOT type-checked. Degraded, never skipped, and never a refusal: the
|
|
27
|
-
* developer's own `tsc` still checks the tree when their project compiles.
|
|
28
|
-
*/
|
|
29
3
|
/** The `typescript` module, as the validator uses it. */
|
|
30
4
|
export type TypeScriptCompiler = typeof ts;
|
|
31
5
|
/**
|
|
@@ -35,12 +9,12 @@ export type TypeScriptCompiler = typeof ts;
|
|
|
35
9
|
*/
|
|
36
10
|
export type TypeScriptResolver = () => Promise<TypeScriptCompiler | undefined>;
|
|
37
11
|
/**
|
|
38
|
-
* A resolver for `specifier`, looked up from
|
|
39
|
-
*
|
|
12
|
+
* A resolver for `specifier`, looked up from `directory` the way Node looks up
|
|
13
|
+
* a package from a file there: its `node_modules`, then each parent's.
|
|
40
14
|
*/
|
|
41
|
-
export declare function typeScriptResolverFor(specifier: string): TypeScriptResolver;
|
|
42
|
-
|
|
43
|
-
export declare const
|
|
15
|
+
export declare function typeScriptResolverFor(specifier: string, directory: string): TypeScriptResolver;
|
|
16
|
+
export declare function projectTypeScriptResolver(generateAt: string): TypeScriptResolver;
|
|
17
|
+
export declare const MINIMUM_TYPESCRIPT = "5.5.0";
|
|
44
18
|
/** How deeply the tree was judged. */
|
|
45
19
|
export type OutputCheck = "types" | "syntax" | "shape";
|
|
46
20
|
export interface OutputAccepted {
|
|
@@ -70,6 +44,15 @@ export declare const SYNTAX_CHECK_UNAVAILABLE_WARNING: string;
|
|
|
70
44
|
*/
|
|
71
45
|
export declare function typeScriptWithoutCompilerApiWarning(version: string, checked: "syntax" | "shape"): string;
|
|
72
46
|
/**
|
|
73
|
-
*
|
|
47
|
+
* The warning a run carries when the project's `typescript` is older than the
|
|
48
|
+
* peer range: its compiler API lacks options the check sets (`Bundler`
|
|
49
|
+
* resolution, `verbatimModuleSyntax`), so its verdict would not be the one a
|
|
50
|
+
* supported compiler gives.
|
|
51
|
+
*/
|
|
52
|
+
export declare function typeScriptBelowRangeWarning(version: string, checked: "syntax" | "shape"): string;
|
|
53
|
+
/**
|
|
54
|
+
* Judges `directory`, which the caller has just filled with `tree`, with the
|
|
55
|
+
* `typescript` `resolveTypeScript` finds — the project's
|
|
56
|
+
* ({@link projectTypeScriptResolver}).
|
|
74
57
|
*/
|
|
75
|
-
export declare function validateOutputTree(directory: string, emission: Pick<ClientEmission, "style" | "tree">, resolveTypeScript
|
|
58
|
+
export declare function validateOutputTree(directory: string, emission: Pick<ClientEmission, "style" | "tree">, resolveTypeScript: TypeScriptResolver): Promise<OutputValidation>;
|
|
@@ -5,11 +5,11 @@ import path from "node:path";
|
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import { carriesGeneratedOwnership } from "../emit/banner.emitter.js";
|
|
7
7
|
import { scanModuleSpecifiers } from "../emit/module-specifier.scanner.js";
|
|
8
|
-
export function typeScriptResolverFor(specifier) {
|
|
8
|
+
export function typeScriptResolverFor(specifier, directory) {
|
|
9
9
|
return async () => {
|
|
10
10
|
let resolved;
|
|
11
11
|
try {
|
|
12
|
-
resolved = createRequire(
|
|
12
|
+
resolved = createRequire(path.join(directory, "package.json")).resolve(specifier);
|
|
13
13
|
}
|
|
14
14
|
catch (error) {
|
|
15
15
|
if (isErrorWithCode(error, "MODULE_NOT_FOUND")) {
|
|
@@ -21,7 +21,25 @@ export function typeScriptResolverFor(specifier) {
|
|
|
21
21
|
return loaded.default ?? loaded;
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
-
export
|
|
24
|
+
export function projectTypeScriptResolver(generateAt) {
|
|
25
|
+
return typeScriptResolverFor("typescript", generateAt);
|
|
26
|
+
}
|
|
27
|
+
export const MINIMUM_TYPESCRIPT = "5.5.0";
|
|
28
|
+
function meetsMinimum(version) {
|
|
29
|
+
const parts = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
|
|
30
|
+
const minimum = MINIMUM_TYPESCRIPT.split(".").map(Number);
|
|
31
|
+
if (parts === null) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
for (let at = 0; at < 3; at += 1) {
|
|
35
|
+
const have = Number(parts[at + 1]);
|
|
36
|
+
const need = minimum[at];
|
|
37
|
+
if (have !== need) {
|
|
38
|
+
return have > need;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
25
43
|
function hasClassicCompilerApi(compiler) {
|
|
26
44
|
return (typeof compiler.createProgram ===
|
|
27
45
|
"function");
|
|
@@ -41,14 +59,34 @@ export function typeScriptWithoutCompilerApiWarning(version, checked) {
|
|
|
41
59
|
`output was NOT type-checked${checked === "shape" ? " or parsed — only its shape was" : " — only its shape and syntax were"}. ` +
|
|
42
60
|
"Your project's own `tsc` checks it when it compiles; a `typescript` 5.5 to 6 restores the generator's own type check.");
|
|
43
61
|
}
|
|
44
|
-
export
|
|
62
|
+
export function typeScriptBelowRangeWarning(version, checked) {
|
|
63
|
+
return (`the installed \`typescript\` ${version} is older than ${MINIMUM_TYPESCRIPT}, the lowest @aventara/client supports, so the generated ` +
|
|
64
|
+
`output was NOT type-checked${checked === "shape" ? " or parsed — only its shape was" : " — only its shape and syntax were"}. ` +
|
|
65
|
+
`Upgrade the project's \`typescript\` to ${MINIMUM_TYPESCRIPT} or later to restore the type check.`);
|
|
66
|
+
}
|
|
67
|
+
function degradedWarning(compiler, checked) {
|
|
68
|
+
if (compiler === undefined) {
|
|
69
|
+
return checked === "syntax"
|
|
70
|
+
? TYPESCRIPT_UNRESOLVED_WARNING
|
|
71
|
+
: SYNTAX_CHECK_UNAVAILABLE_WARNING;
|
|
72
|
+
}
|
|
73
|
+
if (!hasClassicCompilerApi(compiler)) {
|
|
74
|
+
return typeScriptWithoutCompilerApiWarning(versionOf(compiler), checked);
|
|
75
|
+
}
|
|
76
|
+
if (!meetsMinimum(versionOf(compiler))) {
|
|
77
|
+
return typeScriptBelowRangeWarning(versionOf(compiler), checked);
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
export async function validateOutputTree(directory, emission, resolveTypeScript) {
|
|
45
82
|
const { tree, style } = emission;
|
|
46
83
|
const shapeFindings = await shapeFindingsOf(directory, tree, style);
|
|
47
84
|
if (shapeFindings.length > 0) {
|
|
48
85
|
return { accepted: false, checked: "shape", findings: shapeFindings };
|
|
49
86
|
}
|
|
50
87
|
const compiler = await resolveTypeScript();
|
|
51
|
-
if (compiler !== undefined &&
|
|
88
|
+
if (compiler !== undefined &&
|
|
89
|
+
degradedWarning(compiler, "syntax") === undefined) {
|
|
52
90
|
const findings = typeFindingsOf(compiler, directory, tree, style);
|
|
53
91
|
return findings.length > 0
|
|
54
92
|
? { accepted: false, checked: "types", findings }
|
|
@@ -59,11 +97,7 @@ export async function validateOutputTree(directory, emission, resolveTypeScript
|
|
|
59
97
|
return {
|
|
60
98
|
accepted: true,
|
|
61
99
|
checked: "shape",
|
|
62
|
-
warnings: [
|
|
63
|
-
compiler === undefined
|
|
64
|
-
? SYNTAX_CHECK_UNAVAILABLE_WARNING
|
|
65
|
-
: typeScriptWithoutCompilerApiWarning(versionOf(compiler), "shape"),
|
|
66
|
-
],
|
|
100
|
+
warnings: [degradedWarning(compiler, "shape")],
|
|
67
101
|
};
|
|
68
102
|
}
|
|
69
103
|
const findings = syntaxFindingsOf(strip, tree);
|
|
@@ -72,11 +106,7 @@ export async function validateOutputTree(directory, emission, resolveTypeScript
|
|
|
72
106
|
: {
|
|
73
107
|
accepted: true,
|
|
74
108
|
checked: "syntax",
|
|
75
|
-
warnings: [
|
|
76
|
-
compiler === undefined
|
|
77
|
-
? TYPESCRIPT_UNRESOLVED_WARNING
|
|
78
|
-
: typeScriptWithoutCompilerApiWarning(versionOf(compiler), "syntax"),
|
|
79
|
-
],
|
|
109
|
+
warnings: [degradedWarning(compiler, "syntax")],
|
|
80
110
|
};
|
|
81
111
|
}
|
|
82
112
|
async function shapeFindingsOf(directory, tree, style) {
|
|
@@ -225,7 +255,29 @@ function isInside(directory, file) {
|
|
|
225
255
|
}
|
|
226
256
|
function nodeTypeScriptParser() {
|
|
227
257
|
const candidate = nodeModule.stripTypeScriptTypes;
|
|
228
|
-
|
|
258
|
+
if (typeof candidate !== "function") {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
return (source) => {
|
|
262
|
+
const emitWarning = process.emitWarning;
|
|
263
|
+
process.emitWarning = function (warning, ...rest) {
|
|
264
|
+
const [kind] = rest;
|
|
265
|
+
const type = typeof kind === "string"
|
|
266
|
+
? kind
|
|
267
|
+
: kind?.type;
|
|
268
|
+
if (type === "ExperimentalWarning" &&
|
|
269
|
+
String(warning).startsWith("stripTypeScriptTypes ")) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
emitWarning.call(this, warning, ...rest);
|
|
273
|
+
};
|
|
274
|
+
try {
|
|
275
|
+
return candidate(source);
|
|
276
|
+
}
|
|
277
|
+
finally {
|
|
278
|
+
process.emitWarning = emitWarning;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
229
281
|
}
|
|
230
282
|
function syntaxFindingsOf(strip, tree) {
|
|
231
283
|
const decoder = new TextDecoder("utf-8");
|
|
@@ -2,74 +2,6 @@ import * as fsPromises from "node:fs/promises";
|
|
|
2
2
|
import type { AbsolutePath } from "../config/client-config.interface.js";
|
|
3
3
|
import { type ClientEmission, type EmittedTree } from "../emit/emitted-tree.interface.js";
|
|
4
4
|
import { type OutputCheck, type TypeScriptResolver } from "./output.validator.js";
|
|
5
|
-
/**
|
|
6
|
-
* The output is written into `generateAt`, a directory the developer SHARES: it
|
|
7
|
-
* may hold their own files. The generator owns exactly these entries in it — the
|
|
8
|
-
* entry file `AvClient.ts` and `generated/` — and reads, refuses on, moves or
|
|
9
|
-
* deletes nothing else there, apart from the transient entries it names itself
|
|
10
|
-
* (`.aventara-next-*`, `.aventara-ready-*`, `.generated.aventara-previous`) and an
|
|
11
|
-
* unreleased build's entry files, `AvClient.mjs` and `AvClient.d.mts`, which it
|
|
12
|
-
* removes only while they carry the ownership line.
|
|
13
|
-
*
|
|
14
|
-
* - `generated/` is replaced WHOLE, never merged: a file the previous run emitted
|
|
15
|
-
* and this one does not is gone — an unreleased build's `.mjs` modules included.
|
|
16
|
-
* It is replaced only when every file in it carries the generated banner's
|
|
17
|
-
* ownership line (an empty directory owns nothing and is fine); otherwise the
|
|
18
|
-
* run is refused, naming the foreign files.
|
|
19
|
-
* - Each entry file is replaced only when it is absent or carries the ownership
|
|
20
|
-
* line; otherwise the run is refused, naming it.
|
|
21
|
-
* - `AvClient.mjs` and `AvClient.d.mts` — an unreleased build's entry — are
|
|
22
|
-
* removed when they carry the ownership line, so no stale JavaScript is left
|
|
23
|
-
* beside `AvClient.ts`. Without the line each is the developer's own file: never
|
|
24
|
-
* read beyond its head, never named, never removed. (pilot.0's and pilot.1's
|
|
25
|
-
* `AvClient.ts` is the entry file itself, replaced in place.)
|
|
26
|
-
*
|
|
27
|
-
* # The order, and its one window
|
|
28
|
-
*
|
|
29
|
-
* The whole tree — the entry files and `generated/` together — is written into a
|
|
30
|
-
* staging directory inside `generateAt` (same filesystem, so every move is one
|
|
31
|
-
* atomic `rename`) and validated there as one program. Then:
|
|
32
|
-
*
|
|
33
|
-
* 1. the staging directory is renamed `.aventara-ready-*`: from here it is known
|
|
34
|
-
* to be validated;
|
|
35
|
-
* 2. the previous `generated/` → `.generated.aventara-previous`;
|
|
36
|
-
* 3. the staged `generated/` → `generated/`;
|
|
37
|
-
* 4. the previous entry files, and an owned earlier entry, move aside into the
|
|
38
|
-
* ready directory; then each staged entry file moves in;
|
|
39
|
-
* 5. the previous `generated/` and the staging directory are removed.
|
|
40
|
-
*
|
|
41
|
-
* A failed step 3 renames the previous `generated/` straight back; a failed step 4
|
|
42
|
-
* moves what it moved back — the new entry files out, the previous ones in — then
|
|
43
|
-
* the new `generated/` out and the previous back, so a failure at any step leaves
|
|
44
|
-
* the previous output. Node has no atomic exchange of two entries, so two windows
|
|
45
|
-
* remain for a process KILLED mid-replace, and the next run's recovery closes each
|
|
46
|
-
* before it does anything else:
|
|
47
|
-
*
|
|
48
|
-
* - between 2 and 3, `generated/` does not exist: the previous one is put back;
|
|
49
|
-
* - between 3 and the end of 4, the new `generated/` sits beside the old entry
|
|
50
|
-
* files, or beside only some new ones: the validated entry files still staged in
|
|
51
|
-
* `.aventara-ready-*` are moved in, completing the new output rather than
|
|
52
|
-
* leaving a mismatched one.
|
|
53
|
-
*
|
|
54
|
-
* Staging directories that never reached step 1 were not validated and are
|
|
55
|
-
* removed.
|
|
56
|
-
*
|
|
57
|
-
* # Failure is a sentence
|
|
58
|
-
*
|
|
59
|
-
* A refusal, a rejected tree or a filesystem error (an `Error` with a `code`) is
|
|
60
|
-
* an `OutputWriteError` naming its stage — the person running the generator can
|
|
61
|
-
* act on it. Any other error is a defect and propagates with its stack — the very
|
|
62
|
-
* error thrown, not a wrapper — and what the run said before it is kept beside it,
|
|
63
|
-
* read by `warningsRaisedBeforeDefect`.
|
|
64
|
-
*
|
|
65
|
-
* A refusal carries every warning the run raised before it stopped, in the order a
|
|
66
|
-
* success would have returned them: a crash recovered in `prepare` changed the
|
|
67
|
-
* disk, and the person has to hear that even when the run then fails.
|
|
68
|
-
*
|
|
69
|
-
* A failed run removes the directories it created to reach `generateAt` — and only
|
|
70
|
-
* those, and only while empty — so a first run that fails leaves no empty
|
|
71
|
-
* `generateAt` behind.
|
|
72
|
-
*/
|
|
73
5
|
/** The four stages a write passes through, in order. */
|
|
74
6
|
export type OutputWriteStage = "prepare" | "temp" | "validate" | "replace";
|
|
75
7
|
/** A write that stopped; the previous output is intact unless it says otherwise. */
|
|
@@ -99,39 +31,18 @@ export declare function precedeDefect(defect: unknown, earlier: readonly string[
|
|
|
99
31
|
* its warnings; empty when nothing was, or for an error no run threw.
|
|
100
32
|
*/
|
|
101
33
|
export declare function warningsRaisedBeforeDefect(defect: unknown): readonly string[];
|
|
102
|
-
/**
|
|
103
|
-
* The filesystem calls the writer makes, `node:fs/promises` by default. A seam
|
|
104
|
-
* because the filesystem is the writer's one external collaborator, and the only
|
|
105
|
-
* way to prove a failure at the replace stage leaves the previous output intact
|
|
106
|
-
* is to make a real rename fail.
|
|
107
|
-
*/
|
|
108
34
|
export type OutputFileSystem = Pick<typeof fsPromises, "lstat" | "mkdir" | "open" | "readdir" | "rename" | "rm" | "rmdir" | "writeFile">;
|
|
109
35
|
export interface ClientOutputWriteInput {
|
|
110
36
|
/** What `emitClientTree` returned: the tree, and the warnings it raised. */
|
|
111
37
|
readonly emission: ClientEmission;
|
|
112
|
-
/** The resolved `generateAt`. Created when missing. */
|
|
113
38
|
readonly generateAt: AbsolutePath;
|
|
114
|
-
/**
|
|
115
|
-
* Content in the entry files or `generated/` the generator did not produce, which
|
|
116
|
-
* the person running it confirmed may be overwritten or removed — the paths
|
|
117
|
-
* `findForeignOutputContent` listed. Anything foreign that is not listed here is
|
|
118
|
-
* refused.
|
|
119
|
-
*/
|
|
120
39
|
readonly overrideForeign?: readonly string[];
|
|
121
|
-
/** The `typescript` optional peer; the installed one by default. */
|
|
122
40
|
readonly resolveTypeScript?: TypeScriptResolver;
|
|
123
41
|
readonly fileSystem?: OutputFileSystem;
|
|
124
42
|
}
|
|
125
43
|
export interface ClientOutputWritten {
|
|
126
44
|
readonly generateAt: AbsolutePath;
|
|
127
|
-
/** How deeply the tree was validated before it replaced the output. */
|
|
128
45
|
readonly checked: OutputCheck;
|
|
129
|
-
/**
|
|
130
|
-
* Everything the run has to say without failing, in order: the emission's own
|
|
131
|
-
* warnings (the rename ladder's), then the validator's (a degraded check), then
|
|
132
|
-
* the writer's (a recovered crash, a leftover it could not remove). Returned,
|
|
133
|
-
* never printed: the CLI owns the terminal.
|
|
134
|
-
*/
|
|
135
46
|
readonly warnings: readonly string[];
|
|
136
47
|
}
|
|
137
48
|
/**
|
|
@@ -142,25 +53,8 @@ export interface ClientOutputWritten {
|
|
|
142
53
|
*/
|
|
143
54
|
export declare function writeClientOutput(input: ClientOutputWriteInput): Promise<ClientOutputWritten>;
|
|
144
55
|
/**
|
|
145
|
-
* Lists what in the entry files and `<generateAt>/generated/` the generator did
|
|
146
|
-
* not produce — a file without the ownership line, a symbolic link, anything that
|
|
147
|
-
* is not a regular file — so the person running it can be asked before a
|
|
148
|
-
* generation overwrites or removes it. Reads only; looks at nothing else in
|
|
149
|
-
* `generateAt`. Empty when `generateAt` does not exist yet.
|
|
150
|
-
*
|
|
151
|
-
* It sees what the write will see once it has recovered a killed run, without
|
|
152
|
-
* recovering it: a `generated/` the killed run moved aside with nothing in its
|
|
153
|
-
* place is read where it lies and its files named where recovery puts them back
|
|
154
|
-
* (`generated/…`). So the question asked, and `--yes`, cover them too — and a run
|
|
155
|
-
* that stops here has still touched nothing.
|
|
156
|
-
*
|
|
157
56
|
* @throws OutputWriteError when `generateAt` is not a directory, `generated` is
|
|
158
|
-
*
|
|
57
|
+
* not a directory or an entry file is not a file: no confirmation repairs that.
|
|
159
58
|
*/
|
|
160
59
|
export declare function findForeignOutputContent(generateAt: AbsolutePath, fileSystem?: OutputFileSystem): Promise<readonly string[]>;
|
|
161
|
-
/**
|
|
162
|
-
* Whether the entry files and `generated/` in `generateAt` hold exactly `tree` —
|
|
163
|
-
* every file byte for byte, no other file, no owned entry of an earlier build, and
|
|
164
|
-
* nothing a killed run moved aside — so that writing `tree` would change nothing.
|
|
165
|
-
*/
|
|
166
60
|
export declare function ownedOutputMatches(generateAt: AbsolutePath, tree: EmittedTree): Promise<boolean>;
|
|
@@ -3,7 +3,7 @@ import * as fsPromises from "node:fs/promises";
|
|
|
3
3
|
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
|
-
import {
|
|
6
|
+
import { projectTypeScriptResolver, validateOutputTree, } from "./output.validator.js";
|
|
7
7
|
export class OutputWriteError extends Error {
|
|
8
8
|
stage;
|
|
9
9
|
name = "OutputWriteError";
|
|
@@ -94,7 +94,7 @@ export async function writeClientOutput(input) {
|
|
|
94
94
|
await fs.writeFile(target, file.bytes);
|
|
95
95
|
}
|
|
96
96
|
});
|
|
97
|
-
const validation = await stage("validate", paths, () => validateOutputTree(staging, input.emission, input.resolveTypeScript ??
|
|
97
|
+
const validation = await stage("validate", paths, () => validateOutputTree(staging, input.emission, input.resolveTypeScript ?? projectTypeScriptResolver(generateAt)));
|
|
98
98
|
if (validation.accepted) {
|
|
99
99
|
validationWarnings = validation.warnings;
|
|
100
100
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aventara/client",
|
|
3
|
-
"version": "0.1.0-pilot.
|
|
3
|
+
"version": "0.1.0-pilot.4",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "Development-time generator for Aventara typed remote clients.",
|
|
6
6
|
"type": "module",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"LICENSE-ADDITIONAL-PERMISSION.md"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@aventara/core": "0.1.0-pilot.
|
|
26
|
+
"@aventara/core": "0.1.0-pilot.4",
|
|
27
27
|
"c12": "3.3.4",
|
|
28
28
|
"get-tsconfig": "4.10.0"
|
|
29
29
|
},
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@aventara/testing": "0.1.0-pilot.
|
|
42
|
+
"@aventara/testing": "0.1.0-pilot.4",
|
|
43
43
|
"@types/node": "24.10.1",
|
|
44
44
|
"typescript": "^5.9.2"
|
|
45
45
|
},
|