@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
|
@@ -2,24 +2,17 @@ import { randomBytes } from "node:crypto";
|
|
|
2
2
|
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
|
-
import {
|
|
5
|
+
import { CLIENT_ENTRY_FILES, GENERATED_DIRECTORY, LEGACY_CLIENT_ENTRY_FILES, } from "../emit/emitted-tree.interface.js";
|
|
6
6
|
import { resolveInstalledTypeScript, validateOutputTree, } from "./output.validator.js";
|
|
7
|
-
/** A write that stopped; the previous output is intact unless it says otherwise. */
|
|
8
7
|
export class OutputWriteError extends Error {
|
|
9
8
|
stage;
|
|
10
9
|
name = "OutputWriteError";
|
|
11
|
-
/**
|
|
12
|
-
* What the run said before it stopped, in the order a success returns its
|
|
13
|
-
* warnings: the emission's, the validator's, the writer's. Printed before the
|
|
14
|
-
* refusal's sentence.
|
|
15
|
-
*/
|
|
16
10
|
warnings;
|
|
17
11
|
constructor(stage, message, options) {
|
|
18
12
|
super(message, options);
|
|
19
13
|
this.stage = stage;
|
|
20
14
|
this.warnings = options?.warnings ?? [];
|
|
21
15
|
}
|
|
22
|
-
/** The same refusal, with `earlier` said before the warnings it already carries. */
|
|
23
16
|
precededBy(earlier) {
|
|
24
17
|
return new OutputWriteError(this.stage, this.message, {
|
|
25
18
|
cause: this.cause,
|
|
@@ -27,18 +20,7 @@ export class OutputWriteError extends Error {
|
|
|
27
20
|
});
|
|
28
21
|
}
|
|
29
22
|
}
|
|
30
|
-
/**
|
|
31
|
-
* What a run said before a defect stopped it, kept beside the defect rather than
|
|
32
|
-
* on a wrapper: a defect propagates as the very error thrown, with its own stack,
|
|
33
|
-
* so a caller matching it by identity or type still can. Keyed weakly, so it
|
|
34
|
-
* lives exactly as long as the error does. A thrown primitive cannot be a key;
|
|
35
|
-
* what was said before one is not kept.
|
|
36
|
-
*/
|
|
37
23
|
const WARNINGS_BEFORE_DEFECT = new WeakMap();
|
|
38
|
-
/**
|
|
39
|
-
* Records `earlier` as said before `defect`, ahead of anything already recorded,
|
|
40
|
-
* and returns `defect` unchanged so the caller rethrows the same error.
|
|
41
|
-
*/
|
|
42
24
|
export function precedeDefect(defect, earlier) {
|
|
43
25
|
if (earlier.length > 0 && typeof defect === "object" && defect !== null) {
|
|
44
26
|
WARNINGS_BEFORE_DEFECT.set(defect, [
|
|
@@ -48,10 +30,6 @@ export function precedeDefect(defect, earlier) {
|
|
|
48
30
|
}
|
|
49
31
|
return defect;
|
|
50
32
|
}
|
|
51
|
-
/**
|
|
52
|
-
* What the run said before `defect` stopped it, in the order a success returns
|
|
53
|
-
* its warnings; empty when nothing was, or for an error no run threw.
|
|
54
|
-
*/
|
|
55
33
|
export function warningsRaisedBeforeDefect(defect) {
|
|
56
34
|
return typeof defect === "object" && defect !== null
|
|
57
35
|
? (WARNINGS_BEFORE_DEFECT.get(defect) ?? [])
|
|
@@ -60,23 +38,20 @@ export function warningsRaisedBeforeDefect(defect) {
|
|
|
60
38
|
const NEXT_PREFIX = ".aventara-next-";
|
|
61
39
|
const READY_PREFIX = ".aventara-ready-";
|
|
62
40
|
const PREVIOUS_GENERATED = `.${GENERATED_DIRECTORY}.aventara-previous`;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
* @throws OutputWriteError when the write stops; the previous output is intact.
|
|
67
|
-
*/
|
|
68
|
-
export async function writeClientOutput(input) {
|
|
69
|
-
const fs = input.fileSystem ?? fsPromises;
|
|
70
|
-
const generateAt = path.resolve(input.generateAt);
|
|
71
|
-
const paths = {
|
|
41
|
+
const PREVIOUS_ENTRIES = ".previous-entries";
|
|
42
|
+
function outputPathsOf(generateAt) {
|
|
43
|
+
return {
|
|
72
44
|
generateAt,
|
|
73
45
|
generated: path.join(generateAt, GENERATED_DIRECTORY),
|
|
74
|
-
entry: path.join(generateAt, CLIENT_ENTRY_FILE),
|
|
75
46
|
previous: path.join(generateAt, PREVIOUS_GENERATED),
|
|
76
47
|
};
|
|
48
|
+
}
|
|
49
|
+
export async function writeClientOutput(input) {
|
|
50
|
+
const fs = input.fileSystem ?? fsPromises;
|
|
51
|
+
const generateAt = path.resolve(input.generateAt);
|
|
52
|
+
const paths = outputPathsOf(generateAt);
|
|
77
53
|
const writerWarnings = [];
|
|
78
54
|
let validationWarnings = [];
|
|
79
|
-
/** The outermost directory this run created on the way to `generateAt`, if any. */
|
|
80
55
|
let created;
|
|
81
56
|
try {
|
|
82
57
|
return await writeStagedOutput();
|
|
@@ -95,7 +70,6 @@ export async function writeClientOutput(input) {
|
|
|
95
70
|
: precedeDefect(error, said);
|
|
96
71
|
}
|
|
97
72
|
async function writeStagedOutput() {
|
|
98
|
-
// prepare — recover a killed run, then decide whether the two entries are ours.
|
|
99
73
|
const state = await stage("prepare", paths, async () => {
|
|
100
74
|
created = await ensureDirectory(fs, generateAt);
|
|
101
75
|
writerWarnings.push(...(await recoverInterruptedRun(fs, paths)));
|
|
@@ -107,14 +81,11 @@ export async function writeClientOutput(input) {
|
|
|
107
81
|
}
|
|
108
82
|
return inspection;
|
|
109
83
|
});
|
|
110
|
-
// temp — the whole tree, AvClient.ts and generated/ together, staged in generateAt.
|
|
111
84
|
const suffix = randomBytes(6).toString("hex");
|
|
112
85
|
let staging = path.join(generateAt, `${NEXT_PREFIX}${suffix}`);
|
|
113
86
|
let stagingExists = false;
|
|
114
87
|
try {
|
|
115
88
|
await stage("temp", paths, async () => {
|
|
116
|
-
// `mkdir`, not `mkdtemp`: generated/ is moved out of this directory and
|
|
117
|
-
// takes the mode any directory the user creates takes (`mkdtemp` makes 0700).
|
|
118
89
|
await fs.mkdir(staging);
|
|
119
90
|
stagingExists = true;
|
|
120
91
|
for (const file of input.emission.tree) {
|
|
@@ -123,8 +94,7 @@ export async function writeClientOutput(input) {
|
|
|
123
94
|
await fs.writeFile(target, file.bytes);
|
|
124
95
|
}
|
|
125
96
|
});
|
|
126
|
-
|
|
127
|
-
const validation = await stage("validate", paths, () => validateOutputTree(staging, input.emission.tree, input.resolveTypeScript ?? resolveInstalledTypeScript));
|
|
97
|
+
const validation = await stage("validate", paths, () => validateOutputTree(staging, input.emission, input.resolveTypeScript ?? resolveInstalledTypeScript));
|
|
128
98
|
if (validation.accepted) {
|
|
129
99
|
validationWarnings = validation.warnings;
|
|
130
100
|
}
|
|
@@ -133,7 +103,6 @@ export async function writeClientOutput(input) {
|
|
|
133
103
|
"This is a defect in @aventara/client; please report it with these findings:\n" +
|
|
134
104
|
validation.findings.map((finding) => ` ${finding}`).join("\n"));
|
|
135
105
|
}
|
|
136
|
-
// replace — the order and its rollbacks are the module doc's.
|
|
137
106
|
await stage("replace", paths, async () => {
|
|
138
107
|
const ready = path.join(generateAt, `${READY_PREFIX}${suffix}`);
|
|
139
108
|
await fs.rename(staging, ready);
|
|
@@ -151,11 +120,28 @@ export async function writeClientOutput(input) {
|
|
|
151
120
|
}
|
|
152
121
|
throw error;
|
|
153
122
|
}
|
|
123
|
+
const aside = path.join(staging, PREVIOUS_ENTRIES);
|
|
124
|
+
const movedOut = [];
|
|
125
|
+
const movedIn = [];
|
|
154
126
|
try {
|
|
155
|
-
await fs.
|
|
127
|
+
await fs.mkdir(aside);
|
|
128
|
+
for (const name of [...state.entries, ...state.legacy]) {
|
|
129
|
+
await fs.rename(path.join(generateAt, name), path.join(aside, name));
|
|
130
|
+
movedOut.push(name);
|
|
131
|
+
}
|
|
132
|
+
for (const name of CLIENT_ENTRY_FILES) {
|
|
133
|
+
await fs.rename(path.join(staging, name), path.join(generateAt, name));
|
|
134
|
+
movedIn.push(name);
|
|
135
|
+
}
|
|
156
136
|
}
|
|
157
137
|
catch (error) {
|
|
158
138
|
await rollBack(paths, error, async () => {
|
|
139
|
+
for (const name of movedIn.reverse()) {
|
|
140
|
+
await fs.rename(path.join(generateAt, name), path.join(staging, name));
|
|
141
|
+
}
|
|
142
|
+
for (const name of movedOut.reverse()) {
|
|
143
|
+
await fs.rename(path.join(aside, name), path.join(generateAt, name));
|
|
144
|
+
}
|
|
159
145
|
await fs.rename(paths.generated, path.join(staging, GENERATED_DIRECTORY));
|
|
160
146
|
if (movedAside) {
|
|
161
147
|
await fs.rename(paths.previous, paths.generated);
|
|
@@ -186,23 +172,14 @@ export async function writeClientOutput(input) {
|
|
|
186
172
|
finally {
|
|
187
173
|
if (stagingExists) {
|
|
188
174
|
await fs.rm(staging, { recursive: true, force: true }).catch(() => {
|
|
189
|
-
// The next run removes it by name; the error being thrown matters more.
|
|
190
175
|
});
|
|
191
176
|
}
|
|
192
177
|
}
|
|
193
178
|
}
|
|
194
179
|
}
|
|
195
|
-
/** The sentence's ending when the previous output was not changed. */
|
|
196
180
|
function LEFT_AS_THEY_WERE(paths) {
|
|
197
|
-
return `${
|
|
181
|
+
return `${CLIENT_ENTRY_FILES.join(", ")} and ${GENERATED_DIRECTORY}/ in ${paths.generateAt} were left exactly as they were.`;
|
|
198
182
|
}
|
|
199
|
-
/**
|
|
200
|
-
* Creates `generateAt` when missing; refuses when something other than a
|
|
201
|
-
* directory is there.
|
|
202
|
-
*
|
|
203
|
-
* @returns the outermost directory it created — `generateAt` or an ancestor —
|
|
204
|
-
* or `undefined` when `generateAt` already existed.
|
|
205
|
-
*/
|
|
206
183
|
async function ensureDirectory(fs, generateAt) {
|
|
207
184
|
const stats = await lstatOrUndefined(fs, generateAt);
|
|
208
185
|
if (stats === undefined) {
|
|
@@ -213,12 +190,6 @@ async function ensureDirectory(fs, generateAt) {
|
|
|
213
190
|
}
|
|
214
191
|
return undefined;
|
|
215
192
|
}
|
|
216
|
-
/**
|
|
217
|
-
* Removes, after a failed run, the directories it created: `generateAt` and each
|
|
218
|
-
* parent up to `outermost`, innermost first. `rmdir` removes only an empty
|
|
219
|
-
* directory, so nothing anyone else put there meanwhile is lost; the first one
|
|
220
|
-
* that cannot be removed ends the walk, and is named in the warning returned.
|
|
221
|
-
*/
|
|
222
193
|
async function removeCreatedDirectories(fs, generateAt, outermost) {
|
|
223
194
|
for (let directory = generateAt;; directory = path.dirname(directory)) {
|
|
224
195
|
try {
|
|
@@ -235,29 +206,23 @@ async function removeCreatedDirectories(fs, generateAt, outermost) {
|
|
|
235
206
|
}
|
|
236
207
|
}
|
|
237
208
|
}
|
|
238
|
-
/**
|
|
239
|
-
* Leftovers of a run that was killed, recognised only by the names this writer
|
|
240
|
-
* gives them:
|
|
241
|
-
*
|
|
242
|
-
* - a staging directory that never became ready was not validated: removed;
|
|
243
|
-
* - a ready one whose `generated/` was already moved in but whose `AvClient.ts`
|
|
244
|
-
* was not: its `AvClient.ts` is moved in, completing the new pair;
|
|
245
|
-
* - any other ready one: removed;
|
|
246
|
-
* - a previous `generated/` moved aside: put back when nothing replaced it,
|
|
247
|
-
* removed when something did.
|
|
248
|
-
*/
|
|
249
209
|
async function recoverInterruptedRun(fs, paths) {
|
|
250
210
|
const warnings = [];
|
|
251
211
|
for (const entry of (await fs.readdir(paths.generateAt)).sort()) {
|
|
252
212
|
const leftover = path.join(paths.generateAt, entry);
|
|
253
213
|
if (entry.startsWith(READY_PREFIX)) {
|
|
254
|
-
const stagedEntry = path.join(leftover, CLIENT_ENTRY_FILE);
|
|
255
214
|
const generatedMovedIn = (await lstatOrUndefined(fs, path.join(leftover, GENERATED_DIRECTORY))) === undefined;
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
215
|
+
const completed = [];
|
|
216
|
+
for (const name of generatedMovedIn ? CLIENT_ENTRY_FILES : []) {
|
|
217
|
+
const staged = path.join(leftover, name);
|
|
218
|
+
if ((await lstatOrUndefined(fs, staged)) !== undefined) {
|
|
219
|
+
await fs.rename(staged, path.join(paths.generateAt, name));
|
|
220
|
+
completed.push(name);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (completed.length > 0) {
|
|
224
|
+
warnings.push(`an interrupted run had moved its generated/ in but not all of its entry files; ` +
|
|
225
|
+
`the validated ${completed.join(", ")} ${completed.length === 1 ? "was" : "were"} moved in at ${paths.generateAt} before generating.`);
|
|
261
226
|
}
|
|
262
227
|
await fs.rm(leftover, { recursive: true, force: true });
|
|
263
228
|
}
|
|
@@ -277,35 +242,12 @@ async function recoverInterruptedRun(fs, paths) {
|
|
|
277
242
|
}
|
|
278
243
|
return warnings;
|
|
279
244
|
}
|
|
280
|
-
/**
|
|
281
|
-
* Where the `generated/` a run works with comes from once a killed run is
|
|
282
|
-
* recovered: the previous one moved aside when nothing replaced it — recovery
|
|
283
|
-
* puts it back — and `generated/` otherwise. One answer for the recovery and for
|
|
284
|
-
* the read-only look ahead of it, so the two cannot disagree.
|
|
285
|
-
*/
|
|
286
245
|
async function generatedAfterRecovery(fs, paths) {
|
|
287
246
|
return (await lstatOrUndefined(fs, paths.generated)) === undefined &&
|
|
288
247
|
(await lstatOrUndefined(fs, paths.previous)) !== undefined
|
|
289
248
|
? paths.previous
|
|
290
249
|
: paths.generated;
|
|
291
250
|
}
|
|
292
|
-
/**
|
|
293
|
-
* Lists what in `<generateAt>/AvClient.ts` and `<generateAt>/generated/` the
|
|
294
|
-
* generator did not produce — a file without the ownership line, a symbolic link,
|
|
295
|
-
* anything that is not a regular file — so the person running it can be asked
|
|
296
|
-
* before a generation overwrites or removes it (architect, 2026-10-04). Reads
|
|
297
|
-
* only; looks at nothing else in `generateAt`. Empty when `generateAt` does not
|
|
298
|
-
* exist yet.
|
|
299
|
-
*
|
|
300
|
-
* It sees what the write will see once it has recovered a killed run, without
|
|
301
|
-
* recovering it: a `generated/` the killed run moved aside with nothing in its
|
|
302
|
-
* place is read where it lies and its files named where recovery puts them back
|
|
303
|
-
* (`generated/…`). So the question asked, and `--yes`, cover them too — and a
|
|
304
|
-
* run that stops here has still touched nothing.
|
|
305
|
-
*
|
|
306
|
-
* @throws OutputWriteError when `generateAt` is not a directory, `generated` is
|
|
307
|
-
* not a directory or `AvClient.ts` is not a file: no confirmation repairs that.
|
|
308
|
-
*/
|
|
309
251
|
export async function findForeignOutputContent(generateAt, fileSystem = fsPromises) {
|
|
310
252
|
const root = path.resolve(generateAt);
|
|
311
253
|
const stats = await lstatOrUndefined(fileSystem, root);
|
|
@@ -315,21 +257,10 @@ export async function findForeignOutputContent(generateAt, fileSystem = fsPromis
|
|
|
315
257
|
if (!stats.isDirectory()) {
|
|
316
258
|
throw notADirectory(root);
|
|
317
259
|
}
|
|
318
|
-
const paths =
|
|
319
|
-
generateAt: root,
|
|
320
|
-
generated: path.join(root, GENERATED_DIRECTORY),
|
|
321
|
-
entry: path.join(root, CLIENT_ENTRY_FILE),
|
|
322
|
-
previous: path.join(root, PREVIOUS_GENERATED),
|
|
323
|
-
};
|
|
260
|
+
const paths = outputPathsOf(root);
|
|
324
261
|
const inspection = await inspectOwnedEntries(fileSystem, paths, await generatedAfterRecovery(fileSystem, paths));
|
|
325
262
|
return inspection.foreign;
|
|
326
263
|
}
|
|
327
|
-
/**
|
|
328
|
-
* What stands at the two owned entries. `generatedSource` is the directory read
|
|
329
|
-
* as `generated/` — `generated/` itself, or the previous one a killed run moved
|
|
330
|
-
* aside, which recovery will put back there — and its files are named as
|
|
331
|
-
* `generated/…` either way.
|
|
332
|
-
*/
|
|
333
264
|
async function inspectOwnedEntries(fs, paths, generatedSource = paths.generated) {
|
|
334
265
|
const foreign = [];
|
|
335
266
|
const relative = (file) => path.relative(paths.generateAt, file).split(path.sep).join("/");
|
|
@@ -353,23 +284,37 @@ async function inspectOwnedEntries(fs, paths, generatedSource = paths.generated)
|
|
|
353
284
|
}
|
|
354
285
|
}
|
|
355
286
|
}
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
287
|
+
const entries = [];
|
|
288
|
+
for (const name of CLIENT_ENTRY_FILES) {
|
|
289
|
+
const file = path.join(paths.generateAt, name);
|
|
290
|
+
const stats = await lstatOrUndefined(fs, file);
|
|
291
|
+
if (stats === undefined) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (!stats.isFile()) {
|
|
295
|
+
throw new OutputWriteError("prepare", `refusing to replace ${file}: it is not a file, and the generator owns ${name} ` +
|
|
360
296
|
"as one; move it, or choose another `generateAt`. Nothing was written.");
|
|
361
297
|
}
|
|
362
|
-
|
|
363
|
-
|
|
298
|
+
entries.push(name);
|
|
299
|
+
if (!(await carriesOwnershipLine(fs, file))) {
|
|
300
|
+
foreign.push(relative(file));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const legacy = [];
|
|
304
|
+
for (const name of LEGACY_CLIENT_ENTRY_FILES) {
|
|
305
|
+
const file = path.join(paths.generateAt, name);
|
|
306
|
+
if ((await lstatOrUndefined(fs, file))?.isFile() === true &&
|
|
307
|
+
(await carriesOwnershipLine(fs, file))) {
|
|
308
|
+
legacy.push(name);
|
|
364
309
|
}
|
|
365
310
|
}
|
|
366
311
|
return {
|
|
367
312
|
generated: generatedStats === undefined ? "absent" : "present",
|
|
368
|
-
|
|
313
|
+
entries,
|
|
314
|
+
legacy,
|
|
369
315
|
foreign: foreign.sort(),
|
|
370
316
|
};
|
|
371
317
|
}
|
|
372
|
-
/** Foreign content nobody confirmed may be overwritten. */
|
|
373
318
|
function foreignContentRefusal(paths, foreign) {
|
|
374
319
|
const them = foreign.length === 1 ? "it" : "them";
|
|
375
320
|
return new OutputWriteError("prepare", `refusing to generate into ${paths.generateAt}: ${foreign.join(", ")} ${foreign.length === 1 ? "was" : "were"} ` +
|
|
@@ -391,10 +336,6 @@ async function carriesOwnershipLine(fs, file) {
|
|
|
391
336
|
await handle.close();
|
|
392
337
|
}
|
|
393
338
|
}
|
|
394
|
-
/**
|
|
395
|
-
* Undoes a replace that stopped part-way. When the undo itself fails, the
|
|
396
|
-
* sentence says where the previous `generated/` is: the next run puts it back.
|
|
397
|
-
*/
|
|
398
339
|
async function rollBack(paths, cause, undo) {
|
|
399
340
|
try {
|
|
400
341
|
await undo();
|
|
@@ -405,11 +346,6 @@ async function rollBack(paths, cause, undo) {
|
|
|
405
346
|
`${GENERATED_DIRECTORY}/ is intact at ${paths.previous}; the next run puts it back.`, { cause });
|
|
406
347
|
}
|
|
407
348
|
}
|
|
408
|
-
/**
|
|
409
|
-
* Runs one stage, turning a filesystem error into that stage's sentence. An
|
|
410
|
-
* `OutputWriteError` passes through; anything without a `code` is a defect and
|
|
411
|
-
* keeps its stack.
|
|
412
|
-
*/
|
|
413
349
|
async function stage(name, paths, run) {
|
|
414
350
|
try {
|
|
415
351
|
return await run();
|
|
@@ -447,16 +383,18 @@ function describe(error) {
|
|
|
447
383
|
? `${error.code}: ${error.message}`
|
|
448
384
|
: String(error);
|
|
449
385
|
}
|
|
450
|
-
/**
|
|
451
|
-
* Whether `AvClient.ts` and `generated/` in `generateAt` hold exactly `tree` —
|
|
452
|
-
* every file byte for byte, no other file, and nothing a killed run moved aside —
|
|
453
|
-
* so that writing `tree` would change nothing (Phase 12-rest Q6: "up to date").
|
|
454
|
-
*/
|
|
455
386
|
export async function ownedOutputMatches(generateAt, tree) {
|
|
456
387
|
const root = path.resolve(generateAt);
|
|
457
388
|
if ((await lstatOrUndefined(fsPromises, path.join(root, PREVIOUS_GENERATED))) !== undefined) {
|
|
458
389
|
return false;
|
|
459
390
|
}
|
|
391
|
+
for (const name of LEGACY_CLIENT_ENTRY_FILES) {
|
|
392
|
+
const file = path.join(root, name);
|
|
393
|
+
if ((await lstatOrUndefined(fsPromises, file))?.isFile() === true &&
|
|
394
|
+
(await carriesOwnershipLine(fsPromises, file))) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
460
398
|
let present;
|
|
461
399
|
try {
|
|
462
400
|
present = (await fsPromises.readdir(path.join(root, GENERATED_DIRECTORY), {
|
|
@@ -476,7 +414,7 @@ export async function ownedOutputMatches(generateAt, tree) {
|
|
|
476
414
|
throw error;
|
|
477
415
|
}
|
|
478
416
|
const expected = new Set(tree.map((file) => file.path));
|
|
479
|
-
if (present.length +
|
|
417
|
+
if (present.length + CLIENT_ENTRY_FILES.length !== expected.size ||
|
|
480
418
|
present.some((file) => !expected.has(file))) {
|
|
481
419
|
return false;
|
|
482
420
|
}
|
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.2",
|
|
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,9 @@
|
|
|
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.2",
|
|
27
|
+
"c12": "3.3.4",
|
|
28
|
+
"get-tsconfig": "4.10.0"
|
|
27
29
|
},
|
|
28
30
|
"peerDependencies": {
|
|
29
31
|
"typescript": ">=5.5.0"
|
|
@@ -37,12 +39,12 @@
|
|
|
37
39
|
"access": "public"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
|
-
"@aventara/testing": "0.1.0-pilot.
|
|
42
|
+
"@aventara/testing": "0.1.0-pilot.2",
|
|
41
43
|
"@types/node": "24.10.1",
|
|
42
44
|
"typescript": "^5.9.2"
|
|
43
45
|
},
|
|
44
46
|
"scripts": {
|
|
45
|
-
"build": "tsc -p tsconfig.json",
|
|
47
|
+
"build": "tsc -p tsconfig.json --removeComments --declaration false && tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist",
|
|
46
48
|
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
|
|
47
49
|
"test": "vitest run --typecheck --config ../../vitest.config.mts --root .",
|
|
48
50
|
"lint": "biome lint src"
|