@opensip-tools/contracts 1.0.8 → 1.0.10

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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @opensip-tools/contracts@1.0.8 build /home/runner/work/opensip-tools/opensip-tools/packages/contracts
2
+ > @opensip-tools/contracts@1.0.10 build /home/runner/work/opensip-tools/opensip-tools/packages/contracts
3
3
  > tsc
4
4
 
@@ -1,16 +1,16 @@
1
1
 
2
- > @opensip-tools/contracts@1.0.8 test /home/runner/work/opensip-tools/opensip-tools/packages/contracts
2
+ > @opensip-tools/contracts@1.0.10 test /home/runner/work/opensip-tools/opensip-tools/packages/contracts
3
3
  > vitest run --passWithNoTests
4
4
 
5
5
 
6
6
   RUN  v2.1.9 /home/runner/work/opensip-tools/opensip-tools/packages/contracts
7
7
 
8
- ✓ src/__tests__/exit-codes.test.ts (14 tests) 67ms
9
- ✓ src/__tests__/dashboard.test.ts (8 tests) 27ms
10
- ✓ src/__tests__/store.test.ts (23 tests) 323ms
8
+ ✓ src/__tests__/exit-codes.test.ts (14 tests) 58ms
9
+ ✓ src/__tests__/dashboard.test.ts (8 tests) 36ms
10
+ ✓ src/__tests__/store.test.ts (23 tests) 250ms
11
11
 
12
12
   Test Files  3 passed (3)
13
13
   Tests  45 passed (45)
14
-  Start at  21:42:26
15
-  Duration  3.77s (transform 1.57s, setup 0ms, collect 2.53s, tests 418ms, environment 1ms, prepare 2.52s)
14
+  Start at  00:26:54
15
+  Duration  4.07s (transform 1.71s, setup 0ms, collect 2.64s, tests 345ms, environment 1ms, prepare 1.90s)
16
16
 
@@ -1,4 +1,4 @@
1
1
 
2
- > @opensip-tools/contracts@1.0.8 typecheck /home/runner/work/opensip-tools/opensip-tools/packages/contracts
2
+ > @opensip-tools/contracts@1.0.10 typecheck /home/runner/work/opensip-tools/opensip-tools/packages/contracts
3
3
  > tsc --noEmit
4
4
 
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ export type { CliArgs, FitOptions, InitOptions, ToolOptions, } from './types.js'
15
15
  export type { CliOutput, CheckOutput, FindingOutput, TableRow, SummaryOptions, CommandResult, ClearDoneResult, FitDoneResult, SimDoneResult, ListChecksResult, ListRecipesResult, HistoryResult, DashboardResult, InitResult, ExperimentalResult, PluginResult, HelpResult, ErrorResult, } from './types.js';
16
16
  export { EXIT_CODES, getErrorSuggestion } from './exit-codes.js';
17
17
  export type { ErrorSuggestion } from './exit-codes.js';
18
- export { TOOLS_HOME, configurePersistencePaths, saveSession, loadSessions, loadLatestSession, countSessions, clearAllSessions, clearSessionsOlderThan, getStoreDir, getReportsDir, generateSessionId, sanitizeForFilename, } from './persistence/store.js';
18
+ export { configurePersistencePaths, saveSession, loadSessions, loadLatestSession, countSessions, clearAllSessions, clearSessionsOlderThan, getStoreDir, getReportsDir, generateSessionId, sanitizeForFilename, } from './persistence/store.js';
19
19
  export type { StoredSession, CheckCatalogEntry, RecipeCatalogEntry, } from './persistence/store.js';
20
20
  export { generateDashboardHtml } from './persistence/dashboard/index.js';
21
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,YAAY,EACV,OAAO,EACP,UAAU,EACV,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,SAAS,EACT,WAAW,EACX,aAAa,EACb,QAAQ,EACR,cAAc,EACd,aAAa,EACb,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACjE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGvD,OAAO,EACL,UAAU,EACV,yBAAyB,EACzB,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,YAAY,EACV,OAAO,EACP,UAAU,EACV,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,SAAS,EACT,WAAW,EACX,aAAa,EACb,QAAQ,EACR,cAAc,EACd,aAAa,EACb,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACjE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGvD,OAAO,EACL,yBAAyB,EACzB,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC"}
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@
14
14
  // Exit codes + error suggestion helper
15
15
  export { EXIT_CODES, getErrorSuggestion } from './exit-codes.js';
16
16
  // Session persistence
17
- export { TOOLS_HOME, configurePersistencePaths, saveSession, loadSessions, loadLatestSession, countSessions, clearAllSessions, clearSessionsOlderThan, getStoreDir, getReportsDir, generateSessionId, sanitizeForFilename, } from './persistence/store.js';
17
+ export { configurePersistencePaths, saveSession, loadSessions, loadLatestSession, countSessions, clearAllSessions, clearSessionsOlderThan, getStoreDir, getReportsDir, generateSessionId, sanitizeForFilename, } from './persistence/store.js';
18
18
  // Dashboard HTML generator
19
19
  export { generateDashboardHtml } from './persistence/dashboard/index.js';
20
20
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAgCH,uCAAuC;AACvC,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGjE,sBAAsB;AACtB,OAAO,EACL,UAAU,EACV,yBAAyB,EACzB,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAOhC,2BAA2B;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAgCH,uCAAuC;AACvC,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGjE,sBAAsB;AACtB,OAAO,EACL,yBAAyB,EACzB,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAOhC,2BAA2B;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC"}
@@ -6,11 +6,12 @@
6
6
  * `{timestamp}-{tool}-{recipe}.json`.
7
7
  *
8
8
  * The CLI bootstrap calls `configurePersistencePaths(projectPaths)`
9
- * once on startup with paths from `resolveProjectPaths(cwd)`. Until
10
- * that call, the module falls back to a user-global location
11
- * (`~/.opensip-tools/`) so any caller who imports persistence helpers
12
- * before the CLI's preAction hook still gets a valid path. The
13
- * fallback is also exercised by tests that don't bootstrap a CLI.
9
+ * once on startup with paths from `resolveProjectPaths(cwd)`. Any
10
+ * persistence call made before that throws — there is no implicit
11
+ * fallback. This guarantees that user-global state (`~/.opensip-tools/`)
12
+ * only ever contains config.yml, which makes `opensip-tools uninstall`
13
+ * a precise operation. Tests must call `configurePersistencePaths`
14
+ * with a temp dir in their setup.
14
15
  */
15
16
  import type { ProjectPaths } from '@opensip-tools/core';
16
17
  export interface StoredSession {
@@ -67,13 +68,6 @@ export interface RecipeCatalogEntry {
67
68
  readonly mode: string;
68
69
  readonly timeout: number;
69
70
  }
70
- /**
71
- * Fallback path: user-global `~/.opensip-tools/`, used by tests and
72
- * any code path that imports persistence helpers before the CLI has
73
- * called `configurePersistencePaths`. New code should not rely on
74
- * this fallback — call `configurePersistencePaths` first.
75
- */
76
- export declare const TOOLS_HOME: string;
77
71
  /**
78
72
  * Configure where this module writes sessions and reports. Called
79
73
  * once by the CLI bootstrap with the project paths. Idempotent and
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/persistence/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/persistence/store.ts"],"names":[],"mappings":"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"}
@@ -6,27 +6,20 @@
6
6
  * `{timestamp}-{tool}-{recipe}.json`.
7
7
  *
8
8
  * The CLI bootstrap calls `configurePersistencePaths(projectPaths)`
9
- * once on startup with paths from `resolveProjectPaths(cwd)`. Until
10
- * that call, the module falls back to a user-global location
11
- * (`~/.opensip-tools/`) so any caller who imports persistence helpers
12
- * before the CLI's preAction hook still gets a valid path. The
13
- * fallback is also exercised by tests that don't bootstrap a CLI.
9
+ * once on startup with paths from `resolveProjectPaths(cwd)`. Any
10
+ * persistence call made before that throws — there is no implicit
11
+ * fallback. This guarantees that user-global state (`~/.opensip-tools/`)
12
+ * only ever contains config.yml, which makes `opensip-tools uninstall`
13
+ * a precise operation. Tests must call `configurePersistencePaths`
14
+ * with a temp dir in their setup.
14
15
  */
15
16
  import { randomUUID } from 'node:crypto';
16
17
  import { mkdirSync, readFileSync, readdirSync, writeFileSync, unlinkSync } from 'node:fs';
17
- import { homedir } from 'node:os';
18
18
  import { basename, join } from 'node:path';
19
19
  import { logger } from '@opensip-tools/core';
20
- /**
21
- * Fallback path: user-global `~/.opensip-tools/`, used by tests and
22
- * any code path that imports persistence helpers before the CLI has
23
- * called `configurePersistencePaths`. New code should not rely on
24
- * this fallback — call `configurePersistencePaths` first.
25
- */
26
- export const TOOLS_HOME = join(homedir(), '.opensip-tools');
27
20
  /** Mutable per-process state — set by `configurePersistencePaths`. */
28
- let storeDir = join(TOOLS_HOME, 'sessions');
29
- let reportsDir = join(TOOLS_HOME, 'reports');
21
+ let storeDir = null;
22
+ let reportsDir = null;
30
23
  const MAX_SESSIONS = 100;
31
24
  /**
32
25
  * Configure where this module writes sessions and reports. Called
@@ -37,6 +30,18 @@ export function configurePersistencePaths(paths) {
37
30
  storeDir = paths.sessionsDir;
38
31
  reportsDir = paths.reportsDir;
39
32
  }
33
+ function requireStoreDir() {
34
+ if (storeDir === null) {
35
+ throw new Error('Persistence not configured. Call configurePersistencePaths() before using session APIs.');
36
+ }
37
+ return storeDir;
38
+ }
39
+ function requireReportsDir() {
40
+ if (reportsDir === null) {
41
+ throw new Error('Persistence not configured. Call configurePersistencePaths() before using report APIs.');
42
+ }
43
+ return reportsDir;
44
+ }
40
45
  /** Ensure directory exists — mkdirSync with recursive is idempotent */
41
46
  function ensureDir(dir) {
42
47
  mkdirSync(dir, { recursive: true });
@@ -47,38 +52,42 @@ export function sanitizeForFilename(s) {
47
52
  }
48
53
  /** Save a session result to disk */
49
54
  export function saveSession(session) {
50
- ensureDir(storeDir);
55
+ const dir = requireStoreDir();
56
+ ensureDir(dir);
51
57
  const safeRecipe = session.recipe ? `-${sanitizeForFilename(session.recipe)}` : '';
52
58
  const filename = `${session.timestamp.replaceAll(/[:.]/g, '-')}-${session.tool}${safeRecipe}.json`;
53
59
  // Ensure filename stays within the sessions directory
54
- const filepath = join(storeDir, basename(filename));
60
+ const filepath = join(dir, basename(filename));
55
61
  writeFileSync(filepath, JSON.stringify(session, null, 2), 'utf8');
56
62
  pruneOldSessions();
57
63
  return filepath;
58
64
  }
59
65
  /** Count session files in the store directory */
60
66
  export function countSessions() {
61
- ensureDir(storeDir);
62
- return readdirSync(storeDir).filter(f => f.endsWith('.json')).length;
67
+ const dir = requireStoreDir();
68
+ ensureDir(dir);
69
+ return readdirSync(dir).filter(f => f.endsWith('.json')).length;
63
70
  }
64
71
  /** Delete all sessions. Returns the number of files deleted. */
65
72
  export function clearAllSessions() {
66
- ensureDir(storeDir);
67
- const files = readdirSync(storeDir).filter(f => f.endsWith('.json'));
73
+ const dir = requireStoreDir();
74
+ ensureDir(dir);
75
+ const files = readdirSync(dir).filter(f => f.endsWith('.json'));
68
76
  for (const file of files) {
69
- unlinkSync(join(storeDir, file));
77
+ unlinkSync(join(dir, file));
70
78
  }
71
79
  return files.length;
72
80
  }
73
81
  /** Delete sessions older than the given number of days. Returns the number of files deleted. */
74
82
  export function clearSessionsOlderThan(days) {
75
- ensureDir(storeDir);
83
+ const dir = requireStoreDir();
84
+ ensureDir(dir);
76
85
  const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
77
- const files = readdirSync(storeDir).filter(f => f.endsWith('.json'));
86
+ const files = readdirSync(dir).filter(f => f.endsWith('.json'));
78
87
  let deleted = 0;
79
88
  for (const file of files) {
80
89
  try {
81
- const filepath = join(storeDir, file);
90
+ const filepath = join(dir, file);
82
91
  const raw = readFileSync(filepath, 'utf8');
83
92
  const session = JSON.parse(raw);
84
93
  if (session.timestamp) {
@@ -97,8 +106,9 @@ export function clearSessionsOlderThan(days) {
97
106
  }
98
107
  /** Load all sessions, newest first. Optional limit to avoid reading everything. */
99
108
  export function loadSessions(limit) {
100
- ensureDir(storeDir);
101
- const files = readdirSync(storeDir)
109
+ const dir = requireStoreDir();
110
+ ensureDir(dir);
111
+ const files = readdirSync(dir)
102
112
  .filter(f => f.endsWith('.json'))
103
113
  .sort()
104
114
  // eslint-disable-next-line unicorn/no-array-reverse -- target ES2022; Array#toReversed is ES2023 and not in the lib
@@ -107,7 +117,7 @@ export function loadSessions(limit) {
107
117
  const sessions = [];
108
118
  for (const file of toRead) {
109
119
  try {
110
- const raw = readFileSync(join(storeDir, file), 'utf8');
120
+ const raw = readFileSync(join(dir, file), 'utf8');
111
121
  sessions.push(JSON.parse(raw));
112
122
  }
113
123
  catch {
@@ -124,7 +134,8 @@ export function loadLatestSession() {
124
134
  }
125
135
  /** Prune sessions beyond the max count */
126
136
  function pruneOldSessions() {
127
- const files = readdirSync(storeDir)
137
+ const dir = requireStoreDir();
138
+ const files = readdirSync(dir)
128
139
  .filter(f => f.endsWith('.json'))
129
140
  .sort()
130
141
  // eslint-disable-next-line unicorn/no-array-reverse -- target ES2022; Array#toReversed is ES2023 and not in the lib
@@ -133,7 +144,7 @@ function pruneOldSessions() {
133
144
  return;
134
145
  for (const file of files.slice(MAX_SESSIONS)) {
135
146
  try {
136
- unlinkSync(join(storeDir, file));
147
+ unlinkSync(join(dir, file));
137
148
  }
138
149
  catch {
139
150
  // Best effort
@@ -142,12 +153,13 @@ function pruneOldSessions() {
142
153
  }
143
154
  /** Get the store directory path */
144
155
  export function getStoreDir() {
145
- return storeDir;
156
+ return requireStoreDir();
146
157
  }
147
158
  /** Get the reports directory path, creating it if needed */
148
159
  export function getReportsDir() {
149
- ensureDir(reportsDir);
150
- return reportsDir;
160
+ const dir = requireReportsDir();
161
+ ensureDir(dir);
162
+ return dir;
151
163
  }
152
164
  /** Generate a unique session ID */
153
165
  export function generateSessionId() {
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/persistence/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1F,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA6D7C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,gBAAgB,CAAC,CAAC;AAE5D,sEAAsE;AACtE,IAAI,QAAQ,GAAW,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;AACpD,IAAI,UAAU,GAAW,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AACrD,MAAM,YAAY,GAAG,GAAG,CAAC;AAEzB;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAuD;IAC/F,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAC;IAC7B,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;AAChC,CAAC;AAED,uEAAuE;AACvE,SAAS,SAAS,CAAC,GAAW;IAC5B,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,mBAAmB,CAAC,CAAS;IAC3C,OAAO,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,UAAU,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;AACnE,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,WAAW,CAAC,OAAsB;IAChD,SAAS,CAAC,QAAQ,CAAC,CAAC;IACpB,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,mBAAmB,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnF,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,GAAG,UAAU,OAAO,CAAC;IACnG,sDAAsD;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAElE,gBAAgB,EAAE,CAAC;IACnB,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,aAAa;IAC3B,SAAS,CAAC,QAAQ,CAAC,CAAC;IACpB,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AACvE,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,gBAAgB;IAC9B,SAAS,CAAC,QAAQ,CAAC,CAAC;IACpB,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,SAAS,CAAC,QAAQ,CAAC,CAAC;IACpB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACvD,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACtC,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA2B,CAAC;YAC1D,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBACtB,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC1D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,MAAM,EAAE,CAAC;oBACvD,UAAU,CAAC,QAAQ,CAAC,CAAC;oBACrB,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uCAAuC;QACzC,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACpB,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC;SAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;SAChC,IAAI,EAAE;QACP,oHAAoH;SACnH,OAAO,EAAE,CAAC;IAEb,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACrD,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;YACvD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,2CAA2C;YAC3C,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,uBAAuB,EAAE,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,oCAAoC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAClI,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,iBAAiB;IAC/B,MAAM,QAAQ,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC7B,CAAC;AAED,0CAA0C;AAC1C,SAAS,gBAAgB;IACvB,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC;SAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;SAChC,IAAI,EAAE;QACP,oHAAoH;SACnH,OAAO,EAAE,CAAC;IAEb,IAAI,KAAK,CAAC,MAAM,IAAI,YAAY;QAAE,OAAO;IAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7C,IAAI,CAAC;YACH,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;AACH,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,WAAW;IACzB,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,aAAa;IAC3B,SAAS,CAAC,UAAU,CAAC,CAAC;IACtB,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,iBAAiB;IAC/B,OAAO,UAAU,EAAE,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/persistence/store.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensip-tools/contracts",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "license": "MIT",
5
5
  "description": "Shared contract types for OpenSIP Tools — CliOutput / CommandResult shapes, exit codes, session persistence",
6
6
  "repository": {
@@ -19,7 +19,7 @@
19
19
  ".": "./dist/index.js"
20
20
  },
21
21
  "dependencies": {
22
- "@opensip-tools/core": "1.0.8"
22
+ "@opensip-tools/core": "1.0.10"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^22.0.0",
package/src/index.ts CHANGED
@@ -48,7 +48,6 @@ export type { ErrorSuggestion } from './exit-codes.js';
48
48
 
49
49
  // Session persistence
50
50
  export {
51
- TOOLS_HOME,
52
51
  configurePersistencePaths,
53
52
  saveSession,
54
53
  loadSessions,
@@ -6,16 +6,16 @@
6
6
  * `{timestamp}-{tool}-{recipe}.json`.
7
7
  *
8
8
  * The CLI bootstrap calls `configurePersistencePaths(projectPaths)`
9
- * once on startup with paths from `resolveProjectPaths(cwd)`. Until
10
- * that call, the module falls back to a user-global location
11
- * (`~/.opensip-tools/`) so any caller who imports persistence helpers
12
- * before the CLI's preAction hook still gets a valid path. The
13
- * fallback is also exercised by tests that don't bootstrap a CLI.
9
+ * once on startup with paths from `resolveProjectPaths(cwd)`. Any
10
+ * persistence call made before that throws — there is no implicit
11
+ * fallback. This guarantees that user-global state (`~/.opensip-tools/`)
12
+ * only ever contains config.yml, which makes `opensip-tools uninstall`
13
+ * a precise operation. Tests must call `configurePersistencePaths`
14
+ * with a temp dir in their setup.
14
15
  */
15
16
 
16
17
  import { randomUUID } from 'node:crypto';
17
18
  import { mkdirSync, readFileSync, readdirSync, writeFileSync, unlinkSync } from 'node:fs';
18
- import { homedir } from 'node:os';
19
19
  import { basename, join } from 'node:path';
20
20
 
21
21
  import { logger } from '@opensip-tools/core';
@@ -79,17 +79,9 @@ export interface RecipeCatalogEntry {
79
79
  readonly timeout: number;
80
80
  }
81
81
 
82
- /**
83
- * Fallback path: user-global `~/.opensip-tools/`, used by tests and
84
- * any code path that imports persistence helpers before the CLI has
85
- * called `configurePersistencePaths`. New code should not rely on
86
- * this fallback — call `configurePersistencePaths` first.
87
- */
88
- export const TOOLS_HOME = join(homedir(), '.opensip-tools');
89
-
90
82
  /** Mutable per-process state — set by `configurePersistencePaths`. */
91
- let storeDir: string = join(TOOLS_HOME, 'sessions');
92
- let reportsDir: string = join(TOOLS_HOME, 'reports');
83
+ let storeDir: string | null = null;
84
+ let reportsDir: string | null = null;
93
85
  const MAX_SESSIONS = 100;
94
86
 
95
87
  /**
@@ -102,6 +94,20 @@ export function configurePersistencePaths(paths: Pick<ProjectPaths, 'sessionsDir
102
94
  reportsDir = paths.reportsDir;
103
95
  }
104
96
 
97
+ function requireStoreDir(): string {
98
+ if (storeDir === null) {
99
+ throw new Error('Persistence not configured. Call configurePersistencePaths() before using session APIs.');
100
+ }
101
+ return storeDir;
102
+ }
103
+
104
+ function requireReportsDir(): string {
105
+ if (reportsDir === null) {
106
+ throw new Error('Persistence not configured. Call configurePersistencePaths() before using report APIs.');
107
+ }
108
+ return reportsDir;
109
+ }
110
+
105
111
  /** Ensure directory exists — mkdirSync with recursive is idempotent */
106
112
  function ensureDir(dir: string): void {
107
113
  mkdirSync(dir, { recursive: true });
@@ -114,11 +120,12 @@ export function sanitizeForFilename(s: string): string {
114
120
 
115
121
  /** Save a session result to disk */
116
122
  export function saveSession(session: StoredSession): string {
117
- ensureDir(storeDir);
123
+ const dir = requireStoreDir();
124
+ ensureDir(dir);
118
125
  const safeRecipe = session.recipe ? `-${sanitizeForFilename(session.recipe)}` : '';
119
126
  const filename = `${session.timestamp.replaceAll(/[:.]/g, '-')}-${session.tool}${safeRecipe}.json`;
120
127
  // Ensure filename stays within the sessions directory
121
- const filepath = join(storeDir, basename(filename));
128
+ const filepath = join(dir, basename(filename));
122
129
  writeFileSync(filepath, JSON.stringify(session, null, 2), 'utf8');
123
130
 
124
131
  pruneOldSessions();
@@ -127,30 +134,33 @@ export function saveSession(session: StoredSession): string {
127
134
 
128
135
  /** Count session files in the store directory */
129
136
  export function countSessions(): number {
130
- ensureDir(storeDir);
131
- return readdirSync(storeDir).filter(f => f.endsWith('.json')).length;
137
+ const dir = requireStoreDir();
138
+ ensureDir(dir);
139
+ return readdirSync(dir).filter(f => f.endsWith('.json')).length;
132
140
  }
133
141
 
134
142
  /** Delete all sessions. Returns the number of files deleted. */
135
143
  export function clearAllSessions(): number {
136
- ensureDir(storeDir);
137
- const files = readdirSync(storeDir).filter(f => f.endsWith('.json'));
144
+ const dir = requireStoreDir();
145
+ ensureDir(dir);
146
+ const files = readdirSync(dir).filter(f => f.endsWith('.json'));
138
147
  for (const file of files) {
139
- unlinkSync(join(storeDir, file));
148
+ unlinkSync(join(dir, file));
140
149
  }
141
150
  return files.length;
142
151
  }
143
152
 
144
153
  /** Delete sessions older than the given number of days. Returns the number of files deleted. */
145
154
  export function clearSessionsOlderThan(days: number): number {
146
- ensureDir(storeDir);
155
+ const dir = requireStoreDir();
156
+ ensureDir(dir);
147
157
  const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
148
- const files = readdirSync(storeDir).filter(f => f.endsWith('.json'));
158
+ const files = readdirSync(dir).filter(f => f.endsWith('.json'));
149
159
  let deleted = 0;
150
160
 
151
161
  for (const file of files) {
152
162
  try {
153
- const filepath = join(storeDir, file);
163
+ const filepath = join(dir, file);
154
164
  const raw = readFileSync(filepath, 'utf8');
155
165
  const session = JSON.parse(raw) as { timestamp?: string };
156
166
  if (session.timestamp) {
@@ -170,8 +180,9 @@ export function clearSessionsOlderThan(days: number): number {
170
180
 
171
181
  /** Load all sessions, newest first. Optional limit to avoid reading everything. */
172
182
  export function loadSessions(limit?: number): StoredSession[] {
173
- ensureDir(storeDir);
174
- const files = readdirSync(storeDir)
183
+ const dir = requireStoreDir();
184
+ ensureDir(dir);
185
+ const files = readdirSync(dir)
175
186
  .filter(f => f.endsWith('.json'))
176
187
  .sort()
177
188
  // eslint-disable-next-line unicorn/no-array-reverse -- target ES2022; Array#toReversed is ES2023 and not in the lib
@@ -181,7 +192,7 @@ export function loadSessions(limit?: number): StoredSession[] {
181
192
  const sessions: StoredSession[] = [];
182
193
  for (const file of toRead) {
183
194
  try {
184
- const raw = readFileSync(join(storeDir, file), 'utf8');
195
+ const raw = readFileSync(join(dir, file), 'utf8');
185
196
  sessions.push(JSON.parse(raw) as StoredSession);
186
197
  } catch {
187
198
  // Warn about corrupted files — don't crash
@@ -199,7 +210,8 @@ export function loadLatestSession(): StoredSession | null {
199
210
 
200
211
  /** Prune sessions beyond the max count */
201
212
  function pruneOldSessions(): void {
202
- const files = readdirSync(storeDir)
213
+ const dir = requireStoreDir();
214
+ const files = readdirSync(dir)
203
215
  .filter(f => f.endsWith('.json'))
204
216
  .sort()
205
217
  // eslint-disable-next-line unicorn/no-array-reverse -- target ES2022; Array#toReversed is ES2023 and not in the lib
@@ -209,7 +221,7 @@ function pruneOldSessions(): void {
209
221
 
210
222
  for (const file of files.slice(MAX_SESSIONS)) {
211
223
  try {
212
- unlinkSync(join(storeDir, file));
224
+ unlinkSync(join(dir, file));
213
225
  } catch {
214
226
  // Best effort
215
227
  }
@@ -218,13 +230,14 @@ function pruneOldSessions(): void {
218
230
 
219
231
  /** Get the store directory path */
220
232
  export function getStoreDir(): string {
221
- return storeDir;
233
+ return requireStoreDir();
222
234
  }
223
235
 
224
236
  /** Get the reports directory path, creating it if needed */
225
237
  export function getReportsDir(): string {
226
- ensureDir(reportsDir);
227
- return reportsDir;
238
+ const dir = requireReportsDir();
239
+ ensureDir(dir);
240
+ return dir;
228
241
  }
229
242
 
230
243
  /** Generate a unique session ID */