@ontrails/trails 1.0.0-beta.13 → 1.0.0-beta.15

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.
Files changed (165) hide show
  1. package/.turbo/turbo-lint.log +1 -1
  2. package/CHANGELOG.md +29 -0
  3. package/__tests__/examples.test.ts +39 -0
  4. package/dist/src/app.d.ts.map +1 -1
  5. package/dist/src/app.js +12 -1
  6. package/dist/src/app.js.map +1 -1
  7. package/dist/src/cli.js +4 -3
  8. package/dist/src/cli.js.map +1 -1
  9. package/dist/src/trails/add-surface.d.ts +3 -3
  10. package/dist/src/trails/add-surface.d.ts.map +1 -1
  11. package/dist/src/trails/add-surface.js +46 -24
  12. package/dist/src/trails/add-surface.js.map +1 -1
  13. package/dist/src/trails/add-trail.d.ts +3 -1
  14. package/dist/src/trails/add-trail.d.ts.map +1 -1
  15. package/dist/src/trails/add-trail.js +49 -22
  16. package/dist/src/trails/add-trail.js.map +1 -1
  17. package/dist/src/trails/add-trailhead.d.ts +13 -0
  18. package/dist/src/trails/add-trailhead.d.ts.map +1 -0
  19. package/dist/src/trails/add-trailhead.js +88 -0
  20. package/dist/src/trails/add-trailhead.js.map +1 -0
  21. package/dist/src/trails/add-verify.d.ts +1 -1
  22. package/dist/src/trails/add-verify.d.ts.map +1 -1
  23. package/dist/src/trails/add-verify.js +17 -16
  24. package/dist/src/trails/add-verify.js.map +1 -1
  25. package/dist/src/trails/create-scaffold.d.ts +1 -1
  26. package/dist/src/trails/create-scaffold.d.ts.map +1 -1
  27. package/dist/src/trails/create-scaffold.js +34 -27
  28. package/dist/src/trails/create-scaffold.js.map +1 -1
  29. package/dist/src/trails/create.d.ts +9 -13
  30. package/dist/src/trails/create.d.ts.map +1 -1
  31. package/dist/src/trails/create.js +40 -35
  32. package/dist/src/trails/create.js.map +1 -1
  33. package/dist/src/trails/dev-clean.d.ts +9 -0
  34. package/dist/src/trails/dev-clean.d.ts.map +1 -0
  35. package/dist/src/trails/dev-clean.js +66 -0
  36. package/dist/src/trails/dev-clean.js.map +1 -0
  37. package/dist/src/trails/dev-reset.d.ts +6 -0
  38. package/dist/src/trails/dev-reset.d.ts.map +1 -0
  39. package/dist/src/trails/dev-reset.js +39 -0
  40. package/dist/src/trails/dev-reset.js.map +1 -0
  41. package/dist/src/trails/dev-stats.d.ts +7 -0
  42. package/dist/src/trails/dev-stats.d.ts.map +1 -0
  43. package/dist/src/trails/dev-stats.js +61 -0
  44. package/dist/src/trails/dev-stats.js.map +1 -0
  45. package/dist/src/trails/dev-support.d.ts +64 -0
  46. package/dist/src/trails/dev-support.d.ts.map +1 -0
  47. package/dist/src/trails/dev-support.js +181 -0
  48. package/dist/src/trails/dev-support.js.map +1 -0
  49. package/dist/src/trails/draft-promote.d.ts +18 -0
  50. package/dist/src/trails/draft-promote.d.ts.map +1 -0
  51. package/dist/src/trails/draft-promote.js +400 -0
  52. package/dist/src/trails/draft-promote.js.map +1 -0
  53. package/dist/src/trails/guide.d.ts +14 -4
  54. package/dist/src/trails/guide.d.ts.map +1 -1
  55. package/dist/src/trails/guide.js +22 -41
  56. package/dist/src/trails/guide.js.map +1 -1
  57. package/dist/src/trails/load-app.d.ts +9 -1
  58. package/dist/src/trails/load-app.d.ts.map +1 -1
  59. package/dist/src/trails/load-app.js +404 -13
  60. package/dist/src/trails/load-app.js.map +1 -1
  61. package/dist/src/trails/project.d.ts.map +1 -1
  62. package/dist/src/trails/project.js +14 -3
  63. package/dist/src/trails/project.js.map +1 -1
  64. package/dist/src/trails/survey.d.ts +6 -60
  65. package/dist/src/trails/survey.d.ts.map +1 -1
  66. package/dist/src/trails/survey.js +83 -182
  67. package/dist/src/trails/survey.js.map +1 -1
  68. package/dist/src/trails/topo-constants.d.ts +3 -0
  69. package/dist/src/trails/topo-constants.d.ts.map +1 -0
  70. package/dist/src/trails/topo-constants.js +3 -0
  71. package/dist/src/trails/topo-constants.js.map +1 -0
  72. package/dist/src/trails/topo-export.d.ts +19 -0
  73. package/dist/src/trails/topo-export.d.ts.map +1 -0
  74. package/dist/src/trails/topo-export.js +31 -0
  75. package/dist/src/trails/topo-export.js.map +1 -0
  76. package/dist/src/trails/topo-history.d.ts +20 -0
  77. package/dist/src/trails/topo-history.d.ts.map +1 -0
  78. package/dist/src/trails/topo-history.js +32 -0
  79. package/dist/src/trails/topo-history.js.map +1 -0
  80. package/dist/src/trails/topo-pin.d.ts +17 -0
  81. package/dist/src/trails/topo-pin.d.ts.map +1 -0
  82. package/dist/src/trails/topo-pin.js +31 -0
  83. package/dist/src/trails/topo-pin.js.map +1 -0
  84. package/dist/src/trails/topo-read-support.d.ts +58 -0
  85. package/dist/src/trails/topo-read-support.d.ts.map +1 -0
  86. package/dist/src/trails/topo-read-support.js +167 -0
  87. package/dist/src/trails/topo-read-support.js.map +1 -0
  88. package/dist/src/trails/topo-reports.d.ts +54 -0
  89. package/dist/src/trails/topo-reports.d.ts.map +1 -0
  90. package/dist/src/trails/topo-reports.js +128 -0
  91. package/dist/src/trails/topo-reports.js.map +1 -0
  92. package/dist/src/trails/topo-show.d.ts +23 -0
  93. package/dist/src/trails/topo-show.d.ts.map +1 -0
  94. package/dist/src/trails/topo-show.js +49 -0
  95. package/dist/src/trails/topo-show.js.map +1 -0
  96. package/dist/src/trails/topo-store-support.d.ts +13 -0
  97. package/dist/src/trails/topo-store-support.d.ts.map +1 -0
  98. package/dist/src/trails/topo-store-support.js +55 -0
  99. package/dist/src/trails/topo-store-support.js.map +1 -0
  100. package/dist/src/trails/topo-support.d.ts +76 -0
  101. package/dist/src/trails/topo-support.d.ts.map +1 -0
  102. package/dist/src/trails/topo-support.js +132 -0
  103. package/dist/src/trails/topo-support.js.map +1 -0
  104. package/dist/src/trails/topo-unpin.d.ts +20 -0
  105. package/dist/src/trails/topo-unpin.d.ts.map +1 -0
  106. package/dist/src/trails/topo-unpin.js +44 -0
  107. package/dist/src/trails/topo-unpin.js.map +1 -0
  108. package/dist/src/trails/topo-verify.d.ts +5 -0
  109. package/dist/src/trails/topo-verify.d.ts.map +1 -0
  110. package/dist/src/trails/topo-verify.js +24 -0
  111. package/dist/src/trails/topo-verify.js.map +1 -0
  112. package/dist/src/trails/topo.d.ts +5 -0
  113. package/dist/src/trails/topo.d.ts.map +1 -0
  114. package/dist/src/trails/topo.js +63 -0
  115. package/dist/src/trails/topo.js.map +1 -0
  116. package/dist/src/trails/warden.d.ts +3 -2
  117. package/dist/src/trails/warden.d.ts.map +1 -1
  118. package/dist/src/trails/warden.js +37 -27
  119. package/dist/src/trails/warden.js.map +1 -1
  120. package/dist/src/versions.d.ts +12 -0
  121. package/dist/src/versions.d.ts.map +1 -0
  122. package/dist/src/versions.js +23 -0
  123. package/dist/src/versions.js.map +1 -0
  124. package/dist/tsconfig.tsbuildinfo +1 -1
  125. package/package.json +8 -7
  126. package/src/__tests__/add-trail.test.ts +97 -0
  127. package/src/__tests__/create.test.ts +91 -27
  128. package/src/__tests__/draft-promote.test.ts +144 -0
  129. package/src/__tests__/guide.test.ts +10 -5
  130. package/src/__tests__/load-app.test.ts +406 -2
  131. package/src/__tests__/survey.test.ts +221 -60
  132. package/src/__tests__/topo-dev.test.ts +426 -0
  133. package/src/app.ts +24 -2
  134. package/src/clack.ts +1 -1
  135. package/src/cli.ts +4 -3
  136. package/src/trails/add-surface.ts +150 -0
  137. package/src/trails/add-trail.ts +46 -10
  138. package/src/trails/add-verify.ts +11 -6
  139. package/src/trails/create-scaffold.ts +16 -3
  140. package/src/trails/create.ts +76 -71
  141. package/src/trails/dev-clean.ts +77 -0
  142. package/src/trails/dev-reset.ts +45 -0
  143. package/src/trails/dev-stats.ts +67 -0
  144. package/src/trails/dev-support.ts +328 -0
  145. package/src/trails/draft-promote.ts +739 -0
  146. package/src/trails/guide.ts +23 -41
  147. package/src/trails/load-app.ts +556 -14
  148. package/src/trails/project.ts +17 -3
  149. package/src/trails/survey.ts +110 -285
  150. package/src/trails/topo-constants.ts +2 -0
  151. package/src/trails/topo-export.ts +35 -0
  152. package/src/trails/topo-history.ts +38 -0
  153. package/src/trails/topo-pin.ts +38 -0
  154. package/src/trails/topo-read-support.ts +329 -0
  155. package/src/trails/topo-reports.ts +228 -0
  156. package/src/trails/topo-show.ts +54 -0
  157. package/src/trails/topo-store-support.ts +104 -0
  158. package/src/trails/topo-support.ts +230 -0
  159. package/src/trails/topo-unpin.ts +56 -0
  160. package/src/trails/topo-verify.ts +25 -0
  161. package/src/trails/topo.ts +69 -0
  162. package/src/trails/warden.ts +13 -3
  163. package/src/versions.ts +43 -0
  164. package/tsconfig.tests.json +10 -0
  165. package/src/trails/add-trailhead.ts +0 -121
@@ -0,0 +1,328 @@
1
+ import { existsSync, rmSync, statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ import {
5
+ countPinnedSnapshots,
6
+ countPrunableSnapshots,
7
+ countTopoSnapshots,
8
+ pruneUnpinnedSnapshots,
9
+ } from '@ontrails/core/internal/topo-snapshots';
10
+ import {
11
+ openReadTrailsDb,
12
+ openWriteTrailsDb,
13
+ deriveTrailsDbPath,
14
+ deriveTrailsDir,
15
+ } from '@ontrails/core/internal/trails-db';
16
+ import {
17
+ DEFAULT_MAX_AGE,
18
+ DEFAULT_MAX_RECORDS,
19
+ applyTraceCleanup,
20
+ countTraceRecords,
21
+ previewTraceCleanup,
22
+ } from '@ontrails/tracing/internal/dev-state';
23
+
24
+ export const DEFAULT_TOPO_SNAPSHOT_RETENTION = 50;
25
+
26
+ const deriveRootDir = (cwd?: string): string => cwd ?? process.cwd();
27
+
28
+ const removeIfPresent = (filePath: string): boolean => {
29
+ if (!existsSync(filePath)) {
30
+ return false;
31
+ }
32
+ rmSync(filePath, { force: true });
33
+ return true;
34
+ };
35
+
36
+ export interface DevStatsReport {
37
+ readonly db: {
38
+ readonly exists: boolean;
39
+ readonly fileSizeBytes: number;
40
+ readonly path: string;
41
+ };
42
+ readonly lock: {
43
+ readonly exists: boolean;
44
+ readonly fileSizeBytes: number;
45
+ readonly path: string;
46
+ };
47
+ readonly retention: {
48
+ readonly snapshots: number;
49
+ readonly traceAgeMs: number;
50
+ readonly traces: number;
51
+ };
52
+ readonly topo: {
53
+ readonly pinnedCount: number;
54
+ readonly prunableSnapshotCount: number;
55
+ readonly snapshotCount: number;
56
+ };
57
+ readonly tracing: {
58
+ readonly recordCount: number;
59
+ };
60
+ }
61
+
62
+ export interface DevCleanReport {
63
+ readonly dryRun: boolean;
64
+ readonly remaining: {
65
+ readonly pinnedCount: number;
66
+ readonly snapshotCount: number;
67
+ readonly traceCount: number;
68
+ };
69
+ readonly removed: {
70
+ readonly topoSnapshots: number;
71
+ readonly traceRecords: number;
72
+ };
73
+ readonly retention: {
74
+ readonly snapshots: number;
75
+ readonly traceAgeMs: number;
76
+ readonly traces: number;
77
+ };
78
+ }
79
+
80
+ export interface DevResetReport {
81
+ readonly dryRun: boolean;
82
+ readonly removedCount: number;
83
+ readonly removedFiles: readonly string[];
84
+ }
85
+
86
+ interface DevRetentionOptions {
87
+ readonly maxAge?: number;
88
+ readonly maxRecords?: number;
89
+ readonly rootDir?: string;
90
+ readonly snapshotRetention?: number;
91
+ }
92
+
93
+ interface DevCleanupContext {
94
+ readonly dbPath: string;
95
+ readonly dryRun: boolean;
96
+ readonly retention: DevCleanReport['retention'];
97
+ readonly rootDir: string;
98
+ }
99
+
100
+ const buildRetention = (options?: DevRetentionOptions) => ({
101
+ snapshots: options?.snapshotRetention ?? DEFAULT_TOPO_SNAPSHOT_RETENTION,
102
+ traceAgeMs: options?.maxAge ?? DEFAULT_MAX_AGE,
103
+ traces: options?.maxRecords ?? DEFAULT_MAX_RECORDS,
104
+ });
105
+
106
+ const emptyDevClean = (
107
+ retention: DevCleanReport['retention'],
108
+ dryRun: boolean
109
+ ): DevCleanReport => ({
110
+ dryRun,
111
+ remaining: {
112
+ pinnedCount: 0,
113
+ snapshotCount: 0,
114
+ traceCount: 0,
115
+ },
116
+ removed: {
117
+ topoSnapshots: 0,
118
+ traceRecords: 0,
119
+ },
120
+ retention,
121
+ });
122
+
123
+ const buildLockStats = (lockPath: string): DevStatsReport['lock'] => ({
124
+ exists: existsSync(lockPath),
125
+ fileSizeBytes: existsSync(lockPath) ? statSync(lockPath).size : 0,
126
+ path: lockPath,
127
+ });
128
+
129
+ const buildDbStats = (
130
+ dbPath: string,
131
+ exists: boolean
132
+ ): DevStatsReport['db'] => ({
133
+ exists,
134
+ fileSizeBytes: exists ? statSync(dbPath).size : 0,
135
+ path: '.trails/trails.db',
136
+ });
137
+
138
+ const emptyDevStats = (
139
+ dbPath: string,
140
+ lockPath: string,
141
+ retention: DevStatsReport['retention']
142
+ ): DevStatsReport => ({
143
+ db: buildDbStats(dbPath, false),
144
+ lock: buildLockStats(lockPath),
145
+ retention,
146
+ topo: {
147
+ pinnedCount: 0,
148
+ prunableSnapshotCount: 0,
149
+ snapshotCount: 0,
150
+ },
151
+ tracing: {
152
+ recordCount: 0,
153
+ },
154
+ });
155
+
156
+ const liveDevStats = (
157
+ db: Parameters<typeof countPinnedSnapshots>[0],
158
+ dbPath: string,
159
+ lockPath: string,
160
+ retention: DevStatsReport['retention']
161
+ ): DevStatsReport => ({
162
+ db: buildDbStats(dbPath, true),
163
+ lock: buildLockStats(lockPath),
164
+ retention,
165
+ topo: {
166
+ pinnedCount: countPinnedSnapshots(db),
167
+ prunableSnapshotCount: countPrunableSnapshots(db, {
168
+ keep: retention.snapshots,
169
+ }),
170
+ snapshotCount: countTopoSnapshots(db),
171
+ },
172
+ tracing: {
173
+ recordCount: countTraceRecords(db),
174
+ },
175
+ });
176
+
177
+ const deriveDevStatsContext = (options?: DevRetentionOptions) => {
178
+ const rootDir = deriveRootDir(options?.rootDir);
179
+ const dbPath = deriveTrailsDbPath({ rootDir });
180
+ const trailsDir = deriveTrailsDir({ rootDir });
181
+ const lockPath = join(trailsDir, 'trails.lock');
182
+ return {
183
+ dbExists: existsSync(dbPath),
184
+ dbPath,
185
+ lockPath,
186
+ retention: buildRetention(options),
187
+ rootDir,
188
+ };
189
+ };
190
+
191
+ const deriveDevCleanupContext = (
192
+ options?: DevRetentionOptions & { readonly dryRun?: boolean }
193
+ ): DevCleanupContext => {
194
+ const rootDir = deriveRootDir(options?.rootDir);
195
+ return {
196
+ dbPath: deriveTrailsDbPath({ rootDir }),
197
+ dryRun: options?.dryRun ?? false,
198
+ retention: buildRetention(options),
199
+ rootDir,
200
+ };
201
+ };
202
+
203
+ const cleanupTraces = (
204
+ db: Parameters<typeof countPinnedSnapshots>[0],
205
+ context: DevCleanupContext
206
+ ) =>
207
+ context.dryRun
208
+ ? previewTraceCleanup(db, {
209
+ maxAge: context.retention.traceAgeMs,
210
+ maxRecords: context.retention.traces,
211
+ })
212
+ : applyTraceCleanup(db, {
213
+ maxAge: context.retention.traceAgeMs,
214
+ maxRecords: context.retention.traces,
215
+ });
216
+
217
+ const cleanupTopoSnapshots = (
218
+ db: Parameters<typeof countPinnedSnapshots>[0],
219
+ context: DevCleanupContext
220
+ ): number =>
221
+ context.dryRun
222
+ ? countPrunableSnapshots(db, { keep: context.retention.snapshots })
223
+ : pruneUnpinnedSnapshots(db, { keep: context.retention.snapshots });
224
+
225
+ const buildCleanReport = (
226
+ db: Parameters<typeof countPinnedSnapshots>[0],
227
+ context: DevCleanupContext
228
+ ): DevCleanReport => {
229
+ const traceReport = cleanupTraces(db, context);
230
+ const topoRemoved = cleanupTopoSnapshots(db, context);
231
+ const snapshotCount = countTopoSnapshots(db);
232
+
233
+ return {
234
+ dryRun: context.dryRun,
235
+ remaining: {
236
+ pinnedCount: countPinnedSnapshots(db),
237
+ snapshotCount: context.dryRun
238
+ ? snapshotCount - topoRemoved
239
+ : snapshotCount,
240
+ traceCount: context.dryRun
241
+ ? traceReport.remaining - traceReport.removedTotal
242
+ : traceReport.remaining,
243
+ },
244
+ removed: {
245
+ topoSnapshots: topoRemoved,
246
+ traceRecords: traceReport.removedTotal,
247
+ },
248
+ retention: context.retention,
249
+ };
250
+ };
251
+
252
+ const RESET_FILES = [
253
+ '.trails/trails.db',
254
+ '.trails/trails.db-shm',
255
+ '.trails/trails.db-wal',
256
+ '.trails/dev/tracing.db',
257
+ '.trails/dev/tracing.db-shm',
258
+ '.trails/dev/tracing.db-wal',
259
+ ] as const;
260
+
261
+ const presentResetFiles = (
262
+ rootDir: string
263
+ ): readonly (typeof RESET_FILES)[number][] =>
264
+ RESET_FILES.filter((relativePath) => existsSync(join(rootDir, relativePath)));
265
+
266
+ export const buildDevStats = (
267
+ options?: DevRetentionOptions
268
+ ): DevStatsReport => {
269
+ const { dbExists, dbPath, lockPath, retention, rootDir } =
270
+ deriveDevStatsContext(options);
271
+
272
+ if (!dbExists) {
273
+ return emptyDevStats(dbPath, lockPath, retention);
274
+ }
275
+
276
+ const db = openReadTrailsDb({ rootDir });
277
+
278
+ try {
279
+ return liveDevStats(db, dbPath, lockPath, retention);
280
+ } finally {
281
+ db.close();
282
+ }
283
+ };
284
+
285
+ export const cleanDevState = (
286
+ options?: DevRetentionOptions & { readonly dryRun?: boolean }
287
+ ): DevCleanReport => {
288
+ const context = deriveDevCleanupContext(options);
289
+ if (!existsSync(context.dbPath)) {
290
+ return emptyDevClean(context.retention, context.dryRun);
291
+ }
292
+
293
+ const db = context.dryRun
294
+ ? openReadTrailsDb({ rootDir: context.rootDir })
295
+ : openWriteTrailsDb({ rootDir: context.rootDir });
296
+
297
+ try {
298
+ return buildCleanReport(db, context);
299
+ } finally {
300
+ db.close();
301
+ }
302
+ };
303
+
304
+ export const resetDevState = (options?: {
305
+ readonly dryRun?: boolean;
306
+ readonly rootDir?: string;
307
+ }): DevResetReport => {
308
+ const rootDir = deriveRootDir(options?.rootDir);
309
+ const files = presentResetFiles(rootDir);
310
+
311
+ if (options?.dryRun === true) {
312
+ return {
313
+ dryRun: true,
314
+ removedCount: files.length,
315
+ removedFiles: files,
316
+ };
317
+ }
318
+
319
+ const removedFiles = files.filter((relativePath) =>
320
+ removeIfPresent(join(rootDir, relativePath))
321
+ );
322
+
323
+ return {
324
+ dryRun: false,
325
+ removedCount: removedFiles.length,
326
+ removedFiles,
327
+ };
328
+ };