@opensip-tools/contracts 1.0.9 → 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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +6 -6
- package/.turbo/turbo-typecheck.log +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/persistence/store.d.ts +6 -12
- package/dist/persistence/store.d.ts.map +1 -1
- package/dist/persistence/store.js +45 -33
- package/dist/persistence/store.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +0 -1
- package/src/persistence/store.ts +47 -34
package/.turbo/turbo-build.log
CHANGED
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
|
|
2
|
-
> @opensip-tools/contracts@1.0.
|
|
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
|
[1m[7m[36m RUN [39m[27m[22m [36mv2.1.9 [39m[90m/home/runner/work/opensip-tools/opensip-tools/packages/contracts[39m
|
|
7
7
|
|
|
8
|
-
[32m✓[39m src/__tests__/exit-codes.test.ts [2m([22m[2m14 tests[22m[2m)[22m[90m
|
|
9
|
-
[32m✓[39m src/__tests__/dashboard.test.ts [2m([22m[2m8 tests[22m[2m)[22m[90m
|
|
10
|
-
[32m✓[39m src/__tests__/store.test.ts [2m([22m[2m23 tests[22m[2m)[22m[90m
|
|
8
|
+
[32m✓[39m src/__tests__/exit-codes.test.ts [2m([22m[2m14 tests[22m[2m)[22m[90m 58[2mms[22m[39m
|
|
9
|
+
[32m✓[39m src/__tests__/dashboard.test.ts [2m([22m[2m8 tests[22m[2m)[22m[90m 36[2mms[22m[39m
|
|
10
|
+
[32m✓[39m src/__tests__/store.test.ts [2m([22m[2m23 tests[22m[2m)[22m[90m 250[2mms[22m[39m
|
|
11
11
|
|
|
12
12
|
[2m Test Files [22m [1m[32m3 passed[39m[22m[90m (3)[39m
|
|
13
13
|
[2m Tests [22m [1m[32m45 passed[39m[22m[90m (45)[39m
|
|
14
|
-
[2m Start at [22m
|
|
15
|
-
[2m Duration [22m
|
|
14
|
+
[2m Start at [22m 00:26:54
|
|
15
|
+
[2m Duration [22m 4.07s[2m (transform 1.71s, setup 0ms, collect 2.64s, tests 345ms, environment 1ms, prepare 1.90s)[22m
|
|
16
16
|
|
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 {
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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 {
|
|
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,
|
|
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)`.
|
|
10
|
-
*
|
|
11
|
-
* (`~/.opensip-tools/`)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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":"AAAA
|
|
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)`.
|
|
10
|
-
*
|
|
11
|
-
* (`~/.opensip-tools/`)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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 =
|
|
29
|
-
let reportsDir =
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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(
|
|
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
|
-
|
|
83
|
+
const dir = requireStoreDir();
|
|
84
|
+
ensureDir(dir);
|
|
76
85
|
const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
|
|
77
|
-
const files = readdirSync(
|
|
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(
|
|
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
|
-
|
|
101
|
-
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
156
|
+
return requireStoreDir();
|
|
146
157
|
}
|
|
147
158
|
/** Get the reports directory path, creating it if needed */
|
|
148
159
|
export function getReportsDir() {
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/persistence/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;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,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA6D7C,sEAAsE;AACtE,IAAI,QAAQ,GAAkB,IAAI,CAAC;AACnC,IAAI,UAAU,GAAkB,IAAI,CAAC;AACrC,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,SAAS,eAAe;IACtB,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,iBAAiB;IACxB,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,wFAAwF,CAAC,CAAC;IAC5G,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,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,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,SAAS,CAAC,GAAG,CAAC,CAAC;IACf,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,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/C,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,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,SAAS,CAAC,GAAG,CAAC,CAAC;IACf,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AAClE,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,gBAAgB;IAC9B,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,SAAS,CAAC,GAAG,CAAC,CAAC;IACf,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAChE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,SAAS,CAAC,GAAG,CAAC,CAAC;IACf,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACvD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACjC,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,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,SAAS,CAAC,GAAG,CAAC,CAAC;IACf,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC;SAC3B,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,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;YAClD,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,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC;SAC3B,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,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;AACH,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,WAAW;IACzB,OAAO,eAAe,EAAE,CAAC;AAC3B,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,aAAa;IAC3B,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;IAChC,SAAS,CAAC,GAAG,CAAC,CAAC;IACf,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,iBAAiB;IAC/B,OAAO,UAAU,EAAE,CAAC;AACtB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opensip-tools/contracts",
|
|
3
|
-
"version": "1.0.
|
|
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.
|
|
22
|
+
"@opensip-tools/core": "1.0.10"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^22.0.0",
|
package/src/index.ts
CHANGED
package/src/persistence/store.ts
CHANGED
|
@@ -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)`.
|
|
10
|
-
*
|
|
11
|
-
* (`~/.opensip-tools/`)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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 =
|
|
92
|
-
let reportsDir: string =
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
|
|
137
|
-
|
|
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(
|
|
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
|
-
|
|
155
|
+
const dir = requireStoreDir();
|
|
156
|
+
ensureDir(dir);
|
|
147
157
|
const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
|
|
148
|
-
const files = readdirSync(
|
|
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(
|
|
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
|
-
|
|
174
|
-
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
227
|
-
|
|
238
|
+
const dir = requireReportsDir();
|
|
239
|
+
ensureDir(dir);
|
|
240
|
+
return dir;
|
|
228
241
|
}
|
|
229
242
|
|
|
230
243
|
/** Generate a unique session ID */
|