logicstamp-context 0.5.5 → 0.6.0
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/README.md +7 -10
- package/dist/core/pack/loader.d.ts.map +1 -1
- package/dist/core/pack/loader.js +89 -4
- package/dist/core/pack/loader.js.map +1 -1
- package/dist/utils/fileLock.d.ts.map +1 -1
- package/dist/utils/fileLock.js +20 -4
- package/dist/utils/fileLock.js.map +1 -1
- package/dist/utils/schemaValidator.d.ts +24 -0
- package/dist/utils/schemaValidator.d.ts.map +1 -0
- package/dist/utils/schemaValidator.js +137 -0
- package/dist/utils/schemaValidator.js.map +1 -0
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
</picture>
|
|
8
8
|
</a>
|
|
9
9
|
|
|
10
|
-
###
|
|
10
|
+
### Prevent AI from silently breaking your TypeScript architecture.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
Extract deterministic contracts and dependency graphs.<br/>
|
|
13
|
+
Enforce strict architectural diffs in real time.
|
|
14
14
|
|
|
15
15
|
<small><em>Supports: React · Next.js · Vue (TS/TSX) · Express · NestJS</em></small>
|
|
16
16
|
|
|
@@ -21,26 +21,23 @@
|
|
|
21
21
|
<img src="./assets/logicstamp-fox.svg" alt="LogicStamp Fox Mascot" width="100" style="min-width: 80px;">
|
|
22
22
|
</a>
|
|
23
23
|
|
|
24
|
-
[](https://www.npmjs.com/package/logicstamp-context)
|
|
25
25
|

|
|
26
26
|
[](LICENSE)
|
|
27
|
-

|
|
28
28
|
[](https://github.com/LogicStamp/logicstamp-context/actions)
|
|
29
29
|
|
|
30
30
|
</div>
|
|
31
31
|
<br/>
|
|
32
32
|
|
|
33
|
-
**LogicStamp Context** is a static analyzer that extracts deterministic component contracts from TypeScript codebases - giving AI assistants structured architectural context instead of raw source code.
|
|
34
|
-
|
|
35
33
|
<details>
|
|
36
34
|
<summary><strong>📑 Table of Contents</strong></summary>
|
|
37
35
|
|
|
38
36
|
- [The Problem](#the-problem)
|
|
39
37
|
- [Quick Start](#quick-start)
|
|
40
38
|
- [Why Structured Context?](#why-structured-context)
|
|
41
|
-
- [Features](
|
|
39
|
+
- [Features](#-features)
|
|
42
40
|
- [Watch Mode](#watch-mode)
|
|
43
|
-
- [One-time Comparison](#one-time-comparison)
|
|
44
41
|
- [How it Works](#how-it-works)
|
|
45
42
|
- [MCP Server](#mcp-server)
|
|
46
43
|
- [Example Output](#example-output)
|
|
@@ -388,7 +385,7 @@ LogicStamp Context is in beta. Some edge cases are not fully supported.
|
|
|
388
385
|
|
|
389
386
|
## Requirements
|
|
390
387
|
|
|
391
|
-
- Node.js >=
|
|
388
|
+
- Node.js >= 20
|
|
392
389
|
- TypeScript codebase (React, Next.js, Vue (TS/TSX), Express, or NestJS)
|
|
393
390
|
|
|
394
391
|
## Need Help?
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/pack/loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/pack/loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAkBtD;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAsBD,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAGD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAGD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AASD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAM3D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,IAAI,CAQnE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,aAAa,CAQxD;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA8B7E;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAwEpG;AA+CD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4CvG;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsCpG"}
|
package/dist/core/pack/loader.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Loader module - Load contracts, manifests, and source code
|
|
3
3
|
*/
|
|
4
4
|
import { readFile } from 'node:fs/promises';
|
|
5
|
-
import { join, resolve, isAbsolute } from 'node:path';
|
|
5
|
+
import { join, resolve, isAbsolute, relative } from 'node:path';
|
|
6
6
|
import { debugError } from '../../utils/debug.js';
|
|
7
|
+
import { validateUIFContract } from '../../utils/schemaValidator.js';
|
|
7
8
|
import { loadSecurityReport, sanitizeCode } from '../../utils/codeSanitizer.js';
|
|
8
9
|
// Cache expiration time (5 minutes) - balances performance vs memory for long-running processes
|
|
9
10
|
const CACHE_MAX_AGE_MS = 5 * 60 * 1000;
|
|
@@ -111,17 +112,83 @@ export async function loadManifest(basePath) {
|
|
|
111
112
|
* Sidecar path is computed from the manifest key (project-relative): resolved from projectRoot + key + '.uif.json'
|
|
112
113
|
*/
|
|
113
114
|
export async function loadContract(entryId, projectRoot) {
|
|
115
|
+
// Validate path stays within project root (prevents path traversal attacks)
|
|
116
|
+
if (!isPathWithinRoot(entryId, projectRoot)) {
|
|
117
|
+
debugError('loader', 'loadContract', {
|
|
118
|
+
entryId,
|
|
119
|
+
projectRoot,
|
|
120
|
+
message: 'Path traversal attempt detected - path outside project root',
|
|
121
|
+
});
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
114
124
|
// Resolve relative path from project root
|
|
115
125
|
const absolutePath = isAbsolute(entryId) ? entryId : resolve(projectRoot, entryId);
|
|
116
126
|
const sidecarPath = `${absolutePath}.uif.json`;
|
|
127
|
+
// 1. Read file
|
|
128
|
+
let content;
|
|
117
129
|
try {
|
|
118
|
-
|
|
119
|
-
return JSON.parse(content);
|
|
130
|
+
content = await readFile(sidecarPath, 'utf8');
|
|
120
131
|
}
|
|
121
132
|
catch (error) {
|
|
122
|
-
|
|
133
|
+
const err = error;
|
|
134
|
+
// File not found is normal (sidecar doesn't exist yet) - silent return
|
|
135
|
+
if (err.code === 'ENOENT') {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
// Other read errors (permissions, etc.) - log and return
|
|
139
|
+
debugError('loader', 'loadContract', {
|
|
140
|
+
sidecarPath,
|
|
141
|
+
entryId,
|
|
142
|
+
message: 'Failed to read sidecar file',
|
|
143
|
+
errorCode: err.code,
|
|
144
|
+
errorMessage: err.message,
|
|
145
|
+
});
|
|
123
146
|
return null;
|
|
124
147
|
}
|
|
148
|
+
// 2. Parse JSON
|
|
149
|
+
let parsed;
|
|
150
|
+
try {
|
|
151
|
+
parsed = JSON.parse(content);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
const err = error;
|
|
155
|
+
debugError('loader', 'loadContract', {
|
|
156
|
+
sidecarPath,
|
|
157
|
+
entryId,
|
|
158
|
+
message: 'Invalid JSON in sidecar file',
|
|
159
|
+
parseError: err.message,
|
|
160
|
+
});
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
// 3. Validate against UIFContract schema
|
|
164
|
+
const { valid, errors, data } = validateUIFContract(parsed);
|
|
165
|
+
if (!valid) {
|
|
166
|
+
// Cap errors to prevent log spam (AJV can output dozens of lines)
|
|
167
|
+
const MAX_ERRORS = 20;
|
|
168
|
+
const shownErrors = errors.slice(0, MAX_ERRORS);
|
|
169
|
+
const extraCount = errors.length - shownErrors.length;
|
|
170
|
+
debugError('loader', 'loadContract', {
|
|
171
|
+
sidecarPath,
|
|
172
|
+
entryId,
|
|
173
|
+
message: 'Invalid contract schema',
|
|
174
|
+
validationErrors: shownErrors,
|
|
175
|
+
...(extraCount > 0 && { additionalErrors: `+${extraCount} more` }),
|
|
176
|
+
hint: 'This file may have been generated by an older LogicStamp version; rerun `stamp context`',
|
|
177
|
+
});
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
return data;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Check if a file path is within the project root directory
|
|
184
|
+
* Prevents path traversal attacks (e.g., `../../../sensitive.json`)
|
|
185
|
+
*/
|
|
186
|
+
function isPathWithinRoot(filePath, rootPath) {
|
|
187
|
+
const resolvedPath = resolve(rootPath, filePath);
|
|
188
|
+
const relativePath = relative(rootPath, resolvedPath);
|
|
189
|
+
// Path is outside root if relative path starts with '..' or is absolute
|
|
190
|
+
// On Windows, also check for drive letter changes (e.g., C: to D:)
|
|
191
|
+
return !relativePath.startsWith('..') && !isAbsolute(relativePath);
|
|
125
192
|
}
|
|
126
193
|
/**
|
|
127
194
|
* Normalize project root for comparison (handles Windows case-insensitivity and path variations)
|
|
@@ -161,6 +228,15 @@ async function getSecurityReport(projectRoot) {
|
|
|
161
228
|
* Callers should aggregate sanitization info and record it once after batch processing.
|
|
162
229
|
*/
|
|
163
230
|
export async function extractCodeHeader(entryId, projectRoot) {
|
|
231
|
+
// Validate path stays within project root (prevents path traversal attacks)
|
|
232
|
+
if (!isPathWithinRoot(entryId, projectRoot)) {
|
|
233
|
+
debugError('loader', 'extractCodeHeader', {
|
|
234
|
+
entryId,
|
|
235
|
+
projectRoot,
|
|
236
|
+
message: 'Path traversal attempt detected - path outside project root',
|
|
237
|
+
});
|
|
238
|
+
return { header: null };
|
|
239
|
+
}
|
|
164
240
|
try {
|
|
165
241
|
const absolutePath = isAbsolute(entryId) ? entryId : resolve(projectRoot, entryId);
|
|
166
242
|
// Read file content (source file is never modified)
|
|
@@ -201,6 +277,15 @@ export async function extractCodeHeader(entryId, projectRoot) {
|
|
|
201
277
|
* Callers should aggregate sanitization info and record it once after batch processing.
|
|
202
278
|
*/
|
|
203
279
|
export async function readSourceCode(entryId, projectRoot) {
|
|
280
|
+
// Validate path stays within project root (prevents path traversal attacks)
|
|
281
|
+
if (!isPathWithinRoot(entryId, projectRoot)) {
|
|
282
|
+
debugError('loader', 'readSourceCode', {
|
|
283
|
+
entryId,
|
|
284
|
+
projectRoot,
|
|
285
|
+
message: 'Path traversal attempt detected - path outside project root',
|
|
286
|
+
});
|
|
287
|
+
return { code: null };
|
|
288
|
+
}
|
|
204
289
|
try {
|
|
205
290
|
const absolutePath = isAbsolute(entryId) ? entryId : resolve(projectRoot, entryId);
|
|
206
291
|
// Read file content (source file is never modified)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/core/pack/loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/core/pack/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileLock.d.ts","sourceRoot":"","sources":["../../src/utils/fileLock.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oFAAoF;IACpF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,uBAAuB;IACvB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;
|
|
1
|
+
{"version":3,"file":"fileLock.d.ts","sourceRoot":"","sources":["../../src/utils/fileLock.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oFAAoF;IACpF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,uBAAuB;IACvB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AA8FD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA2D7B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,CAAC,CAAC,CAYZ"}
|
package/dist/utils/fileLock.js
CHANGED
|
@@ -35,7 +35,18 @@ function isProcessAlive(pid) {
|
|
|
35
35
|
async function isLockStale(lockPath, staleThreshold) {
|
|
36
36
|
try {
|
|
37
37
|
const content = await readFile(lockPath, 'utf-8');
|
|
38
|
-
|
|
38
|
+
// If file is empty, another process just created it and is writing - not stale
|
|
39
|
+
if (!content.trim()) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
let lockData;
|
|
43
|
+
try {
|
|
44
|
+
lockData = JSON.parse(content);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Can't parse JSON - likely another process is mid-write, not stale
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
39
50
|
// Check if the process that created the lock is still alive
|
|
40
51
|
const alive = isProcessAlive(lockData.pid);
|
|
41
52
|
if (alive === 'unknown') {
|
|
@@ -54,9 +65,14 @@ async function isLockStale(lockPath, staleThreshold) {
|
|
|
54
65
|
}
|
|
55
66
|
return false;
|
|
56
67
|
}
|
|
57
|
-
catch {
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
catch (error) {
|
|
69
|
+
const err = error;
|
|
70
|
+
// File doesn't exist - either never created or already removed
|
|
71
|
+
if (err.code === 'ENOENT') {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
// Other read errors - be conservative, assume not stale
|
|
75
|
+
return false;
|
|
60
76
|
}
|
|
61
77
|
}
|
|
62
78
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileLock.js","sourceRoot":"","sources":["../../src/utils/fileLock.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAqBpC;;;GAGG;AACH,SAAS,cAAc,CAAC,GAAW;IACjC,8DAA8D;IAC9D,+EAA+E;IAC/E,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,GAAG,OAAO,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,gEAAgE;QAChE,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAA8B,CAAC;QAC3C,4CAA4C;QAC5C,kFAAkF;QAClF,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACzB,OAAO,SAAS,CAAC,CAAC,6DAA6D;QACjF,CAAC;QACD,OAAO,KAAK,CAAC,CAAC,yCAAyC;IACzD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,WAAW,CAAC,QAAgB,EAAE,cAAsB;IACjE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"fileLock.js","sourceRoot":"","sources":["../../src/utils/fileLock.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAqBpC;;;GAGG;AACH,SAAS,cAAc,CAAC,GAAW;IACjC,8DAA8D;IAC9D,+EAA+E;IAC/E,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,GAAG,OAAO,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,gEAAgE;QAChE,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAA8B,CAAC;QAC3C,4CAA4C;QAC5C,kFAAkF;QAClF,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACzB,OAAO,SAAS,CAAC,CAAC,6DAA6D;QACjF,CAAC;QACD,OAAO,KAAK,CAAC,CAAC,yCAAyC;IACzD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,WAAW,CAAC,QAAgB,EAAE,cAAsB;IACjE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAElD,+EAA+E;QAC/E,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;YACpB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,QAAyB,CAAC;QAC9B,IAAI,CAAC;YACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;YACpE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,4DAA4D;QAC5D,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,0FAA0F;YAC1F,yDAAyD;YACzD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC;YAC5C,OAAO,GAAG,GAAG,cAAc,GAAG,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC,CAAC,6BAA6B;QAC5C,CAAC;QAED,kFAAkF;QAClF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC;QAC5C,IAAI,GAAG,GAAG,cAAc,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAA8B,CAAC;QAC3C,+DAA+D;QAC/D,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,wDAAwD;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,WAAW,CAAC,QAAgB;IACzC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,sDAAsD;IACxD,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAgB,EAChB,UAAuB,EAAE;IAEzB,MAAM,EACJ,OAAO,GAAG,IAAI,EACd,aAAa,GAAG,EAAE,EAClB,cAAc,GAAG,KAAK,GACvB,GAAG,OAAO,CAAC;IAEZ,MAAM,QAAQ,GAAG,GAAG,QAAQ,OAAO,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,WAAW,GAAoB;QACnC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IAEF,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,OAAO,EAAE,CAAC;QACxC,IAAI,CAAC;YACH,wDAAwD;YACxD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC/F,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;YACpD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YAErB,gBAAgB;YAChB,OAAO;gBACL,OAAO,EAAE,KAAK,IAAI,EAAE;oBAClB,IAAI,QAAQ;wBAAE,OAAO;oBACrB,QAAQ,GAAG,IAAI,CAAC;oBAChB,IAAI,CAAC;wBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;oBACzB,CAAC;oBAAC,MAAM,CAAC;wBACP,+BAA+B;oBACjC,CAAC;gBACH,CAAC;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,GAAG,GAAG,KAA8B,CAAC;YAE3C,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC1B,yCAAyC;gBACzC,IAAI,MAAM,WAAW,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,CAAC;oBAChD,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;oBAC5B,8CAA8C;oBAC9C,SAAS;gBACX,CAAC;gBAED,0DAA0D;gBAC1D,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;gBACjE,SAAS;YACX,CAAC;YAED,iEAAiE;YACjE,+BAA+B;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,kBAAkB;IAClB,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,QAAgB,EAChB,EAAoB,EACpB,UAAuB,EAAE;IAEzB,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAElD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,kBAAkB,CAAC,CAAC;IAC7E,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;YAAS,CAAC;QACT,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema validation utilities using AJV
|
|
3
|
+
* Validates JSON data against LogicStamp schemas at runtime
|
|
4
|
+
*/
|
|
5
|
+
import type { UIFContract } from '../types/UIFContract.js';
|
|
6
|
+
/**
|
|
7
|
+
* Validate data against the UIFContract schema
|
|
8
|
+
* Returns validation result with detailed errors if invalid
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateUIFContract(data: unknown): {
|
|
11
|
+
valid: boolean;
|
|
12
|
+
errors: string[];
|
|
13
|
+
data: UIFContract | null;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Type guard that validates and narrows type
|
|
17
|
+
* Use when you just need a boolean check
|
|
18
|
+
*/
|
|
19
|
+
export declare function isValidUIFContract(data: unknown): data is UIFContract;
|
|
20
|
+
/**
|
|
21
|
+
* Clear the cached validator (useful for testing)
|
|
22
|
+
*/
|
|
23
|
+
export declare function clearValidatorCache(): void;
|
|
24
|
+
//# sourceMappingURL=schemaValidator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaValidator.d.ts","sourceRoot":"","sources":["../../src/utils/schemaValidator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAwD3D;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG;IAClD,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;CAC1B,CA+BA;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,WAAW,CAErE;AAqCD;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAI1C"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema validation utilities using AJV
|
|
3
|
+
* Validates JSON data against LogicStamp schemas at runtime
|
|
4
|
+
*/
|
|
5
|
+
import Ajv from 'ajv';
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
import { join, dirname } from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { debugError } from './debug.js';
|
|
10
|
+
// Resolve schema path relative to this module
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const schemaPath = join(__dirname, '../../schema/logicstamp.context.schema.json');
|
|
13
|
+
// Lazy-load schema and validator to avoid startup cost if validation isn't used
|
|
14
|
+
let ajv = null;
|
|
15
|
+
let contractValidator = null;
|
|
16
|
+
let schemaLoadError = null;
|
|
17
|
+
/**
|
|
18
|
+
* Initialize AJV and compile the UIFContract schema
|
|
19
|
+
* Called lazily on first validation attempt
|
|
20
|
+
*/
|
|
21
|
+
function initializeValidator() {
|
|
22
|
+
if (contractValidator)
|
|
23
|
+
return contractValidator;
|
|
24
|
+
if (schemaLoadError)
|
|
25
|
+
return null;
|
|
26
|
+
try {
|
|
27
|
+
const schemaContent = readFileSync(schemaPath, 'utf8');
|
|
28
|
+
const schema = JSON.parse(schemaContent);
|
|
29
|
+
// Guard against malformed schema file
|
|
30
|
+
if (!schema?.definitions?.UIFContract) {
|
|
31
|
+
throw new Error('UIFContract definition not found in schema');
|
|
32
|
+
}
|
|
33
|
+
ajv = new Ajv({
|
|
34
|
+
allErrors: true, // Collect all errors, not just the first
|
|
35
|
+
strict: false, // Allow schema features that aren't strictly spec-compliant
|
|
36
|
+
verbose: true, // Ensures `data` field is present in errors for typeof reporting
|
|
37
|
+
});
|
|
38
|
+
// Compile the UIFContract definition specifically
|
|
39
|
+
// The schema file has definitions at the root level
|
|
40
|
+
contractValidator = ajv.compile({
|
|
41
|
+
$ref: '#/definitions/UIFContract',
|
|
42
|
+
definitions: schema.definitions,
|
|
43
|
+
});
|
|
44
|
+
return contractValidator;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
// Clean up on failure
|
|
48
|
+
ajv = null;
|
|
49
|
+
contractValidator = null;
|
|
50
|
+
schemaLoadError = error;
|
|
51
|
+
debugError('schemaValidator', 'initializeValidator', {
|
|
52
|
+
error: schemaLoadError.message,
|
|
53
|
+
note: 'Schema validation disabled, contracts will not be validated',
|
|
54
|
+
});
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Validate data against the UIFContract schema
|
|
60
|
+
* Returns validation result with detailed errors if invalid
|
|
61
|
+
*/
|
|
62
|
+
export function validateUIFContract(data) {
|
|
63
|
+
const validator = initializeValidator();
|
|
64
|
+
// If schema failed to load, reject validation
|
|
65
|
+
// This ensures corrupted or invalid contracts are never silently accepted
|
|
66
|
+
if (!validator) {
|
|
67
|
+
return {
|
|
68
|
+
valid: false,
|
|
69
|
+
errors: ['Schema validation unavailable: ' + (schemaLoadError?.message || 'failed to load schema')],
|
|
70
|
+
data: null,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const valid = validator(data);
|
|
74
|
+
if (valid) {
|
|
75
|
+
return {
|
|
76
|
+
valid: true,
|
|
77
|
+
errors: [],
|
|
78
|
+
data: data,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// Format errors for readability
|
|
82
|
+
const errors = formatValidationErrors(validator.errors);
|
|
83
|
+
return {
|
|
84
|
+
valid: false,
|
|
85
|
+
errors,
|
|
86
|
+
data: null,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Type guard that validates and narrows type
|
|
91
|
+
* Use when you just need a boolean check
|
|
92
|
+
*/
|
|
93
|
+
export function isValidUIFContract(data) {
|
|
94
|
+
return validateUIFContract(data).valid;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Format AJV errors into human-readable strings
|
|
98
|
+
*/
|
|
99
|
+
function formatValidationErrors(errors) {
|
|
100
|
+
if (!errors || errors.length === 0)
|
|
101
|
+
return [];
|
|
102
|
+
return errors.map((err) => {
|
|
103
|
+
const path = err.instancePath || '(root)';
|
|
104
|
+
const message = err.message || 'unknown error';
|
|
105
|
+
// Add context for common error types
|
|
106
|
+
switch (err.keyword) {
|
|
107
|
+
case 'required':
|
|
108
|
+
return `${path}: missing required property '${err.params.missingProperty}'`;
|
|
109
|
+
case 'type': {
|
|
110
|
+
// typeof null === 'object' and typeof [] === 'object', so handle explicitly
|
|
111
|
+
const got = err.data === null ? 'null' : Array.isArray(err.data) ? 'array' : typeof err.data;
|
|
112
|
+
return `${path}: ${message} (got ${got})`;
|
|
113
|
+
}
|
|
114
|
+
case 'enum':
|
|
115
|
+
return `${path}: ${message}. Allowed: ${err.params.allowedValues?.join(', ')}`;
|
|
116
|
+
case 'const':
|
|
117
|
+
return `${path}: ${message}. Expected: ${err.params.allowedValue}`;
|
|
118
|
+
case 'additionalProperties':
|
|
119
|
+
return `${path}: unexpected property '${err.params.additionalProperty}'`;
|
|
120
|
+
case 'anyOf':
|
|
121
|
+
case 'oneOf':
|
|
122
|
+
// These are notoriously noisy - simplify the message
|
|
123
|
+
return `${path}: ${message} (schema union mismatch)`;
|
|
124
|
+
default:
|
|
125
|
+
return `${path}: ${message}`;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Clear the cached validator (useful for testing)
|
|
131
|
+
*/
|
|
132
|
+
export function clearValidatorCache() {
|
|
133
|
+
ajv = null;
|
|
134
|
+
contractValidator = null;
|
|
135
|
+
schemaLoadError = null;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=schemaValidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaValidator.js","sourceRoot":"","sources":["../../src/utils/schemaValidator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "logicstamp-context",
|
|
3
|
-
"version": "0.
|
|
4
|
-
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Deterministic architectural context for TypeScript. Extract explicit contracts and dependency graphs to prevent AI-driven architectural drift.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -60,10 +60,11 @@
|
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@toon-format/toon": "^1.0.0",
|
|
63
|
+
"ajv": "^8.18.0",
|
|
63
64
|
"chokidar": "^4.0.3",
|
|
64
65
|
"css-tree": "^3.1.0",
|
|
65
|
-
"glob": "^
|
|
66
|
-
"ts-morph": "^
|
|
66
|
+
"glob": "^13.0.6",
|
|
67
|
+
"ts-morph": "^27.0.2"
|
|
67
68
|
},
|
|
68
69
|
"optionalDependencies": {
|
|
69
70
|
"@anthropic-ai/tokenizer": "^0.0.4",
|
|
@@ -77,6 +78,6 @@
|
|
|
77
78
|
"vitest": "^4.0.16"
|
|
78
79
|
},
|
|
79
80
|
"engines": {
|
|
80
|
-
"node": ">=
|
|
81
|
+
"node": ">=20"
|
|
81
82
|
}
|
|
82
83
|
}
|