@ontrails/trails 1.0.0-beta.14 → 1.0.0-beta.16
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 +208 -0
- package/README.md +27 -0
- package/package.json +19 -8
- package/src/app.ts +17 -7
- package/src/clack.ts +1 -1
- package/src/cli.ts +304 -10
- package/src/completions.ts +240 -0
- package/src/load-app-mirror.ts +160 -0
- package/src/local-state-io.ts +153 -0
- package/src/project-writes.ts +320 -0
- package/src/run-collision.ts +125 -0
- package/src/run-completions-install.ts +179 -0
- package/src/run-example.ts +149 -0
- package/src/run-examples.ts +148 -0
- package/src/run-quiet.ts +75 -0
- package/src/run-trace.ts +273 -0
- package/src/run-warden.ts +39 -0
- package/src/run-watch.ts +432 -0
- package/src/scaffold-versions.generated.ts +12 -0
- package/src/trails/add-surface.ts +172 -0
- package/src/trails/add-trail.ts +73 -27
- package/src/trails/add-verify.ts +68 -23
- package/src/trails/completions-complete.ts +165 -0
- package/src/trails/completions.ts +47 -0
- package/src/trails/create-scaffold.ts +101 -35
- package/src/trails/create.ts +87 -74
- package/src/trails/dev-clean.ts +31 -22
- package/src/trails/dev-reset.ts +9 -3
- package/src/trails/dev-stats.ts +28 -20
- package/src/trails/dev-support.ts +109 -95
- package/src/trails/draft-promote.ts +351 -107
- package/src/trails/guide.ts +55 -38
- package/src/trails/load-app.ts +712 -38
- package/src/trails/root-dir.ts +21 -0
- package/src/trails/run-example.ts +482 -0
- package/src/trails/run-examples.ts +141 -0
- package/src/trails/run.ts +403 -0
- package/src/trails/survey.ts +517 -186
- package/src/trails/topo-activation.ts +385 -0
- package/src/trails/topo-compile.ts +55 -0
- package/src/trails/topo-history.ts +14 -11
- package/src/trails/topo-output-schemas.ts +175 -0
- package/src/trails/topo-pin.ts +25 -16
- package/src/trails/topo-read-support.ts +178 -238
- package/src/trails/topo-reports.ts +445 -63
- package/src/trails/topo-store-support.ts +67 -35
- package/src/trails/topo-support.ts +93 -147
- package/src/trails/topo-unpin.ts +17 -7
- package/src/trails/topo-verify.ts +19 -10
- package/src/trails/topo.ts +64 -31
- package/src/trails/warden-guide.ts +121 -0
- package/src/trails/warden.ts +137 -47
- package/src/versions.ts +28 -0
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/__tests__/examples.test.ts +0 -20
- package/dist/bin/trails.d.ts +0 -3
- package/dist/bin/trails.d.ts.map +0 -1
- package/dist/bin/trails.js +0 -4
- package/dist/bin/trails.js.map +0 -1
- package/dist/src/app.d.ts +0 -2
- package/dist/src/app.d.ts.map +0 -1
- package/dist/src/app.js +0 -22
- package/dist/src/app.js.map +0 -1
- package/dist/src/clack.d.ts +0 -9
- package/dist/src/clack.d.ts.map +0 -1
- package/dist/src/clack.js +0 -84
- package/dist/src/clack.js.map +0 -1
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js +0 -13
- package/dist/src/cli.js.map +0 -1
- package/dist/src/trails/add-surface.d.ts +0 -13
- package/dist/src/trails/add-surface.d.ts.map +0 -1
- package/dist/src/trails/add-surface.js +0 -88
- package/dist/src/trails/add-surface.js.map +0 -1
- package/dist/src/trails/add-trail.d.ts +0 -10
- package/dist/src/trails/add-trail.d.ts.map +0 -1
- package/dist/src/trails/add-trail.js +0 -77
- package/dist/src/trails/add-trail.js.map +0 -1
- package/dist/src/trails/add-trailhead.d.ts +0 -13
- package/dist/src/trails/add-trailhead.d.ts.map +0 -1
- package/dist/src/trails/add-trailhead.js +0 -88
- package/dist/src/trails/add-trailhead.js.map +0 -1
- package/dist/src/trails/add-verify.d.ts +0 -10
- package/dist/src/trails/add-verify.d.ts.map +0 -1
- package/dist/src/trails/add-verify.js +0 -67
- package/dist/src/trails/add-verify.js.map +0 -1
- package/dist/src/trails/create-scaffold.d.ts +0 -15
- package/dist/src/trails/create-scaffold.d.ts.map +0 -1
- package/dist/src/trails/create-scaffold.js +0 -288
- package/dist/src/trails/create-scaffold.js.map +0 -1
- package/dist/src/trails/create.d.ts +0 -22
- package/dist/src/trails/create.d.ts.map +0 -1
- package/dist/src/trails/create.js +0 -121
- package/dist/src/trails/create.js.map +0 -1
- package/dist/src/trails/dev-clean.d.ts +0 -9
- package/dist/src/trails/dev-clean.d.ts.map +0 -1
- package/dist/src/trails/dev-clean.js +0 -65
- package/dist/src/trails/dev-clean.js.map +0 -1
- package/dist/src/trails/dev-reset.d.ts +0 -6
- package/dist/src/trails/dev-reset.d.ts.map +0 -1
- package/dist/src/trails/dev-reset.js +0 -38
- package/dist/src/trails/dev-reset.js.map +0 -1
- package/dist/src/trails/dev-stats.d.ts +0 -7
- package/dist/src/trails/dev-stats.d.ts.map +0 -1
- package/dist/src/trails/dev-stats.js +0 -61
- package/dist/src/trails/dev-stats.js.map +0 -1
- package/dist/src/trails/dev-support.d.ts +0 -64
- package/dist/src/trails/dev-support.d.ts.map +0 -1
- package/dist/src/trails/dev-support.js +0 -178
- package/dist/src/trails/dev-support.js.map +0 -1
- package/dist/src/trails/draft-promote.d.ts +0 -18
- package/dist/src/trails/draft-promote.d.ts.map +0 -1
- package/dist/src/trails/draft-promote.js +0 -386
- package/dist/src/trails/draft-promote.js.map +0 -1
- package/dist/src/trails/guide.d.ts +0 -21
- package/dist/src/trails/guide.d.ts.map +0 -1
- package/dist/src/trails/guide.js +0 -64
- package/dist/src/trails/guide.js.map +0 -1
- package/dist/src/trails/load-app.d.ts +0 -6
- package/dist/src/trails/load-app.d.ts.map +0 -1
- package/dist/src/trails/load-app.js +0 -67
- package/dist/src/trails/load-app.js.map +0 -1
- package/dist/src/trails/project.d.ts +0 -8
- package/dist/src/trails/project.d.ts.map +0 -1
- package/dist/src/trails/project.js +0 -54
- package/dist/src/trails/project.js.map +0 -1
- package/dist/src/trails/survey.d.ts +0 -18
- package/dist/src/trails/survey.d.ts.map +0 -1
- package/dist/src/trails/survey.js +0 -212
- package/dist/src/trails/survey.js.map +0 -1
- package/dist/src/trails/topo-constants.d.ts +0 -3
- package/dist/src/trails/topo-constants.d.ts.map +0 -1
- package/dist/src/trails/topo-constants.js +0 -3
- package/dist/src/trails/topo-constants.js.map +0 -1
- package/dist/src/trails/topo-export.d.ts +0 -18
- package/dist/src/trails/topo-export.d.ts.map +0 -1
- package/dist/src/trails/topo-export.js +0 -34
- package/dist/src/trails/topo-export.js.map +0 -1
- package/dist/src/trails/topo-history.d.ts +0 -24
- package/dist/src/trails/topo-history.d.ts.map +0 -1
- package/dist/src/trails/topo-history.js +0 -33
- package/dist/src/trails/topo-history.js.map +0 -1
- package/dist/src/trails/topo-pin.d.ts +0 -21
- package/dist/src/trails/topo-pin.d.ts.map +0 -1
- package/dist/src/trails/topo-pin.js +0 -35
- package/dist/src/trails/topo-pin.js.map +0 -1
- package/dist/src/trails/topo-read-support.d.ts +0 -54
- package/dist/src/trails/topo-read-support.d.ts.map +0 -1
- package/dist/src/trails/topo-read-support.js +0 -178
- package/dist/src/trails/topo-read-support.js.map +0 -1
- package/dist/src/trails/topo-reports.d.ts +0 -50
- package/dist/src/trails/topo-reports.d.ts.map +0 -1
- package/dist/src/trails/topo-reports.js +0 -122
- package/dist/src/trails/topo-reports.js.map +0 -1
- package/dist/src/trails/topo-show.d.ts +0 -23
- package/dist/src/trails/topo-show.d.ts.map +0 -1
- package/dist/src/trails/topo-show.js +0 -53
- package/dist/src/trails/topo-show.js.map +0 -1
- package/dist/src/trails/topo-store-support.d.ts +0 -13
- package/dist/src/trails/topo-store-support.d.ts.map +0 -1
- package/dist/src/trails/topo-store-support.js +0 -55
- package/dist/src/trails/topo-store-support.js.map +0 -1
- package/dist/src/trails/topo-support.d.ts +0 -87
- package/dist/src/trails/topo-support.d.ts.map +0 -1
- package/dist/src/trails/topo-support.js +0 -165
- package/dist/src/trails/topo-support.js.map +0 -1
- package/dist/src/trails/topo-unpin.d.ts +0 -15
- package/dist/src/trails/topo-unpin.d.ts.map +0 -1
- package/dist/src/trails/topo-unpin.js +0 -39
- package/dist/src/trails/topo-unpin.js.map +0 -1
- package/dist/src/trails/topo-verify.d.ts +0 -5
- package/dist/src/trails/topo-verify.d.ts.map +0 -1
- package/dist/src/trails/topo-verify.js +0 -28
- package/dist/src/trails/topo-verify.js.map +0 -1
- package/dist/src/trails/topo.d.ts +0 -5
- package/dist/src/trails/topo.d.ts.map +0 -1
- package/dist/src/trails/topo.js +0 -67
- package/dist/src/trails/topo.js.map +0 -1
- package/dist/src/trails/warden.d.ts +0 -19
- package/dist/src/trails/warden.d.ts.map +0 -1
- package/dist/src/trails/warden.js +0 -89
- package/dist/src/trails/warden.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/__tests__/create.test.ts +0 -351
- package/src/__tests__/draft-promote.test.ts +0 -144
- package/src/__tests__/guide.test.ts +0 -91
- package/src/__tests__/load-app.test.ts +0 -58
- package/src/__tests__/survey.test.ts +0 -301
- package/src/__tests__/topo-dev.test.ts +0 -424
- package/src/__tests__/warden.test.ts +0 -74
- package/src/trails/add-trailhead.ts +0 -121
- package/src/trails/topo-export.ts +0 -39
- package/src/trails/topo-show.ts +0 -58
- package/tsconfig.json +0 -9
|
@@ -1,73 +1,105 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Stored-export pipeline for topo persistence.
|
|
3
3
|
*
|
|
4
|
-
* Extracted from topo-support.ts
|
|
5
|
-
* keeping
|
|
4
|
+
* Extracted from topo-support.ts to isolate store persistence concerns,
|
|
5
|
+
* keeping module boundaries clean.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { Database } from 'bun:sqlite';
|
|
9
|
+
|
|
8
10
|
import type { Topo } from '@ontrails/core';
|
|
9
|
-
import { InternalError, Result } from '@ontrails/core';
|
|
10
|
-
import type { TopoSaveRecord } from '@ontrails/core/internal/topo-saves';
|
|
11
|
-
import type { StoredTopoExport } from '@ontrails/core/internal/topo-store';
|
|
12
|
-
import {
|
|
13
|
-
getStoredTopoExport,
|
|
14
|
-
persistEstablishedTopoSave,
|
|
15
|
-
} from '@ontrails/core/internal/topo-store';
|
|
16
11
|
import {
|
|
12
|
+
deriveTrailsDir,
|
|
13
|
+
InternalError,
|
|
17
14
|
openWriteTrailsDb,
|
|
18
|
-
|
|
19
|
-
} from '@ontrails/core
|
|
20
|
-
import type {
|
|
21
|
-
|
|
15
|
+
Result,
|
|
16
|
+
} from '@ontrails/core';
|
|
17
|
+
import type {
|
|
18
|
+
LockManifest,
|
|
19
|
+
TopoGraph,
|
|
20
|
+
TopoSnapshot,
|
|
21
|
+
} from '@ontrails/topographer';
|
|
22
|
+
import type { StoredTopoExport } from '@ontrails/topographer/backend-support';
|
|
23
|
+
import { writeLockManifest, writeTopoGraph } from '@ontrails/topographer';
|
|
24
|
+
import {
|
|
25
|
+
createStoredTopoSnapshot,
|
|
26
|
+
getStoredTopoExport,
|
|
27
|
+
} from '@ontrails/topographer/backend-support';
|
|
22
28
|
|
|
23
29
|
import type { TopoExportReport } from './topo-support.js';
|
|
24
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
deriveRootDir,
|
|
32
|
+
deriveTopoCounts,
|
|
33
|
+
readGitState,
|
|
34
|
+
} from './topo-support.js';
|
|
25
35
|
|
|
26
36
|
const persistAndReadStoredExport = (
|
|
27
37
|
app: Topo,
|
|
28
38
|
db: ReturnType<typeof openWriteTrailsDb>,
|
|
29
39
|
rootDir: string
|
|
30
|
-
): Result<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
): Result<
|
|
41
|
+
{ snapshot: TopoSnapshot; storedExport: StoredTopoExport },
|
|
42
|
+
Error
|
|
43
|
+
> => {
|
|
44
|
+
const snapshotResult = createStoredTopoSnapshot(db, app, {
|
|
45
|
+
...readGitState(rootDir),
|
|
46
|
+
...deriveTopoCounts(app),
|
|
34
47
|
});
|
|
35
|
-
if (
|
|
36
|
-
return
|
|
48
|
+
if (snapshotResult.isErr()) {
|
|
49
|
+
return snapshotResult;
|
|
37
50
|
}
|
|
38
51
|
|
|
39
|
-
const
|
|
40
|
-
const storedExport = getStoredTopoExport(db,
|
|
52
|
+
const snapshot = snapshotResult.value;
|
|
53
|
+
const storedExport = getStoredTopoExport(db, snapshot.id);
|
|
41
54
|
|
|
42
55
|
if (storedExport === undefined) {
|
|
43
56
|
return Result.err(
|
|
44
|
-
new InternalError(
|
|
57
|
+
new InternalError(
|
|
58
|
+
`Missing stored topo export for snapshot "${snapshot.id}"`
|
|
59
|
+
)
|
|
45
60
|
);
|
|
46
61
|
}
|
|
47
62
|
|
|
48
63
|
return Result.ok({
|
|
49
|
-
|
|
64
|
+
snapshot,
|
|
50
65
|
storedExport,
|
|
51
66
|
});
|
|
52
67
|
};
|
|
53
68
|
|
|
69
|
+
export const deriveCurrentTopoExport = (
|
|
70
|
+
app: Topo,
|
|
71
|
+
options?: { readonly rootDir?: string }
|
|
72
|
+
): Result<StoredTopoExport, Error> => {
|
|
73
|
+
const rootDir = deriveRootDir(options?.rootDir);
|
|
74
|
+
const db = new Database(':memory:');
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const projected = persistAndReadStoredExport(app, db, rootDir);
|
|
78
|
+
return projected.isErr()
|
|
79
|
+
? projected
|
|
80
|
+
: Result.ok(projected.value.storedExport);
|
|
81
|
+
} finally {
|
|
82
|
+
db.close();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
54
86
|
const writeStoredExportArtifacts = async (
|
|
55
87
|
storedExport: StoredTopoExport,
|
|
56
88
|
trailsDir: string
|
|
57
|
-
): Promise<Pick<TopoExportReport, 'hash' | 'lockPath' | '
|
|
58
|
-
const
|
|
59
|
-
JSON.parse(storedExport.
|
|
89
|
+
): Promise<Pick<TopoExportReport, 'hash' | 'lockPath' | 'topoPath'>> => {
|
|
90
|
+
const topoPath = await writeTopoGraph(
|
|
91
|
+
JSON.parse(storedExport.topoGraphJson) as TopoGraph,
|
|
60
92
|
{ dir: trailsDir }
|
|
61
93
|
);
|
|
62
|
-
const lockPath = await
|
|
63
|
-
JSON.parse(storedExport.
|
|
94
|
+
const lockPath = await writeLockManifest(
|
|
95
|
+
JSON.parse(storedExport.lockManifestJson) as LockManifest,
|
|
64
96
|
{ dir: trailsDir }
|
|
65
97
|
);
|
|
66
98
|
|
|
67
99
|
return {
|
|
68
|
-
hash: storedExport.
|
|
100
|
+
hash: storedExport.topoGraphHash,
|
|
69
101
|
lockPath,
|
|
70
|
-
|
|
102
|
+
topoPath,
|
|
71
103
|
};
|
|
72
104
|
};
|
|
73
105
|
|
|
@@ -75,7 +107,7 @@ export const exportCurrentTopo = async (
|
|
|
75
107
|
app: Topo,
|
|
76
108
|
options?: { readonly rootDir?: string }
|
|
77
109
|
): Promise<Result<TopoExportReport, Error>> => {
|
|
78
|
-
const rootDir =
|
|
110
|
+
const rootDir = deriveRootDir(options?.rootDir);
|
|
79
111
|
const db = openWriteTrailsDb({ rootDir });
|
|
80
112
|
|
|
81
113
|
try {
|
|
@@ -84,12 +116,12 @@ export const exportCurrentTopo = async (
|
|
|
84
116
|
return persisted;
|
|
85
117
|
}
|
|
86
118
|
|
|
87
|
-
const {
|
|
119
|
+
const { snapshot, storedExport } = persisted.value;
|
|
88
120
|
const artifacts = await writeStoredExportArtifacts(
|
|
89
121
|
storedExport,
|
|
90
|
-
|
|
122
|
+
deriveTrailsDir({ rootDir })
|
|
91
123
|
);
|
|
92
|
-
return Result.ok({ ...artifacts,
|
|
124
|
+
return Result.ok({ ...artifacts, snapshot });
|
|
93
125
|
} finally {
|
|
94
126
|
db.close();
|
|
95
127
|
}
|
|
@@ -1,64 +1,44 @@
|
|
|
1
|
-
import { existsSync
|
|
2
|
-
import { tmpdir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
4
2
|
import { fileURLToPath } from 'node:url';
|
|
5
3
|
|
|
4
|
+
import { deriveTrailsDbPath } from '@ontrails/core';
|
|
6
5
|
import type { Topo } from '@ontrails/core';
|
|
7
|
-
import type {
|
|
8
|
-
TopoPinRecord,
|
|
9
|
-
TopoSaveRecord,
|
|
10
|
-
} from '@ontrails/core/internal/topo-saves';
|
|
11
6
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from '@ontrails/
|
|
18
|
-
import { persistEstablishedTopoSave } from '@ontrails/core/internal/topo-store';
|
|
19
|
-
import {
|
|
20
|
-
openReadTrailsDb,
|
|
21
|
-
openWriteTrailsDb,
|
|
22
|
-
resolveTrailsDbPath,
|
|
23
|
-
} from '@ontrails/core/internal/trails-db';
|
|
7
|
+
createTopoSnapshot as persistTopoSnapshot,
|
|
8
|
+
listTopoSnapshots as readTopoSnapshots,
|
|
9
|
+
pinTopoSnapshot,
|
|
10
|
+
unpinTopoSnapshot,
|
|
11
|
+
} from '@ontrails/topographer';
|
|
12
|
+
import type { TopoSnapshot } from '@ontrails/topographer';
|
|
24
13
|
import { z } from 'zod';
|
|
25
14
|
|
|
15
|
+
import {
|
|
16
|
+
createIsolatedExampleRoot,
|
|
17
|
+
writeIsolatedExampleAppModule,
|
|
18
|
+
} from '../local-state-io.js';
|
|
19
|
+
|
|
20
|
+
import { requireTrailRootDir } from './root-dir.js';
|
|
26
21
|
import type { BriefReport, SurveyListReport } from './topo-reports.js';
|
|
27
22
|
|
|
28
|
-
/** Output schema for a topo
|
|
29
|
-
export const
|
|
23
|
+
/** Output schema for a topo snapshot record. Shared across topo trails. */
|
|
24
|
+
export const topoSnapshotOutput = z.object({
|
|
30
25
|
createdAt: z.string(),
|
|
31
26
|
gitDirty: z.boolean(),
|
|
32
27
|
gitSha: z.string().optional(),
|
|
33
28
|
id: z.string(),
|
|
34
|
-
|
|
29
|
+
pinnedAs: z.string().optional(),
|
|
30
|
+
resourceCount: z.number(),
|
|
35
31
|
signalCount: z.number(),
|
|
36
32
|
trailCount: z.number(),
|
|
37
33
|
});
|
|
38
34
|
|
|
39
|
-
/** Output schema for a topo pin record. Shared across topo trails. */
|
|
40
|
-
export const topoPinOutput = z.object({
|
|
41
|
-
createdAt: z.string(),
|
|
42
|
-
name: z.string(),
|
|
43
|
-
saveId: z.string(),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
export const DEFAULT_APP_MODULE = './src/app.ts';
|
|
47
35
|
export const DEFAULT_TOPO_HISTORY_LIMIT = 10;
|
|
48
36
|
export const LOCK_PATH = '.trails/trails.lock';
|
|
49
|
-
export const LEGACY_LOCK_PATH = '.trails/trailhead.lock';
|
|
50
|
-
|
|
51
|
-
/** Resolve the lockfile path, preferring the current name with legacy fallback. */
|
|
52
|
-
export const resolveLockPath = (trailsDir: string): string => {
|
|
53
|
-
const primary = join(trailsDir, 'trails.lock');
|
|
54
|
-
if (existsSync(primary)) {
|
|
55
|
-
return primary;
|
|
56
|
-
}
|
|
57
|
-
const legacy = join(trailsDir, 'trailhead.lock');
|
|
58
|
-
return existsSync(legacy) ? legacy : primary;
|
|
59
|
-
};
|
|
60
37
|
const EXAMPLE_APP_MODULE = fileURLToPath(new URL('../app.ts', import.meta.url));
|
|
61
38
|
|
|
39
|
+
const uniqueExampleRootName = (name: string): string =>
|
|
40
|
+
`${name}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
41
|
+
|
|
62
42
|
export interface TopoSummaryReport {
|
|
63
43
|
readonly app: BriefReport;
|
|
64
44
|
readonly dbPath: string;
|
|
@@ -70,17 +50,16 @@ export interface TopoSummaryReport {
|
|
|
70
50
|
export interface TopoHistoryReport {
|
|
71
51
|
readonly dbPath: string;
|
|
72
52
|
readonly limit: number;
|
|
73
|
-
readonly
|
|
74
|
-
readonly
|
|
75
|
-
readonly
|
|
76
|
-
readonly saves: TopoSaveRecord[];
|
|
53
|
+
readonly pinnedCount: number;
|
|
54
|
+
readonly snapshotCount: number;
|
|
55
|
+
readonly snapshots: TopoSnapshot[];
|
|
77
56
|
}
|
|
78
57
|
|
|
79
58
|
export interface TopoExportReport {
|
|
80
59
|
readonly hash: string;
|
|
81
60
|
readonly lockPath: string;
|
|
82
|
-
readonly
|
|
83
|
-
readonly
|
|
61
|
+
readonly snapshot: TopoSnapshot;
|
|
62
|
+
readonly topoPath: string;
|
|
84
63
|
}
|
|
85
64
|
|
|
86
65
|
export interface TopoVerifyReport {
|
|
@@ -90,7 +69,7 @@ export interface TopoVerifyReport {
|
|
|
90
69
|
readonly stale: false;
|
|
91
70
|
}
|
|
92
71
|
|
|
93
|
-
export const
|
|
72
|
+
export const deriveRootDir = (cwd?: string): string => requireTrailRootDir(cwd);
|
|
94
73
|
|
|
95
74
|
const safeGit = (cwd: string, args: readonly string[]): string | undefined => {
|
|
96
75
|
const proc = Bun.spawnSync({
|
|
@@ -105,7 +84,7 @@ const safeGit = (cwd: string, args: readonly string[]): string | undefined => {
|
|
|
105
84
|
return text.length === 0 ? undefined : text;
|
|
106
85
|
};
|
|
107
86
|
|
|
108
|
-
export const
|
|
87
|
+
export const readGitState = (
|
|
109
88
|
rootDir: string
|
|
110
89
|
): { readonly gitDirty: boolean; readonly gitSha?: string } => {
|
|
111
90
|
const gitSha = safeGit(rootDir, ['rev-parse', 'HEAD']);
|
|
@@ -116,10 +95,10 @@ export const currentGitState = (
|
|
|
116
95
|
};
|
|
117
96
|
};
|
|
118
97
|
|
|
119
|
-
export const
|
|
98
|
+
export const deriveTopoCounts = (
|
|
120
99
|
app: Topo
|
|
121
|
-
): Pick<
|
|
122
|
-
|
|
100
|
+
): Pick<TopoSnapshot, 'resourceCount' | 'signalCount' | 'trailCount'> => ({
|
|
101
|
+
resourceCount: app.resources.size,
|
|
123
102
|
signalCount: app.signals.size,
|
|
124
103
|
trailCount: app.trails.size,
|
|
125
104
|
});
|
|
@@ -130,145 +109,112 @@ const emptyTopoHistory = (
|
|
|
130
109
|
): TopoHistoryReport => ({
|
|
131
110
|
dbPath,
|
|
132
111
|
limit,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
saves: [],
|
|
112
|
+
pinnedCount: 0,
|
|
113
|
+
snapshotCount: 0,
|
|
114
|
+
snapshots: [],
|
|
137
115
|
});
|
|
138
116
|
|
|
139
|
-
const
|
|
117
|
+
const collectTopoHistory = (
|
|
140
118
|
dbPath: string,
|
|
141
119
|
limit: number,
|
|
142
|
-
|
|
143
|
-
allSaves: readonly TopoSaveRecord[]
|
|
120
|
+
snapshots: readonly TopoSnapshot[]
|
|
144
121
|
): TopoHistoryReport => ({
|
|
145
122
|
dbPath,
|
|
146
123
|
limit,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
124
|
+
pinnedCount: snapshots.filter((snapshot) => snapshot.pinnedAs !== undefined)
|
|
125
|
+
.length,
|
|
126
|
+
snapshotCount: snapshots.length,
|
|
127
|
+
snapshots: snapshots.slice(0, limit),
|
|
151
128
|
});
|
|
152
129
|
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
db: Parameters<typeof unpinTopoSave>[0]
|
|
130
|
+
const buildSnapshotInput = (
|
|
131
|
+
app: Topo,
|
|
132
|
+
rootDir: string
|
|
157
133
|
): {
|
|
158
|
-
readonly
|
|
159
|
-
readonly
|
|
160
|
-
readonly
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
134
|
+
readonly gitDirty: boolean;
|
|
135
|
+
readonly gitSha?: string;
|
|
136
|
+
readonly resourceCount: number;
|
|
137
|
+
readonly signalCount: number;
|
|
138
|
+
readonly trailCount: number;
|
|
139
|
+
} => ({
|
|
140
|
+
...readGitState(rootDir),
|
|
141
|
+
...deriveTopoCounts(app),
|
|
142
|
+
});
|
|
165
143
|
|
|
166
|
-
export const
|
|
144
|
+
export const createIsolatedExampleInput = (
|
|
167
145
|
name: string
|
|
168
146
|
): { readonly module: string; readonly rootDir: string } => {
|
|
169
|
-
const rootDir =
|
|
170
|
-
rmSync(rootDir, { force: true, recursive: true });
|
|
171
|
-
mkdirSync(rootDir, { recursive: true });
|
|
147
|
+
const rootDir = createIsolatedExampleRoot(uniqueExampleRootName(name));
|
|
172
148
|
return {
|
|
173
|
-
module: EXAMPLE_APP_MODULE,
|
|
149
|
+
module: writeIsolatedExampleAppModule(rootDir, EXAMPLE_APP_MODULE),
|
|
174
150
|
rootDir,
|
|
175
151
|
};
|
|
176
152
|
};
|
|
177
153
|
|
|
178
|
-
export const createCurrentTopoSave = (
|
|
179
|
-
app: Topo,
|
|
180
|
-
options?: { readonly rootDir?: string }
|
|
181
|
-
): TopoSaveRecord => {
|
|
182
|
-
const rootDir = resolveRootDir(options?.rootDir);
|
|
183
|
-
const db = openWriteTrailsDb({ rootDir });
|
|
184
|
-
|
|
185
|
-
try {
|
|
186
|
-
const result = persistEstablishedTopoSave(db, app, {
|
|
187
|
-
...currentGitState(rootDir),
|
|
188
|
-
...topoCounts(app),
|
|
189
|
-
});
|
|
190
|
-
if (result.isErr()) {
|
|
191
|
-
throw result.error;
|
|
192
|
-
}
|
|
193
|
-
return result.value;
|
|
194
|
-
} finally {
|
|
195
|
-
db.close();
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
|
|
199
154
|
export const listTopoHistory = (options?: {
|
|
200
155
|
readonly limit?: number;
|
|
201
156
|
readonly rootDir?: string;
|
|
202
157
|
}): TopoHistoryReport => {
|
|
203
|
-
const rootDir =
|
|
158
|
+
const rootDir = deriveRootDir(options?.rootDir);
|
|
204
159
|
const limit = options?.limit ?? DEFAULT_TOPO_HISTORY_LIMIT;
|
|
205
|
-
const dbPath =
|
|
160
|
+
const dbPath = deriveTrailsDbPath({ rootDir });
|
|
206
161
|
if (!existsSync(dbPath)) {
|
|
207
162
|
return emptyTopoHistory(dbPath, limit);
|
|
208
163
|
}
|
|
209
|
-
const db = openReadTrailsDb({ rootDir });
|
|
210
164
|
|
|
211
|
-
|
|
212
|
-
return collectedTopoHistory(
|
|
213
|
-
dbPath,
|
|
214
|
-
limit,
|
|
215
|
-
listTopoPins(db),
|
|
216
|
-
listTopoSaves(db)
|
|
217
|
-
);
|
|
218
|
-
} finally {
|
|
219
|
-
db.close();
|
|
220
|
-
}
|
|
165
|
+
return collectTopoHistory(dbPath, limit, readTopoSnapshots({ rootDir }));
|
|
221
166
|
};
|
|
222
167
|
|
|
223
|
-
export const
|
|
168
|
+
export const pinCurrentTopoSnapshot = (
|
|
224
169
|
app: Topo,
|
|
225
170
|
input: { readonly name: string; readonly rootDir?: string }
|
|
226
|
-
): { readonly
|
|
227
|
-
const rootDir =
|
|
228
|
-
const
|
|
171
|
+
): { readonly snapshot: TopoSnapshot } => {
|
|
172
|
+
const rootDir = deriveRootDir(input.rootDir);
|
|
173
|
+
const created = persistTopoSnapshot(app, {
|
|
174
|
+
rootDir,
|
|
175
|
+
...buildSnapshotInput(app, rootDir),
|
|
176
|
+
});
|
|
177
|
+
if (created.isErr()) {
|
|
178
|
+
throw created.error;
|
|
179
|
+
}
|
|
229
180
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
});
|
|
235
|
-
if (result.isErr()) {
|
|
236
|
-
throw result.error;
|
|
237
|
-
}
|
|
238
|
-
const pin = pinTopoSave(db, {
|
|
239
|
-
name: input.name,
|
|
240
|
-
saveId: result.value.id,
|
|
241
|
-
});
|
|
242
|
-
return { pin, save: result.value };
|
|
243
|
-
} finally {
|
|
244
|
-
db.close();
|
|
181
|
+
const snapshot = pinTopoSnapshot(created.value.id, input.name, {
|
|
182
|
+
rootDir,
|
|
183
|
+
});
|
|
184
|
+
if (snapshot === undefined) {
|
|
185
|
+
throw new Error(`Missing topo snapshot "${created.value.id}" to pin`);
|
|
245
186
|
}
|
|
187
|
+
|
|
188
|
+
return { snapshot };
|
|
246
189
|
};
|
|
247
190
|
|
|
248
|
-
export const
|
|
191
|
+
export const removePinnedTopoSnapshot = (input: {
|
|
249
192
|
readonly dryRun: boolean;
|
|
250
193
|
readonly name: string;
|
|
251
194
|
readonly rootDir?: string;
|
|
252
195
|
}): {
|
|
253
196
|
readonly dryRun: boolean;
|
|
254
|
-
readonly pin?: TopoPinRecord;
|
|
255
197
|
readonly removed: boolean;
|
|
198
|
+
readonly snapshot?: TopoSnapshot;
|
|
256
199
|
} => {
|
|
257
|
-
const rootDir =
|
|
258
|
-
if (!existsSync(
|
|
200
|
+
const rootDir = deriveRootDir(input.rootDir);
|
|
201
|
+
if (!existsSync(deriveTrailsDbPath({ rootDir }))) {
|
|
259
202
|
return { dryRun: input.dryRun, removed: false };
|
|
260
203
|
}
|
|
261
|
-
const db = input.dryRun
|
|
262
|
-
? openReadTrailsDb({ rootDir })
|
|
263
|
-
: openWriteTrailsDb({ rootDir });
|
|
264
204
|
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
db.close();
|
|
205
|
+
if (input.dryRun) {
|
|
206
|
+
const snapshot = readTopoSnapshots({ pinned: true, rootDir }).find(
|
|
207
|
+
(candidate) => candidate.pinnedAs === input.name
|
|
208
|
+
);
|
|
209
|
+
return snapshot === undefined
|
|
210
|
+
? { dryRun: true, removed: false }
|
|
211
|
+
: { dryRun: true, removed: false, snapshot };
|
|
273
212
|
}
|
|
213
|
+
|
|
214
|
+
const snapshot = unpinTopoSnapshot(input.name, { rootDir });
|
|
215
|
+
return {
|
|
216
|
+
dryRun: false,
|
|
217
|
+
removed: snapshot !== undefined,
|
|
218
|
+
...(snapshot === undefined ? {} : { snapshot }),
|
|
219
|
+
};
|
|
274
220
|
};
|
package/src/trails/topo-unpin.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { Result, ValidationError, trail } from '@ontrails/core';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
createIsolatedExampleInput,
|
|
6
|
+
removePinnedTopoSnapshot,
|
|
7
|
+
topoSnapshotOutput,
|
|
8
8
|
} from './topo-support.js';
|
|
9
|
+
import { resolveTrailRootDir } from './root-dir.js';
|
|
9
10
|
|
|
10
11
|
export const topoUnpinTrail = trail('topo.unpin', {
|
|
11
12
|
blaze: (input, ctx) => {
|
|
@@ -17,16 +18,24 @@ export const topoUnpinTrail = trail('topo.unpin', {
|
|
|
17
18
|
);
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
+
const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
|
|
22
|
+
if (rootDirResult.isErr()) {
|
|
23
|
+
return Result.err(rootDirResult.error);
|
|
24
|
+
}
|
|
25
|
+
const rootDir = rootDirResult.value;
|
|
21
26
|
return Result.ok(
|
|
22
|
-
|
|
27
|
+
removePinnedTopoSnapshot({
|
|
28
|
+
dryRun: input.dryRun,
|
|
29
|
+
name: input.name,
|
|
30
|
+
rootDir,
|
|
31
|
+
})
|
|
23
32
|
);
|
|
24
33
|
},
|
|
25
34
|
description: 'Remove a named topo pin',
|
|
26
35
|
examples: [
|
|
27
36
|
{
|
|
28
37
|
input: {
|
|
29
|
-
...
|
|
38
|
+
...createIsolatedExampleInput('topo-unpin'),
|
|
30
39
|
dryRun: true,
|
|
31
40
|
name: 'before-auth-refactor',
|
|
32
41
|
},
|
|
@@ -45,7 +54,8 @@ export const topoUnpinTrail = trail('topo.unpin', {
|
|
|
45
54
|
intent: 'destroy',
|
|
46
55
|
output: z.object({
|
|
47
56
|
dryRun: z.boolean(),
|
|
48
|
-
pin: topoPinOutput.optional(),
|
|
49
57
|
removed: z.boolean(),
|
|
58
|
+
snapshot: topoSnapshotOutput.optional(),
|
|
50
59
|
}),
|
|
60
|
+
permit: { scopes: ['topo:delete'] },
|
|
51
61
|
});
|
|
@@ -1,22 +1,31 @@
|
|
|
1
|
-
import { trail } from '@ontrails/core';
|
|
1
|
+
import { Result, trail } from '@ontrails/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { tryLoadFreshAppLease } from './load-app.js';
|
|
5
|
+
import { resolveTrailRootDir } from './root-dir.js';
|
|
5
6
|
import { verifyCurrentTopo } from './topo-read-support.js';
|
|
6
|
-
import { DEFAULT_APP_MODULE } from './topo-support.js';
|
|
7
7
|
|
|
8
8
|
export const topoVerifyTrail = trail('topo.verify', {
|
|
9
9
|
blaze: async (input, ctx) => {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
|
|
11
|
+
if (rootDirResult.isErr()) {
|
|
12
|
+
return Result.err(rootDirResult.error);
|
|
13
|
+
}
|
|
14
|
+
const rootDir = rootDirResult.value;
|
|
15
|
+
const leaseResult = await tryLoadFreshAppLease(input.module, rootDir);
|
|
16
|
+
if (leaseResult.isErr()) {
|
|
17
|
+
return Result.err(leaseResult.error);
|
|
18
|
+
}
|
|
19
|
+
const lease = leaseResult.value;
|
|
20
|
+
try {
|
|
21
|
+
return await verifyCurrentTopo(lease.app, { rootDir });
|
|
22
|
+
} finally {
|
|
23
|
+
lease.release();
|
|
24
|
+
}
|
|
13
25
|
},
|
|
14
26
|
description: 'Verify that the committed lockfile matches the current topo',
|
|
15
27
|
input: z.object({
|
|
16
|
-
module: z
|
|
17
|
-
.string()
|
|
18
|
-
.default(DEFAULT_APP_MODULE)
|
|
19
|
-
.describe('Path to the app module'),
|
|
28
|
+
module: z.string().optional().describe('Path to the app module'),
|
|
20
29
|
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
21
30
|
}),
|
|
22
31
|
intent: 'read',
|