mlld
Version:
mlld: llm scripting language
902 lines (897 loc) • 29.4 kB
JavaScript
import { ResolverError, MlldFileNotFoundError } from './chunk-V5XE5YB5.mjs';
import { __name, __publicField } from './chunk-NJQT543K.mjs';
import * as path2 from 'path';
var _PathMatcher = class _PathMatcher {
constructor(fileSystem) {
__publicField(this, "fileSystem");
__publicField(this, "directoryCache", /* @__PURE__ */ new Map());
__publicField(this, "CACHE_TTL", 5e3);
this.fileSystem = fileSystem;
}
/**
* Find a matching path with fuzzy matching support
*/
async findMatch(targetPath, basePath, config, maxDepth) {
const cfg = {
..._PathMatcher.DEFAULT_CONFIG,
...config
};
if (targetPath.includes("..")) {
return {
exact: false,
confidence: 0
};
}
if (!cfg.enabled) {
const fullPath = path2.join(basePath, targetPath);
const exists = await this.fileSystem.exists(fullPath);
return {
path: exists ? fullPath : void 0,
exact: true,
confidence: exists ? 1 : 0
};
}
const cleanPath = targetPath.startsWith("./") ? targetPath.substring(2) : targetPath;
const segments = cleanPath.split("/").filter((s) => s.length > 0);
if (segments.length > 0 && maxDepth !== void 0) {
const depth = Math.max(0, segments.length - 1);
if (depth >= maxDepth) {
return {
exact: false,
confidence: 0
};
}
}
const exactPath = path2.join(basePath, targetPath);
if (await this.fileSystem.exists(exactPath)) {
return {
path: exactPath,
exact: true,
confidence: 1
};
}
if (process.env.DEBUG_FUZZY) {
console.log(`No exact match for ${exactPath}, trying fuzzy...`);
}
if (segments.length === 0) {
return {
exact: false,
confidence: 0
};
}
try {
const result = await this.matchSegments(segments, basePath, cfg);
if (result.candidates && result.candidates.length > 1) {
const topConfidence = result.candidates[0].confidence;
const equalMatches = result.candidates.filter((c) => c.confidence === topConfidence);
if (equalMatches.length > 1) {
return {
exact: false,
confidence: 0,
candidates: equalMatches
};
}
}
return result;
} catch (error) {
return {
exact: false,
confidence: 0
};
}
}
/**
* Clear the directory cache
*/
clearCache() {
this.directoryCache.clear();
}
/**
* Recursively match path segments with fuzzy matching
*/
async matchSegments(segments, currentPath, config, depth = 0) {
if (process.env.DEBUG_FUZZY) {
console.log(`matchSegments: segments=${segments.join("/")}, currentPath=${currentPath}, depth=${depth}`);
}
if (segments.length === 0) {
return {
path: currentPath,
exact: false,
confidence: 1
};
}
const [currentSegment, ...remainingSegments] = segments;
const entries = await this.getCachedDirectoryEntries(currentPath);
if (!entries) {
if (process.env.DEBUG_FUZZY) {
console.log(`No entries found in directory: ${currentPath}`);
}
return {
exact: false,
confidence: 0
};
}
if (process.env.DEBUG_FUZZY) {
console.log(`Directory entries: ${entries.join(", ")}`);
}
const matches = await this.findSegmentMatches(currentSegment, entries, currentPath, config);
if (matches.length === 0) {
const suggestions = this.generateSuggestions(currentSegment, entries, config);
if (process.env.DEBUG_FUZZY) {
console.log(`No matches for segment '${currentSegment}', generated ${suggestions.length} suggestions`);
}
return {
exact: false,
confidence: 0,
suggestions: suggestions.slice(0, 3)
// Top 3 suggestions
};
}
if (remainingSegments.length === 0) {
matches.sort((a, b) => {
if (a.confidence !== b.confidence) {
return b.confidence - a.confidence;
}
const typePriority = {
exact: 4,
case: 3,
whitespace: 2,
fuzzy: 1
};
return typePriority[b.matchType] - typePriority[a.matchType];
});
return {
path: matches[0].path,
exact: matches[0].matchType === "exact",
confidence: matches[0].confidence,
candidates: matches.length > 1 ? matches : void 0
};
}
const results = [];
for (const match of matches) {
const subResult = await this.matchSegments(remainingSegments, match.path, config, depth + 1);
if (subResult.path) {
results.push({
...subResult,
confidence: match.confidence * subResult.confidence
});
}
}
if (results.length === 0) {
return {
exact: false,
confidence: 0
};
}
results.sort((a, b) => b.confidence - a.confidence);
const topConfidence = results[0].confidence;
const equalResults = results.filter((r) => r.confidence === topConfidence);
if (equalResults.length > 1) {
return {
exact: false,
confidence: 0,
candidates: equalResults.map((r) => ({
path: r.path,
confidence: r.confidence,
matchType: "fuzzy"
}))
};
}
return results[0];
}
/**
* Find matching entries for a path segment
*/
async findSegmentMatches(segment, entries, basePath, config) {
const matches = [];
for (const entry of entries) {
const entryPath = path2.join(basePath, entry);
const stats = await this.fileSystem.stat(entryPath);
const entryParsed = path2.parse(entry);
const segmentParsed = path2.parse(segment);
const entryName = stats.isFile() ? entryParsed.name : entry;
const segmentName = segmentParsed.name;
const entryExt = entryParsed.ext;
const segmentExt = segmentParsed.ext;
if (segmentExt && stats.isFile() && entryExt.toLowerCase() !== segmentExt.toLowerCase()) {
continue;
}
if (entry === segment || stats.isFile() && entryName === segmentName) {
matches.push({
path: entryPath,
confidence: 1,
matchType: "exact"
});
continue;
}
if (config.caseInsensitive) {
if (entry.toLowerCase() === segment.toLowerCase() || stats.isFile() && entryName.toLowerCase() === segmentName.toLowerCase()) {
matches.push({
path: entryPath,
confidence: 0.95,
matchType: "case"
});
continue;
}
}
if (config.normalizeWhitespace) {
const normalizedEntry = this.normalizeWhitespace(entryName);
const normalizedSegment = this.normalizeWhitespace(segmentName);
if (normalizedEntry === normalizedSegment) {
const confidence = this.calculateWhitespaceConfidence(entryName, segmentName);
matches.push({
path: entryPath,
confidence,
matchType: "whitespace"
});
continue;
}
if (config.caseInsensitive && normalizedEntry.toLowerCase() === normalizedSegment.toLowerCase()) {
const confidence = this.calculateWhitespaceConfidence(entryName, segmentName) * 0.95;
matches.push({
path: entryPath,
confidence,
matchType: "fuzzy"
});
}
}
}
return matches;
}
/**
* Normalize whitespace in a string (spaces, dashes, underscores)
*/
normalizeWhitespace(str) {
return str.replace(/[\s\-_]+/g, "-");
}
/**
* Calculate confidence based on whitespace transformation
*/
calculateWhitespaceConfidence(original, target) {
const originalChars = this.getWhitespaceChars(original);
const targetChars = this.getWhitespaceChars(target);
if (originalChars === targetChars) return 0.9;
const charPriority = {
"-": 3,
"_": 2,
" ": 1,
"mixed": 0
};
const originalPriority = charPriority[originalChars] || 0;
const targetPriority = charPriority[targetChars] || 0;
const priorityDiff = Math.abs(originalPriority - targetPriority);
return 0.9 - priorityDiff * 0.1;
}
/**
* Get the predominant whitespace character type
*/
getWhitespaceChars(str) {
const dashes = (str.match(/-/g) || []).length;
const underscores = (str.match(/_/g) || []).length;
const spaces = (str.match(/ /g) || []).length;
if (dashes > 0 && underscores === 0 && spaces === 0) return "-";
if (underscores > 0 && dashes === 0 && spaces === 0) return "_";
if (spaces > 0 && dashes === 0 && underscores === 0) return " ";
return "mixed";
}
/**
* Generate suggestions for a failed match
*/
generateSuggestions(segment, entries, config) {
const suggestions = [];
if (process.env.DEBUG_FUZZY) {
console.log(`Generating suggestions for '${segment}' from entries:`, entries);
}
for (const entry of entries) {
const score = this.calculateSimilarity(segment, entry, config);
if (process.env.DEBUG_FUZZY) {
console.log(` ${entry}: score=${score}, threshold=${config.suggestionThreshold}`);
}
if (score >= config.suggestionThreshold) {
suggestions.push({
entry,
score
});
}
}
return suggestions.sort((a, b) => b.score - a.score).map((s) => s.entry);
}
/**
* Calculate similarity score between two strings
*/
calculateSimilarity(str1, str2, config) {
let s1 = str1;
let s2 = str2;
if (config.normalizeWhitespace) {
s1 = this.normalizeWhitespace(s1);
s2 = this.normalizeWhitespace(s2);
}
if (config.caseInsensitive) {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
}
const maxLen = Math.max(s1.length, s2.length);
if (maxLen === 0) return 1;
const distance = this.levenshteinDistance(s1, s2);
return 1 - distance / maxLen;
}
/**
* Calculate Levenshtein distance between two strings
*/
levenshteinDistance(str1, str2) {
const m = str1.length;
const n = str2.length;
const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
for (let i = 0; i <= m; i++) dp[i][0] = i;
for (let j = 0; j <= n; j++) dp[0][j] = j;
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (str1[i - 1] === str2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = 1 + Math.min(
dp[i - 1][j],
dp[i][j - 1],
dp[i - 1][j - 1]
// substitution
);
}
}
}
return dp[m][n];
}
/**
* Get cached directory entries or read from filesystem
*/
async getCachedDirectoryEntries(dirPath) {
const now = Date.now();
const cached = this.directoryCache.get(dirPath);
if (cached && now - cached.timestamp < this.CACHE_TTL) {
return cached.entries;
}
try {
const entries = await this.fileSystem.readdir(dirPath);
this.directoryCache.set(dirPath, {
entries,
timestamp: now
});
return entries;
} catch (error) {
return null;
}
}
};
__name(_PathMatcher, "PathMatcher");
__publicField(_PathMatcher, "DEFAULT_CONFIG", {
enabled: true,
caseInsensitive: true,
normalizeWhitespace: true,
suggestionThreshold: 0.7
});
var PathMatcher = _PathMatcher;
// core/resolvers/LocalResolver.ts
var _LocalResolver = class _LocalResolver {
constructor(fileSystem) {
__publicField(this, "fileSystem");
__publicField(this, "name", "LOCAL");
__publicField(this, "description", "Resolves modules from local filesystem paths");
__publicField(this, "type", "io");
__publicField(this, "capabilities", {
io: {
read: true,
write: false,
list: true
},
contexts: {
import: true,
path: true,
output: false
},
supportedContentTypes: [
"module",
"data",
"text"
],
defaultContentType: "text",
priority: 20,
cache: {
strategy: "none"
}
// Local files don't need caching
});
__publicField(this, "pathMatcher");
this.fileSystem = fileSystem;
this.pathMatcher = new PathMatcher(fileSystem);
}
/**
* Check if this resolver can handle the reference
* Always returns true since prefix matching is done by ResolverManager
*/
canResolve(ref, config) {
return !!config?.basePath || path2.isAbsolute(ref);
}
/**
* Resolve a reference to local file content
*/
async resolve(ref, config) {
if (!config?.basePath && !path2.isAbsolute(ref)) {
throw new ResolverError("LocalResolver requires basePath in configuration for relative paths", {
resolverName: "LocalResolver",
operation: "resolve"
});
}
const relativePath = this.extractRelativePath(ref, config);
if (config?.basePath) {
const mldMdPath = path2.join(config.basePath, relativePath + ".mld.md");
if (await this.fileSystem.exists(mldMdPath)) {
const content = await this.fileSystem.readFile(mldMdPath, "utf8");
const metadata = {
source: `file://${mldMdPath}`,
mimeType: "text/x-mlld-module",
size: Buffer.byteLength(content, "utf8"),
timestamp: /* @__PURE__ */ new Date(),
taint: []
};
return {
content,
contentType: "module",
mx: metadata,
metadata
};
}
}
if (relativePath.includes("..") || path2.isAbsolute(relativePath)) {
try {
const securePath = await this.resolveFullPath(relativePath, config);
} catch (error) {
throw error;
}
}
const fuzzyConfig = config?.fuzzyMatch !== void 0 ? config.fuzzyMatch : true;
const fuzzyEnabled = typeof fuzzyConfig === "boolean" ? fuzzyConfig : fuzzyConfig.enabled !== false;
let fullPath;
if (fuzzyEnabled && config?.basePath) {
const matchResult = await this.pathMatcher.findMatch(relativePath, config.basePath, typeof fuzzyConfig === "object" ? fuzzyConfig : void 0, config.maxDepth);
if (matchResult.candidates && matchResult.candidates.length > 1) {
const candidatePaths = matchResult.candidates.map((c) => ` - ${path2.relative(config.basePath, c.path)} (${c.matchType} match)`).join("\n");
throw new ResolverError(`Ambiguous path '${relativePath}' matches multiple files:
${candidatePaths}
Please use a more specific path.`, {
resolverName: "LocalResolver",
reference: ref,
operation: "resolve"
});
}
if (matchResult.path) {
fullPath = matchResult.path;
} else {
fullPath = await this.resolveFullPath(relativePath, config);
if (!path2.extname(fullPath)) {
const existsAsIs = await this.fileSystem.exists(fullPath);
if (!existsAsIs) {
const extensions = [
".mld.md",
".mld",
".md",
".mlld.md"
];
let foundWithExtension = false;
for (const ext of extensions) {
const pathWithExt = relativePath + ext;
const extMatchResult = await this.pathMatcher.findMatch(pathWithExt, config.basePath, typeof fuzzyConfig === "object" ? fuzzyConfig : void 0, config.maxDepth);
if (extMatchResult.path) {
fullPath = extMatchResult.path;
foundWithExtension = true;
break;
}
}
if (!foundWithExtension && matchResult.suggestions && matchResult.suggestions.length > 0) {
const suggestions = matchResult.suggestions.slice(0, 3).map((s) => ` - ${s}`).join("\n");
throw new MlldFileNotFoundError(`File not found: ${relativePath}
Did you mean:
${suggestions}`, {
details: {
filePath: relativePath,
operation: "resolve"
}
});
}
}
}
}
} else {
fullPath = await this.resolveFullPath(relativePath, config);
if (!path2.extname(fullPath)) {
const existsAsIs = await this.fileSystem.exists(fullPath);
if (!existsAsIs) {
const withMldMd = fullPath + ".mld.md";
if (await this.fileSystem.exists(withMldMd)) {
fullPath = withMldMd;
} else {
const withMld = fullPath + ".mld";
if (await this.fileSystem.exists(withMld)) {
fullPath = withMld;
} else {
const withMd = fullPath + ".md";
if (await this.fileSystem.exists(withMd)) {
fullPath = withMd;
} else {
const withLegacy = fullPath + ".mlld.md";
if (await this.fileSystem.exists(withLegacy)) {
fullPath = withLegacy;
}
}
}
}
}
}
}
if (config.allowedExtensions) {
const ext = path2.extname(fullPath).toLowerCase();
if (!config.allowedExtensions.includes(ext)) {
throw new ResolverError(`File extension '${ext}' not allowed. Allowed: ${config.allowedExtensions.join(", ")}`, {
resolverName: "LocalResolver",
reference: ref,
operation: "resolve"
});
}
}
try {
const content = await this.fileSystem.readFile(fullPath);
const stats = await this.fileSystem.stat(fullPath);
const contentType = await this.detectContentType(fullPath, content);
const metadata = {
source: `file://${fullPath}`,
timestamp: /* @__PURE__ */ new Date(),
taint: [],
size: content.length,
mimeType: this.getMimeType(fullPath)
};
return {
content,
contentType,
mx: metadata,
metadata
};
} catch (error) {
if (error.code === "ENOENT" || error.message?.includes("File not found")) {
throw new MlldFileNotFoundError(`File not found: ${relativePath}`, {
details: {
filePath: relativePath,
operation: "resolve"
}
});
}
throw ResolverError.resolutionFailed("LocalResolver", ref, error);
}
}
/**
* Write content to a local file
*/
async write(ref, content, config) {
if (!config?.basePath) {
throw new ResolverError("LocalResolver requires basePath in configuration", {
resolverName: "LocalResolver",
operation: "write"
});
}
if (config.readonly) {
throw new ResolverError("Cannot write: LocalResolver is configured as read-only", {
resolverName: "LocalResolver",
operation: "write",
reference: ref
});
}
const relativePath = this.extractRelativePath(ref, config);
const fullPath = await this.resolveFullPath(relativePath, config);
if (config.allowedExtensions) {
const ext = path2.extname(fullPath).toLowerCase();
if (!config.allowedExtensions.includes(ext)) {
throw new ResolverError(`File extension '${ext}' not allowed. Allowed: ${config.allowedExtensions.join(", ")}`, {
resolverName: "LocalResolver",
reference: ref,
operation: "resolve"
});
}
}
try {
const dir = path2.dirname(fullPath);
await this.fileSystem.mkdir(dir, {
recursive: true
});
await this.fileSystem.writeFile(fullPath, content);
} catch (error) {
throw new ResolverError(`Failed to write file: ${error.message}`, {
resolverName: "LocalResolver",
reference: ref,
operation: "write",
originalError: error
});
}
}
/**
* List files under a prefix
*/
async list(prefix, config) {
if (!config?.basePath) {
return [];
}
const relativePath = this.extractRelativePath(prefix, config);
const fuzzyConfig = config?.fuzzyMatch !== void 0 ? config.fuzzyMatch : true;
const fuzzyEnabled = typeof fuzzyConfig === "boolean" ? fuzzyConfig : fuzzyConfig.enabled !== false;
let fullPath;
if (fuzzyEnabled) {
const matchResult = await this.pathMatcher.findMatch(relativePath, config.basePath, typeof fuzzyConfig === "object" ? fuzzyConfig : void 0, config.maxDepth);
if (!matchResult.path) {
return [];
}
fullPath = matchResult.path;
} else {
fullPath = await this.resolveFullPath(relativePath, config);
}
try {
const stats = await this.fileSystem.stat(fullPath);
if (!stats.isDirectory()) {
return [];
}
const entries = await this.fileSystem.readdir(fullPath);
const results = [];
for (const entryName of entries) {
const entryPath = path2.join(fullPath, entryName);
try {
const entryStats = await this.fileSystem.stat(entryPath);
if (config.allowedExtensions && entryStats.isFile()) {
const ext = path2.extname(entryName).toLowerCase();
if (!config.allowedExtensions.includes(ext)) {
continue;
}
}
results.push({
path: path2.join(prefix, entryName),
type: entryStats.isDirectory() ? "directory" : "file",
size: 0,
lastModified: /* @__PURE__ */ new Date()
// Use current time
});
} catch (error) {
continue;
}
}
return results;
} catch (error) {
if (error.code === "ENOENT" || error.message?.includes("Path not found")) {
return [];
}
throw error;
}
}
/**
* Validate configuration
*/
validateConfig(config) {
const errors = [];
const cfg = config ?? {};
if (!cfg.basePath) {
errors.push("basePath is required");
} else if (typeof cfg.basePath !== "string") {
errors.push("basePath must be a string");
}
if (cfg.readonly !== void 0 && typeof cfg.readonly !== "boolean") {
errors.push("readonly must be a boolean");
}
if (cfg.allowedExtensions !== void 0) {
if (!Array.isArray(cfg.allowedExtensions)) {
errors.push("allowedExtensions must be an array");
} else if (!cfg.allowedExtensions.every((ext) => typeof ext === "string")) {
errors.push("allowedExtensions must contain only strings");
}
}
if (cfg.followSymlinks !== void 0 && typeof cfg.followSymlinks !== "boolean") {
errors.push("followSymlinks must be a boolean");
}
if (cfg.maxDepth !== void 0) {
if (typeof cfg.maxDepth !== "number" || cfg.maxDepth < 0) {
errors.push("maxDepth must be a non-negative number");
}
}
return errors;
}
/**
* Check access permissions
*/
async checkAccess(ref, operation, config) {
if (!config?.basePath) {
return false;
}
if (operation === "write" && config.readonly) {
return false;
}
try {
const relativePath = this.extractRelativePath(ref, config);
const mldMdPath = path2.join(config.basePath, relativePath + ".mld.md");
if (await this.fileSystem.exists(mldMdPath)) {
return true;
}
const fuzzyConfig = config?.fuzzyMatch !== void 0 ? config.fuzzyMatch : true;
const fuzzyEnabled = typeof fuzzyConfig === "boolean" ? fuzzyConfig : fuzzyConfig.enabled !== false;
if (fuzzyEnabled && operation === "read") {
const matchResult = await this.pathMatcher.findMatch(relativePath, config.basePath, typeof fuzzyConfig === "object" ? fuzzyConfig : void 0, config.maxDepth);
if (matchResult.path) {
return true;
}
const extensions = [
".mld.md",
".mld",
".md",
".mlld.md"
];
for (const ext of extensions) {
const pathWithExt = relativePath + ext;
const extMatchResult = await this.pathMatcher.findMatch(pathWithExt, config.basePath, typeof fuzzyConfig === "object" ? fuzzyConfig : void 0, config.maxDepth);
if (extMatchResult.path) {
return true;
}
}
return false;
} else {
const fullPath = await this.resolveFullPath(relativePath, config);
if (operation === "read" && !path2.extname(fullPath)) {
const existsAsIs = await this.fileSystem.exists(fullPath);
if (!existsAsIs) {
const withMldMd = fullPath + ".mld.md";
if (await this.fileSystem.exists(withMldMd)) {
return true;
}
const withMld = fullPath + ".mld";
if (await this.fileSystem.exists(withMld)) {
return true;
}
const withMd = fullPath + ".md";
if (await this.fileSystem.exists(withMd)) {
return true;
}
const withLegacy = fullPath + ".mlld.md";
if (await this.fileSystem.exists(withLegacy)) {
return true;
}
}
}
if (operation === "write") {
const dir = path2.dirname(fullPath);
const dirExists = await this.fileSystem.exists(dir);
return dirExists;
} else {
return await this.fileSystem.exists(fullPath);
}
}
} catch (error) {
return false;
}
}
/**
* Extract relative path from reference
* Assumes the ResolverManager has already matched and removed the prefix
*/
extractRelativePath(ref, config) {
const prefixMatch = ref.match(/^@[^/]+\/(.*)/);
if (prefixMatch) {
return prefixMatch[1];
}
return ref;
}
/**
* Resolve and validate the full filesystem path
*/
async resolveFullPath(relativePath, config) {
const basePath = config?.basePath || "/";
const normalizedBase = path2.resolve(basePath);
if (path2.isAbsolute(relativePath)) {
const normalizedPath = path2.resolve(relativePath);
if (normalizedPath.startsWith(normalizedBase)) {
return normalizedPath;
}
throw new ResolverError("Path traversal detected: absolute path is outside base directory", {
resolverName: "LocalResolver",
reference: relativePath,
operation: "resolve"
});
}
const cleanPath = relativePath.startsWith("/") ? relativePath.slice(1) : relativePath;
const fullPath = path2.resolve(config.basePath, cleanPath);
const normalizedFull = path2.resolve(fullPath);
if (!normalizedFull.startsWith(normalizedBase)) {
throw new ResolverError("Path traversal detected: resolved path is outside base directory", {
resolverName: "LocalResolver",
reference: relativePath,
operation: "resolve"
});
}
if (config.maxDepth !== void 0) {
const relativeParts = path2.relative(normalizedBase, normalizedFull).split(path2.sep).filter((p) => p && p !== ".");
const depth = Math.max(0, relativeParts.length - 1);
if (depth >= config.maxDepth) {
throw new ResolverError(`Path exceeds maximum depth of ${config.maxDepth}`, {
resolverName: "LocalResolver",
reference: relativePath,
operation: "resolve"
});
}
}
return normalizedFull;
}
/**
* Detect content type based on file extension and content
*/
async detectContentType(filePath, content) {
const mlldExtensions = [
".mld.md",
".mld",
".md",
".mlld.md",
".mlld"
];
const hasMLLDExtension = mlldExtensions.some((ext) => filePath.endsWith(ext));
if (hasMLLDExtension || !filePath.endsWith(".json")) {
try {
const { parse: parse2 } = await import('./parser-6HNWFG6W.mjs');
const { inferMlldMode } = await import('./mode-F33H23O4.mjs');
const mode = inferMlldMode(filePath);
const result = await parse2(content, {
mode
});
if (result.success && this.hasModuleExports(result.ast)) {
return "module";
}
} catch {
}
}
if (filePath.endsWith(".json")) {
return "data";
}
try {
JSON.parse(content);
return "data";
} catch {
}
return "text";
}
/**
* Check if AST has module exports
*/
hasModuleExports(ast) {
if (!ast || !Array.isArray(ast)) return false;
return ast.some((node) => node && node.type === "Directive" && [
"var",
"exe",
"path"
].includes(node.kind));
}
/**
* Get MIME type based on file extension
*/
getMimeType(filePath) {
const ext = path2.extname(filePath).toLowerCase();
const mimeTypes = {
".mld": "text/x-mlld",
".mlld": "text/x-mlld",
".md": "text/markdown",
".txt": "text/plain",
".json": "application/json",
".yaml": "text/yaml",
".yml": "text/yaml",
".js": "text/javascript",
".ts": "text/typescript",
".py": "text/x-python",
".sh": "text/x-shellscript",
".xml": "text/xml",
".html": "text/html",
".css": "text/css"
};
return mimeTypes[ext] || "text/plain";
}
};
__name(_LocalResolver, "LocalResolver");
var LocalResolver = _LocalResolver;
export { LocalResolver, PathMatcher };
//# sourceMappingURL=chunk-CIWUSUU2.mjs.map
//# sourceMappingURL=chunk-CIWUSUU2.mjs.map