@mmnto/totem 1.14.8 → 1.14.9
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/dist/ast-grep-query.d.ts +22 -2
- package/dist/ast-grep-query.d.ts.map +1 -1
- package/dist/ast-grep-query.js +34 -5
- package/dist/ast-grep-query.js.map +1 -1
- package/dist/ast-grep-query.test.js +45 -1
- package/dist/ast-grep-query.test.js.map +1 -1
- package/dist/compile-lesson.d.ts +24 -1
- package/dist/compile-lesson.d.ts.map +1 -1
- package/dist/compile-lesson.js +112 -58
- package/dist/compile-lesson.js.map +1 -1
- package/dist/compile-lesson.test.js +283 -14
- package/dist/compile-lesson.test.js.map +1 -1
- package/dist/compile-manifest.d.ts +14 -1
- package/dist/compile-manifest.d.ts.map +1 -1
- package/dist/compile-manifest.js +107 -3
- package/dist/compile-manifest.js.map +1 -1
- package/dist/compile-manifest.test.js +123 -1
- package/dist/compile-manifest.test.js.map +1 -1
- package/dist/compile-smoke-gate.d.ts +42 -0
- package/dist/compile-smoke-gate.d.ts.map +1 -0
- package/dist/compile-smoke-gate.js +135 -0
- package/dist/compile-smoke-gate.js.map +1 -0
- package/dist/compile-smoke-gate.test.d.ts +2 -0
- package/dist/compile-smoke-gate.test.d.ts.map +1 -0
- package/dist/compile-smoke-gate.test.js +202 -0
- package/dist/compile-smoke-gate.test.js.map +1 -0
- package/dist/compiler-schema.d.ts +286 -24
- package/dist/compiler-schema.d.ts.map +1 -1
- package/dist/compiler-schema.js +128 -5
- package/dist/compiler-schema.js.map +1 -1
- package/dist/compiler-schema.test.d.ts +2 -0
- package/dist/compiler-schema.test.d.ts.map +1 -0
- package/dist/compiler-schema.test.js +263 -0
- package/dist/compiler-schema.test.js.map +1 -0
- package/dist/compiler.d.ts +22 -5
- package/dist/compiler.d.ts.map +1 -1
- package/dist/compiler.js +9 -3
- package/dist/compiler.js.map +1 -1
- package/dist/compiler.test.js +17 -2
- package/dist/compiler.test.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/ledger.d.ts +1 -1
- package/dist/lesson-pattern.d.ts +26 -0
- package/dist/lesson-pattern.d.ts.map +1 -1
- package/dist/lesson-pattern.js +35 -1
- package/dist/lesson-pattern.js.map +1 -1
- package/dist/lesson-pattern.test.js +72 -0
- package/dist/lesson-pattern.test.js.map +1 -1
- package/dist/rule-engine.d.ts.map +1 -1
- package/dist/rule-engine.js +25 -7
- package/dist/rule-engine.js.map +1 -1
- package/dist/rule-engine.test.js +98 -4
- package/dist/rule-engine.test.js.map +1 -1
- package/dist/rule-tester.d.ts.map +1 -1
- package/dist/rule-tester.js +9 -2
- package/dist/rule-tester.js.map +1 -1
- package/dist/rule-tester.test.js +41 -0
- package/dist/rule-tester.test.js.map +1 -1
- package/package.json +1 -1
package/dist/compile-manifest.js
CHANGED
|
@@ -56,15 +56,119 @@ export function generateInputHash(lessonsDir) {
|
|
|
56
56
|
}
|
|
57
57
|
return hash.digest('hex');
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Deterministic JSON stringify. Walks the input tree and sorts every
|
|
61
|
+
* object's keys alphabetically before serialising. Arrays keep their
|
|
62
|
+
* element order (arrays are ordered by contract). Primitives and
|
|
63
|
+
* `null` pass through unchanged.
|
|
64
|
+
*
|
|
65
|
+
* Used by `generateOutputHash` so that a compound ast-grep rule
|
|
66
|
+
* (`astGrepYamlRule`) produces the same output hash regardless of the
|
|
67
|
+
* JS engine's property insertion order. Without this, two compile
|
|
68
|
+
* runs could emit functionally-identical rules with different key
|
|
69
|
+
* orders and trip `verify-manifest` on an otherwise stable lesson
|
|
70
|
+
* set. The invariant is: structurally-equivalent inputs produce
|
|
71
|
+
* byte-identical outputs.
|
|
72
|
+
*
|
|
73
|
+
* **Contract:** `value` MUST be plain JSON — the output of
|
|
74
|
+
* `JSON.parse()` or a literal composed of `null`, booleans, numbers,
|
|
75
|
+
* strings, arrays, and plain object literals. Class instances
|
|
76
|
+
* (`Date`, `Map`, custom classes) are not supported and may produce
|
|
77
|
+
* output that diverges from `JSON.stringify`: a `Date` serialises to
|
|
78
|
+
* `{}` rather than its ISO string, `[undefined]` throws rather than
|
|
79
|
+
* becoming `[null]`. Callers in Totem always pass `JSON.parse()`
|
|
80
|
+
* output, so these cases are unreachable in practice; the contract
|
|
81
|
+
* is documented here so future callers do not reach for this
|
|
82
|
+
* function as a drop-in `JSON.stringify` replacement.
|
|
83
|
+
*
|
|
84
|
+
* Design notes:
|
|
85
|
+
* - No cycle detection. Input is expected to be a finite tree
|
|
86
|
+
* (NapiConfig + primitive scalars). Cyclic input would stack
|
|
87
|
+
* overflow; that is a hard error, not a silent degradation.
|
|
88
|
+
* - Undefined values inside records are dropped, matching
|
|
89
|
+
* `JSON.stringify` parity. A bare `undefined` throws — it is
|
|
90
|
+
* not a JSON value.
|
|
91
|
+
*/
|
|
92
|
+
/**
|
|
93
|
+
* Detect whether a parsed compiled-rules file contains at least one
|
|
94
|
+
* rule with an `astGrepYamlRule` field. Walks only the `rules` array
|
|
95
|
+
* on the top-level object; does not recurse into nested rule bodies
|
|
96
|
+
* because compound rules live exactly one level deep in the payload.
|
|
97
|
+
*/
|
|
98
|
+
function hasCompoundRule(parsed) {
|
|
99
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
100
|
+
return false;
|
|
101
|
+
const rules = parsed.rules;
|
|
102
|
+
if (!Array.isArray(rules))
|
|
103
|
+
return false;
|
|
104
|
+
for (const r of rules) {
|
|
105
|
+
if (r && typeof r === 'object' && 'astGrepYamlRule' in r)
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
export function canonicalStringify(value) {
|
|
111
|
+
if (value === undefined) {
|
|
112
|
+
throw new TotemParseError('canonicalStringify: undefined is not a JSON value', 'The manifest hash payload must be JSON-parseable. Undefined entries are filtered out of records before serialisation, so a direct undefined here indicates a caller bug rather than malformed data on disk.');
|
|
113
|
+
}
|
|
114
|
+
if (value === null || typeof value !== 'object') {
|
|
115
|
+
return JSON.stringify(value);
|
|
116
|
+
}
|
|
117
|
+
if (Array.isArray(value)) {
|
|
118
|
+
return '[' + value.map((v) => canonicalStringify(v)).join(',') + ']';
|
|
119
|
+
}
|
|
120
|
+
const record = value;
|
|
121
|
+
const keys = Object.keys(record)
|
|
122
|
+
.filter((k) => record[k] !== undefined)
|
|
123
|
+
.sort();
|
|
124
|
+
const parts = [];
|
|
125
|
+
for (const k of keys) {
|
|
126
|
+
parts.push(JSON.stringify(k) + ':' + canonicalStringify(record[k]));
|
|
127
|
+
}
|
|
128
|
+
return '{' + parts.join(',') + '}';
|
|
129
|
+
}
|
|
59
130
|
/**
|
|
60
131
|
* Generate a deterministic SHA-256 hash of the compiled rules file.
|
|
61
132
|
*
|
|
62
|
-
* Line endings are normalized to `\n` before hashing.
|
|
133
|
+
* Line endings are normalized to `\n` before hashing. For files that
|
|
134
|
+
* contain at least one compound `astGrepYamlRule`, the payload is
|
|
135
|
+
* re-serialised through `canonicalStringify` so key-order variation
|
|
136
|
+
* inside the yaml object cannot shift the hash (mmnto/totem#1407).
|
|
137
|
+
* Files without any compound rule keep the byte-stream path for
|
|
138
|
+
* backward compatibility with manifests written by pre-#1407 CLIs:
|
|
139
|
+
* the old and new computation match byte-for-byte in that case, so
|
|
140
|
+
* every user's existing compile-manifest.json stays valid after
|
|
141
|
+
* upgrading without a forced recompile.
|
|
142
|
+
*
|
|
143
|
+
* A parse failure falls back to the raw content so verify-manifest
|
|
144
|
+
* can still catch tampering on partial writes; it does not mask the
|
|
145
|
+
* error.
|
|
63
146
|
*/
|
|
64
147
|
export function generateOutputHash(rulesPath) {
|
|
65
148
|
try {
|
|
66
|
-
const
|
|
67
|
-
|
|
149
|
+
const raw = fs.readFileSync(rulesPath, 'utf-8').replace(/\r\n/g, '\n');
|
|
150
|
+
let payload = raw;
|
|
151
|
+
// Only switch to canonical serialization when the file actually
|
|
152
|
+
// contains a compound rule. We parse first and check the real
|
|
153
|
+
// field on every rule rather than substring-matching the raw
|
|
154
|
+
// bytes: a lesson message or heading could contain the literal
|
|
155
|
+
// string `"astGrepYamlRule"` and falsely flip the path,
|
|
156
|
+
// producing a different hash than pre-#1407 CLIs computed for
|
|
157
|
+
// the same manifest. Parsing is O(file size), same as hashing.
|
|
158
|
+
if (raw.includes('"astGrepYamlRule"')) {
|
|
159
|
+
try {
|
|
160
|
+
const parsed = JSON.parse(raw);
|
|
161
|
+
if (hasCompoundRule(parsed)) {
|
|
162
|
+
payload = canonicalStringify(parsed);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// Malformed JSON: keep the raw byte stream so verify-manifest
|
|
167
|
+
// still surfaces the corruption via a mismatch rather than
|
|
168
|
+
// crashing here.
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return crypto.createHash('sha256').update(payload).digest('hex');
|
|
68
172
|
}
|
|
69
173
|
catch (err) {
|
|
70
174
|
const code = err.code;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-manifest.js","sourceRoot":"","sources":["../src/compile-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,wDAAwD;AAExD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;CAC3C,CAAC,CAAC;AAIH,wDAAwD;AAExD;;;GAGG;AACH,SAAS,cAAc,CAAC,OAAe,EAAE,aAAqB,OAAO;IACnE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,eAAe,CACvB,gCAAgC,OAAO,EAAE,EACzC,+DAA+D,CAChE,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrD,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAkB;IAClD,MAAM,KAAK,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAEzC,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE;iBACf,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC;iBACrD,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,GAAG,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,eAAe,CACvB,2BAA2B,OAAO,KAAK,eAAe,CAAC,GAAG,CAAC,EAAE,EAC7D,0DAA0D,EAC1D,GAAG,CACJ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"compile-manifest.js","sourceRoot":"","sources":["../src/compile-manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
1
2
|
import * as fs from 'node:fs';
|
|
2
3
|
import * as os from 'node:os';
|
|
3
4
|
import * as path from 'node:path';
|
|
4
5
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { generateInputHash, generateOutputHash, readCompileManifest, writeCompileManifest, } from './compile-manifest.js';
|
|
6
|
+
import { canonicalStringify, generateInputHash, generateOutputHash, readCompileManifest, writeCompileManifest, } from './compile-manifest.js';
|
|
6
7
|
import { TotemParseError } from './errors.js';
|
|
7
8
|
import { cleanTmpDir } from './test-utils.js';
|
|
8
9
|
describe('generateInputHash', () => {
|
|
@@ -51,6 +52,127 @@ describe('generateOutputHash', () => {
|
|
|
51
52
|
fs.writeFileSync(pathCRLF, '{"rules": []}\r\n');
|
|
52
53
|
expect(generateOutputHash(pathLF)).toBe(generateOutputHash(pathCRLF));
|
|
53
54
|
});
|
|
55
|
+
it('manifest hash generates identical hashes for astGrepYamlRule objects with differently ordered keys', () => {
|
|
56
|
+
const pathA = path.join(tmpDir, 'rules-a.json');
|
|
57
|
+
const pathB = path.join(tmpDir, 'rules-b.json');
|
|
58
|
+
const ruleA = {
|
|
59
|
+
lessonHash: 'abc',
|
|
60
|
+
lessonHeading: 'Test',
|
|
61
|
+
pattern: '',
|
|
62
|
+
message: 'm',
|
|
63
|
+
engine: 'ast-grep',
|
|
64
|
+
compiledAt: '2026-04-13T00:00:00Z',
|
|
65
|
+
astGrepYamlRule: {
|
|
66
|
+
rule: {
|
|
67
|
+
all: [{ pattern: 'foo($A)' }, { inside: { kind: 'function_declaration' } }],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
// Same semantic rule, scrambled keys at every level.
|
|
72
|
+
const ruleB = {
|
|
73
|
+
engine: 'ast-grep',
|
|
74
|
+
astGrepYamlRule: {
|
|
75
|
+
rule: {
|
|
76
|
+
all: [{ pattern: 'foo($A)' }, { inside: { kind: 'function_declaration' } }],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
pattern: '',
|
|
80
|
+
compiledAt: '2026-04-13T00:00:00Z',
|
|
81
|
+
lessonHash: 'abc',
|
|
82
|
+
message: 'm',
|
|
83
|
+
lessonHeading: 'Test',
|
|
84
|
+
};
|
|
85
|
+
fs.writeFileSync(pathA, JSON.stringify({ version: 1, rules: [ruleA] }, null, 2) + '\n');
|
|
86
|
+
fs.writeFileSync(pathB, JSON.stringify({ version: 1, rules: [ruleB] }, null, 2) + '\n');
|
|
87
|
+
expect(generateOutputHash(pathA)).toBe(generateOutputHash(pathB));
|
|
88
|
+
});
|
|
89
|
+
it('does not switch to canonical path when the literal string appears only in a lesson message', () => {
|
|
90
|
+
// Regression for the substring false-positive flagged on #1412:
|
|
91
|
+
// a rule whose message body contains the bytes `"astGrepYamlRule"`
|
|
92
|
+
// (e.g., a lesson about when to use the new field) must NOT flip
|
|
93
|
+
// the hash computation path. Pre-#1407 CLIs would hash the raw
|
|
94
|
+
// byte stream; a false canonical path produces different bytes.
|
|
95
|
+
const pathByteStream = path.join(tmpDir, 'rules-bytes.json');
|
|
96
|
+
const pathWithStringInMessage = path.join(tmpDir, 'rules-msg.json');
|
|
97
|
+
const plainRule = {
|
|
98
|
+
lessonHash: 'plain',
|
|
99
|
+
lessonHeading: 'regex rule',
|
|
100
|
+
pattern: 'foo',
|
|
101
|
+
message: 'use foo instead of bar',
|
|
102
|
+
engine: 'regex',
|
|
103
|
+
compiledAt: '2026-04-13T00:00:00Z',
|
|
104
|
+
};
|
|
105
|
+
const trickyRule = {
|
|
106
|
+
lessonHash: 'tricky',
|
|
107
|
+
lessonHeading: 'mention the field',
|
|
108
|
+
pattern: 'foo',
|
|
109
|
+
// Literal bytes `"astGrepYamlRule"` inside a message (wrapping
|
|
110
|
+
// the name in single quotes in prose would still JSON-encode to
|
|
111
|
+
// a version that does NOT contain the double-quoted token —
|
|
112
|
+
// this test uses the token explicitly to force the worst case).
|
|
113
|
+
message: 'prefer astGrepPattern over "astGrepYamlRule" for flat patterns',
|
|
114
|
+
engine: 'regex',
|
|
115
|
+
compiledAt: '2026-04-13T00:00:00Z',
|
|
116
|
+
};
|
|
117
|
+
const plainJson = JSON.stringify({ version: 1, rules: [plainRule] }, null, 2) + '\n';
|
|
118
|
+
const trickyJson = JSON.stringify({ version: 1, rules: [trickyRule] }, null, 2) + '\n';
|
|
119
|
+
fs.writeFileSync(pathByteStream, plainJson);
|
|
120
|
+
fs.writeFileSync(pathWithStringInMessage, trickyJson);
|
|
121
|
+
// Hashes differ (different messages), but the tricky rule must
|
|
122
|
+
// have been hashed via the raw-byte-stream path, not canonical.
|
|
123
|
+
// Proof: the canonical path on tricky would produce a different
|
|
124
|
+
// hash than crypto over the raw bytes. Compare against the raw
|
|
125
|
+
// sha256 of the file contents.
|
|
126
|
+
const expectedTrickyHash = crypto
|
|
127
|
+
.createHash('sha256')
|
|
128
|
+
.update(trickyJson.replace(/\r\n/g, '\n'))
|
|
129
|
+
.digest('hex');
|
|
130
|
+
expect(generateOutputHash(pathWithStringInMessage)).toBe(expectedTrickyHash);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
describe('canonicalStringify', () => {
|
|
134
|
+
it('sorts top-level object keys', () => {
|
|
135
|
+
expect(canonicalStringify({ b: 2, a: 1 })).toBe(canonicalStringify({ a: 1, b: 2 }));
|
|
136
|
+
});
|
|
137
|
+
it('sorts keys at every nesting depth', () => {
|
|
138
|
+
const a = { z: { y: { x: 1, w: 2 } }, a: 0 };
|
|
139
|
+
const b = { a: 0, z: { y: { w: 2, x: 1 } } };
|
|
140
|
+
expect(canonicalStringify(a)).toBe(canonicalStringify(b));
|
|
141
|
+
});
|
|
142
|
+
it('preserves array element order (arrays are ordered by contract)', () => {
|
|
143
|
+
expect(canonicalStringify([2, 1, 3])).not.toBe(canonicalStringify([1, 2, 3]));
|
|
144
|
+
});
|
|
145
|
+
it('handles nested arrays of objects with scrambled keys', () => {
|
|
146
|
+
const a = {
|
|
147
|
+
rule: {
|
|
148
|
+
all: [{ pattern: 'foo', kind: 'call' }, { inside: { stopBy: 'end', kind: 'function' } }],
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
const b = {
|
|
152
|
+
rule: {
|
|
153
|
+
all: [{ kind: 'call', pattern: 'foo' }, { inside: { kind: 'function', stopBy: 'end' } }],
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
expect(canonicalStringify(a)).toBe(canonicalStringify(b));
|
|
157
|
+
});
|
|
158
|
+
it('is stable for primitives', () => {
|
|
159
|
+
expect(canonicalStringify('hello')).toBe('"hello"');
|
|
160
|
+
expect(canonicalStringify(42)).toBe('42');
|
|
161
|
+
expect(canonicalStringify(true)).toBe('true');
|
|
162
|
+
expect(canonicalStringify(null)).toBe('null');
|
|
163
|
+
});
|
|
164
|
+
it('handles undefined by omitting the key (JSON.stringify parity)', () => {
|
|
165
|
+
const a = { a: 1, b: undefined };
|
|
166
|
+
const b = { a: 1 };
|
|
167
|
+
expect(canonicalStringify(a)).toBe(canonicalStringify(b));
|
|
168
|
+
});
|
|
169
|
+
it('throws on a bare undefined input (contract violation)', () => {
|
|
170
|
+
// Undefined in record values is filtered out upstream; a bare
|
|
171
|
+
// undefined here means a caller bug, not malformed data on disk.
|
|
172
|
+
// Fail loud rather than silently produce the string "undefined"
|
|
173
|
+
// that would then hash to something no other input produces.
|
|
174
|
+
expect(() => canonicalStringify(undefined)).toThrow(/undefined is not a JSON value/);
|
|
175
|
+
});
|
|
54
176
|
});
|
|
55
177
|
describe('writeCompileManifest + readCompileManifest', () => {
|
|
56
178
|
let tmpDir;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-manifest.test.js","sourceRoot":"","sources":["../src/compile-manifest.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"compile-manifest.test.js","sourceRoot":"","sources":["../src/compile-manifest.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time smoke gate for compiled rules (ADR-087).
|
|
3
|
+
*
|
|
4
|
+
* Runs a freshly built `CompiledRule` against its own `badExample` snippet
|
|
5
|
+
* using the same engine entry points that the runtime uses (`matchAstGrepPattern`
|
|
6
|
+
* for ast-grep, `new RegExp` for regex). If the rule cannot match its own
|
|
7
|
+
* bad example, something structurally wrong happened between the LLM output
|
|
8
|
+
* and the persisted rule shape — either the pattern does not compile against
|
|
9
|
+
* the snippet, or the snippet does not exercise the pattern the prompt
|
|
10
|
+
* promised. Either way, the rule has no business in compiled-rules.json.
|
|
11
|
+
*
|
|
12
|
+
* The gate is intentionally a thin wrapper: its entire purpose is to
|
|
13
|
+
* guarantee that a rule passing here will also fire at runtime on identical
|
|
14
|
+
* input. Any divergence between "smoke gate happy" and "runtime happy" is a
|
|
15
|
+
* bug in this module, not a rule authoring problem.
|
|
16
|
+
*
|
|
17
|
+
* Not wired to Pipeline 1 (manual) rules in mmnto/totem#1408 - a dry-run
|
|
18
|
+
* sweep lands in a follow-up ticket before the Pipeline 1 gate flips on.
|
|
19
|
+
*/
|
|
20
|
+
import type { CompiledRule } from './compiler-schema.js';
|
|
21
|
+
export interface SmokeGateResult {
|
|
22
|
+
/** True when the rule produced at least one match against the badExample. */
|
|
23
|
+
matched: boolean;
|
|
24
|
+
/** Number of matches the engine reported. Zero when `matched` is false. */
|
|
25
|
+
matchCount: number;
|
|
26
|
+
/**
|
|
27
|
+
* When matched is false and the engine refused to execute (invalid regex,
|
|
28
|
+
* ast-grep runtime throw, missing engine fields), this carries the first
|
|
29
|
+
* line of the error so the caller can build a human-readable rejectReason.
|
|
30
|
+
* Absent when matched is true, and absent when matched is false due to the
|
|
31
|
+
* snippet simply not containing anything the pattern would match.
|
|
32
|
+
*/
|
|
33
|
+
reason?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Run the smoke gate for a compiled rule. Returns a `SmokeGateResult` so the
|
|
37
|
+
* caller can decide whether to accept or reject the rule. The caller is
|
|
38
|
+
* responsible for the "zero matches means reject" decision; this function
|
|
39
|
+
* only reports what the engine says.
|
|
40
|
+
*/
|
|
41
|
+
export declare function runSmokeGate(rule: CompiledRule, badExample: string): SmokeGateResult;
|
|
42
|
+
//# sourceMappingURL=compile-smoke-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-smoke-gate.d.ts","sourceRoot":"","sources":["../src/compile-smoke-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAIzD,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA2FD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe,CA8BpF"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time smoke gate for compiled rules (ADR-087).
|
|
3
|
+
*
|
|
4
|
+
* Runs a freshly built `CompiledRule` against its own `badExample` snippet
|
|
5
|
+
* using the same engine entry points that the runtime uses (`matchAstGrepPattern`
|
|
6
|
+
* for ast-grep, `new RegExp` for regex). If the rule cannot match its own
|
|
7
|
+
* bad example, something structurally wrong happened between the LLM output
|
|
8
|
+
* and the persisted rule shape — either the pattern does not compile against
|
|
9
|
+
* the snippet, or the snippet does not exercise the pattern the prompt
|
|
10
|
+
* promised. Either way, the rule has no business in compiled-rules.json.
|
|
11
|
+
*
|
|
12
|
+
* The gate is intentionally a thin wrapper: its entire purpose is to
|
|
13
|
+
* guarantee that a rule passing here will also fire at runtime on identical
|
|
14
|
+
* input. Any divergence between "smoke gate happy" and "runtime happy" is a
|
|
15
|
+
* bug in this module, not a rule authoring problem.
|
|
16
|
+
*
|
|
17
|
+
* Not wired to Pipeline 1 (manual) rules in mmnto/totem#1408 - a dry-run
|
|
18
|
+
* sweep lands in a follow-up ticket before the Pipeline 1 gate flips on.
|
|
19
|
+
*/
|
|
20
|
+
import { matchAstGrepPattern } from './ast-grep-query.js';
|
|
21
|
+
// ─── Helpers ────────────────────────────────────────
|
|
22
|
+
function firstLine(message) {
|
|
23
|
+
const m = /^[^\n]*/.exec(message);
|
|
24
|
+
return (m?.[0] ?? message).trim();
|
|
25
|
+
}
|
|
26
|
+
function lineNumbersFor(snippet) {
|
|
27
|
+
const lineCount = snippet.split('\n').length;
|
|
28
|
+
const result = [];
|
|
29
|
+
for (let i = 1; i <= lineCount; i++)
|
|
30
|
+
result.push(i);
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
// ─── Engine runners ─────────────────────────────────
|
|
34
|
+
function runRegexGate(pattern, badExample) {
|
|
35
|
+
let re;
|
|
36
|
+
try {
|
|
37
|
+
re = new RegExp(pattern);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
return {
|
|
41
|
+
matched: false,
|
|
42
|
+
matchCount: 0,
|
|
43
|
+
reason: `invalid regex: ${firstLine(err instanceof Error ? err.message : String(err))}`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
let matchCount = 0;
|
|
47
|
+
for (const line of badExample.split('\n')) {
|
|
48
|
+
if (re.test(line))
|
|
49
|
+
matchCount++;
|
|
50
|
+
}
|
|
51
|
+
return matchCount > 0 ? { matched: true, matchCount } : { matched: false, matchCount: 0 };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Collect the ordered list of file extensions the ast-grep engine could use
|
|
55
|
+
* when parsing the badExample. Returns every positive match from
|
|
56
|
+
* `fileGlobs`; if no globs are declared, returns a broad default set so an
|
|
57
|
+
* unscoped rule still has a chance to match the snippet under some parser.
|
|
58
|
+
*
|
|
59
|
+
* Why a list rather than a single extension: `.ts` and `.tsx` select
|
|
60
|
+
* different tree-sitter parsers that reject each other's syntax. A TS
|
|
61
|
+
* angle-bracket cast (`const x = <Foo>bar;`) parses as TS but not as TSX;
|
|
62
|
+
* a JSX element parses as TSX but not as TS. If the rule's fileGlobs
|
|
63
|
+
* allow multiple extensions, runtime will pick whichever matches the real
|
|
64
|
+
* file, so the gate must try every declared extension too. Passing on ANY
|
|
65
|
+
* extension is sufficient — runtime scans the same surface.
|
|
66
|
+
*/
|
|
67
|
+
function inferBadExampleExts(rule) {
|
|
68
|
+
const exts = new Set();
|
|
69
|
+
for (const g of rule.fileGlobs ?? []) {
|
|
70
|
+
if (g.startsWith('!'))
|
|
71
|
+
continue;
|
|
72
|
+
for (const m of g.matchAll(/\.(ts|tsx|js|jsx|mjs|cjs)\b/gi)) {
|
|
73
|
+
exts.add(`.${m[1].toLowerCase()}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Unscoped rule: try the full supported set so the gate does not
|
|
77
|
+
// false-reject on a snippet that would match under some parser. Order
|
|
78
|
+
// puts TypeScript first (handles cast syntax), then TSX, then JS
|
|
79
|
+
// variants. Parity with runtime is preserved because runtime also
|
|
80
|
+
// executes the rule against any file whose extension matches.
|
|
81
|
+
return exts.size > 0 ? [...exts] : ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
|
|
82
|
+
}
|
|
83
|
+
function runAstGrepGate(pattern, badExample, rule) {
|
|
84
|
+
const lineNumbers = lineNumbersFor(badExample);
|
|
85
|
+
let lastReason;
|
|
86
|
+
for (const ext of inferBadExampleExts(rule)) {
|
|
87
|
+
try {
|
|
88
|
+
const matches = matchAstGrepPattern(badExample, ext, pattern, lineNumbers);
|
|
89
|
+
if (matches.length > 0) {
|
|
90
|
+
return { matched: true, matchCount: matches.length };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
lastReason = `ast-grep runtime error: ${firstLine(err instanceof Error ? err.message : String(err))}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return lastReason
|
|
98
|
+
? { matched: false, matchCount: 0, reason: lastReason }
|
|
99
|
+
: { matched: false, matchCount: 0 };
|
|
100
|
+
}
|
|
101
|
+
// ─── Public API ─────────────────────────────────────
|
|
102
|
+
/**
|
|
103
|
+
* Run the smoke gate for a compiled rule. Returns a `SmokeGateResult` so the
|
|
104
|
+
* caller can decide whether to accept or reject the rule. The caller is
|
|
105
|
+
* responsible for the "zero matches means reject" decision; this function
|
|
106
|
+
* only reports what the engine says.
|
|
107
|
+
*/
|
|
108
|
+
export function runSmokeGate(rule, badExample) {
|
|
109
|
+
if (!badExample || badExample.trim().length === 0) {
|
|
110
|
+
return { matched: false, matchCount: 0 };
|
|
111
|
+
}
|
|
112
|
+
if (rule.engine === 'regex') {
|
|
113
|
+
return runRegexGate(rule.pattern, badExample);
|
|
114
|
+
}
|
|
115
|
+
if (rule.engine === 'ast-grep') {
|
|
116
|
+
const source = rule.astGrepPattern ?? rule.astGrepYamlRule;
|
|
117
|
+
if (!source) {
|
|
118
|
+
return {
|
|
119
|
+
matched: false,
|
|
120
|
+
matchCount: 0,
|
|
121
|
+
reason: 'ast-grep rule missing both astGrepPattern and astGrepYamlRule',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return runAstGrepGate(source, badExample, rule);
|
|
125
|
+
}
|
|
126
|
+
// Tree-sitter ast engine: not wired into the gate in mmnto/totem#1408.
|
|
127
|
+
// Callers should not pass 'ast' rules; surface a neutral skip so the
|
|
128
|
+
// caller can fall back to legacy verification.
|
|
129
|
+
return {
|
|
130
|
+
matched: false,
|
|
131
|
+
matchCount: 0,
|
|
132
|
+
reason: `smoke gate does not yet cover engine: ${rule.engine}`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=compile-smoke-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-smoke-gate.js","sourceRoot":"","sources":["../src/compile-smoke-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-smoke-gate.test.d.ts","sourceRoot":"","sources":["../src/compile-smoke-gate.test.ts"],"names":[],"mappings":""}
|