General Upgrade, NEW: WebServers, Better Docs
Some checks failed
CI / checks (push) Has been cancelled

This commit is contained in:
Alex Emmet 2026-07-18 03:08:03 +02:00
commit c9f2d78369
120 changed files with 10033 additions and 4887 deletions

View file

@ -4,6 +4,12 @@ import { spawn } from "node:child_process";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import YAML from "yaml";
import {
FIRST_USER_TYPE_ID,
RESERVED_COMMUNICATION_TYPES,
RESERVED_DATA_TYPES,
} from "../type-map/reserved.js";
export interface MTPVitePluginOptions {
typeMaps: string;
@ -17,6 +23,7 @@ export interface VitePlugin {
config?: (...args: any[]) => unknown;
buildStart?: (...args: any[]) => unknown;
configureServer?: (...args: any[]) => unknown;
addWatchFile?: (file: string) => void;
}
const packageRoot = process.env.MTP_PACKAGE_ROOT
@ -26,48 +33,6 @@ const rawEntryName = "mtp_wasm.js";
const wasmEntryName = "mtp_wasm_bg.wasm";
const typeMapEntryName = "mtp_type_map.js";
const reservedCommunicationTypes = [
"Identification",
"IdentificationResponse",
"Register",
"RegisterResponse",
"Challenge",
"ChallengeResponse",
"Ping",
"Pong",
"Disconnect",
"Redirect",
"Shutdown",
"Error",
"ErrorParsing",
"ErrorBadVersion",
"BadRequest",
"Unauthorized",
"Forbidden",
"NotFound",
"TooManyRequests",
"InternalServerError",
"BadGateway",
"ServiceUnavailable",
"GatewayTimeout",
];
const reservedDataTypes = [
"Version",
"Id",
"ClientNonce",
"ServerNonce",
"PublicKeys",
"Signature",
"PqSignature",
"Description",
"Connected",
"Timestamp",
"Error",
"ErrorParsing",
"ErrorMessage",
];
function normalizeOptions(options) {
if (!options?.typeMaps) {
throw new Error("mtp/vite requires a typeMaps option, for example mtp({ typeMaps: './type-maps.yaml' })");
@ -136,62 +101,40 @@ async function hashPackageInputs() {
return hash.digest("hex");
}
function quoteList(values) {
function quoteList(values: string[]): string {
return values.length === 0
? "never"
: values.map((value) => JSON.stringify(value)).join(" | ");
}
function parseTypeMapYaml(source, filePath) {
const communicationTypes = new Set(reservedCommunicationTypes);
const dataTypes = new Set(reservedDataTypes);
let section = null;
let sectionIndent = -1;
for (const [index, originalLine] of source.split(/\r?\n/).entries()) {
const withoutComment = originalLine.replace(/\s+#.*$/, "");
if (!withoutComment.trim()) {
continue;
}
if (/^\t/.test(withoutComment)) {
throw new Error(`${filePath}:${index + 1}: tabs are not supported in type-maps.yaml indentation`);
}
const indent = withoutComment.match(/^ */)?.[0].length ?? 0;
const trimmed = withoutComment.trim();
const sectionMatch = trimmed.match(/^(CommunicationTypes|DataTypes):\s*$/);
if (sectionMatch) {
section = sectionMatch[1];
sectionIndent = indent;
continue;
}
if (section && indent <= sectionIndent) {
section = null;
}
if (!section) {
continue;
}
const entryMatch = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*):\s*\d+\s*$/);
if (!entryMatch) {
throw new Error(`${filePath}:${index + 1}: expected '${section}' entries as 'Name: numeric_id'`);
}
if (section === "CommunicationTypes") {
communicationTypes.add(entryMatch[1]);
} else {
dataTypes.add(entryMatch[1]);
function parseTypeMapYaml(source: string, filePath: string) {
const document = YAML.parseDocument(source, { prettyErrors: false });
if (document.errors.length) {
const error = document.errors[0];
const line = error.pos?.[0] === undefined ? 1 : source.slice(0, error.pos[0]).split("\n").length;
throw new Error(`${filePath}:${line}: ${error.message}`);
}
const root = document.toJS() as { type_maps?: Record<string, { CommunicationTypes?: Record<string, unknown>; DataTypes?: Record<string, unknown> }> };
const communicationTypes = new Set<string>(RESERVED_COMMUNICATION_TYPES);
const dataTypes = new Set<string>(RESERVED_DATA_TYPES);
for (const [version, map] of Object.entries(root.type_maps ?? {})) {
if (!/^\d+\.\d+$/.test(version)) throw new Error(`${filePath}: unparseable type-map version '${version}'`);
const ids = new Map<number, string>();
for (const [section, target] of [["CommunicationTypes", communicationTypes], ["DataTypes", dataTypes]] as const) {
for (const [name, value] of Object.entries(map[section as "CommunicationTypes" | "DataTypes"] ?? {})) {
if (!Number.isInteger(value) || (value as number) < FIRST_USER_TYPE_ID) throw new Error(`${filePath}: ${version}.${section}.${name} must use an integer id >= ${FIRST_USER_TYPE_ID}`);
const id = value as number;
const previous = ids.get(id);
if (previous && previous !== `${section}.${name}`) throw new Error(`${filePath}: duplicate type id ${id} (${previous} and ${section}.${name})`);
ids.set(id, `${section}.${name}`);
target.add(name);
}
}
}
return {
communicationTypes: [...communicationTypes].sort(),
dataTypes: [...dataTypes].sort(),
};
return { communicationTypes: [...communicationTypes].sort(), dataTypes: [...dataTypes].sort() };
}
function generateTypeMapModule(metadata) {
function generateTypeMapModule(metadata: { communicationTypes: string[]; dataTypes: string[] }) {
const js = `export const communicationTypes = ${JSON.stringify(metadata.communicationTypes, null, 2)};\nexport const dataTypes = ${JSON.stringify(metadata.dataTypes, null, 2)};\n`;
const dts = `export type MTPCommunicationType = ${quoteList(metadata.communicationTypes)};\nexport type MTPDataType = ${quoteList(metadata.dataTypes)};\nexport declare const communicationTypes: readonly MTPCommunicationType[];\nexport declare const dataTypes: readonly MTPDataType[];\n`;
return { js, dts };
@ -334,7 +277,7 @@ async function buildIfNeeded(state, force = false) {
export function mtp(options: MTPVitePluginOptions): VitePlugin {
const normalized = normalizeOptions(options);
const state = {
const state: any = {
outDir: null,
typeMapsPath: null,
release: true,