progmune-runtime 2.0.4 โ 2.1.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/.mcp.json +11 -0
- package/.progmune_allowlist +50 -0
- package/.test_report/test_report.md +87 -0
- package/Dockerfile +2 -10
- package/FAQ.md +167 -0
- package/demo-project/auth.ts +55 -0
- package/demo-project/tsconfig.json +8 -0
- package/dist/ab-stats.js +11 -0
- package/dist/acl-breakdown.js +13 -0
- package/dist/all-sessions.js +11 -0
- package/dist/antibody-stats.js +11 -0
- package/dist/audit.js +218 -0
- package/dist/benchmark-count.js +7 -0
- package/dist/benchmark-full.js +17 -0
- package/dist/benchmark-pass-rate.js +54 -0
- package/dist/benchmark-report.js +67 -0
- package/dist/benchmark-save.js +62 -0
- package/dist/benchmark-status.js +15 -0
- package/dist/branch-ledger.js +365 -0
- package/dist/branch-tree-count.js +14 -0
- package/dist/check.js +506 -0
- package/dist/common-fixpath.js +12 -0
- package/dist/constraint-types.js +12 -0
- package/dist/deterministic-replay.js +277 -0
- package/dist/emitter.js +112 -12
- package/dist/exec-metrics.js +11 -0
- package/dist/execute.js +242 -0
- package/dist/extract-ir.js +550 -5
- package/dist/failure-collector.js +143 -0
- package/dist/failure-corpus.js +481 -35
- package/dist/failure-report.js +11 -0
- package/dist/failures.js +11 -0
- package/dist/fast-path-hits.js +13 -0
- package/dist/feedback.js +6 -3
- package/dist/file-lock.js +82 -0
- package/dist/find-session.js +10 -0
- package/dist/fingerprint-list.js +15 -0
- package/dist/gen-history-log.js +13 -0
- package/dist/generate.js +2 -1
- package/dist/generate_500.js +4 -2
- package/dist/genome.js +11 -0
- package/dist/heatmap-data.js +11 -0
- package/dist/heatmap.js +11 -0
- package/dist/immune-reporter.js +33 -22
- package/dist/learned.js +11 -0
- package/dist/ledger-registry.js +241 -0
- package/dist/llm.js +43 -2
- package/dist/load-benchmarks.js +47 -0
- package/dist/main.js +2 -1
- package/dist/mcp-server.mjs +445 -43
- package/dist/memory-layer.js +51 -13
- package/dist/metrics.js +11 -0
- package/dist/obs-web.js +561 -0
- package/dist/p0_ssg_demo.js +255 -40
- package/dist/planner.js +905 -80
- package/dist/protocol-registry.js +105 -0
- package/dist/recent-session.js +12 -0
- package/dist/repair-proposal.js +353 -0
- package/dist/runtime-invariants.js +161 -0
- package/dist/runtime-types.js +117 -0
- package/dist/search-planner.js +38 -9
- package/dist/semantic-snapshot.js +155 -0
- package/dist/semantic-trace.js +1497 -0
- package/dist/semantic-validator.js +3 -2
- package/dist/semantic_guard_test.js +2 -1
- package/dist/sessions.js +11 -0
- package/dist/ssg-validator.js +658 -20
- package/dist/svl-distribution.js +11 -0
- package/dist/terminal-status.js +11 -0
- package/dist/test_failure_corpus.js +3 -1
- package/dist/token-savings.js +11 -0
- package/dist/total-repairs.js +12 -0
- package/dist/unresolved-count.js +12 -0
- package/dist/valid-fingerprints.js +13 -0
- package/dist/validator.js +116 -60
- package/dist/verify-fps.js +11 -0
- package/dist/verify-ledgers.js +11 -0
- package/docs/whitepaper-style.css +77 -0
- package/docs/whitepaper-v2.1.md +609 -0
- package/docs/whitepaper-v2.2.md +1064 -0
- package/docs/whitepaper-v2.2.pdf +0 -0
- package/fly.toml +1 -1
- package/package.json +13 -4
- package/protocols.json +131 -11
- package/public/dashboard.html +119 -0
- package/readme.md +829 -0
- package/server/hub.js +84 -12
- package/test/replay-golden/sess_1780063202050_mgeld.json +9 -0
- package/test/replay-golden/sess_1780064032560_gocld.json +354 -0
- package/test/replay-golden/sess_1780064413331_s2709.json +606 -0
- package/test/replay-golden/sess_1780064792710_y3avo.json +614 -0
- package/test/replay-golden.ts +84 -0
- package/test_benchmark.js +165 -0
- package/test_comprehensive.mjs +638 -0
- package/test_concurrency.js +129 -0
- package/test_ir_robustness.js +85 -0
- package/test_semantic_contracts.js +269 -0
- package/test_ssg_stress.js +156 -0
- package/test_svl3.js +58 -0
- package/tsconfig.json +1 -1
- package/.env.example +0 -3
- package/.progmune_memory/episodic.json +0 -186
- package/.progmune_memory/fingerprints.json +0 -7
- package/.progmune_memory/opt_in.json +0 -4
- package/README.md +0 -118
- package/immune_hub_data/2026-05-14.json +0 -50
package/dist/extract-ir.js
CHANGED
|
@@ -34,9 +34,74 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.extractIR = extractIR;
|
|
37
|
+
exports.extractIRWithTypes = extractIRWithTypes;
|
|
37
38
|
const ts_morph_1 = require("ts-morph");
|
|
38
39
|
const path = __importStar(require("path"));
|
|
39
40
|
const fs = __importStar(require("fs"));
|
|
41
|
+
const ts = __importStar(require("typescript"));
|
|
42
|
+
/** ไป JSDoc ๆณจ้ไธญ่งฃๆ capability ๆณจ่งฃ (@purpose, @tags) */
|
|
43
|
+
function parseCapabilityFromJSDoc(node) {
|
|
44
|
+
const jsdocs = node.getJsDocs?.();
|
|
45
|
+
if (!jsdocs || jsdocs.length === 0)
|
|
46
|
+
return {};
|
|
47
|
+
const result = {};
|
|
48
|
+
for (const doc of jsdocs) {
|
|
49
|
+
// @purpose from the description text
|
|
50
|
+
const comment = doc.getComment?.() || "";
|
|
51
|
+
if (comment && !comment.startsWith("@")) {
|
|
52
|
+
result.purpose = comment.split("\n")[0].trim();
|
|
53
|
+
}
|
|
54
|
+
// @tags from ts-morph tag system (getTags returns @tag annotations)
|
|
55
|
+
const tsTags = doc.getTags?.();
|
|
56
|
+
if (tsTags) {
|
|
57
|
+
for (const t of tsTags) {
|
|
58
|
+
const tn = t.getTagName?.();
|
|
59
|
+
if (tn === "tags" || tn === "tag") {
|
|
60
|
+
const val = t.getCommentText?.() || "";
|
|
61
|
+
result.tags = val.split(/[,\s]+/).map((s) => s.trim()).filter(Boolean);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
/** ไป JSDoc ๆณจ้ไธญ่งฃๆ @protocol ๆณจ่งฃ */
|
|
69
|
+
function parseProtocolFromJSDoc(node) {
|
|
70
|
+
const jsdocs = node.getJsDocs?.();
|
|
71
|
+
if (!jsdocs || jsdocs.length === 0)
|
|
72
|
+
return undefined;
|
|
73
|
+
for (const doc of jsdocs) {
|
|
74
|
+
const tags = doc.getTags?.();
|
|
75
|
+
if (!tags)
|
|
76
|
+
continue;
|
|
77
|
+
for (const tag of tags) {
|
|
78
|
+
const tagName = tag.getTagName?.();
|
|
79
|
+
if (tagName !== 'protocol')
|
|
80
|
+
continue;
|
|
81
|
+
const text = tag.getCommentText?.() || '';
|
|
82
|
+
// ่งฃๆๆ ผๅผ: namespace=file pre_states=["A","B"] post_states=["C"] invalidate=["A"]
|
|
83
|
+
try {
|
|
84
|
+
const nsMatch = text.match(/namespace\s*=\s*(\w+)/);
|
|
85
|
+
const preMatch = text.match(/pre_states\s*=\s*\[([^\]]*)\]/);
|
|
86
|
+
const postMatch = text.match(/post_states\s*=\s*\[([^\]]*)\]/);
|
|
87
|
+
const invMatch = text.match(/invalidate\s*=\s*\[([^\]]*)\]/);
|
|
88
|
+
if (!preMatch || !postMatch)
|
|
89
|
+
return undefined;
|
|
90
|
+
const namespace = nsMatch ? nsMatch[1] : undefined;
|
|
91
|
+
const pre_states = preMatch[1].split(',').map((s) => s.trim().replace(/["']/g, '')).filter(Boolean);
|
|
92
|
+
const post_states = postMatch[1].split(',').map((s) => s.trim().replace(/["']/g, '')).filter(Boolean);
|
|
93
|
+
const invalidate = invMatch
|
|
94
|
+
? invMatch[1].split(',').map((s) => s.trim().replace(/["']/g, '')).filter(Boolean)
|
|
95
|
+
: undefined;
|
|
96
|
+
return { pre_states, post_states, invalidate, namespace };
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
40
105
|
// ่ทๅ็ฑปๅ่็น็็ปๆๅๆ่ฟฐ
|
|
41
106
|
function getTypeDetail(typeNode) {
|
|
42
107
|
if (!typeNode)
|
|
@@ -79,6 +144,18 @@ function getReturnTypeDetail(func) {
|
|
|
79
144
|
const typeNode = func.getReturnTypeNode?.();
|
|
80
145
|
return typeNode ? getTypeDetail(typeNode) : "";
|
|
81
146
|
}
|
|
147
|
+
/** Derive input types from params (auto-capability graph). */
|
|
148
|
+
function deriveInputs(params) {
|
|
149
|
+
return params.map(p => {
|
|
150
|
+
const t = (p.type || "any").replace(/\[\]$/, "").replace(/<[^>]*>/g, "");
|
|
151
|
+
return t.split("|")[0].trim();
|
|
152
|
+
}).filter(t => t !== "any" && t !== "void");
|
|
153
|
+
}
|
|
154
|
+
/** Derive output types from return type (auto-capability graph). */
|
|
155
|
+
function deriveOutputs(returnType) {
|
|
156
|
+
const t = returnType.replace(/\[\]$/, "").replace(/<[^>]*>/g, "").split("|")[0].trim();
|
|
157
|
+
return (t === "void" || t === "any") ? [] : [t];
|
|
158
|
+
}
|
|
82
159
|
function extractDirectCalls(func) {
|
|
83
160
|
const body = func.getBody();
|
|
84
161
|
if (!body)
|
|
@@ -97,7 +174,15 @@ function extractDirectCalls(func) {
|
|
|
97
174
|
});
|
|
98
175
|
return [...new Set(calls)];
|
|
99
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* ไป TypeScript ้กน็ฎๆๅ IR๏ผๅฝๆฐ็ญพๅใๅๆฐใ่ฟๅๅผใๅ่ฎฎๆณจ่งฃ๏ผใ
|
|
179
|
+
* @protocol namespace=dev_pipeline pre_states=[] post_states=["IR_EXTRACTED"] invalidate=["IR_STALE"]
|
|
180
|
+
*/
|
|
100
181
|
function extractIR(projectRoot) {
|
|
182
|
+
return extractIRWithTypes(projectRoot).functions;
|
|
183
|
+
}
|
|
184
|
+
/** Extract both functions and typeโfile mapping. */
|
|
185
|
+
function extractIRWithTypes(projectRoot) {
|
|
101
186
|
const absRoot = path.resolve(projectRoot);
|
|
102
187
|
const project = new ts_morph_1.Project({
|
|
103
188
|
tsConfigFilePath: path.join(absRoot, "tsconfig.json"),
|
|
@@ -124,11 +209,470 @@ function extractIR(projectRoot) {
|
|
|
124
209
|
returnTypeDetail: getReturnTypeDetail(f),
|
|
125
210
|
file: relPath,
|
|
126
211
|
calls: extractDirectCalls(f),
|
|
212
|
+
exported: f.isExported(),
|
|
213
|
+
inputs: deriveInputs(f.getParameters().map(p => ({ name: p.getName(), type: p.getTypeNode()?.getText() || "any" }))),
|
|
214
|
+
outputs: deriveOutputs(f.getReturnTypeNode()?.getText() || "any"),
|
|
215
|
+
protocol: parseProtocolFromJSDoc(f),
|
|
216
|
+
...parseCapabilityFromJSDoc(f),
|
|
127
217
|
});
|
|
128
218
|
}
|
|
129
|
-
//
|
|
219
|
+
// ๆๅ็ฎญๅคดๅฝๆฐ๏ผconst fn = () => {}, export const fn = () => {} ็ญ๏ผ
|
|
220
|
+
for (const vd of sf.getVariableDeclarations()) {
|
|
221
|
+
const init = vd.getInitializer();
|
|
222
|
+
if (!init)
|
|
223
|
+
continue;
|
|
224
|
+
// ็ดๆฅ็ฎญๅคดๅฝๆฐ: const fn = () => {}
|
|
225
|
+
if (ts_morph_1.Node.isArrowFunction(init)) {
|
|
226
|
+
const name = vd.getName();
|
|
227
|
+
funcs.push({
|
|
228
|
+
name,
|
|
229
|
+
params: init.getParameters().map(p => ({
|
|
230
|
+
name: p.getName(),
|
|
231
|
+
type: getParamType(p),
|
|
232
|
+
typeDetail: getParamTypeDetail(p),
|
|
233
|
+
})),
|
|
234
|
+
returnType: getReturnType(init),
|
|
235
|
+
returnTypeDetail: getReturnTypeDetail(init),
|
|
236
|
+
file: relPath,
|
|
237
|
+
exported: vd.isExported(),
|
|
238
|
+
calls: extractDirectCalls(init),
|
|
239
|
+
protocol: parseProtocolFromJSDoc(vd),
|
|
240
|
+
...parseCapabilityFromJSDoc(vd),
|
|
241
|
+
});
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
// ๅ
่ฃ
็็ฎญๅคดๅฝๆฐ: const fn = debounce(() => {})
|
|
245
|
+
if (ts_morph_1.Node.isCallExpression(init)) {
|
|
246
|
+
for (const arg of init.getArguments()) {
|
|
247
|
+
if (ts_morph_1.Node.isArrowFunction(arg)) {
|
|
248
|
+
const name = vd.getName();
|
|
249
|
+
funcs.push({
|
|
250
|
+
name,
|
|
251
|
+
params: arg.getParameters().map(p => ({
|
|
252
|
+
name: p.getName(),
|
|
253
|
+
type: getParamType(p),
|
|
254
|
+
typeDetail: getParamTypeDetail(p),
|
|
255
|
+
})),
|
|
256
|
+
returnType: getReturnType(arg),
|
|
257
|
+
returnTypeDetail: getReturnTypeDetail(arg),
|
|
258
|
+
file: relPath,
|
|
259
|
+
exported: vd.isExported(),
|
|
260
|
+
calls: extractDirectCalls(arg),
|
|
261
|
+
protocol: parseProtocolFromJSDoc(vd),
|
|
262
|
+
});
|
|
263
|
+
break; // ๅชๅ็ฌฌไธไธช็ฎญๅคดๅฝๆฐๅๆฐ
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Extract class methods: ts-morph getFunctions() excludes class members
|
|
268
|
+
for (const cls of sf.getClasses()) {
|
|
269
|
+
const cn = cls.getName();
|
|
270
|
+
if (!cn)
|
|
271
|
+
continue;
|
|
272
|
+
for (const m of cls.getMethods()) {
|
|
273
|
+
const mn = m.getName();
|
|
274
|
+
if (!mn)
|
|
275
|
+
continue;
|
|
276
|
+
funcs.push({
|
|
277
|
+
name: `${cn}.${mn}`,
|
|
278
|
+
params: m.getParameters().map((p) => ({ name: p.getName(), type: getParamType(p), typeDetail: getParamTypeDetail(p) })),
|
|
279
|
+
returnType: m.getReturnTypeNode?.()?.getText?.() || "any",
|
|
280
|
+
returnTypeDetail: m.getReturnTypeNode?.()?.getText?.() || "any",
|
|
281
|
+
file: relPath,
|
|
282
|
+
exported: vd.isExported(), calls: [],
|
|
283
|
+
...parseCapabilityFromJSDoc(m),
|
|
284
|
+
protocol: parseProtocolFromJSDoc(m),
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
291
|
+
// Phase 5: Dynamic external function resolution
|
|
292
|
+
// Replaces hardcoded knownExternals
|
|
293
|
+
// includes: npm packages (.d.ts via ts.resolveModuleName), Node.js built-ins (@types/node)
|
|
294
|
+
// fallback: knownExternals registry for unresolved functions
|
|
295
|
+
// final fallback: any type with empty params with ts-morph + ts.resolveModuleName
|
|
296
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
297
|
+
const declaredNames = new Set(funcs.map(f => f.name));
|
|
298
|
+
// Collect imports for dynamic resolution
|
|
299
|
+
const externalFuncs = new Map();
|
|
300
|
+
// Also track namespace imports: import * as X from 'module' โ X.method
|
|
301
|
+
const namespaceImports = new Map(); // X โ resolved source file
|
|
302
|
+
for (const sf of project.getSourceFiles()) {
|
|
303
|
+
if (sf.getFilePath().includes('node_modules'))
|
|
304
|
+
continue;
|
|
305
|
+
for (const imp of sf.getImportDeclarations()) {
|
|
306
|
+
const mod = imp.getModuleSpecifierValue();
|
|
307
|
+
// Named imports: import { X } from 'mod'
|
|
308
|
+
for (const ni of imp.getNamedImports()) {
|
|
309
|
+
const name = ni.getName();
|
|
310
|
+
if (declaredNames.has(name))
|
|
311
|
+
continue;
|
|
312
|
+
if (externalFuncs.has(name))
|
|
313
|
+
continue;
|
|
314
|
+
try {
|
|
315
|
+
const resolved = imp.getModuleSpecifierSourceFile();
|
|
316
|
+
if (resolved) {
|
|
317
|
+
const sig = extractSignatureFromFile(name, resolved);
|
|
318
|
+
if (sig) {
|
|
319
|
+
externalFuncs.set(name, sig);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const tsResult = ts.resolveModuleName(mod, sf.getFilePath(), {}, ts.sys);
|
|
324
|
+
const resolvedPath = tsResult.resolvedModule?.resolvedFileName;
|
|
325
|
+
if (resolvedPath && fs.existsSync(resolvedPath)) {
|
|
326
|
+
const sig = extractSignatureFromDts(name, resolvedPath, project);
|
|
327
|
+
if (sig) {
|
|
328
|
+
externalFuncs.set(name, sig);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
catch { }
|
|
334
|
+
}
|
|
335
|
+
// Namespace imports: import * as X from 'mod'
|
|
336
|
+
const nsImport = imp.getNamespaceImport();
|
|
337
|
+
if (nsImport) {
|
|
338
|
+
try {
|
|
339
|
+
const resolved = imp.getModuleSpecifierSourceFile();
|
|
340
|
+
if (resolved) {
|
|
341
|
+
namespaceImports.set(nsImport.getText(), resolved);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
const tsResult = ts.resolveModuleName(mod, sf.getFilePath(), {}, ts.sys);
|
|
345
|
+
const resolvedPath = tsResult.resolvedModule?.resolvedFileName;
|
|
346
|
+
if (resolvedPath && fs.existsSync(resolvedPath)) {
|
|
347
|
+
const dtsFile = project.addSourceFileAtPathIfExists(resolvedPath);
|
|
348
|
+
if (dtsFile)
|
|
349
|
+
namespaceImports.set(nsImport.getText(), dtsFile);
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
// Node.js built-in: try @types/node
|
|
353
|
+
const nodeTypesPath = path.join(absRoot, "node_modules/@types/node", mod + ".d.ts");
|
|
354
|
+
if (fs.existsSync(nodeTypesPath)) {
|
|
355
|
+
const nodeDts = project.addSourceFileAtPathIfExists(nodeTypesPath);
|
|
356
|
+
if (nodeDts)
|
|
357
|
+
namespaceImports.set(nsImport.getText(), nodeDts);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
catch { }
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// Resolve namespace calls: fs.readFileSync โ look up 'readFileSync' in 'fs' source file
|
|
367
|
+
function tryResolveFromNamespace(callName, prefix) {
|
|
368
|
+
const sourceFile = namespaceImports.get(prefix);
|
|
369
|
+
if (!sourceFile)
|
|
370
|
+
return false;
|
|
371
|
+
const sig = extractSignatureFromFile(callName, sourceFile);
|
|
372
|
+
if (sig) {
|
|
373
|
+
externalFuncs.set(callName, sig);
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
// Collect undeclared calls (functions used but not declared and not resolved above)
|
|
379
|
+
const allCalls = new Set();
|
|
380
|
+
for (const f of funcs) {
|
|
381
|
+
for (const c of f.calls) {
|
|
382
|
+
allCalls.add(c);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// JS ๅ
็ฝฎๆนๆณๅ TypeScript ่ฏญๆณ่็น๏ผไธไฝไธบๅค้จๅฝๆฐๆด้ฒ
|
|
386
|
+
const ignoredBuiltins = new Set([
|
|
387
|
+
"map", "filter", "reduce", "forEach", "find", "some", "every",
|
|
388
|
+
"push", "pop", "shift", "unshift", "slice", "splice", "concat",
|
|
389
|
+
"join", "split", "replace", "match", "search", "trim", "toLowerCase",
|
|
390
|
+
"toUpperCase", "includes", "indexOf", "startsWith", "endsWith",
|
|
391
|
+
"sort", "reverse", "keys", "values", "entries", "has", "get", "set",
|
|
392
|
+
"toString", "toISOString", "getTime", "getFullYear", "getMonth", "getDate",
|
|
393
|
+
"getHours", "getMinutes", "getSeconds", "floor", "ceil", "round",
|
|
394
|
+
"charAt", "charCodeAt", "substring", "substr", "padStart", "padEnd",
|
|
395
|
+
"getJsDocs", "getTags", "getTagName", "getCommentText", "getText",
|
|
396
|
+
"getName", "getType", "getExpression", "getArguments", "getParameters",
|
|
397
|
+
"getSourceFiles", "getFunctions", "getVariableDeclarations",
|
|
398
|
+
"getInitializer", "getReturnType", "isIdentifier", "isArrowFunction",
|
|
399
|
+
"isCallExpression", "isFunctionDeclaration", "isUnionTypeNode",
|
|
400
|
+
"isTypeReference", "isPropertyAccessExpression", "getTypeNodes",
|
|
401
|
+
"getTypeArguments", "getTypeName", "skip", "traversal",
|
|
402
|
+
"addSourceFilesAtPaths", "getFilePath",
|
|
403
|
+
"then", "catch", "resolve", "reject", // Promise
|
|
404
|
+
]);
|
|
405
|
+
// Minimal fallback registry (Node.js built-ins that ts-morph can't resolve)
|
|
406
|
+
const knownExternals = {
|
|
407
|
+
// fs
|
|
408
|
+
"readFileSync": {
|
|
409
|
+
params: [{ name: "path", type: "string" }, { name: "encoding", type: "string" }],
|
|
410
|
+
returnType: "string", description: "ๅๆญฅ่ฏปๅๆไปถๅ
ๅฎน",
|
|
411
|
+
},
|
|
412
|
+
"writeFileSync": {
|
|
413
|
+
params: [{ name: "path", type: "string" }, { name: "data", type: "string" }],
|
|
414
|
+
returnType: "void", description: "ๅๆญฅๅๅ
ฅๆไปถ",
|
|
415
|
+
},
|
|
416
|
+
"existsSync": {
|
|
417
|
+
params: [{ name: "path", type: "string" }],
|
|
418
|
+
returnType: "boolean", description: "ๆฃๆฅๆไปถๆฏๅฆๅญๅจ",
|
|
419
|
+
},
|
|
420
|
+
"readdirSync": {
|
|
421
|
+
params: [{ name: "path", type: "string" }],
|
|
422
|
+
returnType: "string[]", description: "่ฏปๅ็ฎๅฝๅ
ๅฎน",
|
|
423
|
+
},
|
|
424
|
+
"mkdirSync": {
|
|
425
|
+
params: [{ name: "path", type: "string" }, { name: "options", type: "object" }],
|
|
426
|
+
returnType: "void", description: "ๅๅปบ็ฎๅฝ",
|
|
427
|
+
},
|
|
428
|
+
"unlinkSync": {
|
|
429
|
+
params: [{ name: "path", type: "string" }],
|
|
430
|
+
returnType: "void", description: "ๅ ้คๆไปถ",
|
|
431
|
+
},
|
|
432
|
+
"statSync": {
|
|
433
|
+
params: [{ name: "path", type: "string" }],
|
|
434
|
+
returnType: "object", description: "่ทๅๆไปถ็ถๆ",
|
|
435
|
+
},
|
|
436
|
+
// path
|
|
437
|
+
"resolve": {
|
|
438
|
+
params: [{ name: "segments", type: "string[]" }],
|
|
439
|
+
returnType: "string", description: "่งฃๆ่ทฏๅพๆฎตไธบ็ปๅฏน่ทฏๅพ",
|
|
440
|
+
},
|
|
441
|
+
"relative": {
|
|
442
|
+
params: [{ name: "from", type: "string" }, { name: "to", type: "string" }],
|
|
443
|
+
returnType: "string", description: "่ฎก็ฎ็ธๅฏน่ทฏๅพ",
|
|
444
|
+
},
|
|
445
|
+
"dirname": {
|
|
446
|
+
params: [{ name: "path", type: "string" }],
|
|
447
|
+
returnType: "string", description: "่ทๅ็ฎๅฝๅ",
|
|
448
|
+
},
|
|
449
|
+
"basename": {
|
|
450
|
+
params: [{ name: "path", type: "string" }],
|
|
451
|
+
returnType: "string", description: "่ทๅๆไปถๅ",
|
|
452
|
+
},
|
|
453
|
+
"extname": {
|
|
454
|
+
params: [{ name: "path", type: "string" }],
|
|
455
|
+
returnType: "string", description: "่ทๅๆไปถๆฉๅฑๅ",
|
|
456
|
+
},
|
|
457
|
+
// JSON
|
|
458
|
+
"parse": {
|
|
459
|
+
params: [{ name: "text", type: "string" }],
|
|
460
|
+
returnType: "any", description: "่งฃๆ JSON ๅญ็ฌฆไธฒ",
|
|
461
|
+
},
|
|
462
|
+
"stringify": {
|
|
463
|
+
params: [{ name: "value", type: "any" }, { name: "replacer", type: "any" }],
|
|
464
|
+
returnType: "string", description: "ๅบๅๅไธบ JSON ๅญ็ฌฆไธฒ",
|
|
465
|
+
},
|
|
466
|
+
// console
|
|
467
|
+
"log": {
|
|
468
|
+
params: [{ name: "message", type: "any" }],
|
|
469
|
+
returnType: "void", description: "่พๅบๆฅๅฟ",
|
|
470
|
+
},
|
|
471
|
+
"error": {
|
|
472
|
+
params: [{ name: "message", type: "any" }],
|
|
473
|
+
returnType: "void", description: "่พๅบ้่ฏฏๆฅๅฟ",
|
|
474
|
+
},
|
|
475
|
+
"warn": {
|
|
476
|
+
params: [{ name: "message", type: "any" }],
|
|
477
|
+
returnType: "void", description: "่พๅบ่ญฆๅๆฅๅฟ",
|
|
478
|
+
},
|
|
479
|
+
// Buffer
|
|
480
|
+
"from": {
|
|
481
|
+
params: [{ name: "data", type: "string" }, { name: "encoding", type: "string" }],
|
|
482
|
+
returnType: "Buffer", description: "ไปๅญ็ฌฆไธฒๅๅปบ Buffer",
|
|
483
|
+
},
|
|
484
|
+
// process
|
|
485
|
+
"exit": {
|
|
486
|
+
params: [{ name: "code", type: "number" }],
|
|
487
|
+
returnType: "void", description: "้ๅบ่ฟ็จ",
|
|
488
|
+
},
|
|
489
|
+
"cwd": {
|
|
490
|
+
params: [],
|
|
491
|
+
returnType: "string", description: "่ทๅๅฝๅๅทฅไฝ็ฎๅฝ",
|
|
492
|
+
},
|
|
493
|
+
// child_process
|
|
494
|
+
"execSync": {
|
|
495
|
+
params: [{ name: "command", type: "string" }],
|
|
496
|
+
returnType: "Buffer", description: "ๅๆญฅๆง่กๅฝไปค",
|
|
497
|
+
},
|
|
498
|
+
// crypto
|
|
499
|
+
"createHash": {
|
|
500
|
+
params: [{ name: "algorithm", type: "string" }],
|
|
501
|
+
returnType: "Hash", description: "ๅๅปบๅๅธๅฏน่ฑก",
|
|
502
|
+
},
|
|
503
|
+
"digest": {
|
|
504
|
+
params: [{ name: "encoding", type: "string" }],
|
|
505
|
+
returnType: "string", description: "่พๅบๅๅธๆ่ฆ",
|
|
506
|
+
},
|
|
507
|
+
"update": {
|
|
508
|
+
params: [{ name: "data", type: "string" }],
|
|
509
|
+
returnType: "Hash", description: "ๆดๆฐๅๅธๆฐๆฎ",
|
|
510
|
+
},
|
|
511
|
+
// Date
|
|
512
|
+
"now": {
|
|
513
|
+
params: [],
|
|
514
|
+
returnType: "number", description: "ๅฝๅๆถ้ดๆณ๏ผๆฏซ็ง๏ผ",
|
|
515
|
+
},
|
|
516
|
+
// Object
|
|
517
|
+
"entries": {
|
|
518
|
+
params: [{ name: "obj", type: "object" }],
|
|
519
|
+
returnType: "Array<[string, any]>", description: "่ฟๅๅฏน่ฑก็้ฎๅผๅฏนๆฐ็ป",
|
|
520
|
+
},
|
|
521
|
+
// setTimeout / setInterval
|
|
522
|
+
"setTimeout": {
|
|
523
|
+
params: [{ name: "callback", type: "function" }, { name: "ms", type: "number" }],
|
|
524
|
+
returnType: "number", description: "ๅปถ่ฟๆง่กๅ่ฐ",
|
|
525
|
+
},
|
|
526
|
+
// Math
|
|
527
|
+
"random": {
|
|
528
|
+
params: [],
|
|
529
|
+
returnType: "number", description: "็ๆ 0-1 ้ๆบๆฐ",
|
|
530
|
+
},
|
|
531
|
+
"abs": {
|
|
532
|
+
params: [{ name: "x", type: "number" }],
|
|
533
|
+
returnType: "number", description: "็ปๅฏนๅผ",
|
|
534
|
+
},
|
|
535
|
+
"max": {
|
|
536
|
+
params: [{ name: "values", type: "number[]" }],
|
|
537
|
+
returnType: "number", description: "ๆๅคงๅผ",
|
|
538
|
+
},
|
|
539
|
+
"min": {
|
|
540
|
+
params: [{ name: "values", type: "number[]" }],
|
|
541
|
+
returnType: "number", description: "ๆๅฐๅผ",
|
|
542
|
+
},
|
|
543
|
+
};
|
|
544
|
+
let externalCount = 0;
|
|
545
|
+
let dynamicCount = 0;
|
|
546
|
+
let fallbackCount = 0;
|
|
547
|
+
for (const callName of allCalls) {
|
|
548
|
+
if (declaredNames.has(callName))
|
|
549
|
+
continue;
|
|
550
|
+
if (ignoredBuiltins.has(callName))
|
|
551
|
+
continue;
|
|
552
|
+
if (callName.startsWith("is") && callName[2] === callName[2]?.toUpperCase())
|
|
553
|
+
continue;
|
|
554
|
+
// Try namespace resolution for unresolved calls
|
|
555
|
+
if (!externalFuncs.has(callName) && !knownExternals[callName]) {
|
|
556
|
+
// Check if this function is called as X.method (property access)
|
|
557
|
+
// The callName is already the method name from extractDirectCalls
|
|
558
|
+
// Try each known namespace to find the function
|
|
559
|
+
for (const [nsPrefix] of namespaceImports) {
|
|
560
|
+
if (tryResolveFromNamespace(callName, nsPrefix))
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
// Priority: dynamic resolution > knownExternals fallback > any
|
|
565
|
+
const dynamic = externalFuncs.get(callName);
|
|
566
|
+
const fallback = knownExternals[callName];
|
|
567
|
+
if (dynamic) {
|
|
568
|
+
funcs.push({
|
|
569
|
+
name: callName,
|
|
570
|
+
params: dynamic.params,
|
|
571
|
+
returnType: dynamic.returnType,
|
|
572
|
+
returnTypeDetail: dynamic.returnType,
|
|
573
|
+
file: "(external)",
|
|
574
|
+
calls: [],
|
|
575
|
+
external: true,
|
|
576
|
+
description: dynamic.description,
|
|
577
|
+
});
|
|
578
|
+
dynamicCount++;
|
|
579
|
+
}
|
|
580
|
+
else if (fallback) {
|
|
581
|
+
funcs.push({
|
|
582
|
+
name: callName,
|
|
583
|
+
params: fallback.params,
|
|
584
|
+
returnType: fallback.returnType,
|
|
585
|
+
returnTypeDetail: fallback.returnType,
|
|
586
|
+
file: "(external)",
|
|
587
|
+
calls: [],
|
|
588
|
+
external: true,
|
|
589
|
+
description: fallback.description,
|
|
590
|
+
});
|
|
591
|
+
fallbackCount++;
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
funcs.push({
|
|
595
|
+
name: callName,
|
|
596
|
+
params: [],
|
|
597
|
+
returnType: "any",
|
|
598
|
+
returnTypeDetail: "any",
|
|
599
|
+
file: "(external)",
|
|
600
|
+
calls: [],
|
|
601
|
+
external: true,
|
|
602
|
+
});
|
|
603
|
+
externalCount++;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
const totalExternal = dynamicCount + fallbackCount + externalCount;
|
|
607
|
+
console.error(`๐ฆ ๅค้จๅฝๆฐ: ${totalExternal} (ๅจๆ=${dynamicCount} ๅ้=${fallbackCount} ๆช็ญพๅ=${externalCount}, from ${allCalls.size} total calls)`);
|
|
608
|
+
// Build typeโmodule map for emitter
|
|
609
|
+
const _typeMap = {};
|
|
610
|
+
for (const _sf of project.getSourceFiles()) {
|
|
611
|
+
if (_sf.getFilePath().includes("node_modules"))
|
|
612
|
+
continue;
|
|
613
|
+
const _rp = path.relative(absRoot, _sf.getFilePath());
|
|
614
|
+
for (const _iface of _sf.getInterfaces()) {
|
|
615
|
+
const _n = _iface.getName();
|
|
616
|
+
if (_n && _iface.isExported())
|
|
617
|
+
_typeMap[_n] = _rp;
|
|
618
|
+
}
|
|
619
|
+
for (const _ta of _sf.getTypeAliases()) {
|
|
620
|
+
const _n = _ta.getName();
|
|
621
|
+
if (_n && _ta.isExported())
|
|
622
|
+
_typeMap[_n] = _rp;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return { functions: funcs, typeMap: _typeMap };
|
|
626
|
+
}
|
|
627
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
628
|
+
// Dynamic external signature extraction helpers
|
|
629
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
630
|
+
/** Extract function signature from a resolved ts-morph source file. */
|
|
631
|
+
function extractSignatureFromFile(name, sf) {
|
|
632
|
+
for (const exp of sf.getExportedDeclarations().entries()) {
|
|
633
|
+
if (exp[0] !== name)
|
|
634
|
+
continue;
|
|
635
|
+
for (const decl of exp[1]) {
|
|
636
|
+
if (ts_morph_1.Node.isFunctionDeclaration(decl)) {
|
|
637
|
+
const params = decl.getParameters().map(p => ({
|
|
638
|
+
name: p.getName(),
|
|
639
|
+
type: p.getTypeNode()?.getText() || "any",
|
|
640
|
+
}));
|
|
641
|
+
return {
|
|
642
|
+
params,
|
|
643
|
+
returnType: decl.getReturnTypeNode()?.getText() || "any",
|
|
644
|
+
description: `auto-resolved from ${sf.getFilePath()}`,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
if (ts_morph_1.Node.isVariableDeclaration(decl)) {
|
|
648
|
+
const init = decl.getInitializer();
|
|
649
|
+
if (init && ts_morph_1.Node.isArrowFunction(init)) {
|
|
650
|
+
const params = init.getParameters().map(p => ({
|
|
651
|
+
name: p.getName(),
|
|
652
|
+
type: p.getTypeNode()?.getText() || "any",
|
|
653
|
+
}));
|
|
654
|
+
return {
|
|
655
|
+
params,
|
|
656
|
+
returnType: init.getReturnTypeNode()?.getText() || "any",
|
|
657
|
+
description: `auto-resolved arrow from ${sf.getFilePath()}`,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
/** Extract function signature from a .d.ts file using ts-morph. */
|
|
666
|
+
function extractSignatureFromDts(name, dtsPath, project) {
|
|
667
|
+
try {
|
|
668
|
+
const sf = project.addSourceFileAtPathIfExists(dtsPath);
|
|
669
|
+
if (!sf)
|
|
670
|
+
return null;
|
|
671
|
+
return extractSignatureFromFile(name, sf);
|
|
672
|
+
}
|
|
673
|
+
catch {
|
|
674
|
+
return null;
|
|
130
675
|
}
|
|
131
|
-
return funcs;
|
|
132
676
|
}
|
|
133
677
|
// ่ฅ็ดๆฅ่ฟ่ก
|
|
134
678
|
if (require.main === module) {
|
|
@@ -137,7 +681,8 @@ if (require.main === module) {
|
|
|
137
681
|
console.error("็จๆณ: ts-node extract-ir.ts <้กน็ฎๆ น>");
|
|
138
682
|
process.exit(1);
|
|
139
683
|
}
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
684
|
+
const result = extractIRWithTypes(root);
|
|
685
|
+
const data = { typeMap: result.typeMap, functions: result.functions };
|
|
686
|
+
fs.writeFileSync("ir.json", JSON.stringify(data, null, 2));
|
|
687
|
+
console.log(`โ
IR ๆๅๅฎๆ: ${result.functions.length} ไธชๅฝๆฐ, ${Object.keys(result.typeMap).length} ไธช็ฑปๅ -> ir.json`);
|
|
143
688
|
}
|