@synergenius/flow-weaver 0.24.0 → 0.24.2
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/agent/index.d.ts +2 -1
- package/dist/agent/index.js +2 -0
- package/dist/agent/providers/anthropic.js +14 -1
- package/dist/agent/providers/claude-cli.d.ts +1 -1
- package/dist/agent/providers/claude-cli.js +4 -1
- package/dist/agent/providers/openai-compat.d.ts +1 -1
- package/dist/agent/providers/openai-compat.js +2 -1
- package/dist/agent/providers/platform.d.ts +1 -1
- package/dist/agent/providers/platform.js +3 -1
- package/dist/agent/types.d.ts +21 -2
- package/dist/agent/types.js +6 -1
- package/dist/cli/flow-weaver.mjs +29 -12
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/dist/mcp/tools-debug.js +2 -2
- package/dist/parser.js +32 -13
- package/package.json +1 -1
package/dist/agent/index.d.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Provider-agnostic agent loop with MCP bridge for tool execution.
|
|
5
5
|
* Built-in providers: Anthropic API, Claude CLI, OpenAI-compatible (GPT-4o, Groq, Ollama, etc).
|
|
6
6
|
*/
|
|
7
|
-
export type { StreamEvent, AgentMessage, AgentProvider, ToolDefinition, ToolExecutor, ToolEvent, McpBridge, AgentLoopOptions, AgentLoopResult, StreamOptions, SpawnFn, ClaudeCliProviderOptions, CliSessionOptions, Logger, } from './types.js';
|
|
7
|
+
export type { SplitPrompt, StreamEvent, AgentMessage, AgentProvider, ToolDefinition, ToolExecutor, ToolEvent, McpBridge, AgentLoopOptions, AgentLoopResult, StreamOptions, SpawnFn, ClaudeCliProviderOptions, CliSessionOptions, Logger, } from './types.js';
|
|
8
|
+
export { joinSplitPrompt } from './types.js';
|
|
8
9
|
export { runAgentLoop } from './agent-loop.js';
|
|
9
10
|
export { AnthropicProvider, createAnthropicProvider } from './providers/anthropic.js';
|
|
10
11
|
export type { AnthropicProviderOptions } from './providers/anthropic.js';
|
package/dist/agent/index.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Provider-agnostic agent loop with MCP bridge for tool execution.
|
|
5
5
|
* Built-in providers: Anthropic API, Claude CLI, OpenAI-compatible (GPT-4o, Groq, Ollama, etc).
|
|
6
6
|
*/
|
|
7
|
+
// Prompt utilities
|
|
8
|
+
export { joinSplitPrompt } from './types.js';
|
|
7
9
|
// Agent loop
|
|
8
10
|
export { runAgentLoop } from './agent-loop.js';
|
|
9
11
|
// Providers
|
|
@@ -4,6 +4,19 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Adapted from pack-weaver's streamAnthropicWithTools.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Convert a SplitPrompt to Anthropic's system content block array.
|
|
9
|
+
* The prefix gets cache_control for prompt caching; the suffix does not.
|
|
10
|
+
*/
|
|
11
|
+
function buildSystemBlocks(prompt) {
|
|
12
|
+
const blocks = [
|
|
13
|
+
{ type: 'text', text: prompt.prefix, cache_control: { type: 'ephemeral' } },
|
|
14
|
+
];
|
|
15
|
+
if (prompt.suffix) {
|
|
16
|
+
blocks.push({ type: 'text', text: prompt.suffix });
|
|
17
|
+
}
|
|
18
|
+
return blocks;
|
|
19
|
+
}
|
|
7
20
|
export class AnthropicProvider {
|
|
8
21
|
apiKey;
|
|
9
22
|
model;
|
|
@@ -49,7 +62,7 @@ export class AnthropicProvider {
|
|
|
49
62
|
model,
|
|
50
63
|
max_tokens: maxTokens,
|
|
51
64
|
stream: true,
|
|
52
|
-
...(options?.systemPrompt ? { system: options.systemPrompt } : {}),
|
|
65
|
+
...(options?.systemPrompt ? { system: buildSystemBlocks(options.systemPrompt) } : {}),
|
|
53
66
|
messages: apiMessages,
|
|
54
67
|
...(apiTools.length > 0 ? { tools: apiTools } : {}),
|
|
55
68
|
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Adapted from platform's streamClaudeCliChat. Platform-specific dependencies
|
|
6
6
|
* (spawnSandboxed, getBinPath, config) are replaced with injectable options.
|
|
7
7
|
*/
|
|
8
|
-
import type
|
|
8
|
+
import { type AgentProvider, type AgentMessage, type ToolDefinition, type StreamEvent, type StreamOptions, type ClaudeCliProviderOptions } from '../types.js';
|
|
9
9
|
export declare class ClaudeCliProvider implements AgentProvider {
|
|
10
10
|
private binPath;
|
|
11
11
|
private cwd;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* (spawnSandboxed, getBinPath, config) are replaced with injectable options.
|
|
7
7
|
*/
|
|
8
8
|
import { spawn as nodeSpawn } from 'node:child_process';
|
|
9
|
+
import { joinSplitPrompt, } from '../types.js';
|
|
9
10
|
import { StreamJsonParser } from '../streaming.js';
|
|
10
11
|
import { createMcpBridge } from '../mcp-bridge.js';
|
|
11
12
|
export class ClaudeCliProvider {
|
|
@@ -31,7 +32,9 @@ export class ClaudeCliProvider {
|
|
|
31
32
|
const model = options?.model ?? this.model;
|
|
32
33
|
// Format messages into a single prompt for -p mode
|
|
33
34
|
const prompt = formatPrompt(messages);
|
|
34
|
-
const systemPrompt = options?.systemPrompt
|
|
35
|
+
const systemPrompt = options?.systemPrompt
|
|
36
|
+
? joinSplitPrompt(options.systemPrompt)
|
|
37
|
+
: undefined;
|
|
35
38
|
// Set up MCP bridge for tool access if tools are provided and no config given
|
|
36
39
|
let bridge = null;
|
|
37
40
|
let mcpConfigPath = this.mcpConfigPath;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* No SDK dependency. Uses only Node.js native fetch + SSE parsing.
|
|
6
6
|
* Converts OpenAI's delta format to the canonical StreamEvent union.
|
|
7
7
|
*/
|
|
8
|
-
import type
|
|
8
|
+
import { type AgentProvider, type AgentMessage, type ToolDefinition, type StreamEvent, type StreamOptions } from '../types.js';
|
|
9
9
|
export interface OpenAICompatProviderOptions {
|
|
10
10
|
apiKey: string;
|
|
11
11
|
model?: string;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* No SDK dependency. Uses only Node.js native fetch + SSE parsing.
|
|
6
6
|
* Converts OpenAI's delta format to the canonical StreamEvent union.
|
|
7
7
|
*/
|
|
8
|
+
import { joinSplitPrompt } from '../types.js';
|
|
8
9
|
export class OpenAICompatProvider {
|
|
9
10
|
apiKey;
|
|
10
11
|
model;
|
|
@@ -38,7 +39,7 @@ export class OpenAICompatProvider {
|
|
|
38
39
|
const body = {
|
|
39
40
|
model,
|
|
40
41
|
messages: [
|
|
41
|
-
...(options?.systemPrompt ? [{ role: 'system', content: options.systemPrompt }] : []),
|
|
42
|
+
...(options?.systemPrompt ? [{ role: 'system', content: joinSplitPrompt(options.systemPrompt) }] : []),
|
|
42
43
|
...apiMessages,
|
|
43
44
|
],
|
|
44
45
|
max_tokens: maxTokens,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Uses the platform's AI credits, no local API key needed.
|
|
4
4
|
* Connects to POST /ai-chat/stream and parses SSE events.
|
|
5
5
|
*/
|
|
6
|
-
import type
|
|
6
|
+
import { type AgentProvider, type AgentMessage, type ToolDefinition, type StreamEvent, type StreamOptions } from '../types.js';
|
|
7
7
|
export interface PlatformProviderOptions {
|
|
8
8
|
/** JWT token or API key for platform auth */
|
|
9
9
|
token: string;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Uses the platform's AI credits, no local API key needed.
|
|
4
4
|
* Connects to POST /ai-chat/stream and parses SSE events.
|
|
5
5
|
*/
|
|
6
|
+
import { joinSplitPrompt } from '../types.js';
|
|
6
7
|
export class PlatformProvider {
|
|
7
8
|
token;
|
|
8
9
|
baseUrl;
|
|
@@ -37,7 +38,8 @@ export class PlatformProvider {
|
|
|
37
38
|
const body = { message };
|
|
38
39
|
if (options?.systemPrompt) {
|
|
39
40
|
// Platform doesn't accept system prompt directly via API — embed in message
|
|
40
|
-
|
|
41
|
+
const systemStr = joinSplitPrompt(options.systemPrompt);
|
|
42
|
+
body.message = `[System context: ${systemStr.slice(0, 2000)}]\n\n${message}`;
|
|
41
43
|
}
|
|
42
44
|
const response = await fetch(`${this.baseUrl}/ai-chat/stream`, {
|
|
43
45
|
method: 'POST',
|
package/dist/agent/types.d.ts
CHANGED
|
@@ -65,8 +65,25 @@ export interface ToolEvent {
|
|
|
65
65
|
result?: string;
|
|
66
66
|
isError?: boolean;
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Split system prompt for Anthropic API cache optimization.
|
|
70
|
+
*
|
|
71
|
+
* The prefix (stable FW knowledge) is cached across calls via cache_control.
|
|
72
|
+
* The suffix (per-task context) varies per call but rides on the cached prefix.
|
|
73
|
+
*
|
|
74
|
+
* Providers that support structured system blocks (Anthropic) use both parts.
|
|
75
|
+
* Providers that only accept strings (CLI, OpenAI, platform) concatenate them.
|
|
76
|
+
*/
|
|
77
|
+
export interface SplitPrompt {
|
|
78
|
+
/** Stable prefix — identical across calls. Cacheable. */
|
|
79
|
+
prefix: string;
|
|
80
|
+
/** Dynamic suffix — varies per task/call. Not cached. */
|
|
81
|
+
suffix: string;
|
|
82
|
+
}
|
|
83
|
+
/** Convert a SplitPrompt to a single string (for providers that don't support blocks). */
|
|
84
|
+
export declare function joinSplitPrompt(prompt: SplitPrompt): string;
|
|
68
85
|
export interface StreamOptions {
|
|
69
|
-
systemPrompt?:
|
|
86
|
+
systemPrompt?: SplitPrompt;
|
|
70
87
|
model?: string;
|
|
71
88
|
maxTokens?: number;
|
|
72
89
|
signal?: AbortSignal;
|
|
@@ -89,7 +106,7 @@ export interface McpBridge {
|
|
|
89
106
|
cleanup: () => void;
|
|
90
107
|
}
|
|
91
108
|
export interface AgentLoopOptions {
|
|
92
|
-
systemPrompt?:
|
|
109
|
+
systemPrompt?: SplitPrompt;
|
|
93
110
|
maxIterations?: number;
|
|
94
111
|
maxTokens?: number;
|
|
95
112
|
model?: string;
|
|
@@ -145,6 +162,8 @@ export interface CliSessionOptions {
|
|
|
145
162
|
model: string;
|
|
146
163
|
/** Pre-configured MCP config path. */
|
|
147
164
|
mcpConfigPath?: string;
|
|
165
|
+
/** Disable specific built-in tools (e.g. ['Read', 'Edit', 'Write', 'Bash'] to force MCP tools). */
|
|
166
|
+
disallowedTools?: string[];
|
|
148
167
|
/** Custom spawn function. Defaults to child_process.spawn. */
|
|
149
168
|
spawnFn?: SpawnFn;
|
|
150
169
|
/** Idle timeout in milliseconds. Defaults to 600000 (10 minutes). */
|
package/dist/agent/types.js
CHANGED
|
@@ -3,5 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* All types are pure — no runtime imports, no side effects.
|
|
5
5
|
*/
|
|
6
|
-
|
|
6
|
+
/** Convert a SplitPrompt to a single string (for providers that don't support blocks). */
|
|
7
|
+
export function joinSplitPrompt(prompt) {
|
|
8
|
+
if (!prompt.suffix)
|
|
9
|
+
return prompt.prefix;
|
|
10
|
+
return prompt.prefix + '\n\n' + prompt.suffix;
|
|
11
|
+
}
|
|
7
12
|
//# sourceMappingURL=types.js.map
|
package/dist/cli/flow-weaver.mjs
CHANGED
|
@@ -9886,7 +9886,7 @@ var VERSION;
|
|
|
9886
9886
|
var init_generated_version = __esm({
|
|
9887
9887
|
"src/generated-version.ts"() {
|
|
9888
9888
|
"use strict";
|
|
9889
|
-
VERSION = "0.24.
|
|
9889
|
+
VERSION = "0.24.2";
|
|
9890
9890
|
}
|
|
9891
9891
|
});
|
|
9892
9892
|
|
|
@@ -36053,8 +36053,10 @@ var init_parser2 = __esm({
|
|
|
36053
36053
|
}
|
|
36054
36054
|
try {
|
|
36055
36055
|
let nodeTypes;
|
|
36056
|
-
|
|
36057
|
-
|
|
36056
|
+
const importStats = fs5.statSync(importedFilePath);
|
|
36057
|
+
const cached2 = this.importCache.get(importedFilePath);
|
|
36058
|
+
if (cached2 && cached2.mtime === importStats.mtimeMs) {
|
|
36059
|
+
nodeTypes = cached2.nodeTypes;
|
|
36058
36060
|
} else {
|
|
36059
36061
|
this.importStack.add(importedFilePath);
|
|
36060
36062
|
try {
|
|
@@ -36078,7 +36080,7 @@ var init_parser2 = __esm({
|
|
|
36078
36080
|
const inferredFromImport = this.inferAllUnannotatedFunctions(importedFile, nodeTypes);
|
|
36079
36081
|
nodeTypes.push(...inferredFromImport);
|
|
36080
36082
|
this.project.removeSourceFile(importedFile);
|
|
36081
|
-
this.importCache.set(importedFilePath, nodeTypes);
|
|
36083
|
+
this.importCache.set(importedFilePath, { mtime: importStats.mtimeMs, nodeTypes });
|
|
36082
36084
|
} finally {
|
|
36083
36085
|
this.importStack.delete(importedFilePath);
|
|
36084
36086
|
}
|
|
@@ -36119,8 +36121,21 @@ var init_parser2 = __esm({
|
|
|
36119
36121
|
const importedNames = /* @__PURE__ */ new Set();
|
|
36120
36122
|
namedImports.forEach((ni) => importedNames.add(ni.getName()));
|
|
36121
36123
|
const cacheKey = `npm:${moduleSpecifier}`;
|
|
36122
|
-
|
|
36123
|
-
|
|
36124
|
+
const npmCached = this.importCache.get(cacheKey);
|
|
36125
|
+
if (npmCached) {
|
|
36126
|
+
const currentDir2 = path6.dirname(currentFilePath);
|
|
36127
|
+
const resolvedDts = resolvePackageTypesPath(moduleSpecifier, currentDir2);
|
|
36128
|
+
if (resolvedDts) {
|
|
36129
|
+
try {
|
|
36130
|
+
const dtsStats = fs5.statSync(resolvedDts);
|
|
36131
|
+
if (npmCached.mtime === dtsStats.mtimeMs) {
|
|
36132
|
+
return npmCached.nodeTypes.filter((nt) => importedNames.has(nt.functionName));
|
|
36133
|
+
}
|
|
36134
|
+
} catch {
|
|
36135
|
+
}
|
|
36136
|
+
} else {
|
|
36137
|
+
return npmCached.nodeTypes.filter((nt) => importedNames.has(nt.functionName));
|
|
36138
|
+
}
|
|
36124
36139
|
}
|
|
36125
36140
|
const currentDir = path6.dirname(currentFilePath);
|
|
36126
36141
|
const dtsPath = resolvePackageTypesPath(moduleSpecifier, currentDir);
|
|
@@ -36146,7 +36161,8 @@ var init_parser2 = __esm({
|
|
|
36146
36161
|
allNodeTypes.push(nodeType);
|
|
36147
36162
|
}
|
|
36148
36163
|
this.project.removeSourceFile(dtsFile);
|
|
36149
|
-
|
|
36164
|
+
const dtsMtime = fs5.statSync(dtsPath).mtimeMs;
|
|
36165
|
+
this.importCache.set(cacheKey, { mtime: dtsMtime, nodeTypes: allNodeTypes });
|
|
36150
36166
|
return allNodeTypes.filter((nt) => importedNames.has(nt.functionName));
|
|
36151
36167
|
} catch {
|
|
36152
36168
|
return [];
|
|
@@ -36215,7 +36231,7 @@ var init_parser2 = __esm({
|
|
|
36215
36231
|
const cacheKey = `npm:${imp.importSource}`;
|
|
36216
36232
|
if (this.importCache.has(cacheKey)) {
|
|
36217
36233
|
const cached2 = this.importCache.get(cacheKey);
|
|
36218
|
-
const found = cached2.find((nt) => nt.functionName === imp.functionName);
|
|
36234
|
+
const found = cached2.nodeTypes.find((nt) => nt.functionName === imp.functionName);
|
|
36219
36235
|
if (found) {
|
|
36220
36236
|
return { ...found, name: imp.name, importSource: imp.importSource };
|
|
36221
36237
|
}
|
|
@@ -36245,7 +36261,8 @@ var init_parser2 = __esm({
|
|
|
36245
36261
|
allNodeTypes.push(nodeType);
|
|
36246
36262
|
}
|
|
36247
36263
|
this.project.removeSourceFile(dtsFile);
|
|
36248
|
-
|
|
36264
|
+
const dtsMtime2 = fs5.statSync(dtsPath).mtimeMs;
|
|
36265
|
+
this.importCache.set(cacheKey, { mtime: dtsMtime2, nodeTypes: allNodeTypes });
|
|
36249
36266
|
const found = allNodeTypes.find((nt) => nt.functionName === imp.functionName);
|
|
36250
36267
|
if (found) {
|
|
36251
36268
|
return { ...found, name: imp.name, importSource: imp.importSource };
|
|
@@ -91095,8 +91112,8 @@ var init_debug_session = __esm({
|
|
|
91095
91112
|
import * as path36 from "path";
|
|
91096
91113
|
import * as fs31 from "fs";
|
|
91097
91114
|
async function getExecutionOrder(filePath, workflowName) {
|
|
91098
|
-
const
|
|
91099
|
-
const parsed = await parseWorkflow(
|
|
91115
|
+
const resolvedPath = path36.resolve(filePath);
|
|
91116
|
+
const parsed = await parseWorkflow(resolvedPath, { workflowName });
|
|
91100
91117
|
if (parsed.errors.length > 0) {
|
|
91101
91118
|
throw new Error(`Failed to parse workflow: ${parsed.errors.join(", ")}`);
|
|
91102
91119
|
}
|
|
@@ -95943,7 +95960,7 @@ function parseIntStrict(value) {
|
|
|
95943
95960
|
// src/cli/index.ts
|
|
95944
95961
|
init_logger();
|
|
95945
95962
|
init_error_utils();
|
|
95946
|
-
var version2 = true ? "0.24.
|
|
95963
|
+
var version2 = true ? "0.24.2" : "0.0.0-dev";
|
|
95947
95964
|
var program2 = new Command();
|
|
95948
95965
|
program2.name("fw").description("Flow Weaver Annotations - Compile and validate workflow files").option("-v, --version", "Output the current version").option("--no-color", "Disable colors").option("--color", "Force colors").on("option:version", () => {
|
|
95949
95966
|
logger.banner(version2);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.24.
|
|
1
|
+
export declare const VERSION = "0.24.2";
|
|
2
2
|
//# sourceMappingURL=generated-version.d.ts.map
|
package/dist/mcp/tools-debug.js
CHANGED
|
@@ -13,8 +13,8 @@ import { makeToolResult, makeErrorResult } from './response-utils.js';
|
|
|
13
13
|
* Helper: get execution order for a workflow file by parsing its annotations.
|
|
14
14
|
*/
|
|
15
15
|
async function getExecutionOrder(filePath, workflowName) {
|
|
16
|
-
const
|
|
17
|
-
const parsed = await parseWorkflow(
|
|
16
|
+
const resolvedPath = path.resolve(filePath);
|
|
17
|
+
const parsed = await parseWorkflow(resolvedPath, { workflowName });
|
|
18
18
|
if (parsed.errors.length > 0) {
|
|
19
19
|
throw new Error(`Failed to parse workflow: ${parsed.errors.join(', ')}`);
|
|
20
20
|
}
|
package/dist/parser.js
CHANGED
|
@@ -399,10 +399,12 @@ export class AnnotationParser {
|
|
|
399
399
|
throw new Error(`Circular dependency detected:\n ${cycle.join('\n -> ')}`);
|
|
400
400
|
}
|
|
401
401
|
try {
|
|
402
|
-
// Check cache first
|
|
402
|
+
// Check cache first — validate mtime to detect file changes
|
|
403
403
|
let nodeTypes;
|
|
404
|
-
|
|
405
|
-
|
|
404
|
+
const importStats = fs.statSync(importedFilePath);
|
|
405
|
+
const cached = this.importCache.get(importedFilePath);
|
|
406
|
+
if (cached && cached.mtime === importStats.mtimeMs) {
|
|
407
|
+
nodeTypes = cached.nodeTypes;
|
|
406
408
|
}
|
|
407
409
|
else {
|
|
408
410
|
// Add to import stack for circular dependency detection
|
|
@@ -428,8 +430,8 @@ export class AnnotationParser {
|
|
|
428
430
|
nodeTypes.push(...inferredFromImport);
|
|
429
431
|
// Clean up imported source file to prevent Project bloat
|
|
430
432
|
this.project.removeSourceFile(importedFile);
|
|
431
|
-
// Cache the parsed node types
|
|
432
|
-
this.importCache.set(importedFilePath, nodeTypes);
|
|
433
|
+
// Cache the parsed node types with mtime for invalidation
|
|
434
|
+
this.importCache.set(importedFilePath, { mtime: importStats.mtimeMs, nodeTypes });
|
|
433
435
|
}
|
|
434
436
|
finally {
|
|
435
437
|
// Remove from stack after processing
|
|
@@ -477,10 +479,25 @@ export class AnnotationParser {
|
|
|
477
479
|
return [];
|
|
478
480
|
const importedNames = new Set();
|
|
479
481
|
namedImports.forEach((ni) => importedNames.add(ni.getName()));
|
|
480
|
-
// Check cache
|
|
482
|
+
// Check cache (npm imports use package path mtime for invalidation)
|
|
481
483
|
const cacheKey = `npm:${moduleSpecifier}`;
|
|
482
|
-
|
|
483
|
-
|
|
484
|
+
const npmCached = this.importCache.get(cacheKey);
|
|
485
|
+
if (npmCached) {
|
|
486
|
+
// For npm packages, check mtime of the resolved .d.ts file
|
|
487
|
+
const currentDir = path.dirname(currentFilePath);
|
|
488
|
+
const resolvedDts = resolvePackageTypesPath(moduleSpecifier, currentDir);
|
|
489
|
+
if (resolvedDts) {
|
|
490
|
+
try {
|
|
491
|
+
const dtsStats = fs.statSync(resolvedDts);
|
|
492
|
+
if (npmCached.mtime === dtsStats.mtimeMs) {
|
|
493
|
+
return npmCached.nodeTypes.filter((nt) => importedNames.has(nt.functionName));
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch { /* file gone — re-parse */ }
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
return npmCached.nodeTypes.filter((nt) => importedNames.has(nt.functionName));
|
|
500
|
+
}
|
|
484
501
|
}
|
|
485
502
|
// Resolve .d.ts path
|
|
486
503
|
const currentDir = path.dirname(currentFilePath);
|
|
@@ -509,8 +526,9 @@ export class AnnotationParser {
|
|
|
509
526
|
}
|
|
510
527
|
// Clean up the temporary source file
|
|
511
528
|
this.project.removeSourceFile(dtsFile);
|
|
512
|
-
// Cache all node types from this package
|
|
513
|
-
|
|
529
|
+
// Cache all node types from this package (with mtime of the .d.ts file)
|
|
530
|
+
const dtsMtime = fs.statSync(dtsPath).mtimeMs;
|
|
531
|
+
this.importCache.set(cacheKey, { mtime: dtsMtime, nodeTypes: allNodeTypes });
|
|
514
532
|
// Return only the ones in the import statement
|
|
515
533
|
return allNodeTypes.filter((nt) => importedNames.has(nt.functionName));
|
|
516
534
|
}
|
|
@@ -596,7 +614,7 @@ export class AnnotationParser {
|
|
|
596
614
|
const cacheKey = `npm:${imp.importSource}`;
|
|
597
615
|
if (this.importCache.has(cacheKey)) {
|
|
598
616
|
const cached = this.importCache.get(cacheKey);
|
|
599
|
-
const found = cached.find((nt) => nt.functionName === imp.functionName);
|
|
617
|
+
const found = cached.nodeTypes.find((nt) => nt.functionName === imp.functionName);
|
|
600
618
|
if (found) {
|
|
601
619
|
// Return a copy with the correct name from @fwImport
|
|
602
620
|
return { ...found, name: imp.name, importSource: imp.importSource };
|
|
@@ -627,8 +645,9 @@ export class AnnotationParser {
|
|
|
627
645
|
allNodeTypes.push(nodeType);
|
|
628
646
|
}
|
|
629
647
|
this.project.removeSourceFile(dtsFile);
|
|
630
|
-
// Cache all node types from this package
|
|
631
|
-
|
|
648
|
+
// Cache all node types from this package (with mtime of the .d.ts file)
|
|
649
|
+
const dtsMtime2 = fs.statSync(dtsPath).mtimeMs;
|
|
650
|
+
this.importCache.set(cacheKey, { mtime: dtsMtime2, nodeTypes: allNodeTypes });
|
|
632
651
|
// Find the specific function we need
|
|
633
652
|
const found = allNodeTypes.find((nt) => nt.functionName === imp.functionName);
|
|
634
653
|
if (found) {
|
package/package.json
CHANGED