@aster-cloud/aster-lang-ts 1.0.27 → 1.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/dist/scripts/generate_error_codes.js +49 -6
  2. package/dist/scripts/generate_error_codes.js.map +1 -1
  3. package/dist/src/ai/providers/openai.d.ts +10 -0
  4. package/dist/src/ai/providers/openai.d.ts.map +1 -1
  5. package/dist/src/ai/providers/openai.js +4 -0
  6. package/dist/src/ai/providers/openai.js.map +1 -1
  7. package/dist/src/config/lexicons/regex-guard.d.ts.map +1 -1
  8. package/dist/src/config/lexicons/regex-guard.js +128 -0
  9. package/dist/src/config/lexicons/regex-guard.js.map +1 -1
  10. package/dist/src/core/interpreter.d.ts.map +1 -1
  11. package/dist/src/core/interpreter.js +221 -27
  12. package/dist/src/core/interpreter.js.map +1 -1
  13. package/dist/src/cst/cst_builder.js +0 -14
  14. package/dist/src/cst/cst_builder.js.map +1 -1
  15. package/dist/src/cst/cst_printer.d.ts.map +1 -1
  16. package/dist/src/cst/cst_printer.js +20 -3
  17. package/dist/src/cst/cst_printer.js.map +1 -1
  18. package/dist/src/diagnostics/error_codes.d.ts +3 -3
  19. package/dist/src/diagnostics/error_codes.d.ts.map +1 -1
  20. package/dist/src/diagnostics/error_codes.js +509 -78
  21. package/dist/src/diagnostics/error_codes.js.map +1 -1
  22. package/dist/src/formatter.d.ts +7 -0
  23. package/dist/src/formatter.d.ts.map +1 -1
  24. package/dist/src/formatter.js +175 -5
  25. package/dist/src/formatter.js.map +1 -1
  26. package/dist/src/frontend/canonicalizer.d.ts.map +1 -1
  27. package/dist/src/frontend/canonicalizer.js +47 -6
  28. package/dist/src/frontend/canonicalizer.js.map +1 -1
  29. package/dist/src/frontend/lexer.d.ts +2 -1
  30. package/dist/src/frontend/lexer.d.ts.map +1 -1
  31. package/dist/src/frontend/lexer.js +10 -3
  32. package/dist/src/frontend/lexer.js.map +1 -1
  33. package/dist/src/frontend/transformers.d.ts.map +1 -1
  34. package/dist/src/frontend/transformers.js +50 -3
  35. package/dist/src/frontend/transformers.js.map +1 -1
  36. package/dist/src/lower_to_core.js +101 -4
  37. package/dist/src/lower_to_core.js.map +1 -1
  38. package/dist/src/lsp/canonicalize-options.d.ts +26 -0
  39. package/dist/src/lsp/canonicalize-options.d.ts.map +1 -0
  40. package/dist/src/lsp/canonicalize-options.js +14 -0
  41. package/dist/src/lsp/canonicalize-options.js.map +1 -0
  42. package/dist/src/lsp/completion.d.ts.map +1 -1
  43. package/dist/src/lsp/completion.js +22 -4
  44. package/dist/src/lsp/completion.js.map +1 -1
  45. package/dist/src/lsp/server.js +29 -1
  46. package/dist/src/lsp/server.js.map +1 -1
  47. package/dist/src/lsp/tenant-init.d.ts +55 -0
  48. package/dist/src/lsp/tenant-init.d.ts.map +1 -0
  49. package/dist/src/lsp/tenant-init.js +88 -0
  50. package/dist/src/lsp/tenant-init.js.map +1 -0
  51. package/dist/src/mapping/candidate-generator.d.ts +84 -0
  52. package/dist/src/mapping/candidate-generator.d.ts.map +1 -0
  53. package/dist/src/mapping/candidate-generator.js +114 -0
  54. package/dist/src/mapping/candidate-generator.js.map +1 -0
  55. package/dist/src/mapping/entity-proposer.d.ts +85 -0
  56. package/dist/src/mapping/entity-proposer.d.ts.map +1 -0
  57. package/dist/src/mapping/entity-proposer.js +162 -0
  58. package/dist/src/mapping/entity-proposer.js.map +1 -0
  59. package/dist/src/mapping/mapping-ir.d.ts +85 -0
  60. package/dist/src/mapping/mapping-ir.d.ts.map +1 -0
  61. package/dist/src/mapping/mapping-ir.js +195 -0
  62. package/dist/src/mapping/mapping-ir.js.map +1 -0
  63. package/dist/src/mapping/proof-ir.d.ts +125 -0
  64. package/dist/src/mapping/proof-ir.d.ts.map +1 -0
  65. package/dist/src/mapping/proof-ir.js +48 -0
  66. package/dist/src/mapping/proof-ir.js.map +1 -0
  67. package/dist/src/mapping/quantity-ir.d.ts +93 -0
  68. package/dist/src/mapping/quantity-ir.d.ts.map +1 -0
  69. package/dist/src/mapping/quantity-ir.js +151 -0
  70. package/dist/src/mapping/quantity-ir.js.map +1 -0
  71. package/dist/src/mapping/source-ir.d.ts +82 -0
  72. package/dist/src/mapping/source-ir.d.ts.map +1 -0
  73. package/dist/src/mapping/source-ir.js +231 -0
  74. package/dist/src/mapping/source-ir.js.map +1 -0
  75. package/dist/src/nodeid/change-impact.d.ts +77 -0
  76. package/dist/src/nodeid/change-impact.d.ts.map +1 -0
  77. package/dist/src/nodeid/change-impact.js +132 -0
  78. package/dist/src/nodeid/change-impact.js.map +1 -0
  79. package/dist/src/nodeid/node-id-map.d.ts +69 -0
  80. package/dist/src/nodeid/node-id-map.d.ts.map +1 -0
  81. package/dist/src/nodeid/node-id-map.js +193 -0
  82. package/dist/src/nodeid/node-id-map.js.map +1 -0
  83. package/dist/src/parser/decl-parser.d.ts.map +1 -1
  84. package/dist/src/parser/decl-parser.js +10 -4
  85. package/dist/src/parser/decl-parser.js.map +1 -1
  86. package/dist/src/parser/expr-stmt-parser.d.ts.map +1 -1
  87. package/dist/src/parser/expr-stmt-parser.js +36 -24
  88. package/dist/src/parser/expr-stmt-parser.js.map +1 -1
  89. package/dist/src/parser/span-utils.d.ts +25 -0
  90. package/dist/src/parser/span-utils.d.ts.map +1 -1
  91. package/dist/src/parser/span-utils.js +43 -0
  92. package/dist/src/parser/span-utils.js.map +1 -1
  93. package/dist/src/parser/type-parser.d.ts.map +1 -1
  94. package/dist/src/parser/type-parser.js +21 -0
  95. package/dist/src/parser/type-parser.js.map +1 -1
  96. package/dist/src/typecheck/expression.d.ts.map +1 -1
  97. package/dist/src/typecheck/expression.js +26 -0
  98. package/dist/src/typecheck/expression.js.map +1 -1
  99. package/dist/test/fuzz/fuzz.test.js +62 -2
  100. package/dist/test/fuzz/fuzz.test.js.map +1 -1
  101. package/dist/test/unit/call-arity.test.d.ts +2 -0
  102. package/dist/test/unit/call-arity.test.d.ts.map +1 -0
  103. package/dist/test/unit/call-arity.test.js +71 -0
  104. package/dist/test/unit/call-arity.test.js.map +1 -0
  105. package/dist/test/unit/canonicalizer/canonicalizer.test.js +42 -4
  106. package/dist/test/unit/canonicalizer/canonicalizer.test.js.map +1 -1
  107. package/dist/test/unit/canonicalizer/line-preservation.test.d.ts +2 -0
  108. package/dist/test/unit/canonicalizer/line-preservation.test.d.ts.map +1 -0
  109. package/dist/test/unit/canonicalizer/line-preservation.test.js +71 -0
  110. package/dist/test/unit/canonicalizer/line-preservation.test.js.map +1 -0
  111. package/dist/test/unit/canonicalizer/marker-restore-linear.test.d.ts +2 -0
  112. package/dist/test/unit/canonicalizer/marker-restore-linear.test.d.ts.map +1 -0
  113. package/dist/test/unit/canonicalizer/marker-restore-linear.test.js +98 -0
  114. package/dist/test/unit/canonicalizer/marker-restore-linear.test.js.map +1 -0
  115. package/dist/test/unit/canonicalizer/possessive-anchor.test.d.ts +2 -0
  116. package/dist/test/unit/canonicalizer/possessive-anchor.test.d.ts.map +1 -0
  117. package/dist/test/unit/canonicalizer/possessive-anchor.test.js +101 -0
  118. package/dist/test/unit/canonicalizer/possessive-anchor.test.js.map +1 -0
  119. package/dist/test/unit/formatter-redos.test.d.ts +2 -0
  120. package/dist/test/unit/formatter-redos.test.d.ts.map +1 -0
  121. package/dist/test/unit/formatter-redos.test.js +196 -0
  122. package/dist/test/unit/formatter-redos.test.js.map +1 -0
  123. package/dist/test/unit/interpreter/interpreter.test.js +7 -2
  124. package/dist/test/unit/interpreter/interpreter.test.js.map +1 -1
  125. package/dist/test/unit/lexer/bom.test.d.ts +2 -0
  126. package/dist/test/unit/lexer/bom.test.d.ts.map +1 -0
  127. package/dist/test/unit/lexer/bom.test.js +49 -0
  128. package/dist/test/unit/lexer/bom.test.js.map +1 -0
  129. package/dist/test/unit/list-get-bounds.test.d.ts +2 -0
  130. package/dist/test/unit/list-get-bounds.test.d.ts.map +1 -0
  131. package/dist/test/unit/list-get-bounds.test.js +66 -0
  132. package/dist/test/unit/list-get-bounds.test.js.map +1 -0
  133. package/dist/test/unit/long-bigint-exact.test.d.ts +2 -0
  134. package/dist/test/unit/long-bigint-exact.test.d.ts.map +1 -0
  135. package/dist/test/unit/long-bigint-exact.test.js +79 -0
  136. package/dist/test/unit/long-bigint-exact.test.js.map +1 -0
  137. package/dist/test/unit/lsp/completion-lexicon.test.d.ts +2 -0
  138. package/dist/test/unit/lsp/completion-lexicon.test.d.ts.map +1 -0
  139. package/dist/test/unit/lsp/completion-lexicon.test.js +83 -0
  140. package/dist/test/unit/lsp/completion-lexicon.test.js.map +1 -0
  141. package/dist/test/unit/lsp/tenant-vocabulary.test.d.ts +2 -0
  142. package/dist/test/unit/lsp/tenant-vocabulary.test.d.ts.map +1 -0
  143. package/dist/test/unit/lsp/tenant-vocabulary.test.js +187 -0
  144. package/dist/test/unit/lsp/tenant-vocabulary.test.js.map +1 -0
  145. package/dist/test/unit/map-builtins.test.js +163 -3
  146. package/dist/test/unit/map-builtins.test.js.map +1 -1
  147. package/dist/test/unit/map-structural-equality.test.d.ts +2 -0
  148. package/dist/test/unit/map-structural-equality.test.d.ts.map +1 -0
  149. package/dist/test/unit/map-structural-equality.test.js +64 -0
  150. package/dist/test/unit/map-structural-equality.test.js.map +1 -0
  151. package/dist/test/unit/map-type-guard.test.js +8 -5
  152. package/dist/test/unit/map-type-guard.test.js.map +1 -1
  153. package/dist/test/unit/mapping/candidate-generator.test.d.ts +2 -0
  154. package/dist/test/unit/mapping/candidate-generator.test.d.ts.map +1 -0
  155. package/dist/test/unit/mapping/candidate-generator.test.js +177 -0
  156. package/dist/test/unit/mapping/candidate-generator.test.js.map +1 -0
  157. package/dist/test/unit/mapping/entity-proposer.test.d.ts +2 -0
  158. package/dist/test/unit/mapping/entity-proposer.test.d.ts.map +1 -0
  159. package/dist/test/unit/mapping/entity-proposer.test.js +168 -0
  160. package/dist/test/unit/mapping/entity-proposer.test.js.map +1 -0
  161. package/dist/test/unit/mapping/mapping-ir.test.d.ts +2 -0
  162. package/dist/test/unit/mapping/mapping-ir.test.d.ts.map +1 -0
  163. package/dist/test/unit/mapping/mapping-ir.test.js +144 -0
  164. package/dist/test/unit/mapping/mapping-ir.test.js.map +1 -0
  165. package/dist/test/unit/mapping/proof-ir.test.d.ts +2 -0
  166. package/dist/test/unit/mapping/proof-ir.test.d.ts.map +1 -0
  167. package/dist/test/unit/mapping/proof-ir.test.js +119 -0
  168. package/dist/test/unit/mapping/proof-ir.test.js.map +1 -0
  169. package/dist/test/unit/mapping/quantity-ir.test.d.ts +2 -0
  170. package/dist/test/unit/mapping/quantity-ir.test.d.ts.map +1 -0
  171. package/dist/test/unit/mapping/quantity-ir.test.js +144 -0
  172. package/dist/test/unit/mapping/quantity-ir.test.js.map +1 -0
  173. package/dist/test/unit/mapping/quantity-redos-property.test.d.ts +2 -0
  174. package/dist/test/unit/mapping/quantity-redos-property.test.d.ts.map +1 -0
  175. package/dist/test/unit/mapping/quantity-redos-property.test.js +182 -0
  176. package/dist/test/unit/mapping/quantity-redos-property.test.js.map +1 -0
  177. package/dist/test/unit/mapping/redos-budget.test.d.ts +2 -0
  178. package/dist/test/unit/mapping/redos-budget.test.d.ts.map +1 -0
  179. package/dist/test/unit/mapping/redos-budget.test.js +168 -0
  180. package/dist/test/unit/mapping/redos-budget.test.js.map +1 -0
  181. package/dist/test/unit/mapping/source-ir.test.d.ts +2 -0
  182. package/dist/test/unit/mapping/source-ir.test.d.ts.map +1 -0
  183. package/dist/test/unit/mapping/source-ir.test.js +113 -0
  184. package/dist/test/unit/mapping/source-ir.test.js.map +1 -0
  185. package/dist/test/unit/nodeid/change-impact.test.d.ts +2 -0
  186. package/dist/test/unit/nodeid/change-impact.test.d.ts.map +1 -0
  187. package/dist/test/unit/nodeid/change-impact.test.js +152 -0
  188. package/dist/test/unit/nodeid/change-impact.test.js.map +1 -0
  189. package/dist/test/unit/nodeid/rename-declaration.test.d.ts +2 -0
  190. package/dist/test/unit/nodeid/rename-declaration.test.d.ts.map +1 -0
  191. package/dist/test/unit/nodeid/rename-declaration.test.js +75 -0
  192. package/dist/test/unit/nodeid/rename-declaration.test.js.map +1 -0
  193. package/dist/test/unit/parse-type-recursion.test.d.ts +2 -0
  194. package/dist/test/unit/parse-type-recursion.test.d.ts.map +1 -0
  195. package/dist/test/unit/parse-type-recursion.test.js +58 -0
  196. package/dist/test/unit/parse-type-recursion.test.js.map +1 -0
  197. package/dist/test/unit/regex-guard.test.js +64 -0
  198. package/dist/test/unit/regex-guard.test.js.map +1 -1
  199. package/dist/test/unit/strict-int-coercion.test.d.ts +2 -0
  200. package/dist/test/unit/strict-int-coercion.test.d.ts.map +1 -0
  201. package/dist/test/unit/strict-int-coercion.test.js +96 -0
  202. package/dist/test/unit/strict-int-coercion.test.js.map +1 -0
  203. package/dist/test/unit/ufcs-receiver.test.d.ts +2 -0
  204. package/dist/test/unit/ufcs-receiver.test.d.ts.map +1 -0
  205. package/dist/test/unit/ufcs-receiver.test.js +75 -0
  206. package/dist/test/unit/ufcs-receiver.test.js.map +1 -0
  207. package/package.json +1 -1
@@ -0,0 +1,113 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { parseSourceIr, verifyCoverage, nodeAtOffset } from '../../../src/mapping/source-ir.js';
4
+ import { verifyMapping } from '../../../src/mapping/mapping-ir.js';
5
+ /**
6
+ * SourceIR(ADR 0037 §2/§9.5)—— 人类文档的结构化表示。
7
+ *
8
+ * <p>核心契约:**只标结构,不改内容**。每个节点的 span 必须能逐字节切回原文,
9
+ * 且父必须覆盖子——这两条是「点击原文 ↔ 定位 IR 节点」的前提。
10
+ */
11
+ const POLICY = [
12
+ '# 付款审批政策', '',
13
+ '## 阈值', '',
14
+ '单笔付款超过 $10,000 时需要财务经理审批。', '',
15
+ '- 低于阈值:自动通过', '',
16
+ '- 高于阈值:转人工', '',
17
+ '## 例外', '',
18
+ '紧急付款可事后补批。', '',
19
+ ].join('\n');
20
+ function allNodes(root) {
21
+ const out = [];
22
+ (function walk(n) { out.push(n); n.children.forEach(walk); })(root);
23
+ return out;
24
+ }
25
+ describe('SourceIR — 人类文档的结构化表示', () => {
26
+ it('★每个节点的 span 必须能逐字节切回原文(不做任何改写)', () => {
27
+ const ir = parseSourceIr(POLICY);
28
+ assert.deepStrictEqual(verifyCoverage(ir, POLICY), [], '覆盖不变式被破坏 —— span 切片与 text 不符或存在重叠。');
29
+ for (const n of allNodes(ir)) {
30
+ assert.strictEqual(n.text, POLICY.slice(n.span.start, n.span.end), `${n.nodeId} 的 text 不等于 span 切片 —— 说明做了文本改写。`);
31
+ }
32
+ });
33
+ it('★标题层级必须嵌套(`##` 归到 `#` 之下)', () => {
34
+ const ir = parseSourceIr(POLICY);
35
+ assert.strictEqual(ir.children.length, 1, `顶层应只有一个 \`# \` 标题节点,实际 ${ir.children.length} 个 —— 嵌套没建起来。`);
36
+ const h1 = ir.children[0];
37
+ assert.strictEqual(h1.level, 1);
38
+ const h2s = h1.children.filter(c => c.kind === 'HEADING');
39
+ assert.strictEqual(h2s.length, 2, '`## 阈值` 与 `## 例外` 都应挂在 `#` 之下。');
40
+ assert.ok(h2s.every(h => h.level === 2));
41
+ });
42
+ it('★父的 span 必须覆盖子(否则 nodeAtOffset 无法下降)', () => {
43
+ // 这是我实际踩过的 bug:HEADING 原本只覆盖标题那一行,其子段落在父之外,
44
+ // 于是「点击 $10,000 定位所在节点」返回 DOCUMENT 而不是那个段落。
45
+ const ir = parseSourceIr(POLICY);
46
+ for (const n of allNodes(ir)) {
47
+ for (const c of n.children) {
48
+ assert.ok(c.span.start >= n.span.start && c.span.end <= n.span.end, `${c.nodeId} 不被父 ${n.nodeId} 包含:子 [${c.span.start},${c.span.end}) `
49
+ + `父 [${n.span.start},${n.span.end})`);
50
+ }
51
+ }
52
+ });
53
+ it('★nodeAtOffset 返回**最深**的命中节点', () => {
54
+ const ir = parseSourceIr(POLICY);
55
+ const offset = POLICY.indexOf('$10,000');
56
+ const hit = nodeAtOffset(ir, offset);
57
+ assert.strictEqual(hit?.kind, 'PARAGRAPH', `应命中段落而非祖先,实际 ${hit?.kind}(${hit?.nodeId})`
58
+ + '\n★若返回 DOCUMENT/HEADING,说明包含不变式被破坏、无法逐层下降。');
59
+ assert.ok(hit.text.includes('$10,000'));
60
+ });
61
+ it('★verifyCoverage 必须能抓出错位的 span(否则它是个假门禁)', () => {
62
+ // 反向守卫:手工构造一个 text 与 span 不符的节点,必须被抓。
63
+ const bad = {
64
+ nodeId: 'doc', kind: 'DOCUMENT', span: { start: 0, end: 5 },
65
+ text: '完全不同的内容', children: [],
66
+ };
67
+ const problems = verifyCoverage(bad, POLICY);
68
+ assert.ok(problems.length > 0, 'text 与 span 不符却没被抓到 —— 这道检查形同虚设。');
69
+ });
70
+ it('★围栏代码块内的空行不得把代码块撕开', () => {
71
+ const doc = [
72
+ '# 标题', '',
73
+ '```', 'line1', '', 'line3', '```', '',
74
+ '正文。', '',
75
+ ].join('\n');
76
+ const ir = parseSourceIr(doc);
77
+ assert.deepStrictEqual(verifyCoverage(ir, doc), []);
78
+ const codeBlocks = allNodes(ir).filter(n => n.kind === 'CODE_BLOCK');
79
+ assert.strictEqual(codeBlocks.length, 1, `代码块应是一个整体节点,实际被切成 ${codeBlocks.length} 块。`);
80
+ assert.ok(codeBlocks[0].text.includes('line1') && codeBlocks[0].text.includes('line3'), '代码块内容不完整 —— 空行处被切开了。');
81
+ });
82
+ it('★与 MappingIR 打通:SourceIR 的 offset 可直接做候选映射的 span', () => {
83
+ // 这是 SourceIR 存在的理由:让 MappingIR 能吃**人类文档**而不只是 .aster。
84
+ const ir = parseSourceIr(POLICY);
85
+ const start = POLICY.indexOf('$10,000');
86
+ const text = POLICY.slice(start, start + '$10,000'.length);
87
+ // 该 offset 必须落在某个具体结构节点里(不是孤立的字符)
88
+ assert.ok(nodeAtOffset(ir, start) !== undefined, 'offset 应落在某个 SourceIR 节点内。');
89
+ const r = verifyMapping({ span: { start, end: start + text.length }, text, nodeId: '$.threshold' }, () => ({ kind: 'Int', value: 10000 }));
90
+ assert.strictEqual(r.verdict, 'VERIFIED', `人类文档里的 $10,000 应能验过 Int(10000)。实际 ${r.verdict}:${r.reason}`);
91
+ });
92
+ it('★文档首尾的空白必须原样保留(text 不得被 trim)', () => {
93
+ // 反向守卫。★原先的样本 POLICY 首尾没有可 trim 的空白,于是给 freeze 加
94
+ // `.trim()` 这个变异**测不出来**——契约说「不改内容」,但没有样本能
95
+ // 证伪它。这里显式构造首尾带空白的文档。
96
+ const doc = '\n\n# 标题\n\n正文。\n\n \n';
97
+ const ir = parseSourceIr(doc);
98
+ assert.deepStrictEqual(verifyCoverage(ir, doc), [], '首尾空白应原样保留 —— 若这里变红,多半是某处对 text 做了 trim/规范化。');
99
+ assert.strictEqual(ir.text, doc, 'DOCUMENT 的 text 必须逐字节等于原文(含首尾空白)。');
100
+ });
101
+ it('空文档与纯空白文档不崩', () => {
102
+ for (const doc of ['', ' \n\n \n']) {
103
+ const ir = parseSourceIr(doc);
104
+ assert.deepStrictEqual(verifyCoverage(ir, doc), [], `文档 ${JSON.stringify(doc)} 覆盖检查失败`);
105
+ assert.strictEqual(ir.kind, 'DOCUMENT');
106
+ }
107
+ });
108
+ it('nodeId 在文档内唯一(否则映射会指向多个节点)', () => {
109
+ const ids = allNodes(parseSourceIr(POLICY)).map(n => n.nodeId);
110
+ assert.strictEqual(new Set(ids).size, ids.length, `nodeId 有重复:${ids.filter((v, i) => ids.indexOf(v) !== i).join(', ')}`);
111
+ });
112
+ });
113
+ //# sourceMappingURL=source-ir.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-ir.test.js","sourceRoot":"","sources":["../../../../test/unit/mapping/source-ir.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=change-impact.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-impact.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/nodeid/change-impact.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,152 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { canonicalize } from '../../../src/frontend/canonicalizer.js';
4
+ import { lex } from '../../../src/frontend/lexer.js';
5
+ import { parse } from '../../../src/parser.js';
6
+ import { lowerModule } from '../../../src/lower_to_core.js';
7
+ import { computeNodeIds, canonicalHash } from '../../../src/nodeid/node-id-map.js';
8
+ import { diffNodeIds } from '../../../src/nodeid/change-impact.js';
9
+ /**
10
+ * ADR 0037 §4 的核心能力:跨版本 change impact(TS 侧,与 Java 对等)。
11
+ *
12
+ * <p>每条用例都锚在一个**具体的产品承诺**上(「改阈值 → 立刻知道哪条规则
13
+ * stale」),而不是锚在实现细节(路径字符串长什么样)上——后者会在重构时
14
+ * 变红却证明不了任何事。
15
+ */
16
+ const V1 = [
17
+ 'Module demo.approve.',
18
+ '',
19
+ 'Rule approve given amount, produce:',
20
+ ' If amount greater than 10000:',
21
+ ' Return "REFER".',
22
+ ' Otherwise:',
23
+ ' Return "APPROVE".',
24
+ '',
25
+ ].join('\n');
26
+ /** 完整链路:源码 → canonicalize → lex → parse → lower → Core IR。 */
27
+ function ir(src) {
28
+ const ast = parse(lex(canonicalize(src))).ast;
29
+ return JSON.parse(JSON.stringify(lowerModule(ast)));
30
+ }
31
+ function idsOf(src) {
32
+ return computeNodeIds(ir(src));
33
+ }
34
+ describe('change impact(TS 侧)', () => {
35
+ it('★改阈值:节点身份不变、内容指纹变 —— 这正是 change impact 的前提', () => {
36
+ const v2 = V1.replace('10000', '20000');
37
+ assert.notStrictEqual(v2, V1, '★replace 未生效则本用例测的是「未编辑」,结论无效。');
38
+ const changes = diffNodeIds(idsOf(V1), idsOf(v2));
39
+ // 契约 1:不能出现「旧节点消失 + 新节点出现」——那说明身份没稳住。
40
+ assert.ok(!changes.some(c => c.kind === 'ADDED'), `改一个字面量不应产生 ADDED 节点,实际:${JSON.stringify(changes)}`);
41
+ assert.ok(!changes.some(c => c.kind === 'REMOVED'), `改一个字面量不应产生 REMOVED 节点,实际:${JSON.stringify(changes)}`);
42
+ // 契约 2:必须有 MODIFIED,否则 change impact 根本没察觉到这次修改。
43
+ assert.ok(changes.some(c => c.kind === 'MODIFIED'), '改了阈值却没报告任何 MODIFIED —— change impact 失效。');
44
+ // 契约 3:包含该阈值的那条规则必须被标为 stale(§4 的「谁受影响」)。
45
+ assert.ok(changes.flatMap(c => c.staleAncestors).some(a => a.includes('{approve}')), `改了 approve 规则内的阈值,该规则应出现在 staleAncestors 里。实际:${JSON.stringify(changes)}`);
46
+ });
47
+ it('★在前面插入一条无关规则:原有规则的身份必须纹丝不动', () => {
48
+ // 这是纯下标路径方案的灾难场景(实测 5/16 存活)。命名作用域路径应完全免疫。
49
+ const v2 = V1.replace('Rule approve', 'Rule noop, produce:\n Return "X".\n\nRule approve');
50
+ assert.notStrictEqual(v2, V1, '★replace 未生效则本用例无效。');
51
+ const before = idsOf(V1);
52
+ const after = idsOf(v2);
53
+ for (const [id, identity] of before) {
54
+ const now = after.get(id);
55
+ assert.ok(now !== undefined, `插入无关规则后,原节点 ${id} 的身份丢失了`
56
+ + '\n★这会让 change impact 把「没动过的规则」误报成被删除。');
57
+ // ★根节点 `$` 除外:模块的 decls 确实多了一条规则,它的内容**本就应该**变。
58
+ // 契约是「祖先 stale、后代不受扰」,不是「插入后整棵树一个字节都不许动」。
59
+ if (id !== '$') {
60
+ assert.strictEqual(now.contentHash, identity.contentHash, `原节点 ${id} 内容未变,contentHash 却变了`
61
+ + '\n★多半是 contentHash 混进了位置信息(origin 未剥干净)。');
62
+ }
63
+ }
64
+ // 反向守卫:根节点**必须**变——它是「本模块多了一条规则」的唯一体现。
65
+ assert.notStrictEqual(after.get('$').contentHash, before.get('$').contentHash, '模块新增了一条规则,根节点 contentHash 却没变 —— 内容指纹失效。');
66
+ assert.ok(diffNodeIds(before, after).some(c => c.kind === 'ADDED'), '插入了一条新规则却没有任何 ADDED。');
67
+ });
68
+ it('未修改的程序:不得报告任何变更(防止 change impact 恒报 stale)', () => {
69
+ // 若本用例变红,说明 nodeId 或 contentHash 里混进了非确定性内容
70
+ // (随机序、时间戳、位置…)——那会让每次编译都「全量 stale」,能力等于没有。
71
+ assert.deepStrictEqual(diffNodeIds(idsOf(V1), idsOf(V1)), [], '同一份源码两次编译不应产生任何变更。');
72
+ });
73
+ it('★contentHash 必须剥掉 origin:位置不是内容', () => {
74
+ // 在**文件开头**加一行注释:所有代码整体下移一行,位置全变、内容全同。
75
+ const v2 = `# 一行注释\n${V1}`;
76
+ assert.deepStrictEqual(diffNodeIds(idsOf(V1), idsOf(v2)), [], '只加了一行注释(位置变、内容不变),却报告了变更。'
77
+ + '\n★说明 contentHash 未剥干净 origin —— 这会让每次挪动代码都触发'
78
+ + '\n 全量 stale,change impact 沦为噪声。');
79
+ });
80
+ it('已知局限:重命名规则会使其子树身份迁移(如实暴露,不掩盖)', () => {
81
+ // ADR 0037 §8.5 记录的固有代价(实测 1/16 存活)。本用例把它**钉成已知行为**,
82
+ // 而不是让它在生产里以「一条规则凭空消失 + 一条凭空出现」的形式被发现。
83
+ const v2 = V1.replace('Rule approve given amount', 'Rule assess given amount');
84
+ assert.notStrictEqual(v2, V1, '★replace 未生效则本用例无效。');
85
+ const changes = diffNodeIds(idsOf(V1), idsOf(v2));
86
+ assert.ok(changes.some(c => c.kind === 'REMOVED'), `重命名应表现为旧身份 REMOVED(需由显式 rename 声明消解),实际:${JSON.stringify(changes)}`);
87
+ assert.ok(changes.some(c => c.kind === 'ADDED'), `重命名应表现为新身份 ADDED,实际:${JSON.stringify(changes)}`);
88
+ });
89
+ it('stale 只向上传播,不向下 —— 否则影响面被夸大到整棵子树', () => {
90
+ const changes = diffNodeIds(idsOf(V1), idsOf(V1.replace('10000', '20000')));
91
+ for (const c of changes) {
92
+ for (const ancestor of c.staleAncestors) {
93
+ assert.ok(c.nodeId.startsWith(ancestor), `staleAncestors 含非祖先项:节点 ${c.nodeId} 的 ${ancestor}`
94
+ + '\n★若把后代也标 stale,改一个字面量会波及整棵子树,影响面失真。');
95
+ assert.notStrictEqual(c.nodeId, ancestor, '节点自身不应出现在 staleAncestors 里。');
96
+ }
97
+ }
98
+ });
99
+ });
100
+ describe('canonicalHash:与 Java 同口径', () => {
101
+ it('★object key 按 Unicode code point 升序(不是 UTF-16 code unit 序)', () => {
102
+ // JS 的字符串比较(`<` 与默认 `.sort()`)按 **UTF-16 code unit**;Java 的
103
+ // CanonicalJson 按 **code point**。两者对增补平面字符的排序**相反**——
104
+ // 两侧会对同一棵树产出不同 hash,且只在含这类字符的程序上暴露(极易隐身)。
105
+ //
106
+ // ★判别用例必须选一对在两种排序下**次序相反**的键,否则测试恒绿:
107
+ // U+1F600 😀 的代理对以 D83D 开头 → UTF-16 序排在 U+FF5A z **之前**;
108
+ // 而 code point 序 0x1F600 > 0xFF5A → 应排在**之后**。
109
+ // (最初我用的是 `\u{1F600}` vs `'z'`(U+007A),两种排序结果相同,
110
+ // 把 compareByCodePoint 换成默认 .sort() 测试照样绿 = 假门禁。)
111
+ const emoji = '\u{1F600}';
112
+ const fullwidthZ = '\uFF5A';
113
+ assert.notStrictEqual(emoji < fullwidthZ, emoji.codePointAt(0) < fullwidthZ.codePointAt(0), '★选用的键对在两种排序下次序相同,本用例无法判别 —— 换一对键。');
114
+ // 期望值取自 Java CanonicalJson.canonicalHash(实跑),非 TS 自产回填。
115
+ assert.strictEqual(canonicalHash({ [emoji]: 1, [fullwidthZ]: 2 }), 'b87d68a90ea148ae96b7a6680061882d749a96d3b0a269a9c5b3a18ff647961e', '键序与 Java 不一致 —— 多半是用了 JS 默认的 UTF-16 比较而非 code point 比较。');
116
+ });
117
+ it('★拒绝浮点与超安全整数:静默降级会产出两侧不同的 hash', () => {
118
+ assert.throws(() => canonicalHash({ x: 1.5 }), /安全整数/, '浮点应抛错——跨引擎浮点表示不一致,静默接受会让两侧 hash 不同且无人察觉。');
119
+ assert.throws(() => canonicalHash({ x: Number.MAX_SAFE_INTEGER + 2 }), /安全整数/);
120
+ assert.throws(() => canonicalHash({ x: NaN }), /NaN|Infinity/);
121
+ });
122
+ it('hash 随内容变化(防止退化成常量)', () => {
123
+ assert.notStrictEqual(canonicalHash({ a: 1 }), canonicalHash({ a: 2 }), '不同内容产出了相同 hash —— 指纹失效。');
124
+ });
125
+ it('★与 Java 引擎产出逐字节相同的 hash(跨引擎黄金向量)', () => {
126
+ // ADR 0037 §7 要求 `Verify_TS == Verify_Java`。contentHash 是 verifier 的
127
+ // 输入之一,两侧必须对同一棵树产出同一个 hash。
128
+ //
129
+ // ★下列期望值是从 **Java `CanonicalJson.canonicalHash`** 实跑取得的
130
+ // (aster-lang-core,同一组输入),不是从 TS 自己的输出回填的——
131
+ // 后者会让本测试退化成「TS 和它自己一致」,恒绿且毫无意义。
132
+ //
133
+ // 覆盖四类易分叉点:键序、数组保序、字符串转义、null/bool 表示。
134
+ const golden = [
135
+ [{ kind: 'Func', name: 'approve' },
136
+ '1d734937ddec06a461c07bd9288b2794e5ed4c3f6412c35e77eb4f2ccedfa779'],
137
+ [{ kind: 'Module', name: 'demo.x', decls: [] },
138
+ 'c57535c007893ea6a6e20cf310eac1bb94fe2034ccdbaa5d81c60672dc74b890'],
139
+ [{ z: 2, a: 1, m: [1, 2, 3] },
140
+ 'ab46e446797b892a3626bbb51dac66c3bd1593612eb644be1f3f452232d876e3'],
141
+ [{ s: 'hello "world"\n', b: true, n: null },
142
+ '92c558f3dd8a1fd1e7be98261a6cb4fc37f01433418de7332dbb6540f8d5b8ea'],
143
+ [{ nested: { kind: 'If', cond: { kind: 'Bool', value: true } } },
144
+ '247d63263789e93f4c6700e1badf401abcd26662ca37767b4552873398571c7e'],
145
+ ];
146
+ for (const [value, expected] of golden) {
147
+ assert.strictEqual(canonicalHash(value), expected, `TS 与 Java 的 canonical hash 不一致,输入:${JSON.stringify(value)}`
148
+ + '\n★两侧对同一棵树必须产出同一 hash,否则跨引擎 verifier 无法成立。');
149
+ }
150
+ });
151
+ });
152
+ //# sourceMappingURL=change-impact.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-impact.test.js","sourceRoot":"","sources":["../../../../test/unit/nodeid/change-impact.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rename-declaration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rename-declaration.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/nodeid/rename-declaration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,75 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { canonicalize } from '../../../src/frontend/canonicalizer.js';
4
+ import { lex } from '../../../src/frontend/lexer.js';
5
+ import { parse } from '../../../src/parser.js';
6
+ import { lowerModule } from '../../../src/lower_to_core.js';
7
+ import { computeNodeIds } from '../../../src/nodeid/node-id-map.js';
8
+ import { diffNodeIds, rename } from '../../../src/nodeid/change-impact.js';
9
+ /**
10
+ * 显式 rename 声明(ADR 0037 §8.5 最后一项未决,与 Java 侧对等)。
11
+ *
12
+ * 命名作用域路径对重命名敏感:改一条规则的名字,其整棵子树的 nodeId 全变。
13
+ * 实测 16 节点样本 → 31 条变更,而其中 14 个节点内容逐字节未变。
14
+ */
15
+ const V1 = [
16
+ 'Module demo.approve.', '',
17
+ 'Rule approve given amount, produce:',
18
+ ' If amount greater than 10000:',
19
+ ' Return "REFER".',
20
+ ' Otherwise:',
21
+ ' Return "APPROVE".', '',
22
+ ].join('\n');
23
+ const idsOf = (src) => computeNodeIds(JSON.parse(JSON.stringify(lowerModule(parse(lex(canonicalize(src))).ast))));
24
+ describe('显式 rename 声明(TS 侧)', () => {
25
+ it('★声明后子树身份整体迁移,REMOVED/ADDED 噪声归零', () => {
26
+ const v2 = V1.replace('Rule approve given amount', 'Rule assess given amount');
27
+ assert.notStrictEqual(v2, V1, '★replace 未生效则本用例无效。');
28
+ const before = idsOf(V1), after = idsOf(v2);
29
+ const noiseBefore = diffNodeIds(before, after).filter(c => c.kind !== 'MODIFIED').length;
30
+ const noiseAfter = diffNodeIds(before, after, [rename('approve', 'assess')])
31
+ .filter(c => c.kind !== 'MODIFIED').length;
32
+ assert.ok(noiseBefore > 0, '未声明时应存在 REMOVED/ADDED 噪声(问题本身)。');
33
+ assert.strictEqual(noiseAfter, 0, `声明后不应再有 REMOVED/ADDED,实际 ${noiseAfter} 条。`);
34
+ });
35
+ it('★真实改动仍被报告(反向守卫:声明不得吞掉一切)', () => {
36
+ // 没有这条,「声明重命名」可以退化成「把所有变更都吞掉」,上面那条照样绿。
37
+ const v2 = V1
38
+ .replace('Rule approve given amount', 'Rule assess given amount')
39
+ .replace('10000', '20000');
40
+ const changes = diffNodeIds(idsOf(V1), idsOf(v2), [rename('approve', 'assess')]);
41
+ assert.ok(changes.some(c => c.kind === 'MODIFIED'), '阈值确实改了,必须仍报 MODIFIED。');
42
+ assert.ok(changes.some(c => c.staleAncestors.some(a => a.includes('{assess}'))), '受影响的规则应以**新**名字出现在 staleAncestors 里。');
43
+ });
44
+ it('★contentHash 不唯一 —— 自动配对不可靠的依据', () => {
45
+ const ids = idsOf(['Module m.', '', 'Rule alpha, produce:', ' Return 1.', '',
46
+ 'Rule beta, produce:', ' Return 1.', ''].join('\n'));
47
+ const a = ids.get('$.decls{alpha}.body');
48
+ const b = ids.get('$.decls{beta}.body');
49
+ assert.ok(a && b, '未找到被测节点,测试失去对象。');
50
+ assert.strictEqual(a.contentHash, b.contentHash, '两条同体规则的 body 应有相同 contentHash。'
51
+ + '\n★若本条变红,说明 hash 已唯一,届时可重新评估自动配对方案。');
52
+ });
53
+ it('只替换完整路径段,不做子串误伤(approve 不碰 approveAll)', () => {
54
+ const src = ['Module m.', '', 'Rule approve, produce:', ' Return 1.', '',
55
+ 'Rule approveAll, produce:', ' Return 2.', ''].join('\n');
56
+ const renamed = src.replace('Rule approve,', 'Rule assess,');
57
+ assert.notStrictEqual(renamed, src, '★构造未生效则本用例无效。');
58
+ const changes = diffNodeIds(idsOf(src), idsOf(renamed), [rename('approve', 'assess')]);
59
+ assert.ok(!changes.some(c => c.nodeId.includes('approveAll')), `approveAll 未被修改,不应出现在变更里 —— 多半是把 approve 当子串误伤了。实际:${JSON.stringify(changes)}`);
60
+ assert.ok(changes.every(c => c.kind === 'MODIFIED'), '声明后不应再有 REMOVED/ADDED。');
61
+ });
62
+ it('自相矛盾的声明直接拒绝', () => {
63
+ assert.throws(() => diffNodeIds(idsOf(V1), idsOf(V1), [rename('approve', 'assess'), rename('approve', 'review')]), /多个目标/, '同一名字声明到两个目标,应当拒绝而非静默取其一。');
64
+ });
65
+ it('非法 Rename 构造直接拒绝', () => {
66
+ assert.throws(() => rename('', 'x'), /不能为空/);
67
+ assert.throws(() => rename('x', ' '), /不能为空/);
68
+ assert.throws(() => rename('same', 'same'), /相同/);
69
+ });
70
+ it('不传 renames 时与旧行为一致(向后兼容)', () => {
71
+ const v2 = V1.replace('10000', '20000');
72
+ assert.deepStrictEqual(diffNodeIds(idsOf(V1), idsOf(v2)), diffNodeIds(idsOf(V1), idsOf(v2), []));
73
+ });
74
+ });
75
+ //# sourceMappingURL=rename-declaration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rename-declaration.test.js","sourceRoot":"","sources":["../../../../test/unit/nodeid/rename-declaration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parse-type-recursion.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-type-recursion.test.d.ts","sourceRoot":"","sources":["../../../test/unit/parse-type-recursion.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,58 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { compile } from '../../src/browser.js';
4
+ /**
5
+ * 类型递归的深度护栏(issue aster-lang-ts#145)。
6
+ *
7
+ * ★真实缺陷:`MAX_RECURSION_DEPTH=300` 的设计目标就是「病态输入不许打爆原生栈」,
8
+ * 但它此前**只接在 `parseExpr` 上**;而 `parseType → parseTypePrimary → parseType`
9
+ * 的类型递归完全无护栏。
10
+ *
11
+ * 实测(修复前):`'maybe '.repeat(5000) + 'Int'`(约 30KB)→ 原生 `RangeError`
12
+ * 「Maximum call stack size exceeded」,被 decl-parser 的 catch-all 转成
13
+ * **span 恒为 1:1 的假诊断**——用户既不知道是哪一行,也不知道真正原因是嵌套过深。
14
+ *
15
+ * 对照:400 层**括号**一直正确报「nesting too deep (exceeds limit of 300)」——
16
+ * 同一份护栏,只是没接到类型这条路径上。
17
+ */
18
+ function parseErrorOf(src) {
19
+ const c = compile(src);
20
+ const errs = c.parseErrors ?? [];
21
+ return errs.length > 0 ? errs[0].message : null;
22
+ }
23
+ function nestedMaybe(depth) {
24
+ return `Module p.\nRule r given x as ${'maybe '.repeat(depth)}Int, produce Int:\n Return 1.\n`;
25
+ }
26
+ describe('类型嵌套深度护栏', () => {
27
+ it('★5000 层嵌套类型报可恢复诊断,不是原生 RangeError', () => {
28
+ const msg = parseErrorOf(nestedMaybe(5000));
29
+ assert.ok(msg, '应有解析错误');
30
+ assert.doesNotMatch(msg, /Maximum call stack size exceeded/, `不得渗出原生 RangeError;实际:${msg}`);
31
+ assert.match(msg, /nesting too deep/, `应报嵌套过深;实际:${msg}`);
32
+ });
33
+ it('★超限即报,且报的是嵌套过深而非别的错', () => {
34
+ const msg = parseErrorOf(nestedMaybe(400));
35
+ assert.ok(msg, '400 层已超 300 上限,应报错');
36
+ assert.match(msg, /nesting too deep/, `实际:${msg}`);
37
+ });
38
+ it('★护栏不随深度增加而失效(10000 层同样是可恢复诊断)', () => {
39
+ // 若护栏仍在递归之后,更深的输入只会更早栈溢出。
40
+ const msg = parseErrorOf(nestedMaybe(10000));
41
+ assert.ok(msg);
42
+ assert.doesNotMatch(msg, /Maximum call stack size exceeded/, `实际:${msg}`);
43
+ });
44
+ });
45
+ describe('正常类型不受影响(反向护栏)', () => {
46
+ // ★没有这一组,把 parseType 写成「一律报嵌套过深」也能让上面全部变绿。
47
+ it('普通类型正常解析', () => {
48
+ assert.equal(parseErrorOf('Module p.\nRule r given x as Int, produce Int:\n Return 1.\n'), null);
49
+ });
50
+ it('浅层嵌套类型正常解析', () => {
51
+ assert.equal(parseErrorOf(nestedMaybe(10)), null, '10 层远低于上限');
52
+ });
53
+ it('★接近上限但未超(290 层)仍应通过', () => {
54
+ // 锁住「上限是 300 而不是被我改小了」——防护栏顺手收紧了合法输入。
55
+ assert.equal(parseErrorOf(nestedMaybe(290)), null, '290 层未超 300 上限,应通过');
56
+ });
57
+ });
58
+ //# sourceMappingURL=parse-type-recursion.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-type-recursion.test.js","sourceRoot":"","sources":["../../../test/unit/parse-type-recursion.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C;;;;;;;;;;;;;GAaG;AACH,SAAS,YAAY,CAAC,GAAW;IAC/B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACvB,MAAM,IAAI,GAAI,CAA6C,CAAC,WAAW,IAAI,EAAE,CAAC;IAC9E,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACnD,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,gCAAgC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,CAAC;AAClG,CAAC;AAED,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5C,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACzB,MAAM,CAAC,YAAY,CAAC,GAAI,EAAE,kCAAkC,EAC1D,wBAAwB,GAAG,EAAE,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,GAAI,EAAE,kBAAkB,EAAE,aAAa,GAAG,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,GAAI,EAAE,kBAAkB,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,0BAA0B;QAC1B,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,YAAY,CAAC,GAAI,EAAE,kCAAkC,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,2CAA2C;IAC3C,EAAE,CAAC,UAAU,EAAE,GAAG,EAAE;QAClB,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,+DAA+D,CAAC,EAAE,IAAI,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,YAAY,EAAE,GAAG,EAAE;QACpB,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,sCAAsC;QACtC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,oBAAoB,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -49,6 +49,70 @@ test('regex-guard 测试套件', async (t) => {
49
49
  assert.match(result.error, /invalid regular expression/i);
50
50
  }
51
51
  });
52
+ await t.test('★相邻量词歧义也必须拒绝(嵌套量词检查抓不到它)', () => {
53
+ // ★这个缺口由独立审查者发现,我已复现:
54
+ // `hasNestedQuantifier` 只看「被量词修饰的**分组**」,
55
+ // 而歧义**不需要分组**——两个相邻、匹配同一原子的量词即可产生
56
+ // 2^n 种切分,后缀失配时全部被穷举。
57
+ //
58
+ // 实测(守卫改前全部 ACCEPTED):
59
+ // a*a*a*a*a*a*a*a*a*a*b 16→292ms 20→361ms 22→808ms 24→1705ms(每 +2 翻倍)
60
+ // a+a+a+a+a+a+a+a+b 同样指数
61
+ //
62
+ // ★TS 侧**没有看门狗兜底**(Java 的 replaceAllWithTimeout 有),
63
+ // 单线程 JS 下这就是无限挂死。
64
+ for (const evil of [
65
+ 'a*a*a*a*a*a*a*a*a*a*b',
66
+ 'a+a+a+a+a+a+a+a+b',
67
+ '\\d*\\d*x',
68
+ '[ab]*[ab]*c',
69
+ 'a{1,}a{1,}b',
70
+ // ★以下三条是独立审查者找出的绕过(第一版全部 ACCEPTED,实测 24 字符
71
+ // 输入 1720ms / 1720ms / 640ms,与已修的 a*a*b 同级)。
72
+ // 根因:readQuantifiedAtom 遇 `(` 直接 return null 并注释「交给
73
+ // hasNestedQuantifier」,但后者只看**分组内部**有无量词——
74
+ // `(a)*(a)*` 两侧内部都没有,于是**无人负责**。
75
+ '(a)*(a)*b',
76
+ '(?:a)*(?:a)*b',
77
+ 'a*?a*?b',
78
+ // ★字符类里的括号会骗过深度扫描——这是**加分组支持时新引入**的漏判
79
+ // (上一版遇 `(` 直接 return null,反而不会误算深度)。实测 22 字符 794ms。
80
+ '([)])*([)])*b',
81
+ '([(])*([(])*b',
82
+ // ★这条同样能区分两态(跳过=REJECT / 不跳过=ACCEPT),
83
+ // 且原子内「字符类 + 普通字符」混排,覆盖面比上两条更宽。
84
+ '([)]a)*([)]a)*b',
85
+ ]) {
86
+ const result = compileGuardedRegex(evil, '');
87
+ assert.strictEqual(result.ok, false, `相邻量词模式 ${evil} 未被拒绝`);
88
+ if (!result.ok) {
89
+ assert.match(result.error, /adjacent ambiguous quantifier/i);
90
+ }
91
+ }
92
+ });
93
+ await t.test('★合法模式不得被相邻量词检查误伤(误伤比漏网更糟)', () => {
94
+ // 反向守卫:守卫收得过紧会**静默丢掉**用户的合法 overlay 规则,
95
+ // 那比漏掉一个 ReDoS 更难发现。
96
+ //
97
+ // ★`a*b*c`(不同原子)、`a*a`/`aa*`(只有一个量词)都必须通过。
98
+ // 实证:扫了两仓 79 条**生产**词典正则,零误伤。
99
+ for (const ok of [
100
+ 'a*b*c', '\\d+\\w+', '[a-z]+[0-9]*', '(ab)+c', 'a+b',
101
+ '\\bfoo\\b', 'greater\\s+than', '[\\p{L}]+', 'x{2,5}y',
102
+ 'a*a', 'aa*', '\\s+\\S+', '^(#{1,6})\\s',
103
+ // ★反向:不同原子的相邻量词、含分组的合法模式不得被误伤
104
+ '(a)(b)*c', '(?:ab)*(?:cd)*e', 'a*?b*?c',
105
+ // ★`([)])(a)*b` 是**非回归样本**,不是「字符类跳过」的守卫:
106
+ // 我复刻「跳过/不跳过」两版逐条对拍,它在两态下**判定相同**(均 false),
107
+ // 故变异掉字符类跳过时它照样绿——**没有证明力**。保留它只为确认
108
+ // 含字符类的合法模式不被误伤,不得把它算进「反向守卫有效」的证据。
109
+ // 真正能区分两态的样本全在**攻击方向**(见上面的 `([)])*([)])*b`)。
110
+ '([)])(a)*b',
111
+ ]) {
112
+ const result = compileGuardedRegex(ok, ok.includes('p{') ? 'u' : '');
113
+ assert.strictEqual(result.ok, true, `合法模式 ${ok} 被误伤:${result.ok ? '' : result.error}`);
114
+ }
115
+ });
52
116
  await t.test('overlay-loader 跳过恶意 type-inference 规则', () => {
53
117
  const rules = loadTypeInferenceRules({
54
118
  version: 1,
@@ -1 +1 @@
1
- {"version":3,"file":"regex-guard.test.js","sourceRoot":"","sources":["../../../test/unit/regex-guard.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"regex-guard.test.js","sourceRoot":"","sources":["../../../test/unit/regex-guard.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=strict-int-coercion.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strict-int-coercion.test.d.ts","sourceRoot":"","sources":["../../../test/unit/strict-int-coercion.test.ts"],"names":[],"mappings":""}