@ontrails/trails 1.0.0-beta.30 → 1.0.0-beta.39
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 +153 -0
- package/README.md +3 -3
- package/package.json +17 -16
- package/src/app.ts +27 -6
- package/src/cli.ts +3 -2
- package/src/mcp-app.ts +2 -0
- package/src/mcp-options.ts +20 -5
- package/src/regrade/config.ts +152 -0
- package/src/regrade/history.ts +320 -0
- package/src/regrade/live-api-preserve.ts +8 -0
- package/src/regrade/plan-artifact.ts +265 -0
- package/src/release/index.ts +17 -0
- package/src/release/lock-roundtrip-smoke.ts +230 -0
- package/src/release/notes-cli.ts +171 -0
- package/src/release/notes.ts +390 -0
- package/src/release/pack-coherence.ts +1 -3
- package/src/release/smoke.ts +11 -1
- package/src/release/wayfinder-dogfood-smoke.ts +1 -1
- package/src/run-schema.ts +34 -1
- package/src/trails/compile.ts +4 -5
- package/src/trails/create-adapter.ts +19 -79
- package/src/trails/deprecate.ts +1 -1
- package/src/trails/guide.ts +5 -5
- package/src/trails/load-app.ts +47 -116
- package/src/trails/regrade.ts +3017 -27
- package/src/trails/release-smoke.ts +2 -1
- package/src/trails/revise.ts +1 -1
- package/src/trails/survey.ts +37 -50
- package/src/trails/topo-activation.ts +14 -385
- package/src/trails/topo-read-support.ts +28 -24
- package/src/trails/topo-store-support.ts +21 -12
- package/src/trails/topo-support.ts +1 -0
- package/src/trails/topo.ts +2 -2
- package/src/trails/validate.ts +1 -1
- package/src/trails/version-lifecycle-support.ts +11 -11
- package/src/trails/warden-guide.ts +9 -4
- package/src/trails/warden.ts +15 -0
- package/src/trails/wayfind.ts +64 -12
package/src/trails/guide.ts
CHANGED
|
@@ -10,8 +10,8 @@ import { z } from 'zod';
|
|
|
10
10
|
import { withFreshOperatorApp } from './operator-context.js';
|
|
11
11
|
import { trailDetailOutput } from './topo-output-schemas.js';
|
|
12
12
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
deriveCurrentGuideEntries,
|
|
14
|
+
deriveCurrentTopoDetail,
|
|
15
15
|
readSurfaceLayerNamesFromContext,
|
|
16
16
|
} from './topo-read-support.js';
|
|
17
17
|
import { createIsolatedExampleInput } from './topo-support.js';
|
|
@@ -53,8 +53,8 @@ export const guideTrail = trail('guide', {
|
|
|
53
53
|
blaze: async (input: GuideTrailInput, ctx) =>
|
|
54
54
|
withFreshOperatorApp<GuideTrailOutput>(input, ctx, ({ lease, rootDir }) => {
|
|
55
55
|
if (input.trailId) {
|
|
56
|
-
const detail =
|
|
57
|
-
|
|
56
|
+
const detail = deriveCurrentTopoDetail(lease.app, input.trailId, {
|
|
57
|
+
overlays: lease.overlays,
|
|
58
58
|
rootDir,
|
|
59
59
|
surfaceLayerNames: readSurfaceLayerNamesFromContext(ctx),
|
|
60
60
|
});
|
|
@@ -70,7 +70,7 @@ export const guideTrail = trail('guide', {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
return Result.ok({
|
|
73
|
-
entries:
|
|
73
|
+
entries: deriveCurrentGuideEntries(lease.app, {
|
|
74
74
|
rootDir,
|
|
75
75
|
}) as GuideEntry[],
|
|
76
76
|
mode: 'list' as const,
|
package/src/trails/load-app.ts
CHANGED
|
@@ -19,11 +19,14 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
19
19
|
import {
|
|
20
20
|
deriveSafePath,
|
|
21
21
|
isTrailsError,
|
|
22
|
+
listWorkspacePackages,
|
|
22
23
|
PermissionError,
|
|
23
24
|
Result,
|
|
24
25
|
ValidationError,
|
|
25
26
|
} from '@ontrails/core';
|
|
26
|
-
import type {
|
|
27
|
+
import type { Topo } from '@ontrails/core';
|
|
28
|
+
import { resolveTrailsOverlays } from '@ontrails/adapter-kit';
|
|
29
|
+
import type { TopoGraphOverlayRegistration } from '@ontrails/topographer';
|
|
27
30
|
import { findAppModule } from '@ontrails/cli';
|
|
28
31
|
|
|
29
32
|
import {
|
|
@@ -278,6 +281,8 @@ interface PackageJson {
|
|
|
278
281
|
readonly workspaces?: unknown;
|
|
279
282
|
}
|
|
280
283
|
|
|
284
|
+
type NamedPackageJson = PackageJson & { readonly name: string };
|
|
285
|
+
|
|
281
286
|
const readPackageJson = (packagePath: string): PackageJson | undefined => {
|
|
282
287
|
try {
|
|
283
288
|
return JSON.parse(readFileSync(packagePath, 'utf8')) as PackageJson;
|
|
@@ -327,20 +332,6 @@ const isPackageLocalImport = (
|
|
|
327
332
|
const isScannableModule = (modulePath: string): boolean =>
|
|
328
333
|
SCANNABLE_EXTENSIONS.has(extname(modulePath));
|
|
329
334
|
|
|
330
|
-
const resolveImportedModulePath = (
|
|
331
|
-
importerPath: string,
|
|
332
|
-
importPath: string
|
|
333
|
-
): string => {
|
|
334
|
-
const resolved = import.meta.resolve(
|
|
335
|
-
importPath,
|
|
336
|
-
pathToFileURL(importerPath).href
|
|
337
|
-
);
|
|
338
|
-
return resolveFilesystemModulePath(
|
|
339
|
-
fileURLToPath(resolved),
|
|
340
|
-
dirname(importerPath)
|
|
341
|
-
);
|
|
342
|
-
};
|
|
343
|
-
|
|
344
335
|
const readDirectoryEntries = (directoryPath: string): readonly string[] => {
|
|
345
336
|
try {
|
|
346
337
|
return readdirSync(directoryPath);
|
|
@@ -359,25 +350,41 @@ const safeStat = (
|
|
|
359
350
|
}
|
|
360
351
|
};
|
|
361
352
|
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
)
|
|
353
|
+
const resolveImportedModulePath = (
|
|
354
|
+
importerPath: string,
|
|
355
|
+
importPath: string
|
|
356
|
+
): string => {
|
|
357
|
+
const resolved = import.meta.resolve(
|
|
358
|
+
importPath,
|
|
359
|
+
pathToFileURL(importerPath).href
|
|
360
|
+
);
|
|
361
|
+
const literalPath = resolveFilesystemModulePath(
|
|
362
|
+
fileURLToPath(resolved),
|
|
363
|
+
dirname(importerPath)
|
|
364
|
+
);
|
|
365
|
+
if (safeStat(literalPath)?.isFile()) {
|
|
366
|
+
return literalPath;
|
|
369
367
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
return
|
|
377
|
-
(
|
|
368
|
+
|
|
369
|
+
// `import.meta.resolve` joins relative specifiers without probing the
|
|
370
|
+
// filesystem, so extensionless imports point at paths that do not exist even
|
|
371
|
+
// though Bun resolves them at runtime. Fall back to the runtime resolver so
|
|
372
|
+
// runtime-valid apps stay fresh-loadable.
|
|
373
|
+
try {
|
|
374
|
+
return resolveFilesystemModulePath(
|
|
375
|
+
Bun.resolveSync(importPath, dirname(importerPath)),
|
|
376
|
+
dirname(importerPath)
|
|
377
|
+
);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
const extensionlessHint =
|
|
380
|
+
extname(importPath) === ''
|
|
381
|
+
? ' Extensionless relative imports must match a .ts/.tsx/.js module or a directory index.'
|
|
382
|
+
: '';
|
|
383
|
+
throw new ValidationError(
|
|
384
|
+
`Cannot resolve import "${importPath}" from "${importerPath}" while mirroring the app for fresh loading: "${literalPath}" does not exist and runtime resolution found no match. Fix the specifier or add the missing file.${extensionlessHint}`,
|
|
385
|
+
{ cause: asError(error), context: { importPath, importerPath } }
|
|
378
386
|
);
|
|
379
387
|
}
|
|
380
|
-
return [];
|
|
381
388
|
};
|
|
382
389
|
|
|
383
390
|
interface WorkspacePackage {
|
|
@@ -386,45 +393,12 @@ interface WorkspacePackage {
|
|
|
386
393
|
readonly packageRoot: string;
|
|
387
394
|
}
|
|
388
395
|
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const entryPath = join(baseDir, entry);
|
|
396
|
-
if (safeStat(entryPath)?.isDirectory()) {
|
|
397
|
-
roots.push(entryPath);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
continue;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if (!pattern.includes('*')) {
|
|
404
|
-
roots.push(resolve(cwd, pattern));
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
return roots;
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
const readWorkspacePackages = (cwd: string): readonly WorkspacePackage[] => {
|
|
411
|
-
const packages: WorkspacePackage[] = [];
|
|
412
|
-
for (const packageRoot of listWorkspacePackageRoots(cwd)) {
|
|
413
|
-
const packageJson = readPackageJson(join(packageRoot, 'package.json'));
|
|
414
|
-
if (
|
|
415
|
-
packageJson !== undefined &&
|
|
416
|
-
typeof packageJson.name === 'string' &&
|
|
417
|
-
packageJson.name.length > 0
|
|
418
|
-
) {
|
|
419
|
-
packages.push({
|
|
420
|
-
name: packageJson.name,
|
|
421
|
-
packageJson,
|
|
422
|
-
packageRoot,
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
return packages;
|
|
427
|
-
};
|
|
396
|
+
const readWorkspacePackages = (cwd: string): readonly WorkspacePackage[] =>
|
|
397
|
+
listWorkspacePackages<NamedPackageJson>(cwd).map((workspacePackage) => ({
|
|
398
|
+
name: workspacePackage.manifest.name,
|
|
399
|
+
packageJson: workspacePackage.manifest,
|
|
400
|
+
packageRoot: workspacePackage.packageRoot,
|
|
401
|
+
}));
|
|
428
402
|
|
|
429
403
|
const parsePackageSpecifier = (
|
|
430
404
|
importPath: string
|
|
@@ -1054,54 +1028,11 @@ const resolveLoadedTopo = (
|
|
|
1054
1028
|
return app;
|
|
1055
1029
|
};
|
|
1056
1030
|
|
|
1057
|
-
type LoadedCliAliases = Readonly<
|
|
1058
|
-
Record<string, readonly CliCommandAliasInput[]>
|
|
1059
|
-
>;
|
|
1060
|
-
|
|
1061
|
-
const isStringArray = (value: unknown): value is readonly string[] =>
|
|
1062
|
-
Array.isArray(value) && value.every((item) => typeof item === 'string');
|
|
1063
|
-
|
|
1064
|
-
const isCliAliasInput = (value: unknown): value is CliCommandAliasInput =>
|
|
1065
|
-
typeof value === 'string' || isStringArray(value);
|
|
1066
|
-
|
|
1067
|
-
const resolveLoadedCliAliases = (
|
|
1068
|
-
effectivePath: string,
|
|
1069
|
-
mod: Record<string, unknown>
|
|
1070
|
-
): LoadedCliAliases | undefined => {
|
|
1071
|
-
const value = mod['trailsCliAliases'] ?? mod['cliAliases'];
|
|
1072
|
-
if (value === undefined) {
|
|
1073
|
-
return undefined;
|
|
1074
|
-
}
|
|
1075
|
-
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
1076
|
-
throw new ValidationError(
|
|
1077
|
-
`CLI alias export in "${effectivePath}" must be a record from trail ID to alias list.`
|
|
1078
|
-
);
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
const aliases = value as Record<string, unknown>;
|
|
1082
|
-
for (const [trailId, trailAliases] of Object.entries(aliases)) {
|
|
1083
|
-
if (!Array.isArray(trailAliases)) {
|
|
1084
|
-
throw new ValidationError(
|
|
1085
|
-
`CLI alias export for trail "${trailId}" in "${effectivePath}" must be an array.`
|
|
1086
|
-
);
|
|
1087
|
-
}
|
|
1088
|
-
for (const alias of trailAliases) {
|
|
1089
|
-
if (!isCliAliasInput(alias)) {
|
|
1090
|
-
throw new ValidationError(
|
|
1091
|
-
`CLI alias export for trail "${trailId}" in "${effectivePath}" must contain string aliases or string-array paths.`
|
|
1092
|
-
);
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
return aliases as LoadedCliAliases;
|
|
1098
|
-
};
|
|
1099
|
-
|
|
1100
1031
|
export interface FreshAppLease {
|
|
1101
1032
|
readonly app: Topo;
|
|
1102
|
-
readonly cliAliases?: LoadedCliAliases | undefined;
|
|
1103
1033
|
readonly mirrorRoot: string;
|
|
1104
1034
|
readonly release: () => void;
|
|
1035
|
+
readonly overlays?: readonly TopoGraphOverlayRegistration[] | undefined;
|
|
1105
1036
|
}
|
|
1106
1037
|
|
|
1107
1038
|
export type LoadAppLeaseOptions = LoadAppTrustOptions;
|
|
@@ -1122,8 +1053,8 @@ const createUrlSchemeLease = async (
|
|
|
1122
1053
|
>;
|
|
1123
1054
|
return {
|
|
1124
1055
|
app: resolveLoadedTopo(effectivePath, mod),
|
|
1125
|
-
cliAliases: resolveLoadedCliAliases(effectivePath, mod),
|
|
1126
1056
|
mirrorRoot: absolutePath,
|
|
1057
|
+
overlays: resolveTrailsOverlays(mod, effectivePath),
|
|
1127
1058
|
release: noopRelease,
|
|
1128
1059
|
};
|
|
1129
1060
|
};
|
|
@@ -1144,8 +1075,8 @@ const createFilesystemLease = async (
|
|
|
1144
1075
|
|
|
1145
1076
|
return {
|
|
1146
1077
|
app: resolveLoadedTopo(effectivePath, mod),
|
|
1147
|
-
cliAliases: resolveLoadedCliAliases(effectivePath, mod),
|
|
1148
1078
|
mirrorRoot,
|
|
1079
|
+
overlays: resolveTrailsOverlays(mod, effectivePath),
|
|
1149
1080
|
release,
|
|
1150
1081
|
};
|
|
1151
1082
|
} catch (error) {
|