import { performance } from "node:perf_hooks"; import type { HsonSchemaIssue } from "../../api/livemap/livemap.error.js"; import { validate_schema_hson_graph } from "../schema-hson-validation/validate-schema-hson-graph.js"; import { is_projected_value_hson_node } from "../../core/projected-value-graph.js"; import { classify_live_root_mode } from "../../api/livemap/livemap.document.js"; import { same_direct_source, same_map_flow, same_schema_source_binding, valid_schema_source_binding } from "./source-binding.js"; import { read_schema_issue_presentation } from "./issue-presentation.js"; import type { TrustedSchemaBindingRegistration, TrustedSchemaDirectSource, TrustedSchemaRootMode } from "./protocol.js"; import { resolve_document_schema_issue_source } from "../document-schema-source-lowering/document-schema-source-lowering.js"; import { parse_hson_with_provenance } from "../hson-source-provenance/parse-hson-with-provenance.js"; import { resolve_projected_schema_issue_source } from "../projected-schema-source-lowering/projected-schema-source-lowering.js"; import { consume_trusted_schema_development_registrations } from "./dev-registration.js"; import { consume_trusted_schema_attachments, type TrustedSchemaAttachment } from "./lifecycle-evidence.js"; import { is_owned_trusted_schema, is_trusted_schema_runtime } from "./runtime-origin.js"; import { TRUSTED_SCHEMA_DIAGNOSTICS_PROTOCOL_VERSION, type TrustedSchemaDiagnostic, type TrustedSchemaRequest, type TrustedSchemaResponse } from "./protocol.js"; import { reset_interpolation_captures, read_interpolation_captures, type InterpolationCapture } from "./interpolation-capture.js"; import { map_interpolation_range } from "./interpolation-source.js"; import { query_schema_completion } from "../schema-completion/query.js"; export class TrustedSchemaDiagnosticRuntime { readonly #generation: number; readonly #schemas = new Map(); readonly #proposals = new Map(); readonly #associations = new Map(); readonly #bindings: TrustedSchemaBindingRegistration[] = []; readonly #direct = new Map>(); #loadAttempted = false; #captures: readonly InterpolationCapture[] = []; constructor(generation: number) { this.#generation = generation; } async handle(request: TrustedSchemaRequest): Promise { if (request.protocolVersion !== TRUSTED_SCHEMA_DIAGNOSTICS_PROTOCOL_VERSION) return this.error(request, "PROTOCOL_MISMATCH", "Unsupported trusted Schema diagnostics protocol."); if (request.runtimeGeneration !== this.#generation) return this.error(request, "ASSOCIATION_UNAVAILABLE", "Stale runtime generation."); if (request.type === "handshake") return this.reply(request, "ready"); if (request.type === "ping") return this.reply(request, "pong"); if (request.type === "captures") return Object.freeze({ ...this.reply(request, "captured"), captures: read_interpolation_captures().filter(c => c.site.moduleUrl === request.moduleUrl) }); if (request.type === "shutdown") return this.reply(request, "disposed"); if (request.type === "load") return this.load(request); if (request.type === "associate") return this.associate(request); if (request.type === "associate-source") return this.associateSource(request); if (request.type === "dispose") { if (request.associationId !== undefined) { this.#associations.delete(request.associationId); this.#direct.delete(request.associationId); } return this.reply(request, "disposed"); } if (request.type === "validate") return this.validate(request); if (request.type === "complete") return this.complete(request); return this.error(request, "PROTOCOL_MISMATCH", "Unrecognized trusted Schema diagnostics request."); } private async load(request: Extract): Promise { if (this.#loadAttempted) return this.error(request, "MODULE_LOAD_FAILED", "D1 requires a new generation to load another project."); this.#loadAttempted = true; try { const configured = await import(request.hsonModuleUrl); if (!is_trusted_schema_runtime(configured.hson)) return this.error(request, "RUNTIME_MISMATCH", "Configured runtime is not the D1 validator's supported runtime instance."); reset_interpolation_captures(); let project: Record = {}; let loadFailure: string | undefined; try { project = await import(request.moduleUrl); } catch (cause) { if (read_interpolation_captures().length === 0) throw cause; // The provider still failed; retain already executed evidence and prior // explicit registrations, without pretending remaining code executed. loadFailure = cause instanceof Error ? cause.message : "Project module failed to load."; } this.#captures = read_interpolation_captures(); const registrations = consume_trusted_schema_development_registrations(); const attachments = consume_trusted_schema_attachments(); if (project.hson !== undefined && !is_trusted_schema_runtime(project.hson)) return this.error(request, "RUNTIME_MISMATCH", "Project runtime identity differs from the validator."); const exported = project.trustedSchemas; if (exported !== undefined && (typeof exported !== "object" || exported === null)) return this.error(request, "MODULE_LOAD_FAILED", "trustedSchemas must be an object."); if (exported !== undefined && !is_trusted_schema_runtime(project.hson)) return this.error(request, "RUNTIME_MISMATCH", "Exported Schemas require explicit project runtime-origin evidence."); const schemas = new Map(); const bindings: TrustedSchemaBindingRegistration[] = []; if (exported !== undefined) for (const [id, schema] of Object.entries(exported)) { if (!is_owned_trusted_schema(schema)) return this.error(request, "RUNTIME_MISMATCH", "Exported Schema is not owned by the validator's capability registries."); schemas.set(id, schema); // Export identity, not export spelling, establishes this mapping. for (const [exportName, value] of Object.entries(project)) if (value === schema) bindings.push({ schemaId: id, binding: { moduleUrl: request.moduleUrl, exportName } }); } for (const registration of registrations) { if (!is_trusted_schema_runtime(registration.origin) || !is_owned_trusted_schema(registration.schema)) return this.error(request, "RUNTIME_MISMATCH", "Development registration has missing or incompatible runtime-origin evidence."); if (schemas.has(registration.id) && schemas.get(registration.id) !== registration.schema) return this.error(request, "AMBIGUOUS_REGISTRATION", "Conflicting Schema handles."); schemas.set(registration.id, registration.schema); if (registration.sourceBinding !== undefined) { if (!valid_schema_source_binding(registration.sourceBinding)) return this.error(request, "MODULE_LOAD_FAILED", "Invalid source binding metadata."); bindings.push({ schemaId: registration.id, binding: registration.sourceBinding }); } } // Optional private metadata connects a registration module to another // source module. Export mappings additionally prove exact runtime identity. if (project.trustedSchemaBindings !== undefined) { if (!Array.isArray(project.trustedSchemaBindings)) return this.error(request, "MODULE_LOAD_FAILED", "trustedSchemaBindings must be an array."); for (const record of project.trustedSchemaBindings) { if (!record || !schemas.has(record.schemaId) || !valid_schema_source_binding(record.binding) || record.binding.exportName === undefined) return this.error(request, "MODULE_LOAD_FAILED", "Invalid exported source binding metadata."); bindings.push(record); } } for (const record of bindings) { if (record.binding.exportName !== undefined) { const owner = await import(record.binding.moduleUrl); if (owner[record.binding.exportName] !== schemas.get(record.schemaId)) return this.error(request, "RUNTIME_MISMATCH", "Source export differs from registered Schema object."); } } const resolvedAttachments: TrustedSchemaAttachment[] = []; for (const proposal of attachments) { if (!is_trusted_schema_runtime(proposal.origin)) return this.error(request, "RUNTIME_MISMATCH", "Application runtime identity differs."); if (proposal.evidence.mapFlow !== undefined && proposal.evidence.binding !== undefined) { // D3 does not register Schemas: match the attempted object to D2's // already verified registrations and source binding, never its name. const registration = bindings.find(record => same_schema_source_binding(record.binding, proposal.evidence.binding!) && schemas.get(record.schemaId) === proposal.schema); if (registration === undefined) continue; resolvedAttachments.push({ ...proposal, evidence: Object.freeze({ ...proposal.evidence, schemaId: registration.schemaId }) }); } else { if (schemas.get(proposal.evidence.schemaId) !== proposal.schema) return this.error(request, "RUNTIME_MISMATCH", "Application Schema identity does not match its declared runtime capability."); resolvedAttachments.push(proposal); } } for (const [id, schema] of schemas) this.#schemas.set(id, schema); this.#bindings.push(...bindings.map(record => Object.freeze({ schemaId: record.schemaId, binding: Object.freeze({ ...record.binding }) }))); for (const proposal of resolvedAttachments) this.#proposals.set(proposal.evidence.associationId, proposal); return Object.freeze({ ...this.reply(request, "loaded"), captures: this.#captures, loadFailure, schemaIds: Object.freeze([...this.#schemas.keys()]), bindings: Object.freeze(bindings), associations: Object.freeze(resolvedAttachments.map((record) => record.evidence)) }); } catch (cause) { consume_trusted_schema_development_registrations(); consume_trusted_schema_attachments(); return this.error(request, "MODULE_LOAD_FAILED", cause instanceof Error ? cause.message : "Project module failed to load."); } } private associate(request: Extract): TrustedSchemaResponse { const proposal = this.#proposals.get(request.associationId); if (proposal === undefined || proposal.evidence.correspondence !== "direct") { this.#associations.delete(request.associationId); return this.error(request, "ASSOCIATION_UNAVAILABLE", "No proven direct construction/attachment correspondence."); } this.#associations.set(request.associationId, proposal); return this.reply(request, "associated"); } private associateSource(request: Extract): TrustedSchemaResponse { this.#captures = read_interpolation_captures(); this.#direct.delete(request.associationId); const matches = this.#bindings.filter(record => same_schema_source_binding(record.binding, request.directSource.binding)); const objects = new Set(matches.map(record => this.#schemas.get(record.schemaId))); if (objects.size > 1) return this.error(request, "AMBIGUOUS_REGISTRATION", "Source binding maps to different Schema objects."); const schema = this.#schemas.get(request.schemaId); if (schema === undefined || !matches.some(record => record.schemaId === request.schemaId)) return this.error(request, "ASSOCIATION_UNAVAILABLE", "No current registered source binding."); const lifecycle = request.lifecycleId === undefined ? undefined : this.#proposals.get(request.lifecycleId); const interpolation = request.directSource.interpolation; if (interpolation !== undefined) { const captures = this.#captures.filter(c => c.site.templateId === interpolation.templateId && c.site.sourceRevision === interpolation.sourceRevision); if (captures.length > 1) return this.error(request, "AMBIGUOUS_REGISTRATION", "Multiple evaluated templates occupy this source relationship."); if (captures.length !== 1 || captures[0].evaluationId !== interpolation.evaluationId || captures[0].canonical === undefined || (request.directSource.mapFlow === undefined && request.directSource.templateId !== captures[0].site.templateId) || (request.directSource.mapFlow !== undefined && lifecycle?.evidence.evaluationId !== interpolation.evaluationId)) { return this.error(request, "ASSOCIATION_UNAVAILABLE", "No exact current template evaluation."); } } if (request.directSource.mapFlow !== undefined || request.lifecycleId !== undefined) { const proposals = [...this.#proposals.values()].filter(proposal => same_map_flow(proposal.evidence.mapFlow, request.directSource.mapFlow) && proposal.evidence.binding !== undefined && same_schema_source_binding(proposal.evidence.binding, request.directSource.binding) && proposal.evidence.correspondence === "direct" && proposal.evidence.validationAttempted === true); if (proposals.length > 1) return this.error(request, "AMBIGUOUS_REGISTRATION", "Multiple runtime applications occupy this source relationship."); if (lifecycle === undefined || request.directSource.mapFlow === undefined || lifecycle.schema !== schema || request.directSource.templateId !== request.directSource.mapFlow.templateId || request.directSource.callId !== request.directSource.mapFlow.callId || !same_map_flow(lifecycle.evidence.mapFlow, request.directSource.mapFlow) || lifecycle.evidence.binding === undefined || !same_schema_source_binding(lifecycle.evidence.binding, request.directSource.binding) || lifecycle.evidence.correspondence !== "direct" || lifecycle.evidence.validationAttempted !== true || lifecycle.isCurrent?.() === false) { return this.error(request, "ASSOCIATION_UNAVAILABLE", "No current source-bound map validation attempt."); } } this.#direct.set(request.associationId, { schema, lifecycle, schemaId: request.schemaId, evidence: Object.freeze({ ...request.directSource, mapFlow: request.directSource.mapFlow === undefined ? undefined : Object.freeze({ ...request.directSource.mapFlow }), binding: Object.freeze({ ...request.directSource.binding }) }) }); return this.reply(request, "associated"); } private validate(request: Extract): TrustedSchemaResponse { this.#captures = read_interpolation_captures(); const association = this.#associations.get(request.associationId); const direct = this.#direct.get(request.associationId); const validDirect = direct !== undefined && request.directSource !== undefined && same_direct_source(direct.evidence, request.directSource) && direct.evidence.templateRevision === request.templateRevision && direct.evidence.documentRevision === request.candidateRevision && (direct.lifecycle === undefined || direct.lifecycle.isCurrent?.() !== false) && direct.schemaId === request.schemaId && this.#schemas.get(request.schemaId) === direct.schema; const validLifecycle = request.directSource === undefined && association !== undefined && association.evidence.templateRevision === request.templateRevision && association.evidence.schemaId === request.schemaId && this.#schemas.get(request.schemaId) === association.schema; if (!validDirect && !validLifecycle) return Object.freeze({ ...this.reply(request, "result"), result: { status: "ASSOCIATION_UNAVAILABLE" as const, diagnostics: Object.freeze([]) } }); const schema = validDirect ? direct!.schema : association!.schema; let rootMode: TrustedSchemaRootMode = validDirect ? "projected" : association!.evidence.rootMode; const fromHson = validDirect && direct!.lifecycle !== undefined; const capture = validDirect && direct!.evidence.interpolation !== undefined ? this.#captures.find(c => c.evaluationId === direct!.evidence.interpolation!.evaluationId) : undefined; if (capture !== undefined && this.#captures.filter(c => c.site.templateId === capture.site.templateId && c.site.sourceRevision === capture.site.sourceRevision).length !== 1) { return this.error(request, "AMBIGUOUS_REGISTRATION", "Template evaluated again after association."); } if (direct?.evidence.interpolation !== undefined && (capture === undefined || capture.source !== request.source)) { return Object.freeze({ ...this.reply(request, "result"), result: { status: "ASSOCIATION_UNAVAILABLE" as const, diagnostics: [] } }); } const parseStart = performance.now(); let parsed: ReturnType; try { parsed = parse_hson_with_provenance(request.source, { allowTopLevelDocumentText: fromHson || (!validDirect && rootMode === "document") }); } catch (cause) { return Object.freeze({ ...this.reply(request, "result"), result: { status: "CANDIDATE_INVALID" as const, diagnostics: Object.freeze([]), timings: { parseMs: performance.now() - parseStart, validateMs: 0, lowerMs: 0 } }, message: cause instanceof Error ? cause.message : "Candidate Hson is invalid." }); } const parseMs = performance.now() - parseStart; if (validDirect && !is_projected_value_hson_node(parsed.value)) { const mode = classify_live_root_mode(parsed.value); if (mode === "document") rootMode = mode; } const validateStart = performance.now(); let issues: readonly HsonSchemaIssue[]; try { issues = validate_schema_hson_graph(schema, parsed.value).issues; } catch (cause) { return this.error(request, "VALIDATION_THROW", cause instanceof Error ? cause.message : "Schema validation threw unexpectedly."); } if (validDirect && direct!.lifecycle?.isCurrent?.() === false) return Object.freeze({ ...this.reply(request, "result"), result: { status: "ASSOCIATION_UNAVAILABLE" as const, diagnostics: Object.freeze([]) } }); if (capture !== undefined) { const current = read_interpolation_captures().filter(c => c.site.templateId === capture.site.templateId && c.site.sourceRevision === capture.site.sourceRevision); if (current.length !== 1 || current[0].evaluationId !== capture.evaluationId) return this.error(request, current.length > 1 ? "AMBIGUOUS_REGISTRATION" : "ASSOCIATION_UNAVAILABLE", "Template evidence changed during validation."); } const validateMs = performance.now() - validateStart; const lowerStart = performance.now(); const diagnostics: TrustedSchemaDiagnostic[] = issues.map((issue) => { const resolution = rootMode === "projected" ? resolve_projected_schema_issue_source(parsed.value, parsed.provenance, issue) : resolve_document_schema_issue_source(parsed.value, rootMode, parsed.provenance, issue); const range = resolution.kind === "unresolved" ? { precision: "unresolved" as const } : { precision: resolution.kind, start: resolution.range.start, end: resolution.range.end }; return Object.freeze({ ...read_schema_issue_presentation(issue), code: issue.code, path: Object.freeze([...issue.path]), expected: issue.expected, received: issue.received, attributeName: issue.attributeName, relatedPath: issue.relatedPath, conflictingKey: issue.conflictingKey, range: Object.freeze(range), hostOrigin: capture === undefined ? undefined : map_interpolation_range(capture.site, capture.segments, range) }); }); return Object.freeze({ ...this.reply(request, "result"), result: Object.freeze({ status: issues.length === 0 ? "VALID" : "INVALID", diagnostics: Object.freeze(diagnostics), timings: Object.freeze({ parseMs, validateMs, lowerMs: performance.now() - lowerStart }) }) }); } private reply(request: TrustedSchemaRequest, type: TrustedSchemaResponse["type"]): TrustedSchemaResponse { return Object.freeze({ protocolVersion: TRUSTED_SCHEMA_DIAGNOSTICS_PROTOCOL_VERSION, completionVersion: 1, requestId: request.requestId, runtimeGeneration: this.#generation, type }); } private complete(request: Extract): TrustedSchemaResponse { const direct = this.#direct.get(request.associationId); if (direct === undefined || !same_direct_source(direct.evidence, request.directSource) || direct.evidence.templateRevision !== request.templateRevision || direct.evidence.documentRevision !== request.candidateRevision || direct.schemaId !== request.schemaId || this.#schemas.get(request.schemaId) !== direct.schema || direct.lifecycle?.isCurrent?.() === false) return this.error(request, "ASSOCIATION_UNAVAILABLE", "No current completion contract."); const interpolation = direct.evidence.interpolation; const interpolationCurrent = (): boolean => { if (interpolation === undefined) return true; const captures = read_interpolation_captures().filter(c => c.site.templateId === interpolation.templateId && c.site.sourceRevision === interpolation.sourceRevision); return captures.length === 1 && captures[0].evaluationId === interpolation.evaluationId && captures[0].source === request.source; }; if (!interpolationCurrent()) return this.error(request, "ASSOCIATION_UNAVAILABLE", "No current interpolation evidence."); const completion = query_schema_completion(direct.schema, request.source, request.cursor, request.unknownRanges); if (direct.lifecycle?.isCurrent?.() === false || !interpolationCurrent()) return this.error(request, "ASSOCIATION_UNAVAILABLE", "Completion contract retired."); return { ...this.reply(request, "completed"), completion }; } private error(request: TrustedSchemaRequest, error: NonNullable, message: string): TrustedSchemaResponse { return Object.freeze({ ...this.reply(request, "error"), error, message }); } }