@askdkc/dsh-lsp-server 0.1.12 → 0.1.14
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/README.md +8 -0
- package/lib/code-intelligence-DvhxPnpu.js +890 -0
- package/lib/code-intelligence-DvhxPnpu.js.map +1 -0
- package/lib/code-intelligence-contracts-CWXTofdI.js +8 -0
- package/lib/code-intelligence-contracts-CWXTofdI.js.map +1 -0
- package/lib/code-intelligence-contracts.js +3 -0
- package/lib/code-intelligence.js +5 -0
- package/lib/errors-DtyWAUi0.js.map +1 -1
- package/lib/extra-tool.js +135 -7
- package/lib/extra-tool.js.map +1 -1
- package/lib/grammars/manifest.json +40 -0
- package/lib/grammars/tree-sitter-javascript.wasm +0 -0
- package/lib/grammars/tree-sitter-tsx.wasm +0 -0
- package/lib/grammars/tree-sitter-typescript.wasm +0 -0
- package/lib/launch-ByUvgkVv.js.map +1 -1
- package/lib/parser-worker.mjs +200 -0
- package/lib/provider.js +639 -41
- package/lib/provider.js.map +1 -1
- package/lib/types/code-intelligence-contracts.d.ts +113 -0
- package/lib/types/code-intelligence.d.ts +5 -0
- package/lib/types/errors.d.ts +1 -1
- package/lib/types/extra-tool.d.ts +2 -1
- package/lib/types/provider.d.ts +4 -0
- package/lib/types/runtime/webstack-runtime.d.ts +13 -1
- package/lib/types/service.d.ts +16 -0
- package/lib/types/structure/code-intelligence.d.ts +23 -0
- package/lib/types/structure/coordinates.d.ts +14 -0
- package/lib/types/structure/engine.d.ts +49 -0
- package/lib/types/structure/protocol.d.ts +19 -0
- package/lib/types/structure/queries.d.ts +4 -0
- package/lib/types/structure/snapshots.d.ts +37 -0
- package/lib/types/structure/types.d.ts +147 -0
- package/lib/types/structure/worker-pool.d.ts +66 -0
- package/package.json +17 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launch-ByUvgkVv.js","names":["SERVER_COMMANDS: Record<ServerId, string>","DEFAULT_SERVER_MODES: Record<ServerId, ServerMode>","SERVER_IDS: ServerId[]","DEFAULT_CONFIG: ResolvedConfig","packages: Record<Exclude<ServerId, 'phpantom'>, [string, string]>"],"sources":["../src/config.ts","../src/service.ts","../src/servers/package-bin.ts","../src/servers/launch.ts"],"sourcesContent":["import { configError } from './errors.js'\n\nexport type ServerId = 'phpantom' | 'typescript' | 'svelte' | 'html' | 'css' | 'tailwind'\nexport type ServerMode = 'bundled' | 'path'\n\nexport interface ServerConfig {\n enabled?: boolean\n mode?: ServerMode\n command?: string\n args?: string[]\n env?: Record<string, string>\n initializationOptions?: unknown\n configuration?: unknown\n}\n\nexport interface Config {\n providerId?: string\n missingServerPolicy?: 'warn' | 'error'\n limits?: {\n maxDocumentBytes?: number\n maxMessageBytes?: number\n maxStderrBytes?: number\n }\n timeouts?: {\n navigationMs?: number\n primaryHoverMs?: number\n auxiliaryHoverMs?: number\n diagnosticsMs?: number\n completionMs?: number\n shutdownMs?: number\n killGraceMs?: number\n }\n servers?: Partial<Record<ServerId, ServerConfig>>\n tailwind?: {\n enabled?: boolean\n queryStrategy?: 'candidate' | 'always'\n classAttributes?: string[]\n classRegex?: Array<string | [string, string]>\n includeLanguages?: Record<string, string>\n }\n routing?: {\n bladeSuffixes?: string[]\n tailwindLanguages?: string[]\n }\n}\n\nexport interface ResolvedServerConfig {\n enabled: boolean\n mode: ServerMode\n command: string\n args: string[]\n env: Record<string, string>\n initializationOptions?: unknown\n configuration?: unknown\n}\n\nexport interface ResolvedConfig {\n providerId: string\n missingServerPolicy: 'warn' | 'error'\n limits: {\n maxDocumentBytes: number\n maxMessageBytes: number\n maxStderrBytes: number\n }\n timeouts: {\n navigationMs: number\n primaryHoverMs: number\n auxiliaryHoverMs: number\n diagnosticsMs: number\n completionMs: number\n shutdownMs: number\n killGraceMs: number\n }\n servers: Record<ServerId, ResolvedServerConfig>\n tailwind: {\n enabled: boolean\n queryStrategy: 'candidate' | 'always'\n classAttributes: string[]\n classRegex: Array<string | [string, string]>\n includeLanguages: Record<string, string>\n }\n routing: {\n bladeSuffixes: string[]\n tailwindLanguages: string[]\n }\n}\n\nconst SERVER_COMMANDS: Record<ServerId, string> = {\n phpantom: 'phpantom_lsp',\n typescript: 'typescript-language-server',\n svelte: 'svelteserver',\n html: 'vscode-html-language-server',\n css: 'vscode-css-language-server',\n tailwind: 'tailwindcss-language-server',\n}\n\nconst DEFAULT_SERVER_MODES: Record<ServerId, ServerMode> = {\n phpantom: 'path',\n typescript: 'bundled',\n svelte: 'bundled',\n html: 'bundled',\n css: 'bundled',\n tailwind: 'bundled',\n}\n\nexport const SERVER_IDS: ServerId[] = ['phpantom', 'typescript', 'svelte', 'html', 'css', 'tailwind']\nconst DEFAULT_LIMITS = { maxDocumentBytes: 1_048_576, maxMessageBytes: 8_388_608, maxStderrBytes: 65_536 }\nconst DEFAULT_TIMEOUTS = { navigationMs: 60_000, primaryHoverMs: 30_000, auxiliaryHoverMs: 8_000, diagnosticsMs: 60_000, completionMs: 15_000, shutdownMs: 5_000, killGraceMs: 1_000 }\n\nexport const DEFAULT_CONFIG: ResolvedConfig = {\n providerId: 'webstack',\n missingServerPolicy: 'warn',\n limits: { ...DEFAULT_LIMITS },\n timeouts: { ...DEFAULT_TIMEOUTS },\n servers: Object.fromEntries(SERVER_IDS.map((id) => [id, {\n enabled: true,\n mode: DEFAULT_SERVER_MODES[id],\n command: SERVER_COMMANDS[id],\n args: ['--stdio'],\n env: {},\n }])) as Record<ServerId, ResolvedServerConfig>,\n tailwind: { enabled: true, queryStrategy: 'candidate', classAttributes: ['class', 'className', 'class:list'], classRegex: [], includeLanguages: {} },\n routing: { bladeSuffixes: ['.blade.php'], tailwindLanguages: ['html', 'svelte', 'typescript', 'typescriptreact', 'javascript', 'javascriptreact'] },\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction readPositiveInteger(value: unknown, label: string, fallback: number): number {\n if (value === undefined) return fallback\n if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0 || value > 2_147_483_647) throw configError(`${label} must be a positive integer no greater than 2147483647`)\n return value\n}\n\nfunction readBoolean(value: unknown, label: string, fallback: boolean): boolean {\n if (value === undefined) return fallback\n if (typeof value !== 'boolean') throw configError(`${label} must be a boolean`)\n return value\n}\n\nfunction readString(value: unknown, label: string, fallback: string): string {\n if (value === undefined) return fallback\n if (typeof value !== 'string' || value.trim().length === 0 || value.includes('\\u0000')) throw configError(`${label} must be a non-empty string without NUL`)\n return value\n}\n\nfunction cloneJson(value: unknown, label: string): unknown {\n if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value\n if (Array.isArray(value)) return value.map((item) => cloneJson(item, label))\n if (isRecord(value)) return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneJson(item, label)]))\n throw configError(`${label} must contain JSON-compatible values`)\n}\n\nfunction resolveSuffixes(value: unknown, label: string, fallback: string[]): string[] {\n if (value === undefined) return [...fallback]\n if (!Array.isArray(value) || value.length === 0) throw configError(`${label} must be a non-empty array`)\n const normalized = value.map((suffix, index) => {\n if (typeof suffix !== 'string' || suffix.length === 0 || suffix.includes('/') || suffix.includes('\\\\')) throw configError(`${label}[${index}] is not a valid suffix`)\n return suffix.toLowerCase()\n })\n if (new Set(normalized).size !== normalized.length) throw configError(`${label} contains duplicate suffixes`)\n return normalized\n}\n\nfunction resolveStringArray(value: unknown, label: string, fallback: string[]): string[] {\n if (value === undefined) return [...fallback]\n if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) throw configError(`${label} must be an array of strings`)\n return [...value]\n}\n\nfunction resolveEnv(value: unknown, label: string): Record<string, string> {\n if (value === undefined) return {}\n if (!isRecord(value) || Object.entries(value).some(([key, item]) => key.length === 0 || typeof item !== 'string')) throw configError(`${label} must be a string map`)\n return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, item as string]))\n}\n\nfunction resolveServer(id: ServerId, value: unknown): ResolvedServerConfig {\n if (value !== undefined && !isRecord(value)) throw configError(`servers.${id} must be an object`)\n const input = value as Record<string, unknown> | undefined\n const mode = input?.mode === undefined ? DEFAULT_SERVER_MODES[id] : input.mode\n if (mode !== 'bundled' && mode !== 'path') throw configError(`servers.${id}.mode must be bundled or path`)\n if (id === 'phpantom' && mode === 'bundled') throw configError('PHPantom must use path mode')\n const command = input?.command === undefined ? SERVER_COMMANDS[id] : readString(input.command, `servers.${id}.command`, SERVER_COMMANDS[id])\n if (mode === 'path' && input?.command === undefined && id !== 'phpantom') throw configError(`servers.${id}.command is required in path mode`)\n const argsValue = input?.args\n if (argsValue !== undefined && (!Array.isArray(argsValue) || argsValue.some((item) => typeof item !== 'string'))) throw configError(`servers.${id}.args must be an array of strings`)\n return {\n enabled: readBoolean(input?.enabled, `servers.${id}.enabled`, true),\n mode,\n command,\n args: argsValue === undefined ? ['--stdio'] : [...argsValue] as string[],\n env: resolveEnv(input?.env, `servers.${id}.env`),\n ...(input?.initializationOptions === undefined ? {} : { initializationOptions: cloneJson(input.initializationOptions, `servers.${id}.initializationOptions`) }),\n ...(input?.configuration === undefined ? {} : { configuration: cloneJson(input.configuration, `servers.${id}.configuration`) }),\n }\n}\n\nexport function resolveConfig(input: unknown = {}): ResolvedConfig {\n if (!isRecord(input)) throw configError('config must be an object')\n const limits = isRecord(input.limits) ? input.limits : input.limits === undefined ? {} : (() => { throw configError('limits must be an object') })()\n const timeouts = isRecord(input.timeouts) ? input.timeouts : input.timeouts === undefined ? {} : (() => { throw configError('timeouts must be an object') })()\n const tailwind = isRecord(input.tailwind) ? input.tailwind : input.tailwind === undefined ? {} : (() => { throw configError('tailwind must be an object') })()\n const routing = isRecord(input.routing) ? input.routing : input.routing === undefined ? {} : (() => { throw configError('routing must be an object') })()\n const serverInput = isRecord(input.servers) ? input.servers : input.servers === undefined ? {} : (() => { throw configError('servers must be an object') })()\n const providerId = readString(input.providerId, 'providerId', DEFAULT_CONFIG.providerId)\n const missingServerPolicy = input.missingServerPolicy === undefined ? 'warn' : input.missingServerPolicy\n if (missingServerPolicy !== 'warn' && missingServerPolicy !== 'error') throw configError('missingServerPolicy must be warn or error')\n const classRegex = tailwind.classRegex\n if (classRegex !== undefined && (!Array.isArray(classRegex) || classRegex.some((item) => typeof item !== 'string' && (!Array.isArray(item) || item.length !== 2 || item.some((part) => typeof part !== 'string'))))) throw configError('tailwind.classRegex must contain strings or string pairs')\n const queryStrategy = tailwind.queryStrategy === undefined ? 'candidate' : tailwind.queryStrategy\n for (const expression of (classRegex ?? []) as Array<string | [string, string]>) {\n for (const pattern of Array.isArray(expression) ? expression : [expression]) {\n try { new RegExp(pattern) } catch { throw configError('tailwind.classRegex contains an invalid regular expression') }\n }\n }\n if (queryStrategy !== 'candidate' && queryStrategy !== 'always') throw configError('tailwind.queryStrategy must be candidate or always')\n const includeLanguages = resolveEnv(tailwind.includeLanguages, 'tailwind.includeLanguages')\n return {\n providerId,\n missingServerPolicy,\n limits: {\n maxDocumentBytes: readPositiveInteger(limits.maxDocumentBytes, 'limits.maxDocumentBytes', DEFAULT_LIMITS.maxDocumentBytes),\n maxMessageBytes: readPositiveInteger(limits.maxMessageBytes, 'limits.maxMessageBytes', DEFAULT_LIMITS.maxMessageBytes),\n maxStderrBytes: readPositiveInteger(limits.maxStderrBytes, 'limits.maxStderrBytes', DEFAULT_LIMITS.maxStderrBytes),\n },\n timeouts: {\n navigationMs: readPositiveInteger(timeouts.navigationMs, 'timeouts.navigationMs', DEFAULT_TIMEOUTS.navigationMs),\n primaryHoverMs: readPositiveInteger(timeouts.primaryHoverMs, 'timeouts.primaryHoverMs', DEFAULT_TIMEOUTS.primaryHoverMs),\n auxiliaryHoverMs: readPositiveInteger(timeouts.auxiliaryHoverMs, 'timeouts.auxiliaryHoverMs', DEFAULT_TIMEOUTS.auxiliaryHoverMs),\n diagnosticsMs: readPositiveInteger(timeouts.diagnosticsMs, 'timeouts.diagnosticsMs', DEFAULT_TIMEOUTS.diagnosticsMs),\n completionMs: readPositiveInteger(timeouts.completionMs, 'timeouts.completionMs', DEFAULT_TIMEOUTS.completionMs),\n shutdownMs: readPositiveInteger(timeouts.shutdownMs, 'timeouts.shutdownMs', DEFAULT_TIMEOUTS.shutdownMs),\n killGraceMs: readPositiveInteger(timeouts.killGraceMs, 'timeouts.killGraceMs', DEFAULT_TIMEOUTS.killGraceMs),\n },\n servers: Object.fromEntries(SERVER_IDS.map((id) => [id, resolveServer(id, serverInput[id])])) as Record<ServerId, ResolvedServerConfig>,\n tailwind: {\n enabled: readBoolean(tailwind.enabled, 'tailwind.enabled', true),\n queryStrategy,\n classAttributes: resolveStringArray(tailwind.classAttributes, 'tailwind.classAttributes', DEFAULT_CONFIG.tailwind.classAttributes),\n classRegex: classRegex === undefined ? [] : (classRegex.map((item) => Array.isArray(item) ? [...item] as [string, string] : item)),\n includeLanguages,\n },\n routing: {\n bladeSuffixes: resolveSuffixes(routing.bladeSuffixes, 'routing.bladeSuffixes', DEFAULT_CONFIG.routing.bladeSuffixes),\n tailwindLanguages: resolveStringArray(routing.tailwindLanguages, 'routing.tailwindLanguages', DEFAULT_CONFIG.routing.tailwindLanguages),\n },\n }\n}\n\nif (DEFAULT_CONFIG.tailwind.queryStrategy !== 'candidate' && DEFAULT_CONFIG.tailwind.queryStrategy !== 'always') throw new Error('invalid internal default')\n\nexport const ConfigSchema = {\n parse: resolveConfig,\n validate: resolveConfig,\n} as const\n","import { WebstackLspError } from './errors.js'\nimport type { ResolvedConfig } from './config.js'\n\nexport interface LspPosition { line: number; character: number }\nexport interface LspRange { start: LspPosition; end: LspPosition }\n\nexport interface DiagnosticsRequest { workspaceRoot: string; filePath: string; severity?: 'all' | 'error' | 'warning'; servers?: string[] }\nexport interface CompletionRequest { workspaceRoot: string; filePath: string; position: LspPosition; triggerKind?: 1 | 2 | 3; triggerCharacter?: string }\n\nexport interface NormalizedDiagnostic {\n server: string\n uri: string\n range: LspRange\n severity?: 1 | 2 | 3 | 4\n code?: string | number\n source?: string\n message: string\n tags?: number[]\n servers?: string[]\n}\nexport interface NormalizedCompletionItem { server: string; label: string; kind?: number; detail?: string; documentation?: string; sortText?: string; filterText?: string; insertText?: string; insertTextFormat?: number; deprecated?: boolean }\n/** A skipped diagnostic query is unverified; an empty diagnostics array alone is not evidence of a clean file. */\nexport interface DiagnosticsResult { skipped?: { reason: 'unsupported_file_type'; filePath: string }; diagnostics: NormalizedDiagnostic[]; omittedServers?: string[]; truncated?: { diagnostics: number; characters: number } }\nexport interface CompletionResult { items: NormalizedCompletionItem[]; truncated?: { items: number; characters: number } }\n\nexport interface WebstackLspStatus {\n providerId: string\n /** Routing suffixes; enabled/available servers are reported separately. */\n supportedFileSuffixes?: string[]\n servers: Array<{ id: import('./config.js').ServerId; enabled: boolean; available: boolean; source: 'bundled' | 'path'; command?: string; version?: string; lastError?: string; liveWorkspaces: number; restarts: number }>\n}\n\nexport interface WebstackLspService {\n status(): Promise<WebstackLspStatus>\n diagnostics(request: DiagnosticsRequest, signal?: AbortSignal): Promise<DiagnosticsResult>\n completion(request: CompletionRequest, signal?: AbortSignal): Promise<CompletionResult>\n}\n\nexport interface ExtendedRuntime extends WebstackLspService {}\n\nexport function createWebstackLspService(runtime: ExtendedRuntime, config: Pick<ResolvedConfig, 'limits'> = { limits: { maxDocumentBytes: 1_048_576, maxMessageBytes: 8_388_608, maxStderrBytes: 65_536 } }): WebstackLspService {\n return {\n status: () => runtime.status(),\n diagnostics: (request, signal) => runtime.diagnostics(validateDiagnosticsRequest(request), signal),\n completion: (request, signal) => runtime.completion(validateCompletionRequest(request), signal),\n }\n}\n\nfunction validateDiagnosticsRequest(request: DiagnosticsRequest): DiagnosticsRequest {\n if (!isRequest(request) || typeof request.workspaceRoot !== 'string' || request.workspaceRoot.length === 0 || typeof request.filePath !== 'string' || request.filePath.length === 0) throw new WebstackLspError('LSP_WORKSPACE_REQUIRED', 'workspaceRoot and filePath are required')\n return { ...request, ...(request.servers ? { servers: [...request.servers] } : {}) }\n}\n\nfunction validateCompletionRequest(request: CompletionRequest): CompletionRequest {\n if (!isRequest(request) || typeof request.workspaceRoot !== 'string' || request.workspaceRoot.length === 0 || typeof request.filePath !== 'string' || request.filePath.length === 0 || !isPosition(request.position)) throw new WebstackLspError('LSP_WORKSPACE_REQUIRED', 'workspaceRoot, filePath and position are required')\n return { ...request, position: { ...request.position } }\n}\n\nfunction isPosition(value: unknown): value is LspPosition { return typeof value === 'object' && value !== null && Number.isInteger((value as { line?: unknown }).line) && Number.isInteger((value as { character?: unknown }).character) && (value as LspPosition).line >= 0 && (value as LspPosition).character >= 0 }\nfunction isRequest(value: unknown): value is Record<string, unknown> { return typeof value === 'object' && value !== null && !Array.isArray(value) }\n\nexport function createUnavailableService(providerId = 'webstack'): WebstackLspService {\n return { status: async () => ({ providerId, servers: [] }), diagnostics: async () => Promise.reject(new WebstackLspError('LSP_UNAVAILABLE', 'webstack runtime is unavailable')), completion: async () => Promise.reject(new WebstackLspError('LSP_UNAVAILABLE', 'webstack runtime is unavailable')) }\n}\n","import { createRequire } from 'node:module'\nimport { dirname, resolve } from 'node:path'\nimport { readFile } from 'node:fs/promises'\nimport type { LaunchSpec } from './types.js'\n\nconst require = createRequire(import.meta.url)\n\nexport async function resolvePackageBinary(packageName: string, binName: string, cwd: string, args = ['--stdio']): Promise<LaunchSpec> {\n const packageJson = require.resolve(`${packageName}/package.json`)\n const metadata = JSON.parse(await readFile(packageJson, 'utf8')) as { bin?: string | Record<string, string> }\n const bin = typeof metadata.bin === 'string' ? metadata.bin : metadata.bin?.[binName] ?? metadata.bin?.[packageName]\n if (!bin) throw new Error(`binary ${binName} is not declared by ${packageName}`)\n return { command: process.execPath, args: [resolve(dirname(packageJson), bin), ...args], cwd }\n}\n","import { createRequire } from 'node:module'\nimport type { ResolvedConfig, ServerId } from '../config.js'\nimport { resolvePackageBinary } from './package-bin.js'\nimport type { LaunchSpec } from './types.js'\n\nconst packages: Record<Exclude<ServerId, 'phpantom'>, [string, string]> = {\n typescript: ['typescript-language-server', 'typescript-language-server'],\n svelte: ['svelte-language-server', 'svelteserver'],\n html: ['vscode-langservers-extracted', 'vscode-html-language-server'],\n css: ['vscode-langservers-extracted', 'vscode-css-language-server'],\n tailwind: ['@tailwindcss/language-server', 'tailwindcss-language-server'],\n}\n\nexport async function resolveLaunch(id: ServerId, config: ResolvedConfig, workspace: string): Promise<LaunchSpec> {\n const server = config.servers[id]\n if (server.mode === 'path' || id === 'phpantom') return { command: server.command, args: [...server.args], cwd: workspace, env: { ...server.env } }\n const [pkg, bin] = packages[id]\n return { ...await resolvePackageBinary(pkg, bin, workspace, server.args), env: { ...server.env } }\n}\n\nexport function initializationOptions(id: ServerId, config: ResolvedConfig): unknown {\n const configured = config.servers[id].initializationOptions\n if (configured !== undefined) return configured\n if (id === 'typescript' && config.servers[id].mode === 'bundled') {\n return { tsserver: { path: createRequire(import.meta.url).resolve('typescript/lib/tsserver.js') } }\n }\n if (id === 'tailwind') return { userLanguages: config.tailwind.includeLanguages }\n return null\n}\n\nexport function serverConfiguration(id: ServerId, config: ResolvedConfig): unknown {\n if (config.servers[id].configuration !== undefined) return config.servers[id].configuration\n if (id === 'tailwind') return { tailwindCSS: { classAttributes: config.tailwind.classAttributes, includeLanguages: config.tailwind.includeLanguages, experimental: { classRegex: config.tailwind.classRegex } } }\n return {}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"launch-ByUvgkVv.js","names":["SERVER_COMMANDS: Record<ServerId, string>","DEFAULT_SERVER_MODES: Record<ServerId, ServerMode>","SERVER_IDS: ServerId[]","DEFAULT_CONFIG: ResolvedConfig","packages: Record<Exclude<ServerId, 'phpantom'>, [string, string]>"],"sources":["../src/config.ts","../src/service.ts","../src/servers/package-bin.ts","../src/servers/launch.ts"],"sourcesContent":["import { configError } from './errors.js'\n\nexport type ServerId = 'phpantom' | 'typescript' | 'svelte' | 'html' | 'css' | 'tailwind'\nexport type ServerMode = 'bundled' | 'path'\n\nexport interface ServerConfig {\n enabled?: boolean\n mode?: ServerMode\n command?: string\n args?: string[]\n env?: Record<string, string>\n initializationOptions?: unknown\n configuration?: unknown\n}\n\nexport interface Config {\n providerId?: string\n missingServerPolicy?: 'warn' | 'error'\n limits?: {\n maxDocumentBytes?: number\n maxMessageBytes?: number\n maxStderrBytes?: number\n }\n timeouts?: {\n navigationMs?: number\n primaryHoverMs?: number\n auxiliaryHoverMs?: number\n diagnosticsMs?: number\n completionMs?: number\n shutdownMs?: number\n killGraceMs?: number\n }\n servers?: Partial<Record<ServerId, ServerConfig>>\n tailwind?: {\n enabled?: boolean\n queryStrategy?: 'candidate' | 'always'\n classAttributes?: string[]\n classRegex?: Array<string | [string, string]>\n includeLanguages?: Record<string, string>\n }\n routing?: {\n bladeSuffixes?: string[]\n tailwindLanguages?: string[]\n }\n}\n\nexport interface ResolvedServerConfig {\n enabled: boolean\n mode: ServerMode\n command: string\n args: string[]\n env: Record<string, string>\n initializationOptions?: unknown\n configuration?: unknown\n}\n\nexport interface ResolvedConfig {\n providerId: string\n missingServerPolicy: 'warn' | 'error'\n limits: {\n maxDocumentBytes: number\n maxMessageBytes: number\n maxStderrBytes: number\n }\n timeouts: {\n navigationMs: number\n primaryHoverMs: number\n auxiliaryHoverMs: number\n diagnosticsMs: number\n completionMs: number\n shutdownMs: number\n killGraceMs: number\n }\n servers: Record<ServerId, ResolvedServerConfig>\n tailwind: {\n enabled: boolean\n queryStrategy: 'candidate' | 'always'\n classAttributes: string[]\n classRegex: Array<string | [string, string]>\n includeLanguages: Record<string, string>\n }\n routing: {\n bladeSuffixes: string[]\n tailwindLanguages: string[]\n }\n}\n\nconst SERVER_COMMANDS: Record<ServerId, string> = {\n phpantom: 'phpantom_lsp',\n typescript: 'typescript-language-server',\n svelte: 'svelteserver',\n html: 'vscode-html-language-server',\n css: 'vscode-css-language-server',\n tailwind: 'tailwindcss-language-server',\n}\n\nconst DEFAULT_SERVER_MODES: Record<ServerId, ServerMode> = {\n phpantom: 'path',\n typescript: 'bundled',\n svelte: 'bundled',\n html: 'bundled',\n css: 'bundled',\n tailwind: 'bundled',\n}\n\nexport const SERVER_IDS: ServerId[] = ['phpantom', 'typescript', 'svelte', 'html', 'css', 'tailwind']\nconst DEFAULT_LIMITS = { maxDocumentBytes: 1_048_576, maxMessageBytes: 8_388_608, maxStderrBytes: 65_536 }\nconst DEFAULT_TIMEOUTS = { navigationMs: 60_000, primaryHoverMs: 30_000, auxiliaryHoverMs: 8_000, diagnosticsMs: 60_000, completionMs: 15_000, shutdownMs: 5_000, killGraceMs: 1_000 }\n\nexport const DEFAULT_CONFIG: ResolvedConfig = {\n providerId: 'webstack',\n missingServerPolicy: 'warn',\n limits: { ...DEFAULT_LIMITS },\n timeouts: { ...DEFAULT_TIMEOUTS },\n servers: Object.fromEntries(SERVER_IDS.map((id) => [id, {\n enabled: true,\n mode: DEFAULT_SERVER_MODES[id],\n command: SERVER_COMMANDS[id],\n args: ['--stdio'],\n env: {},\n }])) as Record<ServerId, ResolvedServerConfig>,\n tailwind: { enabled: true, queryStrategy: 'candidate', classAttributes: ['class', 'className', 'class:list'], classRegex: [], includeLanguages: {} },\n routing: { bladeSuffixes: ['.blade.php'], tailwindLanguages: ['html', 'svelte', 'typescript', 'typescriptreact', 'javascript', 'javascriptreact'] },\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction readPositiveInteger(value: unknown, label: string, fallback: number): number {\n if (value === undefined) return fallback\n if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0 || value > 2_147_483_647) throw configError(`${label} must be a positive integer no greater than 2147483647`)\n return value\n}\n\nfunction readBoolean(value: unknown, label: string, fallback: boolean): boolean {\n if (value === undefined) return fallback\n if (typeof value !== 'boolean') throw configError(`${label} must be a boolean`)\n return value\n}\n\nfunction readString(value: unknown, label: string, fallback: string): string {\n if (value === undefined) return fallback\n if (typeof value !== 'string' || value.trim().length === 0 || value.includes('\\u0000')) throw configError(`${label} must be a non-empty string without NUL`)\n return value\n}\n\nfunction cloneJson(value: unknown, label: string): unknown {\n if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value\n if (Array.isArray(value)) return value.map((item) => cloneJson(item, label))\n if (isRecord(value)) return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneJson(item, label)]))\n throw configError(`${label} must contain JSON-compatible values`)\n}\n\nfunction resolveSuffixes(value: unknown, label: string, fallback: string[]): string[] {\n if (value === undefined) return [...fallback]\n if (!Array.isArray(value) || value.length === 0) throw configError(`${label} must be a non-empty array`)\n const normalized = value.map((suffix, index) => {\n if (typeof suffix !== 'string' || suffix.length === 0 || suffix.includes('/') || suffix.includes('\\\\')) throw configError(`${label}[${index}] is not a valid suffix`)\n return suffix.toLowerCase()\n })\n if (new Set(normalized).size !== normalized.length) throw configError(`${label} contains duplicate suffixes`)\n return normalized\n}\n\nfunction resolveStringArray(value: unknown, label: string, fallback: string[]): string[] {\n if (value === undefined) return [...fallback]\n if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) throw configError(`${label} must be an array of strings`)\n return [...value]\n}\n\nfunction resolveEnv(value: unknown, label: string): Record<string, string> {\n if (value === undefined) return {}\n if (!isRecord(value) || Object.entries(value).some(([key, item]) => key.length === 0 || typeof item !== 'string')) throw configError(`${label} must be a string map`)\n return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, item as string]))\n}\n\nfunction resolveServer(id: ServerId, value: unknown): ResolvedServerConfig {\n if (value !== undefined && !isRecord(value)) throw configError(`servers.${id} must be an object`)\n const input = value as Record<string, unknown> | undefined\n const mode = input?.mode === undefined ? DEFAULT_SERVER_MODES[id] : input.mode\n if (mode !== 'bundled' && mode !== 'path') throw configError(`servers.${id}.mode must be bundled or path`)\n if (id === 'phpantom' && mode === 'bundled') throw configError('PHPantom must use path mode')\n const command = input?.command === undefined ? SERVER_COMMANDS[id] : readString(input.command, `servers.${id}.command`, SERVER_COMMANDS[id])\n if (mode === 'path' && input?.command === undefined && id !== 'phpantom') throw configError(`servers.${id}.command is required in path mode`)\n const argsValue = input?.args\n if (argsValue !== undefined && (!Array.isArray(argsValue) || argsValue.some((item) => typeof item !== 'string'))) throw configError(`servers.${id}.args must be an array of strings`)\n return {\n enabled: readBoolean(input?.enabled, `servers.${id}.enabled`, true),\n mode,\n command,\n args: argsValue === undefined ? ['--stdio'] : [...argsValue] as string[],\n env: resolveEnv(input?.env, `servers.${id}.env`),\n ...(input?.initializationOptions === undefined ? {} : { initializationOptions: cloneJson(input.initializationOptions, `servers.${id}.initializationOptions`) }),\n ...(input?.configuration === undefined ? {} : { configuration: cloneJson(input.configuration, `servers.${id}.configuration`) }),\n }\n}\n\nexport function resolveConfig(input: unknown = {}): ResolvedConfig {\n if (!isRecord(input)) throw configError('config must be an object')\n const limits = isRecord(input.limits) ? input.limits : input.limits === undefined ? {} : (() => { throw configError('limits must be an object') })()\n const timeouts = isRecord(input.timeouts) ? input.timeouts : input.timeouts === undefined ? {} : (() => { throw configError('timeouts must be an object') })()\n const tailwind = isRecord(input.tailwind) ? input.tailwind : input.tailwind === undefined ? {} : (() => { throw configError('tailwind must be an object') })()\n const routing = isRecord(input.routing) ? input.routing : input.routing === undefined ? {} : (() => { throw configError('routing must be an object') })()\n const serverInput = isRecord(input.servers) ? input.servers : input.servers === undefined ? {} : (() => { throw configError('servers must be an object') })()\n const providerId = readString(input.providerId, 'providerId', DEFAULT_CONFIG.providerId)\n const missingServerPolicy = input.missingServerPolicy === undefined ? 'warn' : input.missingServerPolicy\n if (missingServerPolicy !== 'warn' && missingServerPolicy !== 'error') throw configError('missingServerPolicy must be warn or error')\n const classRegex = tailwind.classRegex\n if (classRegex !== undefined && (!Array.isArray(classRegex) || classRegex.some((item) => typeof item !== 'string' && (!Array.isArray(item) || item.length !== 2 || item.some((part) => typeof part !== 'string'))))) throw configError('tailwind.classRegex must contain strings or string pairs')\n const queryStrategy = tailwind.queryStrategy === undefined ? 'candidate' : tailwind.queryStrategy\n for (const expression of (classRegex ?? []) as Array<string | [string, string]>) {\n for (const pattern of Array.isArray(expression) ? expression : [expression]) {\n try { new RegExp(pattern) } catch { throw configError('tailwind.classRegex contains an invalid regular expression') }\n }\n }\n if (queryStrategy !== 'candidate' && queryStrategy !== 'always') throw configError('tailwind.queryStrategy must be candidate or always')\n const includeLanguages = resolveEnv(tailwind.includeLanguages, 'tailwind.includeLanguages')\n return {\n providerId,\n missingServerPolicy,\n limits: {\n maxDocumentBytes: readPositiveInteger(limits.maxDocumentBytes, 'limits.maxDocumentBytes', DEFAULT_LIMITS.maxDocumentBytes),\n maxMessageBytes: readPositiveInteger(limits.maxMessageBytes, 'limits.maxMessageBytes', DEFAULT_LIMITS.maxMessageBytes),\n maxStderrBytes: readPositiveInteger(limits.maxStderrBytes, 'limits.maxStderrBytes', DEFAULT_LIMITS.maxStderrBytes),\n },\n timeouts: {\n navigationMs: readPositiveInteger(timeouts.navigationMs, 'timeouts.navigationMs', DEFAULT_TIMEOUTS.navigationMs),\n primaryHoverMs: readPositiveInteger(timeouts.primaryHoverMs, 'timeouts.primaryHoverMs', DEFAULT_TIMEOUTS.primaryHoverMs),\n auxiliaryHoverMs: readPositiveInteger(timeouts.auxiliaryHoverMs, 'timeouts.auxiliaryHoverMs', DEFAULT_TIMEOUTS.auxiliaryHoverMs),\n diagnosticsMs: readPositiveInteger(timeouts.diagnosticsMs, 'timeouts.diagnosticsMs', DEFAULT_TIMEOUTS.diagnosticsMs),\n completionMs: readPositiveInteger(timeouts.completionMs, 'timeouts.completionMs', DEFAULT_TIMEOUTS.completionMs),\n shutdownMs: readPositiveInteger(timeouts.shutdownMs, 'timeouts.shutdownMs', DEFAULT_TIMEOUTS.shutdownMs),\n killGraceMs: readPositiveInteger(timeouts.killGraceMs, 'timeouts.killGraceMs', DEFAULT_TIMEOUTS.killGraceMs),\n },\n servers: Object.fromEntries(SERVER_IDS.map((id) => [id, resolveServer(id, serverInput[id])])) as Record<ServerId, ResolvedServerConfig>,\n tailwind: {\n enabled: readBoolean(tailwind.enabled, 'tailwind.enabled', true),\n queryStrategy,\n classAttributes: resolveStringArray(tailwind.classAttributes, 'tailwind.classAttributes', DEFAULT_CONFIG.tailwind.classAttributes),\n classRegex: classRegex === undefined ? [] : (classRegex.map((item) => Array.isArray(item) ? [...item] as [string, string] : item)),\n includeLanguages,\n },\n routing: {\n bladeSuffixes: resolveSuffixes(routing.bladeSuffixes, 'routing.bladeSuffixes', DEFAULT_CONFIG.routing.bladeSuffixes),\n tailwindLanguages: resolveStringArray(routing.tailwindLanguages, 'routing.tailwindLanguages', DEFAULT_CONFIG.routing.tailwindLanguages),\n },\n }\n}\n\nif (DEFAULT_CONFIG.tailwind.queryStrategy !== 'candidate' && DEFAULT_CONFIG.tailwind.queryStrategy !== 'always') throw new Error('invalid internal default')\n\nexport const ConfigSchema = {\n parse: resolveConfig,\n validate: resolveConfig,\n} as const\n","import { WebstackLspError } from './errors.js'\nimport type { ResolvedConfig } from './config.js'\n\nexport interface LspPosition { line: number; character: number }\nexport interface LspRange { start: LspPosition; end: LspPosition }\n\nexport interface DiagnosticsRequest { workspaceRoot: string; filePath: string; severity?: 'all' | 'error' | 'warning'; servers?: string[] }\nexport interface CompletionRequest { workspaceRoot: string; filePath: string; position: LspPosition; triggerKind?: 1 | 2 | 3; triggerCharacter?: string }\n\nexport interface NormalizedDiagnostic {\n server: string\n uri: string\n range: LspRange\n severity?: 1 | 2 | 3 | 4\n code?: string | number\n source?: string\n message: string\n tags?: number[]\n servers?: string[]\n}\nexport interface NormalizedCompletionItem { server: string; label: string; kind?: number; detail?: string; documentation?: string; sortText?: string; filterText?: string; insertText?: string; insertTextFormat?: number; deprecated?: boolean }\nexport type NavigationResult =\n | { kind: 'locations'; locations: ReadonlyArray<{ uri: string; range: LspRange }>; resolvedWorkspaceUri: string; truncated?: boolean; omitted?: number }\n | { kind: 'hover'; hover: { contents: string; range?: LspRange } | null }\n/** A skipped diagnostic query is unverified; an empty diagnostics array alone is not evidence of a clean file. */\nexport interface DiagnosticsResult { skipped?: { reason: 'unsupported_file_type'; filePath: string }; diagnostics: NormalizedDiagnostic[]; omittedServers?: string[]; truncated?: { diagnostics: number; characters: number } }\nexport interface CompletionResult { items: NormalizedCompletionItem[]; truncated?: { items: number; characters: number } }\n\nexport interface WebstackLspStatus {\n providerId: string\n /** Routing suffixes; enabled/available servers are reported separately. */\n supportedFileSuffixes?: string[]\n servers: Array<{ id: import('./config.js').ServerId; enabled: boolean; available: boolean; source: 'bundled' | 'path'; command?: string; version?: string; lastError?: string; liveWorkspaces: number; restarts: number }>\n}\n\nexport interface WebstackLspService {\n status(): Promise<WebstackLspStatus>\n diagnostics(request: DiagnosticsRequest, signal?: AbortSignal): Promise<DiagnosticsResult>\n completion(request: CompletionRequest, signal?: AbortSignal): Promise<CompletionResult>\n}\n\nexport interface ExtendedRuntime extends WebstackLspService {}\n\nexport function createWebstackLspService(runtime: ExtendedRuntime, config: Pick<ResolvedConfig, 'limits'> = { limits: { maxDocumentBytes: 1_048_576, maxMessageBytes: 8_388_608, maxStderrBytes: 65_536 } }): WebstackLspService {\n return {\n status: () => runtime.status(),\n diagnostics: (request, signal) => runtime.diagnostics(validateDiagnosticsRequest(request), signal),\n completion: (request, signal) => runtime.completion(validateCompletionRequest(request), signal),\n }\n}\n\nfunction validateDiagnosticsRequest(request: DiagnosticsRequest): DiagnosticsRequest {\n if (!isRequest(request) || typeof request.workspaceRoot !== 'string' || request.workspaceRoot.length === 0 || typeof request.filePath !== 'string' || request.filePath.length === 0) throw new WebstackLspError('LSP_WORKSPACE_REQUIRED', 'workspaceRoot and filePath are required')\n return { ...request, ...(request.servers ? { servers: [...request.servers] } : {}) }\n}\n\nfunction validateCompletionRequest(request: CompletionRequest): CompletionRequest {\n if (!isRequest(request) || typeof request.workspaceRoot !== 'string' || request.workspaceRoot.length === 0 || typeof request.filePath !== 'string' || request.filePath.length === 0 || !isPosition(request.position)) throw new WebstackLspError('LSP_WORKSPACE_REQUIRED', 'workspaceRoot, filePath and position are required')\n return { ...request, position: { ...request.position } }\n}\n\nfunction isPosition(value: unknown): value is LspPosition { return typeof value === 'object' && value !== null && Number.isInteger((value as { line?: unknown }).line) && Number.isInteger((value as { character?: unknown }).character) && (value as LspPosition).line >= 0 && (value as LspPosition).character >= 0 }\nfunction isRequest(value: unknown): value is Record<string, unknown> { return typeof value === 'object' && value !== null && !Array.isArray(value) }\n\nexport function createUnavailableService(providerId = 'webstack'): WebstackLspService {\n return { status: async () => ({ providerId, servers: [] }), diagnostics: async () => Promise.reject(new WebstackLspError('LSP_UNAVAILABLE', 'webstack runtime is unavailable')), completion: async () => Promise.reject(new WebstackLspError('LSP_UNAVAILABLE', 'webstack runtime is unavailable')) }\n}\n","import { createRequire } from 'node:module'\nimport { dirname, resolve } from 'node:path'\nimport { readFile } from 'node:fs/promises'\nimport type { LaunchSpec } from './types.js'\n\nconst require = createRequire(import.meta.url)\n\nexport async function resolvePackageBinary(packageName: string, binName: string, cwd: string, args = ['--stdio']): Promise<LaunchSpec> {\n const packageJson = require.resolve(`${packageName}/package.json`)\n const metadata = JSON.parse(await readFile(packageJson, 'utf8')) as { bin?: string | Record<string, string> }\n const bin = typeof metadata.bin === 'string' ? metadata.bin : metadata.bin?.[binName] ?? metadata.bin?.[packageName]\n if (!bin) throw new Error(`binary ${binName} is not declared by ${packageName}`)\n return { command: process.execPath, args: [resolve(dirname(packageJson), bin), ...args], cwd }\n}\n","import { createRequire } from 'node:module'\nimport type { ResolvedConfig, ServerId } from '../config.js'\nimport { resolvePackageBinary } from './package-bin.js'\nimport type { LaunchSpec } from './types.js'\n\nconst packages: Record<Exclude<ServerId, 'phpantom'>, [string, string]> = {\n typescript: ['typescript-language-server', 'typescript-language-server'],\n svelte: ['svelte-language-server', 'svelteserver'],\n html: ['vscode-langservers-extracted', 'vscode-html-language-server'],\n css: ['vscode-langservers-extracted', 'vscode-css-language-server'],\n tailwind: ['@tailwindcss/language-server', 'tailwindcss-language-server'],\n}\n\nexport async function resolveLaunch(id: ServerId, config: ResolvedConfig, workspace: string): Promise<LaunchSpec> {\n const server = config.servers[id]\n if (server.mode === 'path' || id === 'phpantom') return { command: server.command, args: [...server.args], cwd: workspace, env: { ...server.env } }\n const [pkg, bin] = packages[id]\n return { ...await resolvePackageBinary(pkg, bin, workspace, server.args), env: { ...server.env } }\n}\n\nexport function initializationOptions(id: ServerId, config: ResolvedConfig): unknown {\n const configured = config.servers[id].initializationOptions\n if (configured !== undefined) return configured\n if (id === 'typescript' && config.servers[id].mode === 'bundled') {\n return { tsserver: { path: createRequire(import.meta.url).resolve('typescript/lib/tsserver.js') } }\n }\n if (id === 'tailwind') return { userLanguages: config.tailwind.includeLanguages }\n return null\n}\n\nexport function serverConfiguration(id: ServerId, config: ResolvedConfig): unknown {\n if (config.servers[id].configuration !== undefined) return config.servers[id].configuration\n if (id === 'tailwind') return { tailwindCSS: { classAttributes: config.tailwind.classAttributes, includeLanguages: config.tailwind.includeLanguages, experimental: { classRegex: config.tailwind.classRegex } } }\n return {}\n}\n"],"mappings":";;;;;;AAuFA,MAAMA,kBAA4C;CAChD,UAAU;CACV,YAAY;CACZ,QAAQ;CACR,MAAM;CACN,KAAK;CACL,UAAU;CACX;AAED,MAAMC,uBAAqD;CACzD,UAAU;CACV,YAAY;CACZ,QAAQ;CACR,MAAM;CACN,KAAK;CACL,UAAU;CACX;AAED,MAAaC,aAAyB;CAAC;CAAY;CAAc;CAAU;CAAQ;CAAO;CAAW;AACrG,MAAM,iBAAiB;CAAE,kBAAkB;CAAW,iBAAiB;CAAW,gBAAgB;CAAQ;AAC1G,MAAM,mBAAmB;CAAE,cAAc;CAAQ,gBAAgB;CAAQ,kBAAkB;CAAO,eAAe;CAAQ,cAAc;CAAQ,YAAY;CAAO,aAAa;CAAO;AAEtL,MAAaC,iBAAiC;CAC5C,YAAY;CACZ,qBAAqB;CACrB,QAAQ,EAAE,GAAG,gBAAgB;CAC7B,UAAU,EAAE,GAAG,kBAAkB;CACjC,SAAS,OAAO,YAAY,WAAW,KAAK,OAAO,CAAC,IAAI;EACtD,SAAS;EACT,MAAM,qBAAqB;EAC3B,SAAS,gBAAgB;EACzB,MAAM,CAAC,UAAU;EACjB,KAAK,EAAE;EACR,CAAC,CAAC,CAAC;CACJ,UAAU;EAAE,SAAS;EAAM,eAAe;EAAa,iBAAiB;GAAC;GAAS;GAAa;GAAa;EAAE,YAAY,EAAE;EAAE,kBAAkB,EAAE;EAAE;CACpJ,SAAS;EAAE,eAAe,CAAC,aAAa;EAAE,mBAAmB;GAAC;GAAQ;GAAU;GAAc;GAAmB;GAAc;GAAkB;EAAE;CACpJ;AAED,SAAS,SAAS,OAAkD;AAClE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;AAG7E,SAAS,oBAAoB,OAAgB,OAAe,UAA0B;AACpF,KAAI,UAAU,OAAW,QAAO;AAChC,KAAI,OAAO,UAAU,YAAY,CAAC,OAAO,cAAc,MAAM,IAAI,SAAS,KAAK,QAAQ,WAAe,OAAM,YAAY,GAAG,MAAM,wDAAwD;AACzL,QAAO;;AAGT,SAAS,YAAY,OAAgB,OAAe,UAA4B;AAC9E,KAAI,UAAU,OAAW,QAAO;AAChC,KAAI,OAAO,UAAU,UAAW,OAAM,YAAY,GAAG,MAAM,oBAAoB;AAC/E,QAAO;;AAGT,SAAS,WAAW,OAAgB,OAAe,UAA0B;AAC3E,KAAI,UAAU,OAAW,QAAO;AAChC,KAAI,OAAO,UAAU,YAAY,MAAM,MAAM,CAAC,WAAW,KAAK,MAAM,SAAS,KAAS,CAAE,OAAM,YAAY,GAAG,MAAM,yCAAyC;AAC5J,QAAO;;AAGT,SAAS,UAAU,OAAgB,OAAwB;AACzD,KAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU,UAAW,QAAO;AACnH,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,MAAM,KAAK,SAAS,UAAU,MAAM,MAAM,CAAC;AAC5E,KAAI,SAAS,MAAM,CAAE,QAAO,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC,KAAK,CAAC,KAAK,UAAU,CAAC,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC,CAAC;AACzH,OAAM,YAAY,GAAG,MAAM,sCAAsC;;AAGnE,SAAS,gBAAgB,OAAgB,OAAe,UAA8B;AACpF,KAAI,UAAU,OAAW,QAAO,CAAC,GAAG,SAAS;AAC7C,KAAI,CAAC,MAAM,QAAQ,MAAM,IAAI,MAAM,WAAW,EAAG,OAAM,YAAY,GAAG,MAAM,4BAA4B;CACxG,MAAM,aAAa,MAAM,KAAK,QAAQ,UAAU;AAC9C,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,KAAK,OAAO,SAAS,IAAI,IAAI,OAAO,SAAS,KAAK,CAAE,OAAM,YAAY,GAAG,MAAM,GAAG,MAAM,yBAAyB;AACrK,SAAO,OAAO,aAAa;GAC3B;AACF,KAAI,IAAI,IAAI,WAAW,CAAC,SAAS,WAAW,OAAQ,OAAM,YAAY,GAAG,MAAM,8BAA8B;AAC7G,QAAO;;AAGT,SAAS,mBAAmB,OAAgB,OAAe,UAA8B;AACvF,KAAI,UAAU,OAAW,QAAO,CAAC,GAAG,SAAS;AAC7C,KAAI,CAAC,MAAM,QAAQ,MAAM,IAAI,MAAM,MAAM,SAAS,OAAO,SAAS,SAAS,CAAE,OAAM,YAAY,GAAG,MAAM,8BAA8B;AACtI,QAAO,CAAC,GAAG,MAAM;;AAGnB,SAAS,WAAW,OAAgB,OAAuC;AACzE,KAAI,UAAU,OAAW,QAAO,EAAE;AAClC,KAAI,CAAC,SAAS,MAAM,IAAI,OAAO,QAAQ,MAAM,CAAC,MAAM,CAAC,KAAK,UAAU,IAAI,WAAW,KAAK,OAAO,SAAS,SAAS,CAAE,OAAM,YAAY,GAAG,MAAM,uBAAuB;AACrK,QAAO,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC,KAAK,CAAC,KAAK,UAAU,CAAC,KAAK,KAAe,CAAC,CAAC;;AAG9F,SAAS,cAAc,IAAc,OAAsC;AACzE,KAAI,UAAU,UAAa,CAAC,SAAS,MAAM,CAAE,OAAM,YAAY,WAAW,GAAG,oBAAoB;CACjG,MAAM,QAAQ;CACd,MAAM,OAAO,OAAO,SAAS,SAAY,qBAAqB,MAAM,MAAM;AAC1E,KAAI,SAAS,aAAa,SAAS,OAAQ,OAAM,YAAY,WAAW,GAAG,+BAA+B;AAC1G,KAAI,OAAO,cAAc,SAAS,UAAW,OAAM,YAAY,8BAA8B;CAC7F,MAAM,UAAU,OAAO,YAAY,SAAY,gBAAgB,MAAM,WAAW,MAAM,SAAS,WAAW,GAAG,WAAW,gBAAgB,IAAI;AAC5I,KAAI,SAAS,UAAU,OAAO,YAAY,UAAa,OAAO,WAAY,OAAM,YAAY,WAAW,GAAG,mCAAmC;CAC7I,MAAM,YAAY,OAAO;AACzB,KAAI,cAAc,WAAc,CAAC,MAAM,QAAQ,UAAU,IAAI,UAAU,MAAM,SAAS,OAAO,SAAS,SAAS,EAAG,OAAM,YAAY,WAAW,GAAG,mCAAmC;AACrL,QAAO;EACL,SAAS,YAAY,OAAO,SAAS,WAAW,GAAG,WAAW,KAAK;EACnE;EACA;EACA,MAAM,cAAc,SAAY,CAAC,UAAU,GAAG,CAAC,GAAG,UAAU;EAC5D,KAAK,WAAW,OAAO,KAAK,WAAW,GAAG,MAAM;EAChD,GAAI,OAAO,0BAA0B,SAAY,EAAE,GAAG,EAAE,uBAAuB,UAAU,MAAM,uBAAuB,WAAW,GAAG,wBAAwB,EAAE;EAC9J,GAAI,OAAO,kBAAkB,SAAY,EAAE,GAAG,EAAE,eAAe,UAAU,MAAM,eAAe,WAAW,GAAG,gBAAgB,EAAE;EAC/H;;AAGH,SAAgB,cAAc,QAAiB,EAAE,EAAkB;AACjE,KAAI,CAAC,SAAS,MAAM,CAAE,OAAM,YAAY,2BAA2B;CACnE,MAAM,SAAS,SAAS,MAAM,OAAO,GAAG,MAAM,SAAS,MAAM,WAAW,SAAY,EAAE,UAAU;AAAE,QAAM,YAAY,2BAA2B;KAAK;CACpJ,MAAM,WAAW,SAAS,MAAM,SAAS,GAAG,MAAM,WAAW,MAAM,aAAa,SAAY,EAAE,UAAU;AAAE,QAAM,YAAY,6BAA6B;KAAK;CAC9J,MAAM,WAAW,SAAS,MAAM,SAAS,GAAG,MAAM,WAAW,MAAM,aAAa,SAAY,EAAE,UAAU;AAAE,QAAM,YAAY,6BAA6B;KAAK;CAC9J,MAAM,UAAU,SAAS,MAAM,QAAQ,GAAG,MAAM,UAAU,MAAM,YAAY,SAAY,EAAE,UAAU;AAAE,QAAM,YAAY,4BAA4B;KAAK;CACzJ,MAAM,cAAc,SAAS,MAAM,QAAQ,GAAG,MAAM,UAAU,MAAM,YAAY,SAAY,EAAE,UAAU;AAAE,QAAM,YAAY,4BAA4B;KAAK;CAC7J,MAAM,aAAa,WAAW,MAAM,YAAY,cAAc,eAAe,WAAW;CACxF,MAAM,sBAAsB,MAAM,wBAAwB,SAAY,SAAS,MAAM;AACrF,KAAI,wBAAwB,UAAU,wBAAwB,QAAS,OAAM,YAAY,4CAA4C;CACrI,MAAM,aAAa,SAAS;AAC5B,KAAI,eAAe,WAAc,CAAC,MAAM,QAAQ,WAAW,IAAI,WAAW,MAAM,SAAS,OAAO,SAAS,aAAa,CAAC,MAAM,QAAQ,KAAK,IAAI,KAAK,WAAW,KAAK,KAAK,MAAM,SAAS,OAAO,SAAS,SAAS,EAAE,EAAG,OAAM,YAAY,2DAA2D;CAClS,MAAM,gBAAgB,SAAS,kBAAkB,SAAY,cAAc,SAAS;AACpF,MAAK,MAAM,cAAe,cAAc,EAAE,CACxC,MAAK,MAAM,WAAW,MAAM,QAAQ,WAAW,GAAG,aAAa,CAAC,WAAW,CACzE,KAAI;AAAE,MAAI,OAAO,QAAQ;SAAS;AAAE,QAAM,YAAY,6DAA6D;;AAGvH,KAAI,kBAAkB,eAAe,kBAAkB,SAAU,OAAM,YAAY,qDAAqD;CACxI,MAAM,mBAAmB,WAAW,SAAS,kBAAkB,4BAA4B;AAC3F,QAAO;EACL;EACA;EACA,QAAQ;GACN,kBAAkB,oBAAoB,OAAO,kBAAkB,2BAA2B,eAAe,iBAAiB;GAC1H,iBAAiB,oBAAoB,OAAO,iBAAiB,0BAA0B,eAAe,gBAAgB;GACtH,gBAAgB,oBAAoB,OAAO,gBAAgB,yBAAyB,eAAe,eAAe;GACnH;EACD,UAAU;GACR,cAAc,oBAAoB,SAAS,cAAc,yBAAyB,iBAAiB,aAAa;GAChH,gBAAgB,oBAAoB,SAAS,gBAAgB,2BAA2B,iBAAiB,eAAe;GACxH,kBAAkB,oBAAoB,SAAS,kBAAkB,6BAA6B,iBAAiB,iBAAiB;GAChI,eAAe,oBAAoB,SAAS,eAAe,0BAA0B,iBAAiB,cAAc;GACpH,cAAc,oBAAoB,SAAS,cAAc,yBAAyB,iBAAiB,aAAa;GAChH,YAAY,oBAAoB,SAAS,YAAY,uBAAuB,iBAAiB,WAAW;GACxG,aAAa,oBAAoB,SAAS,aAAa,wBAAwB,iBAAiB,YAAY;GAC7G;EACD,SAAS,OAAO,YAAY,WAAW,KAAK,OAAO,CAAC,IAAI,cAAc,IAAI,YAAY,IAAI,CAAC,CAAC,CAAC;EAC7F,UAAU;GACR,SAAS,YAAY,SAAS,SAAS,oBAAoB,KAAK;GAChE;GACA,iBAAiB,mBAAmB,SAAS,iBAAiB,4BAA4B,eAAe,SAAS,gBAAgB;GAClI,YAAY,eAAe,SAAY,EAAE,GAAI,WAAW,KAAK,SAAS,MAAM,QAAQ,KAAK,GAAG,CAAC,GAAG,KAAK,GAAuB,KAAK;GACjI;GACD;EACD,SAAS;GACP,eAAe,gBAAgB,QAAQ,eAAe,yBAAyB,eAAe,QAAQ,cAAc;GACpH,mBAAmB,mBAAmB,QAAQ,mBAAmB,6BAA6B,eAAe,QAAQ,kBAAkB;GACxI;EACF;;AAGH,IAAI,eAAe,SAAS,kBAAkB,eAAe,eAAe,SAAS,kBAAkB,SAAU,OAAM,IAAI,MAAM,2BAA2B;;;;AC/M5J,SAAgB,yBAAyB,SAA0B,SAAyC,EAAE,QAAQ;CAAE,kBAAkB;CAAW,iBAAiB;CAAW,gBAAgB;CAAQ,EAAE,EAAsB;AAC/N,QAAO;EACL,cAAc,QAAQ,QAAQ;EAC9B,cAAc,SAAS,WAAW,QAAQ,YAAY,2BAA2B,QAAQ,EAAE,OAAO;EAClG,aAAa,SAAS,WAAW,QAAQ,WAAW,0BAA0B,QAAQ,EAAE,OAAO;EAChG;;AAGH,SAAS,2BAA2B,SAAiD;AACnF,KAAI,CAAC,UAAU,QAAQ,IAAI,OAAO,QAAQ,kBAAkB,YAAY,QAAQ,cAAc,WAAW,KAAK,OAAO,QAAQ,aAAa,YAAY,QAAQ,SAAS,WAAW,EAAG,OAAM,IAAI,iBAAiB,0BAA0B,0CAA0C;AACpR,QAAO;EAAE,GAAG;EAAS,GAAI,QAAQ,UAAU,EAAE,SAAS,CAAC,GAAG,QAAQ,QAAQ,EAAE,GAAG,EAAE;EAAG;;AAGtF,SAAS,0BAA0B,SAA+C;AAChF,KAAI,CAAC,UAAU,QAAQ,IAAI,OAAO,QAAQ,kBAAkB,YAAY,QAAQ,cAAc,WAAW,KAAK,OAAO,QAAQ,aAAa,YAAY,QAAQ,SAAS,WAAW,KAAK,CAAC,WAAW,QAAQ,SAAS,CAAE,OAAM,IAAI,iBAAiB,0BAA0B,oDAAoD;AAC/T,QAAO;EAAE,GAAG;EAAS,UAAU,EAAE,GAAG,QAAQ,UAAU;EAAE;;AAG1D,SAAS,WAAW,OAAsC;AAAE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAO,UAAW,MAA6B,KAAK,IAAI,OAAO,UAAW,MAAkC,UAAU,IAAK,MAAsB,QAAQ,KAAM,MAAsB,aAAa;;AACpT,SAAS,UAAU,OAAkD;AAAE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;AAElJ,SAAgB,yBAAyB,aAAa,YAAgC;AACpF,QAAO;EAAE,QAAQ,aAAa;GAAE;GAAY,SAAS,EAAE;GAAE;EAAG,aAAa,YAAY,QAAQ,OAAO,IAAI,iBAAiB,mBAAmB,kCAAkC,CAAC;EAAE,YAAY,YAAY,QAAQ,OAAO,IAAI,iBAAiB,mBAAmB,kCAAkC,CAAC;EAAE;;;;;AC5DvS,MAAM,UAAU,cAAc,OAAO,KAAK,IAAI;AAE9C,eAAsB,qBAAqB,aAAqB,SAAiB,KAAa,OAAO,CAAC,UAAU,EAAuB;CACrI,MAAM,cAAc,QAAQ,QAAQ,GAAG,YAAY,eAAe;CAClE,MAAM,WAAW,KAAK,MAAM,MAAM,SAAS,aAAa,OAAO,CAAC;CAChE,MAAM,MAAM,OAAO,SAAS,QAAQ,WAAW,SAAS,MAAM,SAAS,MAAM,YAAY,SAAS,MAAM;AACxG,KAAI,CAAC,IAAK,OAAM,IAAI,MAAM,UAAU,QAAQ,sBAAsB,cAAc;AAChF,QAAO;EAAE,SAAS,QAAQ;EAAU,MAAM,CAAC,QAAQ,QAAQ,YAAY,EAAE,IAAI,EAAE,GAAG,KAAK;EAAE;EAAK;;;;;ACPhG,MAAMC,WAAoE;CACxE,YAAY,CAAC,8BAA8B,6BAA6B;CACxE,QAAQ,CAAC,0BAA0B,eAAe;CAClD,MAAM,CAAC,gCAAgC,8BAA8B;CACrE,KAAK,CAAC,gCAAgC,6BAA6B;CACnE,UAAU,CAAC,gCAAgC,8BAA8B;CAC1E;AAED,eAAsB,cAAc,IAAc,QAAwB,WAAwC;CAChH,MAAM,SAAS,OAAO,QAAQ;AAC9B,KAAI,OAAO,SAAS,UAAU,OAAO,WAAY,QAAO;EAAE,SAAS,OAAO;EAAS,MAAM,CAAC,GAAG,OAAO,KAAK;EAAE,KAAK;EAAW,KAAK,EAAE,GAAG,OAAO,KAAK;EAAE;CACnJ,MAAM,CAAC,KAAK,OAAO,SAAS;AAC5B,QAAO;EAAE,GAAG,MAAM,qBAAqB,KAAK,KAAK,WAAW,OAAO,KAAK;EAAE,KAAK,EAAE,GAAG,OAAO,KAAK;EAAE;;AAGpG,SAAgB,sBAAsB,IAAc,QAAiC;CACnF,MAAM,aAAa,OAAO,QAAQ,IAAI;AACtC,KAAI,eAAe,OAAW,QAAO;AACrC,KAAI,OAAO,gBAAgB,OAAO,QAAQ,IAAI,SAAS,UACrD,QAAO,EAAE,UAAU,EAAE,MAAM,cAAc,OAAO,KAAK,IAAI,CAAC,QAAQ,6BAA6B,EAAE,EAAE;AAErG,KAAI,OAAO,WAAY,QAAO,EAAE,eAAe,OAAO,SAAS,kBAAkB;AACjF,QAAO;;AAGT,SAAgB,oBAAoB,IAAc,QAAiC;AACjF,KAAI,OAAO,QAAQ,IAAI,kBAAkB,OAAW,QAAO,OAAO,QAAQ,IAAI;AAC9E,KAAI,OAAO,WAAY,QAAO,EAAE,aAAa;EAAE,iBAAiB,OAAO,SAAS;EAAiB,kBAAkB,OAAO,SAAS;EAAkB,cAAc,EAAE,YAAY,OAAO,SAAS,YAAY;EAAE,EAAE;AACjN,QAAO,EAAE"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { Language, Parser, Query } from 'web-tree-sitter'
|
|
2
|
+
|
|
3
|
+
const port = (await import('node:worker_threads')).parentPort
|
|
4
|
+
if (!port) throw new Error('structure worker requires a parent port')
|
|
5
|
+
|
|
6
|
+
const assets = {
|
|
7
|
+
javascript: 'tree-sitter-javascript.wasm',
|
|
8
|
+
typescript: 'tree-sitter-typescript.wasm',
|
|
9
|
+
tsx: 'tree-sitter-tsx.wasm',
|
|
10
|
+
}
|
|
11
|
+
const MAX_CACHE_ENTRIES = 16
|
|
12
|
+
const MAX_CACHE_BYTES = 16 * 1024 * 1024
|
|
13
|
+
let init
|
|
14
|
+
const languages = new Map()
|
|
15
|
+
const trees = new Map()
|
|
16
|
+
let cachedBytes = 0
|
|
17
|
+
|
|
18
|
+
async function loadLanguage(language) {
|
|
19
|
+
if (!assets[language]) throw new Error('unsupported grammar')
|
|
20
|
+
init ??= Parser.init()
|
|
21
|
+
await init
|
|
22
|
+
if (!languages.has(language)) {
|
|
23
|
+
const url = new URL(`./grammars/${assets[language]}`, import.meta.url)
|
|
24
|
+
languages.set(language, await Language.load(url))
|
|
25
|
+
}
|
|
26
|
+
return languages.get(language)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function summarizeErrors(root, limit = 100) {
|
|
30
|
+
const stack = [root]
|
|
31
|
+
let errorNodes = 0, missingNodes = 0, visited = 0
|
|
32
|
+
while (stack.length && visited < 200_000) {
|
|
33
|
+
const node = stack.pop()
|
|
34
|
+
visited++
|
|
35
|
+
if (node.type === 'ERROR') errorNodes++
|
|
36
|
+
if (node.isMissing) missingNodes++
|
|
37
|
+
if (errorNodes + missingNodes >= limit) break
|
|
38
|
+
for (const child of node.children) stack.push(child)
|
|
39
|
+
}
|
|
40
|
+
return { hasError: root.hasError, errorNodes: Math.min(errorNodes, limit), missingNodes: Math.min(missingNodes, limit), truncated: stack.length > 0 }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function nodeSummary(node) {
|
|
44
|
+
const name = node.childForFieldName('name')
|
|
45
|
+
return {
|
|
46
|
+
kind: node.type,
|
|
47
|
+
startIndex: node.startIndex,
|
|
48
|
+
endIndex: node.endIndex,
|
|
49
|
+
startPosition: node.startPosition,
|
|
50
|
+
endPosition: node.endPosition,
|
|
51
|
+
...(name ? { nameStartIndex: name.startIndex, nameEndIndex: name.endIndex } : {}),
|
|
52
|
+
childCount: node.namedChildCount,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function run(request) {
|
|
57
|
+
const language = await loadLanguage(request.language)
|
|
58
|
+
let cached = request.snapshotHandle ? trees.get(request.snapshotHandle) : undefined
|
|
59
|
+
let incremental = false
|
|
60
|
+
if (cached && (cached.languageName !== request.language || cached.text !== request.text)) {
|
|
61
|
+
cached.tree.delete()
|
|
62
|
+
cachedBytes -= cached.bytes
|
|
63
|
+
trees.delete(request.snapshotHandle)
|
|
64
|
+
cached = undefined
|
|
65
|
+
}
|
|
66
|
+
let previous = !cached && request.previousSnapshotHandle ? trees.get(request.previousSnapshotHandle) : undefined
|
|
67
|
+
if (previous && previous.languageName !== request.language) previous = undefined
|
|
68
|
+
let tree = cached?.tree
|
|
69
|
+
let parser
|
|
70
|
+
try {
|
|
71
|
+
if (tree) {
|
|
72
|
+
trees.delete(request.snapshotHandle)
|
|
73
|
+
trees.set(request.snapshotHandle, cached)
|
|
74
|
+
} else if (previous && previous.text === request.text) {
|
|
75
|
+
trees.delete(request.previousSnapshotHandle)
|
|
76
|
+
tree = previous.tree
|
|
77
|
+
if (request.snapshotHandle) trees.set(request.snapshotHandle, previous)
|
|
78
|
+
else { cachedBytes -= previous.bytes; tree.delete() }
|
|
79
|
+
} else {
|
|
80
|
+
parser = new Parser()
|
|
81
|
+
parser.setLanguage(language)
|
|
82
|
+
if (previous) {
|
|
83
|
+
try {
|
|
84
|
+
const edit = changedRange(previous.text, request.text)
|
|
85
|
+
previous.tree.edit(edit)
|
|
86
|
+
tree = parser.parse(request.text, previous.tree)
|
|
87
|
+
incremental = Boolean(tree)
|
|
88
|
+
} catch {
|
|
89
|
+
tree = undefined
|
|
90
|
+
} finally {
|
|
91
|
+
trees.delete(request.previousSnapshotHandle)
|
|
92
|
+
cachedBytes -= previous.bytes
|
|
93
|
+
previous.tree.delete()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
tree ??= parser.parse(request.text)
|
|
97
|
+
if (!tree) throw new Error('parser returned no tree')
|
|
98
|
+
if (request.snapshotHandle && Buffer.byteLength(request.text, 'utf8') <= MAX_CACHE_BYTES) {
|
|
99
|
+
const bytes = Buffer.byteLength(request.text, 'utf8')
|
|
100
|
+
trees.set(request.snapshotHandle, { tree, text: request.text, languageName: request.language, bytes })
|
|
101
|
+
cachedBytes += bytes
|
|
102
|
+
while (trees.size > MAX_CACHE_ENTRIES || cachedBytes > MAX_CACHE_BYTES) {
|
|
103
|
+
const [oldestKey, oldest] = trees.entries().next().value
|
|
104
|
+
trees.delete(oldestKey)
|
|
105
|
+
cachedBytes -= oldest.bytes
|
|
106
|
+
oldest.tree.delete()
|
|
107
|
+
if (oldestKey === request.snapshotHandle) {
|
|
108
|
+
tree = undefined
|
|
109
|
+
break
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const root = tree.rootNode
|
|
115
|
+
const errors = summarizeErrors(root)
|
|
116
|
+
if (request.operation === 'inspect') return { status: errors.hasError ? 'partial' : 'ok', errors, abiVersion: language.abiVersion, reusedTree: Boolean(cached), incremental }
|
|
117
|
+
if (request.operation === 'enclosing') {
|
|
118
|
+
const leaf = root.namedDescendantForIndex(request.index, request.index) ?? root
|
|
119
|
+
const allowed = request.kinds?.length ? new Set(request.kinds) : undefined
|
|
120
|
+
const nodes = []
|
|
121
|
+
for (let node = leaf; node && nodes.length < 16; node = node.parent) {
|
|
122
|
+
if (!allowed || allowed.has(node.type)) nodes.push(nodeSummary(node))
|
|
123
|
+
}
|
|
124
|
+
return { status: errors.hasError ? 'partial' : 'ok', nodes, errors, reusedTree: Boolean(cached), incremental }
|
|
125
|
+
}
|
|
126
|
+
const query = new Query(language, request.querySource)
|
|
127
|
+
try {
|
|
128
|
+
const options = {
|
|
129
|
+
matchLimit: Math.max(1, Math.min(2048, request.limit + 1)),
|
|
130
|
+
...(request.startIndex === undefined ? {} : { startIndex: request.startIndex }),
|
|
131
|
+
...(request.endIndex === undefined ? {} : { endIndex: request.endIndex }),
|
|
132
|
+
}
|
|
133
|
+
const matches = query.matches(root, options)
|
|
134
|
+
const result = matches.slice(0, request.limit + 1).map(match => ({
|
|
135
|
+
captures: match.captures.map(capture => ({ name: capture.name, ...nodeSummary(capture.node) })),
|
|
136
|
+
}))
|
|
137
|
+
return { status: errors.hasError ? 'partial' : 'ok', matches: result, truncated: matches.length > request.limit, errors, reusedTree: Boolean(cached), incremental }
|
|
138
|
+
} finally { query.delete() }
|
|
139
|
+
} finally {
|
|
140
|
+
if (tree && (!request.snapshotHandle || trees.get(request.snapshotHandle)?.tree !== tree)) tree.delete()
|
|
141
|
+
parser?.delete()
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function changedRange(oldText, newText) {
|
|
146
|
+
let prefix = 0
|
|
147
|
+
const sharedLength = Math.min(oldText.length, newText.length)
|
|
148
|
+
while (prefix < sharedLength && oldText.charCodeAt(prefix) === newText.charCodeAt(prefix)) prefix++
|
|
149
|
+
while (prefix > 0 && (splitsSurrogate(oldText, prefix) || splitsSurrogate(newText, prefix))) prefix--
|
|
150
|
+
|
|
151
|
+
let suffix = 0
|
|
152
|
+
while (suffix < oldText.length - prefix && suffix < newText.length - prefix &&
|
|
153
|
+
oldText.charCodeAt(oldText.length - suffix - 1) === newText.charCodeAt(newText.length - suffix - 1)) suffix++
|
|
154
|
+
while (suffix > 0 && (splitsSurrogate(oldText, oldText.length - suffix) || splitsSurrogate(newText, newText.length - suffix))) suffix--
|
|
155
|
+
|
|
156
|
+
const oldEndIndex = oldText.length - suffix
|
|
157
|
+
const newEndIndex = newText.length - suffix
|
|
158
|
+
return {
|
|
159
|
+
startIndex: prefix,
|
|
160
|
+
oldEndIndex,
|
|
161
|
+
newEndIndex,
|
|
162
|
+
startPosition: pointAt(oldText, prefix),
|
|
163
|
+
oldEndPosition: pointAt(oldText, oldEndIndex),
|
|
164
|
+
newEndPosition: pointAt(newText, newEndIndex),
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function pointAt(text, index) {
|
|
169
|
+
let row = 0
|
|
170
|
+
let lineStart = 0
|
|
171
|
+
for (let cursor = 0; cursor < index; cursor++) {
|
|
172
|
+
if (text.charCodeAt(cursor) === 10) { row++; lineStart = cursor + 1 }
|
|
173
|
+
}
|
|
174
|
+
return { row, column: index - lineStart }
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function splitsSurrogate(text, index) {
|
|
178
|
+
if (index <= 0 || index >= text.length) return false
|
|
179
|
+
const before = text.charCodeAt(index - 1)
|
|
180
|
+
const after = text.charCodeAt(index)
|
|
181
|
+
return before >= 0xd800 && before <= 0xdbff && after >= 0xdc00 && after <= 0xdfff
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
port.on('message', async request => {
|
|
185
|
+
if (typeof request.releaseSnapshotHandle === 'string') {
|
|
186
|
+
const cached = trees.get(request.releaseSnapshotHandle)
|
|
187
|
+
if (cached) {
|
|
188
|
+
trees.delete(request.releaseSnapshotHandle)
|
|
189
|
+
cachedBytes -= cached.bytes
|
|
190
|
+
cached.tree.delete()
|
|
191
|
+
}
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
const value = await run(request)
|
|
196
|
+
port.postMessage({ id: request.id, ok: true, value })
|
|
197
|
+
} catch (error) {
|
|
198
|
+
port.postMessage({ id: request.id, ok: false, error: error instanceof Error ? error.message.slice(0, 500) : 'structure parse failed' })
|
|
199
|
+
}
|
|
200
|
+
})
|