import type { WrnDiagnostic } from "./diagnostics.ts"; /** Current stable syntax contract. Bump only when parsers/codegen need migration. */ export const WRN_SYNTAX_VERSION = "0.4" as const; export type WrnSyntaxFeature = | "typed-declarations" | "layouts" | "server-client-blocks" | "effects" | "watch" | "lifecycle" | "embedded-api" | "realtime" | "runtime-markers"; export const WRN_SYNTAX_FEATURES: Readonly> = Object.freeze({ "typed-declarations": true, layouts: true, "server-client-blocks": true, effects: true, watch: true, lifecycle: true, "embedded-api": true, realtime: true, "runtime-markers": true, }); export interface SourceRange { start: number; end: number; } export function createSourceRange(start: number, end: number): SourceRange { if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start) { throw new RangeError(`Invalid source range ${start}..${end}`); } return { start, end }; } export function sliceSource(source: string, range: SourceRange): string { return source.slice(range.start, range.end); } export function diagnosticSummary(diagnostics: readonly WrnDiagnostic[]): { errors: number; warnings: number; info: number; codes: Record; } { const summary = { errors: 0, warnings: 0, info: 0, codes: {} as Record }; for (const diagnostic of diagnostics) { if (diagnostic.severity === "error") summary.errors++; else if (diagnostic.severity === "warning") summary.warnings++; else summary.info++; summary.codes[diagnostic.code] = (summary.codes[diagnostic.code] ?? 0) + 1; } return summary; } export function supportsSyntaxFeature(feature: string): feature is WrnSyntaxFeature { return Object.prototype.hasOwnProperty.call(WRN_SYNTAX_FEATURES, feature); }